@leavepulse/ui 0.55.0 → 0.57.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 (73) hide show
  1. package/dist/chunks/{LpActionBar-CnfFVIwC.js → LpActionBar-C3hkVsFz.js} +1 -1
  2. package/dist/chunks/{LpAddressInput-iOJhmRzy.js → LpAddressInput-NUHmplCd.js} +1 -1
  3. package/dist/chunks/{LpAppShell-DcSXik4a.js → LpAppShell-Bl1Ch68N.js} +2 -2
  4. package/dist/chunks/{LpButton-CKSwC6Mu.js → LpButton-BdfZFNqi.js} +31 -7
  5. package/dist/chunks/{LpConfirmDialog-Ca-CTlxT.js → LpConfirmDialog-CrbUZgJE.js} +1 -1
  6. package/dist/chunks/{LpConfirmHost-BAaSaT2b.js → LpConfirmHost-BgMIV8r9.js} +1 -1
  7. package/dist/chunks/{LpDateRangePicker-Drd7EbIz.js → LpDateRangePicker-CxV5jW7p.js} +4 -13
  8. package/dist/chunks/{LpDrawer-CBFVD624.js → LpDrawer-D9lLOC0C.js} +4 -6
  9. package/dist/chunks/LpHintLayer-CI1oZ9Pd.js +154 -0
  10. package/dist/chunks/{LpModalNav-aWwc_5CD.js → LpModalNav-DXyAyhQl.js} +2 -2
  11. package/dist/chunks/{LpNotificationBell-wZ3meeim.js → LpNotificationBell-DEL-RoHS.js} +1 -1
  12. package/dist/chunks/{LpPie-B7ti5qCH.js → LpPie-CXka8Jvx.js} +1 -1
  13. package/dist/chunks/{LpSidebar-C2oolgwy.js → LpSidebar-hZ8D1cVr.js} +1 -1
  14. package/dist/chunks/{LpTableOfContents-BUcAfGPQ.js → LpTableOfContents-DYuqkByy.js} +2 -8
  15. package/dist/chunks/{LpTooltip-CXVKcOXT.js → LpTooltip-suFCM34F.js} +2 -2
  16. package/dist/chunks/LpTour-C1TC2GyY.js +315 -0
  17. package/dist/chunks/{LpUptimeBar-ugcv25xj.js → LpUptimeBar-B4ltCfno.js} +1 -1
  18. package/dist/chunks/TourCard-BQS3ztqj.js +100 -0
  19. package/dist/component-names.d.ts +1 -1
  20. package/dist/components/LpActionBar.vue.js +1 -1
  21. package/dist/components/LpAddressInput.vue.js +1 -1
  22. package/dist/components/LpAppShell.vue.js +1 -1
  23. package/dist/components/LpButton.vue.d.ts +10 -0
  24. package/dist/components/LpButton.vue.js +1 -1
  25. package/dist/components/LpConfirmDialog.vue.js +1 -1
  26. package/dist/components/LpConfirmHost.vue.js +1 -1
  27. package/dist/components/LpDateRangePicker.vue.js +1 -1
  28. package/dist/components/LpDrawer.vue.js +1 -1
  29. package/dist/components/LpHintLayer.vue.d.ts +33 -0
  30. package/dist/components/LpHintLayer.vue.js +4 -0
  31. package/dist/components/LpImageEditor.vue.js +1 -1
  32. package/dist/components/LpModalNav.vue.js +1 -1
  33. package/dist/components/LpNotificationBell.vue.js +1 -1
  34. package/dist/components/LpPie.vue.js +1 -1
  35. package/dist/components/LpSidebar.vue.js +1 -1
  36. package/dist/components/LpTable.vue.js +1 -1
  37. package/dist/components/LpTableOfContents.vue.js +1 -1
  38. package/dist/components/LpTooltip.vue.js +1 -1
  39. package/dist/components/LpTour.vue.d.ts +98 -0
  40. package/dist/components/LpTour.vue.js +4 -0
  41. package/dist/components/LpUptimeBar.vue.js +1 -1
  42. package/dist/components/TourCard.vue.d.ts +28 -0
  43. package/dist/components/TourCard.vue.js +4 -0
  44. package/dist/components/tour.d.ts +21 -0
  45. package/dist/components/tour.js +38 -0
  46. package/dist/composables/scroll.d.ts +17 -0
  47. package/dist/composables/scroll.js +29 -0
  48. package/dist/composables/useGamepadNav.js +2 -7
  49. package/dist/composables/useHints.d.ts +18 -0
  50. package/dist/composables/useHints.js +120 -0
  51. package/dist/composables/useMediaQuery.d.ts +2 -0
  52. package/dist/composables/useMediaQuery.js +18 -0
  53. package/dist/composables/usePaintSelect.js +1 -7
  54. package/dist/index.d.ts +10 -0
  55. package/dist/index.js +38 -22
  56. package/package.json +1 -1
  57. package/src/component-names.ts +2 -0
  58. package/src/components/LpButton.vue +28 -1
  59. package/src/components/LpDateRangePicker.vue +3 -12
  60. package/src/components/LpDrawer.vue +4 -7
  61. package/src/components/LpHintLayer.vue +148 -0
  62. package/src/components/LpTableOfContents.vue +2 -14
  63. package/src/components/LpTooltip.vue +2 -4
  64. package/src/components/LpTour.vue +338 -0
  65. package/src/components/TourCard.vue +41 -0
  66. package/src/components/tour.ts +57 -0
  67. package/src/composables/scroll.ts +54 -0
  68. package/src/composables/useGamepadNav.ts +2 -7
  69. package/src/composables/useHints.ts +151 -0
  70. package/src/composables/useMediaQuery.ts +20 -0
  71. package/src/composables/usePaintSelect.ts +1 -8
  72. package/src/index.ts +10 -0
  73. package/src/tokens/tokens.css +1 -0
