@dxos/react-ui 0.8.4-main.dedc0f3 → 0.8.4-main.ead640a

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 (156) hide show
  1. package/dist/lib/browser/{chunk-Y4PW3CX2.mjs → chunk-24AWTFTZ.mjs} +219 -100
  2. package/dist/lib/browser/chunk-24AWTFTZ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +6 -1
  4. package/dist/lib/browser/index.mjs.map +2 -2
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +54 -26
  7. package/dist/lib/browser/testing/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/{chunk-L6LIOSFT.mjs → chunk-MPRFBTTQ.mjs} +219 -100
  9. package/dist/lib/node-esm/chunk-MPRFBTTQ.mjs.map +7 -0
  10. package/dist/lib/node-esm/index.mjs +6 -1
  11. package/dist/lib/node-esm/index.mjs.map +2 -2
  12. package/dist/lib/node-esm/meta.json +1 -1
  13. package/dist/lib/node-esm/testing/index.mjs +54 -26
  14. package/dist/lib/node-esm/testing/index.mjs.map +4 -4
  15. package/dist/types/src/components/Avatars/Avatar.stories.d.ts +0 -6
  16. package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
  17. package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts +0 -6
  18. package/dist/types/src/components/Avatars/AvatarGroup.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts +0 -6
  20. package/dist/types/src/components/Breadcrumb/Breadcrumb.stories.d.ts.map +1 -1
  21. package/dist/types/src/components/Buttons/Button.d.ts +1 -1
  22. package/dist/types/src/components/Buttons/Button.d.ts.map +1 -1
  23. package/dist/types/src/components/Buttons/Button.stories.d.ts +1 -7
  24. package/dist/types/src/components/Buttons/Button.stories.d.ts.map +1 -1
  25. package/dist/types/src/components/Buttons/IconButton.stories.d.ts +0 -6
  26. package/dist/types/src/components/Buttons/IconButton.stories.d.ts.map +1 -1
  27. package/dist/types/src/components/Buttons/Toggle.stories.d.ts +0 -6
  28. package/dist/types/src/components/Buttons/Toggle.stories.d.ts.map +1 -1
  29. package/dist/types/src/components/Buttons/ToggleGroup.d.ts +2 -6
  30. package/dist/types/src/components/Buttons/ToggleGroup.d.ts.map +1 -1
  31. package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts +2 -12
  32. package/dist/types/src/components/Buttons/ToggleGroup.stories.d.ts.map +1 -1
  33. package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts +0 -6
  34. package/dist/types/src/components/Dialogs/AlertDialog.stories.d.ts.map +1 -1
  35. package/dist/types/src/components/Dialogs/Dialog.stories.d.ts +0 -1
  36. package/dist/types/src/components/Dialogs/Dialog.stories.d.ts.map +1 -1
  37. package/dist/types/src/components/Icon/Icon.d.ts +1 -1
  38. package/dist/types/src/components/Icon/Icon.d.ts.map +1 -1
  39. package/dist/types/src/components/Input/Input.d.ts +0 -2
  40. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  41. package/dist/types/src/components/Input/Input.stories.d.ts +0 -6
  42. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/Link/Link.stories.d.ts +0 -6
  44. package/dist/types/src/components/Link/Link.stories.d.ts.map +1 -1
  45. package/dist/types/src/components/Lists/List.stories.d.ts +0 -6
  46. package/dist/types/src/components/Lists/List.stories.d.ts.map +1 -1
  47. package/dist/types/src/components/Lists/Tree.stories.d.ts +0 -6
  48. package/dist/types/src/components/Lists/Tree.stories.d.ts.map +1 -1
  49. package/dist/types/src/components/Lists/Treegrid.d.ts.map +1 -1
  50. package/dist/types/src/components/Main/Main.d.ts +1 -10
  51. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  52. package/dist/types/src/components/Main/Main.stories.d.ts +1 -2
  53. package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
  54. package/dist/types/src/components/Menus/ContextMenu.stories.d.ts +0 -6
  55. package/dist/types/src/components/Menus/ContextMenu.stories.d.ts.map +1 -1
  56. package/dist/types/src/components/Menus/DropdownMenu.d.ts +1 -1
  57. package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
  58. package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts +0 -6
  59. package/dist/types/src/components/Menus/DropdownMenu.stories.d.ts.map +1 -1
  60. package/dist/types/src/components/Message/Message.stories.d.ts +0 -1
  61. package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
  62. package/dist/types/src/components/Popover/Popover.d.ts +1 -1
  63. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  64. package/dist/types/src/components/Popover/Popover.stories.d.ts +0 -6
  65. package/dist/types/src/components/Popover/Popover.stories.d.ts.map +1 -1
  66. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +0 -6
  67. package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
  68. package/dist/types/src/components/Select/Select.stories.d.ts +0 -6
  69. package/dist/types/src/components/Select/Select.stories.d.ts.map +1 -1
  70. package/dist/types/src/components/Status/Status.stories.d.ts +0 -6
  71. package/dist/types/src/components/Status/Status.stories.d.ts.map +1 -1
  72. package/dist/types/src/components/Tag/Tag.stories.d.ts +8 -8
  73. package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
  74. package/dist/types/src/components/Toast/Toast.stories.d.ts +0 -6
  75. package/dist/types/src/components/Toast/Toast.stories.d.ts.map +1 -1
  76. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -11
  77. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  78. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +0 -6
  79. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  80. package/dist/types/src/components/Tooltip/Tooltip.d.ts +1 -3
  81. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  82. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +0 -6
  83. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
  84. package/dist/types/src/hooks/useSafeArea.d.ts.map +1 -1
  85. package/dist/types/src/hooks/useVisualViewport.d.ts +2 -2
  86. package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
  87. package/dist/types/src/index.d.ts +1 -1
  88. package/dist/types/src/index.d.ts.map +1 -1
  89. package/dist/types/src/playground/Controls.stories.d.ts +0 -6
  90. package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
  91. package/dist/types/src/playground/Custom.stories.d.ts +1 -2
  92. package/dist/types/src/playground/Custom.stories.d.ts.map +1 -1
  93. package/dist/types/src/playground/Typography.stories.d.ts +0 -6
  94. package/dist/types/src/playground/Typography.stories.d.ts.map +1 -1
  95. package/dist/types/src/testing/decorators/index.d.ts +2 -1
  96. package/dist/types/src/testing/decorators/index.d.ts.map +1 -1
  97. package/dist/types/src/testing/decorators/withLayout.d.ts +15 -0
  98. package/dist/types/src/testing/decorators/withLayout.d.ts.map +1 -0
  99. package/dist/types/src/testing/decorators/withTheme.d.ts +3 -0
  100. package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
  101. package/dist/types/src/util/domino.d.ts +18 -0
  102. package/dist/types/src/util/domino.d.ts.map +1 -0
  103. package/dist/types/src/util/index.d.ts +2 -0
  104. package/dist/types/src/util/index.d.ts.map +1 -1
  105. package/dist/types/src/util/usePx.d.ts +8 -0
  106. package/dist/types/src/util/usePx.d.ts.map +1 -0
  107. package/dist/types/tsconfig.tsbuildinfo +1 -1
  108. package/package.json +20 -22
  109. package/src/components/Avatars/Avatar.stories.tsx +0 -2
  110. package/src/components/Avatars/AvatarGroup.stories.tsx +0 -3
  111. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -3
  112. package/src/components/Buttons/Button.stories.tsx +3 -5
  113. package/src/components/Buttons/IconButton.stories.tsx +0 -3
  114. package/src/components/Buttons/Toggle.stories.tsx +0 -3
  115. package/src/components/Buttons/ToggleGroup.stories.tsx +0 -3
  116. package/src/components/Dialogs/AlertDialog.stories.tsx +0 -3
  117. package/src/components/Dialogs/Dialog.stories.tsx +5 -3
  118. package/src/components/Icon/Icon.tsx +1 -1
  119. package/src/components/Input/Input.stories.tsx +0 -3
  120. package/src/components/Input/Input.tsx +3 -3
  121. package/src/components/Link/Link.stories.tsx +0 -3
  122. package/src/components/Lists/List.stories.tsx +2 -6
  123. package/src/components/Lists/Tree.stories.tsx +0 -3
  124. package/src/components/Lists/Treegrid.tsx +57 -16
  125. package/src/components/Main/Main.stories.tsx +3 -3
  126. package/src/components/Main/Main.tsx +17 -8
  127. package/src/components/Menus/ContextMenu.stories.tsx +0 -3
  128. package/src/components/Menus/DropdownMenu.stories.tsx +0 -3
  129. package/src/components/Menus/DropdownMenu.tsx +2 -2
  130. package/src/components/Message/Message.stories.tsx +0 -2
  131. package/src/components/Popover/Popover.stories.tsx +0 -3
  132. package/src/components/Popover/Popover.tsx +5 -5
  133. package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -3
  134. package/src/components/Select/Select.stories.tsx +3 -5
  135. package/src/components/Status/Status.stories.tsx +0 -3
  136. package/src/components/Tag/Tag.stories.tsx +8 -7
  137. package/src/components/Toast/Toast.stories.tsx +0 -3
  138. package/src/components/Toolbar/Toolbar.stories.tsx +0 -3
  139. package/src/components/Toolbar/Toolbar.tsx +14 -4
  140. package/src/components/Tooltip/Tooltip.stories.tsx +0 -3
  141. package/src/components/Tooltip/Tooltip.tsx +2 -1
  142. package/src/hooks/useSafeArea.ts +3 -2
  143. package/src/hooks/useVisualViewport.ts +4 -4
  144. package/src/index.ts +1 -1
  145. package/src/playground/Controls.stories.tsx +3 -5
  146. package/src/playground/Custom.stories.tsx +3 -12
  147. package/src/playground/Typography.stories.tsx +0 -3
  148. package/src/testing/decorators/index.ts +2 -1
  149. package/src/testing/decorators/withLayout.tsx +56 -0
  150. package/src/testing/decorators/withTheme.tsx +31 -0
  151. package/src/util/domino.ts +53 -0
  152. package/src/util/index.ts +2 -0
  153. package/src/util/usePx.ts +61 -0
  154. package/dist/lib/browser/chunk-Y4PW3CX2.mjs.map +0 -7
  155. package/dist/lib/node-esm/chunk-L6LIOSFT.mjs.map +0 -7
  156. package/src/testing/decorators/withTheme.ts +0 -25
