@dxos/react-ui-stack 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29

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.
Files changed (122) hide show
  1. package/dist/lib/browser/index.mjs +704 -67
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/playwright/index.mjs +10 -23
  5. package/dist/lib/browser/playwright/index.mjs.map +2 -2
  6. package/dist/lib/browser/translations.mjs +23 -0
  7. package/dist/lib/browser/translations.mjs.map +7 -0
  8. package/dist/lib/node-esm/index.mjs +705 -67
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/playwright/index.mjs +10 -23
  12. package/dist/lib/node-esm/playwright/index.mjs.map +2 -2
  13. package/dist/lib/node-esm/translations.mjs +25 -0
  14. package/dist/lib/node-esm/translations.mjs.map +7 -0
  15. package/dist/types/src/components/Stack/Stack.d.ts +5 -9
  16. package/dist/types/src/components/Stack/Stack.d.ts.map +1 -1
  17. package/dist/types/src/components/Stack/Stack.stories.d.ts +1 -2
  18. package/dist/types/src/components/Stack/Stack.stories.d.ts.map +1 -1
  19. package/dist/types/src/components/StackContext.d.ts +1 -1
  20. package/dist/types/src/components/StackContext.d.ts.map +1 -1
  21. package/dist/types/src/components/StackItem/MenuSignifier.d.ts.map +1 -1
  22. package/dist/types/src/components/StackItem/StackItem.d.ts +12 -15
  23. package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -1
  24. package/dist/types/src/components/StackItem/StackItem.stories.d.ts +0 -1
  25. package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -1
  26. package/dist/types/src/components/StackItem/StackItemContent.d.ts +4 -37
  27. package/dist/types/src/components/StackItem/StackItemContent.d.ts.map +1 -1
  28. package/dist/types/src/components/StackItem/StackItemDragHandle.d.ts.map +1 -1
  29. package/dist/types/src/components/StackItem/StackItemHeading.d.ts +1 -1
  30. package/dist/types/src/components/StackItem/StackItemHeading.d.ts.map +1 -1
  31. package/dist/types/src/components/StackItem/StackItemResizeHandle.d.ts +1 -1
  32. package/dist/types/src/components/StackItem/StackItemResizeHandle.d.ts.map +1 -1
  33. package/dist/types/src/components/StackItem/StackItemSigil.d.ts +2 -2
  34. package/dist/types/src/components/StackItem/StackItemSigil.d.ts.map +1 -1
  35. package/dist/types/src/components/index.d.ts +1 -2
  36. package/dist/types/src/components/index.d.ts.map +1 -1
  37. package/dist/types/src/components/{defs.d.ts → types.d.ts} +1 -1
  38. package/dist/types/src/components/types.d.ts.map +1 -0
  39. package/dist/types/src/hooks/useStackDropForElements.d.ts +8 -6
  40. package/dist/types/src/hooks/useStackDropForElements.d.ts.map +1 -1
  41. package/dist/types/src/index.d.ts +0 -2
  42. package/dist/types/src/index.d.ts.map +1 -1
  43. package/dist/types/src/playwright/playwright.config.d.ts.map +1 -1
  44. package/dist/types/src/playwright/stack-manager.d.ts.map +1 -1
  45. package/dist/types/src/translations.d.ts +10 -10
  46. package/dist/types/src/translations.d.ts.map +1 -1
  47. package/dist/types/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +49 -47
  49. package/src/components/Stack/Stack.stories.tsx +10 -14
  50. package/src/components/Stack/Stack.tsx +216 -172
  51. package/src/components/StackContext.tsx +1 -1
  52. package/src/components/StackItem/MenuSignifier.tsx +2 -9
  53. package/src/components/StackItem/StackItem.stories.tsx +8 -8
  54. package/src/components/StackItem/StackItem.tsx +48 -31
  55. package/src/components/StackItem/StackItemContent.tsx +23 -44
  56. package/src/components/StackItem/StackItemDragHandle.tsx +4 -3
  57. package/src/components/StackItem/StackItemHeading.tsx +14 -21
  58. package/src/components/StackItem/StackItemResizeHandle.tsx +1 -2
  59. package/src/components/StackItem/StackItemSigil.tsx +10 -7
  60. package/src/components/index.ts +2 -2
  61. package/src/hooks/useStackDropForElements.ts +60 -46
  62. package/src/index.ts +0 -4
  63. package/src/playwright/playwright.config.ts +1 -1
  64. package/src/translations.ts +9 -9
  65. package/dist/lib/browser/chunk-3V2YUQK5.mjs +0 -1375
  66. package/dist/lib/browser/chunk-3V2YUQK5.mjs.map +0 -7
  67. package/dist/lib/browser/testing/index.mjs +0 -31
  68. package/dist/lib/browser/testing/index.mjs.map +0 -7
  69. package/dist/lib/node-esm/chunk-HE3BRF7A.mjs +0 -1377
  70. package/dist/lib/node-esm/chunk-HE3BRF7A.mjs.map +0 -7
  71. package/dist/lib/node-esm/testing/index.mjs +0 -32
  72. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  73. package/dist/types/src/components/Image/Image.d.ts +0 -11
  74. package/dist/types/src/components/Image/Image.d.ts.map +0 -1
  75. package/dist/types/src/components/Image/Image.stories.d.ts +0 -31
  76. package/dist/types/src/components/Image/Image.stories.d.ts.map +0 -1
  77. package/dist/types/src/components/Image/index.d.ts +0 -2
  78. package/dist/types/src/components/Image/index.d.ts.map +0 -1
  79. package/dist/types/src/components/defs.d.ts.map +0 -1
  80. package/dist/types/src/components/deprecated/LayoutControls.d.ts +0 -19
  81. package/dist/types/src/components/deprecated/LayoutControls.d.ts.map +0 -1
  82. package/dist/types/src/exemplars/Card/Card.d.ts +0 -58
  83. package/dist/types/src/exemplars/Card/Card.d.ts.map +0 -1
  84. package/dist/types/src/exemplars/Card/Card.stories.d.ts +0 -44
  85. package/dist/types/src/exemplars/Card/Card.stories.d.ts.map +0 -1
  86. package/dist/types/src/exemplars/Card/CardDragPreview.d.ts +0 -6
  87. package/dist/types/src/exemplars/Card/CardDragPreview.d.ts.map +0 -1
  88. package/dist/types/src/exemplars/Card/fragments.d.ts +0 -13
  89. package/dist/types/src/exemplars/Card/fragments.d.ts.map +0 -1
  90. package/dist/types/src/exemplars/Card/index.d.ts +0 -4
  91. package/dist/types/src/exemplars/Card/index.d.ts.map +0 -1
  92. package/dist/types/src/exemplars/CardStack/CardStack.d.ts +0 -40
  93. package/dist/types/src/exemplars/CardStack/CardStack.d.ts.map +0 -1
  94. package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts +0 -13
  95. package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +0 -1
  96. package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts +0 -9
  97. package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts.map +0 -1
  98. package/dist/types/src/exemplars/CardStack/index.d.ts +0 -3
  99. package/dist/types/src/exemplars/CardStack/index.d.ts.map +0 -1
  100. package/dist/types/src/exemplars/index.d.ts +0 -3
  101. package/dist/types/src/exemplars/index.d.ts.map +0 -1
  102. package/dist/types/src/testing/CardContainer.d.ts +0 -6
  103. package/dist/types/src/testing/CardContainer.d.ts.map +0 -1
  104. package/dist/types/src/testing/index.d.ts +0 -2
  105. package/dist/types/src/testing/index.d.ts.map +0 -1
  106. package/src/components/Image/Image.stories.tsx +0 -58
  107. package/src/components/Image/Image.tsx +0 -137
  108. package/src/components/Image/index.ts +0 -5
  109. package/src/components/deprecated/LayoutControls.tsx +0 -109
  110. package/src/exemplars/Card/Card.stories.tsx +0 -88
  111. package/src/exemplars/Card/Card.tsx +0 -186
  112. package/src/exemplars/Card/CardDragPreview.tsx +0 -22
  113. package/src/exemplars/Card/fragments.ts +0 -24
  114. package/src/exemplars/Card/index.ts +0 -7
  115. package/src/exemplars/CardStack/CardStack.stories.tsx +0 -172
  116. package/src/exemplars/CardStack/CardStack.tsx +0 -136
  117. package/src/exemplars/CardStack/CardStackDragPreview.tsx +0 -61
  118. package/src/exemplars/CardStack/index.ts +0 -6
  119. package/src/exemplars/index.ts +0 -6
  120. package/src/testing/CardContainer.tsx +0 -37
  121. package/src/testing/index.ts +0 -5
  122. /package/src/components/{defs.ts → types.ts} +0 -0
