@inkra/react 0.3.0 → 0.4.3
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/README.md +15 -3
- package/dist/Button.js +3 -2
- package/dist/PencilIcon.d.ts +10 -0
- package/dist/PencilIcon.js +83 -39
- package/dist/PencilLine.js +9 -1
- package/dist/PencilText.js +15 -9
- package/dist/accordion/Accordion.js +65 -5
- package/dist/alert-dialog/index.js +14 -4
- package/dist/audio-player/index.d.ts +2 -0
- package/dist/audio-player/index.js +126 -31
- package/dist/calendar/index.d.ts +4 -1
- package/dist/calendar/index.js +31 -12
- package/dist/carousel/index.js +13 -6
- package/dist/choice-grid/index.d.ts +6 -1
- package/dist/choice-grid/index.js +61 -33
- package/dist/color-picker/GradientEditor.d.ts +39 -0
- package/dist/color-picker/GradientEditor.js +63 -0
- package/dist/color-picker/gradient.d.ts +25 -0
- package/dist/color-picker/gradient.js +60 -0
- package/dist/color-picker/index.d.ts +27 -7
- package/dist/color-picker/index.js +134 -32
- package/dist/color-swatch/index.d.ts +10 -0
- package/dist/color-swatch/index.js +9 -0
- package/dist/command/index.js +27 -4
- package/dist/content/index.d.ts +5 -0
- package/dist/content/index.js +12 -3
- package/dist/context-menu/index.js +1 -1
- package/dist/data-table/index.d.ts +7 -1
- package/dist/data-table/index.js +64 -16
- package/dist/data-table/model.d.ts +2 -0
- package/dist/data-table/model.js +7 -0
- package/dist/date-picker/index.d.ts +2 -0
- package/dist/date-picker/index.js +10 -9
- package/dist/day-agenda/index.d.ts +6 -1
- package/dist/day-agenda/index.js +46 -21
- package/dist/day-agenda/layout.js +65 -9
- package/dist/display/ProgressRing.js +7 -3
- package/dist/display/index.d.ts +4 -1
- package/dist/display/index.js +3 -2
- package/dist/dockable-panel/index.js +5 -5
- package/dist/drawer/index.js +4 -3
- package/dist/editable/index.d.ts +24 -2
- package/dist/editable/index.js +142 -37
- package/dist/empty/illustrations.d.ts +8 -0
- package/dist/empty/illustrations.js +78 -0
- package/dist/empty/index.d.ts +6 -1
- package/dist/empty/index.js +11 -3
- package/dist/flip/index.js +28 -8
- package/dist/form/Field.js +11 -10
- package/dist/form/Textarea.d.ts +3 -1
- package/dist/form/Textarea.js +6 -4
- package/dist/hotkey/index.d.ts +1 -0
- package/dist/hotkey/index.js +6 -4
- package/dist/hover-card/index.js +1 -1
- package/dist/image-cropper/index.js +4 -2
- package/dist/image-viewer/VideoResource.d.ts +21 -0
- package/dist/image-viewer/VideoResource.js +194 -0
- package/dist/image-viewer/index.d.ts +14 -0
- package/dist/image-viewer/index.js +118 -65
- package/dist/image-viewer/lucideGeometry.d.ts +3 -0
- package/dist/image-viewer/lucideGeometry.js +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/input-number/index.js +9 -4
- package/dist/internal/cache.d.ts +4 -0
- package/dist/internal/cache.js +18 -1
- package/dist/internal/iconGeometry.d.ts +2 -0
- package/dist/internal/iconGeometry.js +32 -0
- package/dist/internal/renderTokens.d.ts +4 -0
- package/dist/internal/renderTokens.js +4 -0
- package/dist/internal/stroke.d.ts +7 -0
- package/dist/internal/stroke.js +63 -7
- package/dist/internal/surfaceBudget.d.ts +20 -0
- package/dist/internal/surfaceBudget.js +69 -0
- package/dist/internal/surfaceRenderer.js +87 -28
- package/dist/internal/surfaceVisibility.d.ts +3 -0
- package/dist/internal/surfaceVisibility.js +60 -0
- package/dist/internal/textRenderer.d.ts +2 -0
- package/dist/internal/textRenderer.js +3 -3
- package/dist/internal/usePencilSurface.js +17 -1
- package/dist/internal/wash.js +4 -3
- package/dist/journey-map/terrain.js +10 -5
- package/dist/layout/index.js +1 -1
- package/dist/log/index.js +29 -22
- package/dist/markdown/CodeBlock.d.ts +19 -0
- package/dist/markdown/CodeBlock.js +86 -0
- package/dist/markdown/Deletion.d.ts +2 -0
- package/dist/markdown/Deletion.js +69 -0
- package/dist/markdown/icons.d.ts +14 -0
- package/dist/markdown/icons.js +16 -0
- package/dist/markdown/index.d.ts +17 -0
- package/dist/markdown/index.js +175 -0
- package/dist/node-graph/index.d.ts +2 -0
- package/dist/node-graph/index.js +49 -25
- package/dist/number-ticker/precision.js +9 -2
- package/dist/overflow-list/index.js +5 -2
- package/dist/overlay/Overlay.js +1 -1
- package/dist/overlay/Popover.js +3 -3
- package/dist/overlay/themeBridge.js +14 -2
- package/dist/pagination/index.d.ts +10 -1
- package/dist/pagination/index.js +6 -4
- package/dist/pan-zoom/index.d.ts +2 -0
- package/dist/pan-zoom/index.js +7 -6
- package/dist/pencil.d.ts +1 -0
- package/dist/pencil.js +4 -4
- package/dist/radar/index.d.ts +2 -0
- package/dist/radar/index.js +40 -20
- package/dist/rating/index.d.ts +4 -0
- package/dist/rating/index.js +128 -24
- package/dist/resizable/index.js +1 -1
- package/dist/scroll-area/index.d.ts +1 -1
- package/dist/scroll-area/index.js +5 -2
- package/dist/select/MultipleSelect.js +5 -4
- package/dist/select/SearchableSelect.js +61 -6
- package/dist/select/Select.d.ts +1 -0
- package/dist/select/Select.js +3 -1
- package/dist/select/StepperSelect.d.ts +29 -0
- package/dist/select/StepperSelect.js +119 -0
- package/dist/select/index.d.ts +2 -0
- package/dist/select/index.js +1 -0
- package/dist/select/stepperModel.d.ts +7 -0
- package/dist/select/stepperModel.js +29 -0
- package/dist/selection/Checkbox.js +4 -3
- package/dist/selection/RadioGroup.js +29 -13
- package/dist/selection/Switch.js +4 -3
- package/dist/selection/useNativeChecked.d.ts +2 -1
- package/dist/selection/useNativeChecked.js +26 -8
- package/dist/slider/index.js +50 -6
- package/dist/sortable/index.js +6 -2
- package/dist/sparkline/index.js +17 -2
- package/dist/sparkline/model.js +11 -4
- package/dist/styles/accordion.css +228 -0
- package/dist/styles/alert-dialog.css +139 -0
- package/dist/styles/audio-player.css +22 -0
- package/dist/styles/base.css +638 -0
- package/dist/styles/breadcrumb.css +7 -0
- package/dist/styles/calendar.css +62 -0
- package/dist/styles/carousel.css +173 -0
- package/dist/styles/choice-grid.css +226 -0
- package/dist/styles/color-picker.css +281 -0
- package/dist/styles/color-swatch.css +47 -0
- package/dist/styles/command.css +52 -0
- package/dist/styles/content.css +79 -0
- package/dist/styles/context-menu.css +35 -0
- package/dist/styles/data-table.css +31 -0
- package/dist/styles/date-picker.css +107 -0
- package/dist/styles/day-agenda.css +30 -0
- package/dist/styles/display.css +304 -0
- package/dist/styles/dockable-panel.css +110 -0
- package/dist/styles/drawer.css +133 -0
- package/dist/styles/dropdown-menu.css +68 -0
- package/dist/styles/editable.css +145 -0
- package/dist/styles/empty.css +108 -0
- package/dist/styles/flip.css +101 -0
- package/dist/styles/form.css +634 -0
- package/dist/styles/heatmap.css +256 -0
- package/dist/styles/hotkey.css +189 -0
- package/dist/styles/hover-card.css +27 -0
- package/dist/styles/image-cropper.css +205 -0
- package/dist/styles/image-viewer.css +422 -0
- package/dist/styles/input-number.css +21 -0
- package/dist/styles/journey-map.css +19 -0
- package/dist/styles/keypad.css +92 -0
- package/dist/styles/layout.css +72 -0
- package/dist/styles/leaflet.css +661 -0
- package/dist/styles/log.css +203 -0
- package/dist/styles/markdown.css +379 -0
- package/dist/styles/node-graph.css +142 -0
- package/dist/styles/number-ticker.css +83 -0
- package/dist/styles/overflow-list.css +51 -0
- package/dist/styles/overlay.css +284 -0
- package/dist/styles/pagination.css +21 -0
- package/dist/styles/pan-zoom.css +136 -0
- package/dist/styles/radar.css +1 -0
- package/dist/styles/rating.css +121 -0
- package/dist/styles/resizable.css +108 -0
- package/dist/styles/scroll-area.css +122 -0
- package/dist/styles/select.css +557 -0
- package/dist/styles/selection.css +264 -0
- package/dist/styles/slider.css +145 -0
- package/dist/styles/slot.css +109 -0
- package/dist/styles/sortable.css +159 -0
- package/dist/styles/sparkline.css +24 -0
- package/dist/styles/steps.css +27 -0
- package/dist/styles/tabs.css +79 -0
- package/dist/styles/tag-input.css +213 -0
- package/dist/styles/time-picker.css +66 -0
- package/dist/styles/timeline.css +24 -0
- package/dist/styles/toast.css +160 -0
- package/dist/styles/toggle-group.css +107 -0
- package/dist/styles/tooltip.css +78 -0
- package/dist/styles/transfer.css +148 -0
- package/dist/styles/tree-select.css +131 -0
- package/dist/styles/tree.css +59 -0
- package/dist/styles/typewriter.css +62 -0
- package/dist/styles/upload.css +60 -0
- package/dist/styles/virtual-list.css +112 -0
- package/dist/styles.css +2094 -421
- package/dist/tabs/index.d.ts +9 -0
- package/dist/tabs/index.js +66 -16
- package/dist/tabs/useOverflow.d.ts +20 -0
- package/dist/tabs/useOverflow.js +145 -0
- package/dist/tag-input/index.d.ts +7 -2
- package/dist/tag-input/index.js +70 -26
- package/dist/time-picker/index.js +38 -20
- package/dist/toast/index.d.ts +19 -11
- package/dist/toast/index.js +48 -8
- package/dist/toast/manager.d.ts +42 -0
- package/dist/toast/manager.js +85 -0
- package/dist/toast/useToastNavigation.d.ts +3 -0
- package/dist/toast/useToastNavigation.js +75 -0
- package/dist/tokens.css +4 -0
- package/dist/tooltip/Tooltip.js +3 -3
- package/dist/transfer/index.d.ts +5 -0
- package/dist/transfer/index.js +93 -29
- package/dist/transfer/model.d.ts +4 -0
- package/dist/transfer/model.js +37 -11
- package/dist/tree/index.d.ts +5 -1
- package/dist/tree/index.js +5 -5
- package/dist/tree-select/index.js +67 -30
- package/dist/tree-select/model.d.ts +11 -5
- package/dist/tree-select/model.js +111 -83
- package/dist/typewriter/index.js +10 -10
- package/dist/upload/index.js +30 -16
- package/dist/virtual-list/index.js +7 -5
- package/package.json +413 -3
|
@@ -1,17 +1,27 @@
|
|
|
1
|
-
function
|
|
1
|
+
export function normalizeTreeSelectNodes(nodes) {
|
|
2
2
|
const result = [];
|
|
3
|
-
|
|
3
|
+
const seen = new Set();
|
|
4
|
+
const stack = [{ nodes, position: 0, result }];
|
|
5
|
+
// Preserve preorder duplicate handling without using the JavaScript call stack.
|
|
6
|
+
while (stack.length) {
|
|
7
|
+
const frame = stack[stack.length - 1];
|
|
8
|
+
if (frame.position === frame.nodes.length) {
|
|
9
|
+
if (frame.parent && frame.result.length)
|
|
10
|
+
frame.parent.children = frame.result;
|
|
11
|
+
stack.pop();
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const node = frame.nodes[frame.position++];
|
|
4
15
|
if (!node.id || seen.has(node.id))
|
|
5
16
|
continue;
|
|
6
17
|
seen.add(node.id);
|
|
7
|
-
const
|
|
8
|
-
result.push(
|
|
18
|
+
const copy = { ...node, children: undefined };
|
|
19
|
+
frame.result.push(copy);
|
|
20
|
+
if (node.children?.length)
|
|
21
|
+
stack.push({ nodes: node.children, position: 0, result: [], parent: copy });
|
|
9
22
|
}
|
|
10
23
|
return result;
|
|
11
24
|
}
|
|
12
|
-
export function normalizeTreeSelectNodes(nodes) {
|
|
13
|
-
return normalizeCopy(nodes, new Set());
|
|
14
|
-
}
|
|
15
25
|
export function indexTreeSelectNodes(nodes) {
|
|
16
26
|
const normalized = normalizeTreeSelectNodes(nodes);
|
|
17
27
|
const orderedIds = [];
|
|
@@ -19,116 +29,134 @@ export function indexTreeSelectNodes(nodes) {
|
|
|
19
29
|
const parentById = new Map();
|
|
20
30
|
const branchIds = new Set();
|
|
21
31
|
const disabledIds = new Set();
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
const stack = normalized.map(node => ({ node, parentId: null })).reverse();
|
|
33
|
+
while (stack.length) {
|
|
34
|
+
const { node, parentId } = stack.pop();
|
|
35
|
+
orderedIds.push(node.id);
|
|
36
|
+
byId.set(node.id, node);
|
|
37
|
+
parentById.set(node.id, parentId);
|
|
38
|
+
if (node.disabled)
|
|
39
|
+
disabledIds.add(node.id);
|
|
40
|
+
if (node.children?.length) {
|
|
41
|
+
branchIds.add(node.id);
|
|
42
|
+
for (let position = node.children.length - 1; position >= 0; position--) {
|
|
43
|
+
stack.push({ node: node.children[position], parentId: node.id });
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
|
-
}
|
|
35
|
-
visit(normalized, null);
|
|
46
|
+
}
|
|
36
47
|
return { nodes: normalized, orderedIds, byId, parentById, branchIds, disabledIds };
|
|
37
48
|
}
|
|
38
49
|
function addVisibleSubtree(node, visibleIds, expandedIds) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
50
|
+
const stack = [node];
|
|
51
|
+
while (stack.length) {
|
|
52
|
+
const current = stack.pop();
|
|
53
|
+
visibleIds.add(current.id);
|
|
54
|
+
if (current.children?.length) {
|
|
55
|
+
expandedIds?.add(current.id);
|
|
56
|
+
for (let position = current.children.length - 1; position >= 0; position--) {
|
|
57
|
+
stack.push(current.children[position]);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
}
|
|
46
|
-
|
|
47
|
-
const normalized = normalizeTreeSelectNodes(nodes);
|
|
62
|
+
function filterNormalizedTreeSelectNodes(nodes, query) {
|
|
48
63
|
const needle = query.trim().toLocaleLowerCase();
|
|
49
64
|
const visibleIds = new Set();
|
|
50
65
|
const expandedIds = new Set();
|
|
51
66
|
if (!needle) {
|
|
52
|
-
for (const node of
|
|
53
|
-
addVisibleSubtree(node, visibleIds
|
|
54
|
-
return { nodes
|
|
67
|
+
for (const node of nodes)
|
|
68
|
+
addVisibleSubtree(node, visibleIds);
|
|
69
|
+
return { nodes, visibleIds, expandedIds };
|
|
55
70
|
}
|
|
56
|
-
const
|
|
71
|
+
const result = [];
|
|
72
|
+
const stack = nodes.map(node => ({ node, destination: result })).reverse();
|
|
73
|
+
while (stack.length) {
|
|
74
|
+
const { node, destination, children } = stack.pop();
|
|
75
|
+
if (children) {
|
|
76
|
+
if (children.length) {
|
|
77
|
+
visibleIds.add(node.id);
|
|
78
|
+
expandedIds.add(node.id);
|
|
79
|
+
destination.push({ ...node, children });
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
57
83
|
const haystack = `${node.label} ${node.keywords ?? ''}`.toLocaleLowerCase();
|
|
58
84
|
if (haystack.includes(needle)) {
|
|
59
85
|
addVisibleSubtree(node, visibleIds, expandedIds);
|
|
60
|
-
|
|
86
|
+
destination.push(node);
|
|
87
|
+
}
|
|
88
|
+
else if (node.children?.length) {
|
|
89
|
+
const matches = [];
|
|
90
|
+
stack.push({ node, destination, children: matches });
|
|
91
|
+
for (let position = node.children.length - 1; position >= 0; position--) {
|
|
92
|
+
stack.push({ node: node.children[position], destination: matches });
|
|
93
|
+
}
|
|
61
94
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
visibleIds,
|
|
72
|
-
expandedIds,
|
|
73
|
-
};
|
|
95
|
+
}
|
|
96
|
+
return { nodes: result, visibleIds, expandedIds };
|
|
97
|
+
}
|
|
98
|
+
export function filterTreeSelectNodes(nodes, query) {
|
|
99
|
+
return filterNormalizedTreeSelectNodes(normalizeTreeSelectNodes(nodes), query);
|
|
100
|
+
}
|
|
101
|
+
/** Reuse the component's index instead of cloning every node on each search. */
|
|
102
|
+
export function filterTreeSelectNodesFromIndex(index, query) {
|
|
103
|
+
return filterNormalizedTreeSelectNodes(index.nodes, query);
|
|
74
104
|
}
|
|
75
105
|
function isSelectable(index, id, branchSelection) {
|
|
76
106
|
return index.byId.has(id)
|
|
77
107
|
&& !index.disabledIds.has(id)
|
|
78
108
|
&& (branchSelection === 'all' || !index.branchIds.has(id));
|
|
79
109
|
}
|
|
80
|
-
function descendants(index, id) {
|
|
81
|
-
const node = index.byId.get(id);
|
|
82
|
-
if (!node?.children?.length)
|
|
83
|
-
return [];
|
|
84
|
-
const result = [];
|
|
85
|
-
const visit = (items) => {
|
|
86
|
-
for (const child of items) {
|
|
87
|
-
result.push(child.id);
|
|
88
|
-
if (child.children?.length)
|
|
89
|
-
visit(child.children);
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
visit(node.children);
|
|
93
|
-
return result;
|
|
94
|
-
}
|
|
95
110
|
function orderedSelection(index, selected, previous) {
|
|
96
111
|
const known = index.orderedIds.filter(id => selected.has(id));
|
|
97
|
-
const unknown = previous.filter(
|
|
112
|
+
const unknown = [...new Set(previous)].filter(id => !index.byId.has(id));
|
|
98
113
|
return [...known, ...unknown];
|
|
99
114
|
}
|
|
100
115
|
export function updateTreeSelectSelection(nodes, current, toggledId, selected, options) {
|
|
101
|
-
|
|
116
|
+
return updateTreeSelectSelectionFromIndex(indexTreeSelectNodes(nodes), current, toggledId, selected, options);
|
|
117
|
+
}
|
|
118
|
+
/** Reuse the component's index for selection and aggregate each subtree once. */
|
|
119
|
+
export function updateTreeSelectSelectionFromIndex(index, current, toggledId, selected, options) {
|
|
102
120
|
if (!isSelectable(index, toggledId, options.branchSelection))
|
|
103
121
|
return [...current];
|
|
104
122
|
if (!options.multiple)
|
|
105
123
|
return selected ? [toggledId] : [];
|
|
106
124
|
const next = new Set(current);
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
const stack = [index.byId.get(toggledId)];
|
|
126
|
+
while (stack.length) {
|
|
127
|
+
const node = stack.pop();
|
|
128
|
+
if (isSelectable(index, node.id, options.branchSelection)) {
|
|
129
|
+
if (selected)
|
|
130
|
+
next.add(node.id);
|
|
131
|
+
else
|
|
132
|
+
next.delete(node.id);
|
|
133
|
+
}
|
|
134
|
+
if (options.selectionPropagation !== 'none' && node.children?.length) {
|
|
135
|
+
for (const child of node.children)
|
|
136
|
+
stack.push(child);
|
|
137
|
+
}
|
|
118
138
|
}
|
|
119
139
|
if (options.selectionPropagation === 'both' && options.branchSelection === 'all') {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
140
|
+
const summaries = new Map();
|
|
141
|
+
// Reverse preorder visits children before parents. Include descendants under
|
|
142
|
+
// disabled branches, but never count a disabled node itself as selectable.
|
|
143
|
+
for (let position = index.orderedIds.length - 1; position >= 0; position--) {
|
|
144
|
+
const id = index.orderedIds[position];
|
|
145
|
+
const summary = summaries.get(id) ?? { count: 0, unselected: 0 };
|
|
146
|
+
const enabled = !index.disabledIds.has(id);
|
|
147
|
+
if (enabled && summary.count > 0) {
|
|
148
|
+
if (summary.unselected === 0)
|
|
149
|
+
next.add(id);
|
|
150
|
+
else
|
|
151
|
+
next.delete(id);
|
|
152
|
+
}
|
|
153
|
+
const parentId = index.parentById.get(id);
|
|
154
|
+
if (parentId != null) {
|
|
155
|
+
const parent = summaries.get(parentId) ?? { count: 0, unselected: 0 };
|
|
156
|
+
parent.count += summary.count + (enabled ? 1 : 0);
|
|
157
|
+
parent.unselected += summary.unselected + (enabled && !next.has(id) ? 1 : 0);
|
|
158
|
+
summaries.set(parentId, parent);
|
|
159
|
+
}
|
|
132
160
|
}
|
|
133
161
|
}
|
|
134
162
|
return orderedSelection(index, next, current);
|
package/dist/typewriter/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
2
|
+
import { forwardRef, memo, useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { PencilText } from '../PencilText.js';
|
|
4
4
|
import { useBrowserLayoutEffect } from '../internal/useBrowserLayoutEffect.js';
|
|
5
5
|
import { splitTypewriterGraphemes, typewriterDelayAfter } from './model.js';
|
|
@@ -19,6 +19,14 @@ function usePrefersReducedMotion() {
|
|
|
19
19
|
function safeDuration(value) {
|
|
20
20
|
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
21
21
|
}
|
|
22
|
+
const TypewriterGrapheme = memo(function TypewriterGrapheme({ grapheme, index, revealed: isRevealed }) {
|
|
23
|
+
if (grapheme === '\n')
|
|
24
|
+
return _jsx("br", {});
|
|
25
|
+
if (/^\s+$/u.test(grapheme)) {
|
|
26
|
+
return _jsx("span", { className: "inkra-typewriter__space", "data-revealed": isRevealed, children: grapheme });
|
|
27
|
+
}
|
|
28
|
+
return _jsx(PencilText, { className: "inkra-typewriter__grapheme", "data-revealed": isRevealed, seed: `typewriter-${index}-${grapheme}`, children: grapheme });
|
|
29
|
+
});
|
|
22
30
|
export const Typewriter = forwardRef(function Typewriter({ children, className, defaultPaused = false, locale, onComplete, onPausedChange, paused, punctuationDelay = 240, speed = 48, startDelay = 0, ...props }, forwardedRef) {
|
|
23
31
|
const graphemes = useMemo(() => splitTypewriterGraphemes(children, locale), [children, locale]);
|
|
24
32
|
const [internalPaused, setInternalPaused] = useState(defaultPaused);
|
|
@@ -88,14 +96,6 @@ export const Typewriter = forwardRef(function Typewriter({ children, className,
|
|
|
88
96
|
}, [children, complete, graphemes, isPaused, punctuationDelay, reducedMotion, revealed, speed, startDelay]);
|
|
89
97
|
const classes = ['inkra-typewriter', className].filter(Boolean).join(' ');
|
|
90
98
|
const state = complete ? 'complete' : isPaused ? 'paused' : 'typing';
|
|
91
|
-
return _jsxs("span", { ...props, className: classes, "data-state": state, "data-revealed": revealed, "data-total": graphemes.length, children: [_jsx("span", { "aria-hidden": "true", className: "inkra-typewriter__visual", children: graphemes.map((grapheme, index) => {
|
|
92
|
-
const isRevealed = index < revealed;
|
|
93
|
-
if (grapheme === '\n')
|
|
94
|
-
return _jsx("br", {}, `line-${index}`);
|
|
95
|
-
if (/^\s+$/u.test(grapheme)) {
|
|
96
|
-
return _jsx("span", { className: "inkra-typewriter__space", "data-revealed": isRevealed, children: grapheme }, `space-${index}`);
|
|
97
|
-
}
|
|
98
|
-
return _jsx(PencilText, { className: "inkra-typewriter__grapheme", "data-revealed": isRevealed, seed: `typewriter-${index}-${grapheme}`, children: grapheme }, `${index}-${grapheme}`);
|
|
99
|
-
}) }), _jsx("span", { className: "inkra-typewriter__accessible", children: children })] });
|
|
99
|
+
return _jsxs("span", { ...props, className: classes, "data-state": state, "data-revealed": revealed, "data-total": graphemes.length, children: [_jsx("span", { "aria-hidden": "true", className: "inkra-typewriter__visual", children: graphemes.map((grapheme, index) => _jsx(TypewriterGrapheme, { grapheme: grapheme, index: index, revealed: index < revealed }, `${index}-${grapheme}`)) }), _jsx("span", { className: "inkra-typewriter__accessible", children: children })] });
|
|
100
100
|
});
|
|
101
101
|
export { isTypewriterPunctuation, splitTypewriterGraphemes, typewriterDelayAfter, } from './model.js';
|
package/dist/upload/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef, useEffect, useId, useRef, useState, } from 'react';
|
|
2
|
+
import { forwardRef, memo, useCallback, useEffect, useId, useMemo, useRef, useState, } from 'react';
|
|
3
3
|
import { Button } from '../Button.js';
|
|
4
4
|
import { Progress } from '../display/index.js';
|
|
5
5
|
import { PencilIcon } from '../PencilIcon.js';
|
|
@@ -56,7 +56,10 @@ function asErrorMessage(error, fallback) {
|
|
|
56
56
|
function statusLabel(status, labels) {
|
|
57
57
|
return labels[status];
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
const UploadRow = memo(function UploadRow({ item, labels, disabled, canUpload, startUpload, cancel, remove }) {
|
|
60
|
+
return _jsx("li", { className: "inkra-upload__item", "data-status": item.status, children: _jsxs(PencilSurface, { className: "inkra-upload__item-surface", variant: "paper", radius: 8, children: [_jsxs("div", { className: "inkra-upload__file", children: [_jsx(PencilText, { className: "inkra-upload__name", ellipsis: true, children: item.file.name }), _jsxs("span", { className: "inkra-upload__meta", children: [formatBytes(item.file.size), " \u00B7 ", statusLabel(item.status, labels)] }), item.status === 'uploading' && _jsx(Progress, { size: "sm", value: item.progress ?? 0, "aria-label": `${item.file.name}: ${Math.round(item.progress ?? 0)}%` }), item.error && _jsx("span", { className: "inkra-upload__error", children: item.error })] }), _jsxs("div", { className: "inkra-upload__actions", children: [item.status === 'uploading' && _jsx(Button, { disabled: disabled, size: "sm", variant: "ghost", onClick: () => cancel(item.id), children: labels.cancel }), canUpload && item.status === 'selected' && _jsx(Button, { disabled: disabled, size: "sm", variant: "ghost", onClick: () => startUpload(item.id), children: labels.upload }), canUpload && (item.status === 'error' || item.status === 'cancelled') && _jsx(Button, { disabled: disabled, size: "sm", variant: "ghost", onClick: () => startUpload(item.id, 'retry'), children: labels.retry }), _jsx(Button, { disabled: disabled, size: "sm", variant: "ghost", onClick: () => remove(item.id), children: labels.remove })] })] }) });
|
|
61
|
+
});
|
|
62
|
+
export const Upload = forwardRef(function Upload({ accept, autoUpload = true, capture, className, defaultFiles = [], disabled = false, files, inputProps, labels: labelOverrides, maxFiles = Number.POSITIVE_INFINITY, maxSize = Number.POSITIVE_INFINITY, multiple = true, onFilesChange, onReject, onUpload, validate, 'aria-label': ariaLabel, ...props }, ref) {
|
|
60
63
|
const instanceId = useId();
|
|
61
64
|
const inputRef = useRef(null);
|
|
62
65
|
const itemCounter = useRef(0);
|
|
@@ -71,7 +74,14 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
71
74
|
const itemsRef = useRef(items);
|
|
72
75
|
itemsRef.current = items;
|
|
73
76
|
disabledRef.current = disabled;
|
|
74
|
-
const
|
|
77
|
+
const labelValues = { ...defaultLabels, ...labelOverrides };
|
|
78
|
+
// Snapshot scalar/function values so same-object label edits remain observable.
|
|
79
|
+
const labels = useMemo(() => labelValues, [
|
|
80
|
+
labelValues.browse, labelValues.drop, labelValues.selected, labelValues.upload,
|
|
81
|
+
labelValues.uploading, labelValues.success, labelValues.error, labelValues.cancelled,
|
|
82
|
+
labelValues.retry, labelValues.cancel, labelValues.remove, labelValues.rejected,
|
|
83
|
+
labelValues.acceptError, labelValues.sizeError, labelValues.countError,
|
|
84
|
+
]);
|
|
75
85
|
const commitRef = useRef(() => { });
|
|
76
86
|
const commit = (next, details) => {
|
|
77
87
|
if (!controlled) {
|
|
@@ -81,7 +91,7 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
81
91
|
onFilesChange?.(next, details);
|
|
82
92
|
};
|
|
83
93
|
commitRef.current = commit;
|
|
84
|
-
const updateItem = (id, update, details) => {
|
|
94
|
+
const updateItem = useCallback((id, update, details) => {
|
|
85
95
|
const current = itemsRef.current;
|
|
86
96
|
const index = current.findIndex(item => item.id === id);
|
|
87
97
|
if (index < 0)
|
|
@@ -89,8 +99,8 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
89
99
|
const next = [...current];
|
|
90
100
|
next[index] = update(current[index]);
|
|
91
101
|
commitRef.current(next, { ...details, item: next[index] });
|
|
92
|
-
};
|
|
93
|
-
const startUpload = (id, reason = 'upload-start') => {
|
|
102
|
+
}, []);
|
|
103
|
+
const startUpload = useCallback((id, reason = 'upload-start') => {
|
|
94
104
|
const item = itemsRef.current.find(candidate => candidate.id === id);
|
|
95
105
|
if (!item || !onUpload || disabledRef.current)
|
|
96
106
|
return;
|
|
@@ -123,7 +133,7 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
123
133
|
controllers.current.delete(id);
|
|
124
134
|
updateItem(id, current => ({ ...current, status: 'error', progress: undefined, error: asErrorMessage(error, labels.error) }), { reason: 'upload-error' });
|
|
125
135
|
});
|
|
126
|
-
};
|
|
136
|
+
}, [onUpload, labels.error, updateItem]);
|
|
127
137
|
const addFiles = (incoming) => {
|
|
128
138
|
if (disabled || incoming.length === 0)
|
|
129
139
|
return;
|
|
@@ -173,21 +183,21 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
173
183
|
});
|
|
174
184
|
});
|
|
175
185
|
};
|
|
176
|
-
const remove = (id) => {
|
|
177
|
-
if (
|
|
186
|
+
const remove = useCallback((id) => {
|
|
187
|
+
if (disabledRef.current)
|
|
178
188
|
return;
|
|
179
189
|
controllers.current.get(id)?.controller.abort();
|
|
180
190
|
controllers.current.delete(id);
|
|
181
191
|
const item = itemsRef.current.find(candidate => candidate.id === id);
|
|
182
192
|
commitRef.current(itemsRef.current.filter(candidate => candidate.id !== id), { reason: 'remove', item });
|
|
183
|
-
};
|
|
184
|
-
const cancel = (id) => {
|
|
185
|
-
if (
|
|
193
|
+
}, []);
|
|
194
|
+
const cancel = useCallback((id) => {
|
|
195
|
+
if (disabledRef.current)
|
|
186
196
|
return;
|
|
187
197
|
controllers.current.get(id)?.controller.abort();
|
|
188
198
|
controllers.current.delete(id);
|
|
189
199
|
updateItem(id, item => ({ ...item, status: 'cancelled', progress: undefined, error: undefined }), { reason: 'cancel' });
|
|
190
|
-
};
|
|
200
|
+
}, [updateItem]);
|
|
191
201
|
useEffect(() => {
|
|
192
202
|
mounted.current = true;
|
|
193
203
|
return () => {
|
|
@@ -205,6 +215,10 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
205
215
|
}
|
|
206
216
|
});
|
|
207
217
|
}, [items]);
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
if (disabled)
|
|
220
|
+
setDragActive(false);
|
|
221
|
+
}, [disabled]);
|
|
208
222
|
const openPicker = () => {
|
|
209
223
|
if (!disabled)
|
|
210
224
|
inputRef.current?.click();
|
|
@@ -220,11 +234,11 @@ export const Upload = forwardRef(function Upload({ accept, autoUpload = true, ca
|
|
|
220
234
|
setDragActive(false);
|
|
221
235
|
addFiles(Array.from(event.dataTransfer.files));
|
|
222
236
|
};
|
|
223
|
-
return _jsxs("div", { ...props, ref: ref, className: ['inkra-upload', className].filter(Boolean).join(' '), "data-disabled": disabled || undefined, children: [_jsx("input", { ...inputProps, ref: inputRef, className: ['inkra-upload__input', inputProps?.className].filter(Boolean).join(' '), type: "file", accept: accept, capture: capture, multiple: multiple, disabled: disabled, onChange: event => {
|
|
237
|
+
return _jsxs("div", { ...props, "aria-label": ariaLabel, ref: ref, className: ['inkra-upload', className].filter(Boolean).join(' '), "data-disabled": disabled || undefined, children: [_jsx("input", { ...inputProps, "aria-hidden": "true", ref: inputRef, className: ['inkra-upload__input', inputProps?.className].filter(Boolean).join(' '), type: "file", accept: accept, capture: capture, multiple: multiple, disabled: disabled, tabIndex: -1, onChange: event => {
|
|
224
238
|
addFiles(Array.from(event.currentTarget.files ?? []));
|
|
225
239
|
event.currentTarget.value = '';
|
|
226
|
-
} }), _jsxs(PencilSurface, { as: "div", className: "inkra-upload__dropzone", "data-drag-active": dragActive || undefined, variant: dragActive ? 'selection' : 'outline', role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, onClick: openPicker, onKeyDown: handleKeyDown, onDragEnter: event => { event.preventDefault(); if (!disabled)
|
|
240
|
+
} }), _jsxs(PencilSurface, { as: "div", className: "inkra-upload__dropzone", "data-drag-active": dragActive || undefined, variant: dragActive ? 'selection' : 'outline', role: "button", "aria-label": ariaLabel, tabIndex: disabled ? -1 : 0, "aria-disabled": disabled || undefined, onClick: openPicker, onKeyDown: handleKeyDown, onDragEnter: event => { event.preventDefault(); if (!disabled)
|
|
227
241
|
setDragActive(true); }, onDragOver: event => { event.preventDefault(); if (!disabled)
|
|
228
242
|
event.dataTransfer.dropEffect = 'copy'; }, onDragLeave: event => { if (!event.currentTarget.contains(event.relatedTarget))
|
|
229
|
-
setDragActive(false); }, onDrop: handleDrop, children: [_jsx(PencilIcon, { className: "inkra-upload__icon", paths: uploadPaths, size: 32, sourceSize: 24 }), _jsxs("span", { className: "inkra-upload__prompt", children: [_jsx(PencilText, { children: labels.browse }), _jsx("span", { children: labels.drop })] }), accept && _jsx("span", { className: "inkra-upload__hint", children: accept })] }), rejections.length > 0 && _jsxs("div", { className: "inkra-upload__rejections", role: "alert", children: [_jsx("strong", { children: labels.rejected }), _jsx("ul", { children: rejections.map((rejection, index) => _jsxs("li", { children: [rejection.file.name, ": ", rejection.message] }, `${rejection.file.name}-${index}`)) })] }), items.length > 0 && _jsx("ul", { className: "inkra-upload__list", "aria-live": "polite", children: items.map(item => _jsx(
|
|
243
|
+
setDragActive(false); }, onDrop: handleDrop, children: [_jsx(PencilSurface, { "aria-hidden": "true", as: "span", className: "inkra-upload__focus-ring", seed: `${instanceId}-focus`, variant: "outline" }), _jsx(PencilIcon, { className: "inkra-upload__icon", paths: uploadPaths, size: 32, sourceSize: 24 }), _jsxs("span", { className: "inkra-upload__prompt", children: [_jsx(PencilText, { children: labels.browse }), _jsx("span", { children: labels.drop })] }), accept && _jsx("span", { className: "inkra-upload__hint", children: accept })] }), rejections.length > 0 && _jsxs("div", { className: "inkra-upload__rejections", role: "alert", children: [_jsx("strong", { children: labels.rejected }), _jsx("ul", { children: rejections.map((rejection, index) => _jsxs("li", { children: [rejection.file.name, ": ", rejection.message] }, `${rejection.file.name}-${index}`)) })] }), items.length > 0 && _jsx("ul", { className: "inkra-upload__list", "aria-live": "polite", children: items.map(item => _jsx(UploadRow, { item: item, labels: labels, disabled: disabled, canUpload: Boolean(onUpload), startUpload: startUpload, cancel: cancel, remove: remove }, item.id)) })] });
|
|
230
244
|
});
|
|
@@ -26,12 +26,15 @@ function VirtualListInner({ 'aria-label': ariaLabel, className, empty, emptyLabe
|
|
|
26
26
|
const safePaddingEnd = normalizeVirtualListSpacing(paddingEnd);
|
|
27
27
|
useEffect(() => setHydrated(true), []);
|
|
28
28
|
const resolveScrollElement = useCallback(() => getScrollElement?.() ?? rootRef.current, [getScrollElement]);
|
|
29
|
-
const resolveItemKey = useCallback(
|
|
29
|
+
const resolveItemKey = useCallback(
|
|
30
|
+
// The engine caches measurements by this callback, not by the items array
|
|
31
|
+
// or estimateSize callback. Invalidate it for immutable data/estimate updates.
|
|
32
|
+
(index) => itemKey(items[index], index), [items, itemKey, estimateSize]);
|
|
30
33
|
const resolveEstimateSize = useCallback((index) => {
|
|
31
|
-
const item =
|
|
34
|
+
const item = items[index];
|
|
32
35
|
const value = typeof estimateSize === 'function' ? estimateSize(item, index) : estimateSize;
|
|
33
36
|
return normalizeVirtualListSize(value);
|
|
34
|
-
}, [estimateSize]);
|
|
37
|
+
}, [items, estimateSize]);
|
|
35
38
|
const virtualizer = useVirtualizer({
|
|
36
39
|
count: items.length,
|
|
37
40
|
enabled: hydrated && items.length > 0,
|
|
@@ -90,10 +93,9 @@ function VirtualListInner({ 'aria-label': ariaLabel, className, empty, emptyLabe
|
|
|
90
93
|
virtualizer.scrollToIndex(items.length - 1, { align: 'end' });
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
|
-
const fallbackIndexes = getVirtualListSsrIndexes(items.length, ssrItemCount);
|
|
94
96
|
const renderedRows = virtualized
|
|
95
97
|
? virtualRows.map(row => ({ index: row.index, key: row.key, start: row.start }))
|
|
96
|
-
:
|
|
98
|
+
: getVirtualListSsrIndexes(items.length, ssrItemCount).map(index => ({ index, key: itemKey(items[index], index), start: null }));
|
|
97
99
|
const rootStyle = externalScroll ? style : { blockSize: height, ...style };
|
|
98
100
|
const content = _jsxs(_Fragment, { children: [_jsx("ul", { "aria-label": listLabel, className: "inkra-virtual-list__items", role: listRole, style: virtualized ? { blockSize: `${virtualizer.getTotalSize()}px` } : undefined, children: renderedRows.map(row => {
|
|
99
101
|
const item = items[row.index];
|