@@ -182,7 +182,7 @@ PopoverTrigger.displayName = TRIGGER_NAME;
182
182
  const VIRTUAL_TRIGGER_NAME = 'PopoverVirtualTrigger';
183
183
 
184
184
  interface PopoverVirtualTriggerProps {
185
- virtualRef: RefObject<PopoverTriggerElement>;
185
+ virtualRef: RefObject<PopoverTriggerElement | null>;
186
186
  }
187
187
 
188
188
  const PopoverVirtualTrigger = (props: ScopedProps<PopoverVirtualTriggerProps>) => {
@@ -194,7 +194,7 @@ const PopoverVirtualTrigger = (props: ScopedProps<PopoverVirtualTriggerProps>) =
194
194
  context.triggerRef.current = virtualRef.current;
195
195
  }
196
196
  });
197
- return <PopperPrimitive.Anchor {...popperScope} virtualRef={virtualRef} />;
197
+ return <PopperPrimitive.Anchor {...popperScope} virtualRef={virtualRef as RefObject<PopoverTriggerElement>} />;
198
198
  };
199
199
 
200
200
  PopoverVirtualTrigger.displayName = VIRTUAL_TRIGGER_NAME;
@@ -396,6 +396,7 @@ type PopoverContentImplElement = ElementRef<typeof PopperPrimitive.Content>;
396
396
  type FocusScopeProps = ComponentPropsWithoutRef<typeof FocusScope>;
