@stratakit/structures 0.4.5 → 0.5.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/dist/AccordionItem.d.ts +1 -1
  3. package/dist/AccordionItem.js +69 -124
  4. package/dist/Banner.js +149 -120
  5. package/dist/Chip.js +86 -58
  6. package/dist/DEV/AccordionItem.js +2 -0
  7. package/dist/DEV/Banner.js +3 -0
  8. package/dist/DEV/Chip.js +3 -0
  9. package/dist/DEV/Dialog.js +2 -0
  10. package/dist/DEV/DropdownMenu.js +2 -0
  11. package/dist/DEV/ErrorRegion.js +5 -11
  12. package/dist/DEV/NavigationList.js +130 -0
  13. package/dist/DEV/NavigationRail.js +6 -4
  14. package/dist/DEV/Popover.js +90 -0
  15. package/dist/DEV/Table.js +3 -0
  16. package/dist/DEV/Tabs.js +3 -1
  17. package/dist/DEV/Toolbar.js +2 -0
  18. package/dist/DEV/Tree.js +2 -0
  19. package/dist/DEV/TreeItem.js +7 -2
  20. package/dist/DEV/index.js +4 -0
  21. package/dist/DEV/styles.css.js +1 -1
  22. package/dist/DEV/~utils.ListItem.js +3 -3
  23. package/dist/DEV/~utils.useInit.js +16 -0
  24. package/dist/Dialog.js +142 -132
  25. package/dist/DropdownMenu.js +217 -234
  26. package/dist/ErrorRegion.d.ts +18 -8
  27. package/dist/ErrorRegion.js +129 -154
  28. package/dist/NavigationList.d.ts +110 -0
  29. package/dist/NavigationList.js +130 -0
  30. package/dist/NavigationRail.js +211 -188
  31. package/dist/Popover.d.ts +36 -0
  32. package/dist/Popover.js +99 -0
  33. package/dist/Table.js +119 -108
  34. package/dist/Tabs.js +100 -63
  35. package/dist/Toolbar.js +25 -43
  36. package/dist/Tree.js +15 -12
  37. package/dist/TreeItem.js +398 -314
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +4 -0
  40. package/dist/styles.css.js +1 -1
  41. package/dist/~utils.ListItem.d.ts +3 -3
  42. package/dist/~utils.ListItem.js +21 -34
  43. package/dist/~utils.icons.js +26 -46
  44. package/dist/~utils.useInit.d.ts +8 -0
  45. package/dist/~utils.useInit.js +13 -0
  46. package/package.json +25 -18
@@ -1,168 +1,143 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
- import {
4
- Collection,
5
- CollectionItem,
6
- useCollectionStore
7
- } from "@ariakit/react/collection";
8
- import {
9
- Dialog,
10
- DialogDisclosure,
11
- DialogProvider
12
- } from "@ariakit/react/dialog";
3
+ import { Collection, CollectionItem, useCollectionStore } from "@ariakit/react/collection";
4
+ import { Dialog, DialogDisclosure, DialogProvider } from "@ariakit/react/dialog";
13
5
  import { Role } from "@ariakit/react/role";
14
6
  import { useStoreState } from "@ariakit/react/store";
15
7
  import { Button, Text, VisuallyHidden } from "@stratakit/bricks";
16
8
  import { IconButtonPresentation } from "@stratakit/bricks/secret-internals";
17
- import {
18
- forwardRef,
19
- useControlledState
20
- } from "@stratakit/foundations/secret-internals";
9
+ import { forwardRef, useControlledState } from "@stratakit/foundations/secret-internals";
21
10
  import cx from "classnames";
22
11
  import { ChevronDown, StatusIcon } from "./~utils.icons.js";
