@leavepulse/ui 0.19.0 → 0.19.1
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-CWlfDyKz.js → LpAddressInput-N8O-LtCS.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-JNW4rpx3.js → LpConfirmDialog-B-amVJsM.js} +1 -1
- package/dist/chunks/{LpModal-BJTiTSDJ.js → LpModal-vlrgcEY_.js} +74 -4
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +5 -5
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/src/components/LpModal.vue +122 -3
|
@@ -6,7 +6,7 @@ import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
|
|
|
6
6
|
import { _ as _sfc_main$1 } from "./LpFormField-Bn-ZwC6z.js";
|
|
7
7
|
import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
|
|
8
8
|
import { _ as _sfc_main$5 } from "./LpInput-BauZRyzm.js";
|
|
9
|
-
import { _ as _sfc_main$7 } from "./LpModal-
|
|
9
|
+
import { _ as _sfc_main$7 } from "./LpModal-vlrgcEY_.js";
|
|
10
10
|
import { _ as _sfc_main$6 } from "./LpSelect-D5mU4cZO.js";
|
|
11
11
|
const _hoisted_1 = { class: "flex flex-col gap-3" };
|
|
12
12
|
const _hoisted_2 = { class: "flex items-stretch gap-2" };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineComponent, openBlock, createBlock, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString } from "vue";
|
|
2
2
|
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
|
-
import { _ as _sfc_main$1 } from "./LpModal-
|
|
3
|
+
import { _ as _sfc_main$1 } from "./LpModal-vlrgcEY_.js";
|
|
4
4
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
5
5
|
__name: "LpConfirmDialog",
|
|
6
6
|
props: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineComponent, computed, useSlots, openBlock, createBlock, unref, withCtx, createVNode, createElementVNode, mergeProps, createElementBlock, renderSlot, createTextVNode, toDisplayString, createCommentVNode, normalizeClass } from "vue";
|
|
1
|
+
import { defineComponent, computed, useSlots, ref, watch, onBeforeUnmount, openBlock, createBlock, unref, withCtx, createVNode, createElementVNode, mergeProps, createElementBlock, renderSlot, createTextVNode, toDisplayString, createCommentVNode, normalizeClass } from "vue";
|
|
2
2
|
import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
|
|
3
3
|
import { CLOSE_ICON } from "../components/dropdown.js";
|
|
4
4
|
import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
|
|
@@ -13,6 +13,7 @@ const _hoisted_4 = {
|
|
|
13
13
|
key: 3,
|
|
14
14
|
class: "flex shrink-0 justify-end gap-2 p-5 pt-4"
|
|
15
15
|
};
|
|
16
|
+
const TWEEN_MS = 160;
|
|
16
17
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
17
18
|
__name: "LpModal",
|
|
18
19
|
props: {
|
|
@@ -49,6 +50,73 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
49
50
|
slots.footer ? "" : "pb-5"
|
|
50
51
|
].filter(Boolean).join(" ")
|
|
51
52
|
);
|
|
53
|
+
const panelRef = ref(null);
|
|
54
|
+
const tweening = ref(false);
|
|
55
|
+
const resizing = ref(false);
|
|
56
|
+
let ro;
|
|
57
|
+
let mo;
|
|
58
|
+
let raf = 0;
|
|
59
|
+
let settleTimer;
|
|
60
|
+
const retune = (el) => {
|
|
61
|
+
const from = el.offsetHeight;
|
|
62
|
+
el.style.height = "";
|
|
63
|
+
const to = el.offsetHeight;
|
|
64
|
+
if (to === 0) return;
|
|
65
|
+
if (to === from) {
|
|
66
|
+
el.style.height = `${to}px`;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
el.style.height = `${from}px`;
|
|
70
|
+
void el.offsetHeight;
|
|
71
|
+
el.style.height = `${to}px`;
|
|
72
|
+
resizing.value = true;
|
|
73
|
+
clearTimeout(settleTimer);
|
|
74
|
+
settleTimer = setTimeout(() => {
|
|
75
|
+
resizing.value = false;
|
|
76
|
+
}, TWEEN_MS);
|
|
77
|
+
};
|
|
78
|
+
watch(
|
|
79
|
+
() => props.open,
|
|
80
|
+
(isOpen) => {
|
|
81
|
+
ro?.disconnect();
|
|
82
|
+
mo?.disconnect();
|
|
83
|
+
ro = void 0;
|
|
84
|
+
mo = void 0;
|
|
85
|
+
cancelAnimationFrame(raf);
|
|
86
|
+
clearTimeout(settleTimer);
|
|
87
|
+
tweening.value = false;
|
|
88
|
+
resizing.value = false;
|
|
89
|
+
const el = panelRef.value?.$el;
|
|
90
|
+
if (!isOpen || !(el instanceof HTMLElement)) return;
|
|
91
|
+
el.style.height = `${el.offsetHeight}px`;
|
|
92
|
+
raf = requestAnimationFrame(() => {
|
|
93
|
+
tweening.value = true;
|
|
94
|
+
});
|
|
95
|
+
const watchChildren = () => {
|
|
96
|
+
if (!ro) return;
|
|
97
|
+
ro.disconnect();
|
|
98
|
+
for (const child of Array.from(el.children)) ro.observe(child);
|
|
99
|
+
};
|
|
100
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
101
|
+
ro = new ResizeObserver(() => retune(el));
|
|
102
|
+
watchChildren();
|
|
103
|
+
}
|
|
104
|
+
if (typeof MutationObserver !== "undefined") {
|
|
105
|
+
mo = new MutationObserver(() => {
|
|
106
|
+
watchChildren();
|
|
107
|
+
retune(el);
|
|
108
|
+
});
|
|
109
|
+
mo.observe(el, { childList: true, subtree: true, characterData: true });
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{ flush: "post" }
|
|
113
|
+
);
|
|
114
|
+
onBeforeUnmount(() => {
|
|
115
|
+
ro?.disconnect();
|
|
116
|
+
mo?.disconnect();
|
|
117
|
+
cancelAnimationFrame(raf);
|
|
118
|
+
clearTimeout(settleTimer);
|
|
119
|
+
});
|
|
52
120
|
return (_ctx, _cache) => {
|
|
53
121
|
return openBlock(), createBlock(unref(DialogRoot), {
|
|
54
122
|
open: __props.open,
|
|
@@ -60,7 +128,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
60
128
|
createVNode(unref(DialogOverlay), { class: "lp-scrim fixed inset-0 z-(--z-overlay) backdrop-blur-sm data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]" }),
|
|
61
129
|
createElementVNode("div", _hoisted_1, [
|
|
62
130
|
createVNode(unref(DialogContent), mergeProps({
|
|
63
|
-
|
|
131
|
+
ref_key: "panelRef",
|
|
132
|
+
ref: panelRef,
|
|
133
|
+
class: ["pointer-events-auto 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 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)]", [widthClass.value, tweening.value ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]],
|
|
64
134
|
style: __props.width ? { width: __props.width } : void 0
|
|
65
135
|
}, describedByAttrs.value), {
|
|
66
136
|
default: withCtx(() => [
|
|
@@ -105,14 +175,14 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
105
175
|
renderSlot(_ctx.$slots, "default")
|
|
106
176
|
], 2)) : (openBlock(), createBlock(_sfc_main$2, {
|
|
107
177
|
key: 2,
|
|
108
|
-
class: "min-h-0 flex-1 text-sm text-ink/90",
|
|
178
|
+
class: normalizeClass(["min-h-0 flex-1 text-sm text-ink/90", resizing.value ? "[&_[data-scrollbarimpl]]:invisible" : ""]),
|
|
109
179
|
"content-class": bodyPad.value
|
|
110
180
|
}, {
|
|
111
181
|
default: withCtx(() => [
|
|
112
182
|
renderSlot(_ctx.$slots, "default")
|
|
113
183
|
]),
|
|
114
184
|
_: 3
|
|
115
|
-
}, 8, ["content-class"])),
|
|
185
|
+
}, 8, ["class", "content-class"])),
|
|
116
186
|
_ctx.$slots.footer ? (openBlock(), createElementBlock("footer", _hoisted_4, [
|
|
117
187
|
renderSlot(_ctx.$slots, "footer")
|
|
118
188
|
])) : createCommentVNode("", true)
|
|
@@ -16,15 +16,15 @@ type __VLS_Props = {
|
|
|
16
16
|
*/
|
|
17
17
|
fillBody?: boolean;
|
|
18
18
|
};
|
|
19
|
-
declare var
|
|
19
|
+
declare var __VLS_35: {}, __VLS_54: {}, __VLS_62: {}, __VLS_64: {};
|
|
20
20
|
type __VLS_Slots = {} & {
|
|
21
|
-
title?: (props: typeof
|
|
21
|
+
title?: (props: typeof __VLS_35) => any;
|
|
22
22
|
} & {
|
|
23
|
-
default?: (props: typeof
|
|
23
|
+
default?: (props: typeof __VLS_54) => any;
|
|
24
24
|
} & {
|
|
25
|
-
default?: (props: typeof
|
|
25
|
+
default?: (props: typeof __VLS_62) => any;
|
|
26
26
|
} & {
|
|
27
|
-
footer?: (props: typeof
|
|
27
|
+
footer?: (props: typeof __VLS_64) => any;
|
|
28
28
|
};
|
|
29
29
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
30
30
|
"update:open": (value: boolean) => any;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ } from "./chunks/LayoutCanvas-Dd20boUG.js";
|
|
2
|
-
import { _ as _2 } from "./chunks/LpAddressInput-
|
|
2
|
+
import { _ as _2 } from "./chunks/LpAddressInput-N8O-LtCS.js";
|
|
3
3
|
import { _ as _3 } from "./chunks/LpMapPicker-DFKrd1Ne.js";
|
|
4
4
|
import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
|
|
5
5
|
import { _ as _4 } from "./chunks/LpAlert-BAYHDSS1.js";
|
|
@@ -14,7 +14,7 @@ import { _ as _12 } from "./chunks/LpCard-BmrTvWOe.js";
|
|
|
14
14
|
import { _ as _13 } from "./chunks/LpCheckbox-BO0zuuPh.js";
|
|
15
15
|
import { _ as _14 } from "./chunks/LpCodeBlock-CSiOSPs8.js";
|
|
16
16
|
import { default as default2 } from "./components/LpCommandPalette.vue.js";
|
|
17
|
-
import { _ as _15 } from "./chunks/LpConfirmDialog-
|
|
17
|
+
import { _ as _15 } from "./chunks/LpConfirmDialog-B-amVJsM.js";
|
|
18
18
|
import { _ as _16 } from "./chunks/LpContextMenu-D0mzXqA7.js";
|
|
19
19
|
import { _ as _17 } from "./chunks/LpDatePicker-dY2q4dr4.js";
|
|
20
20
|
import { _ as _18 } from "./chunks/LpDisclosure-DP6yfgfM.js";
|
|
@@ -32,7 +32,7 @@ import { _ as _28 } from "./chunks/LpLightbox-CzhFe9wZ.js";
|
|
|
32
32
|
import { fileNameOf } from "./components/lightbox.js";
|
|
33
33
|
import { _ as _29 } from "./chunks/LpLink-DHwOEp4e.js";
|
|
34
34
|
import { _ as _30 } from "./chunks/LpLogViewer-tmKPNTuU.js";
|
|
35
|
-
import { _ as _31 } from "./chunks/LpModal-
|
|
35
|
+
import { _ as _31 } from "./chunks/LpModal-vlrgcEY_.js";
|
|
36
36
|
import { _ as _32 } from "./chunks/LpNotificationBell-DHze8wYN.js";
|
|
37
37
|
import { _ as _33 } from "./chunks/LpNumberField-C5ar-OwE.js";
|
|
38
38
|
import { _ as _34 } from "./chunks/LpOtpInput-DqaT0Z75.js";
|
package/package.json
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
DialogRoot,
|
|
9
9
|
DialogTitle,
|
|
10
10
|
} from "reka-ui"
|
|
11
|
-
import {
|
|
11
|
+
import type { ComponentPublicInstance } from "vue"
|
|
12
|
+
import { computed, onBeforeUnmount, ref, useSlots, watch } from "vue"
|
|
12
13
|
import { CLOSE_ICON } from "./dropdown"
|
|
13
14
|
import LpIcon from "./LpIcon.vue"
|
|
14
15
|
import LpScrollArea from "./LpScrollArea.vue"
|
|
@@ -70,6 +71,118 @@ const bodyPad = computed(() =>
|
|
|
70
71
|
.filter(Boolean)
|
|
71
72
|
.join(" "),
|
|
72
73
|
)
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* Height is content-derived, so a body that arrives late — a placeholder swapped
|
|
77
|
+
* for the loaded thing — resizes the panel in a single frame, right on top of
|
|
78
|
+
* the open animation. CSS alone can't smooth it: `height: auto` isn't
|
|
79
|
+
* interpolable, and the keyword-interpolation escape hatches
|
|
80
|
+
* (`interpolate-size`, `calc-size()`) are Chromium-only, so they would do
|
|
81
|
+
* nothing in the WebKitGTK launcher. Measuring and pinning a pixel height is
|
|
82
|
+
* what works on every engine.
|
|
83
|
+
*
|
|
84
|
+
* The pin is always the panel's own natural height, so `max-h` still clips and
|
|
85
|
+
* the body keeps `flex-1 min-h-0` — an over-tall panel hands its overflow to the
|
|
86
|
+
* scroll area exactly as before. A panel whose size never moves is pinned once
|
|
87
|
+
* and never transitions.
|
|
88
|
+
*/
|
|
89
|
+
// reka renders a real element but exposes it as a component instance, so the
|
|
90
|
+
// panel is reached through $el.
|
|
91
|
+
const panelRef = ref<ComponentPublicInstance | null>(null)
|
|
92
|
+
const tweening = ref(false)
|
|
93
|
+
const resizing = ref(false)
|
|
94
|
+
|
|
95
|
+
/** Kept in step with the `duration-fast` utility on the panel. */
|
|
96
|
+
const TWEEN_MS = 160
|
|
97
|
+
|
|
98
|
+
let ro: ResizeObserver | undefined
|
|
99
|
+
let mo: MutationObserver | undefined
|
|
100
|
+
let raf = 0
|
|
101
|
+
let settleTimer: ReturnType<typeof setTimeout> | undefined
|
|
102
|
+
|
|
103
|
+
// The height is driven imperatively rather than through a style binding. By the
|
|
104
|
+
// time replacement content is in the DOM the panel has already reflowed to its
|
|
105
|
+
// new size, so a bound value would only ever be written once, with no earlier
|
|
106
|
+
// height for the transition to start from. Writing the OLD height, forcing a
|
|
107
|
+
// reflow, then writing the new one gives the transition both ends.
|
|
108
|
+
const retune = (el: HTMLElement) => {
|
|
109
|
+
const from = el.offsetHeight
|
|
110
|
+
el.style.height = ""
|
|
111
|
+
const to = el.offsetHeight
|
|
112
|
+
if (to === 0) return
|
|
113
|
+
if (to === from) {
|
|
114
|
+
el.style.height = `${to}px`
|
|
115
|
+
return
|
|
116
|
+
}
|
|
117
|
+
el.style.height = `${from}px`
|
|
118
|
+
void el.offsetHeight
|
|
119
|
+
el.style.height = `${to}px`
|
|
120
|
+
|
|
121
|
+
// While the panel is still catching up, the body is shorter than the content
|
|
122
|
+
// it already holds, so the scroll area would flash a bar for an overflow that
|
|
123
|
+
// resolves itself the moment the transition lands.
|
|
124
|
+
resizing.value = true
|
|
125
|
+
clearTimeout(settleTimer)
|
|
126
|
+
settleTimer = setTimeout(() => {
|
|
127
|
+
resizing.value = false
|
|
128
|
+
}, TWEEN_MS)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Keyed off `open` rather than the ref: the panel is portalled and remounts on
|
|
132
|
+
// every open, and the teardown has to run on close, when the ref is already gone.
|
|
133
|
+
watch(
|
|
134
|
+
() => props.open,
|
|
135
|
+
(isOpen) => {
|
|
136
|
+
ro?.disconnect()
|
|
137
|
+
mo?.disconnect()
|
|
138
|
+
ro = undefined
|
|
139
|
+
mo = undefined
|
|
140
|
+
cancelAnimationFrame(raf)
|
|
141
|
+
clearTimeout(settleTimer)
|
|
142
|
+
tweening.value = false
|
|
143
|
+
resizing.value = false
|
|
144
|
+
|
|
145
|
+
const el = panelRef.value?.$el
|
|
146
|
+
if (!isOpen || !(el instanceof HTMLElement)) return
|
|
147
|
+
|
|
148
|
+
el.style.height = `${el.offsetHeight}px`
|
|
149
|
+
// The opening panel must not animate its height in from the pre-content box,
|
|
150
|
+
// so the transition only arms once that first pin has painted.
|
|
151
|
+
raf = requestAnimationFrame(() => {
|
|
152
|
+
tweening.value = true
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Two triggers, since either can move the height alone: the resize observer
|
|
156
|
+
// catches a child growing in place (an image decoding, a list filling in),
|
|
157
|
+
// the mutation observer catches one subtree swapped for another, where every
|
|
158
|
+
// observed box may keep its size. Children are observed, never the panel —
|
|
159
|
+
// that would feed the pin straight back in.
|
|
160
|
+
const watchChildren = () => {
|
|
161
|
+
if (!ro) return
|
|
162
|
+
ro.disconnect()
|
|
163
|
+
for (const child of Array.from(el.children)) ro.observe(child)
|
|
164
|
+
}
|
|
165
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
166
|
+
ro = new ResizeObserver(() => retune(el))
|
|
167
|
+
watchChildren()
|
|
168
|
+
}
|
|
169
|
+
if (typeof MutationObserver !== "undefined") {
|
|
170
|
+
mo = new MutationObserver(() => {
|
|
171
|
+
watchChildren()
|
|
172
|
+
retune(el)
|
|
173
|
+
})
|
|
174
|
+
mo.observe(el, { childList: true, subtree: true, characterData: true })
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
{ flush: "post" },
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
onBeforeUnmount(() => {
|
|
181
|
+
ro?.disconnect()
|
|
182
|
+
mo?.disconnect()
|
|
183
|
+
cancelAnimationFrame(raf)
|
|
184
|
+
clearTimeout(settleTimer)
|
|
185
|
+
})
|
|
73
186
|
</script>
|
|
74
187
|
|
|
75
188
|
<template>
|
|
@@ -100,8 +213,9 @@ const bodyPad = computed(() =>
|
|
|
100
213
|
pass a description keeps the generated id and the link to
|
|
101
214
|
DialogDescription. -->
|
|
102
215
|
<DialogContent
|
|
103
|
-
|
|
104
|
-
|
|
216
|
+
ref="panelRef"
|
|
217
|
+
class="pointer-events-auto 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 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)]"
|
|
218
|
+
:class="[widthClass, tweening ? 'transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none' : '']"
|
|
105
219
|
:style="width ? { width } : undefined"
|
|
106
220
|
v-bind="describedByAttrs"
|
|
107
221
|
>
|
|
@@ -135,9 +249,14 @@ const bodyPad = computed(() =>
|
|
|
135
249
|
>
|
|
136
250
|
<slot />
|
|
137
251
|
</div>
|
|
252
|
+
<!-- While the panel is still growing it is briefly shorter than the body
|
|
253
|
+
it already holds, so the scroll area raises a bar for an overflow
|
|
254
|
+
that resolves itself as the transition lands. Hiding just the bar
|
|
255
|
+
for the length of the tween leaves scrolling itself untouched. -->
|
|
138
256
|
<LpScrollArea
|
|
139
257
|
v-else
|
|
140
258
|
class="min-h-0 flex-1 text-sm text-ink/90"
|
|
259
|
+
:class="resizing ? '[&_[data-scrollbarimpl]]:invisible' : ''"
|
|
141
260
|
:content-class="bodyPad"
|
|
142
261
|
>
|
|
143
262
|
<slot />
|