@dxos/react-ui 0.8.4-main.5ad4a44 → 0.8.4-main.66e292d

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 (174) hide show
  1. package/dist/lib/browser/{chunk-B7HPXBP2.mjs → chunk-N5GDJTT2.mjs} +421 -228
  2. package/dist/lib/browser/chunk-N5GDJTT2.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +5 -1
  4. package/dist/lib/browser/index.mjs.map +1 -1
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +47 -19
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/{chunk-6JCSY5Y7.mjs → chunk-SP7VQH72.mjs} +421 -228
  9. package/dist/lib/node-esm/chunk-SP7VQH72.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +5 -1
  11. package/dist/lib/node-esm/index.mjs.map +1 -1
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +47 -19
  14. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  15. package/dist/types/src/components/Button/Button.d.ts.map +1 -0
  16. package/dist/types/src/components/Button/Button.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/{Buttons → Button}/IconButton.d.ts +0 -1
  18. package/dist/types/src/components/Button/IconButton.d.ts.map +1 -0
  19. package/dist/types/src/components/Button/IconButton.stories.d.ts.map +1 -0
  20. package/dist/types/src/components/Button/Toggle.d.ts.map +1 -0
  21. package/dist/types/src/components/Button/Toggle.stories.d.ts +16 -0
  22. package/dist/types/src/components/Button/Toggle.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/Button/ToggleGroup.d.ts.map +1 -0
  24. package/dist/types/src/components/Button/ToggleGroup.stories.d.ts.map +1 -0
  25. package/dist/types/src/components/Button/index.d.ts.map +1 -0
  26. package/dist/types/src/components/Clipboard/CopyButton.d.ts +1 -1
  27. package/dist/types/src/components/Clipboard/CopyButton.d.ts.map +1 -1
  28. package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -0
  29. package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -0
  30. package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -0
  31. package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -0
  32. package/dist/types/src/components/Dialog/index.d.ts.map +1 -0
  33. package/dist/types/src/components/Icon/Icon.stories.d.ts +17 -0
  34. package/dist/types/src/components/Icon/Icon.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/List/List.d.ts.map +1 -0
  36. package/dist/types/src/components/List/List.stories.d.ts.map +1 -0
  37. package/dist/types/src/components/List/ListDropIndicator.d.ts.map +1 -0
  38. package/dist/types/src/components/List/Tree.d.ts.map +1 -0
  39. package/dist/types/src/components/List/Tree.stories.d.ts.map +1 -0
  40. package/dist/types/src/components/List/TreeDropIndicator.d.ts.map +1 -0
  41. package/dist/types/src/components/List/Treegrid.d.ts.map +1 -0
  42. package/dist/types/src/components/List/Treegrid.stories.d.ts.map +1 -0
  43. package/dist/types/src/components/List/index.d.ts.map +1 -0
  44. package/dist/types/src/components/Main/Main.d.ts +8 -8
  45. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  46. package/dist/types/src/components/Menus/DropdownMenu.d.ts +2 -3
  47. package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
  48. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  49. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts +37 -0
  50. package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -0
  51. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +18 -0
  52. package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -0
  53. package/dist/types/src/components/ScrollContainer/index.d.ts +2 -0
  54. package/dist/types/src/components/ScrollContainer/index.d.ts.map +1 -0
  55. package/dist/types/src/components/Select/Select.d.ts +1 -1
  56. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  57. package/dist/types/src/components/Toolbar/Toolbar.d.ts +10 -6
  58. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  59. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/index.d.ts +4 -3
  61. package/dist/types/src/components/index.d.ts.map +1 -1
  62. package/dist/types/src/hooks/useVisualViewport.d.ts +2 -2
  63. package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
  64. package/dist/types/src/playground/Custom.stories.d.ts.map +1 -1
  65. package/dist/types/src/testing/decorators/index.d.ts +2 -1
  66. package/dist/types/src/testing/decorators/index.d.ts.map +1 -1
  67. package/dist/types/src/testing/decorators/withLayout.d.ts +15 -0
  68. package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -0
  69. package/dist/types/src/testing/decorators/{withSurfaceVariantsLayout.d.ts → withLayoutVariants.d.ts} +2 -2
  70. package/dist/types/src/testing/decorators/withLayoutVariants.d.ts.map +1 -0
  71. package/dist/types/src/util/domino.d.ts +1 -1
  72. package/dist/types/src/util/domino.d.ts.map +1 -1
  73. package/dist/types/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +18 -15
  75. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +1 -1
  76. package/src/components/{Buttons → Button}/Button.stories.tsx +2 -2
  77. package/src/components/{Buttons → Button}/IconButton.tsx +12 -11
  78. package/src/components/{Buttons → Button}/Toggle.stories.tsx +5 -4
  79. package/src/components/Clipboard/CopyButton.tsx +1 -1
  80. package/src/components/{Dialogs → Dialog}/AlertDialog.stories.tsx +1 -1
  81. package/src/components/{Dialogs → Dialog}/Dialog.stories.tsx +1 -1
  82. package/src/components/Icon/Icon.stories.tsx +113 -0
  83. package/src/components/Icon/Icon.tsx +1 -1
  84. package/src/components/{Lists → List}/List.stories.tsx +1 -1
  85. package/src/components/{Lists → List}/ListDropIndicator.tsx +1 -1
  86. package/src/components/Main/Main.stories.tsx +1 -1
  87. package/src/components/Main/Main.tsx +13 -13
  88. package/src/components/Menus/DropdownMenu.stories.tsx +1 -1
  89. package/src/components/Menus/DropdownMenu.tsx +22 -3
  90. package/src/components/Popover/Popover.stories.tsx +1 -1
  91. package/src/components/Popover/Popover.tsx +20 -3
  92. package/src/components/ScrollArea/ScrollArea.stories.tsx +1 -1
  93. package/src/components/ScrollContainer/ScrollContainer.stories.tsx +69 -0
  94. package/src/components/ScrollContainer/ScrollContainer.tsx +231 -0
  95. package/src/components/ScrollContainer/index.ts +5 -0
  96. package/src/components/Select/Select.stories.tsx +2 -2
  97. package/src/components/Select/Select.tsx +4 -4
  98. package/src/components/Toast/Toast.stories.tsx +1 -1
  99. package/src/components/Toolbar/Toolbar.stories.tsx +2 -4
  100. package/src/components/Toolbar/Toolbar.tsx +17 -6
  101. package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
  102. package/src/components/index.ts +4 -3
  103. package/src/hooks/useSafeArea.ts +2 -2
  104. package/src/hooks/useVisualViewport.ts +3 -3
  105. package/src/playground/Controls.stories.tsx +2 -2
  106. package/src/playground/Custom.stories.tsx +6 -8
  107. package/src/testing/decorators/index.ts +2 -1
  108. package/src/testing/decorators/withLayout.tsx +56 -0
  109. package/src/testing/decorators/{withSurfaceVariantsLayout.tsx → withLayoutVariants.tsx} +2 -2
  110. package/src/util/domino.ts +6 -4
  111. package/dist/lib/browser/chunk-B7HPXBP2.mjs.map +0 -7
  112. package/dist/lib/node-esm/chunk-6JCSY5Y7.mjs.map +0 -7
  113. package/dist/types/src/components/Buttons/Button.d.ts.map +0 -1
  114. package/dist/types/src/components/Buttons/Button.stories.d.ts.map +0 -1
  115. package/dist/types/src/components/Buttons/IconButton.d.ts.map +0 -1
  116. package/dist/types/src/components/Buttons/IconButton.stories.d.ts.map +0 -1
  117. package/dist/types/src/components/Buttons/Toggle.d.ts.map +0 -1
  118. package/dist/types/src/components/Buttons/Toggle.stories.d.ts +0 -13
  119. package/dist/types/src/components/Buttons/Toggle.stories.d.ts.map +0 -1
  120. package/dist/types/src/components/Buttons/ToggleGroup.d.ts.map +0 -1
  121. package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts.map +0 -1
  122. package/dist/types/src/components/Buttons/index.d.ts.map +0 -1
  123. package/dist/types/src/components/Dialogs/AlertDialog.d.ts.map +0 -1
  124. package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts.map +0 -1
  125. package/dist/types/src/components/Dialogs/Dialog.d.ts.map +0 -1
  126. package/dist/types/src/components/Dialogs/Dialog.stories.d.ts.map +0 -1
  127. package/dist/types/src/components/Dialogs/index.d.ts.map +0 -1
  128. package/dist/types/src/components/Lists/List.d.ts.map +0 -1
  129. package/dist/types/src/components/Lists/List.stories.d.ts.map +0 -1
  130. package/dist/types/src/components/Lists/ListDropIndicator.d.ts.map +0 -1
  131. package/dist/types/src/components/Lists/Tree.d.ts.map +0 -1
  132. package/dist/types/src/components/Lists/Tree.stories.d.ts.map +0 -1
  133. package/dist/types/src/components/Lists/TreeDropIndicator.d.ts.map +0 -1
  134. package/dist/types/src/components/Lists/Treegrid.d.ts.map +0 -1
  135. package/dist/types/src/components/Lists/Treegrid.stories.d.ts.map +0 -1
  136. package/dist/types/src/components/Lists/index.d.ts.map +0 -1
  137. package/dist/types/src/testing/decorators/withSurfaceVariantsLayout.d.ts.map +0 -1
  138. /package/dist/types/src/components/{Buttons → Button}/Button.d.ts +0 -0
  139. /package/dist/types/src/components/{Buttons → Button}/Button.stories.d.ts +0 -0
  140. /package/dist/types/src/components/{Buttons → Button}/IconButton.stories.d.ts +0 -0
  141. /package/dist/types/src/components/{Buttons → Button}/Toggle.d.ts +0 -0
  142. /package/dist/types/src/components/{Buttons → Button}/ToggleGroup.d.ts +0 -0
  143. /package/dist/types/src/components/{Buttons → Button}/ToggleGroup.stories.d.ts +0 -0
  144. /package/dist/types/src/components/{Buttons → Button}/index.d.ts +0 -0
  145. /package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.d.ts +0 -0
  146. /package/dist/types/src/components/{Dialogs → Dialog}/AlertDialog.stories.d.ts +0 -0
  147. /package/dist/types/src/components/{Dialogs → Dialog}/Dialog.d.ts +0 -0
  148. /package/dist/types/src/components/{Dialogs → Dialog}/Dialog.stories.d.ts +0 -0
  149. /package/dist/types/src/components/{Dialogs → Dialog}/index.d.ts +0 -0
  150. /package/dist/types/src/components/{Lists → List}/List.d.ts +0 -0
  151. /package/dist/types/src/components/{Lists → List}/List.stories.d.ts +0 -0
  152. /package/dist/types/src/components/{Lists → List}/ListDropIndicator.d.ts +0 -0
  153. /package/dist/types/src/components/{Lists → List}/Tree.d.ts +0 -0
  154. /package/dist/types/src/components/{Lists → List}/Tree.stories.d.ts +0 -0
  155. /package/dist/types/src/components/{Lists → List}/TreeDropIndicator.d.ts +0 -0
  156. /package/dist/types/src/components/{Lists → List}/Treegrid.d.ts +0 -0
  157. /package/dist/types/src/components/{Lists → List}/Treegrid.stories.d.ts +0 -0
  158. /package/dist/types/src/components/{Lists → List}/index.d.ts +0 -0
  159. /package/src/components/{Buttons → Button}/Button.tsx +0 -0
  160. /package/src/components/{Buttons → Button}/IconButton.stories.tsx +0 -0
  161. /package/src/components/{Buttons → Button}/Toggle.tsx +0 -0
  162. /package/src/components/{Buttons → Button}/ToggleGroup.stories.tsx +0 -0
  163. /package/src/components/{Buttons → Button}/ToggleGroup.tsx +0 -0
  164. /package/src/components/{Buttons → Button}/index.ts +0 -0
  165. /package/src/components/{Dialogs → Dialog}/AlertDialog.tsx +0 -0
  166. /package/src/components/{Dialogs → Dialog}/Dialog.tsx +0 -0
  167. /package/src/components/{Dialogs → Dialog}/index.ts +0 -0
  168. /package/src/components/{Lists → List}/List.tsx +0 -0
  169. /package/src/components/{Lists → List}/Tree.stories.tsx +0 -0
  170. /package/src/components/{Lists → List}/Tree.tsx +0 -0
  171. /package/src/components/{Lists → List}/TreeDropIndicator.tsx +0 -0
  172. /package/src/components/{Lists → List}/Treegrid.stories.tsx +0 -0
  173. /package/src/components/{Lists → List}/Treegrid.tsx +0 -0
  174. /package/src/components/{Lists → List}/index.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui",