23
- const ErrorRegionRoot = forwardRef(
24
- (props, forwardedRef) => {
25
- const {
26
- label,
27
- items: itemsProp = [],
28
- open: openProp,
29
- setOpen: setOpenProp,
30
- ...rest
31
- } = props;
32
- const labelId = React.useId();
33
- const sectionLabelledBy = props["aria-label"] ? void 0 : label ? labelId : void 0;
34
- const visible = Array.isArray(itemsProp) ? itemsProp.length > 0 : !!label;
35
- const [open, setOpen] = useControlledState(
36
- false,
37
- openProp,
38
- setOpenProp
39
- );
40
- const containerRef = React.useRef(null);
41
- const pulse = () => {
42
- const el = containerRef.current;
43
- if (!el) return;
44
- const id = "--\u{1F95D}ErrorRegion-pulse";
45
- const animations = el.getAnimations({ subtree: true });
46
- if (animations.find((animation) => animation.id === id)) return;
47
- el.animate(
48
- [
49
- {
50
- boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
51
- opacity: 1
52
- },
53
- {
54
- boxShadow: "0 0 15px 2px var(--stratakit-color-border-attention-base)",
55
- opacity: 0.7,
56
- offset: 0.5
57
- },
58
- {
59
- boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
60
- opacity: 1
61
- }
62
- ],
63
- {
64
- id,
65
- duration: 600,
66
- easing: "cubic-bezier(0.4, 0, 0.2, 1)",
67
- pseudoElement: "::before"
68
- }
69
- );
70
- };
71
- const store = useCollectionStore({
72
- setItems: (newItems) => {
73
- const prevItemsSet = new Set(prevItems.map((item) => item.id));
74
- const addedItems = newItems.filter(
75
- (item) => !prevItemsSet.has(item.id)
76
- );
77
- if (addedItems.length === 0) return;
78
- pulse();
79
- setLiveLabel(label);
80
- }
12
+ import { useInit } from "./~utils.useInit.js";
13
+ const ErrorRegionRoot = forwardRef((props, forwardedRef) => {
14
+ useInit();
15
+ const {
16
+ label,
17
+ items = [],
18
+ open: openProp,
19
+ setOpen: setOpenProp,
20
+ ...rest
21
+ } = props;
22
+ const labelId = React.useId();
23
+ const sectionLabelledBy = props["aria-label"] ? void 0 : label ? labelId : void 0;
24
+ const visible = items.length > 0;
25
+ const [open, setOpen] = useControlledState(false, openProp, setOpenProp);
26
+ const containerRef = React.useRef(null);
27
+ const pulse = () => {
28
+ const el = containerRef.current;
29
+ if (!el) return;
30
+ const id = "--\u{1F95D}ErrorRegion-pulse";
31
+ const animations = el.getAnimations({
32
+ subtree: true
81
33
  });
82
- const prevItems = useStoreState(store, "items");
83
- const [liveLabel, setLiveLabel] = React.useState(label);
84
- return /* @__PURE__ */ jsxs(Fragment, { children: [
85
- /* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "polite", "aria-atomic": true, children: liveLabel === label ? liveLabel : void 0 }),
86
- /* @__PURE__ */ jsx(DialogProvider, { open, setOpen, children: /* @__PURE__ */ jsx(
87
- Role.section,
88
- {
89
- "aria-labelledby": sectionLabelledBy,
90
- ...rest,
91
- className: cx("\u{1F95D}ErrorRegion", props.className),
92
- "data-_sk-visible": visible,
93
- "data-_sk-expanded": open,
94
- ref: forwardedRef,
95
- children: /* @__PURE__ */ jsxs("div", { className: "\u{1F95D}ErrorRegionContainer", ref: containerRef, children: [
96
- /* @__PURE__ */ jsxs(
97
- DialogDisclosure,
98
- {
99
- className: "\u{1F95D}ErrorRegionHeader",
100
- render: /* @__PURE__ */ jsx(Button, { variant: "ghost" }),
101
- children: [
102
- /* @__PURE__ */ jsx(StatusIcon, { tone: "attention", className: "\u{1F95D}ErrorRegionIcon" }),
103
- /* @__PURE__ */ jsx(
104
- Text,
105
- {
106
- render: /* @__PURE__ */ jsx("span", {}),
107
- id: labelId,
108
- className: "\u{1F95D}ErrorRegionLabel",
109
- variant: "body-sm",
110
- children: label
111
- }
112
- ),
113
- /* @__PURE__ */ jsx(IconButtonPresentation, { inert: true, variant: "ghost", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
114
- ]
115
- }
116
- ),
117
- /* @__PURE__ */ jsx(
118
- Dialog,
119
- {
120
- className: "\u{1F95D}ErrorRegionDialog",
121
- portal: false,
122
- modal: false,
123
- autoFocusOnShow: false,
124
- "aria-labelledby": labelId,
125
- children: /* @__PURE__ */ jsx(
126
- Collection,
127
- {
128
- store,
129
- className: "\u{1F95D}ErrorRegionItems",
130
- role: "list",
131
- children: itemsProp
132
- }
133
- )
134
- }
135
- )
136
- ] })
137
- }
138
- ) })
139
- ] });
140
- }
141
- );
142
- const ErrorRegionItem = forwardRef(
143
- (props, forwardedRef) => {
144
- const generatedId = React.useId();
145
- const {
146
- message,
147
- messageId = `${generatedId}-message`,
148
- actions,
149
- ...rest
150
- } = props;
151
- return /* @__PURE__ */ jsxs(
152
- CollectionItem,
153
- {
34
+ if (animations.find((animation) => animation.id === id)) return;
35
+ el.animate([{
36
+ boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
37
+ opacity: 1
38
+ }, {
39
+ boxShadow: "0 0 15px 2px var(--stratakit-color-border-attention-base)",
40
+ opacity: 0.7,
41
+ offset: 0.5
42
+ }, {
43
+ boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
44
+ opacity: 1
45
+ }], {
46
+ id,
47
+ duration: 600,
48
+ easing: "cubic-bezier(0.4, 0, 0.2, 1)",
49
+ pseudoElement: "::before"
50
+ });
51
+ };
52
+ const store = useCollectionStore({
53
+ setItems: (newItems) => {
54
+ const prevItemsSet = new Set(prevItems.map((item) => item.id));
55
+ const addedItems = newItems.filter((item_0) => !prevItemsSet.has(item_0.id));
56
+ if (addedItems.length === 0) return;
57
+ pulse();
58
+ setLiveLabel(label);
59
+ }
60
+ });
61
+ const prevItems = useStoreState(store, "items");
62
+ const [liveLabel, setLiveLabel] = React.useState(label);
63
+ return /* @__PURE__ */ jsxs(Fragment, {
64
+ children: [/* @__PURE__ */ jsx(VisuallyHidden, {
65
+ "aria-live": "polite",
66
+ "aria-atomic": true,
67
+ children: liveLabel === label ? liveLabel : void 0
68
+ }), /* @__PURE__ */ jsx(DialogProvider, {
69
+ open,
70
+ setOpen,
71
+ children: /* @__PURE__ */ jsx(Role.section, {
72
+ "aria-labelledby": sectionLabelledBy,
154
73
  ...rest,
155
- role: "listitem",
156
- className: cx("\u{1F95D}ErrorRegionItem", props.className),
74
+ className: cx("\u{1F95D}ErrorRegion", props.className),
75
+ "data-_sk-visible": visible,
76
+ "data-_sk-expanded": open,
157
77
  ref: forwardedRef,
158
- children: [
159
- /* @__PURE__ */ jsx(Text, { id: messageId, variant: "body-sm", children: message }),
160
- /* @__PURE__ */ jsx("div", { className: "\u{1F95D}ErrorRegionItemActions", children: actions })
161
- ]
162
- }
163
- );
164
- }
165
- );
78
+ children: /* @__PURE__ */ jsxs("div", {
79
+ className: "\u{1F95D}ErrorRegionContainer",
80
+ ref: containerRef,
81
+ children: [/* @__PURE__ */ jsxs(DialogDisclosure, {
82
+ className: "\u{1F95D}ErrorRegionHeader",
83
+ render: /* @__PURE__ */ jsx(Button, {
84
+ variant: "ghost"
85
+ }),
86
+ children: [/* @__PURE__ */ jsx(StatusIcon, {
87
+ tone: "attention",
88
+ className: "\u{1F95D}ErrorRegionIcon"
89
+ }), /* @__PURE__ */ jsx(Text, {
90
+ render: /* @__PURE__ */ jsx("span", {}),
91
+ id: labelId,
92
+ className: "\u{1F95D}ErrorRegionLabel",
93
+ variant: "body-sm",
94
+ children: label
95
+ }), /* @__PURE__ */ jsx(IconButtonPresentation, {
96
+ inert: true,
97
+ variant: "ghost",
98
+ children: /* @__PURE__ */ jsx(ChevronDown, {})
99
+ })]
100
+ }), /* @__PURE__ */ jsx(Dialog, {
101
+ className: "\u{1F95D}ErrorRegionDialog",
102
+ portal: false,
103
+ modal: false,
104
+ autoFocusOnShow: false,
105
+ "aria-labelledby": labelId,
106
+ children: /* @__PURE__ */ jsx(Collection, {
107
+ store,
108
+ className: "\u{1F95D}ErrorRegionItems",
109
+ role: "list",
110
+ children: items
111
+ })
112
+ })]
113
+ })
114
+ })
115
+ })]
116
+ });
117
+ });
118
+ const ErrorRegionItem = forwardRef((props, forwardedRef) => {
119
+ const generatedId = React.useId();
120
+ const {
121
+ message,
122
+ messageId = `${generatedId}-message`,
123
+ actions,
124
+ ...rest
125
+ } = props;
126
+ return /* @__PURE__ */ jsxs(CollectionItem, {
127
+ ...rest,
128
+ role: "listitem",
129
+ className: cx("\u{1F95D}ErrorRegionItem", props.className),
130
+ ref: forwardedRef,
131
+ children: [/* @__PURE__ */ jsx(Text, {
132
+ id: messageId,
133
+ variant: "body-sm",
134
+ children: message
135
+ }), /* @__PURE__ */ jsx("div", {
136
+ className: "\u{1F95D}ErrorRegionItemActions",
137
+ children: actions
138
+ })]
139
+ });
140
+ });
166
141
  export {
167
142
  ErrorRegionItem as Item,
168
143
  ErrorRegionRoot as Root
@@ -0,0 +1,110 @@
1
+ import * as React from "react";
2
+ import type { BaseProps, FocusableProps } from "@stratakit/foundations/secret-internals";
3
+ interface NavigationListRootProps extends Omit<BaseProps<"div">, "children"> {
4
+ /**
5
+ * The navigation items to render within the list.
6
+ * Should be an array of `NavigationList.Anchor` elements.
7
+ */
8
+ items: React.JSX.Element[];
9
+ }
10
+ /**
11
+ * A navigation list displays a collection of navigation links, one of which can be "active".
12
+ *
13
+ * Basic example:
14
+ * ```tsx
15
+ * <NavigationList.Root
16
+ * items={[
17
+ * <NavigationList.Anchor key={1} href="/page1" label="Item 1" />,
18
+ * <NavigationList.Anchor key={2} href="/page2" label="Item 2" active />,
19
+ * ]}
20
+ * />
21
+ * ```
22
+ *
23
+ * Example with subgroups:
24
+ * ```tsx
25
+ * <NavigationList.Root
26
+ * items={[
27
+ * <NavigationList.Anchor key={1} href="/page1" label="Item 1" />,
28
+ * <NavigationList.Anchor key={2} href="/page2" label="Item 2" />,
29
+ * <NavigationList.Subgroup
30
+ * key={3}
31
+ * label="Subgroup"
32
+ * items={[
33
+ * <NavigationList.Anchor key={1} href="/page3-1" label="Item 3.1" />,
34
+ * <NavigationList.Anchor key={2} href="/page3-2" label="Item 3.2" />,
35
+ * ]}
36
+ * />,
37
+ * ]}
38
+ * />
39
+ * ```
40
+ */
41
+ declare const NavigationListRoot: React.ForwardRefExoticComponent<NavigationListRootProps & React.RefAttributes<HTMLDivElement | HTMLElement>>;
42
+ interface NavigationListItemActionProps extends FocusableProps {
43
+ }
44
+ interface NavigationListAnchorProps extends Omit<FocusableProps<"a">, "children"> {
45
+ /**
46
+ * The label of the navigation anchor.
47
+ */
48
+ label: string;
49
+ /**
50
+ * The icon shown before the label of the navigation anchor.
51
+ *
52
+ * Can be a URL of an SVG from the `@stratakit/icons` package,
53
+ * or a custom JSX icon.
54
+ */
55
+ icon?: string | React.JSX.Element;
56
+ /**
57
+ * Whether the anchor is active.
58
+ *
59
+ * This will automatically set `aria-current="true"` on the anchor.
60
+ */
61
+ active?: boolean;
62
+ }
63
+ /**
64
+ * An individual anchor within the navigation list. This should perform a navigation
65
+ * to a different view, page or section. (Make sure to use proper routing/navigation)
66
+ *
67
+ * Should be used as an element within the `items` array of `NavigationList.Root`.
68
+ *
69
+ * Example:
70
+ * ```tsx
71
+ * <NavigationList.Anchor href="/profile"> label="Profile" />
72
+ * ```
73
+ */
74
+ declare const NavigationListAnchor: React.ForwardRefExoticComponent<NavigationListAnchorProps & React.RefAttributes<HTMLElement | HTMLAnchorElement>>;
75
+ interface NavigationListSubgroupProps extends Omit<BaseProps, "children">, Pick<NavigationListSubgroupButtonProps, "label" | "icon"> {
76
+ /**
77
+ * The navigation items within the subgroup.
78
+ * Should be an array of `NavigationList.Anchor` elements.
79
+ */
80
+ items: React.JSX.Element[];
81
+ /**
82
+ * Whether the subgroup is expanded by default.
83
+ */
84
+ defaultOpen?: boolean;
85
+ }
86
+ /**
87
+ * A subgroup within the navigation list, used to group related navigation items.
88
+ *
89
+ * Should be used as an element within the `items` array of `NavigationList.Root`.
90
+ *
91
+ * Example:
92
+ * ```tsx
93
+ * <NavigationList.Subgroup label="Management" items={[ … ]} />
94
+ * ```
95
+ */
96
+ declare const NavigationListSubgroup: React.ForwardRefExoticComponent<NavigationListSubgroupProps & React.RefAttributes<HTMLDivElement | HTMLElement>>;
97
+ interface NavigationListSubgroupButtonProps extends Omit<NavigationListItemActionProps, "children"> {
98
+ /**
99
+ * The label for the subgroup.
100
+ */
101
+ label: string;
102
+ /**
103
+ * The icon shown before the label of the subgroup.
104
+ *
105
+ * Can be a URL of an SVG from the `@stratakit/icons` package,
106
+ * or a custom JSX icon.
107
+ */
108
+ icon?: string | React.JSX.Element;
109
+ }
110
+ export { NavigationListRoot as Root, NavigationListAnchor as Anchor, NavigationListSubgroup as Subgroup, };
@@ -0,0 +1,130 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Button as AriaButton } from "@ariakit/react/button";
4
+ import * as AriaDisclosure from "@ariakit/react/disclosure";
5
+ import { Focusable } from "@ariakit/react/focusable";
6
+ import { Role } from "@ariakit/react/role";
7
+ import { Text } from "@stratakit/bricks";
8
+ import { Icon } from "@stratakit/foundations";
9
+ import { forwardRef } from "@stratakit/foundations/secret-internals";
10
+ import cx from "classnames";
11
+ import { ChevronDown } from "./~utils.icons.js";
12
+ import { useInit } from "./~utils.useInit.js";
13
+ const NavigationListRoot = forwardRef((props, forwardedRef) => {
14
+ useInit();
15
+ const {
16
+ items,
17
+ role = "list",
18
+ ...rest
19
+ } = props;
20
+ const itemRole = role === "list" ? "listitem" : void 0;
21
+ const {
22
+ indented
23
+ } = React.useContext(NavigationListRootContext);
24
+ return /* @__PURE__ */ jsx(Role, {
25
+ ...rest,
26
+ role,
27
+ className: cx("\u{1F95D}NavigationListRoot", props.className),
28
+ "data-_sk-indented": indented ? "true" : void 0,
29
+ ref: forwardedRef,
30
+ children: React.Children.map(items, (item) => /* @__PURE__ */ jsx(Role, {
31
+ role: itemRole,
32
+ className: "\u{1F95D}NavigationListItem",
33
+ "data-_sk-indented": indented ? "true" : void 0,
34
+ children: item
35
+ }))
36
+ });
37
+ });
38
+ const NavigationListRootContext = React.createContext({
39
+ indented: false
40
+ });
41
+ const NavigationListItemAction = forwardRef((props, forwardedRef) => {
42
+ return /* @__PURE__ */ jsx(Focusable, {
43
+ accessibleWhenDisabled: true,
44
+ ...props,
45
+ className: cx("\u{1F95D}NavigationListItemAction", props.className),
46
+ ref: forwardedRef
47
+ });
48
+ });
49
+ const NavigationListAnchor = forwardRef((props, forwardedRef) => {
50
+ const {
51
+ active,
52
+ label,
53
+ icon,
54
+ ...rest
55
+ } = props;
56
+ return /* @__PURE__ */ jsxs(NavigationListItemAction, {
57
+ ...rest,
58
+ render: /* @__PURE__ */ jsx(Role.a, {
59
+ "aria-current": active ? "true" : void 0,
60
+ render: props.render
61
+ }),
62
+ ref: forwardedRef,
63
+ children: [typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, {
64
+ href: icon
65
+ }) : icon, /* @__PURE__ */ jsx(Text, {
66
+ variant: "body-sm",
67
+ render: /* @__PURE__ */ jsx("span", {}),
68
+ children: label
69
+ })]
70
+ });
71
+ });
72
+ const NavigationListSubgroup = forwardRef((props, forwardedRef) => {
73
+ const {
74
+ label,
75
+ icon,
76
+ items,
77
+ defaultOpen,
78
+ ...rest
79
+ } = props;
80
+ return /* @__PURE__ */ jsx(Role, {
81
+ ...rest,
82
+ className: cx("\u{1F95D}NavigationListSubgroup", props.className),
83
+ ref: forwardedRef,
84
+ children: /* @__PURE__ */ jsxs(AriaDisclosure.DisclosureProvider, {
85
+ defaultOpen,
86
+ children: [/* @__PURE__ */ jsx(AriaDisclosure.Disclosure, {
87
+ render: /* @__PURE__ */ jsx(NavigationListSubgroupButton, {
88
+ label,
89
+ icon
90
+ })
91
+ }), /* @__PURE__ */ jsx(NavigationListRootContext.Provider, {
92
+ value: {
93
+ indented: true
94
+ },
95
+ children: /* @__PURE__ */ jsx(AriaDisclosure.DisclosureContent, {
96
+ render: /* @__PURE__ */ jsx(NavigationListRoot, {
97
+ items
98
+ })
99
+ })
100
+ })]
101
+ })
102
+ });
103
+ });
104
+ const NavigationListSubgroupButton = forwardRef((props, forwardedRef) => {
105
+ const {
106
+ label,
107
+ icon,
108
+ ...rest
109
+ } = props;
110
+ return /* @__PURE__ */ jsxs(NavigationListItemAction, {
111
+ render: /* @__PURE__ */ jsx(AriaButton, {}),
112
+ ...rest,
113
+ className: cx("\u{1F95D}NavigationListSubgroupButton", props.className),
114
+ ref: forwardedRef,
115
+ children: [typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, {
116
+ href: icon
117
+ }) : icon, /* @__PURE__ */ jsx(Text, {
118
+ variant: "body-sm",
119
+ render: /* @__PURE__ */ jsx("span", {}),
120
+ children: label
121
+ }), /* @__PURE__ */ jsx(ChevronDown, {
122
+ className: "\u{1F95D}NavigationListSubgroupChevron"
123
+ })]
124
+ });
125
+ });
126
+ export {
127
+ NavigationListAnchor as Anchor,
128
+ NavigationListRoot as Root,
129
+ NavigationListSubgroup as Subgroup
130
+ };