@leavepulse/ui 0.22.20 → 0.22.22

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/chunks/{LpAddressInput-CxKwIG6T.js → LpAddressInput-peTpNSJK.js} +3 -3
  2. package/dist/chunks/{LpAppShell-D8CAP9oF.js → LpAppShell-CaQYQKZV.js} +25 -14
  3. package/dist/chunks/{LpConfirmDialog-CmgeAdCh.js → LpConfirmDialog-DOU_zaTI.js} +1 -1
  4. package/dist/chunks/{LpFileTree-CMy2qQ1m.js → LpFileTree-Cw50eG_y.js} +3 -4
  5. package/dist/chunks/{LpFileTreeNode-Cgf68Tup.js → LpFileTreeNode-DFg4PhOK.js} +7 -9
  6. package/dist/chunks/{LpFormField-Bn-ZwC6z.js → LpFormField-CesaPWPO.js} +12 -6
  7. package/dist/chunks/{LpInput-BkomKdqQ.js → LpInput-BfLIWV-P.js} +1 -1
  8. package/dist/chunks/{LpModal-CNNSf5pv.js → LpModal-D_o-56To.js} +13 -76
  9. package/dist/chunks/{LpNotificationBell-DWDEkmdA.js → LpNotificationBell-a08cl0yY.js} +67 -61
  10. package/dist/chunks/{LpPasswordInput-DgUC2Ur7.js → LpPasswordInput-BfjDrvZq.js} +1 -1
  11. package/dist/chunks/LpShift-DXNsauQZ.js +40 -0
  12. package/dist/chunks/{LpTextarea-CRAkhemZ.js → LpTextarea-BDl7PR9U.js} +1 -1
  13. package/dist/component-names.d.ts +1 -1
  14. package/dist/components/LpAddressInput.vue.js +1 -1
  15. package/dist/components/LpAppShell.vue.d.ts +6 -6
  16. package/dist/components/LpAppShell.vue.js +1 -1
  17. package/dist/components/LpConfirmDialog.vue.js +1 -1
  18. package/dist/components/LpFileTree.vue.d.ts +0 -2
  19. package/dist/components/LpFileTree.vue.js +1 -1
  20. package/dist/components/LpFileTreeNode.vue.d.ts +0 -2
  21. package/dist/components/LpFileTreeNode.vue.js +1 -1
  22. package/dist/components/LpFormField.vue.js +1 -1
  23. package/dist/components/LpInput.vue.js +1 -1
  24. package/dist/components/LpModal.vue.js +1 -1
  25. package/dist/components/LpNotificationBell.vue.js +1 -1
  26. package/dist/components/LpPasswordInput.vue.js +1 -1
  27. package/dist/components/LpShift.vue.d.ts +36 -0
  28. package/dist/components/LpShift.vue.js +4 -0
  29. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  30. package/dist/components/LpTextarea.vue.js +1 -1
  31. package/dist/composables/useShift.d.ts +35 -0
  32. package/dist/composables/useShift.js +112 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.js +45 -41
  35. package/package.json +1 -1
  36. package/src/component-names.ts +1 -0
  37. package/src/components/LpAppShell.vue +27 -7
  38. package/src/components/LpFileTree.vue +0 -4
  39. package/src/components/LpFileTreeNode.vue +0 -3
  40. package/src/components/LpFormField.vue +19 -2
  41. package/src/components/LpModal.vue +20 -128
  42. package/src/components/LpNotificationBell.vue +48 -42
  43. package/src/components/LpShift.vue +90 -0
  44. package/src/composables/useShift.ts +236 -0
  45. package/src/index.ts +3 -0
@@ -71,7 +71,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
71
71
  expand: (node: FileNode) => any;
72
72
  select: (node: FileNode) => any;
73
73
  summary: (summary: FileTreeSummary) => any;
74
- contextmenu: (node: FileNode, event: MouseEvent) => any;
75
74
  open: (node: FileNode) => any;
76
75
  collapse: (node: FileNode) => any;
77
76
  "update:selected": (id: string) => any;
@@ -81,7 +80,6 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
81
80
  onExpand?: ((node: FileNode) => any) | undefined;
82
81
  onSelect?: ((node: FileNode) => any) | undefined;
83
82
  onSummary?: ((summary: FileTreeSummary) => any) | undefined;
