@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
@@ -0,0 +1,98 @@
1
+ export interface TourContext {
2
+ /** Undo something when this step is left, in either direction (close the menu it opened). */
3
+ onLeave(cleanup: () => void): void;
4
+ /** Undo something when the tour ends, for what several steps share (a dialog they all live in). */
5
+ onEnd(cleanup: () => void): void;
6
+ }
7
+ export interface TourStep {
8
+ /**
9
+ * What to point at: a `v-hint` id, a CSS selector, an element or a getter.
10
+ * Left out, the step is a centred card with nothing highlighted (an intro).
11
+ * A target that never appears is skipped, so a step for a control the user
12
+ * cannot see costs nothing.
13
+ */
14
+ target?: string | Element | (() => Element | null);
15
+ title: string;
16
+ text?: string;
17
+ /** A shortcut for the same action, shown as keycaps ("Ctrl+K"). */
18
+ keys?: string;
19
+ side?: "top" | "right" | "bottom" | "left";
20
+ /**
21
+ * Ask the user to press the highlighted control themselves; the tour moves
22
+ * on when they do. Doing it beats reading about it.
23
+ */
24
+ try?: boolean;
25
+ /** With `try`, a "do it for me" button runs this instead. */
26
+ perform?: () => void | Promise<void>;
27
+ /** Runs before the step shows: open the dialog or tab its target lives in. */
28
+ enter?: (context: TourContext) => void | Promise<void>;
29
+ }
30
+ declare const _default: typeof __VLS_export;
31
+ export default _default;
32
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
33
+ steps: TourStep[];
34
+ /** Room around the target inside the lit hole, px. */
35
+ padding?: number;
36
+ labels?: Partial<{
37
+ next: string;
38
+ back: string;
39
+ done: string;
40
+ close: string;
41
+ progress: string;
42
+ perform: string;
43
+ tryIt: string;
44
+ }>;
45
+ } & {
46
+ open?: boolean;
47
+ step?: number;
48
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
49
+ "update:open": (value: boolean) => any;
50
+ "update:step": (value: number) => any;
51
+ } & {
52
+ dismiss: (index: number) => any;
53
+ finish: () => any;
54
+ }, string, import("vue").PublicProps, Readonly<{
55
+ steps: TourStep[];
56
+ /** Room around the target inside the lit hole, px. */
57
+ padding?: number;
58
+ labels?: Partial<{
59
+ next: string;
60
+ back: string;
61
+ done: string;
62
+ close: string;
63
+ progress: string;
64
+ perform: string;
65
+ tryIt: string;
66
+ }>;
67
+ } & {
68
+ open?: boolean;
69
+ step?: number;
70
+ }> & Readonly<{
71
+ "onUpdate:open"?: ((value: boolean) => any) | undefined;
72
+ onDismiss?: ((index: number) => any) | undefined;
73
+ "onUpdate:step"?: ((value: number) => any) | undefined;
74
+ onFinish?: (() => any) | undefined;
75
+ }>, {
76
+ padding: number;
77
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
78
+ card?: (props: {
79
+ step: TourStep | undefined;
80
+ index: number;
81
+ next: () => void;
82
+ back: () => void;
83
+ dismiss: () => void;
84
+ }) => any;
85
+ } & {
86
+ card?: (props: {
87
+ step: TourStep | undefined;
88
+ index: number;
89
+ next: () => void;
90
+ back: () => void;
91
+ dismiss: () => void;
92
+ }) => any;
93
+ }>;
94
+ type __VLS_WithSlots<T, S> = T & {
95
+ new (): {
96
+ $slots: S;
97
+ };
98
+ };
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpTour-C1TC2GyY.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpUptimeBar-ugcv25xj.js";
1
+ import { _ as _sfc_main } from "../chunks/LpUptimeBar-B4ltCfno.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -0,0 +1,28 @@
1
+ import type { TourStep } from "./LpTour.vue";
2
+ type __VLS_Props = {
3
+ step: TourStep | undefined;
4
+ index: number;
5
+ last: boolean;
6
+ progress: string;
7
+ labels: {
8
+ next: string;
9
+ back: string;
10
+ done: string;
11
+ close: string;
12
+ perform: string;
13
+ tryIt: string;
14
+ };
15
+ };
16
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
+ next: () => any;
18
+ dismiss: () => any;
19
+ back: () => any;
20
+ perform: () => any;
21
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
22
+ onNext?: (() => any) | undefined;
23
+ onDismiss?: (() => any) | undefined;
24
+ onBack?: (() => any) | undefined;
25
+ onPerform?: (() => any) | undefined;
26
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
27
+ declare const _default: typeof __VLS_export;
28
+ export default _default;
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/TourCard-BQS3ztqj.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -0,0 +1,21 @@
1
+ import type { Box } from "../composables/spatialNav";
2
+ type Size = {
3
+ width: number;
4
+ height: number;
5
+ };
6
+ /** The hole around a target: padded, then clipped to the viewport so a tall
7
+ * target never pushes the dim panels off-screen. */
8
+ export declare function holeOf(target: Box, padding: number, viewport: Size): Box;
9
+ /** The four dim panels around a hole: top, bottom, left, right. */
10
+ export declare function panelsAround(hole: Box, viewport: Size): Box[];
11
+ /**
12
+ * Open the context menu of an element as a right-click would, at its centre.
13
+ * Lets a tour step show a row's menu without the user hunting for it; the menu
14
+ * itself is whatever the app already wires to that element.
15
+ *
16
+ * Returns the way to close it again: a press outside the menu, which every
17
+ * dismissable layer honours. Not Escape — that key also closes whatever is on
18
+ * top, a tour included, and would end the walk instead of the menu.
19
+ */
20
+ export declare function openContextMenu(el: Element): () => void;
21
+ export {};
@@ -0,0 +1,38 @@
1
+ function holeOf(target, padding, viewport) {
2
+ const x = Math.max(0, target.x - padding);
3
+ const y = Math.max(0, target.y - padding);
4
+ const right = Math.min(viewport.width, target.x + target.width + padding);
5
+ const bottom = Math.min(viewport.height, target.y + target.height + padding);
6
+ return { x, y, width: Math.max(0, right - x), height: Math.max(0, bottom - y) };
7
+ }
8
+ function panelsAround(hole, viewport) {
9
+ const bottom = hole.y + hole.height;
10
+ return [
11
+ { x: 0, y: 0, width: viewport.width, height: hole.y },
12
+ { x: 0, y: bottom, width: viewport.width, height: Math.max(0, viewport.height - bottom) },
13
+ { x: 0, y: hole.y, width: hole.x, height: hole.height },
14
+ { x: hole.x + hole.width, y: hole.y, width: Math.max(0, viewport.width - hole.x - hole.width), height: hole.height }
15
+ ];
16
+ }
17
+ function openContextMenu(el) {
18
+ const box = el.getBoundingClientRect();
19
+ el.dispatchEvent(
20
+ new MouseEvent("contextmenu", {
21
+ bubbles: true,
22
+ cancelable: true,
23
+ button: 2,
24
+ clientX: box.left + box.width / 2,
25
+ clientY: box.top + box.height / 2
26
+ })
27
+ );
28
+ return () => {
29
+ const outside = { bubbles: true, cancelable: true, clientX: 0, clientY: 0 };
30
+ document.body.dispatchEvent(new PointerEvent("pointerdown", outside));
31
+ document.body.dispatchEvent(new PointerEvent("pointerup", outside));
32
+ };
33
+ }
34
+ export {
35
+ holeOf,
36
+ openContextMenu,
37
+ panelsAround
38
+ };
@@ -0,0 +1,17 @@
1
+ export interface ScrollParentOptions {
2
+ /** Which overflow counts: vertical (default), horizontal, or either. */
3
+ axis?: "y" | "x" | "both";
4
+ /** Start the search at the element itself rather than its parent. */
5
+ self?: boolean;
6
+ }
7
+ /** The nearest ancestor that scrolls along `axis` and has something to scroll, or null. */
8
+ export declare function scrollParent(el: Element | null | undefined, { axis, self }?: ScrollParentOptions): HTMLElement | null;
9
+ export interface ScrollToElementOptions {
10
+ /** `nearest` scrolls only when the element is out of view. */
11
+ block?: "nearest" | "center" | "start";
12
+ behavior?: ScrollBehavior;
13
+ /** Room kept between the element and the scroller's edge. */
14
+ margin?: number;
15
+ }
16
+ /** Scroll the nearest scroller, and only it, so that `el` is in view. */
17
+ export declare function scrollToElement(el: Element | null | undefined, { block, behavior, margin }?: ScrollToElementOptions): void;
@@ -0,0 +1,29 @@
1
+ const SCROLLS = /(auto|scroll|overlay)/;
2
+ function scrollParent(el, { axis = "y", self = false } = {}) {
3
+ for (let node = self ? el : el?.parentElement; node; node = node.parentElement) {
4
+ if (node === document.body || node === document.documentElement) return null;
5
+ const style = getComputedStyle(node);
6
+ const y = axis !== "x" && SCROLLS.test(style.overflowY) && node.scrollHeight > node.clientHeight;
7
+ const x = axis !== "y" && SCROLLS.test(style.overflowX) && node.scrollWidth > node.clientWidth;
8
+ if (y || x) return node;
9
+ }
10
+ return null;
11
+ }
12
+ function scrollToElement(el, { block = "nearest", behavior = "auto", margin = 0 } = {}) {
13
+ if (!el) return;
14
+ const box = scrollParent(el) ?? document.scrollingElement;
15
+ if (!box) return;
16
+ const outer = box === document.scrollingElement ? new DOMRect(0, 0, window.innerWidth, window.innerHeight) : box.getBoundingClientRect();
17
+ const inner = el.getBoundingClientRect();
18
+ const top = inner.top - outer.top + box.scrollTop;
19
+ let target = box.scrollTop;
20
+ if (block === "start") target = top - margin;
21
+ else if (block === "center") target = top - (box.clientHeight - inner.height) / 2;
22
+ else if (inner.top < outer.top + margin) target = top - margin;
23
+ else if (inner.bottom > outer.bottom - margin) target = top - box.clientHeight + inner.height + margin;
24
+ if (target !== box.scrollTop) box.scrollTo({ top: target, behavior });
25
+ }
26
+ export {
27
+ scrollParent,
28
+ scrollToElement
29
+ };
@@ -1,5 +1,6 @@
1
1
  import { ref, onMounted, onBeforeUnmount, toValue } from "vue";
