ajo-ui 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 +15 -0
- package/README.md +126 -0
- package/dist/accordion.js +130 -0
- package/dist/avatar.js +54 -0
- package/dist/calendar.js +2 -0
- package/dist/carousel.js +239 -0
- package/dist/chart.js +790 -0
- package/dist/checkbox-group.js +70 -0
- package/dist/checkbox.js +77 -0
- package/dist/chunks/bar-CVafh6C1.js +99 -0
- package/dist/chunks/calendar-q8jZ8Cxr.js +1923 -0
- package/dist/chunks/collection-DtRB63U4.js +111 -0
- package/dist/chunks/data-table-DpkWv4y4.js +1039 -0
- package/dist/chunks/menu-B45IyHHC.js +704 -0
- package/dist/chunks/native-BJdhd9XJ.js +20 -0
- package/dist/chunks/popup-C8Bb3l_g.js +459 -0
- package/dist/chunks/popup-surface-BDCgtVU0.js +18 -0
- package/dist/chunks/position-D6_i_SRn.js +434 -0
- package/dist/chunks/virtual-list-B7hjGkjk.js +413 -0
- package/dist/collapsible.js +106 -0
- package/dist/command.js +263 -0
- package/dist/context-menu.js +112 -0
- package/dist/data-table.js +5 -0
- package/dist/dialog.js +207 -0
- package/dist/direction.js +22 -0
- package/dist/drawer.js +139 -0
- package/dist/field.js +26 -0
- package/dist/index.js +38 -0
- package/dist/input-date.js +994 -0
- package/dist/input-group.js +52 -0
- package/dist/input-otp.js +179 -0
- package/dist/menu.js +2 -0
- package/dist/menubar.js +236 -0
- package/dist/message-scroller.js +446 -0
- package/dist/navigation-menu.js +330 -0
- package/dist/popover.js +307 -0
- package/dist/progress.js +39 -0
- package/dist/radio-group.js +107 -0
- package/dist/resizable.js +172 -0
- package/dist/select.js +961 -0
- package/dist/sidebar.js +343 -0
- package/dist/slider.js +259 -0
- package/dist/switch.js +53 -0
- package/dist/tabs.js +182 -0
- package/dist/toast.js +492 -0
- package/dist/toggle-group.js +111 -0
- package/dist/toggle.js +52 -0
- package/dist/toolbar.js +127 -0
- package/dist/tooltip.js +196 -0
- package/dist/utils.js +104 -0
- package/dist/virtual-list.js +2 -0
- package/package.json +250 -0
- package/src/accordion.tsx +261 -0
- package/src/availability.ts +261 -0
- package/src/avatar.tsx +99 -0
- package/src/bar.ts +156 -0
- package/src/calendar.tsx +1441 -0
- package/src/carousel.tsx +424 -0
- package/src/chart.tsx +1194 -0
- package/src/checkbox-group.tsx +132 -0
- package/src/checkbox.tsx +130 -0
- package/src/collapsible.tsx +188 -0
- package/src/collection.ts +154 -0
- package/src/command.tsx +511 -0
- package/src/context-menu.tsx +233 -0
- package/src/data-table-contract.ts +143 -0
- package/src/data-table-model.ts +760 -0
- package/src/data-table.tsx +475 -0
- package/src/dialog.tsx +393 -0
- package/src/direction.tsx +45 -0
- package/src/drawer.tsx +251 -0
- package/src/field.tsx +61 -0
- package/src/index.ts +37 -0
- package/src/input-date.tsx +1539 -0
- package/src/input-group.tsx +142 -0
- package/src/input-otp.tsx +324 -0
- package/src/menu-cluster.ts +124 -0
- package/src/menu.tsx +1095 -0
- package/src/menubar.tsx +459 -0
- package/src/message-scroller.tsx +732 -0
- package/src/native.ts +26 -0
- package/src/navigation-menu.tsx +578 -0
- package/src/popover.tsx +519 -0
- package/src/popup-surface.tsx +31 -0
- package/src/popup.ts +569 -0
- package/src/position.ts +523 -0
- package/src/progress.tsx +70 -0
- package/src/radio-group.tsx +186 -0
- package/src/resizable.tsx +310 -0
- package/src/segments.ts +922 -0
- package/src/select.tsx +1501 -0
- package/src/sidebar.tsx +683 -0
- package/src/slider.tsx +424 -0
- package/src/switch.tsx +104 -0
- package/src/tabs.tsx +314 -0
- package/src/toast.tsx +923 -0
- package/src/toggle-group.tsx +249 -0
- package/src/toggle.tsx +91 -0
- package/src/toolbar.tsx +212 -0
- package/src/tooltip.tsx +359 -0
- package/src/utils.ts +204 -0
- package/src/virtual-list.tsx +205 -0
- package/src/virtual.ts +385 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import { stlx } from "../utils.js";
|
|
2
|
+
import { dom, frame, statefulRootAttrs } from "ajo-cloves";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "ajo/jsx-runtime";
|
|
4
|
+
import { Virtualizer, defaultRangeExtractor, elementScroll, measureElement, observeElementOffset, observeElementRect } from "@tanstack/virtual-core";
|
|
5
|
+
//#region packages/ajo-ui/src/virtual.ts
|
|
6
|
+
var INDEX_ATTRIBUTE = "data-ajo-virtual-index";
|
|
7
|
+
var TARGET_SHAPE_ERROR = "VirtualList target needs key or index";
|
|
8
|
+
var sameGeometry = (left, right) => {
|
|
9
|
+
if (left.total !== right.total || left.rows.length !== right.rows.length) return false;
|
|
10
|
+
for (let index = 0; index < left.rows.length; index++) {
|
|
11
|
+
const a = left.rows[index];
|
|
12
|
+
const b = right.rows[index];
|
|
13
|
+
if (a.key !== b.key || a.index !== b.index || a.start !== b.start || a.size !== b.size) return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
var validateKeys = (keys) => {
|
|
18
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
19
|
+
for (let index = 0; index < keys.length; index++) {
|
|
20
|
+
const key = keys[index];
|
|
21
|
+
if (typeof key !== "string" && (typeof key !== "number" || !Number.isFinite(key))) throw new TypeError(`VirtualList invalid key at ${index}`);
|
|
22
|
+
const previous = indexes.get(key);
|
|
23
|
+
if (previous !== void 0) throw new TypeError(`VirtualList duplicate key ${JSON.stringify(key)} ${previous}/${index}`);
|
|
24
|
+
indexes.set(key, index);
|
|
25
|
+
}
|
|
26
|
+
return indexes;
|
|
27
|
+
};
|
|
28
|
+
var virtual = (host) => {
|
|
29
|
+
let input;
|
|
30
|
+
let instance;
|
|
31
|
+
let keyIndexes = /* @__PURE__ */ new Map();
|
|
32
|
+
let validatedKeys;
|
|
33
|
+
let mounted = false;
|
|
34
|
+
let live = false;
|
|
35
|
+
let mountCleanup;
|
|
36
|
+
let postCommitQueued = false;
|
|
37
|
+
let invalidateQueued = false;
|
|
38
|
+
let syncing = false;
|
|
39
|
+
const positiveSizes = /* @__PURE__ */ new Map();
|
|
40
|
+
let focusedKey;
|
|
41
|
+
let liveRangeExtractor = defaultRangeExtractor;
|
|
42
|
+
const elementsByKey = /* @__PURE__ */ new Map();
|
|
43
|
+
let geometry = {
|
|
44
|
+
rows: [],
|
|
45
|
+
total: 0
|
|
46
|
+
};
|
|
47
|
+
let rendered = geometry;
|
|
48
|
+
const estimate = (index) => {
|
|
49
|
+
const size = input.estimate(index);
|
|
50
|
+
if (!Number.isFinite(size) || size <= 0) throw new RangeError(`VirtualList invalid estimate at ${index}`);
|
|
51
|
+
return size;
|
|
52
|
+
};
|
|
53
|
+
let getItemKey = (index) => index;
|
|
54
|
+
const collect = () => {
|
|
55
|
+
if (!instance) return {
|
|
56
|
+
rows: [],
|
|
57
|
+
total: 0
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
rows: instance.getVirtualItems().map((item) => ({
|
|
61
|
+
index: item.index,
|
|
62
|
+
key: item.key,
|
|
63
|
+
size: item.size,
|
|
64
|
+
start: item.start
|
|
65
|
+
})),
|
|
66
|
+
total: instance.getTotalSize()
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const throwAsync = (error) => {
|
|
70
|
+
if (!host.signal.aborted) host.throw(error);
|
|
71
|
+
};
|
|
72
|
+
const renderNext = () => {
|
|
73
|
+
if (host.signal.aborted) return;
|
|
74
|
+
try {
|
|
75
|
+
host.next();
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throwAsync(error);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const schedule = frame(renderNext);
|
|
81
|
+
const invalidate = (immediate = false) => {
|
|
82
|
+
if (!immediate) return schedule();
|
|
83
|
+
schedule.cancel();
|
|
84
|
+
if (invalidateQueued || host.signal.aborted) return;
|
|
85
|
+
invalidateQueued = true;
|
|
86
|
+
queueMicrotask(() => {
|
|
87
|
+
invalidateQueued = false;
|
|
88
|
+
renderNext();
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
const onChange = () => {
|
|
92
|
+
if (syncing || host.signal.aborted) return;
|
|
93
|
+
try {
|
|
94
|
+
const next = collect();
|
|
95
|
+
geometry = next;
|
|
96
|
+
if (sameGeometry(next, rendered)) {
|
|
97
|
+
schedule.cancel();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
invalidate();
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throwAsync(error);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
const ssrRangeExtractor = (_range) => {
|
|
106
|
+
const count = Math.min(input.prerender, input.keys.length);
|
|
107
|
+
return Array.from({ length: count }, (_, index) => index);
|
|
108
|
+
};
|
|
109
|
+
const refreshLiveRangeExtractor = () => {
|
|
110
|
+
const focusedIndex = focusedKey === void 0 ? void 0 : keyIndexes.get(focusedKey);
|
|
111
|
+
liveRangeExtractor = (range) => {
|
|
112
|
+
const indexes = defaultRangeExtractor(range);
|
|
113
|
+
if (focusedIndex === void 0 || indexes.includes(focusedIndex)) return indexes;
|
|
114
|
+
indexes.push(focusedIndex);
|
|
115
|
+
indexes.sort((left, right) => left - right);
|
|
116
|
+
return indexes;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
const focusedRowKey = () => {
|
|
120
|
+
if (!dom(host)) return void 0;
|
|
121
|
+
let node = host.ownerDocument.activeElement;
|
|
122
|
+
while (node && node !== host) {
|
|
123
|
+
const index = node.getAttribute(INDEX_ATTRIBUTE);
|
|
124
|
+
if (index !== null) return input?.keys[Number(index)];
|
|
125
|
+
node = node.parentElement;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const updateFocus = () => {
|
|
129
|
+
const next = focusedRowKey();
|
|
130
|
+
if (next === focusedKey) return;
|
|
131
|
+
focusedKey = next;
|
|
132
|
+
refreshLiveRangeExtractor();
|
|
133
|
+
if (!live || !instance || host.signal.aborted) return;
|
|
134
|
+
try {
|
|
135
|
+
syncing = true;
|
|
136
|
+
applyOptions();
|
|
137
|
+
geometry = collect();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throwAsync(error);
|
|
140
|
+
} finally {
|
|
141
|
+
syncing = false;
|
|
142
|
+
}
|
|
143
|
+
if (!sameGeometry(geometry, rendered)) invalidate();
|
|
144
|
+
};
|
|
145
|
+
const measure = (node, entry, current) => {
|
|
146
|
+
const index = current.indexFromElement(node);
|
|
147
|
+
const key = input?.keys[index];
|
|
148
|
+
const cached = key === void 0 ? void 0 : positiveSizes.get(key);
|
|
149
|
+
if (!entry && cached !== void 0) return cached;
|
|
150
|
+
const size = measureElement(node, entry, current);
|
|
151
|
+
if (Number.isFinite(size) && size > 0) {
|
|
152
|
+
if (key !== void 0) positiveSizes.set(key, size);
|
|
153
|
+
return size;
|
|
154
|
+
}
|
|
155
|
+
return key === void 0 ? estimate(index) : positiveSizes.get(key) ?? estimate(index);
|
|
156
|
+
};
|
|
157
|
+
const coreOptions = () => {
|
|
158
|
+
const prerender = Math.min(input.prerender, input.keys.length);
|
|
159
|
+
let initialHeight = 0;
|
|
160
|
+
for (let index = 0; index < prerender; index++) initialHeight += estimate(index);
|
|
161
|
+
return {
|
|
162
|
+
count: input.keys.length,
|
|
163
|
+
estimateSize: estimate,
|
|
164
|
+
getItemKey,
|
|
165
|
+
getScrollElement: () => dom(host) ? host : null,
|
|
166
|
+
indexAttribute: INDEX_ATTRIBUTE,
|
|
167
|
+
initialRect: {
|
|
168
|
+
height: initialHeight,
|
|
169
|
+
width: 0
|
|
170
|
+
},
|
|
171
|
+
measureElement: measure,
|
|
172
|
+
observeElementOffset,
|
|
173
|
+
observeElementRect,
|
|
174
|
+
onChange,
|
|
175
|
+
overscan: live ? input.overscan : 0,
|
|
176
|
+
rangeExtractor: live ? liveRangeExtractor : ssrRangeExtractor,
|
|
177
|
+
scrollToFn: elementScroll
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
const applyOptions = () => {
|
|
181
|
+
if (!instance) instance = new Virtualizer(coreOptions());
|
|
182
|
+
else instance.setOptions(coreOptions());
|
|
183
|
+
};
|
|
184
|
+
const postCommit = () => {
|
|
185
|
+
if (postCommitQueued || host.signal.aborted) return;
|
|
186
|
+
postCommitQueued = true;
|
|
187
|
+
queueMicrotask(() => {
|
|
188
|
+
postCommitQueued = false;
|
|
189
|
+
if (host.signal.aborted || !dom(host) || !host.isConnected || !instance) return;
|
|
190
|
+
try {
|
|
191
|
+
syncing = true;
|
|
192
|
+
const firstMount = !mounted;
|
|
193
|
+
if (firstMount) {
|
|
194
|
+
mountCleanup = instance._didMount();
|
|
195
|
+
mounted = true;
|
|
196
|
+
focusedKey = focusedRowKey();
|
|
197
|
+
live = true;
|
|
198
|
+
refreshLiveRangeExtractor();
|
|
199
|
+
host.addEventListener("focusin", updateFocus, { signal: host.signal });
|
|
200
|
+
host.addEventListener("focusout", () => queueMicrotask(updateFocus), { signal: host.signal });
|
|
201
|
+
}
|
|
202
|
+
applyOptions();
|
|
203
|
+
instance._willUpdate();
|
|
204
|
+
const next = collect();
|
|
205
|
+
const changed = !sameGeometry(next, rendered);
|
|
206
|
+
if (changed) geometry = next;
|
|
207
|
+
if (firstMount || changed) invalidate(firstMount);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throwAsync(error);
|
|
210
|
+
} finally {
|
|
211
|
+
syncing = false;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
host.signal.addEventListener("abort", () => {
|
|
216
|
+
schedule.cancel();
|
|
217
|
+
mountCleanup?.();
|
|
218
|
+
mountCleanup = void 0;
|
|
219
|
+
mounted = false;
|
|
220
|
+
live = false;
|
|
221
|
+
focusedKey = void 0;
|
|
222
|
+
positiveSizes.clear();
|
|
223
|
+
elementsByKey.clear();
|
|
224
|
+
keyIndexes.clear();
|
|
225
|
+
validatedKeys = void 0;
|
|
226
|
+
input = void 0;
|
|
227
|
+
instance = void 0;
|
|
228
|
+
geometry = rendered = {
|
|
229
|
+
rows: [],
|
|
230
|
+
total: 0
|
|
231
|
+
};
|
|
232
|
+
}, { once: true });
|
|
233
|
+
return {
|
|
234
|
+
get connected() {
|
|
235
|
+
return mounted && dom(host) && host.isConnected;
|
|
236
|
+
},
|
|
237
|
+
get rows() {
|
|
238
|
+
return geometry.rows;
|
|
239
|
+
},
|
|
240
|
+
get total() {
|
|
241
|
+
return geometry.total;
|
|
242
|
+
},
|
|
243
|
+
measure(key, node) {
|
|
244
|
+
if (!instance) return;
|
|
245
|
+
if (!node) {
|
|
246
|
+
instance.measureElement(null);
|
|
247
|
+
const previous = elementsByKey.get(key);
|
|
248
|
+
if (previous) queueMicrotask(() => {
|
|
249
|
+
if (host.signal.aborted || previous.isConnected || elementsByKey.get(key) !== previous) return;
|
|
250
|
+
elementsByKey.delete(key);
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const index = keyIndexes.get(key);
|
|
255
|
+
if (index === void 0 || host.signal.aborted) return;
|
|
256
|
+
elementsByKey.set(key, node);
|
|
257
|
+
node.setAttribute(INDEX_ATTRIBUTE, String(index));
|
|
258
|
+
instance.measureElement(node);
|
|
259
|
+
},
|
|
260
|
+
scrollTo(target, align = "auto") {
|
|
261
|
+
if (!target || typeof target !== "object") throw new TypeError(TARGET_SHAPE_ERROR);
|
|
262
|
+
const record = target;
|
|
263
|
+
const hasIndex = Object.hasOwn(record, "index");
|
|
264
|
+
const hasKey = Object.hasOwn(record, "key");
|
|
265
|
+
if (hasIndex === hasKey) throw new TypeError(TARGET_SHAPE_ERROR);
|
|
266
|
+
let index;
|
|
267
|
+
if (hasKey) {
|
|
268
|
+
if (typeof record.key !== "string" && (typeof record.key !== "number" || !Number.isFinite(record.key))) throw new TypeError("VirtualList invalid scroll key");
|
|
269
|
+
index = keyIndexes.get(record.key);
|
|
270
|
+
} else {
|
|
271
|
+
if (typeof record.index !== "number" || !Number.isInteger(record.index)) throw new TypeError("VirtualList invalid scroll index");
|
|
272
|
+
index = record.index;
|
|
273
|
+
}
|
|
274
|
+
if (index === void 0 || index < 0 || index >= (input?.keys.length ?? 0)) return false;
|
|
275
|
+
if (!instance || !mounted || host.signal.aborted) return false;
|
|
276
|
+
instance.scrollToIndex(index, {
|
|
277
|
+
align,
|
|
278
|
+
behavior: "auto"
|
|
279
|
+
});
|
|
280
|
+
return true;
|
|
281
|
+
},
|
|
282
|
+
sync(next) {
|
|
283
|
+
const keysChanged = validatedKeys !== next.keys;
|
|
284
|
+
if (keysChanged) {
|
|
285
|
+
const nextIndexes = validateKeys(next.keys);
|
|
286
|
+
if (focusedKey !== void 0 && !nextIndexes.has(focusedKey)) {
|
|
287
|
+
const row = elementsByKey.get(focusedKey);
|
|
288
|
+
const active = dom(host) ? host.ownerDocument.activeElement : null;
|
|
289
|
+
if (row && active && row.contains(active)) {
|
|
290
|
+
if (dom(host)) host.focus({ preventScroll: true });
|
|
291
|
+
}
|
|
292
|
+
focusedKey = void 0;
|
|
293
|
+
}
|
|
294
|
+
for (const key of positiveSizes.keys()) if (!nextIndexes.has(key)) positiveSizes.delete(key);
|
|
295
|
+
if (instance) {
|
|
296
|
+
for (const key of instance.itemSizeCache.keys()) if (!nextIndexes.has(key)) instance.itemSizeCache.delete(key);
|
|
297
|
+
}
|
|
298
|
+
keyIndexes = nextIndexes;
|
|
299
|
+
validatedKeys = next.keys;
|
|
300
|
+
refreshLiveRangeExtractor();
|
|
301
|
+
}
|
|
302
|
+
if (keysChanged || input?.estimateToken !== next.estimateToken) {
|
|
303
|
+
const currentKeys = next.keys;
|
|
304
|
+
getItemKey = (index) => currentKeys[index] ?? index;
|
|
305
|
+
}
|
|
306
|
+
input = next;
|
|
307
|
+
syncing = true;
|
|
308
|
+
try {
|
|
309
|
+
applyOptions();
|
|
310
|
+
geometry = collect();
|
|
311
|
+
rendered = geometry;
|
|
312
|
+
} finally {
|
|
313
|
+
syncing = false;
|
|
314
|
+
}
|
|
315
|
+
postCommit();
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
//#endregion
|
|
320
|
+
//#region packages/ajo-ui/src/virtual-list.tsx
|
|
321
|
+
var ROOT_STYLE = "position:relative;overflow-x:hidden;overflow-y:auto;overflow-anchor:none;margin:0;padding:0;list-style:none";
|
|
322
|
+
var ITEM_STYLE = "box-sizing:border-box;left:0;position:absolute;width:100%;top:";
|
|
323
|
+
var SIZER_KEY = "\0virtual-list-sizer";
|
|
324
|
+
var SNAPSHOT_ERROR = "VirtualList mutated items";
|
|
325
|
+
var unavailableScroll = () => false;
|
|
326
|
+
var vnodeKey = (key) => `\0virtual-list-item:${typeof key}:${key}`;
|
|
327
|
+
var estimate = (value, item, index) => typeof value === "function" ? value(item, index) : value;
|
|
328
|
+
var assertCountOption = (name, value) => {
|
|
329
|
+
if (!Number.isInteger(value) || value < 0) throw new RangeError(`VirtualList invalid ${name}`);
|
|
330
|
+
};
|
|
331
|
+
var VirtualListRoot = function* () {
|
|
332
|
+
const geometry = virtual(this);
|
|
333
|
+
let source;
|
|
334
|
+
let sourceLength = 0;
|
|
335
|
+
let keys = [];
|
|
336
|
+
let apiReceiver;
|
|
337
|
+
let estimateInput;
|
|
338
|
+
let estimateItems;
|
|
339
|
+
let estimateToken;
|
|
340
|
+
const snapshotKeys = (items, getItemKey) => {
|
|
341
|
+
if (items !== source) {
|
|
342
|
+
source = items;
|
|
343
|
+
sourceLength = items.length;
|
|
344
|
+
const next = items.map(getItemKey);
|
|
345
|
+
if (next.length === keys.length && next.every((key, index) => key === keys[index])) return keys;
|
|
346
|
+
keys = next;
|
|
347
|
+
return keys;
|
|
348
|
+
}
|
|
349
|
+
if (items.length !== sourceLength) throw new TypeError(SNAPSHOT_ERROR);
|
|
350
|
+
if (items.length > 0) {
|
|
351
|
+
const last = items.length - 1;
|
|
352
|
+
if (getItemKey(items[0], 0) !== keys[0] || getItemKey(items[last], last) !== keys[last]) throw new TypeError(SNAPSHOT_ERROR);
|
|
353
|
+
}
|
|
354
|
+
return keys;
|
|
355
|
+
};
|
|
356
|
+
const api = { scrollTo: (target, options) => geometry.scrollTo(target, options?.align === "nearest" ? "auto" : options?.align) };
|
|
357
|
+
this.signal.addEventListener("abort", () => {
|
|
358
|
+
api.scrollTo = unavailableScroll;
|
|
359
|
+
source = void 0;
|
|
360
|
+
keys = [];
|
|
361
|
+
apiReceiver = void 0;
|
|
362
|
+
}, { once: true });
|
|
363
|
+
for (const { estimateSize, getItemKey, items, overscan = 4, prerender = 20, renderItem, setApi } of this) {
|
|
364
|
+
assertCountOption("overscan", overscan);
|
|
365
|
+
assertCountOption("prerender", prerender);
|
|
366
|
+
if (estimateSize !== estimateInput || typeof estimateSize === "function" && items !== estimateItems) {
|
|
367
|
+
estimateInput = estimateSize;
|
|
368
|
+
estimateItems = items;
|
|
369
|
+
estimateToken = {};
|
|
370
|
+
}
|
|
371
|
+
geometry.sync({
|
|
372
|
+
estimate: (index) => estimate(estimateSize, items[index], index),
|
|
373
|
+
estimateToken,
|
|
374
|
+
keys: snapshotKeys(items, getItemKey),
|
|
375
|
+
overscan,
|
|
376
|
+
prerender
|
|
377
|
+
});
|
|
378
|
+
if (geometry.connected && setApi && setApi !== apiReceiver) {
|
|
379
|
+
apiReceiver = setApi;
|
|
380
|
+
setApi(api);
|
|
381
|
+
}
|
|
382
|
+
yield /* @__PURE__ */ jsxs(Fragment, { children: [geometry.rows.map(({ index, key, start }) => /* @__PURE__ */ jsx("li", {
|
|
383
|
+
"aria-posinset": index + 1,
|
|
384
|
+
"aria-setsize": items.length,
|
|
385
|
+
"data-slot": "virtual-list-item",
|
|
386
|
+
ref: (element) => geometry.measure(key, element),
|
|
387
|
+
style: `${ITEM_STYLE}${start}px`,
|
|
388
|
+
children: renderItem(items[index], index)
|
|
389
|
+
}, vnodeKey(key))), /* @__PURE__ */ jsx("li", {
|
|
390
|
+
"aria-hidden": "true",
|
|
391
|
+
"data-slot": "virtual-list-sizer",
|
|
392
|
+
role: "none",
|
|
393
|
+
style: `height:${geometry.total}px;pointer-events:none;visibility:hidden`
|
|
394
|
+
}, SIZER_KEY)] });
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
VirtualListRoot.is = "ul";
|
|
398
|
+
/** Virtualized native list with stable identity and bounded DOM work. */
|
|
399
|
+
var VirtualList = ({ estimateSize, getItemKey, items, overscan, prerender, renderItem, setApi, style, tabindex = 0, ...attrs }) => /* @__PURE__ */ jsx(VirtualListRoot, {
|
|
400
|
+
...statefulRootAttrs(attrs),
|
|
401
|
+
estimateSize,
|
|
402
|
+
getItemKey,
|
|
403
|
+
items,
|
|
404
|
+
overscan,
|
|
405
|
+
prerender,
|
|
406
|
+
renderItem,
|
|
407
|
+
setApi,
|
|
408
|
+
"attr:data-slot": "virtual-list",
|
|
409
|
+
"attr:style": stlx(style, ROOT_STYLE),
|
|
410
|
+
"attr:tabindex": tabindex
|
|
411
|
+
});
|
|
412
|
+
//#endregion
|
|
413
|
+
export { VirtualList as t };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { flag } from "./utils.js";
|
|
2
|
+
import { callHandler, controlled, dom, id, statefulRootAttrs } from "ajo-cloves";
|
|
3
|
+
import { context } from "ajo/context";
|
|
4
|
+
import { Fragment, jsx } from "ajo/jsx-runtime";
|
|
5
|
+
//#region packages/ajo-ui/src/collapsible.tsx
|
|
6
|
+
/** Composition context shared by Collapsible parts and derivative families such as Accordion. */
|
|
7
|
+
var CollapsibleContext = context(null);
|
|
8
|
+
var CollapsibleRoot = function* ({ defaultOpen, open }) {
|
|
9
|
+
const rootId = id("collapsible");
|
|
10
|
+
let disabled = false;
|
|
11
|
+
let onOpenChange;
|
|
12
|
+
const state = controlled(this, {
|
|
13
|
+
fallback: Boolean(open ?? defaultOpen),
|
|
14
|
+
onChange: (next, event) => onOpenChange?.(next, event)
|
|
15
|
+
});
|
|
16
|
+
const syncRoot = () => {
|
|
17
|
+
if (!dom(this)) return;
|
|
18
|
+
if (state.value) this.setAttribute("open", "");
|
|
19
|
+
else this.removeAttribute("open");
|
|
20
|
+
this.setAttribute("data-state", state.value ? "open" : "closed");
|
|
21
|
+
if (disabled) {
|
|
22
|
+
this.setAttribute("data-disabled", "true");
|
|
23
|
+
this.setAttribute("aria-disabled", "true");
|
|
24
|
+
} else {
|
|
25
|
+
this.removeAttribute("data-disabled");
|
|
26
|
+
this.removeAttribute("aria-disabled");
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
const setOpen = (next, event) => {
|
|
30
|
+
if (disabled || next === state.value) return;
|
|
31
|
+
state.set(next, event);
|
|
32
|
+
};
|
|
33
|
+
for (const args of this) {
|
|
34
|
+
disabled = Boolean(args.disabled);
|
|
35
|
+
onOpenChange = args.onOpenChange;
|
|
36
|
+
state.sync(args.open != null ? Boolean(args.open) : void 0);
|
|
37
|
+
syncRoot();
|
|
38
|
+
CollapsibleContext({
|
|
39
|
+
contentId: `${rootId}-content`,
|
|
40
|
+
disabled,
|
|
41
|
+
open: state.value,
|
|
42
|
+
setOpen,
|
|
43
|
+
triggerId: `${rootId}-trigger`
|
|
44
|
+
});
|
|
45
|
+
yield /* @__PURE__ */ jsx(Fragment, { children: args.children });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
CollapsibleRoot.is = "details";
|
|
49
|
+
/** Unstyled collapsible disclosure rendered as a native details element. */
|
|
50
|
+
var Collapsible = ({ children, defaultOpen, disabled, onOpenChange, open, ...attrs }) => {
|
|
51
|
+
const opened = Boolean(open ?? defaultOpen);
|
|
52
|
+
const disabledFlag = Boolean(disabled);
|
|
53
|
+
return /* @__PURE__ */ jsx(CollapsibleRoot, {
|
|
54
|
+
"attr:data-slot": "collapsible",
|
|
55
|
+
...statefulRootAttrs(attrs),
|
|
56
|
+
defaultOpen,
|
|
57
|
+
disabled: disabledFlag,
|
|
58
|
+
onOpenChange,
|
|
59
|
+
open,
|
|
60
|
+
"attr:aria-disabled": flag(disabledFlag),
|
|
61
|
+
"attr:data-disabled": flag(disabledFlag),
|
|
62
|
+
"attr:data-state": opened ? "open" : "closed",
|
|
63
|
+
"attr:open": opened || void 0,
|
|
64
|
+
"set:open": opened,
|
|
65
|
+
children
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
/** Unstyled disclosure trigger rendered as a native summary element. */
|
|
69
|
+
var CollapsibleTrigger = ({ children, disabled, id: idArg, locked, "set:onclick": onClick, ...attrs }) => {
|
|
70
|
+
const collapsible = CollapsibleContext();
|
|
71
|
+
const disabledFlag = Boolean(disabled ?? collapsible?.disabled);
|
|
72
|
+
const unavailable = Boolean(disabledFlag || locked);
|
|
73
|
+
return /* @__PURE__ */ jsx("summary", {
|
|
74
|
+
"data-slot": "collapsible-trigger",
|
|
75
|
+
...attrs,
|
|
76
|
+
"aria-controls": collapsible?.contentId,
|
|
77
|
+
"aria-disabled": unavailable ? "true" : void 0,
|
|
78
|
+
"aria-expanded": collapsible?.open ? "true" : "false",
|
|
79
|
+
"data-disabled": flag(disabledFlag),
|
|
80
|
+
"data-state": collapsible?.open ? "open" : "closed",
|
|
81
|
+
id: idArg ?? collapsible?.triggerId,
|
|
82
|
+
"set:onclick": (event) => {
|
|
83
|
+
callHandler(onClick, event);
|
|
84
|
+
const prevented = event.defaultPrevented;
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
if (prevented || unavailable) return;
|
|
87
|
+
collapsible?.setOpen(!collapsible.open, event);
|
|
88
|
+
},
|
|
89
|
+
tabindex: disabledFlag ? -1 : void 0,
|
|
90
|
+
children
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
/** Unstyled content panel natively shown or hidden by its details root. */
|
|
94
|
+
var CollapsibleContent = ({ children, ...attrs }) => {
|
|
95
|
+
const collapsible = CollapsibleContext();
|
|
96
|
+
return /* @__PURE__ */ jsx("div", {
|
|
97
|
+
"data-slot": "collapsible-content",
|
|
98
|
+
...attrs,
|
|
99
|
+
"aria-labelledby": collapsible?.triggerId,
|
|
100
|
+
"data-state": collapsible?.open ? "open" : "closed",
|
|
101
|
+
id: collapsible?.contentId,
|
|
102
|
+
children
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
//#endregion
|
|
106
|
+
export { Collapsible, CollapsibleContent, CollapsibleContext, CollapsibleTrigger };
|