@leavepulse/ui 0.34.0 → 0.35.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.
Files changed (45) hide show
  1. package/dist/assets/{LpTable-Ckoi05Am.css → LpTable-DfSwPCJ-.css} +21 -8
  2. package/dist/chunks/LpModalNav-B7W9VAne.js +142 -0
  3. package/dist/component-names.d.ts +1 -1
  4. package/dist/components/LpActionBar.vue.d.ts +1 -1
  5. package/dist/components/LpAddressInput.vue.d.ts +3 -3
  6. package/dist/components/LpAppShell.vue.d.ts +4 -1
  7. package/dist/components/LpAutocomplete.vue.d.ts +1 -1
  8. package/dist/components/LpBadge.vue.d.ts +2 -2
  9. package/dist/components/LpCodeBlock.vue.d.ts +1 -1
  10. package/dist/components/LpColorPicker.vue.d.ts +9 -4
  11. package/dist/components/LpDropzone.vue.d.ts +4 -4
  12. package/dist/components/LpFileTree.vue.d.ts +4 -4
  13. package/dist/components/LpFormField.vue.d.ts +1 -1
  14. package/dist/components/LpLaneNode.vue.d.ts +11 -0
  15. package/dist/components/LpLightbox.vue.d.ts +2 -2
  16. package/dist/components/LpLogViewer.vue.d.ts +1 -1
  17. package/dist/components/LpMapPicker.vue.d.ts +1 -1
  18. package/dist/components/LpMediaGallery.vue.d.ts +1 -1
  19. package/dist/components/LpModalNav.vue.d.ts +74 -0
  20. package/dist/components/LpModalNav.vue.js +4 -0
  21. package/dist/components/LpPie.vue.d.ts +22 -4
  22. package/dist/components/LpPopover.vue.d.ts +1 -1
  23. package/dist/components/LpProgress.vue.d.ts +2 -2
  24. package/dist/components/LpRollingText.vue.d.ts +3 -3
  25. package/dist/components/LpSelect.vue.d.ts +6 -6
  26. package/dist/components/LpShift.vue.d.ts +1 -1
  27. package/dist/components/LpShortcutsDialog.vue.d.ts +1 -1
  28. package/dist/components/LpSidebar.vue.d.ts +7 -1
  29. package/dist/components/LpSidebarNav.vue.d.ts +2 -2
  30. package/dist/components/LpSlider.vue.d.ts +2 -2
  31. package/dist/components/LpSparkline.vue.d.ts +2 -2
  32. package/dist/components/LpTable.vue.d.ts +9 -9
  33. package/dist/components/LpTable.vue.js +7 -4
  34. package/dist/components/LpTabs.vue.d.ts +1 -1
  35. package/dist/components/LpThemeSwitcher.vue.d.ts +1 -1
  36. package/dist/components/LpTilt.vue.d.ts +1 -1
  37. package/dist/components/LpTooltip.vue.d.ts +1 -1
  38. package/dist/components/LpTopologyCanvas.vue.d.ts +1 -1
  39. package/dist/index.d.ts +1 -0
  40. package/dist/index.js +72 -70
  41. package/package.json +1 -1
  42. package/src/component-names.ts +1 -0
  43. package/src/components/LpModalNav.vue +177 -0
  44. package/src/components/LpTable.vue +22 -2
  45. package/src/index.ts +1 -0
