@dxos/react-ui 0.7.5-main.9d26e3a → 0.7.5-main.e9bb01b

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 (66) hide show
  1. package/dist/lib/browser/index.mjs +272 -200
  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 +646 -577
  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 +272 -200
  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/Dialog.d.ts.map +1 -1
  17. package/dist/types/src/components/Main/Main.d.ts +35 -24
  18. package/dist/types/src/components/Main/Main.d.ts.map +1 -1
  19. package/dist/types/src/components/Main/Main.stories.d.ts +1 -1
  20. package/dist/types/src/components/Menus/ContextMenu.d.ts.map +1 -1
  21. package/dist/types/src/components/Menus/DropdownMenu.d.ts +2 -6
  22. package/dist/types/src/components/Menus/DropdownMenu.d.ts.map +1 -1
  23. package/dist/types/src/components/Popover/Popover.d.ts.map +1 -1
  24. package/dist/types/src/components/Select/Select.d.ts.map +1 -1
  25. package/dist/types/src/components/Separator/Separator.d.ts +3 -1
  26. package/dist/types/src/components/Separator/Separator.d.ts.map +1 -1
  27. package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
  28. package/dist/types/src/components/Tag/Tag.stories.d.ts +12 -5
  29. package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +3 -1
  31. package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
  32. package/dist/types/src/components/Toolbar/Toolbar.d.ts +15 -5
  33. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  34. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +7 -2
  35. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  36. package/dist/types/src/components/Tooltip/Tooltip.d.ts.map +1 -1
  37. package/dist/types/src/hooks/index.d.ts +1 -0
  38. package/dist/types/src/hooks/index.d.ts.map +1 -1
  39. package/dist/types/src/hooks/useSafeArea.d.ts +9 -0
  40. package/dist/types/src/hooks/useSafeArea.d.ts.map +1 -0
  41. package/dist/types/src/hooks/useSafeCollisionPadding.d.ts +10 -0
  42. package/dist/types/src/hooks/useSafeCollisionPadding.d.ts.map +1 -0
  43. package/dist/types/src/hooks/useVisualViewport.d.ts +1 -1
  44. package/dist/types/src/hooks/useVisualViewport.d.ts.map +1 -1
  45. package/dist/types/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +42 -42
  47. package/src/components/Avatars/Avatar.tsx +3 -6
  48. package/src/components/Buttons/IconButton.tsx +25 -7
  49. package/src/components/Dialogs/Dialog.tsx +1 -9
  50. package/src/components/Input/Input.tsx +1 -1
  51. package/src/components/Main/Main.stories.tsx +1 -1
  52. package/src/components/Main/Main.tsx +78 -108
  53. package/src/components/Menus/ContextMenu.tsx +4 -2
  54. package/src/components/Menus/DropdownMenu.tsx +4 -2
  55. package/src/components/Popover/Popover.tsx +4 -0
  56. package/src/components/Select/Select.tsx +4 -1
  57. package/src/components/Separator/Separator.tsx +14 -11
  58. package/src/components/Tag/Tag.stories.tsx +20 -31
  59. package/src/components/Tag/Tag.tsx +15 -6
  60. package/src/components/ThemeProvider/ThemeProvider.tsx +12 -3
  61. package/src/components/Toolbar/Toolbar.tsx +40 -10
  62. package/src/components/Tooltip/Tooltip.tsx +17 -13
  63. package/src/hooks/index.ts +1 -0
  64. package/src/hooks/useSafeArea.ts +25 -0
  65. package/src/hooks/useSafeCollisionPadding.ts +39 -0
  66. package/src/hooks/useVisualViewport.ts +11 -12
@@ -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;
@@ -51,8 +53,15 @@ export const ThemeProvider = ({
51
53
  }
52
54
  }, []);
53
55
 
56
+ const safeAreaPadding = useSafeArea();
57
+
58
+ const contextValue = useMemo(
59
+ () => ({ tx, themeMode, hasIosKeyboard: hasIosKeyboard(), safeAreaPadding, ...rest }),
60
+ [tx, themeMode, safeAreaPadding, rest],
61
+ );
62
+
54
63
  return (
55
- <ThemeContext.Provider value={{ tx, themeMode, hasIosKeyboard: hasIosKeyboard(), ...rest }}>
64
+ <ThemeContext.Provider value={contextValue}>
56
65
  <TranslationsProvider
57
66
  {...{
58
67
  fallback,
@@ -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
  };
@@ -19,6 +19,7 @@ import {
19
19
  import React, { forwardRef, type FunctionComponent } from 'react';
20
20
 
21
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,19 +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
- const elevation = useElevationContext();
58
- return (
59
- <TooltipContentPrimitive
60
- sideOffset={4}
61
- collisionPadding={8}
62
- {...props}
63
- className={tx('tooltip.content', 'tooltip', { elevation }, classNames)}
64
- ref={forwardedRef}
65
- />
66
- );
67
- });
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
+ );
68
72
 
69
73
  export const Tooltip = {
70
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
  };