@dxos/react-ui-stack 0.8.2-staging.7ac8446 → 0.8.3-main.672df60
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/index.mjs +960 -419
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +949 -408
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/lib/node-esm/index.mjs +960 -419
- 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.map +3 -3
- package/dist/types/src/components/{Stack.d.ts → Stack/Stack.d.ts} +4 -1
- package/dist/types/src/components/Stack/Stack.d.ts.map +1 -0
- package/dist/types/src/components/Stack/Stack.stories.d.ts +9 -0
- package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -0
- package/dist/types/src/components/Stack/index.d.ts +2 -0
- package/dist/types/src/components/Stack/index.d.ts.map +1 -0
- package/dist/types/src/components/StackContext.d.ts +14 -10
- package/dist/types/src/components/StackContext.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/MenuSignifier.d.ts.map +1 -0
- package/dist/types/src/components/{StackItem.d.ts → StackItem/StackItem.d.ts} +15 -6
- package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -0
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts +8 -0
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -0
- package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -0
- package/dist/types/src/components/{StackItemDragHandle.d.ts → StackItem/StackItemDragHandle.d.ts} +1 -1
- package/dist/types/src/components/StackItem/StackItemDragHandle.d.ts.map +1 -0
- package/dist/types/src/components/{StackItemHeading.d.ts → StackItem/StackItemHeading.d.ts} +4 -2
- package/dist/types/src/components/StackItem/StackItemHeading.d.ts.map +1 -0
- package/dist/types/src/components/StackItem/StackItemResizeHandle.d.ts.map +1 -0
- package/dist/types/src/components/StackItem/StackItemSigil.d.ts.map +1 -0
- package/dist/types/src/components/StackItem/index.d.ts +2 -0
- package/dist/types/src/components/StackItem/index.d.ts.map +1 -0
- package/dist/types/src/components/defs.d.ts +18 -0
- package/dist/types/src/components/defs.d.ts.map +1 -0
- package/dist/types/src/components/deprecated/LayoutControls.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -2
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +58 -0
- package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -0
- package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts +1 -0
- package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts.map +1 -0
- package/dist/types/src/exemplars/Card/CardDragPreview.d.ts +6 -0
- package/dist/types/src/exemplars/Card/CardDragPreview.d.ts.map +1 -0
- package/dist/types/src/exemplars/Card/fragments.d.ts +6 -0
- package/dist/types/src/exemplars/Card/fragments.d.ts.map +1 -0
- package/dist/types/src/exemplars/Card/index.d.ts +3 -0
- package/dist/types/src/exemplars/Card/index.d.ts.map +1 -0
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts +34 -0
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +1 -0
- package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts +1 -0
- package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts.map +1 -0
- package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts +9 -0
- package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts.map +1 -0
- package/dist/types/src/exemplars/CardStack/index.d.ts +3 -0
- package/dist/types/src/exemplars/CardStack/index.d.ts.map +1 -0
- package/dist/types/src/exemplars/index.d.ts +3 -0
- package/dist/types/src/exemplars/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useStackDropForElements.d.ts +4 -4
- package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/stack-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +22 -20
- package/src/components/{Stack.stories.tsx → Stack/Stack.stories.tsx} +29 -17
- package/src/components/{Stack.tsx → Stack/Stack.tsx} +55 -5
- package/src/components/Stack/index.ts +5 -0
- package/src/components/StackContext.tsx +21 -13
- package/src/components/StackItem/StackItem.stories.tsx +49 -0
- package/src/components/{StackItem.tsx → StackItem/StackItem.tsx} +90 -11
- package/src/components/{StackItemContent.tsx → StackItem/StackItemContent.tsx} +2 -1
- package/src/components/{StackItemDragHandle.tsx → StackItem/StackItemDragHandle.tsx} +2 -2
- package/src/components/{StackItemHeading.tsx → StackItem/StackItemHeading.tsx} +10 -6
- package/src/components/{StackItemResizeHandle.tsx → StackItem/StackItemResizeHandle.tsx} +1 -1
- package/src/components/{StackItemSigil.tsx → StackItem/StackItemSigil.tsx} +3 -15
- package/src/components/StackItem/index.ts +5 -0
- package/src/components/defs.ts +26 -0
- package/src/components/{LayoutControls.tsx → deprecated/LayoutControls.tsx} +3 -23
- package/src/components/index.ts +2 -2
- package/src/exemplars/Card/Card.stories-todo.tsx +135 -0
- package/src/exemplars/Card/Card.tsx +178 -0
- package/src/exemplars/Card/CardDragPreview.tsx +22 -0
- package/src/exemplars/Card/fragments.ts +14 -0
- package/src/exemplars/Card/index.ts +6 -0
- package/src/exemplars/CardStack/CardStack.stories-todo.tsx +80 -0
- package/src/exemplars/CardStack/CardStack.tsx +118 -0
- package/src/exemplars/CardStack/CardStackDragPreview.tsx +61 -0
- package/src/exemplars/CardStack/index.ts +6 -0
- package/src/exemplars/index.ts +6 -0
- package/src/hooks/useStackDropForElements.ts +7 -6
- package/src/index.ts +4 -0
- package/src/testing/stack-manager.ts +6 -6
- package/src/translations.ts +1 -0
- package/dist/types/src/components/LayoutControls.d.ts.map +0 -1
- package/dist/types/src/components/MenuSignifier.d.ts.map +0 -1
- package/dist/types/src/components/Stack.d.ts.map +0 -1
- package/dist/types/src/components/Stack.stories.d.ts +0 -8
- package/dist/types/src/components/Stack.stories.d.ts.map +0 -1
- package/dist/types/src/components/StackItem.d.ts.map +0 -1
- package/dist/types/src/components/StackItemContent.d.ts.map +0 -1
- package/dist/types/src/components/StackItemDragHandle.d.ts.map +0 -1
- package/dist/types/src/components/StackItemHeading.d.ts.map +0 -1
- package/dist/types/src/components/StackItemResizeHandle.d.ts.map +0 -1
- package/dist/types/src/components/StackItemSigil.d.ts.map +0 -1
- /package/dist/types/src/components/{MenuSignifier.d.ts → StackItem/MenuSignifier.d.ts} +0 -0
- /package/dist/types/src/components/{StackItemContent.d.ts → StackItem/StackItemContent.d.ts} +0 -0
- /package/dist/types/src/components/{StackItemResizeHandle.d.ts → StackItem/StackItemResizeHandle.d.ts} +0 -0
- /package/dist/types/src/components/{StackItemSigil.d.ts → StackItem/StackItemSigil.d.ts} +0 -0
- /package/dist/types/src/components/{LayoutControls.d.ts → deprecated/LayoutControls.d.ts} +0 -0
- /package/src/components/{MenuSignifier.tsx → StackItem/MenuSignifier.tsx} +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
6
|
+
|
|
7
|
+
import { type Size as DndSize } from '@dxos/react-ui-dnd';
|
|
8
|
+
|
|
9
|
+
import { type Orientation, type Size } from './Stack';
|
|
10
|
+
|
|
11
|
+
export type StackItemSize = DndSize;
|
|
12
|
+
|
|
13
|
+
export type StackItemData = { id: string; type: 'column' | 'card' };
|
|
14
|
+
|
|
15
|
+
export type StackItemRearrangeHandler<Data extends { id: string } = StackItemData> = (
|
|
16
|
+
source: Data,
|
|
17
|
+
target: Data,
|
|
18
|
+
closestEdge: Edge | null,
|
|
19
|
+
) => void;
|
|
20
|
+
|
|
21
|
+
export type StackContextValue = {
|
|
22
|
+
orientation: Orientation;
|
|
23
|
+
rail: boolean;
|
|
24
|
+
size: Size;
|
|
25
|
+
onRearrange?: StackItemRearrangeHandler;
|
|
26
|
+
};
|
|
@@ -4,17 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { forwardRef } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
Button,
|
|
9
|
-
ButtonGroup,
|
|
10
|
-
type ButtonGroupProps,
|
|
11
|
-
type ButtonProps,
|
|
12
|
-
Icon,
|
|
13
|
-
Tooltip,
|
|
14
|
-
useTranslation,
|
|
15
|
-
} from '@dxos/react-ui';
|
|
7
|
+
import { ButtonGroup, type ButtonGroupProps, type ButtonProps, IconButton, useTranslation } from '@dxos/react-ui';
|
|
16
8
|
|
|
17
|
-
import { translationKey } from '
|
|
9
|
+
import { translationKey } from '../../translations';
|
|
18
10
|
|
|
19
11
|
export type LayoutControlEvent = 'solo' | 'close' | `${'pin' | 'increment'}-${'start' | 'end'}`;
|
|
20
12
|
export type LayoutControlHandler = (event: LayoutControlEvent) => void;
|
|
@@ -35,19 +27,7 @@ export type LayoutControlsProps = Omit<ButtonGroupProps, 'onClick'> & {
|
|
|
35
27
|
};
|
|
36
28
|
|
|
37
29
|
const LayoutControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> & { label: string; icon: string }) => {
|
|
38
|
-
return
|
|
39
|
-
<Tooltip.Root>
|
|
40
|
-
<Tooltip.Trigger asChild>
|
|
41
|
-
<Button variant='ghost' {...props}>
|
|
42
|
-
<span className='sr-only'>{label}</span>
|
|
43
|
-
<Icon icon={icon} />
|
|
44
|
-
</Button>
|
|
45
|
-
</Tooltip.Trigger>
|
|
46
|
-
<Tooltip.Portal>
|
|
47
|
-
<Tooltip.Content side='bottom'>{label}</Tooltip.Content>
|
|
48
|
-
</Tooltip.Portal>
|
|
49
|
-
</Tooltip.Root>
|
|
50
|
-
);
|
|
30
|
+
return <IconButton iconOnly icon={icon} label={label} tooltipSide='bottom' variant='ghost' {...props} />;
|
|
51
31
|
};
|
|
52
32
|
|
|
53
33
|
export const LayoutControls = forwardRef<HTMLDivElement, LayoutControlsProps>(
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// import '@dxos-theme';
|
|
6
|
+
//
|
|
7
|
+
// import React from 'react';
|
|
8
|
+
//
|
|
9
|
+
// import { faker } from '@dxos/random';
|
|
10
|
+
// import { Input, ScrollArea } from '@dxos/react-ui';
|
|
11
|
+
// import { modalSurface, mx } from '@dxos/react-ui-theme';
|
|
12
|
+
// import { withTheme } from '@dxos/storybook-utils';
|
|
13
|
+
//
|
|
14
|
+
// import { Card } from './Card';
|
|
15
|
+
//
|
|
16
|
+
// faker.seed(1);
|
|
17
|
+
//
|
|
18
|
+
// // https://unsplash.com
|
|
19
|
+
// // TODO(burdon): Use https://picsum.photos
|
|
20
|
+
// const testImages = [
|
|
21
|
+
// 'https://images.unsplash.com/photo-1616394158624-a2ba9cfe2994',
|
|
22
|
+
// 'https://images.unsplash.com/photo-1507941097613-9f2157b69235',
|
|
23
|
+
// 'https://images.unsplash.com/photo-1431274172761-fca41d930114',
|
|
24
|
+
// 'https://images.unsplash.com/photo-1513635269975-59663e0ac1ad',
|
|
25
|
+
// 'https://images.unsplash.com/photo-1564221710304-0b37c8b9d729',
|
|
26
|
+
// 'https://images.unsplash.com/photo-1605425183435-25b7e99104a4',
|
|
27
|
+
// ];
|
|
28
|
+
//
|
|
29
|
+
// const ReadonlyCardStory = () => {
|
|
30
|
+
// return (
|
|
31
|
+
// <div className='flex flex-col overflow-y-scroll'>
|
|
32
|
+
// <div className='flex flex-col gap-4'>
|
|
33
|
+
// <Card.Root item={{ id: 'readonly card story 1' }} data-flags='square noPadding'>
|
|
34
|
+
// <Card.Heading data-flags='floating'>
|
|
35
|
+
// <Card.DragHandle data-flags='positionLeft' />
|
|
36
|
+
// <Card.Menu data-flags='positionRight' />
|
|
37
|
+
// </Card.Heading>
|
|
38
|
+
// <Card.Media src={testImages[1]} />
|
|
39
|
+
// </Card.Root>
|
|
40
|
+
//
|
|
41
|
+
// <Card.Root item={{ id: 'readonly card story 2' }}>
|
|
42
|
+
// <Card.Heading>
|
|
43
|
+
// <Card.HeadingLabel title={faker.lorem.sentence(3)} />
|
|
44
|
+
// </Card.Heading>
|
|
45
|
+
// </Card.Root>
|
|
46
|
+
//
|
|
47
|
+
// <Card.Root item={{ id: 'readonly card story 3' }}>
|
|
48
|
+
// <Card.Content classNames={'text-sm font-thin h-[100px]'}>
|
|
49
|
+
// <ScrollArea.Root>
|
|
50
|
+
// <ScrollArea.Viewport>{faker.lorem.sentences(16)}</ScrollArea.Viewport>
|
|
51
|
+
// <ScrollArea.Scrollbar orientation='vertical'>
|
|
52
|
+
// <ScrollArea.Thumb />
|
|
53
|
+
// </ScrollArea.Scrollbar>
|
|
54
|
+
// </ScrollArea.Root>
|
|
55
|
+
// </Card.Content>
|
|
56
|
+
// </Card.Root>
|
|
57
|
+
//
|
|
58
|
+
// <Card.Root item={{ id: 'readonly card story 4' }}>
|
|
59
|
+
// <Card.Heading>
|
|
60
|
+
// <Card.HeadingLabel data-flags='center' title={faker.lorem.sentence(3)} />
|
|
61
|
+
// </Card.Heading>
|
|
62
|
+
// <Card.Content classNames={'text-sm font-thin'}>{faker.lorem.sentences(3)}</Card.Content>
|
|
63
|
+
// </Card.Root>
|
|
64
|
+
//
|
|
65
|
+
// <Card.Root item={{ id: 'readonly card story 5' }}>
|
|
66
|
+
// <Card.Heading>
|
|
67
|
+
// <Card.DragHandle />
|
|
68
|
+
// <Card.HeadingLabel title={faker.lorem.sentence(8)} />
|
|
69
|
+
// {/* TODO(burdon): Menu util. */}
|
|
70
|
+
// <Card.Menu />
|
|
71
|
+
// </Card.Heading>
|
|
72
|
+
// <Card.Content data-flags='gutter' classNames={'gap-2 text-sm font-thin'}>
|
|
73
|
+
// <p>Content with gutter</p>
|
|
74
|
+
// <p className='line-clamp-3'>{faker.lorem.sentences(3)}</p>
|
|
75
|
+
// </Card.Content>
|
|
76
|
+
// </Card.Root>
|
|
77
|
+
//
|
|
78
|
+
// <Card.Root item={{ id: 'readonly card story 6' }}>
|
|
79
|
+
// <Card.Heading>
|
|
80
|
+
// <Card.DragHandle />
|
|
81
|
+
// <Card.HeadingLabel title={faker.lorem.sentence(3)} />
|
|
82
|
+
// <Card.Menu />
|
|
83
|
+
// </Card.Heading>
|
|
84
|
+
// <Card.Content data-flags='gutter' classNames={'text-sm gap-2 font-thin'}>
|
|
85
|
+
// <p className='line-clamp-3'>{faker.lorem.sentences(1)}</p>
|
|
86
|
+
// </Card.Content>
|
|
87
|
+
// <Card.Media className={'h-[200px] grayscale'} src={testImages[0]} />
|
|
88
|
+
// <Card.Content data-flags='gutter' classNames={'text-sm gap-2 font-thin'}>
|
|
89
|
+
// <p className='line-clamp-3'>{faker.lorem.sentences(3)}</p>
|
|
90
|
+
// </Card.Content>
|
|
91
|
+
// </Card.Root>
|
|
92
|
+
// </div>
|
|
93
|
+
// </div>
|
|
94
|
+
// );
|
|
95
|
+
// };
|
|
96
|
+
//
|
|
97
|
+
// const EditableCardStory = () => {
|
|
98
|
+
// return (
|
|
99
|
+
// <div className='flex flex-col h-full justify-center'>
|
|
100
|
+
// <Card.Root item={{ id: 'editable card story 1' }}>
|
|
101
|
+
// <Card.Heading>
|
|
102
|
+
// <Card.DragHandle />
|
|
103
|
+
// <Input.Root>
|
|
104
|
+
// <Input.TextInput classNames={'-mx-2 px-2'} variant='subdued' placeholder={'Title'} />
|
|
105
|
+
// </Input.Root>
|
|
106
|
+
// <Card.Menu />
|
|
107
|
+
// </Card.Heading>
|
|
108
|
+
// <Card.Content data-flags='gutter' classNames={'gap-2 text-sm font-thin'}>
|
|
109
|
+
// {faker.lorem.sentences()}
|
|
110
|
+
// </Card.Content>
|
|
111
|
+
// </Card.Root>
|
|
112
|
+
// </div>
|
|
113
|
+
// );
|
|
114
|
+
// };
|
|
115
|
+
//
|
|
116
|
+
// export default {
|
|
117
|
+
// title: 'ui/react-ui-card/Card',
|
|
118
|
+
// component: Card,
|
|
119
|
+
// decorators: [
|
|
120
|
+
// withTheme,
|
|
121
|
+
// (Story: any) => (
|
|
122
|
+
// <div className={mx('flex h-screen w-full justify-center overflow-hidden', modalSurface)}>
|
|
123
|
+
// <div className='flex flex-col w-[360px] overflow-hidden'>
|
|
124
|
+
// <Story />
|
|
125
|
+
// </div>
|
|
126
|
+
// </div>
|
|
127
|
+
// ),
|
|
128
|
+
// ],
|
|
129
|
+
// parameters: {
|
|
130
|
+
// layout: 'fullscreen',
|
|
131
|
+
// },
|
|
132
|
+
// };
|
|
133
|
+
//
|
|
134
|
+
// export const ReadOnly = () => <ReadonlyCardStory />;
|
|
135
|
+
// export const Editable = () => <EditableCardStory />;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Primitive } from '@radix-ui/react-primitive';
|
|
6
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
7
|
+
import React, {
|
|
8
|
+
type ComponentPropsWithoutRef,
|
|
9
|
+
type ComponentPropsWithRef,
|
|
10
|
+
type FC,
|
|
11
|
+
forwardRef,
|
|
12
|
+
type PropsWithChildren,
|
|
13
|
+
} from 'react';
|
|
14
|
+
|
|
15
|
+
import { Icon, IconButton, type ThemedClassName, Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
|
|
16
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
17
|
+
|
|
18
|
+
import { cardChrome, cardContent, cardHeading, cardRoot, cardText } from './fragments';
|
|
19
|
+
import { StackItem } from '../../components';
|
|
20
|
+
import { translationKey } from '../../translations';
|
|
21
|
+
|
|
22
|
+
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
23
|
+
|
|
24
|
+
const CardRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
25
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
26
|
+
const Root = asChild ? Slot : 'div';
|
|
27
|
+
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
28
|
+
return (
|
|
29
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
30
|
+
{children}
|
|
31
|
+
</Root>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const CardContent = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
37
|
+
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
38
|
+
const Root = asChild ? Slot : 'div';
|
|
39
|
+
const rootProps = asChild
|
|
40
|
+
? { classNames: [cardContent, classNames] }
|
|
41
|
+
: { className: mx(cardContent, classNames), role };
|
|
42
|
+
return (
|
|
43
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
44
|
+
{children}
|
|
45
|
+
</Root>
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
|
|
52
|
+
* in a Popover) and knows this based on the `role` it receives. This will render a `Card.Content` by default, otherwise
|
|
53
|
+
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
54
|
+
*/
|
|
55
|
+
const CardConditionalContent = ({ role, children }: PropsWithChildren<{ role?: string }>) => {
|
|
56
|
+
if (['popover', 'card--kanban'].includes(role ?? 'never')) {
|
|
57
|
+
return (
|
|
58
|
+
<div className={role === 'popover' ? 'popover-card-width' : role === 'card--kanban' ? 'contents' : ''}>
|
|
59
|
+
{children}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
return <CardContent>{children}</CardContent>;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
68
|
+
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
69
|
+
const Root = asChild ? Slot : 'div';
|
|
70
|
+
const rootProps = asChild
|
|
71
|
+
? { classNames: [cardHeading, cardText, classNames] }
|
|
72
|
+
: { className: mx(cardHeading, cardText, classNames), role };
|
|
73
|
+
return (
|
|
74
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
75
|
+
{children}
|
|
76
|
+
</Root>
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const CardToolbar = forwardRef<HTMLDivElement, ToolbarRootProps>(({ children, ...props }, forwardedRef) => {
|
|
82
|
+
return (
|
|
83
|
+
<Toolbar.Root {...props} ref={forwardedRef}>
|
|
84
|
+
{children}
|
|
85
|
+
</Toolbar.Root>
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const CardToolbarIconButton = Toolbar.IconButton;
|
|
90
|
+
const CardToolbarSeparator = Toolbar.Separator;
|
|
91
|
+
|
|
92
|
+
const CardDragHandle = forwardRef<HTMLButtonElement, { toolbarItem?: boolean }>(({ toolbarItem }, forwardedRef) => {
|
|
93
|
+
const { t } = useTranslation(translationKey);
|
|
94
|
+
const Root = toolbarItem ? Toolbar.IconButton : IconButton;
|
|
95
|
+
return (
|
|
96
|
+
<Root
|
|
97
|
+
iconOnly
|
|
98
|
+
icon='ph--dots-six-vertical--regular'
|
|
99
|
+
variant='ghost'
|
|
100
|
+
label={t('card drag handle label')}
|
|
101
|
+
classNames='pli-2'
|
|
102
|
+
ref={forwardedRef}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const CardDragPreview = StackItem.DragPreview;
|
|
108
|
+
|
|
109
|
+
const CardMenu = Primitive.div as FC<ComponentPropsWithRef<'div'>>;
|
|
110
|
+
|
|
111
|
+
type CardPosterProps = {
|
|
112
|
+
alt: string;
|
|
113
|
+
aspect?: 'video' | 'auto';
|
|
114
|
+
} & Partial<{ image: string; icon: string }>;
|
|
115
|
+
|
|
116
|
+
const CardPoster = (props: CardPosterProps) => {
|
|
117
|
+
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
118
|
+
if (props.image) {
|
|
119
|
+
return (
|
|
120
|
+
<img className={`dx-card__poster ${aspect} object-cover is-full bs-auto`} src={props.image} alt={props.alt} />
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (props.icon) {
|
|
124
|
+
return (
|
|
125
|
+
<div
|
|
126
|
+
role='image'
|
|
127
|
+
className={`dx-card__poster grid ${aspect} place-items-center bg-inputSurface text-subdued`}
|
|
128
|
+
aria-label={props.alt}
|
|
129
|
+
>
|
|
130
|
+
<Icon icon={props.icon} size={10} />
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
137
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
138
|
+
const Root = asChild ? Slot : 'div';
|
|
139
|
+
const rootProps = asChild
|
|
140
|
+
? { classNames: [cardChrome, classNames] }
|
|
141
|
+
: { className: mx(cardChrome, classNames), role };
|
|
142
|
+
return (
|
|
143
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
144
|
+
{children}
|
|
145
|
+
</Root>
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const CardText = forwardRef<HTMLParagraphElement, SharedCardProps>(
|
|
151
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
152
|
+
const Root = asChild ? Slot : 'p';
|
|
153
|
+
const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
|
|
154
|
+
return (
|
|
155
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
156
|
+
{children}
|
|
157
|
+
</Root>
|
|
158
|
+
);
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
export const Card = {
|
|
163
|
+
Root: CardRoot,
|
|
164
|
+
Content: CardContent,
|
|
165
|
+
Container: CardConditionalContent,
|
|
166
|
+
Heading: CardHeading,
|
|
167
|
+
Toolbar: CardToolbar,
|
|
168
|
+
ToolbarIconButton: CardToolbarIconButton,
|
|
169
|
+
ToolbarSeparator: CardToolbarSeparator,
|
|
170
|
+
DragHandle: CardDragHandle,
|
|
171
|
+
DragPreview: CardDragPreview,
|
|
172
|
+
Menu: CardMenu,
|
|
173
|
+
Poster: CardPoster,
|
|
174
|
+
Chrome: CardChrome,
|
|
175
|
+
Text: CardText,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export { cardRoot, cardContent, cardHeading, cardText, cardChrome };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type PropsWithChildren } from 'react';
|
|
6
|
+
|
|
7
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
8
|
+
|
|
9
|
+
import { cardContent } from './fragments';
|
|
10
|
+
|
|
11
|
+
const CardDragPreviewRoot = ({ children }: PropsWithChildren<{}>) => {
|
|
12
|
+
return <div className='p-2'>{children}</div>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const CardDragPreviewContent = ({ children }: PropsWithChildren<{}>) => {
|
|
16
|
+
return <div className={mx(cardContent, 'ring-focusLine ring-neutralFocusIndicator')}>{children}</div>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const CardDragPreview = {
|
|
20
|
+
Root: CardDragPreviewRoot,
|
|
21
|
+
Content: CardDragPreviewContent,
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export const cardRoot = 'contain-layout pli-2 plb-1 first-of-type:pbs-0 last-of-type:pbe-0';
|
|
6
|
+
|
|
7
|
+
export const cardContent =
|
|
8
|
+
'rounded overflow-hidden bg-cardSurface border border-separator dark:border-subduedSeparator dx-focus-ring-group-y-indicator relative min-bs-[--rail-item] group/card';
|
|
9
|
+
|
|
10
|
+
export const cardText = 'pli-3 mlb-3';
|
|
11
|
+
|
|
12
|
+
export const cardHeading = 'text-lg font-medium line-clamp-2';
|
|
13
|
+
|
|
14
|
+
export const cardChrome = 'pli-1.5 mlb-1.5 [&_.dx-button]:pli-1.5 [&_.dx-button]:text-start [&_.dx-button]:is-full';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// type CardData = {
|
|
6
|
+
// id: string;
|
|
7
|
+
// title: string;
|
|
8
|
+
// body: string;
|
|
9
|
+
// image?: string;
|
|
10
|
+
// };
|
|
11
|
+
|
|
12
|
+
// const DraggableCard: FC<{ data: CardData; onDelete: (id: string) => void }> = ({ data, onDelete }) => {
|
|
13
|
+
// const { id, title, body, image } = data;
|
|
14
|
+
// const { attributes, listeners, setNodeRef, transform } = useSortable({ id });
|
|
15
|
+
// const t = transform ? Object.assign(transform, { scaleY: 1 }) : null;
|
|
16
|
+
//
|
|
17
|
+
// return (
|
|
18
|
+
// <Card.Root ref={setNodeRef} item={data} style={{ transform: CSS.Transform.toString(t) }}>
|
|
19
|
+
// <Card.Heading>
|
|
20
|
+
// <Card.DragHandle {...listeners} {...attributes} />
|
|
21
|
+
// <Card.Title title={title} />
|
|
22
|
+
// <DropdownMenu.Root>
|
|
23
|
+
// <DropdownMenu.Trigger asChild>
|
|
24
|
+
// <Card.Menu />
|
|
25
|
+
// </DropdownMenu.Trigger>
|
|
26
|
+
// <DropdownMenu.Content>
|
|
27
|
+
// <DropdownMenu.Viewport>
|
|
28
|
+
// <DropdownMenu.Item onClick={() => onDelete(id)}>Delete</DropdownMenu.Item>
|
|
29
|
+
// </DropdownMenu.Viewport>
|
|
30
|
+
// </DropdownMenu.Content>
|
|
31
|
+
// </DropdownMenu.Root>
|
|
32
|
+
// </Card.Heading>
|
|
33
|
+
// <Card.Content classNames={'text-sm'} gutter>
|
|
34
|
+
// <p>{body}</p>
|
|
35
|
+
// </Card.Content>
|
|
36
|
+
// {image && <Card.Media src={image} classNames={'h-[160px]'} />}
|
|
37
|
+
// </Card.Root>
|
|
38
|
+
// );
|
|
39
|
+
// };
|
|
40
|
+
//
|
|
41
|
+
// const DraggableStory: FC<PropsWithChildren> = ({ children }) => {
|
|
42
|
+
// const [cards, setCards] = useState<CardData[]>(
|
|
43
|
+
// Array.from({ length: 7 }).map(() => ({
|
|
44
|
+
// id: faker.string.uuid(),
|
|
45
|
+
// title: faker.lorem.sentence(3),
|
|
46
|
+
// body: faker.lorem.sentences(),
|
|
47
|
+
// image: faker.datatype.boolean() ? faker.helpers.arrayElement(testImages) : undefined,
|
|
48
|
+
// })),
|
|
49
|
+
// );
|
|
50
|
+
//
|
|
51
|
+
// const handleDelete = (id: string) => {
|
|
52
|
+
// setCards((cards) => cards.filter((card) => card.id !== id));
|
|
53
|
+
// };
|
|
54
|
+
//
|
|
55
|
+
// const handleDragEnd = (event: DragEndEvent) => {
|
|
56
|
+
// const { active, over } = event;
|
|
57
|
+
// if (active.id !== over?.id) {
|
|
58
|
+
// setCards((cards) => {
|
|
59
|
+
// const oldIndex = cards.findIndex((card) => card.id === active.id);
|
|
60
|
+
// const newIndex = cards.findIndex((card) => card.id === over?.id);
|
|
61
|
+
// return arrayMove(cards, oldIndex, newIndex);
|
|
62
|
+
// });
|
|
63
|
+
// }
|
|
64
|
+
// };
|
|
65
|
+
//
|
|
66
|
+
// return (
|
|
67
|
+
// <DndContext onDragEnd={handleDragEnd}>
|
|
68
|
+
// <SortableContext items={cards.map(({ id }) => id)} strategy={verticalListSortingStrategy}>
|
|
69
|
+
// <div className='flex flex-col overflow-y-scroll'>
|
|
70
|
+
// <div className='flex flex-col gap-4'>
|
|
71
|
+
// {cards.map((card) => (
|
|
72
|
+
// <DraggableCard key={card.id} data={card} onDelete={handleDelete} />
|
|
73
|
+
// ))}
|
|
74
|
+
// </div>
|
|
75
|
+
// </div>
|
|
76
|
+
// </SortableContext>
|
|
77
|
+
// </DndContext>
|
|
78
|
+
// );
|
|
79
|
+
// };
|
|
80
|
+
// export const Draggable = () => <DraggableStory />;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
6
|
+
import React, { type ComponentPropsWithoutRef, forwardRef } from 'react';
|
|
7
|
+
|
|
8
|
+
import type { ThemedClassName } from '@dxos/react-ui';
|
|
9
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
10
|
+
|
|
11
|
+
import { railGridHorizontalContainFitContent, Stack, type StackProps } from '../../components';
|
|
12
|
+
import { Card } from '../Card';
|
|
13
|
+
|
|
14
|
+
type SharedCardStackProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
15
|
+
|
|
16
|
+
const CardStackStack = forwardRef<
|
|
17
|
+
HTMLDivElement,
|
|
18
|
+
Omit<StackProps, 'orientation' | 'size' | 'rail' | 'separatorOnScroll'>
|
|
19
|
+
>(({ children, classNames, itemsCount = 0, ...props }, forwardedRef) => {
|
|
20
|
+
return (
|
|
21
|
+
<Stack
|
|
22
|
+
orientation='vertical'
|
|
23
|
+
size='contain'
|
|
24
|
+
rail={false}
|
|
25
|
+
classNames={
|
|
26
|
+
/* NOTE(thure): Do not let this element have zero intrinsic size, otherwise the drop indicator will not display. See #9035. */
|
|
27
|
+
['plb-1', itemsCount > 0 && 'plb-2', classNames]
|
|
28
|
+
}
|
|
29
|
+
itemsCount={itemsCount}
|
|
30
|
+
separatorOnScroll={9}
|
|
31
|
+
{...props}
|
|
32
|
+
ref={forwardedRef}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</Stack>
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const CardStackDragHandle = Card.DragHandle;
|
|
40
|
+
|
|
41
|
+
const cardStackHeading = 'mli-2 order-first bg-transparent rounded-bs-md flex items-center';
|
|
42
|
+
|
|
43
|
+
const CardStackHeading = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
44
|
+
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
45
|
+
const Root = asChild ? Slot : 'div';
|
|
46
|
+
const rootProps = asChild
|
|
47
|
+
? { classNames: [cardStackHeading, classNames] }
|
|
48
|
+
: { className: mx(cardStackHeading, classNames), role };
|
|
49
|
+
return (
|
|
50
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
51
|
+
{children}
|
|
52
|
+
</Root>
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const cardStackFooter =
|
|
58
|
+
'plb-2 mli-2 border-bs border-transparent [[data-scroll-separator-end="true"]_&]:border-subduedSeparator';
|
|
59
|
+
|
|
60
|
+
const CardStackFooter = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
61
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
62
|
+
const Root = asChild ? Slot : 'div';
|
|
63
|
+
const rootProps = asChild
|
|
64
|
+
? { classNames: [cardStackFooter, classNames] }
|
|
65
|
+
: { className: mx(cardStackFooter, classNames), role };
|
|
66
|
+
return (
|
|
67
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
68
|
+
{children}
|
|
69
|
+
</Root>
|
|
70
|
+
);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const cardStackContent = [
|
|
75
|
+
'shrink min-bs-0 bg-baseSurface border border-separator rounded-md grid dx-focus-ring-group-x-indicator kanban-drop',
|
|
76
|
+
railGridHorizontalContainFitContent,
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const CardStackContent = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
80
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
81
|
+
const Root = asChild ? Slot : 'div';
|
|
82
|
+
const rootProps = asChild
|
|
83
|
+
? { classNames: [...cardStackContent, classNames] }
|
|
84
|
+
: { className: mx(...cardStackContent, classNames), role };
|
|
85
|
+
return (
|
|
86
|
+
<Root {...props} {...rootProps} data-scroll-separator='false' ref={forwardedRef}>
|
|
87
|
+
{children}
|
|
88
|
+
</Root>
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const cardStackRoot = 'flex flex-col pli-2 plb-2';
|
|
94
|
+
|
|
95
|
+
const CardStackRoot = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
96
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
97
|
+
const Root = asChild ? Slot : 'div';
|
|
98
|
+
const rootProps = asChild
|
|
99
|
+
? { classNames: [cardStackRoot, classNames] }
|
|
100
|
+
: { className: mx(cardStackRoot, classNames), role };
|
|
101
|
+
return (
|
|
102
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
103
|
+
{children}
|
|
104
|
+
</Root>
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
export const CardStack = {
|
|
110
|
+
Root: CardStackRoot,
|
|
111
|
+
Content: CardStackContent,
|
|
112
|
+
Stack: CardStackStack,
|
|
113
|
+
Heading: CardStackHeading,
|
|
114
|
+
Footer: CardStackFooter,
|
|
115
|
+
DragHandle: CardStackDragHandle,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export { cardStackRoot, cardStackFooter, cardStackHeading, cardStackContent };
|