@@ -2,7 +2,7 @@ import { defineComponent, useSlots, computed, ref, watch, onBeforeUnmount, openB
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { useBarStack } from "../components/actionBar.js";
4
4
  import { CLOSE_ICON } from "../components/dropdown.js";
5
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
5
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
6
6
  import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
7
7
  const _hoisted_1 = ["aria-label"];
8
8
  const _hoisted_2 = {
@@ -3,7 +3,7 @@ import { useAsyncTask } from "../composables/useAsyncTask.js";
3
3
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress } from "../components/address.js";
4
4
  import { flagEmoji, loadCountries } from "../components/countries.js";
5
5
  import { _ as _sfc_main$2 } from "./LpAutocomplete-C8IgSMsi.js";
6
- import { _ as _sfc_main$3 } from "./LpButton-CKSwC6Mu.js";
6
+ import { _ as _sfc_main$3 } from "./LpButton-BdfZFNqi.js";
7
7
  import { _ as _sfc_main$1 } from "./LpFormField-CtVYj6tO.js";
8
8
  import { _ as _sfc_main$4 } from "./LpIcon-DYtaid0K.js";
9
9
  import { _ as _sfc_main$5 } from "./LpInput-D-YGQbEv.js";
@@ -1,10 +1,10 @@
1
1
  import { defineComponent, computed, ref, useId, watch, openBlock, createElementBlock, mergeProps, unref, createElementVNode, withModifiers, toDisplayString, createVNode, createSlots, withCtx, renderSlot, normalizeProps, guardReactiveProps, normalizeClass, createBlock } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
3
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
5
5
  import { _ as _sfc_main$5 } from "./LpScrollArea-Hz3-JWUG.js";
6
6
  import { _ as _sfc_main$4 } from "./LpShift-DXNsauQZ.js";
7
- import { _ as _sfc_main$1 } from "./LpSidebar-C2oolgwy.js";
7
+ import { _ as _sfc_main$1 } from "./LpSidebar-hZ8D1cVr.js";
8
8
  import { useMergedAttrs } from "../composables/useMergedClass.js";
9
9
  const _hoisted_1 = ["href"];
10
10
  const _hoisted_2 = { class: "flex min-w-0 flex-1 flex-col" };
@@ -1,7 +1,8 @@
1
- import { defineComponent, computed, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot } from "vue";
1
+ import { defineComponent, computed, openBlock, createBlock, resolveDynamicComponent, mergeProps, unref, withCtx, renderSlot, createElementBlock, normalizeClass, createVNode, createCommentVNode } from "vue";
2
2
  import { tv } from "tailwind-variants";
3
3
  import { useLinkComponent } from "../composables/useLpConfig.js";
4
4
  import { useMergedAttrs } from "../composables/useMergedClass.js";
5
+ import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
5
6
  import { resolveLink } from "../components/link.js";
6
7
  const __default__ = { inheritAttrs: false };
7
8
  const _sfc_main = /* @__PURE__ */ defineComponent({
@@ -17,7 +18,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
17
18
  to: {},
18
19
  href: {},
19
20
  type: { default: "button" },
20
- disabled: { type: Boolean }
21
+ disabled: { type: Boolean },
22
+ loading: { type: Boolean }
21
23
  },
22
24
  setup(__props) {
23
25
  const button = tv({
@@ -54,7 +56,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
54
56
  // icon-only square button — equal sides, no horizontal padding
55
57
  square: { true: "aspect-square p-0" },
56
58
  // soft elevation glow keyed to the variant's colour
57
- glow: { true: "" }
59
+ glow: { true: "" },
60
+ // busy: the label fades out in place (no width jump) and a spinner sits on top
61
+ loading: { true: "relative cursor-progress !text-transparent hover:!text-transparent" }
58
62
  },
59
63
  compoundVariants: [
60
64
  { square: true, size: "xs", class: "size-8" },
@@ -91,10 +95,18 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
91
95
  });
92
96
  const isNativeButton = computed(() => resolved.value.is === "button");
93
97
  function blockDisabled(event) {
94
- if (!props.disabled || isNativeButton.value) return;
98
+ if (!props.loading && (!props.disabled || isNativeButton.value)) return;
95
99
  event.preventDefault();
96
100
  event.stopImmediatePropagation();
97
101
  }
102
+ const SPINNER_TONE = {
103
+ solid: "text-ink-inverse",
104
+ action: "text-ink-inverse",
105
+ danger: "text-ink-inverse",
106
+ light: "text-[#0b1210]",
107
+ muted: "text-muted"
108
+ };
109
+ const spinnerTone = computed(() => SPINNER_TONE[props.variant ?? "solid"] ?? "text-ink");
98
110
  const linkProps = computed(() => {
99
111
  if (isNativeButton.value) return {};
100
112
  if (!props.disabled) return resolved.value.props;
@@ -106,7 +118,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
106
118
  size: props.size,
107
119
  block: props.block,
108
120
  square: props.square,
109
- glow: props.glow
121
+ glow: props.glow,
122
+ loading: props.loading
110
123
  })
111
124
  );
112
125
  return (_ctx, _cache) => {
@@ -115,14 +128,25 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
115
128
  disabled: isNativeButton.value ? __props.disabled : void 0,
116
129
  "aria-disabled": !isNativeButton.value && __props.disabled ? "true" : void 0,
117
130
  tabindex: !isNativeButton.value && __props.disabled ? -1 : void 0,
131
+ "aria-busy": __props.loading ? "true" : void 0,
118
132
  class: unref(classes),
119
133
  onClickCapture: blockDisabled
120
134
  }), {
121
135
  default: withCtx(() => [
122
- renderSlot(_ctx.$slots, "default")
136
+ renderSlot(_ctx.$slots, "default"),
137
+ __props.loading ? (openBlock(), createElementBlock("span", {
138
+ key: 0,
139
+ class: normalizeClass(["pointer-events-none absolute inset-0 flex items-center justify-center animate-[fade-in_var(--duration-fast)_var(--ease-emphasized)] motion-reduce:animate-none", spinnerTone.value]),
140
+ "aria-hidden": "true"
141
+ }, [
142
+ createVNode(_sfc_main$1, {
143
+ name: "lucide:loader-circle",
144
+ class: "size-[1.15em] animate-spin motion-reduce:animate-none"
145
+ })
146
+ ], 2)) : createCommentVNode("", true)
123
147
  ]),
124
148
  _: 3
125
- }, 16, ["type", "disabled", "aria-disabled", "tabindex", "class"]);
149
+ }, 16, ["type", "disabled", "aria-disabled", "tabindex", "aria-busy", "class"]);
126
150
  };