84
- onContextmenu?: ((node: FileNode, event: MouseEvent) => any) | undefined;
85
83
  onOpen?: ((node: FileNode) => any) | undefined;
86
84
  onCollapse?: ((node: FileNode) => any) | undefined;
87
85
  "onUpdate:selected"?: ((id: string) => any) | undefined;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFileTree-CMy2qQ1m.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFileTree-Cw50eG_y.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -30,13 +30,11 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
30
30
  check: (node: FileNode, value: boolean) => any;
31
31
  focus: (node: FileNode) => any;
32
32
  select: (node: FileNode) => any;
33
- contextmenu: (node: FileNode, event: MouseEvent) => any;
34
33
  toggle: (node: FileNode) => any;
35
34
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
36
35
  onCheck?: ((node: FileNode, value: boolean) => any) | undefined;
37
36
  onFocus?: ((node: FileNode) => any) | undefined;
38
37
  onSelect?: ((node: FileNode) => any) | undefined;
39
- onContextmenu?: ((node: FileNode, event: MouseEvent) => any) | undefined;
40
38
  onToggle?: ((node: FileNode) => any) | undefined;
41
39
  }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
42
40
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFileTreeNode-Cgf68Tup.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFileTreeNode-DFg4PhOK.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFormField-Bn-ZwC6z.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFormField-CesaPWPO.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpInput-BkomKdqQ.js";
1
+ import { _ as _sfc_main } from "../chunks/LpInput-BfLIWV-P.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModal-CNNSf5pv.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModal-D_o-56To.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpNotificationBell-DWDEkmdA.js";
1
+ import { _ as _sfc_main } from "../chunks/LpNotificationBell-a08cl0yY.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpPasswordInput-DgUC2Ur7.js";
1
+ import { _ as _sfc_main } from "../chunks/LpPasswordInput-BfjDrvZq.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -0,0 +1,36 @@
1
+ import { ShiftAxis } from '../composables/useShift';
2
+ type __VLS_Props = {
3
+ /**
4
+ * Which dimension eases. `"width"` for a row that gains/loses controls,
5
+ * `"height"` for a stack whose content grows, `"both"` when either can.
6
+ */
7
+ axis?: ShiftAxis;
8
+ /** Tween length in ms. Defaults to the kit's `--duration-fast`. */
9
+ duration?: number;
10
+ /**
11
+ * Turn the easing off entirely — the wrapper stays, the box resizes
12
+ * instantly. For a box that is being driven by something else (a drag, a
13
+ * canvas), where a tween would fight the thing already animating it.
14
+ */
15
+ disabled?: boolean;
16
+ };
17
+ declare var __VLS_1: {
18
+ resizing: boolean;
19
+ };
20
+ type __VLS_Slots = {} & {
21
+ default?: (props: typeof __VLS_1) => any;
22
+ };
23
+ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
24
+ resizing: import('vue').Ref<boolean, boolean>;
25
+ }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
26
+ disabled: boolean;
27
+ axis: ShiftAxis;
28
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
29
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
30
+ declare const _default: typeof __VLS_export;
31
+ export default _default;
32
+ type __VLS_WithSlots<T, S> = T & {
33
+ new (): {
34
+ $slots: S;
35
+ };
36
+ };
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpShift-DXNsauQZ.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, openBlock, createBlock, withCtx, createElementVNode, createElementBlock, Fragment, renderList, toDisplayString, unref } from "vue";
2
- import { _ as _sfc_main$1 } from "../chunks/LpModal-CNNSf5pv.js";
2
+ import { _ as _sfc_main$1 } from "../chunks/LpModal-D_o-56To.js";
3
3
  import { appleModifierLabel } from "../composables/useHotkeys.js";
4
4
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
5
5
  import '../assets/LpShortcutsDialog-B7H-RZLQ.css';const _hoisted_1 = { class: "shortcut-groups" };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTextarea-CRAkhemZ.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTextarea-BDl7PR9U.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -0,0 +1,35 @@
