@leavepulse/ui 0.29.2 → 0.29.4

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.
Files changed (34) hide show
  1. package/dist/assets/{LpTable-ChxURw0m.css → LpTable-Bz3uJNa3.css} +8 -8
  2. package/dist/chunks/{LpAppShell-CGX0DlIW.js → LpAppShell-BJk3b-u4.js} +1 -1
  3. package/dist/chunks/{LpAvatar-CzCE5cXg.js → LpAvatar-D3l4nabg.js} +1 -1
  4. package/dist/chunks/{LpCard-DSefTGEH.js → LpCard-D0M2ZCnB.js} +1 -1
  5. package/dist/chunks/{LpChart-UiU4RhXP.js → LpChart-Bmw7y34Z.js} +1 -1
  6. package/dist/chunks/{LpContextMenu-D0mzXqA7.js → LpContextMenu-B6WBLleS.js} +55 -8
  7. package/dist/chunks/{LpFileTree-BaV7f0O7.js → LpFileTree-DfFqY__1.js} +1 -1
  8. package/dist/chunks/{LpFileTreeNode-BzpBa7R7.js → LpFileTreeNode-COchUMNn.js} +1 -1
  9. package/dist/chunks/{LpLogViewer-CKmEQYnm.js → LpLogViewer-CVaGfTF_.js} +1 -1
  10. package/dist/chunks/{LpNotificationBell-Dvu-b6Vn.js → LpNotificationBell-CTsIcPqX.js} +1 -1
  11. package/dist/chunks/{LpPie-BkjWlfSw.js → LpPie-BL1vTDSM.js} +1 -1
  12. package/dist/chunks/{LpSidebar-CWehDCxN.js → LpSidebar-C_Mr-JPI.js} +1 -1
  13. package/dist/chunks/{LpSidebarNav-5RGNr2D3.js → LpSidebarNav-D6v12-36.js} +1 -1
  14. package/dist/chunks/{LpThemeSwitcher-Dj4VcTu7.js → LpThemeSwitcher-r1te9nB2.js} +1 -1
  15. package/dist/components/LpAppShell.vue.js +1 -1
  16. package/dist/components/LpAvatar.vue.js +1 -1
  17. package/dist/components/LpCard.vue.js +1 -1
  18. package/dist/components/LpChart.vue.js +1 -1
  19. package/dist/components/LpContextMenu.vue.d.ts +7 -0
  20. package/dist/components/LpContextMenu.vue.js +1 -1
  21. package/dist/components/LpFileTree.vue.js +1 -1
  22. package/dist/components/LpFileTreeNode.vue.js +1 -1
  23. package/dist/components/LpLogViewer.vue.js +1 -1
  24. package/dist/components/LpNotificationBell.vue.js +1 -1
  25. package/dist/components/LpPie.vue.js +1 -1
  26. package/dist/components/LpSidebar.vue.js +1 -1
  27. package/dist/components/LpSidebarNav.vue.js +1 -1
  28. package/dist/components/LpTable.vue.d.ts +24 -0
  29. package/dist/components/LpTable.vue.js +247 -118
  30. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  31. package/dist/index.js +12 -12
  32. package/package.json +1 -1
  33. package/src/components/LpContextMenu.vue +49 -0
  34. package/src/components/LpTable.vue +205 -6
@@ -1,11 +1,11 @@
1
- import { defineComponent, ref, computed, watch, onBeforeUnmount, openBlock, createBlock, withCtx, createElementVNode, normalizeClass, createElementBlock, createVNode, createCommentVNode, Fragment, renderList, normalizeStyle, createTextVNode, toDisplayString, TransitionGroup, unref, mergeProps, withModifiers, renderSlot } from "vue";
2
- import { _ as _sfc_main$2 } from "../chunks/LpCheckbox-BO0zuuPh.js";
3
- import { _ as _sfc_main$4 } from "../chunks/LpContextMenu-D0mzXqA7.js";
4
- import { _ as _sfc_main$3 } from "../chunks/LpIcon-CCnX5_2j.js";
1
+ import { defineComponent, ref, computed, watch, onBeforeUnmount, openBlock, createBlock, withCtx, createVNode, createElementVNode, normalizeClass, createElementBlock, createCommentVNode, Fragment, renderList, normalizeStyle, createTextVNode, toDisplayString, TransitionGroup, unref, mergeProps, withModifiers, renderSlot } from "vue";
2
+ import { _ as _sfc_main$3 } from "../chunks/LpCheckbox-BO0zuuPh.js";
3
+ import { _ as _sfc_main$2 } from "../chunks/LpContextMenu-B6WBLleS.js";
4
+ import { _ as _sfc_main$4 } from "../chunks/LpIcon-CCnX5_2j.js";
5
5
  import { _ as _sfc_main$1 } from "../chunks/LpScrollArea-D8xyKXcF.js";
