@dxos/react-ui 0.7.4 → 0.7.5-labs.071a3e2

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 (101) hide show
  1. package/dist/lib/browser/index.mjs +510 -347
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +766 -614
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +510 -347
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/Avatars/Avatar.d.ts +5 -9
  11. package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
  12. package/dist/types/src/components/Avatars/Avatar.stories.d.ts +1 -2
  13. package/dist/types/src/components/Avatars/Avatar.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Buttons/IconButton.d.ts +6 -2
  15. package/dist/types/src/components/Buttons/IconButton.d.ts.map +1 -1
  16. package/dist/types/src/components/Dialogs/AlertDialog.d.ts.map +1 -1
  17. package/dist/types/src/components/Dialogs/Dialog.d.ts.map +1 -1
  18. package/dist/types/src/components/Input/Input.d.ts +5 -6
  19. package/dist/types/src/components/Input/Input.d.ts.map +1 -1
  20. package/dist/types/src/components/Input/Input.stories.d.ts +1 -3
  21. package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
  22. package/dist/types/src/components/Lists/List.d.ts +2 -0
  23. package/dist/types/src/components/Lists/List.d.ts.map +1 -1
  24. package/dist/types/src/components/Lists/ListDropIndicator.d.ts +13 -0
  25. package/dist/types/src/components/Lists/ListDropIndicator.d.ts.map +1 -0
  26. package/dist/types/src/components/Lists/Tree.d.ts +2 -0
  27. package/dist/types/src/components/Lists/Tree.d.ts.map +1 -1
  28. package/dist/types/src/components/Lists/TreeDropIndicator.d.ts +8 -0
  29. package/dist/types/src/components/Lists/TreeDropIndicator.d.ts.map +1 -0
  30. package/dist/types/src/components/Main/Main.d.ts +35 -24
  31. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  32. package/dist/types/src/components/Main/Main.stories.d.ts +1 -1
  33. package/dist/types/src/components/Menus/ContextMenu.d.ts.map +1 -1
  34. package/dist/types/src/components/Menus/DropdownMenu.d.ts +2 -6
  35. package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
  36. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  37. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  38. package/dist/types/src/components/Separator/Separator.d.ts +3 -1
  39. package/dist/types/src/components/Separator/Separator.d.ts.map +1 -1
  40. package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
  41. package/dist/types/src/components/Tag/Tag.stories.d.ts +12 -5
  42. package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
  43. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +4 -2
  44. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
  45. package/dist/types/src/components/ThemeProvider/TranslationsProvider.d.ts +1 -0
  46. package/dist/types/src/components/ThemeProvider/TranslationsProvider.d.ts.map +1 -1
  47. package/dist/types/src/components/Toolbar/Toolbar.d.ts +15 -5
  48. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  49. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +7 -2
  50. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  51. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  52. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts +13 -1
  53. package/dist/types/src/components/Tooltip/Tooltip.stories.d.ts.map +1 -1
  54. package/dist/types/src/hooks/index.d.ts +1 -0
  55. package/dist/types/src/hooks/index.d.ts.map +1 -1
  56. package/dist/types/src/hooks/useSafeArea.d.ts +9 -0
  57. package/dist/types/src/hooks/useSafeArea.d.ts.map +1 -0
  58. package/dist/types/src/hooks/useSafeCollisionPadding.d.ts +10 -0
  59. package/dist/types/src/hooks/useSafeCollisionPadding.d.ts.map +1 -0
  60. package/dist/types/src/hooks/useVisualViewport.d.ts +1 -1
  61. package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
  62. package/dist/types/src/util/ThemedClassName.d.ts +1 -1
  63. package/dist/types/src/util/ThemedClassName.d.ts.map +1 -1
  64. package/dist/types/tsconfig.tsbuildinfo +1 -0
  65. package/package.json +43 -42
  66. package/src/components/Avatars/Avatar.tsx +3 -6
  67. package/src/components/Buttons/IconButton.tsx +25 -7
  68. package/src/components/Clipboard/CopyButton.tsx +1 -1
  69. package/src/components/Dialogs/AlertDialog.tsx +6 -2
  70. package/src/components/Dialogs/Dialog.tsx +7 -11
  71. package/src/components/Input/Input.stories.tsx +4 -6
  72. package/src/components/Input/Input.tsx +29 -44
  73. package/src/components/Lists/List.stories.tsx +2 -2
  74. package/src/components/Lists/List.tsx +3 -0
  75. package/src/components/Lists/ListDropIndicator.tsx +70 -0
  76. package/src/components/Lists/Tree.tsx +3 -0
  77. package/src/components/Lists/TreeDropIndicator.tsx +70 -0
  78. package/src/components/Main/Main.stories.tsx +1 -1
  79. package/src/components/Main/Main.tsx +79 -110
  80. package/src/components/Menus/ContextMenu.tsx +8 -6
  81. package/src/components/Menus/DropdownMenu.tsx +7 -4
  82. package/src/components/Popover/Popover.tsx +8 -2
  83. package/src/components/ScrollArea/ScrollArea.stories.tsx +2 -2
  84. package/src/components/Select/Select.tsx +7 -3
  85. package/src/components/Separator/Separator.tsx +14 -11
  86. package/src/components/Tag/Tag.stories.tsx +20 -31
  87. package/src/components/Tag/Tag.tsx +15 -6
  88. package/src/components/ThemeProvider/ThemeProvider.tsx +13 -5
  89. package/src/components/Toast/Toast.tsx +1 -1
  90. package/src/components/Toolbar/Toolbar.tsx +40 -10
  91. package/src/components/Tooltip/Tooltip.stories.tsx +13 -2
  92. package/src/components/Tooltip/Tooltip.tsx +18 -13
  93. package/src/hooks/index.ts +1 -0
  94. package/src/hooks/useSafeArea.ts +25 -0
  95. package/src/hooks/useSafeCollisionPadding.ts +39 -0
  96. package/src/hooks/useVisualViewport.ts +11 -12
  97. package/src/testing/decorators/withVariants.tsx +4 -4
  98. package/src/util/ThemedClassName.ts +1 -1
  99. package/dist/types/src/playground/Surfaces.stories.d.ts +0 -21
  100. package/dist/types/src/playground/Surfaces.stories.d.ts.map +0 -1
  101. package/src/playground/Surfaces.stories.tsx +0 -73
