@itwin/itwinui-react 5.0.0-alpha.10 → 5.0.0-alpha.12

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 (69) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +13 -2
  3. package/dist/DEV/bricks/Checkbox.js +7 -14
  4. package/dist/DEV/bricks/Description.js +7 -14
  5. package/dist/DEV/bricks/DropdownMenu.js +58 -20
  6. package/dist/DEV/bricks/Field.internal.js +47 -0
  7. package/dist/DEV/bricks/Field.js +116 -85
  8. package/dist/DEV/bricks/Icon.js +144 -7
  9. package/dist/DEV/bricks/Label.js +4 -10
  10. package/dist/DEV/bricks/Radio.js +7 -14
  11. package/dist/DEV/bricks/Root.internal.js +17 -0
  12. package/dist/DEV/bricks/Root.js +73 -27
  13. package/dist/DEV/bricks/Select.js +9 -15
  14. package/dist/DEV/bricks/Spinner.js +23 -8
  15. package/dist/DEV/bricks/Switch.js +8 -15
  16. package/dist/DEV/bricks/Table.js +71 -37
  17. package/dist/DEV/bricks/Tabs.js +4 -29
  18. package/dist/DEV/bricks/TextBox.js +23 -37
  19. package/dist/DEV/bricks/Tooltip.js +1 -1
  20. package/dist/DEV/bricks/TreeItem.js +92 -10
  21. package/dist/DEV/bricks/index.js +3 -1
  22. package/dist/DEV/bricks/styles.css.js +1 -1
  23. package/dist/DEV/bricks/~hooks.js +3 -1
  24. package/dist/DEV/bricks/~utils.js +17 -0
  25. package/dist/DEV/foundations/styles.css.js +1 -1
  26. package/dist/bricks/Badge.d.ts +1 -1
  27. package/dist/bricks/Checkbox.d.ts +13 -5
  28. package/dist/bricks/Checkbox.js +7 -14
  29. package/dist/bricks/Description.d.ts +2 -6
  30. package/dist/bricks/Description.js +7 -14
  31. package/dist/bricks/DropdownMenu.d.ts +9 -9
  32. package/dist/bricks/DropdownMenu.js +57 -19
  33. package/dist/bricks/Field.d.ts +63 -27
  34. package/dist/bricks/Field.internal.d.ts +33 -0
  35. package/dist/bricks/Field.internal.js +47 -0
  36. package/dist/bricks/Field.js +111 -84
  37. package/dist/bricks/Icon.d.ts +12 -1
  38. package/dist/bricks/Icon.js +142 -7
  39. package/dist/bricks/Label.d.ts +5 -12
  40. package/dist/bricks/Label.js +4 -10
  41. package/dist/bricks/Radio.d.ts +14 -5
  42. package/dist/bricks/Radio.js +7 -14
  43. package/dist/bricks/Root.d.ts +12 -0
  44. package/dist/bricks/Root.internal.d.ts +6 -0
  45. package/dist/bricks/Root.internal.js +17 -0
  46. package/dist/bricks/Root.js +73 -27
  47. package/dist/bricks/Select.d.ts +29 -12
  48. package/dist/bricks/Select.js +9 -15
  49. package/dist/bricks/Spinner.js +23 -8
  50. package/dist/bricks/Switch.d.ts +12 -5
  51. package/dist/bricks/Switch.js +8 -15
  52. package/dist/bricks/Table.d.ts +94 -37
  53. package/dist/bricks/Table.js +69 -36
  54. package/dist/bricks/Tabs.d.ts +3 -4
  55. package/dist/bricks/Tabs.js +4 -29
  56. package/dist/bricks/TextBox.d.ts +42 -19
  57. package/dist/bricks/TextBox.js +23 -37
  58. package/dist/bricks/Tooltip.js +1 -1
  59. package/dist/bricks/TreeItem.d.ts +53 -9
  60. package/dist/bricks/TreeItem.js +81 -10
  61. package/dist/bricks/index.d.ts +2 -1
  62. package/dist/bricks/index.js +3 -1
  63. package/dist/bricks/styles.css.js +1 -1
  64. package/dist/bricks/~hooks.d.ts +8 -0
  65. package/dist/bricks/~hooks.js +3 -1
  66. package/dist/bricks/~utils.d.ts +8 -0
  67. package/dist/bricks/~utils.js +17 -0
  68. package/dist/foundations/styles.css.js +1 -1
  69. package/package.json +1 -1
