@leavepulse/ui 0.93.3 → 0.94.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 (76) hide show
  1. package/README.md +53 -3
  2. package/dist/assets/LpTabs-D7EyG21l.css +18 -0
  3. package/dist/assets/LpVideoTrim-D8JDg9Qn.css +8 -0
  4. package/dist/chunks/AppShellContextNav-BoH4ffnC.js +54 -0
  5. package/dist/chunks/{LpAppShell-BWaVRsDB.js → LpAppShell-xEBmGV78.js} +12 -5
  6. package/dist/chunks/{LpDateRangePicker-CAn2yGbn.js → LpDateRangePicker-DRBB8z6t.js} +1 -1
  7. package/dist/chunks/{LpDrawer-iRHwJOGn.js → LpDrawer-BkWjFdV0.js} +81 -42
  8. package/dist/chunks/{LpLightbox-a24V_0Wk.js → LpLightbox-DPgz7IsT.js} +62 -49
  9. package/dist/chunks/{LpMapPicker-DVfiy3rR.js → LpMapPicker-V0Lybi10.js} +19 -52
  10. package/dist/chunks/{LpMediaGallery-CtWA5Uab.js → LpMediaGallery-CTejKF2-.js} +1 -1
  11. package/dist/chunks/{LpModalNav-DivqT1NV.js → LpModalNav-DVXAF-DG.js} +1 -1
  12. package/dist/chunks/{LpRangeCalendar-C9gkNMYw.js → LpRangeCalendar-9BvaLZji.js} +3 -4
  13. package/dist/chunks/LpScrollStrip-D4tsOfAQ.js +78 -0
  14. package/dist/chunks/{LpSidebar-DQtCYlT4.js → LpSidebar-BIhusppV.js} +1 -1
  15. package/dist/component-names.d.ts +1 -1
  16. package/dist/components/AppShellContextNav.vue.js +1 -1
  17. package/dist/components/LpAppShell.vue.js +1 -1
  18. package/dist/components/LpChart.vue.js +3 -3
  19. package/dist/components/LpDateRangePicker.vue.js +1 -1
  20. package/dist/components/LpDrawer.vue.d.ts +10 -2
  21. package/dist/components/LpDrawer.vue.js +1 -1
  22. package/dist/components/LpDropzone.vue.d.ts +1 -1
  23. package/dist/components/LpLightbox.vue.js +1 -1
  24. package/dist/components/LpMapPicker.vue.js +1 -1
  25. package/dist/components/LpMediaGallery.vue.js +1 -1
  26. package/dist/components/LpModalNav.vue.js +1 -1
  27. package/dist/components/LpRangeCalendar.vue.js +1 -1
  28. package/dist/components/LpRevealOnScroll.vue.d.ts +1 -1
  29. package/dist/components/LpScrollStrip.vue.d.ts +48 -0
  30. package/dist/components/LpScrollStrip.vue.js +4 -0
  31. package/dist/components/LpSidebar.vue.js +1 -1
  32. package/dist/components/LpTabs.vue.d.ts +4 -4
  33. package/dist/components/LpTabs.vue.js +104 -90
  34. package/dist/components/LpVideoTrim.vue.js +41 -54
  35. package/dist/composables/gesture.d.ts +141 -0
  36. package/dist/composables/gesture.js +176 -0
  37. package/dist/composables/scroll.d.ts +5 -1
  38. package/dist/composables/scroll.js +16 -9
  39. package/dist/composables/useAppUpdate.d.ts +26 -0
  40. package/dist/composables/useAppUpdate.js +91 -0
  41. package/dist/composables/useGesture.d.ts +107 -0
  42. package/dist/composables/useGesture.js +281 -0
  43. package/dist/composables/usePaintSelect.js +3 -4
  44. package/dist/composables/useScrollStrip.d.ts +27 -0
  45. package/dist/composables/useScrollStrip.js +194 -0
  46. package/dist/composables/useTouchHold.d.ts +3 -1
  47. package/dist/composables/useTouchHold.js +9 -51
  48. package/dist/composables/useZoomPan.js +4 -6
  49. package/dist/index.d.ts +9 -0
  50. package/dist/index.js +180 -152
  51. package/package.json +1 -1
  52. package/src/component-names.ts +1 -0
  53. package/src/components/AppShellContextNav.vue +3 -2
  54. package/src/components/LpAppShell.vue +10 -1
  55. package/src/components/LpChart.vue +2 -2
  56. package/src/components/LpDrawer.vue +121 -54
  57. package/src/components/LpLightbox.vue +33 -20
  58. package/src/components/LpMapPicker.vue +22 -64
  59. package/src/components/LpRangeCalendar.vue +4 -5
  60. package/src/components/LpScrollStrip.vue +96 -0
  61. package/src/components/LpSidebar.vue +2 -2
  62. package/src/components/LpTabs.vue +36 -17
  63. package/src/components/LpVideoTrim.vue +35 -46
  64. package/src/composables/gesture.ts +314 -0
  65. package/src/composables/scroll.ts +20 -9
  66. package/src/composables/useAppUpdate.ts +156 -0
  67. package/src/composables/useGesture.ts +451 -0
  68. package/src/composables/usePaintSelect.ts +4 -5
  69. package/src/composables/useScrollStrip.ts +249 -0
  70. package/src/composables/useTouchHold.ts +15 -73
  71. package/src/composables/useZoomPan.ts +6 -9
  72. package/src/index.ts +27 -0
  73. package/src/tokens/effects.css +40 -0
  74. package/dist/assets/LpTabs-Bh7a-Bdl.css +0 -18
  75. package/dist/assets/LpVideoTrim-CMoUw3dJ.css +0 -8
  76. package/dist/chunks/AppShellContextNav-C6Ho2mof.js +0 -49
