@leavepulse/ui 0.36.1 → 0.37.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/dist/assets/{LpCommandPalette-BERCHiev.css → LpCommandPalette-B-g1fa8r.css} +4 -4
- package/dist/assets/{LpTable-DfSwPCJ-.css → LpTable-BbOFmZfc.css} +9 -9
- package/dist/components/LpCommandPalette.vue.js +14 -30
- package/dist/components/LpTable.vue.d.ts +41 -4
- package/dist/components/LpTable.vue.js +235 -139
- package/dist/composables/useAsyncTask.d.ts +18 -0
- package/dist/composables/useAsyncTask.js +36 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -0
- package/package.json +1 -1
- package/src/components/LpCommandPalette.vue +14 -35
- package/src/components/LpTable.vue +181 -17
- package/src/composables/useAsyncTask.ts +50 -0
- package/src/index.ts +2 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { defineComponent, ref, computed, watch, onBeforeUnmount, openBlock,
|
|
1
|
+
import { defineComponent, ref, computed, shallowRef, watch, onBeforeUnmount, openBlock, createElementBlock, createVNode, mergeProps, withCtx, createElementVNode, normalizeClass, createCommentVNode, Fragment, renderList, normalizeStyle, createBlock, createTextVNode, toDisplayString, TransitionGroup, unref, withModifiers, renderSlot, nextTick } from "vue";
|
|
2
2
|
import { _ as _sfc_main$3 } from "../chunks/LpCheckbox-CVccKYaH.js";
|
|
3
3
|
import { _ as _sfc_main$2 } from "../chunks/LpContextMenu-B6WBLleS.js";
|
|
4
4
|
import { _ as _sfc_main$4 } from "../chunks/LpIcon-CCnX5_2j.js";
|
|
5
|
+
import { _ as _sfc_main$5 } from "../chunks/LpPagination-5FxjmmB5.js";
|
|
5
6
|
import { _ as _sfc_main$1 } from "../chunks/LpScrollArea-D8xyKXcF.js";
|
|
7
|
+
import { useAsyncTask } from "../composables/useAsyncTask.js";
|
|
6
8
|
import { useRovingFocus } from "../composables/useRovingFocus.js";
|
|
7
9
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
8
|
-
import '../assets/LpTable-
|
|
10
|
+
import '../assets/LpTable-BbOFmZfc.css';const _hoisted_1 = { class: "flex flex-col gap-3" };
|
|
9
11
|
const _hoisted_2 = { class: "border-b border-line bg-surface-soft" };
|
|
10
12
|
const _hoisted_3 = {
|
|
11
13
|
key: 0,
|
|
@@ -19,11 +21,18 @@ const _hoisted_6 = {
|
|
|
19
21
|
};
|
|
20
22
|
const _hoisted_7 = ["colspan"];
|
|
21
23
|
const _hoisted_8 = ["onContextmenu", "onClick"];
|
|
24
|
+
const __default__ = { inheritAttrs: false };
|
|
22
25
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
26
|
+
...__default__,
|
|
23
27
|
__name: "LpTable",
|
|
24
28
|
props: {
|
|
25
29
|
columns: {},
|
|
26
|
-
rows: {},
|
|
30
|
+
rows: { default: () => [] },
|
|
31
|
+
load: {},
|
|
32
|
+
pageSize: { default: 25 },
|
|
33
|
+
debounce: { default: 150 },
|
|
34
|
+
loadingLabel: { default: "Loading…" },
|
|
35
|
+
errorLabel: { default: "Could not load" },
|
|
27
36
|
rowKey: {},
|
|
28
37
|
emptyLabel: { default: "Nothing here yet" },
|
|
29
38
|
emptyIcon: { default: "lucide:inbox" },
|
|
@@ -40,7 +49,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
40
49
|
columnsLabel: { default: "Columns" }
|
|
41
50
|
},
|
|
42
51
|
emits: ["update:selected", "update:sort", "rowClick"],
|
|
43
|
-
setup(__props, { emit: __emit }) {
|
|
52
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
44
53
|
const props = __props;
|
|
45
54
|
const emit = __emit;
|
|
46
55
|
function keyFor(row, index) {
|
|
@@ -66,14 +75,40 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
66
75
|
});
|
|
67
76
|
const ownSort = ref(null);
|
|
68
77
|
const activeSort = computed(() => props.sort ?? ownSort.value);
|
|
78
|
+
const loaded = shallowRef([]);
|
|
79
|
+
const total = ref(0);
|
|
80
|
+
const facets = ref({});
|
|
81
|
+
const failed = ref(false);
|
|
82
|
+
const page = ref(1);
|
|
83
|
+
const task = useAsyncTask();
|
|
84
|
+
const loading = task.pending;
|
|
85
|
+
const sourceRows = computed(() => props.load ? loaded.value : props.rows);
|
|
86
|
+
const tableEl = ref(null);
|
|
87
|
+
const floor = ref(0);
|
|
88
|
+
async function holdHeight() {
|
|
89
|
+
await nextTick();
|
|
90
|
+
const height = tableEl.value?.offsetHeight ?? 0;
|
|
91
|
+
if (height > floor.value) floor.value = height;
|
|
92
|
+
}
|
|
69
93
|
const hiddenValues = ref({});
|
|
94
|
+
const animating = ref(false);
|
|
95
|
+
let motionTimer;
|
|
96
|
+
function withMotion(change) {
|
|
97
|
+
if (!props.load) {
|
|
98
|
+
animating.value = true;
|
|
99
|
+
if (motionTimer) clearTimeout(motionTimer);
|
|
100
|
+
motionTimer = setTimeout(() => animating.value = false, 600);
|
|
101
|
+
}
|
|
102
|
+
change();
|
|
103
|
+
}
|
|
70
104
|
const hiddenColumns = ref(/* @__PURE__ */ new Set());
|
|
71
105
|
const visibleColumns = computed(
|
|
72
106
|
() => props.columns.filter((c) => !hiddenColumns.value.has(c.key))
|
|
73
107
|
);
|
|
74
108
|
function valuesOf(col) {
|
|
109
|
+
if (props.load && facets.value[col.key]) return facets.value[col.key];
|
|
75
110
|
const seen = [];
|
|
76
|
-
for (const row of
|
|
111
|
+
for (const row of sourceRows.value) {
|
|
77
112
|
const v = row[col.key];
|
|
78
113
|
if (!seen.includes(v)) seen.push(v);
|
|
79
114
|
}
|
|
@@ -83,13 +118,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
83
118
|
return hiddenValues.value[key]?.has(value) ?? false;
|
|
84
119
|
}
|
|
85
120
|
function toggleValue(key, value) {
|
|
121
|
+
withMotion(() => applyToggle(key, value));
|
|
122
|
+
}
|
|
123
|
+
function applyToggle(key, value) {
|
|
86
124
|
const next = new Set(hiddenValues.value[key] ?? []);
|
|
87
125
|
if (next.has(value)) next.delete(value);
|
|
88
126
|
else next.add(value);
|
|
89
127
|
hiddenValues.value = { ...hiddenValues.value, [key]: next };
|
|
90
128
|
}
|
|
91
129
|
function showAllValues(key) {
|
|
92
|
-
hiddenValues.value = { ...hiddenValues.value, [key]: /* @__PURE__ */ new Set() };
|
|
130
|
+
withMotion(() => hiddenValues.value = { ...hiddenValues.value, [key]: /* @__PURE__ */ new Set() });
|
|
93
131
|
}
|
|
94
132
|
function toggleColumn(key) {
|
|
95
133
|
const next = new Set(hiddenColumns.value);
|
|
@@ -98,6 +136,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
98
136
|
hiddenColumns.value = next;
|
|
99
137
|
}
|
|
100
138
|
const filteredRows = computed(() => {
|
|
139
|
+
if (props.load) return sourceRows.value;
|
|
101
140
|
const active = Object.entries(hiddenValues.value).filter(([, set]) => set.size);
|
|
102
141
|
if (!active.length) return props.rows;
|
|
103
142
|
return props.rows.filter(
|
|
@@ -106,7 +145,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
106
145
|
});
|
|
107
146
|
const displayRows = computed(() => {
|
|
108
147
|
const s = activeSort.value;
|
|
109
|
-
if (!s) return filteredRows.value;
|
|
148
|
+
if (!s || props.load) return filteredRows.value;
|
|
110
149
|
const col = props.columns.find((c) => c.key === s.key);
|
|
111
150
|
if (!col?.sortable) return filteredRows.value;
|
|
112
151
|
const factor = s.dir === "asc" ? 1 : -1;
|
|
@@ -126,8 +165,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
126
165
|
if (!s || s.key !== col.key) next = { key: col.key, dir: "asc" };
|
|
127
166
|
else if (s.dir === "asc") next = { key: col.key, dir: "desc" };
|
|
128
167
|
else next = null;
|
|
129
|
-
|
|
130
|
-
|
|
168
|
+
withMotion(() => {
|
|
169
|
+
ownSort.value = next;
|
|
170
|
+
emit("update:sort", next);
|
|
171
|
+
});
|
|
131
172
|
}
|
|
132
173
|
function sortIcon(col) {
|
|
133
174
|
const s = activeSort.value;
|
|
@@ -136,10 +177,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
136
177
|
}
|
|
137
178
|
function headerMenu(col) {
|
|
138
179
|
const s = activeSort.value;
|
|
139
|
-
const set = (next) => () => {
|
|
180
|
+
const set = (next) => () => withMotion(() => {
|
|
140
181
|
ownSort.value = next;
|
|
141
182
|
emit("update:sort", next);
|
|
142
|
-
};
|
|
183
|
+
});
|
|
143
184
|
const items = [];
|
|
144
185
|
if (col.sortable) {
|
|
145
186
|
items.push(
|
|
@@ -205,8 +246,37 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
205
246
|
}
|
|
206
247
|
];
|
|
207
248
|
}
|
|
249
|
+
function fetchPage(delay = 0) {
|
|
250
|
+
const load = props.load;
|
|
251
|
+
if (!load) return;
|
|
252
|
+
const hidden = Object.fromEntries(
|
|
253
|
+
Object.entries(hiddenValues.value).filter(([, set]) => set.size).map(([key, set]) => [key, [...set]])
|
|
254
|
+
);
|
|
255
|
+
const query = { page: page.value, pageSize: props.pageSize, sort: activeSort.value, hidden };
|
|
256
|
+
task.run((signal) => load(query, signal), {
|
|
257
|
+
delay,
|
|
258
|
+
apply: (result) => {
|
|
259
|
+
loaded.value = result.rows;
|
|
260
|
+
total.value = result.total;
|
|
261
|
+
facets.value = result.facets ?? {};
|
|
262
|
+
failed.value = false;
|
|
263
|
+
holdHeight();
|
|
264
|
+
},
|
|
265
|
+
fail: () => {
|
|
266
|
+
failed.value = true;
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
watch([activeSort, hiddenValues], () => {
|
|
271
|
+
if (!props.load) return;
|
|
272
|
+
if (page.value !== 1) page.value = 1;
|
|
273
|
+
else fetchPage(props.debounce);
|
|
274
|
+
});
|
|
275
|
+
watch(page, () => fetchPage());
|
|
276
|
+
watch(() => props.load, () => fetchPage(), { immediate: true });
|
|
277
|
+
__expose({ reload: () => fetchPage() });
|
|
208
278
|
const selectedSet = computed(() => new Set(props.selected));
|
|
209
|
-
const allKeys = computed(() =>
|
|
279
|
+
const allKeys = computed(() => sourceRows.value.map((r, i) => keyFor(r, i)));
|
|
210
280
|
const allChecked = computed(
|
|
211
281
|
() => allKeys.value.length > 0 && allKeys.value.every((k) => selectedSet.value.has(k))
|
|
212
282
|
);
|
|
@@ -273,143 +343,169 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
273
343
|
},
|
|
274
344
|
{ immediate: true, flush: "post" }
|
|
275
345
|
);
|
|
276
|
-
onBeforeUnmount(() =>
|
|
346
|
+
onBeforeUnmount(() => {
|
|
347
|
+
ro?.disconnect();
|
|
348
|
+
if (motionTimer) clearTimeout(motionTimer);
|
|
349
|
+
});
|
|
277
350
|
const barInsetTop = computed(
|
|
278
351
|
() => props.stickyHeader && headHeight.value ? `${headHeight.value}px` : void 0
|
|
279
352
|
);
|
|
280
353
|
return (_ctx, _cache) => {
|
|
281
|
-
return openBlock(),
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
354
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
355
|
+
createVNode(_sfc_main$1, mergeProps(_ctx.$attrs, {
|
|
356
|
+
fade: false,
|
|
357
|
+
class: "rounded-card border border-line",
|
|
358
|
+
style: __props.load && floor.value ? { minHeight: `${floor.value + 2}px` } : void 0,
|
|
359
|
+
"bar-inset-top": barInsetTop.value
|
|
360
|
+
}), {
|
|
361
|
+
default: withCtx(() => [
|
|
362
|
+
createVNode(_sfc_main$2, {
|
|
363
|
+
items: contextMenu.value,
|
|
364
|
+
always: ""
|
|
365
|
+
}, {
|
|
366
|
+
default: withCtx(() => [
|
|
367
|
+
createElementVNode("table", {
|
|
368
|
+
ref_key: "tableEl",
|
|
369
|
+
ref: tableEl,
|
|
370
|
+
class: "w-full border-collapse text-sm"
|
|
297
371
|
}, [
|
|
298
|
-
createElementVNode("
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
class: ["group border-b border-line/60 outline-none transition-colors last:border-0 hover:bg-surface-soft/60 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-[active]:bg-surface-soft/40", selectedSet.value.has(keyFor(row, index)) ? "bg-brand-soft/40" : ""]
|
|
369
|
-
}, { ref_for: true }, unref(roving).itemProps(String(keyFor(row, index))), {
|
|
370
|
-
onClick: ($event) => emit("rowClick", row),
|
|
371
|
-
onKeydown: onRowKeydown
|
|
372
|
-
}), [
|
|
373
|
-
__props.selectable ? (openBlock(), createElementBlock("td", {
|
|
374
|
-
key: 0,
|
|
375
|
-
class: "w-px px-4 py-3",
|
|
376
|
-
onClick: _cache[0] || (_cache[0] = withModifiers(() => {
|
|
377
|
-
}, ["stop"]))
|
|
372
|
+
createElementVNode("thead", {
|
|
373
|
+
ref_key: "headEl",
|
|
374
|
+
ref: headEl,
|
|
375
|
+
class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10 bg-surface-soft" : "")
|
|
376
|
+
}, [
|
|
377
|
+
createElementVNode("tr", _hoisted_2, [
|
|
378
|
+
__props.selectable ? (openBlock(), createElementBlock("th", _hoisted_3, [
|
|
379
|
+
createVNode(_sfc_main$3, {
|
|
380
|
+
"model-value": allChecked.value,
|
|
381
|
+
indeterminate: someChecked.value,
|
|
382
|
+
"onUpdate:modelValue": toggleAll
|
|
383
|
+
}, null, 8, ["model-value", "indeterminate"])
|
|
384
|
+
])) : createCommentVNode("", true),
|
|
385
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col) => {
|
|
386
|
+
return openBlock(), createElementBlock("th", {
|
|
387
|
+
key: col.key,
|
|
388
|
+
style: normalizeStyle(col.width ? { width: col.width } : void 0),
|
|
389
|
+
class: normalizeClass(["px-4 py-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted", alignClass(col.align)]),
|
|
390
|
+
"aria-sort": activeSort.value?.key === col.key ? activeSort.value.dir === "asc" ? "ascending" : "descending" : void 0
|
|
391
|
+
}, [
|
|
392
|
+
col.sortable || col.filterable ? (openBlock(), createBlock(_sfc_main$2, {
|
|
393
|
+
key: 0,
|
|
394
|
+
items: headerMenu(col)
|
|
395
|
+
}, {
|
|
396
|
+
default: withCtx(() => [
|
|
397
|
+
createElementVNode("button", {
|
|
398
|
+
type: "button",
|
|
399
|
+
class: normalizeClass(["group/sort -mx-1 inline-flex items-center gap-1 rounded px-1 outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring", [
|
|
400
|
+
col.align === "right" ? "flex-row-reverse" : "",
|
|
401
|
+
activeSort.value?.key === col.key ? "text-ink" : ""
|
|
402
|
+
]]),
|
|
403
|
+
onClick: ($event) => toggleSort(col)
|
|
404
|
+
}, [
|
|
405
|
+
createTextVNode(toDisplayString(col.label) + " ", 1),
|
|
406
|
+
!col.sortable ? (openBlock(), createBlock(_sfc_main$4, {
|
|
407
|
+
key: 0,
|
|
408
|
+
name: "lucide:filter",
|
|
409
|
+
size: 12,
|
|
410
|
+
class: normalizeClass(["shrink-0", hiddenValues.value[col.key]?.size ? "text-brand opacity-100" : "opacity-40 group-hover/sort:opacity-70"])
|
|
411
|
+
}, null, 8, ["class"])) : (openBlock(), createBlock(_sfc_main$4, {
|
|
412
|
+
key: 1,
|
|
413
|
+
name: sortIcon(col),
|
|
414
|
+
size: 13,
|
|
415
|
+
class: normalizeClass([
|
|
416
|
+
"shrink-0 transition-[transform,opacity] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)]",
|
|
417
|
+
activeSort.value?.key === col.key ? "opacity-100" : "opacity-40 group-hover/sort:opacity-70"
|
|
418
|
+
])
|
|
419
|
+
}, null, 8, ["name", "class"]))
|
|
420
|
+
], 10, _hoisted_5)
|
|
421
|
+
]),
|
|
422
|
+
_: 2
|
|
423
|
+
}, 1032, ["items"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
424
|
+
createTextVNode(toDisplayString(col.label), 1)
|
|
425
|
+
], 64))
|
|
426
|
+
], 14, _hoisted_4);
|
|
427
|
+
}), 128))
|
|
428
|
+
])
|
|
429
|
+
], 2),
|
|
430
|
+
createVNode(TransitionGroup, {
|
|
431
|
+
tag: "tbody",
|
|
432
|
+
name: "lp-row",
|
|
433
|
+
css: animating.value,
|
|
434
|
+
class: normalizeClass(unref(loading) && displayRows.value.length ? "opacity-60 transition-opacity" : "transition-opacity"),
|
|
435
|
+
ref: (el) => unref(roving).setContainer(el?.$el ?? null)
|
|
436
|
+
}, {
|
|
437
|
+
default: withCtx(() => [
|
|
438
|
+
displayRows.value.length === 0 ? (openBlock(), createElementBlock("tr", _hoisted_6, [
|
|
439
|
+
createElementVNode("td", {
|
|
440
|
+
colspan: colSpan.value,
|
|
441
|
+
class: "px-4 py-10 text-center text-muted"
|
|
378
442
|
}, [
|
|
379
|
-
createVNode(_sfc_main$
|
|
380
|
-
"
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
443
|
+
createVNode(_sfc_main$4, {
|
|
444
|
+
name: unref(loading) ? "lucide:loader-circle" : failed.value ? "lucide:circle-alert" : __props.emptyIcon,
|
|
445
|
+
size: 22,
|
|
446
|
+
class: normalizeClass(["mx-auto mb-2 opacity-60", unref(loading) ? "animate-spin" : ""])
|
|
447
|
+
}, null, 8, ["name", "class"]),
|
|
448
|
+
createElementVNode("div", null, toDisplayString(unref(loading) ? __props.loadingLabel : failed.value ? __props.errorLabel : __props.emptyLabel), 1)
|
|
449
|
+
], 8, _hoisted_7)
|
|
450
|
+
])) : createCommentVNode("", true),
|
|
451
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(displayRows.value, (row, index) => {
|
|
452
|
+
return openBlock(), createElementBlock("tr", mergeProps({
|
|
453
|
+
key: keyFor(row, index),
|
|
454
|
+
onContextmenu: ($event) => onRowContextMenu(row),
|
|
455
|
+
class: ["group border-b border-line/60 outline-none transition-colors last:border-0 hover:bg-surface-soft/60 focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring data-[active]:bg-surface-soft/40", selectedSet.value.has(keyFor(row, index)) ? "bg-brand-soft/40" : ""]
|
|
456
|
+
}, { ref_for: true }, unref(roving).itemProps(String(keyFor(row, index))), {
|
|
457
|
+
onClick: ($event) => emit("rowClick", row),
|
|
458
|
+
onKeydown: onRowKeydown
|
|
459
|
+
}), [
|
|
460
|
+
__props.selectable ? (openBlock(), createElementBlock("td", {
|
|
461
|
+
key: 0,
|
|
462
|
+
class: "w-px px-4 py-3",
|
|
463
|
+
onClick: _cache[0] || (_cache[0] = withModifiers(() => {
|
|
464
|
+
}, ["stop"]))
|
|
388
465
|
}, [
|
|
389
|
-
|
|
390
|
-
row,
|
|
391
|
-
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
466
|
+
createVNode(_sfc_main$3, {
|
|
467
|
+
"model-value": selectedSet.value.has(keyFor(row, index)),
|
|
468
|
+
"onUpdate:modelValue": (v) => toggleRow(keyFor(row, index), v)
|
|
469
|
+
}, null, 8, ["model-value", "onUpdate:modelValue"])
|
|
470
|
+
])) : createCommentVNode("", true),
|
|
471
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col) => {
|
|
472
|
+
return openBlock(), createElementBlock("td", {
|
|
473
|
+
key: col.key,
|
|
474
|
+
class: normalizeClass(["px-4 py-3 text-ink", alignClass(col.align)])
|
|
475
|
+
}, [
|
|
476
|
+
renderSlot(_ctx.$slots, `cell-${col.key}`, {
|
|
477
|
+
row,
|
|
478
|
+
value: row[col.key]
|
|
479
|
+
}, () => [
|
|
480
|
+
createTextVNode(toDisplayString(row[col.key]), 1)
|
|
481
|
+
], true)
|
|
482
|
+
], 2);
|
|
483
|
+
}), 128))
|
|
484
|
+
], 16, _hoisted_8);
|
|
485
|
+
}), 128))
|
|
486
|
+
]),
|
|
487
|
+
_: 3
|
|
488
|
+
}, 8, ["css", "class"])
|
|
489
|
+
], 512)
|
|
490
|
+
]),
|
|
491
|
+
_: 3
|
|
492
|
+
}, 8, ["items"])
|
|
493
|
+
]),
|
|
494
|
+
_: 3
|
|
495
|
+
}, 16, ["style", "bar-inset-top"]),
|
|
496
|
+
__props.load && total.value > __props.pageSize ? (openBlock(), createBlock(_sfc_main$5, {
|
|
497
|
+
key: 0,
|
|
498
|
+
page: page.value,
|
|
499
|
+
"onUpdate:page": _cache[1] || (_cache[1] = ($event) => page.value = $event),
|
|
500
|
+
total: total.value,
|
|
501
|
+
"page-size": __props.pageSize,
|
|
502
|
+
disabled: unref(loading)
|
|
503
|
+
}, null, 8, ["page", "total", "page-size", "disabled"])) : createCommentVNode("", true)
|
|
504
|
+
]);
|
|
409
505
|
};
|
|
410
506
|
}
|
|
411
507
|
});
|
|
412
|
-
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
508
|
+
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-63738e53"]]);
|
|
413
509
|
export {
|
|
414
510
|
LpTable as default
|
|
415
511
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One debounced, abortable request at a time — the shape every "fetch on input"
|
|
3
|
+
* control in the kit needs: the command palette's `search`, the table's `load`.
|
|
4
|
+
*
|
|
5
|
+
* Two races are lost silently without it: a stale response overwriting a
|
|
6
|
+
* fresher one, and a response landing after the control moved on. The token
|
|
7
|
+
* guards both — the abort signal only asks the consumer to stop, it cannot
|
|
8
|
+
* promise they did.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAsyncTask<T>(): {
|
|
11
|
+
pending: import('vue').Ref<boolean, boolean>;
|
|
12
|
+
run: (task: (signal: AbortSignal) => T | Promise<T>, options: {
|
|
13
|
+
delay?: number;
|
|
14
|
+
apply: (value: T) => void;
|
|
15
|
+
fail?: (error: unknown) => void;
|
|
16
|
+
}) => void;
|
|
17
|
+
cancel: () => void;
|
|
18
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ref, getCurrentScope, onScopeDispose } from "vue";
|
|
2
|
+
function useAsyncTask() {
|
|
3
|
+
const pending = ref(false);
|
|
4
|
+
let timer;
|
|
5
|
+
let controller;
|
|
6
|
+
let token = 0;
|
|
7
|
+
function cancel() {
|
|
8
|
+
if (timer) clearTimeout(timer);
|
|
9
|
+
timer = void 0;
|
|
10
|
+
controller?.abort();
|
|
11
|
+
controller = void 0;
|
|
12
|
+
token += 1;
|
|
13
|
+
pending.value = false;
|
|
14
|
+
}
|
|
15
|
+
function run(task, options) {
|
|
16
|
+
cancel();
|
|
17
|
+
pending.value = true;
|
|
18
|
+
const mine = ++token;
|
|
19
|
+
timer = setTimeout(async () => {
|
|
20
|
+
controller = new AbortController();
|
|
21
|
+
try {
|
|
22
|
+
const value = await task(controller.signal);
|
|
23
|
+
if (mine === token) options.apply(value);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (mine === token) options.fail?.(error);
|
|
26
|
+
} finally {
|
|
27
|
+
if (mine === token) pending.value = false;
|
|
28
|
+
}
|
|
29
|
+
}, options.delay ?? 0);
|
|
30
|
+
}
|
|
31
|
+
if (getCurrentScope()) onScopeDispose(cancel);
|
|
32
|
+
return { pending, run, cancel };
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
useAsyncTask
|
|
36
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -101,7 +101,7 @@ export { default as LpStepper } from './components/LpStepper.vue';
|
|
|
101
101
|
export type { Step } from './components/LpStepper.vue';
|
|
102
102
|
export { default as LpSwitch } from './components/LpSwitch.vue';
|
|
103
103
|
export { default as LpTable } from './components/LpTable.vue';
|
|
104
|
-
export type { SortState, TableColumn } from './components/LpTable.vue';
|
|
104
|
+
export type { SortState, TableColumn, TablePage, TableQuery } from './components/LpTable.vue';
|
|
105
105
|
export { default as LpTableOfContents } from './components/LpTableOfContents.vue';
|
|
106
106
|
export type { TocLink } from './components/LpTableOfContents.vue';
|
|
107
107
|
export { default as LpTabs } from './components/LpTabs.vue';
|
|
@@ -142,6 +142,7 @@ export type { UseInputFilterOptions } from './composables/useInputFilter';
|
|
|
142
142
|
export { easeOut, easeTravel, easingToken, prefersReducedMotion } from './composables/easing';
|
|
143
143
|
export { useReveal } from './composables/useReveal';
|
|
144
144
|
export type { UseReveal, UseRevealOptions } from './composables/useReveal';
|
|
145
|
+
export { useAsyncTask } from './composables/useAsyncTask';
|
|
145
146
|
export { useRovingFocus } from './composables/useRovingFocus';
|
|
146
147
|
export type { RovingItem, UseRovingFocusOptions } from './composables/useRovingFocus';
|
|
147
148
|
export { useFormDraft } from './composables/useFormDraft';
|
package/dist/index.js
CHANGED
|
@@ -91,6 +91,7 @@ import { mergeClass, useMergedAttrs, useMergedClass } from "./composables/useMer
|
|
|
91
91
|
import { useInputFilter } from "./composables/useInputFilter.js";
|
|
92
92
|
import { easeOut, easeTravel, easingToken, prefersReducedMotion } from "./composables/easing.js";
|
|
93
93
|
import { useReveal } from "./composables/useReveal.js";
|
|
94
|
+
import { useAsyncTask } from "./composables/useAsyncTask.js";
|
|
94
95
|
import { useRovingFocus } from "./composables/useRovingFocus.js";
|
|
95
96
|
import { useFormDraft } from "./composables/useFormDraft.js";
|
|
96
97
|
import { useSettled } from "./composables/useSettled.js";
|
|
@@ -290,6 +291,7 @@ export {
|
|
|
290
291
|
m as themeToCssVars,
|
|
291
292
|
tileUrl,
|
|
292
293
|
unproject,
|
|
294
|
+
useAsyncTask,
|
|
293
295
|
useClipboard,
|
|
294
296
|
useFormDraft,
|
|
295
297
|
useHotkeys,
|