@wycm9527/morganite 0.0.0-stage → 0.3.4

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/LICENSE +33 -0
  3. package/README.md +62 -2
  4. package/bridge/base.css +138 -0
  5. package/bridge/echarts.d.ts +44 -0
  6. package/bridge/echarts.js +217 -0
  7. package/bridge/element-plus.css +785 -0
  8. package/bridge/fonts.css +124 -0
  9. package/bridge/icon-merge.d.ts +5 -0
  10. package/bridge/icon-merge.js +103 -0
  11. package/bridge/iconpark.config.ts +75 -0
  12. package/bridge/iconpark.css +29 -0
  13. package/bridge/react/ConfirmBar.tsx +43 -0
  14. package/bridge/react/EChart.tsx +45 -0
  15. package/bridge/react/PageHeader.tsx +24 -0
  16. package/bridge/react/SectionHeader.tsx +17 -0
  17. package/bridge/react/StatCard.tsx +33 -0
  18. package/bridge/react/TableSkeleton.tsx +11 -0
  19. package/bridge/react/TrendChart.tsx +8 -0
  20. package/bridge/recipes.css +444 -0
  21. package/bridge/shadcn-globals.css +6 -0
  22. package/bridge/shadcn.css +238 -0
  23. package/bridge/vue/ConfirmBar.vue +23 -0
  24. package/bridge/vue/EChart.vue +43 -0
  25. package/bridge/vue/KitchenSink.vue +507 -0
  26. package/bridge/vue/PageHeader.vue +23 -0
  27. package/bridge/vue/SectionHeader.vue +15 -0
  28. package/bridge/vue/StatCard.vue +33 -0
  29. package/bridge/vue/TableSkeleton.vue +14 -0
  30. package/bridge/vue/TrendChart.vue +11 -0
  31. package/bridge/vue/confirm.js +18 -0
  32. package/bridge/vue/inlineConfirm.js +21 -0
  33. package/design-system/AUDIT.md +277 -0
  34. package/design-system/DESIGN.md +176 -0
  35. package/design-system/contrast.md +71 -0
  36. package/design-system/preview/drawer.html +38 -0
  37. package/design-system/preview/form.html +40 -0
  38. package/design-system/preview/index.html +21 -0
  39. package/design-system/preview/preview.css +190 -0
  40. package/design-system/preview/preview.local.css +17 -0
  41. package/design-system/preview/shell.html +50 -0
  42. package/design-system/scope-map.json +4 -0
  43. package/design-system/style-dictionary.config.mjs +172 -0
  44. package/design-system/theme-map.json +16 -0
  45. package/design-system/themes/dark/THEME.md +29 -0
  46. package/design-system/themes/dark/tokens/semantic.tokens.json +354 -0
  47. package/design-system/token-board.html +137 -0
  48. package/design-system/tokens/primitives.tokens.json +1898 -0
  49. package/design-system/tokens/semantic.tokens.json +1080 -0
  50. package/design-system.json +217 -0
  51. package/migration/roles.json +743 -0
  52. package/package.json +82 -4
  53. package/templates/AGENTS.md +13 -0
  54. package/templates/KitchenSink.tsx +179 -0
  55. package/templates/accept.config.mjs +28 -0
  56. package/templates/entry-css.css +6 -0
  57. package/templates/entry-element-plus.css +9 -0
  58. package/templates/entry-shadcn.css +7 -0
  59. package/templates/notes-css.md +4 -0
  60. package/templates/notes-element-plus.md +7 -0
  61. package/templates/notes-shadcn.md +7 -0