1
+ import { MaybeRefOrGetter, Ref } from 'vue';
2
+ export type ShiftAxis = "height" | "width" | "both";
3
+ export interface UseShiftOptions {
4
+ /** Which dimension is content-derived. Default `"height"`. */
5
+ axis?: MaybeRefOrGetter<ShiftAxis | undefined>;
6
+ /** Tween length. Defaults to the kit's `--duration-fast` (160ms). */
7
+ duration?: MaybeRefOrGetter<number | undefined>;
8
+ /**
9
+ * Gate: while falsy the element is left entirely alone — no observers, no
10
+ * pinning. Use it for a box that only exists while something is open, so a
11
+ * remounted element starts measuring from scratch rather than easing in from
12
+ * a size it held in a previous life. Default: always active.
13
+ */
14
+ enabled?: MaybeRefOrGetter<unknown>;
15
+ }
16
+ export interface UseShift {
17
+ /** Attach to the element whose content-derived size should ease. */
18
+ el: Ref<HTMLElement | null>;
19
+ /**
20
+ * True while a tween owns the size. Bind the transition utilities off this,
21
+ * so the element is only transition-enabled once it has a size to ease FROM
22
+ * (otherwise the very first pin animates in from zero).
23
+ */
24
+ tweening: Ref<boolean>;
25
+ /**
26
+ * True while the element is still catching up to its new size. During the
27
+ * tween the box is briefly smaller than the content it already holds, so any
28
+ * scroll region inside it would raise a bar for an overflow that resolves
29
+ * itself as the tween lands — hide the bar (not the scrolling) on this.
30
+ */
31
+ resizing: Ref<boolean>;
32
+ /** Re-measure and ease to the current natural size. Rarely needed by hand. */
33
+ retune: () => void;
34
+ }
35
+ export declare function useShift(options?: UseShiftOptions): UseShift;
@@ -0,0 +1,112 @@
1
+ import { ref, watch, toValue, onBeforeUnmount } from "vue";
2
+ const TWEEN_MS = 160;
3
+ function useShift(options = {}) {
4
+ const el = ref(null);
5
+ const tweening = ref(false);
6
+ const resizing = ref(false);
7
+ let ro;
8
+ let mo;
9
+ let raf = 0;
10
+ let settleTimer;
11
+ let lastHeight = 0;
12
+ let lastWidth = 0;
13
+ let inFlight = false;
14
+ const axisOf = () => toValue(options.axis) ?? "height";
15
+ const wantsHeight = () => axisOf() !== "width";
16
+ const wantsWidth = () => axisOf() !== "height";
17
+ function reducedMotion() {
18
+ return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
19
+ }
20
+ function retune() {
21
+ const node = el.value;
22
+ if (!node) return;
23
+ if (inFlight) return;
24
+ const fromHeight = lastHeight;
25
+ const fromWidth = lastWidth;
26
+ if (wantsHeight()) {
27
+ node.style.height = "";
28
+ node.style.maxHeight = "none";
29
+ }
30
+ if (wantsWidth()) {
31
+ node.style.width = "";
32
+ node.style.maxWidth = "none";
33
+ }
34
+ const toHeight = wantsHeight() ? node.offsetHeight : 0;
35
+ const toWidth = wantsWidth() ? node.offsetWidth : 0;
36
+ if (wantsHeight()) node.style.maxHeight = "";
37
+ if (wantsWidth()) node.style.maxWidth = "";
38
+ lastHeight = toHeight;
39
+ lastWidth = toWidth;
40
+ const movesHeight = wantsHeight() && fromHeight !== 0 && toHeight !== fromHeight;
41
+ const movesWidth = wantsWidth() && fromWidth !== 0 && toWidth !== fromWidth;
42
+ if (!movesHeight && !movesWidth) return;
43
+ if (movesHeight) node.style.height = `${fromHeight}px`;
44
+ if (movesWidth) node.style.width = `${fromWidth}px`;
45
+ void node.offsetHeight;
46
+ if (movesHeight) node.style.height = `${toHeight}px`;
47
+ if (movesWidth) node.style.width = `${toWidth}px`;
48
+ inFlight = true;
49
+ resizing.value = true;
50
+ clearTimeout(settleTimer);
51
+ const wait = reducedMotion() ? 0 : toValue(options.duration) ?? TWEEN_MS;
52
+ settleTimer = setTimeout(() => {
53
+ inFlight = false;
54
+ resizing.value = false;
55
+ node.style.height = "";
56
+ node.style.width = "";
57
+ lastHeight = node.offsetHeight;
58
+ lastWidth = node.offsetWidth;
59
+ }, wait);
60
+ }
61
+ function teardown() {
62
+ ro?.disconnect();
63
+ mo?.disconnect();
64
+ ro = void 0;
65
+ mo = void 0;
66
+ cancelAnimationFrame(raf);
67
+ clearTimeout(settleTimer);
68
+ tweening.value = false;
69
+ resizing.value = false;
70
+ lastHeight = 0;
71
+ lastWidth = 0;
72
+ inFlight = false;
73
+ }
74
+ watch(
75
+ [el, () => toValue(options.enabled) ?? true],
76
+ ([node, enabled]) => {
77
+ teardown();
78
+ if (!enabled || !node) return;
79
+ lastHeight = node.offsetHeight;
80
+ lastWidth = node.offsetWidth;
81
+ if (wantsHeight()) node.style.height = `${lastHeight}px`;
82
+ if (wantsWidth()) node.style.width = `${lastWidth}px`;
83
+ raf = requestAnimationFrame(() => {
84
+ tweening.value = true;
85
+ node.style.height = "";
86
+ node.style.width = "";
87
+ });
88
+ const watchChildren = () => {
89
+ if (!ro) return;
90
+ ro.disconnect();
91
+ for (const child of Array.from(node.children)) ro.observe(child);
92
+ };
93
+ if (typeof ResizeObserver !== "undefined") {
94
+ ro = new ResizeObserver(() => retune());
95
+ watchChildren();
96
+ }
97
+ if (typeof MutationObserver !== "undefined") {
98
+ mo = new MutationObserver(() => {
99
+ watchChildren();
100
+ retune();
101
+ });
102
+ mo.observe(node, { childList: true, subtree: true, characterData: true });
103
+ }
104
+ },
105
+ { flush: "post" }
106
+ );
107
+ onBeforeUnmount(teardown);
108
+ return { el, tweening, resizing, retune };
109
+ }
110
+ export {
111
+ useShift
112
+ };
package/dist/index.d.ts CHANGED
@@ -67,6 +67,7 @@ export { default as LpSelect } from './components/LpSelect.vue';
67
67
  export type { SelectOption, SelectValue } from './components/LpSelect.vue';
