@dxos/react-ui-stack 0.8.4-main.ead640a → 0.8.4-main.ef1bc66f44
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 +920 -57
- package/dist/lib/browser/index.mjs.map +4 -4
- 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/node-esm/index.mjs +921 -57
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/playwright/index.mjs +10 -23
- package/dist/lib/node-esm/playwright/index.mjs.map +2 -2
- package/dist/types/src/{exemplars → components}/CardStack/CardStack.d.ts +18 -13
- package/dist/types/src/components/CardStack/CardStack.d.ts.map +1 -0
- package/dist/types/src/components/CardStack/CardStack.stories.d.ts.map +1 -0
- package/dist/types/src/{exemplars → components}/CardStack/CardStackDragPreview.d.ts +4 -1
- package/dist/types/src/components/CardStack/CardStackDragPreview.d.ts.map +1 -0
- package/dist/types/src/components/CardStack/index.d.ts.map +1 -0
- package/dist/types/src/components/Stack/Stack.d.ts +7 -7
- package/dist/types/src/components/Stack/Stack.d.ts.map +1 -1
- package/dist/types/src/components/StackContext.d.ts +1 -1
- package/dist/types/src/components/StackContext.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.d.ts +5 -9
- package/dist/types/src/components/StackItem/StackItem.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItem.stories.d.ts.map +1 -1
- package/dist/types/src/components/StackItem/StackItemContent.d.ts +2 -45
- package/dist/types/src/components/StackItem/StackItemContent.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/deprecated/LayoutControls.d.ts +3 -0
- package/dist/types/src/components/deprecated/LayoutControls.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +2 -2
- 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 +2 -2
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +40 -37
- package/src/{exemplars → components}/CardStack/CardStack.stories.tsx +10 -10
- package/src/{exemplars → components}/CardStack/CardStack.tsx +113 -57
- package/src/{exemplars → components}/CardStack/CardStackDragPreview.tsx +12 -9
- package/src/components/Stack/Stack.stories.tsx +2 -2
- package/src/components/Stack/Stack.tsx +19 -17
- package/src/components/StackContext.tsx +1 -1
- package/src/components/StackItem/StackItem.stories.tsx +6 -4
- package/src/components/StackItem/StackItem.tsx +24 -12
- package/src/components/StackItem/StackItemContent.tsx +19 -50
- package/src/components/StackItem/StackItemHeading.tsx +1 -1
- package/src/components/StackItem/StackItemSigil.tsx +3 -3
- package/src/components/deprecated/LayoutControls.tsx +3 -0
- package/src/components/index.ts +2 -2
- package/src/hooks/useStackDropForElements.ts +58 -44
- package/src/index.ts +0 -3
- package/src/playwright/playwright.config.ts +1 -1
- package/src/translations.ts +1 -1
- package/dist/lib/browser/chunk-T4ZCIFCF.mjs +0 -1448
- package/dist/lib/browser/chunk-T4ZCIFCF.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-G2QYUH52.mjs +0 -1450
- package/dist/lib/node-esm/chunk-G2QYUH52.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/Image/Image.d.ts +0 -14
- package/dist/types/src/components/Image/Image.d.ts.map +0 -1
- package/dist/types/src/components/Image/Image.stories.d.ts +0 -32
- package/dist/types/src/components/Image/Image.stories.d.ts.map +0 -1
- package/dist/types/src/components/Image/index.d.ts +0 -2
- package/dist/types/src/components/Image/index.d.ts.map +0 -1
- package/dist/types/src/components/defs.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/Card.d.ts +0 -66
- package/dist/types/src/exemplars/Card/Card.d.ts.map +0 -1
- package/dist/types/src/exemplars/Card/Card.stories.d.ts +0 -21
- 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.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStack.stories.d.ts.map +0 -1
- package/dist/types/src/exemplars/CardStack/CardStackDragPreview.d.ts.map +0 -1
- 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/Image/Image.stories.tsx +0 -78
- package/src/components/Image/Image.tsx +0 -192
- package/src/components/Image/index.ts +0 -5
- package/src/exemplars/Card/Card.stories.tsx +0 -64
- package/src/exemplars/Card/Card.tsx +0 -204
- 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/index.ts +0 -6
- package/src/testing/CardContainer.tsx +0 -37
- package/src/testing/index.ts +0 -5
- /package/dist/types/src/{exemplars → components}/CardStack/CardStack.stories.d.ts +0 -0
- /package/dist/types/src/{exemplars → components}/CardStack/index.d.ts +0 -0
- /package/src/{exemplars → components}/CardStack/index.ts +0 -0
- /package/src/components/{defs.ts → types.ts} +0 -0
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import React, { useMemo } from 'react';
|
|
7
|
-
|
|
8
|
-
import { faker } from '@dxos/random';
|
|
9
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
|
-
|
|
11
|
-
import { Image } from './Image';
|
|
12
|
-
|
|
13
|
-
const seed = Math.random();
|
|
14
|
-
|
|
15
|
-
faker.seed(seed);
|
|
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: 'centered',
|
|
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
|
-
};
|
|
59
|
-
|
|
60
|
-
export const Many: Story = {
|
|
61
|
-
args: {
|
|
62
|
-
src: 'https://dxos.network/bg-kube.svg',
|
|
63
|
-
},
|
|
64
|
-
render: () => {
|
|
65
|
-
const images = useMemo(
|
|
66
|
-
() => Array.from({ length: 9 }, (_, i) => `https://picsum.photos/seed/${seed + i}/500/500`),
|
|
67
|
-
[],
|
|
68
|
-
);
|
|
69
|
-
console.log(images);
|
|
70
|
-
return (
|
|
71
|
-
<div className='is-[60rem] grid grid-cols-3 grid-rows-3 gap-8'>
|
|
72
|
-
{images.map((src, i) => (
|
|
73
|
-
<Image key={i} src={src} classNames='is-[18rem] bs-[12rem]' />
|
|
74
|
-
))}
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
},
|
|
78
|
-
};
|
|
@@ -1,192 +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
|
-
{
|
|
12
|
-
src: string;
|
|
13
|
-
alt?: string;
|
|
14
|
-
crossOrigin?: 'anonymous' | 'use-credentials' | '';
|
|
15
|
-
} & ColorOptions
|
|
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
|
-
// CORS not supported by server.
|
|
32
|
-
const handleImageError = (): void => {
|
|
33
|
-
setCrossOriginState(undefined);
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const handleImageLoad = ({ target }: SyntheticEvent<HTMLImageElement>): void => {
|
|
37
|
-
const img = target as HTMLImageElement;
|
|
38
|
-
if (!canvasRef.current) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const color = extractDominantColor(canvasRef.current, img, { sampleSize, contrast });
|
|
44
|
-
if (color) {
|
|
45
|
-
setDominantColor(`rgb(${color[0]}, ${color[1]}, ${color[2]})`);
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
setCrossOriginState(undefined);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
setImageLoaded(true);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
return (
|
|
55
|
-
<div
|
|
56
|
-
className={mx(`relative flex is-full justify-center overflow-hidden transition-all duration-700`, classNames)}
|
|
57
|
-
style={{
|
|
58
|
-
backgroundColor: dominantColor,
|
|
59
|
-
}}
|
|
60
|
-
>
|
|
61
|
-
{/* Hidden canvas for color extraction. */}
|
|
62
|
-
<canvas ref={canvasRef} style={{ display: 'none' }} aria-hidden='true' />
|
|
63
|
-
|
|
64
|
-
{/* Background gradient overlay for smooth transition. */}
|
|
65
|
-
<div
|
|
66
|
-
className='absolute inset-0 pointer-events-none'
|
|
67
|
-
style={{
|
|
68
|
-
background: dominantColor
|
|
69
|
-
? `radial-gradient(circle at center, transparent 30%, ${dominantColor} 100%)`
|
|
70
|
-
: undefined,
|
|
71
|
-
transition: 'opacity 0.7s ease-in-out',
|
|
72
|
-
opacity: 0.5,
|
|
73
|
-
}}
|
|
74
|
-
/>
|
|
75
|
-
|
|
76
|
-
<img
|
|
77
|
-
src={src}
|
|
78
|
-
alt={alt}
|
|
79
|
-
crossOrigin={crossOriginState}
|
|
80
|
-
onError={handleImageError}
|
|
81
|
-
onLoad={handleImageLoad}
|
|
82
|
-
className={mx('z-10 object-contain transition-opacity duration-500', classNames)}
|
|
83
|
-
style={{
|
|
84
|
-
opacity: imageLoaded ? 1 : 0,
|
|
85
|
-
}}
|
|
86
|
-
/>
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
type ColorOptions = {
|
|
92
|
-
sampleSize?: number;
|
|
93
|
-
contrast?: number;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
// TODO(burdon): Cache?
|
|
97
|
-
const extractDominantColor = (
|
|
98
|
-
canvas: HTMLCanvasElement,
|
|
99
|
-
img: HTMLImageElement,
|
|
100
|
-
{ sampleSize = 64, contrast = 0.95 }: ColorOptions,
|
|
101
|
-
): [number, number, number] | null => {
|
|
102
|
-
const ctx = canvas.getContext('2d');
|
|
103
|
-
if (!ctx) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Draw the image scaled down.
|
|
108
|
-
canvas.width = sampleSize;
|
|
109
|
-
canvas.height = sampleSize;
|
|
110
|
-
ctx.drawImage(img, 0, 0, sampleSize, sampleSize);
|
|
111
|
-
|
|
112
|
-
// Get image data.
|
|
113
|
-
const imageData = ctx.getImageData(0, 0, sampleSize, sampleSize);
|
|
114
|
-
const pixels = imageData.data;
|
|
115
|
-
|
|
116
|
-
// Check for transparent background.
|
|
117
|
-
if (isTransparent(pixels, sampleSize)) {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let r = 0;
|
|
122
|
-
let g = 0;
|
|
123
|
-
let b = 0;
|
|
124
|
-
let totalWeight = 0;
|
|
125
|
-
|
|
126
|
-
// Calculate average color with more weight to vibrant colors.
|
|
127
|
-
for (let i = 0; i < pixels.length; i += 4) {
|
|
128
|
-
const red = pixels[i];
|
|
129
|
-
const green = pixels[i + 1];
|
|
130
|
-
const blue = pixels[i + 2];
|
|
131
|
-
const alpha = pixels[i + 3];
|
|
132
|
-
|
|
133
|
-
// Skip transparent pixels.
|
|
134
|
-
if (alpha === 0) continue;
|
|
135
|
-
|
|
136
|
-
// Calculate saturation to weight vibrant colors more.
|
|
137
|
-
const max = Math.max(red, green, blue);
|
|
138
|
-
const min = Math.min(red, green, blue);
|
|
139
|
-
// Give more weight to saturated colors.
|
|
140
|
-
const saturation = max === 0 ? 0 : (max - min) / max;
|
|
141
|
-
const weight = 1 + saturation * 2;
|
|
142
|
-
|
|
143
|
-
r += red * weight;
|
|
144
|
-
g += green * weight;
|
|
145
|
-
b += blue * weight;
|
|
146
|
-
totalWeight += weight;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
if (totalWeight > 0) {
|
|
150
|
-
// Slightly darken the color for better contrast.
|
|
151
|
-
r = Math.round(Math.round(r / totalWeight) * contrast);
|
|
152
|
-
g = Math.round(Math.round(g / totalWeight) * contrast);
|
|
153
|
-
b = Math.round(Math.round(b / totalWeight) * contrast);
|
|
154
|
-
return [r, g, b];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return null;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Detects if an image has a transparent background by examining edge pixels.
|
|
162
|
-
* @param pixels - Image pixel data from canvas
|
|
163
|
-
* @param sampleSize - Size of the sampled image
|
|
164
|
-
* @param threshold - Percentage threshold for considering background transparent (default: 0.5)
|
|
165
|
-
* @returns True if the image has a transparent background
|
|
166
|
-
*/
|
|
167
|
-
const isTransparent = (pixels: Uint8ClampedArray, sampleSize: number, threshold: number = 0.5): boolean => {
|
|
168
|
-
let edgeTransparentPixels = 0;
|
|
169
|
-
const edgePixels = sampleSize * 4 - 4; // Perimeter minus corners counted twice.
|
|
170
|
-
|
|
171
|
-
for (let x = 0; x < sampleSize; x++) {
|
|
172
|
-
// Top edge.
|
|
173
|
-
const topIndex = x * 4;
|
|
174
|
-
if (pixels[topIndex + 3] === 0) edgeTransparentPixels++;
|
|
175
|
-
|
|
176
|
-
// Bottom edge.
|
|
177
|
-
const bottomIndex = ((sampleSize - 1) * sampleSize + x) * 4;
|
|
178
|
-
if (pixels[bottomIndex + 3] === 0) edgeTransparentPixels++;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
for (let y = 1; y < sampleSize - 1; y++) {
|
|
182
|
-
// Left edge.
|
|
183
|
-
const leftIndex = y * sampleSize * 4;
|
|
184
|
-
if (pixels[leftIndex + 3] === 0) edgeTransparentPixels++;
|
|
185
|
-
|
|
186
|
-
// Right edge.
|
|
187
|
-
const rightIndex = (y * sampleSize + sampleSize - 1) * 4;
|
|
188
|
-
if (pixels[rightIndex + 3] === 0) edgeTransparentPixels++;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return edgeTransparentPixels / edgePixels > threshold;
|
|
192
|
-
};
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
|
6
|
-
import React from 'react';
|
|
7
|
-
|
|
8
|
-
import { faker } from '@dxos/random';
|
|
9
|
-
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
|
-
|
|
11
|
-
import { Card } from './Card';
|
|
12
|
-
|
|
13
|
-
faker.seed(0);
|
|
14
|
-
|
|
15
|
-
type CardStoryProps = {
|
|
16
|
-
title: string;
|
|
17
|
-
description: string;
|
|
18
|
-
image: string;
|
|
19
|
-
showImage: boolean;
|
|
20
|
-
showIcon: boolean;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const DefaultStory = ({ title, description, image, showImage, showIcon }: CardStoryProps) => {
|
|
24
|
-
return (
|
|
25
|
-
<div className='max-is-md'>
|
|
26
|
-
<Card.StaticRoot>
|
|
27
|
-
<Card.Toolbar>
|
|
28
|
-
<Card.DragHandle toolbarItem />
|
|
29
|
-
<Card.ToolbarSeparator variant='gap' />
|
|
30
|
-
<Card.ToolbarIconButton iconOnly variant='ghost' icon='ph--x--regular' label={'remove card label'} />
|
|
31
|
-
</Card.Toolbar>
|
|
32
|
-
{showImage && <Card.Poster alt={title} image={image} />}
|
|
33
|
-
{!showImage && showIcon && <Card.Poster alt={title} icon='ph--building-office--regular' />}
|
|
34
|
-
<Card.Heading>{title}</Card.Heading>
|
|
35
|
-
{description && <Card.Text classNames='line-clamp-2'>{description}</Card.Text>}
|
|
36
|
-
</Card.StaticRoot>
|
|
37
|
-
</div>
|
|
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
|
-
},
|
|
64
|
-
};
|
|
@@ -1,204 +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
|
-
/**
|
|
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
|
-
|
|
35
|
-
const CardStaticRoot = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
36
|
-
({ children, classNames, asChild, role = 'group', ...props }, forwardedRef) => {
|
|
37
|
-
const Root = asChild ? Slot : 'div';
|
|
38
|
-
const rootProps = asChild ? { classNames: [cardRoot, classNames] } : { className: mx(cardRoot, classNames), role };
|
|
39
|
-
return (
|
|
40
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
41
|
-
{children}
|
|
42
|
-
</Root>
|
|
43
|
-
);
|
|
44
|
-
},
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* This should be used by Surface fulfillments in cases where the content may or may not already be encapsulated (e.g.
|
|
49
|
-
* in a Popover) and knows this based on the `role` it receives. This will render a `Card.StaticRoot` by default, otherwise
|
|
50
|
-
* it will render a `div` primitive with the appropriate styling for specific handled situations.
|
|
51
|
-
*/
|
|
52
|
-
const CardSurfaceRoot = ({
|
|
53
|
-
role = 'never',
|
|
54
|
-
children,
|
|
55
|
-
classNames,
|
|
56
|
-
}: ThemedClassName<PropsWithChildren<{ role?: string }>>) => {
|
|
57
|
-
if (['card--popover', 'card--intrinsic', 'card--extrinsic'].includes(role)) {
|
|
58
|
-
return (
|
|
59
|
-
<div
|
|
60
|
-
className={mx(
|
|
61
|
-
role === 'card--popover'
|
|
62
|
-
? 'popover-card-width'
|
|
63
|
-
: ['card--intrinsic', 'card--extrinsic'].includes(role)
|
|
64
|
-
? 'contents'
|
|
65
|
-
: '',
|
|
66
|
-
classNames,
|
|
67
|
-
)}
|
|
68
|
-
>
|
|
69
|
-
{children}
|
|
70
|
-
</div>
|
|
71
|
-
);
|
|
72
|
-
} else {
|
|
73
|
-
return (
|
|
74
|
-
<CardStaticRoot
|
|
75
|
-
classNames={[
|
|
76
|
-
role === 'card--transclusion' && 'mlb-1',
|
|
77
|
-
role === 'card--transclusion' && hoverableControls,
|
|
78
|
-
classNames,
|
|
79
|
-
]}
|
|
80
|
-
>
|
|
81
|
-
{children}
|
|
82
|
-
</CardStaticRoot>
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const CardHeading = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
88
|
-
({ children, classNames, asChild, role = 'heading', ...props }, forwardedRef) => {
|
|
89
|
-
const Root = asChild ? Slot : 'div';
|
|
90
|
-
const rootProps = asChild
|
|
91
|
-
? { classNames: [cardHeading, cardText, classNames] }
|
|
92
|
-
: { className: mx(cardHeading, cardText, classNames), role };
|
|
93
|
-
return (
|
|
94
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
95
|
-
{children}
|
|
96
|
-
</Root>
|
|
97
|
-
);
|
|
98
|
-
},
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
const CardToolbar = forwardRef<HTMLDivElement, ToolbarRootProps>(({ children, classNames, ...props }, forwardedRef) => {
|
|
102
|
-
return (
|
|
103
|
-
<Toolbar.Root {...props} classNames={['bg-transparent density-coarse', classNames]} ref={forwardedRef}>
|
|
104
|
-
{children}
|
|
105
|
-
</Toolbar.Root>
|
|
106
|
-
);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const CardToolbarIconButton = Toolbar.IconButton;
|
|
110
|
-
const CardToolbarSeparator = Toolbar.Separator;
|
|
111
|
-
|
|
112
|
-
const CardDragHandle = forwardRef<HTMLButtonElement, { toolbarItem?: boolean }>(({ toolbarItem }, forwardedRef) => {
|
|
113
|
-
const { t } = useTranslation(translationKey);
|
|
114
|
-
const Root = toolbarItem ? Toolbar.IconButton : IconButton;
|
|
115
|
-
return (
|
|
116
|
-
<Root
|
|
117
|
-
iconOnly
|
|
118
|
-
icon='ph--dots-six-vertical--regular'
|
|
119
|
-
variant='ghost'
|
|
120
|
-
label={t('drag handle label')}
|
|
121
|
-
classNames='pli-2'
|
|
122
|
-
ref={forwardedRef}
|
|
123
|
-
/>
|
|
124
|
-
);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const CardDragPreview = StackItem.DragPreview;
|
|
128
|
-
|
|
129
|
-
const CardMenu = Primitive.div as FC<ComponentPropsWithRef<'div'>>;
|
|
130
|
-
|
|
131
|
-
type CardPosterProps = {
|
|
132
|
-
alt: string;
|
|
133
|
-
aspect?: 'video' | 'auto';
|
|
134
|
-
} & Partial<{ image: string; icon: string }>;
|
|
135
|
-
|
|
136
|
-
const CardPoster = (props: CardPosterProps) => {
|
|
137
|
-
const aspect = props.aspect === 'auto' ? 'aspect-auto' : 'aspect-video';
|
|
138
|
-
if (props.image) {
|
|
139
|
-
return <Image classNames={[`dx-card__poster is-full`, aspect]} src={props.image} alt={props.alt} />;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (props.icon) {
|
|
143
|
-
return (
|
|
144
|
-
<div
|
|
145
|
-
role='image'
|
|
146
|
-
className={mx(`dx-card__poster grid place-items-center bg-inputSurface text-subdued`, aspect)}
|
|
147
|
-
aria-label={props.alt}
|
|
148
|
-
>
|
|
149
|
-
<Icon icon={props.icon} size={10} />
|
|
150
|
-
</div>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const CardChrome = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
156
|
-
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
157
|
-
const Root = asChild ? Slot : 'div';
|
|
158
|
-
const rootProps = asChild
|
|
159
|
-
? { classNames: [cardChrome, classNames] }
|
|
160
|
-
: { className: mx(cardChrome, classNames), role };
|
|
161
|
-
return (
|
|
162
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
163
|
-
{children}
|
|
164
|
-
</Root>
|
|
165
|
-
);
|
|
166
|
-
},
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
const CardText = forwardRef<HTMLDivElement, SharedCardProps>(
|
|
170
|
-
({ children, classNames, asChild, role = 'none', ...props }, forwardedRef) => {
|
|
171
|
-
const Root = asChild ? Slot : 'div';
|
|
172
|
-
const rootProps = asChild ? { classNames: [cardText, classNames] } : { className: mx(cardText, classNames), role };
|
|
173
|
-
return (
|
|
174
|
-
<Root {...props} {...rootProps} ref={forwardedRef}>
|
|
175
|
-
{children}
|
|
176
|
-
</Root>
|
|
177
|
-
);
|
|
178
|
-
},
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
export const Card = {
|
|
182
|
-
StaticRoot: CardStaticRoot,
|
|
183
|
-
SurfaceRoot: CardSurfaceRoot,
|
|
184
|
-
Heading: CardHeading,
|
|
185
|
-
Toolbar: CardToolbar,
|
|
186
|
-
ToolbarIconButton: CardToolbarIconButton,
|
|
187
|
-
ToolbarSeparator: CardToolbarSeparator,
|
|
188
|
-
DragHandle: CardDragHandle,
|
|
189
|
-
DragPreview: CardDragPreview,
|
|
190
|
-
Menu: CardMenu,
|
|
191
|
-
Poster: CardPoster,
|
|
192
|
-
Chrome: CardChrome,
|
|
193
|
-
Text: CardText,
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
export {
|
|
197
|
-
cardRoot,
|
|
198
|
-
cardHeading,
|
|
199
|
-
cardText,
|
|
200
|
-
cardChrome,
|
|
201
|
-
cardSpacing,
|
|
202
|
-
cardStackDefaultInlineSizeRem,
|
|
203
|
-
cardDefaultInlineSize,
|
|
204
|
-
};
|
|
@@ -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 [&_.dx-button]:pis-[calc(var(--dx-cardSpacingInline)-var(--dx-cardSpacingChrome))]';
|
package/src/exemplars/index.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import React, { type PropsWithChildren } from 'react';
|
|
6
|
-
|
|
7
|
-
import { ExtrinsicCardContainer, IntrinsicCardContainer, PopoverCardContainer } from '@dxos/storybook-utils';
|
|
8
|
-
|
|
9
|
-
import { Card } from '../exemplars';
|
|
10
|
-
|
|
11
|
-
export const CardContainer = ({
|
|
12
|
-
children,
|
|
13
|
-
icon = 'ph--placeholder--regular',
|
|
14
|
-
role,
|
|
15
|
-
}: PropsWithChildren<{ icon?: string; role?: string }>) => {
|
|
16
|
-
switch (role) {
|
|
17
|
-
case 'card--popover':
|
|
18
|
-
return <PopoverCardContainer icon={icon}>{children}</PopoverCardContainer>;
|
|
19
|
-
|
|
20
|
-
case 'card--extrinsic':
|
|
21
|
-
return (
|
|
22
|
-
<ExtrinsicCardContainer>
|
|
23
|
-
<Card.StaticRoot>{children}</Card.StaticRoot>
|
|
24
|
-
</ExtrinsicCardContainer>
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
case 'card--intrinsic':
|
|
28
|
-
return (
|
|
29
|
-
<IntrinsicCardContainer>
|
|
30
|
-
<Card.StaticRoot>{children}</Card.StaticRoot>
|
|
31
|
-
</IntrinsicCardContainer>
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
default:
|
|
35
|
-
return <Card.StaticRoot>{children}</Card.StaticRoot>;
|
|
36
|
-
}
|
|
37
|
-
};
|
package/src/testing/index.ts
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|