@@ -0,0 +1,48 @@
1
+ type __VLS_Props = {
2
+ /** Classes for the scrolling row (layout, gap, padding, snap). */
3
+ contentClass?: unknown;
4
+ /** Fade the edges that have items beyond them. */
5
+ fade?: boolean;
6
+ /** Previous/next buttons: `auto` shows them on hover where the pointer is fine (a mouse), true on any device, false never. */
7
+ buttons?: boolean | "auto";
8
+ /** Map a vertical wheel to sideways scrolling while the row can move. */
9
+ wheel?: boolean;
10
+ /** Drag to scroll with a mouse or pen. */
11
+ drag?: boolean;
12
+ /** The row element's tag. */
13
+ as?: string;
14
+ /** Size to the content up to the parent's width (inline-flex), rather than fill the line. */
15
+ inline?: boolean;
16
+ };
17
+ declare var __VLS_9: {};
18
+ type __VLS_Slots = {} & {
19
+ default?: (props: typeof __VLS_9) => any;
20
+ };
21
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
22
+ scroller: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
23
+ page: (direction: -1 | 1) => void;
24
+ scrollBy: (px: number) => void;
25
+ reveal: (el: Element | null | undefined, options?: {
26
+ smooth?: boolean;
27
+ block?: "nearest" | "center";
28
+ }) => void;
29
+ measure: () => void;
30
+ overflowing: import("vue").Ref<boolean, boolean>;
31
+ atStart: import("vue").Ref<boolean, boolean>;
32
+ atEnd: import("vue").Ref<boolean, boolean>;
33
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
34
+ drag: boolean;
35
+ wheel: boolean;
36
+ inline: boolean;
37
+ as: string;
38
+ fade: boolean;
39
+ buttons: boolean | "auto";
40
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
41
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
42
+ declare const _default: typeof __VLS_export;
43
+ export default _default;
44
+ type __VLS_WithSlots<T, S> = T & {
45
+ new (): {
46
+ $slots: S;
47
+ };
48
+ };
@@ -0,0 +1,4 @@
1
+ import { _ as _sfc_main } from "../chunks/LpScrollStrip-D4tsOfAQ.js";
2
+ export {
3
+ _sfc_main as default
4
+ };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebar-DQtCYlT4.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebar-BIhusppV.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -61,13 +61,13 @@ type __VLS_Props = {
61
61
  */
62
62
  history?: boolean;
63
63
  };
64
- declare var __VLS_67: {
64
+ declare var __VLS_77: {
65
65
  value: string;
66
- }, __VLS_69: {};
66
+ }, __VLS_79: {};
67
67
  type __VLS_Slots = {} & {
68
- panel?: (props: typeof __VLS_67) => any;
68
+ panel?: (props: typeof __VLS_77) => any;
69
69
  } & {
70
- default?: (props: typeof __VLS_69) => any;
70
+ default?: (props: typeof __VLS_79) => any;
71
71
  };
72
72
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
73
73
  "update:modelValue": (value: string) => any;
@@ -1,17 +1,17 @@
1
- import { defineComponent, computed, ref, watch, nextTick, useId, openBlock, createBlock, unref, withCtx, createVNode, normalizeClass, createElementBlock, Fragment, renderList, createCommentVNode, renderSlot } from "vue";
1
+ import { defineComponent, computed, ref, watch, nextTick, useId, openBlock, createBlock, unref, withCtx, resolveDynamicComponent, mergeProps, createVNode, normalizeClass, createElementBlock, Fragment, renderList, createCommentVNode, renderSlot } from "vue";
2
2
  import { isHTMLElement } from "../composables/realm.js";
3
3
  import { usePaletteScope, usePaletteCommands } from "../composables/usePaletteCommands.js";
4
4
  import { Motion } from "motion-v";
5
5
  import { TabsRoot, TabsList, TabsTrigger, TabsContent } from "reka-ui";
6
6
  import { usePillTransition } from "../composables/usePillTransition.js";
7
7
  import { useStepHistory } from "../composables/useOverlayHistory.js";
