From 2f00a5ba1adf7e6ebb1d8bea511c828f32cb500d Mon Sep 17 00:00:00 2001 From: "Tino Liu [SSW]" Date: Wed, 15 Jan 2025 14:32:42 +0800 Subject: [PATCH 01/15] setup Marquee component from Magic UI --- components.json | 21 ++++ package.json | 5 + pnpm-lock.yaml | 62 +++++++--- src/components/ui/marquee.tsx | 73 ++++++++++++ src/lib/utils.ts | 6 + styles/tailwind.css | 65 +++++++++++ tailwind.config.js | 208 +++++++++++++++++++++++++++++----- tsconfig.json | 3 + 8 files changed, 399 insertions(+), 44 deletions(-) create mode 100644 components.json create mode 100644 src/components/ui/marquee.tsx create mode 100644 src/lib/utils.ts diff --git a/components.json b/components.json new file mode 100644 index 0000000000..e6add3b96c --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "styles/tailwind.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/package.json b/package.json index 7a5f9fd465..5c9923fa86 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,8 @@ "autoprefixer": "^10.4.20", "axios": "^0.21.4", "btoa": "^1.2.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "date-fns": "^4.1.0", "dotenv": "^8.6.0", "email-validator": "^2.0.4", @@ -56,6 +58,7 @@ "js-cookie": "^3.0.5", "jsonp": "^0.2.1", "lodash": "^4.17.21", + "lucide-react": "^0.471.0", "markdown-toc": "^1.2.0", "moment": "^2.30.1", "moment-locales-webpack-plugin": "^1.2.0", @@ -94,6 +97,8 @@ "serve": "^14.2.4", "slick-carousel": "^1.8.1", "styled-components": "^6.1.13", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", "tailwindcss-animated": "^1.1.2", "three": "^0.165.0", "tinacms": "2.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7e30dfb60..3c9afe5587 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,6 +62,12 @@ importers: btoa: specifier: ^1.2.1 version: 1.2.1 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 date-fns: specifier: ^4.1.0 version: 4.1.0 @@ -92,6 +98,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + lucide-react: + specifier: ^0.471.0 + version: 0.471.0(react@18.3.1) markdown-toc: specifier: ^1.2.0 version: 1.2.0 @@ -206,6 +215,12 @@ importers: styled-components: specifier: ^6.1.13 version: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.15) tailwindcss-animated: specifier: ^1.1.2 version: 1.1.2(tailwindcss@3.4.15) @@ -6742,6 +6757,11 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + lucide-react@0.471.0: + resolution: {integrity: sha512-3L0OOJClsKDETJGK7nABqW8ftaVmUjWzluzPpw/6dGdI1bOmzsLsCjZpAEpg24Xs/U7xdYveQU+CBkHxWy7MrA==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} @@ -8986,8 +9006,13 @@ packages: tabbable@6.2.0: resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - tailwind-merge@2.5.5: - resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} + tailwind-merge@2.6.0: + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' tailwindcss-animated@1.1.2: resolution: {integrity: sha512-SI4owS5ojserhgEYIZA/uFVdNjU2GMB2P3sjtjmFA52VxoUi+Hht6oR5+RdT+CxrX9cNNYEa+vbTWHvN9zbj3w==} @@ -11273,7 +11298,7 @@ snapshots: cross-inspect: 1.0.1 dset: 3.1.4 graphql: 15.9.0 - tslib: 2.6.3 + tslib: 2.8.1 '@graphql-tools/utils@9.2.1(graphql@15.9.0)': dependencies: @@ -13465,13 +13490,13 @@ snapshots: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - '@udecode/cn@33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.5.5)': + '@udecode/cn@33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.6.0)': dependencies: '@udecode/react-utils': 33.0.0(@types/react@16.14.62)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) class-variance-authority: 0.7.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tailwind-merge: 2.5.5 + tailwind-merge: 2.6.0 transitivePeerDependencies: - '@types/react' @@ -17272,7 +17297,7 @@ snapshots: jest-environment-jsdom: 25.5.0(bufferutil@4.0.8)(utf-8-validate@6.0.3) jest-environment-node: 25.5.0 jest-get-type: 25.2.6 - jest-jasmine2: 25.5.4 + jest-jasmine2: 25.5.4(bufferutil@4.0.8)(utf-8-validate@6.0.3) jest-regex-util: 25.2.6 jest-resolve: 25.5.1 jest-util: 25.5.0 @@ -17348,7 +17373,7 @@ snapshots: transitivePeerDependencies: - supports-color - jest-jasmine2@25.5.4: + jest-jasmine2@25.5.4(bufferutil@4.0.8)(utf-8-validate@6.0.3): dependencies: '@babel/traverse': 7.25.9 '@jest/environment': 25.5.0 @@ -17368,7 +17393,10 @@ snapshots: pretty-format: 25.5.0 throat: 5.0.0 transitivePeerDependencies: + - bufferutil + - canvas - supports-color + - utf-8-validate jest-leak-detector@25.5.0: dependencies: @@ -17433,7 +17461,7 @@ snapshots: jest-config: 25.5.4(bufferutil@4.0.8)(utf-8-validate@6.0.3) jest-docblock: 25.3.0 jest-haste-map: 25.5.1 - jest-jasmine2: 25.5.4 + jest-jasmine2: 25.5.4(bufferutil@4.0.8)(utf-8-validate@6.0.3) jest-leak-detector: 25.5.0 jest-message-util: 25.5.0 jest-resolve: 25.5.1 @@ -17883,6 +17911,10 @@ snapshots: dependencies: react: 18.3.1 + lucide-react@0.471.0(react@18.3.1): + dependencies: + react: 18.3.1 + lunr@2.3.9: {} maath@0.10.8(@types/three@0.170.0)(three@0.165.0): @@ -20793,7 +20825,11 @@ snapshots: tabbable@6.2.0: {} - tailwind-merge@2.5.5: {} + tailwind-merge@2.6.0: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.15): + dependencies: + tailwindcss: 3.4.15 tailwindcss-animated@1.1.2(tailwindcss@3.4.15): dependencies: @@ -20926,7 +20962,7 @@ snapshots: '@tinacms/mdx': 1.4.2(react@18.3.1)(typescript@5.7.2)(yup@1.4.0) '@tinacms/schema-tools': 1.6.2(react@18.3.1)(yup@1.4.0) '@tinacms/search': 1.0.28(abstract-level@1.0.4)(encoding@0.1.13)(react@18.3.1)(sucrase@3.35.0)(typescript@5.7.2)(yup@1.4.0) - '@udecode/cn': 33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.5.5) + '@udecode/cn': 33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.6.0) '@udecode/plate': 36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) '@udecode/plate-autoformat': 36.5.6(@udecode/plate-common@36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) '@udecode/plate-block-quote': 36.0.0(@udecode/plate-common@36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) @@ -20975,7 +21011,7 @@ snapshots: slate-history: 0.100.0(slate@0.103.0) slate-hyperscript: 0.100.0(slate@0.103.0) slate-react: 0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0) - tailwind-merge: 2.5.5 + tailwind-merge: 2.6.0 webfontloader: 1.6.28 yup: 1.4.0 zod: 3.23.8 @@ -21012,7 +21048,7 @@ snapshots: '@tinacms/mdx': 1.5.2(react@18.3.1)(typescript@4.9.5)(yup@1.4.0) '@tinacms/schema-tools': 1.6.8(react@18.3.1)(yup@1.4.0) '@tinacms/search': 1.0.35(abstract-level@1.0.4)(react@18.3.1)(sucrase@3.35.0)(typescript@4.9.5)(yup@1.4.0) - '@udecode/cn': 33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.5.5) + '@udecode/cn': 33.0.0(@types/react@16.14.62)(class-variance-authority@0.7.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tailwind-merge@2.6.0) '@udecode/plate': 36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) '@udecode/plate-autoformat': 36.5.6(@udecode/plate-common@36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) '@udecode/plate-block-quote': 36.0.0(@udecode/plate-common@36.5.9(@types/react@16.14.62)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate-history@0.100.0(slate@0.103.0))(slate-hyperscript@0.100.0(slate@0.103.0))(slate-react@0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0))(slate@0.103.0) @@ -21063,7 +21099,7 @@ snapshots: slate-history: 0.100.0(slate@0.103.0) slate-hyperscript: 0.100.0(slate@0.103.0) slate-react: 0.107.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(slate@0.103.0) - tailwind-merge: 2.5.5 + tailwind-merge: 2.6.0 webfontloader: 1.6.28 yup: 1.4.0 zod: 3.23.8 diff --git a/src/components/ui/marquee.tsx b/src/components/ui/marquee.tsx new file mode 100644 index 0000000000..2f7ce1ac54 --- /dev/null +++ b/src/components/ui/marquee.tsx @@ -0,0 +1,73 @@ +import { cn } from '@/lib/utils'; +import { ComponentPropsWithoutRef } from 'react'; + +interface MarqueeProps extends ComponentPropsWithoutRef<'div'> { + /** + * Optional CSS class name to apply custom styles + */ + className?: string; + /** + * Whether to reverse the animation direction + * @default false + */ + reverse?: boolean; + /** + * Whether to pause the animation on hover + * @default false + */ + pauseOnHover?: boolean; + /** + * Content to be displayed in the marquee + */ + children: React.ReactNode; + /** + * Whether to animate vertically instead of horizontally + * @default false + */ + vertical?: boolean; + /** + * Number of times to repeat the content + * @default 4 + */ + repeat?: number; +} + +export default function Marquee({ + className, + reverse = false, + pauseOnHover = false, + children, + vertical = false, + repeat = 4, + ...props +}: MarqueeProps) { + return ( +
+ {Array(repeat) + .fill(0) + .map((_, i) => ( +
+ {children} +
+ ))} +
+ ); +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000000..2819a830d2 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/styles/tailwind.css b/styles/tailwind.css index 61c841c29a..8c3393eef5 100644 --- a/styles/tailwind.css +++ b/styles/tailwind.css @@ -16,4 +16,69 @@ background-color: white !important; border-radius: 50% !important; opacity: 50% !important; +} + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } } \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 855b28ab6e..87a0b6ae9b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -2,17 +2,13 @@ const defaultTheme = require('tailwindcss/defaultTheme'); module.exports = { + darkMode: ['class'], theme: { spacing: { - px: '1px', 0: '0px', - 0.5: '2px', 1: '4px', - 1.5: '6px', 2: '8px', - 2.5: '10px', 3: '12px', - 3.5: '14px', 4: '16px', 5: '20px', 6: '24px', @@ -40,6 +36,11 @@ module.exports = { 72: '288px', 80: '320px', 96: '384px', + px: '1px', + 0.5: '2px', + 1.5: '6px', + 2.5: '10px', + 3.5: '14px', }, borderRadius: { none: '0px', @@ -53,30 +54,105 @@ module.exports = { full: '9999px', }, borderWidth: { - DEFAULT: '1px', 0: '0', 2: '2px', 3: '3px', 4: '4px', 6: '6px', 8: '8px', + DEFAULT: '1px', }, fontSize: { - xxs: ['10px', { lineHeight: '1.2' }], - xs: ['13px', { lineHeight: '1.33' }], - sm: ['14px', { lineHeight: '1.43' }], - base: ['16px', { lineHeight: '1.5' }], - md: ['16px', { lineHeight: '1.5' }], - lg: ['18px', { lineHeight: '1.55' }], - xl: ['20px', { lineHeight: '1.4' }], - '2xl': ['24px', { lineHeight: '1.33' }], - '3xl': ['30px', { lineHeight: '1.2' }], - '4xl': ['36px', { lineHeight: '1.1' }], - '5xl': ['48px', { lineHeight: '1' }], - '6xl': ['60px', { lineHeight: '1' }], - '7xl': ['72px', { lineHeight: '1' }], - '8xl': ['96px', { lineHeight: '1' }], - '9xl': ['128px', { lineHeight: '1' }], + xxs: [ + '10px', + { + lineHeight: '1.2', + }, + ], + xs: [ + '13px', + { + lineHeight: '1.33', + }, + ], + sm: [ + '14px', + { + lineHeight: '1.43', + }, + ], + base: [ + '16px', + { + lineHeight: '1.5', + }, + ], + md: [ + '16px', + { + lineHeight: '1.5', + }, + ], + lg: [ + '18px', + { + lineHeight: '1.55', + }, + ], + xl: [ + '20px', + { + lineHeight: '1.4', + }, + ], + '2xl': [ + '24px', + { + lineHeight: '1.33', + }, + ], + '3xl': [ + '30px', + { + lineHeight: '1.2', + }, + ], + '4xl': [ + '36px', + { + lineHeight: '1.1', + }, + ], + '5xl': [ + '48px', + { + lineHeight: '1', + }, + ], + '6xl': [ + '60px', + { + lineHeight: '1', + }, + ], + '7xl': [ + '72px', + { + lineHeight: '1', + }, + ], + '8xl': [ + '96px', + { + lineHeight: '1', + }, + ], + '9xl': [ + '128px', + { + lineHeight: '1', + }, + ], }, opacity: { 0: '0', @@ -99,8 +175,12 @@ module.exports = { extend: { keyframes: { slideIn: { - '0%': { transform: 'translate3d(0,-100%,0)' }, - '100%': { transform: 'translate3d(0,0,0)' }, + '0%': { + transform: 'translate3d(0,-100%,0)', + }, + '100%': { + transform: 'translate3d(0,0,0)', + }, }, popIn: { '0%': { @@ -112,10 +192,28 @@ module.exports = { transform: 'scale(1)', }, }, + marquee: { + from: { + transform: 'translateX(0)', + }, + to: { + transform: 'translateX(calc(-100% - var(--gap)))', + }, + }, + 'marquee-vertical': { + from: { + transform: 'translateY(0)', + }, + to: { + transform: 'translateY(calc(-100% - var(--gap)))', + }, + }, }, animation: { 'slide-in': 'slideIn 200ms ease-out 1', 'pop-in': 'popIn 0.5s ease-out forwards', + marquee: 'marquee var(--duration) infinite linear', + 'marquee-vertical': 'marquee-vertical var(--duration) linear infinite', }, boxShadow: { xs: '0 0 0 1px rgba(0, 0, 0, 0.05)', @@ -134,7 +232,7 @@ module.exports = { seafoam: { 50: '#EEFDF9', 100: '#E9FBF4', - 150: '#C1F5EB', // Deprecated: This color will be removed in future designs. + 150: '#C1F5EB', 200: '#CFF5E6', 300: '#B4EFD9', 400: '#99E9CB', @@ -174,6 +272,46 @@ module.exports = { success: '#22C55E', warning: '#F59E0B', error: '#EF4444', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + 1: 'hsl(var(--chart-1))', + 2: 'hsl(var(--chart-2))', + 3: 'hsl(var(--chart-3))', + 4: 'hsl(var(--chart-4))', + 5: 'hsl(var(--chart-5))', + }, }, fontFamily: { sans: ['Inter var', ...defaultTheme.fontFamily.sans], @@ -191,7 +329,16 @@ module.exports = { '50vh': '50vh', }, backgroundImage: { - 'blob-bg': "url(\"data:image/svg+xml,%3Csvg preserveAspectRatio='none' viewBox='0 0 194 109' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_566_318)'%3E%3Crect width='194' height='109' fill='white' /%3E%3Cmask id='mask0_566_318' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='194' height='109'%3E%3Crect width='194' height='109' fill='url(%23paint0_linear_566_318)' /%3E%3C/mask%3E%3Cg mask='url(%23mask0_566_318)'%3E%3Crect width='194' height='109' fill='url(%23paint1_linear_566_318)' /%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_566_318' x1='97' y1='0' x2='97' y2='109' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23D9D9D9' stop-opacity='0.45' /%3E%3Cstop offset='0.229052' stop-color='%23D9D9D9' stop-opacity='0.1678' /%3E%3Cstop offset='0.677779' stop-color='%23D9D9D9' stop-opacity='0.0513' /%3E%3Cstop offset='1' stop-color='%23D9D9D9' stop-opacity='0' /%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_566_318' x1='0' y1='54.5' x2='194' y2='54.5' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%2353E9DD' /%3E%3Cstop offset='0.34375' stop-color='%2368D7E4' /%3E%3Cstop offset='0.59375' stop-color='%2359BFF2' /%3E%3Cstop offset='1' stop-color='%234BA8FF' /%3E%3C/linearGradient%3E%3CclipPath id='clip0_566_318'%3E%3Crect width='194' height='109' fill='white' /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E\")" + 0: 194, + 109: ' fill=', + 'blob-bg': 'url(\\\\"data:image/svg+xml,%3Csvg preserveAspectRatio=', + ' viewBox=': 0, + ' xmlns=': 'http', + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', }, }, }, @@ -205,14 +352,13 @@ module.exports = { require('@tailwindcss/aspect-ratio'), require('@tailwindcss/forms'), require('tailwindcss-animated'), + require('tailwindcss-animate'), ], content: [ - './pages/**/*.{js,ts,jsx,tsx}', // Legacy Pages Router + './pages/**/*.{js,ts,jsx,tsx}', // Legacy Pages Router './components/**/*.{js,ts,jsx,tsx}', // All reusable components - './app/**/*.{js,ts,jsx,tsx}', // App Router files - './app/**/**/*.{js,ts,jsx,tsx}', // Nested components in app/ - ], - safelist: [ - 'font-tuner', + './app/**/*.{js,ts,jsx,tsx}', // App Router files + './app/**/**/*.{js,ts,jsx,tsx}', // Nested components in app/ ], + safelist: ['font-tuner'], }; diff --git a/tsconfig.json b/tsconfig.json index 8c48da96d7..75af10d12d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,9 @@ { "compilerOptions": { "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, "target": "es5", "lib": [ "dom", From 19db75bc09de88ee186cc5c5d06952cf2bce8886 Mon Sep 17 00:00:00 2001 From: "Tino Liu [SSW]" Date: Wed, 15 Jan 2025 14:33:12 +0800 Subject: [PATCH 02/15] Implement Marquee into testimonials block --- .../blocks/Testimonial/Testimonials.tsx | 208 +++++++----------- 1 file changed, 80 insertions(+), 128 deletions(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 52c3290af2..40bbe1a102 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -1,104 +1,84 @@ +import Marquee from '@/components/ui/marquee'; +import { cn } from '@/lib/utils'; import Image from 'next/image'; import { useMemo, useRef, useState } from 'react'; import { tinaField } from 'tinacms/dist/react'; import { TinaMarkdown } from 'tinacms/dist/rich-text'; import { formatDate } from 'utils'; -const checkTouchScreen = () => { - let hasTouchScreen = false; - if ('maxTouchPoints' in navigator) { - hasTouchScreen = navigator.maxTouchPoints > 0; - } else { - const mQ = matchMedia?.('(pointer:coarse)'); - if (mQ?.media === '(pointer:coarse)') { - hasTouchScreen = !!mQ.matches; - } else if ('orientation' in window) { - hasTouchScreen = true; // deprecated, but good fallback - } else { - // Only as a last resort, fall back to user agent sniffing - const UA: string = (navigator as Navigator).userAgent; - hasTouchScreen = - /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || - /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA); - } - } - return hasTouchScreen; -}; +// const checkTouchScreen = () => { +// let hasTouchScreen = false; +// if ('maxTouchPoints' in navigator) { +// hasTouchScreen = navigator.maxTouchPoints > 0; +// } else { +// const mQ = matchMedia?.('(pointer:coarse)'); +// if (mQ?.media === '(pointer:coarse)') { +// hasTouchScreen = !!mQ.matches; +// } else if ('orientation' in window) { +// hasTouchScreen = true; // deprecated, but good fallback +// } else { +// // Only as a last resort, fall back to user agent sniffing +// const UA: string = (navigator as Navigator).userAgent; +// hasTouchScreen = +// /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || +// /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA); +// } +// } +// return hasTouchScreen; +// }; -const Testimonial = ({ data, ...props }) => { - const Elem = data.link ? 'a' : 'div'; +const TestimonialCard = ({ ...data }) => { + const Elem = data?.link ? 'a' : 'div'; return ( - - {data.testimonial && ( -
- -
- )} -
- {data.avatar && ( -
- Testimonial avatar -
-
+ +
- {data.name && ( -

+ > +
+ +
+
{data.name} -

- )} - {(data.username || data.date) && ( -

- {data.username && <>@{data.username}} - {data.username && data.date && ( - - )} - {data.date && ( - - {formatDate(data.date)} - - )} + +

+ {data.username}

- )} +
- +
+ {data.testimonial && ( +
+ +
+ )} +
+
); }; export default function TestimonialsBlock({ data, index }) { - const isTouchScreen = useMemo(() => checkTouchScreen(), []); + // const isTouchScreen = useMemo(() => checkTouchScreen(), []); const [isShowingAll, setIsShowingAll] = useState(false); const titleRef = useRef(null); + const firstRow = data.testimonials.slice(0, data.testimonials.length / 2); + const secondRow = data.testimonials.slice(data.testimonials.length / 2); return ( <> @@ -109,53 +89,25 @@ export default function TestimonialsBlock({ data, index }) { > {data?.title || 'Loved by Developers'} -
- {data.testimonials && - data.testimonials.map((testimonial, index) => { - return ( - (([0, 1].includes(index) || isShowingAll || !isTouchScreen) && ( - - )) || - ([2].includes(index) && !isShowingAll && ( -
- -
- )) - ); - })} -
- {!isShowingAll && isTouchScreen ? ( - - ) : null} - {isShowingAll && isTouchScreen ? ( - - ) : null} + +
+ + {firstRow.map((review, index) => ( +
+ +
+ ))} +
+ + {secondRow.map((review, index) => ( +
+ +
+ ))} +
+
+
+
); } From c6bfb8426bcf2fbde7c3dbef69b6ff559ada00e3 Mon Sep 17 00:00:00 2001 From: "Tino Liu [SSW]" Date: Wed, 15 Jan 2025 16:05:40 +0800 Subject: [PATCH 03/15] Fix testimony card height --- components/blocks/Testimonial/Testimonials.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 40bbe1a102..6a0ced9c7f 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -34,7 +34,7 @@ const TestimonialCard = ({ ...data }) => {
Date: Wed, 15 Jan 2025 17:43:06 +0800 Subject: [PATCH 04/15] add src as tailwind config folder --- tailwind.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/tailwind.config.js b/tailwind.config.js index 87a0b6ae9b..f739bde0c2 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -359,6 +359,7 @@ module.exports = { './components/**/*.{js,ts,jsx,tsx}', // All reusable components './app/**/*.{js,ts,jsx,tsx}', // App Router files './app/**/**/*.{js,ts,jsx,tsx}', // Nested components in app/ + './src/**/*.{js,ts,jsx,tsx}', ], safelist: ['font-tuner'], }; From 35af9cc92b7035be7bb788c681884f6aa3d33271 Mon Sep 17 00:00:00 2001 From: "Tino Liu [SSW]" Date: Wed, 15 Jan 2025 18:22:20 +0800 Subject: [PATCH 05/15] changed testimonial card css --- .../blocks/Testimonial/Testimonials.tsx | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 6a0ced9c7f..e8f6dea38a 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -31,10 +31,10 @@ const TestimonialCard = ({ ...data }) => { const Elem = data?.link ? 'a' : 'div'; return ( - +
{
-
- {data.name} -
-

- {data.username} -

+ {data.name && ( +

+ {data.name} +

+ )} + {(data.username || data.date) && ( +

+ {data.username && <>@{data.username}} + {data.username && data.date && ( + + )} + {data.date && ( + + {formatDate(data.date)} + + )} +

+ )}
@@ -91,14 +103,14 @@ export default function TestimonialsBlock({ data, index }) {
- + {firstRow.map((review, index) => (
))}
- + {secondRow.map((review, index) => (
From fa894aea1bf5caa45b2147099e5e13e1c9cb7d46 Mon Sep 17 00:00:00 2001 From: "Tino Liu [SSW]" Date: Wed, 15 Jan 2025 19:02:06 +0800 Subject: [PATCH 06/15] update line clamp style --- components/blocks/Testimonial/Testimonials.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index e8f6dea38a..15326ac54f 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -72,7 +72,7 @@ const TestimonialCard = ({ ...data }) => {
{data.testimonial && ( -
+
Date: Thu, 16 Jan 2025 11:40:01 +0800 Subject: [PATCH 07/15] Adjust the layout of Testimonials --- .../blocks/Testimonial/Testimonials.tsx | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 15326ac54f..2ca7d95790 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -34,16 +34,26 @@ const TestimonialCard = ({ ...data }) => {
-
+
+ {data.testimonial && ( +
+ +
+ )} +
+
Testimonial avatar { />
{data.name && ( -

- {data.name} -

+

{data.name}

)} {(data.username || data.date) && ( -

+

{data.username && <>@{data.username}} {data.username && data.date && ( - + )} {data.date && ( - - {formatDate(data.date)} - + {formatDate(data.date)} )}

)}
-
- {data.testimonial && ( -
- -
- )} -
); From f1971091c78a60052a37e7109fc4fadbdf458a17 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Thu, 16 Jan 2025 14:36:30 +0800 Subject: [PATCH 08/15] Keep the functionality of Testimonials unchanged --- components/blocks/Testimonial/Testimonials.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 2ca7d95790..1bfb709810 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -51,9 +51,17 @@ const TestimonialCard = ({ ...data }) => {
)}
-
+
Testimonial avatar Date: Thu, 16 Jan 2025 15:44:38 +0800 Subject: [PATCH 09/15] fix the error in tailwind.config.js --- tailwind.config.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tailwind.config.js b/tailwind.config.js index 9438f846df..a357ad1054 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -330,18 +330,13 @@ module.exports = { '50vh': '50vh', }, backgroundImage: { - 0: 194, - 109: ' fill=', - 'blob-bg': 'url(\\\\"data:image/svg+xml,%3Csvg preserveAspectRatio=', - ' viewBox=': 0, - ' xmlns=': 'http', + 'blob-bg': + "url(\"data:image/svg+xml,%3Csvg preserveAspectRatio='none' viewBox='0 0 194 109' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_566_318)'%3E%3Crect width='194' height='109' fill='white' /%3E%3Cmask id='mask0_566_318' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='194' height='109'%3E%3Crect width='194' height='109' fill='url(%23paint0_linear_566_318)' /%3E%3C/mask%3E%3Cg mask='url(%23mask0_566_318)'%3E%3Crect width='194' height='109' fill='url(%23paint1_linear_566_318)' /%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_566_318' x1='97' y1='0' x2='97' y2='109' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23D9D9D9' stop-opacity='0.45' /%3E%3Cstop offset='0.229052' stop-color='%23D9D9D9' stop-opacity='0.1678' /%3E%3Cstop offset='0.677779' stop-color='%23D9D9D9' stop-opacity='0.0513' /%3E%3Cstop offset='1' stop-color='%23D9D9D9' stop-opacity='0' /%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_566_318' x1='0' y1='54.5' x2='194' y2='54.5' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%2353E9DD' /%3E%3Cstop offset='0.34375' stop-color='%2368D7E4' /%3E%3Cstop offset='0.59375' stop-color='%2359BFF2' /%3E%3Cstop offset='1' stop-color='%234BA8FF' /%3E%3C/linearGradient%3E%3CclipPath id='clip0_566_318'%3E%3Crect width='194' height='109' fill='white' /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E\")", }, borderRadius: { lg: 'var(--radius)', md: 'calc(var(--radius) - 2px)', sm: 'calc(var(--radius) - 4px)', - 'blob-bg': - "url(\"data:image/svg+xml,%3Csvg preserveAspectRatio='none' viewBox='0 0 194 109' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cg clip-path='url(%23clip0_566_318)'%3E%3Crect width='194' height='109' fill='white' /%3E%3Cmask id='mask0_566_318' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='0' y='0' width='194' height='109'%3E%3Crect width='194' height='109' fill='url(%23paint0_linear_566_318)' /%3E%3C/mask%3E%3Cg mask='url(%23mask0_566_318)'%3E%3Crect width='194' height='109' fill='url(%23paint1_linear_566_318)' /%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_566_318' x1='97' y1='0' x2='97' y2='109' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23D9D9D9' stop-opacity='0.45' /%3E%3Cstop offset='0.229052' stop-color='%23D9D9D9' stop-opacity='0.1678' /%3E%3Cstop offset='0.677779' stop-color='%23D9D9D9' stop-opacity='0.0513' /%3E%3Cstop offset='1' stop-color='%23D9D9D9' stop-opacity='0' /%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_566_318' x1='0' y1='54.5' x2='194' y2='54.5' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%2353E9DD' /%3E%3Cstop offset='0.34375' stop-color='%2368D7E4' /%3E%3Cstop offset='0.59375' stop-color='%2359BFF2' /%3E%3Cstop offset='1' stop-color='%234BA8FF' /%3E%3C/linearGradient%3E%3CclipPath id='clip0_566_318'%3E%3Crect width='194' height='109' fill='white' /%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E\")", }, }, }, From dfed98b79d9d6403c9fc576a90524c22d4bd41f7 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Fri, 17 Jan 2025 15:08:54 +0800 Subject: [PATCH 10/15] Updated some styles to make the gradient effect more natural --- .../blocks/Testimonial/Testimonials.tsx | 63 ++++++++----------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/components/blocks/Testimonial/Testimonials.tsx b/components/blocks/Testimonial/Testimonials.tsx index 1bfb709810..eaa92c75fe 100644 --- a/components/blocks/Testimonial/Testimonials.tsx +++ b/components/blocks/Testimonial/Testimonials.tsx @@ -1,32 +1,10 @@ import Marquee from '@/components/ui/marquee'; import { cn } from '@/lib/utils'; -import Image from 'next/image'; -import { useMemo, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { tinaField } from 'tinacms/dist/react'; import { TinaMarkdown } from 'tinacms/dist/rich-text'; import { formatDate } from 'utils'; -// const checkTouchScreen = () => { -// let hasTouchScreen = false; -// if ('maxTouchPoints' in navigator) { -// hasTouchScreen = navigator.maxTouchPoints > 0; -// } else { -// const mQ = matchMedia?.('(pointer:coarse)'); -// if (mQ?.media === '(pointer:coarse)') { -// hasTouchScreen = !!mQ.matches; -// } else if ('orientation' in window) { -// hasTouchScreen = true; // deprecated, but good fallback -// } else { -// // Only as a last resort, fall back to user agent sniffing -// const UA: string = (navigator as Navigator).userAgent; -// hasTouchScreen = -// /\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) || -// /\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA); -// } -// } -// return hasTouchScreen; -// }; - const TestimonialCard = ({ ...data }) => { const Elem = data?.link ? 'a' : 'div'; @@ -35,19 +13,15 @@ const TestimonialCard = ({ ...data }) => {
{data.testimonial && (
- +
)}
@@ -59,7 +33,7 @@ const TestimonialCard = ({ ...data }) => { > Testimonial avatar { }; export default function TestimonialsBlock({ data, index }) { - // const isTouchScreen = useMemo(() => checkTouchScreen(), []); const [isShowingAll, setIsShowingAll] = useState(false); const titleRef = useRef(null); const firstRow = data.testimonials.slice(0, data.testimonials.length / 2); @@ -106,7 +79,7 @@ export default function TestimonialsBlock({ data, index }) { {data?.title || 'Loved by Developers'} -
+
{firstRow.map((review, index) => (
@@ -121,9 +94,27 @@ export default function TestimonialsBlock({ data, index }) {
))}
-
-
+ ); } From 05839f80f4dfe80b6426dc12ff821eb27b859122 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Mon, 20 Jan 2025 17:02:39 +0800 Subject: [PATCH 11/15] Resolved the issues related to PBI2758 and optimized the corresponding component --- app/docs/[...slug]/DocsPagesClient.tsx | 10 +- .../Docs/toc/index.tsx | 177 +++++++++--------- .../Docs/toc_helper.ts | 13 +- 3 files changed, 103 insertions(+), 97 deletions(-) diff --git a/app/docs/[...slug]/DocsPagesClient.tsx b/app/docs/[...slug]/DocsPagesClient.tsx index 642550a881..edbed0dd89 100644 --- a/app/docs/[...slug]/DocsPagesClient.tsx +++ b/app/docs/[...slug]/DocsPagesClient.tsx @@ -11,12 +11,10 @@ import { TinaMarkdown } from 'tinacms/dist/rich-text'; import { useTocListener } from 'components/AppRouterMigrationComponents/Docs/toc_helper'; export default function DocsClient(props) { - const { PageTableOfContents, NavigationDocsData } = props.props; const DocumentationData = props.tinaProps.data.doc; - - const allData = [DocumentationData, PageTableOfContents, NavigationDocsData]; + const allData = [DocumentationData, PageTableOfContents, NavigationDocsData]; const isScreenSmallerThan1200 = screenResizer().isScreenSmallerThan1200; const isScreenSmallerThan840 = screenResizer().isScreenSmallerThan840; @@ -32,8 +30,6 @@ export default function DocsClient(props) { title: DocumentationData?.next?.title, }; - - const lastEdited = DocumentationData?.last_edited; const date = lastEdited === null ? null : new Date(lastEdited); const formattedDate = date @@ -49,9 +45,7 @@ export default function DocsClient(props) { ? 'grid-cols-[1.25fr_3fr]' : 'grid-cols-[1.25fr_3fr_0.75fr]'; - - - return ( + return (
{/* LEFT COLUMN */} diff --git a/components/AppRouterMigrationComponents/Docs/toc/index.tsx b/components/AppRouterMigrationComponents/Docs/toc/index.tsx index 368a17e768..263d250d9a 100644 --- a/components/AppRouterMigrationComponents/Docs/toc/index.tsx +++ b/components/AppRouterMigrationComponents/Docs/toc/index.tsx @@ -1,17 +1,20 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import styled, { css } from 'styled-components'; import RightArrowSvg from 'public/svg/right-arrow.svg'; import { getDocId } from 'utils/docs/getDocIds'; +import { syncTocScroll } from 'components/AppRouterMigrationComponents/Docs/toc_helper'; interface TocProps { tocItems: Array<{ type: string; text: string }>; activeIds: string[]; } -export const generateMarkdown = (tocItems: Array<{ type: string; text: string }>) => { +export const generateMarkdown = ( + tocItems: Array<{ type: string; text: string }> +) => { return tocItems .map((item) => { const anchor = getDocId(item.text); @@ -23,12 +26,21 @@ export const generateMarkdown = (tocItems: Array<{ type: string; text: string }> const ToC = ({ tocItems, activeIds }: TocProps) => { const [isOpen, setIsOpen] = useState(false); + const tocWrapperRef = useRef(null); useEffect(() => { + console.log(tocWrapperRef.current); + const syncScrollHander = () => syncTocScroll(tocWrapperRef); + window.addEventListener('scroll', syncScrollHander); + syncScrollHander(); + const close = () => setIsOpen(false); const allLinks = document.querySelectorAll('a'); allLinks.forEach((a) => a.addEventListener('click', close)); - return () => allLinks.forEach((a) => a.removeEventListener('click', close)); + + return () => { + allLinks.forEach((a) => a.removeEventListener('click', close)); + }; }, []); if (!tocItems || tocItems.length === 0) { @@ -37,41 +49,80 @@ const ToC = ({ tocItems, activeIds }: TocProps) => { const tocMarkdown = generateMarkdown(tocItems); - - return ( - - - Table of Contents - ( -
  • {children}
  • - ), - a: ({ children, ...props }) => { - const isActive = activeIds.includes(props.href?.slice(1)); // Match href with activeIds - return ( - - {children} - - ); - }, - }} - > - {tocMarkdown} -
    -
    -
    + <> + + + Table of Contents + + ( +
      {children}
    + ), + li: ({ children }) => ( +
  • {children}
  • + ), + a: ({ children, ...props }) => { + const isActive = activeIds.includes(props.href?.slice(1)); // Match href with activeIds + return ( + + {children} + + ); + }, + }} + > + {tocMarkdown} +
    +
    +
    +
    + ); }; export default ToC; - +const TocTitleList = styled.div<{ ref: React.RefObject }>` + scrollbar-width: none; + ::-webkit-scrollbar { + display: none; + } + -ms-overflow-style: none; + + word-wrap: break-word; + white-space: normal; + overflow-wrap: break-word; + + -webkit-mask-image: linear-gradient( + to bottom, + transparent, + black 5%, + black 95%, + transparent + ); + mask-image: linear-gradient( + to bottom, + transparent, + black 5%, + black 95%, + transparent + ); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; +`; const TocDesktopHeader = styled.span` display: none; font-size: 1rem; @@ -89,6 +140,10 @@ const TocDesktopHeader = styled.span` const TocWrapper = styled.div` margin-bottom: -0.375rem; flex: 0 0 auto; + width: 300px; /* 固定宽度 */ + word-wrap: break-word; /* 如果是长单词,打断换行 */ + white-space: normal; /* 强制内容换行 */ + overflow-wrap: break-word; /* 支持现代浏览器换行规则 */ @media (min-width: 1200px) { position: sticky; @@ -96,60 +151,6 @@ const TocWrapper = styled.div` } `; -const TocButton = styled.button<{ isOpen: boolean }>` - display: block; - padding: 0; - outline: none; - border: none; - color: var(--color-secondary); - opacity: 0.65; - background: transparent; - cursor: pointer; - transition: opacity 185ms ease-out; - display: flex; - align-items: center; - line-height: 1; - margin-bottom: 1.125rem; - - span { - margin-right: 0.5rem; - } - - svg { - position: relative; - width: 1.25rem; - height: auto; - fill: var(--color-grey); - transform-origin: 50% 50%; - transition: opacity 180ms ease-out, transform 180ms ease-out; - opacity: 0.5; - } - - :hover, - :focus { - opacity: 1; - - svg { - opacity: 1; - } - } - - ${(props) => - props.isOpen && - css` - color: var(--color-orange); - - svg { - transform: rotate(90deg); - opacity: 1; - } - `} - - @media (min-width: 1200px) { - display: none; - } -`; - const TocContent = styled.div<{ isOpen: boolean; activeIds: string[] }>` display: block; width: 100%; @@ -176,6 +177,7 @@ const TocContent = styled.div<{ isOpen: boolean; activeIds: string[] }>` margin: 0; display: flex; flex-direction: column; + flex-wrap: wrap; } li { @@ -183,7 +185,6 @@ const TocContent = styled.div<{ isOpen: boolean; activeIds: string[] }>` padding: 0.375rem 0; } - ul ul { padding-left: 0.75rem; diff --git a/components/AppRouterMigrationComponents/Docs/toc_helper.ts b/components/AppRouterMigrationComponents/Docs/toc_helper.ts index 6d791a5cd7..4eda969932 100644 --- a/components/AppRouterMigrationComponents/Docs/toc_helper.ts +++ b/components/AppRouterMigrationComponents/Docs/toc_helper.ts @@ -50,7 +50,6 @@ export function useTocListener(data: any) { React.useEffect(() => { if (!contentRef.current) return; - const tocListener = createTocListener(contentRef, setActiveIds); const handleScroll = () => tocListener(); // Define scroll handler @@ -64,3 +63,15 @@ export function useTocListener(data: any) { return { contentRef, activeIds }; } + +export function syncTocScroll(tocWrapperRef: React.RefObject) { + if (!tocWrapperRef.current) return; + const htmlScrollTop = document.documentElement.scrollTop; + const htmlScrollHeight = document.documentElement.scrollHeight; + const scrollPercent = htmlScrollTop / (htmlScrollHeight - htmlScrollTop); + + const tocScrollHeight = tocWrapperRef.current.scrollHeight; + const tocClientHeight = tocWrapperRef.current.clientHeight; + tocWrapperRef.current.scrollTop = + scrollPercent * (tocScrollHeight - tocClientHeight); +} From 331aac8e49d61865f27aebdc6b0b404258c060ad Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Tue, 21 Jan 2025 09:04:15 +0800 Subject: [PATCH 12/15] Update the comments --- .../Docs/toc/index.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/AppRouterMigrationComponents/Docs/toc/index.tsx b/components/AppRouterMigrationComponents/Docs/toc/index.tsx index 263d250d9a..ee8bfe77e6 100644 --- a/components/AppRouterMigrationComponents/Docs/toc/index.tsx +++ b/components/AppRouterMigrationComponents/Docs/toc/index.tsx @@ -72,11 +72,11 @@ const ToC = ({ tocItems, activeIds }: TocProps) => { {children} @@ -140,10 +140,10 @@ const TocDesktopHeader = styled.span` const TocWrapper = styled.div` margin-bottom: -0.375rem; flex: 0 0 auto; - width: 300px; /* 固定宽度 */ - word-wrap: break-word; /* 如果是长单词,打断换行 */ - white-space: normal; /* 强制内容换行 */ - overflow-wrap: break-word; /* 支持现代浏览器换行规则 */ + width: 300px; /* fix width */ + word-wrap: break-word; /* break the long word */ + white-space: normal; + overflow-wrap: break-word; /* suppport Chrome */ @media (min-width: 1200px) { position: sticky; From 1f49befee5d0796bb4513ffa8bd79d8d65357514 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Tue, 21 Jan 2025 13:15:18 +0800 Subject: [PATCH 13/15] Optimized the synchronization function --- .../Docs/toc_helper.ts | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/components/AppRouterMigrationComponents/Docs/toc_helper.ts b/components/AppRouterMigrationComponents/Docs/toc_helper.ts index 4eda969932..fa7aa8599a 100644 --- a/components/AppRouterMigrationComponents/Docs/toc_helper.ts +++ b/components/AppRouterMigrationComponents/Docs/toc_helper.ts @@ -66,12 +66,26 @@ export function useTocListener(data: any) { export function syncTocScroll(tocWrapperRef: React.RefObject) { if (!tocWrapperRef.current) return; - const htmlScrollTop = document.documentElement.scrollTop; - const htmlScrollHeight = document.documentElement.scrollHeight; - const scrollPercent = htmlScrollTop / (htmlScrollHeight - htmlScrollTop); - - const tocScrollHeight = tocWrapperRef.current.scrollHeight; - const tocClientHeight = tocWrapperRef.current.clientHeight; - tocWrapperRef.current.scrollTop = - scrollPercent * (tocScrollHeight - tocClientHeight); + const middleContentLength = document.documentElement.scrollHeight; + const sideBarLength = tocWrapperRef.current.scrollHeight; + const preLength = 100; + const postLength = 1100; + + const middleContentPos = document.documentElement.scrollTop; + + let newPosition = 0; + if (middleContentPos < preLength) { + newPosition = 0; + } else if (middleContentPos > middleContentLength - postLength) { + newPosition = sideBarLength; + } else { + newPosition = + (middleContentPos - preLength) * + (sideBarLength / (middleContentLength - preLength - postLength)); + } + + tocWrapperRef.current.scrollTo({ + top: newPosition, + behavior: 'auto', + }); } From 1742d8dafcad5caefa84a2c0b14c5069ee725ada Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Tue, 21 Jan 2025 13:52:14 +0800 Subject: [PATCH 14/15] Optimized the synchronization function --- .../Docs/toc/index.tsx | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/components/AppRouterMigrationComponents/Docs/toc/index.tsx b/components/AppRouterMigrationComponents/Docs/toc/index.tsx index ee8bfe77e6..9c50d0c551 100644 --- a/components/AppRouterMigrationComponents/Docs/toc/index.tsx +++ b/components/AppRouterMigrationComponents/Docs/toc/index.tsx @@ -29,11 +29,6 @@ const ToC = ({ tocItems, activeIds }: TocProps) => { const tocWrapperRef = useRef(null); useEffect(() => { - console.log(tocWrapperRef.current); - const syncScrollHander = () => syncTocScroll(tocWrapperRef); - window.addEventListener('scroll', syncScrollHander); - syncScrollHander(); - const close = () => setIsOpen(false); const allLinks = document.querySelectorAll('a'); allLinks.forEach((a) => a.addEventListener('click', close)); @@ -43,6 +38,26 @@ const ToC = ({ tocItems, activeIds }: TocProps) => { }; }, []); + useEffect(() => { + if (tocWrapperRef.current && activeIds.length > 0) { + const tocList = tocWrapperRef.current; + + const lastActiveId = activeIds[activeIds.length - 1]; + const activeLink = tocList.querySelector(`a[href="#${lastActiveId}"]`); + + if (activeLink) { + const activeTop = (activeLink as HTMLElement).offsetTop; + const activeHeight = (activeLink as HTMLElement).offsetHeight; + const listHeight = tocList.clientHeight; + + tocList.scrollTo({ + top: activeTop - listHeight / 2 + activeHeight / 2, + behavior: 'smooth', + }); + } + } + }, [activeIds]); + if (!tocItems || tocItems.length === 0) { return null; } From da41bc828bc7e33e4b62233bc6ec748005d5a186 Mon Sep 17 00:00:00 2001 From: ZenoWang1999 Date: Tue, 21 Jan 2025 13:56:15 +0800 Subject: [PATCH 15/15] Optimized the synchronization function --- .../Docs/toc/index.tsx | 2 -- .../Docs/toc_helper.ts | 26 ------------------- 2 files changed, 28 deletions(-) diff --git a/components/AppRouterMigrationComponents/Docs/toc/index.tsx b/components/AppRouterMigrationComponents/Docs/toc/index.tsx index 9c50d0c551..8d02befb8d 100644 --- a/components/AppRouterMigrationComponents/Docs/toc/index.tsx +++ b/components/AppRouterMigrationComponents/Docs/toc/index.tsx @@ -3,9 +3,7 @@ import { useState, useEffect, useRef } from 'react'; import ReactMarkdown from 'react-markdown'; import styled, { css } from 'styled-components'; -import RightArrowSvg from 'public/svg/right-arrow.svg'; import { getDocId } from 'utils/docs/getDocIds'; -import { syncTocScroll } from 'components/AppRouterMigrationComponents/Docs/toc_helper'; interface TocProps { tocItems: Array<{ type: string; text: string }>; diff --git a/components/AppRouterMigrationComponents/Docs/toc_helper.ts b/components/AppRouterMigrationComponents/Docs/toc_helper.ts index fa7aa8599a..8e2b190490 100644 --- a/components/AppRouterMigrationComponents/Docs/toc_helper.ts +++ b/components/AppRouterMigrationComponents/Docs/toc_helper.ts @@ -63,29 +63,3 @@ export function useTocListener(data: any) { return { contentRef, activeIds }; } - -export function syncTocScroll(tocWrapperRef: React.RefObject) { - if (!tocWrapperRef.current) return; - const middleContentLength = document.documentElement.scrollHeight; - const sideBarLength = tocWrapperRef.current.scrollHeight; - const preLength = 100; - const postLength = 1100; - - const middleContentPos = document.documentElement.scrollTop; - - let newPosition = 0; - if (middleContentPos < preLength) { - newPosition = 0; - } else if (middleContentPos > middleContentLength - postLength) { - newPosition = sideBarLength; - } else { - newPosition = - (middleContentPos - preLength) * - (sideBarLength / (middleContentLength - preLength - postLength)); - } - - tocWrapperRef.current.scrollTo({ - top: newPosition, - behavior: 'auto', - }); -}