@@ -1,46 +1,35 @@
1
1
  //
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
+ import React from 'react';
4
5
 
6
+ import { hueTokenThemes } from '@dxos/react-ui-theme';
5
7
  import '@dxos-theme';
8
+ import { type ChromaticPalette, type MessageValence } from '@dxos/react-ui-types';
6
9
 
7
10
  import { Tag } from './Tag';
8
11
  import { withTheme } from '../../testing';
9
12
 
13
+ const palettes = ['neutral', 'success', 'info', 'warning', 'error', ...Object.keys(hueTokenThemes)] as (
14
+ | ChromaticPalette
15
+ | MessageValence
16
+ )[];
17
+
10
18
  export default {
11
19
  title: 'ui/react-ui-core/Tag',
12
20
  component: Tag,
13
21
  decorators: [withTheme],
14
22
  parameters: { chromatic: { disableSnapshot: false } },
15
- argTypes: {
16
- palette: {
17
- control: 'select',
18
- options: [
19
- 'neutral',
20
- 'success',
21
- 'info',
22
- 'warning',
23
- 'error',
24
- 'red',
25
- 'orange',
26
- 'amber',
27
- 'yellow',
28
- 'lime',
29
- 'green',
30
- 'emerald',
31
- 'teal',
32
- 'cyan',
33
- 'sky',
34
- 'blue',
35
- 'indigo',
36
- 'violet',
37
- 'purple',
38
- 'fuchsia',
39
- 'pink',
40
- 'rose',
41
- ],
42
- },
43
- },
44
- } as any;
23
+ } as const;
45
24
 
