@jsswift/ui 1.0.45 → 1.0.46
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/min-ui.js +4 -4
- package/dist/ui.js +10561 -14120
- package/dist/ui.min.js +4 -4
- package/package.json +2 -2
- package/src/01-foundation-helpers.js +0 -402
- package/src/10-primitives-layout.js +14 -1013
- package/src/20-display-content.js +484 -718
- package/src/21-list-content.js +246 -341
- package/src/22-banner-content.js +3 -116
- package/src/23-tooltip.js +276 -326
- package/src/31-form-advanced.js +2638 -3003
- package/src/40-navigation.js +1262 -1454
- package/src/41-tab-panel.js +896 -1090
- package/src/50-feedback.js +999 -1209
- package/src/60-shell-app.js +5 -265
- package/src/61-form-service.js +304 -386
- package/src/70-dialog.js +209 -277
- package/src/71-menu-overlays.js +1175 -1400
- package/src/80-data.js +433 -503
- package/src/modules.json +59 -23
package/src/70-dialog.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
UI.Dialog = (...args) => {
|
|
2
|
-
const { props, children } = JSswift.uiNormalizeArgs(args);
|
|
3
|
-
const slots = props.slots || {};
|
|
4
|
-
const stateList = ["primary", "secondary", "warning", "danger", "success", "info", "light", "dark"];
|
|
5
|
-
const sizeList = ["xs", "sm", "md", "lg", "xl", "full"];
|
|
1
|
+
UI.Dialog = (...args) => {
|
|
2
|
+
const { props, children } = JSswift.uiNormalizeArgs(args);
|
|
3
|
+
const slots = props.slots || {};
|
|
4
|
+
const stateList = ["primary", "secondary", "warning", "danger", "success", "info", "light", "dark"];
|
|
5
|
+
const sizeList = ["xs", "sm", "md", "lg", "xl", "full"];
|
|
6
6
|
let currentProps = { ...props };
|
|
7
7
|
let entry = null;
|
|
8
8
|
let lastActive = null;
|
|
9
|
-
const resolveRender = (value, ctx) => typeof value === "function" ? value(ctx) : value;
|
|
10
|
-
const splitClasses = (value) => String(value || "").split(/\s+/).filter(Boolean);
|
|
11
|
-
const setTrackedClasses = (target, key, classes) => {
|
|
12
|
-
if (!target) return;
|
|
13
|
-
const prev = target[key] || [];
|
|
14
|
-
if (prev.length) target.classList.remove(...prev);
|
|
15
|
-
const next = (classes || []).filter(Boolean);
|
|
16
|
-
if (next.length) target.classList.add(...next);
|
|
17
|
-
target[key] = next;
|
|
18
|
-
};
|
|
9
|
+
const resolveRender = (value, ctx) => typeof value === "function" ? value(ctx) : value;
|
|
10
|
+
const splitClasses = (value) => String(value || "").split(/\s+/).filter(Boolean);
|
|
11
|
+
const setTrackedClasses = (target, key, classes) => {
|
|
12
|
+
if (!target) return;
|
|
13
|
+
const prev = target[key] || [];
|
|
14
|
+
if (prev.length) target.classList.remove(...prev);
|
|
15
|
+
const next = (classes || []).filter(Boolean);
|
|
16
|
+
if (next.length) target.classList.add(...next);
|
|
17
|
+
target[key] = next;
|
|
18
|
+
};
|
|
19
19
|
const setStyleValue = (target, name, value, formatter = (v) => v) => {
|
|
20
20
|
if (!target) return;
|
|
21
21
|
if (value == null || value === "") {
|
|
@@ -120,141 +120,141 @@
|
|
|
120
120
|
};
|
|
121
121
|
};
|
|
122
122
|
const getOptions = () => currentProps;
|
|
123
|
-
const getStateClass = (opts) => {
|
|
124
|
-
const value = uiUnwrap(opts.state ?? opts.color);
|
|
125
|
-
if (!value) return "";
|
|
126
|
-
return stateList.includes(value) ? value : String(value);
|
|
127
|
-
};
|
|
128
|
-
const getSizeClass = (opts) => {
|
|
129
|
-
const value = uiUnwrap(opts.size);
|
|
130
|
-
return (typeof value === "string" && sizeList.includes(value)) ? value : "";
|
|
131
|
-
};
|
|
132
|
-
const getAlignClass = (opts) => {
|
|
133
|
-
const raw = String(uiUnwrap(opts.actionsAlign ?? opts.footerAlign ?? opts.alignActions ?? "end")).toLowerCase();
|
|
134
|
-
if (["start", "left"].includes(raw)) return "start";
|
|
135
|
-
if (["center", "middle"].includes(raw)) return "center";
|
|
136
|
-
if (["between", "space-between"].includes(raw)) return "between";
|
|
137
|
-
if (["stretch", "full"].includes(raw)) return "stretch";
|
|
138
|
-
return "end";
|
|
139
|
-
};
|
|
140
|
-
const getVerticalAlignClass = (opts) => {
|
|
141
|
-
const raw = String(uiUnwrap(opts.align ?? opts.verticalAlign ?? opts.position ?? "center")).toLowerCase();
|
|
142
|
-
if (["top", "start", "flex-start"].includes(raw)) return "cms-dialog-align-top";
|
|
143
|
-
if (["bottom", "end", "flex-end"].includes(raw)) return "cms-dialog-align-bottom";
|
|
144
|
-
return "";
|
|
145
|
-
};
|
|
146
|
-
const isClosable = (opts) => {
|
|
147
|
-
const value = opts.closable ?? opts.dismissible ?? opts.closeButton;
|
|
148
|
-
return value !== false;
|
|
149
|
-
};
|
|
150
|
-
const buildContent = () => {
|
|
151
|
-
const opts = getOptions();
|
|
152
|
-
const ctx = {
|
|
153
|
-
close,
|
|
154
|
-
dismiss: close,
|
|
155
|
-
open,
|
|
156
|
-
toggle,
|
|
157
|
-
update,
|
|
158
|
-
isOpen,
|
|
159
|
-
entry: () => entry,
|
|
160
|
-
props: opts
|
|
161
|
-
};
|
|
162
|
-
const iconFallback = opts.icon != null
|
|
163
|
-
? (typeof opts.icon === "string"
|
|
164
|
-
? UI.Icon({ name: opts.icon, size: opts.iconSize || "md" })
|
|
165
|
-
: JSswift.ui.slot(opts.icon, { as: "icon" }))
|
|
166
|
-
: null;
|
|
167
|
-
const eyebrowNodes = renderSlotToArray(slots, "eyebrow", ctx, resolveRender(opts.eyebrow, ctx));
|
|
168
|
-
const titleNodes = renderSlotToArray(slots, "title", ctx, resolveRender(opts.title ?? opts.heading ?? opts.header, ctx));
|
|
169
|
-
const subtitleNodes = renderSlotToArray(slots, "subtitle", ctx, resolveRender(opts.subtitle ?? opts.description, ctx));
|
|
170
|
-
const iconNodes = renderSlotToArray(slots, "icon", ctx, iconFallback);
|
|
171
|
-
const customHeaderNodes = renderSlotToArray(slots, "header", ctx, resolveRender(opts.headerContent ?? opts.head, ctx));
|
|
172
|
-
const closeSlotNodes = isClosable(opts)
|
|
173
|
-
? renderSlotToArray(slots, "close", ctx, UI.Btn({
|
|
174
|
-
class: "cms-dialog-close",
|
|
175
|
-
size: "sm",
|
|
176
|
-
outline: true,
|
|
177
|
-
"aria-label": opts.closeLabel || "Chiudi dialog",
|
|
178
|
-
"data-dialog-close": true
|
|
179
|
-
}, UI.Icon({ name: opts.closeIcon || "close", size: "sm" })))
|
|
180
|
-
: [];
|
|
181
|
-
const bodyRaw = opts.content ?? opts.body ?? opts.message ?? (children && children.length ? children : null);
|
|
182
|
-
let contentNodes = renderSlotToArray(slots, "content", ctx, resolveRender(bodyRaw, ctx));
|
|
183
|
-
if (!contentNodes.length) contentNodes = renderSlotToArray(slots, "body", ctx, resolveRender(bodyRaw, ctx));
|
|
184
|
-
if (!contentNodes.length) contentNodes = renderSlotToArray(slots, "default", ctx, resolveRender(bodyRaw, ctx));
|
|
185
|
-
const footerRaw = resolveRender(opts.footer ?? opts.actions, ctx);
|
|
186
|
-
let footerNodes = renderSlotToArray(slots, "footer", ctx, footerRaw);
|
|
187
|
-
if (!footerNodes.length) footerNodes = renderSlotToArray(slots, "actions", ctx, footerRaw);
|
|
188
|
-
|
|
189
|
-
let headerEl = null;
|
|
190
|
-
if (customHeaderNodes.length) {
|
|
191
|
-
headerEl = _.div({ class: "cms-dialog-head cms-dialog-head-custom" }, ...customHeaderNodes);
|
|
192
|
-
} else if (eyebrowNodes.length || titleNodes.length || subtitleNodes.length || iconNodes.length || closeSlotNodes.length) {
|
|
193
|
-
headerEl = _.div(
|
|
194
|
-
{ class: "cms-dialog-head" },
|
|
195
|
-
iconNodes.length ? _.div({ class: "cms-dialog-icon" }, ...iconNodes) : null,
|
|
196
|
-
_.div(
|
|
197
|
-
{ class: "cms-dialog-head-main" },
|
|
198
|
-
eyebrowNodes.length ? _.div({ class: "cms-dialog-eyebrow" }, ...eyebrowNodes) : null,
|
|
199
|
-
titleNodes.length ? _.div({ class: "cms-dialog-title" }, ...titleNodes) : null,
|
|
200
|
-
subtitleNodes.length ? _.div({ class: "cms-dialog-subtitle" }, ...subtitleNodes) : null
|
|
201
|
-
),
|
|
202
|
-
closeSlotNodes.length ? _.div({ class: "cms-dialog-close-wrap" }, ...closeSlotNodes) : null
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const bodyEl = _.div(
|
|
207
|
-
{ class: uiClass(["cms-dialog-body", opts.bodyClass]) },
|
|
208
|
-
...contentNodes
|
|
209
|
-
);
|
|
210
|
-
const footerEl = footerNodes.length
|
|
211
|
-
? _.div({
|
|
212
|
-
class: uiClass([
|
|
213
|
-
"cms-dialog-actions",
|
|
214
|
-
`is-${getAlignClass(opts)}`,
|
|
215
|
-
uiWhen(opts.stackActions, "is-stacked"),
|
|
216
|
-
opts.actionsClass,
|
|
217
|
-
opts.footerClass
|
|
218
|
-
])
|
|
219
|
-
}, ...footerNodes)
|
|
220
|
-
: null;
|
|
221
|
-
|
|
222
|
-
return _.div({
|
|
223
|
-
class: uiClass([
|
|
224
|
-
"cms-dialog-shell",
|
|
225
|
-
uiWhen(!!headerEl, "has-head"),
|
|
226
|
-
uiWhen(!!footerEl, "has-footer"),
|
|
227
|
-
uiWhen(opts.divider !== false, "with-divider")
|
|
228
|
-
])
|
|
229
|
-
}, ...[headerEl, bodyEl, footerEl].filter(Boolean));
|
|
230
|
-
};
|
|
231
|
-
const applyEntryOptions = (currentEntry) => {
|
|
232
|
-
if (!currentEntry?.panel) return;
|
|
233
|
-
const opts = getOptions();
|
|
234
|
-
const stateClass = getStateClass(opts);
|
|
235
|
-
const sizeClass = getSizeClass(opts);
|
|
236
|
-
setTrackedClasses(currentEntry.panel, "_dialogClassTokens", [
|
|
237
|
-
"cms-dialog",
|
|
238
|
-
"cms-singularity",
|
|
239
|
-
"cms-clear-set",
|
|
240
|
-
sizeClass ? `cms-dialog-${sizeClass}` : "",
|
|
241
|
-
stateClass,
|
|
242
|
-
stateClass ? `cms-state-${stateClass}` : "",
|
|
243
|
-
uiUnwrap(opts.fullscreen) ? "fullscreen" : "",
|
|
244
|
-
uiUnwrap(opts.scrollable) ? "scrollable" : "",
|
|
245
|
-
uiUnwrap(opts.stickyHeader) ? "sticky-head" : "",
|
|
246
|
-
uiUnwrap(opts.stickyActions) ? "sticky-actions" : "",
|
|
247
|
-
uiUnwrap(opts.borderless) ? "borderless" : "",
|
|
248
|
-
...splitClasses(opts.class),
|
|
249
|
-
...splitClasses(opts.panelClass)
|
|
250
|
-
]);
|
|
251
|
-
setTrackedClasses(currentEntry.overlay, "_dialogOverlayClassTokens", [
|
|
252
|
-
getVerticalAlignClass(opts),
|
|
253
|
-
uiUnwrap(opts.backdropBlur) ? "cms-dialog-overlay-blur" : "",
|
|
254
|
-
...splitClasses(opts.overlayClass)
|
|
255
|
-
]);
|
|
256
|
-
setStyleValue(currentEntry.panel, "--cms-dialog-width", uiUnwrap(opts.width), toCssSize);
|
|
257
|
-
setStyleValue(currentEntry.panel, "--cms-dialog-min-width", uiUnwrap(opts.minWidth), toCssSize);
|
|
123
|
+
const getStateClass = (opts) => {
|
|
124
|
+
const value = uiUnwrap(opts.state ?? opts.color);
|
|
125
|
+
if (!value) return "";
|
|
126
|
+
return stateList.includes(value) ? value : String(value);
|
|
127
|
+
};
|
|
128
|
+
const getSizeClass = (opts) => {
|
|
129
|
+
const value = uiUnwrap(opts.size);
|
|
130
|
+
return (typeof value === "string" && sizeList.includes(value)) ? value : "";
|
|
131
|
+
};
|
|
132
|
+
const getAlignClass = (opts) => {
|
|
133
|
+
const raw = String(uiUnwrap(opts.actionsAlign ?? opts.footerAlign ?? opts.alignActions ?? "end")).toLowerCase();
|
|
134
|
+
if (["start", "left"].includes(raw)) return "start";
|
|
135
|
+
if (["center", "middle"].includes(raw)) return "center";
|
|
136
|
+
if (["between", "space-between"].includes(raw)) return "between";
|
|
137
|
+
if (["stretch", "full"].includes(raw)) return "stretch";
|
|
138
|
+
return "end";
|
|
139
|
+
};
|
|
140
|
+
const getVerticalAlignClass = (opts) => {
|
|
141
|
+
const raw = String(uiUnwrap(opts.align ?? opts.verticalAlign ?? opts.position ?? "center")).toLowerCase();
|
|
142
|
+
if (["top", "start", "flex-start"].includes(raw)) return "cms-dialog-align-top";
|
|
143
|
+
if (["bottom", "end", "flex-end"].includes(raw)) return "cms-dialog-align-bottom";
|
|
144
|
+
return "";
|
|
145
|
+
};
|
|
146
|
+
const isClosable = (opts) => {
|
|
147
|
+
const value = opts.closable ?? opts.dismissible ?? opts.closeButton;
|
|
148
|
+
return value !== false;
|
|
149
|
+
};
|
|
150
|
+
const buildContent = () => {
|
|
151
|
+
const opts = getOptions();
|
|
152
|
+
const ctx = {
|
|
153
|
+
close,
|
|
154
|
+
dismiss: close,
|
|
155
|
+
open,
|
|
156
|
+
toggle,
|
|
157
|
+
update,
|
|
158
|
+
isOpen,
|
|
159
|
+
entry: () => entry,
|
|
160
|
+
props: opts
|
|
161
|
+
};
|
|
162
|
+
const iconFallback = opts.icon != null
|
|
163
|
+
? (typeof opts.icon === "string"
|
|
164
|
+
? UI.Icon({ name: opts.icon, size: opts.iconSize || "md" })
|
|
165
|
+
: JSswift.ui.slot(opts.icon, { as: "icon" }))
|
|
166
|
+
: null;
|
|
167
|
+
const eyebrowNodes = renderSlotToArray(slots, "eyebrow", ctx, resolveRender(opts.eyebrow, ctx));
|
|
168
|
+
const titleNodes = renderSlotToArray(slots, "title", ctx, resolveRender(opts.title ?? opts.heading ?? opts.header, ctx));
|
|
169
|
+
const subtitleNodes = renderSlotToArray(slots, "subtitle", ctx, resolveRender(opts.subtitle ?? opts.description, ctx));
|
|
170
|
+
const iconNodes = renderSlotToArray(slots, "icon", ctx, iconFallback);
|
|
171
|
+
const customHeaderNodes = renderSlotToArray(slots, "header", ctx, resolveRender(opts.headerContent ?? opts.head, ctx));
|
|
172
|
+
const closeSlotNodes = isClosable(opts)
|
|
173
|
+
? renderSlotToArray(slots, "close", ctx, UI.Btn({
|
|
174
|
+
class: "cms-dialog-close",
|
|
175
|
+
size: "sm",
|
|
176
|
+
outline: true,
|
|
177
|
+
"aria-label": opts.closeLabel || "Chiudi dialog",
|
|
178
|
+
"data-dialog-close": true
|
|
179
|
+
}, UI.Icon({ name: opts.closeIcon || "close", size: "sm" })))
|
|
180
|
+
: [];
|
|
181
|
+
const bodyRaw = opts.content ?? opts.body ?? opts.message ?? (children && children.length ? children : null);
|
|
182
|
+
let contentNodes = renderSlotToArray(slots, "content", ctx, resolveRender(bodyRaw, ctx));
|
|
183
|
+
if (!contentNodes.length) contentNodes = renderSlotToArray(slots, "body", ctx, resolveRender(bodyRaw, ctx));
|
|
184
|
+
if (!contentNodes.length) contentNodes = renderSlotToArray(slots, "default", ctx, resolveRender(bodyRaw, ctx));
|
|
185
|
+
const footerRaw = resolveRender(opts.footer ?? opts.actions, ctx);
|
|
186
|
+
let footerNodes = renderSlotToArray(slots, "footer", ctx, footerRaw);
|
|
187
|
+
if (!footerNodes.length) footerNodes = renderSlotToArray(slots, "actions", ctx, footerRaw);
|
|
188
|
+
|
|
189
|
+
let headerEl = null;
|
|
190
|
+
if (customHeaderNodes.length) {
|
|
191
|
+
headerEl = _.div({ class: "cms-dialog-head cms-dialog-head-custom" }, ...customHeaderNodes);
|
|
192
|
+
} else if (eyebrowNodes.length || titleNodes.length || subtitleNodes.length || iconNodes.length || closeSlotNodes.length) {
|
|
193
|
+
headerEl = _.div(
|
|
194
|
+
{ class: "cms-dialog-head" },
|
|
195
|
+
iconNodes.length ? _.div({ class: "cms-dialog-icon" }, ...iconNodes) : null,
|
|
196
|
+
_.div(
|
|
197
|
+
{ class: "cms-dialog-head-main" },
|
|
198
|
+
eyebrowNodes.length ? _.div({ class: "cms-dialog-eyebrow" }, ...eyebrowNodes) : null,
|
|
199
|
+
titleNodes.length ? _.div({ class: "cms-dialog-title" }, ...titleNodes) : null,
|
|
200
|
+
subtitleNodes.length ? _.div({ class: "cms-dialog-subtitle" }, ...subtitleNodes) : null
|
|
201
|
+
),
|
|
202
|
+
closeSlotNodes.length ? _.div({ class: "cms-dialog-close-wrap" }, ...closeSlotNodes) : null
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const bodyEl = _.div(
|
|
207
|
+
{ class: uiClass(["cms-dialog-body", opts.bodyClass]) },
|
|
208
|
+
...contentNodes
|
|
209
|
+
);
|
|
210
|
+
const footerEl = footerNodes.length
|
|
211
|
+
? _.div({
|
|
212
|
+
class: uiClass([
|
|
213
|
+
"cms-dialog-actions",
|
|
214
|
+
`is-${getAlignClass(opts)}`,
|
|
215
|
+
uiWhen(opts.stackActions, "is-stacked"),
|
|
216
|
+
opts.actionsClass,
|
|
217
|
+
opts.footerClass
|
|
218
|
+
])
|
|
219
|
+
}, ...footerNodes)
|
|
220
|
+
: null;
|
|
221
|
+
|
|
222
|
+
return _.div({
|
|
223
|
+
class: uiClass([
|
|
224
|
+
"cms-dialog-shell",
|
|
225
|
+
uiWhen(!!headerEl, "has-head"),
|
|
226
|
+
uiWhen(!!footerEl, "has-footer"),
|
|
227
|
+
uiWhen(opts.divider !== false, "with-divider")
|
|
228
|
+
])
|
|
229
|
+
}, ...[headerEl, bodyEl, footerEl].filter(Boolean));
|
|
230
|
+
};
|
|
231
|
+
const applyEntryOptions = (currentEntry) => {
|
|
232
|
+
if (!currentEntry?.panel) return;
|
|
233
|
+
const opts = getOptions();
|
|
234
|
+
const stateClass = getStateClass(opts);
|
|
235
|
+
const sizeClass = getSizeClass(opts);
|
|
236
|
+
setTrackedClasses(currentEntry.panel, "_dialogClassTokens", [
|
|
237
|
+
"cms-dialog",
|
|
238
|
+
"cms-singularity",
|
|
239
|
+
"cms-clear-set",
|
|
240
|
+
sizeClass ? `cms-dialog-${sizeClass}` : "",
|
|
241
|
+
stateClass,
|
|
242
|
+
stateClass ? `cms-state-${stateClass}` : "",
|
|
243
|
+
uiUnwrap(opts.fullscreen) ? "fullscreen" : "",
|
|
244
|
+
uiUnwrap(opts.scrollable) ? "scrollable" : "",
|
|
245
|
+
uiUnwrap(opts.stickyHeader) ? "sticky-head" : "",
|
|
246
|
+
uiUnwrap(opts.stickyActions) ? "sticky-actions" : "",
|
|
247
|
+
uiUnwrap(opts.borderless) ? "borderless" : "",
|
|
248
|
+
...splitClasses(opts.class),
|
|
249
|
+
...splitClasses(opts.panelClass)
|
|
250
|
+
]);
|
|
251
|
+
setTrackedClasses(currentEntry.overlay, "_dialogOverlayClassTokens", [
|
|
252
|
+
getVerticalAlignClass(opts),
|
|
253
|
+
uiUnwrap(opts.backdropBlur) ? "cms-dialog-overlay-blur" : "",
|
|
254
|
+
...splitClasses(opts.overlayClass)
|
|
255
|
+
]);
|
|
256
|
+
setStyleValue(currentEntry.panel, "--cms-dialog-width", uiUnwrap(opts.width), toCssSize);
|
|
257
|
+
setStyleValue(currentEntry.panel, "--cms-dialog-min-width", uiUnwrap(opts.minWidth), toCssSize);
|
|
258
258
|
setStyleValue(currentEntry.panel, "--cms-dialog-max-width", uiUnwrap(opts.maxWidth), toCssSize);
|
|
259
259
|
setStyleValue(currentEntry.panel, "--cms-dialog-max-height", uiUnwrap(opts.maxHeight), toCssSize);
|
|
260
260
|
setStyleValue(currentEntry.panel, "--cms-dialog-body-max-height", uiUnwrap(opts.bodyMaxHeight ?? opts.contentMaxHeight), toCssSize);
|
|
@@ -266,136 +266,68 @@
|
|
|
266
266
|
if (opts.ariaLabel) currentEntry.panel.setAttribute("aria-label", opts.ariaLabel);
|
|
267
267
|
else currentEntry.panel.removeAttribute("aria-label");
|
|
268
268
|
};
|
|
269
|
-
const renderOpenContent = () => {
|
|
270
|
-
if (!entry?.panel) return;
|
|
271
|
-
entry.panel.replaceChildren(buildContent());
|
|
272
|
-
};
|
|
273
|
-
const update = (nextProps = {}) => {
|
|
274
|
-
if (nextProps && typeof nextProps === "object") currentProps = { ...currentProps, ...nextProps };
|
|
275
|
-
if (entry) {
|
|
276
|
-
applyEntryOptions(entry);
|
|
277
|
-
renderOpenContent();
|
|
278
|
-
}
|
|
279
|
-
return api;
|
|
280
|
-
};
|
|
281
|
-
const open = (nextProps = null) => {
|
|
282
|
-
if (nextProps && typeof nextProps === "object") currentProps = { ...currentProps, ...nextProps };
|
|
283
|
-
if (entry) {
|
|
284
|
-
applyEntryOptions(entry);
|
|
285
|
-
renderOpenContent();
|
|
286
|
-
return entry;
|
|
287
|
-
}
|
|
288
|
-
lastActive = document.activeElement;
|
|
289
|
-
const opts = getOptions();
|
|
290
|
-
const persistent = opts.persistent === true;
|
|
291
|
-
entry = JSswift.overlay.open(() => buildContent(), {
|
|
292
|
-
type: "dialog",
|
|
293
|
-
backdrop: opts.backdrop !== false,
|
|
294
|
-
lockScroll: opts.lockScroll !== false,
|
|
295
|
-
trapFocus: opts.trapFocus !== false,
|
|
296
|
-
autoFocus: opts.autoFocus !== false,
|
|
297
|
-
closeOnOutside: opts.closeOnOutside ?? !persistent,
|
|
298
|
-
closeOnBackdrop: opts.closeOnBackdrop ?? !persistent,
|
|
299
|
-
closeOnEsc: opts.closeOnEsc ?? !persistent,
|
|
269
|
+
const renderOpenContent = () => {
|
|
270
|
+
if (!entry?.panel) return;
|
|
271
|
+
entry.panel.replaceChildren(buildContent());
|
|
272
|
+
};
|
|
273
|
+
const update = (nextProps = {}) => {
|
|
274
|
+
if (nextProps && typeof nextProps === "object") currentProps = { ...currentProps, ...nextProps };
|
|
275
|
+
if (entry) {
|
|
276
|
+
applyEntryOptions(entry);
|
|
277
|
+
renderOpenContent();
|
|
278
|
+
}
|
|
279
|
+
return api;
|
|
280
|
+
};
|
|
281
|
+
const open = (nextProps = null) => {
|
|
282
|
+
if (nextProps && typeof nextProps === "object") currentProps = { ...currentProps, ...nextProps };
|
|
283
|
+
if (entry) {
|
|
284
|
+
applyEntryOptions(entry);
|
|
285
|
+
renderOpenContent();
|
|
286
|
+
return entry;
|
|
287
|
+
}
|
|
288
|
+
lastActive = document.activeElement;
|
|
289
|
+
const opts = getOptions();
|
|
290
|
+
const persistent = opts.persistent === true;
|
|
291
|
+
entry = JSswift.overlay.open(() => buildContent(), {
|
|
292
|
+
type: "dialog",
|
|
293
|
+
backdrop: opts.backdrop !== false,
|
|
294
|
+
lockScroll: opts.lockScroll !== false,
|
|
295
|
+
trapFocus: opts.trapFocus !== false,
|
|
296
|
+
autoFocus: opts.autoFocus !== false,
|
|
297
|
+
closeOnOutside: opts.closeOnOutside ?? !persistent,
|
|
298
|
+
closeOnBackdrop: opts.closeOnBackdrop ?? !persistent,
|
|
299
|
+
closeOnEsc: opts.closeOnEsc ?? !persistent,
|
|
300
300
|
className: uiClassStatic(["cms-dialog"]),
|
|
301
301
|
onClose: () => {
|
|
302
302
|
entry?._dialogViewportCleanup?.();
|
|
303
303
|
entry?.panel?.removeEventListener("click", onPanelClick);
|
|
304
304
|
entry = null;
|
|
305
305
|
getOptions().onClose?.();
|
|
306
|
-
if (lastActive && typeof lastActive.focus === "function") {
|
|
307
|
-
setTimeout(() => lastActive.focus(), 0);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
const onPanelClick = (e) => {
|
|
312
|
-
const target = e.target;
|
|
313
|
-
if (target && target.closest && target.closest("[data-dialog-close]")) close();
|
|
314
|
-
};
|
|
315
|
-
entry.panel.addEventListener("click", onPanelClick);
|
|
316
|
-
applyEntryOptions(entry);
|
|
317
|
-
overlayEnter(entry);
|
|
318
|
-
getOptions().onOpen?.(entry);
|
|
319
|
-
return entry;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const close = () => {
|
|
323
|
-
if (!entry) return;
|
|
324
|
-
const toClose = entry;
|
|
325
|
-
overlayLeave(toClose, () => JSswift.overlay.close(toClose.id));
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
const isOpen = () => !!entry;
|
|
329
|
-
const toggle = (nextProps = null) => isOpen() ? (close(), null) : open(nextProps);
|
|
330
|
-
const api = { open, close, toggle, update, isOpen, entry: () => entry, props: () => ({ ...currentProps }) };
|
|
331
|
-
|
|
332
|
-
return api;
|
|
333
|
-
};
|
|
334
|
-
if (JSswift.isDev?.()) {
|
|
335
|
-
UI.meta = UI.meta || {};
|
|
336
|
-
UI.meta.Dialog = {
|
|
337
|
-
signature: "UI.Dialog(props) | UI.Dialog(props, ...children) -> { open, close, toggle, update, isOpen }",
|
|
338
|
-
props: {
|
|
339
|
-
title: "String|Node|Function|Array|({ close })=>Node",
|
|
340
|
-
subtitle: "String|Node|Function|Array|({ close })=>Node",
|
|
341
|
-
eyebrow: "String|Node|Function|Array|({ close })=>Node",
|
|
342
|
-
icon: "String|Node|Function|Array",
|
|
343
|
-
content: "Node|Function|Array|({ close })=>Node",
|
|
344
|
-
body: "Alias of content",
|
|
345
|
-
actions: "Node|Function|Array|({ close })=>Node",
|
|
346
|
-
footer: "Alias of actions",
|
|
347
|
-
size: "xs|sm|md|lg|xl|full",
|
|
348
|
-
state: "primary|secondary|warning|danger|success|info|light|dark",
|
|
349
|
-
color: "Alias of state",
|
|
350
|
-
width: "string|number",
|
|
351
|
-
minWidth: "string|number",
|
|
352
|
-
maxWidth: "string|number",
|
|
353
|
-
maxHeight: "string|number",
|
|
354
|
-
bodyMaxHeight: "string|number",
|
|
355
|
-
persistent: "boolean",
|
|
356
|
-
closable: "boolean",
|
|
357
|
-
closeButton: "boolean",
|
|
358
|
-
closeIcon: "string",
|
|
359
|
-
align: "top|center|bottom",
|
|
360
|
-
actionsAlign: "start|center|end|between|stretch",
|
|
361
|
-
stickyHeader: "boolean",
|
|
362
|
-
stickyActions: "boolean",
|
|
363
|
-
scrollable: "boolean",
|
|
364
|
-
stackActions: "boolean",
|
|
365
|
-
fullscreen: "boolean",
|
|
366
|
-
backdrop: "boolean",
|
|
367
|
-
backdropBlur: "boolean",
|
|
368
|
-
lockScroll: "boolean",
|
|
369
|
-
trapFocus: "boolean",
|
|
370
|
-
autoFocus: "boolean",
|
|
371
|
-
closeOnOutside: "boolean",
|
|
372
|
-
closeOnBackdrop: "boolean",
|
|
373
|
-
closeOnEsc: "boolean",
|
|
374
|
-
slots: "{ icon?, eyebrow?, title?, subtitle?, header?, content?, body?, footer?, actions?, close?, default? }",
|
|
375
|
-
class: "string",
|
|
376
|
-
panelClass: "string",
|
|
377
|
-
overlayClass: "string",
|
|
378
|
-
style: "object"
|
|
379
|
-
},
|
|
380
|
-
events: {
|
|
381
|
-
onOpen: "(entry)",
|
|
382
|
-
onClose: "void"
|
|
383
|
-
},
|
|
384
|
-
slots: {
|
|
385
|
-
icon: "Dialog icon ({ close })",
|
|
386
|
-
eyebrow: "Eyebrow above the title ({ close })",
|
|
387
|
-
title: "Dialog title ({ close })",
|
|
388
|
-
subtitle: "Dialog subtitle ({ close })",
|
|
389
|
-
header: "Custom header ({ close })",
|
|
390
|
-
content: "Dialog body ({ close })",
|
|
391
|
-
body: "Alias of content ({ close })",
|
|
392
|
-
footer: "Custom footer ({ close })",
|
|
393
|
-
actions: "Dialog actions ({ close })",
|
|
394
|
-
close: "Close action ({ close })",
|
|
395
|
-
default: "Fallback body content ({ close })"
|
|
396
|
-
},
|
|
397
|
-
description: "Standardized dialog overlay with variants, animations, structured slots, and imperative API.",
|
|
398
|
-
returns: "Object { open(overrides?), close(), toggle(overrides?), update(props), isOpen(), entry(), props() }"
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
|
|
306
|
+
if (lastActive && typeof lastActive.focus === "function") {
|
|
307
|
+
setTimeout(() => lastActive.focus(), 0);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
const onPanelClick = (e) => {
|
|
312
|
+
const target = e.target;
|
|
313
|
+
if (target && target.closest && target.closest("[data-dialog-close]")) close();
|
|
314
|
+
};
|
|
315
|
+
entry.panel.addEventListener("click", onPanelClick);
|
|
316
|
+
applyEntryOptions(entry);
|
|
317
|
+
overlayEnter(entry);
|
|
318
|
+
getOptions().onOpen?.(entry);
|
|
319
|
+
return entry;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const close = () => {
|
|
323
|
+
if (!entry) return;
|
|
324
|
+
const toClose = entry;
|
|
325
|
+
overlayLeave(toClose, () => JSswift.overlay.close(toClose.id));
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const isOpen = () => !!entry;
|
|
329
|
+
const toggle = (nextProps = null) => isOpen() ? (close(), null) : open(nextProps);
|
|
330
|
+
const api = { open, close, toggle, update, isOpen, entry: () => entry, props: () => ({ ...currentProps }) };
|
|
331
|
+
|
|
332
|
+
return api;
|
|
333
|
+
};
|