@@ -0,0 +1,238 @@
1
+ /* shadcn/ui + Tailwind v4 桥接本体:把 shadcn 的契约变量全部指向设计系统的语义 token,并按 data-slot 接管组件。
2
+ 本文件只 @import 同目录的 fonts.css(数字字体):项目的 globals.css 先 @import "tailwindcss" 与 design-system/dist/index.css,再 @import 本文件
3
+ (从包名:@import "@wycm9527/morganite/bridge/shadcn.css")。不需要写 .dark 块——dist/themes/dark.css 一变,这里全部跟着变;暗色在 <html> 上加 class dark。 */
4
+
5
+ @import "./fonts.css";
6
+
7
+ :root {
8
+ --background: var(--color-bg-page);
9
+ --foreground: var(--color-text-primary);
10
+ --card: var(--color-bg-surface);
11
+ --card-foreground: var(--color-text-primary);
12
+ --popover: var(--color-bg-elevated);
13
+ --popover-foreground: var(--color-text-primary);
14
+
15
+ --primary: var(--color-action-primary);
16
+ --primary-foreground: var(--color-text-on-primary);
17
+ --secondary: var(--color-bg-hover);
18
+ --secondary-foreground: var(--color-text-primary);
19
+ --muted: var(--color-bg-subtle);
20
+ --muted-foreground: var(--color-text-secondary);
21
+ --accent: var(--color-bg-selected);
22
+ --accent-foreground: var(--color-text-selected);
23
+ --destructive: var(--color-action-danger);
24
+ --destructive-foreground: var(--color-text-on-danger);
25
+ /* 非 shadcn 契约、但业务常用的角色,直接透出 */
26
+ --status-neutral: var(--color-status-neutral);
27
+ --status-neutral-bg: var(--color-status-neutral-bg);
28
+ --data-increase: var(--color-data-increase);
29
+ --data-decrease: var(--color-data-decrease);
30
+
31
+ --border: var(--color-border-default);
32
+ --input: var(--color-border-input);
33
+ --ring: var(--color-border-focus);
34
+
35
+ --sidebar: var(--color-bg-sidebar);
36
+ --sidebar-foreground: var(--color-text-sidebar);
37
+ --sidebar-primary: var(--color-action-primary);
38
+ --sidebar-primary-foreground: var(--color-text-on-primary);
39
+ --sidebar-accent: var(--color-bg-sidebar-selected);
40
+ --sidebar-accent-foreground: var(--color-text-sidebar-selected);
41
+ --sidebar-border: var(--color-border-sidebar);
42
+ --sidebar-ring: var(--color-border-focus);
43
+
44
+ --chart-1: var(--color-chart-1);
45
+ --chart-2: var(--color-chart-2);
46
+ --chart-3: var(--color-chart-3);
47
+ --chart-4: var(--color-chart-4);
48
+ --chart-5: var(--color-chart-5);
49
+
50
+ --radius: var(--radius-md);
51
+ }
52
+
53
+ @theme inline {
54
+ --color-background: var(--background);
55
+ --color-foreground: var(--foreground);
56
+ --color-card: var(--card);
57
+ --color-card-foreground: var(--card-foreground);
58
+ --color-popover: var(--popover);
59
+ --color-popover-foreground: var(--popover-foreground);
60
+ --color-primary: var(--primary);
61
+ --color-primary-foreground: var(--primary-foreground);
62
+ --color-secondary: var(--secondary);
63
+ --color-secondary-foreground: var(--secondary-foreground);
64
+ --color-muted: var(--muted);
65
+ --color-muted-foreground: var(--muted-foreground);
66
+ --color-accent: var(--accent);
67
+ --color-accent-foreground: var(--accent-foreground);
68
+ --color-destructive: var(--destructive);
69
+ --color-destructive-foreground: var(--destructive-foreground);
70
+ --color-status-neutral: var(--status-neutral);
71
+ --color-status-neutral-bg: var(--status-neutral-bg);
72
+ --color-data-increase: var(--data-increase);
73
+ --color-data-decrease: var(--data-decrease);
74
+ --color-border: var(--border);
75
+ --color-input: var(--input);
76
+ --color-ring: var(--ring);
77
+ --color-sidebar: var(--sidebar);
78
+ --color-sidebar-foreground: var(--sidebar-foreground);
79
+ --color-sidebar-primary: var(--sidebar-primary);
80
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
81
+ --color-sidebar-accent: var(--sidebar-accent);
82
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
83
+ --color-sidebar-border: var(--sidebar-border);
84
+ --color-sidebar-ring: var(--sidebar-ring);
85
+ --color-chart-1: var(--chart-1);
86
+ --color-chart-2: var(--chart-2);
87
+ --color-chart-3: var(--chart-3);
88
+ --color-chart-4: var(--chart-4);
89
+ --color-chart-5: var(--chart-5);
90
+ /* shadcn 的派生圆角刚好等于种子的 radius.sm / md / lg / xl(6 / 8 / 12 / 16);@theme inline 不会输出同名变量,与 dist 里的 --radius-* 不冲突 */
91
+ --radius-sm: calc(var(--radius) - 2px);
92
+ --radius-md: var(--radius);
93
+ --radius-lg: calc(var(--radius) + 4px);
94
+ --radius-xl: calc(var(--radius) + 8px);
95
+
96
+ /* 结构层:字体与字号阶梯。text-sm 与 text-base 都是正文 14px(按钮、输入框、菜单),text-xs 是 12px;表格另接小号正文(见下方「表格」) */
97
+ --font-sans: var(--font-family-body);
98
+ --font-mono: var(--font-family-code);
99
+ --text-xs: var(--text-small-size);
100
+ --text-sm: var(--text-body-size);
101
+ --text-base: var(--text-body-size);
102
+ --text-xs--line-height: var(--text-body-line-height);
103
+ --text-sm--line-height: var(--text-body-line-height);
104
+ --text-base--line-height: var(--text-body-line-height);
105
+ --text-lg: var(--text-title-size);
106
+ --text-xl: var(--text-heading-size);
107
+ --text-2xl: var(--text-display-size);
108
+
109
+ /* 动效 */
110
+ --ease-standard: var(--motion-easing-standard);
111
+ --ease-enter: var(--motion-easing-enter);
112
+ --ease-exit: var(--motion-easing-exit);
113
+ --default-transition-duration: var(--motion-duration-fast);
114
+ --default-transition-timing-function: var(--motion-easing-standard);
115
+
116
+ /* 阴影:拆分式 elevation token 组合成 Tailwind 的 shadow-2xs / xs / sm / md / lg(shadcn 的按钮、输入框用 shadow-xs,不映射就是写死的 #0000000d) */
117
+ --shadow-2xs: 0 var(--elevation-card-y) var(--elevation-card-blur) var(--elevation-card-color);
118
+ --shadow-xs: 0 var(--elevation-card-y) var(--elevation-card-blur) var(--elevation-card-color);
119
+ --shadow-sm: 0 var(--elevation-card-y) var(--elevation-card-blur) var(--elevation-card-color);
120
+ --shadow-md: 0 var(--elevation-popover-y) var(--elevation-popover-blur) var(--elevation-popover-color);
121
+ --shadow-lg: 0 var(--elevation-modal-y) var(--elevation-modal-blur) var(--elevation-modal-color);
122
+ }
123
+
124
+ /* Tailwind v4 的 preflight 把所有元素的 border-color 设为 currentColor:shadcn 官方 globals 用 @layer base 把它改回 --border,这里同样要做,
125
+ 否则只写 `border` 的 Card / Alert / Input 会拿文字色当边线(暗色下白边)。outline-color 同理接焦点环 */
126
+ @layer base {
127
+ *, ::before, ::after, ::backdrop { border-color: var(--border); outline-color: var(--color-focus-ring); }
128
+ body { background-color: var(--background); color: var(--foreground); }
129
+ }
130
+
131
+ /* ── 组件级接管:shadcn 组件里写死或指向 --primary 的地方,按 DESIGN 配方改到 token。
132
+ 全部按 data-slot 选择,不依赖 Tailwind 类名;放在无 layer 的位置,天然压过 @layer utilities。 ── */
133
+ /* 全站描边 0.6px(border.width.default,用户决定):Tailwind 的 border / border-* 写死 1px,按类名接到 token——只改本来就带这些类的元素,不会给别的元素加线 */
134
+ [class~="border"] { border-width: var(--border-width-default); }
135
+ [class~="border-t"] { border-top-width: var(--border-width-default); }
136
+ [class~="border-b"] { border-bottom-width: var(--border-width-default); }
137
+ [class~="border-l"] { border-left-width: var(--border-width-default); }
138
+ [class~="border-r"] { border-right-width: var(--border-width-default); }
139
+ /* lucide 图标描边:屏幕上恒为 icon.stroke.width(1.4px),不随图标尺寸缩放(lucide 默认 24 网格里的 2,16px 图标上约 1.3px)。
140
+ icon-merge.js 把多段描边合并成一个形状再一次上色,半透明的图标色在交叠处不叠深;合并后原来的各段设为透明,仍留在原处(IconPark 的同类规则在 iconpark.css) */
141
+ svg.lucide :is(path, circle, ellipse, line, polyline, polygon, rect) { stroke-width: var(--icon-stroke-width); vector-effect: non-scaling-stroke; }
142
+ svg.lucide[data-icon-merged] > :not([data-icon-merge], defs, mask, clipPath, pattern, symbol) { opacity: 0; }
143
+ [data-slot="radio-group-indicator"] svg.lucide * { stroke-width: inherit; vector-effect: none; } /* 单选圆点是实心点、不是线性图标,按屏幕粗细描边会把点撑大,保持 lucide 原样 */
144
+ /* 控件三档高度:shadcn 用 h-8 / h-9 / h-10(32 / 36 / 40)表达 sm / default / lg,接到 control.height.sm / md / lg(24 / 32 / 40);size-9 是图标按钮 */
145
+ [data-slot="button"].h-8, [data-slot="select-trigger"][data-size="sm"] { height: var(--control-height-sm); }
146
+ [data-slot="button"].h-9, [data-slot="input"].h-9, [data-slot="select-trigger"][data-size="default"] { height: var(--control-height-md); }
147
+ [data-slot="button"].h-10 { height: var(--control-height-lg); }
148
+ [data-slot="button"].size-9 { width: var(--control-height-md); height: var(--control-height-md); }
149
+ /* 勾选 / 单选 / 开关的视觉尺寸 16 × 16,命中区外扩到 control.hit-min(24)——用伪元素,不改视觉 */
150
+ [data-slot="checkbox"], [data-slot="radio-group-item"], [data-slot="switch"] { position: relative; }
151
+ [data-slot="checkbox"]::after, [data-slot="radio-group-item"]::after, [data-slot="switch"]::after { content: ""; position: absolute; inset: calc((var(--control-hit-min) - var(--size-icon-md)) / -2); }
152
+ /* 焦点:按钮等沿用 shadcn 的模型(边线换 --ring + 3px 环),环色换成 focus.ring;输入类控件只换边线、不出环(第二条);有 data-slot 的元素不再叠全局 outline */
153
+ [data-slot]:focus-visible { --tw-ring-color: var(--color-focus-ring); }
154
+ [data-slot="input"]:focus-visible, [data-slot="textarea"]:focus-visible, [data-slot="select-trigger"]:focus-visible { --tw-ring-color: transparent; } /* 输入类控件只换边线(--ring),不出环 */
155
+ /* 提示框:不是主色面——反转块(bg.inverse + text.inverse),同 Element 桥接 */
156
+ [data-slot="tooltip-content"] { background-color: var(--color-bg-inverse); color: var(--color-text-inverse); }
157
+ [data-slot="tooltip-content"] svg { background-color: var(--color-bg-inverse); fill: var(--color-bg-inverse); }
158
+ /* 遮罩:shadcn 写死 bg-black/50,接到 bg.overlay */
159
+ [data-slot="dialog-overlay"], [data-slot="alert-dialog-overlay"], [data-slot="sheet-overlay"], [data-slot="drawer-overlay"] { background-color: var(--color-bg-overlay); }
160
+ /* 骨架屏:不是 accent(选中底),是 bg.skeleton;进度条轨道不是 primary 20% 洗色,是 bg.selected(DESIGN 进度条配方,同 Element 桥接) */
161
+ [data-slot="skeleton"] { background-color: var(--color-bg-skeleton); }
162
+ [data-slot="progress"] { background-color: var(--color-bg-selected); }
163
+ /* 卡片标题:同配方 .card-head h2(shadcn 默认随卡片的正文色、字号跟外层) */
164
+ [data-slot="card-title"] { font-size: var(--text-body-size); font-weight: var(--text-weight-strong); color: var(--color-text-card-title); }
165
+ /* 表格:表头 bg.subtle + text.secondary,行 hover bg.hover,选中行与悬停同色 bg.hover、再悬停 bg.selected-hover(shadcn 默认表头正文色、hover 用 muted 50%) */
166
+ [data-slot="table-header"] { background-color: var(--color-bg-subtle); }
167
+ [data-slot="table-head"] { color: var(--color-text-secondary); font-weight: var(--text-weight-label); }
168
+ /* 单元格内边距接 table.cell.padding-x / -y(20 / 14):shadcn 默认 p-2 = 8px、表头 h-10,内容贴边没有呼吸感;表头高度改由内边距决定 */
169
+ [data-slot="table-cell"], [data-slot="table-head"] { padding: var(--table-cell-padding-y) var(--table-cell-padding-x); height: auto; }
170
+ [data-slot="table-cell"]:has([role="checkbox"]), [data-slot="table-head"]:has([role="checkbox"]) { padding-right: 0; } /* 勾选列保持 shadcn 的紧右边 */
171
+ [data-slot="table-row"]:hover { background-color: var(--color-bg-hover); }
172
+ [data-slot="table-row"][data-state="selected"] { background-color: var(--color-bg-hover); } /* 选中行与悬停同色,再悬停深一档 */
173
+ [data-slot="table-row"][data-state="selected"]:hover { background-color: var(--color-bg-selected-hover); }
174
+ [data-slot="table-row"], [data-slot="table-header"] tr { border-color: var(--color-border-default); }
175
+ /* 标签页(shadcn 的 Tabs 形态是分段选择器):外壳 bg.subtle + 发丝线,选中项是白色内胶囊(action.selected),不加投影 */
176
+ [data-slot="tabs-list"] { background-color: var(--color-bg-subtle); border: var(--border-width-default) solid var(--color-border-default); }
177
+ [data-slot="tabs-trigger"][data-state="active"] { background-color: var(--color-action-selected); color: var(--color-text-on-selected); box-shadow: none; border-color: transparent; }
178
+ [data-slot="tabs-trigger"]:not([data-state="active"]) { color: var(--color-text-secondary); }
179
+ /* 标签页内容面板是 Tab 停靠点(Radix 给 tabindex=0),shadcn 写了 outline-none:焦点必须可见 */
180
+ [data-slot="tabs-content"]:focus-visible { outline: var(--border-width-active) solid var(--color-border-focus); outline-offset: var(--spacing-0-5); }
181
+ [data-slot="tabs-trigger"]:focus-visible { outline-width: var(--border-width-active); }
182
+ /* 提示条 destructive 的描述文字是 destructive 的 90% 洗色(白底 3.3:1),改为 text.danger 实色 */
183
+ [data-slot="alert"].text-destructive [data-slot="alert-description"] { color: var(--color-text-danger); }
184
+ [data-slot="alert"].text-destructive { color: var(--color-text-danger); } /* 标题与图标同理:destructive 是危险填充色,暗色深底上只有 3.3:1 */
185
+ /* 弹窗面板是浮层表面(DESIGN「弹窗」= bg.elevated):shadcn 写的 bg-background 在本系统映射页面底,面板会发灰 */
186
+ [data-slot="dialog-content"], [data-slot="alert-dialog-content"], [data-slot="sheet-content"], [data-slot="drawer-content"] { background-color: var(--color-bg-elevated); }
187
+ /* 描边 / 幽灵按钮(outline / ghost):DESIGN「次要按钮 / 文字按钮」——白底 + 控件描边,悬停只换底、文字保持正文色。
188
+ shadcn 悬停用 accent(本系统是「选中」语义:浅底 + 深金字),暗色描边取 --input(填充式下透明);按钮没有变体属性,用稳定的类名片段区分。
189
+ 不限 data-slot="button":确认弹窗的「取消」也是这套类名但没有 data-slot;asChild 包按钮的 Popover / Tooltip / Dialog 触发器,data-slot 会被触发器的覆盖 */
190
+ [class*="bg-background"][class*="hover:bg-accent"] { background-color: var(--color-bg-surface); border-color: var(--color-border-strong); }
191
+ [class*="bg-background"][class*="hover:bg-accent"]:hover { background-color: var(--color-action-secondary-hover); color: var(--color-text-primary); }
192
+ [class*="hover:bg-accent"]:not([class*="bg-background"]):hover { background-color: var(--color-action-quiet-hover); color: var(--color-text-primary); }
193
+ /* 按钮:常规字重、图标间距 spacing.1-5(与效果预览一致);shadcn 是 font-medium + gap-2 */
194
+ [data-slot="button"] { font-weight: var(--text-weight-label); gap: var(--spacing-1-5); }
195
+ [data-slot="button"].h-9:has(> svg):not(.size-9) { padding-inline: var(--spacing-4); } /* 带图标的按钮不收窄(shadcn has-[>svg]:px-3),与无图标按钮同宽留白 */
196
+ [data-slot="button"].h-8:has(> svg) { padding-inline: var(--spacing-3); }
197
+ /* 表格正文是小号字(DESIGN「表格」);按钮、输入框、菜单是正文字号 */
198
+ [data-slot="table"] { font-size: var(--text-body-sm-size); }
199
+ /* 输入类控件:填充式(bg.input,静息边线透明,悬停 border.strong,聚焦 border.focus,正文字号)。shadcn 是透明底 + --input 描边,本系统 --input 透明,框会完全看不出 */
200
+ [data-slot="input"], [data-slot="textarea"], [data-slot="select-trigger"] { background-color: var(--color-bg-input); border-width: var(--border-width-default); border-color: var(--color-border-input); color: var(--color-text-primary); font-size: var(--text-body-size); box-shadow: none; }
201
+ [data-slot="input"]:hover:not(:disabled):not(:focus-visible), [data-slot="textarea"]:hover:not(:disabled):not(:focus-visible), [data-slot="select-trigger"]:hover:not(:disabled):not(:focus-visible) { border-color: var(--color-border-strong); }
202
+ [data-slot="input"]:focus-visible, [data-slot="textarea"]:focus-visible, [data-slot="select-trigger"]:focus-visible { border-color: var(--color-border-focus); }
203
+ [data-slot="input"][aria-invalid="true"], [data-slot="textarea"][aria-invalid="true"], [data-slot="select-trigger"][aria-invalid="true"] { border-color: var(--color-status-error); }
204
+ [data-slot="input"]::placeholder, [data-slot="textarea"]::placeholder, [data-slot="select-trigger"][data-placeholder] { color: var(--color-text-placeholder); }
205
+ [data-slot="select-trigger"] > svg { color: var(--color-icon-muted); opacity: 1; }
206
+ [data-slot="input"]:disabled, [data-slot="textarea"]:disabled, [data-slot="select-trigger"]:disabled { opacity: var(--opacity-disabled); }
207
+ /* 勾选 / 单选 / 开关:未选描 border.strong × border.width.control、表面色底(填充式下 --input 透明,框与轨道会看不见);选中走主色,滑块用 control.knob */
208
+ [data-slot="checkbox"], [data-slot="radio-group-item"] { border-width: var(--border-width-control); border-color: var(--color-border-strong); background-color: var(--color-bg-surface); box-shadow: none; }
209
+ [data-slot="checkbox"] { border-radius: var(--radius-xs); }
210
+ [data-slot="checkbox"]:focus-visible, [data-slot="radio-group-item"]:focus-visible, [data-slot="switch"]:focus-visible { outline: var(--border-width-active) solid var(--color-border-focus); outline-offset: 1px; } /* 上面去掉了 shadcn 的阴影,它的焦点环也画在阴影上,改用外环(同 Element 桥接) */
211
+ [data-slot="checkbox"][data-state="checked"], [data-slot="checkbox"][data-state="indeterminate"] { background-color: var(--color-action-primary); border-color: var(--color-action-primary); color: var(--color-text-on-primary); }
212
+ [data-slot="radio-group-item"][data-state="checked"] { border-color: var(--color-action-primary); }
213
+ [data-slot="switch"][data-state="unchecked"] { background-color: var(--color-border-strong); }
214
+ [data-slot="switch"][data-state="checked"] { background-color: var(--color-action-primary); }
215
+ [data-slot="switch-thumb"] { background-color: var(--color-control-knob); }
216
+ /* 菜单 / 下拉项:hover / focus 用 bg.hover(accent 是「选中」语义,留给 aria-selected / data-state=checked) */
217
+ [data-slot="dropdown-menu-item"]:focus, [data-slot="select-item"]:focus, [data-slot="dropdown-menu-checkbox-item"]:focus, [data-slot="dropdown-menu-radio-item"]:focus, [data-slot="dropdown-menu-sub-trigger"]:focus { background-color: var(--color-bg-hover); color: var(--color-text-primary); }
218
+ [data-slot="select-item"][data-state="checked"] { color: var(--color-text-selected); font-weight: var(--text-weight-label); }
219
+ /* 分页:当前页金底白字(用户 2026-10-09 统一,同 Element 与纯 CSS 配方);shadcn 默认是 outline 描边块 */
220
+ [data-slot="pagination-link"][data-active="true"], [data-slot="pagination-link"][aria-current="page"] { background-color: var(--color-action-primary); border-color: var(--color-action-primary); color: var(--color-text-on-primary); font-weight: var(--text-weight-strong); }
221
+ [data-slot="pagination-link"][data-active="true"]:hover, [data-slot="pagination-link"][aria-current="page"]:hover { background-color: var(--color-action-primary-hover); color: var(--color-text-on-primary); }
222
+
223
+ /* ── 手机(≤ 768,layout.breakpoint.mobile 的字面镜像):浮层全宽贴底,与 Element 桥接同一形态 ── */
224
+ @media (max-width: 768px) {
225
+ [data-slot="dialog-content"], [data-slot="alert-dialog-content"] { top: auto; bottom: 0; left: 0; translate: none; transform: none; max-width: 100vw; width: 100vw; border-radius: var(--radius-xl) var(--radius-xl) 0 0; max-height: 85dvh; overflow: auto; }
226
+ [data-slot="sheet-content"] { width: 100vw !important; max-width: 100vw; }
227
+ }
228
+
229
+ /* 键盘焦点兜底与减少动态效果,与 element-plus.css 同一份约定(shadcn 组件自带 data-slot,走上面的 ring 模型,不再叠 outline) */
230
+ :where(a, button, [role="button"], [role="menuitem"], [role="tab"], summary, [tabindex]:not([tabindex="-1"]):not(input, textarea, select)):not([data-slot]):focus-visible { outline: var(--border-width-active) solid var(--color-border-focus); outline-offset: 1px; }
231
+ @media (prefers-reduced-motion: reduce) {
232
+ :root { --motion-duration-fast: 0ms; --motion-duration-normal: 0ms; --motion-duration-slow: 0ms; }
233
+ *, *::before, *::after { transition-duration: 0s !important; animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; }
234
+ }
235
+
236
+ /* 文字渲染基线,与 element-plus.css 同一份约定:不要给 body 加 Tailwind 的 antialiased(Mac 上把中文削细一档);表单控件继承字体 */
237
+ html { -webkit-font-smoothing: auto; -moz-osx-font-smoothing: auto; }
238
+ button, input, select, textarea { font-family: inherit; }
@@ -0,0 +1,23 @@
1
+ <script setup>
2
+ // 浮层内的原位确认条(种子配方):抽屉 / 弹窗里要确认「丢弃修改 / 确定关闭」时,不再叠第二层 MessageBox,
3
+ // 而是把浮层底部替换成一条确认区域——一句话 + 「留下」(次要,自动聚焦)+ 确认(主按钮;不可逆动作用 danger)。
4
+ // Esc 在条内 = 留下。配合 useInlineConfirm():ask() 返回 Promise<boolean>,用法与 confirmLeave 一样。
5
+ import { onMounted, ref } from 'vue'
6
+ const props = defineProps({
7
+ message: { type: String, required: true },
8
+ confirmText: { type: String, default: '确定离开' },
9
+ cancelText: { type: String, default: '留下' },
10
+ danger: { type: Boolean, default: false }
11
+ })
12
+ const emit = defineEmits(['confirm', 'cancel'])
13
+ const cancelRef = ref()
14
+ onMounted(() => { cancelRef.value?.$el?.focus?.() })
15
+ </script>
16
+
17
+ <template>
18
+ <div class="confirm-bar" role="alertdialog" aria-live="assertive" :aria-label="message" @keydown.esc.stop.prevent="emit('cancel')">
19
+ <span class="msg">{{ message }}</span>
20
+ <el-button ref="cancelRef" @click="emit('cancel')">{{ cancelText }}</el-button>
21
+ <el-button :type="danger ? 'danger' : 'primary'" @click="emit('confirm')">{{ confirmText }}</el-button>
22
+ </div>
23
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup>
2
+ // 通用图表容器(种子配方组件):option 是一个函数,参数是图表 token(见桥接 chartTokens)与 echarts 实例(供 areaGradient 等配方使用)。
3
+ // 轴、网格、图例、提示框、折线 / 柱 / 饼的默认形态来自桥接主题(运行时读 token),页面只写数据与布局,不写颜色、线型。
4
+ // 亮 / 暗切换靠监听 <html class="dark"> 的变化重新注册主题并重建实例(ECharts 的主题在 init 时定格)——不依赖项目的状态管理。
5
+ // 数据变化:option 函数在 watchEffect 里执行,函数体内读到的响应式数据(ref / reactive)自动成为依赖,变了就重新 setOption——页面不需要 key 重建。
6
+ // 柱子端头(起点直角、末端圆角,堆叠只圆最外一段)在这里统一套 withBarEnds,图例开关后重算——页面不写 borderRadius。
7
+ // 高度由外层 .chart / .chart.tall 类(recipes.css)用 token 表达。
8
+ import { onMounted, onBeforeUnmount, ref, watchEffect } from 'vue'
9
+ import * as echarts from 'echarts/core'
10
+ import { LineChart, BarChart, PieChart, HeatmapChart } from 'echarts/charts'
11
+ import { GridComponent, TooltipComponent, LegendComponent, VisualMapComponent } from 'echarts/components'
12
+ import { CanvasRenderer } from 'echarts/renderers'
13
+ import { registerTheme, chartTokens, withBarEnds } from '../echarts.js'
14
+
15
+ echarts.use([LineChart, BarChart, PieChart, HeatmapChart, GridComponent, TooltipComponent, LegendComponent, VisualMapComponent, CanvasRenderer])
16
+
17
+ const props = defineProps({ option: { type: Function, required: true } })
18
+ const el = ref(); let chart
19
+
20
+ const build = (selected) => withBarEnds(props.option(chartTokens(), echarts), { selected })
21
+ const apply = () => { if (chart) chart.setOption(build(), true) }
22
+ // 图例关掉一个堆叠序列后,最外一段换了序列;只合并 series,图例的开关状态不动
23
+ const relegend = ({ selected }) => { const { series } = build(selected); if (series) chart.setOption({ series }) }
24
+ function render() {
25
+ if (!el.value) return
26
+ const name = registerTheme(echarts)
27
+ if (chart) chart.dispose()
28
+ chart = echarts.init(el.value, name)
29
+ for (const type of ['legendselectchanged', 'legendselectall', 'legendinverseselect']) chart.on(type, relegend)
30
+ apply()
31
+ }
32
+ let ro, mo, stop
33
+ onMounted(() => {
34
+ render()
35
+ stop = watchEffect(apply) // 追踪 option 函数里读到的响应式数据
36
+ ro = new ResizeObserver(() => chart && chart.resize()); ro.observe(el.value)
37
+ mo = new MutationObserver(() => requestAnimationFrame(render)); mo.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
38
+ })
39
+ onBeforeUnmount(() => { stop && stop(); ro && ro.disconnect(); mo && mo.disconnect(); chart && chart.dispose() })
40
+ defineExpose({ render })
41
+ </script>
42
+
43
+ <template><div ref="el" class="chart"></div></template>