397
397
  type DismissableLayerProps = ComponentPropsWithoutRef<typeof DismissableLayer>;
398
398
  type PopperContentProps = ThemedClassName<ComponentPropsWithoutRef<typeof PopperPrimitive.Content>>;
399
+
399
400
  interface PopoverContentImplProps
400
401
  extends Omit<PopperContentProps, 'onPlaced'>,
401
402
  Omit<DismissableLayerProps, 'onDismiss'> {
@@ -440,8 +441,7 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
440
441
  const elevation = useElevationContext();
441
442
  const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
442
443
 
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`)
444
+ // Make sure the whole tree has focus guards as our `Popover` may be the last element in the DOM (because of the `Portal`)
445
445
  useFocusGuards();
446
446
 
447
447
  return (
@@ -472,7 +472,7 @@ const PopoverContentImpl = forwardRef<PopoverContentImplElement, PopoverContentI
472
472
  ref={forwardedRef}
473
473
  style={{
474
474
  ...contentProps.style,
475
- // re-namespace exposed content custom properties
475
+ // Re-namespace exposed content custom properties.
476
476
  ...{
477
477
  '--radix-popover-content-transform-origin': 'var(--radix-popper-transform-origin)',
478
478
  '--radix-popover-content-available-width': 'var(--radix-popper-available-width)',
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { type PropsWithChildren } from 'react';
9
7
 
@@ -40,7 +38,6 @@ const meta = {
40
38
  component: ScrollArea as any,
41
39
  render: DefaultStory,
42
40
  decorators: [withTheme],
43
- parameters: { chromatic: { disableSnapshot: false } },
44
41
  } satisfies Meta<typeof DefaultStory>;
45
42
 
46
43
  export default meta;
@@ -2,14 +2,13 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useState } from 'react';
9
7
 
10
8
  import { faker } from '@dxos/random';
11
9
 
12
- import { withSurfaceVariantsLayout, withTheme } from '../../testing';
10
+ import { withTheme } from '../../testing';
11
+ import { withSurfaceVariantsLayout } from '../../testing';
13
12
 
14
13
  import { Select } from './Select';
15
14
 
@@ -45,8 +44,7 @@ const DefaultStory = ({ items = [] }: StoryProps) => {
45
44
  const meta = {
46
45
  title: 'ui/react-ui-core/Select',
47
46
  render: DefaultStory,
48
- decorators: [withSurfaceVariantsLayout(), withTheme],
49
- parameters: { chromatic: { disableSnapshot: false } },
47
+ decorators: [withTheme, withSurfaceVariantsLayout()],
50
48
  } satisfies Meta<typeof DefaultStory>;
51
49
 
52
50
  export default meta;
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
@@ -15,7 +13,6 @@ const meta = {
15
13
  title: 'ui/react-ui-core/Status',
16
14
  component: Status,
17
15
  decorators: [withTheme],
18
- parameters: { chromatic: { disableSnapshot: false } },
19
16
  } satisfies Meta<typeof Status>;
20
17
 
21
18
  export default meta;
@@ -2,8 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
@@ -19,11 +17,6 @@ const palettes = ['neutral', 'success', 'info', 'warning', 'error', ...hues] as
19
17
  const meta = {
20
18
  title: 'ui/react-ui-core/Tag',
21
19
  component: Tag,
22
- decorators: [withTheme],
23
- parameters: { chromatic: { disableSnapshot: false } },
24
- } as const;
25
-
26
- export const Default: Story = {
27
20
  render: () => (
28
21
  <div role='grid' className='grid grid-cols-5 gap-2 max-is-screen-md'>
29
22
  {palettes.map((palette) => (
@@ -33,8 +26,16 @@ export const Default: Story = {
33
26
  ))}
34
27
  </div>
35
28
  ),
29
+ decorators: [withTheme],
30
+ parameters: {
31
+ chromatic: {
32
+ disableSnapshot: false,
33
+ },
34
+ },
36
35
  } satisfies Meta<typeof Tag>;
37
36
 
38
37
  export default meta;
39
38
 
40
39
  type Story = StoryObj<typeof meta>;
40
+
41
+ export const Default: Story = {};
@@ -2,8 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { type ReactNode, useState } from 'react';
9
7
 
@@ -51,7 +49,6 @@ const meta = {
51
49
  component: Toast as any,
52
50
  render: DefaultStory,
53
51
  decorators: [withTheme],
54
- parameters: { chromatic: { disableSnapshot: false } },
55
52
  } satisfies Meta<typeof DefaultStory>;
56
53
 
57
54
  export default meta;
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
@@ -77,7 +75,6 @@ const meta = {
77
75
  component: Toolbar as any,
78
76
  render: DefaultStory,
79
77
  decorators: [withTheme],
80
- parameters: { chromatic: { disableSnapshot: false } },
81
78
  } satisfies Meta<typeof DefaultStory>;
82
79
 
83
80
  export default meta;
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type { ToggleGroupItemProps as ToggleGroupItemPrimitiveProps } from '@radix-ui/react-toggle-group';
6
6
  import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
7
- import React, { forwardRef } from 'react';
7
+ import React, { Fragment, forwardRef } from 'react';
8
8
 
9
9
  import { useThemeContext } from '../../hooks';
10
10
  import { type ThemedClassName } from '../../util';
@@ -22,11 +22,21 @@ import {
22
22
  import { Link, type LinkProps } from '../Link';
23
23
  import { Separator, type SeparatorProps } from '../Separator';
24
24
 
25
- type ToolbarRootProps = ThemedClassName<ToolbarPrimitive.ToolbarProps> & { layoutManaged?: boolean };
25
+ type ToolbarRootProps = ThemedClassName<
26
+ ToolbarPrimitive.ToolbarProps & {
27
+ layoutManaged?: boolean;
28
+ textBlockWidth?: boolean;
29
+ }
30
+ >;
26
31
 
27
32
  const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
28
- ({ classNames, children, layoutManaged, ...props }, forwardedRef) => {
33
+ ({ classNames, children, layoutManaged, textBlockWidth: wrapContents, ...props }, forwardedRef) => {
29
34
  const { tx } = useThemeContext();
35
+ const InnerRoot = wrapContents ? 'div' : Fragment;
36
+ const innerRootProps = wrapContents
37
+ ? { role: 'none', className: tx('toolbar.inner', 'toolbar', { layoutManaged }, classNames) }
38
+ : {};
39
+
30
40
  return (
31
41
  <ToolbarPrimitive.Root
32
42
  {...props}
@@ -34,7 +44,7 @@ const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
34
44
  className={tx('toolbar.root', 'toolbar', { layoutManaged }, classNames)}
35
45
  ref={forwardedRef}
36
46
  >
37
- {children}
47
+ <InnerRoot {...innerRootProps}>{children}</InnerRoot>
38
48
  </ToolbarPrimitive.Root>
39
49
  );
40
50
  },
@@ -2,8 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
@@ -36,7 +34,6 @@ const meta = {
36
34
  component: Tooltip as any,
37
35
  render: DefaultStory,
38
36
  decorators: [withTheme],
39
- parameters: { chromatic: { disableSnapshot: false } },
40
37
  } satisfies Meta<typeof DefaultStory>;
41
38
 
42
39
  export default meta;
@@ -25,6 +25,7 @@ import React, {
25
25
  type FC,
26
26
  type MutableRefObject,
27
27
  type ReactNode,
28
+ type RefObject,
28
29
  type SyntheticEvent,
29
30
  forwardRef,
30
31
  useCallback,
@@ -218,7 +219,7 @@ const TooltipProvider: FC<TooltipProviderProps> = (props: TooltipScopedProps<Too
218
219
  {content}
219
220
  <TooltipArrow className={tx('tooltip.arrow', 'tooltip__arrow')} />
220
221
  </TooltipContent>
221
- <TooltipVirtualTrigger virtualRef={triggerRef} />
222
+ <TooltipVirtualTrigger virtualRef={triggerRef as RefObject<HTMLButtonElement>} />
222
223
  {children}
223
224
  </TooltipContextProvider>
224
225
  </PopperPrimitive.Root>
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { useCallback, useState } from 'react';
6
6
 
7
- import { useResize } from '@dxos/react-hooks';
7
+ import { useViewportResize } from '@dxos/react-hooks';
8
8
 
9
9
  export type SafeAreaPadding = Record<'top' | 'right' | 'bottom' | 'left', number>;
10
10
 
@@ -20,6 +20,7 @@ export const useSafeArea = (): SafeAreaPadding => {
20
20
  left: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-left')),
21
21
  });
22
22
  }, []);
23
- useResize(handleResize);
23
+
24
+ useViewportResize(handleResize);
24
25
  return padding;
25
26
  };
@@ -2,11 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { useCallback, type useEffect, useState } from 'react';
5
+ import { useCallback, useState } from 'react';
6
6
 
7
- import { useResize } from '@dxos/react-hooks';
7
+ import { useViewportResize } from '@dxos/react-hooks';
8
8
 
9
- export const useVisualViewport = (deps?: Parameters<typeof useEffect>[1]) => {
9
+ export const useVisualViewport = (deps?: Parameters<typeof useViewportResize>[1]) => {
10
10
  const [width, setWidth] = useState<number | null>(null);
11
11
  const [height, setHeight] = useState<number | null>(null);
12
12
 
@@ -17,7 +17,7 @@ export const useVisualViewport = (deps?: Parameters<typeof useEffect>[1]) => {
17
17
  }
18
18
  }, []);
19
19
 
20
- useResize(handleResize);
20
+ useViewportResize(handleResize, deps);
21
21
 
22
22
  return { width, height };
23
23
  };
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ export { type Resource, type TFunction } from 'i18next';
6
6
  export { Trans } from 'react-i18next';
7
7
 
8
8
  export * from '@dxos/react-hooks';
9
- export type * from '@dxos/react-ui-types';
9
+ export * from '@dxos/react-ui-types';
10
10
 
11
11
  export * from './components';
12
12
  export * from './hooks';
@@ -2,13 +2,12 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { useState } from 'react';
9
7
 
10
8
  import { Icon, Input, Select, Toggle, Toolbar } from '../components';
11
- import { withSurfaceVariantsLayout, withTheme } from '../testing';
9
+ import { withTheme } from '../testing';
10
+ import { withSurfaceVariantsLayout } from '../testing';
12
11
 
13
12
  const DefaultStory = () => {
14
13
  const [checked, setChecked] = useState<boolean>(false);
@@ -85,8 +84,7 @@ const DefaultStory = () => {
85
84
  const meta = {
86
85
  title: 'ui/react-ui-core/Playground/Controls',
87
86
  render: DefaultStory,
88
- decorators: [withSurfaceVariantsLayout(), withTheme],
89
- parameters: { chromatic: { disableSnapshot: false } },
87
+ decorators: [withTheme, withSurfaceVariantsLayout()],
90
88
  } satisfies Meta<typeof Icon>;
91
89
 
92
90
  export default meta;
@@ -2,23 +2,12 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
10
8
  import { Button, type ButtonProps, IconButton, Tooltip } from '../components';
11
9
  import { withTheme } from '../testing';
12
10
 
13
- // TODO(burdon): Change density to 3 or 4 sizes: (large, medium, small; or 22, 28, 32, 40)
14
- // TODO(burdon): IconButton should be square if no text.
15
- // TODO(burdon): IconButton icon should be auto-sized based on density.
16
-
17
- // TODO(burdon): Remove custom padding from all Buttons.
18
-
19
- // TODO(burdon): Forms w/ labels.
20
- // TODO(burdon): Card preview with sections.
21
-
22
11
  const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
23
12
  return (
24
13
  <Tooltip.Provider>
@@ -125,7 +114,9 @@ const meta = {
125
114
  component: Button,
126
115
  render: DefaultStory,
127
116
  decorators: [withTheme],
128
- parameters: { layout: 'centered' },
117
+ parameters: {
118
+ layout: 'centered',
119
+ },
129
120
  } satisfies Meta<typeof Button>;
130
121
 
131
122
  export default meta;
@@ -2,8 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React from 'react';
9
7
 
@@ -48,7 +46,6 @@ const meta = {
48
46
  title: 'ui/react-ui-core/Playground/Typography',
49
47
  render: DefaultStory,
50
48
  decorators: [withTheme],
51
- parameters: { chromatic: { disableSnapshot: false } },
52
49
  } satisfies Meta<typeof DefaultStory>;
53
50
 
54
51
  export default meta;
@@ -2,5 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export * from './withTheme';
5
+ export * from './withLayout';
6
6
  export * from './withSurfaceVariantsLayout';
7
+ export * from './withTheme';
@@ -0,0 +1,56 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Decorator } from '@storybook/react';
6
+ import React, { type FC, type PropsWithChildren } from 'react';
7
+
8
+ import { type ClassNameValue, type ThemedClassName } from '@dxos/react-ui';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+
11
+ export type ContainerProps = ThemedClassName<PropsWithChildren>;
12
+
13
+ export type ContainerType = 'default' | 'column';
14
+
15
+ export type WithLayoutProps =
16
+ | FC<ContainerProps>
17
+ | { classNames?: ClassNameValue; container?: ContainerType; scroll?: boolean };
18
+
19
+ /**
20
+ * Adds layout container.
21
+ */
22
+ export const withLayout =
23
+ (props: WithLayoutProps): Decorator =>
24
+ (Story) => {
25
+ if (typeof props === 'function') {
26
+ const Container = props;
27
+ return (
28
+ <Container>
29
+ <Story />
30
+ </Container>
31
+ );
32
+ }
33
+
34
+ const Container = layouts[(props as any).container as ContainerType] ?? layouts.default;
35
+ return (
36
+ <Container classNames={mx(props.classNames, props.scroll ? 'overflow-y-auto' : 'overflow-hidden')}>
37
+ <Story />
38
+ </Container>
39
+ );
40
+ };
41
+
42
+ const layouts: Record<ContainerType, FC<ContainerProps>> = {
43
+ default: ({ children, classNames }: ContainerProps) => (
44
+ <div role='none' className={mx(classNames)}>
45
+ {children}
46
+ </div>
47
+ ),
48
+
49
+ column: ({ children, classNames }: ContainerProps) => (
50
+ <div role='none' className='fixed inset-0 flex justify-center overflow-hidden bg-deckSurface'>
51
+ <div role='none' className={mx('flex flex-col is-[40rem] bg-baseSurface', classNames)}>
52
+ {children}
53
+ </div>
54
+ </div>
55
+ ),
56
+ };
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { type Decorator } from '@storybook/react';
6
+ import React, { memo } from 'react';
7
+
8
+ import { defaultTx } from '@dxos/react-ui-theme';
9
+
10
+ import { type ThemeMode, ThemeProvider, Tooltip } from '../../components';
11
+
12
+ /**
13
+ * Adds theme decorator (add to preview.ts)
14
+ */
15
+ export const withTheme: Decorator = (Story, context) => {
16
+ const {
17
+ globals: { theme },
18
+ parameters: { translations },
19
+ } = context;
20
+
21
+ // Prevent re-rendering of the story.
22
+ const MemoizedStory = memo(Story);
23
+
24
+ return (
25
+ <ThemeProvider tx={defaultTx} themeMode={theme as ThemeMode} resourceExtensions={translations} noCache>
26
+ <Tooltip.Provider>
27
+ <MemoizedStory />
28
+ </Tooltip.Provider>
29
+ </ThemeProvider>
30
+ );
31
+ };
@@ -0,0 +1,53 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { mx } from '@dxos/react-ui-theme';
6
+ import { type ClassNameValue } from '@dxos/react-ui-types';
7
+
8
+ /**
9
+ * Super lightweight chainable DOM builder.
10
+ */
11
+ export class Domino<T extends HTMLElement> {
12
+ static of<K extends keyof HTMLElementTagNameMap>(tag: K): Domino<HTMLElementTagNameMap[K]> {
13
+ return new Domino<HTMLElementTagNameMap[K]>(tag);
14
+ }
15
+
16
+ private readonly _el: T;
17
+ constructor(tag: keyof HTMLElementTagNameMap) {
18
+ this._el = document.createElement(tag) as T;
19
+ }
20
+ classNames(...classNames: ClassNameValue[]): this {
21
+ this._el.className = mx(classNames);
22
+ return this;
23
+ }
24
+ text(value: string): this {
25
+ this._el.textContent = value;
26
+ return this;
27
+ }
28
+ data(key: string, value: string): this {
29
+ this._el.dataset[key] = value;
30
+ return this;
31
+ }
32
+ attributes(attr: Record<string, string | undefined>): this {
33
+ Object.entries(attr)
34
+ .filter(([_, value]) => value !== undefined)
35
+ .map(([key, value]) => this._el.setAttribute(key, value!));
36
+ return this;
37
+ }
38
+ style(styles: Partial<CSSStyleDeclaration>): this {
39
+ Object.assign(this._el.style, styles);
40
+ return this;
41
+ }
42
+ children<C extends HTMLElement>(...children: Domino<C>[]): this {
43
+ children.forEach((child) => this._el.appendChild(child.build()));
44
+ return this;
45
+ }
46
+ on(event: string, handler: (e: Event) => void): this {
47
+ this._el.addEventListener(event, handler);
48
+ return this;
49
+ }
50
+ build(): T {
51
+ return this._el;
52
+ }
53
+ }
package/src/util/index.ts CHANGED
@@ -2,5 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export * from './domino';
5
6
  export * from './hasIosKeyboard';
6
7
  export type * from './ThemedClassName';
8
+ export * from './usePx';
@@ -0,0 +1,61 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useCallback, useEffect, useMemo, useState } from 'react';
6
+
7
+ const getDocumentElementFontSize = () => parseFloat(getComputedStyle(document.documentElement).fontSize);
8
+
9
+ /**
10
+ * React hook that converts rem values to pixels and updates when the root font size changes.
11
+ *
12
+ * @param rem The rem value to convert to pixels
13
+ * @returns The current pixel value equivalent of the rem input
14
+ */
15
+ export const usePx = (rem: number): number => {
16
+ const [fontSize, setFontSize] = useState(() => {
17
+ if (typeof document !== 'undefined') {
18
+ return getDocumentElementFontSize();
19
+ }
20
+ return 16; // Default fallback for SSR
21
+ });
22
+
23
+ const updateFontSize = useCallback(() => {
24
+ setFontSize(getDocumentElementFontSize());
25
+ }, []);
26
+
27
+ useEffect(() => {
28
+ if (typeof document === 'undefined') {
29
+ return;
30
+ }
31
+
32
+ // Create a ResizeObserver to watch for font size changes on the document element
33
+ const resizeObserver = new ResizeObserver(updateFontSize);
34
+ resizeObserver.observe(document.documentElement);
35
+
36
+ // Also listen for viewport changes that might affect font size
37
+ const mediaQueryList = window.matchMedia('all');
38
+ const handleMediaChange = () => {
39
+ updateFontSize();
40
+ };
41
+
42
+ if (mediaQueryList.addEventListener) {
43
+ mediaQueryList.addEventListener('change', handleMediaChange);
44
+ } else {
45
+ // Fallback for older browsers
46
+ mediaQueryList.addListener(handleMediaChange);
47
+ }
48
+
49
+ return () => {
50
+ resizeObserver.disconnect();
51
+ if (mediaQueryList.removeEventListener) {
52
+ mediaQueryList.removeEventListener('change', handleMediaChange);
53
+ } else {
54
+ // Fallback for older browsers
55
+ mediaQueryList.removeListener(handleMediaChange);
56
+ }
57
+ };
58
+ }, []);
59
+
60
+ return useMemo(() => rem * fontSize, [fontSize]);
61
+ };