@dxos/react-ui-stack 0.8.4-main.3a94e84 → 0.8.4-main.3c1ae3b
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-P3TQV4BA.mjs → chunk-6YJ6CHWV.mjs} +523 -238
- package/dist/lib/browser/chunk-6YJ6CHWV.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/playwright/index.mjs +4 -0
- package/dist/lib/browser/playwright/index.mjs.map +2 -2
- package/dist/lib/browser/testing/index.mjs +3 -3
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/{chunk-3WVEPAJ4.mjs → chunk-KCFYDRBZ.mjs} +523 -238
- package/dist/lib/node-esm/chunk-KCFYDRBZ.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +9 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/playwright/index.mjs +4 -0
- package/dist/lib/node-esm/playwright/index.mjs.map +2 -2
- package/dist/lib/node-esm/testing/index.mjs +3 -3
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/components/Image/Image.d.ts +14 -0
- package/dist/types/src/components/Image/Image.d.ts.map +1 -0
- package/dist/types/src/components/Image/Image.stories.d.ts +33 -0
- package/dist/types/src/components/Image/Image.stories.d.ts.map +1 -0
- package/dist/types/src/components/Image/index.d.ts +2 -0
- package/dist/types/src/components/Image/index.d.ts.map +1 -0
- package/dist/types/src/components/Stack/Stack.d.ts +15 -7
- package/dist/types/src/components/Stack/Stack.d.ts.map +1 -1
- package/dist/types/src/components/Stack/Stack.stories.d.ts +12 -3
- package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackContext.d.ts +2 -1
- package/dist/types/src/components/StackContext.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.d.ts +7 -6
- package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts +13 -5
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemContent.d.ts +20 -10
- package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemHeading.d.ts +1 -1
- package/dist/types/src/components/StackItem/StackItemHeading.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemResizeHandle.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemSigil.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +27 -14
- package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.stories.d.ts +12 -4
- package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/fragments.d.ts +3 -2
- package/dist/types/src/exemplars/Card/fragments.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts +5 -3
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +9 -3
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useStackDropForElements.d.ts +3 -3
- package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
- package/dist/types/src/testing/CardContainer.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -27
- package/src/components/Image/Image.stories.tsx +84 -0
- package/src/components/Image/Image.tsx +222 -0
- package/src/components/Image/index.ts +5 -0
- package/src/components/Stack/Stack.stories.tsx +8 -9
- package/src/components/Stack/Stack.tsx +223 -25
- package/src/components/StackContext.tsx +2 -1
- package/src/components/StackItem/StackItem.stories.tsx +16 -14
- package/src/components/StackItem/StackItem.tsx +26 -18
- package/src/components/StackItem/StackItemContent.tsx +20 -8
- package/src/components/StackItem/StackItemHeading.tsx +4 -8
- package/src/components/StackItem/StackItemResizeHandle.tsx +2 -1
- package/src/components/StackItem/StackItemSigil.tsx +2 -1
- package/src/components/index.ts +1 -0
- package/src/exemplars/Card/Card.stories.tsx +29 -43
- package/src/exemplars/Card/Card.tsx +80 -54
- package/src/exemplars/Card/fragments.ts +3 -2
- package/src/exemplars/CardStack/CardStack.stories.tsx +12 -11
- package/src/exemplars/CardStack/CardStack.tsx +96 -51
- package/src/hooks/useStackDropForElements.ts +43 -36
- package/src/testing/CardContainer.tsx +9 -6
- package/dist/lib/browser/chunk-P3TQV4BA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3WVEPAJ4.mjs.map +0 -7
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
6
|
import React from 'react';
|
|
9
7
|
|
|
10
8
|
import { faker } from '@dxos/random';
|
|
11
|
-
import { withTheme } from '@dxos/
|
|
9
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
10
|
|
|
13
11
|
import { Card } from './Card';
|
|
14
12
|
|
|
15
|
-
// Set a seed for reproducible random values
|
|
16
13
|
faker.seed(0);
|
|
17
14
|
|
|
18
15
|
type CardStoryProps = {
|
|
@@ -23,44 +20,8 @@ type CardStoryProps = {
|
|
|
23
20
|
showIcon: boolean;
|
|
24
21
|
};
|
|
25
22
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
decorators: [withTheme],
|
|
29
|
-
argTypes: {
|
|
30
|
-
title: {
|
|
31
|
-
control: 'text',
|
|
32
|
-
description: 'Card title',
|
|
33
|
-
},
|
|
34
|
-
description: {
|
|
35
|
-
control: 'text',
|
|
36
|
-
description: 'Card description',
|
|
37
|
-
},
|
|
38
|
-
image: {
|
|
39
|
-
control: 'text',
|
|
40
|
-
description: 'URL for the poster image',
|
|
41
|
-
},
|
|
42
|
-
showImage: {
|
|
43
|
-
control: 'boolean',
|
|
44
|
-
description: 'Whether to show the image',
|
|
45
|
-
},
|
|
46
|
-
showIcon: {
|
|
47
|
-
control: 'boolean',
|
|
48
|
-
description: 'Whether to show an icon (when image is not shown)',
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
args: {
|
|
52
|
-
title: faker.commerce.productName(),
|
|
53
|
-
description: faker.lorem.paragraph(),
|
|
54
|
-
image: faker.image.url(),
|
|
55
|
-
showImage: true,
|
|
56
|
-
showIcon: true,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export default meta;
|
|
61
|
-
|
|
62
|
-
export const Default: StoryObj<CardStoryProps> = {
|
|
63
|
-
render: ({ title, description, image, showImage, showIcon }: CardStoryProps) => (
|
|
23
|
+
const DefaultStory = ({ title, description, image, showImage, showIcon }: CardStoryProps) => {
|
|
24
|
+
return (
|
|
64
25
|
<div className='max-is-md'>
|
|
65
26
|
<Card.StaticRoot>
|
|
66
27
|
<Card.Toolbar>
|
|
@@ -74,5 +35,30 @@ export const Default: StoryObj<CardStoryProps> = {
|
|
|
74
35
|
{description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
|
|
75
36
|
</Card.StaticRoot>
|
|
76
37
|
</div>
|
|
77
|
-
)
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const meta = {
|
|
42
|
+
title: 'ui/react-ui-stack/Card',
|
|
43
|
+
render: DefaultStory,
|
|
44
|
+
decorators: [withTheme],
|
|
45
|
+
parameters: {
|
|
46
|
+
layout: 'centered',
|
|
47
|
+
},
|
|
48
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
49
|
+
|
|
50
|
+
export default meta;
|
|
51
|
+
|
|
52
|
+
type Story = StoryObj<typeof meta>;
|
|
53
|
+
|
|
54
|
+
const image = faker.image.url();
|
|
55
|
+
|
|
56
|
+
export const Default: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
title: faker.commerce.productName(),
|
|
59
|
+
description: faker.lorem.paragraph(),
|
|
60
|
+
image,
|
|
61
|
+
showImage: true,
|
|
62
|
+
showIcon: true,
|
|
63
|
+
},
|
|
78
64
|
};
|
|
@@ -5,28 +5,42 @@
|
|
|
5
5
|
import { Primitive } from '@radix-ui/react-primitive';
|
|
6
6
|
import { Slot } from '@radix-ui/react-slot';
|
|
7
7
|
import React, {
|
|
8
|
-
type ComponentPropsWithoutRef,
|
|
9
8
|
type ComponentPropsWithRef,
|
|
9
|
+
type ComponentPropsWithoutRef,
|
|
10
10
|
type FC,
|
|
11
|
-
forwardRef,
|
|
12
11
|
type PropsWithChildren,
|
|
12
|
+
forwardRef,
|
|
13
13
|
} from 'react';
|
|
14
14
|
|
|
15
15
|
import { Icon, IconButton, type ThemedClassName, Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
|
|
16
|
-
import { hoverableControls, mx } from '@dxos/react-ui-theme';
|
|
16
|
+
import { cardMinInlineSize, hoverableControls, mx } from '@dxos/react-ui-theme';
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
import { StackItem } from '../../components';
|
|
18
|
+
import { Image, StackItem } from '../../components';
|
|
20
19
|
import { translationKey } from '../../translations';
|
|
21
20
|
|
|
21
|
+
import { cardChrome, cardHeading, cardRoot, cardSpacing, cardText } from './fragments';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The default width of cards. It should be no larger than 320px per WCAG 2.1 SC 1.4.10.
|
|
25
|
+
*/
|
|
26
|
+
const cardDefaultInlineSize = cardMinInlineSize;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* This is `cardDefaultInlineSize` plus 2 times the sum of the inner and outer spacing applied by CardStack on the inline axis.
|
|
30
|
+
*/
|
|
31
|
+
const cardStackDefaultInlineSizeRem = cardDefaultInlineSize + 2.125;
|
|
32
|
+
|
|
22
33
|
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
23
34
|
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Use this when ....
|
|
37
|
+
*/
|
|
38
|
+
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps & { id?: string }>(
|
|
39
|
+
({ children, classNames, id, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
26
40
|
const Root = asChild ? Slot : 'div';
|
|
27
41
|
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
28
42
|
return (
|
|
29
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
43
|
+
<Root {...(id && { 'data-object-id': id })} {...props} {...rootProps} ref={forwardedRef}>
|
|
30
44
|
{children}
|
|
31
45
|
</Root>
|
|
32
46
|
);
|
|
@@ -34,44 +48,45 @@ const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
34
48
|
);
|
|
35
49
|
|
|
36
50
|
/**
|
|
37
|
-
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
|
|
38
|
-
*
|
|
39
|
-
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
51
|
+
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g., in a Popover) and knows this based on the `role` it receives.
|
|
52
|
+
* This will render a `Card.StaticRoot` by default, otherwise it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
40
53
|
*/
|
|
41
|
-
const CardSurfaceRoot = (
|
|
42
|
-
role = 'never',
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
54
|
+
const CardSurfaceRoot = forwardRef<HTMLDivElement, ThemedClassName<PropsWithChildren<{ id?: string; role?: string }>>>(
|
|
55
|
+
({ id, role = 'never', children, classNames }, forwardedRef) => {
|
|
56
|
+
if (['card--popover', 'card--intrinsic', 'card--extrinsic'].includes(role)) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
{...(id && { 'data-object-id': id })}
|
|
60
|
+
className={mx(
|
|
61
|
+
role === 'card--popover'
|
|
62
|
+
? 'popover-card-width'
|
|
63
|
+
: ['card--intrinsic', 'card--extrinsic'].includes(role)
|
|
64
|
+
? 'contents'
|
|
65
|
+
: '',
|
|
66
|
+
classNames,
|
|
67
|
+
)}
|
|
68
|
+
ref={forwardedRef}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
} else {
|
|
74
|
+
return (
|
|
75
|
+
<CardStaticRoot
|
|
76
|
+
id={id}
|
|
77
|
+
classNames={[
|
|
78
|
+
role === 'card--transclusion' && 'mlb-1',
|
|
79
|
+
role === 'card--transclusion' && hoverableControls,
|
|
80
|
+
classNames,
|
|
81
|
+
]}
|
|
82
|
+
ref={forwardedRef}
|
|
83
|
+
>
|
|
84
|
+
{children}
|
|
85
|
+
</CardStaticRoot>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
);
|
|
75
90
|
|
|
76
91
|
const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
77
92
|
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
@@ -117,23 +132,26 @@ const CardDragPreview = StackItem.DragPreview;
|
|
|
117
132
|
|
|
118
133
|
const CardMenu = Primitive.div as FC<ComponentPropsWithRef<'div'>>;
|
|
119
134
|
|
|
120
|
-
type CardPosterProps =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
type CardPosterProps = ThemedClassName<
|
|
136
|
+
{
|
|
137
|
+
alt: string;
|
|
138
|
+
aspect?: 'video' | 'auto';
|
|
139
|
+
} & Partial<{ image: string; icon: string }>
|
|
140
|
+
>;
|
|
124
141
|
|
|
125
142
|
const CardPoster = (props: CardPosterProps) => {
|
|
126
143
|
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
127
144
|
if (props.image) {
|
|
128
145
|
return (
|
|
129
|
-
<
|
|
146
|
+
<Image classNames={[`dx-card__poster is-full`, aspect, props.classNames]} src={props.image} alt={props.alt} />
|
|
130
147
|
);
|
|
131
148
|
}
|
|
149
|
+
|
|
132
150
|
if (props.icon) {
|
|
133
151
|
return (
|
|
134
152
|
<div
|
|
135
153
|
role='image'
|
|
136
|
-
className={`dx-card__poster grid
|
|
154
|
+
className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect, props.classNames)}
|
|
137
155
|
aria-label={props.alt}
|
|
138
156
|
>
|
|
139
157
|
<Icon icon={props.icon} size={10} />
|
|
@@ -156,9 +174,9 @@ const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
156
174
|
},
|
|
157
175
|
);
|
|
158
176
|
|
|
159
|
-
const CardText = forwardRef<
|
|
177
|
+
const CardText = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
160
178
|
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
161
|
-
const Root = asChild ? Slot : '
|
|
179
|
+
const Root = asChild ? Slot : 'div';
|
|
162
180
|
const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
|
|
163
181
|
return (
|
|
164
182
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
@@ -183,4 +201,12 @@ export const Card = {
|
|
|
183
201
|
Text: CardText,
|
|
184
202
|
};
|
|
185
203
|
|
|
186
|
-
export {
|
|
204
|
+
export {
|
|
205
|
+
cardRoot,
|
|
206
|
+
cardHeading,
|
|
207
|
+
cardText,
|
|
208
|
+
cardChrome,
|
|
209
|
+
cardSpacing,
|
|
210
|
+
cardStackDefaultInlineSizeRem,
|
|
211
|
+
cardDefaultInlineSize,
|
|
212
|
+
};
|
|
@@ -6,6 +6,7 @@ export const cardRoot =
|
|
|
6
6
|
'rounded overflow-hidden bg-cardSurface border border-separator dark:border-subduedSeparator dx-focus-ring-group-y-indicator relative min-bs-[--rail-item] group/card';
|
|
7
7
|
|
|
8
8
|
export const cardSpacing = 'pli-cardSpacingInline mlb-cardSpacingBlock';
|
|
9
|
+
export const cardNoSpacing = 'pli-0 mlb-0';
|
|
9
10
|
export const labelSpacing = 'mbs-inputSpacingBlock mbe-labelSpacingBlock';
|
|
10
11
|
|
|
11
12
|
export const cardDialogContent = 'p-0 bs-content min-bs-[8rem] max-bs-full md:max-is-[32rem] overflow-hidden';
|
|
@@ -17,7 +18,7 @@ export const cardDialogSearchListRoot =
|
|
|
17
18
|
|
|
18
19
|
export const cardText = cardSpacing;
|
|
19
20
|
|
|
20
|
-
export const cardHeading = 'text-lg font-medium line-clamp-2';
|
|
21
|
+
export const cardHeading = 'text-lg font-medium line-clamp-2 grow';
|
|
21
22
|
|
|
22
23
|
export const cardChrome =
|
|
23
|
-
'pli-[--dx-cardSpacingChrome] mlb-[--dx-cardSpacingChrome] [&_.dx-button]:text-start [&_.dx-button]:is-full';
|
|
24
|
+
'pli-[--dx-cardSpacingChrome] mlb-[--dx-cardSpacingChrome] [&_.dx-button]:text-start [&_.dx-button]:is-full [&_.dx-button]:pis-[calc(var(--dx-cardSpacingInline)-var(--dx-cardSpacingChrome))]';
|
|
@@ -2,21 +2,19 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import '@dxos-theme';
|
|
6
|
-
|
|
7
5
|
import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
8
6
|
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
9
|
-
import React, {
|
|
7
|
+
import React, { useCallback, useState } from 'react';
|
|
10
8
|
|
|
11
9
|
import { faker } from '@dxos/random';
|
|
12
10
|
import { IconButton } from '@dxos/react-ui';
|
|
13
|
-
import {
|
|
11
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
14
12
|
|
|
15
|
-
import { CardStack } from './CardStack';
|
|
16
13
|
import { StackItem } from '../../components';
|
|
17
14
|
import { Card, CardDragPreview } from '../Card';
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
import { CardStack } from './CardStack';
|
|
17
|
+
|
|
20
18
|
faker.seed(0);
|
|
21
19
|
|
|
22
20
|
type CardItem = {
|
|
@@ -96,7 +94,7 @@ const CardStackStory = () => {
|
|
|
96
94
|
|
|
97
95
|
return (
|
|
98
96
|
<CardStack.Root classNames='is-96'>
|
|
99
|
-
<CardStack.Content>
|
|
97
|
+
<CardStack.Content footer>
|
|
100
98
|
<CardStack.Stack id='story column' onRearrange={handleRearrange} itemsCount={column.length}>
|
|
101
99
|
{column.map((card, cardIndex, cardsArray) => {
|
|
102
100
|
const cardItem = { id: card.id, type: 'card' as const };
|
|
@@ -130,7 +128,7 @@ const CardStackStory = () => {
|
|
|
130
128
|
<Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
|
|
131
129
|
</Card.StaticRoot>
|
|
132
130
|
<StackItem.DragPreview>
|
|
133
|
-
{(
|
|
131
|
+
{() => (
|
|
134
132
|
<CardDragPreview.Root>
|
|
135
133
|
<CardDragPreview.Content>
|
|
136
134
|
<Card.Toolbar>
|
|
@@ -159,11 +157,14 @@ const CardStackStory = () => {
|
|
|
159
157
|
);
|
|
160
158
|
};
|
|
161
159
|
|
|
162
|
-
const meta
|
|
160
|
+
const meta = {
|
|
163
161
|
title: 'ui/react-ui-stack/CardStack',
|
|
164
162
|
component: CardStackStory,
|
|
165
|
-
decorators: [withTheme
|
|
166
|
-
|
|
163
|
+
decorators: [withTheme],
|
|
164
|
+
parameters: {
|
|
165
|
+
layout: 'fullscreen',
|
|
166
|
+
},
|
|
167
|
+
} satisfies Meta<typeof CardStackStory>;
|
|
167
168
|
|
|
168
169
|
export default meta;
|
|
169
170
|
|
|
@@ -8,15 +8,72 @@ import React, { type ComponentPropsWithoutRef, forwardRef } from 'react';
|
|
|
8
8
|
import type { ThemedClassName } from '@dxos/react-ui';
|
|
9
9
|
import { mx } from '@dxos/react-ui-theme';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { Stack, type StackProps, railGridHorizontalContainFitContent } from '../../components';
|
|
12
12
|
import { Card } from '../Card';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
//
|
|
15
|
+
// Root
|
|
16
|
+
//
|
|
17
|
+
|
|
18
|
+
const cardStackRoot = 'flex flex-col';
|
|
19
|
+
|
|
20
|
+
// TODO(burdon): Root should be headless.
|
|
21
|
+
const CardStackRoot = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
22
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
23
|
+
const Root = asChild ? Slot : 'div';
|
|
24
|
+
const rootProps = asChild
|
|
25
|
+
? { classNames: [cardStackRoot, classNames] }
|
|
26
|
+
: { className: mx(cardStackRoot, classNames), role };
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
30
|
+
{children}
|
|
31
|
+
</Root>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
//
|
|
37
|
+
// Content
|
|
38
|
+
// TODO(burdon): Rename Viewport (should be the component that scrolls).
|
|
39
|
+
//
|
|
40
|
+
|
|
41
|
+
const cardStackContent = 'shrink min-bs-0 grid dx-focus-ring-group-x-indicator bg-baseSurface';
|
|
42
|
+
|
|
43
|
+
type CardStackContentProps = SharedCardStackProps & {
|
|
44
|
+
footer?: boolean;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const CardStackContent = forwardRef<HTMLDivElement, CardStackContentProps>(
|
|
48
|
+
({ children, classNames, asChild, role = 'none', footer, ...props }, forwardedRef) => {
|
|
49
|
+
const Root = asChild ? Slot : 'div';
|
|
50
|
+
const baseClassNames = footer ? [cardStackContent, railGridHorizontalContainFitContent] : [cardStackContent];
|
|
51
|
+
const rootProps = asChild
|
|
52
|
+
? { classNames: [...baseClassNames, classNames] }
|
|
53
|
+
: { className: mx(...baseClassNames, classNames), role };
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Root {...props} {...rootProps} data-scroll-separator='false' ref={forwardedRef}>
|
|
57
|
+
{children}
|
|
58
|
+
</Root>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
//
|
|
64
|
+
// Stack
|
|
65
|
+
// TODO(burdon): Rename Content.
|
|
66
|
+
//
|
|
67
|
+
|
|
68
|
+
type SharedCardStackProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & {
|
|
69
|
+
asChild?: boolean;
|
|
70
|
+
};
|
|
15
71
|
|
|
16
72
|
const CardStackStack = forwardRef<
|
|
17
73
|
HTMLDivElement,
|
|
18
74
|
Omit<StackProps, 'orientation' | 'size' | 'rail' | 'separatorOnScroll'>
|
|
19
75
|
>(({ children, classNames, itemsCount = 0, ...props }, forwardedRef) => {
|
|
76
|
+
// NOTE: Should not have horizontal padding since separatorOnScroll should be full width.
|
|
20
77
|
return (
|
|
21
78
|
<Stack
|
|
22
79
|
orientation='vertical'
|
|
@@ -24,7 +81,7 @@ const CardStackStack = forwardRef<
|
|
|
24
81
|
rail={false}
|
|
25
82
|
classNames={
|
|
26
83
|
/* NOTE(thure): Do not let this element have zero intrinsic size, otherwise the drop indicator will not display. See #9035. */
|
|
27
|
-
['plb-
|
|
84
|
+
['plb-2', classNames]
|
|
28
85
|
}
|
|
29
86
|
itemsCount={itemsCount}
|
|
30
87
|
separatorOnScroll={9}
|
|
@@ -37,16 +94,19 @@ const CardStackStack = forwardRef<
|
|
|
37
94
|
);
|
|
38
95
|
});
|
|
39
96
|
|
|
40
|
-
|
|
97
|
+
//
|
|
98
|
+
// Item
|
|
99
|
+
//
|
|
41
100
|
|
|
42
|
-
const
|
|
101
|
+
const cardStackItem = 'contain-layout pli-2 plb-1 first-of-type:pbs-0 last-of-type:pbe-0';
|
|
43
102
|
|
|
44
|
-
const
|
|
45
|
-
({ children, classNames, asChild, role = '
|
|
103
|
+
const CardStackItem = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
104
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
46
105
|
const Root = asChild ? Slot : 'div';
|
|
47
106
|
const rootProps = asChild
|
|
48
|
-
? { classNames: [
|
|
49
|
-
: { className: mx(
|
|
107
|
+
? { classNames: [cardStackItem, classNames] }
|
|
108
|
+
: { className: mx(cardStackItem, classNames), role };
|
|
109
|
+
|
|
50
110
|
return (
|
|
51
111
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
52
112
|
{children}
|
|
@@ -55,15 +115,19 @@ const CardStackHeading = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
|
55
115
|
},
|
|
56
116
|
);
|
|
57
117
|
|
|
58
|
-
|
|
59
|
-
|
|
118
|
+
//
|
|
119
|
+
// Heading
|
|
120
|
+
//
|
|
60
121
|
|
|
61
|
-
const
|
|
62
|
-
|
|
122
|
+
const cardStackHeading = 'mli-2 order-first bg-transparent rounded-bs-md flex items-center';
|
|
123
|
+
|
|
124
|
+
const CardStackHeading = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
125
|
+
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
63
126
|
const Root = asChild ? Slot : 'div';
|
|
64
127
|
const rootProps = asChild
|
|
65
|
-
? { classNames: [
|
|
66
|
-
: { className: mx(
|
|
128
|
+
? { classNames: [cardStackHeading, classNames] }
|
|
129
|
+
: { className: mx(cardStackHeading, classNames), role };
|
|
130
|
+
|
|
67
131
|
return (
|
|
68
132
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
69
133
|
{children}
|
|
@@ -72,33 +136,20 @@ const CardStackFooter = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
|
72
136
|
},
|
|
73
137
|
);
|
|
74
138
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
];
|
|
79
|
-
|
|
80
|
-
const CardStackContent = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
81
|
-
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
82
|
-
const Root = asChild ? Slot : 'div';
|
|
83
|
-
const rootProps = asChild
|
|
84
|
-
? { classNames: [...cardStackContent, classNames] }
|
|
85
|
-
: { className: mx(...cardStackContent, classNames), role };
|
|
86
|
-
return (
|
|
87
|
-
<Root {...props} {...rootProps} data-scroll-separator='false' ref={forwardedRef}>
|
|
88
|
-
{children}
|
|
89
|
-
</Root>
|
|
90
|
-
);
|
|
91
|
-
},
|
|
92
|
-
);
|
|
139
|
+
//
|
|
140
|
+
// Footer
|
|
141
|
+
//
|
|
93
142
|
|
|
94
|
-
const
|
|
143
|
+
const cardStackFooter =
|
|
144
|
+
'plb-2 mli-2 border-bs border-transparent [[data-scroll-separator-end="true"]_&]:border-subduedSeparator';
|
|
95
145
|
|
|
96
|
-
const
|
|
146
|
+
const CardStackFooter = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
97
147
|
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
98
148
|
const Root = asChild ? Slot : 'div';
|
|
99
149
|
const rootProps = asChild
|
|
100
|
-
? { classNames: [
|
|
101
|
-
: { className: mx(
|
|
150
|
+
? { classNames: [cardStackFooter, classNames] }
|
|
151
|
+
: { className: mx(cardStackFooter, classNames), role };
|
|
152
|
+
|
|
102
153
|
return (
|
|
103
154
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
104
155
|
{children}
|
|
@@ -107,21 +158,15 @@ const CardStackRoot = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
|
107
158
|
},
|
|
108
159
|
);
|
|
109
160
|
|
|
110
|
-
|
|
161
|
+
//
|
|
162
|
+
// DragHandle
|
|
163
|
+
//
|
|
111
164
|
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
: { className: mx(cardStackItem, classNames), role };
|
|
118
|
-
return (
|
|
119
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
120
|
-
{children}
|
|
121
|
-
</Root>
|
|
122
|
-
);
|
|
123
|
-
},
|
|
124
|
-
);
|
|
165
|
+
const CardStackDragHandle = Card.DragHandle;
|
|
166
|
+
|
|
167
|
+
//
|
|
168
|
+
// CardStack
|
|
169
|
+
//
|
|
125
170
|
|
|
126
171
|
export const CardStack = {
|
|
127
172
|
Root: CardStackRoot,
|