@dxos/react-ui-stack 0.8.4-main.dedc0f3 → 0.8.4-main.e8ec1fe
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-3V2YUQK5.mjs → chunk-3F2KBXLP.mjs} +208 -101
- package/dist/lib/browser/chunk-3F2KBXLP.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +5 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/playwright/index.mjs +10 -23
- package/dist/lib/browser/playwright/index.mjs.map +2 -2
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-HE3BRF7A.mjs → chunk-SYKFLQGK.mjs} +208 -101
- package/dist/lib/node-esm/chunk-SYKFLQGK.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +5 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/playwright/index.mjs +10 -23
- package/dist/lib/node-esm/playwright/index.mjs.map +2 -2
- package/dist/lib/node-esm/testing/index.mjs +1 -1
- package/dist/types/src/components/Image/Image.d.ts +5 -2
- package/dist/types/src/components/Image/Image.d.ts.map +1 -1
- package/dist/types/src/components/Image/Image.stories.d.ts +3 -1
- package/dist/types/src/components/Image/Image.stories.d.ts.map +1 -1
- package/dist/types/src/components/Stack/Stack.d.ts +6 -5
- package/dist/types/src/components/Stack/Stack.d.ts.map +1 -1
- package/dist/types/src/components/Stack/Stack.stories.d.ts +1 -2
- package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.d.ts +4 -3
- package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts +0 -1
- 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.map +1 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +21 -10
- package/dist/types/src/exemplars/Card/Card.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/Card.stories.d.ts +0 -23
- package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +1 -1
- package/dist/types/src/exemplars/Card/fragments.d.ts +1 -1
- package/dist/types/src/exemplars/Card/fragments.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts +3 -1
- package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +1 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +3 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/useStackDropForElements.d.ts +2 -2
- package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -27
- package/src/components/Image/Image.stories.tsx +34 -8
- package/src/components/Image/Image.tsx +158 -73
- package/src/components/Stack/Stack.stories.tsx +2 -4
- package/src/components/Stack/Stack.tsx +93 -38
- package/src/components/StackItem/StackItem.stories.tsx +3 -5
- package/src/components/StackItem/StackItem.tsx +11 -4
- package/src/components/StackItem/StackItemContent.tsx +19 -8
- package/src/components/StackItem/StackItemHeading.tsx +1 -5
- package/src/exemplars/Card/Card.stories.tsx +1 -25
- package/src/exemplars/Card/Card.tsx +39 -15
- package/src/exemplars/Card/fragments.ts +1 -1
- package/src/exemplars/CardStack/CardStack.stories.tsx +5 -4
- package/src/exemplars/CardStack/CardStack.tsx +11 -8
- package/src/hooks/useStackDropForElements.ts +42 -35
- package/dist/lib/browser/chunk-3V2YUQK5.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HE3BRF7A.mjs.map +0 -7
|
@@ -2,19 +2,17 @@
|
|
|
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 { DropdownMenu, Icon } from '@dxos/react-ui';
|
|
11
|
-
import { withTheme } from '@dxos/
|
|
9
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
10
|
|
|
13
11
|
import { StackItem, type StackItemRootProps } from './StackItem';
|
|
14
12
|
|
|
15
13
|
const DefaultStory = (props: StackItemRootProps) => {
|
|
16
14
|
return (
|
|
17
|
-
<StackItem.Root role='section' {...props} classNames='
|
|
15
|
+
<StackItem.Root role='section' {...props} classNames='is-[20rem] border border-separator'>
|
|
18
16
|
<StackItem.Heading>
|
|
19
17
|
<span className='sr-only'>Title</span>
|
|
20
18
|
<div role='none' className='sticky -block-start-px bg-[--sticky-bg] p-1 is-full'>
|
|
@@ -27,7 +25,7 @@ const DefaultStory = (props: StackItemRootProps) => {
|
|
|
27
25
|
</DropdownMenu.Root>
|
|
28
26
|
</div>
|
|
29
27
|
</StackItem.Heading>
|
|
30
|
-
<StackItem.Content
|
|
28
|
+
<StackItem.Content>Content</StackItem.Content>
|
|
31
29
|
</StackItem.Root>
|
|
32
30
|
);
|
|
33
31
|
};
|
|
@@ -63,7 +63,7 @@ type StackItemRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
|
|
|
63
63
|
onSizeChange?: (nextSize: StackItemSize) => void;
|
|
64
64
|
role?: 'article' | 'section';
|
|
65
65
|
disableRearrange?: boolean;
|
|
66
|
-
focusIndicatorVariant?: 'over-all' | 'group';
|
|
66
|
+
focusIndicatorVariant?: 'over-all' | 'group' | 'over-all-always' | 'group-always';
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
const StackItemRoot = forwardRef<HTMLDivElement, StackItemRootProps>(
|
|
@@ -232,15 +232,22 @@ const StackItemRoot = forwardRef<HTMLDivElement, StackItemRootProps>(
|
|
|
232
232
|
'group/stack-item grid relative',
|
|
233
233
|
focusIndicatorVariant === 'over-all'
|
|
234
234
|
? 'dx-focus-ring-inset-over-all'
|
|
235
|
-
:
|
|
236
|
-
? 'dx-focus-ring-
|
|
237
|
-
: '
|
|
235
|
+
: focusIndicatorVariant === 'over-all-always'
|
|
236
|
+
? 'dx-focus-ring-inset-over-all-always'
|
|
237
|
+
: orientation === 'horizontal'
|
|
238
|
+
? focusIndicatorVariant === 'group-always'
|
|
239
|
+
? 'dx-focus-ring-group-x-always'
|
|
240
|
+
: 'dx-focus-ring-group-x'
|
|
241
|
+
: focusIndicatorVariant === 'group-always'
|
|
242
|
+
? 'dx-focus-ring-group-y-always'
|
|
243
|
+
: 'dx-focus-ring-group-y',
|
|
238
244
|
orientation === 'horizontal' ? 'grid-rows-subgrid' : 'grid-cols-subgrid',
|
|
239
245
|
rail && (orientation === 'horizontal' ? 'row-span-2' : 'col-span-2'),
|
|
240
246
|
role === 'section' && orientation !== 'horizontal' && 'border-be border-subduedSeparator',
|
|
241
247
|
classNames,
|
|
242
248
|
)}
|
|
243
249
|
data-dx-stack-item={stackId}
|
|
250
|
+
data-dx-item-id={item.id}
|
|
244
251
|
{...resizeAttributes}
|
|
245
252
|
style={{
|
|
246
253
|
...(stackSize !== 'split' && sizeStyle(size, orientation)),
|
|
@@ -9,7 +9,8 @@ import { mx } from '@dxos/react-ui-theme';
|
|
|
9
9
|
|
|
10
10
|
import { useStack, useStackItem } from '../StackContext';
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
// TODO(burdon): Add prop for container-max-width?
|
|
13
|
+
export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRef<'div'>, 'role' | 'scrollable'>> & {
|
|
13
14
|
/**
|
|
14
15
|
* This flag is required in order to clarify a developer experience that seemed like it needed extra boilerplate
|
|
15
16
|
* (`row-span-2`) or was buggy. See the description of the StackItem.Content component itself for more information.
|
|
@@ -22,15 +23,21 @@ export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRe
|
|
|
22
23
|
statusbar?: boolean;
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
* Whether
|
|
26
|
+
* Whether to support y-axis scrolling.
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
scrollable?: boolean;
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
* Whether to set a certain aspect ratio on the content, including the toolbar and statusbar.
|
|
31
|
-
* convenience and consistency; it can instead be specified by the `classNames` or `style` props as needed.
|
|
31
|
+
* Whether to set a certain aspect ratio on the content, including the toolbar and statusbar.
|
|
32
|
+
* This is provided for convenience and consistency; it can instead be specified by the `classNames` or `style` props as needed.
|
|
32
33
|
*/
|
|
33
34
|
size?: 'intrinsic' | 'video' | 'square';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Whether the consumer intends to do something custom and typical affordances should not apply.
|
|
38
|
+
* @deprecated Replace with override for gridTempateRows.
|
|
39
|
+
*/
|
|
40
|
+
layoutManaged?: boolean;
|
|
34
41
|
};
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -38,7 +45,10 @@ export type StackItemContentProps = ThemedClassName<Omit<ComponentPropsWithoutRe
|
|
|
38
45
|
* The `toolbar` flag must be provided since this component provides for the layout of content with the toolbar.
|
|
39
46
|
*/
|
|
40
47
|
export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps>(
|
|
41
|
-
(
|
|
48
|
+
(
|
|
49
|
+
{ children, toolbar, statusbar, layoutManaged, classNames, size = 'intrinsic', scrollable, ...props },
|
|
50
|
+
forwardedRef,
|
|
51
|
+
) => {
|
|
42
52
|
const { size: stackItemSize } = useStack();
|
|
43
53
|
const { role } = useStackItem();
|
|
44
54
|
const style = useMemo(
|
|
@@ -61,12 +71,13 @@ export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps
|
|
|
61
71
|
{...props}
|
|
62
72
|
className={mx(
|
|
63
73
|
'group grid grid-cols-[100%] density-coarse',
|
|
64
|
-
stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
|
|
65
74
|
size === 'video' ? 'aspect-video' : size === 'square' && 'aspect-square',
|
|
66
|
-
|
|
75
|
+
stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
|
|
76
|
+
scrollable ? 'min-bs-0 overflow-y-auto scrollbar-thin contain-layout' : 'overflow-hidden',
|
|
67
77
|
role === 'section' &&
|
|
68
78
|
toolbar &&
|
|
69
79
|
'[&_.dx-toolbar]:sticky [&_.dx-toolbar]:z-[1] [&_.dx-toolbar]:block-start-0 [&_.dx-toolbar]:-mbe-px [&_.dx-toolbar]:min-is-0',
|
|
80
|
+
toolbar && '[&>.dx-toolbar]:relative [&>.dx-toolbar]:border-be [&>.dx-toolbar]:border-subduedSeparator',
|
|
70
81
|
classNames,
|
|
71
82
|
)}
|
|
72
83
|
style={style}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
6
5
|
import { Slot } from '@radix-ui/react-slot';
|
|
7
6
|
import React, {
|
|
8
7
|
type ComponentPropsWithRef,
|
|
@@ -30,7 +29,6 @@ export const StackItemHeading = ({
|
|
|
30
29
|
...props
|
|
31
30
|
}: StackItemHeadingProps) => {
|
|
32
31
|
const { orientation } = useStack();
|
|
33
|
-
const focusableGroupAttrs = useFocusableGroup({ tabBehavior: 'limited' });
|
|
34
32
|
|
|
35
33
|
const Root = asChild ? Slot : 'div';
|
|
36
34
|
|
|
@@ -38,10 +36,8 @@ export const StackItemHeading = ({
|
|
|
38
36
|
<Root
|
|
39
37
|
role='heading'
|
|
40
38
|
{...props}
|
|
41
|
-
tabIndex={0}
|
|
42
|
-
{...focusableGroupAttrs}
|
|
43
39
|
className={mx(
|
|
44
|
-
'flex items-center
|
|
40
|
+
'flex items-center !border-is-0 bg-headerSurface',
|
|
45
41
|
separateOnScroll
|
|
46
42
|
? 'border-transparent [[data-scroll-separator="true"]_&]:border-subduedSeparator'
|
|
47
43
|
: 'border-subduedSeparator',
|
|
@@ -2,13 +2,11 @@
|
|
|
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
|
|
|
@@ -47,28 +45,6 @@ const meta = {
|
|
|
47
45
|
parameters: {
|
|
48
46
|
layout: 'centered',
|
|
49
47
|
},
|
|
50
|
-
argTypes: {
|
|
51
|
-
title: {
|
|
52
|
-
control: 'text',
|
|
53
|
-
description: 'Card title',
|
|
54
|
-
},
|
|
55
|
-
description: {
|
|
56
|
-
control: 'text',
|
|
57
|
-
description: 'Card description',
|
|
58
|
-
},
|
|
59
|
-
image: {
|
|
60
|
-
control: 'text',
|
|
61
|
-
description: 'URL for the poster image',
|
|
62
|
-
},
|
|
63
|
-
showImage: {
|
|
64
|
-
control: 'boolean',
|
|
65
|
-
description: 'Whether to show the image',
|
|
66
|
-
},
|
|
67
|
-
showIcon: {
|
|
68
|
-
control: 'boolean',
|
|
69
|
-
description: 'Whether to show an icon (when image is not shown)',
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
48
|
} satisfies Meta<typeof DefaultStory>;
|
|
73
49
|
|
|
74
50
|
export default meta;
|
|
@@ -13,21 +13,31 @@ import React, {
|
|
|
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
18
|
import { Image, StackItem } from '../../components';
|
|
19
19
|
import { translationKey } from '../../translations';
|
|
20
20
|
|
|
21
21
|
import { cardChrome, cardHeading, cardRoot, cardSpacing, cardText } from './fragments';
|
|
22
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
|
+
|
|
23
33
|
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
24
34
|
|
|
25
|
-
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
26
|
-
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
35
|
+
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps & { id?: string }>(
|
|
36
|
+
({ children, classNames, id, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
27
37
|
const Root = asChild ? Slot : 'div';
|
|
28
38
|
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
29
39
|
return (
|
|
30
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
40
|
+
<Root {...(id && { 'data-object-id': id })} {...props} {...rootProps} ref={forwardedRef}>
|
|
31
41
|
{children}
|
|
32
42
|
</Root>
|
|
33
43
|
);
|
|
@@ -35,18 +45,19 @@ const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
35
45
|
);
|
|
36
46
|
|
|
37
47
|
/**
|
|
38
|
-
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
|
|
39
|
-
*
|
|
40
|
-
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
48
|
+
* 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.
|
|
49
|
+
* This will render a `Card.StaticRoot` by default, otherwise it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
41
50
|
*/
|
|
42
51
|
const CardSurfaceRoot = ({
|
|
52
|
+
id,
|
|
43
53
|
role = 'never',
|
|
44
54
|
children,
|
|
45
55
|
classNames,
|
|
46
|
-
}: ThemedClassName<PropsWithChildren<{ role?: string }>>) => {
|
|
56
|
+
}: ThemedClassName<PropsWithChildren<{ id?: string; role?: string }>>) => {
|
|
47
57
|
if (['card--popover', 'card--intrinsic', 'card--extrinsic'].includes(role)) {
|
|
48
58
|
return (
|
|
49
59
|
<div
|
|
60
|
+
{...(id && { 'data-object-id': id })}
|
|
50
61
|
className={mx(
|
|
51
62
|
role === 'card--popover'
|
|
52
63
|
? 'popover-card-width'
|
|
@@ -62,6 +73,7 @@ const CardSurfaceRoot = ({
|
|
|
62
73
|
} else {
|
|
63
74
|
return (
|
|
64
75
|
<CardStaticRoot
|
|
76
|
+
id={id}
|
|
65
77
|
classNames={[
|
|
66
78
|
role === 'card--transclusion' && 'mlb-1',
|
|
67
79
|
role === 'card--transclusion' && hoverableControls,
|
|
@@ -118,22 +130,26 @@ const CardDragPreview = StackItem.DragPreview;
|
|
|
118
130
|
|
|
119
131
|
const CardMenu = Primitive.div as FC<ComponentPropsWithRef<'div'>>;
|
|
120
132
|
|
|
121
|
-
type CardPosterProps =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
type CardPosterProps = ThemedClassName<
|
|
134
|
+
{
|
|
135
|
+
alt: string;
|
|
136
|
+
aspect?: 'video' | 'auto';
|
|
137
|
+
} & Partial<{ image: string; icon: string }>
|
|
138
|
+
>;
|
|
125
139
|
|
|
126
140
|
const CardPoster = (props: CardPosterProps) => {
|
|
127
141
|
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
128
142
|
if (props.image) {
|
|
129
|
-
return
|
|
143
|
+
return (
|
|
144
|
+
<Image classNames={[`dx-card__poster is-full`, aspect, props.classNames]} src={props.image} alt={props.alt} />
|
|
145
|
+
);
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
if (props.icon) {
|
|
133
149
|
return (
|
|
134
150
|
<div
|
|
135
151
|
role='image'
|
|
136
|
-
className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect)}
|
|
152
|
+
className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect, props.classNames)}
|
|
137
153
|
aria-label={props.alt}
|
|
138
154
|
>
|
|
139
155
|
<Icon icon={props.icon} size={10} />
|
|
@@ -183,4 +199,12 @@ export const Card = {
|
|
|
183
199
|
Text: CardText,
|
|
184
200
|
};
|
|
185
201
|
|
|
186
|
-
export {
|
|
202
|
+
export {
|
|
203
|
+
cardRoot,
|
|
204
|
+
cardHeading,
|
|
205
|
+
cardText,
|
|
206
|
+
cardChrome,
|
|
207
|
+
cardSpacing,
|
|
208
|
+
cardStackDefaultInlineSizeRem,
|
|
209
|
+
cardDefaultInlineSize,
|
|
210
|
+
};
|
|
@@ -21,4 +21,4 @@ export const cardText = cardSpacing;
|
|
|
21
21
|
export const cardHeading = 'text-lg font-medium line-clamp-2 grow';
|
|
22
22
|
|
|
23
23
|
export const cardChrome =
|
|
24
|
-
'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,15 +2,13 @@
|
|
|
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
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
13
|
import { StackItem } from '../../components';
|
|
16
14
|
import { Card, CardDragPreview } from '../Card';
|
|
@@ -162,7 +160,10 @@ const CardStackStory = () => {
|
|
|
162
160
|
const meta = {
|
|
163
161
|
title: 'ui/react-ui-stack/CardStack',
|
|
164
162
|
component: CardStackStory,
|
|
165
|
-
decorators: [withTheme
|
|
163
|
+
decorators: [withTheme],
|
|
164
|
+
parameters: {
|
|
165
|
+
layout: 'fullscreen',
|
|
166
|
+
},
|
|
166
167
|
} satisfies Meta<typeof CardStackStory>;
|
|
167
168
|
|
|
168
169
|
export default meta;
|
|
@@ -72,17 +72,20 @@ const CardStackFooter = forwardRef<HTMLDivElement, SharedCardStackProps>(
|
|
|
72
72
|
},
|
|
73
73
|
);
|
|
74
74
|
|
|
75
|
-
const cardStackContent =
|
|
76
|
-
'shrink min-bs-0 bg-baseSurface border border-separator rounded-md grid dx-focus-ring-group-x-indicator kanban-drop'
|
|
77
|
-
railGridHorizontalContainFitContent,
|
|
78
|
-
];
|
|
75
|
+
const cardStackContent =
|
|
76
|
+
'shrink min-bs-0 bg-baseSurface border border-separator rounded-md grid dx-focus-ring-group-x-indicator kanban-drop';
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
type CardStackContentProps = SharedCardStackProps & {
|
|
79
|
+
footer?: boolean;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const CardStackContent = forwardRef<HTMLDivElement, CardStackContentProps>(
|
|
83
|
+
({ children, classNames, asChild, role = 'none', footer = true, ...props }, forwardedRef) => {
|
|
82
84
|
const Root = asChild ? Slot : 'div';
|
|
85
|
+
const baseClassNames = footer ? [cardStackContent, railGridHorizontalContainFitContent] : [cardStackContent];
|
|
83
86
|
const rootProps = asChild
|
|
84
|
-
? { classNames: [...
|
|
85
|
-
: { className: mx(...
|
|
87
|
+
? { classNames: [...baseClassNames, classNames] }
|
|
88
|
+
: { className: mx(...baseClassNames, classNames), role };
|
|
86
89
|
return (
|
|
87
90
|
<Root {...props} {...rootProps} data-scroll-separator='false' ref={forwardedRef}>
|
|
88
91
|
{children}
|
|
@@ -10,6 +10,8 @@ import { useLayoutEffect, useState } from 'react';
|
|
|
10
10
|
|
|
11
11
|
import { type Orientation, type StackItemData, type StackItemRearrangeHandler } from '../components';
|
|
12
12
|
|
|
13
|
+
const noop = () => {};
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Hook to handle drag and drop functionality for Stack components.
|
|
15
17
|
*/
|
|
@@ -17,59 +19,64 @@ export const useStackDropForElements = ({
|
|
|
17
19
|
id,
|
|
18
20
|
element,
|
|
19
21
|
scrollElement = element,
|
|
20
|
-
selfDroppable,
|
|
21
22
|
orientation,
|
|
23
|
+
selfDroppable,
|
|
22
24
|
onRearrange,
|
|
23
25
|
}: {
|
|
24
26
|
id?: string;
|
|
25
27
|
element: HTMLDivElement | null;
|
|
26
28
|
scrollElement?: HTMLDivElement | null;
|
|
27
|
-
selfDroppable: boolean;
|
|
28
29
|
orientation: Orientation;
|
|
30
|
+
selfDroppable: boolean;
|
|
29
31
|
onRearrange?: StackItemRearrangeHandler;
|
|
30
32
|
}) => {
|
|
31
33
|
const [dropping, setDropping] = useState(false);
|
|
32
34
|
|
|
33
35
|
useLayoutEffect(() => {
|
|
34
|
-
if (!element
|
|
36
|
+
if (!element) {
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
const acceptSourceType = orientation === 'horizontal' ? 'column' : 'card';
|
|
39
41
|
|
|
40
42
|
return combine(
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
43
|
+
selfDroppable
|
|
44
|
+
? dropTargetForElements({
|
|
45
|
+
element,
|
|
46
|
+
getData: ({ input, element }) => {
|
|
47
|
+
return attachClosestEdge(
|
|
48
|
+
{ id, type: orientation === 'horizontal' ? 'card' : 'column' },
|
|
49
|
+
{ input, element, allowedEdges: [orientation === 'horizontal' ? 'left' : 'top'] },
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
onDragEnter: ({ source }) => {
|
|
53
|
+
if (source.data.type === acceptSourceType) {
|
|
54
|
+
setDropping(true);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
onDrag: ({ source }) => {
|
|
58
|
+
if (source.data.type === acceptSourceType) {
|
|
59
|
+
setDropping(true);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
onDragLeave: () => {
|
|
63
|
+
return setDropping(false);
|
|
64
|
+
},
|
|
65
|
+
onDrop: ({ self, source }) => {
|
|
66
|
+
setDropping(false);
|
|
67
|
+
if (source.data.type === acceptSourceType && selfDroppable && onRearrange) {
|
|
68
|
+
onRearrange(source.data as StackItemData, self.data as StackItemData, extractClosestEdge(self.data));
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
: noop,
|
|
73
|
+
|
|
74
|
+
scrollElement
|
|
75
|
+
? autoScrollForElements({
|
|
76
|
+
element: scrollElement,
|
|
77
|
+
getAllowedAxis: () => orientation,
|
|
78
|
+
})
|
|
79
|
+
: noop,
|
|
73
80
|
);
|
|
74
81
|
}, [element, scrollElement, selfDroppable, orientation, id, onRearrange]);
|
|
75
82
|
|