6
6
  import { useRovingFocus } from "../composables/useRovingFocus.js";
7
7
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
8
- import '../assets/LpTable-ChxURw0m.css';const _hoisted_1 = { class: "w-full border-collapse text-sm" };
8
+ import '../assets/LpTable-Bz3uJNa3.css';const _hoisted_1 = { class: "w-full border-collapse text-sm" };
9
9
  const _hoisted_2 = { class: "border-b border-line bg-surface-soft" };
10
10
  const _hoisted_3 = {
11
11
  key: 0,
@@ -28,7 +28,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
28
28
  selected: { default: () => [] },
29
29
  sort: { default: null },
30
30
  stickyHeader: { type: Boolean },
31
- rowMenu: {}
31
+ rowMenu: {},
32
+ sortAscLabel: { default: "Sort ascending" },
33
+ sortDescLabel: { default: "Sort descending" },
34
+ sortClearLabel: { default: "Clear sorting" },
35
+ filterLabel: { default: "Show only" },
36
+ showAllLabel: { default: "Show all" },
37
+ columnsLabel: { default: "Columns" }
32
38
  },
33
39
  emits: ["update:selected", "update:sort", "rowClick"],
34
40
  setup(__props, { emit: __emit }) {
@@ -49,13 +55,51 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
49
55
  }
50
56
  const ownSort = ref(null);
51
57
  const activeSort = computed(() => props.sort ?? ownSort.value);
58
+ const hiddenValues = ref({});
59
+ const hiddenColumns = ref(/* @__PURE__ */ new Set());
60
+ const visibleColumns = computed(
61
+ () => props.columns.filter((c) => !hiddenColumns.value.has(c.key))
62
+ );
63
+ function valuesOf(col) {
64
+ const seen = [];
65
+ for (const row of props.rows) {
66
+ const v = row[col.key];
67
+ if (!seen.includes(v)) seen.push(v);
68
+ }
69
+ return seen;
70
+ }
71
+ function isValueHidden(key, value) {
72
+ return hiddenValues.value[key]?.has(value) ?? false;
73
+ }
74
+ function toggleValue(key, value) {
75
+ const next = new Set(hiddenValues.value[key] ?? []);
76
+ if (next.has(value)) next.delete(value);
77
+ else next.add(value);
78
+ hiddenValues.value = { ...hiddenValues.value, [key]: next };
79
+ }
80
+ function showAllValues(key) {
81
+ hiddenValues.value = { ...hiddenValues.value, [key]: /* @__PURE__ */ new Set() };
82
+ }
83
+ function toggleColumn(key) {
84
+ const next = new Set(hiddenColumns.value);
85
+ if (next.has(key)) next.delete(key);
86
+ else next.add(key);
87
+ hiddenColumns.value = next;
88
+ }
89
+ const filteredRows = computed(() => {
90
+ const active = Object.entries(hiddenValues.value).filter(([, set]) => set.size);
91
+ if (!active.length) return props.rows;
92
+ return props.rows.filter(
93
+ (row) => active.every(([key, hidden]) => !hidden.has(row[key]))
94
+ );
95
+ });
52
96
  const displayRows = computed(() => {
53
97
  const s = activeSort.value;
54
- if (!s) return props.rows;
98
+ if (!s) return filteredRows.value;
55
99
  const col = props.columns.find((c) => c.key === s.key);
56
- if (!col?.sortable) return props.rows;
100
+ if (!col?.sortable) return filteredRows.value;
57
101
  const factor = s.dir === "asc" ? 1 : -1;
58
- return [...props.rows].sort((a, b) => compare(a[s.key], b[s.key]) * factor);
102
+ return [...filteredRows.value].sort((a, b) => compare(a[s.key], b[s.key]) * factor);
59
103
  });
