├── .changeset └── config.json ├── .eslintrc.cjs ├── .github ├── renovate.json5 └── workflows │ ├── ci.yml │ ├── cloudflare-workers.yaml │ ├── pr.yml │ ├── release.yml │ └── storybook.yaml ├── .gitignore ├── .node-version ├── .npmrc ├── .prettierignore ├── .storybook ├── design-system-docs-decorator.tsx ├── global.css ├── hive-theme-decorator.tsx ├── main.ts ├── preview.tsx └── public │ └── fonts │ ├── NeueMontreal-Bold.otf │ ├── PPNeueMontreal-Medium.woff2 │ └── PPNeueMontreal-Regular.woff2 ├── .vscode └── settings.json ├── README.md ├── package.json ├── packages ├── components │ ├── CHANGELOG.md │ ├── package.json │ ├── src │ │ ├── cn.ts │ │ ├── components │ │ │ ├── anchor.tsx │ │ │ ├── button.tsx │ │ │ ├── call-to-action.stories.tsx │ │ │ ├── call-to-action.tsx │ │ │ ├── cards-colorful.stories.ts │ │ │ ├── cards-colorful.tsx │ │ │ ├── comparison-table │ │ │ │ ├── index.tsx │ │ │ │ └── table.stories.tsx │ │ │ ├── contact-us.stories.tsx │ │ │ ├── contact-us.tsx │ │ │ ├── cookies-consent.stories.tsx │ │ │ ├── cookies-consent.tsx │ │ │ ├── decorations │ │ │ │ ├── arch-decoration-gradient-defs.svg │ │ │ │ ├── arch-decoration.svg │ │ │ │ ├── decorations.stories.tsx │ │ │ │ ├── highlight-decoration.svg │ │ │ │ ├── index.tsx │ │ │ │ └── large-hive-icon-decoration.svg │ │ │ ├── dropdown.tsx │ │ │ ├── explore-main-product-cards.stories.ts │ │ │ ├── explore-main-product-cards.tsx │ │ │ ├── faq │ │ │ │ ├── attach-page-faq-schema.ts │ │ │ │ └── index.tsx │ │ │ ├── feature-list.stories.ts │ │ │ ├── feature-list.tsx │ │ │ ├── get-your-api-game-right-section.stories.ts │ │ │ ├── get-your-api-game-right-section.tsx │ │ │ ├── giscus.tsx │ │ │ ├── heading.stories.tsx │ │ │ ├── heading.tsx │ │ │ ├── hero-gradient.stories.ts │ │ │ ├── hero-gradient.tsx │ │ │ ├── hero-illustration.stories.ts │ │ │ ├── hero-illustration.tsx │ │ │ ├── hero-marketplace.stories.ts │ │ │ ├── hero-marketplace.tsx │ │ │ ├── hero-video.stories.ts │ │ │ ├── hero-video.tsx │ │ │ ├── hero │ │ │ │ ├── hero-decoration-from-logo.tsx │ │ │ │ ├── hero-gradient-defs.tsx │ │ │ │ ├── hero-gradient-ids.ts │ │ │ │ ├── hero-logo.tsx │ │ │ │ ├── hero.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── hive-footer │ │ │ │ ├── index.stories.tsx │ │ │ │ └── index.tsx │ │ │ ├── hive-layout-config.tsx │ │ │ ├── hive-navigation │ │ │ │ ├── graphql-conf-card.tsx │ │ │ │ ├── index.stories.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── local-image-for-stories.png │ │ │ │ └── navigation-menu.tsx │ │ │ ├── icons │ │ │ │ ├── account-box.svg │ │ │ │ ├── apps.svg │ │ │ │ ├── arrow-icon.svg │ │ │ │ ├── bard.svg │ │ │ │ ├── caret-slim.svg │ │ │ │ ├── check.svg │ │ │ │ ├── close.svg │ │ │ │ ├── codegen.svg │ │ │ │ ├── csa-star-level-one.svg │ │ │ │ ├── group.svg │ │ │ │ ├── hive-gateway.svg │ │ │ │ ├── hive.svg │ │ │ │ ├── honour.svg │ │ │ │ ├── icons.stories.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── linkedin.svg │ │ │ │ ├── list.svg │ │ │ │ ├── mesh.svg │ │ │ │ ├── moon.svg │ │ │ │ ├── more.svg │ │ │ │ ├── paper.svg │ │ │ │ ├── pencil.svg │ │ │ │ ├── right-corner.svg │ │ │ │ ├── search.svg │ │ │ │ ├── share.svg │ │ │ │ ├── shield-flash.svg │ │ │ │ ├── stellate.svg │ │ │ │ ├── target.svg │ │ │ │ ├── twitter.svg │ │ │ │ ├── yoga.svg │ │ │ │ └── youtube.svg │ │ │ ├── image.tsx │ │ │ ├── index.ts │ │ │ ├── info-card.stories.tsx │ │ │ ├── info-card.tsx │ │ │ ├── info-list.stories.ts │ │ │ ├── info-list.tsx │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ ├── input-shake.tsx │ │ │ │ └── input.stories.tsx │ │ │ ├── legacy-package-cmd.tsx │ │ │ ├── marketplace-list.stories.tsx │ │ │ ├── marketplace-list.tsx │ │ │ ├── marketplace-search.stories.ts │ │ │ ├── marketplace-search.tsx │ │ │ ├── marquee │ │ │ │ ├── index.tsx │ │ │ │ ├── marquee.stories.tsx │ │ │ │ └── use-tween-playback-rate.ts │ │ │ ├── npm-badge.tsx │ │ │ ├── product-card │ │ │ │ ├── hive-decoration.svg │ │ │ │ ├── hive-gateway-decoration.svg │ │ │ │ ├── index.tsx │ │ │ │ ├── mesh-decoration.svg │ │ │ │ ├── product-card.stories.tsx │ │ │ │ └── yoga-decoration.svg │ │ │ ├── products │ │ │ │ ├── envelop.stories.tsx │ │ │ │ └── tools.stories.tsx │ │ │ ├── schema-type.stories.ts │ │ │ ├── schema-type.tsx │ │ │ ├── stud.stories.tsx │ │ │ ├── stud.tsx │ │ │ ├── tag.tsx │ │ │ ├── text-link.stories.tsx │ │ │ ├── text-link.tsx │ │ │ ├── theme-switcher.tsx │ │ │ ├── tools-and-libraries-cards │ │ │ │ ├── index.tsx │ │ │ │ └── tools-and-libraries.stories.ts │ │ │ ├── version-dropdown.stories.tsx │ │ │ └── version-dropdown.tsx │ │ ├── constants.ts │ │ ├── design-system │ │ │ ├── color-palette.stories.tsx │ │ │ └── typography.stories.tsx │ │ ├── env.d.ts │ │ ├── helpers │ │ │ └── dummy.ts │ │ ├── icon.d.ts │ │ ├── index.ts │ │ ├── logos │ │ │ ├── angular.svg │ │ │ ├── code-generator.svg │ │ │ ├── conductor.svg │ │ │ ├── config.svg │ │ │ ├── fets.svg │ │ │ ├── graphql-foundation.svg │ │ │ ├── guild.svg │ │ │ ├── heltin.svg │ │ │ ├── hive-combination-mark.svg │ │ │ ├── index.tsx │ │ │ ├── kitql.svg │ │ │ ├── mesh.svg │ │ │ ├── modules.svg │ │ │ ├── nextra.svg │ │ │ ├── sse.svg │ │ │ ├── stitching.svg │ │ │ ├── the-guild.svg │ │ │ ├── tools.svg │ │ │ ├── whatsapp.svg │ │ │ ├── ws.svg │ │ │ └── yoga.svg │ │ ├── next-types.ts │ │ ├── product.stories.tsx │ │ ├── products.tsx │ │ ├── server │ │ │ ├── body.client.tsx │ │ │ ├── hive-layout.tsx │ │ │ ├── index.ts │ │ │ ├── mdx-components │ │ │ │ ├── hive-mdx-components.ts │ │ │ │ ├── index.ts │ │ │ │ ├── mdx-components.tsx │ │ │ │ ├── mdx-link.stories.ts │ │ │ │ └── mdx-link.tsx │ │ │ ├── next.config.ts │ │ │ ├── npm.ts │ │ │ ├── pages.ts │ │ │ ├── remark-link-rewrite.ts │ │ │ ├── shared-meta-items.ts │ │ │ ├── theme-layout.tsx │ │ │ └── underscore-redirects.ts │ │ ├── static │ │ │ ├── conf │ │ │ │ ├── graphql-conf-bg.png │ │ │ │ ├── graphql-conf-logo-horiz.svg │ │ │ │ └── graphql-conf-logo.svg │ │ │ ├── dummy │ │ │ │ ├── envelop │ │ │ │ │ ├── communication.png │ │ │ │ │ ├── features-modern.png │ │ │ │ │ ├── features-performant.png │ │ │ │ │ ├── features-pluggable.png │ │ │ │ │ └── hero.png │ │ │ │ └── marketplace │ │ │ │ │ └── logo-modules.svg │ │ │ └── illustrations │ │ │ │ ├── marketplace-cube-bl.png │ │ │ │ ├── marketplace-cube-br.png │ │ │ │ ├── marketplace-cube-tl.png │ │ │ │ ├── marketplace-cube-tr.png │ │ │ │ ├── marketplace-desktop.png │ │ │ │ ├── marketplace-mobile.png │ │ │ │ └── yoga.svg │ │ └── types │ │ │ ├── components.ts │ │ │ ├── severity.ts │ │ │ └── utility.ts │ ├── style.css │ ├── tsconfig.json │ └── tsup.config.ts ├── editor │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── editor │ │ │ ├── editor.stories.tsx │ │ │ ├── enriched-language-service.ts │ │ │ ├── enums.ts │ │ │ ├── executable-document-editor.tsx │ │ │ ├── schema-diff-editor.tsx │ │ │ ├── schema-editor.tsx │ │ │ ├── use-schema-services.ts │ │ │ └── utils.ts │ │ └── index.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── og-image │ ├── global.d.ts │ ├── package.json │ ├── src │ │ ├── __image_snapshots__ │ │ │ ├── handler-test-ts-handler-should-align-title-and-have-container-padding-1-snap.png │ │ │ ├── handler-test-ts-handler-should-align-title-without-whitespaces-1-snap.png │ │ │ ├── handler-test-ts-handler-should-work-1-snap.png │ │ │ ├── handler-test-ts-packages-og-image-src-handler-test-ts-handler-should-align-title-and-have-container-padding-1-snap.png │ │ │ ├── handler-test-ts-packages-og-image-src-handler-test-ts-handler-should-align-title-without-whitespaces-1-snap.png │ │ │ └── handler-test-ts-packages-og-image-src-handler-test-ts-handler-should-works-1-snap.png │ │ ├── components.tsx │ │ ├── conf-handler.tsx │ │ ├── handler.test.ts │ │ ├── handler.tsx │ │ ├── img.ts │ │ ├── index.ts │ │ ├── types.d.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── vender │ │ └── .gitkeep │ └── wrangler.toml ├── remark-mermaid │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.test.ts │ │ ├── index.ts │ │ └── mermaid.tsx │ ├── tsconfig.json │ └── tsup.config.ts └── remark-npm2yarn │ ├── CHANGELOG.md │ ├── README.md │ ├── __tests__ │ ├── __snapshots__ │ │ └── plugin.test.ts.snap │ └── plugin.test.ts │ ├── package.json │ ├── src │ ├── constants.ts │ ├── index.ts │ └── plugin.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── patches ├── esbuild-plugin-svgr.patch └── tsup@8.2.1.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── postcss.config.js ├── prettier.config.js ├── scripts └── copy-wasm.ts ├── setup-file.ts ├── tailwind.config.ts ├── tsconfig.json ├── turbo.json ├── vite.config.ts └── website ├── app ├── _meta.ts ├── docs │ ├── _meta.ts │ ├── import │ │ ├── page.mdx │ │ └── reused.mdx │ ├── mermaid │ │ └── page.mdx │ ├── npm2yarn │ │ └── page.mdx │ ├── page.mdx │ ├── remote │ │ └── page.mdx │ └── video │ │ └── page.mdx ├── favicon.ico ├── layout.tsx ├── not-found.tsx └── page.tsx ├── mdx-components.js ├── next-env.d.ts ├── next.config.ts ├── package.json ├── postcss.config.js ├── public ├── demo.mp4 ├── demo.webm ├── github.jpg ├── subheader-logo.svg └── video-placeholder.webp ├── tailwind.config.ts └── tsconfig.json /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@2.1.0/schema.json", 3 | "changelog": ["@changesets/changelog-github", { "repo": "the-guild-org/the-guild-components" }], 4 | "commit": false, 5 | "linked": [], 6 | "access": "public", 7 | "baseBranch": "main", 8 | "updateInternalDependencies": "patch", 9 | "ignore": ["website", "@theguild/og-image"], 10 | "snapshot": { 11 | "useCalculatedVersion": true, 12 | "prereleaseTemplate": "{tag}-{datetime}-{commit}" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | ignorePatterns: ['__fixtures__'], 4 | extends: [ 5 | '@theguild', 6 | '@theguild/eslint-config/react', 7 | '@theguild/eslint-config/json', 8 | '@theguild/eslint-config/yml', 9 | // '@theguild/eslint-config/mdx', 10 | 'plugin:tailwindcss/recommended', 11 | 'plugin:storybook/recommended', 12 | ], 13 | rules: { 14 | 'tailwindcss/classnames-order': 'off', // conflicts with official prettier-plugin-tailwindcss and tailwind v3 15 | // set more strict to highlight in editor 16 | 'tailwindcss/enforces-shorthand': 'error', 17 | 'tailwindcss/migration-from-tailwind-2': 'error', 18 | 'tailwindcss/no-custom-classname': 'error', 19 | 'prefer-destructuring': ['error', { object: true }], 20 | 'prefer-template': 'error', 21 | '@typescript-eslint/array-type': ['error', { readonly: 'generic' }], 22 | '@typescript-eslint/consistent-type-imports': ['error', { prefer: 'no-type-imports' }], 23 | 24 | // TODO: fix below 25 | '@typescript-eslint/no-explicit-any': 'warn', 26 | 'jsx-a11y/click-events-have-key-events': 'warn', 27 | 'jsx-a11y/no-static-element-interactions': 'warn', 28 | // enable without breaking existing iframes 29 | 'react/iframe-missing-sandbox': 'off', 30 | }, 31 | settings: { 32 | tailwindcss: { 33 | callees: ['clsx', 'cn', 'cva', 'cx'], 34 | config: 'tailwind.config.ts', 35 | whitelist: [ 36 | // TODO: find a way to fix it and remove these classes since they are imported somewhere and are used 37 | 'hive-focus', 38 | 'hive-focus-within', 39 | 'nextra-hamburger', 40 | '@container', // Tailwind ESLint Plugin doesn't see the Container Queries classes, but it does see prefixes like @sm: 41 | ], 42 | }, 43 | }, 44 | overrides: [ 45 | { 46 | files: ['packages/**'], 47 | rules: { 48 | 'import/extensions': ['error', { js: 'never', json: 'always' }], 49 | }, 50 | }, 51 | ], 52 | }; 53 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | extends: ['github>the-guild-org/shared-config:renovate'], 3 | packageRules: [ 4 | { 5 | matchPackageNames: ['eslint-plugin-react-hooks'], 6 | enabled: false, 7 | }, 8 | ], 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: {} 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | 15 | - uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 16 | 17 | - name: setup Node.js 18 | uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4 19 | with: 20 | node-version: 20 21 | cache: pnpm 22 | 23 | - name: Install Dependencies 24 | run: pnpm i 25 | 26 | - name: Detect changes in packages/og-image/src 27 | id: detect-changes 28 | run: | 29 | git fetch origin main 30 | if git diff --quiet main HEAD -- packages/og-image/src; then 31 | echo "changed=no" >> "$GITHUB_OUTPUT" 32 | else 33 | echo "changed=yes" >> "$GITHUB_OUTPUT" 34 | fi 35 | 36 | - name: Get commit hash if OG generation related files changed 37 | id: gethash 38 | if: steps.detect-changes.outputs.changed == 'yes' 39 | run: | 40 | HASH=$(git log -n 1 --pretty=format:"%h" -- packages/og-image/src) 41 | echo "hash=$HASH" >> "$GITHUB_OUTPUT" 42 | 43 | - name: Replace hash for cache in source code 44 | if: steps.gethash.outputs.hash 45 | run: 46 | sed -i 's/hash:$GIT_CHANGED_HASH/hash:${{ steps.gethash.outputs.hash }}/g' 47 | ./packages/og-image/src/index.ts 48 | 49 | - name: Build 50 | run: pnpm build 51 | 52 | - name: Build Docs Example 53 | run: pnpm build-example 54 | 55 | - name: Lint Prettier 56 | run: pnpm lint:prettier 57 | 58 | - name: Lint ESLint 59 | run: pnpm lint 60 | 61 | - name: Type Check 62 | run: pnpm types:check 63 | 64 | - name: Test 65 | run: pnpm test 66 | -------------------------------------------------------------------------------- /.github/workflows/cloudflare-workers.yaml: -------------------------------------------------------------------------------- 1 | name: Cloudflare Workers 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | deploy: 9 | name: Deploy to Cloudflare Workers 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: checkout 13 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - uses: the-guild-org/shared-config/setup@main 18 | name: setup env 19 | with: 20 | nodeVersion: 20 21 | packageManager: pnpm 22 | 23 | - name: Build 24 | run: pnpm build 25 | 26 | - name: Deploy 27 | working-directory: ./packages/og-image 28 | run: pnpm run deploy 29 | env: 30 | CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} 31 | CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} 32 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: PR 2 | 3 | on: 4 | pull_request: {} 5 | 6 | jobs: 7 | # dependencies: 8 | # uses: the-guild-org/shared-config/.github/workflows/changesets-dependencies.yaml@main 9 | # secrets: 10 | # githubToken: ${{ secrets.GUILD_BOT_TOKEN }} 11 | 12 | canary: 13 | uses: the-guild-org/shared-config/.github/workflows/release-snapshot.yml@main 14 | with: 15 | npmTag: alpha 16 | buildScript: build 17 | nodeVersion: 20 18 | packageManager: pnpm 19 | secrets: 20 | githubToken: ${{ secrets.GITHUB_TOKEN }} 21 | npmToken: ${{ secrets.NPM_AUTH_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: [main, v7] 6 | 7 | jobs: 8 | stable: 9 | uses: the-guild-org/shared-config/.github/workflows/release-stable.yml@main 10 | with: 11 | releaseScript: release 12 | nodeVersion: 20 13 | packageManager: pnpm 14 | secrets: 15 | githubToken: ${{ secrets.GITHUB_TOKEN }} 16 | npmToken: ${{ secrets.NPM_AUTH_TOKEN }} 17 | -------------------------------------------------------------------------------- /.github/workflows/storybook.yaml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | jobs: 4 | deployment: 5 | runs-on: ubuntu-24.04 6 | steps: 7 | - name: checkout 8 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 9 | with: 10 | fetch-depth: 2 11 | 12 | - uses: the-guild-org/shared-config/setup@main 13 | name: setup env 14 | with: 15 | nodeVersion: 20 16 | packageManager: pnpm 17 | 18 | - uses: the-guild-org/shared-config/website-cf@main 19 | name: build and deploy storybook 20 | with: 21 | cloudflareApiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} 22 | cloudflareAccountId: ${{ secrets.CF_ACCOUNT_ID }} 23 | githubToken: ${{ secrets.GITHUB_TOKEN }} 24 | projectName: the-guild-docs-storybook 25 | prId: ${{ github.event.pull_request.number }} 26 | mainBranch: main 27 | websiteDirectory: ./ 28 | buildScript: pnpm build-storybook 29 | artifactDir: storybook-static 30 | commentId: storybook-deployment 31 | commentTitle: 📚 Storybook Deployment 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .next/ 3 | dist/ 4 | .idea/ 5 | .husky/_/ 6 | *.log 7 | storybook-static/ 8 | .turbo/ 9 | out/ 10 | packages/og-image/vender/*.wasm 11 | .eslintcache 12 | .wrangler/ 13 | .DS_Store 14 | tsup.config.*.mjs 15 | -------------------------------------------------------------------------------- /.node-version: -------------------------------------------------------------------------------- 1 | 22 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shell-emulator=true 2 | enable-pre-post-scripts=true 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | storybook-static/ 3 | .next/ 4 | .husky/_/ 5 | .changeset/*.md 6 | website/out/ 7 | pnpm-lock.yaml 8 | CHANGELOG.md 9 | -------------------------------------------------------------------------------- /.storybook/design-system-docs-decorator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { StoryContext } from '@storybook/react'; 3 | import { ArrowIcon } from '../packages/components/src/components/icons'; 4 | import { hiveThemeDecorator } from './hive-theme-decorator'; 5 | 6 | export function designSystemDocsDecorator(Story: () => React.ReactNode, ctx: StoryContext) { 7 | return hiveThemeDecorator( 8 | () => ( 9 |
10 |
11 | 12 |

