@nizivo/react 0.1.0 → 0.2.1
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/THIRD_PARTY_NOTICES +12 -5
- package/dist/_engines/react-aria/dialog.client.js +16 -24
- package/dist/_engines/react-aria/drawer.client.js +8 -0
- package/dist/_engines/react-aria/file-upload.client.js +50 -0
- package/dist/_engines/react-aria/menu.client.js +17 -13
- package/dist/_engines/react-aria/open-state.client.js +53 -0
- package/dist/_engines/react-aria/outside-press.client.js +61 -0
- package/dist/_engines/react-aria/overlay.client.js +6 -1
- package/dist/_engines/react-aria/popover.client.js +148 -0
- package/dist/_engines/react-aria/popup.client.js +31 -5
- package/dist/_engines/react-aria/select.client.js +22 -12
- package/dist/_engines/react-aria/toast.client.js +38 -0
- package/dist/_engines/react-aria/tooltip.client.js +11 -11
- package/dist/_internal/data-status.d.ts +3 -0
- package/dist/_internal/data-status.js +13 -0
- package/dist/_internal/glyphs.js +54 -1
- package/dist/_internal/modal.client.js +54 -0
- package/dist/_internal/nested-focus.js +33 -0
- package/dist/alert/alert-dismiss.client.d.ts +1 -1
- package/dist/alert/alert-dismiss.client.js +3 -1
- package/dist/app-shell/app-shell.d.ts +24 -0
- package/dist/app-shell/app-shell.js +56 -0
- package/dist/app-shell/index.d.ts +2 -0
- package/dist/app-shell/index.js +1 -0
- package/dist/avatar/avatar.d.ts +96 -0
- package/dist/avatar/avatar.js +94 -0
- package/dist/avatar/index.d.ts +2 -0
- package/dist/avatar/index.js +1 -0
- package/dist/banner/banner-dismiss.client.d.ts +32 -0
- package/dist/banner/banner-dismiss.client.js +38 -0
- package/dist/banner/banner.d.ts +34 -0
- package/dist/banner/banner.js +47 -0
- package/dist/banner/index.d.ts +3 -0
- package/dist/banner/index.js +3 -0
- package/dist/breadcrumb/breadcrumb.d.ts +46 -0
- package/dist/breadcrumb/breadcrumb.js +46 -0
- package/dist/breadcrumb/breadcrumb.parts.d.ts +6 -0
- package/dist/breadcrumb/breadcrumb.parts.js +9 -0
- package/dist/breadcrumb/index.d.ts +3 -0
- package/dist/breadcrumb/index.js +3 -0
- package/dist/button/button.d.ts +3 -2
- package/dist/card/card.d.ts +9 -2
- package/dist/chip-group/chip-group.client.d.ts +88 -0
- package/dist/chip-group/chip-group.client.js +157 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +1 -0
- package/dist/choice-card/choice-card.client.d.ts +66 -0
- package/dist/choice-card/choice-card.client.js +138 -0
- package/dist/choice-card/index.d.ts +2 -0
- package/dist/choice-card/index.js +1 -0
- package/dist/config/config.client.d.ts +16 -1
- package/dist/config/config.client.js +18 -3
- package/dist/config/index.d.ts +2 -2
- package/dist/contracts/index.d.ts +1 -1
- package/dist/css/app-shell.css +2 -0
- package/dist/css/avatar.css +2 -0
- package/dist/css/banner.css +2 -0
- package/dist/css/base.css +1 -1
- package/dist/css/breadcrumb.css +2 -0
- package/dist/css/card.css +1 -1
- package/dist/css/chip-group.css +2 -0
- package/dist/css/choice-card.css +2 -0
- package/dist/css/delta.css +1 -1
- package/dist/css/drawer.css +2 -0
- package/dist/css/file-upload.css +2 -0
- package/dist/css/form.css +2 -0
- package/dist/css/heading.css +1 -1
- package/dist/css/kpi-tile.css +2 -0
- package/dist/css/metric.css +2 -0
- package/dist/css/mobile-tab-bar.css +2 -0
- package/dist/css/mobile-top-bar.css +2 -0
- package/dist/css/motion.css +2 -0
- package/dist/css/nav-item.css +2 -0
- package/dist/css/number-value.css +2 -0
- package/dist/css/page-header.css +2 -0
- package/dist/css/pagination.css +2 -0
- package/dist/css/password-input.css +2 -0
- package/dist/css/popover.css +2 -0
- package/dist/css/ranked-list.css +2 -0
- package/dist/css/search-field.css +2 -0
- package/dist/css/sidebar.css +2 -0
- package/dist/css/split.css +2 -0
- package/dist/css/sticky-bar.css +2 -0
- package/dist/css/table-selection.css +2 -0
- package/dist/css/table.css +2 -0
- package/dist/css/text.css +1 -1
- package/dist/css/toast.css +2 -0
- package/dist/css/top-bar.css +2 -0
- package/dist/css/trend-card.css +2 -0
- package/dist/dialog/dialog.client.d.ts +1 -1
- package/dist/dialog/dialog.client.js +15 -37
- package/dist/drawer/drawer.client.d.ts +25 -0
- package/dist/drawer/drawer.client.js +154 -0
- package/dist/drawer/drawer.contract.d.ts +98 -0
- package/dist/drawer/drawer.parts.d.ts +6 -0
- package/dist/drawer/drawer.parts.js +16 -0
- package/dist/drawer/index.d.ts +5 -0
- package/dist/drawer/index.js +3 -0
- package/dist/field/field-scope.client.js +9 -2
- package/dist/file-upload/file-upload.client.d.ts +9 -0
- package/dist/file-upload/file-upload.client.js +406 -0
- package/dist/file-upload/file-upload.contract.d.ts +112 -0
- package/dist/file-upload/index.d.ts +3 -0
- package/dist/file-upload/index.js +1 -0
- package/dist/file-upload/seam.d.ts +1 -0
- package/dist/form/form.client.d.ts +38 -0
- package/dist/form/form.client.js +127 -0
- package/dist/form/form.d.ts +36 -0
- package/dist/form/form.js +29 -0
- package/dist/form/index.d.ts +4 -0
- package/dist/form/index.js +4 -0
- package/dist/form/submit-button.client.d.ts +20 -0
- package/dist/form/submit-button.client.js +19 -0
- package/dist/heading/heading.d.ts +4 -1
- package/dist/i18n/format-label.d.ts +24 -0
- package/dist/i18n/format-label.js +19 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.js +1 -0
- package/dist/icon-button/icon-button.client.js +1 -1
- package/dist/icon-button/icon-button.js +1 -1
- package/dist/index.d.ts +84 -5
- package/dist/index.js +49 -4
- package/dist/kpi-tile/index.d.ts +2 -0
- package/dist/kpi-tile/index.js +1 -0
- package/dist/kpi-tile/kpi-tile.d.ts +30 -0
- package/dist/kpi-tile/kpi-tile.js +48 -0
- package/dist/locales/en-US.d.ts +77 -0
- package/dist/locales/en-US.js +83 -0
- package/dist/menu/menu.contract.d.ts +1 -1
- package/dist/metric/index.d.ts +2 -0
- package/dist/metric/index.js +1 -0
- package/dist/metric/metric.d.ts +72 -0
- package/dist/metric/metric.js +116 -0
- package/dist/mobile-tab-bar/index.d.ts +4 -0
- package/dist/mobile-tab-bar/index.js +4 -0
- package/dist/mobile-tab-bar/mobile-tab-bar-inset.client.js +38 -0
- package/dist/mobile-tab-bar/mobile-tab-bar-more.client.d.ts +26 -0
- package/dist/mobile-tab-bar/mobile-tab-bar-more.client.js +60 -0
- package/dist/mobile-tab-bar/mobile-tab-bar.d.ts +39 -0
- package/dist/mobile-tab-bar/mobile-tab-bar.js +54 -0
- package/dist/mobile-tab-bar/mobile-tab-bar.parts.d.ts +6 -0
- package/dist/mobile-tab-bar/mobile-tab-bar.parts.js +9 -0
- package/dist/mobile-top-bar/index.d.ts +2 -0
- package/dist/mobile-top-bar/index.js +1 -0
- package/dist/mobile-top-bar/mobile-top-bar.d.ts +17 -0
- package/dist/mobile-top-bar/mobile-top-bar.js +31 -0
- package/dist/motion/count-up.client.d.ts +39 -0
- package/dist/motion/count-up.client.js +109 -0
- package/dist/motion/index.d.ts +2 -0
- package/dist/motion/index.js +1 -0
- package/dist/nav-item/index.d.ts +2 -0
- package/dist/nav-item/index.js +1 -0
- package/dist/nav-item/nav-item.d.ts +35 -0
- package/dist/nav-item/nav-item.js +50 -0
- package/dist/number-value/index.d.ts +2 -0
- package/dist/number-value/index.js +1 -0
- package/dist/number-value/number-value.d.ts +25 -0
- package/dist/number-value/number-value.js +26 -0
- package/dist/page-header/index.d.ts +2 -0
- package/dist/page-header/index.js +1 -0
- package/dist/page-header/page-header.d.ts +19 -0
- package/dist/page-header/page-header.js +39 -0
- package/dist/pagination/index.d.ts +2 -0
- package/dist/pagination/index.js +1 -0
- package/dist/pagination/pagination.d.ts +74 -0
- package/dist/pagination/pagination.js +136 -0
- package/dist/password-input/index.d.ts +2 -0
- package/dist/password-input/index.js +1 -0
- package/dist/password-input/password-input.client.d.ts +52 -0
- package/dist/password-input/password-input.client.js +106 -0
- package/dist/popover/index.d.ts +5 -0
- package/dist/popover/index.js +3 -0
- package/dist/popover/popover.client.d.ts +20 -0
- package/dist/popover/popover.client.js +113 -0
- package/dist/popover/popover.contract.d.ts +88 -0
- package/dist/popover/popover.parts.d.ts +6 -0
- package/dist/popover/popover.parts.js +13 -0
- package/dist/ranked-list/index.d.ts +3 -0
- package/dist/ranked-list/index.js +3 -0
- package/dist/ranked-list/ranked-list.d.ts +85 -0
- package/dist/ranked-list/ranked-list.js +153 -0
- package/dist/ranked-list/ranked-list.parts.d.ts +6 -0
- package/dist/ranked-list/ranked-list.parts.js +10 -0
- package/dist/search-field/index.d.ts +2 -0
- package/dist/search-field/index.js +1 -0
- package/dist/search-field/search-field.client.d.ts +55 -0
- package/dist/search-field/search-field.client.js +136 -0
- package/dist/select/select.contract.d.ts +1 -1
- package/dist/sidebar/index.d.ts +4 -0
- package/dist/sidebar/index.js +4 -0
- package/dist/sidebar/sidebar-item.client.d.ts +10 -0
- package/dist/sidebar/sidebar-item.client.js +55 -0
- package/dist/sidebar/sidebar.d.ts +45 -0
- package/dist/sidebar/sidebar.js +62 -0
- package/dist/sidebar/sidebar.parts.d.ts +6 -0
- package/dist/sidebar/sidebar.parts.js +11 -0
- package/dist/split/index.d.ts +3 -0
- package/dist/split/index.js +3 -0
- package/dist/split/split.d.ts +28 -0
- package/dist/split/split.js +33 -0
- package/dist/split/split.parts.d.ts +6 -0
- package/dist/split/split.parts.js +10 -0
- package/dist/sticky-bar/index.d.ts +2 -0
- package/dist/sticky-bar/index.js +1 -0
- package/dist/sticky-bar/sticky-bar.d.ts +16 -0
- package/dist/sticky-bar/sticky-bar.js +26 -0
- package/dist/styles.css +66 -7
- package/dist/table/index.d.ts +3 -0
- package/dist/table/index.js +3 -0
- package/dist/table/table.d.ts +101 -0
- package/dist/table/table.js +121 -0
- package/dist/table/table.parts.d.ts +6 -0
- package/dist/table/table.parts.js +18 -0
- package/dist/table-selection/index.d.ts +3 -0
- package/dist/table-selection/index.js +3 -0
- package/dist/table-selection/table-selection.client.d.ts +14 -0
- package/dist/table-selection/table-selection.client.js +84 -0
- package/dist/table-selection/table-selection.d.ts +34 -0
- package/dist/table-selection/table-selection.js +50 -0
- package/dist/text/text.d.ts +13 -2
- package/dist/toast/index.d.ts +4 -0
- package/dist/toast/index.js +3 -0
- package/dist/toast/seam.d.ts +2 -0
- package/dist/toast/store.d.ts +19 -0
- package/dist/toast/store.js +136 -0
- package/dist/toast/toast.contract.d.ts +56 -0
- package/dist/toast/toaster.client.d.ts +10 -0
- package/dist/toast/toaster.client.js +305 -0
- package/dist/tooltip/tooltip.contract.d.ts +1 -1
- package/dist/top-bar/index.d.ts +2 -0
- package/dist/top-bar/index.js +1 -0
- package/dist/top-bar/top-bar.d.ts +17 -0
- package/dist/top-bar/top-bar.js +35 -0
- package/dist/trend-card/index.d.ts +2 -0
- package/dist/trend-card/index.js +1 -0
- package/dist/trend-card/trend-card.d.ts +34 -0
- package/dist/trend-card/trend-card.js +74 -0
- package/package.json +167 -4
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "./seam.js";
|
|
2
|
+
import { RefObject } from "react";
|
|
3
|
+
//#region src/toast/toast.contract.d.ts
|
|
4
|
+
/** Figma "Tone". Critical is announced assertively and stays until dismissed. */
|
|
5
|
+
export type ToastTone = "neutral" | "success" | "critical";
|
|
6
|
+
/** How a message is spoken: `polite` waits for the reader to finish, `assertive` interrupts. */
|
|
7
|
+
export type Politeness = "polite" | "assertive";
|
|
8
|
+
export interface ToastAction {
|
|
9
|
+
/** The button's text, one or two words: "Undo", "View". */
|
|
10
|
+
label: string;
|
|
11
|
+
/** Runs on press; the toast then closes. */
|
|
12
|
+
onAction: () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface ToastOptions {
|
|
15
|
+
/** @default 'neutral' */
|
|
16
|
+
tone?: ToastTone;
|
|
17
|
+
/** A second line under the message. */
|
|
18
|
+
description?: string;
|
|
19
|
+
/**
|
|
20
|
+
* One action, a real button. A toast with an action stays until it is dismissed (WCAG 2.2.1,
|
|
21
|
+
* §12.6), whatever `timeout` says.
|
|
22
|
+
*/
|
|
23
|
+
action?: ToastAction;
|
|
24
|
+
/**
|
|
25
|
+
* Milliseconds before a toast without an action, and not critical, leaves. The time stops while
|
|
26
|
+
* the pointer is over the stack, while focus is in it and while the page is hidden.
|
|
27
|
+
* @default 5000
|
|
28
|
+
*/
|
|
29
|
+
timeout?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Updates the toast with this id in place, if it is still shown, instead of adding one:
|
|
32
|
+
* `toast('Saving…', { id: 'save' })`, then `toast('Saved', { id: 'save', tone: 'success' })`.
|
|
33
|
+
*/
|
|
34
|
+
id?: string;
|
|
35
|
+
/** Called once when the toast leaves, however it left. */
|
|
36
|
+
onClose?: () => void;
|
|
37
|
+
}
|
|
38
|
+
/** Strings the Toaster renders (§16.4). */
|
|
39
|
+
export interface ToasterLabels {
|
|
40
|
+
/** The region's accessible name, which names its shortcut. @default 'Notifications (F8)' */
|
|
41
|
+
region: string;
|
|
42
|
+
/** Each toast's close button. @default 'Dismiss' */
|
|
43
|
+
dismiss: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ToasterProps {
|
|
46
|
+
labels?: Partial<ToasterLabels>;
|
|
47
|
+
/** Toasts shown at once, newest last. Older ones wait, their time stopped, until one leaves. @default 3 */
|
|
48
|
+
limit?: number;
|
|
49
|
+
/**
|
|
50
|
+
* The key that moves focus into the stack and, pressed again there, back to where it was (the
|
|
51
|
+
* `KeyboardEvent.key` value). @default 'F8'
|
|
52
|
+
*/
|
|
53
|
+
hotkey?: string;
|
|
54
|
+
className?: string;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ToasterProps } from "./toast.contract.js";
|
|
2
|
+
import { ReactElement } from "react";
|
|
3
|
+
//#region src/toast/toaster.client.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Shows the toasts `toast()` adds, bottom-centre, newest last, and speaks each once. F8 moves focus
|
|
6
|
+
* to the stack; Tab moves within it; Escape dismisses the focused toast; F8 again, or Escape on the
|
|
7
|
+
* stack itself, returns focus to where it was. Render one, in the root layout.
|
|
8
|
+
*/
|
|
9
|
+
export declare function Toaster({ labels, limit, hotkey, className }: ToasterProps): ReactElement;
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../_internal/cx.js";
|
|
3
|
+
import { useLabels } from "../config/config.client.js";
|
|
4
|
+
import { CircleCheckGlyph, CircleXGlyph, CloseGlyph, InfoGlyph } from "../_internal/glyphs.js";
|
|
5
|
+
import { Button } from "../button/button.js";
|
|
6
|
+
import { markTopLayer } from "../_engines/react-aria/overlay.client.js";
|
|
7
|
+
import { iconButtonElement } from "../icon-button/icon-button-static.js";
|
|
8
|
+
import { useItem, useRegion } from "../_engines/react-aria/toast.client.js";
|
|
9
|
+
import { attach, getServerSnapshot, getSnapshot, pause, resume, show, speakThrough, subscribe, toast } from "./store.js";
|
|
10
|
+
import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
11
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { createPortal } from "react-dom";
|
|
13
|
+
//#region src/toast/toaster.client.tsx
|
|
14
|
+
const LABELS = {
|
|
15
|
+
region: "Notifications (F8)",
|
|
16
|
+
dismiss: "Dismiss"
|
|
17
|
+
};
|
|
18
|
+
/** How long a spoken message stays in the announcer, as React Aria's announcer keeps it. */
|
|
19
|
+
const SPOKEN_FOR = 7e3;
|
|
20
|
+
const GLYPHS = {
|
|
21
|
+
neutral: InfoGlyph,
|
|
22
|
+
success: CircleCheckGlyph,
|
|
23
|
+
critical: CircleXGlyph
|
|
24
|
+
};
|
|
25
|
+
const popoverApi = (element) => "showPopover" in element;
|
|
26
|
+
const noSubscription = () => () => {};
|
|
27
|
+
/** On the page in the browser only: the queue, the portals and the announcer are client state. */
|
|
28
|
+
function useHydrated() {
|
|
29
|
+
return useSyncExternalStore(noSubscription, () => true, () => false);
|
|
30
|
+
}
|
|
31
|
+
/** Focus back to where it was before F8, if that element is still on the page. */
|
|
32
|
+
function returnFocus(returnTo) {
|
|
33
|
+
const to = returnTo.current;
|
|
34
|
+
returnTo.current = null;
|
|
35
|
+
if (to?.isConnected === true) to.focus();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Two visually hidden live regions, polite and assertive, on the page from the Toaster's mount
|
|
39
|
+
* before any message, and marked top layer so a modal's aria-hidden never silences them (O-06).
|
|
40
|
+
*/
|
|
41
|
+
function Announcer() {
|
|
42
|
+
const ref = useRef(null);
|
|
43
|
+
const [said, setSaid] = useState({
|
|
44
|
+
polite: "",
|
|
45
|
+
assertive: ""
|
|
46
|
+
});
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
const element = ref.current;
|
|
49
|
+
return element === null ? void 0 : markTopLayer(element);
|
|
50
|
+
}, []);
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const timers = /* @__PURE__ */ new Set();
|
|
53
|
+
const later = (ms, run) => {
|
|
54
|
+
const timer = setTimeout(() => {
|
|
55
|
+
timers.delete(timer);
|
|
56
|
+
run();
|
|
57
|
+
}, ms);
|
|
58
|
+
timers.add(timer);
|
|
59
|
+
};
|
|
60
|
+
const unregister = speakThrough((text, politeness) => {
|
|
61
|
+
setSaid((current) => ({
|
|
62
|
+
...current,
|
|
63
|
+
[politeness]: ""
|
|
64
|
+
}));
|
|
65
|
+
later(100, () => setSaid((current) => ({
|
|
66
|
+
...current,
|
|
67
|
+
[politeness]: text
|
|
68
|
+
})));
|
|
69
|
+
later(SPOKEN_FOR, () => setSaid((current) => current[politeness] === text ? {
|
|
70
|
+
...current,
|
|
71
|
+
[politeness]: ""
|
|
72
|
+
} : current));
|
|
73
|
+
});
|
|
74
|
+
return () => {
|
|
75
|
+
unregister();
|
|
76
|
+
for (const timer of timers) clearTimeout(timer);
|
|
77
|
+
};
|
|
78
|
+
}, []);
|
|
79
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
80
|
+
ref,
|
|
81
|
+
className: "nz-toast-announcer nz-visually-hidden",
|
|
82
|
+
"data-slot": "toast-announcer",
|
|
83
|
+
"data-nz-top-layer": "",
|
|
84
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
85
|
+
"aria-live": "polite",
|
|
86
|
+
"aria-atomic": "true",
|
|
87
|
+
children: said.polite
|
|
88
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
89
|
+
"aria-live": "assertive",
|
|
90
|
+
"aria-atomic": "true",
|
|
91
|
+
children: said.assertive
|
|
92
|
+
})]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
function ToastItem({ toast, options, labels }) {
|
|
96
|
+
const ref = useRef(null);
|
|
97
|
+
const { toastProps, messageProps, descriptionProps } = useItem(toast, options, ref);
|
|
98
|
+
const { close } = options;
|
|
99
|
+
const { id } = toast;
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
const element = ref.current;
|
|
102
|
+
if (element === null) return void 0;
|
|
103
|
+
const onKey = (event) => {
|
|
104
|
+
if (event.key !== "Escape" || event.isComposing) return;
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
close(id);
|
|
108
|
+
};
|
|
109
|
+
element.addEventListener("keydown", onKey);
|
|
110
|
+
return () => element.removeEventListener("keydown", onKey);
|
|
111
|
+
}, [close, id]);
|
|
112
|
+
const Glyph = GLYPHS[toast.tone];
|
|
113
|
+
const closePart = {
|
|
114
|
+
"data-slot": "toast-close",
|
|
115
|
+
className: "nz-toast-close"
|
|
116
|
+
};
|
|
117
|
+
const { action } = toast;
|
|
118
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
119
|
+
...toastProps,
|
|
120
|
+
ref,
|
|
121
|
+
className: "nz-toast",
|
|
122
|
+
"data-slot": "toast",
|
|
123
|
+
"data-tone": toast.tone,
|
|
124
|
+
"data-nz-scheme": "inverse",
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsx("span", {
|
|
127
|
+
className: "nz-toast-icon",
|
|
128
|
+
"data-slot": "toast-icon",
|
|
129
|
+
"aria-hidden": "true",
|
|
130
|
+
children: /* @__PURE__ */ jsx(Glyph, {})
|
|
131
|
+
}),
|
|
132
|
+
/* @__PURE__ */ jsxs("div", {
|
|
133
|
+
className: "nz-toast-content",
|
|
134
|
+
"data-slot": "toast-content",
|
|
135
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
136
|
+
...messageProps,
|
|
137
|
+
className: "nz-toast-message",
|
|
138
|
+
"data-slot": "toast-message",
|
|
139
|
+
children: toast.message
|
|
140
|
+
}), toast.description !== void 0 && /* @__PURE__ */ jsx("div", {
|
|
141
|
+
...descriptionProps,
|
|
142
|
+
className: "nz-toast-description",
|
|
143
|
+
"data-slot": "toast-description",
|
|
144
|
+
children: toast.description
|
|
145
|
+
})]
|
|
146
|
+
}),
|
|
147
|
+
action !== void 0 && /* @__PURE__ */ jsx(Button, {
|
|
148
|
+
variant: "tertiary",
|
|
149
|
+
size: "sm",
|
|
150
|
+
className: "nz-toast-action",
|
|
151
|
+
"data-slot": "toast-action",
|
|
152
|
+
onClick: () => {
|
|
153
|
+
action.onAction();
|
|
154
|
+
options.close(toast.id);
|
|
155
|
+
},
|
|
156
|
+
children: action.label
|
|
157
|
+
}),
|
|
158
|
+
iconButtonElement({
|
|
159
|
+
label: labels.dismiss,
|
|
160
|
+
icon: /* @__PURE__ */ jsx(CloseGlyph, {}),
|
|
161
|
+
variant: "tertiary",
|
|
162
|
+
size: "sm",
|
|
163
|
+
...closePart,
|
|
164
|
+
onClick: () => options.close(toast.id)
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
/** The region and its toasts, once the page has hydrated. */
|
|
170
|
+
function Stack({ toasts, strings, hotkey, className }) {
|
|
171
|
+
const region = useRef(null);
|
|
172
|
+
const returnTo = useRef(null);
|
|
173
|
+
const count = toasts.length;
|
|
174
|
+
const showing = useRef(count);
|
|
175
|
+
useLayoutEffect(() => {
|
|
176
|
+
showing.current = count;
|
|
177
|
+
});
|
|
178
|
+
useEffect(() => {
|
|
179
|
+
const onKey = (event) => {
|
|
180
|
+
if (event.key !== hotkey || event.altKey || event.ctrlKey || event.metaKey) return;
|
|
181
|
+
const element = region.current;
|
|
182
|
+
if (element === null || showing.current === 0) return;
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
const active = document.activeElement;
|
|
185
|
+
if (active !== null && element.contains(active)) returnFocus(returnTo);
|
|
186
|
+
else {
|
|
187
|
+
returnTo.current = active instanceof HTMLElement ? active : null;
|
|
188
|
+
element.focus();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
document.addEventListener("keydown", onKey);
|
|
192
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
193
|
+
}, [hotkey]);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
const element = region.current;
|
|
196
|
+
if (element === null) return void 0;
|
|
197
|
+
const onKey = (event) => {
|
|
198
|
+
if (event.key !== "Escape" || event.target !== element) return;
|
|
199
|
+
event.preventDefault();
|
|
200
|
+
event.stopPropagation();
|
|
201
|
+
returnFocus(returnTo);
|
|
202
|
+
};
|
|
203
|
+
element.addEventListener("keydown", onKey);
|
|
204
|
+
return () => element.removeEventListener("keydown", onKey);
|
|
205
|
+
}, []);
|
|
206
|
+
const latest = toasts.at(-1)?.id;
|
|
207
|
+
useLayoutEffect(() => {
|
|
208
|
+
const element = region.current;
|
|
209
|
+
if (element === null) return;
|
|
210
|
+
element.hidden = latest === void 0;
|
|
211
|
+
if (!popoverApi(element)) return;
|
|
212
|
+
const open = element.matches(":popover-open");
|
|
213
|
+
if (latest === void 0) {
|
|
214
|
+
if (open) element.hidePopover();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (open && !element.contains(document.activeElement)) element.hidePopover();
|
|
218
|
+
if (!element.matches(":popover-open")) element.showPopover();
|
|
219
|
+
}, [latest]);
|
|
220
|
+
useLayoutEffect(() => {
|
|
221
|
+
const element = region.current;
|
|
222
|
+
return () => {
|
|
223
|
+
if (element === null) return;
|
|
224
|
+
element.hidden = true;
|
|
225
|
+
if (popoverApi(element) && element.matches(":popover-open")) element.hidePopover();
|
|
226
|
+
};
|
|
227
|
+
}, []);
|
|
228
|
+
useLayoutEffect(() => {
|
|
229
|
+
const element = region.current;
|
|
230
|
+
const root = document.documentElement;
|
|
231
|
+
if (element === null || count === 0) {
|
|
232
|
+
root.style.removeProperty("--nz-toast-inset");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const measure = () => {
|
|
236
|
+
const inset = Math.max(0, window.innerHeight - element.getBoundingClientRect().top);
|
|
237
|
+
root.style.setProperty("--nz-toast-inset", `${Math.ceil(inset)}px`);
|
|
238
|
+
};
|
|
239
|
+
measure();
|
|
240
|
+
const observer = new ResizeObserver(measure);
|
|
241
|
+
observer.observe(element);
|
|
242
|
+
window.addEventListener("resize", measure);
|
|
243
|
+
return () => {
|
|
244
|
+
observer.disconnect();
|
|
245
|
+
window.removeEventListener("resize", measure);
|
|
246
|
+
root.style.removeProperty("--nz-toast-inset");
|
|
247
|
+
};
|
|
248
|
+
}, [count]);
|
|
249
|
+
const options = {
|
|
250
|
+
toasts,
|
|
251
|
+
label: strings.region,
|
|
252
|
+
close: toast.dismiss,
|
|
253
|
+
pause: () => pause("region"),
|
|
254
|
+
resume: () => resume("region")
|
|
255
|
+
};
|
|
256
|
+
const regionProps = useRegion(options, region);
|
|
257
|
+
return /* @__PURE__ */ jsx("section", {
|
|
258
|
+
...regionProps,
|
|
259
|
+
ref: region,
|
|
260
|
+
popover: "manual",
|
|
261
|
+
hidden: count === 0,
|
|
262
|
+
className: cx("nz-toast-region", className),
|
|
263
|
+
"data-slot": "toast-region",
|
|
264
|
+
"data-nz-top-layer": "",
|
|
265
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsx(ToastItem, {
|
|
266
|
+
toast,
|
|
267
|
+
options,
|
|
268
|
+
labels: strings
|
|
269
|
+
}, toast.id))
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Shows the toasts `toast()` adds, bottom-centre, newest last, and speaks each once. F8 moves focus
|
|
274
|
+
* to the stack; Tab moves within it; Escape dismisses the focused toast; F8 again, or Escape on the
|
|
275
|
+
* stack itself, returns focus to where it was. Render one, in the root layout.
|
|
276
|
+
*/
|
|
277
|
+
function Toaster({ labels, limit = 3, hotkey = "F8", className }) {
|
|
278
|
+
const toasts = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot).slice(-limit);
|
|
279
|
+
const hydrated = useHydrated();
|
|
280
|
+
const ids = toasts.map((toast) => toast.id).join(" ");
|
|
281
|
+
const strings = {
|
|
282
|
+
...LABELS,
|
|
283
|
+
...useLabels("toaster", labels)
|
|
284
|
+
};
|
|
285
|
+
useEffect(attach, []);
|
|
286
|
+
useEffect(() => show(ids === "" ? [] : ids.split(" ")), [ids]);
|
|
287
|
+
useEffect(() => {
|
|
288
|
+
const update = () => document.hidden ? pause("hidden") : resume("hidden");
|
|
289
|
+
update();
|
|
290
|
+
document.addEventListener("visibilitychange", update);
|
|
291
|
+
return () => {
|
|
292
|
+
document.removeEventListener("visibilitychange", update);
|
|
293
|
+
resume("hidden");
|
|
294
|
+
};
|
|
295
|
+
}, []);
|
|
296
|
+
if (!hydrated) return /* @__PURE__ */ jsx(Fragment, {});
|
|
297
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [createPortal(/* @__PURE__ */ jsx(Stack, {
|
|
298
|
+
toasts,
|
|
299
|
+
strings,
|
|
300
|
+
hotkey,
|
|
301
|
+
className
|
|
302
|
+
}), document.body), createPortal(/* @__PURE__ */ jsx(Announcer, {}), document.body)] });
|
|
303
|
+
}
|
|
304
|
+
//#endregion
|
|
305
|
+
export { Toaster };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RenderProp } from "../contracts/render.js";
|
|
2
2
|
import { ChangeDetails, OpenReason } from "../contracts/details.js";
|
|
3
|
-
import { Align, Side } from "../contracts/state.js";
|
|
4
3
|
import "../_internal/engine-part.js";
|
|
4
|
+
import { Align, Side } from "../contracts/state.js";
|
|
5
5
|
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
6
6
|
//#region src/tooltip/tooltip.contract.d.ts
|
|
7
7
|
/** Logical side; the resolved physical side is `data-side` on the popup. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TopBar } from "./top-bar.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RenderProp } from "../contracts/render.js";
|
|
2
|
+
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
3
|
+
//#region src/top-bar/top-bar.d.ts
|
|
4
|
+
export interface TopBarProps extends Omit<ComponentPropsWithRef<"header">, "title" | "children"> {
|
|
5
|
+
/** The page's name, in Heading/H3's look (Figma's Title). Not a heading element. */
|
|
6
|
+
title?: ReactNode;
|
|
7
|
+
/** In place of `title`, for example a Breadcrumb for a page deeper in the app. */
|
|
8
|
+
start?: ReactNode;
|
|
9
|
+
/** The search, usually a `SearchField` (M49: a real input). 360 px at most. */
|
|
10
|
+
search?: ReactNode;
|
|
11
|
+
/** At the end: the notifications IconButton, the account's Avatar or menu. */
|
|
12
|
+
actions?: ReactNode;
|
|
13
|
+
render?: RenderProp<Record<string, never>>;
|
|
14
|
+
}
|
|
15
|
+
/** The bar above the page on screens 640 px and wider (AppShell's `topBar`). */
|
|
16
|
+
export declare function TopBar({ title, start, search, actions, render, className, ...rest }: TopBarProps): ReactElement;
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { cx } from "../_internal/cx.js";
|
|
2
|
+
import { renderElement } from "../contracts/render.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/top-bar/top-bar.tsx
|
|
5
|
+
/** The bar above the page on screens 640 px and wider (AppShell's `topBar`). */
|
|
6
|
+
function TopBar({ title, start, search, actions, render, className, ...rest }) {
|
|
7
|
+
return renderElement("header", render, {}, {
|
|
8
|
+
"data-slot": "top-bar",
|
|
9
|
+
...rest,
|
|
10
|
+
className: cx("nz-top-bar", className),
|
|
11
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12
|
+
/* @__PURE__ */ jsx("div", {
|
|
13
|
+
className: "nz-top-bar-start",
|
|
14
|
+
"data-slot": "top-bar-start",
|
|
15
|
+
children: start ?? (title != null && /* @__PURE__ */ jsx("span", {
|
|
16
|
+
className: "nz-top-bar-title",
|
|
17
|
+
"data-slot": "top-bar-title",
|
|
18
|
+
children: title
|
|
19
|
+
}))
|
|
20
|
+
}),
|
|
21
|
+
search != null && /* @__PURE__ */ jsx("div", {
|
|
22
|
+
className: "nz-top-bar-search",
|
|
23
|
+
"data-slot": "top-bar-search",
|
|
24
|
+
children: search
|
|
25
|
+
}),
|
|
26
|
+
actions != null && /* @__PURE__ */ jsx("div", {
|
|
27
|
+
className: "nz-top-bar-actions",
|
|
28
|
+
"data-slot": "top-bar-actions",
|
|
29
|
+
children: actions
|
|
30
|
+
})
|
|
31
|
+
] })
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { TopBar };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TrendCard } from "./trend-card.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { RenderProp } from "../contracts/render.js";
|
|
2
|
+
import { CardTone } from "../card/card.js";
|
|
3
|
+
import { DataStatus } from "../_internal/data-status.js";
|
|
4
|
+
import { MetricDataProps } from "../metric/metric.js";
|
|
5
|
+
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
6
|
+
//#region src/trend-card/trend-card.d.ts
|
|
7
|
+
export interface TrendCardState {
|
|
8
|
+
/** The status as rendered: `'empty'` when a ready card has no value. */
|
|
9
|
+
status: DataStatus;
|
|
10
|
+
pending: boolean;
|
|
11
|
+
tone: CardTone;
|
|
12
|
+
}
|
|
13
|
+
export interface TrendCardProps extends MetricDataProps, Omit<ComponentPropsWithRef<"div">, "children"> {
|
|
14
|
+
/** Card's tone; `inverse` scopes the inverse scheme. @default 'surface' */
|
|
15
|
+
tone?: CardTone;
|
|
16
|
+
/** The control at the end of the header, for example the period `Select` or a `SegmentedControl`. */
|
|
17
|
+
action?: ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* The chart, for example `<AreaChart … />` from `@nizivo/charts`. Rendered in every status, so the
|
|
20
|
+
* box stays put: give the chart the same `status` (ChartFrame keeps its box in all four).
|
|
21
|
+
*/
|
|
22
|
+
chart?: ReactNode;
|
|
23
|
+
/** The legend under the chart, for example `<Legend items={…} />`. */
|
|
24
|
+
legend?: ReactNode;
|
|
25
|
+
/** A last row, for example a link to the report. */
|
|
26
|
+
footer?: ReactNode;
|
|
27
|
+
render?: RenderProp<TrendCardState>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A headline number with its change, a period control and a comparison chart. A `role="group"`
|
|
31
|
+
* named by the label. The status contract (§16.5) applies to the headline; the slots render as given.
|
|
32
|
+
*/
|
|
33
|
+
export declare function TrendCard({ label, value, format, locale, delta, comparison, tone, action, chart, legend, footer, status, pending, empty, error, labels, render, className, ...rest }: TrendCardProps): ReactElement;
|
|
34
|
+
//#endregion
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { cx } from "../_internal/cx.js";
|
|
2
|
+
import { renderElement } from "../contracts/render.js";
|
|
3
|
+
import { dataStatusAttrs } from "../_internal/data-status.js";
|
|
4
|
+
import { Metric, resolveStatus } from "../metric/metric.js";
|
|
5
|
+
import { useId } from "react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/trend-card/trend-card.tsx
|
|
8
|
+
/**
|
|
9
|
+
* A headline number with its change, a period control and a comparison chart. A `role="group"`
|
|
10
|
+
* named by the label. The status contract (§16.5) applies to the headline; the slots render as given.
|
|
11
|
+
*/
|
|
12
|
+
function TrendCard({ label, value, format, locale, delta, comparison, tone = "surface", action, chart, legend, footer, status = "ready", pending = false, empty, error, labels, render, className, ...rest }) {
|
|
13
|
+
const labelId = useId();
|
|
14
|
+
const st = resolveStatus(status, value);
|
|
15
|
+
const busy = st === "ready" && pending;
|
|
16
|
+
return renderElement("div", render, {
|
|
17
|
+
status: st,
|
|
18
|
+
pending: busy,
|
|
19
|
+
tone
|
|
20
|
+
}, {
|
|
21
|
+
"data-slot": "trend-card",
|
|
22
|
+
"data-tone": tone,
|
|
23
|
+
"data-padding": "md",
|
|
24
|
+
"data-nz-scheme": tone === "inverse" ? "inverse" : void 0,
|
|
25
|
+
...dataStatusAttrs(st, busy),
|
|
26
|
+
role: "group",
|
|
27
|
+
"aria-labelledby": labelId,
|
|
28
|
+
...rest,
|
|
29
|
+
className: cx("nz-card", "nz-trend-card", className),
|
|
30
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31
|
+
/* @__PURE__ */ jsxs("div", {
|
|
32
|
+
className: "nz-trend-card-header",
|
|
33
|
+
"data-slot": "trend-card-header",
|
|
34
|
+
children: [/* @__PURE__ */ jsx(Metric, {
|
|
35
|
+
size: "display",
|
|
36
|
+
reserveChange: true,
|
|
37
|
+
labelId,
|
|
38
|
+
label,
|
|
39
|
+
value,
|
|
40
|
+
format,
|
|
41
|
+
locale,
|
|
42
|
+
delta,
|
|
43
|
+
comparison,
|
|
44
|
+
status,
|
|
45
|
+
pending,
|
|
46
|
+
empty,
|
|
47
|
+
error,
|
|
48
|
+
labels
|
|
49
|
+
}), action != null && /* @__PURE__ */ jsx("div", {
|
|
50
|
+
className: "nz-trend-card-action",
|
|
51
|
+
"data-slot": "trend-card-action",
|
|
52
|
+
children: action
|
|
53
|
+
})]
|
|
54
|
+
}),
|
|
55
|
+
chart != null && /* @__PURE__ */ jsx("div", {
|
|
56
|
+
className: "nz-trend-card-chart",
|
|
57
|
+
"data-slot": "trend-card-chart",
|
|
58
|
+
children: chart
|
|
59
|
+
}),
|
|
60
|
+
legend != null && /* @__PURE__ */ jsx("div", {
|
|
61
|
+
className: "nz-trend-card-legend",
|
|
62
|
+
"data-slot": "trend-card-legend",
|
|
63
|
+
children: legend
|
|
64
|
+
}),
|
|
65
|
+
footer != null && /* @__PURE__ */ jsx("div", {
|
|
66
|
+
className: "nz-trend-card-footer",
|
|
67
|
+
"data-slot": "trend-card-footer",
|
|
68
|
+
children: footer
|
|
69
|
+
})
|
|
70
|
+
] })
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { TrendCard };
|