@@ -18,8 +18,8 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {
18
18
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
19
19
  "onUpdate:open"?: ((value: boolean) => any) | undefined;
20
20
  }>, {
21
- open: boolean;
22
21
  title: string;
22
+ open: boolean;
23
23
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
24
24
  declare const _default: typeof __VLS_export;
25
25
  export default _default;
@@ -90,7 +90,13 @@ type __VLS_Props = {
90
90
  */
91
91
  autoCollapseAfter?: number;
92
92
  };
93
- declare var __VLS_1: {}, __VLS_18: any, __VLS_20: {}, __VLS_22: {}, __VLS_32: {}, __VLS_49: any, __VLS_51: {}, __VLS_53: {};
93
+ declare var __VLS_1: {}, __VLS_18: {
94
+ item: SidebarItem;
95
+ active: boolean;
96
+ }, __VLS_20: {}, __VLS_22: {}, __VLS_32: {}, __VLS_49: {
97
+ item: SidebarItem;
98
+ active: boolean;
99
+ }, __VLS_51: {}, __VLS_53: {};
94
100
  type __VLS_Slots = {} & {
95
101
  header?: (props: typeof __VLS_1) => any;
96
102
  } & {
@@ -43,8 +43,8 @@ declare const __VLS_export: __VLS_WithSlots<import('vue').DefineComponent<{
43
43
  collapsedKeys: string[];
44
44
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>, {
45
45
  item?: (props: {
46
- item: any;
47
- active: any;
46
+ item: SidebarItem;
47
+ active: boolean;
48
48
  }) => any;
49
49
  }>;
50
50
  type __VLS_WithSlots<T, S> = T & {
@@ -23,8 +23,8 @@ declare const __VLS_export: import('vue').DefineComponent<{
23
23
  onChange?: ((value: number) => any) | undefined;
24
24
  "onUpdate:modelValue"?: ((value: number) => any) | undefined;
25
25
  }>, {
26
- modelValue: number;
27
- min: number;
28
26
  max: number;
27
+ min: number;
29
28
  step: number;
29
+ modelValue: number;
30
30
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
@@ -39,10 +39,10 @@ type __VLS_Props = {
39
39
  declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
40
40
  width: number;
41
41
  height: number;
42
- duration: number;
43
42
  animate: boolean;
44
- tone: "brand" | "action" | "danger" | "accent" | "muted";
43
+ duration: number;
45
44
  smooth: number;
45
+ tone: "brand" | "action" | "danger" | "accent" | "muted";
46
46
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
47
47
  declare const _default: typeof __VLS_export;
48
48
  export default _default;
@@ -27,7 +27,7 @@ export interface SortState {
27
27
  dir: "asc" | "desc";
28
28
  }
29
29
  declare const __VLS_export: <T extends Record<string, unknown>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
30
- props: __VLS_PrettifyLocal<{
30
+ props: import('vue').PublicProps & __VLS_PrettifyLocal<{
31
31
  columns: TableColumn<T>[];
32
32
  rows: T[];
33
33
  /** Stable id per row — required for selection; falls back to index otherwise. */
@@ -55,19 +55,19 @@ declare const __VLS_export: <T extends Record<string, unknown>>(__VLS_props: Non
55
55
  filterLabel?: string;
56
56
  showAllLabel?: string;
57
57
  columnsLabel?: string;
58
- } & __VLS_EmitsToProps<__VLS_NormalizeEmits<{
59
- (e: "update:selected", value: (string | number)[]): void;
60
- (e: "update:sort", value: SortState | null): void;
61
- (e: "rowClick", row: T): void;
62
- }>>> & import('vue').PublicProps & (typeof globalThis extends {
58
+ } & {
59
+ "onUpdate:selected"?: ((value: (string | number)[]) => any) | undefined;
60
+ "onUpdate:sort"?: ((value: SortState | null) => any) | undefined;
61
+ onRowClick?: ((row: T) => any) | undefined;
62
+ }> & (typeof globalThis extends {
63
63
  __VLS_PROPS_FALLBACK: infer P;
64
64
  } ? P : {});
65
65
  expose: (exposed: {}) => void;
66
66
  attrs: any;
67
67
  slots: {
68
- [x: string]: ((props: {
69
- row: any;
70
- value: any;
68
+ [x: `cell-${string}`]: ((props: {
69
+ row: T;
70
+ value: unknown;
71
71
  }) => any) | undefined;
72
72
  };
73
73
  emit: {
@@ -5,7 +5,7 @@ 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-Ckoi05Am.css';const _hoisted_1 = { class: "w-full border-collapse text-sm" };
8
+ import '../assets/LpTable-DfSwPCJ-.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,
@@ -13,7 +13,10 @@ const _hoisted_3 = {
13
13
  };
14
14
  const _hoisted_4 = ["aria-sort"];
15
15
  const _hoisted_5 = ["onClick"];
16
- const _hoisted_6 = { key: "lp-table-empty" };
16
+ const _hoisted_6 = {
17
+ key: "lp-table-empty",
18
+ class: "lp-row-empty"
19
+ };
17
20
  const _hoisted_7 = ["colspan"];
18
21
  const _hoisted_8 = ["onContextmenu", "onClick"];
19
22
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -290,7 +293,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
290
293
  createElementVNode("thead", {
291
294
  ref_key: "headEl",
292
295
  ref: headEl,
293
- class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10" : "")
296
+ class: normalizeClass(__props.stickyHeader ? "sticky top-0 z-10 bg-surface-soft" : "")
294
297
  }, [
295
298
  createElementVNode("tr", _hoisted_2, [
296
299
  __props.selectable ? (openBlock(), createElementBlock("th", _hoisted_3, [
@@ -406,7 +409,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
406
409
  };
407
410
  }
408
411
  });
409
- const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d139c3bb"]]);
412
+ const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-1fada8e7"]]);
410
413
  export {
411
414
  LpTable as default
412
415
  };
@@ -21,7 +21,7 @@ type __VLS_Props = {
21
21
  block?: boolean;
22
22
  };
23
23
  declare var __VLS_49: {
24
- value: any;
24
+ value: string;
25
25
  }, __VLS_51: {};
26
26
  type __VLS_Slots = {} & {
27
27
  panel?: (props: typeof __VLS_49) => any;
@@ -40,8 +40,8 @@ declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {
40
40
  "onUpdate:modelValue"?: ((name: string) => any) | undefined;
41
41
  }>, {
42
42
  size: "sm" | "md" | "lg";
43
- variant: "swatch" | "icon" | "pill";
44
43
  transition: boolean;
44
+ variant: "swatch" | "icon" | "pill";
45
45
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
46
46
  declare const _default: typeof __VLS_export;
47
47
  export default _default;
@@ -17,9 +17,9 @@ type __VLS_Slots = {} & {
17
17
  declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
18
18
  scale: number;
19
19
  max: number;
20
+ perspective: number;
20
21
  lift: number;
21
22
  glare: boolean;
22
- perspective: number;
23
23
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
24
24
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
25
25
  declare const _default: typeof __VLS_export;
@@ -45,8 +45,8 @@ type __VLS_Slots = {} & {
45
45
  content?: (props: typeof __VLS_38) => any;
46
46
  };
47
47
  declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
48
- side: "top" | "right" | "bottom" | "left";
49
48
  delay: number;
49
+ side: "top" | "right" | "bottom" | "left";
50
50
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
51
51
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
52
52
  declare const _default: typeof __VLS_export;
@@ -188,12 +188,12 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
188
188
  }) => any) | undefined;
189
189
  }>, {
190
190
  legend: boolean;
191
+ controls: boolean;
191
192
  connectable: boolean;
192
193
  selectable: boolean;
193
194
  legendPosition: "top-left" | "top-right" | "bottom-left" | "bottom-right";
194
195
  lanes: TopologyLane[];
195
196
  minimap: boolean;
196
- controls: boolean;
197
197
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
198
198
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
199
199
  declare const _default: typeof __VLS_export;
package/dist/index.d.ts CHANGED
@@ -58,6 +58,7 @@ export { default as LpLogViewer } from './components/LpLogViewer.vue';
58
58
  export type { LogLevel, LogLine } from './components/LpLogViewer.vue';
59
59
  export { default as LpMediaGallery } from './components/LpMediaGallery.vue';
60
60
  export { default as LpModal } from './components/LpModal.vue';
61
+ export { default as LpModalNav } from './components/LpModalNav.vue';
61
62
  export { default as LpNotificationBell } from './components/LpNotificationBell.vue';
62
63
  export type { NotificationItem, NotificationLabels, } from './components/LpNotificationBell.vue';
63
64
  export { default as LpNumberField } from './components/LpNumberField.vue';
package/dist/index.js CHANGED
@@ -41,45 +41,46 @@ import { _ as _31 } from "./chunks/LpLink-DCND-zyu.js";
41
41
  import { _ as _32 } from "./chunks/LpLogViewer-y53ZqSVq.js";
42
42
  import { _ as _33 } from "./chunks/LpMediaGallery-Cid7PGYx.js";
43
43
  import { _ as _34 } from "./chunks/LpModal-DAqXzsm-.js";
44
- import { _ as _35 } from "./chunks/LpNotificationBell-lHePf89W.js";
45
- import { _ as _36 } from "./chunks/LpNumberField-CWuX08iX.js";
46
- import { _ as _37 } from "./chunks/LpNumberFlow-amWYaeuA.js";
47
- import { _ as _38 } from "./chunks/LpOtpInput-y2v-pmiN.js";
48
- import { _ as _39 } from "./chunks/LpPagination-5FxjmmB5.js";
49
- import { _ as _40 } from "./chunks/LpPasswordInput--sjYCkLS.js";
50
- import { _ as _41 } from "./chunks/LpPie-c2-q4DJu.js";
51
- import { _ as _42 } from "./chunks/LpPhoneInput-BDSKVl_z.js";
44
+ import { _ as _35 } from "./chunks/LpModalNav-B7W9VAne.js";
45
+ import { _ as _36 } from "./chunks/LpNotificationBell-lHePf89W.js";
46
+ import { _ as _37 } from "./chunks/LpNumberField-CWuX08iX.js";
47
+ import { _ as _38 } from "./chunks/LpNumberFlow-amWYaeuA.js";
48
+ import { _ as _39 } from "./chunks/LpOtpInput-y2v-pmiN.js";
49
+ import { _ as _40 } from "./chunks/LpPagination-5FxjmmB5.js";
50
+ import { _ as _41 } from "./chunks/LpPasswordInput--sjYCkLS.js";
51
+ import { _ as _42 } from "./chunks/LpPie-c2-q4DJu.js";
52
+ import { _ as _43 } from "./chunks/LpPhoneInput-BDSKVl_z.js";
52
53
  import { flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
53
- import { _ as _43 } from "./chunks/LpPopover-8268UmlC.js";
54
- import { _ as _44 } from "./chunks/LpProgress-Btq5W4Ut.js";
55
- import { _ as _45 } from "./chunks/LpRadio-B5AfqTpZ.js";
56
- import { _ as _46 } from "./chunks/LpRadioGroup-xPIuVDO_.js";
57
- import { _ as _47 } from "./chunks/LpRollingText-CM5gkSeF.js";
58
- import { _ as _48 } from "./chunks/LpScrollArea-D8xyKXcF.js";
59
- import { _ as _49 } from "./chunks/LpSegmented-fV0Sk92p.js";
60
- import { _ as _50 } from "./chunks/LpSection-aM6J_u51.js";
61
- import { _ as _51 } from "./chunks/LpSelect-ANIVtrpM.js";
62
- import { _ as _52 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
63
- import { _ as _53 } from "./chunks/LpShift-DXNsauQZ.js";
54
+ import { _ as _44 } from "./chunks/LpPopover-8268UmlC.js";
55
+ import { _ as _45 } from "./chunks/LpProgress-Btq5W4Ut.js";
56
+ import { _ as _46 } from "./chunks/LpRadio-B5AfqTpZ.js";
57
+ import { _ as _47 } from "./chunks/LpRadioGroup-xPIuVDO_.js";
58
+ import { _ as _48 } from "./chunks/LpRollingText-CM5gkSeF.js";
59
+ import { _ as _49 } from "./chunks/LpScrollArea-D8xyKXcF.js";
60
+ import { _ as _50 } from "./chunks/LpSegmented-fV0Sk92p.js";
61
+ import { _ as _51 } from "./chunks/LpSection-aM6J_u51.js";
62
+ import { _ as _52 } from "./chunks/LpSelect-ANIVtrpM.js";
63
+ import { _ as _53 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
64
+ import { _ as _54 } from "./chunks/LpShift-DXNsauQZ.js";
64
65
  import { default as default6 } from "./components/LpShortcutsDialog.vue.js";
65
- import { _ as _54 } from "./chunks/LpSidebar-DcYavim2.js";
66
- import { _ as _55 } from "./chunks/LpSkeleton-CTI-vBgW.js";
67
- import { _ as _56 } from "./chunks/LpSlider-BwNIBkAK.js";
68
- import { _ as _57 } from "./chunks/LpSparkline-C6xQ2dwv.js";
69
- import { _ as _58 } from "./chunks/LpStat-CQaW8R--.js";
70
- import { _ as _59 } from "./chunks/LpStepper-Bk-XaQYU.js";
71
- import { _ as _60 } from "./chunks/LpSwitch-xgnRU2k0.js";
66
+ import { _ as _55 } from "./chunks/LpSidebar-DcYavim2.js";
67
+ import { _ as _56 } from "./chunks/LpSkeleton-CTI-vBgW.js";
68
+ import { _ as _57 } from "./chunks/LpSlider-BwNIBkAK.js";
69
+ import { _ as _58 } from "./chunks/LpSparkline-C6xQ2dwv.js";
70
+ import { _ as _59 } from "./chunks/LpStat-CQaW8R--.js";
71
+ import { _ as _60 } from "./chunks/LpStepper-Bk-XaQYU.js";
72
+ import { _ as _61 } from "./chunks/LpSwitch-xgnRU2k0.js";
72
73
  import { default as default7 } from "./components/LpTable.vue.js";
73
- import { _ as _61 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
74
- import { _ as _62 } from "./chunks/LpTabs-Cefe_lys.js";
75
- import { _ as _63 } from "./chunks/LpTextarea-t7F6gv8E.js";
74
+ import { _ as _62 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
75
+ import { _ as _63 } from "./chunks/LpTabs-Cefe_lys.js";
76
+ import { _ as _64 } from "./chunks/LpTextarea-t7F6gv8E.js";
76
77
  import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
77
- import { D, _ as _64, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
78
- import { _ as _65 } from "./chunks/LpTilt-DuyKbpkA.js";
79
- import { _ as _66 } from "./chunks/LpToaster-gDB1EaU3.js";
80
- import { _ as _67 } from "./chunks/LpTooltip-hCLPAqMG.js";
81
- import { _ as _68 } from "./chunks/LpTopologyCanvas-Cm1L39uL.js";
82
- import { _ as _69 } from "./chunks/LpUptimeBar-DvAMno0S.js";
78
+ import { D, _ as _65, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-r1te9nB2.js";
79
+ import { _ as _66 } from "./chunks/LpTilt-DuyKbpkA.js";
80
+ import { _ as _67 } from "./chunks/LpToaster-gDB1EaU3.js";
81
+ import { _ as _68 } from "./chunks/LpTooltip-hCLPAqMG.js";
82
+ import { _ as _69 } from "./chunks/LpTopologyCanvas-Cm1L39uL.js";
83
+ import { _ as _70 } from "./chunks/LpUptimeBar-DvAMno0S.js";
83
84
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
84
85
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
85
86
  import { blockTitle, defineBlocks } from "./layout/registry.js";
@@ -169,43 +170,44 @@ export {
169
170
  _3 as LpMapPicker,
170
171
  _33 as LpMediaGallery,
171
172
  _34 as LpModal,
172
- _35 as LpNotificationBell,
173
- _36 as LpNumberField,
174
- _37 as LpNumberFlow,
175
- _38 as LpOtpInput,
176
- _39 as LpPagination,
177
- _40 as LpPasswordInput,
178
- _42 as LpPhoneInput,
179
- _41 as LpPie,
180
- _43 as LpPopover,
181
- _44 as LpProgress,
182
- _45 as LpRadio,
183
- _46 as LpRadioGroup,
184
- _47 as LpRollingText,
185
- _48 as LpScrollArea,
186
- _50 as LpSection,
187
- _49 as LpSegmented,
188
- _51 as LpSelect,
189
- _52 as LpServiceNode,
190
- _53 as LpShift,
173
+ _35 as LpModalNav,
174
+ _36 as LpNotificationBell,
175
+ _37 as LpNumberField,
176
+ _38 as LpNumberFlow,
177
+ _39 as LpOtpInput,
178
+ _40 as LpPagination,
179
+ _41 as LpPasswordInput,
180
+ _43 as LpPhoneInput,
181
+ _42 as LpPie,
182
+ _44 as LpPopover,
183
+ _45 as LpProgress,
184
+ _46 as LpRadio,
185
+ _47 as LpRadioGroup,
186
+ _48 as LpRollingText,
187
+ _49 as LpScrollArea,
188
+ _51 as LpSection,
189
+ _50 as LpSegmented,
190
+ _52 as LpSelect,
191
+ _53 as LpServiceNode,
192
+ _54 as LpShift,
191
193
  default6 as LpShortcutsDialog,
192
- _54 as LpSidebar,
193
- _55 as LpSkeleton,
194
- _56 as LpSlider,
195
- _57 as LpSparkline,
196
- _58 as LpStat,
197
- _59 as LpStepper,
198
- _60 as LpSwitch,
194
+ _55 as LpSidebar,
195
+ _56 as LpSkeleton,
196
+ _57 as LpSlider,
197
+ _58 as LpSparkline,
198
+ _59 as LpStat,
199
+ _60 as LpStepper,
200
+ _61 as LpSwitch,
199
201
  default7 as LpTable,
200
- _61 as LpTableOfContents,
201
- _62 as LpTabs,
202
- _63 as LpTextarea,
203
- _64 as LpThemeSwitcher,
204
- _65 as LpTilt,
205
- _66 as LpToaster,
206
- _67 as LpTooltip,
207
- _68 as LpTopologyCanvas,
208
- _69 as LpUptimeBar,
202
+ _62 as LpTableOfContents,
203
+ _63 as LpTabs,
204
+ _64 as LpTextarea,
205
+ _65 as LpThemeSwitcher,
206
+ _66 as LpTilt,
207
+ _67 as LpToaster,
208
+ _68 as LpTooltip,
209
+ _69 as LpTopologyCanvas,
210
+ _70 as LpUptimeBar,
209
211
  N as NO_BACKDROP_ATTR,
210
212
  OSM_TILES,
211
213
  T as THEME_COOKIE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -43,6 +43,7 @@ export const COMPONENT_NAMES = [
43
43
  "LpMapPicker",
44
44
  "LpMediaGallery",
45
45
  "LpModal",
46
+ "LpModalNav",
46
47
  "LpNotificationBell",
47
48
  "LpNumberField",
48
49
  "LpNumberFlow",
@@ -0,0 +1,177 @@
1
+ <script setup lang="ts">
2
+ /*
3
+ * A dialog whose sections stay visible while you read one — settings, a profile
4
+ * with tabs' worth of detail, a wizard that lets you skip a step. The shape
5
+ * these all reach for is a nav column that persists next to the body, and
6
+ * without it they fall back to a row of tabs that runs out of width, or to a
7
+ * back-and-forth between a list screen and a detail screen.
8
+ *
9
+ * The nav lives INSIDE the dialog. Standing it outside, as a sibling floating
10
+ * beside the panel, is the arrangement this component deliberately does not
11
+ * use: it then sits outside the focus trap, `interact-outside` reads a click on
12
+ * it as a click on the scrim and closes the dialog under the pointer, and its
13
+ * height has to be reconciled with a dialog height that changes as content
14
+ * arrives. Inside, all three come out right for free, and `LpModal` keeps the
15
+ * centring every other modal in the kit relies on.
16
+ *
17
+ * Width is the one cost: the rail comes out of the body's width, which is why
18
+ * the default `size` is wider than a plain modal's. Below `mobileBreakpoint`
19
+ * there is no width to spare at all, so the rail becomes a drawer — and since
20
+ * LpSidebar leaves opening it to the app, the header grows the button for it
21
+ * rather than leaving the sections unreachable on a phone.
22
+ */
23
+ import { computed, ref } from "vue"
24
+ import LpButton from "./LpButton.vue"
25
+ import LpIcon from "./LpIcon.vue"
26
+ import LpModal from "./LpModal.vue"
27
+ import LpSidebar from "./LpSidebar.vue"
28
+ import type { SidebarItem, SidebarSection } from "./sidebar"
29
+
30
+ export type { SidebarItem, SidebarSection } from "./sidebar"
31
+
32
+ type Breakpoint = "sm" | "md" | "lg" | "xl"
33
+
34
+ const props = withDefaults(
35
+ defineProps<{
36
+ open?: boolean
37
+ /** Active section id (v-model). */
38
+ modelValue?: string
39
+ /** Flat sections, or grouped ones — the shapes LpSidebar already takes. */
40
+ items?: SidebarItem[]
41
+ sections?: SidebarSection[]
42
+ title?: string
43
+ description?: string
44
+ /**
45
+ * Defaults wider than `LpModal`'s `md`: the rail comes out of the content's
46
+ * width, and at `md` what is left is too narrow to be worth splitting.
47
+ */
48
+ size?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full"
49
+ width?: string
50
+ /** Width of the nav rail (any CSS length). */
51
+ navWidth?: string
52
+ /** Put a filter above the nav — see LpSidebar's `searchable`. */
53
+ searchable?: boolean
54
+ searchPlaceholder?: string
55
+ /** Skeleton rows in place of the nav while the sections load. */
56
+ loading?: boolean
57
+ /** Breakpoint below which the rail becomes a drawer. */
58
+ mobileBreakpoint?: Breakpoint
59
+ /** Label for the button that opens that drawer. */
60
+ navLabel?: string
61
+ /**
62
+ * Let the body fill the height instead of scrolling itself. On by default,
63
+ * unlike a plain modal: the rail is only a full-height column if the two
64
+ * of them agree on a height to fill.
65
+ */
66
+ fillBody?: boolean
67
+ }>(),
68
+ {
69
+ size: "2xl",
70
+ navWidth: "13rem",
71
+ mobileBreakpoint: "lg",
72
+ navLabel: "Sections",
73
+ fillBody: true,
74
+ },
75
+ )
76
+
77
+ const emit = defineEmits<{
78
+ (e: "update:open", value: boolean): void
79
+ (e: "update:modelValue", value: string): void
80
+ }>()
81
+
82
+ const navOpen = ref(false)
83
+
84
+ const active = computed({
85
+ get: () => props.modelValue ?? "",
86
+ set: (v: string) => emit("update:modelValue", v),
87
+ })
88
+
89
+ const hasNav = computed(
90
+ () => Boolean(props.items?.length || props.sections?.length),
91
+ )
92
+
93
+ /*
94
+ * Spelled out rather than built from the prop: a class assembled at runtime is
95
+ * not in anyone's Tailwind scan and would never be generated. Mirrors the
96
+ * rail's own visibility in LpSidebar — the button appears exactly where the
97
+ * rail disappears.
98
+ */
99
+ const TRIGGER_VISIBILITY: Record<Breakpoint, string> = {
100
+ sm: "sm:hidden",
101
+ md: "md:hidden",
102
+ lg: "lg:hidden",
103
+ xl: "xl:hidden",
104
+ }
105
+ const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint])
106
+ </script>
107
+
108
+ <template>
109
+ <LpModal
110
+ :open="open"
111
+ :title="title"
112
+ :description="description"
113
+ :size="size"
114
+ :width="width"
115
+ :fill-body="fillBody"
116
+ @update:open="emit('update:open', $event)"
117
+ >
118
+ <template #title>
119
+ <div class="flex items-center gap-2">
120
+ <LpButton
121
+ v-if="hasNav"
122
+ variant="ghost"
123
+ size="sm"
124
+ square
125
+ :aria-label="navLabel"
126
+ :class="triggerClass"
127
+ @click="navOpen = true"
128
+ >
129
+ <LpIcon name="lucide:menu" :size="18" />
130
+ </LpButton>
131
+ <slot name="title">{{ title }}</slot>
132
+ </div>
133
+ </template>
134
+
135
+ <div class="flex min-h-0 flex-1 gap-4">
136
+ <!-- `shrink-0`: a flex item's default is to give up width to a greedy
137
+ sibling, so a wide table in the body would squeeze the rail down to
138
+ its longest unbreakable word. -->
139
+ <LpSidebar
140
+ v-if="hasNav"
141
+ v-model="active"
142
+ v-model:open="navOpen"
143
+ variant="panel"
144
+ :items="items"
145
+ :sections="sections"
146
+ :width="navWidth"
147
+ :searchable="searchable"
148
+ :search-placeholder="searchPlaceholder"
149
+ :loading="loading"
150
+ responsive
151
+ :mobile-breakpoint="mobileBreakpoint"
152
+ class="shrink-0"
153
+ >
154
+ <template v-if="$slots.navHeader" #header>
155
+ <slot name="navHeader" />
156
+ </template>
157
+ <template v-if="$slots.navItem" #item="slotProps">
158
+ <slot name="navItem" v-bind="slotProps" />
159
+ </template>
160
+ <template v-if="$slots.navFooter" #footer>
161
+ <slot name="navFooter" />
162
+ </template>
163
+ </LpSidebar>
164
+
165
+ <!-- `min-w-0` is the same guard in the other direction: without it a wide
166
+ child sets this column's minimum to its own width, and the dialog
167
+ grows past its size instead of the content scrolling. -->
168
+ <div class="flex min-h-0 min-w-0 flex-1 flex-col">
169
+ <slot />
170
+ </div>
171
+ </div>
172
+
173
+ <template v-if="$slots.footer" #footer>
174
+ <slot name="footer" />
175
+ </template>
176
+ </LpModal>
177
+ </template>
@@ -451,7 +451,14 @@ const barInsetTop = computed(() =>
451
451
  this is the fallback rather than a competitor. -->
452
452
  <LpContextMenu :items="contextMenu" always>
453
453
  <table class="w-full border-collapse text-sm">
454
- <thead ref="headEl" :class="stickyHeader ? 'sticky top-0 z-10' : ''">
454
+ <!-- The background belongs on `thead` itself, not only on its row:
455
+ `border-collapse` collapses the borders, and with a sticky header the
456
+ rows show through that seam — a band of body text rides across the
457
+ header on every scroll. -->
458
+ <thead
459
+ ref="headEl"
460
+ :class="stickyHeader ? 'sticky top-0 z-10 bg-surface-soft' : ''"
461
+ >
455
462
  <tr class="border-b border-line bg-surface-soft">
456
463
  <th v-if="selectable" class="w-px px-4 py-3">
457
464
  <LpCheckbox
@@ -522,7 +529,7 @@ const barInsetTop = computed(() =>
522
529
  name="lp-row"
523
530
  :ref="(el: unknown) => roving.setContainer((el as { $el?: HTMLElement })?.$el ?? null)"
524
531
  >
525
- <tr v-if="displayRows.length === 0" key="lp-table-empty">
532
+ <tr v-if="displayRows.length === 0" key="lp-table-empty" class="lp-row-empty">
526
533
  <td :colspan="colSpan" class="px-4 py-10 text-center text-muted">
527
534
  <LpIcon :name="emptyIcon" :size="22" class="mx-auto mb-2 opacity-60" />
528
535
  <div>{{ emptyLabel }}</div>
@@ -594,6 +601,19 @@ const barInsetTop = computed(() =>
594
601
  transform: translateX(-0.5rem);
595
602
  }
596
603
 
604
+ /*
605
+ * The empty-state row leaves at once, without the fade. It and the first real
606
+ * rows would otherwise share the table for the length of the animation, and a
607
+ * row of blank height sits above them — the table jumps once on every load.
608
+ *
609
+ * `position: absolute` is what a list would use here, but a positioned `<tr>`
610
+ * drops out of the table layout entirely. Nothing sits below this row to be
611
+ * dragged around, so simply not animating its exit costs nothing.
612
+ */
613
+ .lp-row-empty.lp-row-leave-active {
614
+ display: none;
615
+ }
616
+
597
617
  /*
598
618
  * The leaving row stays in the flow. Taking it out with `position: absolute` is
599
619
  * the usual trick for a list, but a `<tr>` positioned that way loses the table's
package/src/index.ts CHANGED
@@ -105,6 +105,7 @@ export { default as LpLogViewer } from "./components/LpLogViewer.vue"
105
105
  export type { LogLevel, LogLine } from "./components/LpLogViewer.vue"
106
106
  export { default as LpMediaGallery } from "./components/LpMediaGallery.vue"
107
107
  export { default as LpModal } from "./components/LpModal.vue"
108
+ export { default as LpModalNav } from "./components/LpModalNav.vue"
108
109
  export { default as LpNotificationBell } from "./components/LpNotificationBell.vue"
109
110
  export type {
110
111
  NotificationItem,