46
- export const Default = { args: { children: 'Hello', palette: 'success' } };
25
+ export const Default = {
26
+ render: () => (
27
+ <div role='grid' className='grid grid-cols-5 gap-2 max-is-screen-md'>
28
+ {palettes.map((palette) => (
29
+ <Tag key={palette} palette={palette}>
30
+ {palette}
31
+ </Tag>
32
+ ))}
33
+ </div>
34
+ ),
35
+ };
@@ -1,5 +1,5 @@
1
1
  //
2
- // Copyright 2022 DXOS.org
2
+ // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
5
  import { Primitive } from '@radix-ui/react-primitive';
@@ -16,8 +16,17 @@ export type TagProps = ThemedClassName<ComponentPropsWithRef<typeof Primitive.sp
16
16
  asChild?: boolean;
17
17
  };
18
18
 
19
- export const Tag = forwardRef<HTMLSpanElement, TagProps>(({ asChild, palette, classNames, ...props }, forwardedRef) => {
20
- const { tx } = useThemeContext();
21
- const Root = asChild ? Slot : Primitive.span;
22
- return <Root {...props} className={tx('tag.root', 'tag', { palette }, classNames)} ref={forwardedRef} />;
23
- });
19
+ export const Tag = forwardRef<HTMLSpanElement, TagProps>(
20
+ ({ asChild, palette = 'neutral', classNames, ...props }, forwardedRef) => {
21
+ const { tx } = useThemeContext();
22
+ const Root = asChild ? Slot : Primitive.span;
23
+ return (
24
+ <Root
25
+ {...props}
26
+ className={tx('tag.root', 'dx-tag', { palette }, classNames)}
27
+ data-hue={palette}
28
+ ref={forwardedRef}
29
+ />
30
+ );
31
+ },
32
+ );
@@ -3,11 +3,12 @@
3
3
  //
4
4
 
5
5
  import { createKeyborg } from 'keyborg';
6
- import React, { createContext, type PropsWithChildren, useEffect } from 'react';
6
+ import React, { createContext, type PropsWithChildren, useEffect, useMemo } from 'react';
7
7
 
8
8
  import { type Density, type Elevation, type ThemeFunction } from '@dxos/react-ui-types';
9
9
 
10
10
  import { TranslationsProvider, type TranslationsProviderProps } from './TranslationsProvider';
11
+ import { type SafeAreaPadding, useSafeArea } from '../../hooks';
11
12
  import { hasIosKeyboard } from '../../util';
12
13
  import { DensityProvider } from '../DensityProvider';
13
14
  import { ElevationProvider } from '../ElevationProvider';
@@ -18,6 +19,7 @@ export type ThemeContextValue = {
18
19
  tx: ThemeFunction<any>;
19
20
  themeMode: ThemeMode;
20
21
  hasIosKeyboard: boolean;
22
+ safeAreaPadding?: SafeAreaPadding;
21
23
  noCache?: boolean;
22
24
  };
23
25
 