Core Elements

13 | 14 |

{ctx.title.split('/').pop()}

15 |

Hive

16 |
17 |
18 |
19 | 20 |
21 |
22 | ), 23 | ctx, 24 | ); 25 | } 26 | 27 | function HiveIconMark(props: React.SVGProps) { 28 | return ( 29 | 30 | 31 | 35 | 36 | 44 | 45 | 46 | 47 | 48 | 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /.storybook/global.css: -------------------------------------------------------------------------------- 1 | @import '../packages/components/style.css'; 2 | -------------------------------------------------------------------------------- /.storybook/hive-theme-decorator.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // @ts-expect-error We have `next` because of `@theguild/components`. 3 | import localFont from 'next/font/local'; 4 | import { StoryContext } from '@storybook/react'; 5 | import { cn } from '../packages/components/src/cn'; 6 | 7 | const neueMontreal = localFont({ 8 | src: [ 9 | { path: '../fonts/PPNeueMontreal-Regular.woff2', weight: '400' }, 10 | { path: '../fonts/PPNeueMontreal-Medium.woff2', weight: '500' }, 11 | { path: '../fonts/NeueMontreal-Bold.otf', weight: '700' }, 12 | ], 13 | variable: '--font-sans', 14 | }); 15 | 16 | export const hiveThemeDecorator = (Story: () => React.ReactNode, ctx: StoryContext) => { 17 | return ( 18 | <> 19 |
32 | 33 | 45 |
46 | {ctx.parameters.forcedLightMode && ( 47 | 48 | forced light mode 49 | 50 | )} 51 | 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin'; 2 | import { Configuration } from 'webpack'; 3 | import { StorybookConfig } from '@storybook/nextjs'; 4 | 5 | export default { 6 | stories: ['../packages/*/src/**/*.stories.@(js|jsx|ts|tsx|mdx)'], 7 | addons: [ 8 | '@storybook/addon-essentials', 9 | '@storybook/addon-links', 10 | 'storybook-dark-mode', // addon to have toolbar for dark/light mode 11 | ], 12 | typescript: { 13 | reactDocgen: false, 14 | }, 15 | env(config) { 16 | return config; 17 | }, 18 | webpackFinal(config: Configuration) { 19 | config.resolve ||= {}; 20 | config.resolve.plugins ||= []; 21 | config.resolve.plugins.push( 22 | new TsconfigPathsPlugin({ 23 | extensions: config.resolve.extensions, 24 | }), 25 | ); 26 | config.resolve.fallback = { 27 | ...config.resolve.fallback, 28 | url: false, 29 | }; 30 | 31 | config.module?.rules?.unshift({ 32 | test: /\.svg$/, 33 | loader: '@svgr/webpack', 34 | options: { 35 | svgo: false, // otherwise setting SVG component width/height will don't affect 36 | }, 37 | }); 38 | 39 | return config; 40 | }, 41 | framework: { 42 | name: '@storybook/nextjs', 43 | options: {}, 44 | }, 45 | core: { 46 | disableTelemetry: true, 47 | }, 48 | staticDirs: ['./public'], 49 | } satisfies StorybookConfig; 50 | -------------------------------------------------------------------------------- /.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider } from 'next-themes'; 2 | import { useDarkMode } from 'storybook-dark-mode'; 3 | import { Preview } from '@storybook/react'; 4 | import { themes } from '@storybook/theming'; 5 | import './global.css'; 6 | 7 | export const parameters: Preview['parameters'] = { 8 | actions: { argTypesRegex: '^on[A-Z].*' }, 9 | options: { 10 | storySort: { 11 | method: 'alphabetical', 12 | order: ['Components', ['Headers'], 'Projects'], 13 | }, 14 | }, 15 | darkMode: { 16 | // Override the default dark theme 17 | dark: { ...themes.normal, appBg: 'white' }, 18 | // Override the default light theme 19 | light: { ...themes.normal, appBg: 'white' }, 20 | classTarget: 'html', 21 | }, 22 | // Remove padding from storybook in mobile 23 | layout: 'fullscreen', 24 | nextjs: { 25 | appDirectory: true, 26 | }, 27 | }; 28 | 29 | export const decorators: Preview['decorators'] = [ 30 | Story => { 31 | const theme = useDarkMode() ? 'dark' : 'light'; 32 | return ( 33 | 34 | 35 | 36 | ); 37 | }, 38 | ]; 39 | -------------------------------------------------------------------------------- /.storybook/public/fonts/NeueMontreal-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-guild-org/docs/37300dd615d049f82387e8087d0c326fa87fc9aa/.storybook/public/fonts/NeueMontreal-Bold.otf -------------------------------------------------------------------------------- /.storybook/public/fonts/PPNeueMontreal-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-guild-org/docs/37300dd615d049f82387e8087d0c326fa87fc9aa/.storybook/public/fonts/PPNeueMontreal-Medium.woff2 -------------------------------------------------------------------------------- /.storybook/public/fonts/PPNeueMontreal-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-guild-org/docs/37300dd615d049f82387e8087d0c326fa87fc9aa/.storybook/public/fonts/PPNeueMontreal-Regular.woff2 -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "tailwindCSS.experimental.classRegex": [ 3 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], 4 | ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], 5 | ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], 6 | ["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] 7 | ], 8 | "eslint.useFlatConfig": false 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Guild Component Library 2 | 3 | ## Getting started 4 | 5 | This is a npm package hosted at: 6 | 7 | Storybook: 8 | 9 | ## Developing 10 | 11 | We recommend running storybook for developing the components. 12 | 13 | ```sh 14 | pnpm start 15 | ``` 16 | 17 | Visit 18 | 19 | ## Links 20 | 21 | - [Storybook](https://storybook.js.org) 22 | - [Tailwind CSS](https://tailwindcss.com) 23 | 24 | This project was based on: 25 | 26 | ```sh 27 | npx degit https://github.com/ben-rogerson/twin.examples/storybook-styled-components-typescript folder-name 28 | ``` 29 | -------------------------------------------------------------------------------- /packages/components/src/cn.ts: -------------------------------------------------------------------------------- 1 | import { clsx } from 'clsx'; 2 | import { twMerge } from 'tailwind-merge'; 3 | 4 | export const cn: typeof clsx = (...args) => { 5 | return twMerge(clsx(args)); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/components/src/components/anchor.tsx: -------------------------------------------------------------------------------- 1 | import { forwardRef, ReactElement } from 'react'; 2 | import NextLink from 'next/link'; 3 | import { cn } from '../cn'; 4 | import { ILink } from '../types/components'; 5 | 6 | export type AnchorProps = ILink; 7 | export const Anchor = forwardRef(function Anchor( 8 | { href = '', children, newWindow, className, ...props }, 9 | forwardedRef, 10 | ): ReactElement { 11 | const classes = cn('outline-none focus-visible:ring', className); 12 | 13 | if (typeof href === 'string') { 14 | if (href.startsWith('#')) { 15 | return ( 16 | 17 | {children} 18 | 19 | ); 20 | } 21 | 22 | if (newWindow && /^https?:\/\//.test(href)) { 23 | return ( 24 | 32 | {children} 33 | 34 | ); 35 | } 36 | } 37 | 38 | return ( 39 | 40 | {/* eslint-disable-next-line react/jsx-no-useless-fragment -- Fragment needed to fix Error: React.Children.only expected to receive a single React element child */} 41 | <>{children} 42 | 43 | ); 44 | }); 45 | -------------------------------------------------------------------------------- /packages/components/src/components/button.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import clsx from 'clsx'; 3 | import { Anchor, AnchorProps } from './anchor'; 4 | 5 | export interface ButtonProps extends AnchorProps { 6 | variant?: 'primary' | 'secondary'; 7 | } 8 | 9 | export const Button = ({ 10 | children, 11 | className, 12 | variant = 'primary', 13 | ...props 14 | }: ButtonProps): ReactElement => { 15 | return ( 16 | 26 | {children} 27 | 28 | ); 29 | }; 30 | -------------------------------------------------------------------------------- /packages/components/src/components/call-to-action.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { CallToAction, CallToActionProps } from './call-to-action'; 4 | 5 | export default { 6 | title: 'Components/CallToAction', 7 | component: CallToAction, 8 | decorators: [hiveThemeDecorator], 9 | args: { 10 | children: 'Click me', 11 | onClick: () => alert('Clicked!'), 12 | }, 13 | argTypes: { 14 | as: { 15 | control: 'select', 16 | options: [undefined, 'span', 'div'], 17 | }, 18 | variant: { 19 | control: 'select', 20 | options: ['primary', 'primary-inverted', 'secondary', 'secondary-inverted', 'tertiary'], 21 | }, 22 | }, 23 | parameters: { 24 | padding: true, 25 | }, 26 | } satisfies Meta; 27 | 28 | export const Primary: StoryObj = { 29 | args: { 30 | variant: 'primary', 31 | }, 32 | }; 33 | 34 | export const PrimaryInverted: StoryObj = { 35 | args: { 36 | variant: 'primary-inverted', 37 | }, 38 | }; 39 | 40 | export const Secondary: StoryObj = { 41 | args: { 42 | variant: 'secondary', 43 | }, 44 | }; 45 | 46 | export const SecondaryInverted: StoryObj = { 47 | args: { 48 | variant: 'secondary-inverted', 49 | }, 50 | }; 51 | 52 | export const Tertiary: StoryObj = { 53 | args: { 54 | variant: 'tertiary', 55 | }, 56 | }; 57 | 58 | export const AsSpan: StoryObj = { 59 | args: { 60 | as: 'span', 61 | children: 'Show More', 62 | onClick: () => { 63 | // no alert 64 | }, 65 | }, 66 | decorators: [ 67 | (Story: React.FC) => ( 68 |
69 | 70 | 71 | 72 |

Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.

73 |
74 | ), 75 | ], 76 | }; 77 | 78 | export const Link: StoryObj = { 79 | args: { 80 | href: 'https://the-guild.dev/graphql/hive/ecosystem', 81 | children: 'Explore the Ecosystem', 82 | variant: 'secondary-inverted', 83 | }, 84 | }; 85 | 86 | export const Submit: StoryObj = { 87 | args: { 88 | type: 'submit', 89 | children: 'Submit', 90 | }, 91 | }; 92 | -------------------------------------------------------------------------------- /packages/components/src/components/cards-colorful.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyCardsColorful } from '../helpers/dummy'; 3 | import { CardsColorful } from './cards-colorful'; 4 | 5 | export default { 6 | title: 'Components/Cards/Colorful', 7 | component: CardsColorful, 8 | argTypes: { 9 | cards: { 10 | name: 'Cards', 11 | }, 12 | }, 13 | } satisfies Meta; 14 | 15 | type Story = StoryObj; 16 | 17 | export const Default: Story = { 18 | args: dummyCardsColorful, 19 | }; 20 | -------------------------------------------------------------------------------- /packages/components/src/components/cards-colorful.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import clsx from 'clsx'; 3 | import { ILink } from '../types/components'; 4 | import { Anchor } from './anchor'; 5 | 6 | export type CardsColorfulProps = { 7 | className?: string; 8 | cards: { 9 | title: string; 10 | description: string; 11 | category: string; 12 | color: string; 13 | link: Omit; 14 | }[]; 15 | }; 16 | 17 | export const CardsColorful = ({ cards, className }: CardsColorfulProps): ReactElement => ( 18 |
19 |
20 | {cards.map(card => ( 21 | 27 |
28 |

{card.category}

29 |

{card.title}

30 |

{card.description}

31 |
32 |
33 | ))} 34 |
35 |
36 | ); 37 | -------------------------------------------------------------------------------- /packages/components/src/components/comparison-table/index.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentProps, FC } from 'react'; 2 | import { cn } from '@theguild/components'; 3 | 4 | export interface ComparisonTableProps extends React.HTMLAttributes { 5 | scheme?: 'green' | 'neutral'; 6 | } 7 | const Table = ({ className, scheme = 'green', ...props }: ComparisonTableProps) => { 8 | return ( 9 | 20 | ); 21 | }; 22 | 23 | const TableRow: FC & { highlight?: boolean }> = ({ 24 | highlight, 25 | className, 26 | ...props 27 | }) => { 28 | return ( 29 | 37 | ); 38 | }; 39 | 40 | const cellStyle = cn( 41 | 'border border-[--border] p-4 first:sticky first:left-0 first:border-l-0 first:bg-[--highlight,var(--highlight-bg)] last:border-r-0 max-sm:first:drop-shadow-2xl [tbody_&]:border-b-0 [thead_&]:border-t-0', 42 | ); 43 | 44 | const TableHeader: FC> = ({ className, ...props }) => { 45 | return
; 46 | }; 47 | 48 | const TableCell: FC> = ({ className, ...props }) => { 49 | return ; 50 | }; 51 | 52 | /** 53 | * It's exported under the name `ComparisonTable` 54 | * because we also reexport `Table` from nextra. 55 | */ 56 | export const ComparisonTable = Object.assign(Table, { 57 | Row: TableRow, 58 | Header: TableHeader, 59 | Cell: TableCell, 60 | }); 61 | -------------------------------------------------------------------------------- /packages/components/src/components/contact-us.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { 4 | ContactButton, 5 | ContactButtonProps, 6 | ContactTextLink, 7 | ContactTextLinkProps, 8 | } from './contact-us'; 9 | 10 | export default { 11 | title: 'Components/ContactUs', 12 | component: ContactButton, 13 | decorators: [hiveThemeDecorator], 14 | parameters: { 15 | padding: true, 16 | }, 17 | } satisfies Meta; 18 | 19 | export const Default: StoryObj = { 20 | name: 'ContactButton', 21 | args: { 22 | // `children` is optional 23 | children: 'Contact us', 24 | variant: 'secondary-inverted', 25 | }, 26 | }; 27 | 28 | export const TextLink: StoryObj = { 29 | name: 'ContactTextLink', 30 | render: args => , 31 | args: { 32 | children: 'Reach out to us about the Enterprise plan', 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /packages/components/src/components/contact-us.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { cn } from '../cn'; 4 | import { CallToAction, CallToActionProps } from './call-to-action'; 5 | 6 | const openCrisp = (event: React.MouseEvent) => { 7 | if (window.$crisp) { 8 | window.$crisp.push(['do', 'chat:open']); 9 | event.preventDefault(); 10 | } 11 | }; 12 | 13 | export interface ContactTextLinkProps 14 | extends Omit, 'href' | 'onClick'> { 15 | children?: React.ReactNode; 16 | } 17 | 18 | export function ContactTextLink(props: ContactTextLinkProps) { 19 | return ( 20 | 29 | {props.children || 'Contact Us'} 30 | 31 | ); 32 | } 33 | 34 | export interface ContactButtonProps 35 | extends Omit { 36 | children?: React.ReactNode; 37 | } 38 | 39 | export function ContactButton(props: ContactButtonProps) { 40 | return ( 41 | 42 | {props.children || 'Contact Us'} 43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /packages/components/src/components/cookies-consent.stories.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { Meta, StoryObj } from '@storybook/react'; 3 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 4 | import { CallToAction } from './call-to-action'; 5 | import { CookiesConsent, CookiesConsentProps } from './cookies-consent'; 6 | 7 | const meta: Meta = { 8 | title: 'Components/CookiesConsent', 9 | component: CookiesConsent, 10 | decorators: [ 11 | hiveThemeDecorator, 12 | Story => { 13 | const [key, setKey] = useState(0); 14 | const remount = () => setKey(Math.random()); 15 | 16 | return ( 17 | <> 18 | { 21 | localStorage.removeItem('cookies'); 22 | remount(); 23 | }} 24 | variant="secondary" 25 | > 26 | Reset Cookies Consent 27 | 28 | 29 | 30 | ); 31 | }, 32 | ], 33 | }; 34 | 35 | export default meta; 36 | 37 | export const Default: StoryObj = { 38 | name: 'CookiesConsent', 39 | }; 40 | -------------------------------------------------------------------------------- /packages/components/src/components/cookies-consent.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ComponentProps, useLayoutEffect, useState } from 'react'; 4 | import { cn } from '../cn'; 5 | import { CallToAction } from './call-to-action'; 6 | 7 | export type CookiesConsentProps = ComponentProps<'div'>; 8 | 9 | export function CookiesConsent(props: CookiesConsentProps) { 10 | const [consented, setConsented] = useState<'unknown' | 'yes' | 'no' | 'closing'>('unknown'); 11 | 12 | useLayoutEffect(() => { 13 | setConsented(localStorage.getItem('cookies') === 'true' ? 'yes' : 'no'); 14 | }, []); 15 | 16 | if (consented === 'unknown' || consented === 'yes') { 17 | return null; 18 | } 19 | 20 | return ( 21 |
{ 29 | if (consented === 'closing') { 30 | setConsented('yes'); 31 | } 32 | }} 33 | > 34 |
35 |

36 | This site uses cookies for analytics and improving your experience. 37 |

{' '} 38 |

By using our services, you consent to cookies.

39 |
40 |
41 | 47 | Privacy Policy 48 | 49 | { 52 | setConsented('closing'); 53 | localStorage.setItem('cookies', 'true'); 54 | }} 55 | className="px-4 py-2" 56 | > 57 | Allow 58 | 59 |
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /packages/components/src/components/decorations/arch-decoration-gradient-defs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/components/src/components/decorations/arch-decoration.svg: -------------------------------------------------------------------------------- 1 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /packages/components/src/components/decorations/decorations.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { 3 | ArchDecoration, 4 | ArchDecorationGradientDefs, 5 | DecorationIsolation, 6 | HighlightDecoration, 7 | LargeHiveIconDecoration, 8 | } from './index'; 9 | 10 | const meta: Meta = { 11 | title: 'Components/Decorations', 12 | component: ArchDecoration, 13 | decorators: [ 14 | Story => ( 15 |
16 | 17 | 18 | 19 |
20 | ), 21 | ], 22 | }; 23 | 24 | export default meta; 25 | 26 | export const ArchDecorationStory: StoryObj = { 27 | name: 'ArchDecoration', 28 | render(args) { 29 | return ( 30 | <> 31 | 32 | 33 | 34 | ); 35 | }, 36 | }; 37 | 38 | export { HighlightDecoration, LargeHiveIconDecoration }; 39 | -------------------------------------------------------------------------------- /packages/components/src/components/decorations/highlight-decoration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 17 | 18 | 19 | 20 | 21 | 22 | 30 | 31 | -------------------------------------------------------------------------------- /packages/components/src/components/decorations/index.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from '../../cn'; 2 | import { ReactComponent as HighlightDecorationSvg } from './highlight-decoration.svg'; 3 | 4 | export { ReactComponent as ArchDecoration } from './arch-decoration.svg'; 5 | export { ReactComponent as ArchDecorationGradientDefs } from './arch-decoration-gradient-defs.svg'; 6 | export { ReactComponent as LargeHiveIconDecoration } from './large-hive-icon-decoration.svg'; 7 | 8 | export type DecorationIsolationProps = React.HTMLAttributes; 9 | 10 | /** 11 | * Decorations must be isolated, as clicking id links scrolls the container with overflow: hidden. 12 | */ 13 | export function DecorationIsolation(props: DecorationIsolationProps) { 14 | return ( 15 |
19 | ); 20 | } 21 | 22 | // Components created from .svg import don't preserve className 23 | export const HighlightDecoration = (props: React.SVGProps) => ( 24 | // eslint-disable-next-line tailwindcss/no-custom-classname 25 | 26 | ); 27 | -------------------------------------------------------------------------------- /packages/components/src/components/explore-main-product-cards.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { 4 | ExploreMainProductCards, 5 | ExploreMainProductCardsProps, 6 | } from './explore-main-product-cards'; 7 | 8 | export default { 9 | title: 'Hive/ExploreMainProductCards', 10 | component: ExploreMainProductCards, 11 | decorators: [hiveThemeDecorator], 12 | parameters: { 13 | forcedLightMode: true, 14 | }, 15 | } satisfies Meta; 16 | 17 | export const Default: StoryObj = { 18 | name: 'ExploreMainProductCards', 19 | }; 20 | -------------------------------------------------------------------------------- /packages/components/src/components/explore-main-product-cards.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from 'react'; 2 | import { cn } from '../cn'; 3 | import { FOUR_MAIN_PRODUCTS } from '../products'; 4 | import { Heading } from './heading'; 5 | import { ArrowIcon } from './icons'; 6 | import { MainProductCard } from './product-card'; 7 | import { TextLink } from './text-link'; 8 | 9 | export interface ExploreMainProductCardsProps extends HTMLAttributes { 10 | isHive?: boolean; 11 | } 12 | 13 | export function ExploreMainProductCards({ 14 | className, 15 | isHive, 16 | ...rest 17 | }: ExploreMainProductCardsProps) { 18 | return ( 19 |
26 |
27 | 28 | Explore Hive 360° GraphQL Ecosystem to reach full potential 29 | 30 | 34 | Learn more 35 | 36 | 37 |
38 |
    39 | {FOUR_MAIN_PRODUCTS.map(product => ( 40 | 41 | ))} 42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /packages/components/src/components/faq/attach-page-faq-schema.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { FC, useEffect } from 'react'; 4 | import { usePathname } from 'next/navigation'; 5 | 6 | export const AttachPageFAQSchema: FC<{ faqPages: string[] }> = ({ faqPages }) => { 7 | const pathname = usePathname(); 8 | 9 | useEffect(() => { 10 | const html = document.querySelector('html')!; 11 | if (faqPages.includes(pathname) && !html.hasAttribute('itemscope')) { 12 | html.setAttribute('itemscope', ''); 13 | html.setAttribute('itemtype', 'https://schema.org/FAQPage'); 14 | 15 | return () => { 16 | html.removeAttribute('itemscope'); 17 | html.removeAttribute('itemtype'); 18 | }; 19 | } 20 | }, []); 21 | 22 | return null; 23 | }; 24 | -------------------------------------------------------------------------------- /packages/components/src/components/feature-list.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyFeatureList } from '../helpers/dummy'; 3 | import { FeatureList } from './feature-list'; 4 | 5 | export default { 6 | title: 'Components/Lists/Features', 7 | component: FeatureList, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | items: { 13 | name: 'Features', 14 | }, 15 | }, 16 | } satisfies Meta; 17 | 18 | type Story = StoryObj; 19 | 20 | export const Default: Story = { args: dummyFeatureList }; 21 | -------------------------------------------------------------------------------- /packages/components/src/components/feature-list.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import clsx from 'clsx'; 3 | import { IFeatureListProps } from '../types/components'; 4 | import { Anchor } from './anchor'; 5 | import { Image } from './image'; 6 | 7 | export const FeatureList = ({ 8 | title, 9 | description, 10 | items, 11 | link, 12 | className, 13 | }: IFeatureListProps): ReactElement => ( 14 |
15 |
16 | {title && ( 17 |
18 |

19 | {title} 20 |

21 | {description && ( 22 |
23 | {description} 24 |
25 | )} 26 | {link && ( 27 | 31 | )} 32 |
33 | )} 34 |
35 | {items.map(item => ( 36 |
37 | 38 |

{item.title}

39 |
40 | {item.description} 41 |
42 | {item.link && ( 43 | 50 | )} 51 |
52 | ))} 53 |
54 |
55 |
56 | ); 57 | -------------------------------------------------------------------------------- /packages/components/src/components/get-your-api-game-right-section.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { GetYourAPIGameRightSection } from './get-your-api-game-right-section'; 4 | 5 | export default { 6 | title: 'Hive/GetYourAPIGameRightSection', 7 | component: GetYourAPIGameRightSection, 8 | decorators: [hiveThemeDecorator], 9 | parameters: { 10 | padding: true, 11 | forcedLightMode: true, 12 | }, 13 | } satisfies Meta; 14 | 15 | export { GetYourAPIGameRightSection }; 16 | -------------------------------------------------------------------------------- /packages/components/src/components/giscus.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { FC } from 'react'; 4 | import { useTheme } from 'nextra-theme-docs'; 5 | import { default as Giscus_, GiscusProps } from '@giscus/react'; 6 | 7 | export const Giscus: FC< 8 | Omit & { 9 | mapping?: GiscusProps['mapping']; 10 | } 11 | > = props => { 12 | const { resolvedTheme } = useTheme(); 13 | return ; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/components/src/components/heading.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { Heading as _Heading, HeadingProps } from './heading'; 4 | 5 | export default { 6 | title: 'Components/Heading', 7 | component: _Heading, 8 | args: { 9 | children: 'Open-source GraphQL management platform', 10 | }, 11 | argTypes: { 12 | as: { 13 | control: { type: 'select' }, 14 | options: ['h1', 'h2', 'h3', 'div'], 15 | }, 16 | size: { 17 | control: { type: 'select' }, 18 | options: ['xl', 'lg', 'md', 'sm'], 19 | }, 20 | children: { 21 | control: 'text', 22 | }, 23 | }, 24 | parameters: { 25 | padding: true, 26 | }, 27 | decorators: [hiveThemeDecorator], 28 | } satisfies Meta; 29 | 30 | export const Heading: StoryObj = { 31 | args: { 32 | as: 'h1', 33 | size: 'xl', 34 | }, 35 | }; 36 | 37 | export const NoScrollingOnClick: StoryObj = { 38 | args: { as: 'h2', size: 'lg' }, 39 | decorators: [ 40 | Story => ( 41 |
42 |
43 | 44 |
45 |
46 | ), 47 | ], 48 | play(ctx) { 49 | const anchor = ctx.canvasElement.querySelector('a')!; 50 | anchor.click(); 51 | if (window.location.hash !== '#open-source-graphql-management-platform') { 52 | throw new Error('Expected hash to be set'); 53 | } 54 | if (window.scrollY !== 0) { 55 | throw new Error('Expected scroll to be at top'); 56 | } 57 | }, 58 | }; 59 | -------------------------------------------------------------------------------- /packages/components/src/components/heading.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ComponentPropsWithoutRef } from 'react'; 4 | import { cn } from '../cn'; 5 | 6 | export interface HeadingProps extends ComponentPropsWithoutRef<'h1'> { 7 | as: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'div' | 'p' | 'span'; 8 | size: 'xl' | 'lg' | 'md' | 'sm' | 'xs'; 9 | } 10 | export function Heading({ as: _as, size, className, children, ...rest }: HeadingProps) { 11 | const Level = _as || 'h2'; 12 | 13 | let sizeStyle = ''; 14 | switch (size) { 15 | // TODO: This should probably be a class, not a component, because the design expects 16 | // an equivalent of `heading-sm lg:heading-xl.` 17 | case 'xl': 18 | sizeStyle = 'text-4xl leading-[1.2] md:text-6xl md:leading-[1.1875] tracking-[-0.64px]'; 19 | break; 20 | case 'lg': 21 | sizeStyle = 'text-4xl leading-[1.2] md:text-[56px] md:leading-[1.14286] tracking-[-0.56px]'; 22 | break; 23 | case 'md': 24 | sizeStyle = 'text-4xl leading-[1.2] md:text-5xl md:leading-[1.16667] tracking-[-0.48px]'; 25 | break; 26 | case 'sm': 27 | sizeStyle = 'text-[40px] leading-[1.2] tracking-[-0.2px]'; 28 | break; 29 | case 'xs': 30 | sizeStyle = 'text-[32px]/[1.25] tracking-[-0.16px]'; 31 | break; 32 | } 33 | 34 | const id = 35 | typeof children === 'string' ? children.replace(/[\s.,]+/g, '-').toLowerCase() : undefined; 36 | 37 | return ( 38 | 39 | {id ? ( 40 | 41 | {children} 42 | 43 | ) : ( 44 | children 45 | )} 46 | 47 | ); 48 | } 49 | 50 | function preventScroll(event: React.MouseEvent) { 51 | if (event.currentTarget.tagName !== 'A') return; 52 | const href = event.currentTarget.getAttribute('href'); 53 | if (href?.[0] !== '#') return; 54 | event.preventDefault(); 55 | const url = new URL(window.location.href); 56 | url.hash = href.slice(1); 57 | window.history.replaceState({}, '', url.toString()); 58 | } 59 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-gradient.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyHeroGradient } from '../helpers/dummy'; 3 | import { HeroGradient } from './hero-gradient'; 4 | 5 | export default { 6 | title: 'Components/Heroes/Gradient', 7 | component: HeroGradient, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | description: { 13 | name: 'Description', 14 | }, 15 | image: { 16 | name: 'Illustration', 17 | }, 18 | link: { 19 | name: 'Call to Action', 20 | }, 21 | version: { 22 | name: 'Library Version', 23 | }, 24 | colors: { 25 | name: 'Background Glow Colors', 26 | }, 27 | }, 28 | } satisfies Meta; 29 | 30 | type Story = StoryObj; 31 | 32 | export const Default: Story = { args: dummyHeroGradient }; 33 | 34 | export const MultiLinks = { 35 | args: { 36 | ...dummyHeroGradient, 37 | link: [ 38 | { 39 | children: 'Start Learning', 40 | title: 'Start Learning', 41 | href: '#', 42 | }, 43 | { 44 | children: 'Docs', 45 | title: 'Docs', 46 | href: '#2', 47 | }, 48 | ], 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-illustration.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyHeroIllustration } from '../helpers/dummy'; 3 | import { HeroIllustration } from './hero-illustration'; 4 | 5 | export default { 6 | title: 'Components/Heroes/Illustration', 7 | component: HeroIllustration, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | description: { 13 | name: 'Description', 14 | }, 15 | image: { 16 | name: 'Illustration', 17 | }, 18 | flipped: { 19 | name: 'Flip Orientation', 20 | }, 21 | link: { 22 | name: 'Link', 23 | }, 24 | }, 25 | } satisfies Meta; 26 | 27 | type Story = StoryObj; 28 | 29 | export const Default: Story = { args: dummyHeroIllustration }; 30 | export const Flipped: Story = { 31 | args: { 32 | ...dummyHeroIllustration, 33 | flipped: false, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-illustration.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import clsx from 'clsx'; 3 | import { IHeroIllustrationProps } from '../types/components'; 4 | import { Button } from './button'; 5 | import { Image } from './image'; 6 | 7 | export const HeroIllustration = ({ 8 | title, 9 | description, 10 | link, 11 | image, 12 | flipped, 13 | className, 14 | }: IHeroIllustrationProps): ReactElement => ( 15 |
16 |
22 | 23 |
24 |

25 | {title} 26 |

27 |

{description}

28 | {link &&
30 |
31 |
32 | ); 33 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-marketplace.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyHeroMarketplace } from '../helpers/dummy'; 3 | import { HeroMarketplace } from './hero-marketplace'; 4 | 5 | export default { 6 | title: 'Components/Heroes/Marketplace', 7 | component: HeroMarketplace, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | description: { 13 | name: 'Description', 14 | }, 15 | link: { 16 | name: 'Call to Action', 17 | }, 18 | }, 19 | } satisfies Meta; 20 | 21 | type Story = StoryObj; 22 | 23 | export const Default: Story = { args: dummyHeroMarketplace }; 24 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-video.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyHeroVideo } from '../helpers/dummy'; 3 | import { HeroVideo } from './hero-video'; 4 | 5 | export default { 6 | title: 'Components/Heroes/Video', 7 | component: HeroVideo, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | description: { 13 | name: 'Description', 14 | }, 15 | video: { 16 | name: 'Video', 17 | }, 18 | flipped: { 19 | name: 'Flip Orientation', 20 | }, 21 | link: { 22 | name: 'Link', 23 | }, 24 | }, 25 | } satisfies Meta; 26 | 27 | type Story = StoryObj; 28 | 29 | export const Default: Story = { args: dummyHeroVideo }; 30 | -------------------------------------------------------------------------------- /packages/components/src/components/hero-video.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { FC } from 'react'; 4 | import { addBasePath } from 'next/dist/client/add-base-path'; 5 | import clsx from 'clsx'; 6 | import { useMounted } from 'nextra/hooks'; 7 | import ReactPlayer from 'react-player/lazy'; 8 | import { IHeroVideoProps } from '../types/components'; 9 | import { Anchor } from './anchor'; 10 | 11 | export const HeroVideo: FC = ({ 12 | title, 13 | description, 14 | link, 15 | video, 16 | flipped, 17 | className, 18 | videoProps, 19 | }) => { 20 | // fixes Hydration failed error 21 | const mounted = useMounted(); 22 | return ( 23 |
24 |
30 |
31 |

32 | {title} 33 |

34 |

35 | {description} 36 |

37 | {link && ( 38 | 45 | )} 46 |
47 |
53 | {mounted && ( 54 | 73 | )} 74 |
75 |
76 |
77 | ); 78 | }; 79 | -------------------------------------------------------------------------------- /packages/components/src/components/hero/hero-decoration-from-logo.tsx: -------------------------------------------------------------------------------- 1 | import { cloneElement, ReactElement } from 'react'; 2 | import { cn } from '../../cn'; 3 | import { DecorationIsolation, DecorationIsolationProps } from '../decorations'; 4 | import { GRADIENT_WHITE_2_ID } from './hero-gradient-ids'; 5 | 6 | export interface HeroDecorationFromLogoProps extends DecorationIsolationProps { 7 | logo: ReactElement; 8 | } 9 | 10 | export function HeroDecorationFromLogo({ logo, ...rest }: HeroDecorationFromLogoProps) { 11 | return ( 12 | 13 | {cloneElement(logo, { 14 | className: cn('absolute -left-1/2 top-1/2 -translate-y-1/2 stroke-white/10 max-lg:hidden'), 15 | fill: `url(#${GRADIENT_WHITE_2_ID})`, 16 | strokeWidth: '0.1', 17 | height: '50%', 18 | width: 'auto', 19 | opacity: 0.8, 20 | })} 21 | {cloneElement(logo, { 22 | className: cn( 23 | 'absolute top-1/2 -translate-y-1/2 stroke-white/10', 24 | '-right-1/2 lg:-right-1/3', 25 | 'h-2/3 lg:h-[calc(100%-5%)]', 26 | ), 27 | fill: `url(#${GRADIENT_WHITE_2_ID})`, 28 | strokeWidth: '0.1', 29 | width: 'auto', 30 | opacity: 0.6, 31 | })} 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/components/src/components/hero/hero-gradient-defs.tsx: -------------------------------------------------------------------------------- 1 | import { GRADIENT_GREEN_ID, GRADIENT_WHITE_2_ID, GRADIENT_WHITE_ID } from './hero-gradient-ids'; 2 | 3 | export function HeroGradientDefs() { 4 | return ( 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/components/src/components/hero/hero-gradient-ids.ts: -------------------------------------------------------------------------------- 1 | // There is just one hero component per website, so we don't need to `useId` here. 2 | const nonce = 'sgz9qg7vvt'; 3 | 4 | export const GRADIENT_WHITE_ID = `white-${nonce}`; 5 | export const GRADIENT_WHITE_2_ID = `white-2-${nonce}`; 6 | export const GRADIENT_GREEN_ID = `green-${nonce}`; 7 | -------------------------------------------------------------------------------- /packages/components/src/components/hero/hero.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentProps } from 'react'; 2 | import { GitHubIcon } from 'nextra/icons'; 3 | import { Meta, StoryObj } from '@storybook/react'; 4 | import { hiveThemeDecorator } from '../../../../../.storybook/hive-theme-decorator'; 5 | import { ModulesLogo } from '../../logos'; 6 | import { CallToAction } from '../call-to-action'; 7 | import { HiveGatewayIcon } from '../icons'; 8 | import { Hero, HeroDecorationFromLogo, HeroLogo } from './index'; 9 | 10 | export default { 11 | title: 'Hive/Hero', 12 | component: Hero, 13 | decorators: [hiveThemeDecorator], 14 | argTypes: { 15 | className: { 16 | name: 'className', 17 | }, 18 | heading: { 19 | name: 'Heading text', 20 | }, 21 | checkmarks: { 22 | name: 'Checkmarks text', 23 | }, 24 | text: { 25 | name: 'Hero text', 26 | }, 27 | }, 28 | parameters: { 29 | padding: true, 30 | forcedLightMode: true, 31 | }, 32 | } satisfies Meta>; 33 | 34 | export const Default: StoryObj> = { 35 | args: { 36 | heading: 'Enterprise Grade Tooling for Your GraphQL Server', 37 | text: 'GraphQL Modules is a toolset of libraries and guidelines dedicated to create reusable, maintainable, testable and extendable modules out of your GraphQL server.', 38 | top: ( 39 | 40 | 41 | 42 | ), 43 | checkmarks: ['Fully open source', 'No vendor lock'], 44 | children: ( 45 | <> 46 | 47 | Get started 48 | 49 | 50 | Changelog 51 | 52 | 53 | 54 | GitHub 55 | 56 | } /> 57 | 58 | ), 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /packages/components/src/components/hero/index.tsx: -------------------------------------------------------------------------------- 1 | import { FC, ReactNode } from 'react'; 2 | import { cn } from '../../cn'; 3 | import { Heading } from '../heading'; 4 | import { CheckIcon } from '../icons'; 5 | import { HeroGradientDefs } from './hero-gradient-defs'; 6 | 7 | export { HeroLogo } from './hero-logo'; 8 | export { HeroDecorationFromLogo } from './hero-decoration-from-logo'; 9 | 10 | export interface HeroProps { 11 | className?: string; 12 | heading: string; 13 | text: string; 14 | checkmarks: string[]; 15 | children?: ReactNode; 16 | top?: ReactNode; 17 | } 18 | 19 | export const Hero: FC = props => { 20 | return ( 21 |
27 | {props.top} 28 | 29 | {props.heading} 30 | 31 |

32 | {props.text} 33 |

34 |
    35 | {props.checkmarks.map(text => ( 36 |
  • 37 | 38 | {text} 39 |
  • 40 | ))} 41 |
42 |
43 | {props.children} 44 |
45 | 46 |
47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /packages/components/src/components/hive-footer/index.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../../.storybook/hive-theme-decorator'; 3 | import { CodegenIcon } from '../icons'; 4 | import { HiveFooter, HiveFooterProps } from './index'; 5 | 6 | export default { 7 | title: 'Hive/HiveFooter', 8 | component: HiveFooter, 9 | decorators: [hiveThemeDecorator], 10 | argTypes: { 11 | items: { 12 | resources: { 13 | name: 'Resources Links', 14 | description: "Use this to add current site's links to the footer.", 15 | }, 16 | }, 17 | }, 18 | } satisfies Meta; 19 | 20 | export const Default: StoryObj = { 21 | name: 'HiveFooter', 22 | args: { 23 | items: { 24 | ...HiveFooter.DEFAULT_ITEMS, 25 | resources: [ 26 | { 27 | children: 'Privacy Policy', 28 | href: 'https://the-guild.dev/graphql/hive/privacy-policy.pdf', 29 | }, 30 | { 31 | children: 'Terms of Use', 32 | href: 'https://the-guild.dev/graphql/hive/terms-of-use.pdf', 33 | }, 34 | ], 35 | }, 36 | }, 37 | }; 38 | 39 | export const CodegenFooter: StoryObj = { 40 | ...Default, 41 | args: { 42 | logo: ( 43 |
44 | 45 | Codegen 46 |
47 | ), 48 | description: 'End-to-end type safety', 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /packages/components/src/components/hive-layout-config.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @internal Don't expose this to websites. 3 | */ 4 | export const __LANDING_WIDTHS_ID = 'hive-l-widths'; 5 | 6 | export interface HiveLayoutConfigProps { 7 | widths: 'landing-narrow' | 'docs-wide'; 8 | } 9 | 10 | /** 11 | * @see {@link HiveLayout} from `@theguild/components/server` for documentation. 12 | */ 13 | export function HiveLayoutConfig({ widths }: HiveLayoutConfigProps) { 14 | return widths === 'landing-narrow' ?
: null; 15 | } 16 | -------------------------------------------------------------------------------- /packages/components/src/components/hive-navigation/graphql-conf-card.tsx: -------------------------------------------------------------------------------- 1 | import { StaticImageData } from 'next/image'; 2 | import { ArrowIcon } from '../icons'; 3 | import { Image } from '../image'; 4 | import { NavigationMenuLink } from './navigation-menu'; 5 | 6 | export interface GraphQLConfCardProps { 7 | image: StaticImageData; 8 | } 9 | export function GraphQLConfCard({ image }: GraphQLConfCardProps) { 10 | return ( 11 | 15 | 16 | 17 | GraphQLConf 2024 18 | 19 |

20 | September 10-12 | San Francisco CA 21 |

22 |

23 | The official GraphQL conference hosted by GraphQL Foundation. 24 |

25 | 26 | Watch The Guild at GraphQLConf 2024 27 | 28 | 29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/components/src/components/hive-navigation/local-image-for-stories.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-guild-org/docs/37300dd615d049f82387e8087d0c326fa87fc9aa/packages/components/src/components/hive-navigation/local-image-for-stories.png -------------------------------------------------------------------------------- /packages/components/src/components/icons/account-box.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/arrow-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/bard.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/caret-slim.svg: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/close.svg: -------------------------------------------------------------------------------- 1 | 9 | 15 | 21 | 22 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/codegen.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/group.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/hive-gateway.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/hive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/honour.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/icons.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentPropsWithoutRef } from 'react'; 2 | import { Meta, StoryObj } from '@storybook/react'; 3 | import * as icons from './index'; 4 | 5 | const meta: Meta = { 6 | title: 'Components/Icons', 7 | component: () => null, 8 | }; 9 | 10 | export default meta; 11 | 12 | export const IconsGallery: StoryObj> = { 13 | render() { 14 | return ( 15 |
    16 | {Object.entries(icons).map(([name, Icon]) => ( 17 |
  • 21 | 22 |

    {name}

    23 |
  • 24 | ))} 25 |
26 | ); 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/index.ts: -------------------------------------------------------------------------------- 1 | export { ReactComponent as CaretSlimIcon } from './caret-slim.svg'; 2 | export { ReactComponent as CloseIcon } from './close.svg'; 3 | export { ReactComponent as MoreIcon } from './more.svg'; 4 | export { ReactComponent as SearchIcon } from './search.svg'; 5 | export { ReactComponent as ShareIcon } from './share.svg'; 6 | export { ReactComponent as CSAStarLevelOneIcon } from './csa-star-level-one.svg'; 7 | export { ReactComponent as ArrowIcon } from './arrow-icon.svg'; 8 | 9 | export { ReactComponent as TwitterIcon } from './twitter.svg'; 10 | export { ReactComponent as LinkedInIcon } from './linkedin.svg'; 11 | export { ReactComponent as YouTubeIcon } from './youtube.svg'; 12 | 13 | export { ReactComponent as CodegenIcon } from './codegen.svg'; 14 | export { ReactComponent as MeshIcon } from './mesh.svg'; 15 | export { ReactComponent as YogaIcon } from './yoga.svg'; 16 | export { ReactComponent as HiveIcon } from './hive.svg'; 17 | export { ReactComponent as HiveGatewayIcon } from './hive-gateway.svg'; 18 | export { ReactComponent as StellateIcon } from './stellate.svg'; 19 | 20 | export { InformationCircleIcon, GitHubIcon, DiscordIcon } from 'nextra/icons'; 21 | 22 | // The following icons come from Iconoir. 23 | // Consider if the app and landing page should use the same ones 24 | // and if so, add `iconoir-react` to deps? 25 | export { ReactComponent as ListIcon } from './list.svg'; 26 | export { ReactComponent as PaperIcon } from './paper.svg'; 27 | export { ReactComponent as TargetIcon } from './target.svg'; 28 | export { ReactComponent as RightCornerIcon } from './right-corner.svg'; 29 | export { ReactComponent as PencilIcon } from './pencil.svg'; 30 | export { ReactComponent as AccountBox } from './account-box.svg'; 31 | export { ReactComponent as BardIcon } from './bard.svg'; 32 | export { ReactComponent as HonourIcon } from './honour.svg'; 33 | export { ReactComponent as ShieldFlashIcon } from './shield-flash.svg'; 34 | export { ReactComponent as GroupIcon } from './group.svg'; 35 | export { ReactComponent as AppsIcon } from './apps.svg'; 36 | export { ReactComponent as CheckIcon } from './check.svg'; 37 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/list.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/mesh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/moon.svg: -------------------------------------------------------------------------------- 1 | 9 | 17 | 18 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/more.svg: -------------------------------------------------------------------------------- 1 | 9 | 17 | 25 | 33 | 34 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/paper.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/pencil.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/right-corner.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/search.svg: -------------------------------------------------------------------------------- 1 | 9 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/share.svg: -------------------------------------------------------------------------------- 1 | 9 | 17 | 25 | 33 | 39 | 45 | 46 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/shield-flash.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/stellate.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/target.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/yoga.svg: -------------------------------------------------------------------------------- 1 | 8 | 11 | 14 | 15 | -------------------------------------------------------------------------------- /packages/components/src/components/icons/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/image.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import NextImage, { ImageProps } from 'next/image'; 3 | 4 | export function Image(props: ImageProps): ReactElement { 5 | return ; 6 | } 7 | -------------------------------------------------------------------------------- /packages/components/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { Anchor } from './anchor'; 2 | export { Button } from './button'; 3 | export { CardsColorful } from './cards-colorful'; 4 | export { FeatureList } from './feature-list'; 5 | export { HeroGradient } from './hero-gradient'; 6 | export { HeroIllustration } from './hero-illustration'; 7 | export { HeroMarketplace } from './hero-marketplace'; 8 | export { HeroVideo } from './hero-video'; 9 | export * from './icons'; 10 | export { Image } from './image'; 11 | export { InfoList } from './info-list'; 12 | export { Input } from './input'; 13 | export { LegacyPackageCmd } from './legacy-package-cmd'; 14 | export { MarketplaceList } from './marketplace-list'; 15 | export { MarketplaceSearch } from './marketplace-search'; 16 | export { NPMBadge } from './npm-badge'; 17 | export { GetYourAPIGameRightSection } from './get-your-api-game-right-section'; 18 | export { HiveNavigation, GraphQLConfCard } from './hive-navigation'; 19 | export type { HiveNavigationProps, GraphQLConfCardProps } from './hive-navigation'; 20 | export { HiveFooter } from './hive-footer'; 21 | export { ToolsAndLibrariesCards } from './tools-and-libraries-cards'; 22 | export * from './hero'; 23 | export * from './product-card'; 24 | export * from './decorations'; 25 | export * from './call-to-action'; 26 | export * from './cookies-consent'; 27 | export * from './heading'; 28 | export * from './info-card'; 29 | export * from './stud'; 30 | export * from './explore-main-product-cards'; 31 | export * from './text-link'; 32 | export * from './contact-us'; 33 | export { Giscus } from './giscus'; 34 | export * from './product-card'; 35 | export * from './version-dropdown'; 36 | export * from './dropdown'; 37 | export * from './marquee'; 38 | export { FrequentlyAskedQuestions } from './faq'; 39 | export { ComparisonTable } from './comparison-table'; 40 | export { HiveLayoutConfig } from './hive-layout-config'; 41 | -------------------------------------------------------------------------------- /packages/components/src/components/info-card.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { AccountBox } from './icons'; 4 | import { InfoCard, InfoCardProps } from './info-card'; 5 | 6 | export default { 7 | title: 'Components/InfoCard', 8 | component: InfoCard, 9 | decorators: [hiveThemeDecorator], 10 | argTypes: { 11 | scheme: { 12 | control: { 13 | type: 'select', 14 | }, 15 | options: ['neutral', 'green'], 16 | }, 17 | }, 18 | } satisfies Meta; 19 | 20 | export const Default: StoryObj = { 21 | args: { 22 | icon: , 23 | as: 'div', 24 | heading: 'Customizable User Roles and Permissions', 25 | children: 26 | 'Control user access with detailed, role-based permissions for enhanced security and flexibility.', 27 | }, 28 | }; 29 | 30 | export const Link: StoryObj = { 31 | args: { 32 | icon: , 33 | href: '#', 34 | heading: 'Customizable User Roles and Permissions', 35 | children: 36 | 'Control user access with detailed, role-based permissions for enhanced security and flexibility.', 37 | }, 38 | }; 39 | -------------------------------------------------------------------------------- /packages/components/src/components/info-card.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { cn } from '../cn'; 3 | import { UnionToIntersection } from '../types/utility'; 4 | import { Anchor, AnchorProps } from './anchor'; 5 | import { Stud } from './stud'; 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-namespace 8 | export declare namespace InfoCardProps { 9 | interface InfoCardBaseProps { 10 | icon: ReactNode; 11 | heading: ReactNode; 12 | scheme?: 'neutral' | 'green'; 13 | } 14 | 15 | interface InfoCardLinkProps extends InfoCardBaseProps, Omit { 16 | href: AnchorProps['href']; 17 | } 18 | 19 | interface InfoCardInertProps extends InfoCardBaseProps, React.HTMLAttributes { 20 | as: 'div' | 'li'; 21 | } 22 | } 23 | 24 | export type InfoCardProps = InfoCardProps.InfoCardLinkProps | InfoCardProps.InfoCardInertProps; 25 | 26 | export function InfoCard({ 27 | icon, 28 | heading, 29 | className, 30 | children, 31 | scheme = 'neutral', 32 | ...rest 33 | }: InfoCardProps) { 34 | let Root: InfoCardProps.InfoCardInertProps['as'] | typeof Anchor; 35 | 36 | if ('href' in rest) { 37 | Root = Anchor; 38 | } else { 39 | Root = rest.as || 'div'; 40 | delete (rest as { as?: unknown }).as; 41 | } 42 | 43 | return ( 44 | )} 56 | > 57 | {icon} 58 |

{heading}

59 |
{children}
60 |
61 | ); 62 | } 63 | -------------------------------------------------------------------------------- /packages/components/src/components/info-list.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { dummyInfoList } from '../helpers/dummy'; 3 | import { InfoList } from './info-list'; 4 | 5 | export default { 6 | title: 'Components/Lists/InfoList', 7 | component: InfoList, 8 | argTypes: { 9 | title: { 10 | name: 'Title', 11 | }, 12 | items: { 13 | name: 'Items', 14 | }, 15 | }, 16 | } satisfies Meta; 17 | 18 | type Story = StoryObj; 19 | 20 | export const Default: Story = { args: dummyInfoList }; 21 | -------------------------------------------------------------------------------- /packages/components/src/components/info-list.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import clsx from 'clsx'; 3 | import { IInfoListProps } from '../types/components'; 4 | import { Anchor } from './anchor'; 5 | 6 | export const InfoList = ({ title, items, className }: IInfoListProps): ReactElement => ( 7 |
8 |
9 | {title && ( 10 |

11 | {title} 12 |

13 | )} 14 |
15 | {items.map((item, index) => ( 16 |
20 |

21 | {item.title} 22 |

23 |

24 | {item.description} 25 |

26 | {item.link && ( 27 | 31 | )} 32 |
33 | ))} 34 |
35 |
36 |
37 | ); 38 | -------------------------------------------------------------------------------- /packages/components/src/components/input/input-shake.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useEffect, useRef } from 'react'; 4 | import { Severity } from '../../types/severity'; 5 | 6 | interface InputShakeProps { 7 | severity?: Severity; 8 | } 9 | 10 | /** 11 | * We need this hack to avoid shaking the input when it's first rendered 12 | * already as critical. 13 | */ 14 | export function InputShake({ severity }: InputShakeProps) { 15 | const ref = useRef(null); 16 | const prevSeverityRef = useRef(severity); 17 | 18 | useEffect(() => { 19 | const shouldShake = prevSeverityRef.current !== 'critical' && severity === 'critical'; 20 | 21 | prevSeverityRef.current = severity; 22 | const container = ref.current?.parentElement; 23 | if (container && shouldShake) { 24 | container.classList.add('animate-shake'); 25 | const cleanUp = () => container.classList.remove('animate-shake'); 26 | container.addEventListener('animationend', cleanUp, { once: true }); 27 | } 28 | }, [severity]); 29 | 30 | return
; 31 | } 32 | -------------------------------------------------------------------------------- /packages/components/src/components/legacy-package-cmd.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, useMemo } from 'react'; 2 | import { Pre, Tabs } from 'nextra/components'; 3 | 4 | const PACKAGE_MANAGERS = ['yarn', 'npm', 'pnpm']; 5 | 6 | type PackageMap = Record<(typeof PACKAGE_MANAGERS)[number], string>; 7 | 8 | const Add: PackageMap = { 9 | yarn: 'yarn add', 10 | npm: 'npm install', 11 | pnpm: 'pnpm add', 12 | }; 13 | 14 | const Run: PackageMap = { 15 | yarn: 'yarn', 16 | npm: 'npm run', 17 | pnpm: 'pnpm', 18 | }; 19 | 20 | const Install: PackageMap = { 21 | yarn: 'yarn', 22 | npm: 'npm install', 23 | pnpm: 'pnpm install', 24 | }; 25 | 26 | const Init: PackageMap = { 27 | yarn: 'yarn init --yes', 28 | npm: 'npm init --yes', 29 | pnpm: 'pnpm init', 30 | }; 31 | 32 | const Global: PackageMap = { 33 | yarn: 'yarn global add', 34 | npm: 'npm install --global', 35 | pnpm: 'pnpm add --global', 36 | }; 37 | 38 | type Command = { 39 | name: string; 40 | cmd: 'add' | 'run' | 'install' | 'init'; 41 | isNpx?: boolean; 42 | isGlobal?: boolean; 43 | }; 44 | 45 | export const LegacyPackageCmd = ({ 46 | packages, 47 | }: { 48 | packages: (string | Command)[]; 49 | }): ReactElement => { 50 | const commands = useMemo( 51 | () => 52 | PACKAGE_MANAGERS.map(pkgManager => 53 | packages 54 | .map(pkg => (typeof pkg === 'string' ? ({ name: pkg, cmd: 'add' } as Command) : pkg)) 55 | .map(pkg => { 56 | switch (pkg.cmd) { 57 | case 'run': 58 | return `${pkgManager === 'npm' && pkg.isNpx ? 'npx' : Run[pkgManager]} ${pkg.name}`; 59 | case 'install': 60 | return `${Install[pkgManager]}${pkg.name ? ` ${pkg.name}` : ''}`; 61 | case 'init': 62 | return Init[pkgManager]; 63 | default: 64 | return `${pkg.isGlobal ? Global[pkgManager] : Add[pkgManager]} ${pkg.name}`; 65 | } 66 | }) 67 | .join('\n'), 68 | ), 69 | [packages], 70 | ); 71 | 72 | return ( 73 | 74 | {PACKAGE_MANAGERS.map((pkgManager, index) => ( 75 | 76 |
77 |             
78 |               {/* eslint-disable-next-line tailwindcss/no-custom-classname */}
79 |               {commands[index]}
80 |             
81 |           
82 |
83 | ))} 84 |
85 | ); 86 | }; 87 | -------------------------------------------------------------------------------- /packages/components/src/components/marketplace-list.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { MarketplaceList } from './marketplace-list'; 4 | import { Default as MarketplaceSearchDefaultStory } from './marketplace-search.stories'; 5 | 6 | const meta: Meta = { 7 | title: 'Components/MarketplaceList', 8 | component: MarketplaceList, 9 | argTypes: { 10 | title: { 11 | name: 'Title', 12 | }, 13 | placeholder: { 14 | name: 'No Results Placeholder', 15 | }, 16 | pagination: { 17 | name: 'Products / Page', 18 | }, 19 | items: { 20 | name: 'Items', 21 | }, 22 | colorScheme: { 23 | control: { 24 | type: 'select', 25 | }, 26 | options: ['green', 'black'], 27 | }, 28 | }, 29 | decorators: [ 30 | hiveThemeDecorator, 31 | Story => ( 32 |
33 | 34 |
35 | ), 36 | ], 37 | }; 38 | 39 | export default meta; 40 | 41 | type Story = StoryObj; 42 | 43 | export const Default: Story = { 44 | name: 'MarketplaceList', 45 | args: { 46 | title: 'Trending & Last Update', 47 | placeholder: 'There are no items available...', 48 | pagination: 4, 49 | items: MarketplaceSearchDefaultStory.args?.primaryList?.items ?? [], 50 | }, 51 | }; 52 | 53 | export const Green: Story = { 54 | name: 'MarketplaceList Green', 55 | args: { ...Default.args, colorScheme: 'green' }, 56 | }; 57 | -------------------------------------------------------------------------------- /packages/components/src/components/marquee/use-tween-playback-rate.ts: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | 3 | export function useTweenPlaybackRate() { 4 | const slowingHandle = useRef(null); 5 | const resumingHandle = useRef(null); 6 | const time = useRef(null); 7 | 8 | return function tweenPlaybackRate(animation: Animation, step: number) { 9 | const currentHandle = step > 0 ? resumingHandle : slowingHandle; 10 | const oppositeHandle = step > 0 ? slowingHandle : resumingHandle; 11 | 12 | if (oppositeHandle.current) { 13 | cancelAnimationFrame(oppositeHandle.current); 14 | oppositeHandle.current = time.current = null; 15 | } 16 | 17 | const now = performance.now(); 18 | 19 | if (time.current) { 20 | const deltaTime = now - time.current; 21 | const { playbackRate } = animation; 22 | 23 | const newValue = Math.min(Math.max(playbackRate + step * deltaTime, 0), 1); 24 | if (newValue === playbackRate) return; 25 | 26 | animation.updatePlaybackRate(newValue); 27 | } 28 | 29 | time.current = now; 30 | currentHandle.current = requestAnimationFrame(() => tweenPlaybackRate(animation, step)); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /packages/components/src/components/npm-badge.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import { Anchor } from './anchor'; 3 | 4 | export const NPMBadge = ({ name }: { name: string }): ReactElement => { 5 | const encodedPackage = encodeURIComponent(name); 6 | return ( 7 | 8 | npm version 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/components/src/components/product-card/hive-decoration.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/product-card/hive-gateway-decoration.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/product-card/mesh-decoration.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/product-card/product-card.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { ProductCard } from '..'; 3 | import { hiveThemeDecorator } from '../../../../../.storybook/hive-theme-decorator'; 4 | import { PRODUCTS } from '../../products'; 5 | 6 | export default { 7 | title: 'Hive/ProductCard', 8 | component: ProductCard, 9 | decorators: [hiveThemeDecorator], 10 | parameters: { 11 | forcedLightMode: true, 12 | }, 13 | } satisfies Meta; 14 | 15 | // interweaved to make sure main product cards look good alongside ancillary product cards 16 | const productsLexicographically = Object.values(PRODUCTS).sort((a, b) => 17 | a.name.localeCompare(b.name), 18 | ); 19 | 20 | export const Default: StoryObj = { 21 | name: 'ProductCard', 22 | render() { 23 | return ( 24 |
    25 | {productsLexicographically.map(product => ( 26 | 27 | ))} 28 |
29 | ); 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/components/src/components/product-card/yoga-decoration.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/components/products/envelop.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | import { Meta, StoryObj } from '@storybook/react'; 3 | import { 4 | dummyCardsColorful, 5 | dummyFeatureList, 6 | dummyHeroGradient, 7 | dummyHeroIllustration, 8 | dummyHeroMarketplace, 9 | dummyHeroVideo, 10 | dummyInfoList, 11 | } from '../../helpers/dummy'; 12 | import { CardsColorful } from '../cards-colorful'; 13 | import { FeatureList } from '../feature-list'; 14 | import { HeroGradient } from '../hero-gradient'; 15 | import { HeroIllustration } from '../hero-illustration'; 16 | import { HeroMarketplace } from '../hero-marketplace'; 17 | import { HeroVideo } from '../hero-video'; 18 | import { InfoList } from '../info-list'; 19 | 20 | export default { 21 | title: 'Products/Envelop', 22 | component: Template, 23 | } satisfies Meta; 24 | 25 | type Story = StoryObj; 26 | 27 | function Template({ children }: { children: ReactNode }): ReactNode { 28 | return children; 29 | } 30 | 31 | export const Home: Story = { 32 | args: { 33 | children: ( 34 | <> 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ), 43 | }, 44 | }; 45 | 46 | export const Marketplace: Story = { 47 | args: { 48 | children: , 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /packages/components/src/components/products/tools.stories.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement } from 'react'; 2 | import { Meta, StoryObj } from '@storybook/react'; 3 | import { HeroGradient } from '../hero-gradient'; 4 | import { InfoList } from '../info-list'; 5 | 6 | export default { 7 | title: 'Products/Tools', 8 | component: Template, 9 | } satisfies Meta; 10 | 11 | type Story = StoryObj; 12 | 13 | function Template(): ReactElement { 14 | return ( 15 | <> 16 | 27 | 47 | 48 | ); 49 | } 50 | 51 | export const Home: Story = {}; 52 | -------------------------------------------------------------------------------- /packages/components/src/components/schema-type.stories.ts: -------------------------------------------------------------------------------- 1 | import dedent from 'dedent'; 2 | import { Meta, StoryObj } from '@storybook/react'; 3 | import { SchemaPage } from './schema-type'; 4 | import marketplaceListImage from '../static/dummy/marketplace/logo-modules.svg'; 5 | 6 | export default { 7 | title: 'Components/Schema Type', 8 | component: SchemaPage, 9 | argTypes: { 10 | schemaName: { 11 | name: 'Title', 12 | }, 13 | }, 14 | } satisfies Meta; 15 | 16 | type Story = StoryObj; 17 | 18 | const dummySchema = dedent(/* GraphQL */ ` 19 | type Query { 20 | ping: Boolean 21 | me: User! 22 | } 23 | 24 | " represents a valid email " 25 | scalar Email 26 | 27 | """ 28 | Represents a simple user 29 | """ 30 | type User { 31 | id: ID! 32 | email: Email! 33 | profile: Profile! 34 | } 35 | 36 | type Profile { 37 | name: String 38 | age: Int 39 | } 40 | `); 41 | 42 | const dummyOperations = dedent(/* GraphQL */ ` 43 | query Me { 44 | me { 45 | id 46 | profile { 47 | name 48 | } 49 | } 50 | ping 51 | } 52 | 53 | fragment UserFields on User { 54 | profile { 55 | name 56 | } 57 | } 58 | `); 59 | 60 | export const Default: Story = { 61 | args: { 62 | schemaName: 'Schema Type 1', 63 | tags: ['TypeScript', 'Frontend', 'Backend'], 64 | editorData: [ 65 | { 66 | title: 'schema.graphql', 67 | frameworks: ['TS', 'React', 'Frontend'], 68 | schema: dummySchema, 69 | image: marketplaceListImage, 70 | }, 71 | { 72 | title: 'operation.graphql', 73 | frameworks: [], 74 | operations: dummyOperations, 75 | image: marketplaceListImage, 76 | }, 77 | { 78 | title: 'codegen.yml', 79 | frameworks: [], 80 | schema: dummySchema, 81 | image: marketplaceListImage, 82 | }, 83 | { 84 | title: '', 85 | schema: dummySchema, 86 | }, 87 | ], 88 | }, 89 | }; 90 | -------------------------------------------------------------------------------- /packages/components/src/components/stud.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { RightCornerIcon } from './icons'; 4 | import { Stud, StudProps } from './stud'; 5 | 6 | export default { 7 | title: 'Components/Stud', 8 | component: Stud, 9 | decorators: [hiveThemeDecorator], 10 | } satisfies Meta; 11 | 12 | export const Default: StoryObj = { 13 | name: 'Stud', 14 | args: { 15 | children: , 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/components/src/components/stud.tsx: -------------------------------------------------------------------------------- 1 | import { ComponentProps } from 'react'; 2 | import { cn } from '../cn'; 3 | 4 | export type StudProps = ComponentProps<'div'>; 5 | export function Stud(props: StudProps) { 6 | return ( 7 |
14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /packages/components/src/components/tag.tsx: -------------------------------------------------------------------------------- 1 | import { ReactElement, ReactNode } from 'react'; 2 | import { cn } from '../cn'; 3 | 4 | export interface TagProps extends React.ComponentPropsWithoutRef<'button'> { 5 | children: ReactNode; 6 | selected?: boolean; 7 | } 8 | 9 | export const Tag = ({ children, selected, onClick, ...rest }: TagProps) => { 10 | return ( 11 | 24 | ); 25 | }; 26 | 27 | export const TagsContainer = ({ 28 | children, 29 | className, 30 | focusgroup, 31 | }: { 32 | children: ReactNode; 33 | className?: string; 34 | focusgroup?: 'horizontal'; 35 | }): ReactElement => { 36 | return ( 37 | // eslint-disable-next-line jsx-a11y/no-static-element-interactions 38 |
42 | {children} 43 |
44 | ); 45 | }; 46 | 47 | const moveFocusWithLeftAndRight = (event: React.KeyboardEvent) => { 48 | if (event.target instanceof HTMLElement && event.target.tagName === 'BUTTON') { 49 | let next: Element | null | undefined; 50 | switch (event.key) { 51 | case 'ArrowRight': 52 | next = event.target.nextElementSibling; 53 | break; 54 | case 'ArrowLeft': 55 | next = event.target.previousElementSibling; 56 | break; 57 | } 58 | if (next && next instanceof HTMLElement && next.tagName === 'BUTTON') { 59 | event.preventDefault(); 60 | next.focus(); 61 | } 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /packages/components/src/components/text-link.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { ArrowIcon } from './icons'; 4 | import { TextLink, TextLinkProps } from './text-link'; 5 | 6 | export default { 7 | title: 'Components/TextLink', 8 | component: TextLink, 9 | decorators: [hiveThemeDecorator], 10 | args: { 11 | href: '#', 12 | }, 13 | } satisfies Meta; 14 | 15 | export const WithUnderline: StoryObj = { 16 | args: { 17 | children: 'The Guild', 18 | }, 19 | }; 20 | 21 | export const WithArrow: StoryObj = { 22 | args: { 23 | children: ( 24 | <> 25 | Learn more 26 | 27 | 28 | ), 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /packages/components/src/components/text-link.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { cn } from '../cn'; 3 | import { Anchor, AnchorProps } from './anchor'; 4 | import { ArrowIcon } from './icons'; 5 | 6 | export type TextLinkProps = AnchorProps; 7 | 8 | export function TextLink({ className, children, ...rest }: TextLinkProps) { 9 | const hasArrow = 10 | children && 11 | flattenFragments(children).some( 12 | child => typeof child === 'object' && child && 'type' in child && child.type === ArrowIcon, 13 | ); 14 | 15 | return ( 16 | 24 | {children} 25 | 26 | ); 27 | } 28 | 29 | function flattenFragments(children: React.ReactNode): React.ReactNode[] { 30 | return React.Children.toArray(children).flatMap(child => 31 | typeof child === 'object' && 'type' in child && child.type === React.Fragment 32 | ? (child.props.children as React.ReactNode[]) 33 | : child, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /packages/components/src/components/theme-switcher.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { FC, ReactNode } from 'react'; 4 | import { useTheme } from 'nextra-theme-docs'; 5 | 6 | export const ThemeSwitcherButton: FC<{ children: ReactNode }> = ({ children }) => { 7 | const { resolvedTheme, setTheme } = useTheme(); 8 | 9 | return ( 10 | 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /packages/components/src/components/tools-and-libraries-cards/index.tsx: -------------------------------------------------------------------------------- 1 | import { HTMLAttributes } from 'react'; 2 | import { cn } from '../../cn'; 3 | import { FOUR_MAIN_PRODUCTS, SIX_HIGHLIGHTED_PRODUCTS } from '../../products'; 4 | import { CallToAction } from '../call-to-action'; 5 | import { Heading } from '../heading'; 6 | import { AncillaryProductCard, MainProductCard } from '../product-card'; 7 | 8 | export interface ToolsAndLibrariesCardsProps extends HTMLAttributes { 9 | isHive?: boolean; 10 | } 11 | export function ToolsAndLibrariesCards({ 12 | className, 13 | isHive, 14 | ...rest 15 | }: ToolsAndLibrariesCardsProps) { 16 | return ( 17 |
24 | 25 | Discover the complete ecosystem of tools and libraries 26 | 27 |

Complete GraphQL Federation Stack

28 |
    29 | {FOUR_MAIN_PRODUCTS.map(product => ( 30 | 31 | ))} 32 |
33 |

Our libraries to support all your GraphQL needs

34 |
    35 | {SIX_HIGHLIGHTED_PRODUCTS.map(product => ( 36 | 37 | ))} 38 |
39 | 43 | Explore the Ecosystem 44 | 45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /packages/components/src/components/tools-and-libraries-cards/tools-and-libraries.stories.ts: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { ToolsAndLibrariesCards } from '.'; 3 | import { hiveThemeDecorator } from '../../../../../.storybook/hive-theme-decorator'; 4 | 5 | export default { 6 | title: 'Hive/ToolsAndLibrariesCards', 7 | component: ToolsAndLibrariesCards, 8 | decorators: [hiveThemeDecorator], 9 | parameters: { 10 | forcedLightMode: true, 11 | }, 12 | } satisfies Meta; 13 | 14 | export const Default: StoryObj = { 15 | name: 'ToolsAndLibrariesCards', 16 | }; 17 | -------------------------------------------------------------------------------- /packages/components/src/components/version-dropdown.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../../.storybook/hive-theme-decorator'; 3 | import { VersionDropdown, VersionDropdownProps } from './version-dropdown'; 4 | 5 | const decorator = (Story: React.FC) => ( 6 |
7 | 8 |
9 | ); 10 | 11 | export default { 12 | title: 'Components/VersionDropdown', 13 | component: VersionDropdown, 14 | decorators: [hiveThemeDecorator, decorator], 15 | } satisfies Meta; 16 | 17 | export const Default: StoryObj = { 18 | name: 'VersionDropdown', 19 | args: { 20 | currentVersion: '1.0.0', 21 | versions: [ 22 | { value: '1.0.0', label: 'Hive Docs 1.0.0', href: '/1.0.0' }, 23 | { value: '1.1.0', label: 'Hive Docs 1.1.0', href: '/1.1.0' }, 24 | ], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/components/src/components/version-dropdown.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { cn } from '../cn'; 4 | import { Dropdown, DropdownContent, DropdownItem, DropdownTrigger } from './dropdown'; 5 | import { CaretSlimIcon, CheckIcon } from './icons'; 6 | 7 | export interface VersionDropdownProps { 8 | chevronPosition?: 'left' | 'right'; 9 | currentVersion: string; 10 | versions: { 11 | label?: string; 12 | value: string; 13 | href?: string; 14 | onClick?: () => void; 15 | }[]; 16 | } 17 | export function VersionDropdown({ 18 | currentVersion, 19 | versions, 20 | chevronPosition = 'left', 21 | }: VersionDropdownProps) { 22 | return ( 23 | 24 | 25 | {chevronPosition === 'left' && } 26 | {currentVersion} 27 | {chevronPosition === 'right' && } 28 | 29 | 30 | 31 | {versions.map(version => ( 32 | 41 | {version.label ?? version.value}{' '} 42 | {version.value === currentVersion && } 43 | 44 | ))} 45 | 46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /packages/components/src/constants.ts: -------------------------------------------------------------------------------- 1 | const siteBaseUrl = 'https://the-guild.dev'; 2 | 3 | export const siteUrl = process.env.SITE_URL; 4 | 5 | /** 6 | * Used in header and footer links to either have 'https://the-guild.dev prefix or '' 7 | */ 8 | export const siteOrigin = siteUrl === siteBaseUrl ? '' : siteBaseUrl; 9 | -------------------------------------------------------------------------------- /packages/components/src/design-system/typography.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { designSystemDocsDecorator } from '../../../../.storybook/design-system-docs-decorator'; 3 | import { Heading, HeadingProps } from '../components/heading'; 4 | 5 | export default { 6 | title: 'Design System/Typography', 7 | decorators: [designSystemDocsDecorator], 8 | } satisfies Meta; 9 | 10 | export const Typography: StoryObj = { 11 | render() { 12 | return ( 13 |
14 |

Heading

15 | {(['xl', 'lg', 'md', 'sm'] satisfies HeadingProps['size'][]).map(size => ( 16 | <> 17 |
18 |

19 | heading {size} 20 |

21 | 22 | Open-source GraphQL management platform 23 | 24 | 25 | ))} 26 |
27 | ); 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/components/src/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import { FC, SVGProps } from 'react'; 3 | export const ReactComponent: FC>; 4 | const src: string; 5 | export default src; 6 | } 7 | 8 | declare module '*.png'; 9 | 10 | interface Window { 11 | $crisp?: { 12 | push(cmd: string[]): void; 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /packages/components/src/icon.d.ts: -------------------------------------------------------------------------------- 1 | import { FC, SVGProps } from 'react'; 2 | 3 | declare const ReactComponent: FC>; 4 | 5 | export { ReactComponent }; 6 | -------------------------------------------------------------------------------- /packages/components/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Navbar, NotFoundPage, useConfig, useTheme, useThemeConfig } from 'nextra-theme-docs'; 2 | export { 3 | Callout, 4 | Cards, 5 | Code, 6 | FileTree, 7 | Mermaid, 8 | Steps, 9 | Tabs, 10 | Bleed, 11 | Collapse, 12 | Search, 13 | Banner, 14 | Table, 15 | } from 'nextra/components'; 16 | export { useMounted } from 'nextra/hooks'; 17 | export * from './components'; 18 | export { PRODUCTS } from './products'; 19 | export * from './types/components'; 20 | export * from './logos'; 21 | export { cn } from './cn'; 22 | export * from './next-types'; 23 | export { normalizePages } from 'nextra/normalize-pages'; 24 | 25 | /** 26 | * @deprecated Consider using `ComparisonTable` instead. 27 | * This name was kept for back-compat, because the public 28 | * API differs, 29 | */ 30 | 31 | declare module 'react' { 32 | interface CSSProperties { 33 | [key: `--${string}`]: string | number | undefined; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/components/src/logos/fets.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /packages/components/src/logos/heltin.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /packages/components/src/logos/hive-combination-mark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/components/src/logos/nextra.svg: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /packages/components/src/logos/the-guild.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 15 | 18 | 21 | 22 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /packages/components/src/next-types.ts: -------------------------------------------------------------------------------- 1 | import { UnionToIntersection } from './types/utility'; 2 | 3 | /** 4 | * Next.js page props type. 5 | * @see https://nextjs.org/docs/app/api-reference/file-conventions/page#props 6 | * @see https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes#good-to-know 7 | */ 8 | export interface NextPageProps< 9 | TParams extends string = never, 10 | TSearchParams extends string = never, 11 | > { 12 | params: Promise< 13 | UnionToIntersection< 14 | { 15 | [K in TParams]: { 16 | [F in K extends `...${infer U}` ? U : K]: K extends `...${string}` ? string[] : string; 17 | }; 18 | }[TParams] 19 | > 20 | >; 21 | searchParams: Promise<{ [K in TSearchParams]?: string | string[] }>; 22 | } 23 | -------------------------------------------------------------------------------- /packages/components/src/product.stories.tsx: -------------------------------------------------------------------------------- 1 | import { Meta, StoryObj } from '@storybook/react'; 2 | import { hiveThemeDecorator } from '../../../.storybook/hive-theme-decorator'; 3 | import { PRODUCTS_MENU_LIST } from './products'; 4 | 5 | export default { 6 | title: 'Components/PRODUCTS_MENU_LIST', 7 | component: () => null, 8 | decorators: [hiveThemeDecorator], 9 | parameters: { 10 | padding: true, 11 | }, 12 | } satisfies Meta; 13 | 14 | export const Default: StoryObj = { 15 | name: 'PRODUCTS_MENU_LIST', 16 | render() { 17 | return ( 18 |
19 | {Object.values(PRODUCTS_MENU_LIST).map(product => product.title)} 20 |
21 | ); 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/components/src/server/body.client.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { DetailedHTMLProps, FC, HtmlHTMLAttributes } from 'react'; 4 | import { usePathname } from 'next/navigation'; 5 | import { cn } from '../cn'; 6 | 7 | export interface BodyProps 8 | extends DetailedHTMLProps, HTMLBodyElement> { 9 | lightOnlyPages?: string[]; 10 | } 11 | 12 | export const Body: FC = ({ lightOnlyPages, children, className, ...rest }) => { 13 | const pathname = usePathname(); 14 | const isLightOnlyPage = lightOnlyPages?.includes(pathname); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | -------------------------------------------------------------------------------- /packages/components/src/server/index.ts: -------------------------------------------------------------------------------- 1 | // Must be in `server` folder because can contain server-only components or imports Node.js builtin 2 | export * from './mdx-components/index.js'; 3 | // Must be in `server` folder because contains import of `useMDXComponents` 4 | export { MDXRemote } from 'nextra/mdx-remote'; 5 | 6 | export { fetchFilePathsFromGitHub } from 'nextra/fetch-filepaths-from-github'; 7 | export { compileMdx } from 'nextra/compile'; 8 | export { 9 | getPageMap, 10 | createIndexPage, 11 | convertToPageMap, 12 | mergeMetaWithPageMap, 13 | normalizePageMap, 14 | } from 'nextra/page-map'; 15 | export { evaluate } from 'nextra/evaluate'; 16 | export { fetchPackageInfo } from './npm.js'; 17 | export { sharedMetaItems } from './shared-meta-items.js'; 18 | export * from './body.client.js'; 19 | export { remarkLinkRewrite } from './remark-link-rewrite.js'; 20 | 21 | /** 22 | * Contain `getPageMap` import which imports `metadata` from pages, in case importing from 23 | * `@theguild/components` will throw: 24 | * 25 | * × Error: You are attempting to export "metadata" from a component marked with "use client", 26 | * which is disallowed. Either remove the export, or the "use client" directive. Read more: https://nextjs.org 27 | */ 28 | export { GuildLayout, getDefaultMetadata } from './theme-layout.js'; 29 | export { HiveLayout } from './hive-layout.js'; 30 | -------------------------------------------------------------------------------- /packages/components/src/server/mdx-components/hive-mdx-components.ts: -------------------------------------------------------------------------------- 1 | import { MDXComponents } from 'nextra/mdx-components'; 2 | import { useMDXComponents } from './mdx-components'; 3 | import { MDXLink } from './mdx-link'; 4 | 5 | /** 6 | * MDX components used in Hive-rebranded websites. 7 | */ 8 | export const useHiveMDXComponents = (components?: MDXComponents): MDXComponents => 9 | useMDXComponents({ 10 | a: MDXLink, 11 | ...components, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/components/src/server/mdx-components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mdx-components'; 2 | export * from './hive-mdx-components'; 3 | 4 | export * from './mdx-link'; 5 | -------------------------------------------------------------------------------- /packages/components/src/server/mdx-components/mdx-components.tsx: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs/promises'; 2 | import path from 'node:path'; 3 | import { addBasePath } from 'next/dist/client/add-base-path'; 4 | import clsx from 'clsx'; 5 | import { useMDXComponents as getDocsMDXComponents } from 'nextra-theme-docs'; 6 | 7 | const docsComponents = getDocsMDXComponents({ 8 | async source({ src, type, ...props }) { 9 | if (!src) { 10 | throw new Error('Must provide `src` prop'); 11 | } 12 | if (src.startsWith('/')) { 13 | const filePath = path.join(process.cwd(), 'public', src); 14 | try { 15 | await fs.access(filePath); 16 | } catch (error) { 17 | const relativePath = path.relative(process.cwd(), filePath); 18 | if (typeof error === 'object' && error && 'code' in error && error.code === 'ENOENT') { 19 | throw new Error(`File doesn't exist: ${relativePath}`); 20 | } 21 | throw new Error(`Error checking file: ${relativePath}`); 22 | } 23 | } 24 | 25 | let ext = path.extname(src).slice(1); // remove dot; 26 | if (ext === 'mov') { 27 | ext = 'quicktime'; 28 | } 29 | return ; 30 | }, 31 | video: ({ className, children, ...props }) => ( 32 | 36 | ), 37 | iframe: ({ className, ...props }) => ( 38 |