@leavepulse/ui 0.35.0 → 0.36.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-FZXfljWV.js → LpAddressInput-C8NZWkH_.js} +1 -1
- package/dist/chunks/{LpConfirmDialog-CQBVaLpo.js → LpConfirmDialog-B6RAvDES.js} +1 -1
- package/dist/chunks/LpModal-D3QxgWaT.js +224 -0
- package/dist/chunks/LpModalNav-K6N5YpQk.js +166 -0
- package/dist/components/LpAddressInput.vue.js +1 -1
- package/dist/components/LpConfirmDialog.vue.js +1 -1
- package/dist/components/LpModal.vue.d.ts +9 -5
- package/dist/components/LpModal.vue.js +1 -1
- package/dist/components/LpModalNav.vue.d.ts +10 -21
- package/dist/components/LpModalNav.vue.js +1 -1
- package/dist/components/LpShortcutsDialog.vue.js +1 -1
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/src/components/LpModal.vue +74 -1
- package/src/components/LpModalNav.vue +95 -70
- package/dist/chunks/LpModal-DAqXzsm-.js +0 -192
- package/dist/chunks/LpModalNav-B7W9VAne.js +0 -142
|
@@ -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-D3QxgWaT.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" };
|
|
@@ -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-D3QxgWaT.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",
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, createElementVNode, createElementBlock, renderSlot, createCommentVNode, mergeProps, createTextVNode, toDisplayString, normalizeClass } from "vue";
|
|
2
|
+
import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
|
|
3
|
+
import { useShift } from "../composables/useShift.js";
|
|
4
|
+
import { useModalLayer } from "../components/modalDepth.js";
|
|
5
|
+
import { CLOSE_ICON } from "../components/dropdown.js";
|
|
6
|
+
import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
|
|
7
|
+
import { _ as _sfc_main$2 } from "./LpScrollArea-D8xyKXcF.js";
|
|
8
|
+
const _hoisted_1 = {
|
|
9
|
+
key: 0,
|
|
10
|
+
"data-lp-modal-aside": "",
|
|
11
|
+
class: "pointer-events-auto flex min-h-0 flex-col"
|
|
12
|
+
};
|
|
13
|
+
const _hoisted_2 = {
|
|
14
|
+
key: 0,
|
|
15
|
+
class: "flex shrink-0 items-start justify-between gap-4 p-5 pb-3"
|
|
16
|
+
};
|
|
17
|
+
const _hoisted_3 = { class: "flex flex-col gap-1" };
|
|
18
|
+
const _hoisted_4 = {
|
|
19
|
+
key: 3,
|
|
20
|
+
class: "flex shrink-0 justify-end gap-2 p-5 pt-4"
|
|
21
|
+
};
|
|
22
|
+
const _hoisted_5 = {
|
|
23
|
+
key: 1,
|
|
24
|
+
"data-lp-modal-aside": "",
|
|
25
|
+
class: "pointer-events-auto flex min-h-0 flex-col"
|
|
26
|
+
};
|
|
27
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
28
|
+
__name: "LpModal",
|
|
29
|
+
props: {
|
|
30
|
+
open: { type: Boolean },
|
|
31
|
+
title: {},
|
|
32
|
+
description: {},
|
|
33
|
+
size: { default: "md" },
|
|
34
|
+
width: {},
|
|
35
|
+
fillBody: { type: Boolean },
|
|
36
|
+
autoFocus: { default: "auto" }
|
|
37
|
+
},
|
|
38
|
+
emits: ["update:open"],
|
|
39
|
+
setup(__props) {
|
|
40
|
+
const { claim, release, layer } = useModalLayer();
|
|
41
|
+
const props = __props;
|
|
42
|
+
const onInteractOutside = (event) => {
|
|
43
|
+
const target = event.target;
|
|
44
|
+
if (target instanceof Element && target.closest("[data-lp-modal-aside]")) {
|
|
45
|
+
event.preventDefault();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const describedByAttrs = computed(
|
|
49
|
+
() => props.description ? {} : { "aria-describedby": void 0 }
|
|
50
|
+
);
|
|
51
|
+
const widthClass = computed(() => {
|
|
52
|
+
if (props.width) return "";
|
|
53
|
+
return {
|
|
54
|
+
sm: "w-[min(92vw,24rem)]",
|
|
55
|
+
md: "w-[min(92vw,28rem)]",
|
|
56
|
+
lg: "w-[min(92vw,34rem)]",
|
|
57
|
+
xl: "w-[min(92vw,42rem)]",
|
|
58
|
+
"2xl": "w-[min(94vw,56rem)]",
|
|
59
|
+
"3xl": "w-[min(95vw,72rem)]",
|
|
60
|
+
full: "w-[96vw]"
|
|
61
|
+
}[props.size];
|
|
62
|
+
});
|
|
63
|
+
const slots = useSlots();
|
|
64
|
+
const hasAside = computed(() => Boolean(slots.aside || slots.asideEnd));
|
|
65
|
+
watch(() => props.open, (open) => open ? claim() : release(), { immediate: true });
|
|
66
|
+
onBeforeUnmount(release);
|
|
67
|
+
const bodyPad = computed(
|
|
68
|
+
() => [
|
|
69
|
+
"px-5",
|
|
70
|
+
props.title || slots.title ? "pt-1" : "pt-5",
|
|
71
|
+
slots.footer ? "" : "pb-5"
|
|
72
|
+
].filter(Boolean).join(" ")
|
|
73
|
+
);
|
|
74
|
+
const panelRef = ref(null);
|
|
75
|
+
const AUTO_FOCUS_ORDER = [
|
|
76
|
+
"input:not([type=hidden]):not([disabled]):not([readonly])",
|
|
77
|
+
"textarea:not([disabled]):not([readonly])",
|
|
78
|
+
"select:not([disabled])",
|
|
79
|
+
"[data-lp-autofocus]",
|
|
80
|
+
"button:not([disabled]):not([data-lp-dialog-close])",
|
|
81
|
+
"[href]",
|
|
82
|
+
"[tabindex]:not([tabindex='-1'])"
|
|
83
|
+
];
|
|
84
|
+
function onOpenAutoFocus(event) {
|
|
85
|
+
if (props.autoFocus === "none") return;
|
|
86
|
+
const panel = panelRef.value?.$el;
|
|
87
|
+
if (!(panel instanceof HTMLElement)) return;
|
|
88
|
+
const selectors = props.autoFocus === "auto" ? AUTO_FOCUS_ORDER : [props.autoFocus];
|
|
89
|
+
for (const selector of selectors) {
|
|
90
|
+
const target = panel.querySelector(selector);
|
|
91
|
+
if (!target || target.hasAttribute("disabled")) continue;
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
if (!target.closest("[data-lp-ring-owner]")) {
|
|
94
|
+
target.setAttribute("data-lp-focus-ring", "");
|
|
95
|
+
target.addEventListener(
|
|
96
|
+
"blur",
|
|
97
|
+
() => target.removeAttribute("data-lp-focus-ring"),
|
|
98
|
+
{ once: true }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
target.focus();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const {
|
|
106
|
+
el: sizedEl,
|
|
107
|
+
tweening,
|
|
108
|
+
resizing
|
|
109
|
+
} = useShift({ axis: "height", enabled: () => props.open });
|
|
110
|
+
watch(
|
|
111
|
+
() => props.open ? panelRef.value : null,
|
|
112
|
+
(panel) => {
|
|
113
|
+
const el = panel?.$el;
|
|
114
|
+
sizedEl.value = el instanceof HTMLElement ? el : null;
|
|
115
|
+
},
|
|
116
|
+
{ flush: "post" }
|
|
117
|
+
);
|
|
118
|
+
return (_ctx, _cache) => {
|
|
119
|
+
return openBlock(), createBlock(unref(DialogRoot), {
|
|
120
|
+
open: __props.open,
|
|
121
|
+
"onUpdate:open": _cache[0] || (_cache[0] = (v) => _ctx.$emit("update:open", v))
|
|
122
|
+
}, {
|
|
123
|
+
default: withCtx(() => [
|
|
124
|
+
createVNode(unref(DialogPortal), null, {
|
|
125
|
+
default: withCtx(() => [
|
|
126
|
+
createVNode(unref(DialogOverlay), {
|
|
127
|
+
class: "lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]",
|
|
128
|
+
style: normalizeStyle({ zIndex: unref(layer).scrim })
|
|
129
|
+
}, null, 8, ["style"]),
|
|
130
|
+
createElementVNode("div", {
|
|
131
|
+
class: "fixed inset-0 flex items-center justify-center pointer-events-none",
|
|
132
|
+
style: normalizeStyle({ zIndex: unref(layer).panel })
|
|
133
|
+
}, [
|
|
134
|
+
createElementVNode("div", {
|
|
135
|
+
class: "flex items-start",
|
|
136
|
+
style: normalizeStyle(hasAside.value ? { maxHeight: "min(90vh, calc(100dvh - 2rem))" } : void 0)
|
|
137
|
+
}, [
|
|
138
|
+
__props.open && _ctx.$slots.aside ? (openBlock(), createElementBlock("aside", _hoisted_1, [
|
|
139
|
+
renderSlot(_ctx.$slots, "aside")
|
|
140
|
+
])) : createCommentVNode("", true),
|
|
141
|
+
createVNode(unref(DialogContent), mergeProps({
|
|
142
|
+
ref_key: "panelRef",
|
|
143
|
+
ref: panelRef,
|
|
144
|
+
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, unref(tweening) ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]],
|
|
145
|
+
style: { ...__props.width ? { width: __props.width } : {}, ...hasAside.value ? { alignSelf: "stretch" } : {} }
|
|
146
|
+
}, describedByAttrs.value, {
|
|
147
|
+
onOpenAutoFocus,
|
|
148
|
+
onInteractOutside,
|
|
149
|
+
onPointerDownOutside: onInteractOutside
|
|
150
|
+
}), {
|
|
151
|
+
default: withCtx(() => [
|
|
152
|
+
__props.title || _ctx.$slots.title ? (openBlock(), createElementBlock("header", _hoisted_2, [
|
|
153
|
+
createElementVNode("div", _hoisted_3, [
|
|
154
|
+
createVNode(unref(DialogTitle), { class: "text-base font-semibold text-ink" }, {
|
|
155
|
+
default: withCtx(() => [
|
|
156
|
+
renderSlot(_ctx.$slots, "title", {}, () => [
|
|
157
|
+
createTextVNode(toDisplayString(__props.title), 1)
|
|
158
|
+
])
|
|
159
|
+
]),
|
|
160
|
+
_: 3
|
|
161
|
+
}),
|
|
162
|
+
__props.description ? (openBlock(), createBlock(unref(DialogDescription), {
|
|
163
|
+
key: 0,
|
|
164
|
+
class: "text-sm text-muted"
|
|
165
|
+
}, {
|
|
166
|
+
default: withCtx(() => [
|
|
167
|
+
createTextVNode(toDisplayString(__props.description), 1)
|
|
168
|
+
]),
|
|
169
|
+
_: 1
|
|
170
|
+
})) : createCommentVNode("", true)
|
|
171
|
+
]),
|
|
172
|
+
createVNode(unref(DialogClose), {
|
|
173
|
+
"data-lp-dialog-close": "",
|
|
174
|
+
class: "group flex shrink-0 items-center rounded-md p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring",
|
|
175
|
+
"aria-label": "Close"
|
|
176
|
+
}, {
|
|
177
|
+
default: withCtx(() => [
|
|
178
|
+
createVNode(_sfc_main$1, {
|
|
179
|
+
name: "lucide:x",
|
|
180
|
+
size: 18,
|
|
181
|
+
class: normalizeClass(unref(CLOSE_ICON))
|
|
182
|
+
}, null, 8, ["class"])
|
|
183
|
+
]),
|
|
184
|
+
_: 1
|
|
185
|
+
})
|
|
186
|
+
])) : createCommentVNode("", true),
|
|
187
|
+
__props.fillBody ? (openBlock(), createElementBlock("div", {
|
|
188
|
+
key: 1,
|
|
189
|
+
class: normalizeClass(["flex min-h-0 flex-1 flex-col overflow-hidden text-sm text-ink/90", bodyPad.value])
|
|
190
|
+
}, [
|
|
191
|
+
renderSlot(_ctx.$slots, "default")
|
|
192
|
+
], 2)) : (openBlock(), createBlock(_sfc_main$2, {
|
|
193
|
+
key: 2,
|
|
194
|
+
class: normalizeClass(["min-h-0 flex-1 text-sm text-ink/90", unref(resizing) ? "[&_[data-scrollbarimpl]]:invisible" : ""]),
|
|
195
|
+
"content-class": bodyPad.value
|
|
196
|
+
}, {
|
|
197
|
+
default: withCtx(() => [
|
|
198
|
+
renderSlot(_ctx.$slots, "default")
|
|
199
|
+
]),
|
|
200
|
+
_: 3
|
|
201
|
+
}, 8, ["class", "content-class"])),
|
|
202
|
+
_ctx.$slots.footer ? (openBlock(), createElementBlock("footer", _hoisted_4, [
|
|
203
|
+
renderSlot(_ctx.$slots, "footer")
|
|
204
|
+
])) : createCommentVNode("", true)
|
|
205
|
+
]),
|
|
206
|
+
_: 3
|
|
207
|
+
}, 16, ["class", "style"]),
|
|
208
|
+
__props.open && _ctx.$slots.asideEnd ? (openBlock(), createElementBlock("aside", _hoisted_5, [
|
|
209
|
+
renderSlot(_ctx.$slots, "asideEnd")
|
|
210
|
+
])) : createCommentVNode("", true)
|
|
211
|
+
], 4)
|
|
212
|
+
], 4)
|
|
213
|
+
]),
|
|
214
|
+
_: 3
|
|
215
|
+
})
|
|
216
|
+
]),
|
|
217
|
+
_: 3
|
|
218
|
+
}, 8, ["open"]);
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
export {
|
|
223
|
+
_sfc_main as _
|
|
224
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { defineComponent, ref, computed, openBlock, createBlock, createSlots, withCtx, renderSlot, createElementVNode, normalizeClass, createVNode, createCommentVNode, createTextVNode, toDisplayString } from "vue";
|
|
2
|
+
import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
|
|
3
|
+
import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
|
|
4
|
+
import { _ as _sfc_main$1 } from "./LpModal-D3QxgWaT.js";
|
|
5
|
+
import { _ as _sfc_main$4 } from "./LpSidebar-DcYavim2.js";
|
|
6
|
+
const _hoisted_1 = { class: "flex items-center gap-2" };
|
|
7
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
8
|
+
__name: "LpModalNav",
|
|
9
|
+
props: {
|
|
10
|
+
open: { type: Boolean },
|
|
11
|
+
modelValue: {},
|
|
12
|
+
items: {},
|
|
13
|
+
sections: {},
|
|
14
|
+
title: {},
|
|
15
|
+
description: {},
|
|
16
|
+
size: { default: "2xl" },
|
|
17
|
+
width: {},
|
|
18
|
+
navWidth: { default: "13rem" },
|
|
19
|
+
searchable: { type: Boolean },
|
|
20
|
+
searchPlaceholder: {},
|
|
21
|
+
loading: { type: Boolean },
|
|
22
|
+
mobileBreakpoint: { default: "lg" },
|
|
23
|
+
navLabel: { default: "Sections" },
|
|
24
|
+
navSide: { default: "start" },
|
|
25
|
+
fillBody: { type: Boolean }
|
|
26
|
+
},
|
|
27
|
+
emits: ["update:open", "update:modelValue"],
|
|
28
|
+
setup(__props, { emit: __emit }) {
|
|
29
|
+
const props = __props;
|
|
30
|
+
const emit = __emit;
|
|
31
|
+
const navOpen = ref(false);
|
|
32
|
+
const active = computed({
|
|
33
|
+
get: () => props.modelValue ?? "",
|
|
34
|
+
set: (v) => emit("update:modelValue", v)
|
|
35
|
+
});
|
|
36
|
+
const hasNav = computed(() => Boolean(props.items?.length || props.sections?.length));
|
|
37
|
+
const RAIL_GAP = {
|
|
38
|
+
sm: "sm:mr-3",
|
|
39
|
+
md: "md:mr-3",
|
|
40
|
+
lg: "lg:mr-3",
|
|
41
|
+
xl: "xl:mr-3"
|
|
42
|
+
};
|
|
43
|
+
const RAIL_GAP_END = {
|
|
44
|
+
sm: "sm:ml-3",
|
|
45
|
+
md: "md:ml-3",
|
|
46
|
+
lg: "lg:ml-3",
|
|
47
|
+
xl: "xl:ml-3"
|
|
48
|
+
};
|
|
49
|
+
const TRIGGER_VISIBILITY = {
|
|
50
|
+
sm: "sm:hidden",
|
|
51
|
+
md: "md:hidden",
|
|
52
|
+
lg: "lg:hidden",
|
|
53
|
+
xl: "xl:hidden"
|
|
54
|
+
};
|
|
55
|
+
const railClass = computed(
|
|
56
|
+
() => (props.navSide === "end" ? RAIL_GAP_END : RAIL_GAP)[props.mobileBreakpoint]
|
|
57
|
+
);
|
|
58
|
+
const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint]);
|
|
59
|
+
const railStyle = { maxHeight: "min(90vh, calc(100dvh - 2rem))" };
|
|
60
|
+
return (_ctx, _cache) => {
|
|
61
|
+
return openBlock(), createBlock(_sfc_main$1, {
|
|
62
|
+
open: __props.open,
|
|
63
|
+
title: __props.title,
|
|
64
|
+
description: __props.description,
|
|
65
|
+
size: __props.size,
|
|
66
|
+
width: __props.width,
|
|
67
|
+
"fill-body": __props.fillBody,
|
|
68
|
+
"onUpdate:open": _cache[5] || (_cache[5] = ($event) => emit("update:open", $event))
|
|
69
|
+
}, createSlots({
|
|
70
|
+
title: withCtx(() => [
|
|
71
|
+
createElementVNode("div", _hoisted_1, [
|
|
72
|
+
hasNav.value ? (openBlock(), createBlock(_sfc_main$2, {
|
|
73
|
+
key: 0,
|
|
74
|
+
variant: "ghost",
|
|
75
|
+
size: "sm",
|
|
76
|
+
square: "",
|
|
77
|
+
"aria-label": __props.navLabel,
|
|
78
|
+
class: normalizeClass(triggerClass.value),
|
|
79
|
+
onClick: _cache[4] || (_cache[4] = ($event) => navOpen.value = true)
|
|
80
|
+
}, {
|
|
81
|
+
default: withCtx(() => [
|
|
82
|
+
createVNode(_sfc_main$3, {
|
|
83
|
+
name: "lucide:menu",
|
|
84
|
+
size: 18
|
|
85
|
+
})
|
|
86
|
+
]),
|
|
87
|
+
_: 1
|
|
88
|
+
}, 8, ["aria-label", "class"])) : createCommentVNode("", true),
|
|
89
|
+
renderSlot(_ctx.$slots, "title", {}, () => [
|
|
90
|
+
createTextVNode(toDisplayString(__props.title), 1)
|
|
91
|
+
])
|
|
92
|
+
])
|
|
93
|
+
]),
|
|
94
|
+
default: withCtx(() => [
|
|
95
|
+
renderSlot(_ctx.$slots, "default")
|
|
96
|
+
]),
|
|
97
|
+
_: 2
|
|
98
|
+
}, [
|
|
99
|
+
hasNav.value && __props.navSide === "start" ? {
|
|
100
|
+
name: "aside",
|
|
101
|
+
fn: withCtx(() => [
|
|
102
|
+
createElementVNode("div", {
|
|
103
|
+
class: normalizeClass(railClass.value)
|
|
104
|
+
}, [
|
|
105
|
+
createVNode(_sfc_main$4, {
|
|
106
|
+
modelValue: active.value,
|
|
107
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => active.value = $event),
|
|
108
|
+
open: navOpen.value,
|
|
109
|
+
"onUpdate:open": _cache[1] || (_cache[1] = ($event) => navOpen.value = $event),
|
|
110
|
+
variant: "panel",
|
|
111
|
+
items: __props.items,
|
|
112
|
+
sections: __props.sections,
|
|
113
|
+
width: __props.navWidth,
|
|
114
|
+
searchable: __props.searchable,
|
|
115
|
+
"search-placeholder": __props.searchPlaceholder,
|
|
116
|
+
loading: __props.loading,
|
|
117
|
+
responsive: "",
|
|
118
|
+
"mobile-breakpoint": __props.mobileBreakpoint,
|
|
119
|
+
style: railStyle,
|
|
120
|
+
class: "rounded-card border border-line bg-surface-raised p-2 shadow-panel"
|
|
121
|
+
}, null, 8, ["modelValue", "open", "items", "sections", "width", "searchable", "search-placeholder", "loading", "mobile-breakpoint"])
|
|
122
|
+
], 2)
|
|
123
|
+
]),
|
|
124
|
+
key: "0"
|
|
125
|
+
} : void 0,
|
|
126
|
+
hasNav.value && __props.navSide === "end" ? {
|
|
127
|
+
name: "asideEnd",
|
|
128
|
+
fn: withCtx(() => [
|
|
129
|
+
createElementVNode("div", {
|
|
130
|
+
class: normalizeClass(railClass.value)
|
|
131
|
+
}, [
|
|
132
|
+
createVNode(_sfc_main$4, {
|
|
133
|
+
modelValue: active.value,
|
|
134
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => active.value = $event),
|
|
135
|
+
open: navOpen.value,
|
|
136
|
+
"onUpdate:open": _cache[3] || (_cache[3] = ($event) => navOpen.value = $event),
|
|
137
|
+
variant: "panel",
|
|
138
|
+
items: __props.items,
|
|
139
|
+
sections: __props.sections,
|
|
140
|
+
width: __props.navWidth,
|
|
141
|
+
searchable: __props.searchable,
|
|
142
|
+
"search-placeholder": __props.searchPlaceholder,
|
|
143
|
+
loading: __props.loading,
|
|
144
|
+
responsive: "",
|
|
145
|
+
"mobile-breakpoint": __props.mobileBreakpoint,
|
|
146
|
+
style: railStyle,
|
|
147
|
+
class: "rounded-card border border-line bg-surface-raised p-2 shadow-panel"
|
|
148
|
+
}, null, 8, ["modelValue", "open", "items", "sections", "width", "searchable", "search-placeholder", "loading", "mobile-breakpoint"])
|
|
149
|
+
], 2)
|
|
150
|
+
]),
|
|
151
|
+
key: "1"
|
|
152
|
+
} : void 0,
|
|
153
|
+
_ctx.$slots.footer ? {
|
|
154
|
+
name: "footer",
|
|
155
|
+
fn: withCtx(() => [
|
|
156
|
+
renderSlot(_ctx.$slots, "footer")
|
|
157
|
+
]),
|
|
158
|
+
key: "2"
|
|
159
|
+
} : void 0
|
|
160
|
+
]), 1032, ["open", "title", "description", "size", "width", "fill-body"]);
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
export {
|
|
165
|
+
_sfc_main as _
|
|
166
|
+
};
|
|
@@ -26,15 +26,19 @@ type __VLS_Props = {
|
|
|
26
26
|
*/
|
|
27
27
|
autoFocus?: "auto" | "none" | string;
|
|
28
28
|
};
|
|
29
|
-
declare var
|
|
29
|
+
declare var __VLS_21: {}, __VLS_41: {}, __VLS_60: {}, __VLS_68: {}, __VLS_70: {}, __VLS_72: {};
|
|
30
30
|
type __VLS_Slots = {} & {
|
|
31
|
-
|
|
31
|
+
aside?: (props: typeof __VLS_21) => any;
|
|
32
32
|
} & {
|
|
33
|
-
|
|
33
|
+
title?: (props: typeof __VLS_41) => any;
|
|
34
34
|
} & {
|
|
35
|
-
default?: (props: typeof
|
|
35
|
+
default?: (props: typeof __VLS_60) => any;
|
|
36
36
|
} & {
|
|
37
|
-
|
|
37
|
+
default?: (props: typeof __VLS_68) => any;
|
|
38
|
+
} & {
|
|
39
|
+
footer?: (props: typeof __VLS_70) => any;
|
|
40
|
+
} & {
|
|
41
|
+
asideEnd?: (props: typeof __VLS_72) => any;
|
|
38
42
|
};
|
|
39
43
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
40
44
|
"update:open": (value: boolean) => any;
|
|
@@ -10,10 +10,6 @@ type __VLS_Props = {
|
|
|
10
10
|
sections?: SidebarSection[];
|
|
11
11
|
title?: string;
|
|
12
12
|
description?: string;
|
|
13
|
-
/**
|
|
14
|
-
* Defaults wider than `LpModal`'s `md`: the rail comes out of the content's
|
|
15
|
-
* width, and at `md` what is left is too narrow to be worth splitting.
|
|
16
|
-
*/
|
|
17
13
|
size?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
|
|
18
14
|
width?: string;
|
|
19
15
|
/** Width of the nav rail (any CSS length). */
|
|
@@ -27,29 +23,22 @@ type __VLS_Props = {
|
|
|
27
23
|
mobileBreakpoint?: Breakpoint;
|
|
28
24
|
/** Label for the button that opens that drawer. */
|
|
29
25
|
navLabel?: string;
|
|
26
|
+
/** Stand the rail to the right of the dialog instead of the left. */
|
|
27
|
+
navSide?: "start" | "end";
|
|
30
28
|
/**
|
|
31
|
-
* Let the body fill the height instead of scrolling itself.
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
* Let the body fill the height instead of scrolling itself. Off by default,
|
|
30
|
+
* as in a plain modal: it existed here to make the rail a full-height
|
|
31
|
+
* column inside the body, and the rail is no longer in the body.
|
|
34
32
|
*/
|
|
35
33
|
fillBody?: boolean;
|
|
36
34
|
};
|
|
37
|
-
declare var
|
|
38
|
-
item: SidebarItem;
|
|
39
|
-
active: boolean;
|
|
40
|
-
}, __VLS_39: {}, __VLS_41: {}, __VLS_44: {};
|
|
35
|
+
declare var __VLS_36: {}, __VLS_38: {}, __VLS_41: {};
|
|
41
36
|
type __VLS_Slots = {} & {
|
|
42
|
-
title?: (props: typeof
|
|
43
|
-
} & {
|
|
44
|
-
navHeader?: (props: typeof __VLS_33) => any;
|
|
45
|
-
} & {
|
|
46
|
-
navItem?: (props: typeof __VLS_36) => any;
|
|
47
|
-
} & {
|
|
48
|
-
navFooter?: (props: typeof __VLS_39) => any;
|
|
37
|
+
title?: (props: typeof __VLS_36) => any;
|
|
49
38
|
} & {
|
|
50
|
-
default?: (props: typeof
|
|
39
|
+
default?: (props: typeof __VLS_38) => any;
|
|
51
40
|
} & {
|
|
52
|
-
footer?: (props: typeof
|
|
41
|
+
footer?: (props: typeof __VLS_41) => any;
|
|
53
42
|
};
|
|
54
43
|
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {} & {
|
|
55
44
|
"update:modelValue": (value: string) => any;
|
|
@@ -59,10 +48,10 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {},
|
|
|
59
48
|
"onUpdate:open"?: ((value: boolean) => any) | undefined;
|
|
60
49
|
}>, {
|
|
61
50
|
size: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full";
|
|
62
|
-
fillBody: boolean;
|
|
63
51
|
mobileBreakpoint: Breakpoint;
|
|
64
52
|
navWidth: string;
|
|
65
53
|
navLabel: string;
|
|
54
|
+
navSide: "start" | "end";
|
|
66
55
|
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
67
56
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
68
57
|
declare const _default: typeof __VLS_export;
|
|
@@ -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-D3QxgWaT.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,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-C8NZWkH_.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";
|
|
@@ -19,7 +19,7 @@ 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-B6RAvDES.js";
|
|
23
23
|
import { _ as _18 } from "./chunks/LpContextMenu-B6WBLleS.js";
|
|
24
24
|
import { _ as _19 } from "./chunks/LpDatePicker-6JFXllj5.js";
|
|
25
25
|
import { _ as _20 } from "./chunks/LpDisclosure-ChQYmjlR.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-D3QxgWaT.js";
|
|
44
|
+
import { _ as _35 } from "./chunks/LpModalNav-K6N5YpQk.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";
|
package/package.json
CHANGED
|
@@ -72,6 +72,20 @@ defineEmits<{ (e: "update:open", value: boolean): void }>()
|
|
|
72
72
|
// Spread rather than a plain `:aria-describedby` binding: with a description
|
|
73
73
|
// the key must be absent entirely so reka-ui's own generated id survives, and
|
|
74
74
|
// only the description-less case overrides it away.
|
|
75
|
+
/*
|
|
76
|
+
* A click on the aside lands outside DialogContent, and reka reads anything
|
|
77
|
+
* outside it as a click on the scrim — the dialog closed under the pointer the
|
|
78
|
+
* moment you picked a section. The aside marks itself, and a click that starts
|
|
79
|
+
* inside one keeps the dialog open. Without an aside nothing carries the
|
|
80
|
+
* attribute and every click behaves exactly as before.
|
|
81
|
+
*/
|
|
82
|
+
const onInteractOutside = (event: Event) => {
|
|
83
|
+
const target = event.target
|
|
84
|
+
if (target instanceof Element && target.closest("[data-lp-modal-aside]")) {
|
|
85
|
+
event.preventDefault()
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
75
89
|
const describedByAttrs = computed(() =>
|
|
76
90
|
props.description ? {} : { "aria-describedby": undefined },
|
|
77
91
|
)
|
|
@@ -93,6 +107,9 @@ const widthClass = computed(() => {
|
|
|
93
107
|
// inset horizontally; top only when there's no header, bottom only when there's
|
|
94
108
|
// no footer (header/footer own those edges).
|
|
95
109
|
const slots = useSlots()
|
|
110
|
+
|
|
111
|
+
// Dialog and aside only form a pair when something fills the slot.
|
|
112
|
+
const hasAside = computed(() => Boolean(slots.aside || slots.asideEnd))
|
|
96
113
|
// The body clips overflow and focus rings are drawn outside their control, so a
|
|
97
114
|
// field flush against the top edge lost its ring; pt-1 clears the ring's width.
|
|
98
115
|
/*
|
|
@@ -225,10 +242,55 @@ watch(
|
|
|
225
242
|
this wrapper: `max-h-full` on a centred flex child resolves against a
|
|
226
243
|
box it is free to overflow, so a tall body stopped handing its
|
|
227
244
|
overflow to the scroll area below and simply ran off-screen. -->
|
|
245
|
+
<!-- A companion panel standing OUTSIDE the dialog, in the same centring
|
|
246
|
+
row: a section list, a preview, a picker. Inside, it would take the
|
|
247
|
+
width the body already wants; measured against the viewport it would
|
|
248
|
+
have to be re-measured on every resize.
|
|
249
|
+
|
|
250
|
+
The row keeps `items-center` until something fills the slot, so a
|
|
251
|
+
dialog without one centres exactly as it always has. With an aside
|
|
252
|
+
the two align to the top of the row instead, and because the row
|
|
253
|
+
collapses to its content the taller of the pair sets that top edge —
|
|
254
|
+
so the aside may be shorter than the dialog, and the dialog is never
|
|
255
|
+
shorter than the aside. Neither is stretched: `stretch` here would
|
|
256
|
+
pull every dialog to the full height of its own ceiling.
|
|
257
|
+
|
|
258
|
+
Height, width, the gap to the dialog and whether it appears at all
|
|
259
|
+
are the caller's, and deliberately so: a utility class written in
|
|
260
|
+
this file is not in the consuming app's Tailwind scan and would never
|
|
261
|
+
be generated. The gap in particular has to go with the aside — kept
|
|
262
|
+
here, it survives an aside hidden at a breakpoint this file knows
|
|
263
|
+
nothing about, and pushes the dialog off centre by its own width. -->
|
|
228
264
|
<div
|
|
229
265
|
class="fixed inset-0 flex items-center justify-center pointer-events-none"
|
|
230
266
|
:style="{ zIndex: layer.panel }"
|
|
231
267
|
>
|
|
268
|
+
<!-- The dialog and its aside stand in a row of their own, and that row is
|
|
269
|
+
what the wrapper centres. Aligning them on the full-screen wrapper
|
|
270
|
+
instead would pin them to the top of the WINDOW, and the dialog would
|
|
271
|
+
be free to end up shorter than the aside beside it.
|
|
272
|
+
`items-start` inside the row: the row collapses to the taller of the
|
|
273
|
+
two, both hang from its top edge, and neither is stretched. -->
|
|
274
|
+
<div
|
|
275
|
+
class="flex items-start"
|
|
276
|
+
:style="hasAside ? { maxHeight: 'min(90vh, calc(100dvh - 2rem))' } : undefined"
|
|
277
|
+
>
|
|
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
|
+
<aside
|
|
288
|
+
v-if="open && $slots.aside"
|
|
289
|
+
data-lp-modal-aside
|
|
290
|
+
class="pointer-events-auto flex min-h-0 flex-col"
|
|
291
|
+
>
|
|
292
|
+
<slot name="aside" />
|
|
293
|
+
</aside>
|
|
232
294
|
<!-- Without a description reka-ui warns on every open, and the opt-out it
|
|
233
295
|
checks for is an ABSENT `aria-describedby` — despite the message
|
|
234
296
|
naming the string "undefined" (a leftover from Radix, where
|
|
@@ -240,9 +302,11 @@ watch(
|
|
|
240
302
|
ref="panelRef"
|
|
241
303
|
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)]"
|
|
242
304
|
:class="[widthClass, tweening ? 'transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none' : '']"
|
|
243
|
-
:style="width ? { width } :
|
|
305
|
+
:style="{ ...(width ? { width } : {}), ...(hasAside ? { alignSelf: 'stretch' } : {}) }"
|
|
244
306
|
v-bind="describedByAttrs"
|
|
245
307
|
@open-auto-focus="onOpenAutoFocus"
|
|
308
|
+
@interact-outside="onInteractOutside"
|
|
309
|
+
@pointer-down-outside="onInteractOutside"
|
|
246
310
|
>
|
|
247
311
|
<header v-if="title || $slots.title" class="flex shrink-0 items-start justify-between gap-4 p-5 pb-3">
|
|
248
312
|
<div class="flex flex-col gap-1">
|
|
@@ -294,6 +358,15 @@ watch(
|
|
|
294
358
|
<slot name="footer" />
|
|
295
359
|
</footer>
|
|
296
360
|
</DialogContent>
|
|
361
|
+
|
|
362
|
+
<aside
|
|
363
|
+
v-if="open && $slots.asideEnd"
|
|
364
|
+
data-lp-modal-aside
|
|
365
|
+
class="pointer-events-auto flex min-h-0 flex-col"
|
|
366
|
+
>
|
|
367
|
+
<slot name="asideEnd" />
|
|
368
|
+
</aside>
|
|
369
|
+
</div>
|
|
297
370
|
</div>
|
|
298
371
|
</DialogPortal>
|
|
299
372
|
</DialogRoot>
|
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/*
|
|
3
|
-
* A dialog whose sections
|
|
4
|
-
* with tabs' worth of detail, a wizard
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* back-and-forth between a list screen and a detail screen.
|
|
3
|
+
* A dialog whose sections stand BESIDE it rather than inside it — settings, a
|
|
4
|
+
* profile with tabs' worth of detail, a wizard you can skip around in. Put the
|
|
5
|
+
* list inside the panel and it takes the width the content already wants; a row
|
|
6
|
+
* of tabs across the top runs out of room at about five.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Width is the one cost: the rail comes out of the body's width, which is why
|
|
18
|
-
* the default `size` is wider than a plain modal's. Below `mobileBreakpoint`
|
|
19
|
-
* there is no width to spare at all, so the rail becomes a drawer — and since
|
|
20
|
-
* LpSidebar leaves opening it to the app, the header grows the button for it
|
|
21
|
-
* rather than leaving the sections unreachable on a phone.
|
|
8
|
+
* This is LpModal's `aside` slot plus LpSidebar, wired together: the pair
|
|
9
|
+
* shares one centring row, so the list lines up with the dialog at any window
|
|
10
|
+
* width and neither has to measure the other. What is added on top is the part
|
|
11
|
+
* that only matters here — below `mobileBreakpoint` there is no room beside the
|
|
12
|
+
* dialog at all, so the rail folds into a drawer and the header grows the
|
|
13
|
+
* button that opens it. LpSidebar leaves that button to the app, and without
|
|
14
|
+
* one the sections are simply unreachable on a phone.
|
|
22
15
|
*/
|
|
23
16
|
import { computed, ref } from "vue"
|
|
24
17
|
import LpButton from "./LpButton.vue"
|
|
@@ -41,10 +34,6 @@ const props = withDefaults(
|
|
|
41
34
|
sections?: SidebarSection[]
|
|
42
35
|
title?: string
|
|
43
36
|
description?: string
|
|
44
|
-
/**
|
|
45
|
-
* Defaults wider than `LpModal`'s `md`: the rail comes out of the content's
|
|
46
|
-
* width, and at `md` what is left is too narrow to be worth splitting.
|
|
47
|
-
*/
|
|
48
37
|
size?: "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "full"
|
|
49
38
|
width?: string
|
|
50
39
|
/** Width of the nav rail (any CSS length). */
|
|
@@ -58,10 +47,12 @@ const props = withDefaults(
|
|
|
58
47
|
mobileBreakpoint?: Breakpoint
|
|
59
48
|
/** Label for the button that opens that drawer. */
|
|
60
49
|
navLabel?: string
|
|
50
|
+
/** Stand the rail to the right of the dialog instead of the left. */
|
|
51
|
+
navSide?: "start" | "end"
|
|
61
52
|
/**
|
|
62
|
-
* Let the body fill the height instead of scrolling itself.
|
|
63
|
-
*
|
|
64
|
-
*
|
|
53
|
+
* Let the body fill the height instead of scrolling itself. Off by default,
|
|
54
|
+
* as in a plain modal: it existed here to make the rail a full-height
|
|
55
|
+
* column inside the body, and the rail is no longer in the body.
|
|
65
56
|
*/
|
|
66
57
|
fillBody?: boolean
|
|
67
58
|
}>(),
|
|
@@ -70,7 +61,7 @@ const props = withDefaults(
|
|
|
70
61
|
navWidth: "13rem",
|
|
71
62
|
mobileBreakpoint: "lg",
|
|
72
63
|
navLabel: "Sections",
|
|
73
|
-
|
|
64
|
+
navSide: "start",
|
|
74
65
|
},
|
|
75
66
|
)
|
|
76
67
|
|
|
@@ -86,23 +77,50 @@ const active = computed({
|
|
|
86
77
|
set: (v: string) => emit("update:modelValue", v),
|
|
87
78
|
})
|
|
88
79
|
|
|
89
|
-
const hasNav = computed(
|
|
90
|
-
() => Boolean(props.items?.length || props.sections?.length),
|
|
91
|
-
)
|
|
80
|
+
const hasNav = computed(() => Boolean(props.items?.length || props.sections?.length))
|
|
92
81
|
|
|
93
82
|
/*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
83
|
+
* Both halves of the responsive switch are spelled out rather than built from
|
|
84
|
+
* the prop: a class assembled at runtime is in nobody's Tailwind scan and would
|
|
85
|
+
* never be generated. The rail hides exactly where the button appears.
|
|
86
|
+
*
|
|
87
|
+
* `responsive` on LpSidebar hides its own rail below the breakpoint and draws
|
|
88
|
+
* the drawer as a SIBLING of it, so wrapping the whole thing in a `hidden`
|
|
89
|
+
* element takes the drawer with it — and the sections are then unreachable at
|
|
90
|
+
* exactly the width where the drawer is the only way to them. What is left here
|
|
91
|
+
* is the gap, which has to disappear along with the rail: an aside that is
|
|
92
|
+
* empty but still spaced pushes the dialog off centre.
|
|
98
93
|
*/
|
|
94
|
+
const RAIL_GAP: Record<Breakpoint, string> = {
|
|
95
|
+
sm: "sm:mr-3",
|
|
96
|
+
md: "md:mr-3",
|
|
97
|
+
lg: "lg:mr-3",
|
|
98
|
+
xl: "xl:mr-3",
|
|
99
|
+
}
|
|
100
|
+
const RAIL_GAP_END: Record<Breakpoint, string> = {
|
|
101
|
+
sm: "sm:ml-3",
|
|
102
|
+
md: "md:ml-3",
|
|
103
|
+
lg: "lg:ml-3",
|
|
104
|
+
xl: "xl:ml-3",
|
|
105
|
+
}
|
|
99
106
|
const TRIGGER_VISIBILITY: Record<Breakpoint, string> = {
|
|
100
107
|
sm: "sm:hidden",
|
|
101
108
|
md: "md:hidden",
|
|
102
109
|
lg: "lg:hidden",
|
|
103
110
|
xl: "xl:hidden",
|
|
104
111
|
}
|
|
112
|
+
const railClass = computed(() =>
|
|
113
|
+
(props.navSide === "end" ? RAIL_GAP_END : RAIL_GAP)[props.mobileBreakpoint],
|
|
114
|
+
)
|
|
105
115
|
const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint])
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* The rail carries the dialog's own ceiling, so a long list scrolls with the
|
|
119
|
+
* dialog instead of running past the bottom of the window. Inline because it
|
|
120
|
+
* mirrors LpModal's max-height, which is not a class this file can put in the
|
|
121
|
+
* consuming app's scan.
|
|
122
|
+
*/
|
|
123
|
+
const railStyle = { maxHeight: "min(90vh, calc(100dvh - 2rem))" }
|
|
106
124
|
</script>
|
|
107
125
|
|
|
108
126
|
<template>
|
|
@@ -115,6 +133,49 @@ const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint])
|
|
|
115
133
|
:fill-body="fillBody"
|
|
116
134
|
@update:open="emit('update:open', $event)"
|
|
117
135
|
>
|
|
136
|
+
<!-- The gap sits on a wrapper rather than on LpSidebar itself: LpSidebar
|
|
137
|
+
splits its `class` between its root and the inner `nav`, so a margin
|
|
138
|
+
given to it lands on both. -->
|
|
139
|
+
<template v-if="hasNav && navSide === 'start'" #aside>
|
|
140
|
+
<div :class="railClass">
|
|
141
|
+
<LpSidebar
|
|
142
|
+
v-model="active"
|
|
143
|
+
v-model:open="navOpen"
|
|
144
|
+
variant="panel"
|
|
145
|
+
:items="items"
|
|
146
|
+
:sections="sections"
|
|
147
|
+
:width="navWidth"
|
|
148
|
+
:searchable="searchable"
|
|
149
|
+
:search-placeholder="searchPlaceholder"
|
|
150
|
+
:loading="loading"
|
|
151
|
+
responsive
|
|
152
|
+
:mobile-breakpoint="mobileBreakpoint"
|
|
153
|
+
:style="railStyle"
|
|
154
|
+
class="rounded-card border border-line bg-surface-raised p-2 shadow-panel"
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
</template>
|
|
158
|
+
|
|
159
|
+
<template v-if="hasNav && navSide === 'end'" #asideEnd>
|
|
160
|
+
<div :class="railClass">
|
|
161
|
+
<LpSidebar
|
|
162
|
+
v-model="active"
|
|
163
|
+
v-model:open="navOpen"
|
|
164
|
+
variant="panel"
|
|
165
|
+
:items="items"
|
|
166
|
+
:sections="sections"
|
|
167
|
+
:width="navWidth"
|
|
168
|
+
:searchable="searchable"
|
|
169
|
+
:search-placeholder="searchPlaceholder"
|
|
170
|
+
:loading="loading"
|
|
171
|
+
responsive
|
|
172
|
+
:mobile-breakpoint="mobileBreakpoint"
|
|
173
|
+
:style="railStyle"
|
|
174
|
+
class="rounded-card border border-line bg-surface-raised p-2 shadow-panel"
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
118
179
|
<template #title>
|
|
119
180
|
<div class="flex items-center gap-2">
|
|
120
181
|
<LpButton
|
|
@@ -132,43 +193,7 @@ const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint])
|
|
|
132
193
|
</div>
|
|
133
194
|
</template>
|
|
134
195
|
|
|
135
|
-
<
|
|
136
|
-
<!-- `shrink-0`: a flex item's default is to give up width to a greedy
|
|
137
|
-
sibling, so a wide table in the body would squeeze the rail down to
|
|
138
|
-
its longest unbreakable word. -->
|
|
139
|
-
<LpSidebar
|
|
140
|
-
v-if="hasNav"
|
|
141
|
-
v-model="active"
|
|
142
|
-
v-model:open="navOpen"
|
|
143
|
-
variant="panel"
|
|
144
|
-
:items="items"
|
|
145
|
-
:sections="sections"
|
|
146
|
-
:width="navWidth"
|
|
147
|
-
:searchable="searchable"
|
|
148
|
-
:search-placeholder="searchPlaceholder"
|
|
149
|
-
:loading="loading"
|
|
150
|
-
responsive
|
|
151
|
-
:mobile-breakpoint="mobileBreakpoint"
|
|
152
|
-
class="shrink-0"
|
|
153
|
-
>
|
|
154
|
-
<template v-if="$slots.navHeader" #header>
|
|
155
|
-
<slot name="navHeader" />
|
|
156
|
-
</template>
|
|
157
|
-
<template v-if="$slots.navItem" #item="slotProps">
|
|
158
|
-
<slot name="navItem" v-bind="slotProps" />
|
|
159
|
-
</template>
|
|
160
|
-
<template v-if="$slots.navFooter" #footer>
|
|
161
|
-
<slot name="navFooter" />
|
|
162
|
-
</template>
|
|
163
|
-
</LpSidebar>
|
|
164
|
-
|
|
165
|
-
<!-- `min-w-0` is the same guard in the other direction: without it a wide
|
|
166
|
-
child sets this column's minimum to its own width, and the dialog
|
|
167
|
-
grows past its size instead of the content scrolling. -->
|
|
168
|
-
<div class="flex min-h-0 min-w-0 flex-1 flex-col">
|
|
169
|
-
<slot />
|
|
170
|
-
</div>
|
|
171
|
-
</div>
|
|
196
|
+
<slot />
|
|
172
197
|
|
|
173
198
|
<template v-if="$slots.footer" #footer>
|
|
174
199
|
<slot name="footer" />
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
import { defineComponent, computed, useSlots, watch, onBeforeUnmount, ref, openBlock, createBlock, unref, withCtx, createVNode, normalizeStyle, createElementVNode, mergeProps, createElementBlock, renderSlot, createTextVNode, toDisplayString, createCommentVNode, normalizeClass } from "vue";
|
|
2
|
-
import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
|
|
3
|
-
import { useShift } from "../composables/useShift.js";
|
|
4
|
-
import { useModalLayer } from "../components/modalDepth.js";
|
|
5
|
-
import { CLOSE_ICON } from "../components/dropdown.js";
|
|
6
|
-
import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
|
|
7
|
-
import { _ as _sfc_main$2 } from "./LpScrollArea-D8xyKXcF.js";
|
|
8
|
-
const _hoisted_1 = {
|
|
9
|
-
key: 0,
|
|
10
|
-
class: "flex shrink-0 items-start justify-between gap-4 p-5 pb-3"
|
|
11
|
-
};
|
|
12
|
-
const _hoisted_2 = { class: "flex flex-col gap-1" };
|
|
13
|
-
const _hoisted_3 = {
|
|
14
|
-
key: 3,
|
|
15
|
-
class: "flex shrink-0 justify-end gap-2 p-5 pt-4"
|
|
16
|
-
};
|
|
17
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
18
|
-
__name: "LpModal",
|
|
19
|
-
props: {
|
|
20
|
-
open: { type: Boolean },
|
|
21
|
-
title: {},
|
|
22
|
-
description: {},
|
|
23
|
-
size: { default: "md" },
|
|
24
|
-
width: {},
|
|
25
|
-
fillBody: { type: Boolean },
|
|
26
|
-
autoFocus: { default: "auto" }
|
|
27
|
-
},
|
|
28
|
-
emits: ["update:open"],
|
|
29
|
-
setup(__props) {
|
|
30
|
-
const { claim, release, layer } = useModalLayer();
|
|
31
|
-
const props = __props;
|
|
32
|
-
const describedByAttrs = computed(
|
|
33
|
-
() => props.description ? {} : { "aria-describedby": void 0 }
|
|
34
|
-
);
|
|
35
|
-
const widthClass = computed(() => {
|
|
36
|
-
if (props.width) return "";
|
|
37
|
-
return {
|
|
38
|
-
sm: "w-[min(92vw,24rem)]",
|
|
39
|
-
md: "w-[min(92vw,28rem)]",
|
|
40
|
-
lg: "w-[min(92vw,34rem)]",
|
|
41
|
-
xl: "w-[min(92vw,42rem)]",
|
|
42
|
-
"2xl": "w-[min(94vw,56rem)]",
|
|
43
|
-
"3xl": "w-[min(95vw,72rem)]",
|
|
44
|
-
full: "w-[96vw]"
|
|
45
|
-
}[props.size];
|
|
46
|
-
});
|
|
47
|
-
const slots = useSlots();
|
|
48
|
-
watch(() => props.open, (open) => open ? claim() : release(), { immediate: true });
|
|
49
|
-
onBeforeUnmount(release);
|
|
50
|
-
const bodyPad = computed(
|
|
51
|
-
() => [
|
|
52
|
-
"px-5",
|
|
53
|
-
props.title || slots.title ? "pt-1" : "pt-5",
|
|
54
|
-
slots.footer ? "" : "pb-5"
|
|
55
|
-
].filter(Boolean).join(" ")
|
|
56
|
-
);
|
|
57
|
-
const panelRef = ref(null);
|
|
58
|
-
const AUTO_FOCUS_ORDER = [
|
|
59
|
-
"input:not([type=hidden]):not([disabled]):not([readonly])",
|
|
60
|
-
"textarea:not([disabled]):not([readonly])",
|
|
61
|
-
"select:not([disabled])",
|
|
62
|
-
"[data-lp-autofocus]",
|
|
63
|
-
"button:not([disabled]):not([data-lp-dialog-close])",
|
|
64
|
-
"[href]",
|
|
65
|
-
"[tabindex]:not([tabindex='-1'])"
|
|
66
|
-
];
|
|
67
|
-
function onOpenAutoFocus(event) {
|
|
68
|
-
if (props.autoFocus === "none") return;
|
|
69
|
-
const panel = panelRef.value?.$el;
|
|
70
|
-
if (!(panel instanceof HTMLElement)) return;
|
|
71
|
-
const selectors = props.autoFocus === "auto" ? AUTO_FOCUS_ORDER : [props.autoFocus];
|
|
72
|
-
for (const selector of selectors) {
|
|
73
|
-
const target = panel.querySelector(selector);
|
|
74
|
-
if (!target || target.hasAttribute("disabled")) continue;
|
|
75
|
-
event.preventDefault();
|
|
76
|
-
if (!target.closest("[data-lp-ring-owner]")) {
|
|
77
|
-
target.setAttribute("data-lp-focus-ring", "");
|
|
78
|
-
target.addEventListener(
|
|
79
|
-
"blur",
|
|
80
|
-
() => target.removeAttribute("data-lp-focus-ring"),
|
|
81
|
-
{ once: true }
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
target.focus();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const {
|
|
89
|
-
el: sizedEl,
|
|
90
|
-
tweening,
|
|
91
|
-
resizing
|
|
92
|
-
} = useShift({ axis: "height", enabled: () => props.open });
|
|
93
|
-
watch(
|
|
94
|
-
() => props.open ? panelRef.value : null,
|
|
95
|
-
(panel) => {
|
|
96
|
-
const el = panel?.$el;
|
|
97
|
-
sizedEl.value = el instanceof HTMLElement ? el : null;
|
|
98
|
-
},
|
|
99
|
-
{ flush: "post" }
|
|
100
|
-
);
|
|
101
|
-
return (_ctx, _cache) => {
|
|
102
|
-
return openBlock(), createBlock(unref(DialogRoot), {
|
|
103
|
-
open: __props.open,
|
|
104
|
-
"onUpdate:open": _cache[0] || (_cache[0] = (v) => _ctx.$emit("update:open", v))
|
|
105
|
-
}, {
|
|
106
|
-
default: withCtx(() => [
|
|
107
|
-
createVNode(unref(DialogPortal), null, {
|
|
108
|
-
default: withCtx(() => [
|
|
109
|
-
createVNode(unref(DialogOverlay), {
|
|
110
|
-
class: "lp-scrim fixed inset-0 data-[state=open]:animate-[fade-in_var(--duration-medium)_var(--ease-emphasized)] data-[state=closed]:animate-[fade-out_120ms_ease]",
|
|
111
|
-
style: normalizeStyle({ zIndex: unref(layer).scrim })
|
|
112
|
-
}, null, 8, ["style"]),
|
|
113
|
-
createElementVNode("div", {
|
|
114
|
-
class: "fixed inset-0 flex items-center justify-center pointer-events-none",
|
|
115
|
-
style: normalizeStyle({ zIndex: unref(layer).panel })
|
|
116
|
-
}, [
|
|
117
|
-
createVNode(unref(DialogContent), mergeProps({
|
|
118
|
-
ref_key: "panelRef",
|
|
119
|
-
ref: panelRef,
|
|
120
|
-
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, unref(tweening) ? "transition-[height] duration-fast ease-[var(--ease-emphasized)] motion-reduce:transition-none" : ""]],
|
|
121
|
-
style: __props.width ? { width: __props.width } : void 0
|
|
122
|
-
}, describedByAttrs.value, { onOpenAutoFocus }), {
|
|
123
|
-
default: withCtx(() => [
|
|
124
|
-
__props.title || _ctx.$slots.title ? (openBlock(), createElementBlock("header", _hoisted_1, [
|
|
125
|
-
createElementVNode("div", _hoisted_2, [
|
|
126
|
-
createVNode(unref(DialogTitle), { class: "text-base font-semibold text-ink" }, {
|
|
127
|
-
default: withCtx(() => [
|
|
128
|
-
renderSlot(_ctx.$slots, "title", {}, () => [
|
|
129
|
-
createTextVNode(toDisplayString(__props.title), 1)
|
|
130
|
-
])
|
|
131
|
-
]),
|
|
132
|
-
_: 3
|
|
133
|
-
}),
|
|
134
|
-
__props.description ? (openBlock(), createBlock(unref(DialogDescription), {
|
|
135
|
-
key: 0,
|
|
136
|
-
class: "text-sm text-muted"
|
|
137
|
-
}, {
|
|
138
|
-
default: withCtx(() => [
|
|
139
|
-
createTextVNode(toDisplayString(__props.description), 1)
|
|
140
|
-
]),
|
|
141
|
-
_: 1
|
|
142
|
-
})) : createCommentVNode("", true)
|
|
143
|
-
]),
|
|
144
|
-
createVNode(unref(DialogClose), {
|
|
145
|
-
"data-lp-dialog-close": "",
|
|
146
|
-
class: "group flex shrink-0 items-center rounded-md p-1 text-muted outline-none transition-colors duration-[var(--duration-fast)] hover:text-ink focus-visible:ring-2 focus-visible:ring-ring",
|
|
147
|
-
"aria-label": "Close"
|
|
148
|
-
}, {
|
|
149
|
-
default: withCtx(() => [
|
|
150
|
-
createVNode(_sfc_main$1, {
|
|
151
|
-
name: "lucide:x",
|
|
152
|
-
size: 18,
|
|
153
|
-
class: normalizeClass(unref(CLOSE_ICON))
|
|
154
|
-
}, null, 8, ["class"])
|
|
155
|
-
]),
|
|
156
|
-
_: 1
|
|
157
|
-
})
|
|
158
|
-
])) : createCommentVNode("", true),
|
|
159
|
-
__props.fillBody ? (openBlock(), createElementBlock("div", {
|
|
160
|
-
key: 1,
|
|
161
|
-
class: normalizeClass(["flex min-h-0 flex-1 flex-col overflow-hidden text-sm text-ink/90", bodyPad.value])
|
|
162
|
-
}, [
|
|
163
|
-
renderSlot(_ctx.$slots, "default")
|
|
164
|
-
], 2)) : (openBlock(), createBlock(_sfc_main$2, {
|
|
165
|
-
key: 2,
|
|
166
|
-
class: normalizeClass(["min-h-0 flex-1 text-sm text-ink/90", unref(resizing) ? "[&_[data-scrollbarimpl]]:invisible" : ""]),
|
|
167
|
-
"content-class": bodyPad.value
|
|
168
|
-
}, {
|
|
169
|
-
default: withCtx(() => [
|
|
170
|
-
renderSlot(_ctx.$slots, "default")
|
|
171
|
-
]),
|
|
172
|
-
_: 3
|
|
173
|
-
}, 8, ["class", "content-class"])),
|
|
174
|
-
_ctx.$slots.footer ? (openBlock(), createElementBlock("footer", _hoisted_3, [
|
|
175
|
-
renderSlot(_ctx.$slots, "footer")
|
|
176
|
-
])) : createCommentVNode("", true)
|
|
177
|
-
]),
|
|
178
|
-
_: 3
|
|
179
|
-
}, 16, ["class", "style"])
|
|
180
|
-
], 4)
|
|
181
|
-
]),
|
|
182
|
-
_: 3
|
|
183
|
-
})
|
|
184
|
-
]),
|
|
185
|
-
_: 3
|
|
186
|
-
}, 8, ["open"]);
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
export {
|
|
191
|
-
_sfc_main as _
|
|
192
|
-
};
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { defineComponent, ref, computed, openBlock, createBlock, createSlots, withCtx, createElementVNode, renderSlot, normalizeProps, guardReactiveProps, createCommentVNode, normalizeClass, createVNode, createTextVNode, toDisplayString } from "vue";
|
|
2
|
-
import { _ as _sfc_main$3 } from "./LpButton-5VXXCjwF.js";
|
|
3
|
-
import { _ as _sfc_main$4 } from "./LpIcon-CCnX5_2j.js";
|
|
4
|
-
import { _ as _sfc_main$1 } from "./LpModal-DAqXzsm-.js";
|
|
5
|
-
import { _ as _sfc_main$2 } from "./LpSidebar-DcYavim2.js";
|
|
6
|
-
const _hoisted_1 = { class: "flex items-center gap-2" };
|
|
7
|
-
const _hoisted_2 = { class: "flex min-h-0 flex-1 gap-4" };
|
|
8
|
-
const _hoisted_3 = { class: "flex min-h-0 min-w-0 flex-1 flex-col" };
|
|
9
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
10
|
-
__name: "LpModalNav",
|
|
11
|
-
props: {
|
|
12
|
-
open: { type: Boolean },
|
|
13
|
-
modelValue: {},
|
|
14
|
-
items: {},
|
|
15
|
-
sections: {},
|
|
16
|
-
title: {},
|
|
17
|
-
description: {},
|
|
18
|
-
size: { default: "2xl" },
|
|
19
|
-
width: {},
|
|
20
|
-
navWidth: { default: "13rem" },
|
|
21
|
-
searchable: { type: Boolean },
|
|
22
|
-
searchPlaceholder: {},
|
|
23
|
-
loading: { type: Boolean },
|
|
24
|
-
mobileBreakpoint: { default: "lg" },
|
|
25
|
-
navLabel: { default: "Sections" },
|
|
26
|
-
fillBody: { type: Boolean, default: true }
|
|
27
|
-
},
|
|
28
|
-
emits: ["update:open", "update:modelValue"],
|
|
29
|
-
setup(__props, { emit: __emit }) {
|
|
30
|
-
const props = __props;
|
|
31
|
-
const emit = __emit;
|
|
32
|
-
const navOpen = ref(false);
|
|
33
|
-
const active = computed({
|
|
34
|
-
get: () => props.modelValue ?? "",
|
|
35
|
-
set: (v) => emit("update:modelValue", v)
|
|
36
|
-
});
|
|
37
|
-
const hasNav = computed(
|
|
38
|
-
() => Boolean(props.items?.length || props.sections?.length)
|
|
39
|
-
);
|
|
40
|
-
const TRIGGER_VISIBILITY = {
|
|
41
|
-
sm: "sm:hidden",
|
|
42
|
-
md: "md:hidden",
|
|
43
|
-
lg: "lg:hidden",
|
|
44
|
-
xl: "xl:hidden"
|
|
45
|
-
};
|
|
46
|
-
const triggerClass = computed(() => TRIGGER_VISIBILITY[props.mobileBreakpoint]);
|
|
47
|
-
return (_ctx, _cache) => {
|
|
48
|
-
return openBlock(), createBlock(_sfc_main$1, {
|
|
49
|
-
open: __props.open,
|
|
50
|
-
title: __props.title,
|
|
51
|
-
description: __props.description,
|
|
52
|
-
size: __props.size,
|
|
53
|
-
width: __props.width,
|
|
54
|
-
"fill-body": __props.fillBody,
|
|
55
|
-
"onUpdate:open": _cache[3] || (_cache[3] = ($event) => emit("update:open", $event))
|
|
56
|
-
}, createSlots({
|
|
57
|
-
title: withCtx(() => [
|
|
58
|
-
createElementVNode("div", _hoisted_1, [
|
|
59
|
-
hasNav.value ? (openBlock(), createBlock(_sfc_main$3, {
|
|
60
|
-
key: 0,
|
|
61
|
-
variant: "ghost",
|
|
62
|
-
size: "sm",
|
|
63
|
-
square: "",
|
|
64
|
-
"aria-label": __props.navLabel,
|
|
65
|
-
class: normalizeClass(triggerClass.value),
|
|
66
|
-
onClick: _cache[0] || (_cache[0] = ($event) => navOpen.value = true)
|
|
67
|
-
}, {
|
|
68
|
-
default: withCtx(() => [
|
|
69
|
-
createVNode(_sfc_main$4, {
|
|
70
|
-
name: "lucide:menu",
|
|
71
|
-
size: 18
|
|
72
|
-
})
|
|
73
|
-
]),
|
|
74
|
-
_: 1
|
|
75
|
-
}, 8, ["aria-label", "class"])) : createCommentVNode("", true),
|
|
76
|
-
renderSlot(_ctx.$slots, "title", {}, () => [
|
|
77
|
-
createTextVNode(toDisplayString(__props.title), 1)
|
|
78
|
-
])
|
|
79
|
-
])
|
|
80
|
-
]),
|
|
81
|
-
default: withCtx(() => [
|
|
82
|
-
createElementVNode("div", _hoisted_2, [
|
|
83
|
-
hasNav.value ? (openBlock(), createBlock(_sfc_main$2, {
|
|
84
|
-
key: 0,
|
|
85
|
-
modelValue: active.value,
|
|
86
|
-
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => active.value = $event),
|
|
87
|
-
open: navOpen.value,
|
|
88
|
-
"onUpdate:open": _cache[2] || (_cache[2] = ($event) => navOpen.value = $event),
|
|
89
|
-
variant: "panel",
|
|
90
|
-
items: __props.items,
|
|
91
|
-
sections: __props.sections,
|
|
92
|
-
width: __props.navWidth,
|
|
93
|
-
searchable: __props.searchable,
|
|
94
|
-
"search-placeholder": __props.searchPlaceholder,
|
|
95
|
-
loading: __props.loading,
|
|
96
|
-
responsive: "",
|
|
97
|
-
"mobile-breakpoint": __props.mobileBreakpoint,
|
|
98
|
-
class: "shrink-0"
|
|
99
|
-
}, createSlots({ _: 2 }, [
|
|
100
|
-
_ctx.$slots.navHeader ? {
|
|
101
|
-
name: "header",
|
|
102
|
-
fn: withCtx(() => [
|
|
103
|
-
renderSlot(_ctx.$slots, "navHeader")
|
|
104
|
-
]),
|
|
105
|
-
key: "0"
|
|
106
|
-
} : void 0,
|
|
107
|
-
_ctx.$slots.navItem ? {
|
|
108
|
-
name: "item",
|
|
109
|
-
fn: withCtx((slotProps) => [
|
|
110
|
-
renderSlot(_ctx.$slots, "navItem", normalizeProps(guardReactiveProps(slotProps)))
|
|
111
|
-
]),
|
|
112
|
-
key: "1"
|
|
113
|
-
} : void 0,
|
|
114
|
-
_ctx.$slots.navFooter ? {
|
|
115
|
-
name: "footer",
|
|
116
|
-
fn: withCtx(() => [
|
|
117
|
-
renderSlot(_ctx.$slots, "navFooter")
|
|
118
|
-
]),
|
|
119
|
-
key: "2"
|
|
120
|
-
} : void 0
|
|
121
|
-
]), 1032, ["modelValue", "open", "items", "sections", "width", "searchable", "search-placeholder", "loading", "mobile-breakpoint"])) : createCommentVNode("", true),
|
|
122
|
-
createElementVNode("div", _hoisted_3, [
|
|
123
|
-
renderSlot(_ctx.$slots, "default")
|
|
124
|
-
])
|
|
125
|
-
])
|
|
126
|
-
]),
|
|
127
|
-
_: 2
|
|
128
|
-
}, [
|
|
129
|
-
_ctx.$slots.footer ? {
|
|
130
|
-
name: "footer",
|
|
131
|
-
fn: withCtx(() => [
|
|
132
|
-
renderSlot(_ctx.$slots, "footer")
|
|
133
|
-
]),
|
|
134
|
-
key: "0"
|
|
135
|
-
} : void 0
|
|
136
|
-
]), 1032, ["open", "title", "description", "size", "width", "fill-body"]);
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
export {
|
|
141
|
-
_sfc_main as _
|
|
142
|
-
};
|