@stratakit/structures 0.4.4 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,167 +1,141 @@
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,
28
- open: openProp,
29
- setOpen: setOpenProp,
30
- ...rest
31
- } = props;
32
- const labelId = React.useId();
33
- const sectionLabelledBy = props["aria-labelledby"] ?? (props["aria-label"] ? void 0 : labelId);
34
- const [open, setOpen] = useControlledState(
35
- false,
36
- openProp,
37
- setOpenProp
38
- );
39
- const containerRef = React.useRef(null);
40
- const pulse = () => {
41
- const el = containerRef.current;
42
- if (!el) return;
43
- const id = "--\u{1F95D}ErrorRegion-pulse";
44
- const animations = el.getAnimations({ subtree: true });
45
- if (animations.find((animation) => animation.id === id)) return;
46
- el.animate(
47
- [
48
- {
49
- boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
50
- opacity: 1
51
- },
52
- {
53
- boxShadow: "0 0 15px 2px var(--stratakit-color-border-attention-base)",
54
- opacity: 0.7,
55
- offset: 0.5
56
- },
57
- {
58
- boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
59
- opacity: 1
60
- }
61
- ],
62
- {
63
- id,
64
- duration: 600,
65
- easing: "cubic-bezier(0.4, 0, 0.2, 1)",
66
- pseudoElement: "::before"
67
- }
68
- );
69
- };
70
- const store = useCollectionStore({
71
- setItems: (newItems) => {
72
- const prevItemsSet = new Set(prevItems.map((item) => item.id));
73
- const addedItems = newItems.filter(
74
- (item) => !prevItemsSet.has(item.id)
75
- );
76
- if (addedItems.length === 0) return;
77
- pulse();
78
- setLiveLabel(label);
79
- }
12
+ const ErrorRegionRoot = forwardRef((props, forwardedRef) => {
13
+ const {
14
+ label,
15
+ items = [],
16
+ open: openProp,
17
+ setOpen: setOpenProp,
18
+ ...rest
19
+ } = props;
20
+ const labelId = React.useId();
21
+ const sectionLabelledBy = props["aria-label"] ? void 0 : label ? labelId : void 0;
22
+ const visible = items.length > 0;
23
+ const [open, setOpen] = useControlledState(false, openProp, setOpenProp);
24
+ const containerRef = React.useRef(null);
25
+ const pulse = () => {
26
+ const el = containerRef.current;
27
+ if (!el) return;
28
+ const id = "--\u{1F95D}ErrorRegion-pulse";
29
+ const animations = el.getAnimations({
30
+ subtree: true
80
31
  });
81
- const prevItems = useStoreState(store, "items");
82
- const [liveLabel, setLiveLabel] = React.useState(label);
83
- return /* @__PURE__ */ jsxs(Fragment, { children: [
84
- /* @__PURE__ */ jsx(VisuallyHidden, { "aria-live": "polite", "aria-atomic": true, children: liveLabel === label ? liveLabel : void 0 }),
85
- /* @__PURE__ */ jsx(DialogProvider, { open, setOpen, children: /* @__PURE__ */ jsx(
86
- Role.section,
87
- {
88
- ...rest,
89
- "aria-labelledby": sectionLabelledBy,
90
- className: cx("\u{1F95D}ErrorRegion", props.className),
91
- "data-_sk-visible": !!label,
92
- "data-_sk-expanded": open,
93
- ref: forwardedRef,
94
- children: /* @__PURE__ */ jsxs("div", { className: "\u{1F95D}ErrorRegionContainer", ref: containerRef, children: [
95
- /* @__PURE__ */ jsxs(
96
- DialogDisclosure,
97
- {
98
- className: "\u{1F95D}ErrorRegionHeader",
99
- render: /* @__PURE__ */ jsx(Button, { variant: "ghost" }),
100
- children: [
101
- /* @__PURE__ */ jsx(StatusIcon, { tone: "attention", className: "\u{1F95D}ErrorRegionIcon" }),
102
- /* @__PURE__ */ jsx(
103
- Text,
104
- {
105
- render: /* @__PURE__ */ jsx("span", {}),
106
- id: labelId,
107
- className: "\u{1F95D}ErrorRegionLabel",
108
- variant: "body-sm",
109
- children: label
110
- }
111
- ),
112
- /* @__PURE__ */ jsx(IconButtonPresentation, { inert: true, variant: "ghost", children: /* @__PURE__ */ jsx(ChevronDown, {}) })
113
- ]
114
- }
115
- ),
116
- /* @__PURE__ */ jsx(
117
- Dialog,
118
- {
119
- className: "\u{1F95D}ErrorRegionDialog",
120
- portal: false,
121
- modal: false,
122
- autoFocusOnShow: false,
123
- "aria-labelledby": labelId,
124
- children: /* @__PURE__ */ jsx(
125
- Collection,
126
- {
127
- store,
128
- className: "\u{1F95D}ErrorRegionItems",
129
- role: "list",
130
- children: items
131
- }
132
- )
133
- }
134
- )
135
- ] })
136
- }
137
- ) })
138
- ] });
139
- }
140
- );
141
- const ErrorRegionItem = forwardRef(
142
- (props, forwardedRef) => {
143
- const generatedId = React.useId();
144
- const {
145
- message,
146
- messageId = `${generatedId}-message`,
147
- actions,
148
- ...rest
149
- } = props;
150
- return /* @__PURE__ */ jsxs(
151
- CollectionItem,
152
- {
32
+ if (animations.find((animation) => animation.id === id)) return;
33
+ el.animate([{
34
+ boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
35
+ opacity: 1
36
+ }, {
37
+ boxShadow: "0 0 15px 2px var(--stratakit-color-border-attention-base)",
38
+ opacity: 0.7,
39
+ offset: 0.5
40
+ }, {
41
+ boxShadow: "0 0 0 0 var(--stratakit-color-border-attention-base)",
42
+ opacity: 1
43
+ }], {
44
+ id,
45
+ duration: 600,
46
+ easing: "cubic-bezier(0.4, 0, 0.2, 1)",
47
+ pseudoElement: "::before"
48
+ });
49
+ };
50
+ const store = useCollectionStore({
51
+ setItems: (newItems) => {
52
+ const prevItemsSet = new Set(prevItems.map((item) => item.id));
53
+ const addedItems = newItems.filter((item_0) => !prevItemsSet.has(item_0.id));
54
+ if (addedItems.length === 0) return;
55
+ pulse();
56
+ setLiveLabel(label);
57
+ }
58
+ });
59
+ const prevItems = useStoreState(store, "items");
60
+ const [liveLabel, setLiveLabel] = React.useState(label);
61
+ return /* @__PURE__ */ jsxs(Fragment, {
62
+ children: [/* @__PURE__ */ jsx(VisuallyHidden, {
63
+ "aria-live": "polite",
64
+ "aria-atomic": true,
65
+ children: liveLabel === label ? liveLabel : void 0
66
+ }), /* @__PURE__ */ jsx(DialogProvider, {
67
+ open,
68
+ setOpen,
69
+ children: /* @__PURE__ */ jsx(Role.section, {
70
+ "aria-labelledby": sectionLabelledBy,
153
71
  ...rest,
154
- role: "listitem",
155
- className: cx("\u{1F95D}ErrorRegionItem", props.className),
72
+ className: cx("\u{1F95D}ErrorRegion", props.className),
73
+ "data-_sk-visible": visible,
74
+ "data-_sk-expanded": open,
156
75
  ref: forwardedRef,
157
- children: [
158
- /* @__PURE__ */ jsx(Text, { id: messageId, variant: "body-sm", children: message }),
159
- /* @__PURE__ */ jsx("div", { className: "\u{1F95D}ErrorRegionItemActions", children: actions })
160
- ]
161
- }
162
- );
163
- }
164
- );
76
+ children: /* @__PURE__ */ jsxs("div", {
77
+ className: "\u{1F95D}ErrorRegionContainer",
78
+ ref: containerRef,
79
+ children: [/* @__PURE__ */ jsxs(DialogDisclosure, {
80
+ className: "\u{1F95D}ErrorRegionHeader",
81
+ render: /* @__PURE__ */ jsx(Button, {
82
+ variant: "ghost"
83
+ }),
84
+ children: [/* @__PURE__ */ jsx(StatusIcon, {
85
+ tone: "attention",
86
+ className: "\u{1F95D}ErrorRegionIcon"
87
+ }), /* @__PURE__ */ jsx(Text, {
88
+ render: /* @__PURE__ */ jsx("span", {}),
89
+ id: labelId,
90
+ className: "\u{1F95D}ErrorRegionLabel",
91
+ variant: "body-sm",
92
+ children: label
93
+ }), /* @__PURE__ */ jsx(IconButtonPresentation, {
94
+ inert: true,
95
+ variant: "ghost",
96
+ children: /* @__PURE__ */ jsx(ChevronDown, {})
97
+ })]
98
+ }), /* @__PURE__ */ jsx(Dialog, {
99
+ className: "\u{1F95D}ErrorRegionDialog",
100
+ portal: false,
101
+ modal: false,
102
+ autoFocusOnShow: false,
103
+ "aria-labelledby": labelId,
104
+ children: /* @__PURE__ */ jsx(Collection, {
105
+ store,
106
+ className: "\u{1F95D}ErrorRegionItems",
107
+ role: "list",
108
+ children: items
109
+ })
110
+ })]
111
+ })
112
+ })
113
+ })]
114
+ });
115
+ });
116
+ const ErrorRegionItem = forwardRef((props, forwardedRef) => {
117
+ const generatedId = React.useId();
118
+ const {
119
+ message,
120
+ messageId = `${generatedId}-message`,
121
+ actions,
122
+ ...rest
123
+ } = props;
124
+ return /* @__PURE__ */ jsxs(CollectionItem, {
125
+ ...rest,
126
+ role: "listitem",
127
+ className: cx("\u{1F95D}ErrorRegionItem", props.className),
128
+ ref: forwardedRef,
129
+ children: [/* @__PURE__ */ jsx(Text, {
130
+ id: messageId,
131
+ variant: "body-sm",
132
+ children: message
133
+ }), /* @__PURE__ */ jsx("div", {
134
+ className: "\u{1F95D}ErrorRegionItemActions",
135
+ children: actions
136
+ })]
137
+ });
138
+ });
165
139
  export {
166
140
  ErrorRegionItem as Item,
167
141
  ErrorRegionRoot as Root
@@ -1,6 +1,36 @@
1
1
  import * as React from "react";
2
2
  import type { BaseProps, FocusableProps } from "@stratakit/foundations/secret-internals";
3
- interface NavigationRailProps extends BaseProps<"nav"> {
3
+ interface NavigationRailRootInnerProps extends BaseProps<"nav"> {
4
+ }
5
+ interface NavigationRailRootProps extends NavigationRailRootInnerProps {
6
+ /**
7
+ * The initial expanded state of the `NavigationRail` when it is first rendered.
8
+ *
9
+ * This prop is recommended over `expanded` when you don't need to fully control the expanded
10
+ * state from the outside.
11
+ *
12
+ * This prop will be ignored if the `expanded` prop is provided.
13
+ *
14
+ * @default false
15
+ */
16
+ defaultExpanded?: boolean;
17
+ /**
18
+ * Control whether the `NavigationRail` is expanded or collapsed.
19
+ *
20
+ * When `true`, the `NavigationRail` shows both icons and labels for its items.
21
+ * When `false`, it shows only icons, with labels available as tooltips.
22
+ *
23
+ * This prop is optional; if not provided, the `NavigationRail` will manage its own state internally.
24
+ *
25
+ * This should be used in conjunction with the `setExpanded` prop to reflect internal state changes.
26
+ */
27
+ expanded?: boolean;
28
+ /**
29
+ * Callback that is called when the expanded state of the `NavigationRail` changes.
30
+ *
31
+ * This is useful for syncing the internal state of the `NavigationRail` with external state.
32
+ */
33
+ setExpanded?: (expanded: boolean) => void;
4
34
  }
5
35
  /**
6
36
  * The `NavigationRail` presents top-level navigation items in a vertical orientation.
@@ -43,7 +73,7 @@ interface NavigationRailProps extends BaseProps<"nav"> {
43
73
  * </NavigationRail.Root>
44
74
  * ```
45
75
  */
46
- declare const NavigationRailRoot: React.ForwardRefExoticComponent<NavigationRailProps & React.RefAttributes<HTMLElement>>;
76
+ declare const NavigationRailRoot: React.ForwardRefExoticComponent<NavigationRailRootProps & React.RefAttributes<HTMLElement>>;
47
77
  interface NavigationRailHeaderProps extends BaseProps<"header"> {
48
78
  }
49
79
  /**
@@ -64,8 +94,11 @@ interface NavigationRailToggleButtonProps extends Omit<FocusableProps<"button">,
64
94
  label?: string;
65
95
  }
66
96
  /**
67
- * `NavigationRail.ToggleButton` toggles the collapsed state of the `NavigationRail`.
97
+ * `NavigationRail.ToggleButton` toggles the expanded/collapsed state of the `NavigationRail`.
68
98
  * It is typically placed inside `NavigationRail.Header`, next to the logo.
99
+ *
100
+ * When this button is clicked, it toggles the internal expanded state of the `NavigationRail`,
101
+ * and also calls the `setExpanded` callback prop if provided, to allow syncing with external state.
69
102
  */
70
103
  declare const NavigationRailToggleButton: React.ForwardRefExoticComponent<NavigationRailToggleButtonProps & React.RefAttributes<HTMLElement | HTMLButtonElement>>;
71
104
  interface NavigationRailContentProps extends BaseProps {