@@ -1,13 +0,0 @@
1
- import '@dxos-theme';
2
- import { type StoryObj } from '@storybook/react-vite';
3
- import React from 'react';
4
- declare const CardStackStory: () => React.JSX.Element;
5
- declare const meta: {
6
- title: string;
7
- component: () => React.JSX.Element;
8
- decorators: import("@storybook/react").Decorator[];
9
- };
10
- export default meta;
11
- type Story = StoryObj<typeof CardStackStory>;
12
- export declare const Default: Story;
13
- //# sourceMappingURL=CardStack.stories.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CardStack.stories.d.ts","sourceRoot":"","sources":["../../../../../src/exemplars/CardStack/CardStack.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAGrB,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAgC,MAAM,OAAO,CAAC;AAyBrD,QAAA,MAAM,cAAc,yBA8HnB,CAAC;AAEF,QAAA,MAAM,IAAI;;;;CAI6B,CAAC;AAExC,eAAe,IAAI,CAAC;AAEpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7C,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC"}
@@ -1,9 +0,0 @@
1
- import React, { type PropsWithChildren } from 'react';
2
- import { type StackProps } from '../../components';
3
- export declare const CardStackDragPreview: {
4
- Root: ({ children }: PropsWithChildren<{}>) => React.JSX.Element;
5
- Heading: ({ children }: PropsWithChildren<{}>) => React.JSX.Element;
6
- Content: ({ children, itemsCount, }: PropsWithChildren<Pick<StackProps, "itemsCount">>) => React.JSX.Element;
7
- Footer: ({ children }: PropsWithChildren<{}>) => React.JSX.Element;
8
- };
9
- //# sourceMappingURL=CardStackDragPreview.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CardStackDragPreview.d.ts","sourceRoot":"","sources":["../../../../../src/exemplars/CardStack/CardStackDragPreview.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAKtD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA8CnD,eAAO,MAAM,oBAAoB;yBA3Ce,iBAAiB,CAAC,EAAE,CAAC;4BAUlB,iBAAiB,CAAC,EAAE,CAAC;yCAmBrE,iBAAiB,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;2BAUF,iBAAiB,CAAC,EAAE,CAAC;CAStE,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from './CardStack';
2
- export * from './CardStackDragPreview';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/exemplars/CardStack/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,wBAAwB,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from './Card';
2
- export * from './CardStack';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/exemplars/index.ts"],"names":[],"mappings":"AAIA,cAAc,QAAQ,CAAC;AACvB,cAAc,aAAa,CAAC"}
@@ -1,6 +0,0 @@
1
- import React, { type PropsWithChildren } from 'react';
2
- export declare const CardContainer: ({ children, icon, role, }: PropsWithChildren<{
3
- icon?: string;
4
- role?: string;
5
- }>) => React.JSX.Element;
6
- //# sourceMappingURL=CardContainer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CardContainer.d.ts","sourceRoot":"","sources":["../../../../src/testing/CardContainer.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,EAAE,KAAK,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAMtD,eAAO,MAAM,aAAa,GAAI,2BAI3B,iBAAiB,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,sBAsBrD,CAAC"}
@@ -1,2 +0,0 @@
1
- export * from './CardContainer';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/testing/index.ts"],"names":[],"mappings":"AAIA,cAAc,iBAAiB,CAAC"}
@@ -1,58 +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 { Image } from './Image';
14
-
15
- faker.seed(1);
16
-
17
- const meta = {
18
- title: 'ui/react-ui-stack/Image',
19
- component: Image,
20
- render: (args) => (
21
- <div className='absolute inset-0 flex place-items-center'>
22
- <Image {...args} />
23
- </div>
24
- ),
25
- decorators: [withTheme],
26
- parameters: {
27
- layout: 'fullscreen',
28
- },
29
- } satisfies Meta<typeof Image>;
30
-
31
- export default meta;
32
-
33
- type Story = StoryObj<typeof meta>;
34
-
35
- export const Default: Story = {
36
- args: {
37
- src: faker.image.url(),
38
- },
39
- };
40
-
41
- /**
42
- * Access to image at 'https://dxos.network/dxos-logotype-blue.png'
43
- * from origin 'http://localhost:9009' has been blocked by CORS policy:
44
- * No 'Access-Control-Allow-Origin' header is present on the requested resource.
45
- */
46
- export const Cors: Story = {
47
- args: {
48
- src: 'https://dxos.network/dxos-logotype-blue.png',
49
- classNames: 'w-[20rem]',
50
- },
51
- };
52
-
53
- export const SVG: Story = {
54
- args: {
55
- src: 'https://dxos.network/bg-kube.svg',
56
- classNames: 'w-[20rem]',
57
- },
58
- };
@@ -1,137 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import React, { type SyntheticEvent, useRef, useState } from 'react';
6
-
7
- import { type ThemedClassName } from '@dxos/react-ui';
8
- import { mx } from '@dxos/react-ui-theme';
9
-
10
- export type ImageProps = ThemedClassName<{
11
- src: string;
12
- alt?: string;
13
- crossOrigin?: 'anonymous' | 'use-credentials' | '';
14
- sampleSize?: number;
15
- contrast?: number;
16
- }>;
17
-
18
- export const Image = ({
19
- classNames,
20
- src,
21
- alt = '',
22
- crossOrigin = 'anonymous',
23
- sampleSize = 64,
24
- contrast = 0.95,
25
- }: ImageProps) => {
26
- const [crossOriginState, setCrossOriginState] = useState<ImageProps['crossOrigin']>(crossOrigin);
27
- const [dominantColor, setDominantColor] = useState<string | undefined>(undefined);
28
- const [imageLoaded, setImageLoaded] = useState<boolean>(false);
29
- const canvasRef = useRef<HTMLCanvasElement>(null);
30
-
31
- // TODO(burdon): Cache?
32
- const extractDominantColor = (img: HTMLImageElement): void => {
33
- const canvas = canvasRef.current;
34
- const ctx = canvas?.getContext('2d');
35
- if (!canvas || !ctx) {
36
- return;
37
- }
38
-
39
- // Draw the image scaled down.
40
- canvas.width = sampleSize;
41
- canvas.height = sampleSize;
42
- ctx.drawImage(img, 0, 0, sampleSize, sampleSize);
43
-
44
- try {
45
- // Get image data.
46
- const imageData = ctx.getImageData(0, 0, sampleSize, sampleSize);
47
- const pixels = imageData.data;
48
-
49
- // Calculate average color with more weight to vibrant colors.
50
- let r = 0;
51
- let g = 0;
52
- let b = 0;
53
- let totalWeight = 0;
54
- for (let i = 0; i < pixels.length; i += 4) {
55
- const red = pixels[i];
56
- const green = pixels[i + 1];
57
- const blue = pixels[i + 2];
58
- const alpha = pixels[i + 3];
59
-
60
- // Skip transparent pixels.
61
- if (alpha === 0) continue;
62
-
63
- // Calculate saturation to weight vibrant colors more.
64
- const max = Math.max(red, green, blue);
65
- const min = Math.min(red, green, blue);
66
- const saturation = max === 0 ? 0 : (max - min) / max;
67
- const weight = 1 + saturation * 2; // Give more weight to saturated colors.
68
-
69
- r += red * weight;
70
- g += green * weight;
71
- b += blue * weight;
72
- totalWeight += weight;
73
- }
74
-
75
- if (totalWeight > 0) {
76
- r = Math.round(r / totalWeight);
77
- g = Math.round(g / totalWeight);
78
- b = Math.round(b / totalWeight);
79
-
80
- // Slightly darken the color for better contrast.
81
- r = Math.round(r * contrast);
82
- g = Math.round(g * contrast);
83
- b = Math.round(b * contrast);
84
- setDominantColor(`rgb(${r}, ${g}, ${b})`);
85
- }
86
- } catch {
87
- setCrossOriginState(undefined);
88
- }
89
- };
90
-
91
- // CORS not supported by server.
92
- const handleImageError = (): void => {
93
- setCrossOriginState(undefined);
94
- };
95
-
96
- const handleImageLoad = (ev: SyntheticEvent<HTMLImageElement>): void => {
97
- const img = ev.target as HTMLImageElement;
98
- extractDominantColor(img);
99
- setImageLoaded(true);
100
- };
101
-
102
- return (
103
- <div
104
- className={mx(`relative flex is-full justify-center overflow-hidden transition-all duration-700`, classNames)}
105
- style={{
106
- backgroundColor: dominantColor,
107
- }}
108
- >
109
- {/* Hidden canvas for color extraction. */}
110
- <canvas ref={canvasRef} style={{ display: 'none' }} aria-hidden='true' />
111
-
112
- {/* Background gradient overlay for smooth transition. */}
113
- <div
114
- className='absolute inset-0 pointer-events-none'
115
- style={{
116
- background: dominantColor
117
- ? `radial-gradient(circle at center, transparent 30%, ${dominantColor} 100%)`
118
- : undefined,
119
- transition: 'opacity 0.7s ease-in-out',
120
- opacity: 0.5,
121
- }}
122
- />
123
-
124
- <img
125
- src={src}
126
- alt={alt}
127
- crossOrigin={crossOriginState}
128
- onError={handleImageError}
129
- onLoad={handleImageLoad}
130
- className={mx('z-10 object-contain transition-opacity duration-500', classNames)}
131
- style={{
132
- opacity: imageLoaded ? 1 : 0,
133
- }}
134
- />
135
- </div>
136
- );
137
- };
@@ -1,5 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- export * from './Image';
@@ -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,88 +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
- faker.seed(0);
16
-
17
- type CardStoryProps = {
18
- title: string;
19
- description: string;
20
- image: string;
21
- showImage: boolean;
22
- showIcon: boolean;
23
- };
24
-
25
- const DefaultStory = ({ title, description, image, showImage, showIcon }: CardStoryProps) => {
26
- return (
27
- <div className='max-is-md'>
28
- <Card.StaticRoot>
29
- <Card.Toolbar>
30
- <Card.DragHandle toolbarItem />
31
- <Card.ToolbarSeparator variant='gap' />
32
- <Card.ToolbarIconButton iconOnly variant='ghost' icon='ph--x--regular' label={'remove card label'} />
33
- </Card.Toolbar>
34
- {showImage && <Card.Poster alt={title} image={image} />}
35
- {!showImage && showIcon && <Card.Poster alt={title} icon='ph--building-office--regular' />}
36
- <Card.Heading>{title}</Card.Heading>
37
- {description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
38
- </Card.StaticRoot>
39
- </div>
40
- );
41
- };
42
-
43
- const meta = {
44
- title: 'ui/react-ui-stack/Card',
45
- render: DefaultStory,
46
- decorators: [withTheme],
47
- parameters: {
48
- layout: 'centered',
49
- },
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
- } satisfies Meta<typeof DefaultStory>;
73
-
74
- export default meta;
75
-
76
- type Story = StoryObj<typeof meta>;
77
-
78
- const image = faker.image.url();
79
-
80
- export const Default: Story = {
81
- args: {
82
- title: faker.commerce.productName(),
83
- description: faker.lorem.paragraph(),
84
- image,
85
- showImage: true,
86
- showIcon: true,
87
- },
88
- };
@@ -1,186 +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 { Image, 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-1',
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 <Image classNames={[`dx-card__poster is-full __bs-auto`, aspect]} src={props.image} alt={props.alt} />;
130
- }
131
-
132
- if (props.icon) {
133
- return (
134
- <div
135
- role='image'
136
- className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect)}
137
- aria-label={props.alt}
138
- >
139
- <Icon icon={props.icon} size={10} />
140
- </div>
141
- );
142
- }
143
- };
144
-
145
- const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
146
- ({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
147
- const Root = asChild ? Slot : 'div';
148
- const rootProps = asChild
149
- ? { classNames: [cardChrome, classNames] }
150
- : { className: mx(cardChrome, classNames), role };
151
- return (
152
- <Root {...props} {...rootProps} ref={forwardedRef}>
153
- {children}
154
- </Root>
155
- );
156
- },
157
- );
158
-
159
- const CardText = forwardRef<HTMLDivElement, SharedCardProps>(
160
- ({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
161
- const Root = asChild ? Slot : 'div';
162
- const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
163
- return (
164
- <Root {...props} {...rootProps} ref={forwardedRef}>
165
- {children}
166
- </Root>
167
- );
168
- },
169
- );
170
-
171
- export const Card = {
172
- StaticRoot: CardStaticRoot,
173
- SurfaceRoot: CardSurfaceRoot,
174
- Heading: CardHeading,
175
- Toolbar: CardToolbar,
176
- ToolbarIconButton: CardToolbarIconButton,
177
- ToolbarSeparator: CardToolbarSeparator,
178
- DragHandle: CardDragHandle,
179
- DragPreview: CardDragPreview,
180
- Menu: CardMenu,
181
- Poster: CardPoster,
182
- Chrome: CardChrome,
183
- Text: CardText,
184
- };
185
-
186
- 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
- };