@@ -3,69 +3,55 @@ import * as React from "react";
3
3
  import { Role } from "@ariakit/react/role";
4
4
  import { Focusable } from "@ariakit/react/focusable";
5
5
  import cx from "classnames";
6
- import { FieldControl } from "./Field.js";
7
6
  import { Icon } from "./Icon.js";
8
7
  import { useMergedRefs } from "./~hooks.js";
9
8
  import { forwardRef } from "./~utils.js";
9
+ import { useFieldControlType } from "./Field.internal.js";
10
10
  const TextBoxInput = forwardRef(
11
11
  (props, forwardedRef) => {
12
- const { id, ...rest } = props;
12
+ useFieldControlType("textlike");
13
13
  const rootContext = React.useContext(TextBoxRootContext);
14
14
  const setDisabled = rootContext?.setDisabled;
15
15
  React.useEffect(() => {
16
16
  setDisabled?.(props.disabled);
17
17
  }, [setDisabled, props.disabled]);
18
18
  return /* @__PURE__ */ jsx(
19
- FieldControl,
19
+ Role.input,
20
20
  {
21
- type: "textlike",
22
- id,
21
+ readOnly: props.disabled,
22
+ ...props,
23
+ className: cx({ "\u{1F95D}-text-box": !rootContext }, props.className),
24
+ placeholder: props.placeholder ?? " ",
23
25
  render: /* @__PURE__ */ jsx(
24
- Role.input,
26
+ Focusable,
25
27
  {
26
- readOnly: props.disabled,
27
- ...rest,
28
- className: cx({ "\u{1F95D}-text-box": !rootContext }, props.className),
29
- placeholder: props.placeholder ?? " ",
30
- render: /* @__PURE__ */ jsx(
31
- Focusable,
32
- {
33
- accessibleWhenDisabled: true,
34
- render: props.render || /* @__PURE__ */ jsx("input", {})
35
- }
36
- ),
37
- ref: useMergedRefs(rootContext?.inputRef, forwardedRef)
28
+ accessibleWhenDisabled: true,
29
+ render: props.render || /* @__PURE__ */ jsx("input", {})
38
30
  }
39
- )
31
+ ),
32
+ ref: useMergedRefs(rootContext?.inputRef, forwardedRef)
40
33
  }
41
34
  );
42
35
  }
43
36
  );
44
37
  const TextBoxTextarea = forwardRef(
45
38
  (props, forwardedRef) => {
46
- const { id, ...rest } = props;
39
+ useFieldControlType("textlike");
47
40
  return /* @__PURE__ */ jsx(
48
- FieldControl,
41
+ Role.textarea,
49
42
  {
50
- type: "textlike",
51
- id,
43
+ readOnly: props.disabled,
44
+ ...props,
45
+ className: cx("\u{1F95D}-text-box", props.className),
46
+ placeholder: props.placeholder ?? " ",
52
47
  render: /* @__PURE__ */ jsx(
53
- Role.textarea,
48
+ Focusable,
54
49
  {
55
- readOnly: props.disabled,
56
- ...rest,
57
- className: cx("\u{1F95D}-text-box", props.className),
58
- placeholder: props.placeholder ?? " ",
59
- render: /* @__PURE__ */ jsx(
60
- Focusable,
61
- {
62
- accessibleWhenDisabled: true,
63
- render: props.render || /* @__PURE__ */ jsx("textarea", {})
64
- }
65
- ),
66
- ref: forwardedRef
50
+ accessibleWhenDisabled: true,
51
+ render: props.render || /* @__PURE__ */ jsx("textarea", {})
67
52
  }
68
- )
53
+ ),
54
+ ref: forwardedRef
69
55
  }
70
56
  );
71
57
  }