68
68
  export { default as LpServiceNode } from './components/LpServiceNode.vue';
69
69
  export type { ServiceNodeData } from './components/LpServiceNode.vue';
70
+ export { default as LpShift } from './components/LpShift.vue';
70
71
  export { default as LpShortcutsDialog } from './components/LpShortcutsDialog.vue';
71
72
  export type { ShortcutEntry, ShortcutGroup } from './components/LpShortcutsDialog.vue';
72
73
  export { default as LpSidebar } from './components/LpSidebar.vue';
@@ -117,6 +118,8 @@ export { useRovingFocus } from './composables/useRovingFocus';
117
118
  export type { RovingItem, UseRovingFocusOptions } from './composables/useRovingFocus';
118
119
  export { useSettled } from './composables/useSettled';
119
120
  export type { UseSettledOptions } from './composables/useSettled';
121
+ export { useShift } from './composables/useShift';
122
+ export type { ShiftAxis, UseShift, UseShiftOptions } from './composables/useShift';
120
123
  export { useTilt } from './composables/useTilt';
121
124
  export type { UseTilt, UseTiltOptions } from './composables/useTilt';
122
125
  export { useZoomPan } from './composables/useZoomPan';
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-CxKwIG6T.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-peTpNSJK.js";
3
3
  import { _ as _3 } from "./chunks/LpMapPicker-DFKrd1Ne.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-D8CAP9oF.js";
6
+ import { _ as _5 } from "./chunks/LpAppShell-CaQYQKZV.js";
7
7
  import { _ as _6 } from "./chunks/LpAutocomplete-DExlIZe_.js";
8
8
  import { _ as _7 } from "./chunks/LpAvatar-B1Ez0ZAx.js";
9
9
  import { _ as _8 } from "./chunks/LpBadge-BPodVcJa.js";
@@ -14,7 +14,7 @@ import { _ as _12 } from "./chunks/LpCard-BmrTvWOe.js";
14
14
  import { _ as _13 } from "./chunks/LpCheckbox-BO0zuuPh.js";
15
15
  import { _ as _14 } from "./chunks/LpCodeBlock-CSiOSPs8.js";
16
16
  import { default as default2 } from "./components/LpCommandPalette.vue.js";
17
- import { _ as _15 } from "./chunks/LpConfirmDialog-CmgeAdCh.js";
17
+ import { _ as _15 } from "./chunks/LpConfirmDialog-DOU_zaTI.js";
18
18
  import { _ as _16 } from "./chunks/LpContextMenu-D0mzXqA7.js";