8
- import { prefersReducedMotion } from "../composables/easing.js";
9
- import { _ as _sfc_main$1 } from "../chunks/LpContextMenu-DEFirphS.js";
10
- import { _ as _sfc_main$3 } from "../chunks/LpSkeleton-leTl53V9.js";
11
- import { _ as _sfc_main$2 } from "../chunks/LpTabFace-uoZgD_SN.js";
8
+ import { _ as _sfc_main$2 } from "../chunks/LpContextMenu-DEFirphS.js";
9
+ import { _ as _sfc_main$4 } from "../chunks/LpSkeleton-leTl53V9.js";
10
+ import { _ as _sfc_main$3 } from "../chunks/LpTabFace-uoZgD_SN.js";
11
+ import { _ as _sfc_main$1 } from "../chunks/LpScrollStrip-D4tsOfAQ.js";
12
12
  import { pickTab, BOTTOM_BAR, BOTTOM_ITEM, BOTTOM_MARKER } from "./tabs.js";
13
13
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
14
- import '../assets/LpTabs-Bh7a-Bdl.css';const _sfc_main = /* @__PURE__ */ defineComponent({
14
+ import '../assets/LpTabs-D7EyG21l.css';const _sfc_main = /* @__PURE__ */ defineComponent({
15
15
  __name: "LpTabs",
16
16
  props: {
17
17
  modelValue: {},
@@ -44,19 +44,21 @@ import '../assets/LpTabs-Bh7a-Bdl.css';const _sfc_main = /* @__PURE__ */ defineC
44
44
  },
45
45
  { deep: true }
46
46
  );
47
+ const strip = ref(null);
48
+ const Passthrough = (_, { slots }) => slots.default?.();
47
49
  const list = ref(null);
48
50
  watch(current, () => {
49
- void nextTick(() => {
50
- const row = list.value?.$el;
51
- const active = row?.querySelector('[data-state="active"]');
52
- if (!row || !active || row.scrollWidth <= row.clientWidth) return;
53
- const start = active.offsetLeft - row.offsetLeft;
54
- const end = start + active.offsetWidth;
55
- if (start >= row.scrollLeft && end <= row.scrollLeft + row.clientWidth) return;
56
- const left = start < row.scrollLeft ? start : end - row.clientWidth;
57
- row.scrollTo({ left, behavior: prefersReducedMotion() ? "auto" : "smooth" });
58
- });
51
+ void nextTick(() => strip.value?.reveal(list.value?.$el?.querySelector('[data-state="active"]')));
59
52
  });
53
+ function holdMousePick(event) {
54
+ if (event.button !== 0 || event.ctrlKey || bottom.value) return;
55
+ if (!isHTMLElement(event.target) || !event.target.closest('[role="tab"]')) return;
56
+ event.stopPropagation();
57
+ event.preventDefault();
58
+ }
59
+ function clickPick(item) {
60
+ if (!item.disabled && !bottom.value) select(item.value);
61
+ }
60
62
  function show(value) {
61
63
  if (props.modelValue === void 0) current.value = value;
62
64
  emit("update:modelValue", value);
@@ -99,86 +101,98 @@ import '../assets/LpTabs-Bh7a-Bdl.css';const _sfc_main = /* @__PURE__ */ defineC
99
101
  "onUpdate:modelValue": _cache[1] || (_cache[1] = (v) => select(v))
100
102
  }, {
101
103
  default: withCtx(() => [
102
- createVNode(unref(TabsList), {
103
- ref_key: "list",
104
- ref: list,
105
- "aria-label": __props.label,
106
- class: normalizeClass(bottom.value ? unref(BOTTOM_BAR) : [
107
- "max-w-full gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
108
- __props.block ? "flex w-full" : "inline-flex",
109
- __props.variant === "contained" ? "rounded-control border border-line bg-surface-soft p-1" : "",
110
- __props.variant === "underline" ? "border-b border-line" : ""
111
- ]),
112
- onPointerleave: _cache[0] || (_cache[0] = ($event) => hovered.value = null)
113
- }, {
104
+ (openBlock(), createBlock(resolveDynamicComponent(bottom.value ? Passthrough : _sfc_main$1), mergeProps({
105
+ ref_key: "strip",
106
+ ref: strip
107
+ }, bottom.value ? {} : {
108
+ inline: !__props.block,
109
+ class: __props.variant === "contained" ? "rounded-control border border-line bg-surface-soft" : void 0,
110
+ contentClass: __props.variant === "contained" ? "p-1" : void 0
111
+ }), {
114
112
  default: withCtx(() => [
115
- (openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
116
- return openBlock(), createBlock(_sfc_main$1, {
117
- key: item.value,
118
- items: item.menu ?? [],
119
- title: item.label,
120
- "title-icon": item.icon
121
- }, {
122
- default: withCtx(() => [
123
- createVNode(unref(TabsTrigger), {
124
- value: item.value,
125
- disabled: item.disabled,
126
- title: __props.compact && item.icon && !bottom.value ? item.label : void 0,
127
- class: normalizeClass(["relative inline-flex items-center justify-center outline-none transition-[color,opacity] duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[disabled]:cursor-not-allowed data-[disabled]:opacity-45", [
128
- bottom.value ? unref(BOTTOM_ITEM) : "shrink-0 gap-1.5 whitespace-nowrap text-sm",
129
- __props.block && !bottom.value ? "flex-1" : "",
130
- __props.variant === "underline" ? "rounded-t-inner px-3 pt-1.5 pb-2" : "",
131
- __props.variant === "contained" || __props.variant === "plain" ? "rounded-inner px-3 py-1.5" : "",
132
- __props.accent ? "data-[state=active]:text-brand" : "data-[state=active]:text-ink",
133
- hovered.value === item.value && !bottom.value ? "text-ink" : !__props.hoverFollow && !bottom.value ? "text-muted hover:text-ink" : "text-muted"
134
- ]]),
135
- onPointerenter: ($event) => __props.hoverFollow && !item.disabled && (hovered.value = item.value),
136
- onPointerup: releaseFocusAfterPointer
113
+ createVNode(unref(TabsList), {
114
+ ref_key: "list",
115
+ ref: list,
116
+ "aria-label": __props.label,
117
+ class: normalizeClass(bottom.value ? unref(BOTTOM_BAR) : [
118
+ "flex w-max min-w-full gap-1",
119
+ __props.variant === "underline" ? "border-b border-line" : ""
120
+ ]),
121
+ onPointerleave: _cache[0] || (_cache[0] = ($event) => hovered.value = null),
122
+ onMousedownCapture: holdMousePick
123
+ }, {
124
+ default: withCtx(() => [
125
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item) => {
126
+ return openBlock(), createBlock(_sfc_main$2, {
127
+ key: item.value,
128
+ items: item.menu ?? [],
129
+ title: item.label,
130
+ "title-icon": item.icon
137
131
  }, {
138
132
  default: withCtx(() => [
139
- pillUnder(item.value, current.value) ? (openBlock(), createBlock(unref(Motion), {
140
- key: 0,
141
- "layout-id": pillId,
142
- transition: unref(pillTransition),
143
- class: normalizeClass(__props.variant === "underline" ? [
144
- "absolute inset-x-0 -bottom-px z-0 h-0.5 rounded-full",
145
- __props.accent ? "bg-brand" : "bg-ink"
146
- ] : bottom.value ? [unref(BOTTOM_MARKER), __props.accent ? "bg-brand" : "bg-ink"] : [
147
- "absolute inset-0 z-0 rounded-inner shadow-sm",
148
- __props.accent ? "border border-brand/35 bg-brand/12" : "border border-line bg-surface-raised"
149
- ])
150
- }, null, 8, ["transition", "class"])) : createCommentVNode("", true),
151
- createVNode(_sfc_main$2, {
152
- item,
153
- bottom: bottom.value,
154
- compact: __props.compact,
155
- active: current.value === item.value
156
- }, null, 8, ["item", "bottom", "compact", "active"])
133
+ createVNode(unref(TabsTrigger), {
134
+ value: item.value,
135
+ disabled: item.disabled,
136
+ title: __props.compact && item.icon && !bottom.value ? item.label : void 0,
137
+ class: normalizeClass(["relative inline-flex items-center justify-center outline-none transition-[color,opacity] duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring data-[disabled]:cursor-not-allowed data-[disabled]:opacity-45", [
138
+ bottom.value ? unref(BOTTOM_ITEM) : "shrink-0 gap-1.5 whitespace-nowrap text-sm",
139
+ __props.block && !bottom.value ? "flex-1" : "",
140
+ __props.variant === "underline" ? "rounded-t-inner px-3 pt-1.5 pb-2" : "",
141
+ __props.variant === "contained" || __props.variant === "plain" ? "rounded-inner px-3 py-1.5" : "",
142
+ __props.accent ? "data-[state=active]:text-brand" : "data-[state=active]:text-ink",
143
+ hovered.value === item.value && !bottom.value ? "text-ink" : !__props.hoverFollow && !bottom.value ? "text-muted hover:text-ink" : "text-muted"
144
+ ]]),
145
+ onPointerenter: ($event) => __props.hoverFollow && !item.disabled && (hovered.value = item.value),
146
+ onPointerup: releaseFocusAfterPointer,
147
+ onClick: ($event) => clickPick(item)
148
+ }, {
149
+ default: withCtx(() => [
150
+ pillUnder(item.value, current.value) ? (openBlock(), createBlock(unref(Motion), {
151
+ key: 0,
152
+ "layout-id": pillId,
153
+ transition: unref(pillTransition),
154
+ class: normalizeClass(__props.variant === "underline" ? [
155
+ "absolute inset-x-0 -bottom-px z-0 h-0.5 rounded-full",
156
+ __props.accent ? "bg-brand" : "bg-ink"
157
+ ] : bottom.value ? [unref(BOTTOM_MARKER), __props.accent ? "bg-brand" : "bg-ink"] : [
158
+ "absolute inset-0 z-0 rounded-inner shadow-sm",
159
+ __props.accent ? "border border-brand/35 bg-brand/12" : "border border-line bg-surface-raised"
160
+ ])
161
+ }, null, 8, ["transition", "class"])) : createCommentVNode("", true),
162
+ createVNode(_sfc_main$3, {
163
+ item,
164
+ bottom: bottom.value,
165
+ compact: __props.compact,
166
+ active: current.value === item.value
167
+ }, null, 8, ["item", "bottom", "compact", "active"])
168
+ ]),
169
+ _: 2
170
+ }, 1032, ["value", "disabled", "title", "class", "onPointerenter", "onClick"])
157
171
  ]),
158
172
  _: 2
159
- }, 1032, ["value", "disabled", "title", "class", "onPointerenter"])
160
- ]),
161
- _: 2
162
- }, 1032, ["items", "title", "title-icon"]);
163
- }), 128)),
164
- !__props.items.length ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(bottom.value ? 4 : 3, (n) => {
165
- return openBlock(), createElementBlock("span", {
166
- key: `skeleton-${n}`,
167
- class: normalizeClass(["flex items-center justify-center", bottom.value ? "min-h-14 flex-col gap-1.5 py-1.5" : "px-1 py-1.5"])
168
- }, [
169
- bottom.value ? (openBlock(), createBlock(_sfc_main$3, {
170
- key: 0,
171
- rounded: "pill",
172
- class: "size-5"
173
- })) : createCommentVNode("", true),
174
- createVNode(_sfc_main$3, {
175
- class: normalizeClass(bottom.value ? "h-2.5 w-10" : "h-5 w-16")
176
- }, null, 8, ["class"])
177
- ], 2);
178
- }), 128)) : createCommentVNode("", true)
173
+ }, 1032, ["items", "title", "title-icon"]);
174
+ }), 128)),
175
+ !__props.items.length ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(bottom.value ? 4 : 3, (n) => {
176
+ return openBlock(), createElementBlock("span", {
177
+ key: `skeleton-${n}`,
178
+ class: normalizeClass(["flex items-center justify-center", bottom.value ? "min-h-14 flex-col gap-1.5 py-1.5" : "px-1 py-1.5"])
179
+ }, [
180
+ bottom.value ? (openBlock(), createBlock(_sfc_main$4, {
181
+ key: 0,
182
+ rounded: "pill",
183
+ class: "size-5"
184
+ })) : createCommentVNode("", true),
185
+ createVNode(_sfc_main$4, {
186
+ class: normalizeClass(bottom.value ? "h-2.5 w-10" : "h-5 w-16")
187
+ }, null, 8, ["class"])
188
+ ], 2);
189
+ }), 128)) : createCommentVNode("", true)
190
+ ]),
191
+ _: 1
192
+ }, 8, ["aria-label", "class"])
179
193
  ]),
