@chenzy-design/core 0.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.
- package/LICENSE +21 -0
- package/dist/back-top.d.ts +16 -0
- package/dist/back-top.d.ts.map +1 -0
- package/dist/back-top.js +29 -0
- package/dist/back-top.js.map +1 -0
- package/dist/back-top.test.d.ts +2 -0
- package/dist/back-top.test.d.ts.map +1 -0
- package/dist/back-top.test.js +44 -0
- package/dist/back-top.test.js.map +1 -0
- package/dist/banner.d.ts +19 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/banner.js +20 -0
- package/dist/banner.js.map +1 -0
- package/dist/banner.test.d.ts +2 -0
- package/dist/banner.test.d.ts.map +1 -0
- package/dist/banner.test.js +29 -0
- package/dist/banner.test.js.map +1 -0
- package/dist/breakpoints.d.ts +30 -0
- package/dist/breakpoints.d.ts.map +1 -0
- package/dist/breakpoints.js +45 -0
- package/dist/breakpoints.js.map +1 -0
- package/dist/breakpoints.test.d.ts +2 -0
- package/dist/breakpoints.test.d.ts.map +1 -0
- package/dist/breakpoints.test.js +46 -0
- package/dist/breakpoints.test.js.map +1 -0
- package/dist/calendar.d.ts +63 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +94 -0
- package/dist/calendar.js.map +1 -0
- package/dist/calendar.test.d.ts +2 -0
- package/dist/calendar.test.d.ts.map +1 -0
- package/dist/calendar.test.js +88 -0
- package/dist/calendar.test.js.map +1 -0
- package/dist/cascader.d.ts +64 -0
- package/dist/cascader.d.ts.map +1 -0
- package/dist/cascader.js +79 -0
- package/dist/cascader.js.map +1 -0
- package/dist/cascader.test.d.ts +2 -0
- package/dist/cascader.test.d.ts.map +1 -0
- package/dist/cascader.test.js +107 -0
- package/dist/cascader.test.js.map +1 -0
- package/dist/color.d.ts +70 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +217 -0
- package/dist/color.js.map +1 -0
- package/dist/color.test.d.ts +2 -0
- package/dist/color.test.d.ts.map +1 -0
- package/dist/color.test.js +161 -0
- package/dist/color.test.js.map +1 -0
- package/dist/config-provider.d.ts +57 -0
- package/dist/config-provider.d.ts.map +1 -0
- package/dist/config-provider.js +49 -0
- package/dist/config-provider.js.map +1 -0
- package/dist/config-provider.test.d.ts +2 -0
- package/dist/config-provider.test.d.ts.map +1 -0
- package/dist/config-provider.test.js +76 -0
- package/dist/config-provider.test.js.map +1 -0
- package/dist/copyable.d.ts +43 -0
- package/dist/copyable.d.ts.map +1 -0
- package/dist/copyable.js +102 -0
- package/dist/copyable.js.map +1 -0
- package/dist/copyable.test.d.ts +2 -0
- package/dist/copyable.test.d.ts.map +1 -0
- package/dist/copyable.test.js +97 -0
- package/dist/copyable.test.js.map +1 -0
- package/dist/date.d.ts +86 -0
- package/dist/date.d.ts.map +1 -0
- package/dist/date.js +226 -0
- package/dist/date.js.map +1 -0
- package/dist/date.test.d.ts +2 -0
- package/dist/date.test.d.ts.map +1 -0
- package/dist/date.test.js +171 -0
- package/dist/date.test.js.map +1 -0
- package/dist/dismiss.d.ts +28 -0
- package/dist/dismiss.d.ts.map +1 -0
- package/dist/dismiss.js +42 -0
- package/dist/dismiss.js.map +1 -0
- package/dist/editable.d.ts +59 -0
- package/dist/editable.d.ts.map +1 -0
- package/dist/editable.js +85 -0
- package/dist/editable.js.map +1 -0
- package/dist/editable.test.d.ts +2 -0
- package/dist/editable.test.d.ts.map +1 -0
- package/dist/editable.test.js +111 -0
- package/dist/editable.test.js.map +1 -0
- package/dist/ellipsis.d.ts +76 -0
- package/dist/ellipsis.d.ts.map +1 -0
- package/dist/ellipsis.js +99 -0
- package/dist/ellipsis.js.map +1 -0
- package/dist/ellipsis.test.d.ts +2 -0
- package/dist/ellipsis.test.d.ts.map +1 -0
- package/dist/ellipsis.test.js +94 -0
- package/dist/ellipsis.test.js.map +1 -0
- package/dist/floating.d.ts +78 -0
- package/dist/floating.d.ts.map +1 -0
- package/dist/floating.js +169 -0
- package/dist/floating.js.map +1 -0
- package/dist/floating.test.d.ts +2 -0
- package/dist/floating.test.d.ts.map +1 -0
- package/dist/floating.test.js +169 -0
- package/dist/floating.test.js.map +1 -0
- package/dist/focus-trap.d.ts +24 -0
- package/dist/focus-trap.d.ts.map +1 -0
- package/dist/focus-trap.js +64 -0
- package/dist/focus-trap.js.map +1 -0
- package/dist/form.d.ts +131 -0
- package/dist/form.d.ts.map +1 -0
- package/dist/form.js +257 -0
- package/dist/form.js.map +1 -0
- package/dist/form.test.d.ts +2 -0
- package/dist/form.test.d.ts.map +1 -0
- package/dist/form.test.js +323 -0
- package/dist/form.test.js.map +1 -0
- package/dist/highlight.d.ts +57 -0
- package/dist/highlight.d.ts.map +1 -0
- package/dist/highlight.js +124 -0
- package/dist/highlight.js.map +1 -0
- package/dist/highlight.test.d.ts +2 -0
- package/dist/highlight.test.d.ts.map +1 -0
- package/dist/highlight.test.js +155 -0
- package/dist/highlight.test.js.map +1 -0
- package/dist/id.d.ts +4 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +14 -0
- package/dist/id.js.map +1 -0
- package/dist/id.test.d.ts +2 -0
- package/dist/id.test.d.ts.map +1 -0
- package/dist/id.test.js +13 -0
- package/dist/id.test.js.map +1 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/inert-background.d.ts +27 -0
- package/dist/inert-background.d.ts.map +1 -0
- package/dist/inert-background.js +92 -0
- package/dist/inert-background.js.map +1 -0
- package/dist/inert-background.test.d.ts +2 -0
- package/dist/inert-background.test.d.ts.map +1 -0
- package/dist/inert-background.test.js +62 -0
- package/dist/inert-background.test.js.map +1 -0
- package/dist/input-number.d.ts +34 -0
- package/dist/input-number.d.ts.map +1 -0
- package/dist/input-number.js +69 -0
- package/dist/input-number.js.map +1 -0
- package/dist/input-number.test.d.ts +2 -0
- package/dist/input-number.test.d.ts.map +1 -0
- package/dist/input-number.test.js +74 -0
- package/dist/input-number.test.js.map +1 -0
- package/dist/list-selection.d.ts +38 -0
- package/dist/list-selection.d.ts.map +1 -0
- package/dist/list-selection.js +61 -0
- package/dist/list-selection.js.map +1 -0
- package/dist/list-selection.test.d.ts +2 -0
- package/dist/list-selection.test.d.ts.map +1 -0
- package/dist/list-selection.test.js +61 -0
- package/dist/list-selection.test.js.map +1 -0
- package/dist/live-announcer.d.ts +38 -0
- package/dist/live-announcer.d.ts.map +1 -0
- package/dist/live-announcer.js +89 -0
- package/dist/live-announcer.js.map +1 -0
- package/dist/live-announcer.test.d.ts +2 -0
- package/dist/live-announcer.test.d.ts.map +1 -0
- package/dist/live-announcer.test.js +70 -0
- package/dist/live-announcer.test.js.map +1 -0
- package/dist/lottie-icon.d.ts +88 -0
- package/dist/lottie-icon.d.ts.map +1 -0
- package/dist/lottie-icon.js +84 -0
- package/dist/lottie-icon.js.map +1 -0
- package/dist/lottie-icon.test.d.ts +2 -0
- package/dist/lottie-icon.test.d.ts.map +1 -0
- package/dist/lottie-icon.test.js +88 -0
- package/dist/lottie-icon.test.js.map +1 -0
- package/dist/notification.d.ts +61 -0
- package/dist/notification.d.ts.map +1 -0
- package/dist/notification.js +133 -0
- package/dist/notification.js.map +1 -0
- package/dist/notification.test.d.ts +2 -0
- package/dist/notification.test.d.ts.map +1 -0
- package/dist/notification.test.js +103 -0
- package/dist/notification.test.js.map +1 -0
- package/dist/overflow-list.d.ts +102 -0
- package/dist/overflow-list.d.ts.map +1 -0
- package/dist/overflow-list.js +159 -0
- package/dist/overflow-list.js.map +1 -0
- package/dist/overflow-list.test.d.ts +2 -0
- package/dist/overflow-list.test.d.ts.map +1 -0
- package/dist/overflow-list.test.js +223 -0
- package/dist/overflow-list.test.js.map +1 -0
- package/dist/pagination.d.ts +51 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +127 -0
- package/dist/pagination.js.map +1 -0
- package/dist/pagination.test.d.ts +2 -0
- package/dist/pagination.test.d.ts.map +1 -0
- package/dist/pagination.test.js +147 -0
- package/dist/pagination.test.js.map +1 -0
- package/dist/progress.d.ts +64 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +92 -0
- package/dist/progress.js.map +1 -0
- package/dist/progress.test.d.ts +2 -0
- package/dist/progress.test.d.ts.map +1 -0
- package/dist/progress.test.js +79 -0
- package/dist/progress.test.js.map +1 -0
- package/dist/resize-observer.d.ts +133 -0
- package/dist/resize-observer.d.ts.map +1 -0
- package/dist/resize-observer.js +358 -0
- package/dist/resize-observer.js.map +1 -0
- package/dist/resize-observer.test.d.ts +2 -0
- package/dist/resize-observer.test.d.ts.map +1 -0
- package/dist/resize-observer.test.js +377 -0
- package/dist/resize-observer.test.js.map +1 -0
- package/dist/roving.d.ts +26 -0
- package/dist/roving.d.ts.map +1 -0
- package/dist/roving.js +61 -0
- package/dist/roving.js.map +1 -0
- package/dist/roving.test.d.ts +2 -0
- package/dist/roving.test.d.ts.map +1 -0
- package/dist/roving.test.js +48 -0
- package/dist/roving.test.js.map +1 -0
- package/dist/scroll-list.d.ts +81 -0
- package/dist/scroll-list.d.ts.map +1 -0
- package/dist/scroll-list.js +175 -0
- package/dist/scroll-list.js.map +1 -0
- package/dist/scroll-list.test.d.ts +2 -0
- package/dist/scroll-list.test.d.ts.map +1 -0
- package/dist/scroll-list.test.js +135 -0
- package/dist/scroll-list.test.js.map +1 -0
- package/dist/scroll-lock.d.ts +11 -0
- package/dist/scroll-lock.d.ts.map +1 -0
- package/dist/scroll-lock.js +45 -0
- package/dist/scroll-lock.js.map +1 -0
- package/dist/scroll-lock.test.d.ts +2 -0
- package/dist/scroll-lock.test.d.ts.map +1 -0
- package/dist/scroll-lock.test.js +55 -0
- package/dist/scroll-lock.test.js.map +1 -0
- package/dist/sider.d.ts +26 -0
- package/dist/sider.d.ts.map +1 -0
- package/dist/sider.js +55 -0
- package/dist/sider.js.map +1 -0
- package/dist/sider.test.d.ts +2 -0
- package/dist/sider.test.d.ts.map +1 -0
- package/dist/sider.test.js +29 -0
- package/dist/sider.test.js.map +1 -0
- package/dist/spin.d.ts +25 -0
- package/dist/spin.d.ts.map +1 -0
- package/dist/spin.js +96 -0
- package/dist/spin.js.map +1 -0
- package/dist/spin.test.d.ts +2 -0
- package/dist/spin.test.d.ts.map +1 -0
- package/dist/spin.test.js +80 -0
- package/dist/spin.test.js.map +1 -0
- package/dist/table.d.ts +101 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +244 -0
- package/dist/table.js.map +1 -0
- package/dist/table.test.d.ts +2 -0
- package/dist/table.test.d.ts.map +1 -0
- package/dist/table.test.js +184 -0
- package/dist/table.test.js.map +1 -0
- package/dist/textarea.d.ts +48 -0
- package/dist/textarea.d.ts.map +1 -0
- package/dist/textarea.js +76 -0
- package/dist/textarea.js.map +1 -0
- package/dist/textarea.test.d.ts +2 -0
- package/dist/textarea.test.d.ts.map +1 -0
- package/dist/textarea.test.js +77 -0
- package/dist/textarea.test.js.map +1 -0
- package/dist/time.d.ts +93 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +181 -0
- package/dist/time.js.map +1 -0
- package/dist/time.test.d.ts +2 -0
- package/dist/time.test.d.ts.map +1 -0
- package/dist/time.test.js +159 -0
- package/dist/time.test.js.map +1 -0
- package/dist/toast.d.ts +60 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +135 -0
- package/dist/toast.js.map +1 -0
- package/dist/toast.test.d.ts +2 -0
- package/dist/toast.test.d.ts.map +1 -0
- package/dist/toast.test.js +127 -0
- package/dist/toast.test.js.map +1 -0
- package/dist/tree.d.ts +129 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +351 -0
- package/dist/tree.js.map +1 -0
- package/dist/tree.test.d.ts +2 -0
- package/dist/tree.test.d.ts.map +1 -0
- package/dist/tree.test.js +291 -0
- package/dist/tree.test.js.map +1 -0
- package/dist/upload.d.ts +62 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +85 -0
- package/dist/upload.js.map +1 -0
- package/dist/upload.test.d.ts +2 -0
- package/dist/upload.test.d.ts.map +1 -0
- package/dist/upload.test.js +169 -0
- package/dist/upload.test.js.map +1 -0
- package/dist/virtual-list.d.ts +83 -0
- package/dist/virtual-list.d.ts.map +1 -0
- package/dist/virtual-list.js +143 -0
- package/dist/virtual-list.js.map +1 -0
- package/dist/virtual-list.test.d.ts +2 -0
- package/dist/virtual-list.test.d.ts.map +1 -0
- package/dist/virtual-list.test.js +148 -0
- package/dist/virtual-list.test.js.map +1 -0
- package/package.json +22 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { computePosition, parsePlacement, makePlacement, } from './floating.js';
|
|
3
|
+
// trigger: 100x40 box centered-ish; popup 80x30; large viewport so no clamping
|
|
4
|
+
const trigger = { x: 200, y: 200, width: 100, height: 40 };
|
|
5
|
+
const popup = { x: 0, y: 0, width: 80, height: 30 };
|
|
6
|
+
const viewport = { width: 1000, height: 800 };
|
|
7
|
+
const base = { triggerRect: trigger, popupRect: popup, viewport, offset: 8 };
|
|
8
|
+
describe('parsePlacement / makePlacement round-trip', () => {
|
|
9
|
+
const all = [
|
|
10
|
+
'top', 'topStart', 'topEnd',
|
|
11
|
+
'bottom', 'bottomStart', 'bottomEnd',
|
|
12
|
+
'left', 'leftStart', 'leftEnd',
|
|
13
|
+
'right', 'rightStart', 'rightEnd',
|
|
14
|
+
];
|
|
15
|
+
it('round-trips every 12-way placement', () => {
|
|
16
|
+
for (const p of all) {
|
|
17
|
+
const { side, align } = parsePlacement(p);
|
|
18
|
+
expect(makePlacement(side, align)).toBe(p);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('computePosition main-axis placement', () => {
|
|
23
|
+
it('top puts popup above the trigger with offset', () => {
|
|
24
|
+
const r = computePosition({ ...base, placement: 'top' });
|
|
25
|
+
expect(r.side).toBe('top');
|
|
26
|
+
// y = trigger.y - popup.height - offset = 200 - 30 - 8 = 162
|
|
27
|
+
expect(r.y).toBe(162);
|
|
28
|
+
});
|
|
29
|
+
it('bottom puts popup below the trigger with offset', () => {
|
|
30
|
+
const r = computePosition({ ...base, placement: 'bottom' });
|
|
31
|
+
// y = trigger.y + trigger.height + offset = 200 + 40 + 8 = 248
|
|
32
|
+
expect(r.y).toBe(248);
|
|
33
|
+
});
|
|
34
|
+
it('left puts popup to the left with offset', () => {
|
|
35
|
+
const r = computePosition({ ...base, placement: 'left' });
|
|
36
|
+
// x = trigger.x - popup.width - offset = 200 - 80 - 8 = 112
|
|
37
|
+
expect(r.x).toBe(112);
|
|
38
|
+
});
|
|
39
|
+
it('right puts popup to the right with offset', () => {
|
|
40
|
+
const r = computePosition({ ...base, placement: 'right' });
|
|
41
|
+
// x = trigger.x + trigger.width + offset = 200 + 100 + 8 = 308
|
|
42
|
+
expect(r.x).toBe(308);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe('computePosition cross-axis align', () => {
|
|
46
|
+
it('top center horizontally centers popup over trigger center', () => {
|
|
47
|
+
const r = computePosition({ ...base, placement: 'top' });
|
|
48
|
+
// triggerCenterX = 250; x = 250 - 80/2 = 210
|
|
49
|
+
expect(r.x).toBe(210);
|
|
50
|
+
});
|
|
51
|
+
it('topStart aligns popup left edge with trigger left edge', () => {
|
|
52
|
+
const r = computePosition({ ...base, placement: 'topStart' });
|
|
53
|
+
expect(r.x).toBe(trigger.x); // 200
|
|
54
|
+
});
|
|
55
|
+
it('topEnd aligns popup right edge with trigger right edge', () => {
|
|
56
|
+
const r = computePosition({ ...base, placement: 'topEnd' });
|
|
57
|
+
// triggerRight - popupWidth = 300 - 80 = 220
|
|
58
|
+
expect(r.x).toBe(220);
|
|
59
|
+
});
|
|
60
|
+
it('leftStart aligns popup top edge with trigger top edge', () => {
|
|
61
|
+
const r = computePosition({ ...base, placement: 'leftStart' });
|
|
62
|
+
expect(r.y).toBe(trigger.y); // 200
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('computePosition autoAdjust flip', () => {
|
|
66
|
+
it('flips top -> bottom when there is no room above but room below', () => {
|
|
67
|
+
const nearTop = { x: 200, y: 10, width: 100, height: 40 };
|
|
68
|
+
const r = computePosition({
|
|
69
|
+
...base,
|
|
70
|
+
triggerRect: nearTop,
|
|
71
|
+
placement: 'top',
|
|
72
|
+
autoAdjust: true,
|
|
73
|
+
});
|
|
74
|
+
expect(r.side).toBe('bottom');
|
|
75
|
+
});
|
|
76
|
+
it('keeps requested side when it fits even with autoAdjust', () => {
|
|
77
|
+
const r = computePosition({ ...base, placement: 'top', autoAdjust: true });
|
|
78
|
+
expect(r.side).toBe('top');
|
|
79
|
+
});
|
|
80
|
+
it('does NOT flip when autoAdjust is off, even if overflowing', () => {
|
|
81
|
+
const nearTop = { x: 200, y: 10, width: 100, height: 40 };
|
|
82
|
+
const r = computePosition({
|
|
83
|
+
...base,
|
|
84
|
+
triggerRect: nearTop,
|
|
85
|
+
placement: 'top',
|
|
86
|
+
autoAdjust: false,
|
|
87
|
+
});
|
|
88
|
+
expect(r.side).toBe('top');
|
|
89
|
+
});
|
|
90
|
+
it('does not flip when the opposite side has even less room', () => {
|
|
91
|
+
// trigger tall, near bottom: top has 10px, bottom has ~5px → keep top? no:
|
|
92
|
+
// top space 600, bottom space tiny → requesting bottom should flip to top
|
|
93
|
+
const nearBottom = { x: 200, y: 760, width: 100, height: 30 };
|
|
94
|
+
const r = computePosition({
|
|
95
|
+
...base,
|
|
96
|
+
triggerRect: nearBottom,
|
|
97
|
+
placement: 'bottom',
|
|
98
|
+
autoAdjust: true,
|
|
99
|
+
});
|
|
100
|
+
expect(r.side).toBe('top');
|
|
101
|
+
});
|
|
102
|
+
it('flips left -> right symmetrically', () => {
|
|
103
|
+
const nearLeft = { x: 10, y: 300, width: 60, height: 40 };
|
|
104
|
+
const r = computePosition({
|
|
105
|
+
...base,
|
|
106
|
+
triggerRect: nearLeft,
|
|
107
|
+
placement: 'left',
|
|
108
|
+
autoAdjust: true,
|
|
109
|
+
});
|
|
110
|
+
expect(r.side).toBe('right');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
describe('computePosition viewport-edge clamping (cross axis)', () => {
|
|
114
|
+
it('clamps cross-axis x so popup stays inside the viewport with padding', () => {
|
|
115
|
+
// trigger at far right; top-center would push popup off-screen right
|
|
116
|
+
const farRight = { x: 980, y: 200, width: 40, height: 40 };
|
|
117
|
+
const r = computePosition({
|
|
118
|
+
...base,
|
|
119
|
+
triggerRect: farRight,
|
|
120
|
+
placement: 'top',
|
|
121
|
+
padding: 4,
|
|
122
|
+
});
|
|
123
|
+
// max x = viewport.width - popup.width - padding = 1000 - 80 - 4 = 916
|
|
124
|
+
expect(r.x).toBe(916);
|
|
125
|
+
});
|
|
126
|
+
it('clamps cross-axis x to padding on the left edge', () => {
|
|
127
|
+
const farLeft = { x: 0, y: 200, width: 20, height: 40 };
|
|
128
|
+
const r = computePosition({ ...base, triggerRect: farLeft, placement: 'top', padding: 4 });
|
|
129
|
+
expect(r.x).toBe(4);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('computePosition arrowOffset', () => {
|
|
133
|
+
it('points the arrow at the trigger center for centered placement', () => {
|
|
134
|
+
const r = computePosition({ ...base, placement: 'bottom' });
|
|
135
|
+
// popup leading x = 210, trigger center x = 250 → arrowOffset = 40
|
|
136
|
+
expect(r.arrowOffset).toBe(40);
|
|
137
|
+
});
|
|
138
|
+
it('keeps the arrow pointing at the trigger even after cross-axis clamp', () => {
|
|
139
|
+
const farRight = { x: 980, y: 200, width: 40, height: 40 };
|
|
140
|
+
const r = computePosition({ ...base, triggerRect: farRight, placement: 'top', padding: 4 });
|
|
141
|
+
// trigger center x = 1000; popup leading x clamped to 916 → 84, clamped to popup-4 = 76
|
|
142
|
+
expect(r.arrowOffset).toBe(76);
|
|
143
|
+
});
|
|
144
|
+
it('pins the arrow near the aligned edge for start alignment by default', () => {
|
|
145
|
+
const r = computePosition({ ...base, placement: 'bottomStart' });
|
|
146
|
+
// default arrowEdgeDistance 12, measured from the leading (left) edge
|
|
147
|
+
expect(r.arrowOffset).toBe(12);
|
|
148
|
+
});
|
|
149
|
+
it('pins the arrow near the aligned edge for end alignment by default', () => {
|
|
150
|
+
const r = computePosition({ ...base, placement: 'bottomEnd' });
|
|
151
|
+
// popup width 80 − edgeDistance 12 = 68
|
|
152
|
+
expect(r.arrowOffset).toBe(68);
|
|
153
|
+
});
|
|
154
|
+
it('honors a custom arrowEdgeDistance', () => {
|
|
155
|
+
const r = computePosition({ ...base, placement: 'bottomStart', arrowEdgeDistance: 20 });
|
|
156
|
+
expect(r.arrowOffset).toBe(20);
|
|
157
|
+
});
|
|
158
|
+
it('points start-aligned arrow at the trigger center when arrowPointAtCenter', () => {
|
|
159
|
+
const r = computePosition({ ...base, placement: 'bottomStart', arrowPointAtCenter: true });
|
|
160
|
+
// popup leading x = trigger.x = 200; trigger center x = 250 → 50
|
|
161
|
+
expect(r.arrowOffset).toBe(50);
|
|
162
|
+
});
|
|
163
|
+
it('points end-aligned arrow at the trigger center when arrowPointAtCenter', () => {
|
|
164
|
+
const r = computePosition({ ...base, placement: 'bottomEnd', arrowPointAtCenter: true });
|
|
165
|
+
// popup leading x = 200+100−80 = 220; trigger center x = 250 → 30
|
|
166
|
+
expect(r.arrowOffset).toBe(30);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=floating.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"floating.test.js","sourceRoot":"","sources":["../src/floating.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,eAAe,EACf,cAAc,EACd,aAAa,GAGd,MAAM,eAAe,CAAC;AAEvB,+EAA+E;AAC/E,MAAM,OAAO,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACjE,MAAM,KAAK,GAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAC1D,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAC9C,MAAM,IAAI,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAE7E,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,MAAM,GAAG,GAAgB;QACvB,KAAK,EAAE,UAAU,EAAE,QAAQ;QAC3B,QAAQ,EAAE,aAAa,EAAE,WAAW;QACpC,MAAM,EAAE,WAAW,EAAE,SAAS;QAC9B,OAAO,EAAE,YAAY,EAAE,UAAU;KAClC,CAAC;IACF,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;YAC1C,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;IACnD,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,6DAA6D;QAC7D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,+DAA+D;QAC/D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1D,4DAA4D;QAC5D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QAC3D,+DAA+D;QAC/D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kCAAkC,EAAE,GAAG,EAAE;IAChD,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACzD,6CAA6C;QAC7C,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;IACrC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,6CAA6C;QAC7C,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,OAAO,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,GAAG,IAAI;YACP,WAAW,EAAE,OAAO;YACpB,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3E,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,OAAO,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,GAAG,IAAI;YACP,WAAW,EAAE,OAAO;YACpB,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,2EAA2E;QAC3E,0EAA0E;QAC1E,MAAM,UAAU,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACpE,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,GAAG,IAAI;YACP,WAAW,EAAE,UAAU;YACvB,SAAS,EAAE,QAAQ;YACnB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,QAAQ,GAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAChE,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,GAAG,IAAI;YACP,WAAW,EAAE,QAAQ;YACrB,SAAS,EAAE,MAAM;YACjB,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,qEAAqE;QACrE,MAAM,QAAQ,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACjE,MAAM,CAAC,GAAG,eAAe,CAAC;YACxB,GAAG,IAAI;YACP,WAAW,EAAE,QAAQ;YACrB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,CAAC;SACX,CAAC,CAAC;QACH,uEAAuE;QACvE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,OAAO,GAAS,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAC9D,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,mEAAmE;QACnE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,QAAQ,GAAS,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QACjE,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5F,wFAAwF;QACxF,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;QACjE,sEAAsE;QACtE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAC/D,wCAAwC;QACxC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC,CAAC;QACxF,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3F,iEAAiE;QACjE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,CAAC,GAAG,eAAe,CAAC,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACzF,kEAAkE;QAClE,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface FocusTrap {
|
|
2
|
+
activate(): void;
|
|
3
|
+
deactivate(): void;
|
|
4
|
+
}
|
|
5
|
+
export interface FocusTrapOptions {
|
|
6
|
+
/**
|
|
7
|
+
* 是否捕获 Tab/Shift+Tab 做焦点循环。false 时仍执行进场聚焦与归还焦点,
|
|
8
|
+
* 但不拦截键盘 Tab(供 keyboard 总开关关闭键盘交互时使用)。默认 true。
|
|
9
|
+
*/
|
|
10
|
+
trapTab?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* deactivate 时是否把焦点归还给激活前聚焦的元素。默认 true。
|
|
13
|
+
* 设 false 时不归还焦点(供 Popover returnFocus=false 等场景)。
|
|
14
|
+
*/
|
|
15
|
+
returnFocus?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* 是否记忆 deactivate 时浮层内最后聚焦的元素,下次 activate 优先恢复到它
|
|
18
|
+
* (而非聚焦首个可聚焦元素)。默认 false。供 Popover rememberFocus 复用。
|
|
19
|
+
* 仅当被记忆的元素在重新 activate 时仍可聚焦才恢复,否则退回首个。
|
|
20
|
+
*/
|
|
21
|
+
rememberFocus?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function useFocusTrap(container: HTMLElement, options?: FocusTrapOptions): FocusTrap;
|
|
24
|
+
//# sourceMappingURL=focus-trap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-trap.d.ts","sourceRoot":"","sources":["../src/focus-trap.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,SAAS;IACxB,QAAQ,IAAI,IAAI,CAAC;IACjB,UAAU,IAAI,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,YAAY,CAC1B,SAAS,EAAE,WAAW,EACtB,OAAO,GAAE,gBAAqB,GAC7B,SAAS,CA0DX"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFocusTrap — trap Tab focus within a container, restore on release.
|
|
3
|
+
* Used by overlays (Modal/Drawer/Dropdown). See specs/00-foundation/a11y.spec.md.
|
|
4
|
+
*/
|
|
5
|
+
const FOCUSABLE = 'a[href],button:not([disabled]),textarea:not([disabled]),input:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
6
|
+
export function useFocusTrap(container, options = {}) {
|
|
7
|
+
const { trapTab = true, returnFocus = true, rememberFocus = false } = options;
|
|
8
|
+
let previouslyFocused = null;
|
|
9
|
+
// rememberFocus:deactivate 时记下浮层内最后聚焦的元素,下次 activate 恢复。
|
|
10
|
+
let lastInnerFocused = null;
|
|
11
|
+
function getFocusable() {
|
|
12
|
+
return Array.from(container.querySelectorAll(FOCUSABLE)).filter((el) => el.offsetParent !== null || el === document.activeElement);
|
|
13
|
+
}
|
|
14
|
+
function onKeydown(e) {
|
|
15
|
+
if (e.key !== 'Tab')
|
|
16
|
+
return;
|
|
17
|
+
const items = getFocusable();
|
|
18
|
+
if (items.length === 0) {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const first = items[0];
|
|
23
|
+
const last = items[items.length - 1];
|
|
24
|
+
const active = document.activeElement;
|
|
25
|
+
if (e.shiftKey && active === first) {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
last.focus();
|
|
28
|
+
}
|
|
29
|
+
else if (!e.shiftKey && active === last) {
|
|
30
|
+
e.preventDefault();
|
|
31
|
+
first.focus();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
activate() {
|
|
36
|
+
previouslyFocused = document.activeElement;
|
|
37
|
+
if (trapTab)
|
|
38
|
+
container.addEventListener('keydown', onKeydown);
|
|
39
|
+
// rememberFocus:若记忆的元素仍在容器内且可聚焦,恢复到它;否则聚焦首个。
|
|
40
|
+
if (rememberFocus &&
|
|
41
|
+
lastInnerFocused &&
|
|
42
|
+
container.contains(lastInnerFocused) &&
|
|
43
|
+
lastInnerFocused.offsetParent !== null) {
|
|
44
|
+
lastInnerFocused.focus();
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
getFocusable()[0]?.focus();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
deactivate() {
|
|
51
|
+
if (trapTab)
|
|
52
|
+
container.removeEventListener('keydown', onKeydown);
|
|
53
|
+
// rememberFocus:归还焦点前记下浮层内当前焦点,供下次 activate 恢复。
|
|
54
|
+
if (rememberFocus) {
|
|
55
|
+
const active = document.activeElement;
|
|
56
|
+
lastInnerFocused = active && container.contains(active) ? active : null;
|
|
57
|
+
}
|
|
58
|
+
if (returnFocus)
|
|
59
|
+
previouslyFocused?.focus();
|
|
60
|
+
previouslyFocused = null;
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=focus-trap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus-trap.js","sourceRoot":"","sources":["../src/focus-trap.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,SAAS,GACb,sIAAsI,CAAC;AA0BzI,MAAM,UAAU,YAAY,CAC1B,SAAsB,EACtB,UAA4B,EAAE;IAE9B,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,WAAW,GAAG,IAAI,EAAE,aAAa,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAC9E,IAAI,iBAAiB,GAAuB,IAAI,CAAC;IACjD,yDAAyD;IACzD,IAAI,gBAAgB,GAAuB,IAAI,CAAC;IAEhD,SAAS,YAAY;QACnB,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAc,SAAS,CAAC,CAAC,CAAC,MAAM,CAC1E,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,KAAK,IAAI,IAAI,EAAE,KAAK,QAAQ,CAAC,aAAa,CAClE,CAAC;IACJ,CAAC;IAED,SAAS,SAAS,CAAC,CAAgB;QACjC,IAAI,CAAC,CAAC,GAAG,KAAK,KAAK;YAAE,OAAO;QAC5B,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QACtC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC;QACtC,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACnC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;aAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAC1C,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ;YACN,iBAAiB,GAAG,QAAQ,CAAC,aAAmC,CAAC;YACjE,IAAI,OAAO;gBAAE,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC9D,6CAA6C;YAC7C,IACE,aAAa;gBACb,gBAAgB;gBAChB,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CAAC;gBACpC,gBAAgB,CAAC,YAAY,KAAK,IAAI,EACtC,CAAC;gBACD,gBAAgB,CAAC,KAAK,EAAE,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,UAAU;YACR,IAAI,OAAO;gBAAE,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACjE,gDAAgD;YAChD,IAAI,aAAa,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAmC,CAAC;gBAC5D,gBAAgB,GAAG,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1E,CAAC;YACD,IAAI,WAAW;gBAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;YAC5C,iBAAiB,GAAG,IAAI,CAAC;QAC3B,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/form.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export type FormValues = Record<string, unknown>;
|
|
2
|
+
/** Validation message: a string, an i18n-key descriptor, or false/undefined for "no error". */
|
|
3
|
+
export type ValidateResult = string | undefined;
|
|
4
|
+
export interface Rule {
|
|
5
|
+
required?: boolean;
|
|
6
|
+
/** numeric / string-length / value-range checks */
|
|
7
|
+
min?: number;
|
|
8
|
+
max?: number;
|
|
9
|
+
minLength?: number;
|
|
10
|
+
maxLength?: number;
|
|
11
|
+
pattern?: RegExp;
|
|
12
|
+
type?: 'email' | 'url' | 'number';
|
|
13
|
+
/** sync or async custom validator; return an error message or undefined */
|
|
14
|
+
validator?: (value: unknown, values: FormValues) => ValidateResult | Promise<ValidateResult>;
|
|
15
|
+
/** override message for the built-in rule on this entry */
|
|
16
|
+
message?: string;
|
|
17
|
+
/**
|
|
18
|
+
* when true, a failure of THIS rule produces a non-blocking warning instead of
|
|
19
|
+
* an error: it surfaces a (yellow) hint but never fails `submit`. Mirrors AntD.
|
|
20
|
+
*/
|
|
21
|
+
warningOnly?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* When validation should run. Spec §2 L26 / §4 L65. A single trigger or a list.
|
|
25
|
+
* `mount` runs once at registration; `change`/`blur` run on the matching event;
|
|
26
|
+
* `submit` only runs during submit(). The render layer reads `getFieldTrigger`
|
|
27
|
+
* to decide which DOM events to wire — core stays framework-free.
|
|
28
|
+
*/
|
|
29
|
+
export type ValidateTrigger = 'change' | 'blur' | 'submit' | 'mount';
|
|
30
|
+
export interface FieldConfig {
|
|
31
|
+
rules?: Rule[];
|
|
32
|
+
initialValue?: unknown;
|
|
33
|
+
/** label used for interpolating default messages */
|
|
34
|
+
label?: string;
|
|
35
|
+
/**
|
|
36
|
+
* names of other fields this field's validation depends on. When any listed
|
|
37
|
+
* field's value changes (validate-on-change), this field is re-validated too —
|
|
38
|
+
* e.g. a "confirm password" field depends on ["password"].
|
|
39
|
+
*/
|
|
40
|
+
dependencies?: string[];
|
|
41
|
+
/**
|
|
42
|
+
* field-level override of the form's `validateTrigger` (spec §4 L84). When
|
|
43
|
+
* omitted the field inherits the form default.
|
|
44
|
+
*/
|
|
45
|
+
trigger?: ValidateTrigger | ValidateTrigger[];
|
|
46
|
+
/**
|
|
47
|
+
* pure transform applied to the field's value when collecting values for
|
|
48
|
+
* `getFieldsValue`/`submit` (spec §4.2 L88). Does NOT mutate state — the live
|
|
49
|
+
* `values` keep the raw value; only the collected payload is transformed.
|
|
50
|
+
*/
|
|
51
|
+
transform?: (value: unknown, values: FormValues) => unknown;
|
|
52
|
+
}
|
|
53
|
+
export type FieldErrors = Record<string, string | undefined>;
|
|
54
|
+
export interface FormState {
|
|
55
|
+
values: FormValues;
|
|
56
|
+
errors: FieldErrors;
|
|
57
|
+
/** non-blocking warnings (from `warningOnly` rules); never affect submit validity */
|
|
58
|
+
warnings: FieldErrors;
|
|
59
|
+
touched: Record<string, boolean>;
|
|
60
|
+
validating: Record<string, boolean>;
|
|
61
|
+
submitting: boolean;
|
|
62
|
+
submitCount: number;
|
|
63
|
+
}
|
|
64
|
+
/** message resolver: built-in messages are emitted as keyed descriptors so the
|
|
65
|
+
* render layer can localize. We pass label/params so the layer can interpolate. */
|
|
66
|
+
export interface MessageDescriptor {
|
|
67
|
+
key: string;
|
|
68
|
+
params?: Record<string, string | number>;
|
|
69
|
+
/** explicit override text (skips i18n) */
|
|
70
|
+
text?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface FormOptions {
|
|
73
|
+
initialValues?: FormValues;
|
|
74
|
+
/** resolve a MessageDescriptor to a display string (i18n). Defaults to key. */
|
|
75
|
+
resolveMessage?: (d: MessageDescriptor) => string;
|
|
76
|
+
/**
|
|
77
|
+
* default validation trigger(s) for every field (spec §4 L65). Fields may
|
|
78
|
+
* override via `FieldConfig.trigger`. Defaults to `['blur','change']`.
|
|
79
|
+
*/
|
|
80
|
+
validateTrigger?: ValidateTrigger | ValidateTrigger[];
|
|
81
|
+
/**
|
|
82
|
+
* stop running a field's rules at the first blocking error (spec §4 L68).
|
|
83
|
+
* Default `false`: every rule runs (so warnings keep accumulating and a later
|
|
84
|
+
* blocking rule can still win); the first blocking error is the surfaced one.
|
|
85
|
+
*/
|
|
86
|
+
stopValidateWithError?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* keep empty-value field keys when collecting values for submit (spec §4 L70).
|
|
89
|
+
* Default `false`: keys whose value is `undefined`/`null`/`''` are dropped
|
|
90
|
+
* from the collected `values` payload.
|
|
91
|
+
*/
|
|
92
|
+
allowEmpty?: boolean;
|
|
93
|
+
}
|
|
94
|
+
export interface FormApi {
|
|
95
|
+
getState(): FormState;
|
|
96
|
+
subscribe(listener: (state: FormState) => void): () => void;
|
|
97
|
+
registerField(name: string, config?: FieldConfig): () => void;
|
|
98
|
+
/**
|
|
99
|
+
* resolved validation triggers for a field: its own `trigger` override, else
|
|
100
|
+
* the form default. Always a normalized array (spec §4 L65/L84). The render
|
|
101
|
+
* layer uses this to decide which DOM events to wire.
|
|
102
|
+
*/
|
|
103
|
+
getFieldTrigger(name: string): ValidateTrigger[];
|
|
104
|
+
getFieldValue(name: string): unknown;
|
|
105
|
+
setFieldValue(name: string, value: unknown, opts?: {
|
|
106
|
+
validate?: boolean;
|
|
107
|
+
}): void;
|
|
108
|
+
setFieldsValue(values: FormValues): void;
|
|
109
|
+
setFieldTouched(name: string, touched?: boolean): void;
|
|
110
|
+
getFieldError(name: string): string | undefined;
|
|
111
|
+
/** the field's non-blocking warning (from a `warningOnly` rule), if any */
|
|
112
|
+
getFieldWarning(name: string): string | undefined;
|
|
113
|
+
/** validate one field; resolves to its (blocking) error (or undefined) */
|
|
114
|
+
validateField(name: string): Promise<string | undefined>;
|
|
115
|
+
/** validate all (or given) fields; resolves to true when valid */
|
|
116
|
+
validate(names?: string[]): Promise<boolean>;
|
|
117
|
+
resetFields(): void;
|
|
118
|
+
/**
|
|
119
|
+
* collected values honoring `allowEmpty` (spec §4 L70): with `allowEmpty:false`
|
|
120
|
+
* keys whose value is empty (`undefined`/`null`/`''`) are dropped.
|
|
121
|
+
*/
|
|
122
|
+
getFieldsValue(): FormValues;
|
|
123
|
+
/** run validation then resolve { valid, values, errors } */
|
|
124
|
+
submit(): Promise<{
|
|
125
|
+
valid: boolean;
|
|
126
|
+
values: FormValues;
|
|
127
|
+
errors: FieldErrors;
|
|
128
|
+
}>;
|
|
129
|
+
}
|
|
130
|
+
export declare function createForm(options?: FormOptions): FormApi;
|
|
131
|
+
//# sourceMappingURL=form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../src/form.ts"],"names":[],"mappings":"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"}
|