diff --git a/.github/actions/spell-checker/ignore_words.txt b/.github/actions/spell-checker/ignore_words.txt index f4002b04..2eb3b1ee 100755 --- a/.github/actions/spell-checker/ignore_words.txt +++ b/.github/actions/spell-checker/ignore_words.txt @@ -3,6 +3,7 @@ 2fa 8f 8fdevelop +abi abtesting ach activedirectory @@ -182,9 +183,9 @@ ipaddress ipfs ipns iq +ir iran iraq -ir isr january javascript @@ -262,8 +263,8 @@ object-oriented objects ofac ok -onboarding onboarded +onboarding onchain online openssl @@ -343,10 +344,11 @@ roguelike rollup rollups rsa +ru ruby russia -ru s3 +saas saml sandbox sanitizer @@ -409,11 +411,11 @@ sunsetting svg swift swiss +sy syria syslog system-admin systemctl -sy table tag tags @@ -429,6 +431,7 @@ thursday’s tld tls token +toolset tooltip tooltips totp @@ -517,4 +520,4 @@ zendesk's zero-trust zimbabwe zip -zw \ No newline at end of file +zw diff --git a/.github/workflows/release-by-develop-hash.yml b/.github/workflows/release-by-develop-hash.yml index 7491206f..3343b629 100644 --- a/.github/workflows/release-by-develop-hash.yml +++ b/.github/workflows/release-by-develop-hash.yml @@ -21,8 +21,8 @@ jobs: merge-by-develop-hash: runs-on: ubuntu-latest env: - RELEASE_BRANCH: "main" - PREPARE_RELEASE_BRANCH: "prepare_release" + RELEASE_BRANCH: main + PREPARE_RELEASE_BRANCH: prepare_release steps: - name: Date safe-guard verification run: | @@ -117,13 +117,15 @@ jobs: - name: Dispatch Event env: - PAT: ${{ secrets.PAT }} + PAT: ${{ secrets.GITHUB_TOKEN }} ENDPOINT: 'https://api.github.com/repos/fleek-platform/website/dispatches' - EVENT_NAME: 'release-by-develop-hash-completed' + EVENT_NAME: 'Release' run: | if ! curl -H "Accept: application/vnd.github+json" \ -H "Authorization: token $PAT" \ --request POST \ - --data '{ "event_type": "$EVENT_NAME" }' $ENDPOINT; then + --data "{ \"event_type\": \"$EVENT_NAME\" }" $ENDPOINT; then echo "⚠️ Warning: Failed to dispatch $EVENT_NAME. Since this triggers the indexer listener, you should dispatch the action manually. If this issue persists, report it internally." + else + echo "✅ Dispatched event $EVENT_NAME" fi diff --git a/.github/workflows/search-indexer.yml b/.github/workflows/search-indexer.yml index b6be9f7e..2b20bfa0 100644 --- a/.github/workflows/search-indexer.yml +++ b/.github/workflows/search-indexer.yml @@ -1,16 +1,21 @@ name: 🚜 Search (Indexer) on: repository_dispatch: - types: release-by-develop-hash-completed + types: Release + workflow_dispatch: jobs: meilisearch: name: MeiliSearch Service runs-on: ubuntu-latest + env: + RELEASE_BRANCH: main environment: production steps: - name: Checkout Repository uses: actions/checkout@v4 + with: + ref: ${{ env.RELEASE_BRANCH }} - name: Install Dependencies run: npm ci @@ -56,3 +61,10 @@ jobs: env: PRIVATE_MEILISEARCH_DOCUMENTS_ADMIN_API_KEY: ${{ secrets.PRIVATE_MEILISEARCH_DOCUMENTS_ADMIN_API_KEY}} PUBLIC_MEILISEARCH_HOST: ${{ vars.PUBLIC_MEILISEARCH_HOST }} + + - name: Billing Indexation + run: | + npm run search:index_billing + env: + PRIVATE_MEILISEARCH_DOCUMENTS_ADMIN_API_KEY: ${{ secrets.PRIVATE_MEILISEARCH_DOCUMENTS_ADMIN_API_KEY}} + PUBLIC_MEILISEARCH_HOST: ${{ vars.PUBLIC_MEILISEARCH_HOST }} diff --git a/.tailwind/tailwind.custom.config.mjs b/.tailwind/tailwind.custom.config.mjs index 7662338c..a0db5d68 100644 --- a/.tailwind/tailwind.custom.config.mjs +++ b/.tailwind/tailwind.custom.config.mjs @@ -81,11 +81,21 @@ export default function (usePx) { "100%": { transform: "rotate(-9deg)", }, - } + }, + 'fade-in': { + '0%': { opacity: 0 }, + '100%': { opacity: 1 }, + }, + 'fade-in-down': { + '0%': { transform: 'translateY(2%)', opacity: 0 }, + '100%': { transform: 'translateY(0)', opacity: 1 }, + }, }, animation: { "blur-out": "blur 0.6s ease-out", "rock": "rock 1.2s infinite", + 'fade-in': 'fade-in 100ms ease-out', + 'fade-in-down': 'fade-in-down 120ms ease-out', }, }, }, diff --git a/README.md b/README.md index f9aba33c..793a3188 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,13 @@ This repository contains the source code and assets for the Fleek.xyz website, w - [Production Service Setup](#production-service-setup) - [Search](#🔎-search) - [Health Check](#health-check) - - [Indexer](#indexer) + - [Indexer](#indexer) - [Put markdown content](#put-markdown-content-development) - [Query via cURL](#query-via-curl) - [Multi-Index Search](#multi-index-search) - [Delete Indexes](#💣-delete-indexes) - - [Images (optimization)](#-images-optimization) + - [Manual Indexation](#manual-indexation-cicd) + - [Images (optimization)](#-images-optimization) - [Migration](#-migration) - [Migrate Gatsby content](#migrate-gatsby-content) - [Custom data](#custom-data) @@ -845,7 +846,7 @@ curl \ -X GET '://
:/health' ``` -### Indexer +## Indexer The Indexer's job referred to as indexation is the process of organizing and storing data in a structured manner to facilitate efficient search and retrieval. @@ -928,6 +929,16 @@ npm run search:serve ⚠️ You'll see a warning message "No master key was found" that can be ignored for local environment development work. If for some reason you want to have a master key, modify the `search:serve` script to include it. +### Manual Indexation (CI/CD) + +The indexation service should trigger on `push` to `main` branch. Alternatively, the repo admin can trigger the job manually. + +1) Open the Job runner [here](https://github.com/fleek-platform/website/actions/workflows/search-indexer.yml) + +2) Locate the row "This workflow has a workflow_dispatch event trigger." and open the **Run workflow** dropdown. In the option "Use workflow from" select `main` branch. + +The Job will index data that exists in the selected `main` branch. Learn how to release to production (main branch) [here](#🚀-release-to-production). + ## 📸 Images (Optimization) The build process can optimize the images but that requires the user to use the correct image components. Use the instructions provided to optimize the images. diff --git a/Services/ProxyServer/DeprecatedContentSitesRedirect/README.md b/Services/ProxyServer/DeprecatedContentSitesRedirect/README.md index a3873067..3869031e 100644 --- a/Services/ProxyServer/DeprecatedContentSitesRedirect/README.md +++ b/Services/ProxyServer/DeprecatedContentSitesRedirect/README.md @@ -170,3 +170,26 @@ Ref: - Nginx location match tester https://nginx.viraptor.info + +## Certificate auto-renewal + +The certificates are auto-renewed by the certbot process. Cronjob's are located at: + +``` +/etc/cron.d/certbot +``` + +Here's an example of a `renew_hook`, for Lets Encrypt renewal. + +```sh +# Trigger when renew successful +renew_hook = systemctl reload nginx +``` + +The configuration file is located at `/etc/letsencrypt/renewal/docs.fleek.xyz.conf`. + +Alternatively, executable scripts can be stored in: + +``` +/etc/letsencrypt/renewal-hooks/deploy/ +``` diff --git a/astro.config.mjs b/astro.config.mjs index 85157aac..9b3d751a 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -70,4 +70,7 @@ export default defineConfig({ theme: 'min-dark', }, }, + redirects: { + '/blog/learn/hottest-tends-2024-serverless-cloud-computing/': '/blog/learn/hottest-trends-2024-serverless-cloud-computing/', + }, }); diff --git a/package-lock.json b/package-lock.json index 29f7dd91..f1613031 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,10 +20,12 @@ "@types/react": "^18.2.74", "@types/react-dom": "^18.2.24", "astro": "^4.15.3", + "class-variance-authority": "^0.7.0", "framer-motion": "^11.0.24", "hono": "^4.4.11", "lodash-es": "^4.17.21", "pixi.js": "^7.2.4", + "posthog-js": "^1.165.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-fast-marquee": "^1.6.4", @@ -34,6 +36,7 @@ "rehype-pretty-code": "^0.13.1", "remark-directive": "^3.0.0", "swiper": "^11.1.0", + "tailwind-merge": "^2.5.2", "tailwindcss": "^3.4.3", "typescript": "^5.4.3" }, @@ -4295,6 +4298,27 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -4869,9 +4893,10 @@ } }, "node_modules/dset": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz", - "integrity": "sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", "engines": { "node": ">=4" } @@ -5217,6 +5242,11 @@ "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", "dev": true }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -8424,9 +8454,10 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, "node_modules/path-to-regexp": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", - "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", @@ -8767,6 +8798,16 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/posthog-js": { + "version": "1.165.0", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.165.0.tgz", + "integrity": "sha512-rUfRJobvOz3Q9Er+zwb32Eq2qs+ToBe/B4k4IoKzmyszI7240Rf4xVWRB0ky8LvmdZfCeYX5knS2Uv3pnn/d5A==", + "dependencies": { + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.0.1" + } + }, "node_modules/preact": { "version": "10.23.2", "resolved": "https://registry.npmjs.org/preact/-/preact-10.23.2.tgz", @@ -10614,6 +10655,16 @@ "node": ">=8" } }, + "node_modules/tailwind-merge": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz", + "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.10", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", @@ -11484,6 +11535,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-vitals": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.3.tgz", + "integrity": "sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 4f67fe88..aad838f6 100644 --- a/package.json +++ b/package.json @@ -61,10 +61,12 @@ "@types/react": "^18.2.74", "@types/react-dom": "^18.2.24", "astro": "^4.15.3", + "class-variance-authority": "^0.7.0", "framer-motion": "^11.0.24", "hono": "^4.4.11", "lodash-es": "^4.17.21", "pixi.js": "^7.2.4", + "posthog-js": "^1.165.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-fast-marquee": "^1.6.4", @@ -75,6 +77,7 @@ "rehype-pretty-code": "^0.13.1", "remark-directive": "^3.0.0", "swiper": "^11.1.0", + "tailwind-merge": "^2.5.2", "tailwindcss": "^3.4.3", "typescript": "^5.4.3" }, diff --git a/public/images/circles.png b/public/images/circles.png index c279bead..3f488ce9 100644 Binary files a/public/images/circles.png and b/public/images/circles.png differ diff --git a/public/images/fleek-meta.png b/public/images/fleek-meta.png index 747960e2..bab877ef 100644 Binary files a/public/images/fleek-meta.png and b/public/images/fleek-meta.png differ diff --git a/public/images/landing-page/hero/hero.webp b/public/images/landing-page/hero/hero.webp new file mode 100644 index 00000000..d2b7fa81 Binary files /dev/null and b/public/images/landing-page/hero/hero.webp differ diff --git a/public/images/landing-page/partners/berachain.svg b/public/images/landing-page/partners/berachain.svg new file mode 100644 index 00000000..7e4ddf55 --- /dev/null +++ b/public/images/landing-page/partners/berachain.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/landing-page/partners/ens.svg b/public/images/landing-page/partners/ens.svg new file mode 100644 index 00000000..8b4f3891 --- /dev/null +++ b/public/images/landing-page/partners/ens.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/landing-page/partners/filecoin.svg b/public/images/landing-page/partners/filecoin.svg new file mode 100644 index 00000000..0088676f --- /dev/null +++ b/public/images/landing-page/partners/filecoin.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/landing-page/partners/polygon.svg b/public/images/landing-page/partners/polygon.svg new file mode 100644 index 00000000..ea1163bb --- /dev/null +++ b/public/images/landing-page/partners/polygon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/images/landing-page/partners/protocollabs.svg b/public/images/landing-page/partners/protocollabs.svg new file mode 100644 index 00000000..f66badf5 --- /dev/null +++ b/public/images/landing-page/partners/protocollabs.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/images/landing-page/partners/velodrome.svg b/public/images/landing-page/partners/velodrome.svg new file mode 100644 index 00000000..040f501e --- /dev/null +++ b/public/images/landing-page/partners/velodrome.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/components/Announcement.tsx b/src/components/Announcement.tsx index 07ce25d4..9c1db4be 100644 --- a/src/components/Announcement.tsx +++ b/src/components/Announcement.tsx @@ -1,55 +1,70 @@ -import { useEffect, useState } from 'react'; -import Container from '@components/Container'; -import Text from '@components/Text'; -import Marquee from 'react-fast-marquee'; +import Link, { Target } from '@components/Link'; +import { useEffect, useState, type PropsWithChildren } from 'react'; +import { FaArrowRight } from 'react-icons/fa6'; import settings from '@base/settings.json'; +import Marquee from 'react-fast-marquee'; +import { useMediaQuery } from '@hooks/useMediaQuery'; +import { cn } from '@utils/cn'; -const announcement = `⚡ ${settings.site.annoucementMarquee.message} ⚡`; //character limit: 130 - -interface AnnouncementProps { - hasMargin?: boolean; -} +type AnnouncementProps = PropsWithChildren & { + variant?: 'content' | 'full' | 'docs'; +}; -const Announcement: React.FC = ({ - hasMargin = true, -}: AnnouncementProps) => { - const [mount, setMount] = useState(false); +export const Announcement: React.FC = ({ + variant = 'full', +}) => { + const [mounted, setMounted] = useState(false); useEffect(() => { - // Init marquee on component mount - setMount(true); + // Init marquee on component mounted + setMounted(true); }, []); + const shouldShowMarquee = useMediaQuery('(max-width: 800px)'); + + if (!settings.site.annoucementMarquee.visible) return null; + + if (variant === 'docs') + return ( + + + ✨ new + + {settings.site.annoucementMarquee.message} + + ); + return ( - - -
-
- - {mount && ( - - {announcement} - - )} - - - {announcement} - -
-
-
-
+ + ✨ new + + + + {mounted && shouldShowMarquee && ( + + {settings.site.annoucementMarquee.message} + + )} + {!shouldShowMarquee && settings.site.annoucementMarquee.message} + + + + ); }; - -export default Announcement; diff --git a/src/components/BestOnchain.tsx b/src/components/BestOnchain.tsx deleted file mode 100644 index d4683150..00000000 --- a/src/components/BestOnchain.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React from 'react'; -import Container from './Container'; -import PageSection from './PageSection'; -import type { RoundedType } from '@components/PageSection'; -import ButtonGray from './ButtonGray'; -import { PricingInfo } from '../content/pricing/config'; -import Text from './Text'; -// @ts-ignore -import imgBolt from '@images/bolt.png?w=120&format=webp'; -import ButtonYellow from './ButtonYellow'; -import PricingCard from './PricingCard'; - -type Formatting = { - rounded?: RoundedType; -}; - -const BestOnchain: React.FC = ({ rounded }) => { - return ( - - -
-
-

