@leavepulse/ui 0.37.3 → 0.37.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.
- package/dist/chunks/{LpAppShell-Cs9jDoqJ.js → LpAppShell-1rTB24lI.js} +1 -1
- package/dist/chunks/{LpDrawer-CslZtgUm.js → LpDrawer-E67O8fFP.js} +147 -61
- package/dist/chunks/{LpModalNav-Dkogc3O_.js → LpModalNav--a3i7c89.js} +1 -1
- package/dist/chunks/{LpSidebar-DvGfofv6.js → LpSidebar-DvxMte3k.js} +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpDrawer.vue.d.ts +87 -28
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpSidebar.vue.js +1 -1
- package/dist/components/modalDepth.d.ts +2 -0
- package/dist/components/modalDepth.js +4 -0
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/src/components/LpDrawer.vue +191 -86
- package/src/components/modalDepth.ts +5 -0
|
@@ -3,7 +3,7 @@ import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
|
3
3
|
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
4
|
import { _ as _sfc_main$5 } from "./LpScrollArea-D8xyKXcF.js";
|
|
5
5
|
import { _ as _sfc_main$4 } from "./LpShift-DXNsauQZ.js";
|
|
6
|
-
import { _ as _sfc_main$1 } from "./LpSidebar-
|
|
6
|
+
import { _ as _sfc_main$1 } from "./LpSidebar-DvxMte3k.js";
|
|
7
7
|
import { useMergedAttrs } from "../composables/useMergedClass.js";
|
|
8
8
|
const _hoisted_1 = { class: "flex min-w-0 flex-1 flex-col" };
|
|
9
9
|
const _hoisted_2 = { class: "relative flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6" };
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { defineComponent, computed, watch, onBeforeUnmount,
|
|
2
|
-
import { usePointerSwipe } from "@vueuse/core";
|
|
1
|
+
import { ref, defineComponent, computed, watch, onBeforeUnmount, useSlots, openBlock, createElementBlock, Fragment, withDirectives, createElementVNode, normalizeStyle, unref, vShow, normalizeClass, toDisplayString, createCommentVNode, renderSlot, createVNode, withCtx, createBlock, createTextVNode } from "vue";
|
|
3
2
|
import { DrawerRoot, DrawerPortal, DrawerOverlay, DrawerContent, DrawerHandle, DrawerTitle, DrawerDescription, DrawerClose } from "vaul-vue";
|
|
4
3
|
import { CLOSE_ICON } from "../components/dropdown.js";
|
|
5
4
|
import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
|
|
6
5
|
import { _ as _sfc_main$2 } from "./LpScrollArea-D8xyKXcF.js";
|
|
7
|
-
import { useModalLayer } from "../components/modalDepth.js";
|
|
6
|
+
import { useModalLayer, anyModalOpen } from "../components/modalDepth.js";
|
|
8
7
|
const _hoisted_1 = { class: "text-base font-semibold text-ink" };
|
|
9
8
|
const _hoisted_2 = { class: "flex flex-col gap-1" };
|
|
10
9
|
const _hoisted_3 = ["data-vaul-no-drag"];
|
|
@@ -12,7 +11,7 @@ const _hoisted_4 = {
|
|
|
12
11
|
key: 3,
|
|
13
12
|
class: "flex justify-end gap-2 px-5 pb-5 pt-4"
|
|
14
13
|
};
|
|
15
|
-
const
|
|
14
|
+
const edgeOwner = ref(null);
|
|
16
15
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
17
16
|
__name: "LpDrawer",
|
|
18
17
|
props: {
|
|
@@ -104,6 +103,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
104
103
|
return "p-5";
|
|
105
104
|
});
|
|
106
105
|
const headerClass = computed(() => showHandle.value ? "px-5 pb-4 pt-1" : "px-5 pb-4 pt-5");
|
|
106
|
+
const NO_DRAG_SELECTOR = "input, textarea, select, button, a[href], label, [contenteditable=''], [contenteditable='true']";
|
|
107
107
|
function tagNoDrag(el, { value }) {
|
|
108
108
|
if (!value) return;
|
|
109
109
|
for (const node of el.querySelectorAll(NO_DRAG_SELECTOR)) {
|
|
@@ -114,97 +114,183 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
114
114
|
mounted: tagNoDrag,
|
|
115
115
|
updated: tagNoDrag
|
|
116
116
|
};
|
|
117
|
-
const
|
|
117
|
+
const EDGE_SLOP = 10;
|
|
118
|
+
const FLICK_SPEED = 0.45;
|
|
119
|
+
const BREAKPOINT_PX = { sm: 640, md: 768, lg: 1024, xl: 1280 };
|
|
120
|
+
const EDGE_IGNORE = "input, textarea, select, [contenteditable=''], [contenteditable='true'], [data-vaul-no-drag], [data-lp-no-edge-pull]";
|
|
118
121
|
const peek = ref(0);
|
|
119
122
|
const peeking = ref(false);
|
|
120
123
|
const handingOff = ref(false);
|
|
124
|
+
const skipOpenSlide = ref(false);
|
|
125
|
+
watch(
|
|
126
|
+
() => props.open,
|
|
127
|
+
(open) => {
|
|
128
|
+
if (!open) skipOpenSlide.value = false;
|
|
129
|
+
}
|
|
130
|
+
);
|
|
121
131
|
function panelPx() {
|
|
122
132
|
const vw = typeof window === "undefined" ? 0 : window.innerWidth;
|
|
123
133
|
const cap = props.width ? Number.parseFloat(props.width) : SIZES[props.size] * 16;
|
|
124
134
|
return Math.min(vw * 0.94, Number.isFinite(cap) ? cap : SIZES[props.size] * 16);
|
|
125
135
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
function edgeEnabled() {
|
|
137
|
+
if (!props.edgeOpen || !isHorizontal.value || props.open || handingOff.value) return false;
|
|
138
|
+
const bp = props.edgeBreakpoint ? BREAKPOINT_PX[props.edgeBreakpoint] : null;
|
|
139
|
+
return bp === null || window.innerWidth < bp;
|
|
140
|
+
}
|
|
141
|
+
let track = null;
|
|
142
|
+
const owner = /* @__PURE__ */ Symbol("edge-pull");
|
|
143
|
+
function reducedMotion() {
|
|
144
|
+
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
145
|
+
}
|
|
146
|
+
function scrollsWithPull(target) {
|
|
147
|
+
let el = target instanceof Element ? target : null;
|
|
148
|
+
const pullsRight = dir.value === "left";
|
|
149
|
+
while (el && el !== document.body) {
|
|
150
|
+
if (el.closest(EDGE_IGNORE) === el) return true;
|
|
151
|
+
if (el.scrollWidth > el.clientWidth) {
|
|
152
|
+
const overflow = getComputedStyle(el).overflowX;
|
|
153
|
+
if (overflow === "auto" || overflow === "scroll") {
|
|
154
|
+
if (pullsRight ? el.scrollLeft > 0 : el.scrollLeft + el.clientWidth < el.scrollWidth) return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
el = el.parentElement;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
function inward(x) {
|
|
162
|
+
return dir.value === "right" ? window.innerWidth - x : x;
|
|
163
|
+
}
|
|
164
|
+
function onTouchStart(e) {
|
|
165
|
+
if (track) dropTrack();
|
|
166
|
+
if (e.touches.length !== 1 || !edgeEnabled() || anyModalOpen() || edgeOwner.value) return;
|
|
167
|
+
const t = e.touches[0];
|
|
168
|
+
if (inward(t.clientX) > props.edgeSize || scrollsWithPull(e.target)) return;
|
|
169
|
+
track = { id: t.identifier, x: t.clientX, y: t.clientY, t: e.timeStamp, lastX: t.clientX, lastT: e.timeStamp, speed: 0 };
|
|
170
|
+
}
|
|
171
|
+
function onTouchMove(e) {
|
|
172
|
+
if (!track) return;
|
|
173
|
+
const t = Array.from(e.changedTouches).find((x) => x.identifier === track.id);
|
|
174
|
+
if (!t) return;
|
|
175
|
+
const sign = dir.value === "right" ? -1 : 1;
|
|
176
|
+
const dx = (t.clientX - track.x) * sign;
|
|
177
|
+
const dy = t.clientY - track.y;
|
|
178
|
+
if (!peeking.value) {
|
|
179
|
+
if (Math.abs(dy) > EDGE_SLOP && Math.abs(dy) > Math.abs(dx)) {
|
|
180
|
+
track = null;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (dx < EDGE_SLOP || Math.abs(dx) < Math.abs(dy)) return;
|
|
184
|
+
if (edgeOwner.value && edgeOwner.value !== owner) {
|
|
185
|
+
track = null;
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
edgeOwner.value = owner;
|
|
131
189
|
peeking.value = true;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
190
|
+
}
|
|
191
|
+
if (e.cancelable) e.preventDefault();
|
|
192
|
+
const dt = e.timeStamp - track.lastT;
|
|
193
|
+
if (dt > 0) track.speed = (t.clientX - track.lastX) * sign / dt * 0.6 + track.speed * 0.4;
|
|
194
|
+
track.lastX = t.clientX;
|
|
195
|
+
track.lastT = e.timeStamp;
|
|
196
|
+
peek.value = Math.max(0, Math.min(1, inward(t.clientX) / panelPx()));
|
|
197
|
+
}
|
|
198
|
+
function dropTrack() {
|
|
199
|
+
track = null;
|
|
200
|
+
if (edgeOwner.value === owner) edgeOwner.value = null;
|
|
201
|
+
}
|
|
202
|
+
function settle() {
|
|
203
|
+
const flick = track !== null && track.speed > FLICK_SPEED && peek.value > 0.05;
|
|
204
|
+
dropTrack();
|
|
205
|
+
if (!peeking.value) return;
|
|
206
|
+
peeking.value = false;
|
|
207
|
+
if (peek.value >= props.edgeOpenThreshold || flick) {
|
|
208
|
+
peek.value = 1;
|
|
209
|
+
handingOff.value = true;
|
|
210
|
+
skipOpenSlide.value = true;
|
|
211
|
+
timers.push(
|
|
145
212
|
window.setTimeout(() => {
|
|
146
213
|
emit("update:open", true);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
214
|
+
timers.push(
|
|
215
|
+
window.setTimeout(() => {
|
|
216
|
+
handingOff.value = false;
|
|
217
|
+
peek.value = 0;
|
|
218
|
+
}, 60)
|
|
219
|
+
);
|
|
220
|
+
}, reducedMotion() ? 0 : 180)
|
|
221
|
+
);
|
|
222
|
+
} else {
|
|
223
|
+
peek.value = 0;
|
|
155
224
|
}
|
|
225
|
+
}
|
|
226
|
+
const timers = [];
|
|
227
|
+
onBeforeUnmount(() => {
|
|
228
|
+
for (const id of timers) window.clearTimeout(id);
|
|
229
|
+
if (edgeOwner.value === owner) edgeOwner.value = null;
|
|
156
230
|
});
|
|
231
|
+
function onTouchEnd(e) {
|
|
232
|
+
if (track && !Array.from(e.changedTouches).some((x) => x.identifier === track.id)) return;
|
|
233
|
+
settle();
|
|
234
|
+
}
|
|
235
|
+
if (typeof document !== "undefined") {
|
|
236
|
+
watch(
|
|
237
|
+
() => props.edgeOpen && isHorizontal.value,
|
|
238
|
+
(on, _, onCleanup) => {
|
|
239
|
+
if (!on) return;
|
|
240
|
+
const passive = { passive: true };
|
|
241
|
+
document.addEventListener("touchstart", onTouchStart, passive);
|
|
242
|
+
document.addEventListener("touchmove", onTouchMove, { passive: false });
|
|
243
|
+
document.addEventListener("touchend", onTouchEnd, passive);
|
|
244
|
+
document.addEventListener("touchcancel", onTouchEnd, passive);
|
|
245
|
+
onCleanup(() => {
|
|
246
|
+
document.removeEventListener("touchstart", onTouchStart);
|
|
247
|
+
document.removeEventListener("touchmove", onTouchMove);
|
|
248
|
+
document.removeEventListener("touchend", onTouchEnd);
|
|
249
|
+
document.removeEventListener("touchcancel", onTouchEnd);
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
{ immediate: true }
|
|
253
|
+
);
|
|
254
|
+
}
|
|
157
255
|
const previewStyle = computed(() => {
|
|
158
256
|
if (!props.edgeOpen || props.open && !handingOff.value) return { display: "none" };
|
|
159
257
|
const off = (1 - peek.value) * 100;
|
|
160
258
|
const axis = dir.value === "right" ? off : -off;
|
|
161
259
|
return {
|
|
162
260
|
transform: `translateX(${axis}%)`,
|
|
163
|
-
transition: peeking.value ? "none" : "transform 0.
|
|
261
|
+
transition: peeking.value || reducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
|
|
164
262
|
pointerEvents: "none"
|
|
165
263
|
};
|
|
166
264
|
});
|
|
167
|
-
const edgeStripStyle = computed(() => {
|
|
168
|
-
const w = `${props.edgeSize}px`;
|
|
169
|
-
return dir.value === "right" ? { right: "0", width: w } : { left: "0", width: w };
|
|
170
|
-
});
|
|
171
|
-
const EDGE_STRIP_HIDDEN = {
|
|
172
|
-
sm: "sm:hidden",
|
|
173
|
-
md: "md:hidden",
|
|
174
|
-
lg: "lg:hidden",
|
|
175
|
-
xl: "xl:hidden"
|
|
176
|
-
};
|
|
177
|
-
const edgeStripClass = computed(
|
|
178
|
-
() => props.edgeBreakpoint ? EDGE_STRIP_HIDDEN[props.edgeBreakpoint] : ""
|
|
179
|
-
);
|
|
180
265
|
return (_ctx, _cache) => {
|
|
181
266
|
return openBlock(), createElementBlock(Fragment, null, [
|
|
182
267
|
__props.edgeOpen && isHorizontal.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
183
|
-
createElementVNode("div", {
|
|
184
|
-
ref_key: "edgeStrip",
|
|
185
|
-
ref: edgeStrip,
|
|
186
|
-
class: normalizeClass(["fixed inset-y-0 z-(--z-overlay) touch-none", edgeStripClass.value]),
|
|
187
|
-
style: normalizeStyle(edgeStripStyle.value),
|
|
188
|
-
"aria-hidden": "true"
|
|
189
|
-
}, null, 6),
|
|
190
268
|
withDirectives(createElementVNode("div", {
|
|
191
|
-
class: "pointer-events-none fixed inset-0
|
|
192
|
-
style: normalizeStyle({ opacity: peek.value })
|
|
269
|
+
class: "lp-scrim pointer-events-none fixed inset-0",
|
|
270
|
+
style: normalizeStyle({ zIndex: unref(layer).scrim, opacity: peek.value, transition: peeking.value || reducedMotion() ? "none" : "opacity 0.18s ease-out" })
|
|
193
271
|
}, null, 4), [
|
|
194
272
|
[vShow, peeking.value || peek.value > 0]
|
|
195
273
|
]),
|
|
196
|
-
createElementVNode("div", {
|
|
274
|
+
withDirectives(createElementVNode("div", {
|
|
197
275
|
class: normalizeClass(contentClass.value),
|
|
198
|
-
style: normalizeStyle([sizeStyle.value, previewStyle.value]),
|
|
199
|
-
"aria-hidden": "true"
|
|
276
|
+
style: normalizeStyle([sizeStyle.value, previewStyle.value, { zIndex: unref(layer).panel }]),
|
|
277
|
+
"aria-hidden": "true",
|
|
278
|
+
inert: ""
|
|
200
279
|
}, [
|
|
201
|
-
hasHeader.value ? (openBlock(), createElementBlock("
|
|
280
|
+
hasHeader.value ? (openBlock(), createElementBlock("header", {
|
|
202
281
|
key: 0,
|
|
203
282
|
class: normalizeClass(["flex items-start justify-between gap-4", headerClass.value])
|
|
204
283
|
}, [
|
|
205
284
|
createElementVNode("span", _hoisted_1, toDisplayString(__props.title), 1)
|
|
206
|
-
], 2)) : createCommentVNode("", true)
|
|
207
|
-
|
|
285
|
+
], 2)) : createCommentVNode("", true),
|
|
286
|
+
createElementVNode("div", {
|
|
287
|
+
class: normalizeClass(["flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden", padClass.value])
|
|
288
|
+
}, [
|
|
289
|
+
peeking.value || peek.value > 0 ? renderSlot(_ctx.$slots, "default", { key: 0 }) : createCommentVNode("", true)
|
|
290
|
+
], 2)
|
|
291
|
+
], 6), [
|
|
292
|
+
[vShow, peeking.value || peek.value > 0]
|
|
293
|
+
])
|
|
208
294
|
], 64)) : createCommentVNode("", true),
|
|
209
295
|
createVNode(unref(DrawerRoot), {
|
|
210
296
|
open: __props.open,
|
|
@@ -226,7 +312,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
226
312
|
createVNode(unref(DrawerContent), {
|
|
227
313
|
class: normalizeClass(contentClass.value),
|
|
228
314
|
style: normalizeStyle([sizeStyle.value, { zIndex: unref(layer).panel }]),
|
|
229
|
-
"data-vaul-animate": handingOff.value ? "false" : void 0
|
|
315
|
+
"data-vaul-animate": handingOff.value || skipOpenSlide.value ? "false" : void 0
|
|
230
316
|
}, {
|
|
231
317
|
default: withCtx(() => [
|
|
232
318
|
__props.resizable && isHorizontal.value ? (openBlock(), createElementBlock("div", {
|
|
@@ -2,7 +2,7 @@ import { defineComponent, ref, computed, openBlock, createBlock, createSlots, wi
|
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
3
|
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
4
|
import { _ as _sfc_main$1 } from "./LpModal-D3QxgWaT.js";
|
|
5
|
-
import { _ as _sfc_main$4 } from "./LpSidebar-
|
|
5
|
+
import { _ as _sfc_main$4 } from "./LpSidebar-DvxMte3k.js";
|
|
6
6
|
const _hoisted_1 = { class: "flex items-center gap-2" };
|
|
7
7
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
8
8
|
__name: "LpModalNav",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, computed, ref, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createBlock, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps } from "vue";
|
|
2
|
-
import { _ as _sfc_main$3 } from "./LpDrawer-
|
|
2
|
+
import { _ as _sfc_main$3 } from "./LpDrawer-E67O8fFP.js";
|
|
3
3
|
import { _ as _sfc_main$1 } from "./LpInput-9EXWUM9B.js";
|
|
4
4
|
import { _ as _sfc_main$2 } from "./LpSidebarNav-CqwvT4H8.js";
|
|
5
5
|
const _hoisted_1 = {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
declare const _default: typeof __VLS_export;
|
|
2
|
+
export default _default;
|
|
3
|
+
declare const __VLS_export: __VLS_WithSlots<import('vue').DefineComponent<{
|
|
3
4
|
open?: boolean;
|
|
4
5
|
/** Edge the drawer attaches to. `side` is a back-compat alias (left/right). */
|
|
5
|
-
direction?:
|
|
6
|
+
direction?: "left" | "right" | "top" | "bottom";
|
|
6
7
|
side?: "left" | "right";
|
|
7
8
|
title?: string;
|
|
8
9
|
description?: string;
|
|
@@ -35,21 +36,20 @@ type __VLS_Props = {
|
|
|
35
36
|
*/
|
|
36
37
|
noDragContent?: boolean;
|
|
37
38
|
/**
|
|
38
|
-
* Open by dragging in from the screen edge (Discord-style).
|
|
39
|
-
*
|
|
40
|
-
* the
|
|
41
|
-
*
|
|
39
|
+
* Open by dragging in from the screen edge (Discord-style). A touch that
|
|
40
|
+
* starts within `edgeSize` of the edge and travels inward drags a preview of
|
|
41
|
+
* the panel (with its content) under the finger; a release past
|
|
42
|
+
* `edgeOpenThreshold`, or a quick flick, opens the drawer. Nothing is laid
|
|
43
|
+
* over the edge, so taps there still reach the page. Left/right drawers only.
|
|
42
44
|
*/
|
|
43
45
|
edgeOpen?: boolean;
|
|
44
46
|
/**
|
|
45
|
-
* Tailwind breakpoint above which the edge
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* but still overlays the screen edge and swallows clicks. Pass the same
|
|
49
|
-
* breakpoint at which the host switches to the drawer (LpSidebar does).
|
|
47
|
+
* Tailwind breakpoint at and above which the edge pull is ignored. Pass the
|
|
48
|
+
* same breakpoint at which the host switches to the drawer (LpSidebar
|
|
49
|
+
* does), so a wide touch screen with the rail visible doesn't pull it too.
|
|
50
50
|
*/
|
|
51
51
|
edgeBreakpoint?: "sm" | "md" | "lg" | "xl";
|
|
52
|
-
/**
|
|
52
|
+
/** Distance (px) from the edge within which a touch can start a pull. */
|
|
53
53
|
edgeSize?: number;
|
|
54
54
|
/** Fraction (0–1) of the panel width the pull must reach to open on release. */
|
|
55
55
|
edgeOpenThreshold?: number;
|
|
@@ -63,18 +63,72 @@ type __VLS_Props = {
|
|
|
63
63
|
minSize?: string;
|
|
64
64
|
/** Max main-axis size while resizing (any CSS length). Default 96vw. */
|
|
65
65
|
maxSize?: string;
|
|
66
|
-
}
|
|
67
|
-
declare var __VLS_37: {}, __VLS_62: {}, __VLS_64: {};
|
|
68
|
-
type __VLS_Slots = {} & {
|
|
69
|
-
title?: (props: typeof __VLS_37) => any;
|
|
70
|
-
} & {
|
|
71
|
-
default?: (props: typeof __VLS_62) => any;
|
|
72
|
-
} & {
|
|
73
|
-
footer?: (props: typeof __VLS_64) => any;
|
|
74
|
-
};
|
|
75
|
-
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
66
|
+
}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
76
67
|
"update:open": (value: boolean) => any;
|
|
77
|
-
}, string, import('vue').PublicProps, Readonly<
|
|
68
|
+
}, string, import('vue').PublicProps, Readonly<{
|
|
69
|
+
open?: boolean;
|
|
70
|
+
/** Edge the drawer attaches to. `side` is a back-compat alias (left/right). */
|
|
71
|
+
direction?: "left" | "right" | "top" | "bottom";
|
|
72
|
+
side?: "left" | "right";
|
|
73
|
+
title?: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
/** Size preset along the drawer's main axis (width for left/right, height for top/bottom). */
|
|
76
|
+
size?: "sm" | "md" | "lg" | "xl";
|
|
77
|
+
/** Explicit size override (any CSS length), wins over `size`. */
|
|
78
|
+
width?: string;
|
|
79
|
+
/** Drag-to-dismiss + click-outside. Set false to force a controlled close. */
|
|
80
|
+
dismissible?: boolean;
|
|
81
|
+
/** Fractions (0–1) or px strings the drawer snaps between, nearest edge first. */
|
|
82
|
+
snapPoints?: (number | string)[];
|
|
83
|
+
/** Show the drag handle (auto-on for bottom/top sheets). */
|
|
84
|
+
handle?: boolean;
|
|
85
|
+
/** Only the handle initiates a drag — frees inner content to scroll. */
|
|
86
|
+
handleOnly?: boolean;
|
|
87
|
+
/** Scale the [vaul-drawer-wrapper] background while open. */
|
|
88
|
+
scaleBackground?: boolean;
|
|
89
|
+
/** Drag fraction (0–1) past which a release dismisses. */
|
|
90
|
+
closeThreshold?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Auto-tag interactive descendants (input, textarea, select, button, links,
|
|
93
|
+
* [contenteditable]) with data-vaul-no-drag so a drag can't start on them —
|
|
94
|
+
* you can select text / use controls without dragging the drawer. On by
|
|
95
|
+
* default; the drawer still drags from empty space, the handle and header.
|
|
96
|
+
*/
|
|
97
|
+
noDragControls?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Mark the entire body as non-draggable: the drawer then only drags from the
|
|
100
|
+
* handle/header. Stronger than noDragControls — use for form-heavy drawers.
|
|
101
|
+
*/
|
|
102
|
+
noDragContent?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Open by dragging in from the screen edge (Discord-style). A touch that
|
|
105
|
+
* starts within `edgeSize` of the edge and travels inward drags a preview of
|
|
106
|
+
* the panel (with its content) under the finger; a release past
|
|
107
|
+
* `edgeOpenThreshold`, or a quick flick, opens the drawer. Nothing is laid
|
|
108
|
+
* over the edge, so taps there still reach the page. Left/right drawers only.
|
|
109
|
+
*/
|
|
110
|
+
edgeOpen?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Tailwind breakpoint at and above which the edge pull is ignored. Pass the
|
|
113
|
+
* same breakpoint at which the host switches to the drawer (LpSidebar
|
|
114
|
+
* does), so a wide touch screen with the rail visible doesn't pull it too.
|
|
115
|
+
*/
|
|
116
|
+
edgeBreakpoint?: "sm" | "md" | "lg" | "xl";
|
|
117
|
+
/** Distance (px) from the edge within which a touch can start a pull. */
|
|
118
|
+
edgeSize?: number;
|
|
119
|
+
/** Fraction (0–1) of the panel width the pull must reach to open on release. */
|
|
120
|
+
edgeOpenThreshold?: number;
|
|
121
|
+
/**
|
|
122
|
+
* Let the user drag the inner edge to resize the panel (horizontal drawers
|
|
123
|
+
* only — e.g. a logs panel whose long lines overflow). The new size is kept
|
|
124
|
+
* for the session. Min/max bound the drag.
|
|
125
|
+
*/
|
|
126
|
+
resizable?: boolean;
|
|
127
|
+
/** Min main-axis size while resizing (any CSS length). Default 22rem. */
|
|
128
|
+
minSize?: string;
|
|
129
|
+
/** Max main-axis size while resizing (any CSS length). Default 96vw. */
|
|
130
|
+
maxSize?: string;
|
|
131
|
+
}> & Readonly<{
|
|
78
132
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
79
133
|
}>, {
|
|
80
134
|
size: "sm" | "md" | "lg" | "xl";
|
|
@@ -85,10 +139,15 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
85
139
|
edgeOpenThreshold: number;
|
|
86
140
|
minSize: string;
|
|
87
141
|
maxSize: string;
|
|
88
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
142
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>, {
|
|
143
|
+
default?: (props: {}) => any;
|
|
144
|
+
} & {
|
|
145
|
+
title?: (props: {}) => any;
|
|
146
|
+
} & {
|
|
147
|
+
default?: (props: {}) => any;
|
|
148
|
+
} & {
|
|
149
|
+
footer?: (props: {}) => any;
|
|
150
|
+
}>;
|
|
92
151
|
type __VLS_WithSlots<T, S> = T & {
|
|
93
152
|
new (): {
|
|
94
153
|
$slots: S;
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { _ as _3 } from "./chunks/LpMapPicker-ul4oYno6.js";
|
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
5
|
import { _ as _4 } from "./chunks/LpActionBar-BlvtGB2P.js";
|
|
6
6
|
import { _ as _5 } from "./chunks/LpAlert-BAYHDSS1.js";
|
|
7
|
-
import { _ as _6 } from "./chunks/LpAppShell-
|
|
7
|
+
import { _ as _6 } from "./chunks/LpAppShell-1rTB24lI.js";
|
|
8
8
|
import { _ as _7 } from "./chunks/LpAutocomplete-DExlIZe_.js";
|
|
9
9
|
import { _ as _8 } from "./chunks/LpAvatar-D3l4nabg.js";
|
|
10
10
|
import { _ as _9 } from "./chunks/LpBadge-Fe0D1bYx.js";
|
|
@@ -24,7 +24,7 @@ import { _ as _18 } from "./chunks/LpContextMenu-B6WBLleS.js";
|
|
|
24
24
|
import { _ as _19 } from "./chunks/LpDatePicker-BPbvu3cZ.js";
|
|
25
25
|
import { _ as _20 } from "./chunks/LpDisclosure-ChQYmjlR.js";
|
|
26
26
|
import { _ as _21 } from "./chunks/LpDivider-CUX46KR0.js";
|
|
27
|
-
import { _ as _22 } from "./chunks/LpDrawer-
|
|
27
|
+
import { _ as _22 } from "./chunks/LpDrawer-E67O8fFP.js";
|
|
28
28
|
import { _ as _23 } from "./chunks/LpDropdownMenu-DJpoqRCb.js";
|
|
29
29
|
import { default as default4 } from "./components/LpDropzone.vue.js";
|
|
30
30
|
import { _ as _24 } from "./chunks/LpEmptyState-Dsdqz1Wj.js";
|
|
@@ -41,7 +41,7 @@ import { _ as _31 } from "./chunks/LpLink-DCND-zyu.js";
|
|
|
41
41
|
import { _ as _32 } from "./chunks/LpLogViewer-y53ZqSVq.js";
|
|
42
42
|
import { _ as _33 } from "./chunks/LpMediaGallery-Cid7PGYx.js";
|
|
43
43
|
import { _ as _34 } from "./chunks/LpModal-D3QxgWaT.js";
|
|
44
|
-
import { _ as _35 } from "./chunks/LpModalNav
|
|
44
|
+
import { _ as _35 } from "./chunks/LpModalNav--a3i7c89.js";
|
|
45
45
|
import { _ as _36 } from "./chunks/LpNotificationBell-lHePf89W.js";
|
|
46
46
|
import { _ as _37 } from "./chunks/LpNumberField-CWuX08iX.js";
|
|
47
47
|
import { _ as _38 } from "./chunks/LpNumberFlow-amWYaeuA.js";
|
|
@@ -63,7 +63,7 @@ import { _ as _52 } from "./chunks/LpSelect-ANIVtrpM.js";
|
|
|
63
63
|
import { _ as _53 } from "./chunks/LpServiceNode-D8uJ8sGk.js";
|
|
64
64
|
import { _ as _54 } from "./chunks/LpShift-DXNsauQZ.js";
|
|
65
65
|
import { default as default6 } from "./components/LpShortcutsDialog.vue.js";
|
|
66
|
-
import { _ as _55 } from "./chunks/LpSidebar-
|
|
66
|
+
import { _ as _55 } from "./chunks/LpSidebar-DvxMte3k.js";
|
|
67
67
|
import { _ as _56 } from "./chunks/LpSkeleton-CTI-vBgW.js";
|
|
68
68
|
import { _ as _57 } from "./chunks/LpSlider-BwNIBkAK.js";
|
|
69
69
|
import { _ as _58 } from "./chunks/LpSparkline-C6xQ2dwv.js";
|
package/package.json
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ref as sharedRef } from "vue"
|
|
3
|
+
|
|
4
|
+
// One pull at a time across every edge-open drawer on the page.
|
|
5
|
+
const edgeOwner = sharedRef<symbol | null>(null)
|
|
6
|
+
</script>
|
|
7
|
+
|
|
1
8
|
<script setup lang="ts">
|
|
2
9
|
/*
|
|
3
10
|
* Side / bottom panel built on vaul-vue (drag-driven drawer over reka's Dialog
|
|
@@ -12,7 +19,6 @@
|
|
|
12
19
|
* solid background) for vaul to find and transform. Without it the drawer still
|
|
13
20
|
* works — the background just doesn't scale.
|
|
14
21
|
*/
|
|
15
|
-
import { usePointerSwipe } from "@vueuse/core"
|
|
16
22
|
import {
|
|
17
23
|
DrawerClose,
|
|
18
24
|
DrawerContent,
|
|
@@ -27,7 +33,7 @@ import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
|
|
|
27
33
|
import { CLOSE_ICON } from "./dropdown"
|
|
28
34
|
import LpIcon from "./LpIcon.vue"
|
|
29
35
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
30
|
-
import { useModalLayer } from "./modalDepth"
|
|
36
|
+
import { anyModalOpen, useModalLayer } from "./modalDepth"
|
|
31
37
|
|
|
32
38
|
type Direction = "top" | "bottom" | "left" | "right"
|
|
33
39
|
|
|
@@ -68,21 +74,20 @@ const props = withDefaults(
|
|
|
68
74
|
*/
|
|
69
75
|
noDragContent?: boolean
|
|
70
76
|
/**
|
|
71
|
-
* Open by dragging in from the screen edge (Discord-style).
|
|
72
|
-
*
|
|
73
|
-
* the
|
|
74
|
-
*
|
|
77
|
+
* Open by dragging in from the screen edge (Discord-style). A touch that
|
|
78
|
+
* starts within `edgeSize` of the edge and travels inward drags a preview of
|
|
79
|
+
* the panel (with its content) under the finger; a release past
|
|
80
|
+
* `edgeOpenThreshold`, or a quick flick, opens the drawer. Nothing is laid
|
|
81
|
+
* over the edge, so taps there still reach the page. Left/right drawers only.
|
|
75
82
|
*/
|
|
76
83
|
edgeOpen?: boolean
|
|
77
84
|
/**
|
|
78
|
-
* Tailwind breakpoint above which the edge
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* but still overlays the screen edge and swallows clicks. Pass the same
|
|
82
|
-
* breakpoint at which the host switches to the drawer (LpSidebar does).
|
|
85
|
+
* Tailwind breakpoint at and above which the edge pull is ignored. Pass the
|
|
86
|
+
* same breakpoint at which the host switches to the drawer (LpSidebar
|
|
87
|
+
* does), so a wide touch screen with the rail visible doesn't pull it too.
|
|
83
88
|
*/
|
|
84
89
|
edgeBreakpoint?: "sm" | "md" | "lg" | "xl"
|
|
85
|
-
/**
|
|
90
|
+
/** Distance (px) from the edge within which a touch can start a pull. */
|
|
86
91
|
edgeSize?: number
|
|
87
92
|
/** Fraction (0–1) of the panel width the pull must reach to open on release. */
|
|
88
93
|
edgeOpenThreshold?: number
|
|
@@ -226,19 +231,40 @@ const vNoDragControls = {
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
// ── Edge-drag-to-open (Discord-style) ──────────────────────────────────────
|
|
229
|
-
// vaul
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
//
|
|
234
|
+
// vaul only drags an *already-open* drawer, so the pull-from-edge gesture is
|
|
235
|
+
// ours. It is detected on the document rather than on an overlay strip: a strip
|
|
236
|
+
// covering the screen edge swallowed every tap there, and the app burger lives
|
|
237
|
+
// exactly in that corner. A touch near the edge is only a candidate; it becomes
|
|
238
|
+
// a pull once the finger travels inward past EDGE_SLOP and mostly sideways, so a
|
|
239
|
+
// tap still reaches the control under it and a vertical scroll stays a scroll.
|
|
240
|
+
// While pulling, a preview of the real panel content follows the finger; on
|
|
241
|
+
// release we open if the pull passed the threshold or was a quick flick, then
|
|
242
|
+
// hand off to the real drawer without a second slide.
|
|
243
|
+
const EDGE_SLOP = 10
|
|
244
|
+
const FLICK_SPEED = 0.45
|
|
245
|
+
const BREAKPOINT_PX = { sm: 640, md: 768, lg: 1024, xl: 1280 } as const
|
|
246
|
+
// Controls whose own drag must win over the pull, plus explicit opt-outs.
|
|
247
|
+
const EDGE_IGNORE =
|
|
248
|
+
"input, textarea, select, [contenteditable=''], [contenteditable='true'], [data-vaul-no-drag], [data-lp-no-edge-pull]"
|
|
249
|
+
|
|
250
|
+
// 0 (closed) … 1 (fully pulled in). Drives the preview transform + scrim.
|
|
236
251
|
const peek = ref(0)
|
|
237
252
|
const peeking = ref(false)
|
|
238
253
|
// True while we hand off from the finger-preview to the real drawer, so vaul's
|
|
239
254
|
// own open animation is suppressed — the preview has already slid it in, a
|
|
240
255
|
// second slide would look like a double-open.
|
|
241
256
|
const handingOff = ref(false)
|
|
257
|
+
// vaul plays slideFromLeft whenever data-vaul-animate is absent, so lifting it
|
|
258
|
+
// right after the hand-off restarted the open slide from off-screen — the panel
|
|
259
|
+
// visibly opened twice. It stays on for as long as the drawer is open and goes
|
|
260
|
+
// with the close, which keeps vaul's close animation.
|
|
261
|
+
const skipOpenSlide = ref(false)
|
|
262
|
+
watch(
|
|
263
|
+
() => props.open,
|
|
264
|
+
(open) => {
|
|
265
|
+
if (!open) skipOpenSlide.value = false
|
|
266
|
+
},
|
|
267
|
+
)
|
|
242
268
|
|
|
243
269
|
// Resolve the panel's main-axis size in px to map drag distance → progress.
|
|
244
270
|
function panelPx(): number {
|
|
@@ -248,44 +274,140 @@ function panelPx(): number {
|
|
|
248
274
|
return Math.min(vw * 0.94, Number.isFinite(cap) ? cap : SIZES[props.size] * 16)
|
|
249
275
|
}
|
|
250
276
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
277
|
+
function edgeEnabled(): boolean {
|
|
278
|
+
if (!props.edgeOpen || !isHorizontal.value || props.open || handingOff.value) return false
|
|
279
|
+
const bp = props.edgeBreakpoint ? BREAKPOINT_PX[props.edgeBreakpoint] : null
|
|
280
|
+
return bp === null || window.innerWidth < bp
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let track: { id: number; x: number; y: number; t: number; lastX: number; lastT: number; speed: number } | null = null
|
|
284
|
+
const owner = Symbol("edge-pull")
|
|
285
|
+
|
|
286
|
+
function reducedMotion(): boolean {
|
|
287
|
+
return typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// A horizontally scrollable ancestor that can still move the way the finger
|
|
291
|
+
// pulls (a carousel, a wide table) keeps the gesture: the pull only claims
|
|
292
|
+
// what nothing under the finger wants.
|
|
293
|
+
function scrollsWithPull(target: EventTarget | null): boolean {
|
|
294
|
+
let el = target instanceof Element ? target : null
|
|
295
|
+
const pullsRight = dir.value === "left"
|
|
296
|
+
while (el && el !== document.body) {
|
|
297
|
+
if (el.closest(EDGE_IGNORE) === el) return true
|
|
298
|
+
if (el.scrollWidth > el.clientWidth) {
|
|
299
|
+
const overflow = getComputedStyle(el).overflowX
|
|
300
|
+
if (overflow === "auto" || overflow === "scroll") {
|
|
301
|
+
if (pullsRight ? el.scrollLeft > 0 : el.scrollLeft + el.clientWidth < el.scrollWidth) return true
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
el = el.parentElement
|
|
305
|
+
}
|
|
306
|
+
return false
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function inward(x: number): number {
|
|
310
|
+
return dir.value === "right" ? window.innerWidth - x : x
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function onTouchStart(e: TouchEvent) {
|
|
314
|
+
if (track) dropTrack()
|
|
315
|
+
if (e.touches.length !== 1 || !edgeEnabled() || anyModalOpen() || edgeOwner.value) return
|
|
316
|
+
const t = e.touches[0]
|
|
317
|
+
if (inward(t.clientX) > props.edgeSize || scrollsWithPull(e.target)) return
|
|
318
|
+
track = { id: t.identifier, x: t.clientX, y: t.clientY, t: e.timeStamp, lastX: t.clientX, lastT: e.timeStamp, speed: 0 }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function onTouchMove(e: TouchEvent) {
|
|
322
|
+
if (!track) return
|
|
323
|
+
const t = Array.from(e.changedTouches).find((x) => x.identifier === track!.id)
|
|
324
|
+
if (!t) return
|
|
325
|
+
const sign = dir.value === "right" ? -1 : 1
|
|
326
|
+
const dx = (t.clientX - track.x) * sign
|
|
327
|
+
const dy = t.clientY - track.y
|
|
328
|
+
if (!peeking.value) {
|
|
329
|
+
if (Math.abs(dy) > EDGE_SLOP && Math.abs(dy) > Math.abs(dx)) {
|
|
330
|
+
track = null
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
if (dx < EDGE_SLOP || Math.abs(dx) < Math.abs(dy)) return
|
|
334
|
+
if (edgeOwner.value && edgeOwner.value !== owner) {
|
|
335
|
+
track = null
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
edgeOwner.value = owner
|
|
256
339
|
peeking.value = true
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
340
|
+
}
|
|
341
|
+
if (e.cancelable) e.preventDefault()
|
|
342
|
+
const dt = e.timeStamp - track.lastT
|
|
343
|
+
if (dt > 0) track.speed = (((t.clientX - track.lastX) * sign) / dt) * 0.6 + track.speed * 0.4
|
|
344
|
+
track.lastX = t.clientX
|
|
345
|
+
track.lastT = e.timeStamp
|
|
346
|
+
peek.value = Math.max(0, Math.min(1, inward(t.clientX) / panelPx()))
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function dropTrack() {
|
|
350
|
+
track = null
|
|
351
|
+
if (edgeOwner.value === owner) edgeOwner.value = null
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function settle() {
|
|
355
|
+
const flick = track !== null && track.speed > FLICK_SPEED && peek.value > 0.05
|
|
356
|
+
dropTrack()
|
|
357
|
+
if (!peeking.value) return
|
|
358
|
+
peeking.value = false
|
|
359
|
+
if (peek.value >= props.edgeOpenThreshold || flick) {
|
|
360
|
+
peek.value = 1
|
|
361
|
+
handingOff.value = true
|
|
362
|
+
skipOpenSlide.value = true
|
|
363
|
+
timers.push(
|
|
274
364
|
window.setTimeout(() => {
|
|
275
365
|
emit("update:open", true)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
366
|
+
timers.push(
|
|
367
|
+
window.setTimeout(() => {
|
|
368
|
+
handingOff.value = false
|
|
369
|
+
peek.value = 0
|
|
370
|
+
}, 60),
|
|
371
|
+
)
|
|
372
|
+
}, reducedMotion() ? 0 : 180),
|
|
373
|
+
)
|
|
374
|
+
} else {
|
|
375
|
+
peek.value = 0
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const timers: number[] = []
|
|
380
|
+
onBeforeUnmount(() => {
|
|
381
|
+
for (const id of timers) window.clearTimeout(id)
|
|
382
|
+
if (edgeOwner.value === owner) edgeOwner.value = null
|
|
287
383
|
})
|
|
288
384
|
|
|
385
|
+
function onTouchEnd(e: TouchEvent) {
|
|
386
|
+
if (track && !Array.from(e.changedTouches).some((x) => x.identifier === track!.id)) return
|
|
387
|
+
settle()
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (typeof document !== "undefined") {
|
|
391
|
+
watch(
|
|
392
|
+
() => props.edgeOpen && isHorizontal.value,
|
|
393
|
+
(on, _, onCleanup) => {
|
|
394
|
+
if (!on) return
|
|
395
|
+
const passive = { passive: true }
|
|
396
|
+
document.addEventListener("touchstart", onTouchStart, passive)
|
|
397
|
+
document.addEventListener("touchmove", onTouchMove, { passive: false })
|
|
398
|
+
document.addEventListener("touchend", onTouchEnd, passive)
|
|
399
|
+
document.addEventListener("touchcancel", onTouchEnd, passive)
|
|
400
|
+
onCleanup(() => {
|
|
401
|
+
document.removeEventListener("touchstart", onTouchStart)
|
|
402
|
+
document.removeEventListener("touchmove", onTouchMove)
|
|
403
|
+
document.removeEventListener("touchend", onTouchEnd)
|
|
404
|
+
document.removeEventListener("touchcancel", onTouchEnd)
|
|
405
|
+
})
|
|
406
|
+
},
|
|
407
|
+
{ immediate: true },
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
|
|
289
411
|
// The preview panel slides with the finger: translate from fully-off (100%)
|
|
290
412
|
// toward 0 as peek → 1. Kept visible through the hand-off so the real drawer
|
|
291
413
|
// can appear underneath without a flash.
|
|
@@ -295,51 +417,34 @@ const previewStyle = computed(() => {
|
|
|
295
417
|
const axis = dir.value === "right" ? off : -off
|
|
296
418
|
return {
|
|
297
419
|
transform: `translateX(${axis}%)`,
|
|
298
|
-
transition: peeking.value ? "none" : "transform 0.
|
|
420
|
+
transition: peeking.value || reducedMotion() ? "none" : "transform 0.18s cubic-bezier(0.32, 0.72, 0, 1)",
|
|
299
421
|
pointerEvents: "none" as const,
|
|
300
422
|
}
|
|
301
423
|
})
|
|
302
|
-
|
|
303
|
-
const edgeStripStyle = computed(() => {
|
|
304
|
-
const w = `${props.edgeSize}px`
|
|
305
|
-
return dir.value === "right"
|
|
306
|
-
? { right: "0", width: w }
|
|
307
|
-
: { left: "0", width: w }
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
// The strip must only exist below the host's mobile breakpoint. It is hidden
|
|
311
|
-
// with the same Tailwind breakpoint classes the rest of the mobile logic
|
|
312
|
-
// (LpSidebar rail, burger) uses — no JS media query needed.
|
|
313
|
-
const EDGE_STRIP_HIDDEN: Record<NonNullable<typeof props.edgeBreakpoint>, string> = {
|
|
314
|
-
sm: "sm:hidden",
|
|
315
|
-
md: "md:hidden",
|
|
316
|
-
lg: "lg:hidden",
|
|
317
|
-
xl: "xl:hidden",
|
|
318
|
-
}
|
|
319
|
-
const edgeStripClass = computed(() =>
|
|
320
|
-
props.edgeBreakpoint ? EDGE_STRIP_HIDDEN[props.edgeBreakpoint] : "",
|
|
321
|
-
)
|
|
322
424
|
</script>
|
|
323
425
|
|
|
324
426
|
<template>
|
|
325
|
-
<!-- Edge-drag-to-open:
|
|
326
|
-
|
|
427
|
+
<!-- Edge-drag-to-open: a finger-following preview of the panel (with its
|
|
428
|
+
real content, inert) and a scrim that darkens with the pull. Nothing
|
|
429
|
+
overlays the screen edge, so taps there reach the page. -->
|
|
327
430
|
<template v-if="edgeOpen && isHorizontal">
|
|
328
431
|
<div
|
|
329
|
-
|
|
330
|
-
class="
|
|
331
|
-
:
|
|
332
|
-
:style="edgeStripStyle"
|
|
333
|
-
aria-hidden="true"
|
|
432
|
+
v-show="peeking || peek > 0"
|
|
433
|
+
class="lp-scrim pointer-events-none fixed inset-0"
|
|
434
|
+
:style="{ zIndex: layer.scrim, opacity: peek, transition: peeking || reducedMotion() ? 'none' : 'opacity 0.18s ease-out' }"
|
|
334
435
|
/>
|
|
335
436
|
<div
|
|
336
437
|
v-show="peeking || peek > 0"
|
|
337
|
-
class="
|
|
338
|
-
:style="{
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
438
|
+
:class="contentClass"
|
|
439
|
+
:style="[sizeStyle, previewStyle, { zIndex: layer.panel }]"
|
|
440
|
+
aria-hidden="true"
|
|
441
|
+
inert
|
|
442
|
+
>
|
|
443
|
+
<header v-if="hasHeader" class="flex items-start justify-between gap-4" :class="headerClass">
|
|
342
444
|
<span class="text-base font-semibold text-ink">{{ title }}</span>
|
|
445
|
+
</header>
|
|
446
|
+
<div class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden" :class="padClass">
|
|
447
|
+
<slot v-if="peeking || peek > 0" />
|
|
343
448
|
</div>
|
|
344
449
|
</div>
|
|
345
450
|
</template>
|
|
@@ -362,7 +467,7 @@ const edgeStripClass = computed(() =>
|
|
|
362
467
|
<DrawerContent
|
|
363
468
|
:class="contentClass"
|
|
364
469
|
:style="[sizeStyle, { zIndex: layer.panel }]"
|
|
365
|
-
:data-vaul-animate="handingOff ? 'false' : undefined"
|
|
470
|
+
:data-vaul-animate="handingOff || skipOpenSlide ? 'false' : undefined"
|
|
366
471
|
>
|
|
367
472
|
<!-- Resize grip on the inner edge (horizontal drawers). data-vaul-no-drag
|
|
368
473
|
so grabbing it resizes the panel instead of starting a vaul drag. -->
|