@arraypress/extension-kit 1.1.0 → 1.2.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.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Chrome's toast (cr-toast), bottom left, as chrome://bookmarks shows one
3
+ * after a delete: "Deleted “Research” Undo". One per page, made by
4
+ * {@link createToast}; a new message replaces the last and restarts the wait.
5
+ *
6
+ * @module ui/toast
7
+ */
8
+
9
+ /** How long chrome://bookmarks shows a toast (its cr-toast-manager's `duration`), in ms. */
10
+ export const TOAST_MS = 10_000;
11
+
12
+ /**
13
+ * What {@link createToast} takes.
14
+ *
15
+ * @typedef {object} ToastOptions
16
+ * @property {string} [undoLabel] The undo button's text. Default "Undo".
17
+ * @property {number} [duration] How long a toast stays, in ms. Default {@link TOAST_MS}.
18
+ * @property {number} [undoDuration] How long one with Undo stays. Default `duration`.
19
+ * @property {Document} [doc] The page. Default `document`.
20
+ */
21
+
22
+ /**
23
+ * A page's toast.
24
+ *
25
+ * @typedef {object} Toast
26
+ * @property {HTMLElement} element The `#toast` element (`role="status"`), in the page's body.
27
+ * @property {(text: string, undo?: (() => unknown) | null) => void} show Show `text`, with an Undo
28
+ * button that hides the toast and runs `undo` when one is given.
29
+ * @property {() => void} hide Take it down (and its Undo with it).
30
+ */
31
+
32
+ /**
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.
36
+ *
37
+ * @param {ToastOptions} [options] - The Undo text, how long it stays, and the page.
38
+ * @returns {Toast} The toast.
39
+ * @example
40
+ * const toast = createToast();
41
+ * await deleteWorkspace(workspace);
42
+ * toast.show(`Deleted “${workspace.name}”`, () => restore(workspace));
43
+ */
44
+ export function createToast({ undoLabel = 'Undo', duration = TOAST_MS, undoDuration = duration, doc = document } = {}) {
45
+ const toast = doc.createElement('div');
46
+ toast.id = 'toast';
47
+ toast.className = 'toast';
48
+ toast.setAttribute('role', 'status');
49
+ toast.setAttribute('aria-live', 'polite');
50
+ toast.hidden = true;
51
+ const text = doc.createElement('span');
52
+ text.id = 'toast-text';
53
+ const undoButton = doc.createElement('button');
54
+ undoButton.id = 'toast-undo';
55
+ undoButton.type = 'button';
56
+ undoButton.textContent = undoLabel;
57
+ undoButton.hidden = true;
58
+ toast.append(text, undoButton);
59
+ doc.body.append(toast);
60
+
61
+ /** @type {ReturnType<typeof setTimeout> | undefined} */
62
+ let timer;
63
+ /** @type {(() => unknown) | null} */
64
+ let undoLast = null;
65
+
66
+ const hide = () => {
67
+ clearTimeout(timer);
68
+ toast.hidden = true;
69
+ undoLast = null;
70
+ };
71
+ undoButton.addEventListener('click', () => {
72
+ const undo = undoLast;
73
+ hide();
74
+ undo?.();
75
+ });
76
+
77
+ return {
78
+ element: toast,
79
+ show(message, undo = null) {
80
+ clearTimeout(timer);
81
+ text.textContent = message;
82
+ undoButton.hidden = !undo;
83
+ undoLast = undo;
84
+ toast.hidden = false;
85
+ timer = setTimeout(hide, undo ? undoDuration : duration);
86
+ },
87
+ hide,
88
+ };
89
+ }