@leavepulse/ui 0.20.2 → 0.20.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 (31) hide show
  1. package/dist/chunks/{LpAddressInput-CCtskgfZ.js → LpAddressInput-BV9M0aec.js} +1 -1
  2. package/dist/chunks/{LpAppShell-D42la4ZD.js → LpAppShell-oYlB1C9T.js} +2 -2
  3. package/dist/chunks/{LpConfirmDialog-Z9hsfm2D.js → LpConfirmDialog-BgaP-GJZ.js} +1 -1
  4. package/dist/chunks/{LpDrawer-CYm-nUIZ.js → LpDrawer-D1ubjU11.js} +1 -1
  5. package/dist/chunks/{LpFileTree-DXb3yKzH.js → LpFileTree-CmKJxz4K.js} +1 -1
  6. package/dist/chunks/{LpLogViewer-tmKPNTuU.js → LpLogViewer-CFckvccr.js} +1 -1
  7. package/dist/chunks/{LpModal-g2dGpF7l.js → LpModal-YaFleOpp.js} +14 -3
  8. package/dist/chunks/{LpNotificationBell-DHze8wYN.js → LpNotificationBell-B1QTe4mK.js} +1 -1
  9. package/dist/chunks/LpScrollArea-BjImFflN.js +120 -0
  10. package/dist/chunks/{LpSidebar-DlLZJk1u.js → LpSidebar-DlvoM3h6.js} +2 -2
  11. package/dist/chunks/{LpSidebarNav-DSDa5mAY.js → LpSidebarNav-Ds95Sp9b.js} +1 -1
  12. package/dist/chunks/{LpTable-DqZ-L4Au.js → LpTable-i2AEwiPd.js} +1 -1
  13. package/dist/components/LpAddressInput.vue.js +1 -1
  14. package/dist/components/LpAppShell.vue.js +1 -1
  15. package/dist/components/LpCommandPalette.vue.js +1 -1
  16. package/dist/components/LpConfirmDialog.vue.js +1 -1
  17. package/dist/components/LpDrawer.vue.js +1 -1
  18. package/dist/components/LpFileTree.vue.js +1 -1
  19. package/dist/components/LpLogViewer.vue.js +1 -1
  20. package/dist/components/LpModal.vue.js +1 -1
  21. package/dist/components/LpNotificationBell.vue.js +1 -1
  22. package/dist/components/LpScrollArea.vue.d.ts +2 -2
  23. package/dist/components/LpScrollArea.vue.js +1 -1
  24. package/dist/components/LpSidebar.vue.js +1 -1
  25. package/dist/components/LpSidebarNav.vue.js +1 -1
  26. package/dist/components/LpTable.vue.js +1 -1
  27. package/dist/index.js +11 -11
  28. package/package.json +1 -1
  29. package/src/components/LpModal.vue +24 -2
  30. package/src/components/LpScrollArea.vue +87 -9
  31. package/dist/chunks/LpScrollArea-DQ0Yudfu.js +0 -72
@@ -6,7 +6,7 @@ import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
6
6
  import { _ as _sfc_main$1 } from "./LpFormField-Bn-ZwC6z.js";
7
7
  import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
8
8
  import { _ as _sfc_main$5 } from "./LpInput-BauZRyzm.js";
9
- import { _ as _sfc_main$7 } from "./LpModal-g2dGpF7l.js";
9
+ import { _ as _sfc_main$7 } from "./LpModal-YaFleOpp.js";
10
10
  import { _ as _sfc_main$6 } from "./LpSelect-D5mU4cZO.js";
11
11
  const _hoisted_1 = { class: "flex flex-col gap-3" };
12
12
  const _hoisted_2 = { class: "flex items-stretch gap-2" };
@@ -1,8 +1,8 @@
1
1
  import { defineComponent, computed, watch, openBlock, createElementBlock, createVNode, createSlots, withCtx, renderSlot, normalizeProps, guardReactiveProps, createElementVNode, normalizeClass, toDisplayString, createCommentVNode, createBlock } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
3
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$4 } from "./LpScrollArea-DQ0Yudfu.js";
5
- import { _ as _sfc_main$1 } from "./LpSidebar-DlLZJk1u.js";
4
+ import { _ as _sfc_main$4 } from "./LpScrollArea-BjImFflN.js";
5
+ import { _ as _sfc_main$1 } from "./LpSidebar-DlvoM3h6.js";
6
6
  const _hoisted_1 = { class: "flex h-dvh overflow-hidden bg-surface text-ink" };
7
7
  const _hoisted_2 = { class: "flex min-w-0 flex-1 flex-col" };