3
- "version": "0.8.4-main.5ad4a44",
3
+ "version": "0.8.4-main.66e292d",
4
4
  "description": "Low-level React components for DXOS, applying a theme to a core group of primitives",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -56,7 +56,7 @@
56
56
  "@radix-ui/react-portal": "1.1.4",
57
57
  "@radix-ui/react-presence": "1.1.2",
58
58
  "@radix-ui/react-primitive": "2.0.2",
59
- "@radix-ui/react-scroll-area": "1.2.3",
59
+ "@radix-ui/react-scroll-area": "1.2.10",
60
60
  "@radix-ui/react-select": "2.1.6",
61
61
  "@radix-ui/react-separator": "1.1.2",
62
62
  "@radix-ui/react-slot": "1.1.2",
@@ -74,32 +74,35 @@
74
74
  "keyborg": "^2.5.0",
75
75
  "react-i18next": "^11.18.6",
76
76
  "react-remove-scroll": "^2.6.0",
77
- "@dxos/debug": "0.8.4-main.5ad4a44",
78
- "@dxos/lit-ui": "0.8.4-main.5ad4a44",
79
- "@dxos/log": "0.8.4-main.5ad4a44",
80
- "@dxos/react-hooks": "0.8.4-main.5ad4a44",
81
- "@dxos/react-input": "0.8.4-main.5ad4a44",
82
- "@dxos/react-list": "0.8.4-main.5ad4a44",
83
- "@dxos/react-ui-types": "0.8.4-main.5ad4a44",
84
- "@dxos/util": "0.8.4-main.5ad4a44"
77
+ "@dxos/async": "0.8.4-main.66e292d",
78
+ "@dxos/invariant": "0.8.4-main.66e292d",
79
+ "@dxos/debug": "0.8.4-main.66e292d",
80
+ "@dxos/lit-ui": "0.8.4-main.66e292d",
81
+ "@dxos/react-hooks": "0.8.4-main.66e292d",
82
+ "@dxos/react-input": "0.8.4-main.66e292d",
83
+ "@dxos/log": "0.8.4-main.66e292d",
84
+ "@dxos/react-list": "0.8.4-main.66e292d",
85
+ "@dxos/react-ui-types": "0.8.4-main.66e292d",
86
+ "@dxos/util": "0.8.4-main.66e292d"
85
87
  },
