@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,517 @@
1
+ // =============================================================================
2
+ // fanUI · 浮层浮出组件:模态框 / 抽屉 / 下拉菜单 / 提示气泡
3
+ // -----------------------------------------------------------------------------
4
+ // .fanui-modal 模态框(backdrop + dialog + content)
5
+ // .fanui-drawer 抽屉 / 侧滑面板
6
+ // .fanui-dropdown 下拉菜单
7
+ // .fanui-tooltip 提示气泡
8
+ // .fanui-popover 浮层卡片
9
+ // 状态统一使用 .is-open / .is-active;显示/隐藏由业务 JS 控制。
10
+ // =============================================================================
11
+
12
+ @use "sass:map";
13
+ @use "../functions" as fn;
14
+ @use "../variables" as v;
15
+ @use "../mixins" as mx;
16
+
17
+ // -----------------------------------------------------------------------------
18
+ // 1. 模态框 Modal
19
+ // -----------------------------------------------------------------------------
20
+
21
+ .fanui-modal {
22
+ position: fixed;
23
+ inset: 0;
24
+ z-index: fn.fanui-var("z-modal", v.$fanui-zindex-modal);
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ padding: fn.fanui-var("space-4");
29
+ opacity: 0;
30
+ visibility: hidden;
31
+ @include mx.fanui-transition((opacity, visibility), v.$fanui-duration-base);
32
+ overflow-y: auto;
33
+ -webkit-overflow-scrolling: touch;
34
+
35
+ &.is-open,
36
+ &.is-active {
37
+ opacity: 1;
38
+ visibility: visible;
39
+
40
+ > .fanui-modal__dialog { transform: translateY(0) scale(1); }
41
+ }
42
+
43
+ // 遮罩
44
+ &__backdrop {
45
+ @include mx.fanui-backdrop();
46
+ z-index: 0;
47
+ }
48
+
49
+ &__dialog {
50
+ position: relative;
51
+ z-index: 1;
52
+ display: flex;
53
+ flex-direction: column;
54
+ width: 100%;
55
+ max-width: var(--fanui-modal-width, #{map.get(v.$fanui-modal-widths, "base")});
56
+ max-height: calc(100vh - #{fn.fanui-var("space-8")});
57
+ margin: auto;
58
+ background-color: fn.fanui-var("color-surface");
59
+ border: 1px solid fn.fanui-var("color-border");
60
+ border-radius: fn.fanui-var("radius-xl");
61
+ box-shadow: fn.fanui-var("shadow-2xl");
62
+ box-sizing: border-box;
63
+ transform: translateY(12px) scale(0.98);
64
+ @include mx.fanui-transition(transform, v.$fanui-duration-slow, v.$fanui-ease-spring);
65
+ overflow: hidden;
66
+ }
67
+
68
+ &__header {
69
+ display: flex;
70
+ align-items: flex-start;
71
+ justify-content: space-between;
72
+ gap: fn.fanui-var("space-4");
73
+ padding: fn.fanui-var("space-5") fn.fanui-var("space-5") fn.fanui-var("space-4");
74
+ flex: 0 0 auto;
75
+ }
76
+
77
+ &__title {
78
+ margin: 0;
79
+ font-size: fn.fanui-var("font-size-lg");
80
+ font-weight: 600;
81
+ line-height: 1.4;
82
+ color: fn.fanui-var("color-text-strong");
83
+ }
84
+
85
+ &__desc {
86
+ margin: 4px 0 0;
87
+ font-size: fn.fanui-var("font-size-sm");
88
+ color: fn.fanui-var("color-text-muted");
89
+ }
90
+
91
+ &__close {
92
+ flex: 0 0 auto;
93
+ display: inline-flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ width: 28px;
97
+ height: 28px;
98
+ margin: -4px -6px 0 0;
99
+ padding: 0;
100
+ font-size: 16px;
101
+ line-height: 1;
102
+ color: fn.fanui-var("color-text-subtle");
103
+ background: none;
104
+ border: 0;
105
+ border-radius: fn.fanui-var("radius-base");
106
+ cursor: pointer;
107
+ @include mx.fanui-transition((color, background-color), v.$fanui-duration-fast);
108
+
109
+ &:hover { color: fn.fanui-var("color-text"); background-color: fn.fanui-var("color-hover"); }
110
+ &:focus-visible { @include mx.fanui-focus-ring(); }
111
+ }
112
+
113
+ &__body {
114
+ flex: 1 1 auto;
115
+ padding: 0 fn.fanui-var("space-5") fn.fanui-var("space-5");
116
+ overflow-y: auto;
117
+ min-height: 0;
118
+ font-size: fn.fanui-var("font-size-base");
119
+ line-height: fn.fanui-var("line-height-relaxed");
120
+ color: fn.fanui-var("color-text");
121
+ @include mx.fanui-scrollbar(8px);
122
+ }
123
+
124
+ &__footer {
125
+ display: flex;
126
+ align-items: center;
127
+ justify-content: flex-end;
128
+ gap: fn.fanui-var("space-3");
129
+ padding: fn.fanui-var("space-4") fn.fanui-var("space-5");
130
+ background-color: fn.fanui-var("color-surface-sunken");
131
+ border-top: 1px solid fn.fanui-var("color-border");
132
+ flex: 0 0 auto;
133
+
134
+ &--start { justify-content: flex-start; }
135
+ &--between { justify-content: space-between; }
136
+ }
137
+
138
+ // 带分区的 header(有下边框)
139
+ &__header--bordered {
140
+ padding-bottom: fn.fanui-var("space-4");
141
+ border-bottom: 1px solid fn.fanui-var("color-border");
142
+ }
143
+
144
+ // ---- 尺寸 ----
145
+ &--sm > .fanui-modal__dialog { --fanui-modal-width: #{map.get(v.$fanui-modal-widths, "sm")}; }
146
+ &--md > .fanui-modal__dialog { --fanui-modal-width: #{map.get(v.$fanui-modal-widths, "base")}; }
147
+ &--lg > .fanui-modal__dialog { --fanui-modal-width: #{map.get(v.$fanui-modal-widths, "lg")}; }
148
+ &--xl > .fanui-modal__dialog { --fanui-modal-width: #{map.get(v.$fanui-modal-widths, "xl")}; }
149
+ &--full > .fanui-modal__dialog {
150
+ --fanui-modal-width: #{map.get(v.$fanui-modal-widths, "full")};
151
+ max-height: 100vh;
152
+ height: 100%;
153
+ border-radius: 0;
154
+ border: 0;
155
+ }
156
+
157
+ // ---- 位置 ----
158
+ &--top { align-items: flex-start; padding-top: fn.fanui-var("space-16"); }
159
+ &--bottom { align-items: flex-end; }
160
+
161
+ // ---- 无边框对话框 ----
162
+ &--plain > .fanui-modal__dialog {
163
+ border: 0;
164
+ box-shadow: fn.fanui-var("shadow-2xl");
165
+ }
166
+
167
+ &--scrollable > .fanui-modal__dialog { overflow: hidden; }
168
+ }
169
+
170
+ // 打开模态时锁定滚动
171
+ .fanui-modal-open {
172
+ overflow: hidden;
173
+ }
174
+
175
+ // -----------------------------------------------------------------------------
176
+ // 2. 抽屉 Drawer / Offcanvas
177
+ // -----------------------------------------------------------------------------
178
+
179
+ .fanui-drawer {
180
+ position: fixed;
181
+ inset: 0;
182
+ z-index: fn.fanui-var("z-offcanvas", v.$fanui-zindex-offcanvas);
183
+ visibility: hidden;
184
+ @include mx.fanui-transition(visibility, v.$fanui-duration-base);
185
+
186
+ &.is-open,
187
+ &.is-active { visibility: visible; }
188
+
189
+ &__backdrop {
190
+ @include mx.fanui-backdrop();
191
+ opacity: 0;
192
+ @include mx.fanui-transition(opacity, v.$fanui-duration-base);
193
+ }
194
+
195
+ &.is-open > .fanui-drawer__backdrop { opacity: 1; }
196
+
197
+ &__panel {
198
+ position: absolute;
199
+ top: 0;
200
+ bottom: 0;
201
+ display: flex;
202
+ flex-direction: column;
203
+ width: var(--fanui-drawer-width, 360px);
204
+ max-width: 92vw;
205
+ background-color: fn.fanui-var("color-surface");
206
+ box-shadow: fn.fanui-var("shadow-2xl");
207
+ box-sizing: border-box;
208
+ @include mx.fanui-transition(transform, v.$fanui-duration-slow, v.$fanui-ease-out);
209
+ z-index: 1;
210
+ }
211
+
212
+ &__header {
213
+ display: flex;
214
+ align-items: center;
215
+ justify-content: space-between;
216
+ gap: fn.fanui-var("space-3");
217
+ padding: fn.fanui-var("space-4") fn.fanui-var("space-5");
218
+ border-bottom: 1px solid fn.fanui-var("color-border");
219
+ flex: 0 0 auto;
220
+ }
221
+
222
+ &__title {
223
+ margin: 0;
224
+ font-size: fn.fanui-var("font-size-md");
225
+ font-weight: 600;
226
+ color: fn.fanui-var("color-text-strong");
227
+ }
228
+
229
+ &__body {
230
+ flex: 1 1 auto;
231
+ padding: fn.fanui-var("space-5");
232
+ overflow-y: auto;
233
+ min-height: 0;
234
+ @include mx.fanui-scrollbar(8px);
235
+ }
236
+
237
+ &__footer {
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: flex-end;
241
+ gap: fn.fanui-var("space-3");
242
+ padding: fn.fanui-var("space-4") fn.fanui-var("space-5");
243
+ border-top: 1px solid fn.fanui-var("color-border");
244
+ flex: 0 0 auto;
245
+ }
246
+
247
+ // 方向
248
+ &--start &__panel {
249
+ left: 0;
250
+ right: auto;
251
+ transform: translateX(-100%);
252
+ }
253
+
254
+ &--end &__panel {
255
+ right: 0;
256
+ left: auto;
257
+ transform: translateX(100%);
258
+ }
259
+
260
+ &--top &__panel {
261
+ left: 0;
262
+ right: 0;
263
+ top: 0;
264
+ bottom: auto;
265
+ width: 100%;
266
+ max-width: none;
267
+ height: var(--fanui-drawer-height, 320px);
268
+ transform: translateY(-100%);
269
+ border-radius: 0 0 fn.fanui-var("radius-xl") fn.fanui-var("radius-xl");
270
+ }
271
+
272
+ &--bottom &__panel {
273
+ left: 0;
274
+ right: 0;
275
+ bottom: 0;
276
+ top: auto;
277
+ width: 100%;
278
+ max-width: none;
279
+ height: var(--fanui-drawer-height, auto);
280
+ max-height: 88vh;
281
+ transform: translateY(100%);
282
+ border-radius: fn.fanui-var("radius-xl") fn.fanui-var("radius-xl") 0 0;
283
+ }
284
+
285
+ &.is-open > .fanui-drawer__panel { transform: translate(0, 0); }
286
+
287
+ // 尺寸
288
+ &--sm > .fanui-drawer__panel { --fanui-drawer-width: 280px; }
289
+ &--lg > .fanui-drawer__panel { --fanui-drawer-width: 480px; }
290
+ &--xl > .fanui-drawer__panel { --fanui-drawer-width: 640px; }
291
+ &--full > .fanui-drawer__panel { --fanui-drawer-width: 100%; max-width: none; }
292
+
293
+ // 底部抽屉的抓手
294
+ &__grabber {
295
+ width: 36px;
296
+ height: 4px;
297
+ margin: fn.fanui-var("space-2") auto 0;
298
+ border-radius: 9999px;
299
+ background-color: fn.fanui-var("color-border-strong");
300
+ flex: 0 0 auto;
301
+ }
302
+ }
303
+
304
+ // -----------------------------------------------------------------------------
305
+ // 3. 下拉菜单 Dropdown
306
+ // -----------------------------------------------------------------------------
307
+
308
+ .fanui-dropdown {
309
+ position: relative;
310
+ display: inline-flex;
311
+
312
+ &__menu {
313
+ position: absolute;
314
+ top: calc(100% + 6px);
315
+ left: 0;
316
+ z-index: fn.fanui-var("z-dropdown", v.$fanui-zindex-dropdown);
317
+ min-width: var(--fanui-dropdown-width, 180px);
318
+ max-width: 320px;
319
+ padding: 6px;
320
+ background-color: fn.fanui-var("color-surface");
321
+ border: 1px solid fn.fanui-var("color-border");
322
+ border-radius: fn.fanui-var("radius-md");
323
+ box-shadow: fn.fanui-var("shadow-lg");
324
+ box-sizing: border-box;
325
+ opacity: 0;
326
+ visibility: hidden;
327
+ transform: translateY(-4px) scale(0.98);
328
+ transform-origin: top left;
329
+ @include mx.fanui-transition((opacity, visibility, transform), v.$fanui-duration-fast, v.$fanui-ease-out);
330
+
331
+ &.is-open,
332
+ &.is-active {
333
+ opacity: 1;
334
+ visibility: visible;
335
+ transform: translateY(0) scale(1);
336
+ }
337
+
338
+ // 对齐
339
+ &--end { left: auto; right: 0; transform-origin: top right; }
340
+ &--start { left: 0; right: auto; }
341
+
342
+ // 向上弹出
343
+ &--up {
344
+ top: auto;
345
+ bottom: calc(100% + 6px);
346
+ transform-origin: bottom left;
347
+ transform: translateY(4px) scale(0.98);
348
+
349
+ // 上 + 右对齐
350
+ &.fanui-dropdown__menu--end { transform-origin: bottom right; }
351
+ }
352
+
353
+ // 无内边距(放置自定义内容)
354
+ &--flush { padding: 0; }
355
+ }
356
+
357
+ // 打开状态时给触发器加点样式
358
+ &.is-open > .fanui-dropdown__trigger,
359
+ &__trigger[aria-expanded="true"] { color: fn.fanui-var("color-text-strong"); }
360
+ }
361
+
362
+ // -----------------------------------------------------------------------------
363
+ // 4. 提示气泡 Tooltip
364
+ // -----------------------------------------------------------------------------
365
+
366
+ .fanui-tooltip {
367
+ position: relative;
368
+ display: inline-flex;
369
+
370
+ &__bubble {
371
+ position: absolute;
372
+ z-index: fn.fanui-var("z-tooltip", v.$fanui-zindex-tooltip);
373
+ max-width: 260px;
374
+ padding: 6px 10px;
375
+ font-size: fn.fanui-var("font-size-xs");
376
+ line-height: 1.5;
377
+ white-space: normal;
378
+ text-align: center;
379
+ color: fn.fanui-var("color-surface");
380
+ background-color: fn.fanui-var("color-text-strong");
381
+ border-radius: fn.fanui-var("radius-base");
382
+ box-shadow: fn.fanui-var("shadow-md");
383
+ opacity: 0;
384
+ visibility: hidden;
385
+ pointer-events: none;
386
+ @include mx.fanui-transition((opacity, visibility, transform), v.$fanui-duration-fast);
387
+
388
+ // 箭头
389
+ &::after {
390
+ content: "";
391
+ position: absolute;
392
+ width: 6px;
393
+ height: 6px;
394
+ background-color: inherit;
395
+ transform: rotate(45deg);
396
+ }
397
+
398
+ // 默认:上方
399
+ bottom: calc(100% + 8px);
400
+ left: 50%;
401
+ transform: translateX(-50%) translateY(4px);
402
+
403
+ &::after { top: 100%; left: 50%; margin: -3px 0 0 -3px; }
404
+ }
405
+
406
+ &:hover > .fanui-tooltip__bubble,
407
+ &:focus-within > .fanui-tooltip__bubble,
408
+ > .fanui-tooltip__bubble.is-open {
409
+ opacity: 1;
410
+ visibility: visible;
411
+ }
412
+
413
+ // 各方位展开后的最终位置
414
+ &:hover > .fanui-tooltip__bubble { transform: translateX(-50%) translateY(0); }
415
+
416
+ &--bottom:hover > .fanui-tooltip__bubble,
417
+ &--bottom:focus-within > .fanui-tooltip__bubble { transform: translateX(-50%) translateY(0); }
418
+
419
+ &--right:hover > .fanui-tooltip__bubble,
420
+ &--right:focus-within > .fanui-tooltip__bubble,
421
+ &--left:hover > .fanui-tooltip__bubble,
422
+ &--left:focus-within > .fanui-tooltip__bubble { transform: translateY(-50%) translateX(0); }
423
+
424
+ // 方位
425
+ &--bottom > .fanui-tooltip__bubble {
426
+ bottom: auto;
427
+ top: calc(100% + 8px);
428
+ transform: translateX(-50%) translateY(-4px);
429
+
430
+ &::after { top: auto; bottom: 100%; margin: 0 0 -3px -3px; }
431
+ }
432
+
433
+ &--right > .fanui-tooltip__bubble {
434
+ bottom: auto;
435
+ top: 50%;
436
+ left: calc(100% + 8px);
437
+ transform: translateY(-50%) translateX(-4px);
438
+
439
+ &::after { top: 50%; left: 0; margin: -3px 0 0 -3px; }
440
+ }
441
+
442
+ &--left > .fanui-tooltip__bubble {
443
+ bottom: auto;
444
+ top: 50%;
445
+ left: auto;
446
+ right: calc(100% + 8px);
447
+ transform: translateY(-50%) translateX(4px);
448
+
449
+ &::after { top: 50%; left: 100%; margin: -3px 0 0 -3px; }
450
+ }
451
+
452
+ &--light > .fanui-tooltip__bubble {
453
+ color: fn.fanui-var("color-text");
454
+ background-color: fn.fanui-var("color-surface");
455
+ border: 1px solid fn.fanui-var("color-border-strong");
456
+ }
457
+
458
+ &--primary > .fanui-tooltip__bubble { background-color: fn.fanui-var("primary"); color: fn.fanui-var("primary-fg"); }
459
+ &--danger > .fanui-tooltip__bubble { background-color: fn.fanui-var("danger"); color: fn.fanui-var("danger-fg"); }
460
+ &--success > .fanui-tooltip__bubble { background-color: fn.fanui-var("success"); color: fn.fanui-var("success-fg"); }
461
+ }
462
+
463
+ // -----------------------------------------------------------------------------
464
+ // 5. 浮层卡片 Popover
465
+ // -----------------------------------------------------------------------------
466
+
467
+ .fanui-popover {
468
+ position: relative;
469
+ display: inline-flex;
470
+
471
+ &__panel {
472
+ position: absolute;
473
+ top: calc(100% + 8px);
474
+ left: 0;
475
+ z-index: fn.fanui-var("z-popover", v.$fanui-zindex-popover);
476
+ width: var(--fanui-popover-width, 280px);
477
+ background-color: fn.fanui-var("color-surface");
478
+ border: 1px solid fn.fanui-var("color-border");
479
+ border-radius: fn.fanui-var("radius-md");
480
+ box-shadow: fn.fanui-var("shadow-xl");
481
+ opacity: 0;
482
+ visibility: hidden;
483
+ transform: translateY(-4px);
484
+ @include mx.fanui-transition((opacity, visibility, transform), v.$fanui-duration-fast, v.$fanui-ease-out);
485
+
486
+ &.is-open,
487
+ &.is-active { opacity: 1; visibility: visible; transform: translateY(0); }
488
+
489
+ &--end { left: auto; right: 0; }
490
+ &--center { left: 50%; transform: translateX(-50%) translateY(-4px); }
491
+ &--center.is-open { transform: translateX(-50%) translateY(0); }
492
+ &--wide { --fanui-popover-width: 420px; }
493
+ }
494
+
495
+ &__header {
496
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
497
+ font-size: fn.fanui-var("font-size-base");
498
+ font-weight: 600;
499
+ color: fn.fanui-var("color-text-strong");
500
+ border-bottom: 1px solid fn.fanui-var("color-border-subtle");
501
+ }
502
+
503
+ &__body {
504
+ padding: fn.fanui-var("space-4");
505
+ font-size: fn.fanui-var("font-size-sm");
506
+ line-height: 1.6;
507
+ color: fn.fanui-var("color-text-muted");
508
+ }
509
+
510
+ &__footer {
511
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
512
+ border-top: 1px solid fn.fanui-var("color-border-subtle");
513
+ display: flex;
514
+ justify-content: flex-end;
515
+ gap: fn.fanui-var("space-2");
516
+ }
517
+ }