@arraypress/extension-kit 1.2.0 → 1.3.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.
package/src/ui/toast.js CHANGED
@@ -3,9 +3,15 @@
3
3
  * after a delete: "Deleted “Research” Undo". One per page, made by
4
4
  * {@link createToast}; a new message replaces the last and restarts the wait.
5
5
  *
6
+ * It's a manual `popover`, so it shows in the top layer: over anything the
7
+ * page stacks (a content script's toast over a site's own fixed bars), and
8
+ * never clipped by an ancestor.
9
+ *
6
10
  * @module ui/toast
7
11
  */
8
12
 
13
+ import { rootDocument } from '../dom/focus.js';
14
+
9
15
  /** How long chrome://bookmarks shows a toast (its cr-toast-manager's `duration`), in ms. */
10
16
  export const TOAST_MS = 10_000;
11
17
 
@@ -16,38 +22,44 @@ export const TOAST_MS = 10_000;
16
22
  * @property {string} [undoLabel] The undo button's text. Default "Undo".
17
23
  * @property {number} [duration] How long a toast stays, in ms. Default {@link TOAST_MS}.
18
24
  * @property {number} [undoDuration] How long one with Undo stays. Default `duration`.
19
- * @property {Document} [doc] The page. Default `document`.
25
+ * @property {ParentNode} [root] Where the toast goes: an element, or a shadow root (a content
26
+ * script's). Default the page's `body`.
27
+ * @property {Document} [doc] The page. Default `root`'s document, else `document`.
20
28
  */
21
29
 
22
30
  /**
23
31
  * A page's toast.
24
32
  *
25
33
  * @typedef {object} Toast
26
- * @property {HTMLElement} element The `#toast` element (`role="status"`), in the page's body.
34
+ * @property {HTMLElement} element The `#toast` element (`role="status"`, `popover="manual"`), in
35
+ * `root`; `:popover-open` while it shows.
27
36
  * @property {(text: string, undo?: (() => unknown) | null) => void} show Show `text`, with an Undo
28
37
  * button that hides the toast and runs `undo` when one is given.
29
38
  * @property {() => void} hide Take it down (and its Undo with it).
30
39
  */
31
40
 
32
41
  /**
33
- * Make the page's toast: `<div id="toast" class="toast" role="status">` with
34
- * `#toast-text` and an Undo button `#toast-undo`, hidden until shown. Make it
35
- * when the page loads, so screen readers know the live region before it speaks.
42
+ * Make the page's toast: `<div id="toast" class="toast" role="status"
43
+ * popover="manual">` with `#toast-text` and an Undo button `#toast-undo`,
44
+ * closed until shown. Make it when the page loads, so screen readers know the
45
+ * live region before it speaks.
36
46
  *
37
- * @param {ToastOptions} [options] - The Undo text, how long it stays, and the page.
47
+ * @param {ToastOptions} [options] - The Undo text, how long it stays, and where it goes.
38
48
  * @returns {Toast} The toast.
39
49
  * @example
40
50
  * const toast = createToast();
41
51
  * await deleteWorkspace(workspace);
42
52
  * toast.show(`Deleted “${workspace.name}”`, () => restore(workspace));
53
+ * // In a content script's shadow root:
54
+ * createToast({ root: host.attachShadow({ mode: 'open' }), duration: 4000 });
43
55
  */
44
- export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDuration = duration, doc = document } = {}) {
56
+ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDuration = duration, root, doc = rootDocument(root) } = {}) {
45
57
  const toast = doc.createElement('div');
46
58
  toast.id = 'toast';
47
59
  toast.className = 'toast';
48
60
  toast.setAttribute('role', 'status');
49
61
  toast.setAttribute('aria-live', 'polite');
50
- toast.hidden = true;
62
+ toast.popover = 'manual';
51
63
  const text = doc.createElement('span');
52
64
  text.id = 'toast-text';
53
65
  const undoButton = doc.createElement('button');
@@ -56,7 +68,7 @@ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDurat
56
68
  undoButton.textContent = undoLabel;
57
69
  undoButton.hidden = true;
58
70
  toast.append(text, undoButton);
59
- doc.body.append(toast);
71
+ (root ?? doc.body).append(toast);
60
72
 
61
73
  /** @type {ReturnType<typeof setTimeout> | undefined} */
62
74
  let timer;
@@ -65,7 +77,7 @@ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDurat
65
77
 
66
78
  const hide = () => {
67
79
  clearTimeout(timer);
68
- toast.hidden = true;
80
+ toast.togglePopover(false);
69
81
  undoLast = null;
70
82
  };
71
83
  undoButton.addEventListener('click', () => {
@@ -81,7 +93,9 @@ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDurat
81
93
  text.textContent = message;
82
94
  undoButton.hidden = !undo;
83
95
  undoLast = undo;
84
- toast.hidden = false;
96
+ // Hidden and shown again, so it's the newest in the top layer (over a popover since).
97
+ toast.togglePopover(false);
98
+ toast.togglePopover(true);
85
99
  timer = setTimeout(hide, undo ? undoDuration : duration);
86
100
  },
87
101
  hide,