@ada-cx/lovelace 1.3.0 → 1.5.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/components/Banner/Banner.css.d.ts +1 -0
- package/dist/components/Banner/Banner.css.js +7 -0
- package/dist/components/Banner/Banner.css.js.map +1 -0
- package/dist/components/Banner/Banner.d.ts +51 -0
- package/dist/components/Banner/Banner.js +25 -0
- package/dist/components/Banner/Banner.js.map +1 -0
- package/dist/components/Banner/BannerSurface.d.ts +17 -0
- package/dist/components/Banner/BannerSurface.js +65 -0
- package/dist/components/Banner/BannerSurface.js.map +1 -0
- package/dist/components/Banner/index.d.ts +1 -0
- package/dist/components/BubbleOverlay/BubbleOverlay.js +21 -20
- package/dist/components/BubbleOverlay/BubbleOverlay.js.map +1 -1
- package/dist/components/Dialog/DialogIcon.js +5 -5
- package/dist/components/DialogOverlay/DialogOverlay.js +23 -22
- package/dist/components/DialogOverlay/DialogOverlay.js.map +1 -1
- package/dist/components/FullscreenOverlay/FullscreenOverlay.js +21 -20
- package/dist/components/FullscreenOverlay/FullscreenOverlay.js.map +1 -1
- package/dist/components/Input/InputRoot.js +4 -4
- package/dist/components/Input/InputTextArea.js +7 -7
- package/dist/components/MenuItem/MenuItemRoot.js +9 -9
- package/dist/components/Sheet/SheetBody.d.ts +3 -1
- package/dist/components/Sheet/SheetBody.js +5 -4
- package/dist/components/Sheet/SheetBody.js.map +1 -1
- package/dist/components/SheetOverlay/SheetOverlay.d.ts +3 -1
- package/dist/components/SheetOverlay/SheetOverlay.js +22 -20
- package/dist/components/SheetOverlay/SheetOverlay.js.map +1 -1
- package/dist/components/ToastNotification/ToastNotification.css.d.ts +1 -14
- package/dist/components/ToastNotification/ToastNotification.css.js +1 -9
- package/dist/components/ToastNotification/ToastNotification.css.js.map +1 -1
- package/dist/components/ToastNotification/ToastNotification.d.ts +30 -11
- package/dist/components/ToastNotification/ToastNotification.js +29 -18
- package/dist/components/ToastNotification/ToastNotification.js.map +1 -1
- package/dist/components/ToastNotification/index.d.ts +1 -1
- package/dist/components/ToastRegion/ToastContent.d.ts +9 -0
- package/dist/components/ToastRegion/ToastRegion.css.d.ts +2 -0
- package/dist/components/ToastRegion/ToastRegion.css.js +7 -0
- package/dist/components/ToastRegion/ToastRegion.css.js.map +1 -0
- package/dist/components/ToastRegion/ToastRegion.d.ts +45 -0
- package/dist/components/ToastRegion/ToastRegion.js +38 -0
- package/dist/components/ToastRegion/ToastRegion.js.map +1 -0
- package/dist/components/ToastRegion/ToastRegionItem.d.ts +13 -0
- package/dist/components/ToastRegion/ToastRegionItem.js +49 -0
- package/dist/components/ToastRegion/ToastRegionItem.js.map +1 -0
- package/dist/components/ToastRegion/ToastRegionLandmark.d.ts +15 -0
- package/dist/components/ToastRegion/ToastRegionLandmark.js +62 -0
- package/dist/components/ToastRegion/ToastRegionLandmark.js.map +1 -0
- package/dist/components/ToastRegion/index.d.ts +4 -0
- package/dist/components/ToastRegion/index.js +3 -0
- package/dist/components/Tooltip/Tooltip.js +8 -8
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/overlayRegistry.d.ts +22 -0
- package/dist/hooks/overlayRegistry.js +32 -0
- package/dist/hooks/overlayRegistry.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +76 -72
- package/dist/runtime/LovelaceProvider.js +12 -12
- package/dist/style.css +1 -1
- package/dist/utils/animation.d.ts +28 -0
- package/dist/utils/animation.js +68 -0
- package/dist/utils/animation.js.map +1 -0
- package/package.json +3 -2
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import { cx as e } from "../../utils/cx.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { Dialog as
|
|
2
|
+
import { OverlayRegistration as t } from "../../hooks/overlayRegistry.js";
|
|
3
|
+
import { sheetDialog as n, sheetModal as r, sheetScrim as i } from "./SheetOverlay.css.js";
|
|
4
|
+
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
5
|
+
import { Dialog as s, Modal as c, ModalOverlay as l } from "react-aria-components";
|
|
6
|
+
import { useLocale as u } from "react-aria";
|
|
6
7
|
//#region src/components/SheetOverlay/SheetOverlay.tsx
|
|
7
|
-
function
|
|
8
|
-
let { direction:
|
|
9
|
-
return /* @__PURE__ */
|
|
10
|
-
isOpen:
|
|
11
|
-
onOpenChange:
|
|
12
|
-
isDismissable:
|
|
13
|
-
className:
|
|
14
|
-
dir:
|
|
15
|
-
children: /* @__PURE__ */
|
|
16
|
-
className:
|
|
17
|
-
children: /* @__PURE__ */
|
|
18
|
-
"aria-label":
|
|
19
|
-
|
|
20
|
-
|
|
8
|
+
function d({ isOpen: d, onOpenChange: f, isDismissable: p = !0, "aria-label": m, "aria-describedby": h, className: g, children: _ }) {
|
|
9
|
+
let { direction: v } = u();
|
|
10
|
+
return /* @__PURE__ */ o(l, {
|
|
11
|
+
isOpen: d,
|
|
12
|
+
onOpenChange: f,
|
|
13
|
+
isDismissable: p,
|
|
14
|
+
className: i,
|
|
15
|
+
dir: v,
|
|
16
|
+
children: [/* @__PURE__ */ a(t, {}), /* @__PURE__ */ a(c, {
|
|
17
|
+
className: r,
|
|
18
|
+
children: /* @__PURE__ */ a(s, {
|
|
19
|
+
"aria-label": m,
|
|
20
|
+
"aria-describedby": h,
|
|
21
|
+
className: e(n, g),
|
|
22
|
+
children: _
|
|
21
23
|
})
|
|
22
|
-
})
|
|
24
|
+
})]
|
|
23
25
|
});
|
|
24
26
|
}
|
|
25
27
|
//#endregion
|
|
26
|
-
export {
|
|
28
|
+
export { d as SheetOverlay };
|
|
27
29
|
|
|
28
30
|
//# sourceMappingURL=SheetOverlay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SheetOverlay.js","names":[],"sources":["../../../src/components/SheetOverlay/SheetOverlay.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { useLocale } from \"react-aria\";\nimport {\n\tDialog as AriaDialog,\n\tModal,\n\tModalOverlay,\n} from \"react-aria-components\";\nimport { cx } from \"../../utils/cx.js\";\nimport { sheetDialog, sheetModal, sheetScrim } from \"./SheetOverlay.css.js\";\n\nexport interface SheetOverlayProps {\n\t/** Whether the sheet is open. */\n\tisOpen: boolean;\n\t/** Called with the next open state on dismiss (scrim tap, Escape) or programmatic close. */\n\tonOpenChange: (isOpen: boolean) => void;\n\t/** Dismiss on scrim tap. Escape always cancels (RAC default). Defaults to `true`. */\n\tisDismissable?: boolean;\n\t/** Accessible name fallback when the child has no `Sheet.Header` title. */\n\t\"aria-label\"?: string;\n\tclassName?: string;\n\tchildren: ReactNode;\n}\n\n/**\n * Modal bottom-sheet presentation: dark scrim, focus-trap, dismiss-on-tap/Escape,\n * bottom-anchored, rounded-top corners, slide-up. Wraps arbitrary children\n * (typically a `Sheet`). For a full-window surface use `FullscreenOverlay`; for a\n * centered dialog `Dialog`; for a non-dimming bottom bubble `BubbleOverlay`.\n *\n * Give the dialog an accessible name via either a `Sheet.Header` title or the\n * `aria-label` prop — with neither (e.g. an actions-only Confirm sheet) RAC\n * emits a missing-name warning.\n */\nexport function SheetOverlay({\n\tisOpen,\n\tonOpenChange,\n\tisDismissable = true,\n\t\"aria-label\": ariaLabel,\n\tclassName,\n\tchildren,\n}: SheetOverlayProps) {\n\t// Stated, not inherited: this renders through a portal, so its direction must\n\t// not depend on where the portal lands. See the portalled-surface scenario\n\t// under the RTL requirement in openspec/specs/accessibility/spec.md.\n\tconst { direction } = useLocale();\n\n\treturn (\n\t\t<ModalOverlay\n\t\t\tisOpen={isOpen}\n\t\t\tonOpenChange={onOpenChange}\n\t\t\tisDismissable={isDismissable}\n\t\t\tclassName={sheetScrim}\n\t\t\tdir={direction}\n\t\t>\n\t\t\t<Modal className={sheetModal}>\n\t\t\t\t<AriaDialog\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\tclassName={cx(sheetDialog, className)}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</AriaDialog>\n\t\t\t</Modal>\n\t\t</ModalOverlay>\n\t);\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"SheetOverlay.js","names":[],"sources":["../../../src/components/SheetOverlay/SheetOverlay.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { useLocale } from \"react-aria\";\nimport {\n\tDialog as AriaDialog,\n\tModal,\n\tModalOverlay,\n} from \"react-aria-components\";\nimport { OverlayRegistration } from \"../../hooks/overlayRegistry.js\";\nimport { cx } from \"../../utils/cx.js\";\nimport { sheetDialog, sheetModal, sheetScrim } from \"./SheetOverlay.css.js\";\n\nexport interface SheetOverlayProps {\n\t/** Whether the sheet is open. */\n\tisOpen: boolean;\n\t/** Called with the next open state on dismiss (scrim tap, Escape) or programmatic close. */\n\tonOpenChange: (isOpen: boolean) => void;\n\t/** Dismiss on scrim tap. Escape always cancels (RAC default). Defaults to `true`. */\n\tisDismissable?: boolean;\n\t/** Accessible name fallback when the child has no `Sheet.Header` title. */\n\t\"aria-label\"?: string;\n\t/** Id of the element describing the sheet, typically a `Sheet.Body`. */\n\t\"aria-describedby\"?: string;\n\tclassName?: string;\n\tchildren: ReactNode;\n}\n\n/**\n * Modal bottom-sheet presentation: dark scrim, focus-trap, dismiss-on-tap/Escape,\n * bottom-anchored, rounded-top corners, slide-up. Wraps arbitrary children\n * (typically a `Sheet`). For a full-window surface use `FullscreenOverlay`; for a\n * centered dialog `Dialog`; for a non-dimming bottom bubble `BubbleOverlay`.\n *\n * Give the dialog an accessible name via either a `Sheet.Header` title or the\n * `aria-label` prop — with neither (e.g. an actions-only Confirm sheet) RAC\n * emits a missing-name warning.\n */\nexport function SheetOverlay({\n\tisOpen,\n\tonOpenChange,\n\tisDismissable = true,\n\t\"aria-label\": ariaLabel,\n\t\"aria-describedby\": ariaDescribedBy,\n\tclassName,\n\tchildren,\n}: SheetOverlayProps) {\n\t// Stated, not inherited: this renders through a portal, so its direction must\n\t// not depend on where the portal lands. See the portalled-surface scenario\n\t// under the RTL requirement in openspec/specs/accessibility/spec.md.\n\tconst { direction } = useLocale();\n\n\treturn (\n\t\t<ModalOverlay\n\t\t\tisOpen={isOpen}\n\t\t\tonOpenChange={onOpenChange}\n\t\t\tisDismissable={isDismissable}\n\t\t\tclassName={sheetScrim}\n\t\t\tdir={direction}\n\t\t>\n\t\t\t<OverlayRegistration />\n\t\t\t<Modal className={sheetModal}>\n\t\t\t\t<AriaDialog\n\t\t\t\t\taria-label={ariaLabel}\n\t\t\t\t\taria-describedby={ariaDescribedBy}\n\t\t\t\t\tclassName={cx(sheetDialog, className)}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</AriaDialog>\n\t\t\t</Modal>\n\t\t</ModalOverlay>\n\t);\n}\n"],"mappings":";;;;;;;AAoCA,SAAgB,EAAa,EAC5B,WACA,iBACA,mBAAgB,IAChB,cAAc,GACd,oBAAoB,GACpB,cACA,eACqB;CAIrB,IAAM,EAAE,iBAAc,GAAW;AAEjC,QACC,kBAAC,GAAD;EACS;EACM;EACC;EACf,WAAW;EACX,KAAK;YALN,CAOC,kBAAC,GAAD,EAAuB,CAAA,EACvB,kBAAC,GAAD;GAAO,WAAW;aACjB,kBAAC,GAAD;IACC,cAAY;IACZ,oBAAkB;IAClB,WAAW,EAAG,GAAa,EAAU;IAEpC;IACW,CAAA;GACN,CAAA,CACM"}
|
|
@@ -14,17 +14,4 @@ export declare const toastRoot: import("@vanilla-extract/recipes").RuntimeFn<{
|
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
16
|
}>;
|
|
17
|
-
export declare const toastMessage:
|
|
18
|
-
variant: {
|
|
19
|
-
"single-line": {
|
|
20
|
-
flexShrink: number;
|
|
21
|
-
whiteSpace: "nowrap";
|
|
22
|
-
textAlign: "center";
|
|
23
|
-
};
|
|
24
|
-
"multi-line": {
|
|
25
|
-
flex: "1 0 0";
|
|
26
|
-
minWidth: number;
|
|
27
|
-
textAlign: "left";
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
}>;
|
|
17
|
+
export declare const toastMessage: string;
|
|
@@ -10,15 +10,7 @@ var t = e({
|
|
|
10
10
|
} },
|
|
11
11
|
defaultVariants: { status: "success" },
|
|
12
12
|
compoundVariants: []
|
|
13
|
-
}), n =
|
|
14
|
-
defaultClassName: "k6tn095",
|
|
15
|
-
variantClassNames: { variant: {
|
|
16
|
-
"single-line": "k6tn096",
|
|
17
|
-
"multi-line": "k6tn097"
|
|
18
|
-
} },
|
|
19
|
-
defaultVariants: { variant: "single-line" },
|
|
20
|
-
compoundVariants: []
|
|
21
|
-
});
|
|
13
|
+
}), n = "k6tn095";
|
|
22
14
|
//#endregion
|
|
23
15
|
export { n as toastMessage, t as toastRoot };
|
|
24
16
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToastNotification.css.js","names":[],"sources":["../../../src/components/ToastNotification/ToastNotification.css.ts"],"sourcesContent":["import { globalStyle, style } from \"@vanilla-extract/css\";\nimport { recipe } from \"@vanilla-extract/recipes\";\nimport { t } from \"../../tokens/index.js\";\n\nconst toastBase = style({\n\tdisplay: \"flex\",\n\
|
|
1
|
+
{"version":3,"file":"ToastNotification.css.js","names":[],"sources":["../../../src/components/ToastNotification/ToastNotification.css.ts"],"sourcesContent":["import { globalStyle, style } from \"@vanilla-extract/css\";\nimport { recipe } from \"@vanilla-extract/recipes\";\nimport { t } from \"../../tokens/index.js\";\n\nconst toastBase = style({\n\tdisplay: \"flex\",\n\tgap: t(\"gap-8\"),\n\talignItems: \"flex-start\",\n\tboxSizing: \"border-box\",\n\twidth: \"100%\",\n\tpaddingInline: t(\"padding-12\"),\n\tpaddingBlock: t(\"padding-8\"),\n\tfontFamily: t(\"font-family-body\"),\n\tfontWeight: t(\"font-weight-medium\"),\n\tfontSize: t(\"font-size-body\"),\n\tlineHeight: t(\"line-height-body\"),\n\ttextAlign: \"center\",\n\toverflowWrap: \"break-word\",\n});\n\nexport const toastRoot = recipe({\n\tbase: toastBase,\n\tvariants: {\n\t\tstatus: {\n\t\t\tsuccess: {\n\t\t\t\tbackgroundColor: t(\"color-background-success-light\"),\n\t\t\t\tcolor: t(\"color-content-success\"),\n\t\t\t},\n\t\t\twarning: {\n\t\t\t\tbackgroundColor: t(\"color-background-warning-light\"),\n\t\t\t\tcolor: t(\"color-content-warning\"),\n\t\t\t},\n\t\t\terror: {\n\t\t\t\tbackgroundColor: t(\"color-background-destructive-light\"),\n\t\t\t\tcolor: t(\"color-content-destructive\"),\n\t\t\t},\n\t\t},\n\t},\n\tdefaultVariants: {\n\t\tstatus: \"success\",\n\t},\n});\n\nexport const toastMessage = style({\n\tmarginInlineEnd: \"auto\",\n\tminWidth: 0,\n});\n\nglobalStyle(`${toastBase} > [data-slot='icon']`, {\n\tfontSize: t(\"icon-size-control\"),\n\tflexShrink: 0,\n\tmarginInlineStart: \"auto\",\n});\n\nglobalStyle(`${toastBase} > [data-slot='button']`, {\n\tcolor: \"inherit\",\n});\n\nglobalStyle(`${toastBase} > [data-slot='button'] [data-slot='icon']`, {\n\tfontSize: t(\"icon-size-sm\"),\n});\n\n// The card disables its dismiss button only while the surface is leaving, which\n// is not a control the reader should read as unavailable — it is the same\n// control, on its way out. Keeping its own look means taking the press away\n// costs nothing on screen.\nglobalStyle(`${toastBase} > [data-slot='button'][data-disabled]`, {\n\tcolor: \"inherit\",\n\tcursor: \"inherit\",\n});\n"],"mappings":""}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { CSSProperties, ReactNode, Ref } from "react";
|
|
1
|
+
import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from "react";
|
|
2
2
|
export type ToastStatus = "success" | "warning" | "error";
|
|
3
|
-
export type ToastVariant = "single-line" | "multi-line";
|
|
4
3
|
export interface ToastNotificationProps {
|
|
5
4
|
/**
|
|
6
5
|
* The outcome being reported — selects the background/content color pair.
|
|
@@ -8,15 +7,35 @@ export interface ToastNotificationProps {
|
|
|
8
7
|
*/
|
|
9
8
|
status?: ToastStatus;
|
|
10
9
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Renders a trailing dismiss button and runs this when the user presses it.
|
|
11
|
+
* Omit it when the consumer takes the toast down on its own.
|
|
13
12
|
*/
|
|
14
|
-
|
|
13
|
+
onDismiss?: () => void;
|
|
14
|
+
/**
|
|
15
|
+
* Accessible name for the dismiss button. Override it when "Dismiss
|
|
16
|
+
* notification" would be ambiguous, or to localize it.
|
|
17
|
+
*/
|
|
18
|
+
dismissLabel?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Takes the press away while the card is on screen only to finish leaving.
|
|
21
|
+
* The container it sits in is `inert` by then, but `inert` lands in Safari
|
|
22
|
+
* 15.5 and the supported baseline is iOS 15, where the attribute does nothing
|
|
23
|
+
* — so the button would stay tabbable and offer a press that no longer acts
|
|
24
|
+
* on anything. Disabling keeps the card's layout still through the travel,
|
|
25
|
+
* which removing the button would not.
|
|
26
|
+
*/
|
|
27
|
+
dismissDisabled?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Attributes for the message element. A container that owns announcement
|
|
30
|
+
* passes its live-region props through here, so the dismiss button stays
|
|
31
|
+
* outside what gets announced.
|
|
32
|
+
*/
|
|
33
|
+
messageProps?: HTMLAttributes<HTMLElement>;
|
|
15
34
|
className?: string;
|
|
16
35
|
style?: CSSProperties;
|
|
17
36
|
ref?: Ref<HTMLDivElement>;
|
|
18
37
|
/**
|
|
19
|
-
* The message to
|
|
38
|
+
* The message to display. A status icon is rendered automatically, so the
|
|
20
39
|
* status is conveyed without color perception; for screen-reader users the
|
|
21
40
|
* icon is decorative, so include the status in the copy when the message
|
|
22
41
|
* alone wouldn't make the outcome clear.
|
|
@@ -28,9 +47,9 @@ export interface ToastNotificationProps {
|
|
|
28
47
|
* auto-dismissing feedback after an action — not for blocking errors or anything
|
|
29
48
|
* needing a decision (use a dialog).
|
|
30
49
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
50
|
+
* Purely presentational: it carries no live region of its own, so it can sit
|
|
51
|
+
* inside a container that owns the announcement. Rendering it standalone
|
|
52
|
+
* announces nothing — reach for `ToastRegion` (queued, transient) or
|
|
53
|
+
* `Banner` (persistent, tied to a condition) instead.
|
|
35
54
|
*/
|
|
36
|
-
export declare function ToastNotification({ status,
|
|
55
|
+
export declare function ToastNotification({ status, onDismiss, dismissLabel, dismissDisabled, messageProps, className, children, ...rest }: ToastNotificationProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,31 +1,42 @@
|
|
|
1
1
|
import { cx as e } from "../../utils/cx.js";
|
|
2
2
|
import { Checkmark as t } from "../Icon/icons/Checkmark.js";
|
|
3
3
|
import { ExclamationTriangle as n } from "../Icon/icons/ExclamationTriangle.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { Xmark as r } from "../Icon/icons/Xmark.js";
|
|
5
|
+
import { XmarkCircle as i } from "../Icon/icons/XmarkCircle.js";
|
|
6
|
+
import { IconButton as a } from "../IconButton/IconButton.js";
|
|
7
|
+
import { toastMessage as o, toastRoot as s } from "./ToastNotification.css.js";
|
|
8
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
9
|
//#region src/components/ToastNotification/ToastNotification.tsx
|
|
8
|
-
var
|
|
10
|
+
var u = {
|
|
9
11
|
success: t,
|
|
10
12
|
warning: n,
|
|
11
|
-
error:
|
|
13
|
+
error: i
|
|
12
14
|
};
|
|
13
|
-
function
|
|
14
|
-
let
|
|
15
|
-
return /* @__PURE__ */
|
|
16
|
-
...
|
|
17
|
-
role: d ? "alert" : "status",
|
|
18
|
-
"aria-live": d ? "assertive" : "polite",
|
|
15
|
+
function d({ status: t = "success", onDismiss: n, dismissLabel: i = "Dismiss notification", dismissDisabled: d = !1, messageProps: f, className: p, children: m, ...h }) {
|
|
16
|
+
let g = u[t];
|
|
17
|
+
return /* @__PURE__ */ l("div", {
|
|
18
|
+
...h,
|
|
19
19
|
"data-status": t,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
className: e(s({ status: t }), p),
|
|
21
|
+
children: [
|
|
22
|
+
/* @__PURE__ */ c(g, {}),
|
|
23
|
+
/* @__PURE__ */ c("span", {
|
|
24
|
+
...f,
|
|
25
|
+
className: e(f?.className, o),
|
|
26
|
+
children: m
|
|
27
|
+
}),
|
|
28
|
+
!!n && /* @__PURE__ */ c(a, {
|
|
29
|
+
label: i,
|
|
30
|
+
variant: "tertiary",
|
|
31
|
+
size: "xs",
|
|
32
|
+
isDisabled: d,
|
|
33
|
+
onPress: n,
|
|
34
|
+
children: /* @__PURE__ */ c(r, {})
|
|
35
|
+
})
|
|
36
|
+
]
|
|
26
37
|
});
|
|
27
38
|
}
|
|
28
39
|
//#endregion
|
|
29
|
-
export {
|
|
40
|
+
export { d as ToastNotification };
|
|
30
41
|
|
|
31
42
|
//# sourceMappingURL=ToastNotification.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToastNotification.js","names":[],"sources":["../../../src/components/ToastNotification/ToastNotification.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode, Ref } from \"react\";\nimport { cx } from \"../../utils/cx.js\";\nimport {\n\tCheckmark,\n\tExclamationTriangle,\n\tXmarkCircle,\n} from \"../Icon/icons/index.js\";\nimport { toastMessage, toastRoot } from \"./ToastNotification.css.js\";\n\nexport type ToastStatus = \"success\" | \"warning\" | \"error\";\
|
|
1
|
+
{"version":3,"file":"ToastNotification.js","names":[],"sources":["../../../src/components/ToastNotification/ToastNotification.tsx"],"sourcesContent":["import type { CSSProperties, HTMLAttributes, ReactNode, Ref } from \"react\";\nimport { cx } from \"../../utils/cx.js\";\nimport {\n\tCheckmark,\n\tExclamationTriangle,\n\tXmark,\n\tXmarkCircle,\n} from \"../Icon/icons/index.js\";\nimport { IconButton } from \"../IconButton/IconButton.js\";\nimport { toastMessage, toastRoot } from \"./ToastNotification.css.js\";\n\nexport type ToastStatus = \"success\" | \"warning\" | \"error\";\n\nconst STATUS_ICON = {\n\tsuccess: Checkmark,\n\twarning: ExclamationTriangle,\n\terror: XmarkCircle,\n} as const;\n\nexport interface ToastNotificationProps {\n\t/**\n\t * The outcome being reported — selects the background/content color pair.\n\t * Bind it to the result of the action, not the UI.\n\t */\n\tstatus?: ToastStatus;\n\t/**\n\t * Renders a trailing dismiss button and runs this when the user presses it.\n\t * Omit it when the consumer takes the toast down on its own.\n\t */\n\tonDismiss?: () => void;\n\t/**\n\t * Accessible name for the dismiss button. Override it when \"Dismiss\n\t * notification\" would be ambiguous, or to localize it.\n\t */\n\tdismissLabel?: string;\n\t/**\n\t * Takes the press away while the card is on screen only to finish leaving.\n\t * The container it sits in is `inert` by then, but `inert` lands in Safari\n\t * 15.5 and the supported baseline is iOS 15, where the attribute does nothing\n\t * — so the button would stay tabbable and offer a press that no longer acts\n\t * on anything. Disabling keeps the card's layout still through the travel,\n\t * which removing the button would not.\n\t */\n\tdismissDisabled?: boolean;\n\t/**\n\t * Attributes for the message element. A container that owns announcement\n\t * passes its live-region props through here, so the dismiss button stays\n\t * outside what gets announced.\n\t */\n\tmessageProps?: HTMLAttributes<HTMLElement>;\n\tclassName?: string;\n\tstyle?: CSSProperties;\n\tref?: Ref<HTMLDivElement>;\n\t/**\n\t * The message to display. A status icon is rendered automatically, so the\n\t * status is conveyed without color perception; for screen-reader users the\n\t * icon is decorative, so include the status in the copy when the message\n\t * alone wouldn't make the outcome clear.\n\t */\n\tchildren?: ReactNode;\n}\n\n/**\n * Transient inline status message on a status-colored background. Use for brief,\n * auto-dismissing feedback after an action — not for blocking errors or anything\n * needing a decision (use a dialog).\n *\n * Purely presentational: it carries no live region of its own, so it can sit\n * inside a container that owns the announcement. Rendering it standalone\n * announces nothing — reach for `ToastRegion` (queued, transient) or\n * `Banner` (persistent, tied to a condition) instead.\n */\nexport function ToastNotification({\n\tstatus = \"success\",\n\tonDismiss,\n\tdismissLabel = \"Dismiss notification\",\n\tdismissDisabled = false,\n\tmessageProps,\n\tclassName,\n\tchildren,\n\t...rest\n}: ToastNotificationProps) {\n\tconst StatusIcon = STATUS_ICON[status];\n\treturn (\n\t\t<div\n\t\t\t{...rest}\n\t\t\tdata-status={status}\n\t\t\tclassName={cx(toastRoot({ status }), className)}\n\t\t>\n\t\t\t<StatusIcon />\n\t\t\t<span\n\t\t\t\t{...messageProps}\n\t\t\t\tclassName={cx(messageProps?.className, toastMessage)}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</span>\n\t\t\t{!!onDismiss && (\n\t\t\t\t<IconButton\n\t\t\t\t\tlabel={dismissLabel}\n\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\tsize=\"xs\"\n\t\t\t\t\tisDisabled={dismissDisabled}\n\t\t\t\t\tonPress={onDismiss}\n\t\t\t\t>\n\t\t\t\t\t<Xmark />\n\t\t\t\t</IconButton>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;;;AAaA,IAAM,IAAc;CACnB,SAAS;CACT,SAAS;CACT,OAAO;CACP;AAuDD,SAAgB,EAAkB,EACjC,YAAS,WACT,cACA,kBAAe,wBACf,qBAAkB,IAClB,iBACA,cACA,aACA,GAAG,KACuB;CAC1B,IAAM,IAAa,EAAY;AAC/B,QACC,kBAAC,OAAD;EACC,GAAI;EACJ,eAAa;EACb,WAAW,EAAG,EAAU,EAAE,WAAQ,CAAC,EAAE,EAAU;YAHhD;GAKC,kBAAC,GAAD,EAAc,CAAA;GACd,kBAAC,QAAD;IACC,GAAI;IACJ,WAAW,EAAG,GAAc,WAAW,EAAa;IAEnD;IACK,CAAA;GACN,CAAC,CAAC,KACF,kBAAC,GAAD;IACC,OAAO;IACP,SAAQ;IACR,MAAK;IACL,YAAY;IACZ,SAAS;cAET,kBAAC,GAAD,EAAS,CAAA;IACG,CAAA;GAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { ToastNotificationProps, ToastStatus,
|
|
1
|
+
export type { ToastNotificationProps, ToastStatus, } from "./ToastNotification.js";
|
|
2
2
|
export { ToastNotification } from "./ToastNotification.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ToastStatus } from "../ToastNotification/ToastNotification.js";
|
|
3
|
+
/** What a caller puts on the queue via `queue.add(content, options)`. */
|
|
4
|
+
export interface ToastContent {
|
|
5
|
+
/** The message to announce and display. */
|
|
6
|
+
message: ReactNode;
|
|
7
|
+
/** The outcome being reported. Defaults to `success`. */
|
|
8
|
+
status?: ToastStatus;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToastRegion.css.js","names":[],"sources":["../../../src/components/ToastRegion/ToastRegion.css.ts"],"sourcesContent":["import { style } from \"@vanilla-extract/css\";\nimport { t } from \"../../tokens/index.js\";\n\nexport const toastRegion = style({\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\toutline: \"none\",\n\t// The outgoing toast is taken out of flow on a replacement, so it anchors here.\n\tposition: \"relative\",\n});\n\n// Slides down from the leading edge of whatever the consumer anchors the region\n// to. The travel is the item's own height, so the consumer only has to clip at\n// that edge — no magic offset that has to track the chrome above it.\nexport const toastRegionItem = style({\n\tposition: \"relative\",\n\toutline: \"none\",\n\tzIndex: 1,\n\tselectors: {\n\t\t\"&[data-entering]\": { transform: \"translateY(-100%)\" },\n\t\t\"&[data-exiting]\": { transform: \"translateY(-100%)\" },\n\t\t// A toast replacing another keeps the same motion: the leaving toast slides\n\t\t// up while the arriving one slides down in front of it. The leaving toast has\n\t\t// to come out of flow for that — sliding both in flow stacked them for the\n\t\t// length of the travel, doubling the region's height and shoving the content\n\t\t// below it down and back.\n\t\t//\n\t\t// Keyed on the queue having dropped the toast rather than on `data-exiting`, which drops\n\t\t// the moment the travel ends while the toast is still mounted: the region\n\t\t// then reflowed to two toasts' height for the one frame before the parent\n\t\t// dropped it, exactly as the arriving toast settled.\n\t\t\"&[data-replacing][data-dropped]\": {\n\t\t\tposition: \"absolute\",\n\t\t\ttop: 0,\n\t\t\tleft: 0,\n\t\t\tright: 0,\n\t\t\tzIndex: 0,\n\t\t},\n\t\t\"&[data-focus-visible]\": {\n\t\t\tboxShadow: `inset 0 0 0 ${t(\"border-width-focus\")} ${t(\"color-border-focus\")}`,\n\t\t},\n\t},\n\t\"@media\": {\n\t\t\"(prefers-reduced-motion: no-preference)\": {\n\t\t\ttransition: `transform ${t(\"duration-medium\")} ${t(\"easing-standard\")}`,\n\t\t},\n\t\t\"(forced-colors: active)\": {\n\t\t\tselectors: {\n\t\t\t\t\"&[data-focus-visible]\": {\n\t\t\t\t\toutline: \"2px solid Highlight\",\n\t\t\t\t\toutlineOffset: \"-2px\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n});\n"],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { CSSProperties, Ref } from "react";
|
|
2
|
+
import type { ToastQueue } from "react-stately";
|
|
3
|
+
import type { ToastContent } from "./ToastContent.js";
|
|
4
|
+
export interface ToastRegionProps {
|
|
5
|
+
/** The queue to display. Create one per surface with `new ToastQueue()`. */
|
|
6
|
+
queue: ToastQueue<ToastContent>;
|
|
7
|
+
/**
|
|
8
|
+
* Overrides the landmark's accessible name. Leave it unset: the default is
|
|
9
|
+
* already localized and counts the toasts on show ("2 notifications."), which
|
|
10
|
+
* is what a screen-reader user hears on navigating here with F6.
|
|
11
|
+
*/
|
|
12
|
+
"aria-label"?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
style?: CSSProperties;
|
|
15
|
+
/** Attaches to the always-mounted anchor, not the landmark. */
|
|
16
|
+
ref?: Ref<HTMLDivElement>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Displays the toasts on a queue as a labelled landmark region.
|
|
20
|
+
*
|
|
21
|
+
* The region owns announcement, dismissal timing and focus: the dismiss button
|
|
22
|
+
* is named in the reader's own language, timers pause while
|
|
23
|
+
* the user hovers or focuses it, focus moves to a sibling toast when a focused
|
|
24
|
+
* one closes and returns to where it came from once the last one goes, and the
|
|
25
|
+
* region is reachable with F6. Anchor and clip it from the consumer side — a
|
|
26
|
+
* toast slides in from the region's leading edge, so give the region's container
|
|
27
|
+
* `overflow: hidden` to hide the travel.
|
|
28
|
+
*
|
|
29
|
+
* No landmark renders while the queue is empty, so it does not sit in the
|
|
30
|
+
* accessibility tree between notifications. The anchor it renders into stays
|
|
31
|
+
* mounted regardless: `ariaHideOutside` decides what an open modal hides at the
|
|
32
|
+
* moment it opens, and it spares a subtree only if a top-layer node is already
|
|
33
|
+
* inside it. An anchor that came and went with the toasts would leave the
|
|
34
|
+
* consumer's chrome hidden, and a toast raised afterwards would mount inside
|
|
35
|
+
* that hidden subtree and never be announced.
|
|
36
|
+
*
|
|
37
|
+
* That exemption is why no landmark renders while a modal overlay is open. The
|
|
38
|
+
* region is anchored in the consumer's chrome, which the overlay paints over and
|
|
39
|
+
* seals off from the pointer, so a toast there would be announced and F6-reachable
|
|
40
|
+
* with a dismiss control nobody could click. Unmounting the landmark is what makes
|
|
41
|
+
* the accessibility tree agree with the screen, and it defers rather than discards:
|
|
42
|
+
* the queue keeps the entry, react-aria pauses its countdown as the item unmounts,
|
|
43
|
+
* and the toast arrives whole once the overlay goes.
|
|
44
|
+
*/
|
|
45
|
+
export declare function ToastRegion({ queue, ref, ...props }: ToastRegionProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useHasOpenOverlay as e } from "../../hooks/overlayRegistry.js";
|
|
2
|
+
import { ToastRegionLandmark as t } from "./ToastRegionLandmark.js";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
import { useCallback as r, useState as i } from "react";
|
|
5
|
+
import { useToastQueue as a } from "react-stately";
|
|
6
|
+
//#region src/components/ToastRegion/ToastRegion.tsx
|
|
7
|
+
function o({ queue: o, ref: s, ...c }) {
|
|
8
|
+
let l = a(o), u = e(), [d, f] = i([]), [p, m] = i(l.visibleToasts);
|
|
9
|
+
if (p !== l.visibleToasts) {
|
|
10
|
+
m(l.visibleToasts);
|
|
11
|
+
let e = (e) => l.visibleToasts.some((t) => t.key === e.key), t = p.map((e, t) => ({
|
|
12
|
+
toast: e,
|
|
13
|
+
index: t
|
|
14
|
+
})).filter(({ toast: t }) => !e(t));
|
|
15
|
+
f((n) => [...n.filter(({ toast: t }) => !e(t)), ...t]);
|
|
16
|
+
}
|
|
17
|
+
u && d.length > 0 && f([]);
|
|
18
|
+
let h = r((e) => {
|
|
19
|
+
f((t) => t.filter(({ toast: t }) => t.key !== e));
|
|
20
|
+
}, []), g = d.filter(({ toast: e }) => !l.visibleToasts.some((t) => t.key === e.key)), _ = [...l.visibleToasts];
|
|
21
|
+
for (let { toast: e, index: t } of g) _.splice(Math.min(t, _.length), 0, e);
|
|
22
|
+
let v = new Set(g.filter(({ index: e }) => e === 0).map(({ toast: e }) => e.key));
|
|
23
|
+
return /* @__PURE__ */ n("div", {
|
|
24
|
+
"data-react-aria-top-layer": "true",
|
|
25
|
+
ref: s,
|
|
26
|
+
children: !u && _.length > 0 && /* @__PURE__ */ n(t, {
|
|
27
|
+
...c,
|
|
28
|
+
state: l,
|
|
29
|
+
toasts: _,
|
|
30
|
+
leavingFromTop: v,
|
|
31
|
+
onExited: h
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { o as ToastRegion };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=ToastRegion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToastRegion.js","names":[],"sources":["../../../src/components/ToastRegion/ToastRegion.tsx"],"sourcesContent":["import type { CSSProperties, Ref } from \"react\";\nimport { useCallback, useState } from \"react\";\nimport type { QueuedToast, ToastQueue } from \"react-stately\";\nimport { useToastQueue } from \"react-stately\";\nimport { useHasOpenOverlay } from \"../../hooks/overlayRegistry.js\";\nimport type { ToastContent } from \"./ToastContent.js\";\nimport { ToastRegionLandmark } from \"./ToastRegionLandmark.js\";\n\nexport interface ToastRegionProps {\n\t/** The queue to display. Create one per surface with `new ToastQueue()`. */\n\tqueue: ToastQueue<ToastContent>;\n\t/**\n\t * Overrides the landmark's accessible name. Leave it unset: the default is\n\t * already localized and counts the toasts on show (\"2 notifications.\"), which\n\t * is what a screen-reader user hears on navigating here with F6.\n\t */\n\t\"aria-label\"?: string;\n\tclassName?: string;\n\tstyle?: CSSProperties;\n\t/** Attaches to the always-mounted anchor, not the landmark. */\n\tref?: Ref<HTMLDivElement>;\n}\n\ninterface ExitingToast {\n\ttoast: QueuedToast<ToastContent>;\n\t/** Where it sat in the region, which is where it has to leave from. */\n\tindex: number;\n}\n\n/**\n * Displays the toasts on a queue as a labelled landmark region.\n *\n * The region owns announcement, dismissal timing and focus: the dismiss button\n * is named in the reader's own language, timers pause while\n * the user hovers or focuses it, focus moves to a sibling toast when a focused\n * one closes and returns to where it came from once the last one goes, and the\n * region is reachable with F6. Anchor and clip it from the consumer side — a\n * toast slides in from the region's leading edge, so give the region's container\n * `overflow: hidden` to hide the travel.\n *\n * No landmark renders while the queue is empty, so it does not sit in the\n * accessibility tree between notifications. The anchor it renders into stays\n * mounted regardless: `ariaHideOutside` decides what an open modal hides at the\n * moment it opens, and it spares a subtree only if a top-layer node is already\n * inside it. An anchor that came and went with the toasts would leave the\n * consumer's chrome hidden, and a toast raised afterwards would mount inside\n * that hidden subtree and never be announced.\n *\n * That exemption is why no landmark renders while a modal overlay is open. The\n * region is anchored in the consumer's chrome, which the overlay paints over and\n * seals off from the pointer, so a toast there would be announced and F6-reachable\n * with a dismiss control nobody could click. Unmounting the landmark is what makes\n * the accessibility tree agree with the screen, and it defers rather than discards:\n * the queue keeps the entry, react-aria pauses its countdown as the item unmounts,\n * and the toast arrives whole once the overlay goes.\n */\nexport function ToastRegion({ queue, ref, ...props }: ToastRegionProps) {\n\tconst state = useToastQueue(queue);\n\tconst hasOpenOverlay = useHasOpenOverlay();\n\tconst [exiting, setExiting] = useState<ExitingToast[]>([]);\n\tconst [previous, setPrevious] = useState(state.visibleToasts);\n\n\t// The queue drops a toast the moment it closes, so hold onto what it removed\n\t// until the exit animation finishes — otherwise React unmounts mid-slide.\n\t// Derive during render rather than in a layout effect: an effect runs after\n\t// the commit that already unmounted the item, so it would come back with its\n\t// exit animation missed. The previous list is state rather than a ref because\n\t// React can discard a render, which would keep a ref write but drop the\n\t// update beside it.\n\tif (previous !== state.visibleToasts) {\n\t\tsetPrevious(state.visibleToasts);\n\t\tconst isVisible = (toast: QueuedToast<ToastContent>) =>\n\t\t\tstate.visibleToasts.some((visible) => visible.key === toast.key);\n\t\tconst removed = previous\n\t\t\t.map((toast, index) => ({ toast, index }))\n\t\t\t.filter(({ toast }) => !isVisible(toast));\n\t\t// A toast the queue handed the slot back to is on screen again, so drop the\n\t\t// copy held for its exit — leaving it would queue the key a second time\n\t\t// when it closes for good, and React cannot render one key twice.\n\t\tsetExiting((current) => [\n\t\t\t...current.filter(({ toast }) => !isVisible(toast)),\n\t\t\t...removed,\n\t\t]);\n\t}\n\n\t// A suppressed region takes its toasts off screen at once, so nothing is left\n\t// to animate out. An exit held here would have no `onExited` to end it and\n\t// would replay when the overlay goes, putting a toast back that already left.\n\tif (hasOpenOverlay && exiting.length > 0) {\n\t\tsetExiting([]);\n\t}\n\n\tconst handleExited = useCallback((key: string) => {\n\t\tsetExiting((current) => current.filter(({ toast }) => toast.key !== key));\n\t}, []);\n\n\t// A toast that returns to the queue before its exit animation finishes is\n\t// visible again, so drop the stale copy rather than render the key twice.\n\tconst leaving = exiting.filter(\n\t\t({ toast }) =>\n\t\t\t!state.visibleToasts.some((visible) => visible.key === toast.key),\n\t);\n\t// Each leaver goes back where it sat rather than after the visible ones: a toast\n\t// removed from the middle of a stack would otherwise jump to the bottom of the\n\t// region and travel back up across its neighbour.\n\tconst toasts = [...state.visibleToasts];\n\tfor (const { toast, index } of leaving) {\n\t\ttoasts.splice(Math.min(index, toasts.length), 0, toast);\n\t}\n\t// Only a toast that held the top can leave from there, which is the one place\n\t// the region can take it out of flow without moving it first.\n\tconst leavingFromTop = new Set(\n\t\tleaving.filter(({ index }) => index === 0).map(({ toast }) => toast.key),\n\t);\n\treturn (\n\t\t<div data-react-aria-top-layer=\"true\" ref={ref}>\n\t\t\t{!hasOpenOverlay && toasts.length > 0 && (\n\t\t\t\t<ToastRegionLandmark\n\t\t\t\t\t{...props}\n\t\t\t\t\tstate={state}\n\t\t\t\t\ttoasts={toasts}\n\t\t\t\t\tleavingFromTop={leavingFromTop}\n\t\t\t\t\tonExited={handleExited}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;AAwDA,SAAgB,EAAY,EAAE,UAAO,QAAK,GAAG,KAA2B;CACvE,IAAM,IAAQ,EAAc,EAAM,EAC5B,IAAiB,GAAmB,EACpC,CAAC,GAAS,KAAc,EAAyB,EAAE,CAAC,EACpD,CAAC,GAAU,KAAe,EAAS,EAAM,cAAc;AAS7D,KAAI,MAAa,EAAM,eAAe;AACrC,IAAY,EAAM,cAAc;EAChC,IAAM,KAAa,MAClB,EAAM,cAAc,MAAM,MAAY,EAAQ,QAAQ,EAAM,IAAI,EAC3D,IAAU,EACd,KAAK,GAAO,OAAW;GAAE;GAAO;GAAO,EAAE,CACzC,QAAQ,EAAE,eAAY,CAAC,EAAU,EAAM,CAAC;AAI1C,KAAY,MAAY,CACvB,GAAG,EAAQ,QAAQ,EAAE,eAAY,CAAC,EAAU,EAAM,CAAC,EACnD,GAAG,EACH,CAAC;;AAMH,CAAI,KAAkB,EAAQ,SAAS,KACtC,EAAW,EAAE,CAAC;CAGf,IAAM,IAAe,GAAa,MAAgB;AACjD,KAAY,MAAY,EAAQ,QAAQ,EAAE,eAAY,EAAM,QAAQ,EAAI,CAAC;IACvE,EAAE,CAAC,EAIA,IAAU,EAAQ,QACtB,EAAE,eACF,CAAC,EAAM,cAAc,MAAM,MAAY,EAAQ,QAAQ,EAAM,IAAI,CAClE,EAIK,IAAS,CAAC,GAAG,EAAM,cAAc;AACvC,MAAK,IAAM,EAAE,UAAO,cAAW,EAC9B,GAAO,OAAO,KAAK,IAAI,GAAO,EAAO,OAAO,EAAE,GAAG,EAAM;CAIxD,IAAM,IAAiB,IAAI,IAC1B,EAAQ,QAAQ,EAAE,eAAY,MAAU,EAAE,CAAC,KAAK,EAAE,eAAY,EAAM,IAAI,CACxE;AACD,QACC,kBAAC,OAAD;EAAK,6BAA0B;EAAY;YACzC,CAAC,KAAkB,EAAO,SAAS,KACnC,kBAAC,GAAD;GACC,GAAI;GACG;GACC;GACQ;GAChB,UAAU;GACT,CAAA;EAEE,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { QueuedToast, ToastState } from "react-stately";
|
|
2
|
+
import type { ToastContent } from "./ToastContent.js";
|
|
3
|
+
export interface ToastRegionItemProps {
|
|
4
|
+
toast: QueuedToast<ToastContent>;
|
|
5
|
+
state: ToastState<ToastContent>;
|
|
6
|
+
/** False once the queue drops the toast, which starts its exit animation. */
|
|
7
|
+
isPresent: boolean;
|
|
8
|
+
/** Whether this toast shares the slot with one being replaced. */
|
|
9
|
+
isReplacing?: boolean;
|
|
10
|
+
/** Runs when the exit animation finishes and the toast can be unmounted. */
|
|
11
|
+
onExited: (key: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function ToastRegionItem({ toast, state, isPresent, isReplacing, onExited, }: ToastRegionItemProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useEnterAnimation as e, useExitAnimation as t } from "../../utils/animation.js";
|
|
2
|
+
import { ToastNotification as n } from "../ToastNotification/ToastNotification.js";
|
|
3
|
+
import { toastRegionItem as r } from "./ToastRegion.css.js";
|
|
4
|
+
import { jsx as i } from "react/jsx-runtime";
|
|
5
|
+
import { useEffect as a, useRef as o } from "react";
|
|
6
|
+
import { mergeProps as s, useFocusRing as c, useToast as l } from "react-aria";
|
|
7
|
+
//#region src/components/ToastRegion/ToastRegionItem.tsx
|
|
8
|
+
function u({ toast: u, state: d, isPresent: f, isReplacing: p, onExited: m }) {
|
|
9
|
+
let h = o(null), { toastProps: g, contentProps: _, titleProps: v, closeButtonProps: y } = l({ toast: u }, d, h), { isFocusVisible: b, focusProps: x } = c(), S = e(h), C = t(h, f);
|
|
10
|
+
a(() => {
|
|
11
|
+
!f && !C && m(u.key);
|
|
12
|
+
}, [
|
|
13
|
+
f,
|
|
14
|
+
C,
|
|
15
|
+
m,
|
|
16
|
+
u.key
|
|
17
|
+
]);
|
|
18
|
+
let { status: w = "success", message: T } = u.content;
|
|
19
|
+
return /* @__PURE__ */ i("div", {
|
|
20
|
+
...s(g, x),
|
|
21
|
+
ref: h,
|
|
22
|
+
tabIndex: f ? g.tabIndex : -1,
|
|
23
|
+
role: f ? g.role : void 0,
|
|
24
|
+
inert: !f,
|
|
25
|
+
className: r,
|
|
26
|
+
"data-replacing": p || void 0,
|
|
27
|
+
"data-dropped": !f || void 0,
|
|
28
|
+
"data-entering": S || void 0,
|
|
29
|
+
"data-exiting": C || void 0,
|
|
30
|
+
"data-focus-visible": b || void 0,
|
|
31
|
+
children: /* @__PURE__ */ i(n, {
|
|
32
|
+
status: w,
|
|
33
|
+
onDismiss: () => d.close(u.key),
|
|
34
|
+
dismissDisabled: !f,
|
|
35
|
+
dismissLabel: y["aria-label"],
|
|
36
|
+
messageProps: {
|
|
37
|
+
..._,
|
|
38
|
+
...v,
|
|
39
|
+
role: w === "error" ? "alert" : "status",
|
|
40
|
+
"aria-live": w === "error" ? "assertive" : "polite"
|
|
41
|
+
},
|
|
42
|
+
children: T
|
|
43
|
+
})
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { u as ToastRegionItem };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=ToastRegionItem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToastRegionItem.js","names":[],"sources":["../../../src/components/ToastRegion/ToastRegionItem.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport { mergeProps, useFocusRing, useToast } from \"react-aria\";\nimport type { QueuedToast, ToastState } from \"react-stately\";\nimport { useEnterAnimation, useExitAnimation } from \"../../utils/animation.js\";\nimport { ToastNotification } from \"../ToastNotification/ToastNotification.js\";\nimport type { ToastContent } from \"./ToastContent.js\";\nimport { toastRegionItem } from \"./ToastRegion.css.js\";\n\nexport interface ToastRegionItemProps {\n\ttoast: QueuedToast<ToastContent>;\n\tstate: ToastState<ToastContent>;\n\t/** False once the queue drops the toast, which starts its exit animation. */\n\tisPresent: boolean;\n\t/** Whether this toast shares the slot with one being replaced. */\n\tisReplacing?: boolean;\n\t/** Runs when the exit animation finishes and the toast can be unmounted. */\n\tonExited: (key: string) => void;\n}\n\nexport function ToastRegionItem({\n\ttoast,\n\tstate,\n\tisPresent,\n\tisReplacing,\n\tonExited,\n}: ToastRegionItemProps) {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst { toastProps, contentProps, titleProps, closeButtonProps } = useToast(\n\t\t{ toast },\n\t\tstate,\n\t\tref,\n\t);\n\tconst { isFocusVisible, focusProps } = useFocusRing();\n\tconst isEntering = useEnterAnimation(ref);\n\tconst isExiting = useExitAnimation(ref, isPresent);\n\n\tuseEffect(() => {\n\t\tif (!isPresent && !isExiting) {\n\t\t\tonExited(toast.key);\n\t\t}\n\t}, [isPresent, isExiting, onExited, toast.key]);\n\n\tconst { status = \"success\", message } = toast.content;\n\n\treturn (\n\t\t<div\n\t\t\t{...mergeProps(toastProps, focusProps)}\n\t\t\tref={ref}\n\t\t\t// The queue has already dropped it, so it is on screen only to finish\n\t\t\t// its travel. Taking only the container out of the tab order left a\n\t\t\t// reachable control whose key the queue no longer holds, so the dismiss\n\t\t\t// button is disabled alongside this.\n\t\t\ttabIndex={isPresent ? toastProps.tabIndex : -1}\n\t\t\t// `useToastRegion` moves focus off a closing toast by taking the first\n\t\t\t// `[role=\"alertdialog\"]` it finds in the region. A dropped toast is still\n\t\t\t// in the DOM for its travel and still sits first, so it would take the\n\t\t\t// focus meant for the arriving one — and being inert, drop it entirely.\n\t\t\trole={isPresent ? toastProps.role : undefined}\n\t\t\tinert={!isPresent}\n\t\t\tclassName={toastRegionItem}\n\t\t\tdata-replacing={isReplacing || undefined}\n\t\t\tdata-dropped={!isPresent || undefined}\n\t\t\tdata-entering={isEntering || undefined}\n\t\t\tdata-exiting={isExiting || undefined}\n\t\t\tdata-focus-visible={isFocusVisible || undefined}\n\t\t>\n\t\t\t<ToastNotification\n\t\t\t\tstatus={status}\n\t\t\t\tonDismiss={() => state.close(toast.key)}\n\t\t\t\tdismissDisabled={!isPresent}\n\t\t\t\tdismissLabel={closeButtonProps[\"aria-label\"]}\n\t\t\t\tmessageProps={{\n\t\t\t\t\t...contentProps,\n\t\t\t\t\t...titleProps,\n\t\t\t\t\t// The queue is one shared surface, so urgency has to be chosen per\n\t\t\t\t\t// toast: a failure interrupts, anything else waits its turn.\n\t\t\t\t\trole: status === \"error\" ? \"alert\" : \"status\",\n\t\t\t\t\t\"aria-live\": status === \"error\" ? \"assertive\" : \"polite\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{message}\n\t\t\t</ToastNotification>\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;AAmBA,SAAgB,EAAgB,EAC/B,UACA,UACA,cACA,gBACA,eACwB;CACxB,IAAM,IAAM,EAAuB,KAAK,EAClC,EAAE,eAAY,iBAAc,eAAY,wBAAqB,EAClE,EAAE,UAAO,EACT,GACA,EACA,EACK,EAAE,mBAAgB,kBAAe,GAAc,EAC/C,IAAa,EAAkB,EAAI,EACnC,IAAY,EAAiB,GAAK,EAAU;AAElD,SAAgB;AACf,EAAI,CAAC,KAAa,CAAC,KAClB,EAAS,EAAM,IAAI;IAElB;EAAC;EAAW;EAAW;EAAU,EAAM;EAAI,CAAC;CAE/C,IAAM,EAAE,YAAS,WAAW,eAAY,EAAM;AAE9C,QACC,kBAAC,OAAD;EACC,GAAI,EAAW,GAAY,EAAW;EACjC;EAKL,UAAU,IAAY,EAAW,WAAW;EAK5C,MAAM,IAAY,EAAW,OAAO,KAAA;EACpC,OAAO,CAAC;EACR,WAAW;EACX,kBAAgB,KAAe,KAAA;EAC/B,gBAAc,CAAC,KAAa,KAAA;EAC5B,iBAAe,KAAc,KAAA;EAC7B,gBAAc,KAAa,KAAA;EAC3B,sBAAoB,KAAkB,KAAA;YAEtC,kBAAC,GAAD;GACS;GACR,iBAAiB,EAAM,MAAM,EAAM,IAAI;GACvC,iBAAiB,CAAC;GAClB,cAAc,EAAiB;GAC/B,cAAc;IACb,GAAG;IACH,GAAG;IAGH,MAAM,MAAW,UAAU,UAAU;IACrC,aAAa,MAAW,UAAU,cAAc;IAChD;aAEA;GACkB,CAAA;EACf,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import type { QueuedToast, ToastState } from "react-stately";
|
|
3
|
+
import type { ToastContent } from "./ToastContent.js";
|
|
4
|
+
export interface ToastRegionLandmarkProps {
|
|
5
|
+
"aria-label"?: string;
|
|
6
|
+
state: ToastState<ToastContent>;
|
|
7
|
+
/** Visible toasts followed by any still playing their exit animation. */
|
|
8
|
+
toasts: QueuedToast<ToastContent>[];
|
|
9
|
+
/** Keys of the leaving toasts that held the top of the region. */
|
|
10
|
+
leavingFromTop: Set<string>;
|
|
11
|
+
onExited: (key: string) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
style?: CSSProperties;
|
|
14
|
+
}
|
|
15
|
+
export declare function ToastRegionLandmark({ state, toasts, leavingFromTop, onExited, className, style, ...props }: ToastRegionLandmarkProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { cx as e } from "../../utils/cx.js";
|
|
2
|
+
import { toastRegion as t } from "./ToastRegion.css.js";
|
|
3
|
+
import { ToastRegionItem as n } from "./ToastRegionItem.js";
|
|
4
|
+
import { jsx as r } from "react/jsx-runtime";
|
|
5
|
+
import { useEffect as i, useRef as a } from "react";
|
|
6
|
+
import { mergeProps as o, useFocusWithin as s, useHover as c, useToastRegion as l } from "react-aria";
|
|
7
|
+
//#region src/components/ToastRegion/ToastRegionLandmark.tsx
|
|
8
|
+
function u({ state: u, toasts: d, leavingFromTop: f, onExited: p, className: m, style: h, ...g }) {
|
|
9
|
+
let _ = a(null), { regionProps: v } = l(g, u, _), y = a(!1), b = a(!1), { hoverProps: x } = c({
|
|
10
|
+
onHoverStart: () => {
|
|
11
|
+
y.current = !0;
|
|
12
|
+
},
|
|
13
|
+
onHoverEnd: () => {
|
|
14
|
+
y.current = !1;
|
|
15
|
+
}
|
|
16
|
+
}), { focusWithinProps: S } = s({
|
|
17
|
+
onFocusWithin: () => {
|
|
18
|
+
b.current = !0;
|
|
19
|
+
},
|
|
20
|
+
onBlurWithin: () => {
|
|
21
|
+
b.current = !1;
|
|
22
|
+
}
|
|
23
|
+
}), C = a(u);
|
|
24
|
+
C.current = u;
|
|
25
|
+
let { visibleToasts: w } = u;
|
|
26
|
+
return i(() => {
|
|
27
|
+
if (w.length === 0) return;
|
|
28
|
+
let e = _.current, t = e?.matches(":hover") ?? !1;
|
|
29
|
+
if (!(y.current || b.current || t)) return;
|
|
30
|
+
C.current.pauseAll();
|
|
31
|
+
let n = () => {
|
|
32
|
+
document.removeEventListener("pointerover", i, !0), document.removeEventListener("pointerout", a, !0), window.removeEventListener("blur", o);
|
|
33
|
+
}, r = () => {
|
|
34
|
+
b.current || (C.current.resumeAll(), n());
|
|
35
|
+
}, i = (t) => {
|
|
36
|
+
let n = t.target;
|
|
37
|
+
e && n instanceof Node && e.contains(n) || r();
|
|
38
|
+
}, a = (e) => {
|
|
39
|
+
e.relatedTarget === null && r();
|
|
40
|
+
}, o = () => r();
|
|
41
|
+
return document.addEventListener("pointerover", i, !0), document.addEventListener("pointerout", a, !0), window.addEventListener("blur", o), n;
|
|
42
|
+
}, [w]), /* @__PURE__ */ r("div", {
|
|
43
|
+
...o(v, x, S),
|
|
44
|
+
ref: _,
|
|
45
|
+
className: e(t, m),
|
|
46
|
+
style: h,
|
|
47
|
+
children: d.map((e) => {
|
|
48
|
+
let t = u.visibleToasts.some((t) => t.key === e.key);
|
|
49
|
+
return /* @__PURE__ */ r(n, {
|
|
50
|
+
toast: e,
|
|
51
|
+
state: u,
|
|
52
|
+
isPresent: t,
|
|
53
|
+
isReplacing: !t && f.has(e.key) && u.visibleToasts.length > 0,
|
|
54
|
+
onExited: p
|
|
55
|
+
}, e.key);
|
|
56
|
+
})
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { u as ToastRegionLandmark };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=ToastRegionLandmark.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToastRegionLandmark.js","names":[],"sources":["../../../src/components/ToastRegion/ToastRegionLandmark.tsx"],"sourcesContent":["import type { CSSProperties } from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport {\n\tmergeProps,\n\tuseFocusWithin,\n\tuseHover,\n\tuseToastRegion,\n} from \"react-aria\";\nimport type { QueuedToast, ToastState } from \"react-stately\";\nimport { cx } from \"../../utils/cx.js\";\nimport type { ToastContent } from \"./ToastContent.js\";\nimport { toastRegion } from \"./ToastRegion.css.js\";\nimport { ToastRegionItem } from \"./ToastRegionItem.js\";\n\nexport interface ToastRegionLandmarkProps {\n\t\"aria-label\"?: string;\n\tstate: ToastState<ToastContent>;\n\t/** Visible toasts followed by any still playing their exit animation. */\n\ttoasts: QueuedToast<ToastContent>[];\n\t/** Keys of the leaving toasts that held the top of the region. */\n\tleavingFromTop: Set<string>;\n\tonExited: (key: string) => void;\n\tclassName?: string;\n\tstyle?: CSSProperties;\n}\n\nexport function ToastRegionLandmark({\n\tstate,\n\ttoasts,\n\tleavingFromTop,\n\tonExited,\n\tclassName,\n\tstyle,\n\t...props\n}: ToastRegionLandmarkProps) {\n\tconst ref = useRef<HTMLDivElement>(null);\n\tconst { regionProps } = useToastRegion(props, state, ref);\n\n\t// `useToastRegion` only pauses on a hover or focus change, while a toast starts\n\t// its own countdown as it mounts. A toast that takes the slot while the reader\n\t// is already on the region would therefore run down under them, since nothing\n\t// enters or leaves to trigger that pause — and on a one-slot surface that is\n\t// the ordinary path, not an edge case.\n\t//\n\t// Hover and focus are tracked apart: leaving one while the other still holds\n\t// is not the reader letting go.\n\tconst isHovered = useRef(false);\n\tconst isFocused = useRef(false);\n\tconst { hoverProps } = useHover({\n\t\tonHoverStart: () => {\n\t\t\tisHovered.current = true;\n\t\t},\n\t\tonHoverEnd: () => {\n\t\t\tisHovered.current = false;\n\t\t},\n\t});\n\tconst { focusWithinProps } = useFocusWithin({\n\t\tonFocusWithin: () => {\n\t\t\tisFocused.current = true;\n\t\t},\n\t\tonBlurWithin: () => {\n\t\t\tisFocused.current = false;\n\t\t},\n\t});\n\tconst stateRef = useRef(state);\n\tstateRef.current = state;\n\tconst { visibleToasts } = state;\n\tuseEffect(() => {\n\t\tif (visibleToasts.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst element = ref.current;\n\t\t// The landmark unmounts whenever the slot empties, so a pointer resting\n\t\t// where the next toast lands has no enter event left to report itself with.\n\t\t// The browser keeps `:hover` current across that, so ask the element.\n\t\tconst isUnderPointer = element?.matches(\":hover\") ?? false;\n\t\tif (!(isHovered.current || isFocused.current || isUnderPointer)) {\n\t\t\treturn;\n\t\t}\n\t\tstateRef.current.pauseAll();\n\n\t\t// The pause has to release itself. `useToastRegion` resumes only from its own\n\t\t// hover and focus callbacks, and `useHover` reports no hover end for a pointer\n\t\t// it never saw arrive — or for a touch pointer, which it ignores in both\n\t\t// directions. Without this the countdown never restarts, so the toast holds\n\t\t// the slot for good and the consumer never hears it close.\n\t\tconst stop = () => {\n\t\t\tdocument.removeEventListener(\"pointerover\", onPointerOver, true);\n\t\t\tdocument.removeEventListener(\"pointerout\", onPointerOut, true);\n\t\t\twindow.removeEventListener(\"blur\", onWindowBlur);\n\t\t};\n\t\tconst release = () => {\n\t\t\tif (isFocused.current) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstateRef.current.resumeAll();\n\t\t\tstop();\n\t\t};\n\t\tconst onPointerOver = (event: PointerEvent) => {\n\t\t\tconst target = event.target;\n\t\t\tif (element && target instanceof Node && element.contains(target)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\trelease();\n\t\t};\n\t\t// A pointer that leaves the document reports no further `pointerover`, so\n\t\t// watching that alone leaves the countdown paused for good. Leaving the\n\t\t// document is a null `relatedTarget`; leaving the frame entirely is the blur.\n\t\tconst onPointerOut = (event: PointerEvent) => {\n\t\t\tif (event.relatedTarget === null) {\n\t\t\t\trelease();\n\t\t\t}\n\t\t};\n\t\tconst onWindowBlur = () => release();\n\t\tdocument.addEventListener(\"pointerover\", onPointerOver, true);\n\t\tdocument.addEventListener(\"pointerout\", onPointerOut, true);\n\t\twindow.addEventListener(\"blur\", onWindowBlur);\n\t\treturn stop;\n\t}, [visibleToasts]);\n\n\treturn (\n\t\t<div\n\t\t\t{...mergeProps(regionProps, hoverProps, focusWithinProps)}\n\t\t\tref={ref}\n\t\t\tclassName={cx(toastRegion, className)}\n\t\t\tstyle={style}\n\t\t>\n\t\t\t{toasts.map((toast) => {\n\t\t\t\tconst isPresent = state.visibleToasts.some(\n\t\t\t\t\t(visible) => visible.key === toast.key,\n\t\t\t\t);\n\t\t\t\treturn (\n\t\t\t\t\t<ToastRegionItem\n\t\t\t\t\t\tkey={toast.key}\n\t\t\t\t\t\ttoast={toast}\n\t\t\t\t\t\tstate={state}\n\t\t\t\t\t\tisPresent={isPresent}\n\t\t\t\t\t\t// A toast leaving the top while another is on screen is the slot\n\t\t\t\t\t\t// changing message, so the two slide past each other rather than\n\t\t\t\t\t\t// each animating alone. The motion takes the leaving one out of\n\t\t\t\t\t\t// flow at the region's top, so it is only right for a toast that\n\t\t\t\t\t\t// sat there — one leaving from deeper in a stack stays in flow and\n\t\t\t\t\t\t// lets the stack close over it.\n\t\t\t\t\t\tisReplacing={\n\t\t\t\t\t\t\t!isPresent &&\n\t\t\t\t\t\t\tleavingFromTop.has(toast.key) &&\n\t\t\t\t\t\t\tstate.visibleToasts.length > 0\n\t\t\t\t\t\t}\n\t\t\t\t\t\tonExited={onExited}\n\t\t\t\t\t/>\n\t\t\t\t);\n\t\t\t})}\n\t\t</div>\n\t);\n}\n"],"mappings":";;;;;;;AA0BA,SAAgB,EAAoB,EACnC,UACA,WACA,mBACA,aACA,cACA,UACA,GAAG,KACyB;CAC5B,IAAM,IAAM,EAAuB,KAAK,EAClC,EAAE,mBAAgB,EAAe,GAAO,GAAO,EAAI,EAUnD,IAAY,EAAO,GAAM,EACzB,IAAY,EAAO,GAAM,EACzB,EAAE,kBAAe,EAAS;EAC/B,oBAAoB;AACnB,KAAU,UAAU;;EAErB,kBAAkB;AACjB,KAAU,UAAU;;EAErB,CAAC,EACI,EAAE,wBAAqB,EAAe;EAC3C,qBAAqB;AACpB,KAAU,UAAU;;EAErB,oBAAoB;AACnB,KAAU,UAAU;;EAErB,CAAC,EACI,IAAW,EAAO,EAAM;AAC9B,GAAS,UAAU;CACnB,IAAM,EAAE,qBAAkB;AAsD1B,QArDA,QAAgB;AACf,MAAI,EAAc,WAAW,EAC5B;EAED,IAAM,IAAU,EAAI,SAId,IAAiB,GAAS,QAAQ,SAAS,IAAI;AACrD,MAAI,EAAE,EAAU,WAAW,EAAU,WAAW,GAC/C;AAED,IAAS,QAAQ,UAAU;EAO3B,IAAM,UAAa;AAGlB,GAFA,SAAS,oBAAoB,eAAe,GAAe,GAAK,EAChE,SAAS,oBAAoB,cAAc,GAAc,GAAK,EAC9D,OAAO,oBAAoB,QAAQ,EAAa;KAE3C,UAAgB;AACjB,KAAU,YAGd,EAAS,QAAQ,WAAW,EAC5B,GAAM;KAED,KAAiB,MAAwB;GAC9C,IAAM,IAAS,EAAM;AACjB,QAAW,aAAkB,QAAQ,EAAQ,SAAS,EAAO,IAGjE,GAAS;KAKJ,KAAgB,MAAwB;AAC7C,GAAI,EAAM,kBAAkB,QAC3B,GAAS;KAGL,UAAqB,GAAS;AAIpC,SAHA,SAAS,iBAAiB,eAAe,GAAe,GAAK,EAC7D,SAAS,iBAAiB,cAAc,GAAc,GAAK,EAC3D,OAAO,iBAAiB,QAAQ,EAAa,EACtC;IACL,CAAC,EAAc,CAAC,EAGlB,kBAAC,OAAD;EACC,GAAI,EAAW,GAAa,GAAY,EAAiB;EACpD;EACL,WAAW,EAAG,GAAa,EAAU;EAC9B;YAEN,EAAO,KAAK,MAAU;GACtB,IAAM,IAAY,EAAM,cAAc,MACpC,MAAY,EAAQ,QAAQ,EAAM,IACnC;AACD,UACC,kBAAC,GAAD;IAEQ;IACA;IACI;IAOX,aACC,CAAC,KACD,EAAe,IAAI,EAAM,IAAI,IAC7B,EAAM,cAAc,SAAS;IAEpB;IACT,EAhBI,EAAM,IAgBV;IAEF;EACG,CAAA"}
|