@dxos/react-ui-stack 0.8.4-main.c1de068 → 0.8.4-main.c4373fc
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-SM27YTH3.mjs} +406 -186
- package/dist/lib/browser/chunk-SM27YTH3.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 +23 -6
- 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-MMAOXKOM.mjs} +406 -186
- package/dist/lib/node-esm/chunk-MMAOXKOM.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 +23 -6
- 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 +11 -0
- package/dist/types/src/components/Image/Image.d.ts.map +1 -0
- package/dist/types/src/components/Image/Image.stories.d.ts +30 -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 +10 -2
- 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 +6 -5
- 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 +16 -8
- 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 +13 -6
- 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 +3 -1
- 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 +1 -1
- 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 +56 -0
- package/src/components/Image/Image.tsx +137 -0
- package/src/components/Image/index.ts +5 -0
- package/src/components/Stack/Stack.stories.tsx +8 -9
- package/src/components/Stack/Stack.tsx +215 -18
- 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 +17 -5
- 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 +30 -12
- package/src/exemplars/Card/fragments.ts +3 -2
- package/src/exemplars/CardStack/CardStack.stories.tsx +11 -10
- package/src/exemplars/CardStack/CardStack.tsx +12 -9
- package/src/hooks/useStackDropForElements.ts +1 -1
- 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,21 +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
|
-
import {
|
|
11
|
-
import { withTheme } from '@dxos/
|
|
8
|
+
import { DropdownMenu, Icon } from '@dxos/react-ui';
|
|
9
|
+
import { withTheme } from '@dxos/react-ui/testing';
|
|
12
10
|
|
|
13
|
-
import { StackItem } from './StackItem';
|
|
11
|
+
import { StackItem, type StackItemRootProps } from './StackItem';
|
|
14
12
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
render: (args) => (
|
|
19
|
-
<StackItem.Root role='section' {...args} classNames='w-[20rem] border border-separator'>
|
|
13
|
+
const DefaultStory = (props: StackItemRootProps) => {
|
|
14
|
+
return (
|
|
15
|
+
<StackItem.Root role='section' {...props} classNames='w-[20rem] border border-separator'>
|
|
20
16
|
<StackItem.Heading>
|
|
21
17
|
<span className='sr-only'>Title</span>
|
|
22
18
|
<div role='none' className='sticky -block-start-px bg-[--sticky-bg] p-1 is-full'>
|
|
@@ -29,18 +25,24 @@ const meta: Meta<typeof StackItem.Root> = {
|
|
|
29
25
|
</DropdownMenu.Root>
|
|
30
26
|
</div>
|
|
31
27
|
</StackItem.Heading>
|
|
32
|
-
<StackItem.Content
|
|
28
|
+
<StackItem.Content>Content</StackItem.Content>
|
|
33
29
|
</StackItem.Root>
|
|
34
|
-
)
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const meta = {
|
|
34
|
+
title: 'ui/react-ui-stack/StackItem',
|
|
35
|
+
component: StackItem.Root as any,
|
|
36
|
+
render: DefaultStory,
|
|
35
37
|
decorators: [withTheme],
|
|
36
38
|
parameters: {
|
|
37
39
|
layout: 'centered',
|
|
38
40
|
},
|
|
39
|
-
}
|
|
41
|
+
} satisfies Meta<typeof DefaultStory>;
|
|
40
42
|
|
|
41
43
|
export default meta;
|
|
42
44
|
|
|
43
|
-
type Story = StoryObj<typeof
|
|
45
|
+
type Story = StoryObj<typeof meta>;
|
|
44
46
|
|
|
45
47
|
export const Default: Story = {
|
|
46
48
|
args: {
|
|
@@ -7,46 +7,47 @@ import { draggable, dropTargetForElements } from '@atlaskit/pragmatic-drag-and-d
|
|
|
7
7
|
import { preserveOffsetOnSource } from '@atlaskit/pragmatic-drag-and-drop/element/preserve-offset-on-source';
|
|
8
8
|
import { setCustomNativeDragPreview } from '@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview';
|
|
9
9
|
import {
|
|
10
|
+
type Edge,
|
|
10
11
|
attachClosestEdge,
|
|
11
12
|
extractClosestEdge,
|
|
12
|
-
type Edge,
|
|
13
13
|
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
14
14
|
import { useFocusableGroup } from '@fluentui/react-tabster';
|
|
15
15
|
import { composeRefs } from '@radix-ui/react-compose-refs';
|
|
16
16
|
import React, {
|
|
17
|
-
forwardRef,
|
|
18
|
-
useLayoutEffect,
|
|
19
|
-
useState,
|
|
20
17
|
type ComponentPropsWithRef,
|
|
21
|
-
useCallback,
|
|
22
18
|
type ReactNode,
|
|
19
|
+
forwardRef,
|
|
20
|
+
useCallback,
|
|
21
|
+
useLayoutEffect,
|
|
23
22
|
useMemo,
|
|
23
|
+
useState,
|
|
24
24
|
} from 'react';
|
|
25
25
|
import { createPortal } from 'react-dom';
|
|
26
26
|
|
|
27
|
-
import { type ThemedClassName
|
|
27
|
+
import { ListItem, type ThemedClassName } from '@dxos/react-ui';
|
|
28
28
|
import { resizeAttributes, sizeStyle } from '@dxos/react-ui-dnd';
|
|
29
29
|
import { mx } from '@dxos/react-ui-theme';
|
|
30
30
|
|
|
31
|
+
import { type StackItemData, type StackItemSize } from '../defs';
|
|
32
|
+
import { type ItemDragState, StackItemContext, idle, useStack, useStackItem } from '../StackContext';
|
|
33
|
+
|
|
31
34
|
import { StackItemContent, type StackItemContentProps } from './StackItemContent';
|
|
32
35
|
import { StackItemDragHandle, type StackItemDragHandleProps } from './StackItemDragHandle';
|
|
33
36
|
import {
|
|
34
37
|
StackItemHeading,
|
|
35
38
|
StackItemHeadingLabel,
|
|
36
|
-
type StackItemHeadingProps,
|
|
37
39
|
type StackItemHeadingLabelProps,
|
|
40
|
+
type StackItemHeadingProps,
|
|
38
41
|
StackItemHeadingStickyContent,
|
|
39
42
|
} from './StackItemHeading';
|
|
40
43
|
import { StackItemResizeHandle, type StackItemResizeHandleProps } from './StackItemResizeHandle';
|
|
41
44
|
import {
|
|
42
45
|
StackItemSigil,
|
|
43
|
-
type StackItemSigilProps,
|
|
44
46
|
type StackItemSigilAction,
|
|
45
|
-
type StackItemSigilButtonProps,
|
|
46
47
|
StackItemSigilButton,
|
|
48
|
+
type StackItemSigilButtonProps,
|
|
49
|
+
type StackItemSigilProps,
|
|
47
50
|
} from './StackItemSigil';
|
|
48
|
-
import { useStack, StackItemContext, idle, type ItemDragState, useStackItem } from '../StackContext';
|
|
49
|
-
import { type StackItemSize, type StackItemData } from '../defs';
|
|
50
51
|
|
|
51
52
|
// NOTE: 48rem fills the screen on a MacbookPro with the sidebars closed.
|
|
52
53
|
export const DEFAULT_HORIZONTAL_SIZE = 48 satisfies StackItemSize;
|
|
@@ -62,7 +63,7 @@ type StackItemRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
|
|
|
62
63
|
onSizeChange?: (nextSize: StackItemSize) => void;
|
|
63
64
|
role?: 'article' | 'section';
|
|
64
65
|
disableRearrange?: boolean;
|
|
65
|
-
focusIndicatorVariant?: 'over-all' | 'group';
|
|
66
|
+
focusIndicatorVariant?: 'over-all' | 'group' | 'over-all-always' | 'group-always';
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
const StackItemRoot = forwardRef<HTMLDivElement, StackItemRootProps>(
|
|
@@ -89,7 +90,7 @@ const StackItemRoot = forwardRef<HTMLDivElement, StackItemRootProps>(
|
|
|
89
90
|
const [closestEdge, setEdge] = useState<Edge | null>(null);
|
|
90
91
|
const [sourceId, setSourceId] = useState<string | null>(null);
|
|
91
92
|
const [dragState, setDragState] = useState<ItemDragState>(idle);
|
|
92
|
-
const { orientation, rail, onRearrange } = useStack();
|
|
93
|
+
const { orientation, rail, onRearrange, size: stackSize, stackId } = useStack();
|
|
93
94
|
const [size = orientation === 'horizontal' ? DEFAULT_HORIZONTAL_SIZE : DEFAULT_VERTICAL_SIZE, setInternalSize] =
|
|
94
95
|
useState(propsSize);
|
|
95
96
|
|
|
@@ -231,18 +232,25 @@ const StackItemRoot = forwardRef<HTMLDivElement, StackItemRootProps>(
|
|
|
231
232
|
'group/stack-item grid relative',
|
|
232
233
|
focusIndicatorVariant === 'over-all'
|
|
233
234
|
? 'dx-focus-ring-inset-over-all'
|
|
234
|
-
:
|
|
235
|
-
? 'dx-focus-ring-
|
|
236
|
-
: '
|
|
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',
|
|
237
244
|
orientation === 'horizontal' ? 'grid-rows-subgrid' : 'grid-cols-subgrid',
|
|
238
245
|
rail && (orientation === 'horizontal' ? 'row-span-2' : 'col-span-2'),
|
|
239
246
|
role === 'section' && orientation !== 'horizontal' && 'border-be border-subduedSeparator',
|
|
240
247
|
classNames,
|
|
241
248
|
)}
|
|
242
|
-
data-dx-stack-item
|
|
249
|
+
data-dx-stack-item={stackId}
|
|
250
|
+
data-dx-item-id={item.id}
|
|
243
251
|
{...resizeAttributes}
|
|
244
252
|
style={{
|
|
245
|
-
...sizeStyle(size, orientation),
|
|
253
|
+
...(stackSize !== 'split' && sizeStyle(size, orientation)),
|
|
246
254
|
...(Number.isFinite(order) && {
|
|
247
255
|
[orientation === 'horizontal' ? 'gridColumn' : 'gridRow']: `${order}`,
|
|
248
256
|
}),
|
|
@@ -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,13 +23,19 @@ 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
|
*/
|
|
28
|
+
scrollable?: boolean;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Whether the consumer intends to do something custom and typical affordances should not apply.
|
|
32
|
+
*/
|
|
33
|
+
// TODO(burdon): This is cryptic; can we remove (only used by plugin-inbox?) Normalize toolbar?
|
|
27
34
|
layoutManaged?: boolean;
|
|
28
35
|
|
|
29
36
|
/**
|
|
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.
|
|
37
|
+
* Whether to set a certain aspect ratio on the content, including the toolbar and statusbar.
|
|
38
|
+
* This is provided for convenience and consistency; it can instead be specified by the `classNames` or `style` props as needed.
|
|
32
39
|
*/
|
|
33
40
|
size?: 'intrinsic' | 'video' | 'square';
|
|
34
41
|
};
|
|
@@ -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(
|
|
@@ -54,6 +64,7 @@ export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps
|
|
|
54
64
|
},
|
|
55
65
|
[toolbar, statusbar, layoutManaged],
|
|
56
66
|
);
|
|
67
|
+
|
|
57
68
|
return (
|
|
58
69
|
<div
|
|
59
70
|
role='none'
|
|
@@ -63,6 +74,7 @@ export const StackItemContent = forwardRef<HTMLDivElement, StackItemContentProps
|
|
|
63
74
|
stackItemSize === 'contain' && 'min-bs-0 overflow-hidden',
|
|
64
75
|
size === 'video' ? 'aspect-video' : size === 'square' && 'aspect-square',
|
|
65
76
|
toolbar && '[&>.dx-toolbar]:relative [&>.dx-toolbar]:border-be [&>.dx-toolbar]:border-subduedSeparator',
|
|
77
|
+
scrollable ? 'min-bs-0 overflow-y-auto scrollbar-thin contain-layout' : 'overflow-hidden',
|
|
66
78
|
role === 'section' &&
|
|
67
79
|
toolbar &&
|
|
68
80
|
'[&_.dx-toolbar]:sticky [&_.dx-toolbar]:z-[1] [&_.dx-toolbar]:block-start-0 [&_.dx-toolbar]:-mbe-px [&_.dx-toolbar]:min-is-0',
|
|
@@ -2,17 +2,16 @@
|
|
|
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
|
-
type ComponentPropsWithoutRef,
|
|
9
7
|
type ComponentPropsWithRef,
|
|
10
|
-
|
|
8
|
+
type ComponentPropsWithoutRef,
|
|
11
9
|
type PropsWithChildren,
|
|
10
|
+
forwardRef,
|
|
12
11
|
} from 'react';
|
|
13
12
|
|
|
14
13
|
import { type ThemedClassName } from '@dxos/react-ui';
|
|
15
|
-
import {
|
|
14
|
+
import { type AttendableId, type Related, useAttention } from '@dxos/react-ui-attention';
|
|
16
15
|
import { mx } from '@dxos/react-ui-theme';
|
|
17
16
|
|
|
18
17
|
import { useStack } from '../StackContext';
|
|
@@ -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',
|
|
@@ -6,9 +6,10 @@ import React from 'react';
|
|
|
6
6
|
|
|
7
7
|
import { ResizeHandle } from '@dxos/react-ui-dnd';
|
|
8
8
|
|
|
9
|
-
import { DEFAULT_EXTRINSIC_SIZE } from './StackItem';
|
|
10
9
|
import { useStack, useStackItem } from '../StackContext';
|
|
11
10
|
|
|
11
|
+
import { DEFAULT_EXTRINSIC_SIZE } from './StackItem';
|
|
12
|
+
|
|
12
13
|
const MIN_WIDTH = 20;
|
|
13
14
|
const MIN_HEIGHT = 3;
|
|
14
15
|
|
|
@@ -11,9 +11,10 @@ import { type AttendableId, type Related, useAttention } from '@dxos/react-ui-at
|
|
|
11
11
|
import { descriptionText, mx } from '@dxos/react-ui-theme';
|
|
12
12
|
import { getHostPlatform } from '@dxos/util';
|
|
13
13
|
|
|
14
|
-
import { MenuSignifierHorizontal } from './MenuSignifier';
|
|
15
14
|
import { translationKey } from '../../translations';
|
|
16
15
|
|
|
16
|
+
import { MenuSignifierHorizontal } from './MenuSignifier';
|
|
17
|
+
|
|
17
18
|
export type KeyBinding = {
|
|
18
19
|
windows?: string;
|
|
19
20
|
macos?: string;
|
package/src/components/index.ts
CHANGED
|
@@ -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,22 +5,33 @@
|
|
|
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
16
|
import { 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
|
+
|
|
22
23
|
type SharedCardProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* The default width of cards. It should be no larger than 320px per WCAG 2.1 SC 1.4.10.
|
|
27
|
+
*/
|
|
28
|
+
const cardDefaultInlineSize = 18;
|
|
29
|
+
/**
|
|
30
|
+
* This is `cardDefaultInlineSize` plus 2 times the sum of the inner and outer spacing applied by CardStack on the
|
|
31
|
+
* inline axis.
|
|
32
|
+
*/
|
|
33
|
+
const cardStackDefaultInlineSizeRem = cardDefaultInlineSize + 2.125;
|
|
34
|
+
|
|
24
35
|
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
25
36
|
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
26
37
|
const Root = asChild ? Slot : 'div';
|
|
@@ -62,7 +73,7 @@ const CardSurfaceRoot = ({
|
|
|
62
73
|
return (
|
|
63
74
|
<CardStaticRoot
|
|
64
75
|
classNames={[
|
|
65
|
-
role === 'card--transclusion' && 'mlb-
|
|
76
|
+
role === 'card--transclusion' && 'mlb-1',
|
|
66
77
|
role === 'card--transclusion' && hoverableControls,
|
|
67
78
|
classNames,
|
|
68
79
|
]}
|
|
@@ -125,15 +136,14 @@ type CardPosterProps = {
|
|
|
125
136
|
const CardPoster = (props: CardPosterProps) => {
|
|
126
137
|
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
127
138
|
if (props.image) {
|
|
128
|
-
return
|
|
129
|
-
<img className={`dx-card__poster ${aspect} object-cover is-full bs-auto`} src={props.image} alt={props.alt} />
|
|
130
|
-
);
|
|
139
|
+
return <Image classNames={[`dx-card__poster is-full __bs-auto`, aspect]} src={props.image} alt={props.alt} />;
|
|
131
140
|
}
|
|
141
|
+
|
|
132
142
|
if (props.icon) {
|
|
133
143
|
return (
|
|
134
144
|
<div
|
|
135
145
|
role='image'
|
|
136
|
-
className={`dx-card__poster grid
|
|
146
|
+
className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect)}
|
|
137
147
|
aria-label={props.alt}
|
|
138
148
|
>
|
|
139
149
|
<Icon icon={props.icon} size={10} />
|
|
@@ -156,9 +166,9 @@ const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
|
156
166
|
},
|
|
157
167
|
);
|
|
158
168
|
|
|
159
|
-
const CardText = forwardRef<
|
|
169
|
+
const CardText = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
160
170
|
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
161
|
-
const Root = asChild ? Slot : '
|
|
171
|
+
const Root = asChild ? Slot : 'div';
|
|
162
172
|
const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
|
|
163
173
|
return (
|
|
164
174
|
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
@@ -183,4 +193,12 @@ export const Card = {
|
|
|
183
193
|
Text: CardText,
|
|
184
194
|
};
|
|
185
195
|
|
|
186
|
-
export {
|
|
196
|
+
export {
|
|
197
|
+
cardRoot,
|
|
198
|
+
cardHeading,
|
|
199
|
+
cardText,
|
|
200
|
+
cardChrome,
|
|
201
|
+
cardSpacing,
|
|
202
|
+
cardStackDefaultInlineSizeRem,
|
|
203
|
+
cardDefaultInlineSize,
|
|
204
|
+
};
|
|
@@ -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 = {
|
|
@@ -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,7 +8,7 @@ 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
|
type SharedCardStackProps = ThemedClassName<ComponentPropsWithoutRef<'div'>> & { asChild?: boolean };
|
|
@@ -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}
|
|
@@ -8,7 +8,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
|
|
|
8
8
|
import { attachClosestEdge, extractClosestEdge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
|
|
9
9
|
import { useLayoutEffect, useState } from 'react';
|
|
10
10
|
|
|
11
|
-
import { type
|
|
11
|
+
import { type Orientation, type StackItemData, type StackItemRearrangeHandler } from '../components';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Hook to handle drag and drop functionality for Stack components.
|
|
@@ -16,18 +16,21 @@ export const CardContainer = ({
|
|
|
16
16
|
switch (role) {
|
|
17
17
|
case 'card--popover':
|
|
18
18
|
return <PopoverCardContainer icon={icon}>{children}</PopoverCardContainer>;
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<IntrinsicCardContainer>
|
|
22
|
-
<Card.StaticRoot>{children}</Card.StaticRoot>
|
|
23
|
-
</IntrinsicCardContainer>
|
|
24
|
-
);
|
|
19
|
+
|
|
25
20
|
case 'card--extrinsic':
|
|
26
21
|
return (
|
|
27
22
|
<ExtrinsicCardContainer>
|
|
28
23
|
<Card.StaticRoot>{children}</Card.StaticRoot>
|
|
29
24
|
</ExtrinsicCardContainer>
|
|
30
25
|
);
|
|
26
|
+
|
|
27
|
+
case 'card--intrinsic':
|
|
28
|
+
return (
|
|
29
|
+
<IntrinsicCardContainer>
|
|
30
|
+
<Card.StaticRoot>{children}</Card.StaticRoot>
|
|
31
|
+
</IntrinsicCardContainer>
|
|
32
|
+
);
|
|
33
|
+
|
|
31
34
|
default:
|
|
32
35
|
return <Card.StaticRoot>{children}</Card.StaticRoot>;
|
|
33
36
|
}
|