@assure-one/design-system 1.13.0 → 1.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.
- package/dist/index.d.ts +9 -1
- package/dist/index.js +187 -130
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -2320,12 +2320,20 @@ interface ToastAction {
|
|
|
2320
2320
|
}
|
|
2321
2321
|
/** Options accepted by `toast()` and (partially) by `update()`. */
|
|
2322
2322
|
interface ToastOptions {
|
|
2323
|
-
title
|
|
2323
|
+
title?: React.ReactNode;
|
|
2324
2324
|
description?: React.ReactNode;
|
|
2325
2325
|
variant?: ToastVariant;
|
|
2326
2326
|
/** Auto-dismiss after N ms. `0` = sticky. `loading` defaults to sticky. */
|
|
2327
2327
|
duration?: number;
|
|
2328
2328
|
action?: ToastAction;
|
|
2329
|
+
/**
|
|
2330
|
+
* Rich-variant escape hatch. When provided, the toast renders `content`
|
|
2331
|
+
* inside its surface instead of the title/chip/countdown layout — the
|
|
2332
|
+
* content owns its own structure and dismissal. Pass a render function to
|
|
2333
|
+
* receive `close` (e.g. a review queue that dismisses itself once empty).
|
|
2334
|
+
* Rich-content toasts are sticky by default (no auto-dismiss countdown).
|
|
2335
|
+
*/
|
|
2336
|
+
content?: React.ReactNode | ((close: () => void) => React.ReactNode);
|
|
2329
2337
|
/**
|
|
2330
2338
|
* When set, a new toast with the same key updates the existing one in place
|
|
2331
2339
|
* instead of stacking a duplicate (e.g. repeated "Saving…" feedback).
|
package/dist/index.js
CHANGED
|
@@ -9493,19 +9493,17 @@ function ToastProvider({ children }) {
|
|
|
9493
9493
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
9494
9494
|
}, []);
|
|
9495
9495
|
const addToast = useCallback((options) => {
|
|
9496
|
-
const variant = options.variant ?? "default";
|
|
9497
|
-
const duration = options.duration ?? (variant === "loading" ? 0 : DEFAULT_DURATION);
|
|
9498
9496
|
const id = `toast-${++toastCount}`;
|
|
9499
9497
|
setToasts((prev) => {
|
|
9500
9498
|
if (options.dedupeKey) {
|
|
9501
9499
|
const existing = prev.find((t) => t.dedupeKey === options.dedupeKey);
|
|
9502
9500
|
if (existing) {
|
|
9503
9501
|
return prev.map(
|
|
9504
|
-
(t) => t.dedupeKey === options.dedupeKey ? { ...t, ...options,
|
|
9502
|
+
(t) => t.dedupeKey === options.dedupeKey ? { ...t, ...options, seq: t.seq + 1 } : t
|
|
9505
9503
|
);
|
|
9506
9504
|
}
|
|
9507
9505
|
}
|
|
9508
|
-
return [...prev, { id, seq: 0, ...options
|
|
9506
|
+
return [...prev, { id, seq: 0, ...options }];
|
|
9509
9507
|
});
|
|
9510
9508
|
return id;
|
|
9511
9509
|
}, []);
|
|
@@ -9513,9 +9511,8 @@ function ToastProvider({ children }) {
|
|
|
9513
9511
|
setToasts(
|
|
9514
9512
|
(prev) => prev.map((t) => {
|
|
9515
9513
|
if (t.id !== id) return t;
|
|
9516
|
-
const variant = patch.variant ?? t.variant;
|
|
9517
9514
|
const duration = patch.duration ?? (patch.variant && patch.variant !== "loading" ? DEFAULT_DURATION : t.duration);
|
|
9518
|
-
return { ...t, ...patch,
|
|
9515
|
+
return { ...t, ...patch, duration, seq: t.seq + 1 };
|
|
9519
9516
|
})
|
|
9520
9517
|
);
|
|
9521
9518
|
}, []);
|
|
@@ -9556,79 +9553,89 @@ function ToastProvider({ children }) {
|
|
|
9556
9553
|
] });
|
|
9557
9554
|
}
|
|
9558
9555
|
var variantConfig = {
|
|
9559
|
-
default: {
|
|
9556
|
+
default: {
|
|
9557
|
+
chip: "bg-pro-bg",
|
|
9558
|
+
icon: "text-pro-fg",
|
|
9559
|
+
stroke: "stroke-pro-fg",
|
|
9560
|
+
ring: "ring-pro-fg/15",
|
|
9561
|
+
Icon: InfoCircleSolidIcon
|
|
9562
|
+
},
|
|
9560
9563
|
success: {
|
|
9561
9564
|
chip: "bg-success-bg",
|
|
9562
9565
|
icon: "text-success-fg",
|
|
9563
|
-
|
|
9566
|
+
stroke: "stroke-success-fg",
|
|
9567
|
+
ring: "ring-success-fg/15",
|
|
9564
9568
|
Icon: CheckCircleSolidIcon
|
|
9565
9569
|
},
|
|
9566
9570
|
destructive: {
|
|
9567
9571
|
chip: "bg-danger-bg",
|
|
9568
9572
|
icon: "text-danger-fg",
|
|
9569
|
-
|
|
9573
|
+
stroke: "stroke-danger-fg",
|
|
9574
|
+
ring: "ring-danger-fg/15",
|
|
9570
9575
|
Icon: XCircleSolidIcon
|
|
9571
9576
|
},
|
|
9572
9577
|
warning: {
|
|
9573
9578
|
chip: "bg-warning-bg",
|
|
9574
9579
|
icon: "text-warning-fg",
|
|
9575
|
-
|
|
9580
|
+
stroke: "stroke-warning-fg",
|
|
9581
|
+
ring: "ring-warning-fg/20",
|
|
9576
9582
|
Icon: AlertTriangleSolidIcon
|
|
9577
9583
|
},
|
|
9578
9584
|
info: {
|
|
9579
9585
|
chip: "bg-info/12",
|
|
9580
9586
|
icon: "text-info",
|
|
9581
|
-
|
|
9587
|
+
stroke: "stroke-info",
|
|
9588
|
+
ring: "ring-info/15",
|
|
9582
9589
|
Icon: InfoCircleSolidIcon
|
|
9583
9590
|
},
|
|
9584
9591
|
loading: {
|
|
9585
9592
|
chip: "bg-pro-bg",
|
|
9586
9593
|
icon: "text-pro-fg",
|
|
9587
|
-
|
|
9594
|
+
stroke: "stroke-pro-fg",
|
|
9595
|
+
ring: "ring-pro-fg/15",
|
|
9588
9596
|
Icon: LoaderIcon,
|
|
9589
9597
|
spin: true
|
|
9590
9598
|
}
|
|
9591
9599
|
};
|
|
9600
|
+
var RING_RADIUS2 = 11;
|
|
9601
|
+
var RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS2;
|
|
9592
9602
|
var SWIPE_DISMISS_PX = 72;
|
|
9593
9603
|
function ToastItem({ toast, onDismiss }) {
|
|
9594
|
-
const
|
|
9604
|
+
const timerRef = useRef(void 0);
|
|
9605
|
+
const remainingRef = useRef(toast.duration ?? DEFAULT_DURATION);
|
|
9606
|
+
const startedAtRef = useRef(0);
|
|
9595
9607
|
const cardRef = useRef(null);
|
|
9596
|
-
const
|
|
9597
|
-
const
|
|
9598
|
-
const
|
|
9599
|
-
const
|
|
9608
|
+
const [exiting, setExiting] = useState(false);
|
|
9609
|
+
const [paused, setPaused] = useState(false);
|
|
9610
|
+
const hasContent = toast.content != null;
|
|
9611
|
+
const duration = toast.duration ?? DEFAULT_DURATION;
|
|
9612
|
+
const isLoading = (toast.variant ?? "default") === "loading";
|
|
9613
|
+
const autoDismiss = duration > 0 && !hasContent && !isLoading;
|
|
9614
|
+
const isError = (toast.variant ?? "default") === "destructive";
|
|
9600
9615
|
const close = useCallback(() => {
|
|
9601
9616
|
setExiting(true);
|
|
9602
9617
|
setTimeout(() => onDismiss(toast.id), 150);
|
|
9603
9618
|
}, [toast.id, onDismiss]);
|
|
9604
9619
|
useEffect(() => {
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
if (remaining <= 0) {
|
|
9618
|
-
close();
|
|
9619
|
-
return;
|
|
9620
|
-
}
|
|
9621
|
-
}
|
|
9622
|
-
raf = requestAnimationFrame(tick);
|
|
9620
|
+
remainingRef.current = toast.duration ?? DEFAULT_DURATION;
|
|
9621
|
+
}, [toast.seq, toast.duration]);
|
|
9622
|
+
useEffect(() => {
|
|
9623
|
+
if (!autoDismiss || exiting || paused) return;
|
|
9624
|
+
startedAtRef.current = Date.now();
|
|
9625
|
+
timerRef.current = setTimeout(close, remainingRef.current);
|
|
9626
|
+
return () => {
|
|
9627
|
+
clearTimeout(timerRef.current);
|
|
9628
|
+
remainingRef.current = Math.max(
|
|
9629
|
+
0,
|
|
9630
|
+
remainingRef.current - (Date.now() - startedAtRef.current)
|
|
9631
|
+
);
|
|
9623
9632
|
};
|
|
9624
|
-
|
|
9625
|
-
return () => cancelAnimationFrame(raf);
|
|
9626
|
-
}, [duration, showProgress, close, toast.seq]);
|
|
9633
|
+
}, [autoDismiss, exiting, paused, close, toast.seq]);
|
|
9627
9634
|
const dragRef = useRef(null);
|
|
9628
9635
|
const onPointerDown = (e) => {
|
|
9629
|
-
if (e.target.closest("button")) return;
|
|
9636
|
+
if (e.target.closest("button, a, input, textarea")) return;
|
|
9630
9637
|
dragRef.current = { startX: e.clientX, dx: 0 };
|
|
9631
|
-
|
|
9638
|
+
setPaused(true);
|
|
9632
9639
|
cardRef.current?.setPointerCapture(e.pointerId);
|
|
9633
9640
|
};
|
|
9634
9641
|
const onPointerMove = (e) => {
|
|
@@ -9649,101 +9656,151 @@ function ToastItem({ toast, onDismiss }) {
|
|
|
9649
9656
|
}
|
|
9650
9657
|
cardRef.current.style.transform = "";
|
|
9651
9658
|
cardRef.current.style.opacity = "";
|
|
9652
|
-
|
|
9659
|
+
setPaused(false);
|
|
9653
9660
|
};
|
|
9654
|
-
const { chip, icon,
|
|
9655
|
-
return (
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
"
|
|
9662
|
-
|
|
9663
|
-
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9661
|
+
const { chip, icon, stroke, ring, Icon: Icon3, spin } = variantConfig[toast.variant ?? "default"];
|
|
9662
|
+
return /* @__PURE__ */ jsx(
|
|
9663
|
+
"div",
|
|
9664
|
+
{
|
|
9665
|
+
ref: cardRef,
|
|
9666
|
+
role: hasContent ? "group" : isError ? "alert" : "status",
|
|
9667
|
+
"aria-atomic": hasContent ? void 0 : true,
|
|
9668
|
+
"data-state": exiting ? "closed" : "open",
|
|
9669
|
+
onPointerEnter: () => setPaused(true),
|
|
9670
|
+
onPointerLeave: () => {
|
|
9671
|
+
if (!dragRef.current) setPaused(false);
|
|
9672
|
+
},
|
|
9673
|
+
onPointerDown,
|
|
9674
|
+
onPointerMove,
|
|
9675
|
+
onPointerUp: endDrag,
|
|
9676
|
+
onPointerCancel: endDrag,
|
|
9677
|
+
onFocusCapture: () => setPaused(true),
|
|
9678
|
+
onBlurCapture: () => {
|
|
9679
|
+
if (!dragRef.current) setPaused(false);
|
|
9680
|
+
},
|
|
9681
|
+
className: cn(
|
|
9682
|
+
"bg-surface border-rule text-fg rounded-card shadow-card pointer-events-auto relative w-80 max-w-[calc(100vw-2rem)] touch-none overflow-hidden border",
|
|
9683
|
+
// Title-mode padding; rich content owns its own internal padding.
|
|
9684
|
+
hasContent ? "" : "p-4",
|
|
9685
|
+
"font-body select-none",
|
|
9686
|
+
// Tactile lift on hover/focus — signals the card is interactive and
|
|
9687
|
+
// that its countdown is held. Shadow only (not motion), so it needs no
|
|
9688
|
+
// reduced-motion fallback; the named property keeps us off transition-all.
|
|
9689
|
+
"transition-shadow duration-[var(--duration-fast)] ease-[var(--ease-out-quart)]",
|
|
9690
|
+
"hover:shadow-lg focus-within:shadow-lg motion-reduce:transition-none",
|
|
9691
|
+
// Unified data-state animation (spec §5.27) — same model as Dialog/Tooltip,
|
|
9692
|
+
// with a subtle zoom so the toast *pops* in rather than just appearing:
|
|
9693
|
+
// open → fade + slide-from-right + zoom-from-95 over --duration-normal (250ms), ease-out-quart
|
|
9694
|
+
// close → fade + slide-to-right + zoom-to-95 over --duration-fast (150ms), ease-in
|
|
9695
|
+
"data-[state=open]:animate-in data-[state=open]:slide-in-from-right-8 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
9696
|
+
"data-[state=open]:duration-[var(--duration-normal)] data-[state=open]:ease-[var(--ease-out-quart)]",
|
|
9697
|
+
"data-[state=closed]:animate-out data-[state=closed]:slide-out-to-right-8 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
9698
|
+
"data-[state=closed]:duration-[var(--duration-fast)] data-[state=closed]:ease-[var(--ease-in)]",
|
|
9699
|
+
"motion-reduce:animate-none"
|
|
9700
|
+
),
|
|
9701
|
+
children: hasContent ? typeof toast.content === "function" ? toast.content(close) : toast.content : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
9702
|
+
/* @__PURE__ */ jsx(
|
|
9703
|
+
"span",
|
|
9704
|
+
{
|
|
9705
|
+
className: cn(
|
|
9706
|
+
// Same-hue inset ring gives the tinted chip a crafted edge instead
|
|
9707
|
+
// of a flat fill — definition without a heavy border.
|
|
9708
|
+
"inline-flex size-8 shrink-0 items-center justify-center rounded-[9px] ring-1 ring-inset",
|
|
9709
|
+
// Staggered pop — chip scales in ~80ms after the card lands so the
|
|
9710
|
+
// status glyph "arrives". motion-safe only; reduced motion shows it
|
|
9711
|
+
// at rest with no entrance.
|
|
9712
|
+
"motion-safe:animate-[toast-chip-in_280ms_var(--ease-out-quart)_80ms_both]",
|
|
9713
|
+
chip,
|
|
9714
|
+
icon,
|
|
9715
|
+
ring
|
|
9716
|
+
),
|
|
9717
|
+
"aria-hidden": "true",
|
|
9718
|
+
children: /* @__PURE__ */ jsx(Icon3, { className: cn("size-5", spin && "animate-spin motion-reduce:animate-none") })
|
|
9719
|
+
}
|
|
9693
9720
|
),
|
|
9694
|
-
children: [
|
|
9695
|
-
/* @__PURE__ */
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
)
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9721
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
9722
|
+
toast.title && /* @__PURE__ */ jsx("p", { className: "text-fg text-sm leading-tight font-semibold", children: toast.title }),
|
|
9723
|
+
toast.description && /* @__PURE__ */ jsx("p", { className: "text-fg-3 mt-0.5 text-sm leading-snug", children: toast.description }),
|
|
9724
|
+
toast.action && /* @__PURE__ */ jsx(
|
|
9725
|
+
"button",
|
|
9726
|
+
{
|
|
9727
|
+
type: "button",
|
|
9728
|
+
onClick: () => {
|
|
9729
|
+
toast.action?.onClick();
|
|
9730
|
+
close();
|
|
9731
|
+
},
|
|
9732
|
+
className: cn(
|
|
9733
|
+
"text-pro-fg mt-2 text-sm font-semibold",
|
|
9734
|
+
"transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out-quart)] hover:underline motion-reduce:transition-none",
|
|
9735
|
+
"focus-visible:[box-shadow:var(--shadow-focus-ring)] rounded-sm focus-visible:outline-none"
|
|
9736
|
+
),
|
|
9737
|
+
children: toast.action.label
|
|
9738
|
+
}
|
|
9739
|
+
)
|
|
9740
|
+
] }),
|
|
9741
|
+
/* @__PURE__ */ jsxs(
|
|
9742
|
+
"button",
|
|
9743
|
+
{
|
|
9744
|
+
type: "button",
|
|
9745
|
+
onClick: close,
|
|
9746
|
+
className: cn(
|
|
9747
|
+
// size-7 click target with a -m-1 offset so the glyph keeps its
|
|
9748
|
+
// top-right optical position while the hit area (and ring) grow.
|
|
9749
|
+
"text-fg-4 hover:text-fg-2 hover:bg-overlay-hover relative -mt-1 -mr-1 inline-flex size-7 shrink-0 items-center justify-center rounded-full",
|
|
9750
|
+
"transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out-quart)] motion-reduce:transition-none",
|
|
9751
|
+
"focus-visible:[box-shadow:var(--shadow-focus-ring)] focus-visible:outline-none"
|
|
9707
9752
|
),
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9753
|
+
"aria-label": "Dismiss",
|
|
9754
|
+
children: [
|
|
9755
|
+
autoDismiss && // Countdown drains as a ring hugging the dismiss button — the timer
|
|
9756
|
+
// and the close affordance share one spot. Pauses in lockstep with
|
|
9757
|
+
// the JS timer (animation-play-state). Hidden under reduced motion;
|
|
9758
|
+
// the timer still governs dismissal.
|
|
9759
|
+
/* @__PURE__ */ jsxs(
|
|
9760
|
+
"svg",
|
|
9713
9761
|
{
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9762
|
+
viewBox: "0 0 28 28",
|
|
9763
|
+
className: "pointer-events-none absolute inset-0 size-7 -rotate-90 motion-reduce:hidden",
|
|
9764
|
+
"aria-hidden": "true",
|
|
9765
|
+
children: [
|
|
9766
|
+
/* @__PURE__ */ jsx(
|
|
9767
|
+
"circle",
|
|
9768
|
+
{
|
|
9769
|
+
cx: "14",
|
|
9770
|
+
cy: "14",
|
|
9771
|
+
r: RING_RADIUS2,
|
|
9772
|
+
fill: "none",
|
|
9773
|
+
strokeWidth: "2",
|
|
9774
|
+
className: "stroke-rule"
|
|
9775
|
+
}
|
|
9776
|
+
),
|
|
9777
|
+
/* @__PURE__ */ jsx(
|
|
9778
|
+
"circle",
|
|
9779
|
+
{
|
|
9780
|
+
cx: "14",
|
|
9781
|
+
cy: "14",
|
|
9782
|
+
r: RING_RADIUS2,
|
|
9783
|
+
fill: "none",
|
|
9784
|
+
strokeWidth: "2",
|
|
9785
|
+
strokeLinecap: "round",
|
|
9786
|
+
className: cn("opacity-90", stroke),
|
|
9787
|
+
style: {
|
|
9788
|
+
strokeDasharray: RING_CIRCUMFERENCE,
|
|
9789
|
+
"--toast-ring-circ": RING_CIRCUMFERENCE,
|
|
9790
|
+
animation: `toast-ring-drain ${duration}ms linear forwards`,
|
|
9791
|
+
animationPlayState: paused ? "paused" : "running"
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
)
|
|
9795
|
+
]
|
|
9725
9796
|
}
|
|
9726
|
-
)
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9732
|
-
|
|
9733
|
-
className: cn(
|
|
9734
|
-
"text-fg-4 hover:text-fg-2 rounded-icon -mr-1 inline-flex size-5 shrink-0 items-center justify-center",
|
|
9735
|
-
"transition-colors duration-[var(--duration-fast)] ease-[var(--ease-out-quart)] motion-reduce:transition-none",
|
|
9736
|
-
"focus-visible:[box-shadow:var(--shadow-focus-ring)] focus-visible:outline-none"
|
|
9737
|
-
),
|
|
9738
|
-
"aria-label": "Dismiss",
|
|
9739
|
-
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5", "aria-hidden": "true" })
|
|
9740
|
-
}
|
|
9741
|
-
)
|
|
9742
|
-
] }),
|
|
9743
|
-
showProgress && /* @__PURE__ */ jsx("div", { className: "bg-rule absolute inset-x-0 bottom-0 h-1", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { ref: barRef, className: cn("h-full", bar), style: { width: "0%" } }) })
|
|
9744
|
-
]
|
|
9745
|
-
}
|
|
9746
|
-
)
|
|
9797
|
+
),
|
|
9798
|
+
/* @__PURE__ */ jsx(XIcon, { className: "relative size-3.5", "aria-hidden": "true" })
|
|
9799
|
+
]
|
|
9800
|
+
}
|
|
9801
|
+
)
|
|
9802
|
+
] }) })
|
|
9803
|
+
}
|
|
9747
9804
|
);
|
|
9748
9805
|
}
|
|
9749
9806
|
var itemVariants = cva(
|