kerfjs 4.3.0-beta.1 → 4.4.0-beta.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/overlay.js CHANGED
@@ -37,228 +37,7 @@ function autoReposition(el, anchor, options = {}) {
37
37
  };
38
38
  }
39
39
 
40
- // src/overlay-toast.ts
41
- var TOAST_SET = /* @__PURE__ */ Symbol("kerf.toasts");
42
- function toastRegion(container) {
43
- if (container !== void 0) return container;
44
- const existing = document.querySelector(".kerf-toasts");
45
- if (existing !== null) return existing;
46
- const region = document.createElement("div");
47
- region.className = "kerf-toasts";
48
- region.setAttribute("aria-live", "polite");
49
- document.body.appendChild(region);
50
- return region;
51
- }
52
- function toast(content, options = {}) {
53
- const {
54
- container,
55
- className = "kerf-toast",
56
- duration = 4e3,
57
- role = "status",
58
- mode = "stack",
59
- collapse = "fade",
60
- variant,
61
- enterClass,
62
- exitClass,
63
- exitDuration = 0
64
- } = options;
65
- const region = toastRegion(container);
66
- const active = region[TOAST_SET] ??= /* @__PURE__ */ new Set();
67
- if (mode === "replace") {
68
- for (const d of [...active]) {
69
- if (collapse === "instant") d.removeNow();
70
- else d();
71
- }
72
- }
73
- const el = document.createElement("div");
74
- el.className = className;
75
- if (variant !== void 0) el.classList.add(`${className}--${variant}`);
76
- el.setAttribute("role", role);
77
- region.appendChild(el);
78
- const disposeMount = mount(el, typeof content === "function" ? content : () => content);
79
- const state = { dismissed: false, removed: false, timer: void 0, exitTimer: void 0 };
80
- if (enterClass !== void 0) {
81
- globalThis.requestAnimationFrame(() => {
82
- if (!state.dismissed) el.classList.add(enterClass);
83
- });
84
- }
85
- const remove = () => {
86
- if (state.removed) return;
87
- state.removed = true;
88
- state.dismissed = true;
89
- if (state.exitTimer !== void 0) clearTimeout(state.exitTimer);
90
- disposeMount();
91
- el.remove();
92
- active.delete(dismiss);
93
- };
94
- const removeNow = () => {
95
- if (state.timer !== void 0) clearTimeout(state.timer);
96
- remove();
97
- };
98
- const fadeOut = () => {
99
- if (state.dismissed) return;
100
- state.dismissed = true;
101
- if (state.timer !== void 0) clearTimeout(state.timer);
102
- if (enterClass !== void 0) el.classList.remove(enterClass);
103
- if (exitClass !== void 0) el.classList.add(exitClass);
104
- if (exitClass !== void 0 || exitDuration > 0) {
105
- state.exitTimer = setTimeout(remove, exitDuration);
106
- } else {
107
- remove();
108
- }
109
- };
110
- function dismiss(options2) {
111
- if (options2?.instant === true) removeNow();
112
- else fadeOut();
113
- }
114
- dismiss.removeNow = removeNow;
115
- active.add(dismiss);
116
- if (duration > 0) state.timer = setTimeout(fadeOut, duration);
117
- return { el, dismiss };
118
- }
119
-
120
- // src/overlay.ts
121
- 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"]';
122
- function focusable(root) {
123
- return Array.from(root.querySelectorAll(FOCUSABLE)).filter(
124
- (el) => !el.hasAttribute("hidden")
125
- );
126
- }
127
- function supportsDialog() {
128
- return typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
129
- }
130
- function supportsPopover() {
131
- return typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.showPopover === "function";
132
- }
133
- function overlay(content, options = {}) {
134
- const {
135
- container = document.body,
136
- className = "kerf-overlay",
137
- dismiss = ["escape", "backdrop"],
138
- initialFocus = true,
139
- trap = true,
140
- role = "dialog",
141
- onDismiss,
142
- outsideIgnore,
143
- native = false
144
- } = options;
145
- const triggers = dismiss === false ? [] : Array.isArray(dismiss) ? dismiss : [dismiss];
146
- const restoreTo = document.activeElement;
147
- const useDialog = native && trap && supportsDialog();
148
- const usePopover = native && !trap && supportsPopover();
149
- const wrapper = useDialog ? document.createElement("dialog") : document.createElement("div");
150
- wrapper.className = className;
151
- if (usePopover) wrapper.setAttribute("popover", "manual");
152
- if (trap && !useDialog) {
153
- wrapper.setAttribute("role", role);
154
- wrapper.setAttribute("aria-modal", "true");
155
- }
156
- container.appendChild(wrapper);
157
- const disposeMount = mount(wrapper, typeof content === "function" ? content : () => content);
158
- let nativeOpened = false;
159
- if (useDialog) {
160
- wrapper.showModal();
161
- nativeOpened = true;
162
- } else if (usePopover) {
163
- wrapper.showPopover();
164
- wrapper.style.inset = "auto";
165
- nativeOpened = true;
166
- }
167
- const removers = [];
168
- const resultBox = {};
169
- const result = new Promise((resolve) => {
170
- resultBox.resolve = resolve;
171
- });
172
- const state = { closed: false };
173
- function close(value) {
174
- if (state.closed) return;
175
- state.closed = true;
176
- for (const remove of removers) remove();
177
- disposeMount();
178
- if (nativeOpened) {
179
- nativeOpened = false;
180
- if (useDialog) wrapper.close();
181
- else wrapper.hidePopover();
182
- }
183
- wrapper.remove();
184
- if (restoreTo instanceof HTMLElement && restoreTo.isConnected) restoreTo.focus();
185
- resultBox.resolve?.(value);
186
- }
187
- function userDismiss() {
188
- onDismiss?.();
189
- close();
190
- }
191
- const wantEscape = triggers.includes("escape");
192
- if (useDialog) {
193
- const onCancel = (event) => {
194
- event.preventDefault();
195
- if (wantEscape) userDismiss();
196
- };
197
- wrapper.addEventListener("cancel", onCancel);
198
- removers.push(() => wrapper.removeEventListener("cancel", onCancel));
199
- } else if (wantEscape || trap) {
200
- const onKeydown = (event) => {
201
- if (wantEscape && event.key === "Escape") {
202
- event.stopPropagation();
203
- userDismiss();
204
- return;
205
- }
206
- if (trap && event.key === "Tab") {
207
- const items = focusable(wrapper);
208
- if (items.length === 0) {
209
- event.preventDefault();
210
- return;
211
- }
212
- const first = items[0];
213
- const last = items[items.length - 1];
214
- const active = document.activeElement;
215
- const outside = !wrapper.contains(active);
216
- if (event.shiftKey && (active === first || outside)) {
217
- event.preventDefault();
218
- last.focus();
219
- } else if (!event.shiftKey && (active === last || outside)) {
220
- event.preventDefault();
221
- first.focus();
222
- }
223
- }
224
- };
225
- document.addEventListener("keydown", onKeydown, true);
226
- removers.push(() => document.removeEventListener("keydown", onKeydown, true));
227
- }
228
- if (triggers.includes("backdrop")) {
229
- const onClick = (event) => {
230
- if (event.target === wrapper) userDismiss();
231
- };
232
- wrapper.addEventListener("click", onClick);
233
- removers.push(() => wrapper.removeEventListener("click", onClick));
234
- }
235
- if (triggers.includes("outside")) {
236
- const ignore = outsideIgnore === void 0 ? [] : Array.isArray(outsideIgnore) ? outsideIgnore : [outsideIgnore];
237
- const onDocClick = (event) => {
238
- const target = event.target;
239
- if (target === null) return;
240
- if (wrapper.contains(target)) return;
241
- if (ignore.some((el) => el === target || el.contains(target))) return;
242
- userDismiss();
243
- };
244
- document.addEventListener("click", onDocClick, true);
245
- removers.push(() => document.removeEventListener("click", onDocClick, true));
246
- }
247
- if (initialFocus !== false) {
248
- if (typeof initialFocus === "string") {
249
- wrapper.querySelector(initialFocus)?.focus();
250
- } else {
251
- const first = focusable(wrapper)[0];
252
- if (first !== void 0) {
253
- first.focus();
254
- } else {
255
- wrapper.tabIndex = -1;
256
- wrapper.focus();
257
- }
258
- }
259
- }
260
- return { el: wrapper, close, result };
261
- }
40
+ // src/overlay-dialogs.ts
262
41
  function confirm(message, options = {}) {
263
42
  const {
264
43
  container,
@@ -546,6 +325,229 @@ function choice(message, actions, options = {}) {
546
325
  void handle.result.then(() => resolveChoice(null));
547
326
  return result;
548
327
  }
328
+
329
+ // src/overlay-toast.ts
330
+ var TOAST_SET = /* @__PURE__ */ Symbol("kerf.toasts");
331
+ function toastRegion(container) {
332
+ if (container !== void 0) return container;
333
+ const existing = document.querySelector(".kerf-toasts");
334
+ if (existing !== null) return existing;
335
+ const region = document.createElement("div");
336
+ region.className = "kerf-toasts";
337
+ region.setAttribute("aria-live", "polite");
338
+ document.body.appendChild(region);
339
+ return region;
340
+ }
341
+ function toast(content, options = {}) {
342
+ const {
343
+ container,
344
+ className = "kerf-toast",
345
+ duration = 4e3,
346
+ role = "status",
347
+ mode = "stack",
348
+ collapse = "fade",
349
+ variant,
350
+ enterClass,
351
+ exitClass,
352
+ exitDuration = 0
353
+ } = options;
354
+ const region = toastRegion(container);
355
+ const active = region[TOAST_SET] ??= /* @__PURE__ */ new Set();
356
+ if (mode === "replace") {
357
+ for (const d of [...active]) {
358
+ if (collapse === "instant") d.removeNow();
359
+ else d();
360
+ }
361
+ }
362
+ const el = document.createElement("div");
363
+ el.className = className;
364
+ if (variant !== void 0) el.classList.add(`${className}--${variant}`);
365
+ el.setAttribute("role", role);
366
+ region.appendChild(el);
367
+ const disposeMount = mount(el, typeof content === "function" ? content : () => content);
368
+ const state = { dismissed: false, removed: false, timer: void 0, exitTimer: void 0 };
369
+ if (enterClass !== void 0) {
370
+ globalThis.requestAnimationFrame(() => {
371
+ if (!state.dismissed) el.classList.add(enterClass);
372
+ });
373
+ }
374
+ const remove = () => {
375
+ if (state.removed) return;
376
+ state.removed = true;
377
+ state.dismissed = true;
378
+ if (state.exitTimer !== void 0) clearTimeout(state.exitTimer);
379
+ disposeMount();
380
+ el.remove();
381
+ active.delete(dismiss);
382
+ };
383
+ const removeNow = () => {
384
+ if (state.timer !== void 0) clearTimeout(state.timer);
385
+ remove();
386
+ };
387
+ const fadeOut = () => {
388
+ if (state.dismissed) return;
389
+ state.dismissed = true;
390
+ if (state.timer !== void 0) clearTimeout(state.timer);
391
+ if (enterClass !== void 0) el.classList.remove(enterClass);
392
+ if (exitClass !== void 0) el.classList.add(exitClass);
393
+ if (exitClass !== void 0 || exitDuration > 0) {
394
+ state.exitTimer = setTimeout(remove, exitDuration);
395
+ } else {
396
+ remove();
397
+ }
398
+ };
399
+ function dismiss(options2) {
400
+ if (options2?.instant === true) removeNow();
401
+ else fadeOut();
402
+ }
403
+ dismiss.removeNow = removeNow;
404
+ active.add(dismiss);
405
+ if (duration > 0) state.timer = setTimeout(fadeOut, duration);
406
+ return { el, dismiss };
407
+ }
408
+
409
+ // src/overlay.ts
410
+ 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"]';
411
+ function focusable(root) {
412
+ return Array.from(root.querySelectorAll(FOCUSABLE)).filter(
413
+ (el) => !el.hasAttribute("hidden")
414
+ );
415
+ }
416
+ function supportsDialog() {
417
+ return typeof HTMLDialogElement !== "undefined" && typeof HTMLDialogElement.prototype.showModal === "function";
418
+ }
419
+ function supportsPopover() {
420
+ return typeof HTMLElement !== "undefined" && typeof HTMLElement.prototype.showPopover === "function";
421
+ }
422
+ function overlay(content, options = {}) {
423
+ const {
424
+ container = document.body,
425
+ className = "kerf-overlay",
426
+ dismiss = ["escape", "backdrop"],
427
+ initialFocus = true,
428
+ trap = true,
429
+ role = "dialog",
430
+ onDismiss,
431
+ outsideIgnore,
432
+ native = false
433
+ } = options;
434
+ const triggers = dismiss === false ? [] : Array.isArray(dismiss) ? dismiss : [dismiss];
435
+ const restoreTo = document.activeElement;
436
+ const useDialog = native && trap && supportsDialog();
437
+ const usePopover = native && !trap && supportsPopover();
438
+ const wrapper = useDialog ? document.createElement("dialog") : document.createElement("div");
439
+ wrapper.className = className;
440
+ if (usePopover) wrapper.setAttribute("popover", "manual");
441
+ if (trap && !useDialog) {
442
+ wrapper.setAttribute("role", role);
443
+ wrapper.setAttribute("aria-modal", "true");
444
+ }
445
+ container.appendChild(wrapper);
446
+ const disposeMount = mount(wrapper, typeof content === "function" ? content : () => content);
447
+ let nativeOpened = false;
448
+ if (useDialog) {
449
+ wrapper.showModal();
450
+ nativeOpened = true;
451
+ } else if (usePopover) {
452
+ wrapper.showPopover();
453
+ wrapper.style.inset = "auto";
454
+ nativeOpened = true;
455
+ }
456
+ const removers = [];
457
+ const resultBox = {};
458
+ const result = new Promise((resolve) => {
459
+ resultBox.resolve = resolve;
460
+ });
461
+ const state = { closed: false };
462
+ function close(value) {
463
+ if (state.closed) return;
464
+ state.closed = true;
465
+ for (const remove of removers) remove();
466
+ disposeMount();
467
+ if (nativeOpened) {
468
+ nativeOpened = false;
469
+ if (useDialog) wrapper.close();
470
+ else wrapper.hidePopover();
471
+ }
472
+ wrapper.remove();
473
+ if (restoreTo instanceof HTMLElement && restoreTo.isConnected) restoreTo.focus();
474
+ resultBox.resolve?.(value);
475
+ }
476
+ function userDismiss() {
477
+ onDismiss?.();
478
+ close();
479
+ }
480
+ const wantEscape = triggers.includes("escape");
481
+ if (useDialog) {
482
+ const onCancel = (event) => {
483
+ event.preventDefault();
484
+ if (wantEscape) userDismiss();
485
+ };
486
+ wrapper.addEventListener("cancel", onCancel);
487
+ removers.push(() => wrapper.removeEventListener("cancel", onCancel));
488
+ } else if (wantEscape || trap) {
489
+ const onKeydown = (event) => {
490
+ if (wantEscape && event.key === "Escape") {
491
+ event.stopPropagation();
492
+ userDismiss();
493
+ return;
494
+ }
495
+ if (trap && event.key === "Tab") {
496
+ const items = focusable(wrapper);
497
+ if (items.length === 0) {
498
+ event.preventDefault();
499
+ return;
500
+ }
501
+ const first = items[0];
502
+ const last = items[items.length - 1];
503
+ const active = document.activeElement;
504
+ const outside = !wrapper.contains(active);
505
+ if (event.shiftKey && (active === first || outside)) {
506
+ event.preventDefault();
507
+ last.focus();
508
+ } else if (!event.shiftKey && (active === last || outside)) {
509
+ event.preventDefault();
510
+ first.focus();
511
+ }
512
+ }
513
+ };
514
+ document.addEventListener("keydown", onKeydown, true);
515
+ removers.push(() => document.removeEventListener("keydown", onKeydown, true));
516
+ }
517
+ if (triggers.includes("backdrop")) {
518
+ const onClick = (event) => {
519
+ if (event.target === wrapper) userDismiss();
520
+ };
521
+ wrapper.addEventListener("click", onClick);
522
+ removers.push(() => wrapper.removeEventListener("click", onClick));
523
+ }
524
+ if (triggers.includes("outside")) {
525
+ const ignore = outsideIgnore === void 0 ? [] : Array.isArray(outsideIgnore) ? outsideIgnore : [outsideIgnore];
526
+ const onDocClick = (event) => {
527
+ const target = event.target;
528
+ if (target === null) return;
529
+ if (wrapper.contains(target)) return;
530
+ if (ignore.some((el) => el === target || el.contains(target))) return;
531
+ userDismiss();
532
+ };
533
+ document.addEventListener("click", onDocClick, true);
534
+ removers.push(() => document.removeEventListener("click", onDocClick, true));
535
+ }
536
+ if (initialFocus !== false) {
537
+ if (typeof initialFocus === "string") {
538
+ wrapper.querySelector(initialFocus)?.focus();
539
+ } else {
540
+ const first = focusable(wrapper)[0];
541
+ if (first !== void 0) {
542
+ first.focus();
543
+ } else {
544
+ wrapper.tabIndex = -1;
545
+ wrapper.focus();
546
+ }
547
+ }
548
+ }
549
+ return { el: wrapper, close, result };
550
+ }
549
551
  function popover(anchor, content, options = {}) {
550
552
  const {
551
553
  container,