@dxos/react-ui-stack 0.8.4-main.f5c0578 → 0.8.4-main.fcfe5033a5
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 +723 -62
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +724 -62
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/components/Stack/Stack.d.ts +13 -10
- 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/MenuSignifier.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.d.ts +12 -15
- 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 +4 -37
- package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemDragHandle.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 +1 -1
- package/dist/types/src/components/StackItem/StackItemResizeHandle.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemSigil.d.ts +2 -2
- package/dist/types/src/components/StackItem/StackItemSigil.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/components/{defs.d.ts → types.d.ts} +1 -1
- package/dist/types/src/components/types.d.ts.map +1 -0
- package/dist/types/src/hooks/useStackDropForElements.d.ts +8 -6
- package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +10 -10
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +41 -38
- package/src/components/Stack/Stack.stories.tsx +13 -17
- package/src/components/Stack/Stack.tsx +238 -52
- package/src/components/StackContext.tsx +2 -1
- package/src/components/StackItem/MenuSignifier.tsx +2 -9
- package/src/components/StackItem/StackItem.stories.tsx +21 -17
- package/src/components/StackItem/StackItem.tsx +51 -34
- package/src/components/StackItem/StackItemContent.tsx +24 -44
- package/src/components/StackItem/StackItemDragHandle.tsx +4 -3
- package/src/components/StackItem/StackItemHeading.tsx +14 -17
- package/src/components/StackItem/StackItemResizeHandle.tsx +1 -2
- package/src/components/StackItem/StackItemSigil.tsx +9 -7
- package/src/components/index.ts +2 -1
- package/src/hooks/useStackDropForElements.ts +60 -46
- package/src/index.ts +0 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/translations.ts +9 -9
- package/dist/lib/browser/chunk-WOG2GQRG.mjs +0 -1200
- package/dist/lib/browser/chunk-WOG2GQRG.mjs.map +0 -7
- package/dist/lib/browser/testing/index.mjs +0 -31
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-PO2QGNXW.mjs +0 -1202
- package/dist/lib/node-esm/chunk-PO2QGNXW.mjs.map +0 -7
- package/dist/lib/node-esm/testing/index.mjs +0 -32
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/components/defs.d.ts.map +0 -1
- package/dist/types/src/components/deprecated/LayoutControls.d.ts +0 -19
- package/dist/types/src/components/deprecated/LayoutControls.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +0 -58
- package/dist/types/src/exemplars/Card/Card.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/Card.stories.d.ts +0 -13
- package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/CardDragPreview.d.ts +0 -6
- package/dist/types/src/exemplars/Card/CardDragPreview.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/fragments.d.ts +0 -13
- package/dist/types/src/exemplars/Card/fragments.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/index.d.ts +0 -4
- package/dist/types/src/exemplars/Card/index.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts +0 -40
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +0 -9
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts +0 -9
- package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/index.d.ts +0 -3
- package/dist/types/src/exemplars/CardStack/index.d.ts.map +0 -1
- package/dist/types/src/exemplars/index.d.ts +0 -3
- package/dist/types/src/exemplars/index.d.ts.map +0 -1
- package/dist/types/src/testing/CardContainer.d.ts +0 -6
- package/dist/types/src/testing/CardContainer.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/src/components/deprecated/LayoutControls.tsx +0 -109
- package/src/exemplars/Card/Card.stories.tsx +0 -78
- package/src/exemplars/Card/Card.tsx +0 -187
- package/src/exemplars/Card/CardDragPreview.tsx +0 -22
- package/src/exemplars/Card/fragments.ts +0 -24
- package/src/exemplars/Card/index.ts +0 -7
- package/src/exemplars/CardStack/CardStack.stories.tsx +0 -173
- package/src/exemplars/CardStack/CardStack.tsx +0 -136
- package/src/exemplars/CardStack/CardStackDragPreview.tsx +0 -61
- package/src/exemplars/CardStack/index.ts +0 -6
- package/src/exemplars/index.ts +0 -6
- package/src/testing/CardContainer.tsx +0 -37
- package/src/testing/index.ts +0 -5
- /package/src/components/{defs.ts → types.ts} +0 -0
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { forwardRef } from 'react';
|
|
6
|
-
|
|
7
|
-
import { ButtonGroup, type ButtonGroupProps, type ButtonProps, IconButton, useTranslation } from '@dxos/react-ui';
|
|
8
|
-
|
|
9
|
-
import { translationKey } from '../../translations';
|
|
10
|
-
|
|
11
|
-
export type LayoutControlEvent = 'solo' | 'close' | `${'pin' | 'increment'}-${'start' | 'end'}`;
|
|
12
|
-
export type LayoutControlHandler = (event: LayoutControlEvent) => void;
|
|
13
|
-
|
|
14
|
-
export type LayoutCapabilities = {
|
|
15
|
-
incrementStart?: boolean;
|
|
16
|
-
incrementEnd?: boolean;
|
|
17
|
-
solo?: boolean;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type LayoutControlsProps = Omit<ButtonGroupProps, 'onClick'> & {
|
|
21
|
-
onClick?: LayoutControlHandler;
|
|
22
|
-
variant?: 'hide-disabled' | 'default';
|
|
23
|
-
close?: boolean | 'minify-start' | 'minify-end';
|
|
24
|
-
capabilities: LayoutCapabilities;
|
|
25
|
-
isSolo?: boolean;
|
|
26
|
-
pin?: 'start' | 'end' | 'both';
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const LayoutControl = ({ icon, label, ...props }: Omit<ButtonProps, 'children'> & { label: string; icon: string }) => {
|
|
30
|
-
return <IconButton iconOnly icon={icon} label={label} tooltipSide='bottom' variant='ghost' {...props} />;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const LayoutControls = forwardRef<HTMLDivElement, LayoutControlsProps>(
|
|
34
|
-
(
|
|
35
|
-
{ onClick, variant = 'default', capabilities: can, isSolo, pin, close = false, children, ...props },
|
|
36
|
-
forwardedRef,
|
|
37
|
-
) => {
|
|
38
|
-
const { t } = useTranslation(translationKey);
|
|
39
|
-
const buttonClassNames = variant === 'hide-disabled' ? 'disabled:hidden !p-1' : '!p-1';
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<ButtonGroup {...props} ref={forwardedRef}>
|
|
43
|
-
{pin && !isSolo && ['both', 'start'].includes(pin) && (
|
|
44
|
-
<LayoutControl
|
|
45
|
-
label={t('pin start label')}
|
|
46
|
-
variant='ghost'
|
|
47
|
-
classNames={buttonClassNames}
|
|
48
|
-
onClick={() => onClick?.('pin-start')}
|
|
49
|
-
icon='ph--caret-line-left--regular'
|
|
50
|
-
/>
|
|
51
|
-
)}
|
|
52
|
-
|
|
53
|
-
{can.solo && (
|
|
54
|
-
<LayoutControl
|
|
55
|
-
label={t('solo layout label')}
|
|
56
|
-
classNames={buttonClassNames}
|
|
57
|
-
onClick={() => onClick?.('solo')}
|
|
58
|
-
icon={isSolo ? 'ph--arrows-in--regular' : 'ph--arrows-out--regular'}
|
|
59
|
-
/>
|
|
60
|
-
)}
|
|
61
|
-
|
|
62
|
-
{!isSolo && can.solo && (
|
|
63
|
-
<>
|
|
64
|
-
<LayoutControl
|
|
65
|
-
label={t('increment start label')}
|
|
66
|
-
disabled={!can.incrementStart}
|
|
67
|
-
classNames={buttonClassNames}
|
|
68
|
-
onClick={() => onClick?.('increment-start')}
|
|
69
|
-
icon='ph--caret-left--regular'
|
|
70
|
-
/>
|
|
71
|
-
<LayoutControl
|
|
72
|
-
label={t('increment end label')}
|
|
73
|
-
disabled={!can.incrementEnd}
|
|
74
|
-
classNames={buttonClassNames}
|
|
75
|
-
onClick={() => onClick?.('increment-end')}
|
|
76
|
-
icon='ph--caret-right--regular'
|
|
77
|
-
/>
|
|
78
|
-
</>
|
|
79
|
-
)}
|
|
80
|
-
|
|
81
|
-
{pin && !isSolo && ['both', 'end'].includes(pin) && (
|
|
82
|
-
<LayoutControl
|
|
83
|
-
label={t('pin end label')}
|
|
84
|
-
classNames={buttonClassNames}
|
|
85
|
-
onClick={() => onClick?.('pin-end')}
|
|
86
|
-
icon='ph--caret-line-right--regular'
|
|
87
|
-
/>
|
|
88
|
-
)}
|
|
89
|
-
|
|
90
|
-
{close && !isSolo && (
|
|
91
|
-
<LayoutControl
|
|
92
|
-
label={t(`${typeof close === 'string' ? 'minify' : 'close'} label`)}
|
|
93
|
-
classNames={buttonClassNames}
|
|
94
|
-
onClick={() => onClick?.('close')}
|
|
95
|
-
data-testid='layoutHeading.close'
|
|
96
|
-
icon={
|
|
97
|
-
close === 'minify-start'
|
|
98
|
-
? 'ph--caret-line-left--regular'
|
|
99
|
-
: close === 'minify-end'
|
|
100
|
-
? 'ph--caret-line-right--regular'
|
|
101
|
-
: 'ph--x--regular'
|
|
102
|
-
}
|
|
103
|
-
/>
|
|
104
|
-
)}
|
|
105
|
-
{children}
|
|
106
|
-
</ButtonGroup>
|
|
107
|
-
);
|
|
108
|
-
},
|
|
109
|
-
);
|
|
@@ -1,78 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,187 +0,0 @@
|
|
|
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 ComponentPropsWithRef,
|
|
9
|
-
type ComponentPropsWithoutRef,
|
|
10
|
-
type FC,
|
|
11
|
-
type PropsWithChildren,
|
|
12
|
-
forwardRef,
|
|
13
|
-
} from 'react';
|
|
14
|
-
|
|
15
|
-
import { Icon, IconButton, type ThemedClassName, Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
|
|
16
|
-
import { hoverableControls, mx } from '@dxos/react-ui-theme';
|
|
17
|
-
|
|
18
|
-
import { StackItem } from '../../components';
|
|
19
|
-
import { translationKey } from '../../translations';
|
|
20
|
-
|
|
21
|
-
import { cardChrome, cardHeading, cardRoot, cardSpacing, cardText } from './fragments';
|
|
22
|
-
|
|
23
|
-
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
24
|
-
|
|
25
|
-
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
26
|
-
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
27
|
-
const Root = asChild ? Slot : 'div';
|
|
28
|
-
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
29
|
-
return (
|
|
30
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
31
|
-
{children}
|
|
32
|
-
</Root>
|
|
33
|
-
);
|
|
34
|
-
},
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
|
|
39
|
-
* in a Popover) and knows this based on the `role` it receives. This will render a `Card.StaticRoot` by default, otherwise
|
|
40
|
-
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
41
|
-
*/
|
|
42
|
-
const CardSurfaceRoot = ({
|
|
43
|
-
role = 'never',
|
|
44
|
-
children,
|
|
45
|
-
classNames,
|
|
46
|
-
}: ThemedClassName<PropsWithChildren<{ role?: string }>>) => {
|
|
47
|
-
if (['card--popover', 'card--intrinsic', 'card--extrinsic'].includes(role)) {
|
|
48
|
-
return (
|
|
49
|
-
<div
|
|
50
|
-
className={mx(
|
|
51
|
-
role === 'card--popover'
|
|
52
|
-
? 'popover-card-width'
|
|
53
|
-
: ['card--intrinsic', 'card--extrinsic'].includes(role)
|
|
54
|
-
? 'contents'
|
|
55
|
-
: '',
|
|
56
|
-
classNames,
|
|
57
|
-
)}
|
|
58
|
-
>
|
|
59
|
-
{children}
|
|
60
|
-
</div>
|
|
61
|
-
);
|
|
62
|
-
} else {
|
|
63
|
-
return (
|
|
64
|
-
<CardStaticRoot
|
|
65
|
-
classNames={[
|
|
66
|
-
role === 'card--transclusion' && 'mlb-[1em]',
|
|
67
|
-
role === 'card--transclusion' && hoverableControls,
|
|
68
|
-
classNames,
|
|
69
|
-
]}
|
|
70
|
-
>
|
|
71
|
-
{children}
|
|
72
|
-
</CardStaticRoot>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
78
|
-
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
79
|
-
const Root = asChild ? Slot : 'div';
|
|
80
|
-
const rootProps = asChild
|
|
81
|
-
? { classNames: [cardHeading, cardText, classNames] }
|
|
82
|
-
: { className: mx(cardHeading, cardText, classNames), role };
|
|
83
|
-
return (
|
|
84
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
85
|
-
{children}
|
|
86
|
-
</Root>
|
|
87
|
-
);
|
|
88
|
-
},
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const CardToolbar = forwardRef<HTMLDivElement, ToolbarRootProps>(({ children, classNames, ...props }, forwardedRef) => {
|
|
92
|
-
return (
|
|
93
|
-
<Toolbar.Root {...props} classNames={['bg-transparent density-coarse', classNames]} ref={forwardedRef}>
|
|
94
|
-
{children}
|
|
95
|
-
</Toolbar.Root>
|
|
96
|
-
);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const CardToolbarIconButton = Toolbar.IconButton;
|
|
100
|
-
const CardToolbarSeparator = Toolbar.Separator;
|
|
101
|
-
|
|
102
|
-
const CardDragHandle = forwardRef<HTMLButtonElement, { toolbarItem?: boolean }>(({ toolbarItem }, forwardedRef) => {
|
|
103
|
-
const { t } = useTranslation(translationKey);
|
|
104
|
-
const Root = toolbarItem ? Toolbar.IconButton : IconButton;
|
|
105
|
-
return (
|
|
106
|
-
<Root
|
|
107
|
-
iconOnly
|
|
108
|
-
icon='ph--dots-six-vertical--regular'
|
|
109
|
-
variant='ghost'
|
|
110
|
-
label={t('drag handle label')}
|
|
111
|
-
classNames='pli-2'
|
|
112
|
-
ref={forwardedRef}
|
|
113
|
-
/>
|
|
114
|
-
);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const CardDragPreview = StackItem.DragPreview;
|
|
118
|
-
|
|
119
|
-
const CardMenu = Primitive.div as FC<ComponentPropsWithRef<'div'>>;
|
|
120
|
-
|
|
121
|
-
type CardPosterProps = {
|
|
122
|
-
alt: string;
|
|
123
|
-
aspect?: 'video' | 'auto';
|
|
124
|
-
} & Partial<{ image: string; icon: string }>;
|
|
125
|
-
|
|
126
|
-
const CardPoster = (props: CardPosterProps) => {
|
|
127
|
-
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
128
|
-
if (props.image) {
|
|
129
|
-
return (
|
|
130
|
-
<img className={`dx-card__poster ${aspect} object-cover is-full bs-auto`} src={props.image} alt={props.alt} />
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
if (props.icon) {
|
|
134
|
-
return (
|
|
135
|
-
<div
|
|
136
|
-
role='image'
|
|
137
|
-
className={`dx-card__poster grid ${aspect} place-items-center bg-inputSurface text-subdued`}
|
|
138
|
-
aria-label={props.alt}
|
|
139
|
-
>
|
|
140
|
-
<Icon icon={props.icon} size={10} />
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
147
|
-
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
148
|
-
const Root = asChild ? Slot : 'div';
|
|
149
|
-
const rootProps = asChild
|
|
150
|
-
? { classNames: [cardChrome, classNames] }
|
|
151
|
-
: { className: mx(cardChrome, classNames), role };
|
|
152
|
-
return (
|
|
153
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
154
|
-
{children}
|
|
155
|
-
</Root>
|
|
156
|
-
);
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const CardText = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
161
|
-
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
162
|
-
const Root = asChild ? Slot : 'div';
|
|
163
|
-
const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
|
|
164
|
-
return (
|
|
165
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
166
|
-
{children}
|
|
167
|
-
</Root>
|
|
168
|
-
);
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
export const Card = {
|
|
173
|
-
StaticRoot: CardStaticRoot,
|
|
174
|
-
SurfaceRoot: CardSurfaceRoot,
|
|
175
|
-
Heading: CardHeading,
|
|
176
|
-
Toolbar: CardToolbar,
|
|
177
|
-
ToolbarIconButton: CardToolbarIconButton,
|
|
178
|
-
ToolbarSeparator: CardToolbarSeparator,
|
|
179
|
-
DragHandle: CardDragHandle,
|
|
180
|
-
DragPreview: CardDragPreview,
|
|
181
|
-
Menu: CardMenu,
|
|
182
|
-
Poster: CardPoster,
|
|
183
|
-
Chrome: CardChrome,
|
|
184
|
-
Text: CardText,
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
export { cardRoot, cardHeading, cardText, cardChrome, cardSpacing };
|
|
@@ -1,22 +0,0 @@
|
|
|
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 { cardRoot } 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(cardRoot, 'ring-focusLine ring-neutralFocusIndicator')}>{children}</div>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export const CardDragPreview = {
|
|
20
|
-
Root: CardDragPreviewRoot,
|
|
21
|
-
Content: CardDragPreviewContent,
|
|
22
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
export const cardRoot =
|
|
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
|
-
|
|
8
|
-
export const cardSpacing = 'pli-cardSpacingInline mlb-cardSpacingBlock';
|
|
9
|
-
export const cardNoSpacing = 'pli-0 mlb-0';
|
|
10
|
-
export const labelSpacing = 'mbs-inputSpacingBlock mbe-labelSpacingBlock';
|
|
11
|
-
|
|
12
|
-
export const cardDialogContent = 'p-0 bs-content min-bs-[8rem] max-bs-full md:max-is-[32rem] overflow-hidden';
|
|
13
|
-
export const cardDialogHeader = 'pli-cardSpacingInline mbs-cardSpacingBlock flex justify-between';
|
|
14
|
-
export const cardDialogOverflow = 'overflow-y-auto min-bs-0 flex-1';
|
|
15
|
-
export const cardDialogPaddedOverflow = `${cardDialogOverflow} plb-cardSpacingBlock`;
|
|
16
|
-
export const cardDialogSearchListRoot =
|
|
17
|
-
'pli-cardSpacingInline pbs-cardSpacingBlock [&>input]:mbe-0 min-bs-0 flex-1 flex flex-col';
|
|
18
|
-
|
|
19
|
-
export const cardText = cardSpacing;
|
|
20
|
-
|
|
21
|
-
export const cardHeading = 'text-lg font-medium line-clamp-2 grow';
|
|
22
|
-
|
|
23
|
-
export const cardChrome =
|
|
24
|
-
'pli-[--dx-cardSpacingChrome] mlb-[--dx-cardSpacingChrome] [&_.dx-button]:text-start [&_.dx-button]:is-full';
|
|
@@ -1,173 +0,0 @@
|
|
|
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, { useCallback, useState } 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 { StackItem } from '../../components';
|
|
16
|
-
import { Card, CardDragPreview } from '../Card';
|
|
17
|
-
|
|
18
|
-
import { CardStack } from './CardStack';
|
|
19
|
-
|
|
20
|
-
// Set a seed for reproducible random values
|
|
21
|
-
faker.seed(0);
|
|
22
|
-
|
|
23
|
-
type CardItem = {
|
|
24
|
-
id: string;
|
|
25
|
-
title: string;
|
|
26
|
-
description: string;
|
|
27
|
-
image: string;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
type StackItemData = {
|
|
31
|
-
id: string;
|
|
32
|
-
type?: 'column' | 'card';
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const CardStackStory = () => {
|
|
36
|
-
const [column, setColumn] = useState<CardItem[]>(
|
|
37
|
-
faker.helpers.multiple(
|
|
38
|
-
() => ({
|
|
39
|
-
id: faker.string.uuid(),
|
|
40
|
-
title: faker.commerce.productName(),
|
|
41
|
-
description: faker.lorem.paragraph(),
|
|
42
|
-
image: faker.image.url(),
|
|
43
|
-
}),
|
|
44
|
-
{ count: 12 },
|
|
45
|
-
),
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const handleRearrange = useCallback((source: StackItemData, target: StackItemData, closestEdge: Edge | null) => {
|
|
49
|
-
setColumn((prevColumn) => {
|
|
50
|
-
const newColumns = [...prevColumn];
|
|
51
|
-
// Reordering cards within a column
|
|
52
|
-
const sourceCardIndex = prevColumn.findIndex((card) => card.id === source.id);
|
|
53
|
-
const targetCardIndex = prevColumn.findIndex((card) => card.id === target.id);
|
|
54
|
-
|
|
55
|
-
if (typeof sourceCardIndex === 'number' && typeof targetCardIndex === 'number') {
|
|
56
|
-
const [movedCard] = newColumns.splice(sourceCardIndex, 1);
|
|
57
|
-
|
|
58
|
-
let insertIndex;
|
|
59
|
-
if (sourceCardIndex < targetCardIndex) {
|
|
60
|
-
insertIndex = closestEdge === 'bottom' ? targetCardIndex : targetCardIndex - 1;
|
|
61
|
-
} else {
|
|
62
|
-
insertIndex = closestEdge === 'bottom' ? targetCardIndex + 1 : targetCardIndex;
|
|
63
|
-
}
|
|
64
|
-
newColumns.splice(insertIndex, 0, movedCard);
|
|
65
|
-
}
|
|
66
|
-
return newColumns;
|
|
67
|
-
});
|
|
68
|
-
}, []);
|
|
69
|
-
|
|
70
|
-
const handleAddCard = useCallback(() => {
|
|
71
|
-
setColumn((prevColumn) => {
|
|
72
|
-
const newColumn = [...prevColumn];
|
|
73
|
-
const newCard = {
|
|
74
|
-
id: faker.string.uuid(),
|
|
75
|
-
title: faker.commerce.productName(),
|
|
76
|
-
description: faker.lorem.paragraph(),
|
|
77
|
-
image: faker.image.url(),
|
|
78
|
-
} satisfies CardItem;
|
|
79
|
-
newColumn.push(newCard);
|
|
80
|
-
console.log('[add card]', prevColumn.length, newColumn.length);
|
|
81
|
-
return newColumn;
|
|
82
|
-
});
|
|
83
|
-
}, []);
|
|
84
|
-
|
|
85
|
-
const handleRemoveCard = useCallback((cardId: string) => {
|
|
86
|
-
setColumn((prevColumn) => {
|
|
87
|
-
const newColumn = [...prevColumn];
|
|
88
|
-
|
|
89
|
-
const cardIndex = prevColumn.findIndex((card) => card.id === cardId);
|
|
90
|
-
if (cardIndex !== -1) {
|
|
91
|
-
newColumn.splice(cardIndex, 1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return newColumn;
|
|
95
|
-
});
|
|
96
|
-
}, []);
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<CardStack.Root classNames='is-96'>
|
|
100
|
-
<CardStack.Content>
|
|
101
|
-
<CardStack.Stack id='story column' onRearrange={handleRearrange} itemsCount={column.length}>
|
|
102
|
-
{column.map((card, cardIndex, cardsArray) => {
|
|
103
|
-
const cardItem = { id: card.id, type: 'card' as const };
|
|
104
|
-
const prevCardId = cardIndex > 0 ? cardsArray[cardIndex - 1].id : undefined;
|
|
105
|
-
const nextCardId = cardIndex < cardsArray.length - 1 ? cardsArray[cardIndex + 1].id : undefined;
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<CardStack.Item asChild key={card.id}>
|
|
109
|
-
<StackItem.Root
|
|
110
|
-
item={cardItem}
|
|
111
|
-
focusIndicatorVariant='group'
|
|
112
|
-
prevSiblingId={prevCardId}
|
|
113
|
-
nextSiblingId={nextCardId}
|
|
114
|
-
>
|
|
115
|
-
<Card.StaticRoot>
|
|
116
|
-
<Card.Toolbar>
|
|
117
|
-
<StackItem.DragHandle asChild>
|
|
118
|
-
<Card.DragHandle toolbarItem />
|
|
119
|
-
</StackItem.DragHandle>
|
|
120
|
-
<Card.ToolbarSeparator variant='gap' />
|
|
121
|
-
<Card.ToolbarIconButton
|
|
122
|
-
iconOnly
|
|
123
|
-
variant='ghost'
|
|
124
|
-
icon='ph--x--regular'
|
|
125
|
-
label='Remove card'
|
|
126
|
-
onClick={() => handleRemoveCard(card.id)}
|
|
127
|
-
/>
|
|
128
|
-
</Card.Toolbar>
|
|
129
|
-
<Card.Poster alt={card.title} image={card.image} />
|
|
130
|
-
<Card.Heading>{card.title}</Card.Heading>
|
|
131
|
-
<Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
|
|
132
|
-
</Card.StaticRoot>
|
|
133
|
-
<StackItem.DragPreview>
|
|
134
|
-
{({ item }) => (
|
|
135
|
-
<CardDragPreview.Root>
|
|
136
|
-
<CardDragPreview.Content>
|
|
137
|
-
<Card.Toolbar>
|
|
138
|
-
<Card.DragHandle toolbarItem />
|
|
139
|
-
</Card.Toolbar>
|
|
140
|
-
<Card.Poster alt={card.title} image={card.image} />
|
|
141
|
-
<Card.Heading>{card.title}</Card.Heading>
|
|
142
|
-
<Card.Text classNames='line-clamp-2'>{card.description}</Card.Text>
|
|
143
|
-
</CardDragPreview.Content>
|
|
144
|
-
</CardDragPreview.Root>
|
|
145
|
-
)}
|
|
146
|
-
</StackItem.DragPreview>
|
|
147
|
-
</StackItem.Root>
|
|
148
|
-
</CardStack.Item>
|
|
149
|
-
);
|
|
150
|
-
})}
|
|
151
|
-
</CardStack.Stack>
|
|
152
|
-
|
|
153
|
-
<CardStack.Footer>
|
|
154
|
-
<IconButton icon='ph--plus--regular' label='Add card' onClick={handleAddCard} classNames='is-full' />
|
|
155
|
-
</CardStack.Footer>
|
|
156
|
-
|
|
157
|
-
<CardStack.Heading>{faker.company.name()}</CardStack.Heading>
|
|
158
|
-
</CardStack.Content>
|
|
159
|
-
</CardStack.Root>
|
|
160
|
-
);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const meta: Meta<typeof CardStackStory> = {
|
|
164
|
-
title: 'ui/react-ui-stack/CardStack',
|
|
165
|
-
component: CardStackStory,
|
|
166
|
-
decorators: [withTheme, withLayout({ fullscreen: true })],
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
export default meta;
|
|
170
|
-
|
|
171
|
-
type Story = StoryObj<typeof CardStackStory>;
|
|
172
|
-
|
|
173
|
-
export const Default: Story = {};
|