86
88
  "devDependencies": {
87
89
  "@dnd-kit/core": "^6.0.5",
88
90
  "@dnd-kit/sortable": "^7.0.1",
89
91
  "@dnd-kit/utilities": "^3.2.0",
92
+ "@phosphor-icons/react": "^2.1.10",
90
93
  "@types/react": "~19.2.2",
91
- "@types/react-dom": "~19.2.1",
94
+ "@types/react-dom": "~19.2.2",
92
95
  "react": "~19.2.0",
93
96
  "react-dom": "~19.2.0",
94
97
  "vite": "7.1.9",
95
- "@dxos/random": "0.8.4-main.5ad4a44",
96
- "@dxos/util": "0.8.4-main.5ad4a44",
97
- "@dxos/react-ui-theme": "0.8.4-main.5ad4a44"
98
+ "@dxos/random": "0.8.4-main.66e292d",
99
+ "@dxos/react-ui-theme": "0.8.4-main.66e292d",
100
+ "@dxos/util": "0.8.4-main.66e292d"
98
101
  },
99
102
  "peerDependencies": {
100
103
  "react": "^19.0.0",
101
104
  "react-dom": "^19.0.0",
102
- "@dxos/react-ui-theme": "0.8.4-main.5ad4a44"
105
+ "@dxos/react-ui-theme": "0.8.4-main.66e292d"
103
106
  },
