@leavepulse/ui 0.37.3 → 0.37.5
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/{LpAddressInput-C8NZWkH_.js → LpAddressInput-DOVpUONY.js} +1 -1
- package/dist/chunks/{LpAppShell-Cs9jDoqJ.js → LpAppShell-1rTB24lI.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-B6RAvDES.js → LpConfirmDialog-BH7az_E7.js} +1 -1
- package/dist/chunks/{LpDrawer-CslZtgUm.js → LpDrawer-E67O8fFP.js} +147 -61
- package/dist/chunks/{LpModal-D3QxgWaT.js → LpModal-Dq_qXN8-.js} +31 -32
- package/dist/chunks/{LpModalNav-Dkogc3O_.js → LpModalNav-B42a7R-3.js} +2 -2
- package/dist/chunks/{LpSidebar-DvGfofv6.js → LpSidebar-DvxMte3k.js} +1 -1
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpAppShell.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpDrawer.vue.d.ts +87 -28
- package/dist/components/LpDrawer.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +7 -7
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.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 +7 -7
- package/package.json +1 -1
- package/src/components/LpDrawer.vue +191 -86
- package/src/components/LpModal.vue +31 -46
- package/src/components/modalDepth.ts +5 -0
|
@@ -6,7 +6,7 @@ import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
|
|
|
6
6
|
import { _ as _sfc_main$1 } from "./LpFormField-CazBc2-a.js";
|
|
7
7
|
import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
|
|
8
8
|
import { _ as _sfc_main$5 } from "./LpInput-9EXWUM9B.js";
|
|
9
|
-
import { _ as _sfc_main$7 } from "./LpModal-
|
|
9
|
+
import { _ as _sfc_main$7 } from "./LpModal-Dq_qXN8-.js";
|
|
10
10
|
import { _ as _sfc_main$6 } from "./LpSelect-ANIVtrpM.js";
|
|
11
11
|
import { useMergedAttrs } from "../composables/useMergedClass.js";
|
|
12
12
|
const _hoisted_1 = { class: "flex items-stretch gap-2" };
|
|
@@ -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,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent, computed, openBlock, createBlock, createSlots, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString, createElementVNode, normalizeClass } from "vue";
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
3
|
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
|
-
import { _ as _sfc_main$1 } from "./LpModal-
|
|
4
|
+
import { _ as _sfc_main$1 } from "./LpModal-Dq_qXN8-.js";
|
|
5
5
|
const _hoisted_1 = { class: "inline-flex items-center gap-2.5" };
|
|
6
6
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
7
7
|
__name: "LpConfirmDialog",
|
|
@@ -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", {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, createElementVNode, createElementBlock, renderSlot, createCommentVNode,
|
|
1
|
+
import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, createElementVNode, mergeProps, createElementBlock, renderSlot, createCommentVNode, normalizeClass, createTextVNode, toDisplayString } from "vue";
|
|
2
2
|
import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
|
|
3
3
|
import { useShift } from "../composables/useShift.js";
|
|
4
4
|
import { useModalLayer } from "../components/modalDepth.js";
|
|
@@ -8,7 +8,7 @@ import { _ as _sfc_main$2 } from "./LpScrollArea-D8xyKXcF.js";
|
|
|
8
8
|
const _hoisted_1 = {
|
|
9
9
|
key: 0,
|
|
10
10
|
"data-lp-modal-aside": "",
|
|
11
|
-
class: "
|
|
11
|
+
class: "flex min-h-0 flex-col"
|
|
12
12
|
};
|
|
13
13
|
const _hoisted_2 = {
|
|
14
14
|
key: 0,
|
|
@@ -22,7 +22,7 @@ const _hoisted_4 = {
|
|
|
22
22
|
const _hoisted_5 = {
|
|
23
23
|
key: 1,
|
|
24
24
|
"data-lp-modal-aside": "",
|
|
25
|
-
class: "
|
|
25
|
+
class: "flex min-h-0 flex-col"
|
|
26
26
|
};
|
|
27
27
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
28
28
|
__name: "LpModal",
|
|
@@ -83,8 +83,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
83
83
|
];
|
|
84
84
|
function onOpenAutoFocus(event) {
|
|
85
85
|
if (props.autoFocus === "none") return;
|
|
86
|
-
const panel = panelRef.value
|
|
87
|
-
if (!
|
|
86
|
+
const panel = panelRef.value;
|
|
87
|
+
if (!panel) return;
|
|
88
88
|
const selectors = props.autoFocus === "auto" ? AUTO_FOCUS_ORDER : [props.autoFocus];
|
|
89
89
|
for (const selector of selectors) {
|
|
90
90
|
const target = panel.querySelector(selector);
|
|
@@ -110,8 +110,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
110
110
|
watch(
|
|
111
111
|
() => props.open ? panelRef.value : null,
|
|
112
112
|
(panel) => {
|
|
113
|
-
|
|
114
|
-
sizedEl.value = el instanceof HTMLElement ? el : null;
|
|
113
|
+
sizedEl.value = panel;
|
|
115
114
|
},
|
|
116
115
|
{ flush: "post" }
|
|
117
116
|
);
|
|
@@ -131,24 +130,24 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
131
130
|
class: "fixed inset-0 flex items-center justify-center pointer-events-none",
|
|
132
131
|
style: normalizeStyle({ zIndex: unref(layer).panel })
|
|
133
132
|
}, [
|
|
134
|
-
|
|
135
|
-
class: "flex items-start",
|
|
136
|
-
style:
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
133
|
+
createVNode(unref(DialogContent), mergeProps({
|
|
134
|
+
class: "pointer-events-auto flex min-h-0 items-start outline-none data-[state=open]:animate-[rise-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[rise-out_120ms_cubic-bezier(0.4,0,1,1)]",
|
|
135
|
+
style: { maxHeight: "min(90vh, calc(100dvh - 2rem))" }
|
|
136
|
+
}, describedByAttrs.value, {
|
|
137
|
+
onOpenAutoFocus,
|
|
138
|
+
onInteractOutside,
|
|
139
|
+
onPointerDownOutside: onInteractOutside
|
|
140
|
+
}), {
|
|
141
|
+
default: withCtx(() => [
|
|
142
|
+
_ctx.$slots.aside ? (openBlock(), createElementBlock("aside", _hoisted_1, [
|
|
143
|
+
renderSlot(_ctx.$slots, "aside")
|
|
144
|
+
])) : createCommentVNode("", true),
|
|
145
|
+
createElementVNode("div", {
|
|
146
|
+
ref_key: "panelRef",
|
|
147
|
+
ref: panelRef,
|
|
148
|
+
class: normalizeClass(["flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel", [widthClass.value, unref(tweening) ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]]),
|
|
149
|
+
style: normalizeStyle({ ...__props.width ? { width: __props.width } : {}, ...hasAside.value ? { alignSelf: "stretch" } : {} })
|
|
150
|
+
}, [
|
|
152
151
|
__props.title || _ctx.$slots.title ? (openBlock(), createElementBlock("header", _hoisted_2, [
|
|
153
152
|
createElementVNode("div", _hoisted_3, [
|
|
154
153
|
createVNode(unref(DialogTitle), { class: "text-base font-semibold text-ink" }, {
|
|
@@ -202,13 +201,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
202
201
|
_ctx.$slots.footer ? (openBlock(), createElementBlock("footer", _hoisted_4, [
|
|
203
202
|
renderSlot(_ctx.$slots, "footer")
|
|
204
203
|
])) : createCommentVNode("", true)
|
|
205
|
-
]),
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
], 6),
|
|
205
|
+
_ctx.$slots.asideEnd ? (openBlock(), createElementBlock("aside", _hoisted_5, [
|
|
206
|
+
renderSlot(_ctx.$slots, "asideEnd")
|
|
207
|
+
])) : createCommentVNode("", true)
|
|
208
|
+
]),
|
|
209
|
+
_: 3
|
|
210
|
+
}, 16)
|
|
212
211
|
], 4)
|
|
213
212
|
]),
|
|
214
213
|
_: 3
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineComponent, ref, computed, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementVNode, normalizeClass, createVNode, createCommentVNode, createTextVNode, toDisplayString } from "vue";
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
3
|
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
|
-
import { _ as _sfc_main$1 } from "./LpModal-
|
|
5
|
-
import { _ as _sfc_main$4 } from "./LpSidebar-
|
|
4
|
+
import { _ as _sfc_main$1 } from "./LpModal-Dq_qXN8-.js";
|
|
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;
|
|
@@ -26,19 +26,19 @@ type __VLS_Props = {
|
|
|
26
26
|
*/
|
|
27
27
|
autoFocus?: "auto" | "none" | string;
|
|
28
28
|
};
|
|
29
|
-
declare var
|
|
29
|
+
declare var __VLS_31: {}, __VLS_39: {}, __VLS_58: {}, __VLS_66: {}, __VLS_68: {}, __VLS_70: {};
|
|
30
30
|
type __VLS_Slots = {} & {
|
|
31
|
-
aside?: (props: typeof
|
|
31
|
+
aside?: (props: typeof __VLS_31) => any;
|
|
32
32
|
} & {
|
|
33
|
-
title?: (props: typeof
|
|
33
|
+
title?: (props: typeof __VLS_39) => any;
|
|
34
34
|
} & {
|
|
35
|
-
default?: (props: typeof
|
|
35
|
+
default?: (props: typeof __VLS_58) => any;
|
|
36
36
|
} & {
|
|
37
|
-
default?: (props: typeof
|
|
37
|
+
default?: (props: typeof __VLS_66) => any;
|
|
38
38
|
} & {
|
|
39
|
-
footer?: (props: typeof
|
|
39
|
+
footer?: (props: typeof __VLS_68) => any;
|
|
40
40
|
} & {
|
|
41
|
-
asideEnd?: (props: typeof
|
|
41
|
+
asideEnd?: (props: typeof __VLS_70) => any;
|
|
42
42
|
};
|
|
43
43
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
44
44
|
"update:open": (value: boolean) => any;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineComponent, openBlock, createBlock, withCtx, createElementVNode, createElementBlock, Fragment, renderList, toDisplayString, unref } from "vue";
|
|
2
|
-
import { _ as _sfc_main$1 } from "../chunks/LpModal-
|
|
2
|
+
import { _ as _sfc_main$1 } from "../chunks/LpModal-Dq_qXN8-.js";
|
|
3
3
|
import { appleModifierLabel } from "../composables/useHotkeys.js";
|
|
4
4
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
5
5
|
import '../assets/LpShortcutsDialog-B7H-RZLQ.css';const _hoisted_1 = { class: "shortcut-groups" };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
|
|
2
|
-
import { _ as _2 } from "./chunks/LpAddressInput-
|
|
2
|
+
import { _ as _2 } from "./chunks/LpAddressInput-DOVpUONY.js";
|
|
3
3
|
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";
|
|
@@ -19,12 +19,12 @@ import { _ as _15 } from "./chunks/LpCodeBlock-Bo0B4TFR.js";
|
|
|
19
19
|
import { _ as _16 } from "./chunks/LpColorPicker-CFC6iaNK.js";
|
|
20
20
|
import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
|
|
21
21
|
import { default as default3 } from "./components/LpCommandPalette.vue.js";
|
|
22
|
-
import { _ as _17 } from "./chunks/LpConfirmDialog-
|
|
22
|
+
import { _ as _17 } from "./chunks/LpConfirmDialog-BH7az_E7.js";
|
|
23
23
|
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";
|
|
@@ -40,8 +40,8 @@ import { fileNameOf } from "./components/lightbox.js";
|
|
|
40
40
|
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
|
-
import { _ as _34 } from "./chunks/LpModal-
|
|
44
|
-
import { _ as _35 } from "./chunks/LpModalNav-
|
|
43
|
+
import { _ as _34 } from "./chunks/LpModal-Dq_qXN8-.js";
|
|
44
|
+
import { _ as _35 } from "./chunks/LpModalNav-B42a7R-3.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. -->
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
DialogRoot,
|
|
9
9
|
DialogTitle,
|
|
10
10
|
} from "reka-ui"
|
|
11
|
-
import type { ComponentPublicInstance } from "vue"
|
|
12
11
|
import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
|
|
13
12
|
import { useShift } from "../composables/useShift"
|
|
14
13
|
import { useModalLayer } from "./modalDepth"
|
|
@@ -143,7 +142,7 @@ const bodyPad = computed(() =>
|
|
|
143
142
|
* scroll area exactly as before. A panel whose size never moves is pinned once
|
|
144
143
|
* and never transitions.
|
|
145
144
|
*/
|
|
146
|
-
const panelRef = ref<
|
|
145
|
+
const panelRef = ref<HTMLElement | null>(null)
|
|
147
146
|
|
|
148
147
|
/**
|
|
149
148
|
* Controls that are worth landing on when the dialog opens, in order of
|
|
@@ -171,8 +170,8 @@ const AUTO_FOCUS_ORDER = [
|
|
|
171
170
|
*/
|
|
172
171
|
function onOpenAutoFocus(event: Event) {
|
|
173
172
|
if (props.autoFocus === "none") return
|
|
174
|
-
const panel = panelRef.value
|
|
175
|
-
if (!
|
|
173
|
+
const panel = panelRef.value
|
|
174
|
+
if (!panel) return
|
|
176
175
|
|
|
177
176
|
const selectors =
|
|
178
177
|
props.autoFocus === "auto" ? AUTO_FOCUS_ORDER : [props.autoFocus]
|
|
@@ -210,13 +209,10 @@ const {
|
|
|
210
209
|
resizing,
|
|
211
210
|
} = useShift({ axis: "height", enabled: () => props.open })
|
|
212
211
|
|
|
213
|
-
// reka renders a real element but exposes it as a component instance, so the
|
|
214
|
-
// sized element is handed over through $el once the panel has mounted.
|
|
215
212
|
watch(
|
|
216
213
|
() => (props.open ? panelRef.value : null),
|
|
217
214
|
(panel) => {
|
|
218
|
-
|
|
219
|
-
sizedEl.value = el instanceof HTMLElement ? el : null
|
|
215
|
+
sizedEl.value = panel
|
|
220
216
|
},
|
|
221
217
|
{ flush: "post" },
|
|
222
218
|
)
|
|
@@ -265,48 +261,38 @@ watch(
|
|
|
265
261
|
class="fixed inset-0 flex items-center justify-center pointer-events-none"
|
|
266
262
|
:style="{ zIndex: layer.panel }"
|
|
267
263
|
>
|
|
268
|
-
<!-- The dialog and its
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
264
|
+
<!-- The dialog and its asides stand in one row, and that row IS the
|
|
265
|
+
DialogContent. An aside rendered as a sibling of DialogContent sat
|
|
266
|
+
outside the modal: reka marks everything outside aria-hidden and traps
|
|
267
|
+
focus inside, so a section list beside the dialog could be neither
|
|
268
|
+
tabbed to nor read by a screen reader. Inside DialogContent it belongs
|
|
269
|
+
to the dialog, while the visible card is the inner panel, so nothing
|
|
270
|
+
moves on screen.
|
|
271
|
+
`items-start`: the row collapses to the taller of the two, both hang
|
|
272
|
+
from its top edge, and neither is stretched. Without a description
|
|
273
|
+
reka-ui warns on every open, and the opt-out it checks for is an
|
|
274
|
+
ABSENT `aria-describedby`; a caller that passes a description keeps
|
|
275
|
+
the link to DialogDescription. -->
|
|
276
|
+
<DialogContent
|
|
277
|
+
class="pointer-events-auto flex min-h-0 items-start outline-none data-[state=open]:animate-[rise-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[rise-out_120ms_cubic-bezier(0.4,0,1,1)]"
|
|
278
|
+
:style="{ maxHeight: 'min(90vh, calc(100dvh - 2rem))' }"
|
|
279
|
+
v-bind="describedByAttrs"
|
|
280
|
+
@open-auto-focus="onOpenAutoFocus"
|
|
281
|
+
@interact-outside="onInteractOutside"
|
|
282
|
+
@pointer-down-outside="onInteractOutside"
|
|
277
283
|
>
|
|
278
|
-
<!-- Bound to `open`, not to the slot alone: DialogContent unmounts itself
|
|
279
|
-
with the dialog, but an aside is an ordinary node in the portal and
|
|
280
|
-
stayed on screen after the dialog closed — a section list floating
|
|
281
|
-
over the page with nothing behind it.
|
|
282
|
-
|
|
283
|
-
`pointer-events-auto` because the wrapper lets clicks through to the
|
|
284
|
-
overlay, and the aside is not the overlay. `data-lp-modal-aside` is
|
|
285
|
-
what tells `interact-outside` a click here is not a click on the
|
|
286
|
-
scrim — without it the dialog closes under the pointer. -->
|
|
287
284
|
<aside
|
|
288
|
-
v-if="
|
|
285
|
+
v-if="$slots.aside"
|
|
289
286
|
data-lp-modal-aside
|
|
290
|
-
class="
|
|
287
|
+
class="flex min-h-0 flex-col"
|
|
291
288
|
>
|
|
292
289
|
<slot name="aside" />
|
|
293
290
|
</aside>
|
|
294
|
-
|
|
295
|
-
checks for is an ABSENT `aria-describedby` — despite the message
|
|
296
|
-
naming the string "undefined" (a leftover from Radix, where
|
|
297
|
-
`aria-describedby={undefined}` is how you drop the attribute).
|
|
298
|
-
Dropping it here is what silences the warning; a caller that does
|
|
299
|
-
pass a description keeps the generated id and the link to
|
|
300
|
-
DialogDescription. -->
|
|
301
|
-
<DialogContent
|
|
291
|
+
<div
|
|
302
292
|
ref="panelRef"
|
|
303
|
-
class="
|
|
293
|
+
class="flex max-h-[min(90vh,calc(100dvh-2rem))] min-h-0 flex-col overflow-hidden rounded-card border border-line bg-surface-raised shadow-panel"
|
|
304
294
|
:class="[widthClass, tweening ? 'transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none' : '']"
|
|
305
295
|
:style="{ ...(width ? { width } : {}), ...(hasAside ? { alignSelf: 'stretch' } : {}) }"
|
|
306
|
-
v-bind="describedByAttrs"
|
|
307
|
-
@open-auto-focus="onOpenAutoFocus"
|
|
308
|
-
@interact-outside="onInteractOutside"
|
|
309
|
-
@pointer-down-outside="onInteractOutside"
|
|
310
296
|
>
|
|
311
297
|
<header v-if="title || $slots.title" class="flex shrink-0 items-start justify-between gap-4 p-5 pb-3">
|
|
312
298
|
<div class="flex flex-col gap-1">
|
|
@@ -357,16 +343,15 @@ watch(
|
|
|
357
343
|
<footer v-if="$slots.footer" class="flex shrink-0 justify-end gap-2 p-5 pt-4">
|
|
358
344
|
<slot name="footer" />
|
|
359
345
|
</footer>
|
|
360
|
-
</
|
|
361
|
-
|
|
346
|
+
</div>
|
|
362
347
|
<aside
|
|
363
|
-
v-if="
|
|
348
|
+
v-if="$slots.asideEnd"
|
|
364
349
|
data-lp-modal-aside
|
|
365
|
-
class="
|
|
350
|
+
class="flex min-h-0 flex-col"
|
|
366
351
|
>
|
|
367
352
|
<slot name="asideEnd" />
|
|
368
353
|
</aside>
|
|
369
|
-
</
|
|
354
|
+
</DialogContent>
|
|
370
355
|
</div>
|
|
371
356
|
</DialogPortal>
|
|
372
357
|
</DialogRoot>
|