@teamix-evo/ui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -184
- package/manifest.json +680 -492
- package/package.json +20 -10
- package/src/components/accordion/accordion.meta.md +5 -4
- package/src/components/accordion/accordion.stories.tsx +14 -9
- package/src/components/accordion/accordion.tsx +104 -8
- package/src/components/affix/affix.meta.md +20 -2
- package/src/components/affix/affix.stories.tsx +102 -25
- package/src/components/affix/affix.tsx +79 -9
- package/src/components/alert/alert.meta.md +44 -13
- package/src/components/alert/alert.stories.tsx +66 -21
- package/src/components/alert/alert.tsx +81 -34
- package/src/components/alert-dialog/alert-dialog.meta.md +61 -16
- package/src/components/alert-dialog/alert-dialog.stories.tsx +145 -3
- package/src/components/alert-dialog/alert-dialog.tsx +60 -13
- package/src/components/anchor/anchor.meta.md +8 -3
- package/src/components/anchor/anchor.stories.tsx +3 -3
- package/src/components/anchor/anchor.tsx +2 -2
- package/src/components/app/app.meta.md +9 -4
- package/src/components/app/app.stories.tsx +9 -7
- package/src/components/aspect-ratio/aspect-ratio.meta.md +4 -3
- package/src/components/aspect-ratio/aspect-ratio.stories.tsx +3 -3
- package/src/components/auto-complete/auto-complete.meta.md +14 -6
- package/src/components/auto-complete/auto-complete.stories.tsx +47 -4
- package/src/components/auto-complete/auto-complete.tsx +119 -71
- package/src/components/avatar/avatar.meta.md +6 -7
- package/src/components/avatar/avatar.stories.tsx +21 -3
- package/src/components/avatar/avatar.tsx +24 -23
- package/src/components/badge/badge.meta.md +10 -9
- package/src/components/badge/badge.stories.tsx +2 -2
- package/src/components/badge/badge.tsx +9 -15
- package/src/components/breadcrumb/breadcrumb.meta.md +27 -7
- package/src/components/breadcrumb/breadcrumb.stories.tsx +127 -4
- package/src/components/breadcrumb/breadcrumb.tsx +22 -8
- package/src/components/button/button.meta.md +258 -21
- package/src/components/button/button.stories.tsx +549 -41
- package/src/components/button/button.tsx +335 -33
- package/src/components/button/demo/as-child.tsx +24 -0
- package/src/components/button/demo/basic.tsx +8 -0
- package/src/components/button/demo/block.tsx +16 -0
- package/src/components/button/demo/loading.tsx +19 -0
- package/src/components/button/demo/shapes.tsx +18 -0
- package/src/components/button/demo/sizes.tsx +19 -0
- package/src/components/button/demo/variants.tsx +19 -0
- package/src/components/button/demo/with-icon.tsx +20 -0
- package/src/components/calendar/calendar.meta.md +13 -3
- package/src/components/calendar/calendar.stories.tsx +6 -6
- package/src/components/calendar/calendar.tsx +73 -8
- package/src/components/card/card.meta.md +27 -5
- package/src/components/card/card.stories.tsx +42 -3
- package/src/components/card/card.tsx +146 -63
- package/src/components/carousel/carousel.meta.md +4 -3
- package/src/components/carousel/carousel.stories.tsx +11 -6
- package/src/components/cascader/cascader.meta.md +47 -17
- package/src/components/cascader/cascader.stories.tsx +22 -10
- package/src/components/cascader/cascader.tsx +428 -85
- package/src/components/checkbox/checkbox.meta.md +75 -7
- package/src/components/checkbox/checkbox.stories.tsx +161 -3
- package/src/components/checkbox/checkbox.tsx +77 -9
- package/src/components/collapsible/collapsible.meta.md +14 -6
- package/src/components/collapsible/collapsible.stories.tsx +10 -2
- package/src/components/collapsible/collapsible.tsx +93 -6
- package/src/components/color-picker/color-picker.meta.md +12 -7
- package/src/components/color-picker/color-picker.stories.tsx +86 -7
- package/src/components/color-picker/color-picker.tsx +20 -9
- package/src/components/command/command.meta.md +29 -13
- package/src/components/command/command.stories.tsx +4 -4
- package/src/components/command/command.tsx +19 -8
- package/src/components/context-menu/context-menu.meta.md +11 -8
- package/src/components/context-menu/context-menu.stories.tsx +11 -3
- package/src/components/context-menu/context-menu.tsx +21 -8
- package/src/components/data-table/data-table.meta.md +6 -5
- package/src/components/data-table/data-table.stories.tsx +13 -6
- package/src/components/data-table/data-table.tsx +2 -2
- package/src/components/date-picker/date-picker.meta.md +88 -19
- package/src/components/date-picker/date-picker.stories.tsx +55 -5
- package/src/components/date-picker/date-picker.tsx +1489 -91
- package/src/components/descriptions/descriptions.meta.md +10 -5
- package/src/components/descriptions/descriptions.stories.tsx +3 -3
- package/src/components/descriptions/descriptions.tsx +22 -14
- package/src/components/dialog/dialog.meta.md +76 -13
- package/src/components/dialog/dialog.stories.tsx +182 -20
- package/src/components/dialog/dialog.tsx +67 -15
- package/src/components/dialog/imperative.tsx +252 -0
- package/src/components/drawer/drawer.meta.md +33 -34
- package/src/components/drawer/drawer.stories.tsx +29 -12
- package/src/components/drawer/drawer.tsx +22 -113
- package/src/components/dropdown-menu/dropdown-menu.meta.md +78 -10
- package/src/components/dropdown-menu/dropdown-menu.stories.tsx +88 -2
- package/src/components/dropdown-menu/dropdown-menu.tsx +24 -10
- package/src/components/ellipsis/ellipsis.meta.md +87 -0
- package/src/components/ellipsis/ellipsis.stories.tsx +72 -0
- package/src/components/ellipsis/ellipsis.tsx +153 -0
- package/src/components/empty/empty.meta.md +9 -4
- package/src/components/empty/empty.stories.tsx +4 -4
- package/src/components/empty/empty.tsx +10 -3
- package/src/components/field/field.meta.md +47 -9
- package/src/components/field/field.stories.tsx +385 -5
- package/src/components/field/field.tsx +263 -35
- package/src/components/filter-bar/filter-bar.meta.md +92 -0
- package/src/components/filter-bar/filter-bar.stories.tsx +1083 -0
- package/src/components/filter-bar/filter-bar.tsx +568 -0
- package/src/components/flex/flex.meta.md +54 -6
- package/src/components/flex/flex.stories.tsx +107 -20
- package/src/components/flex/flex.tsx +27 -4
- package/src/components/float-button/float-button.meta.md +8 -3
- package/src/components/float-button/float-button.stories.tsx +9 -7
- package/src/components/float-button/float-button.tsx +1 -1
- package/src/components/form/form.meta.md +39 -17
- package/src/components/form/form.stories.tsx +350 -3
- package/src/components/form/form.tsx +101 -35
- package/src/components/grid/grid.meta.md +7 -2
- package/src/components/grid/grid.stories.tsx +6 -4
- package/src/components/hover-card/hover-card.meta.md +20 -9
- package/src/components/hover-card/hover-card.stories.tsx +34 -5
- package/src/components/hover-card/hover-card.tsx +51 -13
- package/src/components/icon/DEVELOPMENT.md +809 -0
- package/src/components/icon/icon.meta.md +170 -0
- package/src/components/icon/icon.stories.tsx +344 -0
- package/src/components/icon/icon.tsx +248 -0
- package/src/components/image/image.meta.md +9 -4
- package/src/components/image/image.stories.tsx +3 -3
- package/src/components/image/image.tsx +6 -4
- package/src/components/input/demo/basic.tsx +12 -0
- package/src/components/input/demo/clearable.tsx +21 -0
- package/src/components/input/demo/show-count.tsx +18 -0
- package/src/components/input/demo/sizes.tsx +15 -0
- package/src/components/input/input.meta.md +39 -33
- package/src/components/input/input.stories.tsx +62 -35
- package/src/components/input/input.tsx +97 -98
- package/src/components/input-group/input-group.meta.md +54 -22
- package/src/components/input-group/input-group.stories.tsx +49 -16
- package/src/components/input-group/input-group.tsx +44 -8
- package/src/components/input-number/input-number.meta.md +64 -7
- package/src/components/input-number/input-number.stories.tsx +46 -8
- package/src/components/input-number/input-number.tsx +99 -26
- package/src/components/input-otp/input-otp.meta.md +4 -3
- package/src/components/input-otp/input-otp.stories.tsx +3 -3
- package/src/components/input-otp/input-otp.tsx +1 -1
- package/src/components/item/item.meta.md +8 -3
- package/src/components/item/item.stories.tsx +8 -5
- package/src/components/item/item.tsx +7 -6
- package/src/components/kbd/kbd.meta.md +13 -4
- package/src/components/kbd/kbd.stories.tsx +4 -4
- package/src/components/kbd/kbd.tsx +10 -5
- package/src/components/label/label.meta.md +18 -10
- package/src/components/label/label.stories.tsx +64 -6
- package/src/components/label/label.tsx +91 -19
- package/src/components/masonry/masonry.meta.md +8 -3
- package/src/components/masonry/masonry.stories.tsx +7 -5
- package/src/components/masonry/masonry.tsx +1 -0
- package/src/components/mentions/mentions.meta.md +36 -6
- package/src/components/mentions/mentions.stories.tsx +120 -6
- package/src/components/mentions/mentions.tsx +11 -5
- package/src/components/menubar/menubar.meta.md +30 -12
- package/src/components/menubar/menubar.stories.tsx +62 -2
- package/src/components/menubar/menubar.tsx +9 -9
- package/src/components/native-select/native-select.meta.md +8 -3
- package/src/components/native-select/native-select.stories.tsx +8 -5
- package/src/components/native-select/native-select.tsx +1 -1
- package/src/components/navigation-menu/navigation-menu.meta.md +19 -9
- package/src/components/navigation-menu/navigation-menu.stories.tsx +112 -9
- package/src/components/navigation-menu/navigation-menu.tsx +8 -4
- package/src/components/notification/notification.meta.md +52 -10
- package/src/components/notification/notification.stories.tsx +11 -9
- package/src/components/notification/notification.tsx +36 -21
- package/src/components/page-header/DEVELOPMENT.md +842 -0
- package/src/components/page-header/page-header.meta.md +208 -0
- package/src/components/page-header/page-header.stories.tsx +421 -0
- package/src/components/page-header/page-header.tsx +281 -0
- package/src/components/pagination/pagination.meta.md +140 -37
- package/src/components/pagination/pagination.stories.tsx +232 -10
- package/src/components/pagination/pagination.tsx +355 -63
- package/src/components/popconfirm/popconfirm.meta.md +9 -4
- package/src/components/popconfirm/popconfirm.stories.tsx +3 -4
- package/src/components/popconfirm/popconfirm.tsx +2 -2
- package/src/components/popover/popover.meta.md +62 -5
- package/src/components/popover/popover.stories.tsx +83 -7
- package/src/components/popover/popover.tsx +77 -28
- package/src/components/progress/progress.meta.md +38 -6
- package/src/components/progress/progress.stories.tsx +3 -3
- package/src/components/progress/progress.tsx +24 -16
- package/src/components/radio-group/radio-group.meta.md +79 -7
- package/src/components/radio-group/radio-group.stories.tsx +39 -3
- package/src/components/radio-group/radio-group.tsx +149 -18
- package/src/components/rate/rate.meta.md +35 -4
- package/src/components/rate/rate.stories.tsx +13 -5
- package/src/components/rate/rate.tsx +37 -10
- package/src/components/resizable/resizable.meta.md +7 -4
- package/src/components/resizable/resizable.stories.tsx +6 -6
- package/src/components/resizable/resizable.tsx +1 -1
- package/src/components/result/result.meta.md +7 -2
- package/src/components/result/result.stories.tsx +4 -8
- package/src/components/result/result.tsx +24 -15
- package/src/components/scroll-area/scroll-area.meta.md +4 -3
- package/src/components/scroll-area/scroll-area.stories.tsx +12 -4
- package/src/components/scroll-area/scroll-area.tsx +3 -3
- package/src/components/segmented/segmented.meta.md +7 -4
- package/src/components/segmented/segmented.stories.tsx +37 -8
- package/src/components/segmented/segmented.tsx +15 -7
- package/src/components/select/select.meta.md +197 -52
- package/src/components/select/select.stories.tsx +238 -63
- package/src/components/select/select.tsx +718 -171
- package/src/components/separator/separator.meta.md +4 -3
- package/src/components/separator/separator.stories.tsx +3 -3
- package/src/components/separator/separator.tsx +3 -7
- package/src/components/sheet/sheet.meta.md +32 -16
- package/src/components/sheet/sheet.stories.tsx +116 -10
- package/src/components/sheet/sheet.tsx +116 -29
- package/src/components/sidebar/sidebar.meta.md +37 -18
- package/src/components/sidebar/sidebar.stories.tsx +701 -29
- package/src/components/sidebar/sidebar.tsx +615 -142
- package/src/components/skeleton/skeleton.meta.md +4 -5
- package/src/components/skeleton/skeleton.stories.tsx +4 -4
- package/src/components/skeleton/skeleton.tsx +7 -7
- package/src/components/slider/slider.meta.md +57 -5
- package/src/components/slider/slider.stories.tsx +58 -6
- package/src/components/slider/slider.tsx +154 -13
- package/src/components/sonner/sonner.meta.md +58 -7
- package/src/components/sonner/sonner.stories.tsx +78 -5
- package/src/components/sonner/sonner.tsx +137 -8
- package/src/components/spinner/spinner.meta.md +62 -13
- package/src/components/spinner/spinner.stories.tsx +66 -14
- package/src/components/spinner/spinner.tsx +111 -9
- package/src/components/statistic/statistic.meta.md +7 -2
- package/src/components/statistic/statistic.stories.tsx +3 -7
- package/src/components/statistic/statistic.tsx +5 -6
- package/src/components/steps/steps.meta.md +18 -4
- package/src/components/steps/steps.stories.tsx +43 -3
- package/src/components/steps/steps.tsx +15 -12
- package/src/components/switch/switch.meta.md +51 -5
- package/src/components/switch/switch.stories.tsx +6 -6
- package/src/components/switch/switch.tsx +109 -41
- package/src/components/table/table.meta.md +17 -6
- package/src/components/table/table.stories.tsx +10 -5
- package/src/components/table/table.tsx +4 -4
- package/src/components/tabs/tabs.meta.md +38 -25
- package/src/components/tabs/tabs.stories.tsx +111 -25
- package/src/components/tabs/tabs.tsx +125 -54
- package/src/components/tag/tag.meta.md +105 -40
- package/src/components/tag/tag.stories.tsx +189 -16
- package/src/components/tag/tag.tsx +222 -21
- package/src/components/textarea/textarea.meta.md +35 -19
- package/src/components/textarea/textarea.stories.tsx +32 -6
- package/src/components/textarea/textarea.tsx +33 -9
- package/src/components/time-picker/time-picker.meta.md +124 -32
- package/src/components/time-picker/time-picker.stories.tsx +85 -15
- package/src/components/time-picker/time-picker.tsx +913 -61
- package/src/components/timeline/timeline.meta.md +14 -6
- package/src/components/timeline/timeline.stories.tsx +37 -7
- package/src/components/timeline/timeline.tsx +35 -14
- package/src/components/toggle/toggle.meta.md +5 -4
- package/src/components/toggle/toggle.stories.tsx +4 -4
- package/src/components/toggle/toggle.tsx +4 -3
- package/src/components/toggle-group/toggle-group.meta.md +5 -4
- package/src/components/toggle-group/toggle-group.stories.tsx +3 -3
- package/src/components/toggle-group/toggle-group.tsx +2 -2
- package/src/components/tooltip/tooltip.meta.md +55 -5
- package/src/components/tooltip/tooltip.stories.tsx +42 -5
- package/src/components/tooltip/tooltip.tsx +81 -21
- package/src/components/tour/tour.meta.md +9 -4
- package/src/components/tour/tour.stories.tsx +3 -3
- package/src/components/tour/tour.tsx +4 -4
- package/src/components/transfer/transfer.meta.md +11 -6
- package/src/components/transfer/transfer.stories.tsx +4 -8
- package/src/components/transfer/transfer.tsx +28 -21
- package/src/components/tree/tree.meta.md +63 -5
- package/src/components/tree/tree.stories.tsx +31 -12
- package/src/components/tree/tree.tsx +9 -8
- package/src/components/tree-select/tree-select.meta.md +59 -8
- package/src/components/tree-select/tree-select.stories.tsx +3 -3
- package/src/components/tree-select/tree-select.tsx +42 -7
- package/src/components/typography/typography.meta.md +61 -14
- package/src/components/typography/typography.stories.tsx +12 -11
- package/src/components/typography/typography.tsx +43 -28
- package/src/components/upload/upload.meta.md +49 -4
- package/src/components/upload/upload.stories.tsx +72 -12
- package/src/components/upload/upload.tsx +170 -37
- package/src/components/watermark/watermark.meta.md +7 -2
- package/src/components/watermark/watermark.stories.tsx +101 -9
- package/src/components/watermark/watermark.tsx +1 -0
- package/src/hooks/use-breakpoint.ts +117 -0
- package/src/hooks/use-debounce-callback.ts +52 -0
- package/src/hooks/use-mobile.ts +23 -0
- package/src/stories/theme-tokens.stories.tsx +747 -0
- package/src/utils/trigger-input.ts +53 -0
- package/src/components/button-group/button-group.meta.md +0 -92
- package/src/components/button-group/button-group.stories.tsx +0 -90
- package/src/components/button-group/button-group.tsx +0 -75
- package/src/components/combobox/combobox.meta.md +0 -93
- package/src/components/combobox/combobox.stories.tsx +0 -55
- package/src/components/combobox/combobox.tsx +0 -130
- package/src/components/space/space.meta.md +0 -94
- package/src/components/space/space.stories.tsx +0 -94
- package/src/components/space/space.tsx +0 -106
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import type { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
4
|
+
|
|
5
|
+
/* ──────────────────────────────────────────────────────────────────────────
|
|
6
|
+
* Theme Tokens · Token Atlas
|
|
7
|
+
*
|
|
8
|
+
* 一份"设计档案"式的运行时变量浏览器:读取当前 Storybook 主题(由顶栏的
|
|
9
|
+
* Theme 切换器决定)下,Tailwind v4 实际解析的颜色 / 圆角 / 阴影 / 字号 /
|
|
10
|
+
* 字重值。所有数值都通过 getComputedStyle 在浏览器里实时拿;切换 variant
|
|
11
|
+
* 时无需刷新页面,数值会跟着变。
|
|
12
|
+
*
|
|
13
|
+
* 不直接读 token 文件,因为各 variant 的覆盖面不同(opentrek 没显式声明
|
|
14
|
+
* shadow / radius 档,而是吃 Tailwind v4 默认值)。统一通过"在隐藏 probe
|
|
15
|
+
* 上挂 utility class → 读 computed style"获取真实生效值,展示与业务实际
|
|
16
|
+
* 渲染完全一致的结果。
|
|
17
|
+
* ────────────────────────────────────────────────────────────────────────── */
|
|
18
|
+
|
|
19
|
+
// ── 数据维度 ────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const COLOR_GROUPS: Array<{ label: string; tokens: string[] }> = [
|
|
22
|
+
{
|
|
23
|
+
label: 'Brand · 品牌',
|
|
24
|
+
tokens: ['primary', 'primary-foreground', 'accent', 'accent-foreground'],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: 'Semantic · 语义',
|
|
28
|
+
tokens: [
|
|
29
|
+
'destructive',
|
|
30
|
+
'destructive-foreground',
|
|
31
|
+
'success',
|
|
32
|
+
'success-foreground',
|
|
33
|
+
'warning',
|
|
34
|
+
'warning-foreground',
|
|
35
|
+
'info',
|
|
36
|
+
'info-foreground',
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
label: 'Text · 文字',
|
|
41
|
+
tokens: ['foreground', 'muted-foreground'],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'Border · 边框',
|
|
45
|
+
// eslint-disable-next-line teamix-evo/no-bare-border -- these are token slugs, not classNames
|
|
46
|
+
tokens: ['border', 'input', 'ring'],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: 'Surface · 背景',
|
|
50
|
+
tokens: [
|
|
51
|
+
'background',
|
|
52
|
+
'card',
|
|
53
|
+
'card-foreground',
|
|
54
|
+
'muted',
|
|
55
|
+
'popover',
|
|
56
|
+
'popover-foreground',
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const RADIUS_SCALE: Array<{ key: string; cls: string }> = [
|
|
62
|
+
{ key: 'sm', cls: 'rounded-sm' },
|
|
63
|
+
{ key: 'md', cls: 'rounded-md' },
|
|
64
|
+
{ key: 'lg', cls: 'rounded-lg' },
|
|
65
|
+
{ key: 'xl', cls: 'rounded-xl' },
|
|
66
|
+
{ key: '2xl', cls: 'rounded-2xl' },
|
|
67
|
+
{ key: 'full', cls: 'rounded-full' },
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const SHADOW_SCALE: Array<{ key: string; cls: string }> = [
|
|
71
|
+
{ key: 'sm', cls: 'shadow-sm' },
|
|
72
|
+
{ key: 'md', cls: 'shadow-md' },
|
|
73
|
+
{ key: 'lg', cls: 'shadow-lg' },
|
|
74
|
+
{ key: 'xl', cls: 'shadow-xl' },
|
|
75
|
+
{ key: '2xl', cls: 'shadow-2xl' },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const TEXT_SIZES: Array<{
|
|
79
|
+
key: string;
|
|
80
|
+
cls: string;
|
|
81
|
+
px: number;
|
|
82
|
+
semantic?: string;
|
|
83
|
+
}> = [
|
|
84
|
+
{ key: 'xs', cls: 'text-xs', px: 12, semantic: 'body-1 · 正文' },
|
|
85
|
+
{ key: 'sm', cls: 'text-sm', px: 14, semantic: 'body-2 · 小标题 / 强调正文' },
|
|
86
|
+
{
|
|
87
|
+
key: 'base',
|
|
88
|
+
cls: 'text-base',
|
|
89
|
+
px: 16,
|
|
90
|
+
semantic: 'subhead · 卡片标题 / 区块标题',
|
|
91
|
+
},
|
|
92
|
+
{ key: 'lg', cls: 'text-lg', px: 18, semantic: 'title · 页面主标题' },
|
|
93
|
+
{ key: 'xl', cls: 'text-xl', px: 20, semantic: 'headline · 数据指标-中' },
|
|
94
|
+
{ key: '2xl', cls: 'text-2xl', px: 24, semantic: 'display-1 · 页头标题' },
|
|
95
|
+
{ key: '3xl', cls: 'text-3xl', px: 28, semantic: 'display-2 · 数据指标-大' },
|
|
96
|
+
{ key: '4xl', cls: 'text-4xl', px: 36, semantic: '超大(备用 / 营销)' },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const TEXT_DISPLAY_TOKENS: Array<{
|
|
100
|
+
varName: string;
|
|
101
|
+
px: number;
|
|
102
|
+
semantic: string;
|
|
103
|
+
}> = [
|
|
104
|
+
{ varName: '--text-display-3', px: 32, semantic: 'display-3 · 运营标题' },
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
const TEXT_WEIGHTS: Array<{ key: string; cls: string; weight: number }> = [
|
|
108
|
+
{ key: 'normal', cls: 'font-normal', weight: 400 },
|
|
109
|
+
{ key: 'medium', cls: 'font-medium', weight: 500 },
|
|
110
|
+
{ key: 'semibold', cls: 'font-semibold', weight: 600 },
|
|
111
|
+
{ key: 'bold', cls: 'font-bold', weight: 700 },
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
// ── 运行时读取 ─────────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
/** 触发 token 重计算的信号:监听 <html> 的 data-theme 变化 + window resize。 */
|
|
117
|
+
function useThemeTick(): number {
|
|
118
|
+
const [tick, setTick] = useState(0);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const bump = () => setTick((n) => n + 1);
|
|
121
|
+
const observer = new MutationObserver(bump);
|
|
122
|
+
observer.observe(document.documentElement, {
|
|
123
|
+
attributes: true,
|
|
124
|
+
attributeFilter: ['data-theme', 'class'],
|
|
125
|
+
});
|
|
126
|
+
window.addEventListener('resize', bump);
|
|
127
|
+
return () => {
|
|
128
|
+
observer.disconnect();
|
|
129
|
+
window.removeEventListener('resize', bump);
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
return tick;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** 读取 :root 上指定 CSS 变量的当前值(去前后空白)。SSR 安全。 */
|
|
136
|
+
function readVar(name: string): string {
|
|
137
|
+
if (typeof window === 'undefined') return '';
|
|
138
|
+
const v = getComputedStyle(document.documentElement).getPropertyValue(name);
|
|
139
|
+
return v.trim();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** 把任意 css 长度字符串(rem/px/em)规范化成同时显示原值 + 像素值。 */
|
|
143
|
+
function fmtLength(raw: string): string {
|
|
144
|
+
if (!raw) return '—';
|
|
145
|
+
if (raw.endsWith('px')) return raw;
|
|
146
|
+
if (raw.endsWith('rem')) {
|
|
147
|
+
const n = parseFloat(raw);
|
|
148
|
+
if (!Number.isNaN(n)) return `${raw} · ${Math.round(n * 16)}px`;
|
|
149
|
+
}
|
|
150
|
+
return raw;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── 视觉原子 ───────────────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 档案条目 — Token Atlas 的核心展示单元。
|
|
157
|
+
* 左侧:变量名(mono) + 当前值(mono dim);右侧:视觉样本。
|
|
158
|
+
* 一行一条,横向规则线分隔,刻意像档案/印刷品。
|
|
159
|
+
*/
|
|
160
|
+
function AtlasRow({
|
|
161
|
+
name,
|
|
162
|
+
value,
|
|
163
|
+
preview,
|
|
164
|
+
children,
|
|
165
|
+
}: {
|
|
166
|
+
name: string;
|
|
167
|
+
value: string;
|
|
168
|
+
preview?: ReactNode;
|
|
169
|
+
children?: ReactNode;
|
|
170
|
+
}) {
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
className="group grid items-center gap-x-6 border-t border-border/60 py-5 transition-colors hover:bg-muted/40"
|
|
174
|
+
style={{ gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1.4fr)' }}
|
|
175
|
+
>
|
|
176
|
+
<div className="min-w-0 flex flex-col gap-1">
|
|
177
|
+
<code className="font-mono text-xs tracking-tight text-foreground">
|
|
178
|
+
{name}
|
|
179
|
+
</code>
|
|
180
|
+
<code className="font-mono text-xxs uppercase tracking-widest text-muted-foreground/80">
|
|
181
|
+
{value || '—'}
|
|
182
|
+
</code>
|
|
183
|
+
</div>
|
|
184
|
+
<div className="min-w-0">{preview ?? children}</div>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function GroupBlock({
|
|
190
|
+
index,
|
|
191
|
+
label,
|
|
192
|
+
caption,
|
|
193
|
+
children,
|
|
194
|
+
}: {
|
|
195
|
+
index: string;
|
|
196
|
+
label: string;
|
|
197
|
+
caption?: string;
|
|
198
|
+
children: ReactNode;
|
|
199
|
+
}) {
|
|
200
|
+
return (
|
|
201
|
+
<section className="mb-16">
|
|
202
|
+
<header className="mb-4 flex items-baseline gap-4 border-b border-foreground pb-3">
|
|
203
|
+
<span className="font-mono text-xxs tracking-widest text-muted-foreground">
|
|
204
|
+
{index}
|
|
205
|
+
</span>
|
|
206
|
+
<h3 className="text-lg font-medium tracking-tight text-foreground">
|
|
207
|
+
{label}
|
|
208
|
+
</h3>
|
|
209
|
+
{caption ? (
|
|
210
|
+
<span className="ml-auto font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
211
|
+
{caption}
|
|
212
|
+
</span>
|
|
213
|
+
) : null}
|
|
214
|
+
</header>
|
|
215
|
+
<div>{children}</div>
|
|
216
|
+
</section>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function AtlasFrame({
|
|
221
|
+
title,
|
|
222
|
+
subtitle,
|
|
223
|
+
children,
|
|
224
|
+
}: {
|
|
225
|
+
title: string;
|
|
226
|
+
subtitle: string;
|
|
227
|
+
children: ReactNode;
|
|
228
|
+
}) {
|
|
229
|
+
const themeRef = useRef<string>('');
|
|
230
|
+
useEffect(() => {
|
|
231
|
+
themeRef.current =
|
|
232
|
+
document.documentElement.getAttribute('data-theme') || 'opentrek';
|
|
233
|
+
}, []);
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<div
|
|
237
|
+
className="mx-auto w-full max-w-5xl px-10 py-12"
|
|
238
|
+
style={{
|
|
239
|
+
fontFamily: 'var(--font-sans)',
|
|
240
|
+
color: 'var(--color-foreground)',
|
|
241
|
+
background: 'var(--color-background)',
|
|
242
|
+
}}
|
|
243
|
+
>
|
|
244
|
+
{/* 页眉:档案抬头 + 元信息条 */}
|
|
245
|
+
<header className="mb-12 flex items-end justify-between gap-8 border-b-2 border-foreground pb-6">
|
|
246
|
+
<div>
|
|
247
|
+
<p className="font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
248
|
+
Teamix Evo · Token Atlas
|
|
249
|
+
</p>
|
|
250
|
+
<h2 className="mt-3 text-3xl font-medium tracking-tight">{title}</h2>
|
|
251
|
+
<p className="mt-2 max-w-prose text-sm leading-relaxed text-muted-foreground">
|
|
252
|
+
{subtitle}
|
|
253
|
+
</p>
|
|
254
|
+
</div>
|
|
255
|
+
<ThemeBadge />
|
|
256
|
+
</header>
|
|
257
|
+
{children}
|
|
258
|
+
<footer className="mt-16 border-t border-border pt-4 font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
259
|
+
values resolved at runtime · getComputedStyle(:root)
|
|
260
|
+
</footer>
|
|
261
|
+
</div>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** 顶栏当前主题徽标 — 跟着 data-theme 实时刷新。 */
|
|
266
|
+
function ThemeBadge() {
|
|
267
|
+
const tick = useThemeTick();
|
|
268
|
+
const theme = useMemo(() => {
|
|
269
|
+
if (typeof document === 'undefined') return 'opentrek';
|
|
270
|
+
void tick;
|
|
271
|
+
return document.documentElement.getAttribute('data-theme') || 'opentrek';
|
|
272
|
+
}, [tick]);
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<div className="flex items-center gap-2 border border-foreground px-3 py-1.5">
|
|
276
|
+
<span
|
|
277
|
+
className="inline-block size-2 rounded-full"
|
|
278
|
+
style={{ background: 'var(--color-primary)' }}
|
|
279
|
+
aria-hidden
|
|
280
|
+
/>
|
|
281
|
+
<span className="font-mono text-xxs uppercase tracking-widest">
|
|
282
|
+
{theme}
|
|
283
|
+
</span>
|
|
284
|
+
</div>
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ── Color story ────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
function ColorSwatch({ token }: { token: string }) {
|
|
291
|
+
const tick = useThemeTick();
|
|
292
|
+
const varName = `--color-${token}`;
|
|
293
|
+
const value = useMemo(() => readVar(varName), [varName, tick]);
|
|
294
|
+
const isForeground = token.endsWith('-foreground');
|
|
295
|
+
|
|
296
|
+
// 给 foreground token 配对 chip:在对应的容器色块上绘制实际文字
|
|
297
|
+
const pairBg = isForeground
|
|
298
|
+
? `--color-${token.replace(/-foreground$/, '')}`
|
|
299
|
+
: null;
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<AtlasRow name={varName} value={value}>
|
|
303
|
+
<div className="flex items-stretch gap-3">
|
|
304
|
+
<div
|
|
305
|
+
className="h-14 w-24 border border-border/60"
|
|
306
|
+
style={{ background: `var(${varName})` }}
|
|
307
|
+
aria-label={`${token} swatch`}
|
|
308
|
+
/>
|
|
309
|
+
{pairBg ? (
|
|
310
|
+
<div
|
|
311
|
+
className="flex h-14 flex-1 items-center justify-center border border-border/60 px-3"
|
|
312
|
+
style={{ background: `var(${pairBg})`, color: `var(${varName})` }}
|
|
313
|
+
>
|
|
314
|
+
<span className="font-mono text-xs uppercase tracking-widest">
|
|
315
|
+
on {pairBg.replace('--color-', '')}
|
|
316
|
+
</span>
|
|
317
|
+
</div>
|
|
318
|
+
) : (
|
|
319
|
+
<div className="flex h-14 flex-1 items-center border border-dashed border-border/60 px-4">
|
|
320
|
+
<span className="font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
321
|
+
bg-{token}
|
|
322
|
+
</span>
|
|
323
|
+
</div>
|
|
324
|
+
)}
|
|
325
|
+
</div>
|
|
326
|
+
</AtlasRow>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export const Color: StoryObj = {
|
|
331
|
+
name: '01 · Color',
|
|
332
|
+
parameters: {
|
|
333
|
+
layout: 'fullscreen',
|
|
334
|
+
controls: { disable: true },
|
|
335
|
+
docs: {
|
|
336
|
+
description: {
|
|
337
|
+
story:
|
|
338
|
+
'颜色档案:按品牌 / 语义 / 文字 / 边框 / 背景五组陈列。每条同时展示变量名、HSL/HEX 原值,以及与之配对的 foreground 在容器色上的真实可读性。',
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
render: () => (
|
|
343
|
+
<AtlasFrame
|
|
344
|
+
title="Color · 颜色档案"
|
|
345
|
+
subtitle="所有语义色槽位的当前生效值。foreground 直接绘制在对应容器色上,可一眼判断对比度是否过关。"
|
|
346
|
+
>
|
|
347
|
+
{COLOR_GROUPS.map((group, i) => (
|
|
348
|
+
<GroupBlock
|
|
349
|
+
key={group.label}
|
|
350
|
+
index={`§ 01.${String(i + 1).padStart(2, '0')}`}
|
|
351
|
+
label={group.label}
|
|
352
|
+
caption={`${group.tokens.length} tokens`}
|
|
353
|
+
>
|
|
354
|
+
{group.tokens.map((t) => (
|
|
355
|
+
<ColorSwatch key={t} token={t} />
|
|
356
|
+
))}
|
|
357
|
+
</GroupBlock>
|
|
358
|
+
))}
|
|
359
|
+
</AtlasFrame>
|
|
360
|
+
),
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// ── Corner story ──────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
/** 把 utility class 挂到 probe 上读取 computed border-radius。 */
|
|
366
|
+
function useRadiusFromClass(cls: string): string {
|
|
367
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
|
368
|
+
const tick = useThemeTick();
|
|
369
|
+
const [val, setVal] = useState<string>('');
|
|
370
|
+
useLayoutEffect(() => {
|
|
371
|
+
if (!ref.current) return;
|
|
372
|
+
const cs = getComputedStyle(ref.current);
|
|
373
|
+
setVal(cs.borderTopLeftRadius);
|
|
374
|
+
}, [cls, tick]);
|
|
375
|
+
return val;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function RadiusRow({ k, cls }: { k: string; cls: string }) {
|
|
379
|
+
const probeRef = useRef<HTMLDivElement | null>(null);
|
|
380
|
+
const tick = useThemeTick();
|
|
381
|
+
const [value, setValue] = useState('');
|
|
382
|
+
useLayoutEffect(() => {
|
|
383
|
+
if (!probeRef.current) return;
|
|
384
|
+
setValue(getComputedStyle(probeRef.current).borderTopLeftRadius);
|
|
385
|
+
}, [cls, tick]);
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<AtlasRow name={`rounded-${k}`} value={fmtLength(value)}>
|
|
389
|
+
<div className="flex items-center gap-4">
|
|
390
|
+
{/* 隐形 probe 用来取值,与可见样本同一 class 同一渲染路径 */}
|
|
391
|
+
<div ref={probeRef} className={cls} style={{ display: 'none' }} />
|
|
392
|
+
<div
|
|
393
|
+
className={`flex h-16 w-16 items-center justify-center border-2 border-foreground ${cls}`}
|
|
394
|
+
aria-label={`rounded-${k} sample`}
|
|
395
|
+
>
|
|
396
|
+
<span className="font-mono text-xxs uppercase tracking-widest">
|
|
397
|
+
{k}
|
|
398
|
+
</span>
|
|
399
|
+
</div>
|
|
400
|
+
<div
|
|
401
|
+
className={`flex h-16 w-32 items-end justify-end border border-border bg-muted p-2 ${cls}`}
|
|
402
|
+
>
|
|
403
|
+
<span className="font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
404
|
+
surface
|
|
405
|
+
</span>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
</AtlasRow>
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/** 组件专属圆角条目 — 通过自定义 var 读取(没有则隐藏整条)。 */
|
|
413
|
+
function ComponentRadiusRow({
|
|
414
|
+
name,
|
|
415
|
+
varName,
|
|
416
|
+
}: {
|
|
417
|
+
name: string;
|
|
418
|
+
varName: string;
|
|
419
|
+
}) {
|
|
420
|
+
const tick = useThemeTick();
|
|
421
|
+
const value = useMemo(() => readVar(varName), [varName, tick]);
|
|
422
|
+
if (!value) return null;
|
|
423
|
+
return (
|
|
424
|
+
<AtlasRow name={varName} value={fmtLength(value)}>
|
|
425
|
+
<div
|
|
426
|
+
className="flex h-16 w-32 items-center justify-center border-2 border-dashed border-foreground bg-muted"
|
|
427
|
+
style={{ borderRadius: `var(${varName})` }}
|
|
428
|
+
>
|
|
429
|
+
<span className="font-mono text-xxs uppercase tracking-widest">
|
|
430
|
+
{name}
|
|
431
|
+
</span>
|
|
432
|
+
</div>
|
|
433
|
+
</AtlasRow>
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export const Corner: StoryObj = {
|
|
438
|
+
name: '02 · Corner',
|
|
439
|
+
parameters: {
|
|
440
|
+
layout: 'fullscreen',
|
|
441
|
+
controls: { disable: true },
|
|
442
|
+
docs: {
|
|
443
|
+
description: {
|
|
444
|
+
story:
|
|
445
|
+
'圆角档案:Tailwind v4 实际生效的 rounded-{sm/md/lg/xl/2xl/full} 几何形态。值随主题变化(opentrek 圆润 0.5rem,uni-manager 锐利 0–12px 阶梯)。',
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
render: () => (
|
|
450
|
+
<AtlasFrame
|
|
451
|
+
title="Corner · 圆角档案"
|
|
452
|
+
subtitle="每条展示一个 Tailwind 圆角档位的实际曲率。左侧粗描边样本看几何形态,右侧填色样本看落到 surface 上的视觉重量。"
|
|
453
|
+
>
|
|
454
|
+
<GroupBlock
|
|
455
|
+
index="§ 02.01"
|
|
456
|
+
label="Scale · 阶梯"
|
|
457
|
+
caption={`${RADIUS_SCALE.length} steps`}
|
|
458
|
+
>
|
|
459
|
+
{RADIUS_SCALE.map((r) => (
|
|
460
|
+
<RadiusRow key={r.key} k={r.key} cls={r.cls} />
|
|
461
|
+
))}
|
|
462
|
+
</GroupBlock>
|
|
463
|
+
<GroupBlock
|
|
464
|
+
index="§ 02.02"
|
|
465
|
+
label="Component · 组件专属"
|
|
466
|
+
caption="optional"
|
|
467
|
+
>
|
|
468
|
+
<ComponentRadiusRow name="button" varName="--radius-button" />
|
|
469
|
+
<ComponentRadiusRow name="dialog" varName="--radius-dialog" />
|
|
470
|
+
<ComponentRadiusRow name="tag" varName="--radius-tag" />
|
|
471
|
+
<BaseRadiusRow />
|
|
472
|
+
</GroupBlock>
|
|
473
|
+
</AtlasFrame>
|
|
474
|
+
),
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
/** Token base 圆角(--radius)— 各 variant 必定声明,值是其余 rounded-* 的基准。 */
|
|
478
|
+
function BaseRadiusRow() {
|
|
479
|
+
const tick = useThemeTick();
|
|
480
|
+
const value = useMemo(() => readVar('--radius'), [tick]);
|
|
481
|
+
return (
|
|
482
|
+
<AtlasRow name="--radius" value={fmtLength(value)}>
|
|
483
|
+
<div
|
|
484
|
+
className="flex h-16 w-32 items-center justify-center border-2 border-foreground bg-card"
|
|
485
|
+
style={{ borderRadius: 'var(--radius)' }}
|
|
486
|
+
>
|
|
487
|
+
<span className="font-mono text-xxs uppercase tracking-widest">
|
|
488
|
+
base
|
|
489
|
+
</span>
|
|
490
|
+
</div>
|
|
491
|
+
</AtlasRow>
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// ── Shadow story ──────────────────────────────────────────────────────────
|
|
496
|
+
|
|
497
|
+
function ShadowRow({ k, cls }: { k: string; cls: string }) {
|
|
498
|
+
const probeRef = useRef<HTMLDivElement | null>(null);
|
|
499
|
+
const tick = useThemeTick();
|
|
500
|
+
const [value, setValue] = useState('');
|
|
501
|
+
useLayoutEffect(() => {
|
|
502
|
+
if (!probeRef.current) return;
|
|
503
|
+
setValue(getComputedStyle(probeRef.current).boxShadow);
|
|
504
|
+
}, [cls, tick]);
|
|
505
|
+
|
|
506
|
+
return (
|
|
507
|
+
<AtlasRow name={`shadow-${k}`} value={value || 'none'}>
|
|
508
|
+
<div className="flex items-center gap-6 py-3">
|
|
509
|
+
<div ref={probeRef} className={cls} style={{ display: 'none' }} />
|
|
510
|
+
<div
|
|
511
|
+
className={`flex h-20 w-32 items-center justify-center border border-border bg-card ${cls}`}
|
|
512
|
+
>
|
|
513
|
+
<span className="font-mono text-xxs uppercase tracking-widest text-muted-foreground">
|
|
514
|
+
shadow-{k}
|
|
515
|
+
</span>
|
|
516
|
+
</div>
|
|
517
|
+
<div className="flex-1 text-xs leading-relaxed text-muted-foreground">
|
|
518
|
+
<p className="break-all font-mono">{value || '—'}</p>
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
</AtlasRow>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export const Shadow: StoryObj = {
|
|
526
|
+
name: '03 · Shadow',
|
|
527
|
+
parameters: {
|
|
528
|
+
layout: 'fullscreen',
|
|
529
|
+
controls: { disable: true },
|
|
530
|
+
docs: {
|
|
531
|
+
description: {
|
|
532
|
+
story:
|
|
533
|
+
'阴影档案:5 档 shadow utility 落在 card surface 上的真实视觉。右侧展开 box-shadow 完整 CSS 串,便于核对偏移 / 模糊 / 透明度。',
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
render: () => (
|
|
538
|
+
<AtlasFrame
|
|
539
|
+
title="Shadow · 阴影档案"
|
|
540
|
+
subtitle="每档以 card 容器为载体,展示阴影对悬浮层级的真实贡献。展开值显示完整 box-shadow 串。"
|
|
541
|
+
>
|
|
542
|
+
<GroupBlock
|
|
543
|
+
index="§ 03.01"
|
|
544
|
+
label="Elevation · 阶梯"
|
|
545
|
+
caption={`${SHADOW_SCALE.length} steps`}
|
|
546
|
+
>
|
|
547
|
+
{SHADOW_SCALE.map((s) => (
|
|
548
|
+
<ShadowRow key={s.key} k={s.key} cls={s.cls} />
|
|
549
|
+
))}
|
|
550
|
+
</GroupBlock>
|
|
551
|
+
</AtlasFrame>
|
|
552
|
+
),
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
// ── Text story ────────────────────────────────────────────────────────────
|
|
556
|
+
|
|
557
|
+
function TextSizeRow({
|
|
558
|
+
k,
|
|
559
|
+
cls,
|
|
560
|
+
px,
|
|
561
|
+
semantic,
|
|
562
|
+
}: {
|
|
563
|
+
k: string;
|
|
564
|
+
cls: string;
|
|
565
|
+
px: number;
|
|
566
|
+
semantic?: string;
|
|
567
|
+
}) {
|
|
568
|
+
const probeRef = useRef<HTMLDivElement | null>(null);
|
|
569
|
+
const tick = useThemeTick();
|
|
570
|
+
const [value, setValue] = useState('');
|
|
571
|
+
useLayoutEffect(() => {
|
|
572
|
+
if (!probeRef.current) return;
|
|
573
|
+
setValue(getComputedStyle(probeRef.current).fontSize);
|
|
574
|
+
}, [cls, tick]);
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<AtlasRow
|
|
578
|
+
name={`text-${k}`}
|
|
579
|
+
value={`${value || `${px}px`} · spec ${px}px${
|
|
580
|
+
semantic ? ` · ${semantic}` : ''
|
|
581
|
+
}`}
|
|
582
|
+
>
|
|
583
|
+
<div className={`flex items-baseline gap-4 ${cls}`}>
|
|
584
|
+
<span ref={probeRef} className={cls} style={{ display: 'none' }} />
|
|
585
|
+
<span className="font-medium tracking-tight">敏捷的设计系统</span>
|
|
586
|
+
<span className="text-muted-foreground">— Token Atlas</span>
|
|
587
|
+
</div>
|
|
588
|
+
</AtlasRow>
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/** 语义字号 token 行 — 如 --text-display-3,独立于 Tailwind 默认 8 档之外。 */
|
|
593
|
+
function TextSemanticRow({
|
|
594
|
+
varName,
|
|
595
|
+
px,
|
|
596
|
+
semantic,
|
|
597
|
+
}: {
|
|
598
|
+
varName: string;
|
|
599
|
+
px: number;
|
|
600
|
+
semantic: string;
|
|
601
|
+
}) {
|
|
602
|
+
const tick = useThemeTick();
|
|
603
|
+
const value = useMemo(() => readVar(varName), [varName, tick]);
|
|
604
|
+
if (!value) return null;
|
|
605
|
+
return (
|
|
606
|
+
<AtlasRow
|
|
607
|
+
name={varName}
|
|
608
|
+
value={`${fmtLength(value)} · spec ${px}px · ${semantic}`}
|
|
609
|
+
>
|
|
610
|
+
<div
|
|
611
|
+
className="flex items-baseline gap-4"
|
|
612
|
+
style={{ fontSize: `var(${varName})` }}
|
|
613
|
+
>
|
|
614
|
+
<span className="font-medium tracking-tight">敏捷的设计系统</span>
|
|
615
|
+
<span className="text-muted-foreground">— Token Atlas</span>
|
|
616
|
+
</div>
|
|
617
|
+
</AtlasRow>
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function TextWeightRow({
|
|
622
|
+
k,
|
|
623
|
+
cls,
|
|
624
|
+
weight,
|
|
625
|
+
}: {
|
|
626
|
+
k: string;
|
|
627
|
+
cls: string;
|
|
628
|
+
weight: number;
|
|
629
|
+
}) {
|
|
630
|
+
const probeRef = useRef<HTMLSpanElement | null>(null);
|
|
631
|
+
const tick = useThemeTick();
|
|
632
|
+
const [value, setValue] = useState('');
|
|
633
|
+
useLayoutEffect(() => {
|
|
634
|
+
if (!probeRef.current) return;
|
|
635
|
+
setValue(getComputedStyle(probeRef.current).fontWeight);
|
|
636
|
+
}, [cls, tick]);
|
|
637
|
+
|
|
638
|
+
return (
|
|
639
|
+
<AtlasRow name={`font-${k}`} value={`${value || weight} · ${weight}`}>
|
|
640
|
+
<div className="flex items-baseline gap-4 text-xl">
|
|
641
|
+
<span ref={probeRef} className={`${cls} tracking-tight`}>
|
|
642
|
+
The quick brown fox · 敏捷的棕狐狸
|
|
643
|
+
</span>
|
|
644
|
+
</div>
|
|
645
|
+
</AtlasRow>
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function FontFamilyRow({ token, sample }: { token: string; sample: string }) {
|
|
650
|
+
const tick = useThemeTick();
|
|
651
|
+
const value = useMemo(() => readVar(token), [token, tick]);
|
|
652
|
+
return (
|
|
653
|
+
<AtlasRow name={token} value={value || '—'}>
|
|
654
|
+
<p
|
|
655
|
+
className="text-lg tracking-tight"
|
|
656
|
+
style={{ fontFamily: `var(${token})` }}
|
|
657
|
+
>
|
|
658
|
+
{sample}
|
|
659
|
+
</p>
|
|
660
|
+
</AtlasRow>
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export const Text: StoryObj = {
|
|
665
|
+
name: '04 · Text',
|
|
666
|
+
parameters: {
|
|
667
|
+
layout: 'fullscreen',
|
|
668
|
+
controls: { disable: true },
|
|
669
|
+
docs: {
|
|
670
|
+
description: {
|
|
671
|
+
story:
|
|
672
|
+
'文字档案:Tailwind 8 档字号(含语义映射 body-1/body-2/subhead/title/headline/display-1/display-2) + display-3 语义 token + 4 档字重 + sans/mono 字体族。所有数值由 probe 元素的 computed font-size / font-weight 反推,与业务实际渲染完全一致。',
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
render: () => (
|
|
677
|
+
<AtlasFrame
|
|
678
|
+
title="Text · 文字档案"
|
|
679
|
+
subtitle="字号、字重、字体族三组。中英文混排样本同时验证拉丁字形和 CJK 字形的视觉协调。"
|
|
680
|
+
>
|
|
681
|
+
<GroupBlock
|
|
682
|
+
index="§ 04.01"
|
|
683
|
+
label="Size · 字号"
|
|
684
|
+
caption={`${TEXT_SIZES.length + TEXT_DISPLAY_TOKENS.length} steps`}
|
|
685
|
+
>
|
|
686
|
+
{TEXT_SIZES.map((s) => (
|
|
687
|
+
<TextSizeRow
|
|
688
|
+
key={s.key}
|
|
689
|
+
k={s.key}
|
|
690
|
+
cls={s.cls}
|
|
691
|
+
px={s.px}
|
|
692
|
+
semantic={s.semantic}
|
|
693
|
+
/>
|
|
694
|
+
))}
|
|
695
|
+
{TEXT_DISPLAY_TOKENS.map((t) => (
|
|
696
|
+
<TextSemanticRow
|
|
697
|
+
key={t.varName}
|
|
698
|
+
varName={t.varName}
|
|
699
|
+
px={t.px}
|
|
700
|
+
semantic={t.semantic}
|
|
701
|
+
/>
|
|
702
|
+
))}
|
|
703
|
+
</GroupBlock>
|
|
704
|
+
<GroupBlock
|
|
705
|
+
index="§ 04.02"
|
|
706
|
+
label="Weight · 字重"
|
|
707
|
+
caption={`${TEXT_WEIGHTS.length} steps`}
|
|
708
|
+
>
|
|
709
|
+
{TEXT_WEIGHTS.map((w) => (
|
|
710
|
+
<TextWeightRow key={w.key} k={w.key} cls={w.cls} weight={w.weight} />
|
|
711
|
+
))}
|
|
712
|
+
</GroupBlock>
|
|
713
|
+
<GroupBlock index="§ 04.03" label="Family · 字体族">
|
|
714
|
+
<FontFamilyRow
|
|
715
|
+
token="--font-sans"
|
|
716
|
+
sample="Sans · 设计系统的呼吸感 0123456789"
|
|
717
|
+
/>
|
|
718
|
+
<FontFamilyRow
|
|
719
|
+
token="--font-mono"
|
|
720
|
+
sample="Mono · const tokens = readAtlas() // 0O1lI"
|
|
721
|
+
/>
|
|
722
|
+
</GroupBlock>
|
|
723
|
+
</AtlasFrame>
|
|
724
|
+
),
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
// ── Meta ───────────────────────────────────────────────────────────────────
|
|
728
|
+
|
|
729
|
+
// 注:消费 _unused 仅为避免 ts-noUnusedLocals 抱怨(部分工具方法仅在条件分支用)。
|
|
730
|
+
const _unused: CSSProperties | undefined = undefined;
|
|
731
|
+
void _unused;
|
|
732
|
+
void useRadiusFromClass;
|
|
733
|
+
|
|
734
|
+
const meta: Meta = {
|
|
735
|
+
title: '设计系统 · Design System/Theme Tokens',
|
|
736
|
+
parameters: {
|
|
737
|
+
layout: 'fullscreen',
|
|
738
|
+
docs: {
|
|
739
|
+
description: {
|
|
740
|
+
component:
|
|
741
|
+
'Token Atlas — 当前主题(由顶栏 Theme 切换器决定)下,Tailwind v4 实际解析的颜色 / 圆角 / 阴影 / 字号 / 字重值的运行时档案。所有值通过 getComputedStyle 实时读取;切换 variant 数值随之刷新。四个 story 分别覆盖 Color / Corner / Shadow / Text 四个维度。',
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
export default meta;
|