@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,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createScrollList helpers — framework-agnostic single-column picker math.
|
|
3
|
+
* Pure functions only: scroll offset ↔ selected index mapping, disabled-item
|
|
4
|
+
* skipping, and keyboard target resolution. No DOM, no framework deps, no
|
|
5
|
+
* internal mutable state — the render layer (svelte) owns scroll state and the
|
|
6
|
+
* actual CSS scroll-snap. See specs/components/show/ScrollList.spec.md §3
|
|
7
|
+
* (single-column subset; momentum/cyclic/multi-column deferred).
|
|
8
|
+
*/
|
|
9
|
+
/** Map a scrollTop offset to the centered item index (clamped to range). */
|
|
10
|
+
export function offsetToIndex(scrollTop, itemHeight, count) {
|
|
11
|
+
if (itemHeight <= 0 || count <= 0)
|
|
12
|
+
return 0;
|
|
13
|
+
const raw = Math.round(scrollTop / itemHeight);
|
|
14
|
+
return Math.max(0, Math.min(count - 1, raw));
|
|
15
|
+
}
|
|
16
|
+
/** The scrollTop that centers a given index. */
|
|
17
|
+
export function indexToOffset(index, itemHeight) {
|
|
18
|
+
return index * itemHeight;
|
|
19
|
+
}
|
|
20
|
+
/** Index of an item by value, or -1. */
|
|
21
|
+
export function indexOfValue(items, value) {
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return -1;
|
|
24
|
+
return items.findIndex((it) => it.value === value);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the nearest enabled index at or after `from` in `dir` (+1/-1).
|
|
28
|
+
* If none exists in that direction, returns the original `from` unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export function nextEnabledIndex(items, from, dir) {
|
|
31
|
+
let i = from + dir;
|
|
32
|
+
while (i >= 0 && i < items.length) {
|
|
33
|
+
if (!items[i]?.disabled)
|
|
34
|
+
return i;
|
|
35
|
+
i += dir;
|
|
36
|
+
}
|
|
37
|
+
return from;
|
|
38
|
+
}
|
|
39
|
+
/** First enabled index (for Home / value resolution), or -1 if all disabled. */
|
|
40
|
+
export function firstEnabledIndex(items) {
|
|
41
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
42
|
+
if (!items[i]?.disabled)
|
|
43
|
+
return i;
|
|
44
|
+
}
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
47
|
+
/** Last enabled index (for End), or -1 if all disabled. */
|
|
48
|
+
export function lastEnabledIndex(items) {
|
|
49
|
+
for (let i = items.length - 1; i >= 0; i -= 1) {
|
|
50
|
+
if (!items[i]?.disabled)
|
|
51
|
+
return i;
|
|
52
|
+
}
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Resolve the target index for a keyboard action from the current index.
|
|
57
|
+
* Returns the current index when the action can't move (boundary / all
|
|
58
|
+
* neighbors disabled). `page` is how many rows PageUp/PageDown move.
|
|
59
|
+
*/
|
|
60
|
+
export function keyboardTarget(items, current, key, page = 3) {
|
|
61
|
+
switch (key) {
|
|
62
|
+
case 'ArrowUp':
|
|
63
|
+
return nextEnabledIndex(items, current, -1);
|
|
64
|
+
case 'ArrowDown':
|
|
65
|
+
return nextEnabledIndex(items, current, 1);
|
|
66
|
+
case 'Home': {
|
|
67
|
+
const f = firstEnabledIndex(items);
|
|
68
|
+
return f === -1 ? current : f;
|
|
69
|
+
}
|
|
70
|
+
case 'End': {
|
|
71
|
+
const l = lastEnabledIndex(items);
|
|
72
|
+
return l === -1 ? current : l;
|
|
73
|
+
}
|
|
74
|
+
case 'PageUp': {
|
|
75
|
+
let target = current;
|
|
76
|
+
for (let n = 0; n < page; n += 1) {
|
|
77
|
+
const nxt = nextEnabledIndex(items, target, -1);
|
|
78
|
+
if (nxt === target)
|
|
79
|
+
break;
|
|
80
|
+
target = nxt;
|
|
81
|
+
}
|
|
82
|
+
return target;
|
|
83
|
+
}
|
|
84
|
+
case 'PageDown': {
|
|
85
|
+
let target = current;
|
|
86
|
+
for (let n = 0; n < page; n += 1) {
|
|
87
|
+
const nxt = nextEnabledIndex(items, target, 1);
|
|
88
|
+
if (nxt === target)
|
|
89
|
+
break;
|
|
90
|
+
target = nxt;
|
|
91
|
+
}
|
|
92
|
+
return target;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
// cyclic(循环列)—— 无限轮:用「重复 N 份」+ 中段定位实现。
|
|
100
|
+
// 渲染层把 data 重复 `repeat` 份铺成一个长列表(virtual count = count*repeat),
|
|
101
|
+
// 滚到靠近首/尾时命令式跳回中段(无动画),视觉无缝。下面是纯索引数学。
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/** 正取模(结果恒非负),把任意整数索引折回 [0, count)。 */
|
|
104
|
+
export function wrapIndex(index, count) {
|
|
105
|
+
if (count <= 0)
|
|
106
|
+
return 0;
|
|
107
|
+
return ((index % count) + count) % count;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* cyclic 列的「虚拟总行数」。把真实 count 重复 repeat 份,
|
|
111
|
+
* repeat 必须为奇数且 >= 3,使「中段」居中。count<=0 返回 0。
|
|
112
|
+
*/
|
|
113
|
+
export function cyclicVirtualCount(count, repeat) {
|
|
114
|
+
if (count <= 0)
|
|
115
|
+
return 0;
|
|
116
|
+
return count * Math.max(3, repeat | 1);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* cyclic 列的「中段基准虚拟索引」:把真实索引 logical 映射到位于正中那一份的虚拟索引。
|
|
120
|
+
* 例如 count=24、repeat=5 → 中段从 2*24=48 起,logical=9 → 57。
|
|
121
|
+
*/
|
|
122
|
+
export function cyclicCenterIndex(logical, count, repeat) {
|
|
123
|
+
if (count <= 0)
|
|
124
|
+
return 0;
|
|
125
|
+
const r = Math.max(3, repeat | 1);
|
|
126
|
+
const mid = Math.floor(r / 2);
|
|
127
|
+
return mid * count + wrapIndex(logical, count);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* cyclic 滚动落定后,判断是否需要把虚拟索引「重定位」回中段(避免滚出重复区边界)。
|
|
131
|
+
* 返回新的虚拟索引(与传入逻辑值同余、但落在中段),渲染层据此命令式无动画跳转。
|
|
132
|
+
* 当 virtualIndex 已在安全中段(距首尾各至少 count 行)时返回原值(无需跳)。
|
|
133
|
+
*/
|
|
134
|
+
export function cyclicRecenter(virtualIndex, count, repeat) {
|
|
135
|
+
if (count <= 0)
|
|
136
|
+
return 0;
|
|
137
|
+
const r = Math.max(3, repeat | 1);
|
|
138
|
+
const total = count * r;
|
|
139
|
+
// 安全区:[count, total-count)。在区内不动。
|
|
140
|
+
if (virtualIndex >= count && virtualIndex < total - count)
|
|
141
|
+
return virtualIndex;
|
|
142
|
+
const logical = wrapIndex(virtualIndex, count);
|
|
143
|
+
return cyclicCenterIndex(logical, count, repeat);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 推进一帧惯性:指数衰减 v' = v * decay^(dt/16.67),位移取平均速度积分。
|
|
147
|
+
* @param velocity 当前速度(px/ms,正=向下/scrollTop 增大)
|
|
148
|
+
* @param dt 帧间隔(ms)
|
|
149
|
+
* @param decay 每 ~16.67ms(60fps 一帧)的衰减系数,默认 0.95
|
|
150
|
+
* @param minV 停止阈值(px/ms),默认 0.02
|
|
151
|
+
*/
|
|
152
|
+
export function momentumStep(velocity, dt, decay = 0.95, minV = 0.02) {
|
|
153
|
+
const frames = dt / (1000 / 60);
|
|
154
|
+
const next = velocity * Math.pow(decay, frames);
|
|
155
|
+
// 平均速度积分(梯形)得位移,避免高速时单帧跨度过大。
|
|
156
|
+
const delta = ((velocity + next) / 2) * dt;
|
|
157
|
+
return { velocity: next, delta, done: Math.abs(next) < minV };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 根据当前 scrollTop 与剩余速度预测「最终吸附目标行」(含一点惯性前瞻),clamp 到 [0,count)。
|
|
161
|
+
* 用于释放瞬间直接算出目标行,再 smooth 吸附(比逐帧更稳,避免抖动)。
|
|
162
|
+
* @param scrollTop 当前偏移
|
|
163
|
+
* @param velocity 当前速度(px/ms)
|
|
164
|
+
* @param itemHeight 行高
|
|
165
|
+
* @param count 行数
|
|
166
|
+
* @param projection 速度前瞻系数(默认 80:v*80 px 的滑行预估)
|
|
167
|
+
*/
|
|
168
|
+
export function projectSettleIndex(scrollTop, velocity, itemHeight, count, projection = 80) {
|
|
169
|
+
if (itemHeight <= 0 || count <= 0)
|
|
170
|
+
return 0;
|
|
171
|
+
const projected = scrollTop + velocity * projection;
|
|
172
|
+
const raw = Math.round(projected / itemHeight);
|
|
173
|
+
return Math.max(0, Math.min(count - 1, raw));
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=scroll-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-list.js","sourceRoot":"","sources":["../src/scroll-list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-list.test.d.ts","sourceRoot":"","sources":["../src/scroll-list.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { offsetToIndex, indexToOffset, indexOfValue, nextEnabledIndex, firstEnabledIndex, lastEnabledIndex, keyboardTarget, wrapIndex, cyclicVirtualCount, cyclicCenterIndex, cyclicRecenter, momentumStep, projectSettleIndex, } from './scroll-list.js';
|
|
3
|
+
const items = [
|
|
4
|
+
{ value: 0, label: '0' },
|
|
5
|
+
{ value: 1, label: '1', disabled: true },
|
|
6
|
+
{ value: 2, label: '2' },
|
|
7
|
+
{ value: 3, label: '3' },
|
|
8
|
+
{ value: 4, label: '4', disabled: true },
|
|
9
|
+
{ value: 5, label: '5' },
|
|
10
|
+
];
|
|
11
|
+
describe('offset ↔ index', () => {
|
|
12
|
+
it('rounds offset to nearest index and clamps', () => {
|
|
13
|
+
expect(offsetToIndex(0, 36, 6)).toBe(0);
|
|
14
|
+
expect(offsetToIndex(40, 36, 6)).toBe(1); // 40/36 ≈ 1.1 → 1
|
|
15
|
+
expect(offsetToIndex(90, 36, 6)).toBe(3); // 90/36 = 2.5 → 3 (round half up)
|
|
16
|
+
expect(offsetToIndex(9999, 36, 6)).toBe(5); // clamp to last
|
|
17
|
+
expect(offsetToIndex(-50, 36, 6)).toBe(0); // clamp to first
|
|
18
|
+
});
|
|
19
|
+
it('indexToOffset centers an index', () => {
|
|
20
|
+
expect(indexToOffset(0, 36)).toBe(0);
|
|
21
|
+
expect(indexToOffset(3, 36)).toBe(108);
|
|
22
|
+
});
|
|
23
|
+
it('handles degenerate inputs', () => {
|
|
24
|
+
expect(offsetToIndex(10, 0, 6)).toBe(0);
|
|
25
|
+
expect(offsetToIndex(10, 36, 0)).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('indexOfValue', () => {
|
|
29
|
+
it('finds the index of a value', () => {
|
|
30
|
+
expect(indexOfValue(items, 3)).toBe(3);
|
|
31
|
+
expect(indexOfValue(items, 99)).toBe(-1);
|
|
32
|
+
expect(indexOfValue(items, undefined)).toBe(-1);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe('enabled-index navigation (skips disabled)', () => {
|
|
36
|
+
it('nextEnabledIndex skips disabled in both directions', () => {
|
|
37
|
+
expect(nextEnabledIndex(items, 0, 1)).toBe(2); // skip disabled 1
|
|
38
|
+
expect(nextEnabledIndex(items, 3, 1)).toBe(5); // skip disabled 4
|
|
39
|
+
expect(nextEnabledIndex(items, 2, -1)).toBe(0); // skip disabled 1
|
|
40
|
+
});
|
|
41
|
+
it('returns the same index at a boundary', () => {
|
|
42
|
+
expect(nextEnabledIndex(items, 5, 1)).toBe(5); // no enabled after
|
|
43
|
+
expect(nextEnabledIndex(items, 0, -1)).toBe(0); // no enabled before
|
|
44
|
+
});
|
|
45
|
+
it('first/last enabled', () => {
|
|
46
|
+
expect(firstEnabledIndex(items)).toBe(0);
|
|
47
|
+
expect(lastEnabledIndex(items)).toBe(5);
|
|
48
|
+
expect(firstEnabledIndex([{ value: 'x', label: 'x', disabled: true }])).toBe(-1);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('keyboardTarget', () => {
|
|
52
|
+
it('ArrowUp/Down skip disabled', () => {
|
|
53
|
+
expect(keyboardTarget(items, 0, 'ArrowDown')).toBe(2);
|
|
54
|
+
expect(keyboardTarget(items, 2, 'ArrowUp')).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
it('Home/End jump to first/last enabled', () => {
|
|
57
|
+
expect(keyboardTarget(items, 3, 'Home')).toBe(0);
|
|
58
|
+
expect(keyboardTarget(items, 0, 'End')).toBe(5);
|
|
59
|
+
});
|
|
60
|
+
it('PageDown moves up to `page` enabled rows, stopping at boundary', () => {
|
|
61
|
+
// from 0, page 3, enabled order: 0→2→3→5 ; 3 steps → 5
|
|
62
|
+
expect(keyboardTarget(items, 0, 'PageDown', 3)).toBe(5);
|
|
63
|
+
// from 0, page 2 → 0→2→3 → index 3
|
|
64
|
+
expect(keyboardTarget(items, 0, 'PageDown', 2)).toBe(3);
|
|
65
|
+
});
|
|
66
|
+
it('PageUp stops at the first enabled', () => {
|
|
67
|
+
expect(keyboardTarget(items, 5, 'PageUp', 10)).toBe(0);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('cyclic 循环索引', () => {
|
|
71
|
+
it('wrapIndex 正取模折回 [0,count)', () => {
|
|
72
|
+
expect(wrapIndex(0, 24)).toBe(0);
|
|
73
|
+
expect(wrapIndex(24, 24)).toBe(0);
|
|
74
|
+
expect(wrapIndex(25, 24)).toBe(1);
|
|
75
|
+
expect(wrapIndex(-1, 24)).toBe(23);
|
|
76
|
+
expect(wrapIndex(-25, 24)).toBe(23);
|
|
77
|
+
expect(wrapIndex(5, 0)).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
it('cyclicVirtualCount 重复奇数份', () => {
|
|
80
|
+
expect(cyclicVirtualCount(24, 5)).toBe(120);
|
|
81
|
+
expect(cyclicVirtualCount(24, 4)).toBe(120); // 4 |1 = 5
|
|
82
|
+
expect(cyclicVirtualCount(24, 1)).toBe(72); // min 3
|
|
83
|
+
expect(cyclicVirtualCount(0, 5)).toBe(0);
|
|
84
|
+
});
|
|
85
|
+
it('cyclicCenterIndex 映射到中段', () => {
|
|
86
|
+
// count=24, repeat=5 → mid=2 → 中段从 48 起
|
|
87
|
+
expect(cyclicCenterIndex(9, 24, 5)).toBe(57);
|
|
88
|
+
expect(cyclicCenterIndex(0, 24, 5)).toBe(48);
|
|
89
|
+
expect(cyclicCenterIndex(24 + 9, 24, 5)).toBe(57); // 同余
|
|
90
|
+
});
|
|
91
|
+
it('cyclicRecenter 仅在边界外重定位', () => {
|
|
92
|
+
// total=120, 安全区 [24,96)
|
|
93
|
+
expect(cyclicRecenter(57, 24, 5)).toBe(57); // 区内不动
|
|
94
|
+
expect(cyclicRecenter(10, 24, 5)).toBe(cyclicCenterIndex(10, 24, 5)); // 越下界
|
|
95
|
+
expect(cyclicRecenter(100, 24, 5)).toBe(cyclicCenterIndex(wrapIndex(100, 24), 24, 5));
|
|
96
|
+
// 重定位后必同余
|
|
97
|
+
expect(wrapIndex(cyclicRecenter(5, 24, 5), 24)).toBe(5);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('惯性物理 momentum', () => {
|
|
101
|
+
it('momentumStep 按帧指数衰减且方向不变', () => {
|
|
102
|
+
const s = momentumStep(1, 16.67, 0.95, 0.02);
|
|
103
|
+
expect(s.velocity).toBeLessThan(1);
|
|
104
|
+
expect(s.velocity).toBeGreaterThan(0);
|
|
105
|
+
expect(s.delta).toBeGreaterThan(0);
|
|
106
|
+
expect(s.done).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
it('momentumStep 低于阈值标记 done', () => {
|
|
109
|
+
const s = momentumStep(0.01, 16.67, 0.95, 0.02);
|
|
110
|
+
expect(s.done).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('momentumStep 负速度(向上)位移为负', () => {
|
|
113
|
+
const s = momentumStep(-1, 16.67);
|
|
114
|
+
expect(s.delta).toBeLessThan(0);
|
|
115
|
+
expect(s.velocity).toBeGreaterThan(-1);
|
|
116
|
+
});
|
|
117
|
+
it('momentumStep 连续推进速度单调收敛到 0', () => {
|
|
118
|
+
let v = 2;
|
|
119
|
+
for (let i = 0; i < 200; i += 1)
|
|
120
|
+
v = momentumStep(v, 16.67).velocity;
|
|
121
|
+
expect(Math.abs(v)).toBeLessThan(0.02);
|
|
122
|
+
});
|
|
123
|
+
it('projectSettleIndex 含速度前瞻并 clamp', () => {
|
|
124
|
+
// scrollTop 0,速度 0 → 行 0
|
|
125
|
+
expect(projectSettleIndex(0, 0, 36, 24)).toBe(0);
|
|
126
|
+
// scrollTop=360(行10),向下速度前瞻 80px:(360+80)/36≈12.2 → round 12
|
|
127
|
+
expect(projectSettleIndex(360, 1, 36, 24, 80)).toBe(12);
|
|
128
|
+
// clamp 到末行
|
|
129
|
+
expect(projectSettleIndex(99999, 5, 36, 24)).toBe(23);
|
|
130
|
+
// clamp 到首行
|
|
131
|
+
expect(projectSettleIndex(0, -5, 36, 24)).toBe(0);
|
|
132
|
+
expect(projectSettleIndex(0, 0, 0, 24)).toBe(0);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=scroll-list.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-list.test.js","sourceRoot":"","sources":["../src/scroll-list.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useScrollLock — lock body scroll while an overlay (Modal/Drawer) is open.
|
|
3
|
+
* Compensates for the scrollbar width to avoid layout shift. Reference-counted
|
|
4
|
+
* so nested/stacked overlays don't unlock prematurely. Call the returned
|
|
5
|
+
* function to release. No framework deps; client-only (guards window/document).
|
|
6
|
+
* See specs/components/feedback/Modal.spec.md §6.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useScrollLock(): () => void;
|
|
9
|
+
/** test-only reset of the internal counter */
|
|
10
|
+
export declare function __resetScrollLock(): void;
|
|
11
|
+
//# sourceMappingURL=scroll-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-lock.d.ts","sourceRoot":"","sources":["../src/scroll-lock.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,wBAAgB,aAAa,IAAI,MAAM,IAAI,CA4B1C;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,IAAI,IAAI,CAIxC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useScrollLock — lock body scroll while an overlay (Modal/Drawer) is open.
|
|
3
|
+
* Compensates for the scrollbar width to avoid layout shift. Reference-counted
|
|
4
|
+
* so nested/stacked overlays don't unlock prematurely. Call the returned
|
|
5
|
+
* function to release. No framework deps; client-only (guards window/document).
|
|
6
|
+
* See specs/components/feedback/Modal.spec.md §6.
|
|
7
|
+
*/
|
|
8
|
+
let lockCount = 0;
|
|
9
|
+
let savedOverflow = '';
|
|
10
|
+
let savedPaddingRight = '';
|
|
11
|
+
export function useScrollLock() {
|
|
12
|
+
if (typeof document === 'undefined')
|
|
13
|
+
return () => { };
|
|
14
|
+
const body = document.body;
|
|
15
|
+
if (lockCount === 0) {
|
|
16
|
+
savedOverflow = body.style.overflow;
|
|
17
|
+
savedPaddingRight = body.style.paddingRight;
|
|
18
|
+
// scrollbar-width compensation so content doesn't jump
|
|
19
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
20
|
+
if (scrollbarWidth > 0) {
|
|
21
|
+
const current = parseFloat(window.getComputedStyle(body).paddingRight) || 0;
|
|
22
|
+
body.style.paddingRight = `${current + scrollbarWidth}px`;
|
|
23
|
+
}
|
|
24
|
+
body.style.overflow = 'hidden';
|
|
25
|
+
}
|
|
26
|
+
lockCount += 1;
|
|
27
|
+
let released = false;
|
|
28
|
+
return () => {
|
|
29
|
+
if (released)
|
|
30
|
+
return;
|
|
31
|
+
released = true;
|
|
32
|
+
lockCount = Math.max(0, lockCount - 1);
|
|
33
|
+
if (lockCount === 0) {
|
|
34
|
+
body.style.overflow = savedOverflow;
|
|
35
|
+
body.style.paddingRight = savedPaddingRight;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** test-only reset of the internal counter */
|
|
40
|
+
export function __resetScrollLock() {
|
|
41
|
+
lockCount = 0;
|
|
42
|
+
savedOverflow = '';
|
|
43
|
+
savedPaddingRight = '';
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=scroll-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-lock.js","sourceRoot":"","sources":["../src/scroll-lock.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,IAAI,aAAa,GAAG,EAAE,CAAC;AACvB,IAAI,iBAAiB,GAAG,EAAE,CAAC;AAE3B,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;IAErD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAE3B,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;QACpC,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC;QAC5C,uDAAuD;QACvD,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,GAAG,QAAQ,CAAC,eAAe,CAAC,WAAW,CAAC;QAChF,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YAC5E,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,OAAO,GAAG,cAAc,IAAI,CAAC;QAC5D,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;IACjC,CAAC;IACD,SAAS,IAAI,CAAC,CAAC;IAEf,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,GAAG,EAAE;QACV,IAAI,QAAQ;YAAE,OAAO;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC;QACvC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,aAAa,CAAC;YACpC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,iBAAiB,CAAC;QAC9C,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,iBAAiB;IAC/B,SAAS,GAAG,CAAC,CAAC;IACd,aAAa,GAAG,EAAE,CAAC;IACnB,iBAAiB,GAAG,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-lock.test.d.ts","sourceRoot":"","sources":["../src/scroll-lock.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
import { useScrollLock, __resetScrollLock } from './scroll-lock.js';
|
|
3
|
+
// node test env has no DOM; stub the minimal document/window surface useScrollLock touches.
|
|
4
|
+
function makeDom() {
|
|
5
|
+
const body = { style: { overflow: '', paddingRight: '' } };
|
|
6
|
+
return {
|
|
7
|
+
document: {
|
|
8
|
+
body,
|
|
9
|
+
documentElement: { clientWidth: 1000 },
|
|
10
|
+
},
|
|
11
|
+
window: {
|
|
12
|
+
innerWidth: 1000, // == clientWidth → no scrollbar compensation
|
|
13
|
+
getComputedStyle: () => ({ paddingRight: '0px' }),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe('useScrollLock', () => {
|
|
18
|
+
let dom;
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
__resetScrollLock();
|
|
21
|
+
dom = makeDom();
|
|
22
|
+
vi.stubGlobal('document', dom.document);
|
|
23
|
+
vi.stubGlobal('window', dom.window);
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => vi.unstubAllGlobals());
|
|
26
|
+
it('locks body overflow and releases it', () => {
|
|
27
|
+
const release = useScrollLock();
|
|
28
|
+
expect(dom.document.body.style.overflow).toBe('hidden');
|
|
29
|
+
release();
|
|
30
|
+
expect(dom.document.body.style.overflow).toBe('');
|
|
31
|
+
});
|
|
32
|
+
it('is reference-counted: nested locks unlock only at zero', () => {
|
|
33
|
+
const r1 = useScrollLock();
|
|
34
|
+
const r2 = useScrollLock();
|
|
35
|
+
expect(dom.document.body.style.overflow).toBe('hidden');
|
|
36
|
+
r1();
|
|
37
|
+
expect(dom.document.body.style.overflow).toBe('hidden'); // still held by r2
|
|
38
|
+
r2();
|
|
39
|
+
expect(dom.document.body.style.overflow).toBe('');
|
|
40
|
+
});
|
|
41
|
+
it('release is idempotent', () => {
|
|
42
|
+
const release = useScrollLock();
|
|
43
|
+
release();
|
|
44
|
+
release(); // no throw, no double-decrement
|
|
45
|
+
expect(dom.document.body.style.overflow).toBe('');
|
|
46
|
+
});
|
|
47
|
+
it('restores the original overflow value', () => {
|
|
48
|
+
dom.document.body.style.overflow = 'scroll';
|
|
49
|
+
const release = useScrollLock();
|
|
50
|
+
expect(dom.document.body.style.overflow).toBe('hidden');
|
|
51
|
+
release();
|
|
52
|
+
expect(dom.document.body.style.overflow).toBe('scroll');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=scroll-lock.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scroll-lock.test.js","sourceRoot":"","sources":["../src/scroll-lock.test.ts"],"names":[],"mappings":"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"}
|
package/dist/sider.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Breakpoint } from './breakpoints.js';
|
|
2
|
+
export type SiderTrigger = 'click' | 'breakpoint';
|
|
3
|
+
export type { Breakpoint };
|
|
4
|
+
export interface SiderOptions {
|
|
5
|
+
/** controlled collapsed value; when provided, internal state is not mutated */
|
|
6
|
+
collapsed?: boolean;
|
|
7
|
+
defaultCollapsed?: boolean;
|
|
8
|
+
/** auto-collapse below this breakpoint */
|
|
9
|
+
breakpoint?: Breakpoint;
|
|
10
|
+
onChange?: (collapsed: boolean, trigger: SiderTrigger) => void;
|
|
11
|
+
onBreakpoint?: (matched: boolean, breakpoint: Breakpoint) => void;
|
|
12
|
+
}
|
|
13
|
+
export interface SiderApi {
|
|
14
|
+
/** stable id for aria-controls */
|
|
15
|
+
readonly id: string;
|
|
16
|
+
/** current collapsed state */
|
|
17
|
+
getCollapsed(): boolean;
|
|
18
|
+
/** subscribe to collapsed changes; returns unsubscribe */
|
|
19
|
+
subscribe(listener: (collapsed: boolean) => void): () => void;
|
|
20
|
+
toggle(): void;
|
|
21
|
+
setCollapsed(value: boolean, trigger?: SiderTrigger): void;
|
|
22
|
+
/** start matchMedia breakpoint watching (call on mount). no-op without breakpoint. */
|
|
23
|
+
watchBreakpoint(): () => void;
|
|
24
|
+
}
|
|
25
|
+
export declare function createSider(options?: SiderOptions): SiderApi;
|
|
26
|
+
//# sourceMappingURL=sider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sider.d.ts","sourceRoot":"","sources":["../src/sider.ts"],"names":[],"mappings":"AAMA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,YAAY,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0CAA0C;IAC1C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/D,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,QAAQ;IACvB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,YAAY,IAAI,OAAO,CAAC;IACxB,0DAA0D;IAC1D,SAAS,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,MAAM,IAAI,IAAI,CAAC;IACf,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3D,sFAAsF;IACtF,eAAe,IAAI,MAAM,IAAI,CAAC;CAC/B;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,QAAQ,CAsDhE"}
|
package/dist/sider.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createSider — framework-agnostic collapse state machine for Layout.Sider.
|
|
3
|
+
* Handles controlled/uncontrolled collapse + optional breakpoint matchMedia.
|
|
4
|
+
* No DOM rendering, no framework deps. See specs/components/basic/Layout.spec.md.
|
|
5
|
+
*/
|
|
6
|
+
import { useId } from './id.js';
|
|
7
|
+
import { BREAKPOINTS } from './breakpoints.js';
|
|
8
|
+
export function createSider(options = {}) {
|
|
9
|
+
const { collapsed: controlled, defaultCollapsed = false, breakpoint, onChange, onBreakpoint, } = options;
|
|
10
|
+
const isControlled = controlled !== undefined;
|
|
11
|
+
let internal = isControlled ? controlled : defaultCollapsed;
|
|
12
|
+
const listeners = new Set();
|
|
13
|
+
function current() {
|
|
14
|
+
return isControlled ? controlled : internal;
|
|
15
|
+
}
|
|
16
|
+
function emit() {
|
|
17
|
+
for (const l of listeners)
|
|
18
|
+
l(current());
|
|
19
|
+
}
|
|
20
|
+
function setCollapsed(value, trigger = 'click') {
|
|
21
|
+
if (!isControlled) {
|
|
22
|
+
if (internal === value)
|
|
23
|
+
return;
|
|
24
|
+
internal = value;
|
|
25
|
+
emit();
|
|
26
|
+
}
|
|
27
|
+
onChange?.(value, trigger);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
id: useId('cd-sider'),
|
|
31
|
+
getCollapsed: current,
|
|
32
|
+
subscribe(listener) {
|
|
33
|
+
listeners.add(listener);
|
|
34
|
+
return () => listeners.delete(listener);
|
|
35
|
+
},
|
|
36
|
+
toggle() {
|
|
37
|
+
setCollapsed(!current(), 'click');
|
|
38
|
+
},
|
|
39
|
+
setCollapsed,
|
|
40
|
+
watchBreakpoint() {
|
|
41
|
+
if (!breakpoint || typeof window === 'undefined' || !window.matchMedia) {
|
|
42
|
+
return () => { };
|
|
43
|
+
}
|
|
44
|
+
const mql = window.matchMedia(`(max-width: ${BREAKPOINTS[breakpoint] - 0.02}px)`);
|
|
45
|
+
const handle = (e) => {
|
|
46
|
+
onBreakpoint?.(e.matches, breakpoint);
|
|
47
|
+
setCollapsed(e.matches, 'breakpoint');
|
|
48
|
+
};
|
|
49
|
+
handle(mql);
|
|
50
|
+
mql.addEventListener('change', handle);
|
|
51
|
+
return () => mql.removeEventListener('change', handle);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=sider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sider.js","sourceRoot":"","sources":["../src/sider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,WAAW,EAAmB,MAAM,kBAAkB,CAAC;AA4BhE,MAAM,UAAU,WAAW,CAAC,UAAwB,EAAE;IACpD,MAAM,EACJ,SAAS,EAAE,UAAU,EACrB,gBAAgB,GAAG,KAAK,EACxB,UAAU,EACV,QAAQ,EACR,YAAY,GACb,GAAG,OAAO,CAAC;IAEZ,MAAM,YAAY,GAAG,UAAU,KAAK,SAAS,CAAC;IAC9C,IAAI,QAAQ,GAAG,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC5D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;IAE1D,SAAS,OAAO;QACd,OAAO,YAAY,CAAC,CAAC,CAAE,UAAsB,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC3D,CAAC;IACD,SAAS,IAAI;QACX,KAAK,MAAM,CAAC,IAAI,SAAS;YAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,SAAS,YAAY,CAAC,KAAc,EAAE,UAAwB,OAAO;QACnE,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,IAAI,QAAQ,KAAK,KAAK;gBAAE,OAAO;YAC/B,QAAQ,GAAG,KAAK,CAAC;YACjB,IAAI,EAAE,CAAC;QACT,CAAC;QACD,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,UAAU,CAAC;QACrB,YAAY,EAAE,OAAO;QACrB,SAAS,CAAC,QAAQ;YAChB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM;YACJ,YAAY,CAAC,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;QACD,YAAY;QACZ,eAAe;YACb,IAAI,CAAC,UAAU,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;gBACvE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;YAClB,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,eAAe,WAAW,CAAC,UAAU,CAAC,GAAG,IAAI,KAAK,CAAC,CAAC;YAClF,MAAM,MAAM,GAAG,CAAC,CAAuC,EAAQ,EAAE;gBAC/D,YAAY,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;gBACtC,YAAY,CAAC,CAAC,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;YACxC,CAAC,CAAC;YACF,MAAM,CAAC,GAAG,CAAC,CAAC;YACZ,GAAG,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACvC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sider.test.d.ts","sourceRoot":"","sources":["../src/sider.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { createSider } from './sider.js';
|
|
3
|
+
describe('createSider', () => {
|
|
4
|
+
it('uncontrolled: toggles and notifies subscribers', () => {
|
|
5
|
+
const s = createSider({ defaultCollapsed: false });
|
|
6
|
+
const seen = [];
|
|
7
|
+
s.subscribe((c) => seen.push(c));
|
|
8
|
+
expect(s.getCollapsed()).toBe(false);
|
|
9
|
+
s.toggle();
|
|
10
|
+
expect(s.getCollapsed()).toBe(true);
|
|
11
|
+
expect(seen).toEqual([true]);
|
|
12
|
+
});
|
|
13
|
+
it('controlled: does not mutate internal, still fires onChange', () => {
|
|
14
|
+
const onChange = vi.fn();
|
|
15
|
+
const s = createSider({ collapsed: false, onChange });
|
|
16
|
+
s.toggle();
|
|
17
|
+
expect(s.getCollapsed()).toBe(false); // unchanged (controlled)
|
|
18
|
+
expect(onChange).toHaveBeenCalledWith(true, 'click');
|
|
19
|
+
});
|
|
20
|
+
it('exposes a stable id for aria-controls', () => {
|
|
21
|
+
const s = createSider();
|
|
22
|
+
expect(s.id).toMatch(/^cd-sider-/);
|
|
23
|
+
});
|
|
24
|
+
it('watchBreakpoint is a no-op without a breakpoint', () => {
|
|
25
|
+
const s = createSider();
|
|
26
|
+
expect(typeof s.watchBreakpoint()).toBe('function');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=sider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sider.test.js","sourceRoot":"","sources":["../src/sider.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,GAAc,EAAE,CAAC;QAC3B,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CAAC,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,CAAC,GAAG,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QACtD,CAAC,CAAC,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,yBAAyB;QAC/D,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,GAAG,WAAW,EAAE,CAAC;QACxB,MAAM,CAAC,OAAO,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/spin.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createSpinController — framework-agnostic loading-state debouncer for Spin.
|
|
3
|
+
* Handles `delay` (wait before showing, to avoid flicker on fast requests) and
|
|
4
|
+
* `minShowTime` (keep showing at least this long once shown). Uses timers only;
|
|
5
|
+
* SSR-safe because the consumer starts it in a client effect. No DOM, no
|
|
6
|
+
* framework deps. See specs/components/feedback/Spin.spec.md §3.
|
|
7
|
+
*/
|
|
8
|
+
export interface SpinOptions {
|
|
9
|
+
delay?: number;
|
|
10
|
+
minShowTime?: number;
|
|
11
|
+
/** initial spinning request */
|
|
12
|
+
spinning?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface SpinController {
|
|
15
|
+
/** the debounced, effective visibility — what the render layer should show */
|
|
16
|
+
getEffective(): boolean;
|
|
17
|
+
/** subscribe to effective-visibility changes; returns unsubscribe */
|
|
18
|
+
subscribe(listener: (effective: boolean) => void): () => void;
|
|
19
|
+
/** push a new spinning request (controlled by the host) */
|
|
20
|
+
setSpinning(value: boolean): void;
|
|
21
|
+
/** clear timers (call on unmount) */
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
24
|
+
export declare function createSpinController(options?: SpinOptions): SpinController;
|
|
25
|
+
//# sourceMappingURL=spin.d.ts.map
|