@dxos/react-ui-stack 0.8.3-staging.0fa589b → 0.8.4-main.3a94e84
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 +1198 -0
- package/dist/lib/browser/chunk-P3TQV4BA.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +40 -1160
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/{node/testing/index.cjs → browser/playwright/index.mjs} +4 -28
- package/dist/lib/{node/testing/index.cjs.map → browser/playwright/index.mjs.map} +2 -2
- package/dist/lib/browser/testing/index.mjs +25 -51
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/chunk-3WVEPAJ4.mjs +1200 -0
- package/dist/lib/node-esm/chunk-3WVEPAJ4.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +40 -1161
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/playwright/index.mjs +59 -0
- package/dist/lib/node-esm/playwright/index.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +24 -51
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/components/Stack/Stack.stories.d.ts +1 -1
- package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.d.ts +1 -1
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts +1 -1
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemContent.d.ts +2 -2
- package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +6 -10
- package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.stories.d.ts +13 -0
- package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +1 -0
- package/dist/types/src/exemplars/Card/fragments.d.ts +1 -2
- package/dist/types/src/exemplars/Card/fragments.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts +7 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +9 -0
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +1 -0
- package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/playwright/index.d.ts +2 -0
- package/dist/types/src/playwright/index.d.ts.map +1 -0
- package/dist/types/src/playwright/playwright.config.d.ts +3 -0
- package/dist/types/src/playwright/playwright.config.d.ts.map +1 -0
- package/dist/types/src/playwright/stack-manager.d.ts.map +1 -0
- package/dist/types/src/testing/CardContainer.d.ts +6 -0
- package/dist/types/src/testing/CardContainer.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +1 -1
- package/dist/types/src/translations.d.ts +13 -14
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -25
- package/src/components/Stack/Stack.stories.tsx +1 -1
- package/src/components/StackItem/StackItem.stories.tsx +1 -1
- package/src/components/StackItem/StackItemContent.tsx +3 -3
- package/src/components/index.ts +1 -1
- package/src/exemplars/Card/Card.stories.tsx +78 -0
- package/src/exemplars/Card/Card.tsx +33 -29
- package/src/exemplars/Card/CardDragPreview.tsx +2 -2
- package/src/exemplars/Card/fragments.ts +1 -3
- package/src/exemplars/CardStack/CardStack.stories.tsx +172 -0
- package/src/exemplars/CardStack/CardStack.tsx +18 -1
- package/src/hooks/useStackDropForElements.ts +4 -1
- package/src/index.ts +3 -4
- package/src/playwright/index.ts +5 -0
- package/src/playwright/playwright.config.ts +17 -0
- package/src/playwright/smoke.spec.ts +7 -5
- package/src/testing/CardContainer.tsx +34 -0
- package/src/testing/index.ts +1 -1
- package/src/translations.ts +5 -3
- package/dist/lib/node/index.cjs +0 -1220
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts +0 -1
- package/dist/types/src/exemplars/Card/Card.stories-todo.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts +0 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories-todo.d.ts.map +0 -1
- package/dist/types/src/testing/stack-manager.d.ts.map +0 -1
- package/src/exemplars/Card/Card.stories-todo.tsx +0 -135
- package/src/exemplars/CardStack/CardStack.stories-todo.tsx +0 -80
- package/src/playwright/playwright.config.cts +0 -18
- /package/dist/types/src/{testing → playwright}/stack-manager.d.ts +0 -0
- /package/src/{testing → playwright}/stack-manager.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-stack",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4-main.3a94e84",
|
|
4
4
|
"description": "A stack component.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -14,18 +14,23 @@
|
|
|
14
14
|
"browser": "./dist/lib/browser/index.mjs",
|
|
15
15
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
16
16
|
},
|
|
17
|
+
"./playwright": {
|
|
18
|
+
"types": "./dist/types/src/playwright/index.d.ts",
|
|
19
|
+
"browser": "./dist/lib/browser/playwright/index.mjs",
|
|
20
|
+
"node": "./dist/lib/node-esm/playwright/index.mjs"
|
|
21
|
+
},
|
|
17
22
|
"./testing": {
|
|
18
23
|
"types": "./dist/types/src/testing/index.d.ts",
|
|
19
24
|
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
20
|
-
"node":
|
|
21
|
-
"require": "./dist/lib/node/testing/index.cjs",
|
|
22
|
-
"default": "./dist/lib/node-esm/testing/index.mjs"
|
|
23
|
-
}
|
|
25
|
+
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
24
26
|
}
|
|
25
27
|
},
|
|
26
28
|
"types": "dist/types/src/index.d.ts",
|
|
27
29
|
"typesVersions": {
|
|
28
30
|
"*": {
|
|
31
|
+
"playwright": [
|
|
32
|
+
"dist/types/src/playwright/index.d.ts"
|
|
33
|
+
],
|
|
29
34
|
"testing": [
|
|
30
35
|
"dist/types/src/testing/index.d.ts"
|
|
31
36
|
]
|
|
@@ -49,37 +54,36 @@
|
|
|
49
54
|
"@radix-ui/react-slot": "1.1.2",
|
|
50
55
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
51
56
|
"react-resize-detector": "^11.0.1",
|
|
52
|
-
"@dxos/echo-schema": "0.8.
|
|
53
|
-
"@dxos/keyboard": "0.8.
|
|
54
|
-
"@dxos/react-ui-attention": "0.8.
|
|
55
|
-
"@dxos/react-ui-dnd": "0.8.
|
|
56
|
-
"@dxos/
|
|
57
|
-
"@dxos/live-object": "0.8.
|
|
57
|
+
"@dxos/echo-schema": "0.8.4-main.3a94e84",
|
|
58
|
+
"@dxos/keyboard": "0.8.4-main.3a94e84",
|
|
59
|
+
"@dxos/react-ui-attention": "0.8.4-main.3a94e84",
|
|
60
|
+
"@dxos/react-ui-dnd": "0.8.4-main.3a94e84",
|
|
61
|
+
"@dxos/storybook-utils": "0.8.4-main.3a94e84",
|
|
62
|
+
"@dxos/live-object": "0.8.4-main.3a94e84",
|
|
63
|
+
"@dxos/util": "0.8.4-main.3a94e84"
|
|
58
64
|
},
|
|
59
65
|
"devDependencies": {
|
|
60
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
61
66
|
"@types/react": "~18.2.0",
|
|
62
67
|
"@types/react-dom": "~18.2.0",
|
|
63
68
|
"react": "~18.2.0",
|
|
64
69
|
"react-dom": "~18.2.0",
|
|
65
70
|
"vite": "5.4.7",
|
|
66
|
-
"@dxos/app-graph": "0.8.
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/
|
|
71
|
+
"@dxos/app-graph": "0.8.4-main.3a94e84",
|
|
72
|
+
"@dxos/echo-schema": "0.8.4-main.3a94e84",
|
|
73
|
+
"@dxos/random": "0.8.4-main.3a94e84",
|
|
74
|
+
"@dxos/client": "0.8.4-main.3a94e84",
|
|
75
|
+
"@dxos/react-ui": "0.8.4-main.3a94e84",
|
|
76
|
+
"@dxos/react-ui-theme": "0.8.4-main.3a94e84",
|
|
77
|
+
"@dxos/storybook-utils": "0.8.4-main.3a94e84",
|
|
78
|
+
"@dxos/test-utils": "0.8.4-main.3a94e84"
|
|
74
79
|
},
|
|
75
80
|
"peerDependencies": {
|
|
76
|
-
"@phosphor-icons/react": "^2.1.5",
|
|
77
81
|
"react": "~18.2.0",
|
|
78
82
|
"react-dom": "~18.2.0",
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/react-ui-theme": "0.8.
|
|
82
|
-
"@dxos/
|
|
83
|
+
"@dxos/client": "0.8.4-main.3a94e84",
|
|
84
|
+
"@dxos/react-ui": "0.8.4-main.3a94e84",
|
|
85
|
+
"@dxos/react-ui-theme": "0.8.4-main.3a94e84",
|
|
86
|
+
"@dxos/random": "0.8.4-main.3a94e84"
|
|
83
87
|
},
|
|
84
88
|
"publishConfig": {
|
|
85
89
|
"access": "public"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import '@dxos-theme';
|
|
6
6
|
|
|
7
7
|
import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
8
|
-
import { type Meta, type StoryObj } from '@storybook/react';
|
|
8
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
9
9
|
import React, { useState, useCallback } from 'react';
|
|
10
10
|
|
|
11
11
|
import { faker } from '@dxos/random';
|
|
@@ -9,7 +9,7 @@ import { mx } from '@dxos/react-ui-theme';
|
|
|
9
9
|
|
|
10
10
|
import { useStack, useStackItem } from '../StackContext';
|
|
11
11
|
|
|
12
|
-
export type StackItemContentProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & {
|
|
12
|
+
export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'role'>> & {
|
|
13
13
|
/**
|
|
14
14
|
* This flag is required in order to clarify a developer experience that seemed like it needed extra boilerplate
|
|
15
15
|
* (`row-span-2`) or was buggy. See the description of the StackItem.Content component itself for more information.
|
|
@@ -59,10 +59,10 @@ export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps
|
|
|
59
59
|
role='none'
|
|
60
60
|
{...props}
|
|
61
61
|
className={mx(
|
|
62
|
-
'group grid grid-cols-[100%]',
|
|
62
|
+
'group grid grid-cols-[100%] density-coarse',
|
|
63
63
|
stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
|
|
64
64
|
size === 'video' ? 'aspect-video' : size === 'square' && 'aspect-square',
|
|
65
|
-
toolbar && '[
|
|
65
|
+
toolbar && '[&>.dx-toolbar]:relative [&>.dx-toolbar]:border-be [&>.dx-toolbar]:border-subduedSeparator',
|
|
66
66
|
role === 'section' &&
|
|
67
67
|
toolbar &&
|
|
68
68
|
'[&_.dx-toolbar]:sticky [&_.dx-toolbar]:z-[1] [&_.dx-toolbar]:block-start-0 [&_.dx-toolbar]:-mbe-px [&_.dx-toolbar]:min-is-0',
|
package/src/components/index.ts
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
import { faker } from '@dxos/random';
|
|
11
|
+
import { withTheme } from '@dxos/storybook-utils';
|
|
12
|
+
|
|
13
|
+
import { Card } from './Card';
|
|
14
|
+
|
|
15
|
+
// Set a seed for reproducible random values
|
|
16
|
+
faker.seed(0);
|
|
17
|
+
|
|
18
|
+
type CardStoryProps = {
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
image: string;
|
|
22
|
+
showImage: boolean;
|
|
23
|
+
showIcon: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const meta: Meta<CardStoryProps> = {
|
|
27
|
+
title: 'ui/react-ui-stack/Card',
|
|
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) => (
|
|
64
|
+
<div className='max-is-md'>
|
|
65
|
+
<Card.StaticRoot>
|
|
66
|
+
<Card.Toolbar>
|
|
67
|
+
<Card.DragHandle toolbarItem />
|
|
68
|
+
<Card.ToolbarSeparator variant='gap' />
|
|
69
|
+
<Card.ToolbarIconButton iconOnly variant='ghost' icon='ph--x--regular' label={'remove card label'} />
|
|
70
|
+
</Card.Toolbar>
|
|
71
|
+
{showImage && <Card.Poster alt={title} image={image} />}
|
|
72
|
+
{!showImage && showIcon && <Card.Poster alt={title} icon='ph--building-office--regular' />}
|
|
73
|
+
<Card.Heading>{title}</Card.Heading>
|
|
74
|
+
{description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
|
|
75
|
+
</Card.StaticRoot>
|
|
76
|
+
</div>
|
|
77
|
+
),
|
|
78
|
+
};
|
|
@@ -15,30 +15,16 @@ import React, {
|
|
|
15
15
|
import { Icon, IconButton, type ThemedClassName, Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
|
|
16
16
|
import { hoverableControls, mx } from '@dxos/react-ui-theme';
|
|
17
17
|
|
|
18
|
-
import { cardChrome,
|
|
18
|
+
import { cardChrome, cardRoot, cardHeading, cardText, cardSpacing } from './fragments';
|
|
19
19
|
import { StackItem } from '../../components';
|
|
20
20
|
import { translationKey } from '../../translations';
|
|
21
21
|
|
|
22
22
|
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
23
23
|
|
|
24
|
-
const
|
|
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>(
|
|
24
|
+
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
37
25
|
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
38
26
|
const Root = asChild ? Slot : 'div';
|
|
39
|
-
const rootProps = asChild
|
|
40
|
-
? { classNames: [cardContent, classNames] }
|
|
41
|
-
: { className: mx(cardContent, classNames), role };
|
|
27
|
+
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
42
28
|
return (
|
|
43
29
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
44
30
|
{children}
|
|
@@ -49,21 +35,40 @@ const CardContent = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
49
35
|
|
|
50
36
|
/**
|
|
51
37
|
* 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.
|
|
38
|
+
* in a Popover) and knows this based on the `role` it receives. This will render a `Card.StaticRoot` by default, otherwise
|
|
53
39
|
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
54
40
|
*/
|
|
55
|
-
const
|
|
56
|
-
|
|
41
|
+
const CardSurfaceRoot = ({
|
|
42
|
+
role = 'never',
|
|
43
|
+
children,
|
|
44
|
+
classNames,
|
|
45
|
+
}: ThemedClassName<PropsWithChildren<{ role?: string }>>) => {
|
|
46
|
+
if (['card--popover', 'card--intrinsic', 'card--extrinsic'].includes(role)) {
|
|
57
47
|
return (
|
|
58
|
-
<div
|
|
48
|
+
<div
|
|
49
|
+
className={mx(
|
|
50
|
+
role === 'card--popover'
|
|
51
|
+
? 'popover-card-width'
|
|
52
|
+
: ['card--intrinsic', 'card--extrinsic'].includes(role)
|
|
53
|
+
? 'contents'
|
|
54
|
+
: '',
|
|
55
|
+
classNames,
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
59
58
|
{children}
|
|
60
59
|
</div>
|
|
61
60
|
);
|
|
62
61
|
} else {
|
|
63
62
|
return (
|
|
64
|
-
<
|
|
63
|
+
<CardStaticRoot
|
|
64
|
+
classNames={[
|
|
65
|
+
role === 'card--transclusion' && 'mlb-[1em]',
|
|
66
|
+
role === 'card--transclusion' && hoverableControls,
|
|
67
|
+
classNames,
|
|
68
|
+
]}
|
|
69
|
+
>
|
|
65
70
|
{children}
|
|
66
|
-
</
|
|
71
|
+
</CardStaticRoot>
|
|
67
72
|
);
|
|
68
73
|
}
|
|
69
74
|
};
|
|
@@ -84,7 +89,7 @@ const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
84
89
|
|
|
85
90
|
const CardToolbar = forwardRef<HTMLDivElement, ToolbarRootProps>(({ children, classNames, ...props }, forwardedRef) => {
|
|
86
91
|
return (
|
|
87
|
-
<Toolbar.Root {...props} classNames={['bg-transparent', classNames]} ref={forwardedRef}>
|
|
92
|
+
<Toolbar.Root {...props} classNames={['bg-transparent density-coarse', classNames]} ref={forwardedRef}>
|
|
88
93
|
{children}
|
|
89
94
|
</Toolbar.Root>
|
|
90
95
|
);
|
|
@@ -101,7 +106,7 @@ const CardDragHandle = forwardRef<HTMLButtonElement, { toolbarItem?: boolean }>(
|
|
|
101
106
|
iconOnly
|
|
102
107
|
icon='ph--dots-six-vertical--regular'
|
|
103
108
|
variant='ghost'
|
|
104
|
-
label={t('
|
|
109
|
+
label={t('drag handle label')}
|
|
105
110
|
classNames='pli-2'
|
|
106
111
|
ref={forwardedRef}
|
|
107
112
|
/>
|
|
@@ -164,9 +169,8 @@ const CardText = forwardRef<HTMLParagraphElement, SharedCardProps>(
|
|
|
164
169
|
);
|
|
165
170
|
|
|
166
171
|
export const Card = {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
Container: CardConditionalContent,
|
|
172
|
+
StaticRoot: CardStaticRoot,
|
|
173
|
+
SurfaceRoot: CardSurfaceRoot,
|
|
170
174
|
Heading: CardHeading,
|
|
171
175
|
Toolbar: CardToolbar,
|
|
172
176
|
ToolbarIconButton: CardToolbarIconButton,
|
|
@@ -179,4 +183,4 @@ export const Card = {
|
|
|
179
183
|
Text: CardText,
|
|
180
184
|
};
|
|
181
185
|
|
|
182
|
-
export { cardRoot,
|
|
186
|
+
export { cardRoot, cardHeading, cardText, cardChrome, cardSpacing };
|
|
@@ -6,14 +6,14 @@ import React, { type PropsWithChildren } from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { mx } from '@dxos/react-ui-theme';
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { cardRoot } from './fragments';
|
|
10
10
|
|
|
11
11
|
const CardDragPreviewRoot = ({ children }: PropsWithChildren<{}>) => {
|
|
12
12
|
return <div className='p-2'>{children}</div>;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const CardDragPreviewContent = ({ children }: PropsWithChildren<{}>) => {
|
|
16
|
-
return <div className={mx(
|
|
16
|
+
return <div className={mx(cardRoot, 'ring-focusLine ring-neutralFocusIndicator')}>{children}</div>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
export const CardDragPreview = {
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export const cardRoot =
|
|
6
|
-
|
|
7
|
-
export const cardContent =
|
|
5
|
+
export const cardRoot =
|
|
8
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';
|
|
9
7
|
|
|
10
8
|
export const cardSpacing = 'pli-cardSpacingInline mlb-cardSpacingBlock';
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import '@dxos-theme';
|
|
6
|
+
|
|
7
|
+
import { type Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
8
|
+
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
9
|
+
import React, { useState, useCallback } from 'react';
|
|
10
|
+
|
|
11
|
+
import { faker } from '@dxos/random';
|
|
12
|
+
import { IconButton } from '@dxos/react-ui';
|
|
13
|
+
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
14
|
+
|
|
15
|
+
import { CardStack } from './CardStack';
|
|
16
|
+
import { StackItem } from '../../components';
|
|
17
|
+
import { Card, CardDragPreview } from '../Card';
|
|
18
|
+
|
|
19
|
+
// Set a seed for reproducible random values
|
|
20
|
+
faker.seed(0);
|
|
21
|
+
|
|
22
|
+
type CardItem = {
|
|
23
|
+
id: string;
|
|
24
|
+
title: string;
|
|
25
|
+
description: string;
|
|
26
|
+
image: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type StackItemData = {
|
|
30
|
+
id: string;
|
|
31
|
+
type?: 'column' | 'card';
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const CardStackStory = () => {
|
|
35
|
+
const [column, setColumn] = useState<CardItem[]>(
|
|
36
|
+
faker.helpers.multiple(
|
|
37
|
+
() => ({
|
|
38
|
+
id: faker.string.uuid(),
|
|
39
|
+
title: faker.commerce.productName(),
|
|
40
|
+
description: faker.lorem.paragraph(),
|
|
41
|
+
image: faker.image.url(),
|
|
42
|
+
}),
|
|
43
|
+
{ count: 12 },
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const handleRearrange = useCallback((source: StackItemData, target: StackItemData, closestEdge: Edge | null) => {
|
|
48
|
+
setColumn((prevColumn) => {
|
|
49
|
+
const newColumns = [...prevColumn];
|
|
50
|
+
// Reordering cards within a column
|
|
51
|
+
const sourceCardIndex = prevColumn.findIndex((card) => card.id === source.id);
|
|
52
|
+
const targetCardIndex = prevColumn.findIndex((card) => card.id === target.id);
|
|
53
|
+
|
|
54
|
+
if (typeof sourceCardIndex === 'number' && typeof targetCardIndex === 'number') {
|
|
55
|
+
const [movedCard] = newColumns.splice(sourceCardIndex, 1);
|
|
56
|
+
|
|
57
|
+
let insertIndex;
|
|
58
|
+
if (sourceCardIndex < targetCardIndex) {
|
|
59
|
+
insertIndex = closestEdge === 'bottom' ? targetCardIndex : targetCardIndex - 1;
|
|
60
|
+
} else {
|
|
61
|
+
insertIndex = closestEdge === 'bottom' ? targetCardIndex + 1 : targetCardIndex;
|
|
62
|
+
}
|
|
63
|
+
newColumns.splice(insertIndex, 0, movedCard);
|
|
64
|
+
}
|
|
65
|
+
return newColumns;
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
const handleAddCard = useCallback(() => {
|
|
70
|
+
setColumn((prevColumn) => {
|
|
71
|
+
const newColumn = [...prevColumn];
|
|
72
|
+
const newCard = {
|
|
73
|
+
id: faker.string.uuid(),
|
|
74
|
+
title: faker.commerce.productName(),
|
|
75
|
+
description: faker.lorem.paragraph(),
|
|
76
|
+
image: faker.image.url(),
|
|
77
|
+
} satisfies CardItem;
|
|
78
|
+
newColumn.push(newCard);
|
|
79
|
+
console.log('[add card]', prevColumn.length, newColumn.length);
|
|
80
|
+
return newColumn;
|
|
81
|
+
});
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
const handleRemoveCard = useCallback((cardId: string) => {
|
|
85
|
+
setColumn((prevColumn) => {
|
|
86
|
+
const newColumn = [...prevColumn];
|
|
87
|
+
|
|
88
|
+
const cardIndex = prevColumn.findIndex((card) => card.id === cardId);
|
|
89
|
+
if (cardIndex !== -1) {
|
|
90
|
+
newColumn.splice(cardIndex, 1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return newColumn;
|
|
94
|
+
});
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<CardStack.Root classNames='is-96'>
|
|
99
|
+
<CardStack.Content>
|
|
100
|
+
<CardStack.Stack id='story column' onRearrange={handleRearrange} itemsCount={column.length}>
|
|
101
|
+
{column.map((card, cardIndex, cardsArray) => {
|
|
102
|
+
const cardItem = { id: card.id, type: 'card' as const };
|
|
103
|
+
const prevCardId = cardIndex > 0 ? cardsArray[cardIndex - 1].id : undefined;
|
|
104
|
+
const nextCardId = cardIndex < cardsArray.length - 1 ? cardsArray[cardIndex + 1].id : undefined;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<CardStack.Item asChild key={card.id}>
|
|
108
|
+
<StackItem.Root
|
|
109
|
+
item={cardItem}
|
|
110
|
+
focusIndicatorVariant='group'
|
|
111
|
+
prevSiblingId={prevCardId}
|
|
112
|
+
nextSiblingId={nextCardId}
|
|
113
|
+
>
|
|
114
|
+
<Card.StaticRoot>
|
|
115
|
+
<Card.Toolbar>
|
|
116
|
+
<StackItem.DragHandle asChild>
|
|
117
|
+
<Card.DragHandle toolbarItem />
|
|
118
|
+
</StackItem.DragHandle>
|
|
119
|
+
<Card.ToolbarSeparator variant='gap' />
|
|
120
|
+
<Card.ToolbarIconButton
|
|
121
|
+
iconOnly
|
|
122
|
+
variant='ghost'
|
|
123
|
+
icon='ph--x--regular'
|
|
124
|
+
label='Remove card'
|
|
125
|
+
onClick={() => handleRemoveCard(card.id)}
|
|
126
|
+
/>
|
|
127
|
+
</Card.Toolbar>
|
|
128
|
+
<Card.Poster alt={card.title} image={card.image} />
|
|
129
|
+
<Card.Heading>{card.title}</Card.Heading>
|
|
130
|
+
<Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
|
|
131
|
+
</Card.StaticRoot>
|
|
132
|
+
<StackItem.DragPreview>
|
|
133
|
+
{({ item }) => (
|
|
134
|
+
<CardDragPreview.Root>
|
|
135
|
+
<CardDragPreview.Content>
|
|
136
|
+
<Card.Toolbar>
|
|
137
|
+
<Card.DragHandle toolbarItem />
|
|
138
|
+
</Card.Toolbar>
|
|
139
|
+
<Card.Poster alt={card.title} image={card.image} />
|
|
140
|
+
<Card.Heading>{card.title}</Card.Heading>
|
|
141
|
+
<Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
|
|
142
|
+
</CardDragPreview.Content>
|
|
143
|
+
</CardDragPreview.Root>
|
|
144
|
+
)}
|
|
145
|
+
</StackItem.DragPreview>
|
|
146
|
+
</StackItem.Root>
|
|
147
|
+
</CardStack.Item>
|
|
148
|
+
);
|
|
149
|
+
})}
|
|
150
|
+
</CardStack.Stack>
|
|
151
|
+
|
|
152
|
+
<CardStack.Footer>
|
|
153
|
+
<IconButton icon='ph--plus--regular' label='Add card' onClick={handleAddCard} classNames='is-full' />
|
|
154
|
+
</CardStack.Footer>
|
|
155
|
+
|
|
156
|
+
<CardStack.Heading>{faker.company.name()}</CardStack.Heading>
|
|
157
|
+
</CardStack.Content>
|
|
158
|
+
</CardStack.Root>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const meta: Meta<typeof CardStackStory> = {
|
|
163
|
+
title: 'ui/react-ui-stack/CardStack',
|
|
164
|
+
component: CardStackStory,
|
|
165
|
+
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export default meta;
|
|
169
|
+
|
|
170
|
+
type Story = StoryObj<typeof CardStackStory>;
|
|
171
|
+
|
|
172
|
+
export const Default: Story = {};
|
|
@@ -107,6 +107,22 @@ const CardStackRoot = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
|
107
107
|
},
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
+
const cardStackItem = 'contain-layout pli-2 plb-1 first-of-type:pbs-0 last-of-type:pbe-0';
|
|
111
|
+
|
|
112
|
+
const CardStackItem = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
113
|
+
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
114
|
+
const Root = asChild ? Slot : 'div';
|
|
115
|
+
const rootProps = asChild
|
|
116
|
+
? { classNames: [cardStackItem, classNames] }
|
|
117
|
+
: { className: mx(cardStackItem, classNames), role };
|
|
118
|
+
return (
|
|
119
|
+
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
120
|
+
{children}
|
|
121
|
+
</Root>
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
110
126
|
export const CardStack = {
|
|
111
127
|
Root: CardStackRoot,
|
|
112
128
|
Content: CardStackContent,
|
|
@@ -114,6 +130,7 @@ export const CardStack = {
|
|
|
114
130
|
Heading: CardStackHeading,
|
|
115
131
|
Footer: CardStackFooter,
|
|
116
132
|
DragHandle: CardStackDragHandle,
|
|
133
|
+
Item: CardStackItem,
|
|
117
134
|
};
|
|
118
135
|
|
|
119
|
-
export { cardStackRoot, cardStackFooter, cardStackHeading, cardStackContent };
|
|
136
|
+
export { cardStackRoot, cardStackFooter, cardStackHeading, cardStackContent, cardStackItem };
|
|
@@ -66,7 +66,10 @@ export const useStackDropForElements = ({
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
}),
|
|
69
|
-
autoScrollForElements({
|
|
69
|
+
autoScrollForElements({
|
|
70
|
+
element: scrollElement as Element,
|
|
71
|
+
getAllowedAxis: () => orientation,
|
|
72
|
+
}),
|
|
70
73
|
);
|
|
71
74
|
}, [element, scrollElement, selfDroppable, orientation, id, onRearrange]);
|
|
72
75
|
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './components';
|
|
6
|
-
|
|
7
5
|
// TODO(thure): Consider exporting exemplars from separate endpoints.
|
|
8
|
-
export * from './exemplars';
|
|
9
6
|
|
|
10
|
-
export
|
|
7
|
+
export * from './components';
|
|
8
|
+
export * from './exemplars';
|
|
9
|
+
export * from './translations';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { defineConfig } from '@playwright/test';
|
|
6
|
+
|
|
7
|
+
import { e2ePreset } from '@dxos/test-utils/playwright';
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
...e2ePreset(import.meta.dirname),
|
|
11
|
+
// TODO(wittjosiah): Avoid hard-coding ports.
|
|
12
|
+
webServer: {
|
|
13
|
+
command: 'moon run storybook:serve-e2e -- --port=9003',
|
|
14
|
+
port: 9003,
|
|
15
|
+
reuseExistingServer: false,
|
|
16
|
+
},
|
|
17
|
+
});
|