@k2b/ui 0.12.0 → 0.14.0

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.
Files changed (106) hide show
  1. package/README.md +1 -1
  2. package/dist/browser/content/MarkdownView.js +13 -5
  3. package/dist/browser/content/MarkdownView.js.map +1 -1
  4. package/dist/browser/content/PdfPreview.js +5 -2
  5. package/dist/browser/content/PdfPreview.js.map +1 -1
  6. package/dist/browser/content/index.js +1 -0
  7. package/dist/browser/content/index.js.map +1 -1
  8. package/dist/browser/content/markdown-info-blocks.js +363 -0
  9. package/dist/browser/content/markdown-info-blocks.js.map +1 -0
  10. package/dist/browser/feedback/InstallGuide.js +267 -0
  11. package/dist/browser/feedback/InstallGuide.js.map +1 -0
  12. package/dist/browser/feedback/announce.js +50 -0
  13. package/dist/browser/feedback/announce.js.map +1 -0
  14. package/dist/browser/feedback/dialog-core.js +88 -2
  15. package/dist/browser/feedback/dialog-core.js.map +1 -1
  16. package/dist/browser/feedback/index.js +4 -0
  17. package/dist/browser/feedback/index.js.map +1 -1
  18. package/dist/browser/feedback/install.js +99 -0
  19. package/dist/browser/feedback/install.js.map +1 -0
  20. package/dist/browser/feedback/prompts.js +11 -1
  21. package/dist/browser/feedback/prompts.js.map +1 -1
  22. package/dist/browser/feedback/theme-color.js +15 -0
  23. package/dist/browser/feedback/theme-color.js.map +1 -0
  24. package/dist/browser/feedback/toast.js +10 -44
  25. package/dist/browser/feedback/toast.js.map +1 -1
  26. package/dist/browser/inputs/QrScanner.js +164 -0
  27. package/dist/browser/inputs/QrScanner.js.map +1 -0
  28. package/dist/browser/inputs/index.js +1 -0
  29. package/dist/browser/inputs/index.js.map +1 -1
  30. package/dist/browser/intl/messages.js +96 -2
  31. package/dist/browser/intl/messages.js.map +1 -1
  32. package/dist/browser/layout/Discussion.js +2 -1
  33. package/dist/browser/layout/Discussion.js.map +1 -1
  34. package/dist/browser/layout/MobileShell.js +284 -0
  35. package/dist/browser/layout/MobileShell.js.map +1 -0
  36. package/dist/browser/layout/TabBar.js +69 -0
  37. package/dist/browser/layout/TabBar.js.map +1 -0
  38. package/dist/browser/layout/index.js +2 -0
  39. package/dist/browser/layout/index.js.map +1 -1
  40. package/dist/browser/surfaces/NoticeCard.js +55 -60
  41. package/dist/browser/surfaces/NoticeCard.js.map +1 -1
  42. package/dist/browser/surfaces/index.js +1 -1
  43. package/dist/browser/surfaces/index.js.map +1 -1
  44. package/dist/ssr/content/MarkdownView.js +13 -5
  45. package/dist/ssr/content/MarkdownView.js.map +1 -1
  46. package/dist/ssr/content/PdfPreview.js +4 -2
  47. package/dist/ssr/content/PdfPreview.js.map +1 -1
  48. package/dist/ssr/content/index.js +1 -0
  49. package/dist/ssr/content/index.js.map +1 -1
  50. package/dist/ssr/content/markdown-info-blocks.js +363 -0
  51. package/dist/ssr/content/markdown-info-blocks.js.map +1 -0
  52. package/dist/ssr/feedback/InstallGuide.js +201 -0
  53. package/dist/ssr/feedback/InstallGuide.js.map +1 -0
  54. package/dist/ssr/feedback/announce.js +50 -0
  55. package/dist/ssr/feedback/announce.js.map +1 -0
  56. package/dist/ssr/feedback/dialog-core.js +88 -2
  57. package/dist/ssr/feedback/dialog-core.js.map +1 -1
  58. package/dist/ssr/feedback/index.js +4 -0
  59. package/dist/ssr/feedback/index.js.map +1 -1
  60. package/dist/ssr/feedback/install.js +99 -0
  61. package/dist/ssr/feedback/install.js.map +1 -0
  62. package/dist/ssr/feedback/prompts.js +11 -1
  63. package/dist/ssr/feedback/prompts.js.map +1 -1
  64. package/dist/ssr/feedback/theme-color.js +15 -0
  65. package/dist/ssr/feedback/theme-color.js.map +1 -0
  66. package/dist/ssr/feedback/toast.js +10 -44
  67. package/dist/ssr/feedback/toast.js.map +1 -1
  68. package/dist/ssr/inputs/QrScanner.js +128 -0
  69. package/dist/ssr/inputs/QrScanner.js.map +1 -0
  70. package/dist/ssr/inputs/index.js +1 -0
  71. package/dist/ssr/inputs/index.js.map +1 -1
  72. package/dist/ssr/intl/messages.js +96 -2
  73. package/dist/ssr/intl/messages.js.map +1 -1
  74. package/dist/ssr/layout/Discussion.js +2 -1
  75. package/dist/ssr/layout/Discussion.js.map +1 -1
  76. package/dist/ssr/layout/MobileShell.js +253 -0
  77. package/dist/ssr/layout/MobileShell.js.map +1 -0
  78. package/dist/ssr/layout/TabBar.js +25 -0
  79. package/dist/ssr/layout/TabBar.js.map +1 -0
  80. package/dist/ssr/layout/index.js +2 -0
  81. package/dist/ssr/layout/index.js.map +1 -1
  82. package/dist/ssr/surfaces/NoticeCard.js +21 -30
  83. package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
  84. package/dist/ssr/surfaces/index.js +1 -1
  85. package/dist/ssr/surfaces/index.js.map +1 -1
  86. package/dist/styles.css +1 -1
  87. package/dist/tabler.css +1 -1
  88. package/dist/types/content/MarkdownView.d.ts +6 -2
  89. package/dist/types/content/index.d.ts +1 -0
  90. package/dist/types/content/markdown-info-blocks.d.ts +67 -0
  91. package/dist/types/feedback/InstallGuide.d.ts +26 -0
  92. package/dist/types/feedback/announce.d.ts +19 -0
  93. package/dist/types/feedback/dialog-core.d.ts +5 -0
  94. package/dist/types/feedback/index.d.ts +7 -0
  95. package/dist/types/feedback/install.d.ts +32 -0
  96. package/dist/types/feedback/prompts.d.ts +2 -0
  97. package/dist/types/feedback/theme-color.d.ts +6 -0
  98. package/dist/types/inputs/QrScanner.d.ts +20 -0
  99. package/dist/types/inputs/index.d.ts +2 -0
  100. package/dist/types/intl/messages.d.ts +47 -0
  101. package/dist/types/layout/MobileShell.d.ts +40 -0
  102. package/dist/types/layout/TabBar.d.ts +26 -0
  103. package/dist/types/layout/index.d.ts +4 -0
  104. package/dist/types/surfaces/NoticeCard.d.ts +4 -4
  105. package/dist/types/surfaces/index.d.ts +1 -1
  106. package/package.json +3 -2
@@ -0,0 +1,50 @@
1
+ /** A live region needs a moment between being found and being written to. */
2
+ const ANNOUNCE_DELAY_MS = 100;
3
+ const ANNOUNCEMENT_LIFETIME_MS = 7_000;
4
+ const LIVE_ATTRIBUTE = "data-k2b-live";
5
+ const VISUALLY_HIDDEN = "position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;";
6
+
7
+ /**
8
+ * One pair of persistent, empty live regions per document, polite and assertive, shared by `announce` and every
9
+ * toast. A region that already exists when its text arrives is announced reliably, unlike one inserted with its text
10
+ * already inside. They sit directly in the body, outside every portal and the toast rail, which moves into a fresh
11
+ * top-layer element for every toast, so no scope that closes takes them along.
12
+ */
13
+ export const ensureLiveRegions = doc => {
14
+ const existing = doc.querySelector(`[${LIVE_ATTRIBUTE}]`);
15
+ if (existing) return existing;
16
+ const live = doc.createElement("div");
17
+ live.setAttribute(LIVE_ATTRIBUTE, "");
18
+ live.style.cssText = VISUALLY_HIDDEN;
19
+ for (const politeness of ["polite", "assertive"]) {
20
+ const region = doc.createElement("div");
21
+ region.setAttribute("role", politeness === "assertive" ? "alert" : "status");
22
+ region.setAttribute("aria-live", politeness);
23
+ // Both roles are atomic by default, which would read every line still in the region again with each new one.
24
+ region.setAttribute("aria-atomic", "false");
25
+ region.dataset.politeness = politeness;
26
+ live.appendChild(region);
27
+ }
28
+ doc.body.appendChild(live);
29
+ return live;
30
+ };
31
+
32
+ /**
33
+ * Tells screen readers about an outcome without showing anything: a change the screen shows but the focused control
34
+ * does not say, because a shortcut acted, the control was busy, or it was replaced. Each message is its own line, so
35
+ * a burst of messages or the same message twice is still read. Calls without `document`, including during SSR, do
36
+ * nothing.
37
+ */
38
+ export const announce = (message, options) => {
39
+ if (typeof document === "undefined" || !message) return;
40
+ const politeness = options?.politeness ?? "polite";
41
+ const region = ensureLiveRegions(document).querySelector(`[data-politeness="${politeness}"]`);
42
+ if (!region) return;
43
+ const line = document.createElement("div");
44
+ line.textContent = message;
45
+ setTimeout(() => {
46
+ region.appendChild(line);
47
+ setTimeout(() => line.remove(), ANNOUNCEMENT_LIFETIME_MS);
48
+ }, ANNOUNCE_DELAY_MS);
49
+ };
50
+ //# sourceMappingURL=announce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["feedback/announce.ts"],"sourcesContent":["export type AnnouncePoliteness = \"polite\" | \"assertive\";\n\nexport type AnnounceOptions = {\n /** `polite` waits until the screen reader is idle; `assertive` interrupts it. Defaults to `polite`. */\n politeness?: AnnouncePoliteness;\n};\n\n/** A live region needs a moment between being found and being written to. */\nconst ANNOUNCE_DELAY_MS = 100;\nconst ANNOUNCEMENT_LIFETIME_MS = 7_000;\nconst LIVE_ATTRIBUTE = \"data-k2b-live\";\nconst VISUALLY_HIDDEN =\n \"position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;\";\n\n/**\n * One pair of persistent, empty live regions per document, polite and assertive, shared by `announce` and every\n * toast. A region that already exists when its text arrives is announced reliably, unlike one inserted with its text\n * already inside. They sit directly in the body, outside every portal and the toast rail, which moves into a fresh\n * top-layer element for every toast, so no scope that closes takes them along.\n */\nexport const ensureLiveRegions = (doc: Document): HTMLElement => {\n const existing = doc.querySelector<HTMLElement>(`[${LIVE_ATTRIBUTE}]`);\n if (existing) return existing;\n const live = doc.createElement(\"div\");\n live.setAttribute(LIVE_ATTRIBUTE, \"\");\n live.style.cssText = VISUALLY_HIDDEN;\n for (const politeness of [\"polite\", \"assertive\"] as const) {\n const region = doc.createElement(\"div\");\n region.setAttribute(\"role\", politeness === \"assertive\" ? \"alert\" : \"status\");\n region.setAttribute(\"aria-live\", politeness);\n // Both roles are atomic by default, which would read every line still in the region again with each new one.\n region.setAttribute(\"aria-atomic\", \"false\");\n region.dataset.politeness = politeness;\n live.appendChild(region);\n }\n doc.body.appendChild(live);\n return live;\n};\n\n/**\n * Tells screen readers about an outcome without showing anything: a change the screen shows but the focused control\n * does not say, because a shortcut acted, the control was busy, or it was replaced. Each message is its own line, so\n * a burst of messages or the same message twice is still read. Calls without `document`, including during SSR, do\n * nothing.\n */\nexport const announce = (message: string, options?: AnnounceOptions): void => {\n if (typeof document === \"undefined\" || !message) return;\n const politeness = options?.politeness ?? \"polite\";\n const region = ensureLiveRegions(document).querySelector<HTMLElement>(`[data-politeness=\"${politeness}\"]`);\n if (!region) return;\n const line = document.createElement(\"div\");\n line.textContent = message;\n setTimeout(() => {\n region.appendChild(line);\n setTimeout(() => line.remove(), ANNOUNCEMENT_LIFETIME_MS);\n }, ANNOUNCE_DELAY_MS);\n};\n"],"mappings":"AAOA;AACA,MAAM,iBAAiB,GAAG,GAAG;AAC7B,MAAM,wBAAwB,GAAG,KAAK;AACtC,MAAM,cAAc,GAAG,eAAe;AACtC,MAAM,eAAe,GACnB,gIAAgI;;AAElI;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,iBAAiB,GAAI,GAAa,IAAkB;EAC/D,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAc,IAAI,cAAc,GAAG,CAAC;EACtE,IAAI,QAAQ,EAAE,OAAO,QAAQ;EAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC;EACrC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,EAAE,CAAC;EACrC,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,eAAe;EACpC,KAAK,MAAM,UAAU,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAW;IACzD,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC;IACvC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,KAAK,WAAW,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5E,MAAM,CAAC,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC;IAC5C;IACA,MAAM,CAAC,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC;IAC3C,MAAM,CAAC,OAAO,CAAC,UAAU,GAAG,UAAU;IACtC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;EAC1B;EACA,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;EAC1B,OAAO,IAAI;AACb,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAM,QAAQ,GAAG,CAAC,OAAe,EAAE,OAAyB,KAAW;EAC5E,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,CAAC,OAAO,EAAE;EACjD,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,QAAQ;EAClD,MAAM,MAAM,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAc,qBAAqB,UAAU,IAAI,CAAC;EAC1G,IAAI,CAAC,MAAM,EAAE;EACb,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;EAC1C,IAAI,CAAC,WAAW,GAAG,OAAO;EAC1B,UAAU,CAAC,MAAM;IACf,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC;IACxB,UAAU,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,wBAAwB,CAAC;EAC3D,CAAC,EAAE,iBAAiB,CAAC;AACvB,CAAC","ignoreList":[]}
@@ -6,6 +6,12 @@ import { isPointInsideToast } from "./toast";
6
6
  const DEFAULT_PANEL_CLASS = "k2b-dialog";