180
194
  _: 1
181
- }, 8, ["aria-label", "class"]),
195
+ }, 16)),
182
196
  _ctx.$slots.panel ? (openBlock(), createBlock(unref(Motion), {
183
197
  key: 0,
184
198
  layout: true,
@@ -210,7 +224,7 @@ import '../assets/LpTabs-Bh7a-Bdl.css';const _sfc_main = /* @__PURE__ */ defineC
210
224
  };
211
225
  }
212
226
  });
213
- const LpTabs = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-ef828291"]]);
227
+ const LpTabs = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-43a45129"]]);
214
228
  export {
215
229
  LpTabs as default
216
230
  };
@@ -1,5 +1,6 @@
1
- import { defineComponent, ref, computed, watch, shallowRef, nextTick, onMounted, onBeforeUnmount, openBlock, createElementBlock, mergeProps, unref, createVNode, withCtx, createElementVNode, toHandlers, Transition, normalizeProps, guardReactiveProps, normalizeStyle, Fragment, renderList, normalizeClass, createCommentVNode, toDisplayString, renderSlot, createBlock, createTextVNode, TransitionGroup, withKeys, withModifiers } from "vue";
1
+ import { defineComponent, ref, computed, watch, onBeforeUnmount, shallowRef, nextTick, onMounted, openBlock, createElementBlock, mergeProps, unref, createVNode, withCtx, createElementVNode, toHandlers, Transition, normalizeProps, guardReactiveProps, normalizeStyle, Fragment, renderList, normalizeClass, createCommentVNode, toDisplayString, renderSlot, createBlock, createTextVNode, TransitionGroup, withKeys, withModifiers } from "vue";
2
2
  import { startPointerDrag } from "../composables/pointerDrag.js";
