kerfjs 5.0.0-beta.7 → 5.0.0-beta.71
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/CHANGELOG.md +2488 -117
- package/README.md +96 -37
- package/ai/cursorrules +110 -61
- package/ai/manifest.json +16 -7
- package/ai/skill.md +120 -75
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +10 -6
- package/dist/actions.js.map +1 -1
- package/dist/array-signal.js +1 -1
- package/dist/async.js +22 -19
- package/dist/async.js.map +1 -1
- package/dist/attach.js +1 -66
- package/dist/attach.js.map +1 -1
- package/dist/{chunk-HW7KSM2Y.js → chunk-3C6F6NM4.js} +1 -1
- package/dist/{chunk-HW7KSM2Y.js.map → chunk-3C6F6NM4.js.map} +1 -1
- package/dist/{chunk-GY4XV2UV.js → chunk-4TX6YJDM.js} +10 -3
- package/dist/chunk-4TX6YJDM.js.map +1 -0
- package/dist/{chunk-QIP723L4.js → chunk-5WRGJZV6.js} +3 -2
- package/dist/chunk-5WRGJZV6.js.map +1 -0
- package/dist/{chunk-KZJXHFIB.js → chunk-BI4HGICV.js} +3 -3
- package/dist/chunk-BI4HGICV.js.map +1 -0
- package/dist/chunk-FM6N2B4N.js +70 -0
- package/dist/chunk-FM6N2B4N.js.map +1 -0
- package/dist/{chunk-LVH3GC6B.js → chunk-H7WTRM6F.js} +3 -3
- package/dist/chunk-H7WTRM6F.js.map +1 -0
- package/dist/{chunk-U6FK33SG.js → chunk-IHMDBBUJ.js} +2 -2
- package/dist/chunk-IHMDBBUJ.js.map +1 -0
- package/dist/chunk-JN7EJM3B.js +86 -0
- package/dist/chunk-JN7EJM3B.js.map +1 -0
- package/dist/{chunk-UZJ6I4T6.js → chunk-MK42GLPV.js} +1 -1
- package/dist/chunk-MK42GLPV.js.map +1 -0
- package/dist/{chunk-SVATPF5R.js → chunk-TLL3KXT6.js} +249 -160
- package/dist/chunk-TLL3KXT6.js.map +1 -0
- package/dist/{chunk-ZDCJZCNO.js → chunk-UGNPDLNI.js} +78 -45
- package/dist/chunk-UGNPDLNI.js.map +1 -0
- package/dist/{chunk-V46JKE44.js → chunk-WKIPLNVO.js} +1 -1
- package/dist/chunk-WKIPLNVO.js.map +1 -0
- package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
- package/dist/dev.d.ts +31 -13
- package/dist/dev.js +101 -35
- package/dist/dev.js.map +1 -1
- package/dist/html.js +5 -4
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +29 -20
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime.d.ts +13 -5
- package/dist/jsx-runtime.js +1 -1
- package/dist/list.d.ts +7 -3
- package/dist/list.js +126 -28
- package/dist/list.js.map +1 -1
- package/dist/overlay.d.ts +25 -28
- package/dist/overlay.js +580 -275
- package/dist/overlay.js.map +1 -1
- package/dist/remount.js +2 -2
- package/dist/router.d.ts +13 -4
- package/dist/router.js +37 -10
- package/dist/router.js.map +1 -1
- package/dist/scope.d.ts +35 -8
- package/dist/scope.js +61 -30
- package/dist/scope.js.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/timing.d.ts +3 -2
- package/dist/timing.js +1 -1
- package/dist/timing.js.map +1 -1
- package/llms.txt +11 -4
- package/package.json +32 -7
- package/setup/cli.mjs +90 -0
- package/setup/index.d.mts +37 -0
- package/setup/index.mjs +1347 -0
- package/setup/jsonc.mjs +201 -0
- package/setup/state.schema.json +56 -0
- package/dist/chunk-GY4XV2UV.js.map +0 -1
- package/dist/chunk-KPXIOG2C.js +0 -54
- package/dist/chunk-KPXIOG2C.js.map +0 -1
- package/dist/chunk-KZJXHFIB.js.map +0 -1
- package/dist/chunk-LVH3GC6B.js.map +0 -1
- package/dist/chunk-QIP723L4.js.map +0 -1
- package/dist/chunk-SVATPF5R.js.map +0 -1
- package/dist/chunk-U6FK33SG.js.map +0 -1
- package/dist/chunk-UZJ6I4T6.js.map +0 -1
- package/dist/chunk-V46JKE44.js.map +0 -1
- package/dist/chunk-ZDCJZCNO.js.map +0 -1
package/dist/overlay.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { jsx, escapeHtml } from './chunk-ZDCJZCNO.js';
|
|
1
|
+
import { attach } from './chunk-FM6N2B4N.js';
|
|
2
|
+
import { mount } from './chunk-TLL3KXT6.js';
|
|
3
|
+
import { delegate } from './chunk-JN7EJM3B.js';
|
|
6
4
|
|
|
7
5
|
|
|
6
|
+
import { jsx, escapeHtml } from './chunk-UGNPDLNI.js';
|
|
8
7
|
|
|
8
|
+
import { OVERLAY_HOST_SELECTOR } from './chunk-4TX6YJDM.js';
|
|
9
|
+
import { devHooks } from './chunk-3C6F6NM4.js';
|
|
9
10
|
|
|
10
11
|
// src/overlay-position.ts
|
|
11
12
|
function positionAnchored(el, anchor, options = {}) {
|
|
@@ -19,12 +20,25 @@ function positionAnchored(el, anchor, options = {}) {
|
|
|
19
20
|
const belowTop = anchorRect.bottom + gap;
|
|
20
21
|
const aboveTop = anchorRect.top - gap - elementRect.height;
|
|
21
22
|
let below = placement !== "top";
|
|
22
|
-
if (below && belowTop + elementRect.height > viewportHeight && aboveTop >= 0)
|
|
23
|
-
|
|
23
|
+
if (below && belowTop + elementRect.height > viewportHeight && aboveTop >= 0)
|
|
24
|
+
below = false;
|
|
25
|
+
else if (!below && aboveTop < 0 && belowTop + elementRect.height <= viewportHeight)
|
|
26
|
+
below = true;
|
|
24
27
|
let left = align === "end" ? anchorRect.right - elementRect.width : anchorRect.left;
|
|
25
28
|
left = Math.max(0, Math.min(left, viewportWidth - elementRect.width));
|
|
26
|
-
|
|
27
|
-
el.style.
|
|
29
|
+
const [originX, originY, scaleX, scaleY] = fixedOrigin(el);
|
|
30
|
+
el.style.left = `${(left - originX) / scaleX}px`;
|
|
31
|
+
el.style.top = `${((below ? belowTop : aboveTop) - originY) / scaleY}px`;
|
|
32
|
+
}
|
|
33
|
+
function fixedOrigin(el) {
|
|
34
|
+
const parent = el.parentNode;
|
|
35
|
+
if (!parent) return [0, 0, 1, 1];
|
|
36
|
+
const probe = el.ownerDocument.createElement("div");
|
|
37
|
+
probe.style.cssText = "position:fixed;left:0;top:0;width:1px;height:1px;margin:0;padding:0;border:0";
|
|
38
|
+
parent.insertBefore(probe, el);
|
|
39
|
+
const rect = probe.getBoundingClientRect();
|
|
40
|
+
probe.remove();
|
|
41
|
+
return [rect.left, rect.top, rect.width || 1, rect.height || 1];
|
|
28
42
|
}
|
|
29
43
|
function autoReposition(el, anchor, options = {}) {
|
|
30
44
|
const reposition = () => positionAnchored(el, anchor, options);
|
|
@@ -40,9 +54,9 @@ function autoReposition(el, anchor, options = {}) {
|
|
|
40
54
|
// src/overlay-core.ts
|
|
41
55
|
var FOCUSABLE = 'a[href],area[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),iframe,[tabindex]:not([tabindex="-1"]),[contenteditable="true"]';
|
|
42
56
|
function focusable(root) {
|
|
43
|
-
const items = Array.from(
|
|
44
|
-
|
|
45
|
-
);
|
|
57
|
+
const items = Array.from(
|
|
58
|
+
root.querySelectorAll(FOCUSABLE)
|
|
59
|
+
).filter((el) => !el.hasAttribute("hidden") && el.tabIndex >= 0);
|
|
46
60
|
for (const el of items) if (!el.hasAttribute("tabindex")) el.tabIndex = 0;
|
|
47
61
|
return items;
|
|
48
62
|
}
|
|
@@ -52,10 +66,78 @@ function supportsDialog() {
|
|
|
52
66
|
function supportsPopover() {
|
|
53
67
|
return typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.showPopover === "function";
|
|
54
68
|
}
|
|
55
|
-
var
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
return
|
|
69
|
+
var OVERLAY_ARBITRATION = /* @__PURE__ */ Symbol("kerf.overlayArbitration");
|
|
70
|
+
function overlayArbitration(doc) {
|
|
71
|
+
const arbitrated = doc;
|
|
72
|
+
return arbitrated[OVERLAY_ARBITRATION] ??= {
|
|
73
|
+
stack: [],
|
|
74
|
+
handled: /* @__PURE__ */ new WeakSet(),
|
|
75
|
+
// KF-AHY6H4: an app-owned modal `<dialog>` opened outside kerf would leave
|
|
76
|
+
// the kerf surfaces already showing inert beneath it. A dialog fires
|
|
77
|
+
// `toggle` after it opens (it does not bubble, so listen in the capture
|
|
78
|
+
// phase); re-host above it exactly as kerf does for its own dialogs, which
|
|
79
|
+
// are skipped here because `overlay()` already re-hosted synchronously.
|
|
80
|
+
onToggle: (event) => {
|
|
81
|
+
const dialog = event.target;
|
|
82
|
+
const arbitration = arbitrated[OVERLAY_ARBITRATION];
|
|
83
|
+
if (dialog.tagName === "DIALOG" && !arbitration.stack.some(({ el }) => el === dialog) && isOpenModal(dialog, arbitration))
|
|
84
|
+
rehostAbove(arbitration, dialog);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function rehostAbove(arbitration, dialog) {
|
|
89
|
+
for (const { el, liftAbove } of arbitration.stack)
|
|
90
|
+
if (el !== dialog && el.isConnected && !dialog.contains(el)) liftAbove?.();
|
|
91
|
+
}
|
|
92
|
+
function isOpenModal(dialog, arbitration) {
|
|
93
|
+
if (!dialog.isConnected || dialog.open !== true)
|
|
94
|
+
return false;
|
|
95
|
+
if (arbitration.stack.some(({ el, modal }) => modal && el === dialog))
|
|
96
|
+
return true;
|
|
97
|
+
try {
|
|
98
|
+
return dialog.matches(":modal");
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function modalDialogOpen(doc, arbitration) {
|
|
104
|
+
return arbitration.stack.some(({ el }) => isOpenModal(el, arbitration)) || Array.from(doc.querySelectorAll("dialog[open]")).some(
|
|
105
|
+
(dialog) => isOpenModal(dialog, arbitration)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function overlayHost(anchor) {
|
|
109
|
+
const arbitration = overlayArbitration(document);
|
|
110
|
+
const dialog = anchor.closest("dialog");
|
|
111
|
+
if (dialog === null || !isOpenModal(dialog, arbitration)) return void 0;
|
|
112
|
+
let host = Array.from(dialog.querySelectorAll(OVERLAY_HOST_SELECTOR)).find(
|
|
113
|
+
(el) => el.closest("dialog") === dialog
|
|
114
|
+
);
|
|
115
|
+
if (host === void 0 && arbitration.stack.some(({ el }) => el === dialog)) {
|
|
116
|
+
dialog.insertAdjacentHTML(
|
|
117
|
+
"beforeend",
|
|
118
|
+
'<div data-kerf-overlay-host data-morph-skip data-morph-preserve style="display:contents"></div>'
|
|
119
|
+
);
|
|
120
|
+
host = dialog.lastElementChild;
|
|
121
|
+
}
|
|
122
|
+
return host;
|
|
123
|
+
}
|
|
124
|
+
function ownsInput(stack, self, kind) {
|
|
125
|
+
for (let i = stack.indexOf(self) + 1; i < stack.length; i++) {
|
|
126
|
+
const above = stack[i];
|
|
127
|
+
if (above.modal || kind !== "trap" && above.triggers.includes(kind))
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
function assertInitialFocusSelector(selector) {
|
|
133
|
+
try {
|
|
134
|
+
document.createDocumentFragment().querySelector(selector);
|
|
135
|
+
} catch (cause) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`overlay(): invalid initialFocus selector ${JSON.stringify(selector)}`,
|
|
138
|
+
{ cause }
|
|
139
|
+
);
|
|
140
|
+
}
|
|
59
141
|
}
|
|
60
142
|
function overlay(content, options = {}) {
|
|
61
143
|
const {
|
|
@@ -69,10 +151,15 @@ function overlay(content, options = {}) {
|
|
|
69
151
|
outsideIgnore,
|
|
70
152
|
native = false
|
|
71
153
|
} = options;
|
|
154
|
+
if (typeof initialFocus === "string")
|
|
155
|
+
assertInitialFocusSelector(initialFocus);
|
|
72
156
|
const triggers = dismiss === false ? [] : Array.isArray(dismiss) ? dismiss : [dismiss];
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
157
|
+
const arbitration = overlayArbitration(document);
|
|
158
|
+
const containerDialog = container.closest("dialog");
|
|
159
|
+
const liftOverModal = !native && !(containerDialog !== null && isOpenModal(containerDialog, arbitration)) && modalDialogOpen(document, arbitration);
|
|
160
|
+
const topLayer = native || liftOverModal;
|
|
161
|
+
const useDialog = topLayer && trap && supportsDialog();
|
|
162
|
+
const usePopover = topLayer && !trap && supportsPopover();
|
|
76
163
|
const wrapper = useDialog ? document.createElement("dialog") : document.createElement("div");
|
|
77
164
|
wrapper.className = className;
|
|
78
165
|
if (usePopover) wrapper.setAttribute("popover", "manual");
|
|
@@ -80,122 +167,224 @@ function overlay(content, options = {}) {
|
|
|
80
167
|
wrapper.setAttribute("role", role);
|
|
81
168
|
wrapper.setAttribute("aria-modal", "true");
|
|
82
169
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (useDialog) {
|
|
91
|
-
wrapper.showModal();
|
|
92
|
-
nativeOpened = true;
|
|
93
|
-
} else if (usePopover) {
|
|
94
|
-
wrapper.showPopover();
|
|
95
|
-
wrapper.style.inset = "auto";
|
|
96
|
-
nativeOpened = true;
|
|
97
|
-
}
|
|
170
|
+
const surface = {
|
|
171
|
+
el: wrapper,
|
|
172
|
+
triggers,
|
|
173
|
+
modal: trap,
|
|
174
|
+
restoreTo: document.activeElement
|
|
175
|
+
};
|
|
176
|
+
const owns = (kind, event) => !(event !== void 0 && arbitration.handled.has(event)) && ownsInput(arbitration.stack, surface, kind);
|
|
98
177
|
const removers = [];
|
|
99
178
|
const resultBox = {};
|
|
100
179
|
const result = new Promise((resolve) => {
|
|
101
180
|
resultBox.resolve = resolve;
|
|
102
181
|
});
|
|
103
182
|
const state = { closed: false };
|
|
183
|
+
let disposeMount;
|
|
184
|
+
let nativeOpened = false;
|
|
185
|
+
if (!trap)
|
|
186
|
+
surface.liftAbove = () => {
|
|
187
|
+
if (!supportsPopover()) {
|
|
188
|
+
devHooks.overlayBlockedByModal?.("hidden");
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const el = wrapper;
|
|
192
|
+
if (nativeOpened) el.hidePopover();
|
|
193
|
+
else {
|
|
194
|
+
wrapper.setAttribute("popover", "manual");
|
|
195
|
+
wrapper.style.inset = "auto";
|
|
196
|
+
}
|
|
197
|
+
el.showPopover();
|
|
198
|
+
nativeOpened = true;
|
|
199
|
+
if (wrapper.querySelector(FOCUSABLE) !== null)
|
|
200
|
+
devHooks.overlayBlockedByModal?.("inert");
|
|
201
|
+
};
|
|
104
202
|
function close(value) {
|
|
105
203
|
if (state.closed) return;
|
|
106
204
|
state.closed = true;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
205
|
+
const { stack } = arbitration;
|
|
206
|
+
const stackIndex = stack.indexOf(surface);
|
|
207
|
+
if (stackIndex !== -1) stack.splice(stackIndex, 1);
|
|
208
|
+
if (stack.length === 0)
|
|
209
|
+
document.removeEventListener("toggle", arbitration.onToggle, true);
|
|
210
|
+
for (const other of stack)
|
|
211
|
+
if (other.restoreTo !== null && wrapper.contains(other.restoreTo))
|
|
212
|
+
other.restoreTo = surface.restoreTo;
|
|
213
|
+
const active = document.activeElement;
|
|
111
214
|
for (const remove of removers) remove();
|
|
112
|
-
disposeMount();
|
|
215
|
+
disposeMount?.();
|
|
113
216
|
if (nativeOpened) {
|
|
114
217
|
nativeOpened = false;
|
|
115
218
|
if (useDialog) wrapper.close();
|
|
116
219
|
else wrapper.hidePopover();
|
|
117
220
|
}
|
|
118
221
|
wrapper.remove();
|
|
119
|
-
|
|
222
|
+
restoreFocus(active);
|
|
120
223
|
resultBox.resolve?.(value);
|
|
121
224
|
}
|
|
225
|
+
function restoreFocus(active) {
|
|
226
|
+
if (active instanceof HTMLElement && active.isConnected && arbitration.stack.some((other) => other.el.contains(active))) {
|
|
227
|
+
if (document.activeElement !== active) active.focus();
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const { restoreTo } = surface;
|
|
231
|
+
if (restoreTo instanceof HTMLElement && restoreTo.isConnected)
|
|
232
|
+
restoreTo.focus();
|
|
233
|
+
}
|
|
122
234
|
function userDismiss() {
|
|
123
235
|
onDismiss?.();
|
|
124
236
|
close();
|
|
125
237
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
238
|
+
try {
|
|
239
|
+
container.appendChild(wrapper);
|
|
240
|
+
disposeMount = mount(
|
|
241
|
+
wrapper,
|
|
242
|
+
typeof content === "function" ? content : () => content
|
|
243
|
+
);
|
|
244
|
+
if (arbitration.stack.push(surface) === 1)
|
|
245
|
+
document.addEventListener("toggle", arbitration.onToggle, true);
|
|
246
|
+
if (useDialog) {
|
|
247
|
+
wrapper.showModal();
|
|
248
|
+
nativeOpened = true;
|
|
249
|
+
rehostAbove(arbitration, wrapper);
|
|
250
|
+
} else if (usePopover) {
|
|
251
|
+
wrapper.showPopover();
|
|
252
|
+
nativeOpened = true;
|
|
253
|
+
wrapper.style.inset = "auto";
|
|
254
|
+
}
|
|
255
|
+
installDismissal();
|
|
256
|
+
if (container.closest(OVERLAY_HOST_SELECTOR) !== null) {
|
|
257
|
+
makeTabReachable();
|
|
258
|
+
removers.push(attach(wrapper, () => close));
|
|
259
|
+
}
|
|
260
|
+
applyInitialFocus();
|
|
261
|
+
} catch (error) {
|
|
262
|
+
close();
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
if (liftOverModal) {
|
|
266
|
+
if (!useDialog && !usePopover) devHooks.overlayBlockedByModal?.("hidden");
|
|
267
|
+
else if (usePopover && wrapper.querySelector(FOCUSABLE) !== null)
|
|
268
|
+
devHooks.overlayBlockedByModal?.("inert");
|
|
269
|
+
}
|
|
270
|
+
return { el: wrapper, close, result };
|
|
271
|
+
function installDismissal() {
|
|
272
|
+
const wantEscape = triggers.includes("escape");
|
|
273
|
+
if (useDialog) {
|
|
274
|
+
const onCancel = (event) => {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
if (wantEscape && owns("escape")) userDismiss();
|
|
277
|
+
};
|
|
278
|
+
wrapper.addEventListener("cancel", onCancel);
|
|
279
|
+
removers.push(() => wrapper.removeEventListener("cancel", onCancel));
|
|
280
|
+
} else if (wantEscape || trap) {
|
|
281
|
+
const onKeydown = (event) => {
|
|
282
|
+
if (wantEscape && event.key === "Escape") {
|
|
283
|
+
if (!owns("escape", event)) return;
|
|
284
|
+
event.stopPropagation();
|
|
145
285
|
event.preventDefault();
|
|
286
|
+
arbitration.handled.add(event);
|
|
287
|
+
userDismiss();
|
|
146
288
|
return;
|
|
147
289
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
290
|
+
if (trap && event.key === "Tab" && owns("trap")) {
|
|
291
|
+
const items = focusable(wrapper);
|
|
292
|
+
if (items.length === 0) {
|
|
293
|
+
event.preventDefault();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
const first = items[0];
|
|
297
|
+
const last = items[items.length - 1];
|
|
298
|
+
const active = document.activeElement;
|
|
299
|
+
const outside = !wrapper.contains(active);
|
|
300
|
+
if (event.shiftKey && (active === first || outside)) {
|
|
301
|
+
event.preventDefault();
|
|
302
|
+
last.focus();
|
|
303
|
+
} else if (!event.shiftKey && (active === last || outside)) {
|
|
304
|
+
event.preventDefault();
|
|
305
|
+
first.focus();
|
|
306
|
+
}
|
|
158
307
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
308
|
+
};
|
|
309
|
+
document.addEventListener("keydown", onKeydown, true);
|
|
310
|
+
removers.push(
|
|
311
|
+
() => document.removeEventListener("keydown", onKeydown, true)
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (triggers.includes("backdrop")) {
|
|
315
|
+
const onClick = (event) => {
|
|
316
|
+
if (event.target !== wrapper || !owns("backdrop", event)) return;
|
|
317
|
+
arbitration.handled.add(event);
|
|
318
|
+
userDismiss();
|
|
319
|
+
};
|
|
320
|
+
wrapper.addEventListener("click", onClick);
|
|
321
|
+
removers.push(() => wrapper.removeEventListener("click", onClick));
|
|
322
|
+
}
|
|
323
|
+
if (triggers.includes("outside")) {
|
|
324
|
+
const ignore = outsideIgnore === void 0 ? [] : Array.isArray(outsideIgnore) ? outsideIgnore : [outsideIgnore];
|
|
325
|
+
const onDocClick = (event) => {
|
|
326
|
+
const target = event.target;
|
|
327
|
+
if (target === null) return;
|
|
328
|
+
if (wrapper.contains(target)) return;
|
|
329
|
+
if (ignore.some((el) => el === target || el.contains(target))) return;
|
|
330
|
+
if (!owns("outside", event)) return;
|
|
331
|
+
arbitration.handled.add(event);
|
|
332
|
+
userDismiss();
|
|
333
|
+
};
|
|
334
|
+
document.addEventListener("click", onDocClick, true);
|
|
335
|
+
removers.push(
|
|
336
|
+
() => document.removeEventListener("click", onDocClick, true)
|
|
337
|
+
);
|
|
338
|
+
}
|
|
171
339
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
176
|
-
const target = event.target;
|
|
177
|
-
if (target === null) return;
|
|
178
|
-
if (wrapper.contains(target)) return;
|
|
179
|
-
if (ignore.some((el) => el === target || el.contains(target))) return;
|
|
180
|
-
userDismiss();
|
|
340
|
+
function makeTabReachable() {
|
|
341
|
+
focusable(wrapper);
|
|
342
|
+
const onTab = (event) => {
|
|
343
|
+
if (event.key === "Tab") focusable(wrapper);
|
|
181
344
|
};
|
|
182
|
-
document.addEventListener("
|
|
183
|
-
removers.push(() => document.removeEventListener("
|
|
345
|
+
document.addEventListener("keydown", onTab, true);
|
|
346
|
+
removers.push(() => document.removeEventListener("keydown", onTab, true));
|
|
184
347
|
}
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const first = focusable(wrapper)[0];
|
|
190
|
-
if (first !== void 0) {
|
|
191
|
-
first.focus();
|
|
348
|
+
function applyInitialFocus() {
|
|
349
|
+
if (initialFocus !== false) {
|
|
350
|
+
if (typeof initialFocus === "string") {
|
|
351
|
+
wrapper.querySelector(initialFocus)?.focus();
|
|
192
352
|
} else {
|
|
193
|
-
|
|
194
|
-
|
|
353
|
+
const first = focusable(wrapper)[0];
|
|
354
|
+
if (first !== void 0) {
|
|
355
|
+
first.focus();
|
|
356
|
+
} else {
|
|
357
|
+
wrapper.tabIndex = -1;
|
|
358
|
+
wrapper.focus();
|
|
359
|
+
}
|
|
195
360
|
}
|
|
196
361
|
}
|
|
197
362
|
}
|
|
198
|
-
|
|
363
|
+
}
|
|
364
|
+
function wireDialog(handle, wire, settle) {
|
|
365
|
+
const disposers = [];
|
|
366
|
+
const failure = [];
|
|
367
|
+
try {
|
|
368
|
+
wire(
|
|
369
|
+
(dispose) => void disposers.push(dispose),
|
|
370
|
+
(action) => () => {
|
|
371
|
+
try {
|
|
372
|
+
action();
|
|
373
|
+
} catch (error) {
|
|
374
|
+
failure[0] = error;
|
|
375
|
+
handle.close(failure);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
disposers.forEach((dispose) => dispose());
|
|
381
|
+
handle.close();
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
return handle.result.then((value) => {
|
|
385
|
+
if (value === failure) throw failure[0];
|
|
386
|
+
return settle(value);
|
|
387
|
+
});
|
|
199
388
|
}
|
|
200
389
|
function popover(anchor, content, options = {}) {
|
|
201
390
|
const {
|
|
@@ -212,7 +401,7 @@ function popover(anchor, content, options = {}) {
|
|
|
212
401
|
} = options;
|
|
213
402
|
const extraIgnore = outsideIgnore === void 0 ? [] : Array.isArray(outsideIgnore) ? [...outsideIgnore] : [outsideIgnore];
|
|
214
403
|
const handle = overlay(content, {
|
|
215
|
-
container,
|
|
404
|
+
container: container ?? overlayHost(anchor),
|
|
216
405
|
className,
|
|
217
406
|
dismiss,
|
|
218
407
|
trap: false,
|
|
@@ -221,8 +410,19 @@ function popover(anchor, content, options = {}) {
|
|
|
221
410
|
outsideIgnore: [anchor, ...extraIgnore],
|
|
222
411
|
native
|
|
223
412
|
});
|
|
224
|
-
|
|
413
|
+
let stopReposition;
|
|
414
|
+
try {
|
|
415
|
+
stopReposition = autoReposition(handle.el, anchor, {
|
|
416
|
+
placement,
|
|
417
|
+
align,
|
|
418
|
+
gap
|
|
419
|
+
});
|
|
420
|
+
} catch (error) {
|
|
421
|
+
handle.close();
|
|
422
|
+
throw error;
|
|
423
|
+
}
|
|
225
424
|
void handle.result.then(stopReposition);
|
|
425
|
+
void handle.result.then(attach(anchor, () => handle.close));
|
|
226
426
|
return handle;
|
|
227
427
|
}
|
|
228
428
|
function tooltip(anchor, content, options = {}) {
|
|
@@ -242,16 +442,40 @@ function tooltip(anchor, content, options = {}) {
|
|
|
242
442
|
let current;
|
|
243
443
|
let presence = 0;
|
|
244
444
|
function show() {
|
|
245
|
-
|
|
445
|
+
if (!anchor.isConnected) return;
|
|
446
|
+
const handle = overlay(body, {
|
|
447
|
+
container: container ?? overlayHost(anchor),
|
|
448
|
+
className,
|
|
449
|
+
dismiss: false,
|
|
450
|
+
trap: false,
|
|
451
|
+
initialFocus: false,
|
|
452
|
+
native
|
|
453
|
+
});
|
|
246
454
|
handle.el.setAttribute("role", role);
|
|
247
|
-
|
|
248
|
-
|
|
455
|
+
let stop;
|
|
456
|
+
try {
|
|
457
|
+
stop = autoReposition(handle.el, anchor, { placement, align, gap });
|
|
458
|
+
} catch (error) {
|
|
459
|
+
handle.close();
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
const unwatch = attach(anchor, () => () => {
|
|
463
|
+
presence = 0;
|
|
464
|
+
if (timers.hide !== void 0) clearTimeout(timers.hide);
|
|
465
|
+
hide();
|
|
466
|
+
});
|
|
467
|
+
current = { handle, stop, unwatch };
|
|
468
|
+
void handle.result.then(() => {
|
|
469
|
+
if (current?.handle === handle) hide();
|
|
470
|
+
});
|
|
249
471
|
}
|
|
250
472
|
function hide() {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
current.handle.close();
|
|
473
|
+
const shown = current;
|
|
474
|
+
if (shown === void 0) return;
|
|
254
475
|
current = void 0;
|
|
476
|
+
shown.unwatch();
|
|
477
|
+
shown.stop();
|
|
478
|
+
shown.handle.close();
|
|
255
479
|
}
|
|
256
480
|
const onEnter = (event) => {
|
|
257
481
|
presence |= event.type === "focus" ? 2 : 1;
|
|
@@ -284,7 +508,14 @@ function tooltip(anchor, content, options = {}) {
|
|
|
284
508
|
|
|
285
509
|
// src/overlay-choice.ts
|
|
286
510
|
function choice(message, actions, options = {}) {
|
|
287
|
-
const {
|
|
511
|
+
const {
|
|
512
|
+
container,
|
|
513
|
+
className = "kerf-overlay",
|
|
514
|
+
title,
|
|
515
|
+
defaultValue,
|
|
516
|
+
native = false,
|
|
517
|
+
render
|
|
518
|
+
} = options;
|
|
288
519
|
const hasDefault = "defaultValue" in options;
|
|
289
520
|
const actionAttrs = actions.map((_, i) => ({ "data-choice": String(i) }));
|
|
290
521
|
const body = render !== void 0 ? render({ message, actions: actionAttrs }) : jsx("div", {
|
|
@@ -317,20 +548,28 @@ function choice(message, actions, options = {}) {
|
|
|
317
548
|
trap: true,
|
|
318
549
|
native
|
|
319
550
|
});
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
551
|
+
void wireDialog(
|
|
552
|
+
handle,
|
|
553
|
+
(track) => {
|
|
554
|
+
track(
|
|
555
|
+
delegate(handle.el, "click", "[data-choice]", (_event, el) => {
|
|
556
|
+
resolveChoice(actions[Number(el.getAttribute("data-choice"))].value);
|
|
557
|
+
handle.close();
|
|
558
|
+
})
|
|
559
|
+
);
|
|
560
|
+
if (hasDefault) {
|
|
561
|
+
const onKeydown = (event) => {
|
|
562
|
+
if (event.key === "Enter") {
|
|
563
|
+
event.preventDefault();
|
|
564
|
+
resolveChoice(defaultValue);
|
|
565
|
+
handle.close();
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
handle.el.addEventListener("keydown", onKeydown);
|
|
330
569
|
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
|
|
570
|
+
},
|
|
571
|
+
() => resolveChoice(null)
|
|
572
|
+
);
|
|
334
573
|
return result;
|
|
335
574
|
}
|
|
336
575
|
|
|
@@ -346,7 +585,11 @@ function confirm(message, options = {}) {
|
|
|
346
585
|
native = false,
|
|
347
586
|
render
|
|
348
587
|
} = options;
|
|
349
|
-
const body = render !== void 0 ? render({
|
|
588
|
+
const body = render !== void 0 ? render({
|
|
589
|
+
message,
|
|
590
|
+
ok: { "data-confirm": "ok" },
|
|
591
|
+
cancel: { "data-confirm": "cancel" }
|
|
592
|
+
}) : jsx("div", {
|
|
350
593
|
class: "kerf-confirm",
|
|
351
594
|
children: [
|
|
352
595
|
title !== void 0 ? jsx("h2", { class: "kerf-confirm__title", children: title }) : "",
|
|
@@ -354,7 +597,11 @@ function confirm(message, options = {}) {
|
|
|
354
597
|
jsx("div", {
|
|
355
598
|
class: "kerf-confirm__actions",
|
|
356
599
|
children: [
|
|
357
|
-
jsx("button", {
|
|
600
|
+
jsx("button", {
|
|
601
|
+
type: "button",
|
|
602
|
+
"data-confirm": "cancel",
|
|
603
|
+
children: cancelText
|
|
604
|
+
}),
|
|
358
605
|
jsx("button", {
|
|
359
606
|
type: "button",
|
|
360
607
|
"data-confirm": "ok",
|
|
@@ -373,61 +620,75 @@ function confirm(message, options = {}) {
|
|
|
373
620
|
trap: true,
|
|
374
621
|
native
|
|
375
622
|
});
|
|
376
|
-
|
|
377
|
-
handle
|
|
378
|
-
|
|
379
|
-
|
|
623
|
+
return wireDialog(
|
|
624
|
+
handle,
|
|
625
|
+
() => {
|
|
626
|
+
delegate(handle.el, "click", "[data-confirm]", (_event, el) => {
|
|
627
|
+
handle.close(el.getAttribute("data-confirm") === "ok");
|
|
628
|
+
});
|
|
629
|
+
},
|
|
630
|
+
(value) => value === true
|
|
631
|
+
);
|
|
380
632
|
}
|
|
381
633
|
|
|
382
|
-
// src/overlay-
|
|
383
|
-
function
|
|
634
|
+
// src/overlay-prompt.ts
|
|
635
|
+
function runValidator(validate, value, owner) {
|
|
636
|
+
const result = validate?.(value);
|
|
637
|
+
if (typeof result?.then === "function") {
|
|
638
|
+
result.then(void 0, () => void 0);
|
|
639
|
+
throw new TypeError(`${owner}: validate returned a promise.`);
|
|
640
|
+
}
|
|
641
|
+
return typeof result === "string" && result.length > 0 ? result : void 0;
|
|
642
|
+
}
|
|
643
|
+
function prompt(message, options = {}) {
|
|
384
644
|
const {
|
|
385
645
|
container,
|
|
386
646
|
className = "kerf-overlay",
|
|
387
647
|
title,
|
|
648
|
+
defaultValue = "",
|
|
649
|
+
placeholder,
|
|
650
|
+
inputType = "text",
|
|
388
651
|
okText = "OK",
|
|
389
652
|
cancelText = "Cancel",
|
|
653
|
+
validate,
|
|
390
654
|
native = false,
|
|
391
655
|
render
|
|
392
656
|
} = options;
|
|
393
|
-
const
|
|
394
|
-
"data-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
});
|
|
657
|
+
const inputAttrs = {
|
|
658
|
+
"data-prompt-input": "",
|
|
659
|
+
type: inputType,
|
|
660
|
+
value: defaultValue,
|
|
661
|
+
...placeholder !== void 0 ? { placeholder } : {}
|
|
662
|
+
};
|
|
400
663
|
const body = render !== void 0 ? render({
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
})),
|
|
407
|
-
ok: { "data-form": "ok" },
|
|
408
|
-
cancel: { "data-form": "cancel" }
|
|
664
|
+
message,
|
|
665
|
+
input: inputAttrs,
|
|
666
|
+
error: { "data-prompt-error": "" },
|
|
667
|
+
ok: { "data-prompt": "ok" },
|
|
668
|
+
cancel: { "data-prompt": "cancel" }
|
|
409
669
|
}) : jsx("div", {
|
|
410
|
-
class: "kerf-
|
|
670
|
+
class: "kerf-prompt",
|
|
411
671
|
children: [
|
|
412
|
-
title !== void 0 ? jsx("h2", { class: "kerf-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
]
|
|
421
|
-
})
|
|
422
|
-
),
|
|
672
|
+
title !== void 0 ? jsx("h2", { class: "kerf-prompt__title", children: title }) : "",
|
|
673
|
+
jsx("label", { class: "kerf-prompt__message", children: message }),
|
|
674
|
+
jsx("input", { class: "kerf-prompt__input", ...inputAttrs }),
|
|
675
|
+
jsx("p", {
|
|
676
|
+
class: "kerf-prompt__error",
|
|
677
|
+
"data-prompt-error": "",
|
|
678
|
+
children: ""
|
|
679
|
+
}),
|
|
423
680
|
jsx("div", {
|
|
424
|
-
class: "kerf-
|
|
681
|
+
class: "kerf-prompt__actions",
|
|
425
682
|
children: [
|
|
426
|
-
jsx("button", { type: "button", "data-form": "cancel", children: cancelText }),
|
|
427
683
|
jsx("button", {
|
|
428
684
|
type: "button",
|
|
429
|
-
"data-
|
|
430
|
-
|
|
685
|
+
"data-prompt": "cancel",
|
|
686
|
+
children: cancelText
|
|
687
|
+
}),
|
|
688
|
+
jsx("button", {
|
|
689
|
+
type: "button",
|
|
690
|
+
"data-prompt": "ok",
|
|
691
|
+
class: "kerf-prompt__ok",
|
|
431
692
|
children: okText
|
|
432
693
|
})
|
|
433
694
|
]
|
|
@@ -438,108 +699,120 @@ function form(fields, options = {}) {
|
|
|
438
699
|
container,
|
|
439
700
|
className,
|
|
440
701
|
dismiss: ["escape", "backdrop"],
|
|
441
|
-
initialFocus: "[data-
|
|
702
|
+
initialFocus: "[data-prompt-input]",
|
|
442
703
|
trap: true,
|
|
443
704
|
native
|
|
444
705
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (errorEl !== null) {
|
|
472
|
-
errorEl.textContent = error;
|
|
473
|
-
errorEl.hidden = false;
|
|
706
|
+
return wireDialog(
|
|
707
|
+
handle,
|
|
708
|
+
(track, guard) => {
|
|
709
|
+
const inputFor = (problem = "render missing") => {
|
|
710
|
+
const input = handle.el.querySelector(
|
|
711
|
+
"input[data-prompt-input]"
|
|
712
|
+
);
|
|
713
|
+
if (input !== null) return input;
|
|
714
|
+
throw new Error(`prompt(): ${problem} <input data-prompt-input>.`);
|
|
715
|
+
};
|
|
716
|
+
inputFor();
|
|
717
|
+
const errorEl = handle.el.querySelector(
|
|
718
|
+
"[data-prompt-error]"
|
|
719
|
+
);
|
|
720
|
+
if (errorEl !== null) errorEl.hidden = true;
|
|
721
|
+
const attemptOk = guard(() => {
|
|
722
|
+
const input = inputFor("removed after open:");
|
|
723
|
+
const value = input.value;
|
|
724
|
+
const error = runValidator(validate, value, "prompt()");
|
|
725
|
+
if (error !== void 0) {
|
|
726
|
+
if (errorEl !== null) {
|
|
727
|
+
errorEl.textContent = error;
|
|
728
|
+
errorEl.hidden = false;
|
|
729
|
+
}
|
|
730
|
+
input.focus();
|
|
731
|
+
return;
|
|
474
732
|
}
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
event.preventDefault();
|
|
493
|
-
attemptOk();
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
return handle.result.then(
|
|
497
|
-
(value) => value !== null && typeof value === "object" ? value : null
|
|
733
|
+
handle.close(value);
|
|
734
|
+
});
|
|
735
|
+
track(
|
|
736
|
+
delegate(handle.el, "click", "[data-prompt]", (_event, el) => {
|
|
737
|
+
if (el.getAttribute("data-prompt") === "ok") attemptOk();
|
|
738
|
+
else handle.close(null);
|
|
739
|
+
})
|
|
740
|
+
);
|
|
741
|
+
const onKeydown = (event) => {
|
|
742
|
+
if (event.key === "Enter" && event.target?.matches("input[data-prompt-input]")) {
|
|
743
|
+
event.preventDefault();
|
|
744
|
+
attemptOk();
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
handle.el.addEventListener("keydown", onKeydown);
|
|
748
|
+
},
|
|
749
|
+
(value) => typeof value === "string" ? value : null
|
|
498
750
|
);
|
|
499
751
|
}
|
|
500
752
|
|
|
501
|
-
// src/overlay-
|
|
502
|
-
function
|
|
753
|
+
// src/overlay-form.ts
|
|
754
|
+
function form(fields, options = {}) {
|
|
503
755
|
const {
|
|
504
756
|
container,
|
|
505
757
|
className = "kerf-overlay",
|
|
506
758
|
title,
|
|
507
|
-
defaultValue = "",
|
|
508
|
-
placeholder,
|
|
509
|
-
inputType = "text",
|
|
510
759
|
okText = "OK",
|
|
511
760
|
cancelText = "Cancel",
|
|
512
|
-
validate,
|
|
513
761
|
native = false,
|
|
514
762
|
render
|
|
515
763
|
} = options;
|
|
516
|
-
const
|
|
517
|
-
"data-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
764
|
+
const fieldAttrs = (field) => ({
|
|
765
|
+
"data-field": field.name,
|
|
766
|
+
name: field.name,
|
|
767
|
+
type: field.type ?? "text",
|
|
768
|
+
value: field.defaultValue ?? "",
|
|
769
|
+
...field.placeholder !== void 0 ? { placeholder: field.placeholder } : {}
|
|
770
|
+
});
|
|
522
771
|
const body = render !== void 0 ? render({
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
772
|
+
fields: fields.map((field) => ({
|
|
773
|
+
name: field.name,
|
|
774
|
+
label: field.label ?? field.name,
|
|
775
|
+
input: fieldAttrs(field),
|
|
776
|
+
error: { "data-field-error": field.name }
|
|
777
|
+
})),
|
|
778
|
+
ok: { "data-form": "ok" },
|
|
779
|
+
cancel: { "data-form": "cancel" }
|
|
528
780
|
}) : jsx("div", {
|
|
529
|
-
class: "kerf-
|
|
781
|
+
class: "kerf-form",
|
|
530
782
|
children: [
|
|
531
|
-
title !== void 0 ? jsx("h2", { class: "kerf-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
783
|
+
title !== void 0 ? jsx("h2", { class: "kerf-form__title", children: title }) : "",
|
|
784
|
+
...fields.map(
|
|
785
|
+
(field) => jsx("div", {
|
|
786
|
+
class: "kerf-form__field",
|
|
787
|
+
children: [
|
|
788
|
+
jsx("label", {
|
|
789
|
+
class: "kerf-form__label",
|
|
790
|
+
children: field.label ?? field.name
|
|
791
|
+
}),
|
|
792
|
+
jsx("input", {
|
|
793
|
+
class: "kerf-form__input",
|
|
794
|
+
...fieldAttrs(field)
|
|
795
|
+
}),
|
|
796
|
+
jsx("p", {
|
|
797
|
+
class: "kerf-form__error",
|
|
798
|
+
"data-field-error": field.name,
|
|
799
|
+
children: ""
|
|
800
|
+
})
|
|
801
|
+
]
|
|
802
|
+
})
|
|
803
|
+
),
|
|
535
804
|
jsx("div", {
|
|
536
|
-
class: "kerf-
|
|
805
|
+
class: "kerf-form__actions",
|
|
537
806
|
children: [
|
|
538
|
-
jsx("button", { type: "button", "data-prompt": "cancel", children: cancelText }),
|
|
539
807
|
jsx("button", {
|
|
540
808
|
type: "button",
|
|
541
|
-
"data-
|
|
542
|
-
|
|
809
|
+
"data-form": "cancel",
|
|
810
|
+
children: cancelText
|
|
811
|
+
}),
|
|
812
|
+
jsx("button", {
|
|
813
|
+
type: "button",
|
|
814
|
+
"data-form": "ok",
|
|
815
|
+
class: "kerf-form__ok",
|
|
543
816
|
children: okText
|
|
544
817
|
})
|
|
545
818
|
]
|
|
@@ -550,42 +823,69 @@ function prompt(message, options = {}) {
|
|
|
550
823
|
container,
|
|
551
824
|
className,
|
|
552
825
|
dismiss: ["escape", "backdrop"],
|
|
553
|
-
initialFocus: "[data-
|
|
826
|
+
initialFocus: "[data-field]",
|
|
554
827
|
trap: true,
|
|
555
828
|
native
|
|
556
829
|
});
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
830
|
+
return wireDialog(
|
|
831
|
+
handle,
|
|
832
|
+
(track, guard) => {
|
|
833
|
+
const errorFor = (name) => Array.from(
|
|
834
|
+
handle.el.querySelectorAll("[data-field-error]")
|
|
835
|
+
).find((el) => el.getAttribute("data-field-error") === name) ?? null;
|
|
836
|
+
const inputFor = (name, problem = "render missing") => {
|
|
837
|
+
const input = Array.from(
|
|
838
|
+
handle.el.querySelectorAll("input[data-field]")
|
|
839
|
+
).find((el) => el.getAttribute("data-field") === name);
|
|
840
|
+
if (input !== void 0) return input;
|
|
841
|
+
throw new Error(`form(): ${problem} <input data-field="${name}">.`);
|
|
842
|
+
};
|
|
843
|
+
for (const field of fields) {
|
|
844
|
+
inputFor(field.name);
|
|
845
|
+
const errorEl = errorFor(field.name);
|
|
846
|
+
if (errorEl !== null) errorEl.hidden = true;
|
|
572
847
|
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
848
|
+
const attemptOk = guard(() => {
|
|
849
|
+
const record = {};
|
|
850
|
+
let firstInvalid = null;
|
|
851
|
+
for (const field of fields) {
|
|
852
|
+
const el = inputFor(field.name, "removed after open:");
|
|
853
|
+
const value = el.value;
|
|
854
|
+
record[field.name] = value;
|
|
855
|
+
const error = runValidator(field.validate, value, "form()");
|
|
856
|
+
const errorEl = errorFor(field.name);
|
|
857
|
+
if (error !== void 0) {
|
|
858
|
+
if (errorEl !== null) {
|
|
859
|
+
errorEl.textContent = error;
|
|
860
|
+
errorEl.hidden = false;
|
|
861
|
+
}
|
|
862
|
+
if (firstInvalid === null) firstInvalid = el;
|
|
863
|
+
} else if (errorEl !== null) {
|
|
864
|
+
errorEl.hidden = true;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (firstInvalid !== null) {
|
|
868
|
+
firstInvalid.focus();
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
handle.close(record);
|
|
872
|
+
});
|
|
873
|
+
track(
|
|
874
|
+
delegate(handle.el, "click", "[data-form]", (_event, el) => {
|
|
875
|
+
if (el.getAttribute("data-form") === "ok") attemptOk();
|
|
876
|
+
else handle.close(null);
|
|
877
|
+
})
|
|
878
|
+
);
|
|
879
|
+
const onKeydown = (event) => {
|
|
880
|
+
if (event.key === "Enter" && event.target?.matches("[data-field]")) {
|
|
881
|
+
event.preventDefault();
|
|
882
|
+
attemptOk();
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
handle.el.addEventListener("keydown", onKeydown);
|
|
886
|
+
},
|
|
887
|
+
(value) => value !== null && typeof value === "object" ? value : null
|
|
888
|
+
);
|
|
589
889
|
}
|
|
590
890
|
|
|
591
891
|
// src/overlay-toast.ts
|
|
@@ -628,7 +928,12 @@ function toast(content, options = {}) {
|
|
|
628
928
|
region.appendChild(el);
|
|
629
929
|
const render = typeof content === "function" ? content : () => typeof content === "string" ? escapeHtml(content) : content;
|
|
630
930
|
const disposeMount = mount(el, render);
|
|
631
|
-
const state = {
|
|
931
|
+
const state = {
|
|
932
|
+
dismissed: false,
|
|
933
|
+
removed: false,
|
|
934
|
+
timer: void 0,
|
|
935
|
+
exitTimer: void 0
|
|
936
|
+
};
|
|
632
937
|
if (enterClass !== void 0) {
|
|
633
938
|
globalThis.requestAnimationFrame(() => {
|
|
634
939
|
if (!state.dismissed) el.classList.add(enterClass);
|