104
107
  "publishConfig": {
105
108
  "access": "public"
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
 
11
11
  import { Breadcrumb, type BreadcrumbRootProps } from './Breadcrumb';
12
12
 
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { withSurfaceVariantsLayout } from '../../testing';
9
+ import { withLayoutVariants } from '../../testing';
10
10
  import { Icon } from '../Icon';
11
11
 
12
12
  import { Button, ButtonGroup, type ButtonProps } from './Button';
@@ -36,7 +36,7 @@ const meta = {
36
36
  title: 'ui/react-ui-core/Button',
37
37
  component: Button,
38
38
  render: DefaultStory,
39
- decorators: [withTheme, withSurfaceVariantsLayout()],
39
+ decorators: [withTheme, withLayoutVariants()],
40
40
  } satisfies Meta<typeof Button>;
41
41
 
42
42
  export default meta;
@@ -18,15 +18,23 @@ type IconButtonProps = Omit<ButtonProps, 'children'> &
18
18
  noTooltip?: boolean;
19
19
  caretDown?: boolean;
20
20
  iconClassNames?: ThemedClassName<any>['classNames'];
21
- tooltipPortal?: boolean;
22
21
  tooltipSide?: TooltipSide;
23
22
  };
24
23
 
24
+ const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props, forwardedRef) =>
25
+ props.iconOnly ? (
26
+ <IconOnlyButton {...props} ref={forwardedRef} />
27
+ ) : (
28
+ <LabelledIconButton {...props} ref={forwardedRef} />
29
+ ),
30
+ );
31
+
25
32
  const IconOnlyButton = forwardRef<HTMLButtonElement, IconButtonProps>(
26
- ({ noTooltip, tooltipPortal = true, tooltipSide, ...props }, forwardedRef) => {
33
+ ({ noTooltip, tooltipSide, ...props }, forwardedRef) => {
27
34
  if (noTooltip) {
28
35
  return <LabelledIconButton {...props} ref={forwardedRef} />;
29
36
  }
37
+
30
38
  return (
31
39
  <Tooltip.Trigger asChild content={props.label} side={tooltipSide}>
32
40
  <LabelledIconButton {...props} ref={forwardedRef} />
@@ -35,8 +43,9 @@ const IconOnlyButton = forwardRef<HTMLButtonElement, IconButtonProps>(
35
43
  },
36
44
  );
37
45
 
46
+ // TODO(burdon): Inherit size from container/density.
38
47
  const LabelledIconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
39
- ({ icon, size, iconOnly, label, classNames, iconClassNames, caretDown, ...props }, forwardedRef) => {
48
+ ({ icon, size = 5, iconOnly, label, classNames, iconClassNames, caretDown, ...props }, forwardedRef) => {
40
49
  const { tx } = useThemeContext();
41
50
  return (
42
51
  <Button {...props} classNames={tx('iconButton.root', 'iconButton', { iconOnly }, classNames)} ref={forwardedRef}>
@@ -48,14 +57,6 @@ const LabelledIconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
48
57
  },
49
58
  );
50
59
 
51
- const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>((props, forwardedRef) =>
52
- props.iconOnly ? (
53
- <IconOnlyButton {...props} ref={forwardedRef} />
54
- ) : (
55
- <LabelledIconButton {...props} ref={forwardedRef} />
56
- ),
57
- );
58
-
59
60
  export { IconButton };
60
61
 
61
62
  export type { IconButtonProps };
@@ -8,11 +8,9 @@ import React from 'react';
8
8
  import { withTheme } from '../../testing';
9
9
  import { Icon } from '../Icon';
10
10
 
11
- import { Toggle } from './Toggle';
11
+ import { Toggle, type ToggleProps } from './Toggle';
12
12
 
13
- type StorybookToggleProps = {};
14
-
15
- const DefaultStory = (props: StorybookToggleProps) => {
13
+ const DefaultStory = (props: ToggleProps) => {
16
14
  return (
17
15
  <Toggle {...props}>
18
16
  <Icon icon='ph--text-b--regular' />
@@ -25,6 +23,9 @@ const meta = {
25
23
  component: Toggle,
26
24
  render: DefaultStory,
27
25
  decorators: [withTheme],
26
+ parameters: {
27
+ layout: 'centered',
28
+ },
28
29
  } satisfies Meta<typeof Toggle>;
29
30
 
30
31
  export default meta;
@@ -6,7 +6,7 @@ import React from 'react';
6
6
 
7
7
  import { mx } from '@dxos/react-ui-theme';
8
8
 
9
- import { Button, type ButtonProps, IconButton } from '../Buttons';
9
+ import { Button, type ButtonProps, IconButton } from '../Button';
10
10
  import { Icon, type IconProps } from '../Icon';
11
11
  import { useTranslation } from '../ThemeProvider';
12
12
  import { type TooltipScopedProps, useTooltipContext } from '../Tooltip';
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
  import { Toolbar } from '../Toolbar';
11
11
 
12
12
  import { AlertDialog } from './AlertDialog';
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
 
11
11
  import { Dialog } from './Dialog';
12
12
 
@@ -0,0 +1,113 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { IconBase, type IconProps, type IconWeight } from '@phosphor-icons/react';
6
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
+ import React, { type FC, type ReactElement, type SVGProps, forwardRef } from 'react';
8
+
9
+ import { getSize, mx } from '@dxos/react-ui-theme';
10
+
11
+ import { withTheme } from '../../testing';
12
+
13
+ import { Icon } from './Icon';
14
+
15
+ /**
16
+ * Create icon from serializable data.
17
+ * https://github.com/phosphor-icons/react#custom-icons
18
+ * https://github.com/phosphor-icons/core/tree/main/assets
19
+ */
20
+ const createIcon = ({
21
+ name,
22
+ weights,
23
+ }: {
24
+ name: string;
25
+ weights: Record<string, SVGProps<SVGPathElement>[]>;
26
+ }): FC<IconProps> => {
27
+ const CustomIcon = forwardRef<SVGSVGElement, IconProps>((props, ref) => (
28
+ <IconBase
29
+ ref={ref}
30
+ {...props}
31
+ weights={
32
+ new Map<IconWeight, ReactElement>(
33
+ Object.entries(weights).map(
34
+ ([key, paths]) =>
35
+ [
36
+ key,
37
+ <>
38
+ {paths.map((props, i) => (
39
+ <path key={`${key}-${i}`} {...props} />
40
+ ))}
41
+ </>,
42
+ ] as [IconWeight, ReactElement],
43
+ ),
44
+ )
45
+ }
46
+ />
47
+ ));
48
+
49
+ CustomIcon.displayName = name;
50
+ return CustomIcon;
51
+ };
52
+
53
+ const DefaultStory = ({ CustomIcon }: { CustomIcon: FC<IconProps> }) => {
54
+ return (
55
+ <div className='grid grid-cols-2 gap-8'>
56
+ <CustomIcon weight={'regular'} className={mx(getSize(16))} />
57
+ <Icon icon='ph--github-logo--regular' classNames={mx(getSize(16))} />
58
+ </div>
59
+ );
60
+ };
61
+
62
+ const meta = {
63
+ title: 'ui/react-ui-core/Icon',
64
+ render: DefaultStory,
65
+ decorators: [withTheme],
66
+ parameters: {
67
+ layout: 'centered',
68
+ },
69
+ } satisfies Meta<typeof DefaultStory>;
70
+
71
+ export default meta;
72
+
73
+ type Story = StoryObj<typeof meta>;
74
+
75
+ export const Default: Story = {
76
+ args: {
77
+ CustomIcon: createIcon({
78
+ name: 'GithubLogo',
79
+ weights: {
80
+ // https://github.com/phosphor-icons/core/tree/main/assets
81
+ // <path d="M119.83,56A52,52,0,0,0,76,32a51.92,51.92,0,0,0-3.49,44.7A49.28,49.28,0,0,0,64,104v8a48,48,0,0,0,48,48h48a48,48,0,0,0,48-48v-8a49.28,49.28,0,0,0-8.51-27.3A51.92,51.92,0,0,0,196,32a52,52,0,0,0-43.83,24Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
82
+ // <path d="M104,232V192a32,32,0,0,1,32-32h0a32,32,0,0,1,32,32v40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
83
+ // <path d="M104,208H72a32,32,0,0,1-32-32A32,32,0,0,0,8,144" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/>
84
+ regular: [
85
+ {
86
+ d: 'M119.83,56A52,52,0,0,0,76,32a51.92,51.92,0,0,0-3.49,44.7A49.28,49.28,0,0,0,64,104v8a48,48,0,0,0,48,48h48a48,48,0,0,0,48-48v-8a49.28,49.28,0,0,0-8.51-27.3A51.92,51.92,0,0,0,196,32a52,52,0,0,0-43.83,24Z',
87
+ fill: 'none',
88
+ stroke: 'currentColor',
89
+ strokeLinecap: 'round',
90
+ strokeLinejoin: 'round',
91
+ strokeWidth: '16',
92
+ },
93
+ {
94
+ d: 'M104,232V192a32,32,0,0,1,32-32h0a32,32,0,0,1,32,32v40',
95
+ fill: 'none',
96
+ stroke: 'currentColor',
97
+ strokeLinecap: 'round',
98
+ strokeLinejoin: 'round',
99
+ strokeWidth: '16',
100
+ },
101
+ {
102
+ d: 'M104,208H72a32,32,0,0,1-32-32A32,32,0,0,0,8,144',
103
+ fill: 'none',
104
+ stroke: 'currentColor',
105
+ strokeLinecap: 'round',
106
+ strokeLinejoin: 'round',
107
+ strokeWidth: '16',
108
+ },
109
+ ],
110
+ },
111
+ }),
112
+ },
113
+ };
@@ -16,7 +16,7 @@ export type IconProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.s
16
16
  };
17
17
 
18
18
  export const Icon = memo(
19
- forwardRef<SVGSVGElement, IconProps>(({ icon, classNames, size, ...props }, forwardedRef) => {
19
+ forwardRef<SVGSVGElement, IconProps>(({ icon, classNames, size = 4, ...props }, forwardedRef) => {
20
20
  const { tx } = useThemeContext();
21
21
  const href = useIconHref(icon);
22
22
  return (
@@ -229,7 +229,7 @@ export const SelectableListbox: Story = {
229
229
  onClick={() => setSelectedId(id)}
230
230
  onKeyUp={(event) => handleKeyUp(event, id)}
231
231
  >
232
- <ListItem.Heading classNames='flex pis-1 pie-1 items-center grow truncate'>{text}</ListItem.Heading>
232
+ <ListItem.Heading classNames='flex pli-1 items-center grow truncate'>{text}</ListItem.Heading>
233
233
  </ListItem.Root>
234
234
  ))}
235
235
  </List>
@@ -18,7 +18,7 @@ const orientationStyles: Record<Orientation, HTMLAttributes<HTMLElement>['classN
18
18
  horizontal:
19
19
  'h-[--line-thickness] left-[calc(var(--line-inset)+var(--terminal-radius))] right-[--line-inset] before:left-[--terminal-inset]',
20
20
  vertical:
21
- 'w-[--line-thickness] top-[calc(var(--line-inset)+var(--terminal-radius))] bottom-[--line-inset] before:top-[--terminal-inset]',
21
+ 'is-[--line-thickness] top-[calc(var(--line-inset)+var(--terminal-radius))] bottom-[--line-inset] before:top-[--terminal-inset]',
22
22
  };
23
23
 
24
24
  const edgeStyles: Record<Edge, HTMLAttributes<HTMLElement>['className']> = {
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
 
11
11
  import { Main, useSidebars } from './Main';
12
12
 
@@ -31,22 +31,12 @@ import { type Label, toLocalizedString, useTranslation } from '../ThemeProvider'
31
31
 
32
32
  import { useSwipeToDismiss } from './useSwipeToDismiss';
33
33
 
34
+ const MAIN_NAME = 'Main';
34
35
  const MAIN_ROOT_NAME = 'MainRoot';
35
36
  const NAVIGATION_SIDEBAR_NAME = 'NavigationSidebar';
36
37
  const COMPLEMENTARY_SIDEBAR_NAME = 'ComplementarySidebar';
37
- const MAIN_NAME = 'Main';
38
38
  const GENERIC_CONSUMER_NAME = 'GenericConsumer';
39
39
 
40
- type SidebarState = 'expanded' | 'collapsed' | 'closed';
41
-
42
- type MainContextValue = {
43
- resizing: boolean;
44
- navigationSidebarState: SidebarState;
45
- setNavigationSidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
46
- complementarySidebarState: SidebarState;
47
- setComplementarySidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
48
- };
49
-
50
40
  const landmarkAttr = 'data-main-landmark';
51
41
 
52
42
  /**
@@ -72,8 +62,8 @@ const useLandmarkMover = (propsOnKeyDown: ComponentPropsWithoutRef<'div'>['onKey
72
62
  [propsOnKeyDown],
73
63
  );
74
64
 
75
- // TODO(thure): This was disconnected once before in #8818, if this should change again to support the browser
76
- // extension, please ensure the change doesn’t break web, desktop and mobile.
65
+ // TODO(thure): This was disconnected once before in #8818;
66
+ // if this should change again to support the browser extension, please ensure the change doesn’t break web, desktop and mobile.
77
67
  const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited', ignoreDefaultKeydown: { Tab: true } });
78
68
 
79
69
  return {
@@ -84,6 +74,16 @@ const useLandmarkMover = (propsOnKeyDown: ComponentPropsWithoutRef<'div'>['onKey
84
74
  };
85
75
  };
86
76
 
77
+ type SidebarState = 'expanded' | 'collapsed' | 'closed';
78
+
79
+ type MainContextValue = {
80
+ resizing: boolean;
81
+ navigationSidebarState: SidebarState;
82
+ setNavigationSidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
83
+ complementarySidebarState: SidebarState;
84
+ setComplementarySidebarState: Dispatch<SetStateAction<SidebarState | undefined>>;
85
+ };
86
+
87
87
  const [MainProvider, useMainContext] = createContext<MainContextValue>(MAIN_NAME, {
88
88
  resizing: false,
89
89
  navigationSidebarState: 'closed',
@@ -6,7 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useRef, useState } from 'react';
7
7
 
8
8
  import { withTheme } from '../../testing';
9
- import { Button } from '../Buttons';
9
+ import { Button } from '../Button';
10
10
 
11
11
  import { DropdownMenu } from './DropdownMenu';
12
12
 
@@ -25,6 +25,7 @@ import React, {
25
25
  forwardRef,
26
26
  useCallback,
27
27
  useEffect,
28
+ useMemo,
28
29
  useRef,
29
30
  } from 'react';
30
31
 
@@ -42,7 +43,7 @@ const DROPDOWN_MENU_NAME = 'DropdownMenu';
42
43
 
43
44
  type ScopedProps<P> = P & { __scopeDropdownMenu?: Scope };
44
45
  const [createDropdownMenuContext, createDropdownMenuScope] = createContextScope(DROPDOWN_MENU_NAME, [createMenuScope]);
45
- const useMenuScope = createMenuScope();
46
+ const useMenuScope: (scope?: Scope) => any = createMenuScope();
46
47
 
47
48
  type DropdownMenuContextValue = {
48
49
  triggerId: string;
@@ -234,19 +235,35 @@ interface DropdownMenuContentProps extends Omit<MenuContentProps, 'onEntryFocus'
234
235
 
235
236
  const DropdownMenuContent = forwardRef<DropdownMenuContentElement, DropdownMenuContentProps>(
236
237
  (props: ScopedProps<DropdownMenuContentProps>, forwardedRef) => {
237
- const { __scopeDropdownMenu, classNames, collisionPadding = 8, ...contentProps } = props;
238
+ const { __scopeDropdownMenu, classNames, collisionPadding = 8, collisionBoundary, ...contentProps } = props;
238
239
  const { tx } = useThemeContext();
239
240
  const context = useDropdownMenuContext(CONTENT_NAME, __scopeDropdownMenu);
240
241
  const elevation = useElevationContext();
241
242
  const menuScope = useMenuScope(__scopeDropdownMenu);
242
243
  const hasInteractedOutsideRef = useRef(false);
243
244
  const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
245
+
246
+ // Check for the closest annotated collision boundary in the DOM tree.
247
+ const computedCollisionBoundary = useMemo(() => {
248
+ const closestBoundary = context.triggerRef.current?.closest(
249
+ '[data-popover-collision-boundary]',
250
+ ) as HTMLElement | null;
251
+ return closestBoundary
252
+ ? Array.isArray(collisionBoundary)
253
+ ? [closestBoundary, ...collisionBoundary]
254
+ : collisionBoundary
255
+ ? [closestBoundary, collisionBoundary]
256
+ : [closestBoundary]
257
+ : collisionBoundary;
258
+ }, [context.open, collisionBoundary, context.triggerRef.current]);
259
+
244
260
  return (
245
261
  <MenuPrimitive.Content
246
262
  id={context.contentId}
247
263
  aria-labelledby={context.triggerId}
248
264
  {...menuScope}
249
265
  {...contentProps}
266
+ collisionBoundary={computedCollisionBoundary}
250
267
  collisionPadding={safeCollisionPadding}
251
268
  ref={forwardedRef}
252
269
  onCloseAutoFocus={composeEventHandlers(props.onCloseAutoFocus, (event) => {
@@ -621,7 +638,9 @@ export const DropdownMenu = {
621
638
  SubContent: DropdownMenuSubContent,
622
639
  };
623
640
 
624
- const useDropdownMenuMenuScope = useMenuScope;
641
+ type DropdownMenuScope = Scope;
642
+
643
+ const useDropdownMenuMenuScope: (scope?: DropdownMenuScope) => any = useMenuScope;
625
644
 
626
645
  export { createDropdownMenuScope, useDropdownMenuContext, useDropdownMenuMenuScope };
627
646
 
@@ -8,7 +8,7 @@ import React, { type PropsWithChildren, type ReactNode, useRef, useState } from
8
8
  import { faker } from '@dxos/random';
9
9
 
10
10
  import { withTheme } from '../../testing';
11
- import { Button } from '../Buttons';
11
+ import { Button } from '../Button';
12
12
 
13
13
  import { Popover } from './Popover';
14
14
 
@@ -31,6 +31,7 @@ import React, {
31
31
  forwardRef,
32
32
  useCallback,
33
33
  useEffect,
34
+ useMemo,
34
35
  useRef,
35
36
  useState,
36
37
  } from 'react';
@@ -396,6 +397,7 @@ type PopoverContentImplElement = ElementRef<typeof PopperPrimitive.Content>;
396
397
  type FocusScopeProps = ComponentPropsWithoutRef<typeof FocusScope>;
397
398
  type DismissableLayerProps = ComponentPropsWithoutRef<typeof DismissableLayer>;
398
399
  type PopperContentProps = ThemedClassName<ComponentPropsWithoutRef<typeof PopperPrimitive.Content>>;
400
+
399
401
  interface PopoverContentImplProps
400
402
  extends Omit<PopperContentProps, 'onPlaced'>,
401
403
  Omit<DismissableLayerProps, 'onDismiss'> {
@@ -431,6 +433,7 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
431
433
  onFocusOutside,
432
434
  onInteractOutside,
433
435
  collisionPadding = 8,
436
+ collisionBoundary,
434
437
  classNames,
435
438
  ...contentProps
436
439
  } = props;
@@ -440,10 +443,23 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
440
443
  const elevation = useElevationContext();
441
444
  const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
442
445
 
443
- // Make sure the whole tree has focus guards as our `Popover` may be
444
- // the last element in the DOM (because of the `Portal`)
446
+ // Make sure the whole tree has focus guards as our `Popover` may be the last element in the DOM (because of the `Portal`)
445
447
  useFocusGuards();
446
448
 
449
+ // Check for the closest annotated collision boundary in the DOM tree.
450
+ const computedCollisionBoundary = useMemo(() => {
451
+ const closestBoundary = context.triggerRef.current?.closest(
452
+ '[data-popover-collision-boundary]',
453
+ ) as HTMLElement | null;
454
+ return closestBoundary
455
+ ? Array.isArray(collisionBoundary)
456
+ ? [closestBoundary, ...collisionBoundary]
457
+ : collisionBoundary
458
+ ? [closestBoundary, collisionBoundary]
459
+ : [closestBoundary]
460
+ : collisionBoundary;
461
+ }, [context.open, collisionBoundary, context.triggerRef.current]);
462
+
447
463
  return (
448
464
  <FocusScope
449
465
  asChild
@@ -468,11 +484,12 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
468
484
  {...popperScope}
469
485
  {...contentProps}
470
486
  collisionPadding={safeCollisionPadding}
487
+ collisionBoundary={computedCollisionBoundary}
471
488
  className={tx('popover.content', 'popover', { elevation }, classNames)}
472
489
  ref={forwardedRef}
473
490
  style={{
474
491
  ...contentProps.style,
475
- // re-namespace exposed content custom properties
492
+ // Re-namespace exposed content custom properties.
476
493
  ...{
477
494
  '--radix-popover-content-transform-origin': 'var(--radix-popper-transform-origin)',
478
495
  '--radix-popover-content-available-width': 'var(--radix-popper-available-width)',
@@ -34,7 +34,7 @@ const DefaultStory = ({ children }: PropsWithChildren<{}>) => {
34
34
  };
35
35
 
36
36
  const meta = {
37
- title: 'ui/react-ui-core/Scroll area',
37
+ title: 'ui/react-ui-core/ScrollArea',
38
38
  component: ScrollArea as any,
39
39
  render: DefaultStory,
40
40
  decorators: [withTheme],
@@ -0,0 +1,69 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React, { useEffect, useRef, useState } from 'react';
7
+
8
+ import { faker } from '@dxos/random';
9
+
10
+ import { withLayout, withTheme } from '../../testing';
11
+ import { Button } from '../Button';
12
+ import { Toolbar } from '../Toolbar';
13
+
14
+ import { ScrollContainer, type ScrollContainerRootProps, type ScrollController } from './ScrollContainer';
15
+
16
+ const DefaultStory = (props: ScrollContainerRootProps) => {
17
+ const [lines, setLines] = useState<string[]>([]);
18
+ const [running, setRunning] = useState(true);
19
+ const scroller = useRef<ScrollController>(null);
20
+ useEffect(() => {
21
+ if (!running) {
22
+ return;
23
+ }
24
+
25
+ const i = setInterval(() => {
26
+ setLines((lines) => [...lines, faker.lorem.paragraph()]);
27
+ }, 500);
28
+
29
+ return () => clearInterval(i);
30
+ }, [running]);
31
+
32
+ return (
33
+ <div className='flex flex-col bs-full overflow-hidden'>
34
+ <Toolbar.Root>
35
+ <Button onClick={() => setRunning((running) => !running)}>{running ? 'Stop' : 'Start'}</Button>
36
+ <Button onClick={() => scroller.current?.scrollToBottom()}>Scroll to bottom</Button>
37
+ <div className='flex-1' />
38
+ <div>{lines.length}</div>
39
+ </Toolbar.Root>
40
+ <ScrollContainer.Root {...props} ref={scroller}>
41
+ <ScrollContainer.Viewport>
42
+ {lines.map((line, index) => (
43
+ <div key={index} className='p-2'>
44
+ {line}
45
+ </div>
46
+ ))}
47
+ </ScrollContainer.Viewport>
48
+ </ScrollContainer.Root>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ const meta = {
54
+ title: 'ui/react-ui-core/ScrollContainer',
55
+ component: ScrollContainer.Root,
56
+ render: DefaultStory,
57
+ decorators: [withTheme, withLayout({ container: 'column', classNames: 'is-[30rem]' })],
58
+ } satisfies Meta<typeof DefaultStory>;
59
+
60
+ export default meta;
61
+
62
+ type Story = StoryObj<typeof meta>;
63
+
64
+ export const Default: Story = {
65
+ args: {
66
+ pin: true,
67
+ fade: true,
68
+ },
69
+ };