@dxos/react-ui 0.8.4-main.21d9917 → 0.8.4-main.2244d791bb
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.
- package/dist/lib/browser/{chunk-CEKVHJ27.mjs → chunk-6DTBPJE4.mjs} +4 -4
- package/dist/lib/browser/chunk-6DTBPJE4.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +456 -309
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +5 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-2NHEX4AD.mjs → chunk-JKHQSGNU.mjs} +4 -4
- package/dist/lib/node-esm/chunk-JKHQSGNU.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +456 -309
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +5 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts +7 -0
- package/dist/types/src/components/AnchoredOverflow/AnchoredOverflow.d.ts.map +1 -1
- package/dist/types/src/components/Avatars/Avatar.d.ts.map +1 -1
- package/dist/types/src/components/Breadcrumb/Breadcrumb.d.ts.map +1 -1
- package/dist/types/src/components/Button/Button.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.d.ts +4 -2
- package/dist/types/src/components/Dialog/AlertDialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/AlertDialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.d.ts +7 -1
- package/dist/types/src/components/Dialog/Dialog.d.ts.map +1 -1
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts +1 -5
- package/dist/types/src/components/Dialog/Dialog.stories.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.d.ts.map +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts +1 -1
- package/dist/types/src/components/Input/Input.stories.d.ts.map +1 -1
- package/dist/types/src/components/List/List.d.ts.map +1 -1
- package/dist/types/src/components/List/Treegrid.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.d.ts +8 -9
- package/dist/types/src/components/Main/Main.d.ts.map +1 -1
- package/dist/types/src/components/Main/Main.stories.d.ts +0 -3
- package/dist/types/src/components/Main/Main.stories.d.ts.map +1 -1
- package/dist/types/src/components/Menu/ContextMenu.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.d.ts.map +1 -1
- package/dist/types/src/components/Message/Message.stories.d.ts +2 -3
- package/dist/types/src/components/Message/Message.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts +23 -26
- package/dist/types/src/components/ScrollArea/ScrollArea.d.ts.map +1 -1
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts +43 -9
- package/dist/types/src/components/ScrollArea/ScrollArea.stories.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.d.ts.map +1 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts +6 -1
- package/dist/types/src/components/ScrollContainer/ScrollContainer.stories.d.ts.map +1 -1
- package/dist/types/src/components/Select/Select.d.ts.map +1 -1
- package/dist/types/src/components/Skeleton/Skeleton.d.ts +12 -0
- package/dist/types/src/components/Skeleton/Skeleton.d.ts.map +1 -0
- package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts +17 -0
- package/dist/types/src/components/Skeleton/Skeleton.stories.d.ts.map +1 -0
- package/dist/types/src/components/Skeleton/index.d.ts +2 -0
- package/dist/types/src/components/Skeleton/index.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/Splitter.d.ts +26 -0
- package/dist/types/src/components/Splitter/Splitter.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/Splitter.stories.d.ts +7 -0
- package/dist/types/src/components/Splitter/Splitter.stories.d.ts.map +1 -0
- package/dist/types/src/components/Splitter/index.d.ts +2 -0
- package/dist/types/src/components/Splitter/index.d.ts.map +1 -0
- package/dist/types/src/components/Tag/Tag.d.ts.map +1 -1
- package/dist/types/src/components/Tag/Tag.stories.d.ts +1 -5
- package/dist/types/src/components/Tag/Tag.stories.d.ts.map +1 -1
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts +1 -0
- package/dist/types/src/components/ThemeProvider/ThemeProvider.d.ts.map +1 -1
- package/dist/types/src/components/Toast/Toast.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +7 -6
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/generics.stories.d.ts +17 -0
- package/dist/types/src/exemplars/generics.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/slot.stories.d.ts +14 -0
- package/dist/types/src/exemplars/slot.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/tabster.stories.d.ts +8 -0
- package/dist/types/src/exemplars/tabster.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/virtualizer.stories.d.ts +11 -0
- package/dist/types/src/exemplars/virtualizer.stories.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playground/Controls.stories.d.ts.map +1 -1
- package/dist/types/src/primitives/Container/Container.d.ts +23 -0
- package/dist/types/src/primitives/Container/Container.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Container.stories.d.ts +11 -0
- package/dist/types/src/primitives/Container/Container.stories.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Layout.d.ts +18 -0
- package/dist/types/src/primitives/Container/Layout.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/Layout.stories.d.ts +10 -0
- package/dist/types/src/primitives/Container/Layout.stories.d.ts.map +1 -0
- package/dist/types/src/primitives/Container/index.d.ts +3 -0
- package/dist/types/src/primitives/Container/index.d.ts.map +1 -0
- package/dist/types/src/primitives/Flex/Flex.d.ts +8 -0
- package/dist/types/src/primitives/Flex/Flex.d.ts.map +1 -0
- package/dist/types/src/primitives/Flex/index.d.ts +2 -0
- package/dist/types/src/primitives/Flex/index.d.ts.map +1 -0
- package/dist/types/src/primitives/index.d.ts +3 -0
- package/dist/types/src/primitives/index.d.ts.map +1 -0
- package/dist/types/src/testing/decorators/withTheme.d.ts +3 -2
- package/dist/types/src/testing/decorators/withTheme.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +23 -22
- package/src/components/AnchoredOverflow/AnchoredOverflow.tsx +10 -12
- package/src/components/Avatars/Avatar.stories.tsx +2 -2
- package/src/components/Avatars/Avatar.tsx +2 -9
- package/src/components/Avatars/AvatarGroup.stories.tsx +2 -2
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +2 -2
- package/src/components/Breadcrumb/Breadcrumb.tsx +5 -31
- package/src/components/Button/Button.stories.tsx +3 -3
- package/src/components/Button/Button.tsx +1 -7
- package/src/components/Button/IconButton.stories.tsx +2 -2
- package/src/components/Button/IconButton.tsx +1 -1
- package/src/components/Button/Toggle.stories.tsx +2 -2
- package/src/components/Button/ToggleGroup.stories.tsx +2 -2
- package/src/components/Dialog/AlertDialog.stories.tsx +6 -7
- package/src/components/Dialog/AlertDialog.tsx +78 -9
- package/src/components/Dialog/Dialog.stories.tsx +11 -11
- package/src/components/Dialog/Dialog.tsx +44 -19
- package/src/components/Icon/Icon.stories.tsx +2 -2
- package/src/components/Icon/Icon.tsx +1 -1
- package/src/components/Input/Input.stories.tsx +11 -10
- package/src/components/Input/Input.tsx +10 -25
- package/src/components/Link/Link.stories.tsx +2 -2
- package/src/components/Link/Link.tsx +1 -1
- package/src/components/List/List.stories.tsx +2 -2
- package/src/components/List/List.tsx +7 -13
- package/src/components/List/Tree.stories.tsx +2 -2
- package/src/components/List/Treegrid.stories.tsx +2 -2
- package/src/components/List/Treegrid.tsx +4 -9
- package/src/components/Main/Main.stories.tsx +41 -23
- package/src/components/Main/Main.tsx +128 -71
- package/src/components/Menu/ContextMenu.stories.tsx +2 -2
- package/src/components/Menu/ContextMenu.tsx +7 -31
- package/src/components/Menu/DropdownMenu.stories.tsx +2 -2
- package/src/components/Menu/DropdownMenu.tsx +8 -8
- package/src/components/Message/Message.stories.tsx +23 -8
- package/src/components/Message/Message.tsx +8 -21
- package/src/components/Popover/Popover.stories.tsx +2 -2
- package/src/components/Popover/Popover.tsx +3 -3
- package/src/components/ScrollArea/ScrollArea.stories.tsx +152 -76
- package/src/components/ScrollArea/ScrollArea.tsx +68 -116
- package/src/components/ScrollArea/index.ts +1 -1
- package/src/components/ScrollContainer/ScrollContainer.stories.tsx +24 -9
- package/src/components/ScrollContainer/ScrollContainer.tsx +14 -9
- package/src/components/Select/Select.stories.tsx +2 -2
- package/src/components/Select/Select.tsx +9 -25
- package/src/components/Separator/Separator.tsx +1 -1
- package/src/components/Skeleton/Skeleton.stories.tsx +52 -0
- package/src/components/Skeleton/Skeleton.tsx +26 -0
- package/src/components/Skeleton/index.ts +5 -0
- package/src/components/Splitter/Splitter.stories.tsx +73 -0
- package/src/components/Splitter/Splitter.tsx +123 -0
- package/src/components/Splitter/index.ts +5 -0
- package/src/components/Status/Status.stories.tsx +2 -2
- package/src/components/Status/Status.tsx +2 -2
- package/src/components/Tag/Tag.stories.tsx +3 -7
- package/src/components/Tag/Tag.tsx +1 -6
- package/src/components/ThemeProvider/ThemeProvider.tsx +2 -1
- package/src/components/Toast/Toast.stories.tsx +2 -2
- package/src/components/Toast/Toast.tsx +6 -10
- package/src/components/Toolbar/Toolbar.stories.tsx +2 -2
- package/src/components/Toolbar/Toolbar.tsx +13 -9
- package/src/components/Tooltip/Tooltip.stories.tsx +2 -2
- package/src/components/Tooltip/Tooltip.tsx +2 -2
- package/src/components/index.ts +2 -0
- package/src/exemplars/generics.stories.tsx +44 -0
- package/src/exemplars/slot.stories.tsx +108 -0
- package/src/exemplars/tabster.stories.tsx +127 -0
- package/src/exemplars/virtualizer.stories.tsx +133 -0
- package/src/index.ts +1 -0
- package/src/playground/Controls.stories.tsx +3 -4
- package/src/playground/Custom.stories.tsx +2 -2
- package/src/playground/Typography.stories.tsx +2 -2
- package/src/primitives/Container/Container.stories.tsx +67 -0
- package/src/primitives/Container/Container.tsx +79 -0
- package/src/primitives/Container/Layout.stories.tsx +57 -0
- package/src/primitives/Container/Layout.tsx +61 -0
- package/src/primitives/Container/index.ts +6 -0
- package/src/primitives/Flex/Flex.tsx +26 -0
- package/src/primitives/Flex/index.ts +5 -0
- package/src/primitives/index.ts +6 -0
- package/src/testing/decorators/withLayoutVariants.tsx +1 -1
- package/src/testing/decorators/withTheme.tsx +19 -17
- package/dist/lib/browser/chunk-CEKVHJ27.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-2NHEX4AD.mjs.map +0 -7
|
@@ -6,6 +6,8 @@ import type { ToggleGroupItemProps as ToggleGroupItemPrimitiveProps } from '@rad
|
|
|
6
6
|
import * as ToolbarPrimitive from '@radix-ui/react-toolbar';
|
|
7
7
|
import React, { Fragment, forwardRef } from 'react';
|
|
8
8
|
|
|
9
|
+
import { type ToolbarStyleProps } from '@dxos/ui-theme';
|
|
10
|
+
|
|
9
11
|
import { useThemeContext } from '../../hooks';
|
|
10
12
|
import { type ThemedClassName } from '../../util';
|
|
11
13
|
import {
|
|
@@ -23,22 +25,24 @@ import { Link, type LinkProps } from '../Link';
|
|
|
23
25
|
import { Separator, type SeparatorProps } from '../Separator';
|
|
24
26
|
|
|
25
27
|
type ToolbarRootProps = ThemedClassName<
|
|
26
|
-
ToolbarPrimitive.ToolbarProps &
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
28
|
+
ToolbarPrimitive.ToolbarProps &
|
|
29
|
+
ToolbarStyleProps & {
|
|
30
|
+
textBlockWidth?: boolean;
|
|
31
|
+
}
|
|
31
32
|
>;
|
|
32
33
|
|
|
33
|
-
// TODO(burdon): Implement asChild.
|
|
34
|
+
// TODO(burdon): Implement asChild property.
|
|
34
35
|
const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
|
|
35
|
-
(
|
|
36
|
+
(
|
|
37
|
+
{ classNames, children, density, disabled, layoutManaged, textBlockWidth: textBlockWidthProp, ...props },
|
|
38
|
+
forwardedRef,
|
|
39
|
+
) => {
|
|
36
40
|
const { tx } = useThemeContext();
|
|
37
41
|
const InnerRoot = textBlockWidthProp ? 'div' : Fragment;
|
|
38
42
|
const innerRootProps = textBlockWidthProp
|
|
39
43
|
? {
|
|
40
44
|
role: 'none',
|
|
41
|
-
className: tx('toolbar.inner',
|
|
45
|
+
className: tx('toolbar.inner', { layoutManaged }, classNames),
|
|
42
46
|
}
|
|
43
47
|
: {};
|
|
44
48
|
|
|
@@ -46,7 +50,7 @@ const ToolbarRoot = forwardRef<HTMLDivElement, ToolbarRootProps>(
|
|
|
46
50
|
<ToolbarPrimitive.Root
|
|
47
51
|
{...props}
|
|
48
52
|
data-arrow-keys={props.orientation === 'vertical' ? 'up down' : 'left right'}
|
|
49
|
-
className={tx('toolbar.root',
|
|
53
|
+
className={tx('toolbar.root', { density, disabled, layoutManaged }, classNames)}
|
|
50
54
|
ref={forwardedRef}
|
|
51
55
|
>
|
|
52
56
|
<InnerRoot {...innerRootProps}>{children}</InnerRoot>
|
|
@@ -30,10 +30,10 @@ const DefaultStory = ({ tooltips, defaultOpen }: StoryProps) => (
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
const meta = {
|
|
33
|
-
title: 'ui/react-ui-core/Tooltip',
|
|
33
|
+
title: 'ui/react-ui-core/components/Tooltip',
|
|
34
34
|
component: Tooltip as any,
|
|
35
35
|
render: DefaultStory,
|
|
36
|
-
decorators: [withTheme],
|
|
36
|
+
decorators: [withTheme()],
|
|
37
37
|
} satisfies Meta<typeof DefaultStory>;
|
|
38
38
|
|
|
39
39
|
export default meta;
|
|
@@ -215,9 +215,9 @@ const TooltipProvider: FC<TooltipProviderProps> = (props: TooltipScopedProps<Too
|
|
|
215
215
|
isPointerInTransitRef.current = inTransit;
|
|
216
216
|
}, [])}
|
|
217
217
|
>
|
|
218
|
-
<TooltipContent side={side} className={tx('tooltip.content',
|
|
218
|
+
<TooltipContent side={side} className={tx('tooltip.content', { elevation })}>
|
|
219
219
|
{content}
|
|
220
|
-
<TooltipArrow className={tx('tooltip.arrow'
|
|
220
|
+
<TooltipArrow className={tx('tooltip.arrow')} />
|
|
221
221
|
</TooltipContent>
|
|
222
222
|
<TooltipVirtualTrigger virtualRef={triggerRef as RefObject<HTMLButtonElement>} />
|
|
223
223
|
{children}
|
package/src/components/index.ts
CHANGED
|
@@ -21,6 +21,8 @@ export * from './ScrollArea';
|
|
|
21
21
|
export * from './ScrollContainer';
|
|
22
22
|
export * from './Select';
|
|
23
23
|
export * from './Separator';
|
|
24
|
+
export * from './Skeleton';
|
|
25
|
+
export * from './Splitter';
|
|
24
26
|
export * from './Tag';
|
|
25
27
|
export * from './Toast';
|
|
26
28
|
export * from './Toolbar';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React, { type ReactElement, type Ref, forwardRef } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type SlottableProps } from '@dxos/ui-types';
|
|
9
|
+
|
|
10
|
+
import { withTheme } from '../testing';
|
|
11
|
+
|
|
12
|
+
const ComponentInner = forwardRef<HTMLDivElement, ComponentProps>(({ children, ...props }, forwardedRef) => {
|
|
13
|
+
return (
|
|
14
|
+
<div {...props} ref={forwardedRef}>
|
|
15
|
+
{children}
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
ComponentInner.displayName = 'Component';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Generic component pattern.
|
|
24
|
+
*/
|
|
25
|
+
type ComponentProps<P extends HTMLElement = any> = SlottableProps<P>;
|
|
26
|
+
|
|
27
|
+
const Component = ComponentInner as <P extends HTMLElement>(
|
|
28
|
+
props: SlottableProps<P> & { ref?: Ref<P> },
|
|
29
|
+
) => ReactElement;
|
|
30
|
+
|
|
31
|
+
const meta = {
|
|
32
|
+
title: 'ui/react-ui-core/exemplars/generics',
|
|
33
|
+
component: Component,
|
|
34
|
+
decorators: [withTheme()],
|
|
35
|
+
parameters: {
|
|
36
|
+
layout: 'centered',
|
|
37
|
+
},
|
|
38
|
+
} satisfies Meta;
|
|
39
|
+
|
|
40
|
+
export default meta;
|
|
41
|
+
|
|
42
|
+
type Story = StoryObj<typeof meta>;
|
|
43
|
+
|
|
44
|
+
export const Single: Story = {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
6
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
7
|
+
import React, { type PropsWithChildren, forwardRef } from 'react';
|
|
8
|
+
|
|
9
|
+
import { mx } from '@dxos/ui-theme';
|
|
10
|
+
import { type SlottableClassName, type SlottableProps, type ThemedClassName } from '@dxos/ui-types';
|
|
11
|
+
|
|
12
|
+
import { withTheme } from '../testing';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Composition
|
|
16
|
+
*
|
|
17
|
+
* All Radix primitive parts that render a DOM element accept an asChild prop.
|
|
18
|
+
* When asChild is set to true, Radix will not render a default DOM element, instead cloning the part's child and passing it the props and behavior required to make it functional.
|
|
19
|
+
* https://www.radix-ui.com/primitives/docs/guides/composition
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// Outer primitive (like Tooltip.Trigger or Focus.Group).
|
|
23
|
+
const Outer = forwardRef<HTMLDivElement, SlottableProps<HTMLDivElement>>(
|
|
24
|
+
({ children, className, classNames, asChild, ...props }, forwardedRef) => {
|
|
25
|
+
const Root = asChild ? Slot : 'div';
|
|
26
|
+
return (
|
|
27
|
+
<Root {...props} className={mx(className, classNames)} data-outer='true' ref={forwardedRef}>
|
|
28
|
+
{children}
|
|
29
|
+
</Root>
|
|
30
|
+
);
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Middle primitive (like Dialog.Trigger or Mosaic.Cell).
|
|
35
|
+
const Middle = forwardRef<HTMLDivElement, SlottableProps<HTMLDivElement>>(
|
|
36
|
+
({ children, className, classNames, asChild, ...props }, forwardedRef) => {
|
|
37
|
+
const Root = asChild ? Slot : 'div';
|
|
38
|
+
return (
|
|
39
|
+
<Root {...props} className={mx(className, classNames)} data-middle='true' ref={forwardedRef}>
|
|
40
|
+
{children}
|
|
41
|
+
</Root>
|
|
42
|
+
);
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Leaf component (like Card.Root).
|
|
47
|
+
const Leaf = forwardRef<HTMLButtonElement, SlottableClassName<PropsWithChildren>>(
|
|
48
|
+
({ className, classNames, children, ...props }, forwardedRef) => {
|
|
49
|
+
return (
|
|
50
|
+
<button {...props} className={mx('p-2 outline-none border rounded', className, classNames)} ref={forwardedRef}>
|
|
51
|
+
{children}
|
|
52
|
+
</button>
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Test 1: Single asChild.
|
|
58
|
+
const TestSingle = ({ classNames, ...props }: ThemedClassName<{ role?: string }>) => (
|
|
59
|
+
<Outer asChild {...props} className={mx('p-2', classNames)}>
|
|
60
|
+
<Leaf>Single asChild</Leaf>
|
|
61
|
+
</Outer>
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
// Test 2: Nested asChild.
|
|
65
|
+
const TestNested = ({ classNames, ...props }: ThemedClassName<{ role?: string }>) => {
|
|
66
|
+
return (
|
|
67
|
+
<Outer asChild {...props} className={mx('p-2', classNames)}>
|
|
68
|
+
<Middle asChild>
|
|
69
|
+
<Leaf>Nested asChild</Leaf>
|
|
70
|
+
</Middle>
|
|
71
|
+
</Outer>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Test 3: Complex.
|
|
76
|
+
const TestInner = ({ classNames, ...props }: ThemedClassName<{ role?: string }>) => (
|
|
77
|
+
<Outer asChild {...props} className={mx('p-2', classNames)}>
|
|
78
|
+
<Middle asChild>
|
|
79
|
+
<Leaf>
|
|
80
|
+
<div role='none'>Leaf</div>
|
|
81
|
+
</Leaf>
|
|
82
|
+
</Middle>
|
|
83
|
+
</Outer>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const meta = {
|
|
87
|
+
title: 'ui/react-ui-core/exemplars/slot',
|
|
88
|
+
decorators: [withTheme()],
|
|
89
|
+
parameters: {
|
|
90
|
+
layout: 'centered',
|
|
91
|
+
},
|
|
92
|
+
} satisfies Meta;
|
|
93
|
+
|
|
94
|
+
export default meta;
|
|
95
|
+
|
|
96
|
+
type Story = StoryObj<typeof meta>;
|
|
97
|
+
|
|
98
|
+
export const Single: Story = {
|
|
99
|
+
render: () => <TestSingle role='listitem' classNames='border-red-500' />,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const Nested: Story = {
|
|
103
|
+
render: () => <TestNested role='listitem' classNames='border-green-500' />,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const Inner: Story = {
|
|
107
|
+
render: () => <TestInner role='listitem' classNames='border-blue-500' />,
|
|
108
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
useArrowNavigationGroup,
|
|
7
|
+
useFocusFinders,
|
|
8
|
+
useFocusableGroup,
|
|
9
|
+
useMergedTabsterAttributes_unstable,
|
|
10
|
+
} from '@fluentui/react-tabster';
|
|
11
|
+
import { type Decorator, type Meta, type StoryObj } from '@storybook/react-vite';
|
|
12
|
+
import React, { forwardRef, useEffect, useMemo, useRef } from 'react';
|
|
13
|
+
import { createTabster, disposeTabster } from 'tabster';
|
|
14
|
+
|
|
15
|
+
import { Input, ScrollArea } from '@dxos/react-ui';
|
|
16
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
17
|
+
import { mx } from '@dxos/ui-theme';
|
|
18
|
+
|
|
19
|
+
// TODO(burdon): Factor out styles (incl. tabster debugging).
|
|
20
|
+
// TODO(burdon): Implement horizontal movement between columns when column is selected.
|
|
21
|
+
// TODO(burdon): Prevent tab out of app.
|
|
22
|
+
|
|
23
|
+
const border =
|
|
24
|
+
'rounded-sm outline-none border border-subduedSeparator focus:border-primary-500 focus-within:border-rose-500';
|
|
25
|
+
|
|
26
|
+
const Board = forwardRef<HTMLDivElement, { columns: string[][] }>(({ columns }, ref) => {
|
|
27
|
+
const arrowNavigationAttrs = useArrowNavigationGroup({ axis: 'horizontal', memorizeCurrent: true, tabbable: true });
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div ref={ref} tabIndex={0} {...arrowNavigationAttrs} className='flex bs-full is-full overflow-hidden'>
|
|
31
|
+
<div className={mx('flex bs-full overflow-x-auto p-4 gap-4')}>
|
|
32
|
+
{columns.map((column) => (
|
|
33
|
+
<Column key={column[0]} items={column} />
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const Column = forwardRef<HTMLDivElement, { items: string[] }>(({ items }, ref) => {
|
|
41
|
+
const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited' });
|
|
42
|
+
const arrowNavigationAttrs = useArrowNavigationGroup({ axis: 'vertical', memorizeCurrent: true });
|
|
43
|
+
const tabsterAttrs = useMergedTabsterAttributes_unstable(focusableGroupAttrs, arrowNavigationAttrs);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<ScrollArea.Root tabIndex={0} orientation='vertical' classNames={mx('is-[25rem]', border)}>
|
|
47
|
+
<ScrollArea.Viewport {...tabsterAttrs} classNames='p-4 gap-4' ref={ref}>
|
|
48
|
+
{items.map((item) => (
|
|
49
|
+
<Item key={item} value={item} />
|
|
50
|
+
))}
|
|
51
|
+
</ScrollArea.Viewport>
|
|
52
|
+
</ScrollArea.Root>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const Item = forwardRef<HTMLDivElement, { value: string }>(({ value }, ref) => {
|
|
57
|
+
const focusableGroupAttrs = useFocusableGroup();
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div
|
|
61
|
+
ref={ref}
|
|
62
|
+
tabIndex={0}
|
|
63
|
+
{...focusableGroupAttrs}
|
|
64
|
+
className={mx('flex shrink-0 is-full gap-4 p-4 items-center', border)}
|
|
65
|
+
>
|
|
66
|
+
<Input.Root>
|
|
67
|
+
<Input.Checkbox />
|
|
68
|
+
</Input.Root>
|
|
69
|
+
<Input.Root>
|
|
70
|
+
<Input.TextInput defaultValue={value} />
|
|
71
|
+
</Input.Root>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const DefaultStory = () => {
|
|
77
|
+
const columns = useMemo(() => {
|
|
78
|
+
return [['A1', 'A2', 'A3'], ['B1'], ['C1', 'C2', 'C3', 'C4', 'C5', 'C6'], ['D1', 'D2']];
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
82
|
+
const { findFirstFocusable } = useFocusFinders();
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (ref.current) {
|
|
85
|
+
findFirstFocusable(ref.current)?.focus();
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
88
|
+
|
|
89
|
+
return <Board columns={columns} ref={ref} />;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// TODO(burdon): This doesn't seem to be necessary or recongized; memoization doesn't work.
|
|
93
|
+
const withTabster: Decorator = (Story) => {
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const tabster = createTabster(window, {
|
|
96
|
+
autoRoot: {},
|
|
97
|
+
// TODO(burdon): Not called.
|
|
98
|
+
// checkUncontrolledCompletely: (el) => {
|
|
99
|
+
// console.log(el);
|
|
100
|
+
// return true;
|
|
101
|
+
// },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
disposeTabster(tabster);
|
|
106
|
+
};
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
109
|
+
return <Story />;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const meta: Meta<typeof DefaultStory> = {
|
|
113
|
+
title: 'ui/react-ui-core/exemplars/tabster',
|
|
114
|
+
component: DefaultStory,
|
|
115
|
+
decorators: [withTheme(), withLayout({ layout: 'fullscreen' }), withTabster],
|
|
116
|
+
parameters: {
|
|
117
|
+
layout: 'fullscreen',
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default meta;
|
|
122
|
+
|
|
123
|
+
type Story = StoryObj<typeof meta>;
|
|
124
|
+
|
|
125
|
+
export const Default: Story = {
|
|
126
|
+
args: {},
|
|
127
|
+
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta } from '@storybook/react-vite';
|
|
6
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
7
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { faker } from '@dxos/random';
|
|
10
|
+
import { Layout, ScrollArea, Toolbar } from '@dxos/react-ui';
|
|
11
|
+
import { withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
12
|
+
|
|
13
|
+
faker.seed(999);
|
|
14
|
+
|
|
15
|
+
type TestItem = {
|
|
16
|
+
name: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const meta: Meta = {
|
|
20
|
+
title: 'ui/react-ui-core/exemplars/virtualizer',
|
|
21
|
+
decorators: [withLayout({ layout: 'column' }), withTheme()],
|
|
22
|
+
parameters: {
|
|
23
|
+
layout: 'fullscreen',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
|
|
29
|
+
const NUM_ITEMS = 500;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* https://tanstack.com/virtual/latest/docs/introduction
|
|
33
|
+
*/
|
|
34
|
+
export const Default = {
|
|
35
|
+
render: () => {
|
|
36
|
+
const [index, setIndex] = useState(0);
|
|
37
|
+
const items = useMemo<TestItem[]>(
|
|
38
|
+
() =>
|
|
39
|
+
Array.from({ length: NUM_ITEMS }, () => ({
|
|
40
|
+
name: faker.lorem.paragraph(),
|
|
41
|
+
})),
|
|
42
|
+
[],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const parentRef = useRef(null);
|
|
46
|
+
const [viewport, setViewport] = useState<HTMLElement | null>(null);
|
|
47
|
+
const virtualizer = useVirtualizer({
|
|
48
|
+
getScrollElement: () => viewport,
|
|
49
|
+
estimateSize: () => 40,
|
|
50
|
+
count: items.length,
|
|
51
|
+
gap: 8,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
virtualizer.scrollToIndex(index, { align: 'start' });
|
|
56
|
+
}, [virtualizer, index]);
|
|
57
|
+
|
|
58
|
+
const virtualItems = virtualizer.getVirtualItems();
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<Layout.Main toolbar>
|
|
62
|
+
<ScrollToolbar items={items} index={index} setIndex={setIndex} />
|
|
63
|
+
<ScrollArea.Root orientation='vertical' margin>
|
|
64
|
+
<ScrollArea.Viewport classNames='p-2' ref={setViewport}>
|
|
65
|
+
<div
|
|
66
|
+
role='none'
|
|
67
|
+
style={{
|
|
68
|
+
position: 'relative',
|
|
69
|
+
height: virtualizer.getTotalSize(),
|
|
70
|
+
width: '100%',
|
|
71
|
+
}}
|
|
72
|
+
ref={parentRef}
|
|
73
|
+
>
|
|
74
|
+
{virtualItems.map((virtualItem) => (
|
|
75
|
+
<div
|
|
76
|
+
key={virtualItem.key}
|
|
77
|
+
role='list'
|
|
78
|
+
className='grid grid-cols-[3rem_1fr] overflow-hidden border border-separator rounded-sm'
|
|
79
|
+
style={{
|
|
80
|
+
position: 'absolute',
|
|
81
|
+
top: 0,
|
|
82
|
+
left: 0,
|
|
83
|
+
width: '100%',
|
|
84
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
85
|
+
}}
|
|
86
|
+
data-index={virtualItem.index}
|
|
87
|
+
ref={virtualizer.measureElement}
|
|
88
|
+
>
|
|
89
|
+
<div className='p-1'>{virtualItem.index + 1}</div>
|
|
90
|
+
<div className='p-1'>{items[virtualItem.index].name}</div>
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
</ScrollArea.Viewport>
|
|
95
|
+
</ScrollArea.Root>
|
|
96
|
+
</Layout.Main>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const ScrollToolbar = ({
|
|
102
|
+
items,
|
|
103
|
+
index,
|
|
104
|
+
setIndex,
|
|
105
|
+
}: {
|
|
106
|
+
items: any[];
|
|
107
|
+
index: number;
|
|
108
|
+
setIndex: (index: number) => void;
|
|
109
|
+
}) => {
|
|
110
|
+
return (
|
|
111
|
+
<Toolbar.Root classNames='grid grid-cols-3'>
|
|
112
|
+
<div />
|
|
113
|
+
<div className='flex justify-center gap-1'>
|
|
114
|
+
<Toolbar.IconButton icon='ph--arrow-line-left--regular' iconOnly label='start' onClick={() => setIndex(0)} />
|
|
115
|
+
<Toolbar.IconButton
|
|
116
|
+
icon='ph--arrows-out-line-horizontal--regular'
|
|
117
|
+
iconOnly
|
|
118
|
+
label='random'
|
|
119
|
+
onClick={() => setIndex(Math.floor(Math.random() * items.length))}
|
|
120
|
+
/>
|
|
121
|
+
<Toolbar.IconButton
|
|
122
|
+
icon='ph--arrow-line-right--regular'
|
|
123
|
+
iconOnly
|
|
124
|
+
label='end'
|
|
125
|
+
onClick={() => setIndex(items.length - 1)}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
<div className='p-1 text-right'>
|
|
129
|
+
{index + 1}/{items.length}
|
|
130
|
+
</div>
|
|
131
|
+
</Toolbar.Root>
|
|
132
|
+
);
|
|
133
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -6,8 +6,7 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
|
6
6
|
import React, { useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { Icon, Input, Select, Toggle, Toolbar } from '../components';
|
|
9
|
-
import { withTheme } from '../testing';
|
|
10
|
-
import { withLayoutVariants } from '../testing';
|
|
9
|
+
import { withLayoutVariants, withTheme } from '../testing';
|
|
11
10
|
|
|
12
11
|
const DefaultStory = () => {
|
|
13
12
|
const [checked, setChecked] = useState<boolean>(false);
|
|
@@ -82,9 +81,9 @@ const DefaultStory = () => {
|
|
|
82
81
|
};
|
|
83
82
|
|
|
84
83
|
const meta = {
|
|
85
|
-
title: 'ui/react-ui-core/
|
|
84
|
+
title: 'ui/react-ui-core/playground/Controls',
|
|
86
85
|
render: DefaultStory,
|
|
87
|
-
decorators: [withTheme, withLayoutVariants()],
|
|
86
|
+
decorators: [withTheme(), withLayoutVariants()],
|
|
88
87
|
} satisfies Meta<typeof Icon>;
|
|
89
88
|
|
|
90
89
|
export default meta;
|
|
@@ -108,10 +108,10 @@ const DefaultStory = ({ children, ...args }: Omit<ButtonProps, 'ref'>) => {
|
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
const meta = {
|
|
111
|
-
title: 'ui/react-ui-core/
|
|
111
|
+
title: 'ui/react-ui-core/playground/Custom',
|
|
112
112
|
component: Button,
|
|
113
113
|
render: DefaultStory,
|
|
114
|
-
decorators: [withTheme],
|
|
114
|
+
decorators: [withTheme()],
|
|
115
115
|
parameters: {
|
|
116
116
|
layout: 'centered',
|
|
117
117
|
},
|
|
@@ -43,9 +43,9 @@ const DefaultStory = () => {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
const meta = {
|
|
46
|
-
title: 'ui/react-ui-core/
|
|
46
|
+
title: 'ui/react-ui-core/playground/Typography',
|
|
47
47
|
render: DefaultStory,
|
|
48
|
-
decorators: [withTheme],
|
|
48
|
+
decorators: [withTheme()],
|
|
49
49
|
} satisfies Meta<typeof DefaultStory>;
|
|
50
50
|
|
|
51
51
|
export default meta;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Input } from '../../components';
|
|
9
|
+
import { withTheme } from '../../testing';
|
|
10
|
+
|
|
11
|
+
import { Container, type ContainerRootProps } from './Container';
|
|
12
|
+
|
|
13
|
+
const DefaultStory = (props: ContainerRootProps) => {
|
|
14
|
+
return (
|
|
15
|
+
<div className='plb-2 is-[20rem] border border-separator rounded-sm'>
|
|
16
|
+
<Container.Root {...props}>
|
|
17
|
+
<Container.Column>
|
|
18
|
+
<Input.Root>
|
|
19
|
+
<Input.Label>Label</Input.Label>
|
|
20
|
+
<Input.TextInput />
|
|
21
|
+
</Input.Root>
|
|
22
|
+
</Container.Column>
|
|
23
|
+
</Container.Root>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const meta: Meta<typeof Container.Root> = {
|
|
29
|
+
title: 'ui/react-ui-core/primitives/Container',
|
|
30
|
+
component: Container.Root,
|
|
31
|
+
render: DefaultStory,
|
|
32
|
+
decorators: [withTheme()],
|
|
33
|
+
parameters: {
|
|
34
|
+
layout: 'centered',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default meta;
|
|
39
|
+
|
|
40
|
+
type Story = StoryObj<typeof meta>;
|
|
41
|
+
|
|
42
|
+
// TODO(burdon): Requires container.
|
|
43
|
+
export const Default = () => {
|
|
44
|
+
return (
|
|
45
|
+
<Container.Root>
|
|
46
|
+
<Container.Column>Column</Container.Column>
|
|
47
|
+
</Container.Root>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const SM: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
variant: 'sm',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const MD: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
variant: 'md',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const LG: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
variant: 'lg',
|
|
66
|
+
},
|
|
67
|
+
};
|