@@ -27,7 +29,7 @@ export type ThemeContextValue = {
27
29
  export const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
28
30
 
29
31
  export type ThemeProviderProps = Omit<TranslationsProviderProps, 'children'> &
30
- Partial<ThemeContextValue> &
32
+ Partial<Omit<ThemeContextValue, 'safeAreaPadding'>> &
31
33
  PropsWithChildren<{
32
34
  rootDensity?: Density;
33
35
  rootElevation?: Elevation;
@@ -41,7 +43,6 @@ export const ThemeProvider = ({
41
43
  tx = (_path, defaultClassName, _styleProps, ..._options) => defaultClassName,
42
44
  themeMode = 'dark',
43
45
  rootDensity = 'fine',
44
- rootElevation = 'base',
45
46
  ...rest
46
47
  }: ThemeProviderProps) => {
47
48
  useEffect(() => {
@@ -52,8 +53,15 @@ export const ThemeProvider = ({
52
53
  }
53
54
  }, []);
54
55
 
56
+ const safeAreaPadding = useSafeArea();
57
+
58
+ const contextValue = useMemo(
59
+ () => ({ tx, themeMode, hasIosKeyboard: hasIosKeyboard(), safeAreaPadding, ...rest }),
60
+ [tx, themeMode, safeAreaPadding, rest],
61
+ );
62
+
55
63
  return (
56
- <ThemeContext.Provider value={{ tx, themeMode, hasIosKeyboard: hasIosKeyboard(), ...rest }}>
64
+ <ThemeContext.Provider value={contextValue}>
57
65
  <TranslationsProvider
58
66
  {...{
59
67
  fallback,
@@ -61,7 +69,7 @@ export const ThemeProvider = ({
61
69
  appNs,
62
70
  }}
63
71
  >
64
- <ElevationProvider elevation={rootElevation}>
72
+ <ElevationProvider elevation='base'>
65
73
  <DensityProvider density={rootDensity}>{children}</DensityProvider>
66
74
  </ElevationProvider>
67
75
  </TranslationsProvider>
@@ -45,7 +45,7 @@ const ToastRoot = forwardRef<HTMLLIElement, ToastRootProps>(({ classNames, child
45
45
  const { tx } = useThemeContext();
46
46
  return (
47
47
  <ToastRootPrimitive {...props} className={tx('toast.root', 'toast', {}, classNames)} ref={forwardedRef}>
48
- <ElevationProvider elevation='chrome'>{children}</ElevationProvider>
48
+ <ElevationProvider elevation='toast'>{children}</ElevationProvider>
49
49
  </ToastRootPrimitive>
50
50
  );
51
51
  });
@@ -2,6 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import type { ToggleGroupItemProps as ToggleGroupItemPrimitiveProps } from '@radix-ui/react-toggle-group';
5
6
  import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
6
7
  import React, { forwardRef } from 'react';
7
8
 
@@ -15,6 +16,8 @@ import {
15
16
  Toggle,
16
17
  type ToggleGroupItemProps,
17
18
  type ToggleProps,
19
+ IconButton,
20
+ type IconButtonProps,
18
21
  } from '../Buttons';
19
22
  import { Link, type LinkProps } from '../Link';
20
23
  import { Separator, type SeparatorProps } from '../Separator';
@@ -40,6 +43,16 @@ const ToolbarButton = forwardRef<HTMLButtonElement, ToolbarButtonProps>((props,
40
43
  );
41
44
  });
42
45
 
46
+ type ToolbarIconButtonProps = IconButtonProps;
47
+
48
+ const ToolbarIconButton = forwardRef<HTMLButtonElement, ToolbarIconButtonProps>((props, forwardedRef) => {
49
+ return (
50
+ <ToolbarPrimitive.Button asChild>
51
+ <IconButton {...props} ref={forwardedRef} />
52
+ </ToolbarPrimitive.Button>
53
+ );
54
+ });
55
+
43
56
  type ToolbarToggleProps = ToggleProps;
44
57
 
45
58
  const ToolbarToggle = forwardRef<HTMLButtonElement, ToolbarToggleProps>((props, forwardedRef) => {
@@ -88,35 +101,52 @@ const ToolbarToggleGroupItem = forwardRef<HTMLButtonElement, ToolbarToggleGroupI
88
101
  },
89
102
  );
90
103
 
91
- type ToolbarSeparatorProps = SeparatorProps;
104
+ type ToolbarToggleGroupIconItemProps = Omit<ToggleGroupItemPrimitiveProps, 'className'> & IconButtonProps;
92
105
 
93
- const ToolbarSeparator = (props: SeparatorProps) => {
94
- return (
95
- <ToolbarPrimitive.Separator asChild>
96
- <Separator orientation='vertical' {...props} />
97
- </ToolbarPrimitive.Separator>
98
- );
99
- };
106
+ const ToolbarToggleGroupIconItem = forwardRef<HTMLButtonElement, ToolbarToggleGroupIconItemProps>(
107
+ ({ variant, density, elevation, classNames, icon, label, iconOnly, ...props }, forwardedRef) => {
108
+ return (
109
+ <ToolbarPrimitive.ToolbarToggleItem {...props} asChild>
110
+ <IconButton {...{ variant, density, elevation, classNames, icon, label, iconOnly }} ref={forwardedRef} />
111
+ </ToolbarPrimitive.ToolbarToggleItem>
112
+ );
113
+ },
114
+ );
115
+
116
+ type ToolbarSeparatorProps = SeparatorProps & { variant?: 'gap' | 'line' };
100
117
 
101
- const ToolbarExpander = () => <div className={'grow'} />;
118
+ const ToolbarSeparator = forwardRef<HTMLDivElement, ToolbarSeparatorProps>(
119
+ ({ variant = 'line', ...props }, forwardedRef) => {
120
+ return variant === 'line' ? (
121
+ <ToolbarPrimitive.Separator asChild>
122
+ <Separator {...props} ref={forwardedRef} />
123
+ </ToolbarPrimitive.Separator>
124
+ ) : (
125
+ <ToolbarPrimitive.Separator className='grow' ref={forwardedRef} />
126
+ );
127
+ },
128
+ );
102
129
 
103
130
  export const Toolbar = {
104
131
  Root: ToolbarRoot,
105
132
  Button: ToolbarButton,
133
+ IconButton: ToolbarIconButton,
106
134
  Link: ToolbarLink,
107
135
  Toggle: ToolbarToggle,
108
136
  ToggleGroup: ToolbarToggleGroup,
109
137
  ToggleGroupItem: ToolbarToggleGroupItem,
138
+ ToggleGroupIconItem: ToolbarToggleGroupIconItem,
110
139
  Separator: ToolbarSeparator,
111
- Expander: ToolbarExpander,
112
140
  };
113
141
 
114
142
  export type {
115
143
  ToolbarRootProps,
116
144
  ToolbarButtonProps,
145
+ ToolbarIconButtonProps,
117
146
  ToolbarLinkProps,
118
147
  ToolbarToggleProps,
119
148
  ToolbarToggleGroupProps,
120
149
  ToolbarToggleGroupItemProps,
150
+ ToolbarToggleGroupIconItemProps,
121
151
  ToolbarSeparatorProps,
122
152
  };
@@ -12,11 +12,12 @@ import { Button } from '../Buttons';
12
12
 
13
13
  type StoryTooltipProps = {
14
14
  content: string;
15
+ defaultOpen?: boolean;
15
16
  };
16
17
 
17
- const StoryTooltip = ({ content }: StoryTooltipProps) => (
18
+ const StoryTooltip = ({ content, defaultOpen }: StoryTooltipProps) => (
18
19
  <Tooltip.Provider>
19
- <Tooltip.Root defaultOpen>
20
+ <Tooltip.Root defaultOpen={defaultOpen}>
20
21
  <Tooltip.Trigger asChild>
21
22
  <Button>Trigger tooltip</Button>
22
23
  </Tooltip.Trigger>
@@ -44,3 +45,13 @@ export const Default = {
44
45
  chromatic: { delay: 500 },
45
46
  },
46
47
  };
48
+
49
+ export const Testing = {
50
+ args: {
51
+ defaultOption: true,
52
+ content: 'This is the tooltip content',
53
+ },
54
+ parameters: {
55
+ chromatic: { delay: 500 },
56
+ },
57
+ };
@@ -18,7 +18,8 @@ import {
18
18
  } from '@radix-ui/react-tooltip';
19
19
  import React, { forwardRef, type FunctionComponent } from 'react';
20
20
 
21
- import { useThemeContext } from '../../hooks';
21
+ import { useElevationContext, useThemeContext } from '../../hooks';
22
+ import { useSafeCollisionPadding } from '../../hooks/useSafeCollisionPadding';
22
23
  import { type ThemedClassName } from '../../util';
23
24
 
24
25
  type TooltipProviderProps = TooltipProviderPrimitiveProps;
@@ -52,18 +53,22 @@ const TooltipArrow = forwardRef<SVGSVGElement, TooltipArrowProps>(({ classNames,
52
53
 
53
54
  type TooltipContentProps = ThemedClassName<TooltipContentPrimitiveProps>;
54
55
 
55
- const TooltipContent = forwardRef<HTMLDivElement, TooltipContentProps>(({ classNames, ...props }, forwardedRef) => {
56
- const { tx } = useThemeContext();
57
- return (
58
- <TooltipContentPrimitive
59
- sideOffset={4}
60
- collisionPadding={8}
61
- {...props}
62
- className={tx('tooltip.content', 'tooltip', {}, classNames)}
63
- ref={forwardedRef}
64
- />
65
- );
66
- });
56
+ const TooltipContent = forwardRef<HTMLDivElement, TooltipContentProps>(
57
+ ({ classNames, collisionPadding = 8, ...props }, forwardedRef) => {
58
+ const { tx } = useThemeContext();
59
+ const elevation = useElevationContext();
60
+ const safeCollisionPadding = useSafeCollisionPadding(collisionPadding);
61
+ return (
62
+ <TooltipContentPrimitive
63
+ sideOffset={4}
64
+ {...props}
65
+ collisionPadding={safeCollisionPadding}
66
+ className={tx('tooltip.content', 'tooltip', { elevation }, classNames)}
67
+ ref={forwardedRef}
68
+ />
69
+ );
70
+ },
71
+ );
67
72
 
68
73
  export const Tooltip = {
69
74
  Provider: TooltipProvider,
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './useDensityContext';
6
6
  export * from './useElevationContext';
7
+ export * from './useSafeArea';
7
8
  export * from './useTranslationsContext';
8
9
  export * from './useThemeContext';
9
10
  export * from './useVisualViewport';
@@ -0,0 +1,25 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useCallback, useState } from 'react';
6
+
7
+ import { useResize } from '@dxos/react-hooks';
8
+
9
+ export type SafeAreaPadding = Record<'top' | 'right' | 'bottom' | 'left', number>;
10
+
11
+ export const initialSafeArea = { top: NaN, right: NaN, bottom: NaN, left: NaN };
12
+
13
+ export const useSafeArea = (): SafeAreaPadding => {
14
+ const [padding, setPadding] = useState<SafeAreaPadding>(initialSafeArea);
15
+ const handleResize = useCallback(() => {
16
+ setPadding({
17
+ top: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-top')),
18
+ right: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-right')),
19
+ bottom: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom')),
20
+ left: parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-left')),
21
+ });
22
+ }, []);
23
+ useResize(handleResize);
24
+ return padding;
25
+ };
@@ -0,0 +1,39 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type TooltipContentProps } from '@radix-ui/react-tooltip';
6
+ import { useMemo } from 'react';
7
+
8
+ import { type SafeAreaPadding } from './useSafeArea';
9
+ import { useThemeContext } from './useThemeContext';
10
+
11
+ type PaddingProp = TooltipContentProps['collisionPadding'];
12
+ type PaddingRecord = Exclude<PaddingProp, number | undefined>;
13
+
14
+ const propIsNumber = (prop: PaddingProp): prop is number => Number.isFinite(prop);
15
+ const propsIsRecord = (prop: PaddingProp): prop is PaddingRecord => !!(prop && typeof prop === 'object');
16
+
17
+ const safePadding = (
18
+ propsPadding: TooltipContentProps['collisionPadding'],
19
+ safePadding: SafeAreaPadding,
20
+ side: keyof SafeAreaPadding,
21
+ ) => {
22
+ return (
23
+ (propIsNumber(safePadding[side]) ? safePadding[side] : 0) +
24
+ (propIsNumber(propsPadding) ? propsPadding : propsIsRecord(propsPadding) ? propsPadding[side] ?? 0 : 0)
25
+ );
26
+ };
27
+
28
+ export const useSafeCollisionPadding = (collisionPadding?: PaddingProp) => {
29
+ const { safeAreaPadding } = useThemeContext();
30
+ return useMemo(
31
+ () => ({
32
+ top: safePadding(collisionPadding, safeAreaPadding!, 'top'),
33
+ right: safePadding(collisionPadding, safeAreaPadding!, 'right'),
34
+ bottom: safePadding(collisionPadding, safeAreaPadding!, 'bottom'),
35
+ left: safePadding(collisionPadding, safeAreaPadding!, 'left'),
36
+ }),
37
+ [collisionPadding, safeAreaPadding],
38
+ );
39
+ };
@@ -2,23 +2,22 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { useEffect, useState } from 'react';
5
+ import { useCallback, type useEffect, useState } from 'react';
6
+
7
+ import { useResize } from '@dxos/react-hooks';
6
8
 
7
9
  export const useVisualViewport = (deps?: Parameters<typeof useEffect>[1]) => {
8
10
  const [width, setWidth] = useState<number | null>(null);
9
11
  const [height, setHeight] = useState<number | null>(null);
10
12
 
11
- useEffect(() => {
12
- const handleResize = () => {
13
- if (window.visualViewport) {
14
- setWidth(window.visualViewport.width);
15
- setHeight(window.visualViewport.height);
16
- }
17
- };
18
- window.visualViewport?.addEventListener('resize', handleResize);
19
- handleResize();
20
- return () => window.visualViewport?.removeEventListener('resize', handleResize);
21
- }, deps ?? []);
13
+ const handleResize = useCallback(() => {
14
+ if (window.visualViewport) {
15
+ setWidth(window.visualViewport.width);
16
+ setHeight(window.visualViewport.height);
17
+ }
18
+ }, []);
19
+
20
+ useResize(handleResize);
22
21
 
23
22
  return { width, height };
24
23
  };
@@ -7,7 +7,7 @@ import '@dxos-theme';
7
7
  import { type Decorator } from '@storybook/react';
8
8
  import React from 'react';
9
9
 
10
- import { modalSurface, groupSurface, mx, surfaceElevation } from '@dxos/react-ui-theme';
10
+ import { modalSurface, groupSurface, mx, surfaceShadow } from '@dxos/react-ui-theme';
11
11
  import { type Density, type Elevation } from '@dxos/react-ui-types';
12
12
 
13
13
  import { DensityProvider, ElevationProvider } from '../../components';
@@ -20,8 +20,8 @@ type Config = {
20
20
  export const withVariants = ({
21
21
  elevations = [
22
22
  { elevation: 'base' },
23
- { elevation: 'group', surface: groupSurface },
24
- { elevation: 'chrome', surface: modalSurface },
23
+ { elevation: 'positioned', surface: groupSurface },
24
+ { elevation: 'dialog', surface: modalSurface },
25
25
  ],
26
26
  densities = ['coarse', 'fine'],
27
27
  }: Config = {}): Decorator => {
@@ -32,7 +32,7 @@ export const withVariants = ({
32
32
  <div className='flex flex-col gap-8'>
33
33
  {densities.map((density) => (
34
34
  <DensityProvider key={density} density={density}>
35
- <div className={mx('p-4 rounded-lg', surface, surfaceElevation({ elevation }))}>
35
+ <div className={mx('p-4 rounded-lg', surface, surfaceShadow({ elevation }))}>
36
36
  <Story />
37
37
  </div>
38
38
  </DensityProvider>
@@ -4,4 +4,4 @@
4
4
 
5
5
  import { type ClassNameValue } from '@dxos/react-ui-types';
6
6
 
7
- export type ThemedClassName<P> = Omit<P, 'className'> & { classNames?: ClassNameValue };
7
+ export type ThemedClassName<P = {}> = Omit<P, 'className'> & { classNames?: ClassNameValue };
@@ -1,21 +0,0 @@
1
- import '@dxos-theme';
2
- import React, { type PropsWithChildren } from 'react';
3
- type SurfaceLevel = 'base' | 'group' | 'chrome' | 'fixed' | 'input' | 'accent';
4
- declare const _default: {
5
- title: string;
6
- component: ({ children, level }: PropsWithChildren & {
7
- level: SurfaceLevel;
8
- }) => React.JSX.Element;
9
- render: () => React.JSX.Element;
10
- decorators: import("@storybook/react/*").Decorator[];
11
- parameters: {
12
- chromatic: {
13
- disableSnapshot: boolean;
14
- };
15
- };
16
- };
17
- export default _default;
18
- export declare const Default: {
19
- args: {};
20
- };
21
- //# sourceMappingURL=Surfaces.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Surfaces.stories.d.ts","sourceRoot":"","sources":["../../../../src/playground/Surfaces.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAgBtD,KAAK,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;;;qCAEzC,iBAAiB,GAAG;QAAE,KAAK,EAAE,YAAY,CAAA;KAAE;;;;;;;;;AAsCjF,wBAME;AAEF,eAAO,MAAM,OAAO;;CAEnB,CAAC"}
@@ -1,73 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import '@dxos-theme';
6
-
7
- import React, { type PropsWithChildren } from 'react';
8
-
9
- import {
10
- baseSurface,
11
- modalSurface,
12
- groupSurface,
13
- mx,
14
- surfaceElevation,
15
- fixedSurface,
16
- fixedBorder,
17
- attentionSurface,
18
- accentSurface,
19
- } from '@dxos/react-ui-theme';
20
-
21
- import { withTheme } from '../testing';
22
-
23
- type SurfaceLevel = 'base' | 'group' | 'chrome' | 'fixed' | 'input' | 'accent';
24
-
25
- const Surface = ({ children, level }: PropsWithChildren & { level: SurfaceLevel }) => {
26
- const surface =
27
- level === 'chrome'
28
- ? [modalSurface, surfaceElevation({ elevation: 'chrome' })]
29
- : level === 'group'
30
- ? [groupSurface, surfaceElevation({ elevation: 'group' })]
31
- : level === 'input'
32
- ? [attentionSurface, surfaceElevation({ elevation: 'group' })]
33
- : level === 'fixed'
34
- ? [fixedSurface, fixedBorder, 'border', surfaceElevation({ elevation: 'chrome' })]
35
- : level === 'accent'
36
- ? [accentSurface, surfaceElevation({ elevation: 'chrome' })]
37
- : [baseSurface];
38
-
39
- return (
40
- <div
41
- role='region'
42
- className={mx('flex justify-center items-center m-8 p-2 w-[320px] h-[160px] rounded-lg', ...surface)}
43
- >
44
- <div>{level}</div>
45
- {children}
46
- </div>
47
- );
48
- };
49
-
50
- const SurfaceStory = () => {
51
- return (
52
- <div className='bg-cubes p-10 m-0'>
53
- <Surface level='fixed' />
54
- <Surface level='base' />
55
- <Surface level='group' />
56
- <Surface level='chrome' />
57
- <Surface level='input' />
58
- <Surface level='accent' />
59
- </div>
60
- );
61
- };
62
-
63
- export default {
64
- title: 'ui/react-ui-core/Playground/Surfaces',
65
- component: Surface,
66
- render: SurfaceStory,
67
- decorators: [withTheme],
68
- parameters: { chromatic: { disableSnapshot: false } },
69
- };
70
-
71
- export const Default = {
72
- args: {},
73
- };