127
151
  }
128
152
  });
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, watch, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementBlock, toDisplayString, createCommentVNode, createVNode, createTextVNode, unref, createElementVNode, normalizeClass, nextTick } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
3
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
5
5
  import { _ as _sfc_main$1 } from "./LpModal-JNa3B_Lr.js";
6
6
  const _hoisted_1 = { class: "inline-flex items-center gap-2.5" };
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, watch, openBlock, createBlock, createCommentVNode } from "vue";
2
2
  import { confirmQueue, settleConfirm } from "../components/confirm.js";
3
- import { _ as _sfc_main$1 } from "./LpConfirmDialog-Ca-CTlxT.js";
3
+ import { _ as _sfc_main$1 } from "./LpConfirmDialog-CrbUZgJE.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpConfirmHost",
6
6
  props: {
@@ -1,9 +1,10 @@
1
- import { defineComponent, ref, computed, onMounted, onBeforeUnmount, useAttrs, openBlock, createElementBlock, normalizeStyle, unref, normalizeClass, createVNode, withCtx, createElementVNode, mergeProps, createCommentVNode, Fragment, renderList, toDisplayString } from "vue";
1
+ import { defineComponent, ref, computed, useAttrs, openBlock, createElementBlock, normalizeStyle, unref, normalizeClass, createVNode, withCtx, createElementVNode, mergeProps, createCommentVNode, Fragment, renderList, toDisplayString } from "vue";
2
2
  import { PopoverRoot, PopoverTrigger, PopoverPortal, PopoverContent } from "reka-ui";
3
3
  import { useFieldControl } from "../composables/useField.js";
4
4
  import { useLocale } from "../composables/useLocale.js";
5
5
  import { useLabels } from "../composables/useLpConfig.js";
6
6
  import { mergeClass } from "../composables/useMergedClass.js";
7
+ import { useMediaQuery } from "../composables/useMediaQuery.js";
7
8
  import { normalizeRange, formatRange, defaultRangePresets, sameRange, clampRange } from "../components/dateRange.js";
8
9
  import { POPOVER_PANEL } from "../components/dropdown.js";
9
10
  import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
@@ -75,17 +76,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
75
76
  function presetFits(preset) {
76
77
  return !!clampRange(preset.range(), props.min, props.max);
77
78
  }
78
- const wide = ref(false);
79
- let query;
80
- const onQuery = () => {
81
- wide.value = !!query?.matches;
82
- };
83
- onMounted(() => {
84
- query = window.matchMedia("(min-width: 640px)");
85
- onQuery();
86
- query.addEventListener("change", onQuery);
87
- });
88
- onBeforeUnmount(() => query?.removeEventListener("change", onQuery));
79
+ const wide = useMediaQuery("(min-width: 640px)");
89
80
  const shellSize = {
90
81
  sm: "h-(--size-control-sm) text-xs",
91
82
  md: "h-(--size-control-md) text-sm",
@@ -175,7 +166,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
175
166
  "is-disabled": __props.isDisabled,
176
167
  "is-date-unavailable": __props.isDateUnavailable,
177
168
  "maximum-days": __props.maximumDays,
178
- months: wide.value ? 2 : 1,
169
+ months: unref(wide) ? 2 : 1,
179
170
  locale: unref(locale),
180
171
  class: "border-0 bg-transparent",
181
172
  "onUpdate:modelValue": pick
@@ -1,6 +1,7 @@
1
1
  import { ref, defineComponent, computed, watch, onBeforeUnmount, useSlots, openBlock, createElementBlock, Fragment, withDirectives, createElementVNode, normalizeStyle, unref, normalizeClass, vShow, toDisplayString, createCommentVNode, renderSlot, createVNode, withCtx, mergeProps, createBlock, createTextVNode } from "vue";
2
2
  import { DrawerRoot, DrawerPortal, DrawerOverlay, DrawerContent, DrawerTitle, DrawerHandle, DrawerDescription, DrawerClose } from "vaul-vue";
3
3
  import { CLOSE_ICON } from "../components/dropdown.js";
4
+ import { prefersReducedMotion } from "../composables/easing.js";
4
5
  import { useGlass } from "../composables/useGlass.js";
5
6
  import { useLabels } from "../composables/useLpConfig.js";
6
7
  import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
@@ -169,9 +170,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
169
170
  }
170
171
  let track = null;
171
172
  const owner = /* @__PURE__ */ Symbol("edge-pull");
172
- function reducedMotion() {
173
- return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
174
- }
175
173
  function scrollsWithPull(target) {
176
174
  let el = target instanceof Element ? target : null;
177
175
  const pullsRight = dir.value === "left";
@@ -246,7 +244,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
246
244
  peek.value = 0;
247
245
  }, 60)