60
104
  function compare(a, b) {
61
105
  if (a == null && b == null) return 0;
@@ -79,6 +123,77 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
79
123
  if (s?.key !== col.key) return "lucide:chevrons-up-down";
80
124
  return s.dir === "asc" ? "lucide:arrow-up" : "lucide:arrow-down";
81
125
  }
126
+ function headerMenu(col) {
127
+ const s = activeSort.value;
128
+ const set = (next) => () => {
129
+ ownSort.value = next;
130
+ emit("update:sort", next);
131
+ };
132
+ const items = [];
133
+ if (col.sortable) {
134
+ items.push(
135
+ {
136
+ label: props.sortAscLabel,
137
+ icon: "lucide:arrow-up",
138
+ disabled: s?.key === col.key && s.dir === "asc",
139
+ onSelect: set({ key: col.key, dir: "asc" })
140
+ },
141
+ {
142
+ label: props.sortDescLabel,
143
+ icon: "lucide:arrow-down",
144
+ disabled: s?.key === col.key && s.dir === "desc",
145
+ onSelect: set({ key: col.key, dir: "desc" })
146
+ },
147
+ {
148
+ label: props.sortClearLabel,
149
+ icon: "lucide:x",
150
+ disabled: !s,
151
+ onSelect: set(null)
152
+ }
153
+ );
154
+ }
155
+ if (col.filterable) {
156
+ const hidden = hiddenValues.value[col.key];
157
+ items.push({
158
+ label: props.filterLabel,
159
+ icon: "lucide:filter",
160
+ separatorBefore: items.length > 0,
161
+ children: [
162
+ ...valuesOf(col).map((value) => ({
163
+ label: col.valueLabel?.(value) ?? String(value ?? "—"),
164
+ checked: !isValueHidden(col.key, value),
165
+ onSelect: () => toggleValue(col.key, value)
166
+ })),
167
+ {
168
+ label: props.showAllLabel,
169
+ icon: "lucide:list",
170
+ separatorBefore: true,
171
+ disabled: !hidden?.size,
172
+ onSelect: () => showAllValues(col.key)
173
+ }
174
+ ]
175
+ });
176
+ }
177
+ return items;
178
+ }
179
+ function tableMenu() {
180
+ const offered = props.columns.filter((c) => !c.alwaysVisible && c.label);
181
+ if (!offered.length) return [];
182
+ return [
183
+ {
184
+ label: props.columnsLabel,
185
+ icon: "lucide:columns-3",
186
+ children: offered.map((col) => ({
187
+ label: col.label,
188
+ checked: !hiddenColumns.value.has(col.key),
189
+ // The last visible column cannot be hidden: a table with no columns is
190
+ // a blank card the operator has no way back from.
191
+ disabled: !hiddenColumns.value.has(col.key) && visibleColumns.value.length <= 1,
192
+ onSelect: () => toggleColumn(col.key)
193
+ }))
194
+ }
195
+ ];
196
+ }
82
197
  const selectedSet = computed(() => new Set(props.selected));
83
198
  const allKeys = computed(() => props.rows.map((r, i) => keyFor(r, i)));
