@zhang_jifan/fanui 2.1.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.
Files changed (127) hide show
  1. package/ACCESSIBILITY.md +61 -0
  2. package/CHANGELOG.md +112 -0
  3. package/COMMERCIAL-LICENSE.md +71 -0
  4. package/DESIGN-SYSTEM.md +294 -0
  5. package/LICENSE +661 -0
  6. package/README.md +348 -0
  7. package/USAGE.md +1926 -0
  8. package/_index.scss +20 -0
  9. package/dist/fanui.cjs +15 -0
  10. package/dist/fanui.css +28209 -0
  11. package/dist/fanui.d.ts +353 -0
  12. package/dist/fanui.esm.js +71 -0
  13. package/dist/fanui.js +4782 -0
  14. package/dist/fanui.min.css +1 -0
  15. package/dist/fanui.mjs +71 -0
  16. package/dist/modules/fanui-advanced.css +830 -0
  17. package/dist/modules/fanui-advanced.min.css +129 -0
  18. package/dist/modules/fanui-alert.css +575 -0
  19. package/dist/modules/fanui-alert.min.css +122 -0
  20. package/dist/modules/fanui-badge.css +426 -0
  21. package/dist/modules/fanui-badge.min.css +77 -0
  22. package/dist/modules/fanui-base.css +1394 -0
  23. package/dist/modules/fanui-base.min.css +236 -0
  24. package/dist/modules/fanui-blog.css +762 -0
  25. package/dist/modules/fanui-blog.min.css +108 -0
  26. package/dist/modules/fanui-button.css +526 -0
  27. package/dist/modules/fanui-button.min.css +56 -0
  28. package/dist/modules/fanui-card.css +291 -0
  29. package/dist/modules/fanui-card.min.css +52 -0
  30. package/dist/modules/fanui-choice.css +539 -0
  31. package/dist/modules/fanui-choice.min.css +83 -0
  32. package/dist/modules/fanui-dashboard.css +1057 -0
  33. package/dist/modules/fanui-dashboard.min.css +160 -0
  34. package/dist/modules/fanui-data.css +687 -0
  35. package/dist/modules/fanui-data.min.css +125 -0
  36. package/dist/modules/fanui-feedback.css +432 -0
  37. package/dist/modules/fanui-feedback.min.css +79 -0
  38. package/dist/modules/fanui-form.css +779 -0
  39. package/dist/modules/fanui-form.min.css +123 -0
  40. package/dist/modules/fanui-glass.css +497 -0
  41. package/dist/modules/fanui-glass.min.css +46 -0
  42. package/dist/modules/fanui-input-plus.css +799 -0
  43. package/dist/modules/fanui-input-plus.min.css +124 -0
  44. package/dist/modules/fanui-interactive.css +960 -0
  45. package/dist/modules/fanui-interactive.min.css +138 -0
  46. package/dist/modules/fanui-layout.css +4399 -0
  47. package/dist/modules/fanui-layout.min.css +1172 -0
  48. package/dist/modules/fanui-marketing.css +1125 -0
  49. package/dist/modules/fanui-marketing.min.css +144 -0
  50. package/dist/modules/fanui-nav.css +458 -0
  51. package/dist/modules/fanui-nav.min.css +76 -0
  52. package/dist/modules/fanui-overlay.css +547 -0
  53. package/dist/modules/fanui-overlay.min.css +91 -0
  54. package/dist/modules/fanui-table.css +290 -0
  55. package/dist/modules/fanui-table.min.css +62 -0
  56. package/dist/modules/fanui-tabs.css +333 -0
  57. package/dist/modules/fanui-tabs.min.css +58 -0
  58. package/dist/modules/fanui-themes.css +2728 -0
  59. package/dist/modules/fanui-themes.min.css +40 -0
  60. package/dist/modules/fanui-tokens.css +587 -0
  61. package/dist/modules/fanui-tokens.min.css +3 -0
  62. package/dist/modules/fanui-utilities.css +6557 -0
  63. package/dist/modules/fanui-utilities.min.css +1762 -0
  64. package/dist/modules/sizes.json +103 -0
  65. package/docs/MIGRATION-v2.md +179 -0
  66. package/examples/01-script-tag.html +149 -0
  67. package/examples/02-react/README.md +39 -0
  68. package/examples/02-react/index.html +12 -0
  69. package/examples/02-react/package.json +20 -0
  70. package/examples/02-react/src/App.jsx +108 -0
  71. package/examples/02-react/src/main.jsx +13 -0
  72. package/examples/02-react/src/useFanUI.js +87 -0
  73. package/examples/02-react/vite.config.js +6 -0
  74. package/examples/03-vue3/README.md +67 -0
  75. package/examples/03-vue3/index.html +12 -0
  76. package/examples/03-vue3/package.json +19 -0
  77. package/examples/03-vue3/src/App.vue +93 -0
  78. package/examples/03-vue3/src/main.js +8 -0
  79. package/examples/03-vue3/src/useFanUI.js +85 -0
  80. package/examples/03-vue3/vite.config.js +6 -0
  81. package/examples/04-vue2-cdn.html +93 -0
  82. package/examples/README.md +34 -0
  83. package/package.json +127 -0
  84. package/src/_functions.scss +319 -0
  85. package/src/_mixins.scss +1202 -0
  86. package/src/_tokens.scss +180 -0
  87. package/src/_variables.scss +491 -0
  88. package/src/base/_animations.scss +210 -0
  89. package/src/base/_reset.scss +359 -0
  90. package/src/base/_rtl.scss +133 -0
  91. package/src/base/_typography.scss +353 -0
  92. package/src/components/_advanced.scss +763 -0
  93. package/src/components/_alert.scss +322 -0
  94. package/src/components/_badge.scss +279 -0
  95. package/src/components/_blog.scss +693 -0
  96. package/src/components/_button.scss +351 -0
  97. package/src/components/_capabilities.scss +463 -0
  98. package/src/components/_card.scss +281 -0
  99. package/src/components/_choice.scss +444 -0
  100. package/src/components/_dashboard.scss +950 -0
  101. package/src/components/_data.scss +618 -0
  102. package/src/components/_feedback.scss +403 -0
  103. package/src/components/_form.scss +408 -0
  104. package/src/components/_glass.scss +374 -0
  105. package/src/components/_input-plus.scss +730 -0
  106. package/src/components/_interactive.scss +885 -0
  107. package/src/components/_marketing.scss +982 -0
  108. package/src/components/_nav.scss +480 -0
  109. package/src/components/_overlay.scss +517 -0
  110. package/src/components/_table.scss +276 -0
  111. package/src/components/_tabs.scss +338 -0
  112. package/src/fanui.d.ts +353 -0
  113. package/src/fanui.js +4782 -0
  114. package/src/fanui.scss +79 -0
  115. package/src/glass/_switches.scss +36 -0
  116. package/src/glass/_tokens.scss +259 -0
  117. package/src/layout/_container.scss +111 -0
  118. package/src/layout/_flex.scss +164 -0
  119. package/src/layout/_grid.scss +180 -0
  120. package/src/themes/_accents.scss +201 -0
  121. package/src/themes/_palettes.scss +180 -0
  122. package/src/utilities/_borders.scss +104 -0
  123. package/src/utilities/_display.scss +145 -0
  124. package/src/utilities/_effects.scss +210 -0
  125. package/src/utilities/_sizing.scss +123 -0
  126. package/src/utilities/_spacing.scss +118 -0
  127. package/src/utilities/_texture.scss +323 -0