@@ -43,6 +43,7 @@ const Tooltip = forwardRef(
43
43
  AkTooltip.Tooltip,
44
44
  {
45
45
  "aria-hidden": "true",
46
+ portal: true,
46
47
  ...rest,
47
48
  unmountOnHide,
48
49
  className: cx("\u{1F95D}-tooltip", props.className),
@@ -53,7 +54,6 @@ const Tooltip = forwardRef(
53
54
  ...props.style
54
55
  },
55
56
  wrapperProps: popover.wrapperProps,
56
- portal: popover.portal,
57
57
  children: content
58
58
  }
59
59
  )
@@ -1,5 +1,4 @@
1
1
  import * as React from "react";
2
- import { IconButton } from "./IconButton.js";
3
2
  import { type BaseProps } from "./~utils.js";
4
3
  interface TreeItemRootProps extends Omit<BaseProps, "content" | "children"> {
5
4
  /** Specifies the nesting level of the tree item. Nesting levels start at 1. */
@@ -28,7 +27,7 @@ interface TreeItemRootProps extends Omit<BaseProps, "content" | "children"> {
28
27
  * Used to determine if a tree item is a parent node. If `undefined`, it is a leaf node (i.e. not expandable).
29
28
  *
30
29
  * @default undefined
31
- * */
30
+ */
32
31
  expanded?: boolean;
33
32
  /**
34
33
  * Callback fired when the tree item is expanded.
@@ -62,17 +61,45 @@ interface TreeItemRootProps extends Omit<BaseProps, "content" | "children"> {
62
61
  /** Secondary line of text to display additional information about the tree item. */
63
62
  description?: React.ReactNode;
64
63
  /**
65
- * The actions available for the tree item. Must be a list of `Tree.ItemAction` components.
64
+ * The secondary actions available for the tree item. Must be a list of `Tree.ItemAction` components.
66
65
  *
67
66
  * Example:
68
67
  * ```tsx
69
68
  * actions={[
69
+ * error && <Tree.ItemAction key={…} icon={…} label={…} />,
70
70
  * <Tree.ItemAction key={…} icon={…} label={…} />,
71
71
  * <Tree.ItemAction key={…} icon={…} label={…} />,
72
72
  * ]}
73
73
  * ```
74
+ *
75
+ * Excess actions will automatically get collapsed into an overflow menu.
76
+ * - Normally, the third action and onwards will overflow.
77
+ * - When the `error` prop is set, the _second_ action and onwards will overflow.
78
+ *
79
+ * The actions are normally hidden until the treeitem is hovered or focused.
80
+ * When the `error` prop is set, the actions will be made visible by default. The first
81
+ * action slot can be used to display an error-related action.
82
+ *
83
+ * ```tsx
84
+ * actions={[
85
+ * error && <Tree.ItemAction key={…} icon={…} label={…} />,
86
+ * <Tree.ItemAction key={…} icon={…} label={…} />,
87
+ * <Tree.ItemAction key={…} icon={…} label={…} />,
88
+ * ]}
89
+ * ```
90
+ *
91
+ * @experimental
74
92
  */
75
93
  actions?: React.ReactNode[];
94
+ /**
95
+ * Specifies if the tree item is in an error state.
96
+ *
97
+ * Can be combined with the `actions` prop to display an error-related action. The first
98
+ * action will be made visible by default.
99
+ *
100
+ * @default false
101
+ */
102
+ error?: boolean;
76
103
  }
77
104
  /**
78
105
  * A treeitem is a node in a tree structure that may be expanded or collapsed to reveal or hide its descendants.
@@ -101,23 +128,40 @@ interface TreeItemRootProps extends Omit<BaseProps, "content" | "children"> {
101
128
  * Secondary actions can be passed into the `actions` prop.
102
129
  */
103
130
  declare const TreeItemRoot: React.ForwardRefExoticComponent<TreeItemRootProps & React.RefAttributes<HTMLElement | HTMLDivElement>>;
104
- type IconButtonProps = React.ComponentProps<typeof IconButton>;
105
- interface TreeItemActionProps extends BaseProps<"button">, Pick<IconButtonProps, "label" | "icon"> {
131
+ interface TreeItemActionProps extends Omit<BaseProps<"button">, "children"> {
132
+ /**
133
+ * Label for the action.
134
+ *
135
+ * Will be displayed as a tooltip when the action is an icon-button,
136
+ * otherwise will be displayed as a label inside the menu-item.
137
+ */
138
+ label: string;
139
+ /**
140
+ * Icon for the action.
141
+ *
142
+ * Can be a URL of an SVG from the `@itwin/itwinui-icons` package, or a JSX element.
143
+ *
144
+ * Required when the action is displayed as an icon-button (i.e. not overflowing).
145
+ */
146
+ icon?: string | React.JSX.Element;
106
147
  /**
107
- * Controls the visibility of the action.
148
+ * Controls the visibility of the action (only when the action is displayed as icon-button).
108
149
  *
109
150
  * If `true`, the action is always visible.
110
151
  * If `false`, the action is hidden and becomes inaccessible, but still occupies space.
111
152
  *
112
- * By default, the action is shown only when the treeitem receives hover/focus.
153
+ * By default, the action is shown only when the treeitem receives hover/focus. When the
154
+ * treeitem has an `error`, the action will become always visible (i.e. it will default
155
+ * to `true` when `error` is set).
113
156
  */
114
157
  visible?: boolean;
115
158
  }
116
159
  /**
117
160
  * A secondary action for `<Tree.Item>`, to be passed into the `actions` prop. The action is typically
118
- * displayed as an icon-button on the right end of the treeitem.
161
+ * displayed as an icon-button or a menu-item (e.g. when overflowing).
119
162
  *
120
- * By default, the action appears only on hover/focus. This can be controlled by the `visible` prop.
163
+ * By default, the action appears only when the treeitem has hover/focus or an error. This behavior can
164
+ * be overridden using the `visible` prop.
121
165
  */
122
166
  declare const TreeItemAction: React.ForwardRefExoticComponent<TreeItemActionProps & React.RefAttributes<HTMLElement | HTMLButtonElement>>;
123
167
  export { TreeItemRoot as Root, TreeItemAction as Action };
@@ -2,15 +2,17 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import cx from "classnames";
4
4
  import { Role } from "@ariakit/react/role";
5
+ import { PopoverProvider } from "@ariakit/react/popover";
5
6
  import {
6
7
  CompositeItem
7
8
  } from "@ariakit/react/composite";
8
9
  import { Toolbar } from "@ariakit/react/toolbar";
9
10
  import * as ListItem from "./~utils.ListItem.js";
10
11
  import { IconButton } from "./IconButton.js";
11
- import { Icon } from "./Icon.js";
12
+ import * as DropdownMenu from "./DropdownMenu.js";
13
+ import { Icon, StatusWarning, MoreHorizontal } from "./Icon.js";
12
14
  import { forwardRef } from "./~utils.js";
13
- import { useEventHandlers } from "./~hooks.js";
15
+ import { useEventHandlers, useSafeContext } from "./~hooks.js";
14
16
  import { GhostAligner, useGhostAlignment } from "./~utils.GhostAligner.js";
15
17
  const TreeItemContext = React.createContext(void 0);
16
18
  const TreeItemRoot = forwardRef(
@@ -19,11 +21,12 @@ const TreeItemRoot = forwardRef(
19
21
  "aria-level": level,
20
22
  selected,
21
23
  expanded,
22
- icon,
24
+ icon: iconProp,
23
25
  unstable_decorations,
24
26
  label,
25
27
  description,
26
28
  actions,
29
+ error,
27
30
  onSelectedChange,
28
31
  onExpandedChange,
29
32
  onClick: onClickProp,
@@ -48,6 +51,7 @@ const TreeItemRoot = forwardRef(
48
51
  const labelId = React.useId();
49
52
  const descriptionId = React.useId();
50
53
  const decorationId = React.useId();
54
+ const icon = error ? /* @__PURE__ */ jsx(StatusWarning, {}) : iconProp;
51
55
  const describedBy = React.useMemo(() => {
52
56
  const idRefs = [];
53
57
  if (description) idRefs.push(descriptionId);
@@ -61,9 +65,10 @@ const TreeItemRoot = forwardRef(
61
65
  () => ({
62
66
  level,
63
67
  expanded,
64
- selected
68
+ selected,
69
+ error
65
70
  }),
66
- [level, expanded, selected]
71
+ [level, expanded, selected, error]
67
72
  ),
68
73
  children: /* @__PURE__ */ jsx(
69
74
  CompositeItem,
@@ -90,6 +95,7 @@ const TreeItemRoot = forwardRef(
90
95
  {
91
96
  "data-kiwi-expanded": expanded,
92
97
  "data-kiwi-selected": selected,
98
+ "data-kiwi-error": error ? true : void 0,
93
99
  className: "\u{1F95D}-tree-item-node",
94
100
  style: { "--\u{1F95D}tree-item-level": level },
95
101
  role: void 0,
@@ -139,24 +145,89 @@ const TreeItemRoot = forwardRef(
139
145
  }
140
146
  );
141
147
  const TreeItemActions = forwardRef((props, forwardedRef) => {
142
- return /* @__PURE__ */ jsx(
148
+ const { children, ...rest } = props;
149
+ const actions = React.Children.toArray(children).filter(Boolean);
150
+ const { error } = useSafeContext(TreeItemContext);
151
+ const limit = error ? 2 : 3;
152
+ return /* @__PURE__ */ jsxs(
143
153
  Toolbar,
144
154
  {
145
- ...props,
155
+ ...rest,
146
156
  onClick: useEventHandlers(props.onClick, (e) => e.stopPropagation()),
147
157
  onKeyDown: useEventHandlers(props.onKeyDown, (e) => e.stopPropagation()),
148
- className: cx("\u{1F95D}-tree-item-actions", props.className),
158
+ className: cx("\u{1F95D}-tree-item-actions-container", props.className),
159
+ focusLoop: false,
149
160
  ref: forwardedRef,
150
- children: props.children
161
+ children: [
162
+ actions.slice(0, limit - 1),
163
+ actions.length === limit ? actions[limit - 1] : null,
164
+ actions.length > limit ? /* @__PURE__ */ jsx(TreeItemActionsOverflowMenu, { children: actions.slice(limit - 1) }) : null
165
+ ]
151
166
  }
152
167
  );
153
168
  });
169
+ const arrowKeys = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"];
170
+ const TreeItemActionsOverflowMenuContext = React.createContext(false);
171
+ function TreeItemActionsOverflowMenu({ children }) {
172
+ const [open, setOpen] = React.useState(false);
173
+ const isArrowKeyPressed = React.useRef(false);
174
+ return /* @__PURE__ */ jsx(PopoverProvider, { placement: "right-start", children: /* @__PURE__ */ jsxs(
175
+ DropdownMenu.Root,
176
+ {
177
+ open,
178
+ setOpen: React.useCallback((value) => {
179
+ if (value && !isArrowKeyPressed.current) {
180
+ setOpen(true);
181
+ } else {
182
+ setOpen(false);
183
+ }
184
+ }, []),
185
+ children: [
186
+ /* @__PURE__ */ jsx(
187
+ DropdownMenu.Button,
188
+ {
189
+ onKeyDown: (e) => {
190
+ if (arrowKeys.includes(e.key)) {
191
+ isArrowKeyPressed.current = true;
192
+ }
193
+ queueMicrotask(() => {
194
+ isArrowKeyPressed.current = false;
195
+ });
196
+ },
197
+ render: /* @__PURE__ */ jsx(TreeItemAction, { label: "More", icon: /* @__PURE__ */ jsx(MoreHorizontal, {}) })
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsx(TreeItemActionsOverflowMenuContext.Provider, { value: true, children: /* @__PURE__ */ jsx(DropdownMenu.Content, { children }) })
201
+ ]
202
+ }
203
+ ) });
204
+ }
154
205
  const TreeItemAction = forwardRef(
155
206
  (props, forwardedRef) => {
156
- const { visible, ...rest } = props;
207
+ const { error } = useSafeContext(TreeItemContext);
208
+ const {
209
+ visible = error ? true : void 0,
210
+ // visible by default during error state
211
+ label,
212
+ icon,
213
+ ...rest
214
+ } = props;
215
+ if (React.useContext(TreeItemActionsOverflowMenuContext)) {
216
+ return /* @__PURE__ */ jsx(
217
+ DropdownMenu.Item,
218
+ {
219
+ ...rest,
220
+ label,
221
+ icon,
222
+ ref: forwardedRef
223
+ }
224
+ );
225
+ }
157
226
  return /* @__PURE__ */ jsx(
158
227
  IconButton,
159
228
  {
229
+ label,
230
+ icon,
160
231
  inert: visible === false ? true : void 0,
161
232
  ...rest,
162
233
  variant: "ghost",
@@ -10,7 +10,7 @@ export * as DropdownMenu from "./DropdownMenu.js";
10
10
  export { Divider } from "./Divider.js";
11
11
  export { Icon } from "./Icon.js";
12
12
  export { IconButton } from "./IconButton.js";
13
- export { Field } from "./Field.js";
13
+ export * as Field from "./Field.js";
14
14
  export { Kbd } from "./Kbd.js";
15
15
  export { Label } from "./Label.js";
16
16
  export { ProgressBar } from "./ProgressBar.js";
@@ -19,6 +19,7 @@ export * as Select from "./Select.js";
19
19
  export { Spinner } from "./Spinner.js";
20
20
  export { Skeleton } from "./Skeleton.js";
21
21
  export { Switch } from "./Switch.js";
22
+ export * as Table from "./Table.js";
22
23
  export * as Tabs from "./Tabs.js";
23
24
  export { Text } from "./Text.js";
24
25
  export * as TextBox from "./TextBox.js";
@@ -11,7 +11,7 @@ import * as DropdownMenu from "./DropdownMenu.js";
11
11
  import { Divider } from "./Divider.js";
12
12
  import { Icon } from "./Icon.js";
13
13
  import { IconButton } from "./IconButton.js";
14
- import { Field } from "./Field.js";
14
+ import * as Field from "./Field.js";
15
15
  import { Kbd } from "./Kbd.js";
16
16
  import { Label } from "./Label.js";
17
17
  import { ProgressBar } from "./ProgressBar.js";
@@ -20,6 +20,7 @@ import * as Select from "./Select.js";
20
20
  import { Spinner } from "./Spinner.js";
21
21
  import { Skeleton } from "./Skeleton.js";
22
22
  import { Switch } from "./Switch.js";
23
+ import * as Table from "./Table.js";
23
24
  import * as Tabs from "./Tabs.js";
24
25
  import { Text } from "./Text.js";
25
26
  import * as TextBox from "./TextBox.js";
@@ -48,6 +49,7 @@ export {
48
49
  Skeleton,
49
50
  Spinner,
50
51
  Switch,
52
+ Table,
51
53
  Tabs,
52
54
  Text,
53
55
  TextBox,