@stratakit/structures 0.1.0 → 0.1.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/CHANGELOG.md +18 -0
- package/dist/AccordionItem.d.ts +133 -0
- package/dist/AccordionItem.js +103 -0
- package/dist/Banner.d.ts +81 -0
- package/dist/Banner.js +71 -0
- package/dist/Chip.d.ts +35 -0
- package/dist/Chip.js +41 -0
- package/dist/DEV/AccordionItem.js +109 -0
- package/dist/DEV/Banner.js +72 -0
- package/dist/DEV/Chip.js +42 -0
- package/dist/DEV/DropdownMenu.js +235 -0
- package/dist/DEV/ErrorRegion.js +164 -0
- package/dist/DEV/Table.js +151 -0
- package/dist/DEV/Tabs.js +66 -0
- package/dist/DEV/Toolbar.js +27 -0
- package/dist/DEV/Tree.js +26 -0
- package/dist/DEV/TreeItem.js +395 -0
- package/dist/DEV/index.js +21 -0
- package/dist/DEV/styles.css.js +1 -1
- package/dist/DEV/~utils.ListItem.js +49 -0
- package/dist/DEV/~utils.icons.js +61 -0
- package/dist/DropdownMenu.d.ts +113 -0
- package/dist/DropdownMenu.js +228 -0
- package/dist/ErrorRegion.d.ts +83 -0
- package/dist/ErrorRegion.js +162 -0
- package/dist/Table.d.ts +172 -0
- package/dist/Table.js +144 -0
- package/dist/Tabs.d.ts +81 -0
- package/dist/Tabs.js +62 -0
- package/dist/Toolbar.d.ts +36 -0
- package/dist/Toolbar.js +25 -0
- package/dist/Tree.d.ts +22 -0
- package/dist/Tree.js +25 -0
- package/dist/TreeItem.d.ts +183 -0
- package/dist/TreeItem.js +370 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +21 -0
- package/dist/styles.css.js +1 -1
- package/dist/~utils.ListItem.d.ts +14 -0
- package/dist/~utils.ListItem.js +46 -0
- package/dist/~utils.icons.d.ts +10 -0
- package/dist/~utils.icons.js +56 -0
- package/package.json +4 -4
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { MenuItemCheckboxProps, MenuProviderProps } from "@ariakit/react/menu";
|
|
3
|
+
import type { PredefinedSymbol } from "@stratakit/bricks/secret-internals";
|
|
4
|
+
import type { AnyString, BaseProps, FocusableProps } from "@stratakit/foundations/secret-internals";
|
|
5
|
+
interface DropdownMenuProps extends Pick<MenuProviderProps, "children" | "placement" | "open" | "setOpen" | "defaultOpen"> {
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A dropdown menu displays a list of actions or commands when the menu button is clicked.
|
|
9
|
+
*
|
|
10
|
+
* `DropdownMenu` is a compound component with subcomponents exposed for different parts.
|
|
11
|
+
*
|
|
12
|
+
* Example:
|
|
13
|
+
* ```tsx
|
|
14
|
+
* <DropdownMenu.Root>
|
|
15
|
+
* <DropdownMenu.Button>Actions</DropdownMenu.Button>
|
|
16
|
+
*
|
|
17
|
+
* <DropdownMenu.Content>
|
|
18
|
+
* <DropdownMenu.Item label="Add" />
|
|
19
|
+
* <DropdownMenu.Item label="Edit" />
|
|
20
|
+
* <DropdownMenu.Item label="Delete" />
|
|
21
|
+
* </DropdownMenu.Content>
|
|
22
|
+
* </DropdownMenu.Root>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* **Note**: `DropdownMenu` should not be used for navigation; it is only intended for actions.
|
|
26
|
+
*/
|
|
27
|
+
declare function DropdownMenuRoot(props: DropdownMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
28
|
+
declare namespace DropdownMenuRoot {
|
|
29
|
+
var displayName: string;
|
|
30
|
+
}
|
|
31
|
+
interface DropdownMenuContentProps extends FocusableProps {
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The actual "menu" portion containing the items shown within the dropdown.
|
|
35
|
+
*
|
|
36
|
+
* Should be used as a child of `DropdownMenu.Root`.
|
|
37
|
+
*/
|
|
38
|
+
declare const DropdownMenuContent: React.ForwardRefExoticComponent<DropdownMenuContentProps & React.RefAttributes<HTMLDivElement | HTMLElement>>;
|
|
39
|
+
interface DropdownMenuButtonProps extends FocusableProps<"button"> {
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The button that triggers the dropdown menu to open. Should be used as a child of `DropdownMenu.Root`.
|
|
43
|
+
*
|
|
44
|
+
* Example:
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <DropdownMenu.Button>Actions</DropdownMenu.Button>
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* By default it will render a solid `Button` with a disclosure arrow. This can be
|
|
50
|
+
* customized by passing a `render` prop.
|
|
51
|
+
*
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <DropdownMenu.Button
|
|
54
|
+
* render={<IconButton variant="ghost" label="More" icon={<Icon href={…} />} />}
|
|
55
|
+
* />
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const DropdownMenuButton: React.ForwardRefExoticComponent<DropdownMenuButtonProps & React.RefAttributes<HTMLElement | HTMLButtonElement>>;
|
|
59
|
+
interface DropdownMenuItemProps extends Omit<FocusableProps<"button">, "children">, Partial<Pick<DropdownMenuItemShortcutsProps, "shortcuts"> & Pick<DropdownMenuIconProps, "icon">> {
|
|
60
|
+
/** The primary text label for the menu-item. */
|
|
61
|
+
label: React.ReactNode;
|
|
62
|
+
/** Dot shown on the right end of the menu-item. Value will be used as accessible description. */
|
|
63
|
+
unstable_dot?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A single menu item within the dropdown menu. Should be used as a child of `DropdownMenu.Content`.
|
|
67
|
+
*
|
|
68
|
+
* Example:
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <DropdownMenu.Item label="Add" />
|
|
71
|
+
* <DropdownMenu.Item label="Edit" />
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare const DropdownMenuItem: React.ForwardRefExoticComponent<DropdownMenuItemProps & React.RefAttributes<HTMLElement | HTMLButtonElement>>;
|
|
75
|
+
interface DropdownMenuItemShortcutsProps extends Omit<BaseProps<"span">, "children"> {
|
|
76
|
+
/**
|
|
77
|
+
* A string defining the keyboard shortcut(s) associated with the menu item.
|
|
78
|
+
*
|
|
79
|
+
* ```tsx
|
|
80
|
+
* shortcuts="S" // A single key shortcut
|
|
81
|
+
* ```
|
|
82
|
+
*
|
|
83
|
+
* Multiple keys should be separated by the `+` character. If one of the keys is
|
|
84
|
+
* recognized as a symbol name or a modifier key, it will be displayed as a symbol.
|
|
85
|
+
*
|
|
86
|
+
* ```tsx
|
|
87
|
+
* shortcuts="Control+Enter" // A multi-key shortcut, displayed as "Ctrl ⏎"
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
shortcuts: AnyString | `${PredefinedSymbol}+${AnyString}`;
|
|
91
|
+
}
|
|
92
|
+
interface DropdownMenuIconProps extends BaseProps<"svg"> {
|
|
93
|
+
/**
|
|
94
|
+
* An optional icon displayed before the menu-item label.
|
|
95
|
+
*
|
|
96
|
+
* Can be a URL of an SVG from the `@stratakit/icons` package,
|
|
97
|
+
* or a custom JSX icon.
|
|
98
|
+
*/
|
|
99
|
+
icon?: string | React.JSX.Element;
|
|
100
|
+
}
|
|
101
|
+
interface DropdownMenuCheckboxItemProps extends Omit<FocusableProps<"button">, "onChange" | "children" | "name">, Pick<MenuItemCheckboxProps, "defaultChecked" | "checked" | "onChange" | "name" | "value">, Pick<DropdownMenuItemProps, "label" | "icon"> {
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A single menu item within the dropdown menu. Should be used as a child of `DropdownMenu.Content`.
|
|
105
|
+
*
|
|
106
|
+
* Example:
|
|
107
|
+
* ```tsx
|
|
108
|
+
* <DropdownMenu.CheckboxItem name="add" label="Add" />
|
|
109
|
+
* <DropdownMenu.CheckboxItem name="edit" label="Edit" />
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare const DropdownMenuCheckboxItem: React.ForwardRefExoticComponent<DropdownMenuCheckboxItemProps & React.RefAttributes<HTMLElement | HTMLButtonElement>>;
|
|
113
|
+
export { DropdownMenuRoot as Root, DropdownMenuButton as Button, DropdownMenuContent as Content, DropdownMenuItem as Item, DropdownMenuCheckboxItem as CheckboxItem, };
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button as ButtonAk } from "@ariakit/react/button";
|
|
3
|
+
import {
|
|
4
|
+
Menu,
|
|
5
|
+
MenuButton,
|
|
6
|
+
MenuItem,
|
|
7
|
+
MenuItemCheckbox,
|
|
8
|
+
MenuProvider,
|
|
9
|
+
useMenuContext
|
|
10
|
+
} from "@ariakit/react/menu";
|
|
11
|
+
import { usePopoverContext } from "@ariakit/react/popover";
|
|
12
|
+
import { useStoreState } from "@ariakit/react/store";
|
|
13
|
+
import { Button, Kbd } from "@stratakit/bricks";
|
|
14
|
+
import {
|
|
15
|
+
DisclosureArrow,
|
|
16
|
+
Dot,
|
|
17
|
+
predefinedSymbols
|
|
18
|
+
} from "@stratakit/bricks/secret-internals";
|
|
19
|
+
import { Icon } from "@stratakit/foundations";
|
|
20
|
+
import {
|
|
21
|
+
forwardRef,
|
|
22
|
+
usePopoverApi
|
|
23
|
+
} from "@stratakit/foundations/secret-internals";
|
|
24
|
+
import cx from "classnames";
|
|
25
|
+
import * as React from "react";
|
|
26
|
+
import * as ListItem from "./~utils.ListItem.js";
|
|
27
|
+
import { Checkmark } from "./~utils.icons.js";
|
|
28
|
+
function DropdownMenuRoot(props) {
|
|
29
|
+
const {
|
|
30
|
+
children,
|
|
31
|
+
placement,
|
|
32
|
+
open: openProp,
|
|
33
|
+
setOpen: setOpenProp,
|
|
34
|
+
defaultOpen: defaultOpenProp
|
|
35
|
+
} = props;
|
|
36
|
+
return /* @__PURE__ */ jsx(
|
|
37
|
+
MenuProvider,
|
|
38
|
+
{
|
|
39
|
+
placement,
|
|
40
|
+
defaultOpen: defaultOpenProp,
|
|
41
|
+
open: openProp,
|
|
42
|
+
setOpen: setOpenProp,
|
|
43
|
+
popover: usePopoverContext(),
|
|
44
|
+
children
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const DropdownMenuContent = forwardRef(
|
|
49
|
+
(props, forwardedRef) => {
|
|
50
|
+
const popover = usePopoverApi(useMenuContext());
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
Menu,
|
|
53
|
+
{
|
|
54
|
+
portal: true,
|
|
55
|
+
unmountOnHide: true,
|
|
56
|
+
...props,
|
|
57
|
+
gutter: 4,
|
|
58
|
+
style: { ...popover.style, ...props.style },
|
|
59
|
+
wrapperProps: popover.wrapperProps,
|
|
60
|
+
className: cx("\u{1F95D}-dropdown-menu", props.className),
|
|
61
|
+
ref: forwardedRef
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
const DropdownMenuButton = forwardRef(
|
|
67
|
+
(props, forwardedRef) => {
|
|
68
|
+
const { accessibleWhenDisabled = true, children, ...rest } = props;
|
|
69
|
+
const open = useStoreState(useMenuContext(), (state) => state?.open);
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
MenuButton,
|
|
72
|
+
{
|
|
73
|
+
accessibleWhenDisabled: true,
|
|
74
|
+
render: /* @__PURE__ */ jsxs(Button, { accessibleWhenDisabled, children: [
|
|
75
|
+
children,
|
|
76
|
+
/* @__PURE__ */ jsx(DisclosureArrow, {})
|
|
77
|
+
] }),
|
|
78
|
+
...rest,
|
|
79
|
+
className: cx("\u{1F95D}-dropdown-menu-button", props.className),
|
|
80
|
+
"data-has-popover-open": open || void 0,
|
|
81
|
+
ref: forwardedRef
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
const DropdownMenuItem = forwardRef(
|
|
87
|
+
(props, forwardedRef) => {
|
|
88
|
+
const { label, shortcuts, icon, unstable_dot, ...rest } = props;
|
|
89
|
+
const dotId = React.useId();
|
|
90
|
+
return /* @__PURE__ */ jsxs(
|
|
91
|
+
MenuItem,
|
|
92
|
+
{
|
|
93
|
+
accessibleWhenDisabled: true,
|
|
94
|
+
render: /* @__PURE__ */ jsx(
|
|
95
|
+
ListItem.Root,
|
|
96
|
+
{
|
|
97
|
+
render: /* @__PURE__ */ jsx(
|
|
98
|
+
ButtonAk,
|
|
99
|
+
{
|
|
100
|
+
accessibleWhenDisabled: true,
|
|
101
|
+
"aria-describedby": dotId,
|
|
102
|
+
...rest,
|
|
103
|
+
className: cx("\u{1F95D}-dropdown-menu-item", props.className),
|
|
104
|
+
ref: forwardedRef
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
children: [
|
|
110
|
+
icon ? /* @__PURE__ */ jsx(DropdownMenuIcon, { icon }) : null,
|
|
111
|
+
/* @__PURE__ */ jsx(ListItem.Content, { render: /* @__PURE__ */ jsx("span", {}), children: label }),
|
|
112
|
+
shortcuts ? /* @__PURE__ */ jsx(DropdownMenuItemShortcuts, { shortcuts }) : null,
|
|
113
|
+
unstable_dot ? /* @__PURE__ */ jsx(
|
|
114
|
+
ListItem.Decoration,
|
|
115
|
+
{
|
|
116
|
+
render: /* @__PURE__ */ jsx(Dot, { id: dotId, className: "\u{1F95D}-dropdown-menu-item-dot", children: unstable_dot })
|
|
117
|
+
}
|
|
118
|
+
) : null
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
const DropdownMenuItemShortcuts = forwardRef((props, forwardedRef) => {
|
|
125
|
+
const { shortcuts, ...rest } = props;
|
|
126
|
+
const shortcutKeys = React.useMemo(() => {
|
|
127
|
+
return shortcuts.split("+").map((key) => ({
|
|
128
|
+
key: key.trim(),
|
|
129
|
+
isSymbol: key in predefinedSymbols
|
|
130
|
+
}));
|
|
131
|
+
}, [shortcuts]);
|
|
132
|
+
return /* @__PURE__ */ jsx(
|
|
133
|
+
ListItem.Decoration,
|
|
134
|
+
{
|
|
135
|
+
render: /* @__PURE__ */ jsx("span", {}),
|
|
136
|
+
...rest,
|
|
137
|
+
className: cx("\u{1F95D}-dropdown-menu-item-shortcuts", props.className),
|
|
138
|
+
ref: forwardedRef,
|
|
139
|
+
children: shortcutKeys.map(({ key, isSymbol }, index) => {
|
|
140
|
+
if (isSymbol) {
|
|
141
|
+
return /* @__PURE__ */ jsx(
|
|
142
|
+
Kbd,
|
|
143
|
+
{
|
|
144
|
+
variant: "ghost",
|
|
145
|
+
symbol: key
|
|
146
|
+
},
|
|
147
|
+
`${key + index}`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return /* @__PURE__ */ jsx(Kbd, { variant: "ghost", children: key }, `${key + index}`);
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
const DropdownMenuIcon = forwardRef(
|
|
156
|
+
(props, forwardedRef) => {
|
|
157
|
+
const { icon, ...rest } = props;
|
|
158
|
+
return /* @__PURE__ */ jsx(
|
|
159
|
+
ListItem.Decoration,
|
|
160
|
+
{
|
|
161
|
+
render: /* @__PURE__ */ jsx(
|
|
162
|
+
Icon,
|
|
163
|
+
{
|
|
164
|
+
href: typeof icon === "string" ? icon : void 0,
|
|
165
|
+
render: React.isValidElement(icon) ? icon : void 0,
|
|
166
|
+
...rest,
|
|
167
|
+
ref: forwardedRef
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
const DropdownMenuCheckboxItem = forwardRef((props, forwardedRef) => {
|
|
175
|
+
const {
|
|
176
|
+
label,
|
|
177
|
+
icon,
|
|
178
|
+
defaultChecked,
|
|
179
|
+
checked,
|
|
180
|
+
onChange,
|
|
181
|
+
name,
|
|
182
|
+
value = defaultChecked ? "on" : void 0,
|
|
183
|
+
// For defaultChecked to work
|
|
184
|
+
...rest
|
|
185
|
+
} = props;
|
|
186
|
+
return /* @__PURE__ */ jsxs(
|
|
187
|
+
MenuItemCheckbox,
|
|
188
|
+
{
|
|
189
|
+
accessibleWhenDisabled: true,
|
|
190
|
+
defaultChecked,
|
|
191
|
+
checked,
|
|
192
|
+
name,
|
|
193
|
+
value,
|
|
194
|
+
onChange,
|
|
195
|
+
render: /* @__PURE__ */ jsx(
|
|
196
|
+
ListItem.Root,
|
|
197
|
+
{
|
|
198
|
+
render: /* @__PURE__ */ jsx(
|
|
199
|
+
ButtonAk,
|
|
200
|
+
{
|
|
201
|
+
accessibleWhenDisabled: true,
|
|
202
|
+
...rest,
|
|
203
|
+
className: cx("\u{1F95D}-dropdown-menu-item", props.className),
|
|
204
|
+
ref: forwardedRef
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
}
|
|
208
|
+
),
|
|
209
|
+
children: [
|
|
210
|
+
icon ? /* @__PURE__ */ jsx(DropdownMenuIcon, { icon }) : null,
|
|
211
|
+
/* @__PURE__ */ jsx(ListItem.Content, { render: /* @__PURE__ */ jsx("span", {}), children: label }),
|
|
212
|
+
/* @__PURE__ */ jsx(
|
|
213
|
+
ListItem.Decoration,
|
|
214
|
+
{
|
|
215
|
+
render: /* @__PURE__ */ jsx(Checkmark, { className: "\u{1F95D}-dropdown-menu-checkmark" })
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
export {
|
|
223
|
+
DropdownMenuButton as Button,
|
|
224
|
+
DropdownMenuCheckboxItem as CheckboxItem,
|
|
225
|
+
DropdownMenuContent as Content,
|
|
226
|
+
DropdownMenuItem as Item,
|
|
227
|
+
DropdownMenuRoot as Root
|
|
228
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { BaseProps } from "@stratakit/foundations/secret-internals";
|
|
3
|
+
interface ErrorRegionRootProps extends Omit<BaseProps, "children"> {
|
|
4
|
+
/**
|
|
5
|
+
* Label for the error header, usually indicating the number of errors displayed.
|
|
6
|
+
* By default this is used as a name of the region navigational landmark, however an explicit `aria-label` or `aria-labelledby` is strongly suggested.
|
|
7
|
+
*
|
|
8
|
+
* Use `undefined` if you don't want to display errors rather than conditionally rendering the component.
|
|
9
|
+
*/
|
|
10
|
+
label?: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* A list of error items where each item describes an individual error. Must be a list of `ErrorRegion.Item` components.
|
|
13
|
+
*/
|
|
14
|
+
items?: React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* The controlled expanded state of the error.
|
|
17
|
+
*/
|
|
18
|
+
expanded?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the error is expanded.
|
|
21
|
+
*
|
|
22
|
+
* Should be used with the `expanded` prop.
|
|
23
|
+
*/
|
|
24
|
+
onExpandedChange?: (expanded: boolean) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A collapsible region that displays a list of error messages, which might originate from another
|
|
28
|
+
* component, such as `Tree`.
|
|
29
|
+
*
|
|
30
|
+
* This component is rendered as a [region landmark](https://www.w3.org/WAI/ARIA/apg/patterns/landmarks/examples/region.html)
|
|
31
|
+
* and should be labelled either using `label` or `aria-label`/`aria-labelledby`. Changes to the `label` prop will be
|
|
32
|
+
* announced communicated using a [live region](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Guides/Live_regions).
|
|
33
|
+
*
|
|
34
|
+
* Example:
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <ErrorRegion.Root
|
|
37
|
+
* label="3 issues found"
|
|
38
|
+
* items={
|
|
39
|
+
* <>
|
|
40
|
+
* <ErrorRegion.Item message="…" />
|
|
41
|
+
* <ErrorRegion.Item message="…" />
|
|
42
|
+
* <ErrorRegion.Item message="…" />
|
|
43
|
+
* </>
|
|
44
|
+
* }
|
|
45
|
+
* />
|
|
46
|
+
*/
|
|
47
|
+
declare const ErrorRegionRoot: React.ForwardRefExoticComponent<ErrorRegionRootProps & React.RefAttributes<HTMLDivElement | HTMLElement>>;
|
|
48
|
+
interface ErrorRegionItemProps extends Omit<BaseProps, "children"> {
|
|
49
|
+
/**
|
|
50
|
+
* The error message. Consumers might consider using `Anchor` component to link to the associated element in the UI.
|
|
51
|
+
*/
|
|
52
|
+
message?: React.ReactNode;
|
|
53
|
+
/**
|
|
54
|
+
* The `id` of the message node which can be used to semantically associate the error item with the related UI item i.e. `Tree.Item`.
|
|
55
|
+
*/
|
|
56
|
+
messageId?: string;
|
|
57
|
+
/**
|
|
58
|
+
* The actions available for this item. Must be a list of anchors, each rendered as a button using `<Anchor render={<button />} />`.
|
|
59
|
+
*/
|
|
60
|
+
actions?: React.ReactNode;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* An individual error item within the `ErrorRegion` component. It displays an error message and optional actions.
|
|
64
|
+
*
|
|
65
|
+
* The `messageId` prop can be used to semantically associate the error item with the related UI item, such as a `Tree.Item`.
|
|
66
|
+
*
|
|
67
|
+
* Example:
|
|
68
|
+
* ```tsx
|
|
69
|
+
* <ErrorRegion.Item
|
|
70
|
+
* message={<>Something went wrong with <Anchor href="item-10001">Item 10001</Anchor>.</>}
|
|
71
|
+
* messageId="item-10001-error"
|
|
72
|
+
* actions={<Button>Retry</Button>}
|
|
73
|
+
* />
|
|
74
|
+
*
|
|
75
|
+
* <Tree.Item
|
|
76
|
+
* id="item-10001"
|
|
77
|
+
* label="Item 10001"
|
|
78
|
+
* error="item-10001-error"
|
|
79
|
+
* />
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare const ErrorRegionItem: React.ForwardRefExoticComponent<ErrorRegionItemProps & React.RefAttributes<HTMLDivElement | HTMLElement>>;
|
|
83
|
+
export { ErrorRegionRoot as Root, ErrorRegionItem as Item };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import {
|
|
3
|
+
Collection,
|
|
4
|
+
CollectionItem,
|
|
5
|
+
useCollectionStore
|
|
6
|
+
} from "@ariakit/react/collection";
|
|
7
|
+
import {
|
|
8
|
+
Dialog,
|
|
9
|
+
DialogDisclosure,
|
|
10
|
+
DialogProvider
|
|
11
|
+
} from "@ariakit/react/dialog";
|
|
12
|
+
import { Role } from "@ariakit/react/role";
|
|
13
|
+
import { useStoreState } from "@ariakit/react/store";
|
|
14
|
+
import { Button, Text, VisuallyHidden } from "@stratakit/bricks";
|
|
15
|
+
import { IconButtonPresentation } from "@stratakit/bricks/secret-internals";
|
|
16
|
+
import {
|
|
17
|
+
forwardRef,
|
|
18
|
+
useControlledState
|
|
19
|
+
} from "@stratakit/foundations/secret-internals";
|
|
20
|
+
import cx from "classnames";
|
|
21
|
+
import * as React from "react";
|
|
22
|
+
import { ChevronDown, StatusIcon } from "./~utils.icons.js";
|
|
23
|
+
const ErrorRegionRoot = forwardRef(
|
|
24
|
+
(props, forwardedRef) => {
|
|
25
|
+
const { label, items, expanded, onExpandedChange, ...rest } = props;
|
|
26
|
+
const labelId = React.useId();
|
|
27
|
+
const sectionLabelledBy = props["aria-labelledby"] ?? (props["aria-label"] ? void 0 : labelId);
|
|
28
|
+
const [open, setOpen] = useControlledState(
|
|
29
|
+
false,
|
|
30
|
+
expanded,
|
|
31
|
+
onExpandedChange
|
|
32
|
+
);
|
|
33
|
+
const containerRef = React.useRef(null);
|
|
34
|
+
const pulse = () => {
|
|
35
|
+
const el = containerRef.current;
|
|
36
|
+
if (!el) return;
|
|
37
|
+
const id = "--\u{1F95D}error-region-pulse";
|
|
38
|
+
const animations = el.getAnimations({ subtree: true });
|
|
39
|
+
if (animations.find((animation) => animation.id === id)) return;
|
|
40
|
+
el.animate(
|
|
41
|
+
[
|
|
42
|
+
{
|
|
43
|
+
boxShadow: "0 0 0 0 var(--ids-color-border-attention-base)",
|
|
44
|
+
opacity: 1
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
boxShadow: "0 0 15px 2px var(--ids-color-border-attention-base)",
|
|
48
|
+
opacity: 0.7,
|
|
49
|
+
offset: 0.5
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
boxShadow: "0 0 0 0 var(--ids-color-border-attention-base)",
|
|
53
|
+
opacity: 1
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
{
|
|
57
|
+
id,
|
|
58
|
+
duration: 600,
|
|
59
|
+
easing: "cubic-bezier(0.4, 0, 0.2, 1)",
|
|
60
|
+
pseudoElement: "::before"
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
const store = useCollectionStore({
|
|
65
|
+
setItems: (newItems) => {
|
|
66
|
+
const prevItemsSet = new Set(prevItems.map((item) => item.id));
|
|
67
|
+
const addedItems = newItems.filter(
|
|
68
|
+
(item) => !prevItemsSet.has(item.id)
|
|
69
|
+
);
|
|
70
|
+
if (addedItems.length === 0) return;
|
|
71
|
+
pulse();
|
|
72
|
+
setLiveLabel(label);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const prevItems = useStoreState(store, "items");
|
|
76
|
+
const [liveLabel, setLiveLabel] = React.useState(label);
|
|
77
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
78
|
+
/* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "polite", "aria-atomic": true, children: liveLabel === label ? liveLabel : void 0 }),
|
|
79
|
+
/* @__PURE__ */ jsx(DialogProvider, { open, setOpen, children: /* @__PURE__ */ jsx(
|
|
80
|
+
Role.section,
|
|
81
|
+
{
|
|
82
|
+
...rest,
|
|
83
|
+
"aria-labelledby": sectionLabelledBy,
|
|
84
|
+
className: cx("\u{1F95D}-error-region", props.className),
|
|
85
|
+
"data-kiwi-visible": !!label,
|
|
86
|
+
"data-kiwi-expanded": open,
|
|
87
|
+
ref: forwardedRef,
|
|
88
|
+
children: /* @__PURE__ */ jsxs("div", { className: "\u{1F95D}-error-region-container", ref: containerRef, children: [
|
|
89
|
+
/* @__PURE__ */ jsxs(
|
|
90
|
+
DialogDisclosure,
|
|
91
|
+
{
|
|
92
|
+
className: "\u{1F95D}-error-region-header",
|
|
93
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "ghost" }),
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx(StatusIcon, { tone: "attention", className: "\u{1F95D}-error-region-icon" }),
|
|
96
|
+
/* @__PURE__ */ jsx(
|
|
97
|
+
Text,
|
|
98
|
+
{
|
|
99
|
+
render: /* @__PURE__ */ jsx("span", {}),
|
|
100
|
+
id: labelId,
|
|
101
|
+
className: "\u{1F95D}-error-region-label",
|
|
102
|
+
variant: "body-sm",
|
|
103
|
+
children: label
|
|
104
|
+
}
|
|
105
|
+
),
|
|
106
|
+
/* @__PURE__ */ jsx(IconButtonPresentation, { inert: true, variant: "ghost", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
Dialog,
|
|
112
|
+
{
|
|
113
|
+
className: "\u{1F95D}-error-region-dialog",
|
|
114
|
+
portal: false,
|
|
115
|
+
modal: false,
|
|
116
|
+
autoFocusOnShow: false,
|
|
117
|
+
"aria-labelledby": labelId,
|
|
118
|
+
children: /* @__PURE__ */ jsx(
|
|
119
|
+
Collection,
|
|
120
|
+
{
|
|
121
|
+
store,
|
|
122
|
+
className: "\u{1F95D}-error-region-items",
|
|
123
|
+
role: "list",
|
|
124
|
+
children: items
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
] })
|
|
130
|
+
}
|
|
131
|
+
) })
|
|
132
|
+
] });
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
const ErrorRegionItem = forwardRef(
|
|
136
|
+
(props, forwardedRef) => {
|
|
137
|
+
const generatedId = React.useId();
|
|
138
|
+
const {
|
|
139
|
+
message,
|
|
140
|
+
messageId = `${generatedId}-message`,
|
|
141
|
+
actions,
|
|
142
|
+
...rest
|
|
143
|
+
} = props;
|
|
144
|
+
return /* @__PURE__ */ jsxs(
|
|
145
|
+
CollectionItem,
|
|
146
|
+
{
|
|
147
|
+
...rest,
|
|
148
|
+
role: "listitem",
|
|
149
|
+
className: cx("\u{1F95D}-error-region-item", props.className),
|
|
150
|
+
ref: forwardedRef,
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ jsx(Text, { id: messageId, variant: "body-sm", children: message }),
|
|
153
|
+
/* @__PURE__ */ jsx("div", { className: "\u{1F95D}-error-region-item-actions", children: actions })
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
export {
|
|
160
|
+
ErrorRegionItem as Item,
|
|
161
|
+
ErrorRegionRoot as Root
|
|
162
|
+
};
|