3
+ import { createGesture } from "../composables/useGesture.js";
3
4
  import { nodeWindow, isHTMLElement } from "../composables/realm.js";
4
5
  import { useLabels } from "../composables/useLpConfig.js";
5
6
  import { useMergedAttrs } from "../composables/useMergedClass.js";
@@ -20,7 +21,7 @@ import { formatClipTime, widestClipTime } from "./videoBackdrop.js";
20
21
  import { stillSpan, FrameSampler } from "./videoFrames.js";
21
22
  import { normalizeRange, ticks, tickStep, containRect, setEnd, revealTime, zoomView, shiftView, adjustRegion, dragEnd, parseClipTime, regionBetween } from "./videoTrim.js";
22
23
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
23
- import '../assets/LpVideoTrim-CMoUw3dJ.css';const _hoisted_1 = ["data-disabled"];
24
+ import '../assets/LpVideoTrim-D8JDg9Qn.css';const _hoisted_1 = ["data-disabled"];
24
25
  const _hoisted_2 = ["src", "poster"];
25
26
  const _hoisted_3 = ["src"];
26
27
  const _hoisted_4 = ["data-live", "aria-label"];
@@ -261,8 +262,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
261
262
  }
262
263
  const marks = computed(() => ready.value ? ticks(view.value, tickStep(span.value, trackWidth.value)) : []);
