diff --git a/apps/docs/src/components/demo-block/index.tsx b/apps/docs/src/components/demo-block/index.tsx index a5639da25..d9eda7dbd 100644 --- a/apps/docs/src/components/demo-block/index.tsx +++ b/apps/docs/src/components/demo-block/index.tsx @@ -40,7 +40,6 @@ const ResetIcon = () => ( ); -// Build scope for react-runner to resolve imports const scope = { import: { react: React, diff --git a/apps/docs/src/components/highlighted-code/HighlightedCode.tsx b/apps/docs/src/components/highlighted-code/HighlightedCode.tsx index f2f8ecde8..5c5d13c63 100644 --- a/apps/docs/src/components/highlighted-code/HighlightedCode.tsx +++ b/apps/docs/src/components/highlighted-code/HighlightedCode.tsx @@ -53,4 +53,4 @@ Prism.languages.bash = Prism.languages.shell = { )} ); - }; \ No newline at end of file + }; diff --git a/apps/docs/src/containers/theme-studio/theme-document-adapter.ts b/apps/docs/src/containers/theme-studio/theme-document-adapter.ts index 3eff1c899..765342491 100644 --- a/apps/docs/src/containers/theme-studio/theme-document-adapter.ts +++ b/apps/docs/src/containers/theme-studio/theme-document-adapter.ts @@ -203,14 +203,25 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum 'date-picker.header-border': fields.border, 'date-picker.header-button-color': fields.mutedForeground, 'date-picker.header-button-color-hover': fields.primary, + 'date-picker.header-action-hover-bg': fields.muted, + 'date-picker.header-action-focus-ring': fields.ring, 'date-picker.today-color': fields.primary, 'date-picker.today-color-hover': fields.primary, + 'date-picker.today-hover-bg': fields.accent, + 'date-picker.today-active-bg': fields.muted, + 'date-picker.today-focus-ring': fields.ring, 'picker.cell-hover-bg': fields.muted, 'date-picker.cell-selected-bg': fields.primary, 'date-picker.cell-selected-color': fields.primaryForeground, 'date-picker.cell-selected-hover-bg': fields.primary, 'date-picker.cell-today-border': fields.primary, 'date-picker.cell-radius': fields.inputRadius, + 'date-picker.panel-item-hover-bg': fields.muted, + 'date-picker.panel-item-current-color': fields.primary, + 'date-picker.panel-item-current-border': fields.primary, + 'date-picker.panel-item-selected-bg': fields.accent, + 'date-picker.panel-item-selected-color': fields.primary, + 'date-picker.panel-item-selected-hover-bg': fields.muted, 'date-picker.range-bg': fields.accent, 'time-picker.column-border': fields.border, 'time-picker.cell-bg-selected': fields.primary, @@ -222,11 +233,15 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum 'calendar.bg': fields.card, 'calendar.border': fields.border, 'calendar.radius': fields.cardRadius, - 'calendar.hover': fields.muted, - 'calendar.nav-button-color': fields.mutedForeground, - 'calendar.nav-button-color-hover': fields.primary, - 'calendar.title-color': fields.cardForeground, - 'calendar.title-color-hover': fields.primary, + 'calendar.cell-hover-bg': fields.muted, + 'calendar.header-action-color': fields.mutedForeground, + 'calendar.header-action-color-hover': fields.primary, + 'calendar.header-action-hover-bg': fields.muted, + 'calendar.header-action-focus-ring': fields.ring, + 'calendar.header-label-color': fields.cardForeground, + 'calendar.header-label-color-hover': fields.primary, + 'calendar.header-label-hover-bg': fields.muted, + 'calendar.header-label-focus-ring': fields.ring, 'calendar.cell-header-color': fields.mutedForeground, 'calendar.week-number-color': fields.mutedForeground, 'calendar.cell-color': fields.cardForeground, @@ -238,10 +253,16 @@ export function buildThemeDocumentFromDraft(draft: ThemeEditorDraft): ThemeDocum 'calendar.range-bg': fields.accent, 'calendar.cell-focus-outline': fields.shadowFocus, 'calendar.cell-dot-color': fields.primary, - 'calendar.panel-cell-radius': fields.inputRadius, - 'calendar.panel-cell-color-selected': fields.primaryForeground, - 'calendar.panel-cell-bg-selected': fields.primary, - 'calendar.today-link-color': fields.primary, + 'calendar.panel-item-radius': fields.inputRadius, + 'calendar.panel-item-hover-bg': fields.muted, + 'calendar.panel-item-selected-color': fields.primaryForeground, + 'calendar.panel-item-selected-bg': fields.primary, + 'calendar.panel-item-selected-hover-bg': fields.primary, + 'calendar.today-color': fields.primary, + 'calendar.today-color-hover': fields.primary, + 'calendar.today-hover-bg': fields.accent, + 'calendar.today-active-bg': fields.muted, + 'calendar.today-focus-ring': fields.ring, 'cascader.height.sm': fields.fieldHeightSm, 'cascader.height.md': fields.fieldHeightMd, 'cascader.height.lg': fields.fieldHeightLg, diff --git a/apps/docs/src/containers/token-explorer/index.tsx b/apps/docs/src/containers/token-explorer/index.tsx new file mode 100644 index 000000000..07c06727b --- /dev/null +++ b/apps/docs/src/containers/token-explorer/index.tsx @@ -0,0 +1,601 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import registryData from '@tiny-design/tokens/registry-runtime'; +import { useSearchParams } from 'react-router-dom'; +import { + Button, + Calendar, + Card, + DatePicker, + Input, + Select, + Tag, + Textarea, + TimePicker, +} from '@tiny-design/react'; +import { useLocaleContext } from '../../context/locale-context'; +import './token-explorer.scss'; + +type RegistryToken = { + key: string; + cssVar: string; + category: string; + component?: string; + type: string; + group?: string; + description?: string; + source: string; + defaultValue?: string; + fallback?: string; + status: string; +}; + +type RegistryShape = { + version: number; + generatedAt: string; + tokens: RegistryToken[]; +}; + +type PreviewRenderer = () => React.ReactElement; + +const registry = registryData as RegistryShape; + +function titleCase(value: string): string { + return value + .split(/[-.]/) + .filter(Boolean) + .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) + .join(' '); +} + +function getSemanticGroup(component: string, tokenKey: string): string { + const remainder = tokenKey.replace(`${component}.`, ''); + + if (remainder.startsWith('header-')) return 'Header'; + if (remainder.startsWith('cell-')) return 'Cells'; + if (remainder.startsWith('panel-item-')) return 'Panel Items'; + if (remainder.startsWith('today-')) return 'Today Action'; + if (remainder.startsWith('input-')) return 'Input'; + if (remainder.startsWith('range-')) return 'Range'; + if (remainder.startsWith('week-number-')) return 'Week Numbers'; + if (remainder.startsWith('month-content-')) return 'Panel Items'; + if (remainder.startsWith('footer-')) return 'Footer'; + if (remainder.startsWith('body-')) return 'Body'; + + const [firstSegment] = remainder.split('.'); + return titleCase(firstSegment); +} + +function formatComponentLabel(component: string): string { + return component + .split('-') + .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) + .join(' '); +} + +function isColorValue(value: string | undefined): boolean { + if (!value) return false; + if (typeof CSS !== 'undefined' && typeof CSS.supports === 'function') { + return CSS.supports('color', value); + } + return false; +} + +const TokenExplorer = (): React.ReactElement => { + const { siteLocale } = useLocaleContext(); + const isZh = siteLocale.locale === 'zh_CN'; + const labels = useMemo( + () => + isZh + ? { + title: 'Token Explorer', + intro: + '浏览组件 token 的最小可用面板。组件列表会自动从 registry 生成,重点组件提供实时预览,用来检查命名、分组、默认值与运行时 CSS 变量。', + component: '组件', + search: '搜索 token', + searchPlaceholder: '按 key、描述或 CSS 变量搜索', + summary: '摘要', + groups: '分组', + type: '类型', + tokens: 'Tokens', + components: '组件数', + generatedAt: '生成时间', + previewStatus: '预览', + defaultValue: '默认值', + fallback: 'Fallback', + resolvedValue: '当前值', + source: '来源', + preview: '实时预览', + previewDesc: + '右侧预览直接使用当前 docs 主题环境,适合快速核对 token 是否命中对应界面。', + previewMissing: '这个组件还没有专用预览,当前版本先提供 token 浏览能力。', + copyKey: '复制 Key', + copyVar: '复制变量', + copied: '已复制', + empty: '没有匹配的 token。', + allGroups: '全部分组', + allTypes: '全部类型', + previewReady: '已配置', + previewMissingBadge: '待补', + } + : { + title: 'Token Explorer', + intro: + 'A minimal token browser for component tokens. The component list is generated from the registry, and key components include live previews so token naming, grouping, defaults, and runtime CSS vars are easy to inspect.', + component: 'Component', + search: 'Search Tokens', + searchPlaceholder: 'Search by key, description, or CSS variable', + summary: 'Summary', + groups: 'Groups', + type: 'Type', + tokens: 'Tokens', + components: 'Components', + generatedAt: 'Generated', + previewStatus: 'Preview', + defaultValue: 'Default', + fallback: 'Fallback', + resolvedValue: 'Resolved', + source: 'Source', + preview: 'Live Preview', + previewDesc: + 'The preview uses the current docs theme environment, so it is useful for quick token-to-UI verification.', + previewMissing: + 'This component does not have a dedicated preview yet. In this version, token browsing still works without it.', + copyKey: 'Copy Key', + copyVar: 'Copy Var', + copied: 'Copied', + empty: 'No matching tokens.', + allGroups: 'All Groups', + allTypes: 'All Types', + previewReady: 'Ready', + previewMissingBadge: 'Pending', + }, + [isZh] + ); + + const [searchParams, setSearchParams] = useSearchParams(); + + const previewRenderers = useMemo>( + () => ({ + 'date-picker': () => ( +
+ + +
+ +
+
+
+ + +
+ +
+
+
+
+ ), + calendar: () => ( +
+ + +
+ +
+
+
+
+ ), + button: () => ( +
+ + +
+ + + +
+
+
+
+ ), + input: () => ( +
+ + +
+ + +
+
+
+
+ ), + textarea: () => ( +
+ + +