84
199
  const allChecked = computed(
@@ -158,123 +273,137 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
158
273
  "bar-inset-top": barInsetTop.value
159
274
  }, {
160
275
  default: withCtx(() => [
161
- createElementVNode("table", _hoisted_1, [
162
- createElementVNode("thead", {
163
- ref_key: "headEl",
164
- ref: headEl,
165
- class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10" : "")
166
- }, [
167
- createElementVNode("tr", _hoisted_2, [
168
- __props.selectable ? (openBlock(), createElementBlock("th", _hoisted_3, [
169
- createVNode(_sfc_main$2, {
170
- "model-value": allChecked.value,
171
- indeterminate: someChecked.value,
172
- "onUpdate:modelValue": toggleAll
173
- }, null, 8, ["model-value", "indeterminate"])
174
- ])) : createCommentVNode("", true),
175
- (openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (col) => {
176
- return openBlock(), createElementBlock("th", {
177
- key: col.key,
178
- style: normalizeStyle(col.width ? { width: col.width } : void 0),
179
- class: normalizeClass(["px-4 py-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted", alignClass(col.align)]),
180
- "aria-sort": activeSort.value?.key === col.key ? activeSort.value.dir === "asc" ? "ascending" : "descending" : void 0
181
- }, [
182
- col.sortable ? (openBlock(), createElementBlock("button", {
183
- key: 0,
184
- type: "button",
185
- 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", [
186
- col.align === "right" ? "flex-row-reverse" : "",
187
- activeSort.value?.key === col.key ? "text-ink" : ""
188
- ]]),
189
- onClick: ($event) => toggleSort(col)
190
- }, [
191
- createTextVNode(toDisplayString(col.label) + " ", 1),
276
+ createVNode(_sfc_main$2, {
277
+ items: tableMenu()
278
+ }, {
279
+ default: withCtx(() => [
280
+ createElementVNode("table", _hoisted_1, [
281
+ createElementVNode("thead", {
282
+ ref_key: "headEl",
283
+ ref: headEl,
284
+ class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10" : "")
285
+ }, [
286
+ createElementVNode("tr", _hoisted_2, [
287
+ __props.selectable ? (openBlock(), createElementBlock("th", _hoisted_3, [
192
288
  createVNode(_sfc_main$3, {
193
- name: sortIcon(col),
194
- size: 13,
195
- class: normalizeClass([
196
- "shrink-0 transition-[transform,opacity] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)]",
197
- activeSort.value?.key === col.key ? "opacity-100" : "opacity-40 group-hover/sort:opacity-70"
198
- ])
199
- }, null, 8, ["name", "class"])
200
- ], 10, _hoisted_5)) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
201
- createTextVNode(toDisplayString(col.label), 1)
202
- ], 64))
203
- ], 14, _hoisted_4);
204
- }), 128))
205
- ])
206
- ], 2),
207
- createVNode(TransitionGroup, {
208
- tag: "tbody",
209
- name: "lp-row",
210
- ref: (el) => unref(roving).setContainer(el?.$el ?? null)
211
- }, {
212
- default: withCtx(() => [
213
- displayRows.value.length === 0 ? (openBlock(), createElementBlock("tr", _hoisted_6, [
214
- createElementVNode("td", {
215
- colspan: colSpan.value,
216
- class: "px-4 py-10 text-center text-muted"
217
- }, [
218
- createVNode(_sfc_main$3, {
219
- name: __props.emptyIcon,
220
- size: 22,
221
- class: "mx-auto mb-2 opacity-60"
222
- }, null, 8, ["name"]),
223
- createElementVNode("div", null, toDisplayString(__props.emptyLabel), 1)
224
- ], 8, _hoisted_7)
225
- ])) : createCommentVNode("", true),
226
- (openBlock(true), createElementBlock(Fragment, null, renderList(displayRows.value, (row, index) => {
227
- return openBlock(), createBlock(_sfc_main$4, {
228
- key: keyFor(row, index),
229
- items: menuFor(row)
230
- }, {
231
- default: withCtx(() => [
232
- createElementVNode("tr", mergeProps({
233
- 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" : ""]
234
- }, { ref_for: true }, unref(roving).itemProps(String(keyFor(row, index))), {
235
- onClick: ($event) => emit("rowClick", row),
236
- onKeydown: onRowKeydown
237
- }), [
238
- __props.selectable ? (openBlock(), createElementBlock("td", {
289
+ "model-value": allChecked.value,
290
+ indeterminate: someChecked.value,
291
+ "onUpdate:modelValue": toggleAll
292
+ }, null, 8, ["model-value", "indeterminate"])
293
+ ])) : createCommentVNode("", true),
294
+ (openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col) => {
295
+ return openBlock(), createElementBlock("th", {
296
+ key: col.key,
297
+ style: normalizeStyle(col.width ? { width: col.width } : void 0),
298
+ class: normalizeClass(["px-4 py-3 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted", alignClass(col.align)]),
299
+ "aria-sort": activeSort.value?.key === col.key ? activeSort.value.dir === "asc" ? "ascending" : "descending" : void 0
300
+ }, [
301
+ col.sortable ? (openBlock(), createBlock(_sfc_main$2, {
239
302
  key: 0,
240
- class: "w-px px-4 py-3",
241
- onClick: _cache[0] || (_cache[0] = withModifiers(() => {
242
- }, ["stop"]))
243
- }, [
244
- createVNode(_sfc_main$2, {
245
- "model-value": selectedSet.value.has(keyFor(row, index)),
246
- "onUpdate:modelValue": (v) => toggleRow(keyFor(row, index), v)
247
- }, null, 8, ["model-value", "onUpdate:modelValue"])
248
- ])) : createCommentVNode("", true),
249
- (openBlock(true), createElementBlock(Fragment, null, renderList(__props.columns, (col) => {
250
- return openBlock(), createElementBlock("td", {
251
- key: col.key,
252
- class: normalizeClass(["px-4 py-3 text-ink", alignClass(col.align)])
253
- }, [
254
- renderSlot(_ctx.$slots, `cell-${col.key}`, {
255
- row,
256
- value: row[col.key]
257
- }, () => [
258
- createTextVNode(toDisplayString(row[col.key]), 1)
259
- ], true)
260
- ], 2);
261
- }), 128))
262
- ], 16, _hoisted_8)
263
- ]),
264
- _: 2
265
- }, 1032, ["items"]);
266
- }), 128))
267
- ]),
268
- _: 3
269
- }, 512)
270
- ])
303
+ items: headerMenu(col)
304
+ }, {
305
+ default: withCtx(() => [
306
+ createElementVNode("button", {
307
+ type: "button",
308
+ 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", [
309
+ col.align === "right" ? "flex-row-reverse" : "",
310
+ activeSort.value?.key === col.key ? "text-ink" : ""
311
+ ]]),
312
+ onClick: ($event) => toggleSort(col)
313
+ }, [
314
+ createTextVNode(toDisplayString(col.label) + " ", 1),
315
+ createVNode(_sfc_main$4, {
316
+ name: sortIcon(col),
317
+ size: 13,
318
+ class: normalizeClass([
319
+ "shrink-0 transition-[transform,opacity] duration-[var(--duration-fast)] ease-[var(--ease-emphasized)]",
320
+ activeSort.value?.key === col.key ? "opacity-100" : "opacity-40 group-hover/sort:opacity-70"
321
+ ])
322
+ }, null, 8, ["name", "class"])
323
+ ], 10, _hoisted_5)
324
+ ]),
325
+ _: 2
326
+ }, 1032, ["items"])) : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
327
+ createTextVNode(toDisplayString(col.label), 1)
328
+ ], 64))
329
+ ], 14, _hoisted_4);
330
+ }), 128))
331
+ ])
332
+ ], 2),
333
+ createVNode(TransitionGroup, {
334
+ tag: "tbody",
335
+ name: "lp-row",
336
+ ref: (el) => unref(roving).setContainer(el?.$el ?? null)
337
+ }, {
338
+ default: withCtx(() => [
339
+ displayRows.value.length === 0 ? (openBlock(), createElementBlock("tr", _hoisted_6, [
340
+ createElementVNode("td", {
341
+ colspan: colSpan.value,
342
+ class: "px-4 py-10 text-center text-muted"
343
+ }, [
344
+ createVNode(_sfc_main$4, {
345
+ name: __props.emptyIcon,
346
+ size: 22,
347
+ class: "mx-auto mb-2 opacity-60"
348
+ }, null, 8, ["name"]),
349
+ createElementVNode("div", null, toDisplayString(__props.emptyLabel), 1)
350
+ ], 8, _hoisted_7)
351
+ ])) : createCommentVNode("", true),
352
+ (openBlock(true), createElementBlock(Fragment, null, renderList(displayRows.value, (row, index) => {
353
+ return openBlock(), createBlock(_sfc_main$2, {
354
+ key: keyFor(row, index),
355
+ items: menuFor(row)
356
+ }, {
357
+ default: withCtx(() => [
358
+ createElementVNode("tr", mergeProps({
359
+ 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" : ""]
360
+ }, { ref_for: true }, unref(roving).itemProps(String(keyFor(row, index))), {
361
+ onClick: ($event) => emit("rowClick", row),
362
+ onKeydown: onRowKeydown
363
+ }), [
364
+ __props.selectable ? (openBlock(), createElementBlock("td", {
365
+ key: 0,
366
+ class: "w-px px-4 py-3",
367
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {
368
+ }, ["stop"]))
369
+ }, [
370
+ createVNode(_sfc_main$3, {
371
+ "model-value": selectedSet.value.has(keyFor(row, index)),
372
+ "onUpdate:modelValue": (v) => toggleRow(keyFor(row, index), v)
373
+ }, null, 8, ["model-value", "onUpdate:modelValue"])
374
+ ])) : createCommentVNode("", true),
375
+ (openBlock(true), createElementBlock(Fragment, null, renderList(visibleColumns.value, (col) => {
376
+ return openBlock(), createElementBlock("td", {
377
+ key: col.key,
378
+ class: normalizeClass(["px-4 py-3 text-ink", alignClass(col.align)])
379
+ }, [
380
+ renderSlot(_ctx.$slots, `cell-${col.key}`, {
381
+ row,
382
+ value: row[col.key]
383
+ }, () => [
384
+ createTextVNode(toDisplayString(row[col.key]), 1)
385
+ ], true)
386
+ ], 2);
387
+ }), 128))
388
+ ], 16, _hoisted_8)
389
+ ]),
390
+ _: 2
391
+ }, 1032, ["items"]);
392
+ }), 128))
393
+ ]),
394
+ _: 3
395
+ }, 512)
396
+ ])
397
+ ]),
398
+ _: 3
399
+ }, 8, ["items"])
271
400
  ]),