19
19
  import { _ as _17 } from "./chunks/LpDatePicker-dY2q4dr4.js";
20
20
  import { _ as _18 } from "./chunks/LpDisclosure-DP6yfgfM.js";
@@ -22,22 +22,22 @@ import { _ as _19 } from "./chunks/LpDivider-CUX46KR0.js";
22
22
  import { _ as _20 } from "./chunks/LpDrawer-DAJN5rGK.js";
23
23
  import { _ as _21 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
24
24
  import { _ as _22 } from "./chunks/LpEmptyState-CrfyRJUG.js";
25
- import { _ as _23 } from "./chunks/LpFileTree-CMy2qQ1m.js";
25
+ import { _ as _23 } from "./chunks/LpFileTree-Cw50eG_y.js";
26
26
  import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
27
- import { _ as _24 } from "./chunks/LpFormField-Bn-ZwC6z.js";
27
+ import { _ as _24 } from "./chunks/LpFormField-CesaPWPO.js";
28
28
  import { _ as _25 } from "./chunks/LpIcon-CCnX5_2j.js";
29
29
  import { _ as _26 } from "./chunks/LpInfraNode-qIL3l7IO.js";
30
- import { _ as _27 } from "./chunks/LpInput-BkomKdqQ.js";
30
+ import { _ as _27 } from "./chunks/LpInput-BfLIWV-P.js";
31
31
  import { _ as _28 } from "./chunks/LpLightbox-Ci35Y_xP.js";
32
32
  import { fileNameOf } from "./components/lightbox.js";
33
33
  import { _ as _29 } from "./chunks/LpLink-DHwOEp4e.js";
34
34
  import { _ as _30 } from "./chunks/LpLogViewer-B1aLUVdz.js";
35
- import { _ as _31 } from "./chunks/LpModal-CNNSf5pv.js";
36
- import { _ as _32 } from "./chunks/LpNotificationBell-DWDEkmdA.js";
35
+ import { _ as _31 } from "./chunks/LpModal-D_o-56To.js";
36
+ import { _ as _32 } from "./chunks/LpNotificationBell-a08cl0yY.js";
37
37
  import { _ as _33 } from "./chunks/LpNumberField-B3JuQFol.js";
38
38
  import { _ as _34 } from "./chunks/LpOtpInput-DqaT0Z75.js";
39
39
  import { _ as _35 } from "./chunks/LpPagination-CeGuNe_q.js";
40
- import { _ as _36 } from "./chunks/LpPasswordInput-DgUC2Ur7.js";
40
+ import { _ as _36 } from "./chunks/LpPasswordInput-BfjDrvZq.js";
41
41
  import { _ as _37 } from "./chunks/LpPhoneInput-NXXjciJJ.js";
42
42
  import { flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
43
43
  import { _ as _38 } from "./chunks/LpPopover-8268UmlC.js";
@@ -48,24 +48,25 @@ import { _ as _42 } from "./chunks/LpScrollArea-D8xyKXcF.js";
48
48
  import { _ as _43 } from "./chunks/LpSegmented-BmpanyAo.js";
49
49
  import { _ as _44 } from "./chunks/LpSelect-Cw8-gL5F.js";
50
50
  import { _ as _45 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
51
+ import { _ as _46 } from "./chunks/LpShift-DXNsauQZ.js";
51
52
  import { default as default3 } from "./components/LpShortcutsDialog.vue.js";
52
- import { _ as _46 } from "./chunks/LpSidebar-D3GGLSE7.js";
53
- import { _ as _47 } from "./chunks/LpSkeleton-eSFIo4kD.js";
54
- import { _ as _48 } from "./chunks/LpSlider-B1SF3pRV.js";
55
- import { _ as _49 } from "./chunks/LpStat-CdbEFOvn.js";
56
- import { _ as _50 } from "./chunks/LpStepper-5dFTrW21.js";
57
- import { _ as _51 } from "./chunks/LpSwitch-v6fnccSM.js";
58
- import { _ as _52 } from "./chunks/LpTable-DTj52qlg.js";
59
- import { _ as _53 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
60
- import { _ as _54 } from "./chunks/LpTabs-Cefe_lys.js";
61
- import { _ as _55 } from "./chunks/LpTextarea-CRAkhemZ.js";
53
+ import { _ as _47 } from "./chunks/LpSidebar-D3GGLSE7.js";
54
+ import { _ as _48 } from "./chunks/LpSkeleton-eSFIo4kD.js";
55
+ import { _ as _49 } from "./chunks/LpSlider-B1SF3pRV.js";
56
+ import { _ as _50 } from "./chunks/LpStat-CdbEFOvn.js";
57
+ import { _ as _51 } from "./chunks/LpStepper-5dFTrW21.js";
58
+ import { _ as _52 } from "./chunks/LpSwitch-v6fnccSM.js";
59
+ import { _ as _53 } from "./chunks/LpTable-DTj52qlg.js";
60
+ import { _ as _54 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
61
+ import { _ as _55 } from "./chunks/LpTabs-Cefe_lys.js";
62
+ import { _ as _56 } from "./chunks/LpTextarea-BDl7PR9U.js";
62
63
  import { p as presets, a as parseTheme, s as serializeTheme } from "./chunks/LpThemeSwitcher-B_kj0T2b.js";
63
- import { D, _ as _56, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-B_kj0T2b.js";
64
- import { _ as _57 } from "./chunks/LpTilt-DuyKbpkA.js";
65
- import { _ as _58 } from "./chunks/LpToaster-gDB1EaU3.js";
66
- import { _ as _59 } from "./chunks/LpTooltip-D1hbjQWG.js";
67
- import { _ as _60 } from "./chunks/LpTopologyCanvas-ebP67CLx.js";
68
- import { _ as _61 } from "./chunks/LpUptimeBar-CB366k0O.js";
64
+ import { D, _ as _57, N, T, b, c, d, e, f, g, i, l, h, j, n, r, t, k, m, u, v } from "./chunks/LpThemeSwitcher-B_kj0T2b.js";
65
+ import { _ as _58 } from "./chunks/LpTilt-DuyKbpkA.js";
66
+ import { _ as _59 } from "./chunks/LpToaster-gDB1EaU3.js";
67
+ import { _ as _60 } from "./chunks/LpTooltip-D1hbjQWG.js";
68
+ import { _ as _61 } from "./chunks/LpTopologyCanvas-ebP67CLx.js";
69
+ import { _ as _62 } from "./chunks/LpUptimeBar-CB366k0O.js";
69
70
  import { deserializeLayout, serializeLayout } from "./layout/tree.js";
70
71
  import { addLeaf, addTab, countBlocks, countLeaves, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, setActiveTab } from "./layout/tree.js";
71
72
  import { blockTitle, defineBlocks } from "./layout/registry.js";
@@ -74,6 +75,7 @@ import { appleModifierLabel, isAppleKeyboard, useHotkeys } from "./composables/u
74
75
  import { useInputFilter } from "./composables/useInputFilter.js";
75
76
  import { useRovingFocus } from "./composables/useRovingFocus.js";
76
77
  import { useSettled } from "./composables/useSettled.js";
78
+ import { useShift } from "./composables/useShift.js";
77
79
  import { useTilt } from "./composables/useTilt.js";
78
80
  import { useZoomPan } from "./composables/useZoomPan.js";
79
81
  import { useToast } from "./composables/useToast.js";
@@ -155,23 +157,24 @@ export {
155
157
  _43 as LpSegmented,
156
158
  _44 as LpSelect,
157
159
  _45 as LpServiceNode,
160
+ _46 as LpShift,
158
161
  default3 as LpShortcutsDialog,
159
- _46 as LpSidebar,
160
- _47 as LpSkeleton,
161
- _48 as LpSlider,
162
- _49 as LpStat,
163
- _50 as LpStepper,
164
- _51 as LpSwitch,
165
- _52 as LpTable,
166
- _53 as LpTableOfContents,
167
- _54 as LpTabs,
168
- _55 as LpTextarea,
169
- _56 as LpThemeSwitcher,
170
- _57 as LpTilt,
171
- _58 as LpToaster,
172
- _59 as LpTooltip,
173
- _60 as LpTopologyCanvas,
174
- _61 as LpUptimeBar,
162
+ _47 as LpSidebar,
163
+ _48 as LpSkeleton,
164
+ _49 as LpSlider,
165
+ _50 as LpStat,
166
+ _51 as LpStepper,
167
+ _52 as LpSwitch,
168
+ _53 as LpTable,
169
+ _54 as LpTableOfContents,
170
+ _55 as LpTabs,
171
+ _56 as LpTextarea,
172
+ _57 as LpThemeSwitcher,
173
+ _58 as LpTilt,
174
+ _59 as LpToaster,
175
+ _60 as LpTooltip,
176
+ _61 as LpTopologyCanvas,
177
+ _62 as LpUptimeBar,
175
178
  N as NO_BACKDROP_ATTR,
176
179
  OSM_TILES,
177
180
  T as THEME_COOKIE,
@@ -239,6 +242,7 @@ export {
239
242
  useLayout,
240
243
  useRovingFocus,
241
244
  useSettled,
245
+ useShift,
242
246
  u as useTheme,
243
247
  useTilt,
244
248
  useToast,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.22.20",
3
+ "version": "0.22.22",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -51,6 +51,7 @@ export const COMPONENT_NAMES = [
51
51
  "LpSegmented",
52
52
  "LpSelect",
53
53
  "LpServiceNode",
54
+ "LpShift",
54
55
  "LpShortcutsDialog",
55
56
  "LpSidebar",
56
57
  "LpSidebarNav",
@@ -11,6 +11,12 @@
11
11
  * - a default `isActive` (path prefix-match) and a default page title derived
12
12
  * from the nav items, so the header title and the active pill never drift.
13
13
  *
14
+ * The header bar's two content-driven regions — the title block and the actions
15
+ * cluster — ease between sizes rather than snapping (LpShift). Those regions
16
+ * change constantly in normal use: actions teleport in per route or per section
17
+ * tab, and every label is translated, so their measurements move under the
18
+ * user. Without the easing each change re-lays the bar in a single frame.
19
+ *
14
20
  * Everything product-specific goes through slots: #logo (brand in the rail
15
21
  * header), #sidebar-actions / #sidebar-footer (pass-through to LpSidebar),
16
22
  * #header-actions (right side of the top bar — bell, search, theme switch),
@@ -18,10 +24,11 @@
18
24
  * Overlays that aren't part of the frame (command palette, toaster) live in the
19
25
  * host next to <LpAppShell>.
20
26
  */
21
- import { computed, ref, watch } from "vue"
27
+ import { computed, watch } from "vue"
22
28
  import LpButton from "./LpButton.vue"
23
29
  import LpIcon from "./LpIcon.vue"
24
30
  import LpScrollArea from "./LpScrollArea.vue"
31
+ import LpShift from "./LpShift.vue"
25
32
  import LpSidebar from "./LpSidebar.vue"
26
33
  import type { SidebarItem, SidebarSection } from "./sidebar"
27
34
 
@@ -195,13 +202,26 @@ defineExpose({ openDrawer: _openDrawer })
195
202
  <LpIcon name="lucide:menu" :size="18" />
196
203
  </LpButton>
197
204
 
198
- <slot name="header-title">
199
- <h1 class="truncate text-lg font-semibold">{{ pageTitle }}</h1>
200
- </slot>
201
-
202
- <div v-if="$slots['header-actions']" class="ml-auto flex items-center gap-2">
205
+ <!-- The title block eases its height: a page that adds a subtitle, or a
206
+ translation that wraps a long title onto a second line, otherwise
207
+ re-lays the whole bar in one frame. Width is left free so the
208
+ truncation still resolves against the space actually available. -->
209
+ <LpShift axis="height" class="flex min-w-0 flex-1 items-center">
210
+ <slot name="header-title">
211
+ <h1 class="truncate text-lg font-semibold">{{ pageTitle }}</h1>
212
+ </slot>
213
+ </LpShift>
214
+
215
+ <!-- Actions ease their WIDTH. This is the bar's worst offender: page
216
+ actions arrive by teleport, so a button appears or disappears with
217
+ the route or the section tab, and a translation swaps a short label
218
+ for a long one — each snapping every control to its left sideways.
219
+ Kept mounted regardless of the slot, so a page that only teleports
220
+ into it still gets the easing (an empty box measures zero wide and
221
+ costs nothing). -->
222
+ <LpShift axis="width" class="ml-auto flex shrink-0 items-center gap-2">
203
223
  <slot name="header-actions" />
204
- </div>
224
+ </LpShift>
205
225
  </header>
206
226
 
207
227
  <!-- full-bleed: page owns the whole region (canvas/map); otherwise the
@@ -103,9 +103,6 @@ const emit = defineEmits<{
103
103
  (e: "update:checked", ids: string[]): void
104
104
  /** Ticked set changed, with the rolled-up stats already computed. */
105
105
  (e: "summary", summary: FileTreeSummary): void
106
- /* Not prevented here: a caller that wants the native menu on some rows needs
107
- the event intact to decide. */
108
- (e: "contextmenu", node: FileNode, event: MouseEvent): void
109
106
  }>()
110
107
 
111
108
  defineSlots<{
@@ -531,7 +528,6 @@ defineExpose({ reveal, expandAll, collapseAll, checkAll, clearChecked, summary }
531
528
  @toggle="onToggle"
532
529
  @focus="focusedId = $event.id"
533
530
  @check="onCheck"
534
- @contextmenu="(n, e) => emit('contextmenu', n, e)"
535
531
  >
536
532
  <template #row="slotProps">
537
533
  <slot name="row" v-bind="slotProps" />
@@ -47,7 +47,6 @@ const emit = defineEmits<{
47
47
  (e: "toggle", node: FileNode): void
48
48
  (e: "focus", node: FileNode): void
49
49
  (e: "check", node: FileNode, value: boolean): void
50
- (e: "contextmenu", node: FileNode, event: MouseEvent): void
51
50
  }>()
52
51
 
53
52
  // Declared explicitly: the component renders itself, so an inferred slot type
@@ -177,7 +176,6 @@ function onActivate() {
177
176
  ]"
178
177
  :style="{ paddingLeft: `${depth * 14 + 6}px` }"
179
178
  @click.stop="onActivate"
180
- @contextmenu="emit('contextmenu', node, $event)"
181
179
  >
182
180
  <!-- Chevron sits in a fixed-width slot so names line up whether or not
183
181
  the row can expand. Leaves get the same gutter, left empty. -->
@@ -283,7 +281,6 @@ function onActivate() {
283
281
  @toggle="emit('toggle', $event)"
284
282
  @focus="emit('focus', $event)"
285
283
  @check="(n, v) => emit('check', n, v)"
286
- @contextmenu="(n, e) => emit('contextmenu', n, e)"
287
284
  >
288
285
  <template #row="slotProps">
289
286
  <slot name="row" v-bind="slotProps" />
@@ -2,9 +2,15 @@
2
2
  /*
3
3
  * Form-control wrapper: label + the control (default slot) + hint/error.
4
4
  * Error takes priority over hint. Uses reka Label for proper association.
5
+ *
6
+ * The message line eases its height (LpShift): validation messages arrive
7
+ * and clear as the user types, and each one appearing under the control shoved
8
+ * every field below it down a line. The control itself is left outside the
9
+ * eased box, so the thing being typed into never moves.
5
10
  */
6
11
  import { Label } from "reka-ui"
7
12
  import { useId } from "vue"
13
+ import LpShift from "./LpShift.vue"
8
14
 
9
15
  defineProps<{
10
16
  label?: string
@@ -17,6 +23,7 @@ const id = useId()
17
23
  </script>
18
24
 
19
25
  <template>
26
+ <!-- `gap` covers label→control only; the message carries its own spacing. -->
20
27
  <div class="flex flex-col gap-1.5">
21
28
  <Label v-if="label" :for="id" class="text-sm font-medium text-ink">
22
29
  {{ label }}
@@ -25,7 +32,17 @@ const id = useId()
25
32
 
26
33
  <slot :id="id" />
27
34
 
28
- <p v-if="error" class="text-xs text-danger">{{ error }}</p>
29
- <p v-else-if="hint" class="text-xs text-muted">{{ hint }}</p>
35
+ <!-- Swapping hint→error changes the text but not the height, so the eased
36
+ box only ever moves when a message appears or clears.
37
+
38
+ The gap that used to separate the message from the control is carried
39
+ INSIDE the eased box as padding, not by the parent's `gap`: a flex gap
40
+ applies to the wrapper whether or not it holds a message, which would
41
+ leave every message-less field a row taller than before — and would be
42
+ a step the easing could not cover, since it exists even at height 0. -->
43
+ <LpShift axis="height">
44
+ <p v-if="error" class="pt-1.5 text-xs text-danger">{{ error }}</p>
45
+ <p v-else-if="hint" class="pt-1.5 text-xs text-muted">{{ hint }}</p>
46
+ </LpShift>
30
47
  </div>
31
48
  </template>