263
264
  const markDigits = computed(() => tickStep(span.value, trackWidth.value) < 1);
264
- const pointers = /* @__PURE__ */ new Map();
265
- let pinch = null;
266
265
  const pinching = ref(false);
267
266
  let edgeFrame = 0;
268
267
  let lastX = 0;
@@ -310,48 +309,40 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
310
309
  edgeScroll();
311
310
  });
312
311
  }
313
- function onTrackDown(event) {
314
- if (off.value || event.pointerType === "mouse" && event.button !== 0) return;
315
- event.currentTarget.setPointerCapture(event.pointerId);
316
- pointers.set(event.pointerId, event.clientX);
317
- if (pointers.size === 2) {
312
+ let pinchDistance = 0;
313
+ let pinchAnchor = 0;
314
+ const track = createGesture({
315
+ threshold: 0,
316
+ onPress(event) {
317
+ if (off.value || event.pointerType === "mouse" && event.button !== 0) return false;
318
+ event.preventDefault();
319
+ trackEl.value?.querySelector("[data-lp-playhead]")?.focus({ preventScroll: true });
320
+ pause();
321
+ scrubbing.value = true;
322
+ lastX = event.clientX;
323
+ seek(timeAt(event.clientX));
324
+ },
325
+ onDrag({ point }) {
326
+ if (!scrubbing.value) return;
327
+ lastX = point.x;
328
+ seek(timeAt(point.x));
329
+ edgeScroll();
330
+ },
331
+ onDragEnd: () => scrubbing.value = false,
332
+ onTap: () => scrubbing.value = false,
333
+ onPinchStart(pinch) {
318
334
  scrubbing.value = false;
319
- const [a, b] = [...pointers.values()];
320
- pinch = { distance: Math.max(8, Math.abs(a - b)), anchor: timeAt((a + b) / 2) };
321
335
  pinching.value = true;
322
- return;
323
- }
324
- if (pointers.size > 2) return;
325
- event.preventDefault();
326
- trackEl.value?.querySelector("[data-lp-playhead]")?.focus({ preventScroll: true });
327
- pause();
328
- scrubbing.value = true;
329
- lastX = event.clientX;
330
- seek(timeAt(event.clientX));
331
- }
332
- function onTrackMove(event) {
333
- if (!pointers.has(event.pointerId)) return;
334
- pointers.set(event.pointerId, event.clientX);
335
- if (pinch && pointers.size === 2) {
336
- const [a, b] = [...pointers.values()];
337
- const distance = Math.max(8, Math.abs(a - b));
338
- zoomBy(distance / pinch.distance, pinch.anchor);
339
- pinch.distance = distance;
340
- return;
341
- }
342
- if (!scrubbing.value) return;
343
- lastX = event.clientX;
344
- seek(timeAt(event.clientX));
345
- edgeScroll();
346
- }
347
- function onTrackUp(event) {
348
- pointers.delete(event.pointerId);
349
- if (pointers.size < 2) {
350
- pinch = null;
351
- pinching.value = false;
352
- }
353
- if (!pointers.size) scrubbing.value = false;
354
- }
336
+ pinchDistance = pinch.distance;
337
+ pinchAnchor = timeAt(pinch.origin.x);
338
+ },
339
+ onPinch(pinch) {
340
+ zoomBy(pinch.distance / Math.max(8, pinchDistance), pinchAnchor);
341
+ pinchDistance = pinch.distance;
342
+ },
343
+ onPinchEnd: () => pinching.value = false
344
+ });
345
+ onBeforeUnmount(track.dispose);
355
346
  function onOverviewDown(event) {
356
347
  if (off.value || event.button !== 0) return;
357
348
  const box = event.currentTarget.getBoundingClientRect();
@@ -908,17 +899,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
908
899
  ]),
909
900
  _: 1
910
901
  }, 16),