- Plus the Best of Onchain -

-
- fleek bolt icon -
- Storage, Compute, Domains, CDN & More -
- fleek bolt icon -
-
-
-
- onchain - onchain -
-
-
- ); -}; - -export default BestOnchain; diff --git a/src/components/BlogHeader.astro b/src/components/BlogHeader.astro index 98ed2e1b..d2d039c9 100644 --- a/src/components/BlogHeader.astro +++ b/src/components/BlogHeader.astro @@ -1,8 +1,8 @@ --- import SearchBtn from '@components/SearchBtn'; -import clsx from 'clsx'; import { getPathSubDirsSorted } from '@utils/paths'; import { customFilterByDirectoryName } from '@utils/menu'; +import { Button } from './Button'; const ROOT = 'all'; const { title, indexName, category } = Astro.props; @@ -15,9 +15,11 @@ const isBlogRoot = Astro.url.pathname.startsWith('/blog'); ---
-
+

{title}

@@ -65,18 +67,20 @@ const isBlogRoot = Astro.url.pathname.startsWith('/blog'); { isBlogRoot && categories.map((catName) => ( - {customFilterByDirectoryName({ name: catName, })} - + )) }
diff --git a/src/components/BlogPosts.astro b/src/components/BlogPosts.astro index 63aa454e..13b8f1c2 100644 --- a/src/components/BlogPosts.astro +++ b/src/components/BlogPosts.astro @@ -1,9 +1,7 @@ --- import { Image } from 'astro:assets'; -import type { CollectionEntry } from 'astro:content'; import type { ImageMetadata } from 'astro'; -import ButtonGray from './ButtonGray'; import { formatDate } from '@utils/date'; const { allPosts, collection } = Astro.props; @@ -22,17 +20,21 @@ type AllPosts = { ---
{ allPosts.map((post: AllPosts) => ( -
+
{post.data?.image && ( - + {post.data.title} )} -
-

+

+

{formatDate({ date: post.data.date })}

diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 5100713d..549e0446 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,20 +1,80 @@ -import Text from '@components/Text'; -import GlowWrapper from '@components/GlowWrapper'; +import Link, { Target } from '@components/Link'; +import type { PropsWithChildren } from 'react'; +import { type VariantProps, cva } from 'class-variance-authority'; +import { cn } from '@utils/cn'; -interface Props { - className?: string; -} +export const buttonVariants = cva( + 'flex font-medium no-underline gap-4 select-none font-plex-sans cursor-pointer items-center justify-center transition-all ring-0 outline-none focus-visible:ring-2', + { + variants: { + variant: { + primary: + 'bg-yellow-dark-9 hover:bg-yellow-dark-10 active:bg-yellow-dark-9 text-yellow-dark-1 ring-yellow-dark-8', + secondary: + 'bg-gray-dark-3 hover:bg-gray-dark-4 active:bg-gray-dark-3 !text-gray-dark-11 ring-gray-dark-8', + tertiary: + 'bg-gray-dark-12 text-gray-dark-1 hover:bg-gray-dark-11 active:bg-gray-dark-12 ring-gray-dark-8', + ghost: + 'bg-transparent hover:bg-gray-dark-3 active:bg-gray-dark-2 text-gray-dark-11 ring-gray-dark-8', + 'app-primary': + 'bg-yellow-dark-3 hover:bg-yellow-dark-4 active:bg-yellow-dark-3 text-yellow-dark-11 ring-yellow-dark-8', + 'app-success': + 'bg-ui-green hover:bg-ui-light-green text-ui-faded-green ring-ui-faded-green', + }, + size: { + sm: 'h-32 gap-4 px-8 rounded-8 text-[1.4rem]', + md: 'h-40 gap-8 px-16 rounded-12 text-[1.6rem]', + lg: 'h-44 gap-8 rounded-12 text-[1.6rem]', + }, + }, + defaultVariants: { + variant: 'primary', + size: 'md', + }, + }, +); + +type ButtonProps = PropsWithChildren & + VariantProps & { + id?: string; + href?: string; + className?: string; + target?: Target; + rel?: string; + onClick?: (e?: React.MouseEvent) => void; + }; + +export const Button: React.FC = ({ + id, + children, + href, + target, + rel, + onClick, + variant, + size, + className, +}) => { + if (href) + return ( + + {children} + + ); -const Button: React.FC> = (props) => { return ( - - - + ); }; - -export default Button; diff --git a/src/components/ButtonGhost.tsx b/src/components/ButtonGhost.tsx deleted file mode 100644 index 980b6379..00000000 --- a/src/components/ButtonGhost.tsx +++ /dev/null @@ -1,22 +0,0 @@ -interface Props { - className?: string; - border?: string; - onClick?: () => void; -} - -const ButtonGhost: React.FC> = ({ - onClick, - className, - ...props -}) => { - return ( - - ); -}; - -export default ButtonGhost; diff --git a/src/components/ButtonGray.tsx b/src/components/ButtonGray.tsx deleted file mode 100644 index 2e59098f..00000000 --- a/src/components/ButtonGray.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import Text from '@components/Text'; - -interface Props { - className?: string; - border?: string; - onClick?: () => void; -} - -const ButtonGray: React.FC> = ({ - onClick, - className, - ...props -}) => { - return ( - - ); -}; - -export default ButtonGray; diff --git a/src/components/ButtonRainbowOutlined.tsx b/src/components/ButtonRainbowOutlined.tsx deleted file mode 100644 index 9fabe4a0..00000000 --- a/src/components/ButtonRainbowOutlined.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import Text from '@components/Text'; - -const ButtonRainbowOutlined: React.FC = (props) => { - return ( - - ); -}; - -export default ButtonRainbowOutlined; diff --git a/src/components/ButtonYellow.tsx b/src/components/ButtonYellow.tsx deleted file mode 100644 index cd228655..00000000 --- a/src/components/ButtonYellow.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import Text from '@components/Text'; -import type { PropsWithChildren } from 'react'; - -type ButtonYellowProps = React.ButtonHTMLAttributes & - PropsWithChildren & { - className?: string; - border?: string; - }; - -const ButtonYellow = ({ - children, - className, - border, - ...props -}: ButtonYellowProps) => { - return ( - - ); -}; - -export default ButtonYellow; diff --git a/src/components/ButtonYellowOutline.tsx b/src/components/ButtonYellowOutline.tsx deleted file mode 100644 index cf35b26c..00000000 --- a/src/components/ButtonYellowOutline.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import Text from '@components/Text'; - -type ButtonWhiteProps = React.PropsWithChildren & { - className?: string; - color?: string; -}; - -const ButtonYellowOutline: React.FC = (props) => { - return ( - - ); -}; - -export default ButtonYellowOutline; diff --git a/src/components/CardWrapper.tsx b/src/components/CardWrapper.tsx deleted file mode 100644 index 8f40a6e4..00000000 --- a/src/components/CardWrapper.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import GlowWrapper from '@components/GlowWrapper'; -import clsx from 'clsx'; - -interface Props { - className?: string; - noInnerPadding?: boolean; - image?: string; - header?: boolean; -} - -const CardWrapper: React.FC> = (props) => { - return ( - -
- {props.image && ( -
- -
- )} -
- {props.children} -
-
-
- ); -}; - -export default CardWrapper; diff --git a/src/components/CardsWithDottedLinesBackground.tsx b/src/components/CardsWithDottedLinesBackground.tsx deleted file mode 100644 index 67507a1c..00000000 --- a/src/components/CardsWithDottedLinesBackground.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import Container from '@components/Container'; -import GridLayout from '@components/GridLayout'; -import PageSection from '@components/PageSection'; -import TemplateAppCard from '@components/TemplateAppCard'; -import TextGlowHoverEffect from './TextGlowHoverEffect'; -import Link, { Target } from '@components/Link'; -import type { RoundedType } from '@components/PageSection'; -import ButtonGray from './ButtonGray'; -import Text from './Text'; - -interface Card { - title: string; - description: string; - icon: { - src: string; - alt: string; - }; - cta: { - url: string; - text: string; - }; - image: string; -} - -interface CardSection { - title: string; - cards: Array; -} - -interface Props { - headline: string; - copy?: string; - cardSections: Array; - cta?: { - url: string; - text: string; - }; - rounded?: RoundedType; -} - -const CardsWithDottedLinesBackground: React.FC = (props) => ( - - - -
-
-

- {props.headline} -

- {props.copy &&

{props.copy}

} -
-
- bg-squiggle - bg-squiggle -
- {props.cardSections.map((section) => ( -
-
- {section.cards.map( - ({ title, description, icon, cta, image }) => ( - - - - ), - )} -
-
- ))} - {props.cta && ( -
- - - {props.cta.text} - - -
- )} -
-
-
-
-
-
-); - -export default CardsWithDottedLinesBackground; diff --git a/src/components/Changelog/ChangelogListBody.astro b/src/components/Changelog/ChangelogListBody.astro index 658d17fa..f6c7aa62 100644 --- a/src/components/Changelog/ChangelogListBody.astro +++ b/src/components/Changelog/ChangelogListBody.astro @@ -8,7 +8,7 @@ const { Content } = await entry!.render();
diff --git a/src/components/DeployOnFleek.tsx b/src/components/DeployOnFleek.tsx deleted file mode 100644 index 8ab8a2fa..00000000 --- a/src/components/DeployOnFleek.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import type { RoundedType } from '@components/PageSection'; -import Container from './Container'; -import PageSection from '@components/PageSection'; -import DeployWithImage from './DeployWithImage'; - -interface Props { - rounded?: RoundedType; -} - -const DeployOnFleek: React.FC = ({ rounded }) => ( - Deploy Apps in a Flash} - copy="Link your repo and go live. Deploy from a Git Provider or the Fleek CLI." - cta={{ url: 'https://app.fleek.xyz/', text: 'try it out' }} - rounded={rounded} - /> -); - -export default DeployOnFleek; - -// className="pb-64 pt-24 xl:py-40" diff --git a/src/components/DeployWithImage.tsx b/src/components/DeployWithImage.tsx deleted file mode 100644 index 4df9cd4c..00000000 --- a/src/components/DeployWithImage.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import clsx from 'clsx'; - -import Container from '@components/Container'; -import PageSection from '@components/PageSection'; -import GridLayout from '@components/GridLayout'; -import Button from '@components/Button'; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; - -import type { RoundedType } from '@components/PageSection'; -import Link, { Target } from '@components/Link'; - -import { down } from '@utils/screens'; -import Text from './Text'; - -const IconList = [ - { - icon: '/svg/git-integration-icon.svg', - description: 'Git integration, actions & check runs', - }, - { - icon: '/svg/zig-zag-icon.svg', - description: 'Deploy previews with every pull request', - }, - { - icon: '/svg/ns-icon.svg', - description: 'automatic updates on every push, with zero downtime ', - }, -]; - -type OptionalProps = - | { - // TODO: Verify as original was import type { StaticImageData } from "next/image"; - image: string; - children?: never; - } - | { - image?: never; - children?: React.ReactNode; - }; - -type Props = { - kicker: string; - headline: string | JSX.Element; - copy: string; - cta?: { - url: string; - text: string; - }; - floatingImageEffect?: boolean; - inverse?: boolean; - className?: string; - rounded?: RoundedType; -}; - -type CardProp = { - icon: string; - description: string; -}; - -const Card: React.FC = (props) => { - return ( -
-
-
- -
-
-
- {props.description} -
-
- ); -}; - -const DeployWithImage: React.FC = (props) => { - return ( - - -
-
-

- {props.headline} -

- -

- {props.copy} -

- -
- {IconList.map((item, index) => { - return ( - - ); - })} -
-
-
- -
-
-

- {props.headline} -

- -

- {props.copy} -

-
-
-
-
- ); -}; - -export default DeployWithImage; diff --git a/src/components/DocItemLink.tsx b/src/components/DocItemLink.tsx index 6cf8ac01..5c49f794 100644 --- a/src/components/DocItemLink.tsx +++ b/src/components/DocItemLink.tsx @@ -20,27 +20,25 @@ const DocItemLink: React.FC = ({ flex-row rounded-10 p-20 - text-white no-underline ${isNext ? 'ml-auto items-end' : 'mr-auto items-start'} border - border-gray-400 - hover:border-white + border-gray-dark-6 + hover:border-gray-dark-7 + hover:bg-gray-dark-1 `} >
- {!isNext && } + {!isNext && } {isNext ? 'Next' : 'Previous'} - {isNext && } -
-
- {docItem.title} + {isNext && }
+ {docItem.title}
); diff --git a/src/components/DocsCards.tsx b/src/components/DocsCards.tsx deleted file mode 100644 index d3f18175..00000000 --- a/src/components/DocsCards.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import clsx from 'clsx'; -import GridLayout from '@components/GridLayout'; -import PageSection from '@components/PageSection'; -import CardWrapper from '@components/CardWrapper'; -import Text from '@components/Text'; - -type Feature = { - title: string; - description: string; - icon: string; - className?: string; - url: string; -}; - -type BlackFeatureCardsProps = { - title: string; - features: Array; - className?: string; -}; - -const BlackFeatureCards: React.FC = ({ - title, - features, - className, -}) => ( -
- -
-
- {title} -
-
-
- bg-squiggle - {features.map((feature, index) => ( - - -
- globe with bolt - - {feature.title} - - - {feature.description} - -
-
-
- ))} -
-
-
-); - -export default BlackFeatureCards; diff --git a/src/components/DocsHero.tsx b/src/components/DocsHero.tsx deleted file mode 100644 index 009f5ad5..00000000 --- a/src/components/DocsHero.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import PageSection from '@components/PageSection'; -import Text from './Text'; -import ButtonYellow from './ButtonYellow'; -import ButtonGray from './ButtonGray'; - -type Props = { - title: string; - description: string; -}; - -const DocsHero = ({ title, description }: Props) => ( -
- -
- -

{title}

-
-

{description}

- -
-
-
-); - -export default DocsHero; diff --git a/src/components/DocsTitledCards.tsx b/src/components/DocsTitledCards.tsx deleted file mode 100644 index c8a76d32..00000000 --- a/src/components/DocsTitledCards.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import GridLayout from '@components/GridLayout'; -import PageSection from '@components/PageSection'; -import Text from '@components/Text'; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; -import TransparentCard from '@components/TransparentCard'; - -type Items = { - title: string; - description: string; - icon: { - src: string; - alt: string; - }; - url: string; -}; - -type Props = { - title: string; - items: Items[]; -}; -const DocsTitledCards = ({ title, items }: Props) => ( -
- - -
- - {title} - -
- {items.map((item, index) => ( - - - - ))} -
-
-
-
-
-); - -export default DocsTitledCards; diff --git a/src/components/ExplainerBlocks.tsx b/src/components/ExplainerBlocks.tsx deleted file mode 100644 index a9d638b4..00000000 --- a/src/components/ExplainerBlocks.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import ExplainerCard from '@components/ExplainerCard'; -import Container from './Container'; -import PageSection from './PageSection'; - -const List1 = [ - { - icon: '/svg/edge-icon.svg', - title: 'Build Seamlessly', - description: - 'Using either the Platform, CLI, or SDK you can build how you want.', - }, - { - icon: '/svg/colaborate-icon.svg', - title: 'Work Collaboratively', - description: - 'Invite your team, share deploy previews, test builds and more.', - }, - { - icon: '/svg/concentric-circles-icon.svg', - title: 'Deploy Globally', - description: 'The Fleek Edge helps your app load lightning fast worldwide.', - }, -]; -const List2 = [ - { - icon: '/svg/ddos-icon.svg', - title: 'DDoS Protection', - description: - 'Using either the Platform, CLI, or SDK you can build how you want.', - }, - { - icon: '/svg/monitoring-icon.svg', - title: 'Monitoring & Alerting', - description: - 'Invite your team, share deploy previews, test builds and more.', - }, - { - icon: '/svg/ssl-icon.svg', - title: 'Domains & SSL', - description: 'The Fleek Edge helps your app load lightning fast worldwide.', - }, -]; - -type props = { - index: number; - header: string; -}; - -const ExplainerBlocks: React.FC = ({ index, header }) => { - function ListSelector() { - if (index == 1) { - return List1; - } else if (index == 2) { - return List2; - } - return List2; - } - - var List = ListSelector(); - - return ( - - -
- {/* */} -

- {header} -

- {/*
*/} -
-
- {List.map((item, index) => { - return ( - - ); - })} - - -
-
-
- ); -}; - -export default ExplainerBlocks; diff --git a/src/components/ExplainerCard.tsx b/src/components/ExplainerCard.tsx deleted file mode 100644 index b322f955..00000000 --- a/src/components/ExplainerCard.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { Props } from 'astro'; -import React from 'react'; -import Text from './Text'; - -type CardProp = { - icon: string; - title: string; - description: string; -}; - -const ExplainerCard: React.FC = (props) => { - return ( -
-
-
- -
-
-

- {props.title} -

-
-
-

- {props.description} -

-
-
-
- ); -}; - -export default ExplainerCard; diff --git a/src/components/FAQ.tsx b/src/components/FAQ.tsx deleted file mode 100644 index 6d719ea9..00000000 --- a/src/components/FAQ.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import Container from '@components/Container'; -import PageSection from '@components/PageSection'; -import GridLayout from '@components/GridLayout'; -import Text from '@components/Text'; -import { useState } from 'react'; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; -import { pricing } from '@base/settings.json'; - -import type { PropsWithChildren } from 'react'; - -const Dropdown = ({ - title, - children, -}: PropsWithChildren<{ title: string }>) => { - const [isOpen, setIsOpen] = useState(false); - - return ( -
-
setIsOpen(!isOpen)} - > - - {title} - -
- {isOpen ? - : +} -
-
- {isOpen &&
{children}
} -
- ); -}; - -const FAQ = () => ( - - - -
- - Frequently Asked Questions - -
-
- {pricing.faq.map(({ question, answer }) => ( - - {answer} - - ))} -
-
-
-
-); - -export default FAQ; diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 6f9e1f9d..708d47c3 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,132 +1,143 @@ -import GridLayout from '@components/GridLayout'; -import Container from '@components/Container'; -import Text from '@components/Text'; import IconSocial from '@components/IconSocial'; import StatusBar from '@components/StatusBar'; import Link, { Target } from '@components/Link'; -import config from './config'; import { FaXTwitter } from 'react-icons/fa6'; +import type React from 'react'; +import { Container } from '../LandingPage/Container'; +import { Text } from '../LandingPage/Text'; +import config from './config'; const { product, developers, company, resources, fleekPlatformOrgUrl } = config; -const Footer = () => { +const Footer: React.FC = () => { return ( -
- - -
- fleek logo - - The edge-optimized cloud platform - -
- - - - - - - - - + +
+
+ fleek logo +

The lightning fast onchain cloud.

+ +
+ +
+
+
+
+
+ Product +
    + {product.map((item, index) => ( +
  • + + {item.text} + +
  • + ))} +
-
- +
+ Developers +
    + {developers.map((item, index) => ( +
  • + + {item.text} + +
  • + ))} +
-
-
-
-
- Product -
    - {product.map((item, index) => ( -
  • - - {item.text} - -
  • - ))} -
-
-
- Developers -
    - {developers.map((item, index) => ( -
  • - - {item.text} - -
  • - ))} -
-
-
- Resources -
    - {resources.map((item, index) => ( -
  • - - {item.text} - -
  • - ))} -
-
-
- Company -
    - {company.map((item, index) => ( -
  • - - {item.text} - -
  • - ))} -
-
+
+ Resources +
    + {resources.map((item, index) => ( +
  • + + {item.text} + +
  • + ))} +
+
+
+ Company +
    + {company.map((item, index) => ( +
  • + + {item.text} + +
  • + ))} +
- - -
+
+
+ ); }; diff --git a/src/components/GlobeWithFloatingCards.tsx b/src/components/GlobeWithFloatingCards.tsx deleted file mode 100644 index 4196bda0..00000000 --- a/src/components/GlobeWithFloatingCards.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import Container from '@components/Container'; -import PageSection from '@components/PageSection'; -import GridLayout from '@components/GridLayout'; -import ButtonYellow from './ButtonYellow'; -import { useState } from 'react'; -import ButtonGray from './ButtonGray'; -import Link from './Link'; - -const GlobeWithFloatingCards = () => { - const [showPlayer, setShowPlayer] = useState(false); - const [showPlayerMobile, setShowPlayerMobile] = useState(false); - const [hoverEffect, setHoverEffect] = useState(false); - - return ( - - -
- -
-

- Build Lightning Fast -

- -

- Fleek is an edge-optimized cloud platform. Effortlessly build, - ship and
- scale highly performant apps. -

- -

- Fleek is an edge-optimized cloud platform. Effortlessly build, - ship and scale highly performant apps. -

-
- - - - Try it now - - - - View docs - -
-
-
-
-
-
setShowPlayer(true)} - > - Fleek setHoverEffect(true)} - onMouseLeave={() => setHoverEffect(false)} - /> - Fleek -
- -
setShowPlayerMobile(!showPlayerMobile)} - > - Fleek setHoverEffect(true)} - onMouseLeave={() => setHoverEffect(false)} - /> - Fleek -
- -
-
-
-
-
-
- ); -}; - -export default GlobeWithFloatingCards; diff --git a/src/components/GlowWrapper.tsx b/src/components/GlowWrapper.tsx deleted file mode 100644 index 6ca53117..00000000 --- a/src/components/GlowWrapper.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import clsx from 'clsx'; - -interface Props { - className?: string; - hidden?: boolean; -} - -const GlowWrapper: React.FC> = ({ - className, - hidden, - children, -}) => { - return ( -
- {children} - -
- ); -}; - -export default GlowWrapper; diff --git a/src/components/GoBackButton.tsx b/src/components/GoBackButton.tsx new file mode 100644 index 00000000..35257fa1 --- /dev/null +++ b/src/components/GoBackButton.tsx @@ -0,0 +1,18 @@ +import { FaChevronLeft } from 'react-icons/fa6'; +import { Button } from './Button'; + +export const GoBackButton: React.FC = () => { + const handleClick = () => { + if (history.length > 2) { + history.back(); + } else { + window.location.href = '/blog'; + } + }; + + return ( + + ); +}; diff --git a/src/components/HostingOnFleek.tsx b/src/components/HostingOnFleek.tsx deleted file mode 100644 index 49a42aa0..00000000 --- a/src/components/HostingOnFleek.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import ImageWithCopy from '@components/ImageWithCopy'; -import type { RoundedType } from '@components/PageSection'; -interface Props { - rounded?: RoundedType; -} - -const HostingOnFleek: React.FC = ({ rounded }) => ( - Plus the Best of Onchain} - copy="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eu libero ac nunc mollis accumsan at non purus." - cta={{ url: 'https://app.fleek.xyz/', text: 'try it out' }} - rounded={rounded} - > -
- Hosting on Fleek -
-
-); - -export default HostingOnFleek; diff --git a/src/components/ImageWithCopy.tsx b/src/components/ImageWithCopy.tsx deleted file mode 100644 index d6df1993..00000000 --- a/src/components/ImageWithCopy.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import clsx from 'clsx'; - -import Container from '@components/Container'; -import PageSection from '@components/PageSection'; -import GridLayout from '@components/GridLayout'; -import Button from '@components/Button'; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; -import Text from './Text'; -import type { RoundedType } from '@components/PageSection'; -import Link, { Target } from '@components/Link'; - -import { down } from '@utils/screens'; - -const list = [ - { - icon: '/svg/serverless-icon.svg', - title: 'DECENTRALIZED STORAGE', - }, - { - icon: '/svg/compute-icon.svg', - title: 'VERIFIABLE COMPUTE', - }, - { - icon: '/svg/gateways-icon.svg', - title: 'DECENTRALIZED CDN', - }, -]; - -type OptionalProps = - | { - // TODO: Verify as original was import type { StaticImageData } from "next/image"; - image: string; - children?: never; - } - | { - image?: never; - children: React.ReactNode; - }; - -type Props = { - kicker: string; - headline: string | JSX.Element; - copy: string; - cta?: { - url: string; - text: string; - }; - floatingImageEffect?: boolean; - inverse?: boolean; - className?: string; - rounded?: RoundedType; -}; - -type CardProp = { - icon: string; - title: string; -}; - -const Card: React.FC = (props) => { - return ( -
-
-
-
- -
-
-
-
- {props.title} -
-
- ); -}; - -const ImageWithCopy: React.FC = (props) => { - return ( - - -
- -
-
- {props.children} - {!props.children && props.image && ( - - )} -
-
-
- {/*
- {props.kicker} -
*/} -

- {props.headline} -

-

- {props.copy} -

-
- {list.map((item, index) => { - return ( - - ); - })} -
-
-
-
-
-
- ); -}; - -export default ImageWithCopy; diff --git a/src/components/ImpressUsers.tsx b/src/components/ImpressUsers.tsx deleted file mode 100644 index fa906b40..00000000 --- a/src/components/ImpressUsers.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import clsx from 'clsx'; - -import Container from '@components/Container'; -import PageSection from '@components/PageSection'; -import GridLayout from '@components/GridLayout'; -import Button from '@components/Button'; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; -import { GrShare } from 'react-icons/gr'; - -import type { RoundedType } from '@components/PageSection'; -import Link, { Target } from '@components/Link'; - -import { down } from '@utils/screens'; -import Text from './Text'; - -const IconList = [ - { - icon: '/svg/seo-icon.svg', - description: 'improve seo', - }, - { - icon: '/svg/bounce-icon.svg', - description: 'reduce bounce rates', - }, - { - icon: '/svg/hosting-icon.svg', - description: 'gain & retain more users ', - }, -]; - -type OptionalProps = - | { - // TODO: Verify as original was import type { StaticImageData } from "next/image"; - image: string; - children?: never; - } - | { - image?: never; - children?: React.ReactNode; - }; - -type Props = { - kicker?: string; - headline?: string | JSX.Element; - copy?: string; - cta?: { - url: string; - text: string; - }; - floatingImageEffect?: boolean; - inverse?: boolean; - className?: string; - rounded?: RoundedType; -}; - -type CardProp = { - icon: string; - description: string; -}; - -const Card: React.FC = (props) => { - return ( -
-
-
- -
- -
- {props.description} -
-
-
- ); -}; - -const ImpressUsers: React.FC = ({ rounded }) => { - return ( - - -
- -
-

- Gain More Users -

- -

- Your app needs to load fast, otherwise you'll{' '} - - lose customers - - .
- Fleek runs your app in 100+ edge locations to ensure fast loading - worldwide. -

- -
- {IconList.map((item, index) => { - return ( - - ); - })} -
-
-
- -
-
-
-
- ); -}; - -export default ImpressUsers; diff --git a/src/components/JoinOurFriends.tsx b/src/components/JoinOurFriends.tsx deleted file mode 100644 index 2748638a..00000000 --- a/src/components/JoinOurFriends.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import LoopingCardSlider from '@components/LoopingCardSlider'; - -const JoinOurFriends = () => ( - -); - -export default JoinOurFriends; diff --git a/src/components/LandingPage/BlurFade.tsx b/src/components/LandingPage/BlurFade.tsx new file mode 100644 index 00000000..802d2345 --- /dev/null +++ b/src/components/LandingPage/BlurFade.tsx @@ -0,0 +1,66 @@ +import { useRef } from 'react'; +import { + AnimatePresence, + motion, + useInView, + type UseInViewOptions, + type Variants, +} from 'framer-motion'; + +type MarginType = UseInViewOptions['margin']; + +type BlurFadeProps = { + children: React.ReactNode; + className?: string; + variant?: { + hidden: { y: number }; + visible: { y: number }; + }; + duration?: number; + delay?: number; + yOffset?: number; + inView?: boolean; + inViewMargin?: MarginType; + blur?: string; +}; + +export const BlurFade: React.FC = ({ + children, + className, + variant, + duration = 0.4, + delay = 0, + yOffset = 6, + inView = false, + inViewMargin = '-50px', + blur = '6px', +}) => { + const ref = useRef(null); + const inViewResult = useInView(ref, { once: true, margin: inViewMargin }); + const isInView = !inView || inViewResult; + const defaultVariants: Variants = { + hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` }, + visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` }, + }; + const combinedVariants = variant || defaultVariants; + + return ( + + + {children} + + + ); +}; diff --git a/src/components/BuildUseCases.tsx b/src/components/LandingPage/BuildUseCases.tsx similarity index 52% rename from src/components/BuildUseCases.tsx rename to src/components/LandingPage/BuildUseCases.tsx index b40612c8..a5814250 100644 --- a/src/components/BuildUseCases.tsx +++ b/src/components/LandingPage/BuildUseCases.tsx @@ -1,40 +1,23 @@ -import CardsWithDottedLinesBackground from '@components/CardsWithDottedLinesBackground'; +import CardsWithDottedLinesBackground from '@components/LandingPage/CardsWithDottedLinesBackground'; -import type { RoundedType } from '@components/PageSection'; - -interface Props { - rounded?: RoundedType; -} -// TODO: Should the card declaration be in the settings? -const BuildUseCases: React.FC = (props) => ( +const BuildUseCases: React.FC = () => ( = (props) => ( title: 'Astro Template', description: 'Deploy the Astro boilerplate for an efficient, modern web experience.', - icon: { src: '/svg/react-icon.svg', alt: 'React App' }, + icon: { src: '/svg/react-icon.svg', alt: 'Astro App' }, cta: { - url: 'https://app.fleek.xyz/templates/clmf7io4a0009ic08ya3sjwyj/', + url: 'https://app.fleek.xyz/templates/clx3f5nem000333n7acqcxiwj/', text: 'try it', }, image: '/svg/astro-template.svg', }, + { + title: 'Vue Template', + description: `Leverage the Vue boilerplate to streamline your project's setup.`, + icon: { src: '/svg/react-icon.svg', alt: 'Vue.js App' }, + cta: { + url: 'https://app.fleek.xyz/templates/clx3gdagl0001byl7romw0ynd/', + text: 'try it', + }, + image: '/svg/vuejs-template.svg', + }, ], }, ]} diff --git a/src/components/LandingPage/CardsWithDottedLinesBackground.tsx b/src/components/LandingPage/CardsWithDottedLinesBackground.tsx new file mode 100644 index 00000000..79be6eaf --- /dev/null +++ b/src/components/LandingPage/CardsWithDottedLinesBackground.tsx @@ -0,0 +1,91 @@ +import Link, { Target } from '@components/Link'; +import { Container } from './Container'; +import { Text } from './Text'; +import { Button } from '../Button'; + +interface Card { + title: string; + description: string; + icon: { + src: string; + alt: string; + }; + cta: { + url: string; + text: string; + }; + image: string; +} + +interface CardSection { + title: string; + cards: Array; +} + +interface Props { + headline: string; + copy?: string; + cardSections: Array; + cta?: { + url: string; + text: string; + }; +} + +const CardsWithDottedLinesBackground: React.FC = (props) => ( + +
+
+ {props.headline} + {props.copy &&

{props.copy}

} +
+
+
+ {props.cardSections.map((section) => ( +
+
+ {section.cards.map(({ title, description, cta, image }) => ( + +
+ {title} +
+
+

+ {title} +

+

{description}

+
+ + ))} +
+
+ ))} + {props.cta && ( + + )} +
+
+
+
+); + +export default CardsWithDottedLinesBackground; diff --git a/src/components/LandingPage/Container.tsx b/src/components/LandingPage/Container.tsx new file mode 100644 index 00000000..bb8efbd3 --- /dev/null +++ b/src/components/LandingPage/Container.tsx @@ -0,0 +1,42 @@ +import { cn } from '@utils/cn'; +import type { PropsWithChildren } from 'react'; + +type ContainerProps = PropsWithChildren & { + classNameOuterContainer?: string; + classNameInnerContainer?: string; + gradient?: 'left' | 'right'; +}; + +export const Container: React.FC = ({ + children, + classNameOuterContainer, + classNameInnerContainer, + gradient = 'left', +}) => { + return ( +
+
+
+ {children} +
+
+ ); +}; diff --git a/src/components/LandingPage/DeployOnFleek.tsx b/src/components/LandingPage/DeployOnFleek.tsx new file mode 100644 index 00000000..8d26908d --- /dev/null +++ b/src/components/LandingPage/DeployOnFleek.tsx @@ -0,0 +1,79 @@ +import { down } from '@utils/screens'; +import { Text } from './Text'; +import { Container } from './Container'; + +const IconList = [ + { + icon: '/svg/git-integration-icon.svg', + description: 'Git integration, actions & check runs', + }, + { + icon: '/svg/zig-zag-icon.svg', + description: 'Deploy previews with every pull request', + }, + { + icon: '/svg/ns-icon.svg', + description: 'automatic updates on every push, with zero downtime ', + }, +]; + +type CardProp = { + icon: string; + description: string; +}; + +const Card: React.FC = (props) => { + return ( +
+
+ {props.description} +
+ {props.description} +
+ ); +}; + +const DeployOnFleek: React.FC = () => { + return ( + +
+
+
+ Deploy apps in a flash + + Link your repo and go live. Deploy from a Git Provider or the + Fleek CLI. + +
+ Deploy to repo +
+ {IconList.map((item, index) => { + return ( + + ); + })} +
+
+ Deploy to repo +
+
+ ); +}; + +export default DeployOnFleek; diff --git a/src/components/LandingPage/ExplainerBlocks.tsx b/src/components/LandingPage/ExplainerBlocks.tsx new file mode 100644 index 00000000..c0566ed4 --- /dev/null +++ b/src/components/LandingPage/ExplainerBlocks.tsx @@ -0,0 +1,100 @@ +import React from 'react'; +import { Container } from './Container'; +import { Text } from './Text'; + +const List = [ + { + icon: '/svg/edge-icon.svg', + title: 'Build Seamlessly', + description: + 'Using either the Platform, CLI, or SDK you can build how you want.', + }, + { + icon: '/svg/colaborate-icon.svg', + title: 'Work Collaboratively', + description: + 'Invite your team, share deploy previews, test builds and more.', + }, + { + icon: '/svg/concentric-circles-icon.svg', + title: 'Deploy Globally', + description: 'Fleek automatically deploys your app to 100+ edge locations.', + }, + { + icon: '/svg/ddos-icon.svg', + title: 'DDoS Protection', + description: 'Effortlessly protect your site from DDoS attacks.', + }, + { + icon: '/svg/monitoring-icon.svg', + title: 'Monitoring & Alerting', + description: 'Get insights and alerts for your app & infra usage.', + }, + { + icon: '/svg/ssl-icon.svg', + title: 'Domains & SSL', + description: + 'Seamless domain management & free SSL certificates (Lets Encrypt).', + }, +]; + +type CardProp = { + icon: string; + title: string; + description: string; +}; + +const ExplainerCard: React.FC = (props) => { + return ( +
+
+ {props.title} +
+
+

+ {props.title} +

+
+
+

+ {props.description} +

+
+
+ ); +}; + +const ExplainerBlocks: React.FC = () => { + return ( + +
+ Enjoy the (developer) experience +
+
+ {List.map((item, index) => { + return ( + + ); + })} + bg-squiggle +
+
+ ); +}; + +export default ExplainerBlocks; diff --git a/src/components/LandingPage/Hero.tsx b/src/components/LandingPage/Hero.tsx new file mode 100644 index 00000000..ba992889 --- /dev/null +++ b/src/components/LandingPage/Hero.tsx @@ -0,0 +1,67 @@ +import settings from '@base/settings.json'; +import { FaChevronRight } from 'react-icons/fa6'; +import { Announcement } from '../Announcement'; +import { Button } from '../Button'; +import { Target } from '@components/Link'; +import { BlurFade } from './BlurFade'; +import { Text } from './Text'; + +export const Hero = () => { + const calculateDelay = (factor: number) => 0.25 * factor; + + return ( +
+
+
+ {settings.site.annoucementMarquee.visible && ( + + + + )} +
+ + {settings.landingPage.hero.h1} + + + + {settings.landingPage.hero.h2} + + +
+ + + + + + +
+
+
+ +
+
+
+ + Fleek dashboard + +
+
+
+
+
+ ); +}; diff --git a/src/components/LandingPage/ImpressUsers.tsx b/src/components/LandingPage/ImpressUsers.tsx new file mode 100644 index 00000000..1e4437dd --- /dev/null +++ b/src/components/LandingPage/ImpressUsers.tsx @@ -0,0 +1,97 @@ +import { Container } from './Container'; +import { Text } from './Text'; + +const IconList = [ + { + icon: '/svg/seo-icon.svg', + description: 'improve seo', + }, + { + icon: '/svg/bounce-icon.svg', + description: 'reduce bounce rates', + }, + { + icon: '/svg/hosting-icon.svg', + description: 'gain & retain more users ', + }, +]; + +type CardProp = { + icon: string; + description: string; +}; + +const Card: React.FC = (props) => { + return ( +
+ {props.description} + {props.description} +
+ ); +}; + +const ImpressUsers: React.FC = () => { + return ( + +
+
+ +
+
+
+ Gain more users + + Your app needs to load fast, otherwise you'll lose customers. + Fleek runs your app in 100+ edge locations to ensure fast loading + worldwide. + +
+ +
+ {IconList.map((item, index) => { + return ( + + ); + })} +
+
+
+ Globe +
+
+
+ ); +}; + +export default ImpressUsers; diff --git a/src/components/LandingPage/Partners.tsx b/src/components/LandingPage/Partners.tsx new file mode 100644 index 00000000..29dc711e --- /dev/null +++ b/src/components/LandingPage/Partners.tsx @@ -0,0 +1,46 @@ +import type React from 'react'; +import settings from '@base/settings.json'; +import Link from '@components/Link'; + +type PartnerProps = { + name: string; + logo: string; + caseStudyUrl: string; +}; + +const Partner: React.FC = ({ name, logo, caseStudyUrl }) => { + const className = 'flex h-80 items-center justify-center sm:h-116'; + const img = ( + {name} + ); + + if (caseStudyUrl) + return ( + + {img} + + ); + + return
{img}
; +}; + +export const Partners: React.FC = () => { + return ( +
+

+ Join 100,000+ +  developers who rely on Fleek to power their Web apps and tools. +

+
+ {settings.landingPage.partners.map((partner) => ( + + ))} +
+
+ ); +}; diff --git a/src/components/LandingPage/PricingLanding.tsx b/src/components/LandingPage/PricingLanding.tsx new file mode 100644 index 00000000..f89f794c --- /dev/null +++ b/src/components/LandingPage/PricingLanding.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { PricingInfo } from '../../content/pricing/config'; +import PricingCard from '../PricingCard'; +import { Container } from './Container'; +import { Text } from './Text'; + +const PricingLanding: React.FC = () => { + return ( + +
+ Best pricing. Period. +
+ {PricingInfo.map((item, index) => { + return ( + + ); + })} +
+
+
+ ); +}; + +export default PricingLanding; diff --git a/src/components/LandingPage/ReadyToLive.tsx b/src/components/LandingPage/ReadyToLive.tsx new file mode 100644 index 00000000..aa9204c4 --- /dev/null +++ b/src/components/LandingPage/ReadyToLive.tsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { Container } from './Container'; +import { Text } from './Text'; +import { Button } from '../Button'; + +const ReadyToLive: React.FC = () => { + return ( + + Are you on Fleek yet? + + + ); +}; + +export default ReadyToLive; diff --git a/src/components/LandingPage/Text.tsx b/src/components/LandingPage/Text.tsx new file mode 100644 index 00000000..7db62112 --- /dev/null +++ b/src/components/LandingPage/Text.tsx @@ -0,0 +1,52 @@ +import { cn } from '@utils/cn'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { createElement, forwardRef, type PropsWithChildren } from 'react'; + +const textVariants = cva([], { + variants: { + variant: { + title: + 'text-balance font-sans text-[3.6rem] font-semibold leading-[1.125] -tracking-2 text-gray-dark-12 md:text-[5.2rem]', + description: + 'text-balance font-plex-sans text-[1.8rem] font-medium text-gray-dark-11', + feature: + 'font-plex-sans text-[1.2rem] font-medium uppercase tracking-[0.256rem] text-gray-dark-11', + }, + }, + defaultVariants: { + variant: 'title', + }, +}); + +export const validTextElement = [ + 'p', + 'span', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', +] as const; + +type ValidTextElement = (typeof validTextElement)[number]; + +type TextProps = PropsWithChildren & + React.HTMLAttributes & + VariantProps & { + as?: ValidTextElement; + }; + +export const Text = forwardRef( + ({ children, as = 'p', variant, className, ...props }, ref) => { + return createElement( + as, + { + ref, + className: cn(textVariants({ variant }), className), + ...props, + }, + children, + ); + }, +); diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 0e48aabb..b7dec8c5 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -1,4 +1,5 @@ -import type { FC, PropsWithChildren } from 'react'; +import React, { forwardRef } from 'react'; +import type { PropsWithChildren } from 'react'; export enum Target { Blank = '_blank', @@ -7,23 +8,13 @@ export enum Target { Top = '_top', } -interface Props { - href?: string; - className?: string; - target?: Target; - tabIndex?: number; -} - -export const Link: FC> = ({ - href, - className, - target, - tabIndex, - children, -}) => ( - +const Link = forwardRef< + HTMLAnchorElement, + PropsWithChildren> +>(({ children, ...props }, ref) => ( + {children} -); +)); export default Link; diff --git a/src/components/LiveOnTheEdge.tsx b/src/components/LiveOnTheEdge.tsx deleted file mode 100644 index 578fa72e..00000000 --- a/src/components/LiveOnTheEdge.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import Container from '@components/Container'; -import ActionButton from '@components/ActionButton'; -import GridLayout from '@components/GridLayout'; -import PageSection from '@components/PageSection'; -import Text from '@components/Text'; -import TextGlowHoverEffect from './TextGlowHoverEffect'; - -const LiveOnTheEdge = () => ( - - - -
- - Ready to live on the edge? - - -
-
-
-
-); - -export default LiveOnTheEdge; diff --git a/src/components/LoopingCardSlider.tsx b/src/components/LoopingCardSlider.tsx deleted file mode 100644 index 063b3c0a..00000000 --- a/src/components/LoopingCardSlider.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Swiper, SwiperSlide } from 'swiper/react'; -import Marquee from 'react-fast-marquee'; - -import Container from '@components/Container'; -import GridLayout from '@components/GridLayout'; -import PageSection from '@components/PageSection'; -// TODO: needs correct replacement, check original intent -// import ExportedImage from "next-image-export-optimizer"; -import TextGlowHoverEffect from '@components/TextGlowHoverEffect'; - -import { down, up } from '@utils/screens'; - -interface Card { - bannerImage: string; -} - -interface Props { - headline: string; - cards: Array; -} - -const Card: React.FC<{ card: Card }> = ({ card }) => { - return ( -
- {'Framework -
- ); -}; - -const LoopingCardSlider: React.FC = (props) => { - return ( - - -
- -
- -

{props.headline}

-
-
-
-
- -
- {props.cards.concat(props.cards).map((card, index) => ( - - ))} -
-
- -
- {/* */} -

{props.headline}

- {/*
*/} -
-
-
- - {props.cards.map((card, index) => ( - - - - ))} - -
-
-
-
- ); -}; - -export default LoopingCardSlider; diff --git a/src/components/NavBar/component.css b/src/components/NavBar/component.css deleted file mode 100644 index 89f47b43..00000000 --- a/src/components/NavBar/component.css +++ /dev/null @@ -1,146 +0,0 @@ -@tailwind components; - -@layer components { - .nav-container { - @apply relative z-10 flex select-none items-center justify-between gap-33 py-16 pl-16 lg:px-28; - - nav { - @apply hidden items-center gap-0 pl-0 leading-10 xl:flex; - } - - .nav-drop-down-container { - @apply relative cursor-pointer py-20; - } - } - - .nav-link { - @apply px-18; - } - - .nav-button { - @apply ml-auto w-48 px-16 font-plex-sans text-25 leading-[150%] text-ui-white xl:hidden; - } - - .nav-menu { - @apply fixed left-0 top-0 z-40 flex h-full w-full flex-col overflow-y-scroll bg-black pb-50; - } - - .nav-menu-open { - @apply pointer-events-auto flex gap-[1.438rem] opacity-100; - } - - .nav-menu-closed { - @apply pointer-events-none opacity-0; - } - - .nav-menu-logo { - @apply flex py-16 pl-24; - } - - .nav-menu-close-button { - @apply ml-auto mr-18 w-42 px-16 font-plex-sans text-25 leading-[150%] text-ui-white; - } - - .nav-menu-mobile { - @apply flex flex-col items-center justify-center; - - .nav-menu-item { - @apply relative w-full font-plex-sans text-18; - } - } - - .nav-menu-mobile-sub-menu { - @apply w-full; - - li { - a { - @apply inline-block leading-loose; - } - } - } - - .nav-menu-mobile-sub-menu-container { - @apply w-full sm:w-1/2 md:w-1/4; - } - - .nav-sub-menu-ctas-mobile { - @apply mt-10 hidden w-full flex-col items-start gap-10 pl-20 group-hover:flex; - - .nav-sub-menu-cta { - @apply block w-auto px-12 py-6; - } - } - - .nav-menu-item-selected { - @apply hidden; - } - - .nav-menu-item-children { - @apply flex flex-col items-center gap-16 rounded-12 bg-fleek-gradient p-12; - } - - .nav-menu-item-children-link { - @apply font-plex-sans text-13 font-medium leading-[150%]; - } - - .nav-menu-launch-app { - @apply mx-auto; - } - - .nav-sub-menu-container { - @apply absolute top-full hidden h-auto w-[30vw] min-w-[600px] cursor-default gap-40 rounded-12 border-1 border-ui-mid-grey bg-[#222222cd] backdrop-blur-xl group-hover:block; - } - - .nav-sub-menu-wrap { - @apply flex w-full; - } - - .nav-sub-menu-main-col { - @apply flex-1; - } - - .nav-sub-menu-nav-cols { - @apply grid grid-cols-2 gap-20 p-20; - } - - .nav-sub-menu-cta-items { - @apply grid grid-cols-2 gap-50 p-36 pt-18; - } - - .nav-sub-menu-nav-col-title { - @apply pb-20 font-sans text-12 uppercase text-gray-600 dark:text-gray-500; - } - - .nav-sub-menu-nav-col-list { - @apply flex flex-col gap-4; - - a { - @apply text-16 capitalize transition hover:opacity-80; - } - } - - .nav-sub-menu-cta { - @apply relative z-[1] inline-block w-auto cursor-pointer items-center justify-between gap-15 overflow-hidden rounded-2 bg-gray-100 p-12 font-sans text-16 text-gray-900 transition hover:bg-gray-400 hover:text-gray-600; - - > span { - @apply relative z-[1] inline-block text-16 capitalize transition-transform; - } - } - - .nav-sub-menu-side-container { - @apply w-1/3 border-l-1 border-gray-300 bg-ui-fleek-black p-36; - } - - a > .nav-menu-mobile-sub-menu-label { - @apply select-none rounded-5 px-10 hover:bg-gray-900; - } - - a > .nav-menu-mobile-sub-menu-label, - .nav-menu-mobile .nav-menu-item > a > .nav-m { - @apply hover:text-ui-faded-gray; - } - - .nav-menu-mobile .nav-menu-item:has(> a[href='/docs']) { - @apply hidden; - } -} diff --git a/src/components/NavBar/index.tsx b/src/components/NavBar/index.tsx deleted file mode 100644 index d99c0552..00000000 --- a/src/components/NavBar/index.tsx +++ /dev/null @@ -1,371 +0,0 @@ -import './component.css'; -import Link, { Target } from '@components/Link'; -import { useState, useEffect } from 'react'; -import clsx from 'clsx'; -import { useMediaQuery } from '@hooks/useMediaQuery'; -import { up } from '@utils/screens'; -import ButtonYellowOutline from '@components/ButtonYellowOutline'; -import Container from '@components/Container'; -import Text from '@components/Text'; -import { FaXTwitter } from 'react-icons/fa6'; -import { FaDiscord } from 'react-icons/fa'; -import { RxHamburgerMenu } from 'react-icons/rx'; -import { RxCross2 } from 'react-icons/rx'; - -import { hasSecondaryMenuItem, isActivePath } from '@utils/url'; - -import { NavBarDefault } from './config'; - -import ButtonYellow from '@components/ButtonYellow'; -import ButtonGray from '@components/ButtonGray'; -import SupportMenu from '@components/Support/SupportMenu'; - -export type NavProps = { pathname: string }; -export type NavSubMenuCtaProps = Omit; -export type NavSubMenuNavColProps = { - label: string; - items: NavSubMenuNavColItem[]; -}; - -export type MenuSettingsItem = { - label?: string; - subMenu?: NavSubMenuProps[]; - url?: string; - description?: string; - icon?: string; - openInNewTab?: boolean; -}; -export type NavSubMenuNavColItem = Omit; - -export type NavSubMenuProps = { - label: string; - url: string; - description: string; - icon: string; - openInNewTab?: boolean; -}; - -const NavSubMenuNavCol = ({ - label, - url, - description, - icon, -}: NavSubMenuNavColItem) => { - return ( - -
-
- -
-
-
{label}
-
{description}
-
-
- - ); -}; - -const NavSubMenu = ({ subMenu }: MenuSettingsItem) => { - return ( -
-
-
-
- {subMenu?.map(({ label, url, description, icon }, index) => ( - - ))} -
-
-
-
- ); -}; - -const Nav = ({ pathname }: NavProps) => { - const [isOpen, setIsOpen] = useState(false); - - const hasSecondaryMenu = hasSecondaryMenuItem(pathname); - - useEffect(() => { - if (isOpen) { - document.body.classList.add('body-no-scroll'); - } else { - document.body.classList.remove('body-no-scroll'); - } - }, [isOpen]); - - const isLg = useMediaQuery(up('lg')); - - useEffect(() => { - setIsOpen(false); - }, [pathname]); - - useEffect(() => { - if (isLg) { - setIsOpen(false); - } - }, [isLg]); - - const docsPaths = [ - '/docs', - '/guides', - '/references', - '/templates', - '/support', - ]; - - return ( - -
-
- - fleek logo - - -
- -
-
- - - -
-
- - - -
- - - -
-
- -
-
- fleek logo - -
- - -
- -
-
-
- - - -
-
- - - -
-
-
- - {hasSecondaryMenu && } -
- ); -}; - -export default Nav; diff --git a/src/components/NavBar/config.ts b/src/components/Navbar/config.ts similarity index 65% rename from src/components/NavBar/config.ts rename to src/components/Navbar/config.ts index cf807abd..2390c881 100644 --- a/src/components/NavBar/config.ts +++ b/src/components/Navbar/config.ts @@ -1,33 +1,17 @@ -import type { MenuSettingsItem } from './index'; +export type NavMenuItem = { + label: string; + subMenu?: NavSubMenuItem[]; + url?: string; + description?: string; + icon?: string; + openInNewTab?: boolean; +}; -// Warning: when editing enable Typescript LSP -// to avoid any typos. -// -// Each `menuItem` is an object with key `label` -// and `url`. There are the optionals `openInNewTab` -// and the `subMenu`. -// -// { -// label: "Blog", -// url: "/blog", -// openInNewTab: true or false -// } -// -// The `subMenu` takes a list of `subMenuItems`. -// The apearance is limited to the conditions -// supported by the styles or component. -// -// For this reason the data is placed in -// the `main` and `side` (optional) fields. -// -// { -// label: "A title" -// items: -// } -// -export const NavBarDefault: MenuSettingsItem[] = [ +export type NavSubMenuItem = Omit; + +export const navbarMenu: NavMenuItem[] = [ { - label: 'PRODUCTS ', + label: 'Features', subMenu: [ { label: 'Platform', @@ -56,19 +40,21 @@ export const NavBarDefault: MenuSettingsItem[] = [ ], }, { - label: 'DEVELOPERS', + label: 'Developers', subMenu: [ { label: 'Fleek Network', url: 'https://fleek.network', description: 'Edge-optimized infrastructure', icon: '/svg/infra-navbar-icon.svg', + openInNewTab: true, }, { label: 'Github', url: 'https://github.com/fleek-platform', description: 'Our code repositories', icon: '/svg/github-navbar-icon.svg', + openInNewTab: true, }, { label: 'Changelog', @@ -81,11 +67,12 @@ export const NavBarDefault: MenuSettingsItem[] = [ url: 'https://status.fleek.xyz/', description: 'Status uptime monitoring', icon: '/svg/status-navbar-icon.svg', + openInNewTab: true, }, ], }, { - label: 'RESOURCES', + label: 'Resources', subMenu: [ { label: 'Documentation', @@ -102,50 +89,28 @@ export const NavBarDefault: MenuSettingsItem[] = [ { label: 'Media kit', url: 'https://www.notion.so/fleek/Fleek-Brand-Kit-9a2bcf7eb40740a9b7e951fc951b478a', - description: 'Our branding guidelines.', + description: 'Our branding guidelines', icon: '/svg/media-navbar-icon.svg', + openInNewTab: true, }, { label: 'Support Center', url: '/support', - openInNewTab: true, description: 'Get help', icon: '/svg/community-navbar-icon.svg', }, ], }, { - label: 'DOCS', + label: 'Docs', url: '/docs', }, { - label: 'BLOG', + label: 'Blog', url: '/blog', }, { - label: 'PRICING', + label: 'Pricing', url: '/pricing', }, ]; - -export const NavBarDocs: MenuSettingsItem[] = [ - { - label: 'documentation', - url: '/docs', - }, - { - label: 'guides', - url: '/guides', - }, - { - label: 'Changelog', - url: '/changelog', - }, - { - label: 'Support', - url: '/support', - openInNewTab: true, - }, -]; - -export default NavBarDefault; diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx new file mode 100644 index 00000000..af9e63e2 --- /dev/null +++ b/src/components/Navbar/index.tsx @@ -0,0 +1,310 @@ +import type React from 'react'; +import { navbarMenu, type NavMenuItem, type NavSubMenuItem } from './config'; +import Link, { Target } from '@components/Link'; +import { useCallback, useState } from 'react'; +import { FaArrowRight, FaDiscord, FaXmark, FaXTwitter } from 'react-icons/fa6'; +import { cn } from '@utils/cn'; +import { RxHamburgerMenu } from 'react-icons/rx'; +import { isActivePath } from '@utils/url'; +import { Button } from '../Button'; + +const NavbarMobileItem: React.FC = ({ + label, + subMenu, + url, + openInNewTab, +}) => { + if (!subMenu) + return ( + + {label} + + ); + + return ( +
+ {label} +
+ {subMenu.map((subMenuItem) => ( + + {subMenuItem.description} + {subMenuItem.label} + + ))} +
+
+ ); +}; + +const NavbarMobile: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + + const toggle = () => setIsOpen((prev) => !prev); + + return ( + <> + + {isOpen && ( +
+
+ fleek logo + +
+
+ {navbarMenu.map((navbarItem) => ( + + ))} +
+
+ + +
+
+ )} + + ); +}; + +type PopoverDimensions = { + left: number; + height: number | string; +}; + +type OnMouseEnterSubMenuProps = PopoverDimensions & { + idx: number; +}; + +const NavbarSubMenuItem: React.FC = ({ + url, + icon, + label, + description, + openInNewTab, +}) => { + return ( + +
+ + {description} +
+
+ {label} + + {description} + +
+ + ); +}; + +type NavbarItemProps = NavMenuItem & { + idx: number; + pathname: string; + hovering: number | null; + popoverDimensions: PopoverDimensions | null; + onMouseEnterSubMenu: ({ + idx, + left, + height, + }: OnMouseEnterSubMenuProps) => void; + removeHovering: () => void; +}; + +const NavbarItem: React.FC = ({ + idx, + pathname, + subMenu, + url, + label, + hovering, + popoverDimensions, + onMouseEnterSubMenu, + removeHovering, +}) => { + const isActivePage = isActivePath({ lookup: url || '', pathname }); + + if (!subMenu) + return ( + + {label} + + ); + + const isActiveSubMenu = hovering === idx + 1; + + return ( + <> +
+ onMouseEnterSubMenu({ + idx, + left: e.currentTarget.offsetLeft, + height: 'auto', + }) + } + onMouseEnter={(e) => + onMouseEnterSubMenu({ + idx, + left: e.currentTarget.offsetLeft, + height: 'auto', + }) + } + > + {label} +
+
+ {isActiveSubMenu && ( +
+ {subMenu.map((item, idx) => ( + + ))} +
+ )} +
+ + ); +}; + +type NavbarProps = { + variant?: 'fixed' | 'sticky'; + pathname: string; + className?: string; +}; + +export const Navbar: React.FC = ({ + pathname, + className, + variant = 'sticky', +}) => { + const [hovering, setHovering] = useState(null); + const [popoverDimensions, setPopoverDimensions] = + useState(null); + + const onMouseEnterSubMenu = useCallback( + ({ idx, left, height }: OnMouseEnterSubMenuProps) => { + setHovering(idx + 1); + setPopoverDimensions({ left, height }); + }, + [], + ); + + return ( +
+
+ +
+ ); +}; diff --git a/src/components/NewsletterSubscriptionModal.tsx b/src/components/NewsletterSubscriptionModal.tsx index 5b2a73b8..bdad1b55 100644 --- a/src/components/NewsletterSubscriptionModal.tsx +++ b/src/components/NewsletterSubscriptionModal.tsx @@ -3,11 +3,11 @@ import { MdEmail } from 'react-icons/md'; import React, { useState } from 'react'; import { PiWarningCircleFill } from 'react-icons/pi'; import { GoCheckCircleFill } from 'react-icons/go'; -import ButtonGray from './ButtonGray'; import settings from '@base/settings.json'; import Loading from '@components/Loading'; import type { Dispatch, SetStateAction, MouseEvent } from 'react'; +import { Button } from './Button'; const { activeHostedFormApi } = settings.newsletterSubscription; @@ -170,12 +170,9 @@ export const CtaNewsletterModal = () => { return ( <> - + ); diff --git a/src/components/OnPageNavigation/OnPageNavigation.astro b/src/components/OnPageNavigation/OnPageNavigation.astro index b2bb1e5e..526e2430 100644 --- a/src/components/OnPageNavigation/OnPageNavigation.astro +++ b/src/components/OnPageNavigation/OnPageNavigation.astro @@ -9,14 +9,10 @@ type Props = { const { headings } = Astro.props as Props; --- -