7
7
  const DEFAULT_CONTENT_CLASS = "k2b-dialog__viewport";
8
8
  let nextDialogTitleId = 0;
9
+ /** The history state key that marks a dialog's same-URL entry. */
10
+ const HISTORY_MARKER = "k2bDialog";
11
+ const historyRecord = () => typeof history.state === "object" && history.state !== null ? {
12
+ ...history.state
13
+ } : {};
14
+ const historyMarker = () => historyRecord()[HISTORY_MARKER];
9
15
  const resolveInitialFocusTarget = (entry, dialog) => {
10
16
  const {
11
17
  initialFocus
@@ -47,6 +53,67 @@ export const createDialogCore = () => {
47
53
  const state = {
48
54
  stack: []
49
55
  };
56
+ /** Markers of this core's entries in the browser history, oldest first. */
57
+ let historyIds = [];
58
+ let nextHistoryId = 0;
59
+ let historySyncScheduled = false;
60
+ let listeningToHistory = false;
61
+ /** Settles when the synthetic Back that removes closed dialogs' entries has arrived. */
62
+ let returning;
63
+
64
+ /** Closed dialogs leave the history with one Back to the newest dialog that is still open, or to the page. */
65
+ const syncHistory = () => {
66
+ historySyncScheduled = false;
67
+ const openIds = new Set(state.stack.map(entry => entry.historyId));
68
+ const current = historyIds.at(-1);
69
+ let steps = 0;
70
+ while (historyIds.length > 0 && !openIds.has(historyIds.at(-1))) {
71
+ historyIds.pop();
72
+ steps++;
73
+ }
74
+ // Someone else moved the history meanwhile; leave it alone.
75
+ if (steps === 0 || historyMarker() !== current) return;
76
+ const back = new Promise(resolve => window.addEventListener("popstate", () => resolve(), {
77
+ once: true
78
+ }));
79
+ returning = back;
80
+ void back.then(() => {
81
+ if (returning === back) returning = undefined;
82
+ });
83
+ history.go(-steps);
84
+ };
85
+ const releaseHistory = entry => {
86
+ if (entry.historyId === undefined || historySyncScheduled) return;
87
+ historySyncScheduled = true;
88
+ queueMicrotask(syncHistory);
89
+ };
90
+
91
+ /** Back or Forward moved to another entry: close every dialog opened after the one it shows. */
92
+ const onPopState = () => {
93
+ const marker = historyMarker();
94
+ const index = typeof marker === "number" ? historyIds.indexOf(marker) : -1;
95
+ historyIds = historyIds.slice(0, index + 1);
96
+ const gone = entry => entry.historyId !== undefined && !historyIds.includes(entry.historyId);
97
+ while (state.stack.some(gone)) popTop(undefined);
98
+ };
99
+ const pushHistory = entry => {
100
+ if (!listeningToHistory) {
101
+ window.addEventListener("popstate", onPopState);
102
+ listeningToHistory = true;
103
+ }
104
+ const previous = historyRecord();
105
+ // A reload or Forward never brings back a dialog or its sensitive transient state.
106
+ if (historyIds.length === 0 && previous[HISTORY_MARKER] !== undefined) {
107
+ delete previous[HISTORY_MARKER];
108
+ history.replaceState(previous, "");
109
+ }
110
+ entry.historyId = ++nextHistoryId;
111
+ historyIds.push(entry.historyId);
112
+ history.pushState({
113
+ ...previous,
114
+ [HISTORY_MARKER]: entry.historyId
115
+ }, "");
116
+ };
50
117
  const ensureDialogElement = () => {
51
118
  if (typeof document === "undefined") throw new Error("@k2b/ui dialogs can only be opened in the browser");
52
119
  // Reuse the shared element whenever it is still in the document. Resolving
@@ -159,6 +226,7 @@ export const createDialogCore = () => {
159
226
  const popTop = result => {
160
227
  const top = state.stack.pop();
161
228
  if (!top) return;
229
+ releaseHistory(top);
162
230
  top.dispose?.();
163
231
  top.container.remove();
164
232
  const dialog = state.element;
@@ -201,6 +269,7 @@ export const createDialogCore = () => {
201
269
  dialog?.remove();
202
270
  unlockPageScroll();
203
271
  for (const entry of entries.reverse()) {
272
+ releaseHistory(entry);
204
273
  entry.dispose?.();
205
274
  entry.container.remove();
206
275
  entry.resolve?.(undefined);
@@ -217,6 +286,11 @@ export const createDialogCore = () => {
217
286
  };
218
287
  const open = (view, options = {}) => {
219
288
  if (options.signal?.aborted) return Promise.resolve(undefined);
289
+ if (options.history) {
290
+ // Dialogs closed just now leave the history first, or this dialog's entry would be the one removed.
291
+ if (historySyncScheduled) syncHistory();
292
+ if (returning) return returning.then(() => open(view, options));
293
+ }
220
294
  const dialog = ensureDialogElement();
221
295
  const previousTop = state.stack[state.stack.length - 1];
222
296
  if (previousTop) previousTop.container.style.display = "none";
@@ -244,6 +318,7 @@ export const createDialogCore = () => {
244
318
  if (index < 0) return;
245
319
  if (index === state.stack.length - 1) popTop(undefined);else {
246
320
  state.stack.splice(index, 1);
321
+ releaseHistory(entry);
247
322
  entry.dispose?.();
248
323
  entry.container.remove();
249
324
  entry.resolve?.(undefined);
@@ -251,12 +326,18 @@ export const createDialogCore = () => {
251
326
  };
252
327
  entry.resolve = value => {
253
328
  options.signal?.removeEventListener("abort", abort);
254
- resolve(value);
329
+ if (entry.historyId === undefined) {
330
+ resolve(value);
331
+ return;
332
+ }
333
+ // After the history sync, so a caller that navigates or opens the next dialog finds the history settled.
334
+ queueMicrotask(() => void (returning ?? Promise.resolve()).then(() => resolve(value)));
255
335
  };
256
336
  const closeTyped = result => {
257
337
  if (state.stack[state.stack.length - 1] !== entry) return;
258
338
  popTop(result);
259
339
  };
340
+ if (options.history) pushHistory(entry);
260
341
  state.stack.push(entry);
261
342
  try {
262
343
  entry.dispose = render(() => view(closeTyped, {
@@ -292,8 +373,13 @@ export const createDialogCore = () => {
292
373
  once: true
293
374
  });
294
375
  if (options.signal?.aborted) abort();
376
+ // Where opening left focus, the native dialog's own pick included. Focus that the content or a person moved
377
+ // before the next frame stays: a browser can hold that frame back while keys already arrive.
378
+ const opened = document.activeElement;
295
379
  schedule(() => {
296
- if (state.stack[state.stack.length - 1] === entry) resolveInitialFocusTarget(entry, dialog)?.focus();
380
+ if (state.stack[state.stack.length - 1] !== entry) return;
381
+ if (document.activeElement !== opened && !focusUnclaimed(dialog)) return;
382
+ resolveInitialFocusTarget(entry, dialog)?.focus();
297
383
  });
298
384
  } catch (error) {
299
385
  entry.resolve = undefined;
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["feedback/dialog-core.ts"],"sourcesContent":["import type { JSX } from \"solid-js\";\nimport { render } from \"solid-js/web\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { getK2bPortalRoot } from \"../internal/portal\";\nimport { resolveUiMessages } from \"../intl/messages\";\nimport { isPointInsideToast } from \"./toast\";\n\nexport type DialogClose<T> = (result?: T) => void;\n\nexport type OpenDialogOptions = {\n /** Close only this dialog when its owning operation is cancelled. */\n signal?: AbortSignal;\n panelClassName?: string;\n contentClassName?: string;\n initialFocus?: \"first-input\" | \"none\" | ((dialog: HTMLDialogElement) => HTMLElement | null);\n cancelBehavior?: \"resolve-undefined\" | \"ignore\";\n ariaLabel?: string;\n};\n\nexport type DialogRender<T> = (\n close: DialogClose<T>,\n context: {\n dialog: HTMLDialogElement;\n /** Switch this entry between a modal and a modeless window without remounting. */\n setModal: (modal: boolean) => void;\n /** Set viewport coordinates, or restore the panel stylesheet position with null. */\n setPosition: (position: { x: number; y: number } | null) => void;\n /** Route Escape/backdrop through the same guarded handler as Cancel and X. */\n setDismissHandler: (handler: () => void | Promise<void>) => void;\n /** Request dismissal through this entry's cancellation policy and guard. */\n requestDismiss: () => Promise<void>;\n },\n) => JSX.Element;\n\nexport type DialogCore = {\n open: <T>(view: DialogRender<T>, options?: OpenDialogOptions) => Promise<T | undefined>;\n close: (result?: unknown) => void;\n isOpen: () => boolean;\n};\n\ntype DialogStackEntry = {\n container: HTMLDivElement;\n dispose?: () => void;\n resolve?: (value: unknown) => void;\n panelClassName: string;\n cancelBehavior: NonNullable<OpenDialogOptions[\"cancelBehavior\"]>;\n initialFocus: NonNullable<OpenDialogOptions[\"initialFocus\"]>;\n opener?: HTMLElement;\n openerRing: boolean;\n ariaLabel?: string;\n dismissHandler?: () => void | Promise<void>;\n dismissPending?: boolean;\n modal: boolean;\n position?: { x: number; y: number } | null;\n};\n\ntype DialogState = {\n element?: HTMLDialogElement;\n modal?: boolean;\n stack: DialogStackEntry[];\n scrollLocked?: boolean;\n previousBodyOverflow?: string;\n previousHtmlOverflow?: string;\n mouseDownOnDialog?: boolean;\n connectionObserver?: MutationObserver;\n};\n\nconst DEFAULT_PANEL_CLASS = \"k2b-dialog\";\nconst DEFAULT_CONTENT_CLASS = \"k2b-dialog__viewport\";\nlet nextDialogTitleId = 0;\n\nconst resolveInitialFocusTarget = (entry: DialogStackEntry, dialog: HTMLDialogElement): HTMLElement | null => {\n const { initialFocus } = entry;\n if (initialFocus === \"none\") return null;\n if (typeof initialFocus === \"function\") return initialFocus(dialog);\n const input = entry.container.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), [role='combobox']:not([disabled]):not([aria-disabled='true'])\",\n );\n return (\n input ??\n entry.container.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), a[href], [tabindex]:not([tabindex='-1'])\",\n )\n );\n};\n\nconst applyAccessibleName = (dialog: HTMLDialogElement, entry: DialogStackEntry): void => {\n dialog.removeAttribute(\"aria-label\");\n dialog.removeAttribute(\"aria-labelledby\");\n if (entry.ariaLabel) {\n dialog.setAttribute(\"aria-label\", entry.ariaLabel);\n return;\n }\n const heading = entry.container.querySelector<HTMLElement>(\"h1, h2, h3\");\n if (!heading) {\n dialog.setAttribute(\"aria-label\", resolveUiMessages().dialog);\n return;\n }\n heading.id ||= `k2b-dialog-title-${++nextDialogTitleId}`;\n dialog.setAttribute(\"aria-labelledby\", heading.id);\n};\n\nconst schedule = (callback: () => void): void => {\n if (typeof requestAnimationFrame === \"function\") requestAnimationFrame(callback);\n else queueMicrotask(callback);\n};\n\n/**\n * Whether focus is still where closing a level left it, on the page or the\n * dialog: nothing took it before the next frame, neither a close handler nor a\n * Tab pressed right after Escape.\n */\nconst focusUnclaimed = (dialog: HTMLDialogElement): boolean => {\n const active = document.activeElement;\n return !active || active === document.body || active === dialog;\n};\n\nexport const createDialogCore = (): DialogCore => {\n const state: DialogState = { stack: [] };\n\n const ensureDialogElement = () => {\n if (typeof document === \"undefined\") throw new Error(\"@k2b/ui dialogs can only be opened in the browser\");\n // Reuse the shared element whenever it is still in the document. Resolving\n // the portal root first would create a second <dialog> when focus has moved\n // into another `.k2b-ui` scope, orphaning the levels already on the stack.\n if (state.element?.isConnected) return state.element;\n\n const element = document.createElement(\"dialog\");\n element.onclose = () => {\n // Repeated Escape can force a non-cancelable native close. A retained\n // entry (ignore, an async guard, or a parent) still owns the modal.\n const top = state.stack[state.stack.length - 1];\n if (state.element !== element || element.open || !top) return;\n if (top.modal) element.showModal();\n else element.show();\n resolveInitialFocusTarget(top, element)?.focus();\n };\n getK2bPortalRoot().appendChild(element);\n state.element = element;\n return element;\n };\n\n const lockPageScroll = () => {\n if (typeof document === \"undefined\" || state.scrollLocked) return;\n state.previousBodyOverflow = document.body.style.overflow;\n state.previousHtmlOverflow = document.documentElement.style.overflow;\n document.body.style.overflow = \"hidden\";\n document.documentElement.style.overflow = \"hidden\";\n state.scrollLocked = true;\n };\n\n const unlockPageScroll = () => {\n if (typeof document === \"undefined\" || !state.scrollLocked) return;\n document.body.style.overflow = state.previousBodyOverflow ?? \"\";\n document.documentElement.style.overflow = state.previousHtmlOverflow ?? \"\";\n state.scrollLocked = false;\n state.previousBodyOverflow = undefined;\n state.previousHtmlOverflow = undefined;\n };\n\n const applyPresentation = (entry: DialogStackEntry) => {\n const dialog = state.element;\n if (!dialog || state.stack[state.stack.length - 1] !== entry) return;\n const position = entry.position;\n dialog.style.position = position ? \"fixed\" : \"\";\n dialog.style.inset = position ? `${position.y}px auto auto ${position.x}px` : \"\";\n dialog.style.margin = position ? \"0\" : \"\";\n dialog.style.transform = position ? \"none\" : \"\";\n dialog.dataset.modeless = String(!entry.modal);\n dialog.setAttribute(\"aria-modal\", String(entry.modal));\n if (dialog.open && state.modal !== entry.modal) {\n const active = document.activeElement;\n dialog.close();\n if (entry.modal) dialog.showModal();\n else dialog.show();\n if (active instanceof HTMLElement && active.isConnected && (!entry.modal || dialog.contains(active))) {\n active.focus({ preventScroll: true });\n } else resolveInitialFocusTarget(entry, dialog)?.focus({ preventScroll: true });\n }\n state.modal = entry.modal;\n if (entry.modal) lockPageScroll();\n else unlockPageScroll();\n };\n\n const modelessEscape = (event: KeyboardEvent) => {\n const top = state.stack[state.stack.length - 1];\n if (!top || top.modal || event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n event.preventDefault();\n void requestDismiss(top);\n };\n\n const stopConnectionObserver = () => {\n document.removeEventListener(\"keydown\", modelessEscape);\n state.connectionObserver?.disconnect();\n state.connectionObserver = undefined;\n };\n\n const applyCancelBehavior = (\n dialog: HTMLDialogElement,\n close: () => void,\n behavior: NonNullable<OpenDialogOptions[\"cancelBehavior\"]>,\n ) => {\n dialog.oncancel = (event) => {\n if (behavior === \"ignore\") {\n event.preventDefault();\n return;\n }\n event.preventDefault();\n close();\n };\n dialog.onmousedown = (event) => {\n state.mouseDownOnDialog = event.target === dialog;\n };\n dialog.onclick = (event) => {\n const realBackdropClick = state.mouseDownOnDialog === true;\n state.mouseDownOnDialog = false;\n if (event.target !== dialog || !state.modal) return;\n if (!realBackdropClick) return;\n if (behavior === \"ignore\") return;\n if (isPointInsideToast(event.clientX, event.clientY)) return;\n close();\n };\n };\n\n const requestDismiss = async (entry: DialogStackEntry) => {\n if (state.stack[state.stack.length - 1] !== entry || entry.dismissPending || entry.cancelBehavior === \"ignore\") return;\n if (!entry.dismissHandler) {\n popTop(undefined);\n return;\n }\n entry.dismissPending = true;\n try {\n await entry.dismissHandler();\n } catch (error) {\n // A failed guard must never discard the dialog's state.\n console.error(\"Dialog dismissal failed\", error);\n } finally {\n entry.dismissPending = false;\n }\n };\n\n const popTop = (result?: unknown) => {\n const top = state.stack.pop();\n if (!top) return;\n top.dispose?.();\n top.container.remove();\n\n const dialog = state.element;\n const previous = state.stack[state.stack.length - 1];\n if (previous && dialog) {\n previous.container.style.display = \"\";\n dialog.className = previous.panelClassName;\n applyPresentation(previous);\n applyAccessibleName(dialog, previous);\n applyCancelBehavior(dialog, () => void requestDismiss(previous), previous.cancelBehavior);\n schedule(() => {\n if (!focusUnclaimed(dialog)) return;\n if (top.opener?.isConnected) returnFocus(top.opener, top.openerRing);\n else resolveInitialFocusTarget(previous, dialog)?.focus();\n });\n } else if (dialog) {\n dialog.oncancel = null;\n dialog.onmousedown = null;\n dialog.onclick = null;\n dialog.removeAttribute(\"aria-label\");\n dialog.removeAttribute(\"aria-labelledby\");\n if (dialog.open && typeof dialog.close === \"function\") dialog.close();\n else dialog.removeAttribute(\"open\");\n dialog.remove();\n state.element = undefined;\n stopConnectionObserver();\n unlockPageScroll();\n // After the Escape that closed it: WebKit lights the ring of whatever\n // that key press leaves focused.\n schedule(() => {\n if (top.opener?.isConnected && focusUnclaimed(dialog)) returnFocus(top.opener, top.openerRing);\n });\n }\n\n top.resolve?.(result);\n };\n\n const resetDisconnectedDialog = () => {\n if (state.stack.length === 0 || state.element?.isConnected) return;\n const entries = state.stack.splice(0);\n const dialog = state.element;\n state.element = undefined;\n stopConnectionObserver();\n dialog?.remove();\n unlockPageScroll();\n for (const entry of entries.reverse()) {\n entry.dispose?.();\n entry.container.remove();\n entry.resolve?.(undefined);\n }\n };\n\n const observeConnection = () => {\n if (state.connectionObserver || typeof MutationObserver === \"undefined\") return;\n document.addEventListener(\"keydown\", modelessEscape);\n state.connectionObserver = new MutationObserver(resetDisconnectedDialog);\n state.connectionObserver.observe(document.documentElement, { childList: true, subtree: true });\n };\n\n const open = <T>(view: DialogRender<T>, options: OpenDialogOptions = {}): Promise<T | undefined> => {\n if (options.signal?.aborted) return Promise.resolve(undefined);\n const dialog = ensureDialogElement();\n const previousTop = state.stack[state.stack.length - 1];\n if (previousTop) previousTop.container.style.display = \"none\";\n const activeElement = document.activeElement;\n\n const panelClassName = options.panelClassName ?? DEFAULT_PANEL_CLASS;\n const cancelBehavior = options.cancelBehavior ?? \"resolve-undefined\";\n const initialFocus = options.initialFocus ?? \"first-input\";\n dialog.className = panelClassName;\n\n const container = document.createElement(\"div\");\n container.className = options.contentClassName ?? DEFAULT_CONTENT_CLASS;\n dialog.appendChild(container);\n\n const entry: DialogStackEntry = {\n container,\n panelClassName,\n cancelBehavior,\n initialFocus,\n opener: activeElement instanceof HTMLElement ? activeElement : undefined,\n openerRing: ringOnReturn(activeElement),\n ariaLabel: options.ariaLabel,\n modal: true,\n };\n\n return new Promise((resolve, reject) => {\n const abort = () => {\n const index = state.stack.indexOf(entry);\n if (index < 0) return;\n if (index === state.stack.length - 1) popTop(undefined);\n else {\n state.stack.splice(index, 1);\n entry.dispose?.();\n entry.container.remove();\n entry.resolve?.(undefined);\n }\n };\n entry.resolve = (value) => {\n options.signal?.removeEventListener(\"abort\", abort);\n resolve(value as T | undefined);\n };\n const closeTyped: DialogClose<T> = (result) => {\n if (state.stack[state.stack.length - 1] !== entry) return;\n popTop(result);\n };\n\n state.stack.push(entry);\n try {\n entry.dispose = render(\n () =>\n view(closeTyped, {\n dialog,\n setModal: (modal) => {\n entry.modal = modal;\n applyPresentation(entry);\n },\n setPosition: (position) => {\n if (position && (!Number.isFinite(position.x) || !Number.isFinite(position.y))) return;\n entry.position = position;\n applyPresentation(entry);\n },\n requestDismiss: () => requestDismiss(entry),\n setDismissHandler: (handler) => {\n entry.dismissHandler = handler;\n },\n }),\n container,\n );\n applyPresentation(entry);\n applyAccessibleName(dialog, entry);\n applyCancelBehavior(dialog, () => void requestDismiss(entry), cancelBehavior);\n\n if (state.stack.length === 1) {\n // The native dialog returns focus to the element focused when it\n // opened, with a ring of the browser's choosing. Opening from a\n // blurred document leaves the return to popTop and its opener ring.\n if (activeElement instanceof HTMLElement) activeElement.blur();\n if (typeof dialog.showModal === \"function\") {\n if (entry.modal) dialog.showModal();\n else dialog.show();\n } else dialog.setAttribute(\"open\", \"\");\n observeConnection();\n }\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n if (options.signal?.aborted) abort();\n schedule(() => {\n if (state.stack[state.stack.length - 1] === entry) resolveInitialFocusTarget(entry, dialog)?.focus();\n });\n } catch (error) {\n entry.resolve = undefined;\n if (state.stack[state.stack.length - 1] === entry) popTop(undefined);\n reject(error);\n }\n });\n };\n\n const close: DialogCore[\"close\"] = (result) => {\n let first = true;\n while (state.stack.length > 0) {\n popTop(first ? result : undefined);\n first = false;\n }\n };\n\n return {\n open,\n close,\n isOpen: () => state.stack.length > 0,\n };\n};\n\nexport const dialogCore = createDialogCore();\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["feedback/dialog-core.ts"],"sourcesContent":["import type { JSX } from \"solid-js\";\nimport { render } from \"solid-js/web\";\nimport { returnFocus, ringOnReturn } from \"../internal/focus-return\";\nimport { getK2bPortalRoot } from \"../internal/portal\";\nimport { resolveUiMessages } from \"../intl/messages\";\nimport { isPointInsideToast } from \"./toast\";\n\nexport type DialogClose<T> = (result?: T) => void;\n\nexport type OpenDialogOptions = {\n /** Close only this dialog when its owning operation is cancelled. */\n signal?: AbortSignal;\n panelClassName?: string;\n contentClassName?: string;\n initialFocus?: \"first-input\" | \"none\" | ((dialog: HTMLDialogElement) => HTMLElement | null);\n cancelBehavior?: \"resolve-undefined\" | \"ignore\";\n ariaLabel?: string;\n /**\n * Add a same-URL history entry while the dialog is open, so the browser's or the phone's Back closes the dialog\n * instead of leaving the page. Closing the dialog removes the entry again; the returned promise settles after that.\n */\n history?: boolean;\n};\n\nexport type DialogRender<T> = (\n close: DialogClose<T>,\n context: {\n dialog: HTMLDialogElement;\n /** Switch this entry between a modal and a modeless window without remounting. */\n setModal: (modal: boolean) => void;\n /** Set viewport coordinates, or restore the panel stylesheet position with null. */\n setPosition: (position: { x: number; y: number } | null) => void;\n /** Route Escape/backdrop through the same guarded handler as Cancel and X. */\n setDismissHandler: (handler: () => void | Promise<void>) => void;\n /** Request dismissal through this entry's cancellation policy and guard. */\n requestDismiss: () => Promise<void>;\n },\n) => JSX.Element;\n\nexport type DialogCore = {\n open: <T>(view: DialogRender<T>, options?: OpenDialogOptions) => Promise<T | undefined>;\n close: (result?: unknown) => void;\n isOpen: () => boolean;\n};\n\ntype DialogStackEntry = {\n container: HTMLDivElement;\n dispose?: () => void;\n resolve?: (value: unknown) => void;\n panelClassName: string;\n cancelBehavior: NonNullable<OpenDialogOptions[\"cancelBehavior\"]>;\n initialFocus: NonNullable<OpenDialogOptions[\"initialFocus\"]>;\n opener?: HTMLElement;\n openerRing: boolean;\n ariaLabel?: string;\n dismissHandler?: () => void | Promise<void>;\n dismissPending?: boolean;\n modal: boolean;\n position?: { x: number; y: number } | null;\n /** The marker of this dialog's history entry, for dialogs opened with `history`. */\n historyId?: number;\n};\n\ntype DialogState = {\n element?: HTMLDialogElement;\n modal?: boolean;\n stack: DialogStackEntry[];\n scrollLocked?: boolean;\n previousBodyOverflow?: string;\n previousHtmlOverflow?: string;\n mouseDownOnDialog?: boolean;\n connectionObserver?: MutationObserver;\n};\n\nconst DEFAULT_PANEL_CLASS = \"k2b-dialog\";\nconst DEFAULT_CONTENT_CLASS = \"k2b-dialog__viewport\";\nlet nextDialogTitleId = 0;\n/** The history state key that marks a dialog's same-URL entry. */\nconst HISTORY_MARKER = \"k2bDialog\";\n\nconst historyRecord = (): Record<string, unknown> =>\n typeof history.state === \"object\" && history.state !== null ? { ...history.state } : {};\nconst historyMarker = (): unknown => historyRecord()[HISTORY_MARKER];\n\nconst resolveInitialFocusTarget = (entry: DialogStackEntry, dialog: HTMLDialogElement): HTMLElement | null => {\n const { initialFocus } = entry;\n if (initialFocus === \"none\") return null;\n if (typeof initialFocus === \"function\") return initialFocus(dialog);\n const input = entry.container.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), [role='combobox']:not([disabled]):not([aria-disabled='true'])\",\n );\n return (\n input ??\n entry.container.querySelector<HTMLElement>(\n \"input:not([type='hidden']):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), a[href], [tabindex]:not([tabindex='-1'])\",\n )\n );\n};\n\nconst applyAccessibleName = (dialog: HTMLDialogElement, entry: DialogStackEntry): void => {\n dialog.removeAttribute(\"aria-label\");\n dialog.removeAttribute(\"aria-labelledby\");\n if (entry.ariaLabel) {\n dialog.setAttribute(\"aria-label\", entry.ariaLabel);\n return;\n }\n const heading = entry.container.querySelector<HTMLElement>(\"h1, h2, h3\");\n if (!heading) {\n dialog.setAttribute(\"aria-label\", resolveUiMessages().dialog);\n return;\n }\n heading.id ||= `k2b-dialog-title-${++nextDialogTitleId}`;\n dialog.setAttribute(\"aria-labelledby\", heading.id);\n};\n\nconst schedule = (callback: () => void): void => {\n if (typeof requestAnimationFrame === \"function\") requestAnimationFrame(callback);\n else queueMicrotask(callback);\n};\n\n/**\n * Whether focus is still where closing a level left it, on the page or the\n * dialog: nothing took it before the next frame, neither a close handler nor a\n * Tab pressed right after Escape.\n */\nconst focusUnclaimed = (dialog: HTMLDialogElement): boolean => {\n const active = document.activeElement;\n return !active || active === document.body || active === dialog;\n};\n\nexport const createDialogCore = (): DialogCore => {\n const state: DialogState = { stack: [] };\n /** Markers of this core's entries in the browser history, oldest first. */\n let historyIds: number[] = [];\n let nextHistoryId = 0;\n let historySyncScheduled = false;\n let listeningToHistory = false;\n /** Settles when the synthetic Back that removes closed dialogs' entries has arrived. */\n let returning: Promise<void> | undefined;\n\n /** Closed dialogs leave the history with one Back to the newest dialog that is still open, or to the page. */\n const syncHistory = () => {\n historySyncScheduled = false;\n const openIds = new Set(state.stack.map((entry) => entry.historyId));\n const current = historyIds.at(-1);\n let steps = 0;\n while (historyIds.length > 0 && !openIds.has(historyIds.at(-1))) {\n historyIds.pop();\n steps++;\n }\n // Someone else moved the history meanwhile; leave it alone.\n if (steps === 0 || historyMarker() !== current) return;\n const back = new Promise<void>((resolve) => window.addEventListener(\"popstate\", () => resolve(), { once: true }));\n returning = back;\n void back.then(() => {\n if (returning === back) returning = undefined;\n });\n history.go(-steps);\n };\n\n const releaseHistory = (entry: DialogStackEntry) => {\n if (entry.historyId === undefined || historySyncScheduled) return;\n historySyncScheduled = true;\n queueMicrotask(syncHistory);\n };\n\n /** Back or Forward moved to another entry: close every dialog opened after the one it shows. */\n const onPopState = () => {\n const marker = historyMarker();\n const index = typeof marker === \"number\" ? historyIds.indexOf(marker) : -1;\n historyIds = historyIds.slice(0, index + 1);\n const gone = (entry: DialogStackEntry) => entry.historyId !== undefined && !historyIds.includes(entry.historyId);\n while (state.stack.some(gone)) popTop(undefined);\n };\n\n const pushHistory = (entry: DialogStackEntry) => {\n if (!listeningToHistory) {\n window.addEventListener(\"popstate\", onPopState);\n listeningToHistory = true;\n }\n const previous = historyRecord();\n // A reload or Forward never brings back a dialog or its sensitive transient state.\n if (historyIds.length === 0 && previous[HISTORY_MARKER] !== undefined) {\n delete previous[HISTORY_MARKER];\n history.replaceState(previous, \"\");\n }\n entry.historyId = ++nextHistoryId;\n historyIds.push(entry.historyId);\n history.pushState({ ...previous, [HISTORY_MARKER]: entry.historyId }, \"\");\n };\n\n const ensureDialogElement = () => {\n if (typeof document === \"undefined\") throw new Error(\"@k2b/ui dialogs can only be opened in the browser\");\n // Reuse the shared element whenever it is still in the document. Resolving\n // the portal root first would create a second <dialog> when focus has moved\n // into another `.k2b-ui` scope, orphaning the levels already on the stack.\n if (state.element?.isConnected) return state.element;\n\n const element = document.createElement(\"dialog\");\n element.onclose = () => {\n // Repeated Escape can force a non-cancelable native close. A retained\n // entry (ignore, an async guard, or a parent) still owns the modal.\n const top = state.stack[state.stack.length - 1];\n if (state.element !== element || element.open || !top) return;\n if (top.modal) element.showModal();\n else element.show();\n resolveInitialFocusTarget(top, element)?.focus();\n };\n getK2bPortalRoot().appendChild(element);\n state.element = element;\n return element;\n };\n\n const lockPageScroll = () => {\n if (typeof document === \"undefined\" || state.scrollLocked) return;\n state.previousBodyOverflow = document.body.style.overflow;\n state.previousHtmlOverflow = document.documentElement.style.overflow;\n document.body.style.overflow = \"hidden\";\n document.documentElement.style.overflow = \"hidden\";\n state.scrollLocked = true;\n };\n\n const unlockPageScroll = () => {\n if (typeof document === \"undefined\" || !state.scrollLocked) return;\n document.body.style.overflow = state.previousBodyOverflow ?? \"\";\n document.documentElement.style.overflow = state.previousHtmlOverflow ?? \"\";\n state.scrollLocked = false;\n state.previousBodyOverflow = undefined;\n state.previousHtmlOverflow = undefined;\n };\n\n const applyPresentation = (entry: DialogStackEntry) => {\n const dialog = state.element;\n if (!dialog || state.stack[state.stack.length - 1] !== entry) return;\n const position = entry.position;\n dialog.style.position = position ? \"fixed\" : \"\";\n dialog.style.inset = position ? `${position.y}px auto auto ${position.x}px` : \"\";\n dialog.style.margin = position ? \"0\" : \"\";\n dialog.style.transform = position ? \"none\" : \"\";\n dialog.dataset.modeless = String(!entry.modal);\n dialog.setAttribute(\"aria-modal\", String(entry.modal));\n if (dialog.open && state.modal !== entry.modal) {\n const active = document.activeElement;\n dialog.close();\n if (entry.modal) dialog.showModal();\n else dialog.show();\n if (active instanceof HTMLElement && active.isConnected && (!entry.modal || dialog.contains(active))) {\n active.focus({ preventScroll: true });\n } else resolveInitialFocusTarget(entry, dialog)?.focus({ preventScroll: true });\n }\n state.modal = entry.modal;\n if (entry.modal) lockPageScroll();\n else unlockPageScroll();\n };\n\n const modelessEscape = (event: KeyboardEvent) => {\n const top = state.stack[state.stack.length - 1];\n if (!top || top.modal || event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n event.preventDefault();\n void requestDismiss(top);\n };\n\n const stopConnectionObserver = () => {\n document.removeEventListener(\"keydown\", modelessEscape);\n state.connectionObserver?.disconnect();\n state.connectionObserver = undefined;\n };\n\n const applyCancelBehavior = (\n dialog: HTMLDialogElement,\n close: () => void,\n behavior: NonNullable<OpenDialogOptions[\"cancelBehavior\"]>,\n ) => {\n dialog.oncancel = (event) => {\n if (behavior === \"ignore\") {\n event.preventDefault();\n return;\n }\n event.preventDefault();\n close();\n };\n dialog.onmousedown = (event) => {\n state.mouseDownOnDialog = event.target === dialog;\n };\n dialog.onclick = (event) => {\n const realBackdropClick = state.mouseDownOnDialog === true;\n state.mouseDownOnDialog = false;\n if (event.target !== dialog || !state.modal) return;\n if (!realBackdropClick) return;\n if (behavior === \"ignore\") return;\n if (isPointInsideToast(event.clientX, event.clientY)) return;\n close();\n };\n };\n\n const requestDismiss = async (entry: DialogStackEntry) => {\n if (state.stack[state.stack.length - 1] !== entry || entry.dismissPending || entry.cancelBehavior === \"ignore\") return;\n if (!entry.dismissHandler) {\n popTop(undefined);\n return;\n }\n entry.dismissPending = true;\n try {\n await entry.dismissHandler();\n } catch (error) {\n // A failed guard must never discard the dialog's state.\n console.error(\"Dialog dismissal failed\", error);\n } finally {\n entry.dismissPending = false;\n }\n };\n\n const popTop = (result?: unknown) => {\n const top = state.stack.pop();\n if (!top) return;\n releaseHistory(top);\n top.dispose?.();\n top.container.remove();\n\n const dialog = state.element;\n const previous = state.stack[state.stack.length - 1];\n if (previous && dialog) {\n previous.container.style.display = \"\";\n dialog.className = previous.panelClassName;\n applyPresentation(previous);\n applyAccessibleName(dialog, previous);\n applyCancelBehavior(dialog, () => void requestDismiss(previous), previous.cancelBehavior);\n schedule(() => {\n if (!focusUnclaimed(dialog)) return;\n if (top.opener?.isConnected) returnFocus(top.opener, top.openerRing);\n else resolveInitialFocusTarget(previous, dialog)?.focus();\n });\n } else if (dialog) {\n dialog.oncancel = null;\n dialog.onmousedown = null;\n dialog.onclick = null;\n dialog.removeAttribute(\"aria-label\");\n dialog.removeAttribute(\"aria-labelledby\");\n if (dialog.open && typeof dialog.close === \"function\") dialog.close();\n else dialog.removeAttribute(\"open\");\n dialog.remove();\n state.element = undefined;\n stopConnectionObserver();\n unlockPageScroll();\n // After the Escape that closed it: WebKit lights the ring of whatever\n // that key press leaves focused.\n schedule(() => {\n if (top.opener?.isConnected && focusUnclaimed(dialog)) returnFocus(top.opener, top.openerRing);\n });\n }\n\n top.resolve?.(result);\n };\n\n const resetDisconnectedDialog = () => {\n if (state.stack.length === 0 || state.element?.isConnected) return;\n const entries = state.stack.splice(0);\n const dialog = state.element;\n state.element = undefined;\n stopConnectionObserver();\n dialog?.remove();\n unlockPageScroll();\n for (const entry of entries.reverse()) {\n releaseHistory(entry);\n entry.dispose?.();\n entry.container.remove();\n entry.resolve?.(undefined);\n }\n };\n\n const observeConnection = () => {\n if (state.connectionObserver || typeof MutationObserver === \"undefined\") return;\n document.addEventListener(\"keydown\", modelessEscape);\n state.connectionObserver = new MutationObserver(resetDisconnectedDialog);\n state.connectionObserver.observe(document.documentElement, { childList: true, subtree: true });\n };\n\n const open = <T>(view: DialogRender<T>, options: OpenDialogOptions = {}): Promise<T | undefined> => {\n if (options.signal?.aborted) return Promise.resolve(undefined);\n if (options.history) {\n // Dialogs closed just now leave the history first, or this dialog's entry would be the one removed.\n if (historySyncScheduled) syncHistory();\n if (returning) return returning.then(() => open(view, options));\n }\n const dialog = ensureDialogElement();\n const previousTop = state.stack[state.stack.length - 1];\n if (previousTop) previousTop.container.style.display = \"none\";\n const activeElement = document.activeElement;\n\n const panelClassName = options.panelClassName ?? DEFAULT_PANEL_CLASS;\n const cancelBehavior = options.cancelBehavior ?? \"resolve-undefined\";\n const initialFocus = options.initialFocus ?? \"first-input\";\n dialog.className = panelClassName;\n\n const container = document.createElement(\"div\");\n container.className = options.contentClassName ?? DEFAULT_CONTENT_CLASS;\n dialog.appendChild(container);\n\n const entry: DialogStackEntry = {\n container,\n panelClassName,\n cancelBehavior,\n initialFocus,\n opener: activeElement instanceof HTMLElement ? activeElement : undefined,\n openerRing: ringOnReturn(activeElement),\n ariaLabel: options.ariaLabel,\n modal: true,\n };\n\n return new Promise((resolve, reject) => {\n const abort = () => {\n const index = state.stack.indexOf(entry);\n if (index < 0) return;\n if (index === state.stack.length - 1) popTop(undefined);\n else {\n state.stack.splice(index, 1);\n releaseHistory(entry);\n entry.dispose?.();\n entry.container.remove();\n entry.resolve?.(undefined);\n }\n };\n entry.resolve = (value) => {\n options.signal?.removeEventListener(\"abort\", abort);\n if (entry.historyId === undefined) {\n resolve(value as T | undefined);\n return;\n }\n // After the history sync, so a caller that navigates or opens the next dialog finds the history settled.\n queueMicrotask(() => void (returning ?? Promise.resolve()).then(() => resolve(value as T | undefined)));\n };\n const closeTyped: DialogClose<T> = (result) => {\n if (state.stack[state.stack.length - 1] !== entry) return;\n popTop(result);\n };\n\n if (options.history) pushHistory(entry);\n state.stack.push(entry);\n try {\n entry.dispose = render(\n () =>\n view(closeTyped, {\n dialog,\n setModal: (modal) => {\n entry.modal = modal;\n applyPresentation(entry);\n },\n setPosition: (position) => {\n if (position && (!Number.isFinite(position.x) || !Number.isFinite(position.y))) return;\n entry.position = position;\n applyPresentation(entry);\n },\n requestDismiss: () => requestDismiss(entry),\n setDismissHandler: (handler) => {\n entry.dismissHandler = handler;\n },\n }),\n container,\n );\n applyPresentation(entry);\n applyAccessibleName(dialog, entry);\n applyCancelBehavior(dialog, () => void requestDismiss(entry), cancelBehavior);\n\n if (state.stack.length === 1) {\n // The native dialog returns focus to the element focused when it\n // opened, with a ring of the browser's choosing. Opening from a\n // blurred document leaves the return to popTop and its opener ring.\n if (activeElement instanceof HTMLElement) activeElement.blur();\n if (typeof dialog.showModal === \"function\") {\n if (entry.modal) dialog.showModal();\n else dialog.show();\n } else dialog.setAttribute(\"open\", \"\");\n observeConnection();\n }\n options.signal?.addEventListener(\"abort\", abort, { once: true });\n if (options.signal?.aborted) abort();\n // Where opening left focus, the native dialog's own pick included. Focus that the content or a person moved\n // before the next frame stays: a browser can hold that frame back while keys already arrive.\n const opened = document.activeElement;\n schedule(() => {\n if (state.stack[state.stack.length - 1] !== entry) return;\n if (document.activeElement !== opened && !focusUnclaimed(dialog)) return;\n resolveInitialFocusTarget(entry, dialog)?.focus();\n });\n } catch (error) {\n entry.resolve = undefined;\n if (state.stack[state.stack.length - 1] === entry) popTop(undefined);\n reject(error);\n }\n });\n };\n\n const close: DialogCore[\"close\"] = (result) => {\n let first = true;\n while (state.stack.length > 0) {\n popTop(first ? result : undefined);\n first = false;\n }\n };\n\n return {\n open,\n close,\n isOpen: () => state.stack.length > 0,\n };\n};\n\nexport const dialogCore = createDialogCore();\n"],"mappings":"AACA,SAAS,MAAM,QAAQ,cAAc;AACrC,SAAS,WAAW,EAAE,YAAY,QAAQ,0BAA0B;AACpE,SAAS,gBAAgB,QAAQ,oBAAoB;AACrD,SAAS,iBAAiB,QAAQ,kBAAkB;AACpD,SAAS,kBAAkB,QAAQ,SAAS;AAqE5C,MAAM,mBAAmB,GAAG,YAAY;AACxC,MAAM,qBAAqB,GAAG,sBAAsB;AACpD,IAAI,iBAAiB,GAAG,CAAC;AACzB;AACA,MAAM,cAAc,GAAG,WAAW;AAElC,MAAM,aAAa,GAAG,MACpB,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,GAAG;EAAE,GAAG,OAAO,CAAC;AAAM,CAAC,GAAG,CAAC,CAAC;AACzF,MAAM,aAAa,GAAG,MAAe,aAAa,CAAC,CAAC,CAAC,cAAc,CAAC;AAEpE,MAAM,yBAAyB,GAAG,CAAC,KAAuB,EAAE,MAAyB,KAAyB;EAC5G,MAAM;IAAE;EAAa,CAAC,GAAG,KAAK;EAC9B,IAAI,YAAY,KAAK,MAAM,EAAE,OAAO,IAAI;EACxC,IAAI,OAAO,YAAY,KAAK,UAAU,EAAE,OAAO,YAAY,CAAC,MAAM,CAAC;EACnE,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,aAAa,CACzC,6JACF,CAAC;EACD,OACE,KAAK,IACL,KAAK,CAAC,SAAS,CAAC,aAAa,CAC3B,gKACF,CAAC;AAEL,CAAC;AAED,MAAM,mBAAmB,GAAG,CAAC,MAAyB,EAAE,KAAuB,KAAW;EACxF,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC;EACpC,MAAM,CAAC,eAAe,CAAC,iBAAiB,CAAC;EACzC,IAAI,KAAK,CAAC,SAAS,EAAE;IACnB,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC;IAClD;EACF;EACA,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,aAAa,CAAc,YAAY,CAAC;EACxE,IAAI,CAAC,OAAO,EAAE;IACZ,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7D;EACF;EACA,OAAO,CAAC,EAAE,KAAK,oBAAoB,EAAE,iBAAiB,EAAE;EACxD,MAAM,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,QAAQ,GAAI,QAAoB,IAAW;EAC/C,IAAI,OAAO,qBAAqB,KAAK,UAAU,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC,KAC5E,cAAc,CAAC,QAAQ,CAAC;AAC/B,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,MAAM,cAAc,GAAI,MAAyB,IAAc;EAC7D,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;EACrC,OAAO,CAAC,MAAM,IAAI,MAAM,KAAK,QAAQ,CAAC,IAAI,IAAI,MAAM,KAAK,MAAM;AACjE,CAAC;AAED,OAAO,MAAM,gBAAgB,GAAG,MAAkB;EAChD,MAAM,KAAkB,GAAG;IAAE,KAAK,EAAE;EAAG,CAAC;EACxC;EACA,IAAI,UAAoB,GAAG,EAAE;EAC7B,IAAI,aAAa,GAAG,CAAC;EACrB,IAAI,oBAAoB,GAAG,KAAK;EAChC,IAAI,kBAAkB,GAAG,KAAK;EAC9B;EACA,IAAI,SAAoC;;EAExC;EACA,MAAM,WAAW,GAAG,MAAM;IACxB,oBAAoB,GAAG,KAAK;IAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAE,KAAK,IAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC;IACb,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;MAC/D,UAAU,CAAC,GAAG,CAAC,CAAC;MAChB,KAAK,EAAE;IACT;IACA;IACA,IAAI,KAAK,KAAK,CAAC,IAAI,aAAa,CAAC,CAAC,KAAK,OAAO,EAAE;IAChD,MAAM,IAAI,GAAG,IAAI,OAAO,CAAQ,OAAO,IAAK,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,EAAE;MAAE,IAAI,EAAE;IAAK,CAAC,CAAC,CAAC;IACjH,SAAS,GAAG,IAAI;IAChB,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM;MACnB,IAAI,SAAS,KAAK,IAAI,EAAE,SAAS,GAAG,SAAS;IAC/C,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC;EACpB,CAAC;EAED,MAAM,cAAc,GAAI,KAAuB,IAAK;IAClD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,oBAAoB,EAAE;IAC3D,oBAAoB,GAAG,IAAI;IAC3B,cAAc,CAAC,WAAW,CAAC;EAC7B,CAAC;;EAED;EACA,MAAM,UAAU,GAAG,MAAM;IACvB,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1E,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAI,KAAuB,IAAK,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;IAChH,OAAO,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC;EAClD,CAAC;EAED,MAAM,WAAW,GAAI,KAAuB,IAAK;IAC/C,IAAI,CAAC,kBAAkB,EAAE;MACvB,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC;MAC/C,kBAAkB,GAAG,IAAI;IAC3B;IACA,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC;IAChC;IACA,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,cAAc,CAAC,KAAK,SAAS,EAAE;MACrE,OAAO,QAAQ,CAAC,cAAc,CAAC;MAC/B,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpC;IACA,KAAK,CAAC,SAAS,GAAG,EAAE,aAAa;IACjC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IAChC,OAAO,CAAC,SAAS,CAAC;MAAE,GAAG,QAAQ;MAAE,CAAC,cAAc,GAAG,KAAK,CAAC;IAAU,CAAC,EAAE,EAAE,CAAC;EAC3E,CAAC;EAED,MAAM,mBAAmB,GAAG,MAAM;IAChC,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC;IACzG;IACA;IACA;IACA,IAAI,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,KAAK,CAAC,OAAO;IAEpD,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC;IAChD,OAAO,CAAC,OAAO,GAAG,MAAM;MACtB;MACA;MACA,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;MAC/C,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE;MACvD,IAAI,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,KAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;MACnB,yBAAyB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IAClD,CAAC;IACD,gBAAgB,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC;IACvC,KAAK,CAAC,OAAO,GAAG,OAAO;IACvB,OAAO,OAAO;EAChB,CAAC;EAED,MAAM,cAAc,GAAG,MAAM;IAC3B,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,KAAK,CAAC,YAAY,EAAE;IAC3D,KAAK,CAAC,oBAAoB,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ;IACzD,KAAK,CAAC,oBAAoB,GAAG,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ;IACpE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ;IACvC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ;IAClD,KAAK,CAAC,YAAY,GAAG,IAAI;EAC3B,CAAC;EAED,MAAM,gBAAgB,GAAG,MAAM;IAC7B,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE;IAC5D,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,oBAAoB,IAAI,EAAE;IAC/D,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,oBAAoB,IAAI,EAAE;IAC1E,KAAK,CAAC,YAAY,GAAG,KAAK;IAC1B,KAAK,CAAC,oBAAoB,GAAG,SAAS;IACtC,KAAK,CAAC,oBAAoB,GAAG,SAAS;EACxC,CAAC;EAED,MAAM,iBAAiB,GAAI,KAAuB,IAAK;IACrD,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO;IAC5B,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,EAAE;IAC9D,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ;IAC/B,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,EAAE;IAC/C,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,QAAQ,GAAG,GAAG,QAAQ,CAAC,CAAC,gBAAgB,QAAQ,CAAC,CAAC,IAAI,GAAG,EAAE;IAChF,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,GAAG,GAAG,EAAE;IACzC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,EAAE;IAC/C,MAAM,CAAC,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC;IAC9C,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,EAAE;MAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;MACrC,MAAM,CAAC,KAAK,CAAC,CAAC;MACd,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAC/B,MAAM,CAAC,IAAI,CAAC,CAAC;MAClB,IAAI,MAAM,YAAY,WAAW,IAAI,MAAM,CAAC,WAAW,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE;QACpG,MAAM,CAAC,KAAK,CAAC;UAAE,aAAa,EAAE;QAAK,CAAC,CAAC;MACvC,CAAC,MAAM,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE;MAAK,CAAC,CAAC;IACjF;IACA,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;IACzB,IAAI,KAAK,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAC7B,gBAAgB,CAAC,CAAC;EACzB,CAAC;EAED,MAAM,cAAc,GAAI,KAAoB,IAAK;IAC/C,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,WAAW,EAAE;IAChG,KAAK,CAAC,cAAc,CAAC,CAAC;IACtB,KAAK,cAAc,CAAC,GAAG,CAAC;EAC1B,CAAC;EAED,MAAM,sBAAsB,GAAG,MAAM;IACnC,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,cAAc,CAAC;IACvD,KAAK,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IACtC,KAAK,CAAC,kBAAkB,GAAG,SAAS;EACtC,CAAC;EAED,MAAM,mBAAmB,GAAG,CAC1B,MAAyB,EACzB,KAAiB,EACjB,QAA0D,KACvD;IACH,MAAM,CAAC,QAAQ,GAAI,KAAK,IAAK;MAC3B,IAAI,QAAQ,KAAK,QAAQ,EAAE;QACzB,KAAK,CAAC,cAAc,CAAC,CAAC;QACtB;MACF;MACA,KAAK,CAAC,cAAc,CAAC,CAAC;MACtB,KAAK,CAAC,CAAC;IACT,CAAC;IACD,MAAM,CAAC,WAAW,GAAI,KAAK,IAAK;MAC9B,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC,MAAM,KAAK,MAAM;IACnD,CAAC;IACD,MAAM,CAAC,OAAO,GAAI,KAAK,IAAK;MAC1B,MAAM,iBAAiB,GAAG,KAAK,CAAC,iBAAiB,KAAK,IAAI;MAC1D,KAAK,CAAC,iBAAiB,GAAG,KAAK;MAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;MAC7C,IAAI,CAAC,iBAAiB,EAAE;MACxB,IAAI,QAAQ,KAAK,QAAQ,EAAE;MAC3B,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;MACtD,KAAK,CAAC,CAAC;IACT,CAAC;EACH,CAAC;EAED,MAAM,cAAc,GAAG,MAAO,KAAuB,IAAK;IACxD,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,cAAc,KAAK,QAAQ,EAAE;IAChH,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE;MACzB,MAAM,CAAC,SAAS,CAAC;MACjB;IACF;IACA,KAAK,CAAC,cAAc,GAAG,IAAI;IAC3B,IAAI;MACF,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC,CAAC,OAAO,KAAK,EAAE;MACd;MACA,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC;IACjD,CAAC,SAAS;MACR,KAAK,CAAC,cAAc,GAAG,KAAK;IAC9B;EACF,CAAC;EAED,MAAM,MAAM,GAAI,MAAgB,IAAK;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE;IACV,cAAc,CAAC,GAAG,CAAC;IACnB,GAAG,CAAC,OAAO,GAAG,CAAC;IACf,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAEtB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACpD,IAAI,QAAQ,IAAI,MAAM,EAAE;MACtB,QAAQ,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE;MACrC,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC,cAAc;MAC1C,iBAAiB,CAAC,QAAQ,CAAC;MAC3B,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC;MACrC,mBAAmB,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC;MACzF,QAAQ,CAAC,MAAM;QACb,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;QAC7B,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,KAChE,yBAAyB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;MAC3D,CAAC,CAAC;IACJ,CAAC,MAAM,IAAI,MAAM,EAAE;MACjB,MAAM,CAAC,QAAQ,GAAG,IAAI;MACtB,MAAM,CAAC,WAAW,GAAG,IAAI;MACzB,MAAM,CAAC,OAAO,GAAG,IAAI;MACrB,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC;MACpC,MAAM,CAAC,eAAe,CAAC,iBAAiB,CAAC;MACzC,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,KACjE,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC;MACnC,MAAM,CAAC,MAAM,CAAC,CAAC;MACf,KAAK,CAAC,OAAO,GAAG,SAAS;MACzB,sBAAsB,CAAC,CAAC;MACxB,gBAAgB,CAAC,CAAC;MAClB;MACA;MACA,QAAQ,CAAC,MAAM;QACb,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,CAAC;MAChG,CAAC,CAAC;IACJ;IAEA,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC;EACvB,CAAC;EAED,MAAM,uBAAuB,GAAG,MAAM;IACpC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,WAAW,EAAE;IAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO;IAC5B,KAAK,CAAC,OAAO,GAAG,SAAS;IACzB,sBAAsB,CAAC,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC,CAAC;IAChB,gBAAgB,CAAC,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE;MACrC,cAAc,CAAC,KAAK,CAAC;MACrB,KAAK,CAAC,OAAO,GAAG,CAAC;MACjB,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;MACxB,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;IAC5B;EACF,CAAC;EAED,MAAM,iBAAiB,GAAG,MAAM;IAC9B,IAAI,KAAK,CAAC,kBAAkB,IAAI,OAAO,gBAAgB,KAAK,WAAW,EAAE;IACzE,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,cAAc,CAAC;IACpD,KAAK,CAAC,kBAAkB,GAAG,IAAI,gBAAgB,CAAC,uBAAuB,CAAC;IACxE,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;MAAE,SAAS,EAAE,IAAI;MAAE,OAAO,EAAE;IAAK,CAAC,CAAC;EAChG,CAAC;EAED,MAAM,IAAI,GAAG,CAAI,IAAqB,EAAE,OAA0B,GAAG,CAAC,CAAC,KAA6B;IAClG,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC;IAC9D,IAAI,OAAO,CAAC,OAAO,EAAE;MACnB;MACA,IAAI,oBAAoB,EAAE,WAAW,CAAC,CAAC;MACvC,IAAI,SAAS,EAAE,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjE;IACA,MAAM,MAAM,GAAG,mBAAmB,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACvD,IAAI,WAAW,EAAE,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;IAC7D,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa;IAE5C,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,mBAAmB;IACpE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,mBAAmB;IACpE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,aAAa;IAC1D,MAAM,CAAC,SAAS,GAAG,cAAc;IAEjC,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC;IAC/C,SAAS,CAAC,SAAS,GAAG,OAAO,CAAC,gBAAgB,IAAI,qBAAqB;IACvE,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC;IAE7B,MAAM,KAAuB,GAAG;MAC9B,SAAS;MACT,cAAc;MACd,cAAc;MACd,YAAY;MACZ,MAAM,EAAE,aAAa,YAAY,WAAW,GAAG,aAAa,GAAG,SAAS;MACxE,UAAU,EAAE,YAAY,CAAC,aAAa,CAAC;MACvC,SAAS,EAAE,OAAO,CAAC,SAAS;MAC5B,KAAK,EAAE;IACT,CAAC;IAED,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK;MACtC,MAAM,KAAK,GAAG,MAAM;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACxC,IAAI,KAAK,GAAG,CAAC,EAAE;QACf,IAAI,KAAK,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,KACnD;UACH,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;UAC5B,cAAc,CAAC,KAAK,CAAC;UACrB,KAAK,CAAC,OAAO,GAAG,CAAC;UACjB,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;UACxB,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;QAC5B;MACF,CAAC;MACD,KAAK,CAAC,OAAO,GAAI,KAAK,IAAK;QACzB,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC;QACnD,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE;UACjC,OAAO,CAAC,KAAsB,CAAC;UAC/B;QACF;QACA;QACA,cAAc,CAAC,MAAM,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,OAAO,CAAC,KAAsB,CAAC,CAAC,CAAC;MACzG,CAAC;MACD,MAAM,UAA0B,GAAI,MAAM,IAAK;QAC7C,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,EAAE;QACnD,MAAM,CAAC,MAAM,CAAC;MAChB,CAAC;MAED,IAAI,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC;MACvC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;MACvB,IAAI;QACF,KAAK,CAAC,OAAO,GAAG,MAAM,CACpB,MACE,IAAI,CAAC,UAAU,EAAE;UACf,MAAM;UACN,QAAQ,EAAG,KAAK,IAAK;YACnB,KAAK,CAAC,KAAK,GAAG,KAAK;YACnB,iBAAiB,CAAC,KAAK,CAAC;UAC1B,CAAC;UACD,WAAW,EAAG,QAAQ,IAAK;YACzB,IAAI,QAAQ,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;YAChF,KAAK,CAAC,QAAQ,GAAG,QAAQ;YACzB,iBAAiB,CAAC,KAAK,CAAC;UAC1B,CAAC;UACD,cAAc,EAAE,MAAM,cAAc,CAAC,KAAK,CAAC;UAC3C,iBAAiB,EAAG,OAAO,IAAK;YAC9B,KAAK,CAAC,cAAc,GAAG,OAAO;UAChC;QACF,CAAC,CAAC,EACJ,SACF,CAAC;QACD,iBAAiB,CAAC,KAAK,CAAC;QACxB,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC;QAClC,mBAAmB,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,CAAC,EAAE,cAAc,CAAC;QAE7E,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;UAC5B;UACA;UACA;UACA,IAAI,aAAa,YAAY,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;UAC9D,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,UAAU,EAAE;YAC1C,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,KAC/B,MAAM,CAAC,IAAI,CAAC,CAAC;UACpB,CAAC,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;UACtC,iBAAiB,CAAC,CAAC;QACrB;QACA,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE;UAAE,IAAI,EAAE;QAAK,CAAC,CAAC;QAChE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACpC;QACA;QACA,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa;QACrC,QAAQ,CAAC,MAAM;UACb,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,EAAE;UACnD,IAAI,QAAQ,CAAC,aAAa,KAAK,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE;UAClE,yBAAyB,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;QACnD,CAAC,CAAC;MACJ,CAAC,CAAC,OAAO,KAAK,EAAE;QACd,KAAK,CAAC,OAAO,GAAG,SAAS;QACzB,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,EAAE,MAAM,CAAC,SAAS,CAAC;QACpE,MAAM,CAAC,KAAK,CAAC;MACf;IACF,CAAC,CAAC;EACJ,CAAC;EAED,MAAM,KAA0B,GAAI,MAAM,IAAK;IAC7C,IAAI,KAAK,GAAG,IAAI;IAChB,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;MAC7B,MAAM,CAAC,KAAK,GAAG,MAAM,GAAG,SAAS,CAAC;MAClC,KAAK,GAAG,KAAK;IACf;EACF,CAAC;EAED,OAAO;IACL,IAAI;IACJ,KAAK;IACL,MAAM,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG;EACrC,CAAC;AACH,CAAC;AAED,OAAO,MAAM,UAAU,GAAG,gBAAgB,CAAC,CAAC","ignoreList":[]}
@@ -1,7 +1,11 @@
1
+ export { announce } from "./announce";
1
2
  export { dialogCore } from "./dialog-core";
2
3
  export { createHoverPreview, HoverPreview } from "./HoverPreview";
3
4
  export { default as InlineGuidance } from "./InlineGuidance";
5
+ export { default as InstallGuide } from "./InstallGuide";
6
+ export { createInstallPrompt, installationPlatform } from "./install";
4
7
  export { createFormState, DialogHeader, prompts } from "./prompts";
5
8
  export { Tooltip } from "./Tooltip";
9
+ export { syncThemeColor } from "./theme-color";
6
10
  export { isPointInsideToast, toast } from "./toast";
7
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["feedback/index.ts"],"sourcesContent":["export type { DialogClose, DialogCore, DialogRender, OpenDialogOptions } from \"./dialog-core\";\nexport { dialogCore } from \"./dialog-core\";\nexport type { HoverPreviewController, HoverPreviewOptions, HoverPreviewPlacement, HoverPreviewProps } from \"./HoverPreview\";\nexport { createHoverPreview, HoverPreview } from \"./HoverPreview\";\nexport type { InlineGuidanceProps } from \"./InlineGuidance\";\nexport { default as InlineGuidance } from \"./InlineGuidance\";\nexport type {\n ConfirmOptions,\n DialogOptions,\n FieldSchema,\n PromptFieldBase,\n PromptSearchInput,\n PromptSearchItem,\n PromptSearchOptions,\n} from \"./prompts\";\nexport { createFormState, DialogHeader, prompts } from \"./prompts\";\nexport type { TooltipAnchorProps, TooltipPlacement, TooltipProps, TooltipTriggerProps } from \"./Tooltip\";\nexport { Tooltip } from \"./Tooltip\";\nexport type { ToastAction, ToastFn, ToastHandle, ToastOptions, ToastSlot, ToastVariant } from \"./toast\";\nexport { isPointInsideToast, toast } from \"./toast\";\n"],"mappings":"AACA,SAAS,UAAU,QAAQ,eAAe;AAE1C,SAAS,kBAAkB,EAAE,YAAY,QAAQ,gBAAgB;AAEjE,SAAS,OAAO,IAAI,cAAc,QAAQ,kBAAkB;AAU5D,SAAS,eAAe,EAAE,YAAY,EAAE,OAAO,QAAQ,WAAW;AAElE,SAAS,OAAO,QAAQ,WAAW;AAEnC,SAAS,kBAAkB,EAAE,KAAK,QAAQ,SAAS","ignoreList":[]}
1
+ {"version":3,"names":[],"sources":["feedback/index.ts"],"sourcesContent":["export type { AnnounceOptions, AnnouncePoliteness } from \"./announce\";\nexport { announce } from \"./announce\";\nexport type { DialogClose, DialogCore, DialogRender, OpenDialogOptions } from \"./dialog-core\";\nexport { dialogCore } from \"./dialog-core\";\nexport type { HoverPreviewController, HoverPreviewOptions, HoverPreviewPlacement, HoverPreviewProps } from \"./HoverPreview\";\nexport { createHoverPreview, HoverPreview } from \"./HoverPreview\";\nexport type { InlineGuidanceProps } from \"./InlineGuidance\";\nexport { default as InlineGuidance } from \"./InlineGuidance\";\nexport type { InstallGuideProps } from \"./InstallGuide\";\nexport { default as InstallGuide } from \"./InstallGuide\";\nexport type { InstallationPlatform, InstallPrompt } from \"./install\";\nexport { createInstallPrompt, installationPlatform } from \"./install\";\nexport type {\n ConfirmOptions,\n DialogOptions,\n FieldSchema,\n PromptFieldBase,\n PromptSearchInput,\n PromptSearchItem,\n PromptSearchOptions,\n} from \"./prompts\";\nexport { createFormState, DialogHeader, prompts } from \"./prompts\";\nexport type { TooltipAnchorProps, TooltipPlacement, TooltipProps, TooltipTriggerProps } from \"./Tooltip\";\nexport { Tooltip } from \"./Tooltip\";\nexport { syncThemeColor } from \"./theme-color\";\nexport type { ToastAction, ToastFn, ToastHandle, ToastOptions, ToastSlot, ToastVariant } from \"./toast\";\nexport { isPointInsideToast, toast } from \"./toast\";\n"],"mappings":"AACA,SAAS,QAAQ,QAAQ,YAAY;AAErC,SAAS,UAAU,QAAQ,eAAe;AAE1C,SAAS,kBAAkB,EAAE,YAAY,QAAQ,gBAAgB;AAEjE,SAAS,OAAO,IAAI,cAAc,QAAQ,kBAAkB;AAE5D,SAAS,OAAO,IAAI,YAAY,QAAQ,gBAAgB;AAExD,SAAS,mBAAmB,EAAE,oBAAoB,QAAQ,WAAW;AAUrE,SAAS,eAAe,EAAE,YAAY,EAAE,OAAO,QAAQ,WAAW;AAElE,SAAS,OAAO,QAAQ,WAAW;AACnC,SAAS,cAAc,QAAQ,eAAe;AAE9C,SAAS,kBAAkB,EAAE,KAAK,QAAQ,SAAS","ignoreList":[]}
@@ -0,0 +1,99 @@
1
+ import { createSignal, onCleanup } from "solid-js";
2
+
3
+ /**
4
+ * Which installation steps a browser needs. `apple-in-app` and `in-app` are browsers embedded in another app, on
5
+ * iPhone or iPad and elsewhere, which cannot install at all. `apple-browser` is Chrome, Firefox, Edge, or another
6
+ * browser on iPhone or iPad, which installs through its Share menu but less reliably than Safari. `android-browser`
7
+ * is an Android browser other than Chrome.
8
+ */
9
+
10
+ /** Tokens of apps that open links in their own browser view. Case-sensitive, so `Line/` matches only LINE. */
11
+ const EMBEDDED = /FBAN|FBAV|FB_IAB|Instagram|LinkedInApp|Twitter|Snapchat|musical_ly|BytedanceWebview|Pinterest|MicroMessenger|\bLine\/|GSA\/|; wv\)/;
12
+ /** Browsers on iPhone and iPad other than Safari. */
13
+ const APPLE_BROWSER = /CriOS\/|FxiOS\/|EdgiOS\/|OPiOS\/|OPT\/|Ddg\/|DuckDuckGo\/|YaBrowser\//;
14
+ /** Android browsers whose user agent also names Chrome. */
15
+ const ANDROID_OTHER = /SamsungBrowser|EdgA\/|OPR\/|YaBrowser|Firefox\//;
16
+
17
+ /**
18
+ * Classifies the browser from its user agent; `platform` and `touchPoints` only tell an iPad that reports a Mac apart.
19
+ * A pure function, so the server and the browser compute the same value from the same user agent. Not a device
20
+ * check: an installed app on iPhone may itself look like an embedded browser.
21
+ */
22
+ export function installationPlatform(userAgent, platform, touchPoints) {
23
+ if (/iPad|iPhone|iPod/.test(userAgent) || platform === "MacIntel" && touchPoints > 1) {
24
+ if (EMBEDDED.test(userAgent)) return "apple-in-app";
25
+ if (APPLE_BROWSER.test(userAgent)) return "apple-browser";
26
+ // Safari and every real browser send `Safari/`; a bare web view inside an app, such as a QR scanner, does not.
27
+ if (!/Safari\//.test(userAgent)) return "apple-in-app";
28
+ return "apple-mobile";
29
+ }
30
+ if (EMBEDDED.test(userAgent)) return "in-app";
31
+ if (/Macintosh/.test(userAgent) && /Safari/.test(userAgent) && !/Chrome|Chromium|Edg\//.test(userAgent)) return "apple-desktop";
32
+ if (/Android/.test(userAgent)) return /Chrome\//.test(userAgent) && !ANDROID_OTHER.test(userAgent) ? "android" : "android-browser";
33
+ return "generic";
34
+ }
35
+ function isInstallPrompt(event) {
36
+ return "prompt" in event && typeof event.prompt === "function";
37
+ }
38
+ /**
39
+ * Tracks whether the page runs installed and captures the browser's installation prompt. Browser-only; call it once,
40
+ * when the page loads, under an owner that lives as long as the page: Chrome offers its prompt only once, early.
41
+ */
42
+ export function createInstallPrompt() {
43
+ const displayMode = matchMedia("(display-mode: standalone)");
44
+ const isStandalone = () => displayMode.matches || "standalone" in navigator && navigator.standalone === true;
45
+ const [installed, setInstalled] = createSignal(isStandalone());
46
+ const [prompt, setPrompt] = createSignal();
47
+ const [busy, setBusy] = createSignal(false);
48
+ const [requested, setRequested] = createSignal(false);
49
+ const [failed, setFailed] = createSignal(false);
50
+ const capture = event => {
51
+ if (!isInstallPrompt(event)) return;
52
+ event.preventDefault();
53
+ if (installed()) return;
54
+ setPrompt(event);
55
+ setRequested(false);
56
+ };
57
+ const complete = () => {
58
+ setInstalled(true);
59
+ setPrompt(undefined);
60
+ };
61
+ const displayChanged = () => {
62
+ if (isStandalone()) complete();
63
+ };
64
+ window.addEventListener("beforeinstallprompt", capture);
65
+ window.addEventListener("appinstalled", complete);
66
+ displayMode.addEventListener("change", displayChanged);
67
+ onCleanup(() => {
68
+ window.removeEventListener("beforeinstallprompt", capture);
69
+ window.removeEventListener("appinstalled", complete);
70
+ displayMode.removeEventListener("change", displayChanged);
71
+ });
72
+ const install = async () => {
73
+ if (busy() || installed()) return;
74
+ const event = prompt();
75
+ setFailed(false);
76
+ if (!event) return;
77
+ // Consume once, and invoke synchronously within the user's click.
78
+ setPrompt(undefined);
79
+ setBusy(true);
80
+ try {
81
+ const result = await event.prompt();
82
+ if (!installed()) setRequested(result.outcome === "accepted");
83
+ } catch {
84
+ setFailed(true);
85
+ } finally {
86
+ setBusy(false);
87
+ }
88
+ };
89
+ return {
90
+ platform: installationPlatform(navigator.userAgent, navigator.platform, navigator.maxTouchPoints),
91
+ installed,
92
+ canPrompt: () => Boolean(prompt()),
93
+ busy,
94
+ requested,
95
+ failed,
96
+ install
97
+ };
98
+ }
99
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":[],"sources":["feedback/install.ts"],"sourcesContent":["import { createSignal, onCleanup } from \"solid-js\";\n\n/**\n * Which installation steps a browser needs. `apple-in-app` and `in-app` are browsers embedded in another app, on\n * iPhone or iPad and elsewhere, which cannot install at all. `apple-browser` is Chrome, Firefox, Edge, or another\n * browser on iPhone or iPad, which installs through its Share menu but less reliably than Safari. `android-browser`\n * is an Android browser other than Chrome.\n */\nexport type InstallationPlatform =\n | \"in-app\"\n | \"apple-in-app\"\n | \"apple-browser\"\n | \"apple-mobile\"\n | \"apple-desktop\"\n | \"android-browser\"\n | \"android\"\n | \"generic\";\n\n/** Tokens of apps that open links in their own browser view. Case-sensitive, so `Line/` matches only LINE. */\nconst EMBEDDED =\n /FBAN|FBAV|FB_IAB|Instagram|LinkedInApp|Twitter|Snapchat|musical_ly|BytedanceWebview|Pinterest|MicroMessenger|\\bLine\\/|GSA\\/|; wv\\)/;\n/** Browsers on iPhone and iPad other than Safari. */\nconst APPLE_BROWSER = /CriOS\\/|FxiOS\\/|EdgiOS\\/|OPiOS\\/|OPT\\/|Ddg\\/|DuckDuckGo\\/|YaBrowser\\//;\n/** Android browsers whose user agent also names Chrome. */\nconst ANDROID_OTHER = /SamsungBrowser|EdgA\\/|OPR\\/|YaBrowser|Firefox\\//;\n\n/**\n * Classifies the browser from its user agent; `platform` and `touchPoints` only tell an iPad that reports a Mac apart.\n * A pure function, so the server and the browser compute the same value from the same user agent. Not a device\n * check: an installed app on iPhone may itself look like an embedded browser.\n */\nexport function installationPlatform(userAgent: string, platform: string, touchPoints: number): InstallationPlatform {\n if (/iPad|iPhone|iPod/.test(userAgent) || (platform === \"MacIntel\" && touchPoints > 1)) {\n if (EMBEDDED.test(userAgent)) return \"apple-in-app\";\n if (APPLE_BROWSER.test(userAgent)) return \"apple-browser\";\n // Safari and every real browser send `Safari/`; a bare web view inside an app, such as a QR scanner, does not.\n if (!/Safari\\//.test(userAgent)) return \"apple-in-app\";\n return \"apple-mobile\";\n }\n if (EMBEDDED.test(userAgent)) return \"in-app\";\n if (/Macintosh/.test(userAgent) && /Safari/.test(userAgent) && !/Chrome|Chromium|Edg\\//.test(userAgent)) return \"apple-desktop\";\n if (/Android/.test(userAgent)) return /Chrome\\//.test(userAgent) && !ANDROID_OTHER.test(userAgent) ? \"android\" : \"android-browser\";\n return \"generic\";\n}\n\ninterface BeforeInstallPromptEvent extends Event {\n prompt(): Promise<{ outcome: \"accepted\" | \"dismissed\" }>;\n}\n\nfunction isInstallPrompt(event: Event): event is BeforeInstallPromptEvent {\n return \"prompt\" in event && typeof event.prompt === \"function\";\n}\n\nexport type InstallPrompt = {\n platform: InstallationPlatform;\n /** Running from the Home Screen or as an installed app, or installed during this visit. */\n installed: () => boolean;\n /** The browser offered its own installation dialog, which `install()` opens. */\n canPrompt: () => boolean;\n busy: () => boolean;\n /** The person accepted the browser's dialog; the browser finishes the installation. */\n requested: () => boolean;\n /** The browser's dialog could not be opened. */\n failed: () => boolean;\n /** Opens the browser's dialog once. Call it directly from a click. */\n install: () => Promise<void>;\n};\n\n/**\n * Tracks whether the page runs installed and captures the browser's installation prompt. Browser-only; call it once,\n * when the page loads, under an owner that lives as long as the page: Chrome offers its prompt only once, early.\n */\nexport function createInstallPrompt(): InstallPrompt {\n const displayMode = matchMedia(\"(display-mode: standalone)\");\n const isStandalone = () => displayMode.matches || (\"standalone\" in navigator && navigator.standalone === true);\n const [installed, setInstalled] = createSignal(isStandalone());\n const [prompt, setPrompt] = createSignal<BeforeInstallPromptEvent>();\n const [busy, setBusy] = createSignal(false);\n const [requested, setRequested] = createSignal(false);\n const [failed, setFailed] = createSignal(false);\n const capture = (event: Event) => {\n if (!isInstallPrompt(event)) return;\n event.preventDefault();\n if (installed()) return;\n setPrompt(event);\n setRequested(false);\n };\n const complete = () => {\n setInstalled(true);\n setPrompt(undefined);\n };\n const displayChanged = () => {\n if (isStandalone()) complete();\n };\n window.addEventListener(\"beforeinstallprompt\", capture);\n window.addEventListener(\"appinstalled\", complete);\n displayMode.addEventListener(\"change\", displayChanged);\n onCleanup(() => {\n window.removeEventListener(\"beforeinstallprompt\", capture);\n window.removeEventListener(\"appinstalled\", complete);\n displayMode.removeEventListener(\"change\", displayChanged);\n });\n const install = async () => {\n if (busy() || installed()) return;\n const event = prompt();\n setFailed(false);\n if (!event) return;\n // Consume once, and invoke synchronously within the user's click.\n setPrompt(undefined);\n setBusy(true);\n try {\n const result = await event.prompt();\n if (!installed()) setRequested(result.outcome === \"accepted\");\n } catch {\n setFailed(true);\n } finally {\n setBusy(false);\n }\n };\n return {\n platform: installationPlatform(navigator.userAgent, navigator.platform, navigator.maxTouchPoints),\n installed,\n canPrompt: () => Boolean(prompt()),\n busy,\n requested,\n failed,\n install,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -48,6 +48,9 @@ export const createFormState = schema => {
48
48
  const messages = resolveUiMessages();
49
49
  const [values, setValues] = createStore({});
50
50
  const [errors, setErrors] = createStore({});
51
+ // Errors appear with the first submit and then follow every change. Before that, a check that fails mid-input,
52
+ // such as a URL still being typed, would interrupt the user with an alert on the first character.
53
+ let submitted = false;
51
54
  for (const [key, field] of Object.entries(schema)) {
52
55
  if (field.type !== "info" && "default" in field) setValues(key, field.default);
53
56
  }
@@ -80,9 +83,10 @@ export const createFormState = schema => {
80
83
  };
81
84
  const updateField = (key, value) => {
82
85
  setValues(key, value);
83
- setErrors(key, validateField(key, value) ?? undefined);
86
+ if (submitted) setErrors(key, validateField(key, value) ?? undefined);
84
87
  };
85
88
  const validateAll = () => {
89
+ submitted = true;
86
90
  let valid = true;
87
91
  for (const [key, field] of Object.entries(schema)) {
88
92
  if (field.type === "info") continue;
@@ -93,6 +97,7 @@ export const createFormState = schema => {
93
97
  return valid;
94
98
  };
95
99
  const reset = () => {
100
+ submitted = false;
96
101
  for (const [key, field] of Object.entries(schema)) {
97
102
  if (field.type === "info") continue;
98
103
  setValues(key, "default" in field ? field.default : undefined);
@@ -551,6 +556,7 @@ const openSearchPrompt = (resolver, options) => dialogCore.open(close => {
551
556
  initialFocus: "first-input",
552
557
  signal: options?.signal,
553
558
  cancelBehavior: options?.cancelBehavior,
559
+ history: options?.history,
554
560
  ariaLabel: options?.ariaLabel ?? options?.title ?? resolveUiMessages().search
555
561
  });
556
562
  const promptText = (content, defaultValue, options) => prompts.form({
@@ -597,6 +603,7 @@ export const prompts = {
597
603
  contentClassName: contentClass(options?.surface),
598
604
  signal: options?.signal,
599
605
  cancelBehavior: options?.cancelBehavior,
606
+ history: options?.history,
600
607
  ariaLabel: options?.ariaLabel ?? options?.title ?? resolveUiMessages().info
601
608
  }),
602
609
  success: (content, options) => prompts.alert(content, {
@@ -660,6 +667,7 @@ export const prompts = {
660
667
  initialFocus: confirmationPhrase ? () => confirmationInput ?? null : undefined,
661
668
  signal: options?.signal,
662
669
  cancelBehavior: options?.cancelBehavior,
670
+ history: options?.history,
663
671
  ariaLabel: options?.ariaLabel ?? options?.title ?? resolveUiMessages().confirmation
664
672
  });
665
673
  },
@@ -699,6 +707,7 @@ export const prompts = {
699
707
  contentClassName: contentClass(options?.surface),
700
708
  signal: options?.signal,
701
709
  cancelBehavior: options?.cancelBehavior,
710
+ history: options?.history,
702
711
  ariaLabel: options?.ariaLabel ?? options?.title ?? resolveUiMessages().dialog
703
712
  }),
704
713
  search: openSearchPrompt,
@@ -718,6 +727,7 @@ export const prompts = {
718
727
  contentClassName: contentClass(options?.surface),
719
728
  signal: options?.signal,
720
729
  cancelBehavior: options?.cancelBehavior,
730
+ history: options?.history,
721
731
  ariaLabel: options?.ariaLabel ?? options?.title ?? resolveUiMessages().error
722
732
  })
723
733
  };