272
401
  _: 3
273
402
  }, 8, ["bar-inset-top"]);
274
403
  };
275
404
  }
276
405
  });
277
- const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-359c1c0d"]]);
406
+ const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-f9c874a7"]]);
278
407
  export {
279
408
  LpTable as default
280
409
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpThemeSwitcher-Dj4VcTu7.js";
1
+ import { _ as _sfc_main } from "../chunks/LpThemeSwitcher-r1te9nB2.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
package/dist/index.js CHANGED
@@ -3,21 +3,21 @@ import { _ as _2 } from "./chunks/LpAddressInput-Bk6D8Bcy.js";
3
3
  import { _ as _3 } from "./chunks/LpMapPicker-CXvoTrAe.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
5
  import { _ as _4 } from "./chunks/LpAlert-BAYHDSS1.js";
6
- import { _ as _5 } from "./chunks/LpAppShell-CGX0DlIW.js";
6
+ import { _ as _5 } from "./chunks/LpAppShell-BJk3b-u4.js";
7
7
  import { _ as _6 } from "./chunks/LpAutocomplete-DExlIZe_.js";
8
- import { _ as _7 } from "./chunks/LpAvatar-CzCE5cXg.js";
8
+ import { _ as _7 } from "./chunks/LpAvatar-D3l4nabg.js";
9
9
  import { _ as _8 } from "./chunks/LpBadge-BPodVcJa.js";
10
10
  import { _ as _9 } from "./chunks/LpBreadcrumbs-5uvViCXi.js";
11
11
  import { _ as _10 } from "./chunks/LpButton-5VXXCjwF.js";
12
12
  import { _ as _11 } from "./chunks/LpCalendar-B7ZL0ty3.js";
13
- import { _ as _12 } from "./chunks/LpCard-DSefTGEH.js";
14
- import { _ as _13 } from "./chunks/LpChart-UiU4RhXP.js";
13
+ import { _ as _12 } from "./chunks/LpCard-D0M2ZCnB.js";
14
+ import { _ as _13 } from "./chunks/LpChart-Bmw7y34Z.js";
15
15
  import { CHART_PALETTE, axisTicks, formatTick, niceScale, resolveSlices } from "./components/chart.js";
16
16
  import { _ as _14 } from "./chunks/LpCheckbox-BO0zuuPh.js";
17
17
  import { _ as _15 } from "./chunks/LpCodeBlock-rS_6mO3D.js";
18
18
  import { default as default2 } from "./components/LpCommandPalette.vue.js";
19
19
  import { _ as _16 } from "./chunks/LpConfirmDialog-CjOGcj1e.js";
20
- import { _ as _17 } from "./chunks/LpContextMenu-D0mzXqA7.js";
20
+ import { _ as _17 } from "./chunks/LpContextMenu-B6WBLleS.js";
21
21
  import { _ as _18 } from "./chunks/LpDatePicker-dY2q4dr4.js";
22
22
  import { _ as _19 } from "./chunks/LpDisclosure-BnwP4Q-b.js";
23
23
  import { _ as _20 } from "./chunks/LpDivider-CUX46KR0.js";
@@ -25,7 +25,7 @@ import { _ as _21 } from "./chunks/LpDrawer-DAJN5rGK.js";
25
25
  import { _ as _22 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
26
26
  import { default as default3 } from "./components/LpDropzone.vue.js";
27
27
  import { _ as _23 } from "./chunks/LpEmptyState-BV3q7Qyi.js";
28
- import { _ as _24 } from "./chunks/LpFileTree-BaV7f0O7.js";
28
+ import { _ as _24 } from "./chunks/LpFileTree-DfFqY__1.js";
29
29
  import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
30
30
  import { _ as _25 } from "./chunks/LpFormField-fV_2ZdCk.js";
31
31
  import { _ as _26 } from "./chunks/LpIcon-CCnX5_2j.js";
@@ -35,15 +35,15 @@ import { _ as _28 } from "./chunks/LpInput-CYcTL9gj.js";
35
35
  import { _ as _29 } from "./chunks/LpLightbox-Ci35Y_xP.js";
36
36
  import { fileNameOf } from "./components/lightbox.js";
37
37
  import { _ as _30 } from "./chunks/LpLink-BHZ1CLlF.js";
38
- import { _ as _31 } from "./chunks/LpLogViewer-CKmEQYnm.js";
38
+ import { _ as _31 } from "./chunks/LpLogViewer-CVaGfTF_.js";
39
39
  import { _ as _32 } from "./chunks/LpModal-D_o-56To.js";
40
- import { _ as _33 } from "./chunks/LpNotificationBell-Dvu-b6Vn.js";
40
+ import { _ as _33 } from "./chunks/LpNotificationBell-CTsIcPqX.js";
41
41
  import { _ as _34 } from "./chunks/LpNumberField-NPE8Bumx.js";
42
42
  import { _ as _35 } from "./chunks/LpNumberFlow-amWYaeuA.js";
43
43
  import { _ as _36 } from "./chunks/LpOtpInput-DqaT0Z75.js";
44
44
  import { _ as _37 } from "./chunks/LpPagination-CeGuNe_q.js";
45
45
  import { _ as _38 } from "./chunks/LpPasswordInput-DrYE4J5d.js";
46
- import { _ as _39 } from "./chunks/LpPie-BkjWlfSw.js";
46
+ import { _ as _39 } from "./chunks/LpPie-BL1vTDSM.js";
47
47
  import { _ as _40 } from "./chunks/LpPhoneInput-NXXjciJJ.js";
48
48
  import { flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
49
49
  import { _ as _41 } from "./chunks/LpPopover-8268UmlC.js";
@@ -58,7 +58,7 @@ import { _ as _49 } from "./chunks/LpSelect-ANIVtrpM.js";
58
58
  import { _ as _50 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
59
59
  import { _ as _51 } from "./chunks/LpShift-DXNsauQZ.js";
60
60
  import { default as default5 } from "./components/LpShortcutsDialog.vue.js";
61
- import { _ as _52 } from "./chunks/LpSidebar-CWehDCxN.js";
61
+ import { _ as _52 } from "./chunks/LpSidebar-C_Mr-JPI.js";
62
62
  import { _ as _53 } from "./chunks/LpSkeleton-CTI-vBgW.js";
63
63
  import { _ as _54 } from "./chunks/LpSlider-B1SF3pRV.js";
64
64
  import { _ as _55 } from "./chunks/LpSparkline-C6xQ2dwv.js";
@@ -69,8 +69,8 @@ import { default as default6 } from "./components/LpTable.vue.js";
69
69
  import { _ as _59 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
70
70
  import { _ as _60 } from "./chunks/LpTabs-Cefe_lys.js";
71
71
  import { _ as _61 } from "./chunks/LpTextarea-BMIOfw5l.js";
72
- import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-Dj4VcTu7.js";
73
- import { D, _ as _62, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-Dj4VcTu7.js";
72
+ import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
73
+ import { D, _ as _62, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
74
74
  import { _ as _63 } from "./chunks/LpTilt-DuyKbpkA.js";
75
75
  import { _ as _64 } from "./chunks/LpToaster-gDB1EaU3.js";
76
76
  import { _ as _65 } from "./chunks/LpTooltip-hCLPAqMG.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.29.2",
3
+ "version": "0.29.4",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -32,6 +32,13 @@ export interface ContextMenuItemDef {
32
32
  onSelect?: () => void
33
33
  /** Nested submenu. When present, onSelect is ignored. */
34
34
  children?: ContextMenuItemDef[]
35
+ /**
36
+ * Renders as a tick-box rather than a plain item, and the menu stays open on
37
+ * select. For the lists where the point is to set several at once — which
38
+ * columns to show, which values to filter by — and closing after each one
39
+ * would mean reopening the menu for every tick.
40
+ */
41
+ checked?: boolean
35
42
  }
36
43
 
37
44
  const props = defineProps<{
@@ -85,7 +92,28 @@ const ICON =
85
92
  <ContextMenuSubContent :class="PANEL" :side-offset="2" :align-offset="-4">
86
93
  <template v-for="(sub, j) in item.children" :key="j">
87
94
  <ContextMenuSeparator v-if="sub.separatorBefore" class="my-1 h-px bg-line" />
95
+ <!-- The tick-box case, as in the top-level menu: this is
96
+ where most of them live, since a submenu is what a list
97
+ of choices opens into. -->
98
+ <ContextMenuItem
99
+ v-if="sub.checked !== undefined"
100
+ :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
101
+ :disabled="sub.disabled"
102
+ @select.prevent="sub.onSelect?.()"
103
+ >
104
+ <span class="flex items-center gap-2">
105
+ <LpIcon
106
+ :name="sub.checked ? 'lucide:check' : 'lucide:minus'"
107
+ :size="15"
108
+ :class="[ICON, sub.checked ? '' : 'opacity-25']"
109
+ />
110
+ {{ sub.label }}
111
+ </span>
112
+ <kbd v-if="sub.shortcut" class="text-xs text-muted">{{ sub.shortcut }}</kbd>
113
+ </ContextMenuItem>
114
+
88
115
  <ContextMenuItem
116
+ v-else
89
117
  :class="[ITEM, 'justify-between', sub.danger ? 'text-danger data-[highlighted]:bg-danger-soft data-[highlighted]:text-danger' : 'text-ink data-[highlighted]:text-brand']"
90
118
  :disabled="sub.disabled"
91
119
  @select="sub.onSelect?.()"
@@ -101,6 +129,27 @@ const ICON =
101
129
  </ContextMenuPortal>
102
130
  </ContextMenuSub>
103
131
 
132
+ <!-- Tick-box item. `@select.prevent` keeps the menu open: these come
133
+ in lists where several are set in one visit, and closing after
134
+ each tick would mean reopening for the next. The tick occupies a
135
+ fixed slot so unticked rows stay aligned with ticked ones. -->
136
+ <ContextMenuItem
137
+ v-else-if="item.checked !== undefined"
138
+ :class="[ITEM, 'justify-between text-ink data-[highlighted]:text-brand']"
139
+ :disabled="item.disabled"
140
+ @select.prevent="item.onSelect?.()"
141
+ >
142
+ <span class="flex items-center gap-2">
143
+ <LpIcon
144
+ :name="item.checked ? 'lucide:check' : 'lucide:minus'"
145
+ :size="15"
146
+ :class="[ICON, item.checked ? '' : 'opacity-25']"
147
+ />
148
+ {{ item.label }}
149
+ </span>
150
+ <kbd v-if="item.shortcut" class="text-xs text-muted">{{ item.shortcut }}</kbd>
151
+ </ContextMenuItem>
152
+
104
153
  <!-- Leaf item -->
105
154
  <ContextMenuItem
106
155
  v-else