@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,291 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { flattenVisible, findNode, collectExpandable, collectExpandableToDepth, conduct, toggleCheck, normalizeToLeaves, computeFilteredKeys, computeDropPosition, isAncestorOrSelf, collectCheckedByStrategy, siblingKeys, accordionExpand, } from './tree.js';
|
|
3
|
+
const data = [
|
|
4
|
+
{
|
|
5
|
+
key: '1',
|
|
6
|
+
label: 'Root 1',
|
|
7
|
+
children: [
|
|
8
|
+
{ key: '1-1', label: 'Child 1-1' },
|
|
9
|
+
{
|
|
10
|
+
key: '1-2',
|
|
11
|
+
label: 'Child 1-2',
|
|
12
|
+
children: [
|
|
13
|
+
{ key: '1-2-1', label: 'Leaf 1-2-1' },
|
|
14
|
+
{ key: '1-2-2', label: 'Leaf 1-2-2' },
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
{ key: '2', label: 'Root 2' },
|
|
20
|
+
];
|
|
21
|
+
describe('flattenVisible', () => {
|
|
22
|
+
it('shows only roots when nothing expanded', () => {
|
|
23
|
+
const flat = flattenVisible(data, new Set());
|
|
24
|
+
expect(flat.map((f) => f.node.key)).toEqual(['1', '2']);
|
|
25
|
+
expect(flat[0]?.hasChildren).toBe(true);
|
|
26
|
+
expect(flat[1]?.hasChildren).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it('reveals children of expanded nodes in document order', () => {
|
|
29
|
+
const flat = flattenVisible(data, new Set(['1', '1-2']));
|
|
30
|
+
expect(flat.map((f) => f.node.key)).toEqual([
|
|
31
|
+
'1',
|
|
32
|
+
'1-1',
|
|
33
|
+
'1-2',
|
|
34
|
+
'1-2-1',
|
|
35
|
+
'1-2-2',
|
|
36
|
+
'2',
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
it('reports level, posInSet and setSize', () => {
|
|
40
|
+
const flat = flattenVisible(data, new Set(['1']));
|
|
41
|
+
const child = flat.find((f) => f.node.key === '1-2');
|
|
42
|
+
expect(child?.level).toBe(1);
|
|
43
|
+
expect(child?.posInSet).toBe(2);
|
|
44
|
+
expect(child?.setSize).toBe(2);
|
|
45
|
+
expect(child?.parentKey).toBe('1');
|
|
46
|
+
});
|
|
47
|
+
it('reports isLast and ancestorIsLast for tree guide lines', () => {
|
|
48
|
+
const flat = flattenVisible(data, new Set(['1', '1-2']));
|
|
49
|
+
const byKey = (k) => flat.find((f) => f.node.key === k);
|
|
50
|
+
// root '1' has sibling '2' after it → not last; root level has no ancestors
|
|
51
|
+
expect(byKey('1')?.isLast).toBe(false);
|
|
52
|
+
expect(byKey('1')?.ancestorIsLast).toEqual([]);
|
|
53
|
+
// '1-2' is the last child of '1'; its ancestor '1' was NOT last
|
|
54
|
+
expect(byKey('1-2')?.isLast).toBe(true);
|
|
55
|
+
expect(byKey('1-2')?.ancestorIsLast).toEqual([false]);
|
|
56
|
+
// '1-2-1' ancestors: '1'(false) then '1-2'(true)
|
|
57
|
+
expect(byKey('1-2-1')?.ancestorIsLast).toEqual([false, true]);
|
|
58
|
+
expect(byKey('1-2-1')?.isLast).toBe(false);
|
|
59
|
+
// last root '2'
|
|
60
|
+
expect(byKey('2')?.isLast).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('findNode / collectExpandable', () => {
|
|
64
|
+
it('finds a deep node', () => {
|
|
65
|
+
expect(findNode(data, '1-2-1')?.label).toBe('Leaf 1-2-1');
|
|
66
|
+
expect(findNode(data, 'nope')).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
it('collects every parent key', () => {
|
|
69
|
+
expect(collectExpandable(data).sort()).toEqual(['1', '1-2']);
|
|
70
|
+
});
|
|
71
|
+
it('collects parents up to a depth', () => {
|
|
72
|
+
expect(collectExpandableToDepth(data, 1)).toEqual(['1']);
|
|
73
|
+
expect(collectExpandableToDepth(data, 2).sort()).toEqual(['1', '1-2']);
|
|
74
|
+
});
|
|
75
|
+
it('depth 0 (or below) expands nothing', () => {
|
|
76
|
+
expect(collectExpandableToDepth(data, 0)).toEqual([]);
|
|
77
|
+
expect(collectExpandableToDepth(data, -1)).toEqual([]);
|
|
78
|
+
});
|
|
79
|
+
it('depth beyond tree height is clamped to every parent', () => {
|
|
80
|
+
expect(collectExpandableToDepth(data, 99).sort()).toEqual(collectExpandable(data).sort());
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('conduct (related mode)', () => {
|
|
84
|
+
it('checking a parent fully checks it; leaves drive parent state', () => {
|
|
85
|
+
// base = the two leaves of 1-2
|
|
86
|
+
const { checked, half } = conduct(data, new Set(['1-2-1', '1-2-2']));
|
|
87
|
+
expect(checked.has('1-2')).toBe(true); // all children checked → parent checked
|
|
88
|
+
expect(half.has('1')).toBe(true); // 1 has 1-1 unchecked → half
|
|
89
|
+
expect(checked.has('1')).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
it('partial children → parent half-checked', () => {
|
|
92
|
+
const { checked, half } = conduct(data, new Set(['1-2-1']));
|
|
93
|
+
expect(half.has('1-2')).toBe(true);
|
|
94
|
+
expect(half.has('1')).toBe(true);
|
|
95
|
+
expect(checked.has('1-2')).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
it('all leaves checked → root checked, no half', () => {
|
|
98
|
+
const { checked, half } = conduct(data, new Set(['1-1', '1-2-1', '1-2-2']));
|
|
99
|
+
expect(checked.has('1')).toBe(true);
|
|
100
|
+
expect(half.size).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
it('disabled leaves are excluded from the count', () => {
|
|
103
|
+
const d = [
|
|
104
|
+
{
|
|
105
|
+
key: 'p',
|
|
106
|
+
label: 'P',
|
|
107
|
+
children: [
|
|
108
|
+
{ key: 'a', label: 'A' },
|
|
109
|
+
{ key: 'b', label: 'B', disabled: true },
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
// only enabled leaf 'a' checked → parent fully checked (b ignored)
|
|
114
|
+
const { checked, half } = conduct(d, new Set(['a']));
|
|
115
|
+
expect(checked.has('p')).toBe(true);
|
|
116
|
+
expect(half.has('p')).toBe(false);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('toggleCheck', () => {
|
|
120
|
+
it('toggles a leaf on and off', () => {
|
|
121
|
+
const on = toggleCheck(data, new Set(), '1-1');
|
|
122
|
+
expect(on.has('1-1')).toBe(true);
|
|
123
|
+
const off = toggleCheck(data, on, '1-1');
|
|
124
|
+
expect(off.has('1-1')).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
it('toggling a parent toggles all its leaves', () => {
|
|
127
|
+
const on = toggleCheck(data, new Set(), '1-2');
|
|
128
|
+
expect(on.has('1-2-1')).toBe(true);
|
|
129
|
+
expect(on.has('1-2-2')).toBe(true);
|
|
130
|
+
const off = toggleCheck(data, on, '1-2');
|
|
131
|
+
expect(off.has('1-2-1')).toBe(false);
|
|
132
|
+
expect(off.has('1-2-2')).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
it('round-trips conduct output: unchecking a leaf after conduct removes it', () => {
|
|
135
|
+
// simulate the controlled flow: check a parent, run conduct (adds parent keys),
|
|
136
|
+
// feed that polluted set back, then uncheck a single leaf.
|
|
137
|
+
const base = toggleCheck(data, new Set(), '1'); // check whole subtree under root 1
|
|
138
|
+
const { checked } = conduct(data, base); // checked now contains parent keys '1','1-2'
|
|
139
|
+
expect(checked.has('1')).toBe(true);
|
|
140
|
+
// feed conduct output (with parent keys) back and uncheck a deep leaf
|
|
141
|
+
const next = toggleCheck(data, checked, '1-2-1');
|
|
142
|
+
expect(next.has('1-2-1')).toBe(false); // the leaf is actually removed
|
|
143
|
+
expect(next.has('1-1')).toBe(true); // siblings stay
|
|
144
|
+
const re = conduct(data, next);
|
|
145
|
+
expect(re.checked.has('1')).toBe(false); // root no longer fully checked
|
|
146
|
+
expect(re.half.has('1')).toBe(true); // root half-checked
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('normalizeToLeaves', () => {
|
|
150
|
+
it('expands parent keys to their leaves and keeps leaves as-is', () => {
|
|
151
|
+
const norm = normalizeToLeaves(data, new Set(['1-2', '1-1']));
|
|
152
|
+
expect([...norm].sort()).toEqual(['1-1', '1-2-1', '1-2-2']);
|
|
153
|
+
});
|
|
154
|
+
it('drops unknown keys', () => {
|
|
155
|
+
expect(normalizeToLeaves(data, new Set(['ghost'])).size).toBe(0);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('computeFilteredKeys', () => {
|
|
159
|
+
it('expands ancestor chain of a match', () => {
|
|
160
|
+
const { matched, expand } = computeFilteredKeys(data, (n) => n.label.includes('1-2-1'));
|
|
161
|
+
expect(matched.has('1-2-1')).toBe(true);
|
|
162
|
+
expect(expand.has('1')).toBe(true);
|
|
163
|
+
expect(expand.has('1-2')).toBe(true);
|
|
164
|
+
expect(expand.has('2')).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
it('returns empty sets when nothing matches', () => {
|
|
167
|
+
const { matched, expand } = computeFilteredKeys(data, () => false);
|
|
168
|
+
expect(matched.size).toBe(0);
|
|
169
|
+
expect(expand.size).toBe(0);
|
|
170
|
+
});
|
|
171
|
+
it('works with an arbitrary custom predicate (filterTreeNode 谓词)', () => {
|
|
172
|
+
// a predicate keying off something other than label substring
|
|
173
|
+
const { matched, expand } = computeFilteredKeys(data, (n) => String(n.key).endsWith('-1'));
|
|
174
|
+
expect(matched.has('1-1')).toBe(true);
|
|
175
|
+
expect(matched.has('1-2-1')).toBe(true);
|
|
176
|
+
// ancestor chain of every match is expanded
|
|
177
|
+
expect(expand.has('1')).toBe(true);
|
|
178
|
+
expect(expand.has('1-2')).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe('computeDropPosition', () => {
|
|
182
|
+
it('splits a 32px row into before / inside / after thirds', () => {
|
|
183
|
+
expect(computeDropPosition(4, 32)).toBe('before'); // < 8 (1/4)
|
|
184
|
+
expect(computeDropPosition(16, 32)).toBe('inside'); // middle
|
|
185
|
+
expect(computeDropPosition(28, 32)).toBe('after'); // > 24 (3/4)
|
|
186
|
+
});
|
|
187
|
+
it('boundaries: edges fold to inside', () => {
|
|
188
|
+
expect(computeDropPosition(8, 32)).toBe('inside'); // not < 8
|
|
189
|
+
expect(computeDropPosition(24, 32)).toBe('inside'); // not > 24
|
|
190
|
+
});
|
|
191
|
+
it('clamps out-of-range offsets', () => {
|
|
192
|
+
expect(computeDropPosition(-10, 32)).toBe('before');
|
|
193
|
+
expect(computeDropPosition(999, 32)).toBe('after');
|
|
194
|
+
});
|
|
195
|
+
it('only before/after when inside not allowed (leaf target)', () => {
|
|
196
|
+
expect(computeDropPosition(10, 32, false)).toBe('before');
|
|
197
|
+
expect(computeDropPosition(20, 32, false)).toBe('after');
|
|
198
|
+
expect(computeDropPosition(16, 32, false)).toBe('after'); // exactly half → after
|
|
199
|
+
});
|
|
200
|
+
it('degenerate row height returns after', () => {
|
|
201
|
+
expect(computeDropPosition(0, 0)).toBe('after');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe('isAncestorOrSelf', () => {
|
|
205
|
+
it('is true for the same key', () => {
|
|
206
|
+
expect(isAncestorOrSelf(data, '1', '1')).toBe(true);
|
|
207
|
+
});
|
|
208
|
+
it('is true for a descendant', () => {
|
|
209
|
+
expect(isAncestorOrSelf(data, '1', '1-1')).toBe(true);
|
|
210
|
+
expect(isAncestorOrSelf(data, '1', '1-2-1')).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
it('is false for unrelated nodes', () => {
|
|
213
|
+
expect(isAncestorOrSelf(data, '1-1', '1')).toBe(false);
|
|
214
|
+
expect(isAncestorOrSelf(data, '2', '1-1')).toBe(false);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
describe('siblingKeys', () => {
|
|
218
|
+
it('returns root siblings excluding self', () => {
|
|
219
|
+
expect(siblingKeys(data, '1')).toEqual(['2']);
|
|
220
|
+
expect(siblingKeys(data, '2')).toEqual(['1']);
|
|
221
|
+
});
|
|
222
|
+
it('returns same-parent siblings excluding self', () => {
|
|
223
|
+
expect(siblingKeys(data, '1-1')).toEqual(['1-2']);
|
|
224
|
+
expect(siblingKeys(data, '1-2')).toEqual(['1-1']);
|
|
225
|
+
});
|
|
226
|
+
it('returns deeper-level siblings', () => {
|
|
227
|
+
expect(siblingKeys(data, '1-2-1')).toEqual(['1-2-2']);
|
|
228
|
+
});
|
|
229
|
+
it('returns empty for unknown key', () => {
|
|
230
|
+
expect(siblingKeys(data, 'nope')).toEqual([]);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('accordionExpand', () => {
|
|
234
|
+
it('adds key and removes its siblings', () => {
|
|
235
|
+
// expand 1-2 while 1-1 already expanded → 1-1 collapses
|
|
236
|
+
const next = accordionExpand(data, new Set(['1', '1-1']), '1-2');
|
|
237
|
+
expect([...next].sort()).toEqual(['1', '1-2']);
|
|
238
|
+
});
|
|
239
|
+
it('keeps non-sibling expanded nodes (different level not mutually exclusive)', () => {
|
|
240
|
+
// 1 (root) expanded, expand 1-2-1 deep child → root 1 untouched
|
|
241
|
+
const next = accordionExpand(data, new Set(['1', '1-2']), '1-2-1');
|
|
242
|
+
expect([...next].sort()).toEqual(['1', '1-2', '1-2-1']);
|
|
243
|
+
});
|
|
244
|
+
it('collapses other root when expanding a root', () => {
|
|
245
|
+
const next = accordionExpand(data, new Set(['2']), '1');
|
|
246
|
+
expect([...next]).toEqual(['1']);
|
|
247
|
+
});
|
|
248
|
+
it('does not mutate the input set', () => {
|
|
249
|
+
const input = new Set(['1', '1-1']);
|
|
250
|
+
accordionExpand(data, input, '1-2');
|
|
251
|
+
expect([...input].sort()).toEqual(['1', '1-1']);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
describe('collectCheckedByStrategy', () => {
|
|
255
|
+
// Fully check the whole tree: conduct returns every node as checked.
|
|
256
|
+
const allChecked = conduct(data, new Set(['1', '2'])).checked;
|
|
257
|
+
it("'all' returns every checked node in document order", () => {
|
|
258
|
+
expect(collectCheckedByStrategy(data, allChecked, 'all')).toEqual([
|
|
259
|
+
'1',
|
|
260
|
+
'1-1',
|
|
261
|
+
'1-2',
|
|
262
|
+
'1-2-1',
|
|
263
|
+
'1-2-2',
|
|
264
|
+
'2',
|
|
265
|
+
]);
|
|
266
|
+
});
|
|
267
|
+
it("'parent' collapses fully-checked subtrees to top-most parents", () => {
|
|
268
|
+
expect(collectCheckedByStrategy(data, allChecked, 'parent')).toEqual(['1', '2']);
|
|
269
|
+
});
|
|
270
|
+
it("'child' keeps only checked leaves", () => {
|
|
271
|
+
expect(collectCheckedByStrategy(data, allChecked, 'child')).toEqual([
|
|
272
|
+
'1-1',
|
|
273
|
+
'1-2-1',
|
|
274
|
+
'1-2-2',
|
|
275
|
+
'2',
|
|
276
|
+
]);
|
|
277
|
+
});
|
|
278
|
+
it('partial check: parent keeps deepest fully-checked node, not half parent', () => {
|
|
279
|
+
const partial = conduct(data, new Set(['1-2'])).checked; // 1-2 + its leaves
|
|
280
|
+
expect(collectCheckedByStrategy(data, partial, 'parent')).toEqual(['1-2']);
|
|
281
|
+
expect(collectCheckedByStrategy(data, partial, 'child')).toEqual(['1-2-1', '1-2-2']);
|
|
282
|
+
expect(collectCheckedByStrategy(data, partial, 'all')).toEqual(['1-2', '1-2-1', '1-2-2']);
|
|
283
|
+
});
|
|
284
|
+
it('empty checked set yields empty for all strategies', () => {
|
|
285
|
+
const empty = new Set();
|
|
286
|
+
expect(collectCheckedByStrategy(data, empty, 'all')).toEqual([]);
|
|
287
|
+
expect(collectCheckedByStrategy(data, empty, 'parent')).toEqual([]);
|
|
288
|
+
expect(collectCheckedByStrategy(data, empty, 'child')).toEqual([]);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
//# sourceMappingURL=tree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.test.js","sourceRoot":"","sources":["../src/tree.test.ts"],"names":[],"mappings":"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"}
|
package/dist/upload.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upload — framework-agnostic helpers for file upload progress/state.
|
|
3
|
+
* Pure functions; the render layer owns the XHR. See specs/components/input/Upload.spec.md.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Percent complete (0–100, integer) from loaded/total bytes. Clamps to [0,100];
|
|
7
|
+
* returns 0 when total is 0/unknown (避免除零得到 NaN/Infinity)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function computeUploadPercent(loaded: number, total: number): number;
|
|
10
|
+
/** Whether an HTTP status code counts as a successful upload (2xx). */
|
|
11
|
+
export declare function isUploadOk(status: number): boolean;
|
|
12
|
+
/** A unit of work the queue can run; resolves when the upload settles. */
|
|
13
|
+
export type UploadTask = () => void | Promise<void>;
|
|
14
|
+
export interface UploadQueue {
|
|
15
|
+
/** Enqueue a task; runs immediately if a slot is free, else waits in FIFO order. */
|
|
16
|
+
add: (task: UploadTask) => void;
|
|
17
|
+
/** Number of tasks currently running. */
|
|
18
|
+
readonly active: number;
|
|
19
|
+
/** Number of tasks waiting for a free slot. */
|
|
20
|
+
readonly pending: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Concurrency-limited FIFO scheduler. At most `limit` tasks run at once;
|
|
24
|
+
* when one settles, the next queued task starts (complete-one-refill-one).
|
|
25
|
+
*
|
|
26
|
+
* `limit <= 0` means unlimited — every task starts immediately, preserving
|
|
27
|
+
* the no-throttle behaviour. Pure of any DOM: the render layer wraps each
|
|
28
|
+
* XHR in a task whose promise settles on load/error/abort.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createUploadQueue(limit?: number): UploadQueue;
|
|
31
|
+
/** Why a file failed size validation (or `null` when it passes). */
|
|
32
|
+
export type UploadSizeError = 'min' | 'max' | null;
|
|
33
|
+
/**
|
|
34
|
+
* Validate one file's size against optional bounds (both in KB, matching the
|
|
35
|
+
* `maxSize` / `minSize` props). Pure: the render layer maps the result onto
|
|
36
|
+
* item status + an i18n message.
|
|
37
|
+
*
|
|
38
|
+
* - `max` when `maxSize` is set and `size > maxSize * 1024`.
|
|
39
|
+
* - `min` when `minSize` is set and `size < minSize * 1024`.
|
|
40
|
+
* - `null` otherwise (no bound set, or within range).
|
|
41
|
+
*
|
|
42
|
+
* `max` is checked first so a single offending file reports the upper bound
|
|
43
|
+
* when both are somehow violated (cannot happen for a coherent min ≤ max).
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateFileSize(bytes: number, opts?: {
|
|
46
|
+
minSize?: number | undefined;
|
|
47
|
+
maxSize?: number | undefined;
|
|
48
|
+
}): UploadSizeError;
|
|
49
|
+
/**
|
|
50
|
+
* Normalise a `beforeUpload` result into an action for one file.
|
|
51
|
+
* - `false` → skip the file (treat as rejected/removed).
|
|
52
|
+
* - `true` / `undefined` → upload the original file.
|
|
53
|
+
* - a `File` → upload this (possibly transformed) file instead.
|
|
54
|
+
*/
|
|
55
|
+
export type BeforeUploadResult = boolean | File | void;
|
|
56
|
+
export interface BeforeUploadDecision {
|
|
57
|
+
upload: boolean;
|
|
58
|
+
/** The file to upload when `upload` is true (original or replacement). */
|
|
59
|
+
file?: File;
|
|
60
|
+
}
|
|
61
|
+
export declare function resolveBeforeUpload(original: File, result: BeforeUploadResult): BeforeUploadDecision;
|
|
62
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,0EAA0E;AAC1E,MAAM,MAAM,UAAU,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEpD,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,GAAG,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IAChC,yCAAyC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,SAAI,GAAG,WAAW,CAgCxD;AAED,oEAAoE;AACpE,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,CAAC;AAEnD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAO,GACxE,eAAe,CAKjB;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,EACd,MAAM,EAAE,kBAAkB,GACzB,oBAAoB,CAItB"}
|
package/dist/upload.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* upload — framework-agnostic helpers for file upload progress/state.
|
|
3
|
+
* Pure functions; the render layer owns the XHR. See specs/components/input/Upload.spec.md.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Percent complete (0–100, integer) from loaded/total bytes. Clamps to [0,100];
|
|
7
|
+
* returns 0 when total is 0/unknown (避免除零得到 NaN/Infinity)。
|
|
8
|
+
*/
|
|
9
|
+
export function computeUploadPercent(loaded, total) {
|
|
10
|
+
if (total <= 0)
|
|
11
|
+
return 0;
|
|
12
|
+
const pct = Math.round((loaded / total) * 100);
|
|
13
|
+
return Math.min(100, Math.max(0, pct));
|
|
14
|
+
}
|
|
15
|
+
/** Whether an HTTP status code counts as a successful upload (2xx). */
|
|
16
|
+
export function isUploadOk(status) {
|
|
17
|
+
return status >= 200 && status < 300;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Concurrency-limited FIFO scheduler. At most `limit` tasks run at once;
|
|
21
|
+
* when one settles, the next queued task starts (complete-one-refill-one).
|
|
22
|
+
*
|
|
23
|
+
* `limit <= 0` means unlimited — every task starts immediately, preserving
|
|
24
|
+
* the no-throttle behaviour. Pure of any DOM: the render layer wraps each
|
|
25
|
+
* XHR in a task whose promise settles on load/error/abort.
|
|
26
|
+
*/
|
|
27
|
+
export function createUploadQueue(limit = 0) {
|
|
28
|
+
const waiting = [];
|
|
29
|
+
let active = 0;
|
|
30
|
+
const unlimited = limit <= 0;
|
|
31
|
+
function runNext() {
|
|
32
|
+
while ((unlimited || active < limit) && waiting.length > 0) {
|
|
33
|
+
const task = waiting.shift();
|
|
34
|
+
active++;
|
|
35
|
+
// Normalise sync/async/throwing tasks into a single settle path.
|
|
36
|
+
Promise.resolve()
|
|
37
|
+
.then(task)
|
|
38
|
+
.catch(() => { })
|
|
39
|
+
.finally(() => {
|
|
40
|
+
active--;
|
|
41
|
+
runNext();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
add(task) {
|
|
47
|
+
waiting.push(task);
|
|
48
|
+
runNext();
|
|
49
|
+
},
|
|
50
|
+
get active() {
|
|
51
|
+
return active;
|
|
52
|
+
},
|
|
53
|
+
get pending() {
|
|
54
|
+
return waiting.length;
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate one file's size against optional bounds (both in KB, matching the
|
|
60
|
+
* `maxSize` / `minSize` props). Pure: the render layer maps the result onto
|
|
61
|
+
* item status + an i18n message.
|
|
62
|
+
*
|
|
63
|
+
* - `max` when `maxSize` is set and `size > maxSize * 1024`.
|
|
64
|
+
* - `min` when `minSize` is set and `size < minSize * 1024`.
|
|
65
|
+
* - `null` otherwise (no bound set, or within range).
|
|
66
|
+
*
|
|
67
|
+
* `max` is checked first so a single offending file reports the upper bound
|
|
68
|
+
* when both are somehow violated (cannot happen for a coherent min ≤ max).
|
|
69
|
+
*/
|
|
70
|
+
export function validateFileSize(bytes, opts = {}) {
|
|
71
|
+
const { minSize, maxSize } = opts;
|
|
72
|
+
if (maxSize !== undefined && bytes > maxSize * 1024)
|
|
73
|
+
return 'max';
|
|
74
|
+
if (minSize !== undefined && bytes < minSize * 1024)
|
|
75
|
+
return 'min';
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
export function resolveBeforeUpload(original, result) {
|
|
79
|
+
if (result === false)
|
|
80
|
+
return { upload: false };
|
|
81
|
+
if (result instanceof File)
|
|
82
|
+
return { upload: true, file: result };
|
|
83
|
+
return { upload: true, file: original };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../src/upload.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,KAAa;IAChE,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC;AACvC,CAAC;AAcD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAK,GAAG,CAAC;IACzC,MAAM,OAAO,GAAiB,EAAE,CAAC;IACjC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,SAAS,GAAG,KAAK,IAAI,CAAC,CAAC;IAE7B,SAAS,OAAO;QACd,OAAO,CAAC,SAAS,IAAI,MAAM,GAAG,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,EAAG,CAAC;YAC9B,MAAM,EAAE,CAAC;YACT,iEAAiE;YACjE,OAAO,CAAC,OAAO,EAAE;iBACd,IAAI,CAAC,IAAI,CAAC;iBACV,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;iBACf,OAAO,CAAC,GAAG,EAAE;gBACZ,MAAM,EAAE,CAAC;gBACT,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACP,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,CAAC,IAAgB;YAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,MAAM;YACR,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,OAAO;YACT,OAAO,OAAO,CAAC,MAAM,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC;AAKD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAa,EACb,OAAuE,EAAE;IAEzE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAClC,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,GAAG,OAAO,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,GAAG,OAAO,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC;IAClE,OAAO,IAAI,CAAC;AACd,CAAC;AAgBD,MAAM,UAAU,mBAAmB,CACjC,QAAc,EACd,MAA0B;IAE1B,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC/C,IAAI,MAAM,YAAY,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAClE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.test.d.ts","sourceRoot":"","sources":["../src/upload.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { computeUploadPercent, isUploadOk, createUploadQueue, resolveBeforeUpload, validateFileSize, } from './upload.js';
|
|
3
|
+
/** A deferred whose promise resolves only when `resolve()` is called. */
|
|
4
|
+
function defer() {
|
|
5
|
+
let resolve;
|
|
6
|
+
const promise = new Promise((r) => {
|
|
7
|
+
resolve = r;
|
|
8
|
+
});
|
|
9
|
+
return { promise, resolve };
|
|
10
|
+
}
|
|
11
|
+
describe('computeUploadPercent', () => {
|
|
12
|
+
it('computes integer percent from loaded/total', () => {
|
|
13
|
+
expect(computeUploadPercent(50, 200)).toBe(25);
|
|
14
|
+
expect(computeUploadPercent(200, 200)).toBe(100);
|
|
15
|
+
expect(computeUploadPercent(0, 200)).toBe(0);
|
|
16
|
+
});
|
|
17
|
+
it('rounds to the nearest integer', () => {
|
|
18
|
+
expect(computeUploadPercent(1, 3)).toBe(33);
|
|
19
|
+
expect(computeUploadPercent(2, 3)).toBe(67);
|
|
20
|
+
});
|
|
21
|
+
it('clamps into [0,100]', () => {
|
|
22
|
+
expect(computeUploadPercent(300, 200)).toBe(100);
|
|
23
|
+
expect(computeUploadPercent(-10, 200)).toBe(0);
|
|
24
|
+
});
|
|
25
|
+
it('returns 0 when total is 0 or negative (avoid divide-by-zero)', () => {
|
|
26
|
+
expect(computeUploadPercent(10, 0)).toBe(0);
|
|
27
|
+
expect(computeUploadPercent(10, -5)).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('isUploadOk', () => {
|
|
31
|
+
it('treats 2xx as success', () => {
|
|
32
|
+
expect(isUploadOk(200)).toBe(true);
|
|
33
|
+
expect(isUploadOk(201)).toBe(true);
|
|
34
|
+
expect(isUploadOk(299)).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it('treats non-2xx as failure', () => {
|
|
37
|
+
expect(isUploadOk(199)).toBe(false);
|
|
38
|
+
expect(isUploadOk(300)).toBe(false);
|
|
39
|
+
expect(isUploadOk(404)).toBe(false);
|
|
40
|
+
expect(isUploadOk(500)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('createUploadQueue', () => {
|
|
44
|
+
it('never runs more than `limit` tasks at once', async () => {
|
|
45
|
+
const q = createUploadQueue(2);
|
|
46
|
+
const defers = [defer(), defer(), defer(), defer()];
|
|
47
|
+
const started = [];
|
|
48
|
+
defers.forEach((d, i) => {
|
|
49
|
+
q.add(() => {
|
|
50
|
+
started.push(i);
|
|
51
|
+
return d.promise;
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
// Synchronously, only the first 2 may start; the rest queue.
|
|
55
|
+
await Promise.resolve();
|
|
56
|
+
expect(started).toEqual([0, 1]);
|
|
57
|
+
expect(q.active).toBe(2);
|
|
58
|
+
expect(q.pending).toBe(2);
|
|
59
|
+
});
|
|
60
|
+
it('refills a freed slot when a task completes (FIFO order)', async () => {
|
|
61
|
+
const q = createUploadQueue(2);
|
|
62
|
+
const defers = [defer(), defer(), defer(), defer()];
|
|
63
|
+
const started = [];
|
|
64
|
+
defers.forEach((d, i) => {
|
|
65
|
+
q.add(() => {
|
|
66
|
+
started.push(i);
|
|
67
|
+
return d.promise;
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
await Promise.resolve();
|
|
71
|
+
expect(started).toEqual([0, 1]);
|
|
72
|
+
// Finish task 0 → task 2 should start.
|
|
73
|
+
defers[0].resolve();
|
|
74
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
75
|
+
expect(started).toEqual([0, 1, 2]);
|
|
76
|
+
// Finish task 1 → task 3 should start.
|
|
77
|
+
defers[1].resolve();
|
|
78
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
79
|
+
expect(started).toEqual([0, 1, 2, 3]);
|
|
80
|
+
expect(q.pending).toBe(0);
|
|
81
|
+
});
|
|
82
|
+
it('runs everything immediately when limit <= 0 (unlimited)', async () => {
|
|
83
|
+
const q = createUploadQueue(0);
|
|
84
|
+
const defers = [defer(), defer(), defer()];
|
|
85
|
+
const started = [];
|
|
86
|
+
defers.forEach((d, i) => {
|
|
87
|
+
q.add(() => {
|
|
88
|
+
started.push(i);
|
|
89
|
+
return d.promise;
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
await Promise.resolve();
|
|
93
|
+
expect(started).toEqual([0, 1, 2]);
|
|
94
|
+
expect(q.active).toBe(3);
|
|
95
|
+
expect(q.pending).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
it('keeps draining even if a task rejects', async () => {
|
|
98
|
+
const q = createUploadQueue(1);
|
|
99
|
+
const started = [];
|
|
100
|
+
q.add(() => {
|
|
101
|
+
started.push(0);
|
|
102
|
+
return Promise.reject(new Error('boom'));
|
|
103
|
+
});
|
|
104
|
+
q.add(() => {
|
|
105
|
+
started.push(1);
|
|
106
|
+
});
|
|
107
|
+
// Let the rejected task settle and the next one run.
|
|
108
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
109
|
+
expect(started).toEqual([0, 1]);
|
|
110
|
+
expect(q.active).toBe(0);
|
|
111
|
+
expect(q.pending).toBe(0);
|
|
112
|
+
});
|
|
113
|
+
it('handles synchronous tasks', async () => {
|
|
114
|
+
const q = createUploadQueue(2);
|
|
115
|
+
const ran = [];
|
|
116
|
+
for (let i = 0; i < 5; i++)
|
|
117
|
+
q.add(() => void ran.push(i));
|
|
118
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
119
|
+
expect(ran).toEqual([0, 1, 2, 3, 4]);
|
|
120
|
+
expect(q.active).toBe(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe('validateFileSize', () => {
|
|
124
|
+
it('passes when no bounds are set', () => {
|
|
125
|
+
expect(validateFileSize(0)).toBeNull();
|
|
126
|
+
expect(validateFileSize(10 * 1024 * 1024, {})).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
it('reports "max" when over maxSize (KB)', () => {
|
|
129
|
+
// maxSize 100KB → boundary is 102400 bytes.
|
|
130
|
+
expect(validateFileSize(102_401, { maxSize: 100 })).toBe('max');
|
|
131
|
+
expect(validateFileSize(102_400, { maxSize: 100 })).toBeNull();
|
|
132
|
+
expect(validateFileSize(50_000, { maxSize: 100 })).toBeNull();
|
|
133
|
+
});
|
|
134
|
+
it('reports "min" when under minSize (KB)', () => {
|
|
135
|
+
// minSize 10KB → boundary is 10240 bytes.
|
|
136
|
+
expect(validateFileSize(10_239, { minSize: 10 })).toBe('min');
|
|
137
|
+
expect(validateFileSize(10_240, { minSize: 10 })).toBeNull();
|
|
138
|
+
expect(validateFileSize(50_000, { minSize: 10 })).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
it('passes within [minSize, maxSize]', () => {
|
|
141
|
+
expect(validateFileSize(50_000, { minSize: 10, maxSize: 100 })).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
it('checks max before min', () => {
|
|
144
|
+
// size below min AND above max is impossible for min<=max; verify max wins
|
|
145
|
+
// when both technically match a degenerate inverted range.
|
|
146
|
+
expect(validateFileSize(200_000, { minSize: 300, maxSize: 100 })).toBe('max');
|
|
147
|
+
});
|
|
148
|
+
it('treats 0-byte files as under any minSize', () => {
|
|
149
|
+
expect(validateFileSize(0, { minSize: 1 })).toBe('min');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('resolveBeforeUpload', () => {
|
|
153
|
+
const original = new File(['a'], 'a.txt');
|
|
154
|
+
it('uploads the original on true / undefined', () => {
|
|
155
|
+
expect(resolveBeforeUpload(original, true)).toEqual({ upload: true, file: original });
|
|
156
|
+
expect(resolveBeforeUpload(original, undefined)).toEqual({ upload: true, file: original });
|
|
157
|
+
});
|
|
158
|
+
it('skips the file on false', () => {
|
|
159
|
+
expect(resolveBeforeUpload(original, false)).toEqual({ upload: false });
|
|
160
|
+
});
|
|
161
|
+
it('replaces with a returned File', () => {
|
|
162
|
+
const replacement = new File(['bb'], 'b.txt');
|
|
163
|
+
expect(resolveBeforeUpload(original, replacement)).toEqual({
|
|
164
|
+
upload: true,
|
|
165
|
+
file: replacement,
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=upload.test.js.map
|