8
8
  const _hoisted_3 = { class: "flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6" };
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, openBlock, createBlock, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
- import { _ as _sfc_main$1 } from "./LpModal-g2dGpF7l.js";
3
+ import { _ as _sfc_main$1 } from "./LpModal-YaFleOpp.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpConfirmDialog",
6
6
  props: {
@@ -3,7 +3,7 @@ import { usePointerSwipe } from "@vueuse/core";
3
3
  import { DrawerRoot, DrawerPortal, DrawerOverlay, DrawerContent, DrawerHandle, DrawerTitle, DrawerDescription, DrawerClose } from "vaul-vue";
4
4
  import { CLOSE_ICON } from "../components/dropdown.js";
5
5
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
6
- import { _ as _sfc_main$2 } from "./LpScrollArea-DQ0Yudfu.js";
6
+ import { _ as _sfc_main$2 } from "./LpScrollArea-BjImFflN.js";
7
7
  const _hoisted_1 = { class: "text-base font-semibold text-ink" };
8
8
  const _hoisted_2 = { class: "flex flex-col gap-1" };
9
9
  const _hoisted_3 = ["data-vaul-no-drag"];
@@ -2,7 +2,7 @@ import { defineComponent, ref, watch, computed, openBlock, createElementBlock, F
2
2
  import { formatSize, sortNodes, computeStats, subtreeIds, ancestorIds } from "../components/fileTree.js";
3
3
  import { _ as _sfc_main$1 } from "./LpEmptyState-CrfyRJUG.js";
4
4
  import { _ as _sfc_main$3 } from "./LpFileTreeNode-DFg4PhOK.js";
5
- import { _ as _sfc_main$2 } from "./LpScrollArea-DQ0Yudfu.js";
5
+ import { _ as _sfc_main$2 } from "./LpScrollArea-BjImFflN.js";
6
6
  const _hoisted_1 = {
7
7
  key: 0,
8
8
  class: "flex h-full w-full min-w-0 flex-col gap-1 overflow-hidden"
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, ref, watch, nextTick, onMounted, onBeforeUnmount, openBlock, createElementBlock, createVNode, normalizeStyle, withCtx, createElementVNode, toDisplayString, createCommentVNode, Fragment, createBlock, resolveDynamicComponent, TransitionGroup, mergeProps, renderList, normalizeClass, createTextVNode, Transition } from "vue";
2
2
  import { _ as _sfc_main$3 } from "./LpContextMenu-D0mzXqA7.js";
3
3
  import { _ as _sfc_main$2 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$1 } from "./LpScrollArea-DQ0Yudfu.js";
4
+ import { _ as _sfc_main$1 } from "./LpScrollArea-BjImFflN.js";
5
5
  const _hoisted_1 = { class: "relative overflow-hidden rounded-card border border-line bg-surface font-mono text-xs leading-relaxed" };
6
6
  const _hoisted_2 = {
7
7
  key: 0,
@@ -2,7 +2,7 @@ import { defineComponent, computed, useSlots, ref, watch, onBeforeUnmount, openB
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
3
3
  import { CLOSE_ICON } from "../components/dropdown.js";
4
4
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
5
- import { _ as _sfc_main$2 } from "./LpScrollArea-DQ0Yudfu.js";
5
+ import { _ as _sfc_main$2 } from "./LpScrollArea-BjImFflN.js";
6
6
  const _hoisted_1 = { class: "fixed inset-0 z-(--z-modal) flex items-center justify-center pointer-events-none" };
7
7
  const _hoisted_2 = {
8
8
  key: 0,
@@ -57,21 +57,28 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
57
57
  let mo;
58
58
  let raf = 0;
59
59
  let settleTimer;
60
+ let lastHeight = 0;
61
+ let inFlight = false;
60
62
  const retune = (el) => {
61
- const from = el.offsetHeight;
63
+ if (inFlight) return;
64
+ const from = lastHeight;
62
65
  el.style.height = "";
63
66
  el.style.maxHeight = "none";
64
67
  const to = el.offsetHeight;
65
68
  el.style.maxHeight = "";
66
- if (to === 0 || to === from) return;
69
+ lastHeight = to;
70
+ if (to === 0 || from === 0 || to === from) return;
67
71
  el.style.height = `${from}px`;
68
72
  void el.offsetHeight;
69
73
  el.style.height = `${to}px`;
74
+ inFlight = true;
70
75
  resizing.value = true;
71
76
  clearTimeout(settleTimer);
72
77
  settleTimer = setTimeout(() => {
78
+ inFlight = false;
73
79
  resizing.value = false;
74
80
  el.style.height = "";
81
+ lastHeight = el.offsetHeight;
75
82
  }, TWEEN_MS);
76
83
  };
77
84
  watch(
@@ -85,8 +92,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
85
92
  clearTimeout(settleTimer);
86
93
  tweening.value = false;
87
94
  resizing.value = false;
95
+ lastHeight = 0;
96
+ inFlight = false;
88
97
  const el = panelRef.value?.$el;
89
98
  if (!isOpen || !(el instanceof HTMLElement)) return;
99
+ lastHeight = el.offsetHeight;
90
100
  el.style.height = `${el.offsetHeight}px`;
91
101
  raf = requestAnimationFrame(() => {
92
102
  tweening.value = true;
@@ -175,6 +185,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
175
185
  renderSlot(_ctx.$slots, "default")
176
186
  ], 2)) : (openBlock(), createBlock(_sfc_main$2, {
177
187
  key: 2,
188
+ fade: "",
178
189
  class: normalizeClass(["min-h-0 flex-1 text-sm text-ink/90", resizing.value ? "[&_[data-scrollbarimpl]]:invisible" : ""]),
179
190
  "content-class": bodyPad.value
180
191
  }, {
@@ -5,7 +5,7 @@ import { _ as _sfc_main$4 } from "./LpContextMenu-D0mzXqA7.js";
5
5
  import { _ as _sfc_main$2 } from "./LpEmptyState-CrfyRJUG.js";
6
6
  import { _ as _sfc_main$5 } from "./LpIcon-CCnX5_2j.js";
7
7
  import { _ as _sfc_main$1 } from "./LpPopover-8268UmlC.js";
8
- import { _ as _sfc_main$3 } from "./LpScrollArea-DQ0Yudfu.js";
8
+ import { _ as _sfc_main$3 } from "./LpScrollArea-BjImFflN.js";
9
9
  const _hoisted_1 = { class: "flex items-center justify-between gap-2 px-3 py-2.5" };
10
10
  const _hoisted_2 = { class: "text-sm font-semibold" };
11
11
  const _hoisted_3 = {
@@ -0,0 +1,120 @@
1
+ import { defineComponent, ref, computed, watch, onBeforeUnmount, openBlock, createBlock, unref, normalizeClass, withCtx, createVNode, normalizeStyle, createElementVNode, renderSlot } from "vue";
2
+ import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "reka-ui";
3
+ const barFade = "opacity-0 data-[state=visible]:animate-[fade-in_180ms_ease] data-[state=visible]:opacity-100 data-[state=hidden]:animate-[fade-out_240ms_ease]";
4
+ const FADE_PX = 14;
5
+ const _sfc_main = /* @__PURE__ */ defineComponent({
6
+ __name: "LpScrollArea",
7
+ props: {
8
+ fade: { type: Boolean },
9
+ instant: { type: Boolean, default: false },
10
+ contentClass: {},
11
+ barInsetTop: {}
12
+ },
13
+ emits: ["scroll"],
14
+ setup(__props, { expose: __expose, emit: __emit }) {
15
+ const props = __props;
16
+ const emit = __emit;
17
+ const viewportRef = ref(null);
18
+ const viewportEl = computed(
19
+ () => viewportRef.value?.viewportElement ?? null
20
+ );
21
+ __expose({ viewportEl });
22
+ const dragging = ref(false);
23
+ const atTop = ref(true);
24
+ const atBottom = ref(true);
25
+ const fadeMask = computed(() => {
26
+ const top = atTop.value ? "black 0" : `transparent 0, black ${FADE_PX}px`;
27
+ const bottom = atBottom.value ? "black 100%" : `black calc(100% - ${FADE_PX}px), transparent 100%`;
28
+ return `linear-gradient(to bottom, ${top}, ${bottom})`;
29
+ });
30
+ function measureEdges(el) {
31
+ atTop.value = el.scrollTop <= 1;
32
+ atBottom.value = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
33
+ }
34
+ function onViewportScroll(event) {
35
+ if (props.fade) measureEdges(event.target);
36
+ emit("scroll", event);
37
+ }
38
+ let edgeObserver;
39
+ watch(
40
+ [viewportEl, () => props.fade],
41
+ ([el, enabled]) => {
42
+ edgeObserver?.disconnect();
43
+ edgeObserver = void 0;
44
+ if (!el || !enabled || typeof ResizeObserver === "undefined") return;
45
+ measureEdges(el);
46
+ edgeObserver = new ResizeObserver(() => measureEdges(el));
47
+ edgeObserver.observe(el);
48
+ for (const child of Array.from(el.children)) edgeObserver.observe(child);
49
+ },
50
+ { immediate: true, flush: "post" }
51
+ );
52
+ onBeforeUnmount(() => edgeObserver?.disconnect());
53
+ function onThumbPointerDown() {
54
+ dragging.value = true;
55
+ const stop = () => {
56
+ dragging.value = false;
57
+ window.removeEventListener("pointerup", stop);
58
+ window.removeEventListener("pointercancel", stop);
59
+ };
60
+ window.addEventListener("pointerup", stop);
61
+ window.addEventListener("pointercancel", stop);
62
+ }
63
+ return (_ctx, _cache) => {
64
+ return openBlock(), createBlock(unref(ScrollAreaRoot), {
65
+ class: normalizeClass(["relative flex flex-col overflow-hidden", dragging.value ? "select-none" : void 0]),
66
+ type: "hover",
67
+ "scroll-hide-delay": 500
68
+ }, {
69
+ default: withCtx(() => [
70
+ createVNode(unref(ScrollAreaScrollbar), {
71
+ orientation: "vertical",
72
+ "force-mount": "",
73
+ class: normalizeClass(["group flex w-3.5 touch-none select-none justify-end py-px pl-2.5 pr-px", barFade]),
74
+ style: normalizeStyle(__props.barInsetTop ? { marginTop: __props.barInsetTop } : void 0)
75
+ }, {
76
+ default: withCtx(() => [
77
+ createVNode(unref(ScrollAreaThumb), {
78
+ class: "min-w-1 rounded-l-pill bg-brand/40 transition-[min-width,background-color] duration-[var(--duration-fast)] group-hover:min-w-1.5 group-hover:bg-brand",
79
+ onPointerdown: onThumbPointerDown
80
+ })
81
+ ]),
82
+ _: 1
83
+ }, 8, ["style"]),
84
+ createVNode(unref(ScrollAreaScrollbar), {
85
+ orientation: "horizontal",
86
+ class: normalizeClass(["group flex h-3.5 flex-col touch-none select-none justify-end px-px pb-px pt-2.5", barFade])
87
+ }, {
88
+ default: withCtx(() => [
89
+ createVNode(unref(ScrollAreaThumb), {
90
+ class: "min-h-1 rounded-t-pill bg-brand/40 transition-[min-height,background-color] duration-[var(--duration-fast)] group-hover:min-h-1.5 group-hover:bg-brand",
91
+ onPointerdown: onThumbPointerDown
92
+ })
93
+ ]),
94
+ _: 1
95
+ }),
96
+ createVNode(unref(ScrollAreaViewport), {
97
+ ref_key: "viewportRef",
98
+ ref: viewportRef,
99
+ class: normalizeClass(["w-full min-h-0 min-w-0 flex-1 [&>div]:!block [&>div]:!min-w-0", __props.instant ? "" : "scroll-smooth motion-reduce:scroll-auto"]),
100
+ style: normalizeStyle(__props.fade ? { maskImage: fadeMask.value, WebkitMaskImage: fadeMask.value } : void 0),
101
+ onScrollPassive: onViewportScroll
102
+ }, {
103
+ default: withCtx(() => [
104
+ createElementVNode("div", {
105
+ class: normalizeClass(__props.contentClass)
106
+ }, [
107
+ renderSlot(_ctx.$slots, "default")
108
+ ], 2)
109
+ ]),
110
+ _: 3
111
+ }, 8, ["class", "style"])
112
+ ]),
113
+ _: 3
114
+ }, 8, ["class"]);
115
+ };
116
+ }
117
+ });
118
+ export {
119
+ _sfc_main as _
120
+ };
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps, createBlock } from "vue";
2
- import { _ as _sfc_main$2 } from "./LpDrawer-CYm-nUIZ.js";
3
- import { _ as _sfc_main$1 } from "./LpSidebarNav-DSDa5mAY.js";
2
+ import { _ as _sfc_main$2 } from "./LpDrawer-D1ubjU11.js";
3
+ import { _ as _sfc_main$1 } from "./LpSidebarNav-Ds95Sp9b.js";
4
4
  const _hoisted_1 = {
5
5
  key: 1,
6
6
  class: "mt-2 flex shrink-0 flex-col gap-2"
@@ -4,7 +4,7 @@ import { usePillTransition } from "../composables/usePillTransition.js";
4
4
  import { _ as _sfc_main$4 } from "./LpBadge-CXzBPnwO.js";
5
5
  import { _ as _sfc_main$2 } from "./LpContextMenu-D0mzXqA7.js";
6
6
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
7
- import { _ as _sfc_main$1 } from "./LpScrollArea-DQ0Yudfu.js";
7
+ import { _ as _sfc_main$1 } from "./LpScrollArea-BjImFflN.js";
8
8
  const _hoisted_1 = {
9
9
  key: 0,
10
10
  class: "flex min-h-0 flex-1 flex-col gap-4"
@@ -2,7 +2,7 @@ import { defineComponent, computed, ref, watch, onBeforeUnmount, openBlock, crea
2
2
  import { _ as _sfc_main$2 } from "./LpCheckbox-BO0zuuPh.js";
3
3
  import { _ as _sfc_main$4 } from "./LpContextMenu-D0mzXqA7.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
5
- import { _ as _sfc_main$1 } from "./LpScrollArea-DQ0Yudfu.js";
5
+ import { _ as _sfc_main$1 } from "./LpScrollArea-BjImFflN.js";
6
6
  const _hoisted_1 = { class: "w-full border-collapse text-sm" };
7
7
  const _hoisted_2 = { class: "border-b border-line bg-surface-soft" };
8
8
  const _hoisted_3 = {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAddressInput-CCtskgfZ.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAddressInput-BV9M0aec.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-D42la4ZD.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-oYlB1C9T.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, ref, computed, watch, nextTick, onMounted, onBeforeUnmount, openBlock, createBlock, unref, withCtx, createVNode, withModifiers, createTextVNode, createElementVNode, withDirectives, withKeys, vModelText, createElementBlock, toDisplayString, createCommentVNode, Fragment, renderList, normalizeClass } from "vue";
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription } from "reka-ui";
3
3
  import { _ as _sfc_main$1 } from "../chunks/LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-DQ0Yudfu.js";
4
+ import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-BjImFflN.js";
5
5
  import '../assets/LpCommandPalette-Cp6Y-j7g.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
6
6
  const _hoisted_2 = ["placeholder", "onKeydown"];
7
7
  const _hoisted_3 = {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmDialog-Z9hsfm2D.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmDialog-BgaP-GJZ.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpDrawer-CYm-nUIZ.js";
1
+ import { _ as _sfc_main } from "../chunks/LpDrawer-D1ubjU11.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFileTree-DXb3yKzH.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFileTree-CmKJxz4K.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpLogViewer-tmKPNTuU.js";
1
+ import { _ as _sfc_main } from "../chunks/LpLogViewer-CFckvccr.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModal-g2dGpF7l.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModal-YaFleOpp.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpNotificationBell-DHze8wYN.js";
1
+ import { _ as _sfc_main } from "../chunks/LpNotificationBell-B1QTe4mK.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -4,9 +4,9 @@ type __VLS_Props = {
4
4
  contentClass?: string;
5
5
  barInsetTop?: string;
6
6
  };
7
- declare var __VLS_40: {};
7
+ declare var __VLS_44: {};
8
8
  type __VLS_Slots = {} & {
9
- default?: (props: typeof __VLS_40) => any;
9
+ default?: (props: typeof __VLS_44) => any;
10
10
  };
11
11
  declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
12
12
  viewportEl: import('vue').ComputedRef<HTMLElement | null>;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpScrollArea-DQ0Yudfu.js";
1
+ import { _ as _sfc_main } from "../chunks/LpScrollArea-BjImFflN.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebar-DlLZJk1u.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebar-DlvoM3h6.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebarNav-DSDa5mAY.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebarNav-Ds95Sp9b.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTable-DqZ-L4Au.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTable-i2AEwiPd.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-CCtskgfZ.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-BV9M0aec.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-D42la4ZD.js";
6
+ import { _ as _5 } from "./chunks/LpAppShell-oYlB1C9T.js";
7
7
  import { _ as _6 } from "./chunks/LpAutocomplete-DIgXmtCA.js";
8
8
  import { _ as _7 } from "./chunks/LpAvatar-B1Ez0ZAx.js";
9
9
  import { _ as _8 } from "./chunks/LpBadge-CXzBPnwO.js";
@@ -14,15 +14,15 @@ 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-Z9hsfm2D.js";
17
+ import { _ as _15 } from "./chunks/LpConfirmDialog-BgaP-GJZ.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";
21
21
  import { _ as _19 } from "./chunks/LpDivider-CUX46KR0.js";
22
- import { _ as _20 } from "./chunks/LpDrawer-CYm-nUIZ.js";
22
+ import { _ as _20 } from "./chunks/LpDrawer-D1ubjU11.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-DXb3yKzH.js";
25
+ import { _ as _23 } from "./chunks/LpFileTree-CmKJxz4K.js";
26
26
  import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
27
27
  import { _ as _24 } from "./chunks/LpFormField-Bn-ZwC6z.js";
28
28
  import { _ as _25 } from "./chunks/LpIcon-CCnX5_2j.js";
@@ -31,9 +31,9 @@ import { _ as _27 } from "./chunks/LpInput-BauZRyzm.js";
31
31
  import { _ as _28 } from "./chunks/LpLightbox-CzhFe9wZ.js";
32
32
  import { fileNameOf } from "./components/lightbox.js";
33
33
  import { _ as _29 } from "./chunks/LpLink-DHwOEp4e.js";
34
- import { _ as _30 } from "./chunks/LpLogViewer-tmKPNTuU.js";
35
- import { _ as _31 } from "./chunks/LpModal-g2dGpF7l.js";
36
- import { _ as _32 } from "./chunks/LpNotificationBell-DHze8wYN.js";
34
+ import { _ as _30 } from "./chunks/LpLogViewer-CFckvccr.js";
35
+ import { _ as _31 } from "./chunks/LpModal-YaFleOpp.js";
36
+ import { _ as _32 } from "./chunks/LpNotificationBell-B1QTe4mK.js";
37
37
  import { _ as _33 } from "./chunks/LpNumberField-C5ar-OwE.js";
38
38
  import { _ as _34 } from "./chunks/LpOtpInput-DqaT0Z75.js";
39
39
  import { _ as _35 } from "./chunks/LpPagination-CeGuNe_q.js";
@@ -44,17 +44,17 @@ import { _ as _38 } from "./chunks/LpPopover-8268UmlC.js";
44
44
  import { _ as _39 } from "./chunks/LpProgress-BhI0IbjC.js";
45
45
  import { _ as _40 } from "./chunks/LpRadio-CL-pI0_O.js";
46
46
  import { _ as _41 } from "./chunks/LpRadioGroup-B4yDTmi6.js";
47
- import { _ as _42 } from "./chunks/LpScrollArea-DQ0Yudfu.js";
47
+ import { _ as _42 } from "./chunks/LpScrollArea-BjImFflN.js";
48
48
  import { _ as _43 } from "./chunks/LpSegmented-BmpanyAo.js";
49
49
  import { _ as _44 } from "./chunks/LpSelect-D5mU4cZO.js";
50
50
  import { _ as _45 } from "./chunks/LpServiceNode-Dc-Ut5RS.js";
51
- import { _ as _46 } from "./chunks/LpSidebar-DlLZJk1u.js";
51
+ import { _ as _46 } from "./chunks/LpSidebar-DlvoM3h6.js";
52
52
  import { _ as _47 } from "./chunks/LpSkeleton-eSFIo4kD.js";
53
53
  import { _ as _48 } from "./chunks/LpSlider-B1SF3pRV.js";
54
54
  import { _ as _49 } from "./chunks/LpStat-CdbEFOvn.js";
55
55
  import { _ as _50 } from "./chunks/LpStepper-5dFTrW21.js";
56
56
  import { _ as _51 } from "./chunks/LpSwitch-v6fnccSM.js";
57
- import { _ as _52 } from "./chunks/LpTable-DqZ-L4Au.js";
57
+ import { _ as _52 } from "./chunks/LpTable-i2AEwiPd.js";
58
58
  import { _ as _53 } from "./chunks/LpTableOfContents-CjvxPMpU.js";
59
59
  import { _ as _54 } from "./chunks/LpTabs-D7aTVPOb.js";
60
60
  import { _ as _55 } from "./chunks/LpTextarea-CRAkhemZ.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.20.2",
3
+ "version": "0.20.4",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -99,6 +99,10 @@ let ro: ResizeObserver | undefined
99
99
  let mo: MutationObserver | undefined
100
100
  let raf = 0
101
101
  let settleTimer: ReturnType<typeof setTimeout> | undefined
102
+ /** Where the panel last settled, so a retune has a height to ease FROM. */
103
+ let lastHeight = 0
104
+ /** True while a tween owns the height, so observers don't cancel it midway. */
105
+ let inFlight = false
102
106
 
103
107
  // The height is driven imperatively rather than through a style binding. By the
104
108
  // time replacement content is in the DOM the panel has already reflowed to its
@@ -106,7 +110,12 @@ let settleTimer: ReturnType<typeof setTimeout> | undefined
106
110
  // height for the transition to start from. Writing the OLD height, forcing a
107
111
  // reflow, then writing the new one gives the transition both ends.
108
112
  const retune = (el: HTMLElement) => {
109
- const from = el.offsetHeight
113
+ // One swap wakes both observers — the mutation as content lands, the resize as
114
+ // it reflows — and the second call would clear the pin the first just wrote.
115
+ if (inFlight) return
116
+ // Remembered, not measured: by the time either observer fires the panel has
117
+ // already reflowed, so the element can only report where it is going.
118
+ const from = lastHeight
110
119
  el.style.height = ""
111
120
  // `max-h` comes from a class, so the panel already reports the clipped figure
112
121
  // once the cap bites. Pinning that would leave a short panel sitting over
@@ -115,24 +124,30 @@ const retune = (el: HTMLElement) => {
115
124
  el.style.maxHeight = "none"
116
125
  const to = el.offsetHeight
117
126
  el.style.maxHeight = ""
127
+ lastHeight = to
118
128
  // Nothing moved, so there is nothing to ease — and leaving the height unset
119
129
  // keeps the panel free to size itself.
120
- if (to === 0 || to === from) return
130
+ if (to === 0 || from === 0 || to === from) return
121
131
  el.style.height = `${from}px`
122
132
  void el.offsetHeight
123
133
  el.style.height = `${to}px`
124
134
 
135
+ inFlight = true
125
136
  // While the panel is still catching up, the body is shorter than the content
126
137
  // it already holds, so the scroll area would flash a bar for an overflow that
127
138
  // resolves itself the moment the transition lands.
128
139
  resizing.value = true
129
140
  clearTimeout(settleTimer)
130
141
  settleTimer = setTimeout(() => {
142
+ inFlight = false
131
143
  resizing.value = false
132
144
  // Released once the panel has arrived. A height left pinned in pixels stops
133
145
  // being a starting point and becomes a cap: content that grows afterwards
134
146
  // is clipped, and `max-h` can no longer size the panel itself.
135
147
  el.style.height = ""
148
+ // Re-read rather than trusting `to`: `max-h` may have clamped the panel
149
+ // short of it, and the next tween has to start from where it really sits.
150
+ lastHeight = el.offsetHeight
136
151
  }, TWEEN_MS)
137
152
  }
138
153
 
@@ -150,9 +165,13 @@ watch(
150
165
  tweening.value = false
151
166
  resizing.value = false
152
167
 
168
+ lastHeight = 0
169
+ inFlight = false
170
+
153
171
  const el = panelRef.value?.$el
154
172
  if (!isOpen || !(el instanceof HTMLElement)) return
155
173
 
174
+ lastHeight = el.offsetHeight
156
175
  el.style.height = `${el.offsetHeight}px`
157
176
  // The opening panel must not animate its height in from the pre-content box,
158
177
  // so the transition only arms once that first pin has painted. The pin is
@@ -264,8 +283,11 @@ onBeforeUnmount(() => {
264
283
  it already holds, so the scroll area raises a bar for an overflow
265
284
  that resolves itself as the transition lands. Hiding just the bar
266
285
  for the length of the tween leaves scrolling itself untouched. -->
286
+ <!-- Faded because a capped panel cuts its body mid-row, and a hard edge
287
+ at the crop reads as the content ending there. -->
267
288
  <LpScrollArea
268
289
  v-else
290
+ fade
269
291
  class="min-h-0 flex-1 text-sm text-ink/90"
270
292
  :class="resizing ? '[&_[data-scrollbarimpl]]:invisible' : ''"
271
293
  :content-class="bodyPad"
@@ -6,7 +6,7 @@
6
6
  * exit animation). Rounded only on the OUTER edge — the side flush to the wall
7
7
  * stays square. Themed via tokens; native scroll/keyboard/touch preserved.
8
8
  */
9
- import { computed, ref } from "vue"
9
+ import { computed, onBeforeUnmount, ref, watch } from "vue"
10
10
  import {
11
11
  ScrollAreaRoot,
12
12
  ScrollAreaScrollbar,
@@ -14,7 +14,8 @@ import {
14
14
  ScrollAreaViewport,
15
15
  } from "reka-ui"
16
16
 
17
- // `fade` softens the top/bottom edges to hint more content.
17
+ // `fade` softens whichever of the top/bottom edges currently has content beyond
18
+ // it, so a clipped list reads as continuing rather than as cut off.
18
19
  // `contentClass` styles the scrollable CONTENT container (the viewport's inner
19
20
  // wrapper) — put list layout here (display:grid / gap / padding), not on the
20
21
  // component root, since items live inside reka's viewport.
@@ -40,7 +41,7 @@ const props = withDefaults(
40
41
  // drive scroll-following (e.g. log tails) can track position. The listener is
41
42
  // bound on ScrollAreaViewport, whose `$attrs` reka forwards onto the scrollable
42
43
  // element itself.
43
- defineEmits<{ scroll: [event: Event] }>()
44
+ const emit = defineEmits<{ scroll: [event: Event] }>()
44
45
 
45
46
  // Expose the scrollable viewport element so callers can read/write scrollTop
46
47
  // (auto-scroll to bottom, "is at bottom" checks). reka's ScrollAreaViewport
@@ -56,6 +57,78 @@ defineExpose({ viewportEl })
56
57
  // would snap back to visible once the animation finished.
57
58
  const barFade =
58
59
  "opacity-0 data-[state=visible]:animate-[fade-in_180ms_ease] data-[state=visible]:opacity-100 data-[state=hidden]:animate-[fade-out_240ms_ease]"
60
+
61
+ // Dragging the thumb pulls the pointer off the bar and across the content, and
62
+ // the browser reads that as a text selection — the rows highlight blue while
63
+ // the user is only trying to scroll. Suppressed for the duration of the drag
64
+ // rather than with a blanket `select-none`, which would cost the content its
65
+ // selectability for good.
66
+ const dragging = ref(false)
67
+
68
+ /** How far the fade reaches in from an edge that has content beyond it. */
69
+ const FADE_PX = 14
70
+
71
+ const atTop = ref(true)
72
+ const atBottom = ref(true)
73
+
74
+ /// Only the edges that actually hide something are softened. A fixed mask dims
75
+ /// the last line of a list that has already been scrolled to the end, which
76
+ /// reads as content missing rather than as content continuing.
77
+ const fadeMask = computed(() => {
78
+ const top = atTop.value ? "black 0" : `transparent 0, black ${FADE_PX}px`
79
+ const bottom = atBottom.value
80
+ ? "black 100%"
81
+ : `black calc(100% - ${FADE_PX}px), transparent 100%`
82
+ return `linear-gradient(to bottom, ${top}, ${bottom})`
83
+ })
84
+
85
+ function measureEdges(el: HTMLElement) {
86
+ atTop.value = el.scrollTop <= 1
87
+ // A rounded-up scrollHeight can sit a pixel above the sum, so the comparison
88
+ // is given the same slack as the top rather than demanding an exact landing.
89
+ atBottom.value = el.scrollTop + el.clientHeight >= el.scrollHeight - 1
90
+ }
91
+
92
+ function onViewportScroll(event: Event) {
93
+ if (props.fade) measureEdges(event.target as HTMLElement)
94
+ emit("scroll", event)
95
+ }
96
+
97
+ // Scrolling is not the only thing that changes which edges hide content: a list
98
+ // that fills in, a panel that resizes, or content short enough to need no fade
99
+ // at all never fire a scroll event. Watched only when `fade` is on, since
100
+ // nothing else here depends on the measurements.
101
+ let edgeObserver: ResizeObserver | undefined
102
+
103
+ watch(
104
+ [viewportEl, () => props.fade],
105
+ ([el, enabled]) => {
106
+ edgeObserver?.disconnect()
107
+ edgeObserver = undefined
108
+ if (!el || !enabled || typeof ResizeObserver === "undefined") return
109
+ measureEdges(el)
110
+ edgeObserver = new ResizeObserver(() => measureEdges(el))
111
+ edgeObserver.observe(el)
112
+ // The content box grows independently of the viewport's own size.
113
+ for (const child of Array.from(el.children)) edgeObserver.observe(child)
114
+ },
115
+ { immediate: true, flush: "post" },
116
+ )
117
+
118
+ onBeforeUnmount(() => edgeObserver?.disconnect())
119
+
120
+ function onThumbPointerDown() {
121
+ dragging.value = true
122
+ const stop = () => {
123
+ dragging.value = false
124
+ window.removeEventListener("pointerup", stop)
125
+ window.removeEventListener("pointercancel", stop)
126
+ }
127
+ // Bound on the window: the pointer is captured by the thumb, and the release
128
+ // routinely happens far outside the scroll area.
129
+ window.addEventListener("pointerup", stop)
130
+ window.addEventListener("pointercancel", stop)
131
+ }
59
132
  </script>
60
133
 
61
134
  <template>
@@ -65,7 +138,12 @@ const barFade =
65
138
  by `flex-1` inside a capped panel — is itself content-derived, so the
66
139
  viewport grew to the full content height and scrolled nothing while the
67
140
  root clipped the overflow. -->
68
- <ScrollAreaRoot class="relative flex flex-col overflow-hidden" type="hover" :scroll-hide-delay="500">
141
+ <ScrollAreaRoot
142
+ class="relative flex flex-col overflow-hidden"
143
+ :class="dragging ? 'select-none' : undefined"
144
+ type="hover"
145
+ :scroll-hide-delay="500"
146
+ >
69
147
  <!-- The bar itself is a pointer target far wider than it looks: the visible
70
148
  rail is the thumb, sized below, while the track pads it out to a
71
149
  grabbable strip. A 4px-wide hit area is near-impossible to catch with a
@@ -91,6 +169,7 @@ const barFade =
91
169
  the positioning reka owns. -->
92
170
  <ScrollAreaThumb
93
171
  class="min-w-1 rounded-l-pill bg-brand/40 transition-[min-width,background-color] duration-[var(--duration-fast)] group-hover:min-w-1.5 group-hover:bg-brand"
172
+ @pointerdown="onThumbPointerDown"
94
173
  />
95
174
  </ScrollAreaScrollbar>
96
175
 
@@ -101,17 +180,16 @@ const barFade =
101
180
  >
102
181
  <ScrollAreaThumb
103
182
  class="min-h-1 rounded-t-pill bg-brand/40 transition-[min-height,background-color] duration-[var(--duration-fast)] group-hover:min-h-1.5 group-hover:bg-brand"
183
+ @pointerdown="onThumbPointerDown"
104
184
  />
105
185
  </ScrollAreaScrollbar>
106
186
 
107
187
  <ScrollAreaViewport
108
188
  ref="viewportRef"
109
189
  class="w-full min-h-0 min-w-0 flex-1 [&>div]:!block [&>div]:!min-w-0"
110
- :class="[
111
- instant ? '' : 'scroll-smooth motion-reduce:scroll-auto',
112
- fade ? '[mask-image:linear-gradient(to_bottom,transparent_0,black_14px,black_calc(100%-14px),transparent_100%)]' : '',
113
- ]"
114
- @scroll.passive="$emit('scroll', $event)"
190
+ :class="instant ? '' : 'scroll-smooth motion-reduce:scroll-auto'"
191
+ :style="fade ? { maskImage: fadeMask, WebkitMaskImage: fadeMask } : undefined"
192
+ @scroll.passive="onViewportScroll"
115
193
  >
116
194
  <div :class="contentClass">
117
195
  <slot />
@@ -1,72 +0,0 @@
1
- import { defineComponent, ref, computed, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, normalizeClass, createElementVNode, renderSlot } from "vue";
2
- import { ScrollAreaRoot, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "reka-ui";
3
- const barFade = "opacity-0 data-[state=visible]:animate-[fade-in_180ms_ease] data-[state=visible]:opacity-100 data-[state=hidden]:animate-[fade-out_240ms_ease]";
4
- const _sfc_main = /* @__PURE__ */ defineComponent({
5
- __name: "LpScrollArea",
6
- props: {
7
- fade: { type: Boolean },
8
- instant: { type: Boolean, default: false },
9
- contentClass: {},
10
- barInsetTop: {}
11
- },
12
- emits: ["scroll"],
13
- setup(__props, { expose: __expose }) {
14
- const viewportRef = ref(null);
15
- const viewportEl = computed(
16
- () => viewportRef.value?.viewportElement ?? null
17
- );
18
- __expose({ viewportEl });
19
- return (_ctx, _cache) => {
20
- return openBlock(), createBlock(unref(ScrollAreaRoot), {
21
- class: "relative flex flex-col overflow-hidden",
22
- type: "hover",
23
- "scroll-hide-delay": 500
24
- }, {
25
- default: withCtx(() => [
26
- createVNode(unref(ScrollAreaScrollbar), {
27
- orientation: "vertical",
28
- "force-mount": "",
29
- class: normalizeClass(["group flex w-3.5 touch-none select-none justify-end py-px pl-2.5 pr-px", barFade]),
30
- style: normalizeStyle(__props.barInsetTop ? { marginTop: __props.barInsetTop } : void 0)
31
- }, {
32
- default: withCtx(() => [
33
- createVNode(unref(ScrollAreaThumb), { class: "min-w-1 rounded-l-pill bg-brand/40 transition-[min-width,background-color] duration-[var(--duration-fast)] group-hover:min-w-1.5 group-hover:bg-brand" })
34
- ]),
35
- _: 1
36
- }, 8, ["style"]),
37
- createVNode(unref(ScrollAreaScrollbar), {
38
- orientation: "horizontal",
39
- class: normalizeClass(["group flex h-3.5 flex-col touch-none select-none justify-end px-px pb-px pt-2.5", barFade])
40
- }, {
41
- default: withCtx(() => [
42
- createVNode(unref(ScrollAreaThumb), { class: "min-h-1 rounded-t-pill bg-brand/40 transition-[min-height,background-color] duration-[var(--duration-fast)] group-hover:min-h-1.5 group-hover:bg-brand" })
43
- ]),
44
- _: 1
45
- }),
46
- createVNode(unref(ScrollAreaViewport), {
47
- ref_key: "viewportRef",
48
- ref: viewportRef,
49
- class: normalizeClass(["w-full min-h-0 min-w-0 flex-1 [&>div]:!block [&>div]:!min-w-0", [
50
- __props.instant ? "" : "scroll-smooth motion-reduce:scroll-auto",
51
- __props.fade ? "[mask-image:linear-gradient(to_bottom,transparent_0,black_14px,black_calc(100%-14px),transparent_100%)]" : ""
52
- ]]),
53
- onScrollPassive: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("scroll", $event))
54
- }, {
55
- default: withCtx(() => [
56
- createElementVNode("div", {
57
- class: normalizeClass(__props.contentClass)
58
- }, [
59
- renderSlot(_ctx.$slots, "default")
60
- ], 2)
61
- ]),
62
- _: 3
63
- }, 8, ["class"])
64
- ]),
65
- _: 3
66
- });
67
- };
68
- }
69
- });
70
- export {
71
- _sfc_main as _
72
- };