@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,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { markTopLayer } from "./overlay.client.js";
|
|
3
|
+
import { useToast, useToastRegion } from "react-aria/useToast";
|
|
4
|
+
//#region src/_engines/react-aria/toast.client.tsx
|
|
5
|
+
/** The ToastState the hooks read, over the store: React Aria never adds a toast here. */
|
|
6
|
+
function state(options) {
|
|
7
|
+
return {
|
|
8
|
+
visibleToasts: options.toasts.map((toast) => ({
|
|
9
|
+
key: toast.id,
|
|
10
|
+
content: toast
|
|
11
|
+
})),
|
|
12
|
+
add: () => "",
|
|
13
|
+
close: options.close,
|
|
14
|
+
pauseAll: options.pause,
|
|
15
|
+
resumeAll: options.resume
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function useRegion(options, ref) {
|
|
19
|
+
const { regionProps } = useToastRegion({ "aria-label": options.label }, state(options), ref);
|
|
20
|
+
return regionProps;
|
|
21
|
+
}
|
|
22
|
+
function useItem(toast, options, ref) {
|
|
23
|
+
const { toastProps, titleProps, descriptionProps } = useToast({ toast: {
|
|
24
|
+
key: toast.id,
|
|
25
|
+
content: toast
|
|
26
|
+
} }, state(options), ref);
|
|
27
|
+
const { "aria-describedby": describedBy, ...rest } = toastProps;
|
|
28
|
+
return {
|
|
29
|
+
toastProps: toast.description === void 0 ? rest : {
|
|
30
|
+
...rest,
|
|
31
|
+
"aria-describedby": describedBy
|
|
32
|
+
},
|
|
33
|
+
messageProps: titleProps,
|
|
34
|
+
descriptionProps
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { markTopLayer, useItem, useRegion };
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { mergeProps } from "../../contracts/merge-props.js";
|
|
3
|
-
import { details } from "../../contracts/details.js";
|
|
4
3
|
import { stateAttrs } from "../../contracts/state.js";
|
|
5
4
|
import { defined, engineDom } from "./dom.js";
|
|
6
5
|
import { EngineLocale } from "./locale.client.js";
|
|
7
6
|
import { popupStyle, resolveSide, toPlacement } from "./overlay.client.js";
|
|
7
|
+
import { useOpenState } from "./open-state.client.js";
|
|
8
8
|
import { use } from "react";
|
|
9
9
|
import { jsx } from "react/jsx-runtime";
|
|
10
10
|
import { Tooltip, TooltipTrigger, TooltipTriggerStateContext, useLocale } from "react-aria-components";
|
|
11
11
|
import { useFocusable } from "react-aria/useFocusable";
|
|
12
12
|
import { useObjectRef } from "react-aria/useObjectRef";
|
|
13
13
|
//#region src/_engines/react-aria/tooltip.client.tsx
|
|
14
|
-
const Root = ({ open, defaultOpen, onOpenChange, delay, disabled, children }) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
14
|
+
const Root = ({ open, defaultOpen, onOpenChange, delay, disabled, children }) => {
|
|
15
|
+
const [isOpen, setOpen, key] = useOpenState(open, defaultOpen, onOpenChange);
|
|
16
|
+
return /* @__PURE__ */ jsx(EngineLocale, { children: /* @__PURE__ */ jsx(TooltipTrigger, {
|
|
17
|
+
delay,
|
|
18
|
+
isOpen,
|
|
19
|
+
onOpenChange: setOpen,
|
|
20
|
+
...defined({ isDisabled: disabled }),
|
|
21
|
+
children
|
|
22
|
+
}, key) });
|
|
23
|
+
};
|
|
24
24
|
/**
|
|
25
25
|
* The trigger is the consumer's own element (often a server `<Button>`), so it takes the engine's
|
|
26
26
|
* hover/focus props through `useFocusable` instead of a React Aria `Button`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/_internal/data-status.ts
|
|
2
|
+
/** The §16.5 attributes for a resolved status (ready with no value is 'empty' upstream). */
|
|
3
|
+
function dataStatusAttrs(status, pending) {
|
|
4
|
+
return {
|
|
5
|
+
"data-loading": status === "loading" ? "" : void 0,
|
|
6
|
+
"data-empty": status === "empty" ? "" : void 0,
|
|
7
|
+
"data-error": status === "error" ? "" : void 0,
|
|
8
|
+
"data-pending": pending ? "" : void 0,
|
|
9
|
+
"aria-busy": status === "loading" || pending || void 0
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { dataStatusAttrs };
|
package/dist/_internal/glyphs.js
CHANGED
|
@@ -17,6 +17,8 @@ function svg(name, paths) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
const SpinnerGlyph = () => svg("spinner", /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }));
|
|
20
|
+
/** Lucide's `chevron-right`: the Breadcrumb separator, and Pagination's next (turned for previous). */
|
|
21
|
+
const ChevronGlyph = () => svg("chevron", /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" }));
|
|
20
22
|
const ChevronsUpDownGlyph = () => svg("chevrons-up-down", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m7 15 5 5 5-5" }), /* @__PURE__ */ jsx("path", { d: "m7 9 5-5 5 5" })] }));
|
|
21
23
|
const CircleXGlyph = () => svg("circle-x", /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22
24
|
/* @__PURE__ */ jsx("circle", {
|
|
@@ -27,8 +29,59 @@ const CircleXGlyph = () => svg("circle-x", /* @__PURE__ */ jsxs(Fragment, { chil
|
|
|
27
29
|
/* @__PURE__ */ jsx("path", { d: "m15 9-6 6" }),
|
|
28
30
|
/* @__PURE__ */ jsx("path", { d: "m9 9 6 6" })
|
|
29
31
|
] }));
|
|
32
|
+
const CircleCheckGlyph = () => svg("circle-check", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("circle", {
|
|
33
|
+
cx: "12",
|
|
34
|
+
cy: "12",
|
|
35
|
+
r: "10"
|
|
36
|
+
}), /* @__PURE__ */ jsx("path", { d: "m9 12 2 2 4-4" })] }));
|
|
37
|
+
const InfoGlyph = () => svg("info", /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
|
+
/* @__PURE__ */ jsx("circle", {
|
|
39
|
+
cx: "12",
|
|
40
|
+
cy: "12",
|
|
41
|
+
r: "10"
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
44
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
45
|
+
] }));
|
|
30
46
|
const CloseGlyph = () => svg("close", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M18 6 6 18" }), /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })] }));
|
|
31
47
|
const CheckGlyph = () => svg("check", /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }));
|
|
32
48
|
const MinusGlyph = () => svg("minus", /* @__PURE__ */ jsx("path", { d: "M5 12h14" }));
|
|
49
|
+
/** PasswordInput's toggle while the password is hidden. */
|
|
50
|
+
const EyeGlyph = () => svg("eye", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }), /* @__PURE__ */ jsx("circle", {
|
|
51
|
+
cx: "12",
|
|
52
|
+
cy: "12",
|
|
53
|
+
r: "3"
|
|
54
|
+
})] }));
|
|
55
|
+
/** PasswordInput's toggle while the password is shown. */
|
|
56
|
+
const EyeOffGlyph = () => svg("eye-off", /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
|
|
58
|
+
/* @__PURE__ */ jsx("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
|
|
59
|
+
/* @__PURE__ */ jsx("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
|
|
60
|
+
/* @__PURE__ */ jsx("path", { d: "m2 2 20 20" })
|
|
61
|
+
] }));
|
|
62
|
+
/** SearchField's magnifier. */
|
|
63
|
+
const SearchGlyph = () => svg("search", /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m21 21-4.34-4.34" }), /* @__PURE__ */ jsx("circle", {
|
|
64
|
+
cx: "11",
|
|
65
|
+
cy: "11",
|
|
66
|
+
r: "8"
|
|
67
|
+
})] }));
|
|
68
|
+
/** Lucide's `ellipsis` (Feather's `more-horizontal`): MobileTabBar's More. */
|
|
69
|
+
const EllipsisGlyph = () => svg("ellipsis", /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
70
|
+
/* @__PURE__ */ jsx("circle", {
|
|
71
|
+
cx: "12",
|
|
72
|
+
cy: "12",
|
|
73
|
+
r: "1"
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsx("circle", {
|
|
76
|
+
cx: "19",
|
|
77
|
+
cy: "12",
|
|
78
|
+
r: "1"
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsx("circle", {
|
|
81
|
+
cx: "5",
|
|
82
|
+
cy: "12",
|
|
83
|
+
r: "1"
|
|
84
|
+
})
|
|
85
|
+
] }));
|
|
33
86
|
//#endregion
|
|
34
|
-
export { CheckGlyph, ChevronsUpDownGlyph, CircleXGlyph, CloseGlyph, MinusGlyph, SpinnerGlyph };
|
|
87
|
+
export { CheckGlyph, ChevronGlyph, ChevronsUpDownGlyph, CircleCheckGlyph, CircleXGlyph, CloseGlyph, EllipsisGlyph, EyeGlyph, EyeOffGlyph, InfoGlyph, MinusGlyph, SearchGlyph, SpinnerGlyph };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use, useLayoutEffect, useMemo, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/_internal/modal.client.tsx
|
|
5
|
+
const NestingContext = createContext(null);
|
|
6
|
+
/**
|
|
7
|
+
* The nesting state of one modal popup: the modal it was opened from (`parent`, null at the root),
|
|
8
|
+
* whether a modal opened from it is open (`childOpen`), and its own `nesting`, which
|
|
9
|
+
* `ModalNestingScope` hands to the modals opened inside it.
|
|
10
|
+
*/
|
|
11
|
+
function useModalNesting() {
|
|
12
|
+
const parent = use(NestingContext);
|
|
13
|
+
const [openChildren, setOpenChildren] = useState(0);
|
|
14
|
+
return {
|
|
15
|
+
parent,
|
|
16
|
+
nesting: useMemo(() => ({ childOpened: (delta) => setOpenChildren((n) => n + delta) }), []),
|
|
17
|
+
childOpen: openChildren > 0
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Wraps a modal popup's children: modals opened inside report to it, and it reports to its parent. */
|
|
21
|
+
function ModalNestingScope({ parent, nesting, children }) {
|
|
22
|
+
return /* @__PURE__ */ jsxs(NestingContext, {
|
|
23
|
+
value: nesting,
|
|
24
|
+
children: [parent !== null && /* @__PURE__ */ jsx(OpenSignal, { parent }), children]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Mounted only while this popup's content is mounted, i.e. while it is open. */
|
|
28
|
+
function OpenSignal({ parent }) {
|
|
29
|
+
useLayoutEffect(() => {
|
|
30
|
+
parent.childOpened(1);
|
|
31
|
+
return () => parent.childOpened(-1);
|
|
32
|
+
}, [parent]);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const TABBABLE = "a[href], button, input, select, textarea, summary, [tabindex], [contenteditable]:not([contenteditable=\"false\"])";
|
|
36
|
+
/** The first tabbable, enabled, rendered element inside `root`, if any. */
|
|
37
|
+
function firstTabbable(root) {
|
|
38
|
+
if (root === null) return null;
|
|
39
|
+
for (const element of root.querySelectorAll(TABBABLE)) if (element.tabIndex >= 0 && !element.matches(":disabled") && element.getClientRects().length > 0) return element;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Focus on open (§12.2) for a modal leaf whose slots start with `name`: an element the consumer
|
|
44
|
+
* marked `autoFocus` keeps it (the engine sees focus inside and leaves it); an alertdialog focuses
|
|
45
|
+
* its least destructive action, the first `<name>-close` in its footer, else any; a dialog focuses
|
|
46
|
+
* the first tabbable element in its `<name>-body`; otherwise the engine focuses the popup itself, so
|
|
47
|
+
* a long text starts at its top.
|
|
48
|
+
*/
|
|
49
|
+
function initialFocus(name, role) {
|
|
50
|
+
const close = `[data-slot="${name}-close"]:not(:disabled)`;
|
|
51
|
+
return (content) => role === "alertdialog" ? content.querySelector(`[data-slot="${name}-footer"] ${close}`) ?? content.querySelector(close) : firstTabbable(content.querySelector(`[data-slot="${name}-body"]`));
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { ModalNestingScope, firstTabbable, initialFocus, useModalNesting };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/_internal/nested-focus.ts
|
|
2
|
+
/**
|
|
3
|
+
* True while an input's keydown is being handled. An arrow key moves focus to the next radio as the
|
|
4
|
+
* keydown's default action, in the same task, so a focus seen while this is set came from a key.
|
|
5
|
+
*/
|
|
6
|
+
let keyDown = false;
|
|
7
|
+
function markKeyDown() {
|
|
8
|
+
keyDown = true;
|
|
9
|
+
setTimeout(() => {
|
|
10
|
+
keyDown = false;
|
|
11
|
+
}, 0);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* WebKit does not match :focus-visible on a radio reached with an arrow key, so the label marks a
|
|
15
|
+
* keyboard focus itself (nz.base draws the outline for `[data-focus-visible]`). Written on the DOM,
|
|
16
|
+
* not through state: re-rendering a focused radio makes React re-write its `name`, and Chromium then
|
|
17
|
+
* stops treating an unchecked group as one Tab stop.
|
|
18
|
+
*/
|
|
19
|
+
function markFocus(event) {
|
|
20
|
+
const visible = event.currentTarget.matches(":focus-visible") || keyDown;
|
|
21
|
+
event.currentTarget.parentElement?.toggleAttribute("data-focus-visible", visible);
|
|
22
|
+
}
|
|
23
|
+
function clearFocus(event) {
|
|
24
|
+
event.currentTarget.parentElement?.removeAttribute("data-focus-visible");
|
|
25
|
+
}
|
|
26
|
+
/** Drops `undefined` values, so a spread never writes an empty attribute or handler. */
|
|
27
|
+
function definedProps(props) {
|
|
28
|
+
const out = {};
|
|
29
|
+
for (const [key, value] of Object.entries(props)) if (value !== void 0) out[key] = value;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { clearFocus, definedProps, markFocus, markKeyDown };
|
|
@@ -28,5 +28,5 @@ export interface AlertDismissProps extends Omit<ComponentPropsWithRef<"button">,
|
|
|
28
28
|
* With no `render`, an IconButton (tertiary, `sm`: the 16 px glyph Figma draws, in a 32 px target)
|
|
29
29
|
* with no tooltip, as `Dialog.Close` does.
|
|
30
30
|
*/
|
|
31
|
-
export declare function AlertDismiss({ onDismiss, labels, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }: AlertDismissProps): ReactElement;
|
|
31
|
+
export declare function AlertDismiss({ onDismiss, labels: labelsProp, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }: AlertDismissProps): ReactElement;
|
|
32
32
|
//#endregion
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cx } from "../_internal/cx.js";
|
|
3
3
|
import { renderElement } from "../contracts/render.js";
|
|
4
|
+
import { useLabels } from "../config/config.client.js";
|
|
4
5
|
import { CloseGlyph } from "../_internal/glyphs.js";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
//#region src/alert/alert-dismiss.client.tsx
|
|
@@ -8,7 +9,8 @@ import { jsx } from "react/jsx-runtime";
|
|
|
8
9
|
* With no `render`, an IconButton (tertiary, `sm`: the 16 px glyph Figma draws, in a 32 px target)
|
|
9
10
|
* with no tooltip, as `Dialog.Close` does.
|
|
10
11
|
*/
|
|
11
|
-
function AlertDismiss({ onDismiss, labels, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }) {
|
|
12
|
+
function AlertDismiss({ onDismiss, labels: labelsProp, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }) {
|
|
13
|
+
const labels = useLabels("alertDismiss", labelsProp);
|
|
12
14
|
const own = render === void 0;
|
|
13
15
|
return renderElement("button", render, {}, {
|
|
14
16
|
"data-slot": "alert-dismiss",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
2
|
+
//#region src/app-shell/app-shell.d.ts
|
|
3
|
+
/** Strings this part renders (§16.4). */
|
|
4
|
+
export interface AppShellLabels {
|
|
5
|
+
/** The skip link, the first Tab stop. @default 'Skip to content' */
|
|
6
|
+
skip: string;
|
|
7
|
+
}
|
|
8
|
+
export interface AppShellProps extends Omit<ComponentPropsWithRef<"div">, "children"> {
|
|
9
|
+
/** A `Sidebar`, shown from 640 px. */
|
|
10
|
+
sidebar?: ReactNode;
|
|
11
|
+
/** A `TopBar`, shown from 640 px. */
|
|
12
|
+
topBar?: ReactNode;
|
|
13
|
+
/** A `MobileTopBar`, shown below 640 px. */
|
|
14
|
+
mobileTopBar?: ReactNode;
|
|
15
|
+
/** A `MobileTabBar`, pinned to the bottom below 640 px. */
|
|
16
|
+
tabBar?: ReactNode;
|
|
17
|
+
/** The `<main>` element's id, the skip link's target. Generated when absent. */
|
|
18
|
+
mainId?: string;
|
|
19
|
+
labels?: Partial<AppShellLabels>;
|
|
20
|
+
/** The page, inside `<main>`. */
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
export declare function AppShell({ sidebar, topBar, mobileTopBar, tabBar, mainId, labels, className, children, ...rest }: AppShellProps): ReactElement;
|
|
24
|
+
//#endregion
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cx } from "../_internal/cx.js";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/app-shell/app-shell.tsx
|
|
5
|
+
function AppShell({ sidebar, topBar, mobileTopBar, tabBar, mainId, labels, className, children, ...rest }) {
|
|
6
|
+
const generated = useId();
|
|
7
|
+
const main = mainId ?? `nz-main${generated.replaceAll(/[^\w-]/g, "")}`;
|
|
8
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
9
|
+
"data-slot": "app-shell",
|
|
10
|
+
...rest,
|
|
11
|
+
className: cx("nz-app-shell", className),
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ jsx("a", {
|
|
14
|
+
className: "nz-app-shell-skip",
|
|
15
|
+
"data-slot": "app-shell-skip",
|
|
16
|
+
href: `#${main}`,
|
|
17
|
+
children: labels?.skip ?? "Skip to content"
|
|
18
|
+
}),
|
|
19
|
+
sidebar != null && /* @__PURE__ */ jsx("div", {
|
|
20
|
+
className: "nz-app-shell-sidebar",
|
|
21
|
+
"data-slot": "app-shell-sidebar",
|
|
22
|
+
children: sidebar
|
|
23
|
+
}),
|
|
24
|
+
/* @__PURE__ */ jsxs("div", {
|
|
25
|
+
className: "nz-app-shell-frame",
|
|
26
|
+
"data-slot": "app-shell-frame",
|
|
27
|
+
children: [
|
|
28
|
+
topBar != null && /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "nz-app-shell-top-bar",
|
|
30
|
+
"data-slot": "app-shell-top-bar",
|
|
31
|
+
children: topBar
|
|
32
|
+
}),
|
|
33
|
+
mobileTopBar != null && /* @__PURE__ */ jsx("div", {
|
|
34
|
+
className: "nz-app-shell-mobile-top-bar",
|
|
35
|
+
"data-slot": "app-shell-mobile-top-bar",
|
|
36
|
+
children: mobileTopBar
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsx("main", {
|
|
39
|
+
id: main,
|
|
40
|
+
tabIndex: -1,
|
|
41
|
+
className: "nz-app-shell-main",
|
|
42
|
+
"data-slot": "app-shell-main",
|
|
43
|
+
children
|
|
44
|
+
})
|
|
45
|
+
]
|
|
46
|
+
}),
|
|
47
|
+
tabBar != null && /* @__PURE__ */ jsx("div", {
|
|
48
|
+
className: "nz-app-shell-tab-bar",
|
|
49
|
+
"data-slot": "app-shell-tab-bar",
|
|
50
|
+
children: tabBar
|
|
51
|
+
})
|
|
52
|
+
]
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { AppShell };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AppShell } from "./app-shell.js";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { RenderProp } from "../contracts/render.js";
|
|
2
|
+
import { PluralLabel } from "../i18n/format-label.js";
|
|
3
|
+
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
4
|
+
//#region src/avatar/avatar.d.ts
|
|
5
|
+
/** Figma "Size": Sm 24, Md 32, Lg 40, XL 56 px. */
|
|
6
|
+
export type AvatarSize = "sm" | "md" | "lg" | "xl";
|
|
7
|
+
export interface AvatarState {
|
|
8
|
+
size: AvatarSize;
|
|
9
|
+
/** An image source or `image` element was given. */
|
|
10
|
+
image: boolean;
|
|
11
|
+
decorative: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface AvatarProps extends Omit<ComponentPropsWithRef<"span">, "children"> {
|
|
14
|
+
/**
|
|
15
|
+
* The person's or store's name. It is the avatar's accessible name (unless `decorative`) and the
|
|
16
|
+
* source of the initials.
|
|
17
|
+
*/
|
|
18
|
+
name: string;
|
|
19
|
+
/**
|
|
20
|
+
* The photo. Without it (or `image`), the initials show. Pass it only when the image is known to
|
|
21
|
+
* load: if it fails, Chromium and Firefox show the initials, but WebKit (Safari) draws its
|
|
22
|
+
* broken-image mark over them, and no script-free markup avoids that. For a URL that may be gone,
|
|
23
|
+
* check it on the server and leave `src` out (the initials render), or render the initials alone.
|
|
24
|
+
*/
|
|
25
|
+
src?: string;
|
|
26
|
+
/** Passed to the `<img>` with `src`. */
|
|
27
|
+
srcSet?: string;
|
|
28
|
+
sizes?: string;
|
|
29
|
+
/**
|
|
30
|
+
* A framework image element in place of `src`, for example `image={<NextImage src={photo} alt="" />}`.
|
|
31
|
+
* It is cloned with the image slot's class and `alt=""`, and laid over the initials like `src`.
|
|
32
|
+
*/
|
|
33
|
+
image?: ReactElement<{
|
|
34
|
+
className?: string | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Replaces the initials derived from `name` (the first letters of the first and last words).
|
|
38
|
+
* Figma: at most two letters.
|
|
39
|
+
*/
|
|
40
|
+
initials?: string;
|
|
41
|
+
/** @default 'md' */
|
|
42
|
+
size?: AvatarSize;
|
|
43
|
+
/**
|
|
44
|
+
* The name is printed next to the avatar, so the avatar is hidden from assistive technology
|
|
45
|
+
* (Figma: `alt=""` when the name is printed next to it). @default false
|
|
46
|
+
*/
|
|
47
|
+
decorative?: boolean;
|
|
48
|
+
/** Composition of the root `<span>`. */
|
|
49
|
+
render?: RenderProp<AvatarState>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* A person's or store's photo laid over their initials, with no script: the initials are always
|
|
53
|
+
* rendered, so a missing photo needs no `onError`. Known limit: a `src` that fails to load shows
|
|
54
|
+
* WebKit's (Safari's) broken-image mark over the initials, with no script-free fix; pass `src` only
|
|
55
|
+
* when it is known to load, or leave it out and the initials show.
|
|
56
|
+
*/
|
|
57
|
+
export declare function Avatar({ name, src, srcSet, sizes, image, initials, size, decorative, render, className, ...rest }: AvatarProps): ReactElement;
|
|
58
|
+
/**
|
|
59
|
+
* AvatarGroup's own strings (§16.4): serialisable, English defaults. `{count}` is replaced by the
|
|
60
|
+
* formatted count, and a `PluralLabel` (`{ one, few, many, other }`, the locale's CLDR categories)
|
|
61
|
+
* picks its template by that count through `formatLabel`.
|
|
62
|
+
*/
|
|
63
|
+
export interface AvatarGroupLabels {
|
|
64
|
+
/** The visible count after the shown avatars. @default '+{count}' */
|
|
65
|
+
overflow: string | PluralLabel;
|
|
66
|
+
/** The count's words in the group's accessible name ("Maya, Jonah, Priya and 4 more"). @default '{count} more' */
|
|
67
|
+
more: string | PluralLabel;
|
|
68
|
+
}
|
|
69
|
+
export interface AvatarGroupState {
|
|
70
|
+
size: AvatarSize;
|
|
71
|
+
/** People not shown as an avatar. */
|
|
72
|
+
hidden: number;
|
|
73
|
+
}
|
|
74
|
+
export interface AvatarGroupProps extends ComponentPropsWithRef<"span"> {
|
|
75
|
+
/** How many avatars to show before the count. Figma: up to 3. @default 3 */
|
|
76
|
+
max?: number;
|
|
77
|
+
/**
|
|
78
|
+
* Everyone in the group, when it is more than the avatars passed (the server sends three faces and
|
|
79
|
+
* a count). @default the number of avatars
|
|
80
|
+
*/
|
|
81
|
+
total?: number;
|
|
82
|
+
/** Applied to every avatar and the count. @default 'md' */
|
|
83
|
+
size?: AvatarSize;
|
|
84
|
+
/** BCP 47 tag for the count and the list of names. @default 'en-US' */
|
|
85
|
+
locale?: string;
|
|
86
|
+
labels?: Partial<AvatarGroupLabels>;
|
|
87
|
+
render?: RenderProp<AvatarGroupState>;
|
|
88
|
+
/** `Avatar` elements. */
|
|
89
|
+
children?: ReactNode;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A stack of avatars and a count, announced as one item (Figma: "Maya, Jonah, Priya and 4 more"):
|
|
93
|
+
* the root is `role="img"` named by the list of names, and every avatar inside is decorative.
|
|
94
|
+
*/
|
|
95
|
+
export declare function AvatarGroup({ max, total, size, locale, labels, render, className, children, ...rest }: AvatarGroupProps): ReactElement;
|
|
96
|
+
//#endregion
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { cx } from "../_internal/cx.js";
|
|
2
|
+
import { renderElement } from "../contracts/render.js";
|
|
3
|
+
import { formatLabel } from "../i18n/format-label.js";
|
|
4
|
+
import { Children, cloneElement, isValidElement } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/avatar/avatar.tsx
|
|
7
|
+
/** A word's first character (a whole code point, so an astral letter is not split). */
|
|
8
|
+
const letter = (word) => Array.from(word)[0] ?? "";
|
|
9
|
+
/** The first and last words' first letters, upper-cased; one word gives one letter. */
|
|
10
|
+
function avatarInitials(name) {
|
|
11
|
+
const words = name.trim().split(/\s+/u).filter((word) => word !== "");
|
|
12
|
+
const first = words[0];
|
|
13
|
+
if (first === void 0) return "";
|
|
14
|
+
const last = words.length > 1 ? words[words.length - 1] : void 0;
|
|
15
|
+
return (letter(first) + (last === void 0 ? "" : letter(last))).toUpperCase();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A person's or store's photo laid over their initials, with no script: the initials are always
|
|
19
|
+
* rendered, so a missing photo needs no `onError`. Known limit: a `src` that fails to load shows
|
|
20
|
+
* WebKit's (Safari's) broken-image mark over the initials, with no script-free fix; pass `src` only
|
|
21
|
+
* when it is known to load, or leave it out and the initials show.
|
|
22
|
+
*/
|
|
23
|
+
function Avatar({ name, src, srcSet, sizes, image, initials, size = "md", decorative = false, render, className, ...rest }) {
|
|
24
|
+
return renderElement("span", render, {
|
|
25
|
+
size,
|
|
26
|
+
image: src !== void 0 || image !== void 0,
|
|
27
|
+
decorative
|
|
28
|
+
}, {
|
|
29
|
+
"data-slot": "avatar",
|
|
30
|
+
"data-size": size,
|
|
31
|
+
...decorative ? { "aria-hidden": true } : {
|
|
32
|
+
role: "img",
|
|
33
|
+
"aria-label": name
|
|
34
|
+
},
|
|
35
|
+
...rest,
|
|
36
|
+
className: cx("nz-avatar", className),
|
|
37
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
38
|
+
className: "nz-avatar-initials",
|
|
39
|
+
"data-slot": "avatar-initials",
|
|
40
|
+
"aria-hidden": "true",
|
|
41
|
+
children: initials ?? avatarInitials(name)
|
|
42
|
+
}), image !== void 0 ? cloneElement(image, {
|
|
43
|
+
"data-slot": "avatar-image",
|
|
44
|
+
alt: "",
|
|
45
|
+
className: cx("nz-avatar-image", image.props.className)
|
|
46
|
+
}) : src !== void 0 && /* @__PURE__ */ jsx("img", {
|
|
47
|
+
className: "nz-avatar-image",
|
|
48
|
+
"data-slot": "avatar-image",
|
|
49
|
+
alt: "",
|
|
50
|
+
src,
|
|
51
|
+
srcSet,
|
|
52
|
+
sizes,
|
|
53
|
+
loading: "lazy",
|
|
54
|
+
decoding: "async"
|
|
55
|
+
})] })
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A stack of avatars and a count, announced as one item (Figma: "Maya, Jonah, Priya and 4 more"):
|
|
60
|
+
* the root is `role="img"` named by the list of names, and every avatar inside is decorative.
|
|
61
|
+
*/
|
|
62
|
+
function AvatarGroup({ max = 3, total, size = "md", locale = "en-US", labels, render, className, children, ...rest }) {
|
|
63
|
+
const avatars = Children.toArray(children).filter((child) => isValidElement(child));
|
|
64
|
+
const shown = avatars.slice(0, Math.max(0, max));
|
|
65
|
+
const hidden = Math.max(0, (total ?? avatars.length) - shown.length);
|
|
66
|
+
const names = shown.map((avatar) => avatar.props.name);
|
|
67
|
+
if (hidden > 0) names.push(formatLabel(labels?.more ?? "{count} more", { count: hidden }, locale));
|
|
68
|
+
const label = new Intl.ListFormat(locale, {
|
|
69
|
+
style: "long",
|
|
70
|
+
type: "conjunction"
|
|
71
|
+
}).format(names);
|
|
72
|
+
return renderElement("span", render, {
|
|
73
|
+
size,
|
|
74
|
+
hidden
|
|
75
|
+
}, {
|
|
76
|
+
"data-slot": "avatar-group",
|
|
77
|
+
"data-size": size,
|
|
78
|
+
role: "img",
|
|
79
|
+
"aria-label": label,
|
|
80
|
+
...rest,
|
|
81
|
+
className: cx("nz-avatar-group", className),
|
|
82
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [shown.map((avatar) => cloneElement(avatar, {
|
|
83
|
+
size,
|
|
84
|
+
decorative: true
|
|
85
|
+
})), hidden > 0 && /* @__PURE__ */ jsx("span", {
|
|
86
|
+
className: "nz-avatar-group-more",
|
|
87
|
+
"data-slot": "avatar-group-more",
|
|
88
|
+
"aria-hidden": "true",
|
|
89
|
+
children: formatLabel(labels?.overflow ?? "+{count}", { count: hidden }, locale)
|
|
90
|
+
})] })
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { Avatar, AvatarGroup, avatarInitials };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Avatar, AvatarGroup, AvatarGroupLabels, AvatarGroupProps, AvatarGroupState, AvatarProps, AvatarSize, AvatarState } from "./avatar.js";
|
|
2
|
+
export { Avatar, AvatarGroup, type AvatarGroupLabels, type AvatarGroupProps, type AvatarGroupState, type AvatarProps, type AvatarSize, type AvatarState };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Avatar, AvatarGroup } from "./avatar.js";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { RenderProp } from "../contracts/render.js";
|
|
2
|
+
import { ComponentPropsWithRef, ReactElement, ReactNode } from "react";
|
|
3
|
+
//#region src/banner/banner-dismiss.client.d.ts
|
|
4
|
+
/** Strings this part renders (§16.4). */
|
|
5
|
+
export interface BannerDismissLabels {
|
|
6
|
+
/** The button's accessible name. @default 'Dismiss' */
|
|
7
|
+
dismiss: string;
|
|
8
|
+
}
|
|
9
|
+
export interface BannerDismissProps extends Omit<ComponentPropsWithRef<"button">, "children"> {
|
|
10
|
+
/**
|
|
11
|
+
* Called with no arguments, so a Server Component can pass a Server Action. The Banner does not hide
|
|
12
|
+
* itself: the caller stops rendering it.
|
|
13
|
+
*/
|
|
14
|
+
onDismiss?: () => void;
|
|
15
|
+
labels?: Partial<BannerDismissLabels>;
|
|
16
|
+
/** The close glyph, decorative. Defaults to the internal `close` glyph (Lucide's `x`). */
|
|
17
|
+
children?: ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Your element in place of the default IconButton; it must render a `<button>`. A plain element
|
|
20
|
+
* keeps its own look. An IconButton, for example
|
|
21
|
+
* `render={<IconButton icon={<X />} label="Dismiss" variant="tertiary" />}` to add its
|
|
22
|
+
* tooltip, keeps its variant and size, and in a Banner takes the icon/secondary glyph colour, as a
|
|
23
|
+
* composed IconButton does in `Dialog.Close`.
|
|
24
|
+
*/
|
|
25
|
+
render?: RenderProp<Record<string, never>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* With no `render`, an IconButton (tertiary, `md`: a 20 px glyph, Figma draws 18, in a 40 px target,
|
|
29
|
+
* the height of the Banner's action) with no tooltip, as `Dialog.Close` does.
|
|
30
|
+
*/
|
|
31
|
+
export declare function BannerDismiss({ onDismiss, labels: labelsProp, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }: BannerDismissProps): ReactElement;
|
|
32
|
+
//#endregion
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cx } from "../_internal/cx.js";
|
|
3
|
+
import { renderElement } from "../contracts/render.js";
|
|
4
|
+
import { useLabels } from "../config/config.client.js";
|
|
5
|
+
import { CloseGlyph } from "../_internal/glyphs.js";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/banner/banner-dismiss.client.tsx
|
|
8
|
+
/**
|
|
9
|
+
* With no `render`, an IconButton (tertiary, `md`: a 20 px glyph, Figma draws 18, in a 40 px target,
|
|
10
|
+
* the height of the Banner's action) with no tooltip, as `Dialog.Close` does.
|
|
11
|
+
*/
|
|
12
|
+
function BannerDismiss({ onDismiss, labels: labelsProp, "aria-label": ariaLabel, type, render, className, onClick, children, ...rest }) {
|
|
13
|
+
const labels = useLabels("bannerDismiss", labelsProp);
|
|
14
|
+
const own = render === void 0;
|
|
15
|
+
return renderElement("button", render, {}, {
|
|
16
|
+
"data-slot": "banner-dismiss",
|
|
17
|
+
...own && {
|
|
18
|
+
"data-variant": "tertiary",
|
|
19
|
+
"data-size": "md"
|
|
20
|
+
},
|
|
21
|
+
...rest,
|
|
22
|
+
type: own ? type ?? "button" : void 0,
|
|
23
|
+
"aria-label": ariaLabel ?? labels?.dismiss ?? "Dismiss",
|
|
24
|
+
className: cx("nz-banner-dismiss", own && "nz-icon-button", "nz-hit-target", className),
|
|
25
|
+
onClick: (event) => {
|
|
26
|
+
onClick?.(event);
|
|
27
|
+
if (!event.defaultPrevented) onDismiss?.();
|
|
28
|
+
},
|
|
29
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
30
|
+
className: "nz-icon-button-icon",
|
|
31
|
+
"data-slot": "icon-button-icon",
|
|
32
|
+
"aria-hidden": "true",
|
|
33
|
+
children: children ?? /* @__PURE__ */ jsx(CloseGlyph, {})
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { BannerDismiss };
|