2
2
  import { focusableIn, nextInDirection } from "./spatialNav.js";
3
+ import { scrollParent } from "./scroll.js";
3
4
  const BUTTON = { a: 0, b: 1, lb: 4, rb: 5, start: 9, up: 12, down: 13, left: 14, right: 15 };
4
5
  const STICK_THRESHOLD = 0.55;
5
6
  const REPEAT_DELAY_MS = 380;
@@ -76,13 +77,7 @@ function useGamepadNav(options = {}) {
76
77
  }
77
78
  }
78
79
  function scrollBy(dx, dy) {
79
- let element = document.activeElement;
80
- while (element && element !== document.body) {
81
- const style = getComputedStyle(element);
82
- if (/(auto|scroll)/.test(style.overflowY + style.overflowX) && element.scrollHeight > element.clientHeight) break;
83
- element = element.parentElement;
84
- }
85
- (element && element !== document.body ? element : document.scrollingElement)?.scrollBy(dx, dy);
80
+ (scrollParent(document.activeElement, { axis: "both", self: true }) ?? document.scrollingElement)?.scrollBy(dx, dy);
86
81
  }
87
82
  function poll(now) {
88
83
  frame = requestAnimationFrame(poll);
@@ -0,0 +1,18 @@
1
+ import { type Directive } from "vue";
2
+ export type HintEvent = "seen" | "used" | "hover";
3
+ type Listener = (id: string, el: HTMLElement) => void;
4
+ /** `v-hint="'page.feature'"` on the element that represents a feature. */
5
+ export declare const vHint: Directive<HTMLElement, string | null | undefined>;
6
+ export declare const hints: {
7
+ /** Ids of everything hinted that is in the DOM now. Reactive. */
8
+ ids(): string[];
9
+ /** Whether an element with this id is in the DOM. Reactive. */
10
+ present(id: string): boolean;
11
+ /** The first connected element with this id that has a box, or null. */
12
+ element(id: string): HTMLElement | null;
13
+ /** Subscribe to a signal; returns the unsubscribe. */
14
+ on(event: HintEvent, listener: Listener): () => void;
15
+ /** Report use that is not a click on the element. */
16
+ use(id: string): void;
17
+ };
18
+ export {};
@@ -0,0 +1,120 @@
1
+ import { shallowReactive } from "vue";
2
+ const SEEN_MS = 800;
3
+ const SEEN_RATIO = 0.5;
4
+ const mounted = shallowReactive(/* @__PURE__ */ new Map());
5
+ const listeners = { seen: /* @__PURE__ */ new Set(), used: /* @__PURE__ */ new Set(), hover: /* @__PURE__ */ new Set() };
6
+ const idOf = /* @__PURE__ */ new WeakMap();
7
+ const timers = /* @__PURE__ */ new WeakMap();
8
+ const seenOnce = /* @__PURE__ */ new WeakSet();
9
+ function emit(event, id, el) {
10
+ for (const listener of listeners[event]) listener(id, el);
11
+ }
12
+ let observer = null;
13
+ function watchSight(el) {
14
+ if (typeof IntersectionObserver === "undefined") {
15
+ seenOnce.add(el);
16
+ emit("seen", idOf.get(el), el);
17
+ return;
18
+ }
19
+ observer ??= new IntersectionObserver(
20
+ (entries) => {
21
+ for (const entry of entries) {
22
+ const target = entry.target;
23
+ const id = idOf.get(target);
24
+ if (!id || seenOnce.has(target)) continue;
25
+ clearTimeout(timers.get(target));
26
+ if (entry.intersectionRatio >= SEEN_RATIO) {
27
+ timers.set(
28
+ target,
29
+ setTimeout(() => {
30
+ seenOnce.add(target);
31
+ observer?.unobserve(target);
32
+ emit("seen", id, target);
33
+ }, SEEN_MS)
34
+ );
35
+ }
36
+ }
37
+ },
38
+ { threshold: [0, SEEN_RATIO] }
39
+ );
40
+ observer.observe(el);
41
+ }
42
+ function onClick(event) {
43
+ const el = event.currentTarget;
44
+ const id = idOf.get(el);
45
+ if (id) emit("used", id, el);
46
+ }
47
+ function onHover(event) {
48
+ const el = event.currentTarget;
49
+ const id = idOf.get(el);
50
+ if (id) emit("hover", id, el);
51
+ }
52
+ function attach(el, id) {
53
+ idOf.set(el, id);
54
+ el.dataset.hint = id;
55
+ const set = mounted.get(id) ?? /* @__PURE__ */ new Set();
56
+ set.add(el);
57
+ mounted.set(id, new Set(set));
58
+ el.addEventListener("click", onClick, { capture: true });
59
+ el.addEventListener("pointerenter", onHover);
60
+ el.addEventListener("focusin", onHover);
61
+ watchSight(el);
62
+ }
63
+ function detach(el) {
64
+ const id = idOf.get(el);
65
+ if (!id) return;
66
+ clearTimeout(timers.get(el));
67
+ observer?.unobserve(el);
68
+ el.removeEventListener("click", onClick, { capture: true });
69
+ el.removeEventListener("pointerenter", onHover);
70
+ el.removeEventListener("focusin", onHover);
71
+ const set = new Set(mounted.get(id));
72
+ set.delete(el);
73
+ if (set.size) mounted.set(id, set);
74
+ else mounted.delete(id);
75
+ idOf.delete(el);
76
+ }
77
+ const vHint = {
78
+ mounted(el, binding) {
79
+ if (binding.value) attach(el, binding.value);
80
+ },
81
+ updated(el, binding) {
82
+ if (binding.value === idOf.get(el)) return;
83
+ detach(el);
84
+ if (binding.value) attach(el, binding.value);
85
+ },
86
+ beforeUnmount(el) {
87
+ detach(el);
88
+ }
89
+ };
90
+ const hints = {
91
+ /** Ids of everything hinted that is in the DOM now. Reactive. */
92
+ ids() {
93
+ return [...mounted.keys()];
94
+ },
95
+ /** Whether an element with this id is in the DOM. Reactive. */
96
+ present(id) {
97
+ return mounted.has(id);
98
+ },
99
+ /** The first connected element with this id that has a box, or null. */
100
+ element(id) {
101
+ for (const el of mounted.get(id) ?? []) {
102
+ if (el.isConnected && el.getClientRects().length) return el;
103
+ }
104
+ return null;
105
+ },
106
+ /** Subscribe to a signal; returns the unsubscribe. */
107
+ on(event, listener) {
108
+ listeners[event].add(listener);
109
+ return () => listeners[event].delete(listener);
110
+ },
111
+ /** Report use that is not a click on the element. */
112
+ use(id) {
113
+ const el = hints.element(id);
114
+ for (const listener of listeners.used) listener(id, el ?? document.body);
115
+ }
116
+ };
117
+ export {
118
+ hints,
119
+ vHint
120
+ };
@@ -0,0 +1,2 @@
1
+ import { type Ref } from "vue";
2
+ export declare function useMediaQuery(query: string): Ref<boolean>;
@@ -0,0 +1,18 @@
1
+ import { ref, onMounted, onBeforeUnmount } from "vue";
2
+ function useMediaQuery(query) {
3
+ const matches = ref(false);
4
+ let list;
5
+ const update = () => {
6
+ matches.value = !!list?.matches;
7
+ };
8
+ onMounted(() => {
9
+ list = window.matchMedia?.(query);
10
+ update();
11
+ list?.addEventListener("change", update);
12
+ });
13
+ onBeforeUnmount(() => list?.removeEventListener("change", update));
14
+ return matches;
15
+ }
16
+ export {
17
+ useMediaQuery
18
+ };
@@ -1,16 +1,10 @@
1
1
  import { ref, toValue, onMounted, onBeforeUnmount, readonly } from "vue";
2
2
  import { useHotkeys } from "./useHotkeys.js";
3
+ import { scrollParent } from "./scroll.js";
3
4
  const HOLD_MS = 320;
4
5
  const SLOP_PX = 8;
5
6
  const EDGE_PX = 40;
6
7
  const SCROLL_STEP = 14;
7
- function scrollParent(el) {
8
- for (let node = el?.parentElement; node; node = node.parentElement) {
9
- const { overflowY } = getComputedStyle(node);
10
- if ((overflowY === "auto" || overflowY === "scroll") && node.scrollHeight > node.clientHeight) return node;
11
- }
12
- return null;
13
- }
14
8
  function usePaintSelect(options) {
15
9
  const resolve = options.resolve ?? ((raw) => raw);
16
10
  let stroke = null;
package/dist/index.d.ts CHANGED
@@ -157,6 +157,11 @@ export { default as LpThemeSwitcher } from "./components/LpThemeSwitcher.vue";
157
157
  export { default as LpTilt } from "./components/LpTilt.vue";
158
158
  export { default as LpToaster } from "./components/LpToaster.vue";
159
159
  export { default as LpTooltip } from "./components/LpTooltip.vue";
160
+ export { default as LpTour } from "./components/LpTour.vue";
161
+ export type { TourContext, TourStep } from "./components/LpTour.vue";
162
+ export { holeOf, openContextMenu, panelsAround } from "./components/tour";
163
+ export { default as LpHintLayer } from "./components/LpHintLayer.vue";
164
+ export type { HintNote } from "./components/LpHintLayer.vue";
160
165
  export { default as LpTooltipProvider } from "./components/LpTooltipProvider.vue";
161
166
  export { default as LpTopologyCanvas } from "./components/LpTopologyCanvas.vue";
162
167
  export type { CanvasViewport, EdgeCategory, EdgeObserved, TopologyEdge, TopologyLane, TopologyNode, } from "./components/LpTopologyCanvas.vue";
@@ -205,6 +210,11 @@ export type { FieldContext, FieldControlState } from "./composables/useField";
205
210
  export type { UseInputFilterOptions } from "./composables/useInputFilter";
206
211
  export { easeOut, easeTravel, easingToken, prefersReducedMotion } from "./composables/easing";
207
212
  export { useReveal } from "./composables/useReveal";
213
+ export { hints, vHint } from "./composables/useHints";
214
+ export type { HintEvent } from "./composables/useHints";
215
+ export { scrollParent, scrollToElement } from "./composables/scroll";
216
+ export type { ScrollParentOptions, ScrollToElementOptions } from "./composables/scroll";
217
+ export { useMediaQuery } from "./composables/useMediaQuery";
208
218
  export type { UseReveal, UseRevealOptions } from "./composables/useReveal";
209
219
  export { useAsyncTask } from "./composables/useAsyncTask";
210
220
  export { useRovingFocus } from "./composables/useRovingFocus";