911
- createElementVNode("div", {
902
+ createElementVNode("div", mergeProps({
912
903
  ref_key: "trackEl",
913
904
  ref: trackEl,
914
- class: normalizeClass(["relative h-14 touch-none rounded-control bg-surface-soft transition-opacity duration-(--duration-fast) motion-reduce:transition-none", off.value ? "opacity-60" : "cursor-text"]),
905
+ class: ["relative h-14 touch-none rounded-control bg-surface-soft transition-opacity duration-(--duration-fast) motion-reduce:transition-none", off.value ? "opacity-60" : "cursor-text"],
915
906
  "aria-label": unref(text).timeline,
916
- role: "group",
917
- onPointerdown: onTrackDown,
918
- onPointermove: onTrackMove,
919
- onPointerup: onTrackUp,
920
- onPointercancel: onTrackUp
921
- }, [
907
+ role: "group"
908
+ }, unref(track).handlers), [
922
909
  createElementVNode("div", _hoisted_13, [
923
910
  createVNode(TransitionGroup, normalizeProps(guardReactiveProps(unref(FADE))), {
924
911
  default: withCtx(() => [
@@ -1001,7 +988,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1001
988
  createElementVNode("span", { class: "absolute -top-0.5 -left-1.5 size-3 rounded-full bg-white shadow-[0_0_0_1px_rgb(0_0_0/0.3),0_1px_3px_rgb(0_0_0/0.35)] group-focus-visible/playhead:ring-2 group-focus-visible/playhead:ring-ring" }, null, -1)
1002
989
  ])], 2)
1003
990
  ], 12, _hoisted_16)
1004
- ], 42, _hoisted_12),
991
+ ], 16, _hoisted_12),
1005
992
  createVNode(Transition, normalizeProps(guardReactiveProps(unref(FADE))), {
1006
993
  default: withCtx(() => [
1007
994
  zoomed.value ? (openBlock(), createElementBlock("div", {
@@ -1100,7 +1087,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1100
1087
  };
1101
1088
  }
1102
1089
  });
1103
- const LpVideoTrim = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-ba15829e"]]);
1090
+ const LpVideoTrim = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5ee84a84"]]);
1104
1091
  export {
1105
1092
  LpVideoTrim as default
1106
1093
  };
@@ -0,0 +1,141 @@
1
+ import type { Direction } from "./spatialNav";
2
+ export interface GesturePoint {
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export type GestureAxis = "x" | "y";
7
+ /** How far a touch may drift before a press stops being a tap or a hold. */
8
+ export declare const TOUCH_SLOP = 8;
9
+ /** A mouse is steadier: a press that moves this far is a drag. */
10
+ export declare const MOUSE_SLOP = 4;
11
+ /** Px/ms at release past which a short swipe still counts (a flick). */
12
+ export declare const FLICK_VELOCITY = 0.45;
13
+ /** How long a finger holds still before it drags (selects, paints) rather than scrolls the page. */
14
+ export declare const HOLD_TO_DRAG_MS = 320;
15
+ /** How long a press must stay for a long press, in ms. */
16
+ export declare const LONG_PRESS_MS = 500;
17
+ /** The slop for a pointer type: a finger gets more room than a mouse. */
18
+ export declare function slopFor(pointerType: string): number;
19
+ export interface VelocityTracker {
20
+ /** Record where the pointer is at `time` (ms, an event's timeStamp). */
21
+ add: (point: GesturePoint, time: number) => void;
22
+ /** Px/ms over the last moments, zero when the pointer rested before `now`. */
23
+ velocity: (now?: number) => GesturePoint;
24
+ reset: () => void;
25
+ }
26
+ /**
27
+ * Velocity from the samples in the last VELOCITY_WINDOW_MS rather than from
28
+ * the last two events: a jittery final frame (two events a millisecond apart)
29
+ * would otherwise decide how far a flick travels.
30
+ */
31
+ export declare function createVelocityTracker(): VelocityTracker;
32
+ /**
33
+ * Which axis a movement has committed to, or null while it is still inside
34
+ * the slop or too diagonal to call. `ratio` is how much the winning axis must
35
+ * lead by: 1 takes any lead, higher waits for a clearer one.
36
+ */
37
+ export declare function lockAxis(movement: GesturePoint, slop: number, ratio?: number): GestureAxis | null;
38
+ export interface SwipeOptions {
39
+ /** Distance in px that is a swipe at any speed. Default 60. */
40
+ distance?: number;
41
+ /** Px/ms that turns a shorter move into a swipe. Default FLICK_VELOCITY. */
42
+ velocity?: number;
43
+ /** A flick must still travel this far, so a twitch is not one. Default 16. */
44
+ minDistance?: number;
45
+ /** Only these axes count. Default both. */
46
+ axis?: GestureAxis;
47
+ /** How much the swipe's axis must lead the other. Default 1.5. */
48
+ ratio?: number;
49
+ }
50
+ /** Which way a released movement swiped, or null when it was not a swipe. */
51
+ export declare function swipeDirection(movement: GesturePoint, velocity: GesturePoint, options?: SwipeOptions): Direction | null;
52
+ /**
53
+ * How far an edge moves when pulled `overshoot` px past it: the further the
54
+ * pull, the less each pixel gives, approaching `size` and never passing it.
55
+ * The sign follows the overshoot. `c` is the stiffness, iOS uses 0.55.
56
+ */
57
+ export declare function rubberBand(overshoot: number, size: number, c?: number): number;
58
+ /** Clamp `value` into [min, max], letting it stretch past either end by rubberBand. */
59
+ export declare function rubberClamp(value: number, min: number, max: number, size: number, c?: number): number;
60
+ export interface DismissOptions {
61
+ /** Offset toward the dismissing side, in px (positive is toward it). */
62
+ offset: number;
63
+ /** Velocity toward the dismissing side, px/ms. */
64
+ velocity: number;
65
+ /** The size of what is being swiped away along the swipe, in px. */
66
+ size: number;
67
+ /** Fraction of `size` past which a slow release dismisses. Default 0.5. */
68
+ ratio?: number;
69
+ /** Px/ms past which a release dismisses from any distance. Default FLICK_VELOCITY. */
70
+ flick?: number;
71
+ }
72
+ /** Swipe to dismiss: whether a release should carry it away or let it spring back. */
73
+ export declare function shouldDismiss({ offset, velocity, size, ratio, flick }: DismissOptions): boolean;
74
+ export interface PageSwipeOptions {
75
+ index: number;
76
+ count: number;
77
+ /** Drag offset along the strip in px, positive toward the previous item. */
78
+ offset: number;
79
+ /** Velocity in px/ms, same sign as `offset`. */
80
+ velocity: number;
81
+ /** Width of one item in px. */
82
+ size: number;
83
+ /** Fraction of `size` that pages at any speed. Default 0.25. */
84
+ ratio?: number;
85
+ flick?: number;
86
+ /** Wrap past the ends instead of stopping. */
87
+ loop?: boolean;
88
+ }
89
+ /** Swipe between items: the index a release lands on, one step at most. */
90
+ export declare function pageAfterSwipe({ index, count, offset, velocity, size, ratio, flick, loop }: PageSwipeOptions): number;
91
+ /** Where a fling at `velocity` px/ms comes to rest with exponential friction of time constant `tau` ms. */
92
+ export declare function momentumDistance(velocity: number, tau?: number): number;
93
+ export interface PinchFrame {
94
+ /** Distance between the two pointers now, over the distance at the start. */
95
+ scale: number;
96
+ /** Midpoint of the two pointers. */
97
+ origin: GesturePoint;
98
+ distance: number;
99
+ }
100
+ export declare function distance(a: GesturePoint, b: GesturePoint): number;
101
+ export declare function midpoint(a: GesturePoint, b: GesturePoint): GesturePoint;
102
+ /** The pinch between two pointers, measured against the distance it started at. */
103
+ export declare function pinchFrame(a: GesturePoint, b: GesturePoint, startDistance: number): PinchFrame;
104
+ /** The wheel's delta in pixels, whatever unit the device reported it in. */
105
+ export declare function wheelPixels(delta: number, mode: number, page: number): number;
106
+ export interface DragSessionOptions {
107
+ /** Px of travel before a press becomes a drag. */
108
+ slop: number;
109
+ /** Only a drag along this axis counts; one along the other is rejected. `lock` takes whichever comes first. */
110
+ axis?: GestureAxis | "lock";
111
+ }
112
+ export interface DragFrame {
113
+ /** `pending` inside the slop, `drag` once committed, `rejected` when it went the other way. */
114
+ phase: "pending" | "drag" | "rejected";
115
+ /** True on the frame the drag committed. */
116
+ first: boolean;
117
+ start: GesturePoint;
118
+ point: GesturePoint;
119
+ movement: GesturePoint;
120
+ delta: GesturePoint;
121
+ velocity: GesturePoint;
122
+ axis: GestureAxis | null;
123
+ elapsed: number;
124
+ }
125
+ export interface DragSession {
126
+ readonly phase: DragFrame["phase"];
127
+ /** Feed the pointer's position; returns where the gesture stands. */
128
+ move: (point: GesturePoint, time: number) => DragFrame;
129
+ /** The final frame at release, with the swipe it made (if it was a drag). */
130
+ end: (point: GesturePoint, time: number, swipe?: SwipeOptions) => DragFrame & {
131
+ swipe: Direction | null;
132
+ };
133
+ }
134
+ /**
135
+ * One press followed from down to up, whatever events carry it: useGesture
136
+ * feeds it pointer events, a pull from the screen edge feeds it touches. It
137
+ * decides when the press becomes a drag, along which axis, how fast it moves
138
+ * and whether the release was a swipe, so every gesture in the kit draws the
139
+ * line in the same place.
140
+ */
141
+ export declare function createDragSession(start: GesturePoint, time: number, options: DragSessionOptions): DragSession;