248
246
  );
249
- }, reducedMotion() ? 0 : 180)
247
+ }, prefersReducedMotion() ? 0 : 180)
250
248
  );
251
249
  } else {
252
250
  peek.value = 0;
@@ -287,7 +285,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
287
285
  const axis = dir.value === "right" ? off : -off;
288
286
  return {
289
287
  transform: `translateX(${axis}%)`,
290
- transition: peeking.value || reducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
288
+ transition: peeking.value || prefersReducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
291
289
  pointerEvents: "none"
292
290
  };
293
291
  });
@@ -296,7 +294,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
296
294
  __props.edgeOpen && isHorizontal.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
297
295
  withDirectives(createElementVNode("div", {
298
296
  class: normalizeClass(["lp-scrim pointer-events-none fixed inset-0", { "lp-scrim--light": unref(frosted), "lp-scrim--nested": unref(layer).nested }]),
299
- style: normalizeStyle({ zIndex: unref(layer).scrim, opacity: peek.value, transition: peeking.value || reducedMotion() ? "none" : "opacity 0.18s ease-out" })
297
+ style: normalizeStyle({ zIndex: unref(layer).scrim, opacity: peek.value, transition: peeking.value || unref(prefersReducedMotion)() ? "none" : "opacity 0.18s ease-out" })
300
298
  }, null, 6), [
301
299
  [vShow, peeking.value || peek.value > 0]
302
300
  ]),
