@jirawatpyk/aura-react 5.5.0 → 5.6.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/README.md +19 -2
- package/dist/aura.bundle.js +180 -38
- package/dist/cjs/index.cjs +180 -38
- package/dist/esm/DataTable.js +34 -12
- package/dist/esm/Toaster.js +148 -28
- package/dist/index.d.cts +31 -9
- package/dist/index.d.ts +31 -9
- package/dist/styles.css +25 -4
- package/dist/styles.layer.css +25 -4
- package/package.json +1 -1
package/dist/esm/Toaster.js
CHANGED
|
@@ -3,8 +3,8 @@ import * as React from "react";
|
|
|
3
3
|
import { ALERT_ICON } from "./Alert.js";
|
|
4
4
|
import { Icon } from "./Icon.js";
|
|
5
5
|
import { IconButton } from "./IconButton.js";
|
|
6
|
-
import { cx, useMounted } from "./internal.js";
|
|
7
|
-
import { useStrings } from "./locale.js";
|
|
6
|
+
import { cx, plainClick, useIsoLayoutEffect, useMounted } from "./internal.js";
|
|
7
|
+
import { useLinkComponent, useStrings } from "./locale.js";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
9
9
|
const MAX_VISIBLE = 3;
|
|
10
10
|
const toastState = {
|
|
@@ -73,10 +73,35 @@ toast.dismiss = function(id) {
|
|
|
73
73
|
promote();
|
|
74
74
|
emitToasts();
|
|
75
75
|
};
|
|
76
|
+
const hotkeyOrigin = /* @__PURE__ */ new WeakMap();
|
|
77
|
+
function outsideToaster(el) {
|
|
78
|
+
return el && el !== document.body && !el.closest(".aura-toaster") ? el : null;
|
|
79
|
+
}
|
|
76
80
|
function ToastItem(props) {
|
|
77
81
|
const str = useStrings();
|
|
82
|
+
const Link = useLinkComponent();
|
|
83
|
+
const node = React.useRef(null);
|
|
84
|
+
const origin = React.useRef(null);
|
|
85
|
+
useIsoLayoutEffect(function() {
|
|
86
|
+
origin.current = outsideToaster(document.activeElement);
|
|
87
|
+
const el = node.current;
|
|
88
|
+
return function() {
|
|
89
|
+
if (!el || !el.contains(document.activeElement)) return;
|
|
90
|
+
const back = [hotkeyOrigin.get(el), origin.current].filter(function(b) {
|
|
91
|
+
return !!b && b.isConnected;
|
|
92
|
+
})[0];
|
|
93
|
+
hotkeyOrigin.delete(el);
|
|
94
|
+
setTimeout(function() {
|
|
95
|
+
if (back && back.isConnected && (!document.activeElement || document.activeElement === document.body))
|
|
96
|
+
back.focus();
|
|
97
|
+
}, 0);
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
78
100
|
const t = props.toast, timer = React.useRef(null), left = React.useRef(t.duration || 5e3), since = React.useRef(0);
|
|
79
|
-
const hover = React.useRef(false);
|
|
101
|
+
const hover = React.useRef(false), focused = React.useRef(false);
|
|
102
|
+
function syncFocus() {
|
|
103
|
+
if (focused.current && !(node.current && node.current.contains(document.activeElement))) focused.current = false;
|
|
104
|
+
}
|
|
80
105
|
function start() {
|
|
81
106
|
if (left.current === Infinity || timer.current) return;
|
|
82
107
|
since.current = Date.now();
|
|
@@ -85,11 +110,10 @@ function ToastItem(props) {
|
|
|
85
110
|
}, left.current);
|
|
86
111
|
}
|
|
87
112
|
function resume() {
|
|
88
|
-
|
|
89
|
-
start();
|
|
113
|
+
syncFocus();
|
|
114
|
+
if (!hover.current && !focused.current) start();
|
|
90
115
|
}
|
|
91
116
|
function pause() {
|
|
92
|
-
hover.current = true;
|
|
93
117
|
if (timer.current) {
|
|
94
118
|
clearTimeout(timer.current);
|
|
95
119
|
timer.current = null;
|
|
@@ -101,23 +125,56 @@ function ToastItem(props) {
|
|
|
101
125
|
if (timer.current) clearTimeout(timer.current);
|
|
102
126
|
timer.current = null;
|
|
103
127
|
left.current = t.duration || 5e3;
|
|
104
|
-
|
|
128
|
+
syncFocus();
|
|
129
|
+
if (!hover.current && !focused.current) start();
|
|
105
130
|
return function() {
|
|
106
131
|
if (timer.current) clearTimeout(timer.current);
|
|
107
132
|
};
|
|
108
133
|
},
|
|
109
134
|
[t.rev]
|
|
110
135
|
);
|
|
136
|
+
const rich = t.description != null && typeof t.description !== "boolean" && t.description !== "";
|
|
137
|
+
const actions = (t.actions && t.actions.length ? t.actions : t.action ? [t.action] : []).slice(0, 2);
|
|
138
|
+
function run(a) {
|
|
139
|
+
if (a.onClick) a.onClick();
|
|
140
|
+
if (a.dismiss !== false) toast.dismiss(t.id);
|
|
141
|
+
}
|
|
111
142
|
return /* @__PURE__ */ React.createElement(
|
|
112
143
|
"div",
|
|
113
144
|
{
|
|
114
|
-
|
|
145
|
+
ref: node,
|
|
146
|
+
onKeyDown: function(e) {
|
|
147
|
+
if (e.key === "Escape") {
|
|
148
|
+
e.stopPropagation();
|
|
149
|
+
toast.dismiss(t.id);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
className: cx(
|
|
153
|
+
"aura-toast",
|
|
154
|
+
"aura-toast--" + t.tone,
|
|
155
|
+
t.loading && "is-loading",
|
|
156
|
+
/* Two actions, or one beside a description, go on their own row under the text so it keeps its width. */
|
|
157
|
+
(actions.length > 1 || actions.length && rich) && "has-action-row"
|
|
158
|
+
),
|
|
115
159
|
role: t.tone === "danger" ? "alert" : "status",
|
|
116
160
|
"aria-busy": t.loading || void 0,
|
|
117
|
-
onMouseEnter:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
161
|
+
onMouseEnter: function() {
|
|
162
|
+
hover.current = true;
|
|
163
|
+
pause();
|
|
164
|
+
},
|
|
165
|
+
onMouseLeave: function() {
|
|
166
|
+
hover.current = false;
|
|
167
|
+
resume();
|
|
168
|
+
},
|
|
169
|
+
onFocus: function() {
|
|
170
|
+
focused.current = true;
|
|
171
|
+
pause();
|
|
172
|
+
},
|
|
173
|
+
onBlur: function(e) {
|
|
174
|
+
if (node.current && e.relatedTarget && node.current.contains(e.relatedTarget)) return;
|
|
175
|
+
focused.current = false;
|
|
176
|
+
resume();
|
|
177
|
+
}
|
|
121
178
|
},
|
|
122
179
|
/* @__PURE__ */ React.createElement(
|
|
123
180
|
Icon,
|
|
@@ -126,19 +183,33 @@ function ToastItem(props) {
|
|
|
126
183
|
className: cx("aura-toast__icon", t.loading && "aura-spin")
|
|
127
184
|
}
|
|
128
185
|
),
|
|
129
|
-
/* @__PURE__ */ React.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React.createElement("p", { className: "aura-toast__title" }, t.title),
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
186
|
+
/* @__PURE__ */ React.createElement("div", { className: "aura-toast__body" }, /* @__PURE__ */ React.createElement("p", { className: "aura-toast__title" }, t.title), rich ? /* @__PURE__ */ React.createElement("div", { className: "aura-toast__text" }, t.description) : null),
|
|
187
|
+
actions.length ? /* @__PURE__ */ React.createElement("div", { className: "aura-toast__actions" }, actions.map(function(a, i) {
|
|
188
|
+
return a.href ? /* @__PURE__ */ React.createElement(
|
|
189
|
+
Link,
|
|
190
|
+
{
|
|
191
|
+
key: i,
|
|
192
|
+
href: a.href,
|
|
193
|
+
className: "aura-toast__action",
|
|
194
|
+
onClick: function(e) {
|
|
195
|
+
if (plainClick(e)) run(a);
|
|
196
|
+
else if (a.onClick) a.onClick();
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
a.label
|
|
200
|
+
) : /* @__PURE__ */ React.createElement(
|
|
201
|
+
"button",
|
|
202
|
+
{
|
|
203
|
+
key: i,
|
|
204
|
+
type: "button",
|
|
205
|
+
className: "aura-toast__action",
|
|
206
|
+
onClick: function() {
|
|
207
|
+
run(a);
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
a.label
|
|
211
|
+
);
|
|
212
|
+
})) : null,
|
|
142
213
|
/* @__PURE__ */ React.createElement(
|
|
143
214
|
IconButton,
|
|
144
215
|
{
|
|
@@ -152,7 +223,22 @@ function ToastItem(props) {
|
|
|
152
223
|
)
|
|
153
224
|
);
|
|
154
225
|
}
|
|
155
|
-
function
|
|
226
|
+
function hotkeyMatcher(spec) {
|
|
227
|
+
const parts = spec.split("+").map(function(p) {
|
|
228
|
+
return p.trim().toLowerCase();
|
|
229
|
+
});
|
|
230
|
+
const key = parts[parts.length - 1] || "";
|
|
231
|
+
const code = /^[a-z]$/.test(key) ? "Key" + key.toUpperCase() : /^[0-9]$/.test(key) ? "Digit" + key : null;
|
|
232
|
+
const want = function(m) {
|
|
233
|
+
return parts.indexOf(m) >= 0 && parts.indexOf(m) < parts.length - 1;
|
|
234
|
+
};
|
|
235
|
+
return function(e) {
|
|
236
|
+
if (e.altKey !== want("alt") || e.ctrlKey !== want("ctrl") || e.shiftKey !== want("shift") || e.metaKey !== (want("meta") || want("cmd")))
|
|
237
|
+
return false;
|
|
238
|
+
return code ? e.code === code : e.key.toLowerCase() === key;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function Toaster(props = {}) {
|
|
156
242
|
const t = useStrings();
|
|
157
243
|
const mounted = useMounted();
|
|
158
244
|
const s = React.useState(toastState.list);
|
|
@@ -165,15 +251,49 @@ function Toaster(props) {
|
|
|
165
251
|
});
|
|
166
252
|
};
|
|
167
253
|
}, []);
|
|
254
|
+
const region = React.useRef(null);
|
|
255
|
+
const hotkey = props.hotkey === void 0 ? "Alt+T" : props.hotkey;
|
|
256
|
+
React.useEffect(
|
|
257
|
+
function() {
|
|
258
|
+
if (!hotkey) return;
|
|
259
|
+
const match = hotkeyMatcher(hotkey);
|
|
260
|
+
function onKey(e) {
|
|
261
|
+
if (!e.key || e.repeat || e.isComposing || !match(e) || !region.current) return;
|
|
262
|
+
const el = e.target;
|
|
263
|
+
const editable = !!el && (el.isContentEditable || /^(INPUT|TEXTAREA)$/.test(el.tagName || ""));
|
|
264
|
+
const mac = /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent || "");
|
|
265
|
+
if (editable && mac && e.altKey && !e.ctrlKey && !e.metaKey && e.key.length === 1) return;
|
|
266
|
+
const items = region.current.querySelectorAll(".aura-toast");
|
|
267
|
+
const last = items[items.length - 1];
|
|
268
|
+
if (!last) return;
|
|
269
|
+
const target = last.querySelector(".aura-toast__action") || last.querySelector("button");
|
|
270
|
+
if (!target) return;
|
|
271
|
+
e.preventDefault();
|
|
272
|
+
const from = outsideToaster(document.activeElement);
|
|
273
|
+
if (from) hotkeyOrigin.set(last, from);
|
|
274
|
+
target.focus();
|
|
275
|
+
}
|
|
276
|
+
document.addEventListener("keydown", onKey);
|
|
277
|
+
return function() {
|
|
278
|
+
document.removeEventListener("keydown", onKey);
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
[hotkey]
|
|
282
|
+
);
|
|
168
283
|
if (!mounted) return null;
|
|
284
|
+
const pos = props.position || "bottom";
|
|
285
|
+
const offset = props.offset;
|
|
169
286
|
return createPortal(
|
|
170
287
|
/* @__PURE__ */ React.createElement(
|
|
171
288
|
"div",
|
|
172
289
|
{
|
|
173
|
-
|
|
290
|
+
ref: region,
|
|
291
|
+
className: cx("aura-toaster", pos.indexOf("top") === 0 && "is-top", /center$/.test(pos) && "is-center"),
|
|
292
|
+
style: offset != null ? { "--aura-toaster-offset": typeof offset === "number" ? offset + "px" : offset } : void 0,
|
|
174
293
|
role: "region",
|
|
175
294
|
"aria-live": "polite",
|
|
176
|
-
"aria-label": t.notifications
|
|
295
|
+
"aria-label": hotkey ? t.notifications + " (" + hotkey + ")" : t.notifications,
|
|
296
|
+
"aria-keyshortcuts": hotkey || void 0
|
|
177
297
|
},
|
|
178
298
|
s[0].map(function(t2) {
|
|
179
299
|
return /* @__PURE__ */ React.createElement(ToastItem, { key: t2.id, toast: t2 });
|
package/dist/index.d.cts
CHANGED
|
@@ -124,6 +124,11 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
|
|
|
124
124
|
onSortChange?: ((sort: DataTableSort | null) => void) | undefined;
|
|
125
125
|
/** Adds a checkbox column. */
|
|
126
126
|
selectable?: boolean | undefined;
|
|
127
|
+
/** Which rows can be selected (5.6). A row it rejects shows no checkbox (the cell stays, so columns line up), is
|
|
128
|
+
* skipped by select-all, Space and the count, and never appears in `onSelectionChange` — even if passed in. */
|
|
129
|
+
isRowSelectable?: RowCallback<boolean, Row> | undefined;
|
|
130
|
+
/** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
|
|
131
|
+
rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
|
|
127
132
|
/** Controlled selection (row keys); pair with onSelectionChange. */
|
|
128
133
|
selected?: Array<string | number> | undefined;
|
|
129
134
|
/** Initial selection when uncontrolled. */
|
|
@@ -487,14 +492,23 @@ export interface AlertProps {
|
|
|
487
492
|
onDismiss?: (() => void) | undefined;
|
|
488
493
|
className?: string | undefined;
|
|
489
494
|
}
|
|
495
|
+
/** A button (or, with `href`, a link through AuraProvider's `linkComponent`) on a toast (5.6: `href`, `dismiss`). */
|
|
496
|
+
export interface ToastAction {
|
|
497
|
+
label: string;
|
|
498
|
+
onClick?: (() => void) | undefined;
|
|
499
|
+
/** Makes the action a link (routes through `linkComponent`, so `next/link` navigates client-side). */
|
|
500
|
+
href?: string | undefined;
|
|
501
|
+
/** Default true: the toast closes when the action runs. `false` keeps it (a list of items to work through). */
|
|
502
|
+
dismiss?: boolean | undefined;
|
|
503
|
+
}
|
|
490
504
|
export interface ToastOptions {
|
|
491
505
|
title: string;
|
|
492
|
-
|
|
506
|
+
/** Text, or (5.6) rich content — lines with their own links, read once inside the toast's live region. */
|
|
507
|
+
description?: React$1.ReactNode | undefined;
|
|
493
508
|
tone?: FeedbackTone | undefined;
|
|
494
|
-
action?:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
} | undefined;
|
|
509
|
+
action?: ToastAction | undefined;
|
|
510
|
+
/** Up to two actions in a row (5.6); used instead of `action` when given. */
|
|
511
|
+
actions?: ToastAction[] | undefined;
|
|
498
512
|
/** ms before it closes itself. Default 5000; Infinity keeps it until dismissed. */
|
|
499
513
|
duration?: number | undefined;
|
|
500
514
|
/** Reuse an id to replace a toast in place (same position, timer restarts) — never a second toast. */
|
|
@@ -1285,10 +1299,18 @@ export declare namespace toast {
|
|
|
1285
1299
|
var dismiss: (id: string) => void;
|
|
1286
1300
|
}
|
|
1287
1301
|
export interface ToasterProps {
|
|
1288
|
-
/** Default `bottom
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1302
|
+
/** Default `bottom` (bottom right). `top` is top right; `top-center` / `bottom-center` centre the stack (5.6).
|
|
1303
|
+
* Below 640px toasts span the width either way. */
|
|
1304
|
+
position?: "bottom" | "bottom-right" | "bottom-center" | "top" | "top-right" | "top-center" | undefined;
|
|
1305
|
+
/** Distance from the top or bottom edge (5.6), e.g. `64` to clear a 56px top bar; px or any CSS length. Sets
|
|
1306
|
+
* `--aura-toaster-offset`; the safe-area inset is added on top. Default 24px (16px below 640px). */
|
|
1307
|
+
offset?: number | string | undefined;
|
|
1308
|
+
/** Keyboard shortcut to the newest toast (5.6): focuses its action, else its close button, and pauses its timer;
|
|
1309
|
+
* Escape closes it and returns focus. Default `"Alt+T"` (matched on the physical key, so macOS Option+T works);
|
|
1310
|
+
* `false` turns it off. */
|
|
1311
|
+
hotkey?: string | false | undefined;
|
|
1312
|
+
}
|
|
1313
|
+
export declare function Toaster(props?: ToasterProps): React$1.ReactElement | null;
|
|
1292
1314
|
export declare const PasswordField: React$1.ForwardRefExoticComponent<PasswordFieldProps & React$1.RefAttributes<HTMLInputElement>>;
|
|
1293
1315
|
export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErrorSummaryProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1294
1316
|
export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -124,6 +124,11 @@ export interface DataTableProps<Row extends Record<string, any> = Record<string,
|
|
|
124
124
|
onSortChange?: ((sort: DataTableSort | null) => void) | undefined;
|
|
125
125
|
/** Adds a checkbox column. */
|
|
126
126
|
selectable?: boolean | undefined;
|
|
127
|
+
/** Which rows can be selected (5.6). A row it rejects shows no checkbox (the cell stays, so columns line up), is
|
|
128
|
+
* skipped by select-all, Space and the count, and never appears in `onSelectionChange` — even if passed in. */
|
|
129
|
+
isRowSelectable?: RowCallback<boolean, Row> | undefined;
|
|
130
|
+
/** Accessible name for a rejected row's empty cell, e.g. `(r) => 'Not awaiting review'` (5.6). */
|
|
131
|
+
rowSelectDisabledLabel?: RowCallback<string, Row> | undefined;
|
|
127
132
|
/** Controlled selection (row keys); pair with onSelectionChange. */
|
|
128
133
|
selected?: Array<string | number> | undefined;
|
|
129
134
|
/** Initial selection when uncontrolled. */
|
|
@@ -487,14 +492,23 @@ export interface AlertProps {
|
|
|
487
492
|
onDismiss?: (() => void) | undefined;
|
|
488
493
|
className?: string | undefined;
|
|
489
494
|
}
|
|
495
|
+
/** A button (or, with `href`, a link through AuraProvider's `linkComponent`) on a toast (5.6: `href`, `dismiss`). */
|
|
496
|
+
export interface ToastAction {
|
|
497
|
+
label: string;
|
|
498
|
+
onClick?: (() => void) | undefined;
|
|
499
|
+
/** Makes the action a link (routes through `linkComponent`, so `next/link` navigates client-side). */
|
|
500
|
+
href?: string | undefined;
|
|
501
|
+
/** Default true: the toast closes when the action runs. `false` keeps it (a list of items to work through). */
|
|
502
|
+
dismiss?: boolean | undefined;
|
|
503
|
+
}
|
|
490
504
|
export interface ToastOptions {
|
|
491
505
|
title: string;
|
|
492
|
-
|
|
506
|
+
/** Text, or (5.6) rich content — lines with their own links, read once inside the toast's live region. */
|
|
507
|
+
description?: React$1.ReactNode | undefined;
|
|
493
508
|
tone?: FeedbackTone | undefined;
|
|
494
|
-
action?:
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
} | undefined;
|
|
509
|
+
action?: ToastAction | undefined;
|
|
510
|
+
/** Up to two actions in a row (5.6); used instead of `action` when given. */
|
|
511
|
+
actions?: ToastAction[] | undefined;
|
|
498
512
|
/** ms before it closes itself. Default 5000; Infinity keeps it until dismissed. */
|
|
499
513
|
duration?: number | undefined;
|
|
500
514
|
/** Reuse an id to replace a toast in place (same position, timer restarts) — never a second toast. */
|
|
@@ -1285,10 +1299,18 @@ export declare namespace toast {
|
|
|
1285
1299
|
var dismiss: (id: string) => void;
|
|
1286
1300
|
}
|
|
1287
1301
|
export interface ToasterProps {
|
|
1288
|
-
/** Default `bottom
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1302
|
+
/** Default `bottom` (bottom right). `top` is top right; `top-center` / `bottom-center` centre the stack (5.6).
|
|
1303
|
+
* Below 640px toasts span the width either way. */
|
|
1304
|
+
position?: "bottom" | "bottom-right" | "bottom-center" | "top" | "top-right" | "top-center" | undefined;
|
|
1305
|
+
/** Distance from the top or bottom edge (5.6), e.g. `64` to clear a 56px top bar; px or any CSS length. Sets
|
|
1306
|
+
* `--aura-toaster-offset`; the safe-area inset is added on top. Default 24px (16px below 640px). */
|
|
1307
|
+
offset?: number | string | undefined;
|
|
1308
|
+
/** Keyboard shortcut to the newest toast (5.6): focuses its action, else its close button, and pauses its timer;
|
|
1309
|
+
* Escape closes it and returns focus. Default `"Alt+T"` (matched on the physical key, so macOS Option+T works);
|
|
1310
|
+
* `false` turns it off. */
|
|
1311
|
+
hotkey?: string | false | undefined;
|
|
1312
|
+
}
|
|
1313
|
+
export declare function Toaster(props?: ToasterProps): React$1.ReactElement | null;
|
|
1292
1314
|
export declare const PasswordField: React$1.ForwardRefExoticComponent<PasswordFieldProps & React$1.RefAttributes<HTMLInputElement>>;
|
|
1293
1315
|
export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErrorSummaryProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1294
1316
|
export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
|
package/dist/styles.css
CHANGED
|
@@ -496,7 +496,9 @@ a.aura-icon-btn { text-decoration: none; }
|
|
|
496
496
|
display: flex; flex-direction: column; gap: var(--aura-space-2); width: min(380px, calc(100vw - 2 * var(--aura-space-4)));
|
|
497
497
|
font-family: var(--font-sans);
|
|
498
498
|
}
|
|
499
|
-
.aura-toaster.is-top { bottom: auto; top: var(--aura-space-6); }
|
|
499
|
+
.aura-toaster.is-top { bottom: auto; top: calc(var(--aura-toaster-offset, var(--aura-space-6)) + env(safe-area-inset-top, 0px)); }
|
|
500
|
+
/* 5.6: top-center / bottom-center; `offset` (or --aura-toaster-offset) clears an app's top bar. */
|
|
501
|
+
.aura-toaster.is-center { right: auto; left: 50%; transform: translateX(-50%); }
|
|
500
502
|
.aura-toast {
|
|
501
503
|
display: flex; align-items: flex-start; gap: var(--aura-space-3); padding: var(--aura-space-3) var(--aura-space-2) var(--aura-space-3) var(--aura-space-4);
|
|
502
504
|
background: var(--aura-bg-surface); color: var(--aura-fg-primary);
|
|
@@ -511,10 +513,27 @@ a.aura-icon-btn { text-decoration: none; }
|
|
|
511
513
|
.aura-toast__body { flex: 1; min-width: 0; padding-top: 1px; }
|
|
512
514
|
.aura-toast__title { margin: 0; font-size: 14px; font-weight: 500; line-height: 20px; }
|
|
513
515
|
.aura-toast__text { margin: 2px 0 0; font-size: 13px; line-height: 1.5; color: var(--aura-fg-secondary); }
|
|
516
|
+
.aura-toast__text > * { margin: 0; }
|
|
517
|
+
.aura-toast__text a { color: var(--aura-fg-accent); text-underline-offset: 2px; }
|
|
518
|
+
.aura-toast__text a:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-radius: var(--aura-radius-xs); }
|
|
519
|
+
.aura-toast__actions { flex: none; align-self: center; display: flex; gap: var(--aura-space-1); }
|
|
520
|
+
/* Two actions, or one under a description (5.6): the actions take a row of their own below the text, lined up with it
|
|
521
|
+
* (the action's own padding sits left of the text column), so the text keeps the toast's width. */
|
|
522
|
+
.aura-toast.has-action-row { flex-wrap: wrap; row-gap: var(--aura-space-1); }
|
|
523
|
+
.aura-toast.has-action-row .aura-toast__body { flex-basis: 0; }
|
|
524
|
+
.aura-toast.has-action-row .aura-toast__actions { order: 3; flex-basis: 100%; flex-wrap: wrap; margin-left: calc(16px + var(--aura-space-3) - var(--aura-space-2)); margin-bottom: calc(-1 * var(--aura-space-1)); }
|
|
514
525
|
.aura-toast__action {
|
|
515
526
|
flex: none; align-self: center; height: var(--aura-control-sm); padding: 0 var(--aura-space-2);
|
|
516
527
|
border: 0; border-radius: var(--aura-radius-sm); background: transparent; color: var(--aura-fg-accent);
|
|
517
528
|
font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
|
|
529
|
+
display: inline-flex; align-items: center; text-decoration: none; white-space: nowrap;
|
|
530
|
+
}
|
|
531
|
+
/* Touch (5.6): the 32px action gets a 44px hit area, as IconButton does. Links in the text only get a little padding —
|
|
532
|
+
* lines sit 20px apart, so a 44px box would overlap the next line's link. */
|
|
533
|
+
@media (pointer: coarse) {
|
|
534
|
+
.aura-toast__action { position: relative; }
|
|
535
|
+
.aura-toast__action::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, var(--aura-touch-target)); height: max(100%, var(--aura-touch-target)); transform: translate(-50%, -50%); }
|
|
536
|
+
.aura-toast__text a { padding-block: 2px; }
|
|
518
537
|
}
|
|
519
538
|
.aura-toast__action:hover { background: var(--aura-bg-surface-hover); }
|
|
520
539
|
.aura-toast__action:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 0; }
|
|
@@ -1203,6 +1222,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1203
1222
|
.aura-dialog__foot > * { width: 100%; }
|
|
1204
1223
|
.aura-drawer:not(.aura-drawer--nav) { max-width: none; }
|
|
1205
1224
|
.aura-toaster { left: var(--aura-space-4); right: var(--aura-space-4); bottom: var(--aura-space-4); width: auto; }
|
|
1225
|
+
.aura-toaster.is-center { left: var(--aura-space-4); right: var(--aura-space-4); transform: none; }
|
|
1226
|
+
.aura-toaster.is-top { top: calc(var(--aura-toaster-offset, var(--aura-space-4)) + env(safe-area-inset-top, 0px)); }
|
|
1206
1227
|
.aura-card { padding: var(--aura-space-4); }
|
|
1207
1228
|
.aura-stat { padding: var(--aura-space-4); }
|
|
1208
1229
|
.aura-stat__value { font-size: 24px; }
|
|
@@ -1385,9 +1406,9 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1385
1406
|
@media (max-width: 767.98px) { :root:has(.aura-bottomnav--below-md) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1386
1407
|
@media (max-width: 1023.98px) { :root:has(.aura-bottomnav--below-lg) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1387
1408
|
@media (max-width: 1279.98px) { :root:has(.aura-bottomnav--below-xl) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1388
|
-
/* Toasts clear a BottomNav wherever it shows (phones and tablets). */
|
|
1389
|
-
.aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-6) + var(--aura-bottomnav-offset, 0px)); }
|
|
1390
|
-
@media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-4) + var(--aura-bottomnav-offset, 0px)); } }
|
|
1409
|
+
/* Toasts clear a BottomNav wherever it shows (phones and tablets), else the bottom safe-area inset (5.6). */
|
|
1410
|
+
.aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-6)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); }
|
|
1411
|
+
@media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-4)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); } }
|
|
1391
1412
|
|
|
1392
1413
|
/* ---------- 4.19: ActionBar ---------- */
|
|
1393
1414
|
/* Sticky, not fixed: it keeps its place at the end of its parent, so it never covers the last field. */
|
package/dist/styles.layer.css
CHANGED
|
@@ -498,7 +498,9 @@ a.aura-icon-btn { text-decoration: none; }
|
|
|
498
498
|
display: flex; flex-direction: column; gap: var(--aura-space-2); width: min(380px, calc(100vw - 2 * var(--aura-space-4)));
|
|
499
499
|
font-family: var(--font-sans);
|
|
500
500
|
}
|
|
501
|
-
.aura-toaster.is-top { bottom: auto; top: var(--aura-space-6); }
|
|
501
|
+
.aura-toaster.is-top { bottom: auto; top: calc(var(--aura-toaster-offset, var(--aura-space-6)) + env(safe-area-inset-top, 0px)); }
|
|
502
|
+
/* 5.6: top-center / bottom-center; `offset` (or --aura-toaster-offset) clears an app's top bar. */
|
|
503
|
+
.aura-toaster.is-center { right: auto; left: 50%; transform: translateX(-50%); }
|
|
502
504
|
.aura-toast {
|
|
503
505
|
display: flex; align-items: flex-start; gap: var(--aura-space-3); padding: var(--aura-space-3) var(--aura-space-2) var(--aura-space-3) var(--aura-space-4);
|
|
504
506
|
background: var(--aura-bg-surface); color: var(--aura-fg-primary);
|
|
@@ -513,10 +515,27 @@ a.aura-icon-btn { text-decoration: none; }
|
|
|
513
515
|
.aura-toast__body { flex: 1; min-width: 0; padding-top: 1px; }
|
|
514
516
|
.aura-toast__title { margin: 0; font-size: 14px; font-weight: 500; line-height: 20px; }
|
|
515
517
|
.aura-toast__text { margin: 2px 0 0; font-size: 13px; line-height: 1.5; color: var(--aura-fg-secondary); }
|
|
518
|
+
.aura-toast__text > * { margin: 0; }
|
|
519
|
+
.aura-toast__text a { color: var(--aura-fg-accent); text-underline-offset: 2px; }
|
|
520
|
+
.aura-toast__text a:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-radius: var(--aura-radius-xs); }
|
|
521
|
+
.aura-toast__actions { flex: none; align-self: center; display: flex; gap: var(--aura-space-1); }
|
|
522
|
+
/* Two actions, or one under a description (5.6): the actions take a row of their own below the text, lined up with it
|
|
523
|
+
* (the action's own padding sits left of the text column), so the text keeps the toast's width. */
|
|
524
|
+
.aura-toast.has-action-row { flex-wrap: wrap; row-gap: var(--aura-space-1); }
|
|
525
|
+
.aura-toast.has-action-row .aura-toast__body { flex-basis: 0; }
|
|
526
|
+
.aura-toast.has-action-row .aura-toast__actions { order: 3; flex-basis: 100%; flex-wrap: wrap; margin-left: calc(16px + var(--aura-space-3) - var(--aura-space-2)); margin-bottom: calc(-1 * var(--aura-space-1)); }
|
|
516
527
|
.aura-toast__action {
|
|
517
528
|
flex: none; align-self: center; height: var(--aura-control-sm); padding: 0 var(--aura-space-2);
|
|
518
529
|
border: 0; border-radius: var(--aura-radius-sm); background: transparent; color: var(--aura-fg-accent);
|
|
519
530
|
font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
|
|
531
|
+
display: inline-flex; align-items: center; text-decoration: none; white-space: nowrap;
|
|
532
|
+
}
|
|
533
|
+
/* Touch (5.6): the 32px action gets a 44px hit area, as IconButton does. Links in the text only get a little padding —
|
|
534
|
+
* lines sit 20px apart, so a 44px box would overlap the next line's link. */
|
|
535
|
+
@media (pointer: coarse) {
|
|
536
|
+
.aura-toast__action { position: relative; }
|
|
537
|
+
.aura-toast__action::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, var(--aura-touch-target)); height: max(100%, var(--aura-touch-target)); transform: translate(-50%, -50%); }
|
|
538
|
+
.aura-toast__text a { padding-block: 2px; }
|
|
520
539
|
}
|
|
521
540
|
.aura-toast__action:hover { background: var(--aura-bg-surface-hover); }
|
|
522
541
|
.aura-toast__action:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 0; }
|
|
@@ -1205,6 +1224,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1205
1224
|
.aura-dialog__foot > * { width: 100%; }
|
|
1206
1225
|
.aura-drawer:not(.aura-drawer--nav) { max-width: none; }
|
|
1207
1226
|
.aura-toaster { left: var(--aura-space-4); right: var(--aura-space-4); bottom: var(--aura-space-4); width: auto; }
|
|
1227
|
+
.aura-toaster.is-center { left: var(--aura-space-4); right: var(--aura-space-4); transform: none; }
|
|
1228
|
+
.aura-toaster.is-top { top: calc(var(--aura-toaster-offset, var(--aura-space-4)) + env(safe-area-inset-top, 0px)); }
|
|
1208
1229
|
.aura-card { padding: var(--aura-space-4); }
|
|
1209
1230
|
.aura-stat { padding: var(--aura-space-4); }
|
|
1210
1231
|
.aura-stat__value { font-size: 24px; }
|
|
@@ -1387,9 +1408,9 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1387
1408
|
@media (max-width: 767.98px) { :root:has(.aura-bottomnav--below-md) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1388
1409
|
@media (max-width: 1023.98px) { :root:has(.aura-bottomnav--below-lg) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1389
1410
|
@media (max-width: 1279.98px) { :root:has(.aura-bottomnav--below-xl) { --aura-bottomnav-offset: calc(var(--aura-bottomnav-height) + env(safe-area-inset-bottom, 0px)); } }
|
|
1390
|
-
/* Toasts clear a BottomNav wherever it shows (phones and tablets). */
|
|
1391
|
-
.aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-6) + var(--aura-bottomnav-offset, 0px)); }
|
|
1392
|
-
@media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-space-4) + var(--aura-bottomnav-offset, 0px)); } }
|
|
1411
|
+
/* Toasts clear a BottomNav wherever it shows (phones and tablets), else the bottom safe-area inset (5.6). */
|
|
1412
|
+
.aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-6)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); }
|
|
1413
|
+
@media (max-width: 639.98px) { .aura-toaster:not(.is-top) { bottom: calc(var(--aura-toaster-offset, var(--aura-space-4)) + var(--aura-bottomnav-offset, env(safe-area-inset-bottom, 0px))); } }
|
|
1393
1414
|
|
|
1394
1415
|
/* ---------- 4.19: ActionBar ---------- */
|
|
1395
1416
|
/* Sticky, not fixed: it keeps its place at the end of its parent, so it never covers the last field. */
|
package/package.json
CHANGED