├── .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 |
21 |
22 | ),
23 | ctx,
24 | );
25 | }
26 |
27 | function HiveIconMark(props: React.SVGProps) {
28 | return (
29 |
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 |
27 |
--------------------------------------------------------------------------------
/packages/components/src/components/decorations/arch-decoration.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 &&
}
29 |
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 |
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 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/arrow-icon.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/bard.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/caret-slim.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/check.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/close.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/codegen.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/group.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/hive-gateway.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/hive.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/honour.svg:
--------------------------------------------------------------------------------
1 |
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 |
9 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/list.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/mesh.svg:
--------------------------------------------------------------------------------
1 |
16 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/moon.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/more.svg:
--------------------------------------------------------------------------------
1 |
34 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/paper.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/pencil.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/right-corner.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/search.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/share.svg:
--------------------------------------------------------------------------------
1 |
46 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/shield-flash.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/stellate.svg:
--------------------------------------------------------------------------------
1 |
19 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/target.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/twitter.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/yoga.svg:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/packages/components/src/components/icons/youtube.svg:
--------------------------------------------------------------------------------
1 |
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 |
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/packages/components/src/components/product-card/hive-decoration.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/components/src/components/product-card/hive-gateway-decoration.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/components/src/components/product-card/mesh-decoration.svg:
--------------------------------------------------------------------------------
1 |
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 |
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