@@ -0,0 +1,154 @@
1
+ import { defineComponent, computed, ref, shallowRef, watch, onMounted, onBeforeUnmount, openBlock, createBlock, Teleport, createElementBlock, Fragment, renderList, unref, normalizeStyle, createElementVNode, createVNode, withCtx, createCommentVNode, normalizeClass, toDisplayString, createTextVNode } from "vue";
2
+ import { PopoverRoot, PopoverAnchor, PopoverPortal, PopoverContent } from "reka-ui";
3
+ import { hints } from "../composables/useHints.js";
4
+ import { useLabels } from "../composables/useLpConfig.js";
5
+ import { PANEL_SURFACE, POPOVER_ANIM } from "../components/dropdown.js";
6
+ import { _ as _sfc_main$1 } from "./LpButton-BdfZFNqi.js";
7
+ const _hoisted_1 = ["aria-label", "onPointerenter", "onFocus", "onClick"];
8
+ const _hoisted_2 = { class: "flex items-center gap-2 font-medium" };
9
+ const _hoisted_3 = { class: "rounded-full bg-brand/15 px-1.5 text-[11px] text-brand" };
10
+ const _hoisted_4 = {
11
+ key: 0,
12
+ class: "mt-1.5 text-muted"
13
+ };
14
+ const _hoisted_5 = { class: "mt-3 flex justify-end" };
15
+ const _sfc_main = /* @__PURE__ */ defineComponent({
16
+ __name: "LpHintLayer",
17
+ props: {
18
+ fresh: {},
19
+ notes: {},
20
+ limit: { default: 3 },
21
+ labels: {}
22
+ },
23
+ emits: ["read"],
24
+ setup(__props, { emit: __emit }) {
25
+ const props = __props;
26
+ const emit = __emit;
27
+ const DEFAULT_LABELS = { gotIt: "Got it", badge: "New" };
28
+ const labels = useLabels("hintLayer", DEFAULT_LABELS, () => props.labels);
29
+ const OPEN_DELAY_MS = 350;
30
+ const shown = computed(() => props.fresh.filter((id) => hints.present(id) && props.notes[id]).slice(0, props.limit));
31
+ const spots = ref([]);
32
+ const openId = ref(null);
33
+ const anchor = shallowRef(null);
34
+ let frame = 0;
35
+ let timer;
36
+ function place() {
37
+ spots.value = shown.value.flatMap((id) => {
38
+ const el = hints.element(id);
39
+ if (!el) return [];
40
+ const r = el.getBoundingClientRect();
41
+ return r.width || r.height ? [{ id, x: r.right, y: r.top }] : [];
42
+ });
43
+ frame = shown.value.length ? requestAnimationFrame(place) : 0;
44
+ }
45
+ watch(
46
+ shown,
47
+ (ids) => {
48
+ if (ids.length && !frame) frame = requestAnimationFrame(place);
49
+ if (openId.value && !ids.includes(openId.value)) openId.value = null;
50
+ },
51
+ { immediate: true }
52
+ );
53
+ function reveal(id) {
54
+ clearTimeout(timer);
55
+ timer = setTimeout(() => {
56
+ if (!shown.value.includes(id)) return;
57
+ anchor.value = hints.element(id);
58
+ openId.value = anchor.value ? id : null;
59
+ }, OPEN_DELAY_MS);
60
+ }
61
+ function read() {
62
+ const id = openId.value;
63
+ openId.value = null;
64
+ if (id) emit("read", id);
65
+ }
66
+ let off = [];
67
+ onMounted(() => {
68
+ off = [
69
+ hints.on("hover", (id) => {
70
+ if (shown.value.includes(id)) reveal(id);
71
+ }),
72
+ hints.on("used", (id) => {
73
+ if (!shown.value.includes(id)) return;
74
+ clearTimeout(timer);
75
+ if (openId.value === id) openId.value = null;
76
+ emit("read", id);
77
+ })
78
+ ];
79
+ });
80
+ onBeforeUnmount(() => {
81
+ cancelAnimationFrame(frame);
82
+ clearTimeout(timer);
83
+ for (const stop of off) stop();
84
+ });
85
+ const note = computed(() => openId.value ? props.notes[openId.value] : void 0);
86
+ return (_ctx, _cache) => {
87
+ return openBlock(), createBlock(Teleport, { to: "body" }, [
88
+ (openBlock(true), createElementBlock(Fragment, null, renderList(spots.value, (spot) => {
89
+ return openBlock(), createElementBlock("button", {
90
+ key: spot.id,
91
+ type: "button",
92
+ class: "fixed z-(--z-popover) -translate-x-1/2 -translate-y-1/2 rounded-full p-1",
93
+ style: normalizeStyle({ left: `${spot.x}px`, top: `${spot.y}px` }),
94
+ "aria-label": `${unref(labels).badge}: ${__props.notes[spot.id]?.title ?? ""}`,
95
+ onPointerenter: ($event) => reveal(spot.id),
96
+ onFocus: ($event) => reveal(spot.id),
97
+ onClick: ($event) => reveal(spot.id)
98
+ }, [..._cache[1] || (_cache[1] = [
99
+ createElementVNode("span", { class: "relative flex size-2" }, [
100
+ createElementVNode("span", { class: "absolute inline-flex size-full animate-ping rounded-full bg-brand opacity-60 motion-reduce:animate-none" }),
101
+ createElementVNode("span", { class: "relative inline-flex size-2 rounded-full bg-brand ring-2 ring-surface" })
102
+ ], -1)
103
+ ])], 44, _hoisted_1);
104
+ }), 128)),
105
+ createVNode(unref(PopoverRoot), {
106
+ open: !!openId.value,
107
+ "onUpdate:open": _cache[0] || (_cache[0] = (value) => !value && (openId.value = null))
108
+ }, {
109
+ default: withCtx(() => [
110
+ anchor.value ? (openBlock(), createBlock(unref(PopoverAnchor), {
111
+ key: 0,
112
+ reference: anchor.value
113
+ }, null, 8, ["reference"])) : createCommentVNode("", true),
114
+ createVNode(unref(PopoverPortal), null, {
115
+ default: withCtx(() => [
116
+ createVNode(unref(PopoverContent), {
117
+ side: "bottom",
118
+ "side-offset": 8,
119
+ "collision-padding": 8,
120
+ class: normalizeClass([unref(PANEL_SURFACE), unref(POPOVER_ANIM), "z-(--z-popover) w-72 rounded-card p-3 text-sm text-ink outline-none"])
121
+ }, {
122
+ default: withCtx(() => [
123
+ createElementVNode("p", _hoisted_2, [
124
+ createElementVNode("span", _hoisted_3, toDisplayString(unref(labels).badge), 1),
125
+ createTextVNode(" " + toDisplayString(note.value?.title), 1)
126
+ ]),
127
+ note.value?.text ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString(note.value.text), 1)) : createCommentVNode("", true),
128
+ createElementVNode("div", _hoisted_5, [
129
+ createVNode(_sfc_main$1, {
130
+ size: "sm",
131
+ onClick: read
132
+ }, {
133
+ default: withCtx(() => [
134
+ createTextVNode(toDisplayString(unref(labels).gotIt), 1)
135
+ ]),
136
+ _: 1
137
+ })
138
+ ])
139
+ ]),
140
+ _: 1
141
+ }, 8, ["class"])
142
+ ]),
143
+ _: 1
144
+ })
145
+ ]),
146
+ _: 1
147
+ }, 8, ["open"])
148
+ ]);
149
+ };
150
+ }
151
+ });
152
+ export {
153
+ _sfc_main as _
154
+ };
@@ -1,10 +1,10 @@
1
1
  import { defineComponent, computed, ref, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementVNode, normalizeClass, createVNode, createCommentVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { useGlass } from "../composables/useGlass.js";
3
3
  import { useLabels } from "../composables/useLpConfig.js";
4
- import { _ as _sfc_main$2 } from "./LpButton-CKSwC6Mu.js";
4
+ import { _ as _sfc_main$2 } from "./LpButton-BdfZFNqi.js";
5
5
  import { _ as _sfc_main$3 } from "./LpIcon-DYtaid0K.js";
6
6
  import { _ as _sfc_main$1 } from "./LpModal-JNa3B_Lr.js";
7
- import { _ as _sfc_main$4 } from "./LpSidebar-C2oolgwy.js";
7
+ import { _ as _sfc_main$4 } from "./LpSidebar-hZ8D1cVr.js";
8
8
  const _hoisted_1 = { class: "flex items-center gap-2" };
9
9
  const _sfc_main = /* @__PURE__ */ defineComponent({
10
10
  __name: "LpModalNav",
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, useModel, getCurrentInstance, ref, onMounted, onBeforeUnmount, openBlock, createBlock, withCtx, createElementVNode, toDisplayString, createElementBlock, createCommentVNode, createVNode, Fragment, renderList, resolveDynamicComponent, normalizeClass, Transition, mergeModels } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
3
  import { _ as _sfc_main$8 } from "./LpBadge-Fe0D1bYx.js";
4
- import { _ as _sfc_main$7 } from "./LpButton-CKSwC6Mu.js";
4
+ import { _ as _sfc_main$7 } from "./LpButton-BdfZFNqi.js";
5
5
  import { _ as _sfc_main$5 } from "./LpContextMenu-EF_90u3g.js";
6
6
  import { _ as _sfc_main$3 } from "./LpEmptyState-BJbZXC8D.js";
7
7
  import { _ as _sfc_main$6 } from "./LpIcon-DYtaid0K.js";
@@ -7,7 +7,7 @@ import { _ as _sfc_main$1 } from "./LpIcon-DYtaid0K.js";
7
7
  import { _ as _sfc_main$4 } from "./LpNumberFlow-z4WPZcZu.js";
8
8
  import { _ as _sfc_main$5 } from "./LpRollingText-DB7upf-5.js";
9
9
  import { _ as _sfc_main$6 } from "./LpShift-DXNsauQZ.js";
10
- import { _ as _sfc_main$3 } from "./LpTooltip-CXVKcOXT.js";
10
+ import { _ as _sfc_main$3 } from "./LpTooltip-suFCM34F.js";
11
11
  import { useMergedAttrs } from "../composables/useMergedClass.js";
12
12
  import { useLabels } from "../composables/useLpConfig.js";
13
13
  const _hoisted_1 = {
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, ref, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, unref, normalizeClass, renderSlot, createCommentVNode, createBlock, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
2
2
  import { useLabels } from "../composables/useLpConfig.js";
3
- import { _ as _sfc_main$3 } from "./LpDrawer-CBFVD624.js";
3
+ import { _ as _sfc_main$3 } from "./LpDrawer-D9lLOC0C.js";
4
4
  import { _ as _sfc_main$1 } from "./LpInput-D-YGQbEv.js";
5
5
  import { _ as _sfc_main$2 } from "./LpSidebarNav-C9dHu-2b.js";
6
6
  const _hoisted_1 = ["aria-label"];
@@ -1,6 +1,7 @@
1
1
  import { defineComponent, computed, ref, useId, onMounted, watch, onBeforeUnmount, openBlock, createElementBlock, createElementVNode, toDisplayString, Fragment, renderList, normalizeStyle, normalizeClass, createBlock, unref, createCommentVNode, createTextVNode } from "vue";
2
2
  import { Motion } from "motion-v";
3
3
  import { useLabels } from "../composables/useLpConfig.js";
4
+ import { scrollParent } from "../composables/scroll.js";
4
5
  import { usePillTransition } from "../composables/usePillTransition.js";
5
6
  const _hoisted_1 = ["aria-label"];
6
7
  const _hoisted_2 = { class: "mb-2.5 text-[11px] font-semibold uppercase tracking-[0.08em] text-muted" };
@@ -63,18 +64,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
63
64
  rafId = null;
64
65
  safetyTimer = null;
65
66
  }
66
- function scrollerOf(el) {
67
- for (let node = el.parentElement; node; node = node.parentElement) {
68
- const { overflowY } = getComputedStyle(node);
69
- if (/(auto|scroll|overlay)/.test(overflowY) && node.scrollHeight > node.clientHeight) return node;
70
- }
71
- return null;
72
- }
73
67
  function suppressSpyUntilScrollEnds(target) {
74
68
  spySuppressed = true;
75
69
  if (rafId != null) cancelAnimationFrame(rafId);
76
70
  if (safetyTimer != null) clearTimeout(safetyTimer);
77
- const scroller = scrollerOf(target);
71
+ const scroller = scrollParent(target);
78
72
  const position = () => scroller ? scroller.scrollTop : window.scrollY;
79
73
  let last = position();
80
74
  const tick = () => {
@@ -2,6 +2,7 @@ import { defineComponent, ref, watch, onBeforeUnmount, openBlock, createBlock, r
2
2
  import { injectTooltipProviderContext, TooltipProvider, TooltipRoot, TooltipTrigger, TooltipPortal, TooltipContent } from "reka-ui";
3
3
  import { springStep, springSettled } from "../composables/usePillTransition.js";
4
4
  import { PANEL_SURFACE, TOOLTIP_ANIM } from "../components/dropdown.js";
5
+ import { prefersReducedMotion } from "../composables/easing.js";
5
6
  import '../assets/LpTooltip-B61SYd4K.css';const _sfc_main = /* @__PURE__ */ defineComponent({
6
7
  __name: "LpTooltip",
7
8
  props: {
@@ -18,7 +19,6 @@ import '../assets/LpTooltip-B61SYd4K.css';const _sfc_main = /* @__PURE__ */ defi
18
19
  const props = __props;
19
20
  const shared = injectTooltipProviderContext(null);
20
21
  const PassThrough = (_, { slots }) => slots.default?.();
21
- const reducedMotion = () => typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
22
22
  const target = { x: 0, y: 0 };
23
23
  const offset = ref({ x: 0, y: 0 });
24
24
  let sx = { value: 0, velocity: 0 };
@@ -52,7 +52,7 @@ import '../assets/LpTooltip-B61SYd4K.css';const _sfc_main = /* @__PURE__ */ defi
52
52
  const half = props.side === "top" || props.side === "bottom" ? rect.height / 2 : 0;
53
53
  const dir = props.side === "bottom" ? -1 : 1;
54
54
  target.y = e.clientY - (rect.top + rect.height / 2) + half * dir;
55
- if (reducedMotion()) {
55
+ if (prefersReducedMotion()) {
56
56
  offset.value = { x: target.x, y: target.y };
57
57
  return;
58
58
  }