@astralui/core 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +145 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +145 -37
- package/dist/index.js.map +1 -1
- package/dist/styles.css +15 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -167,48 +167,139 @@ function AstralThemeProvider({ colors, children }) {
|
|
|
167
167
|
children
|
|
168
168
|
] });
|
|
169
169
|
}
|
|
170
|
-
|
|
170
|
+
var dialogSeq = 0;
|
|
171
|
+
var dialogStack = [];
|
|
172
|
+
var FOCUSABLE = [
|
|
173
|
+
"a[href]",
|
|
174
|
+
"area[href]",
|
|
175
|
+
"button:not([disabled])",
|
|
176
|
+
'input:not([disabled]):not([type="hidden"])',
|
|
177
|
+
"select:not([disabled])",
|
|
178
|
+
"textarea:not([disabled])",
|
|
179
|
+
"iframe",
|
|
180
|
+
"object",
|
|
181
|
+
"embed",
|
|
182
|
+
"[contenteditable]",
|
|
183
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
184
|
+
].join(",");
|
|
185
|
+
function useDialog(opened, onClose) {
|
|
186
|
+
const panelRef = useRef(null);
|
|
187
|
+
const idRef = useRef("");
|
|
188
|
+
if (!idRef.current) idRef.current = "au-dialog-title-" + String(++dialogSeq);
|
|
189
|
+
const onCloseRef = useRef(onClose);
|
|
190
|
+
onCloseRef.current = onClose;
|
|
171
191
|
useEffect(() => {
|
|
172
192
|
if (!opened) return;
|
|
193
|
+
const panel = panelRef.current;
|
|
194
|
+
if (!panel) return;
|
|
195
|
+
const restoreTo = document.activeElement;
|
|
196
|
+
dialogStack.push(panel);
|
|
197
|
+
const isTop = () => dialogStack[dialogStack.length - 1] === panel;
|
|
198
|
+
const focusables = () => Array.from(panel.querySelectorAll(FOCUSABLE)).filter(
|
|
199
|
+
(el) => el.offsetWidth > 0 || el.offsetHeight > 0 || el === document.activeElement
|
|
200
|
+
);
|
|
201
|
+
const raf = requestAnimationFrame(() => {
|
|
202
|
+
if (panel.contains(document.activeElement)) return;
|
|
203
|
+
const list = focusables();
|
|
204
|
+
const target = list.find((el) => !el.classList.contains("au-modal-x")) ?? list[0] ?? panel;
|
|
205
|
+
target.focus();
|
|
206
|
+
});
|
|
173
207
|
const onKey = (e) => {
|
|
174
|
-
if (
|
|
208
|
+
if (!isTop()) return;
|
|
209
|
+
if (e.key === "Escape") {
|
|
210
|
+
onCloseRef.current();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (e.key !== "Tab") return;
|
|
214
|
+
const list = focusables();
|
|
215
|
+
if (!list.length) {
|
|
216
|
+
e.preventDefault();
|
|
217
|
+
panel.focus();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const first = list[0];
|
|
221
|
+
const last = list[list.length - 1];
|
|
222
|
+
const active = document.activeElement;
|
|
223
|
+
if (!panel.contains(active)) {
|
|
224
|
+
e.preventDefault();
|
|
225
|
+
(e.shiftKey ? last : first).focus();
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (e.shiftKey && active === first) {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
last.focus();
|
|
231
|
+
} else if (!e.shiftKey && active === last) {
|
|
232
|
+
e.preventDefault();
|
|
233
|
+
first.focus();
|
|
234
|
+
}
|
|
175
235
|
};
|
|
176
|
-
document.addEventListener("keydown", onKey);
|
|
177
|
-
const
|
|
236
|
+
document.addEventListener("keydown", onKey, true);
|
|
237
|
+
const prevOverflow = document.body.style.overflow;
|
|
178
238
|
document.body.style.overflow = "hidden";
|
|
179
239
|
return () => {
|
|
180
|
-
|
|
181
|
-
document.
|
|
240
|
+
cancelAnimationFrame(raf);
|
|
241
|
+
document.removeEventListener("keydown", onKey, true);
|
|
242
|
+
const at = dialogStack.indexOf(panel);
|
|
243
|
+
if (at !== -1) dialogStack.splice(at, 1);
|
|
244
|
+
document.body.style.overflow = prevOverflow;
|
|
245
|
+
if (restoreTo && document.contains(restoreTo)) restoreTo.focus();
|
|
182
246
|
};
|
|
183
|
-
}, [opened
|
|
247
|
+
}, [opened]);
|
|
248
|
+
return { panelRef, titleId: idRef.current };
|
|
184
249
|
}
|
|
185
250
|
function AstralModal({ opened, onClose, title, brand, width = 460, zIndex, children }) {
|
|
186
|
-
|
|
251
|
+
const { panelRef, titleId } = useDialog(opened, onClose);
|
|
187
252
|
if (!opened) return null;
|
|
188
253
|
const style = { maxWidth: width, ...brand ? { "--au-ic": brand } : {} };
|
|
189
254
|
return createPortal(
|
|
190
|
-
/* @__PURE__ */ jsx("div", { className: "au-modal-overlay", onMouseDown: onClose, style: zIndex != null ? { zIndex } : void 0, children: /* @__PURE__ */ jsxs(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
255
|
+
/* @__PURE__ */ jsx("div", { className: "au-modal-overlay", onMouseDown: onClose, style: zIndex != null ? { zIndex } : void 0, children: /* @__PURE__ */ jsxs(
|
|
256
|
+
"div",
|
|
257
|
+
{
|
|
258
|
+
ref: panelRef,
|
|
259
|
+
className: "au-modal-panel",
|
|
260
|
+
style,
|
|
261
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
262
|
+
role: "dialog",
|
|
263
|
+
"aria-modal": "true",
|
|
264
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
265
|
+
tabIndex: -1,
|
|
266
|
+
children: [
|
|
267
|
+
/* @__PURE__ */ jsxs("div", { className: "au-modal-head", children: [
|
|
268
|
+
/* @__PURE__ */ jsx("span", { className: "au-modal-title", id: titleId, children: title }),
|
|
269
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "au-modal-x", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, {}) })
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ jsx("div", { className: "au-modal-body", children })
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
) }),
|
|
197
275
|
document.body
|
|
198
276
|
);
|
|
199
277
|
}
|
|
200
278
|
function AstralDrawer({ opened, onClose, title, brand, width = 440, zIndex, children }) {
|
|
201
|
-
|
|
279
|
+
const { panelRef, titleId } = useDialog(opened, onClose);
|
|
202
280
|
if (!opened) return null;
|
|
203
281
|
const style = { width, ...brand ? { "--au-ic": brand } : {} };
|
|
204
282
|
return createPortal(
|
|
205
|
-
/* @__PURE__ */ jsx("div", { className: "au-drawer-overlay", onMouseDown: onClose, style: zIndex != null ? { zIndex } : void 0, children: /* @__PURE__ */ jsxs(
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
283
|
+
/* @__PURE__ */ jsx("div", { className: "au-drawer-overlay", onMouseDown: onClose, style: zIndex != null ? { zIndex } : void 0, children: /* @__PURE__ */ jsxs(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
ref: panelRef,
|
|
287
|
+
className: "au-drawer-panel",
|
|
288
|
+
style,
|
|
289
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
290
|
+
role: "dialog",
|
|
291
|
+
"aria-modal": "true",
|
|
292
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
293
|
+
tabIndex: -1,
|
|
294
|
+
children: [
|
|
295
|
+
/* @__PURE__ */ jsxs("div", { className: "au-drawer-head", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("span", { className: "au-drawer-title", id: titleId, children: title }),
|
|
297
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "au-modal-x", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, {}) })
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ jsx("div", { className: "au-drawer-body", children })
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
) }),
|
|
212
303
|
document.body
|
|
213
304
|
);
|
|
214
305
|
}
|
|
@@ -448,6 +539,8 @@ function AstralMenu({ trigger, items, align = "end", width = 220 }) {
|
|
|
448
539
|
)
|
|
449
540
|
] });
|
|
450
541
|
}
|
|
542
|
+
var DEFAULT_LIMIT = 4;
|
|
543
|
+
var EXIT_MS = 220;
|
|
451
544
|
var toasts = [];
|
|
452
545
|
var listeners = /* @__PURE__ */ new Set();
|
|
453
546
|
var timers = /* @__PURE__ */ new Map();
|
|
@@ -469,7 +562,7 @@ function scheduleAutoClose(t) {
|
|
|
469
562
|
function show(opts) {
|
|
470
563
|
const id = opts.id ?? `toast-${++counter}`;
|
|
471
564
|
const existing = toasts.find((t) => t.id === id);
|
|
472
|
-
const toast = { withCloseButton: true, ...existing, ...opts, id };
|
|
565
|
+
const toast = { withCloseButton: true, ...existing, ...opts, id, exiting: false };
|
|
473
566
|
toasts = existing ? toasts.map((t) => t.id === id ? toast : t) : [...toasts, toast];
|
|
474
567
|
emit();
|
|
475
568
|
scheduleAutoClose(toast);
|
|
@@ -479,7 +572,7 @@ function update(opts) {
|
|
|
479
572
|
if (!opts.id) return show(opts);
|
|
480
573
|
const existing = toasts.find((t) => t.id === opts.id);
|
|
481
574
|
if (!existing) return show(opts);
|
|
482
|
-
const toast = { ...existing, ...opts, id: opts.id };
|
|
575
|
+
const toast = { ...existing, ...opts, id: opts.id, exiting: false };
|
|
483
576
|
toasts = toasts.map((t) => t.id === opts.id ? toast : t);
|
|
484
577
|
emit();
|
|
485
578
|
scheduleAutoClose(toast);
|
|
@@ -487,10 +580,16 @@ function update(opts) {
|
|
|
487
580
|
}
|
|
488
581
|
function hide(id) {
|
|
489
582
|
const t = toasts.find((x) => x.id === id);
|
|
583
|
+
if (!t || t.exiting) return;
|
|
490
584
|
clearTimer(id);
|
|
491
|
-
toasts = toasts.
|
|
585
|
+
toasts = toasts.map((x) => x.id === id ? { ...x, exiting: true } : x);
|
|
492
586
|
emit();
|
|
493
|
-
|
|
587
|
+
timers.set(id, setTimeout(() => {
|
|
588
|
+
toasts = toasts.filter((x) => x.id !== id);
|
|
589
|
+
timers.delete(id);
|
|
590
|
+
emit();
|
|
591
|
+
t.onClose?.();
|
|
592
|
+
}, EXIT_MS));
|
|
494
593
|
}
|
|
495
594
|
function clean() {
|
|
496
595
|
toasts.forEach((t) => clearTimer(t.id));
|
|
@@ -518,20 +617,29 @@ function Spinner() {
|
|
|
518
617
|
function ToastCard({ t }) {
|
|
519
618
|
const accent = t.color ? `var(--astral-color-${t.color}-6)` : "var(--astral-color-default-border)";
|
|
520
619
|
const lead = t.loading ? /* @__PURE__ */ jsx(Spinner, {}) : t.icon ? t.icon : t.color && DEFAULT_ICON[t.color] || /* @__PURE__ */ jsx(IconInfoCircle, { size: 16 });
|
|
521
|
-
return /* @__PURE__ */ jsxs(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
t.
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
620
|
+
return /* @__PURE__ */ jsxs(
|
|
621
|
+
"div",
|
|
622
|
+
{
|
|
623
|
+
className: t.exiting ? "au-toast au-toast-out" : "au-toast",
|
|
624
|
+
style: { ["--au-toast-accent"]: accent },
|
|
625
|
+
role: "status",
|
|
626
|
+
children: [
|
|
627
|
+
/* @__PURE__ */ jsx("span", { className: "au-toast-lead", children: lead }),
|
|
628
|
+
/* @__PURE__ */ jsxs("div", { className: "au-toast-body", children: [
|
|
629
|
+
t.title && /* @__PURE__ */ jsx("div", { className: "au-toast-title", children: t.title }),
|
|
630
|
+
t.message != null && /* @__PURE__ */ jsx("div", { className: "au-toast-msg", children: t.message })
|
|
631
|
+
] }),
|
|
632
|
+
t.withCloseButton !== false && /* @__PURE__ */ jsx("button", { type: "button", className: "au-toast-x", onClick: () => hide(t.id), "aria-label": "Close", children: /* @__PURE__ */ jsx(IconX, { size: 14 }) })
|
|
633
|
+
]
|
|
634
|
+
}
|
|
635
|
+
);
|
|
529
636
|
}
|
|
530
|
-
function AstralToaster() {
|
|
637
|
+
function AstralToaster({ limit = DEFAULT_LIMIT } = {}) {
|
|
531
638
|
const items = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
532
639
|
if (typeof document === "undefined") return null;
|
|
640
|
+
const visible = limit > 0 ? items.filter((t, i) => t.exiting || i >= items.length - limit) : items;
|
|
533
641
|
return createPortal(
|
|
534
|
-
/* @__PURE__ */ jsx("div", { className: "au-toast-host", role: "region", "aria-live": "polite", "aria-label": "Notifications", children:
|
|
642
|
+
/* @__PURE__ */ jsx("div", { className: "au-toast-host", role: "region", "aria-live": "polite", "aria-label": "Notifications", children: visible.map((t) => /* @__PURE__ */ jsx(ToastCard, { t }, t.id)) }),
|
|
535
643
|
document.body
|
|
536
644
|
);
|
|
537
645
|
}
|