@@ -0,0 +1,618 @@
1
+ // =============================================================================
2
+ // fanUI · 数据展示组件:头像 / 列表 / 分页 / 步骤条 / 时间线 / 描述列表
3
+ // =============================================================================
4
+
5
+ @use "sass:map";
6
+ @use "../functions" as fn;
7
+ @use "../variables" as v;
8
+ @use "../mixins" as mx;
9
+
10
+ // -----------------------------------------------------------------------------
11
+ // 1. 头像 Avatar
12
+ // -----------------------------------------------------------------------------
13
+
14
+ .fanui-avatar {
15
+ --fanui-avatar-size: 40px;
16
+ --fanui-avatar-radius: 50%;
17
+
18
+ position: relative;
19
+ display: inline-flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ flex: 0 0 auto;
23
+ width: var(--fanui-avatar-size);
24
+ height: var(--fanui-avatar-size);
25
+ font-size: calc(var(--fanui-avatar-size) * 0.4);
26
+ font-weight: 600;
27
+ line-height: 1;
28
+ color: fn.fanui-var("color-text-inverse");
29
+ background-color: fn.fanui-var("primary");
30
+ border-radius: var(--fanui-avatar-radius);
31
+ overflow: hidden;
32
+ user-select: none;
33
+ box-sizing: border-box;
34
+
35
+ > img,
36
+ > video {
37
+ width: 100%;
38
+ height: 100%;
39
+ object-fit: cover;
40
+ display: block;
41
+ }
42
+
43
+ &__icon { display: inline-flex; font-size: 1.1em; }
44
+ &__text { font-size: 0.85em; letter-spacing: 0.02em; }
45
+
46
+ // 尺寸
47
+ &--xs { --fanui-avatar-size: 24px; font-size: 11px; }
48
+ &--sm { --fanui-avatar-size: 32px; font-size: 13px; }
49
+ &--md { --fanui-avatar-size: 40px; }
50
+ &--lg { --fanui-avatar-size: 48px; }
51
+ &--xl { --fanui-avatar-size: 64px; font-size: 22px; }
52
+ &--2xl { --fanui-avatar-size: 80px; font-size: 28px; }
53
+
54
+ &--square { --fanui-avatar-radius: #{fn.fanui-var("radius-base")}; }
55
+ &--rounded { --fanui-avatar-radius: #{fn.fanui-var("radius-lg")}; }
56
+
57
+ &--bordered {
58
+ border: 2px solid fn.fanui-var("color-surface");
59
+ box-shadow: fn.fanui-var("shadow-sm");
60
+ }
61
+
62
+ // 语义色
63
+ @each $name, $color in v.$fanui-theme-colors {
64
+ &--#{$name} {
65
+ background-color: #{fn.fanui-var("#{$name}", $color)};
66
+ color: #{fn.fanui-var("#{$name}-fg", fn.fanui-contrast($color))};
67
+ }
68
+ }
69
+
70
+ &--neutral { background-color: fn.fanui-var("color-surface-sunken"); color: fn.fanui-var("color-text-muted"); }
71
+
72
+ // 状态点
73
+ &__status {
74
+ position: absolute;
75
+ right: 0;
76
+ bottom: 0;
77
+ width: 28%;
78
+ height: 28%;
79
+ min-width: 8px;
80
+ min-height: 8px;
81
+ border-radius: 50%;
82
+ border: 2px solid fn.fanui-var("color-surface");
83
+ background-color: fn.fanui-var("color-text-subtle");
84
+ box-sizing: content-box;
85
+
86
+ &--online { background-color: fn.fanui-var("success"); }
87
+ &--busy { background-color: fn.fanui-var("danger"); }
88
+ &--away { background-color: fn.fanui-var("warning"); }
89
+ &--offline { background-color: fn.fanui-var("color-text-subtle"); }
90
+ }
91
+ }
92
+
93
+ // 头像组
94
+ .fanui-avatar-group {
95
+ display: inline-flex;
96
+ align-items: center;
97
+
98
+ > .fanui-avatar {
99
+ margin-inline-start: -10px;
100
+ border: 2px solid fn.fanui-var("color-surface");
101
+ @include mx.fanui-transition((transform, z-index), v.$fanui-duration-fast);
102
+
103
+ &:first-child { margin-inline-start: 0; }
104
+ &:hover { transform: translateY(-2px); z-index: 2; }
105
+ }
106
+
107
+ &--sm > .fanui-avatar { margin-inline-start: -8px; }
108
+ &--lg > .fanui-avatar { margin-inline-start: -12px; }
109
+
110
+ &__more {
111
+ background-color: fn.fanui-var("color-surface-sunken") !important;
112
+ color: fn.fanui-var("color-text-muted") !important;
113
+ font-size: 12px;
114
+ }
115
+ }
116
+
117
+ // 头像 + 文本
118
+ .fanui-avatar-media {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: fn.fanui-var("space-3");
122
+ min-width: 0;
123
+
124
+ &__body { min-width: 0; }
125
+ &__title {
126
+ font-size: fn.fanui-var("font-size-base");
127
+ font-weight: 500;
128
+ color: fn.fanui-var("color-text-strong");
129
+ @include mx.fanui-truncate(1);
130
+ }
131
+ &__desc {
132
+ font-size: fn.fanui-var("font-size-sm");
133
+ color: fn.fanui-var("color-text-muted");
134
+ @include mx.fanui-truncate(1);
135
+ }
136
+ }
137
+
138
+ // -----------------------------------------------------------------------------
139
+ // 2. 列表 List / List Group
140
+ // -----------------------------------------------------------------------------
141
+
142
+ .fanui-list-group {
143
+ display: flex;
144
+ flex-direction: column;
145
+ padding: 0;
146
+ margin: 0;
147
+ list-style: none;
148
+ background-color: fn.fanui-var("color-surface");
149
+ border: 1px solid fn.fanui-var("color-border");
150
+ border-radius: fn.fanui-var("radius-lg");
151
+ overflow: hidden;
152
+
153
+ &--flush { border: 0; border-radius: 0; background: transparent; }
154
+ &--separated { gap: fn.fanui-var("space-2"); border: 0; background: transparent; overflow: visible; }
155
+ }
156
+
157
+ .fanui-list-item {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: fn.fanui-var("space-3");
161
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
162
+ font-size: fn.fanui-var("font-size-base");
163
+ color: fn.fanui-var("color-text");
164
+ background-color: transparent;
165
+ border-bottom: 1px solid fn.fanui-var("color-border");
166
+ text-decoration: none;
167
+ box-sizing: border-box;
168
+ @include mx.fanui-transition(background-color, v.$fanui-duration-fast);
169
+
170
+ &:last-child { border-bottom: 0; }
171
+
172
+ &--interactive {
173
+ cursor: pointer;
174
+
175
+ &:hover { background-color: fn.fanui-var("color-hover"); text-decoration: none; }
176
+ &:active { background-color: fn.fanui-var("color-active"); }
177
+ &:focus-visible { @include mx.fanui-focus-ring(); }
178
+ }
179
+
180
+ &.is-active,
181
+ &--active {
182
+ background-color: fn.fanui-var("primary-soft");
183
+ color: fn.fanui-var("primary");
184
+ // 修正笔误:原为 "primary-bordery"(不存在的令牌名,只会永远走 fallback)
185
+ border-color: fn.fanui-var("primary-border", fn.fanui-var("primary"));
186
+ // C1:选中行底色是 primary-soft,嵌套的次级文字(__meta / __desc)若沿用
187
+ // 全局 subtle,在暗色下只剩 4.08:1。这里局部提升一档:
188
+ // 优先用主题色的 ink 档(与底色同色系),无主题色时退回 muted。
189
+ --fanui-color-text-subtle: var(--fanui-primary-ink, var(--fanui-color-text-muted));
190
+ }
191
+
192
+ &.is-disabled { opacity: 0.55; pointer-events: none; }
193
+
194
+ .fanui-list-group--separated & {
195
+ border: 1px solid fn.fanui-var("color-border");
196
+ border-radius: fn.fanui-var("radius-md");
197
+ background-color: fn.fanui-var("color-surface");
198
+ }
199
+
200
+ &__icon {
201
+ flex: 0 0 auto;
202
+ display: inline-flex;
203
+ width: 20px;
204
+ justify-content: center;
205
+ color: fn.fanui-var("color-text-muted");
206
+ }
207
+
208
+ &__body { flex: 1 1 auto; min-width: 0; }
209
+
210
+ &__title {
211
+ font-weight: 500;
212
+ color: fn.fanui-var("color-text-strong");
213
+ @include mx.fanui-truncate(1);
214
+ }
215
+
216
+ &__desc {
217
+ margin-top: 2px;
218
+ font-size: fn.fanui-var("font-size-sm");
219
+ color: fn.fanui-var("color-text-muted");
220
+ @include mx.fanui-truncate(2);
221
+ }
222
+
223
+ &__meta {
224
+ flex: 0 0 auto;
225
+ font-size: fn.fanui-var("font-size-sm");
226
+ color: fn.fanui-var("color-text-subtle");
227
+ white-space: nowrap;
228
+ }
229
+
230
+ &__action { flex: 0 0 auto; margin-inline-start: auto; }
231
+
232
+ &--sm { padding: 6px 12px; font-size: fn.fanui-var("font-size-sm"); }
233
+ &--lg { padding: fn.fanui-var("space-4") fn.fanui-var("space-5"); }
234
+ }
235
+
236
+ .fanui-list-header {
237
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
238
+ font-size: fn.fanui-var("font-size-xs");
239
+ font-weight: 600;
240
+ letter-spacing: 0.05em;
241
+ text-transform: uppercase;
242
+ color: fn.fanui-var("color-text-subtle");
243
+ background-color: fn.fanui-var("color-surface-sunken");
244
+ border-bottom: 1px solid fn.fanui-var("color-border");
245
+ }
246
+
247
+ // 描述列表 Descriptions
248
+ .fanui-descriptions {
249
+ display: grid;
250
+ gap: fn.fanui-var("space-3") fn.fanui-var("space-6");
251
+ font-size: fn.fanui-var("font-size-base");
252
+
253
+ &__item { min-width: 0; }
254
+
255
+ &__label {
256
+ margin-bottom: 2px;
257
+ font-size: fn.fanui-var("font-size-sm");
258
+ color: fn.fanui-var("color-text-muted");
259
+ }
260
+
261
+ &__value {
262
+ color: fn.fanui-var("color-text-strong");
263
+ word-break: break-word;
264
+ }
265
+
266
+ &--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
267
+ &--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
268
+ &--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
269
+
270
+ @include mx.fanui-media-down("md") {
271
+ &--3, &--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
272
+ }
273
+
274
+ @include mx.fanui-media-down("sm") {
275
+ &--2, &--3, &--4 { grid-template-columns: minmax(0, 1fr); }
276
+ }
277
+
278
+ // 行内(一行一条)
279
+ &--inline {
280
+ display: flex;
281
+ flex-wrap: wrap;
282
+ gap: fn.fanui-var("space-5");
283
+ grid-template-columns: none;
284
+
285
+ .fanui-descriptions__item { display: flex; gap: 8px; }
286
+ .fanui-descriptions__label { margin-bottom: 0; }
287
+ }
288
+ }
289
+
290
+ // -----------------------------------------------------------------------------
291
+ // 3. 分页 Pagination
292
+ // -----------------------------------------------------------------------------
293
+
294
+ .fanui-pagination {
295
+ display: flex;
296
+ flex-wrap: wrap;
297
+ align-items: center;
298
+ gap: fn.fanui-var("space-1");
299
+ padding: 0;
300
+ margin: 0;
301
+ list-style: none;
302
+
303
+ &__item {
304
+ display: inline-flex;
305
+ align-items: center;
306
+ justify-content: center;
307
+ min-width: 32px;
308
+ height: 32px;
309
+ padding: 0 8px;
310
+ font-size: fn.fanui-var("font-size-base");
311
+ font-weight: 500;
312
+ color: fn.fanui-var("color-text");
313
+ text-decoration: none;
314
+ background-color: transparent;
315
+ border: 1px solid fn.fanui-var("color-border");
316
+ border-radius: fn.fanui-var("radius-base");
317
+ cursor: pointer;
318
+ box-sizing: border-box;
319
+ user-select: none;
320
+ font-variant-numeric: tabular-nums;
321
+ @include mx.fanui-transition((color, background-color, border-color), v.$fanui-duration-fast);
322
+
323
+ &:hover:not(.is-disabled):not([aria-disabled="true"]):not(.is-active) {
324
+ color: fn.fanui-var("primary");
325
+ border-color: fn.fanui-var("primary");
326
+ background-color: fn.fanui-var("primary-soft");
327
+ text-decoration: none;
328
+ }
329
+
330
+ &:focus-visible { @include mx.fanui-focus-ring(); }
331
+
332
+ &.is-active,
333
+ &[aria-current="page"] {
334
+ color: fn.fanui-var("primary-fg");
335
+ background-color: fn.fanui-var("primary");
336
+ border-color: fn.fanui-var("primary");
337
+ }
338
+
339
+ &.is-disabled,
340
+ &[aria-disabled="true"] {
341
+ color: fn.fanui-var("color-disabled-text");
342
+ cursor: not-allowed;
343
+ pointer-events: none;
344
+ }
345
+
346
+ &--ellipsis {
347
+ border-color: transparent;
348
+ background: transparent;
349
+ color: fn.fanui-var("color-text-subtle");
350
+ cursor: default;
351
+ pointer-events: none;
352
+ }
353
+
354
+ &--icon { padding: 0; }
355
+ }
356
+
357
+ &__info {
358
+ margin-inline-start: fn.fanui-var("space-3");
359
+ font-size: fn.fanui-var("font-size-sm");
360
+ color: fn.fanui-var("color-text-muted");
361
+ }
362
+
363
+ // 变体
364
+ &--borderless .fanui-pagination__item { border-color: transparent; }
365
+
366
+ &--pill .fanui-pagination__item { border-radius: 9999px; }
367
+
368
+ &--sm .fanui-pagination__item { min-width: 26px; height: 26px; font-size: fn.fanui-var("font-size-sm"); }
369
+ &--lg .fanui-pagination__item { min-width: 38px; height: 38px; font-size: fn.fanui-var("font-size-md"); }
370
+
371
+ &--center { justify-content: center; }
372
+ &--end { justify-content: flex-end; }
373
+
374
+ // 紧凑「上一页/页码/下一页」布局
375
+ &--simple {
376
+ gap: fn.fanui-var("space-2");
377
+
378
+ .fanui-pagination__item { border-color: transparent; }
379
+ }
380
+ }
381
+
382
+ // 每页条数选择器
383
+ .fanui-pagination-page-size {
384
+ display: inline-flex;
385
+ align-items: center;
386
+ gap: fn.fanui-var("space-2");
387
+ font-size: fn.fanui-var("font-size-sm");
388
+ color: fn.fanui-var("color-text-muted");
389
+
390
+ .fanui-select { width: auto; min-width: 72px; }
391
+ }
392
+
393
+ // -----------------------------------------------------------------------------
394
+ // 4. 步骤条 Steps
395
+ // -----------------------------------------------------------------------------
396
+
397
+ .fanui-steps {
398
+ --fanui-step-dot: 28px;
399
+
400
+ display: flex;
401
+ align-items: flex-start;
402
+ width: 100%;
403
+ list-style: none;
404
+ margin: 0;
405
+ padding: 0;
406
+
407
+ &__item {
408
+ position: relative;
409
+ flex: 1 1 0;
410
+ min-width: 0;
411
+ display: flex;
412
+ flex-direction: column;
413
+ align-items: center;
414
+ text-align: center;
415
+ gap: fn.fanui-var("space-2");
416
+
417
+ // 连接线
418
+ &:not(:last-child)::after {
419
+ content: "";
420
+ position: absolute;
421
+ top: calc(var(--fanui-step-dot) / 2);
422
+ left: calc(50% + var(--fanui-step-dot) / 2 + 6px);
423
+ right: calc(-50% + var(--fanui-step-dot) / 2 + 6px);
424
+ height: 2px;
425
+ background-color: fn.fanui-var("color-border");
426
+ @include mx.fanui-transition(background-color, v.$fanui-duration-base);
427
+ }
428
+
429
+ &.is-done:not(:last-child)::after { background-color: fn.fanui-var("primary"); }
430
+ }
431
+
432
+ &__dot {
433
+ display: inline-flex;
434
+ align-items: center;
435
+ justify-content: center;
436
+ flex: 0 0 auto;
437
+ width: var(--fanui-step-dot);
438
+ height: var(--fanui-step-dot);
439
+ font-size: fn.fanui-var("font-size-sm");
440
+ font-weight: 600;
441
+ line-height: 1;
442
+ color: fn.fanui-var("color-text-muted");
443
+ background-color: fn.fanui-var("color-surface");
444
+ border: 2px solid fn.fanui-var("color-border-strong");
445
+ border-radius: 50%;
446
+ box-sizing: border-box;
447
+ z-index: 1;
448
+ @include mx.fanui-transition((color, background-color, border-color), v.$fanui-duration-base);
449
+ }
450
+
451
+ &__title {
452
+ font-size: fn.fanui-var("font-size-base");
453
+ font-weight: 500;
454
+ color: fn.fanui-var("color-text-muted");
455
+ @include mx.fanui-transition(color, v.$fanui-duration-base);
456
+ }
457
+
458
+ &__desc {
459
+ font-size: fn.fanui-var("font-size-sm");
460
+ color: fn.fanui-var("color-text-subtle");
461
+ line-height: 1.5;
462
+ }
463
+
464
+ // ---- 状态 ----
465
+ &__item.is-active {
466
+ .fanui-steps__dot {
467
+ color: fn.fanui-var("primary-fg");
468
+ background-color: fn.fanui-var("primary");
469
+ border-color: fn.fanui-var("primary");
470
+ box-shadow: 0 0 0 4px fn.fanui-var("primary-soft");
471
+ }
472
+ .fanui-steps__title { color: fn.fanui-var("color-text-strong"); font-weight: 600; }
473
+ }
474
+
475
+ &__item.is-done {
476
+ .fanui-steps__dot {
477
+ color: fn.fanui-var("primary-fg");
478
+ background-color: fn.fanui-var("primary");
479
+ border-color: fn.fanui-var("primary");
480
+ }
481
+ .fanui-steps__title { color: fn.fanui-var("color-text"); }
482
+ }
483
+
484
+ &__item.is-error {
485
+ .fanui-steps__dot { color: fn.fanui-var("danger-fg"); background-color: fn.fanui-var("danger"); border-color: fn.fanui-var("danger"); }
486
+ .fanui-steps__title { color: fn.fanui-var("danger"); }
487
+ }
488
+
489
+ // ---- 变体 ----
490
+ &--simple .fanui-steps__item {
491
+ &:not(:last-child)::after {
492
+ left: calc(var(--fanui-step-dot) / 2 + 8px);
493
+ right: calc(-50% + var(--fanui-step-dot) / 2 + 12px);
494
+ }
495
+ }
496
+
497
+ &--navigation {
498
+ gap: fn.fanui-var("space-3");
499
+ align-items: stretch;
500
+
501
+ .fanui-steps__item {
502
+ flex-direction: row;
503
+ align-items: flex-start;
504
+ text-align: start;
505
+ gap: fn.fanui-var("space-3");
506
+ padding: fn.fanui-var("space-3");
507
+ border: 1px solid fn.fanui-var("color-border");
508
+ border-radius: fn.fanui-var("radius-md");
509
+ cursor: pointer;
510
+
511
+ &::after { display: none; }
512
+ &:hover { border-color: fn.fanui-var("primary"); }
513
+ &.is-active { border-color: fn.fanui-var("primary"); background-color: fn.fanui-var("primary-soft"); }
514
+ }
515
+
516
+ .fanui-steps__dot { margin-top: 1px; }
517
+ }
518
+
519
+ // 纵向
520
+ &--vertical {
521
+ flex-direction: column;
522
+ gap: 0;
523
+
524
+ .fanui-steps__item {
525
+ flex-direction: row;
526
+ align-items: flex-start;
527
+ text-align: start;
528
+ gap: fn.fanui-var("space-3");
529
+ padding-bottom: fn.fanui-var("space-5");
530
+ flex: 0 0 auto;
531
+
532
+ &:not(:last-child)::after {
533
+ top: calc(var(--fanui-step-dot) + 4px);
534
+ left: calc(var(--fanui-step-dot) / 2 - 1px);
535
+ right: auto;
536
+ bottom: 4px;
537
+ width: 2px;
538
+ height: auto;
539
+ }
540
+
541
+ &:last-child { padding-bottom: 0; }
542
+ }
543
+
544
+ .fanui-steps__body { text-align: start; }
545
+ }
546
+
547
+ &__body { min-width: 0; }
548
+ }
549
+
550
+ // -----------------------------------------------------------------------------
551
+ // 5. 时间线 Timeline
552
+ // -----------------------------------------------------------------------------
553
+
554
+ .fanui-timeline {
555
+ position: relative;
556
+ list-style: none;
557
+ margin: 0;
558
+ padding: 0;
559
+
560
+ &__item {
561
+ position: relative;
562
+ display: flex;
563
+ gap: fn.fanui-var("space-4");
564
+ padding-bottom: fn.fanui-var("space-5");
565
+
566
+ &:last-child { padding-bottom: 0; }
567
+
568
+ // 连接线
569
+ &:not(:last-child)::before {
570
+ content: "";
571
+ position: absolute;
572
+ top: 18px;
573
+ bottom: 0;
574
+ left: 6px;
575
+ width: 2px;
576
+ background-color: fn.fanui-var("color-border");
577
+ }
578
+ }
579
+
580
+ &__dot {
581
+ position: relative;
582
+ flex: 0 0 auto;
583
+ width: 14px;
584
+ height: 14px;
585
+ margin-top: 4px;
586
+ background-color: fn.fanui-var("color-surface");
587
+ border: 2px solid fn.fanui-var("color-border-strong");
588
+ border-radius: 50%;
589
+ box-sizing: border-box;
590
+ z-index: 1;
591
+
592
+ &--primary { background-color: fn.fanui-var("primary"); border-color: fn.fanui-var("primary"); }
593
+ &--success { background-color: fn.fanui-var("success"); border-color: fn.fanui-var("success"); }
594
+ &--warning { background-color: fn.fanui-var("warning"); border-color: fn.fanui-var("warning"); }
595
+ &--danger { background-color: fn.fanui-var("danger"); border-color: fn.fanui-var("danger"); }
596
+ }
597
+
598
+ &__body { flex: 1 1 auto; min-width: 0; }
599
+
600
+ &__time {
601
+ font-size: fn.fanui-var("font-size-sm");
602
+ color: fn.fanui-var("color-text-subtle");
603
+ font-variant-numeric: tabular-nums;
604
+ }
605
+
606
+ &__title {
607
+ font-size: fn.fanui-var("font-size-base");
608
+ font-weight: 600;
609
+ color: fn.fanui-var("color-text-strong");
610
+ }
611
+
612
+ &__desc {
613
+ margin-top: 4px;
614
+ font-size: fn.fanui-var("font-size-base");
615
+ line-height: 1.6;
616
+ color: fn.fanui-var("color-text-muted");
617
+ }
618
+ }