@dxos/react-ui-card 0.8.2 → 0.11.1

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 (74) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/index.mjs +288 -0
  3. package/dist/lib/index.mjs.map +1 -0
  4. package/dist/lib/translations.mjs +11 -0
  5. package/dist/lib/translations.mjs.map +1 -0
  6. package/dist/types/src/components/Avatar/Avatar.d.ts +14 -0
  7. package/dist/types/src/components/Avatar/Avatar.d.ts.map +1 -0
  8. package/dist/types/src/components/Avatar/Avatar.stories.d.ts +15 -0
  9. package/dist/types/src/components/Avatar/Avatar.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/Avatar/avatar-name.d.ts +6 -0
  11. package/dist/types/src/components/Avatar/avatar-name.d.ts.map +1 -0
  12. package/dist/types/src/components/Avatar/index.d.ts +3 -0
  13. package/dist/types/src/components/Avatar/index.d.ts.map +1 -0
  14. package/dist/types/src/components/CardTile/CardTile.d.ts +46 -0
  15. package/dist/types/src/components/CardTile/CardTile.d.ts.map +1 -0
  16. package/dist/types/src/components/CardTile/CardTile.stories.d.ts +27 -0
  17. package/dist/types/src/components/CardTile/CardTile.stories.d.ts.map +1 -0
  18. package/dist/types/src/components/CardTile/index.d.ts +2 -0
  19. package/dist/types/src/components/CardTile/index.d.ts.map +1 -0
  20. package/dist/types/src/components/Row/Row.d.ts +100 -0
  21. package/dist/types/src/components/Row/Row.d.ts.map +1 -0
  22. package/dist/types/src/components/Row/Row.stories.d.ts +23 -0
  23. package/dist/types/src/components/Row/Row.stories.d.ts.map +1 -0
  24. package/dist/types/src/components/Row/index.d.ts +2 -0
  25. package/dist/types/src/components/Row/index.d.ts.map +1 -0
  26. package/dist/types/src/components/index.d.ts +3 -1
  27. package/dist/types/src/components/index.d.ts.map +1 -1
  28. package/dist/types/src/hooks/index.d.ts +2 -0
  29. package/dist/types/src/hooks/index.d.ts.map +1 -0
  30. package/dist/types/src/hooks/useActorContact.d.ts +5 -0
  31. package/dist/types/src/hooks/useActorContact.d.ts.map +1 -0
  32. package/dist/types/src/index.d.ts +2 -1
  33. package/dist/types/src/index.d.ts.map +1 -1
  34. package/dist/types/src/translations.d.ts +11 -0
  35. package/dist/types/src/translations.d.ts.map +1 -0
  36. package/dist/types/src/util.d.ts +3 -0
  37. package/dist/types/src/util.d.ts.map +1 -0
  38. package/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/package.json +39 -30
  40. package/src/components/Avatar/Avatar.stories.tsx +31 -0
  41. package/src/components/Avatar/Avatar.tsx +39 -0
  42. package/src/components/Avatar/avatar-name.ts +19 -0
  43. package/src/components/Avatar/index.ts +7 -0
  44. package/src/components/CardTile/CardTile.stories.tsx +54 -0
  45. package/src/components/CardTile/CardTile.tsx +103 -0
  46. package/src/components/CardTile/index.ts +5 -0
  47. package/src/components/Row/Row.stories.tsx +65 -0
  48. package/src/components/Row/Row.tsx +379 -0
  49. package/src/components/Row/index.ts +5 -0
  50. package/src/components/index.ts +4 -2
  51. package/src/hooks/index.ts +5 -0
  52. package/src/hooks/useActorContact.ts +31 -0
  53. package/src/index.ts +3 -2
  54. package/src/translations.ts +19 -0
  55. package/src/util.ts +7 -0
  56. package/README.md +0 -3
  57. package/dist/lib/browser/index.mjs +0 -200
  58. package/dist/lib/browser/index.mjs.map +0 -7
  59. package/dist/lib/browser/meta.json +0 -1
  60. package/dist/lib/node/index.cjs +0 -243
  61. package/dist/lib/node/index.cjs.map +0 -7
  62. package/dist/lib/node/meta.json +0 -1
  63. package/dist/lib/node-esm/index.mjs +0 -202
  64. package/dist/lib/node-esm/index.mjs.map +0 -7
  65. package/dist/lib/node-esm/meta.json +0 -1
  66. package/dist/types/src/components/Card.d.ts +0 -52
  67. package/dist/types/src/components/Card.d.ts.map +0 -1
  68. package/dist/types/src/components/Card.stories.d.ts +0 -57
  69. package/dist/types/src/components/Card.stories.d.ts.map +0 -1
  70. package/dist/types/src/theme.d.ts +0 -31
  71. package/dist/types/src/theme.d.ts.map +0 -1
  72. package/src/components/Card.stories.tsx +0 -217
  73. package/src/components/Card.tsx +0 -149
  74. package/src/theme.ts +0 -101
@@ -0,0 +1,379 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { format, intervalToDuration } from 'date-fns';
6
+ import React, { type MouseEvent, useCallback, useRef } from 'react';
7
+
8
+ import { type Database, Obj } from '@dxos/echo';
9
+ import { EID, type URI } from '@dxos/keys';
10
+ import {
11
+ Card,
12
+ DxAnchorActivate,
13
+ Icon,
14
+ IconButton,
15
+ type IconButtonProps,
16
+ SystemIconButton,
17
+ Tag,
18
+ useTranslation,
19
+ } from '@dxos/react-ui';
20
+ import { type Actor, type Message } from '@dxos/types';
21
+ import { toHue } from '@dxos/ui-theme';
22
+
23
+ import { useActorContact } from '../../hooks';
24
+ import { translationKey } from '../../translations';
25
+ import { Avatar, avatarName } from '../Avatar';
26
+
27
+ /**
28
+ * Shared Card-row primitives rendered inside a `Card.Body`. These are the single source for the
29
+ * person/date/tags/ref/star rows used across the inbox tiles (`EventStack`/`InboxStack`), the preview
30
+ * cards (`EventCard`/`MessageCard`), and the article headers — so a row of each kind is defined exactly
31
+ * once and every surface composes from it.
32
+ */
33
+
34
+ //
35
+ // AnchorIconButton — internal helper, not exported on Row.
36
+ //
37
+
38
+ type AnchorIconButtonProps = {
39
+ compact?: boolean;
40
+ icon: string;
41
+ fallbackIcon?: string;
42
+ label: string;
43
+ fallbackLabel?: string;
44
+ title?: string;
45
+ value?: URI.URI;
46
+ size?: 4 | 5 | 6;
47
+ onClick?: () => void;
48
+ };
49
+
50
+ /**
51
+ * Icon-only button that opens an ECHO object's preview card via `DxAnchorActivate`.
52
+ * Falls back to `onClick` when `value` is absent.
53
+ */
54
+ const AnchorIconButton = ({
55
+ compact,
56
+ icon,
57
+ fallbackIcon,
58
+ label,
59
+ fallbackLabel,
60
+ title,
61
+ value,
62
+ size = 4,
63
+ onClick,
64
+ }: AnchorIconButtonProps) => {
65
+ const buttonRef = useRef<HTMLButtonElement>(null);
66
+
67
+ const handleClick = useCallback(() => {
68
+ if (value) {
69
+ buttonRef.current?.dispatchEvent(
70
+ new DxAnchorActivate({
71
+ trigger: buttonRef.current,
72
+ dxn: value.toString(),
73
+ label,
74
+ kind: 'card',
75
+ title,
76
+ }),
77
+ );
78
+ } else {
79
+ onClick?.();
80
+ }
81
+ }, [value, label, title, onClick]);
82
+
83
+ return (
84
+ <IconButton
85
+ classNames={compact ? 'min-h-0' : 'aspect-square'}
86
+ variant='ghost'
87
+ disabled={!value && !onClick}
88
+ icon={value ? icon : (fallbackIcon ?? icon)}
89
+ iconOnly
90
+ size={size}
91
+ label={value ? label : (fallbackLabel ?? label)}
92
+ onClick={handleClick}
93
+ ref={buttonRef}
94
+ />
95
+ );
96
+ };
97
+
98
+ //
99
+ // Date
100
+ //
101
+
102
+ type RowDateProps = {
103
+ start: Date;
104
+ end?: Date;
105
+ };
106
+
107
+ /** A Card.Row rendering a date or date range with a calendar icon. */
108
+ const RowDate = ({ start, end }: RowDateProps) => {
109
+ let { hours = 0, minutes = 0 } = (end && intervalToDuration({ start, end })) ?? {};
110
+ // Prefer 90m over 1h 30m.
111
+ if (hours === 1 && minutes !== 0) {
112
+ hours = 0;
113
+ minutes += 60;
114
+ }
115
+ const duration = [hours > 0 && `${hours}h`, minutes > 0 && `${minutes}m`].filter(Boolean).join(' ');
116
+
117
+ return (
118
+ <Card.Row>
119
+ <Card.Block>
120
+ <Icon icon='ph--calendar--regular' />
121
+ </Card.Block>
122
+ <div className='flex items-center gap-2 overflow-hidden whitespace-nowrap'>
123
+ <div className='truncate text-description'>{format(start, 'PPp')}</div>
124
+ {duration.length > 0 && <div className='text-description text-xs'>({duration})</div>}
125
+ </div>
126
+ </Card.Row>
127
+ );
128
+ };
129
+
130
+ RowDate.displayName = 'Row.Date';
131
+
132
+ //
133
+ // Ref
134
+ //
135
+
136
+ type RowRefProps = {
137
+ object: Obj.Any;
138
+ };
139
+
140
+ /** A Card.Row rendering an ECHO ref/relation with a card-preview anchor icon. */
141
+ const RowRef = ({ object }: RowRefProps) => {
142
+ const label = Obj.getLabel(object, { fallback: 'typename' }) ?? 'object';
143
+ const icon = Obj.getIcon(object)?.icon ?? 'ph--cube--regular';
144
+ const echoUri = EID.tryParse(Obj.getURI(object).toString());
145
+
146
+ // TODO(burdon): Nav?
147
+ return (
148
+ <Card.Row>
149
+ <Card.Block>
150
+ <AnchorIconButton icon={icon} label={label} title={label} value={echoUri} />
151
+ </Card.Block>
152
+ <div className='flex items-center'>
153
+ <span className='truncate text-primary-text'>{label}</span>
154
+ </div>
155
+ </Card.Row>
156
+ );
157
+ };
158
+
159
+ RowRef.displayName = 'Row.Ref';
160
+
161
+ //
162
+ // Person
163
+ //
164
+
165
+ /** Recipient/participant kind; drives the leading icon and accessible label. */
166
+ export type PersonRole = 'from' | 'to' | 'cc' | 'bcc' | 'attendee';
167
+
168
+ type RowPersonProps = {
169
+ actor: Actor.Actor;
170
+ /** Recipient/participant kind (icon + aria only; no visible prefix). */
171
+ role?: PersonRole;
172
+ /**
173
+ * Render a static `DxAvatar` in the gutter (hook-free) instead of the interactive contact anchor.
174
+ * Used by list tiles where resolving a contact per row (a hook) would be too costly.
175
+ */
176
+ avatar?: boolean;
177
+ db?: Database.Database;
178
+ onContactCreate?: (actor: Actor.Actor) => void;
179
+ /** Render a trailing remove button (e.g. attendee rows in the editable event header). */
180
+ onRemove?: () => void;
181
+ /** Click handler for the avatar (e.g. select the message); avatar variant only. */
182
+ onClick?: (event: MouseEvent) => void;
183
+ };
184
+
185
+ /**
186
+ * Static avatar variant — no contact resolution. Suitable for virtualized list tiles.
187
+ */
188
+ const PersonAvatarRow = ({ actor, onClick }: Pick<RowPersonProps, 'actor' | 'onClick'>) => (
189
+ <Card.Row>
190
+ <Card.Block>
191
+ <Avatar actor={actor} onClick={onClick} />
192
+ </Card.Block>
193
+ <Card.Text>{avatarName(actor) || actor.email}</Card.Text>
194
+ </Card.Row>
195
+ );
196
+
197
+ /**
198
+ * Interactive variant — resolves the contact to a card-preview anchor, with a create-contact fallback.
199
+ */
200
+ const PersonAnchorRow = ({
201
+ actor,
202
+ role,
203
+ db,
204
+ onContactCreate,
205
+ onRemove,
206
+ }: Omit<RowPersonProps, 'avatar' | 'onClick'>) => {
207
+ const { t } = useTranslation(translationKey);
208
+ const contactDXN = useActorContact(db, actor);
209
+
210
+ const handleContactCreate = useCallback(() => onContactCreate?.(actor), [actor, onContactCreate]);
211
+
212
+ // TODO(burdon): Reconcile with Avatar if space member.
213
+ return (
214
+ <Card.Row>
215
+ <Card.Block>
216
+ <AnchorIconButton
217
+ icon='ph--user--regular'
218
+ fallbackIcon='ph--user-plus--regular'
219
+ label={t('show-contact.label')}
220
+ fallbackLabel={t('create-contact.label')}
221
+ title={role ? `${role}: ${actor.name}` : actor.name}
222
+ value={contactDXN}
223
+ onClick={onContactCreate ? handleContactCreate : undefined}
224
+ />
225
+ </Card.Block>
226
+ <Card.Text>{actor.name || actor.email}</Card.Text>
227
+ {onRemove && (
228
+ <Card.Block end>
229
+ <IconButton
230
+ variant='ghost'
231
+ iconOnly
232
+ icon='ph--x--regular'
233
+ label={t('remove-attendee.label')}
234
+ onClick={onRemove}
235
+ />
236
+ </Card.Block>
237
+ )}
238
+ </Card.Row>
239
+ );
240
+ };
241
+
242
+ /** A Card.Row rendering a person (sender, recipient, attendee). */
243
+ const RowPerson = ({ avatar, onClick, ...props }: RowPersonProps) =>
244
+ avatar ? <PersonAvatarRow actor={props.actor} onClick={onClick} /> : <PersonAnchorRow {...props} />;
245
+
246
+ RowPerson.displayName = 'Row.Person';
247
+
248
+ //
249
+ // Tags
250
+ //
251
+
252
+ type TagItem = { id: string; label?: string; hue?: string };
253
+
254
+ type RowTagsProps = {
255
+ /** Optional — callers may pass an undefined/empty list (e.g. a message with no tags). */
256
+ tags?: TagItem[];
257
+ /** When provided, each chip is clickable and stops event propagation. */
258
+ onTagClick?: (label: string) => void;
259
+ };
260
+
261
+ /** A Card.Row rendering a set of label+hue tag chips, optionally clickable. */
262
+ const RowTags = ({ tags, onTagClick }: RowTagsProps) => {
263
+ if (!tags?.length) {
264
+ return null;
265
+ }
266
+
267
+ return (
268
+ <Card.Row>
269
+ <Card.Block>
270
+ <Icon icon='ph--tag--regular' />
271
+ </Card.Block>
272
+ <div className='flex flex-wrap gap-1 py-1 -mx-0.5' data-testid='extracted-tags'>
273
+ {tags.map((tag) => (
274
+ <Tag
275
+ key={tag.id}
276
+ hue={toHue(tag.hue)}
277
+ data-testid={`message-tag-${tag.id}`}
278
+ onClick={
279
+ onTagClick
280
+ ? (event) => {
281
+ event.stopPropagation();
282
+ onTagClick(tag.label ?? tag.id);
283
+ }
284
+ : undefined
285
+ }
286
+ >
287
+ {tag.label ?? tag.id}
288
+ </Tag>
289
+ ))}
290
+ </div>
291
+ </Card.Row>
292
+ );
293
+ };
294
+
295
+ RowTags.displayName = 'Row.Tags';
296
+
297
+ //
298
+ // Attachments
299
+ //
300
+
301
+ type RowAttachmentsProps = {
302
+ /** Optional — callers may pass an undefined/empty list (e.g. a message with no attachments). */
303
+ attachments?: readonly Message.Attachment[];
304
+ };
305
+
306
+ /**
307
+ * A Card.Row listing a message's attachments by name with a generic file icon. Not yet clickable —
308
+ * resolving the attachment's ref to open/preview it is a follow-up.
309
+ */
310
+ const RowAttachments = ({ attachments }: RowAttachmentsProps) => {
311
+ if (!attachments?.length) {
312
+ return null;
313
+ }
314
+
315
+ return (
316
+ <Card.Row>
317
+ <Card.Block>
318
+ <Icon icon='ph--paperclip--regular' />
319
+ </Card.Block>
320
+ <div className='flex flex-wrap gap-1 py-1 -mx-0.5' data-testid='message-attachments'>
321
+ {attachments.map((attachment) => (
322
+ <Tag key={attachment.ref.uri} hue='neutral' classNames='inline-flex items-center gap-1'>
323
+ <Icon icon='ph--file--regular' size={3} />
324
+ {attachment.name ?? attachment.ref.uri}
325
+ </Tag>
326
+ ))}
327
+ </div>
328
+ </Card.Row>
329
+ );
330
+ };
331
+
332
+ RowAttachments.displayName = 'Row.Attachments';
333
+
334
+ //
335
+ // Star
336
+ //
337
+
338
+ type RowStarProps = {
339
+ starred?: boolean;
340
+ /** Toggle handler; the button renders only when provided. */
341
+ onToggle?: () => void;
342
+ };
343
+
344
+ /**
345
+ * Star toggle for a `Card.Block` leading gutter (shared by event/message tiles and headers). Stops
346
+ * the click from bubbling so starring doesn't also select/activate the surrounding tile or card.
347
+ */
348
+ const RowStar = ({ starred, onToggle }: RowStarProps) => {
349
+ const handleClick = useCallback<NonNullable<IconButtonProps['onClick']>>(
350
+ (event) => {
351
+ event.stopPropagation();
352
+ onToggle?.();
353
+ },
354
+ [onToggle],
355
+ );
356
+
357
+ if (!onToggle) {
358
+ return null;
359
+ }
360
+
361
+ return <SystemIconButton.Star iconOnly variant='ghost' active={starred} onClick={handleClick} />;
362
+ };
363
+
364
+ RowStar.displayName = 'Row.Star';
365
+
366
+ //
367
+ // Row
368
+ //
369
+
370
+ export const Row = {
371
+ Date: RowDate,
372
+ Ref: RowRef,
373
+ Person: RowPerson,
374
+ Tags: RowTags,
375
+ Attachments: RowAttachments,
376
+ Star: RowStar,
377
+ };
378
+
379
+ export type { RowAttachmentsProps, RowDateProps, RowPersonProps, RowRefProps, RowStarProps, RowTagsProps };
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './Row';
@@ -1,5 +1,7 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- export * from './Card';
5
+ export * from './Avatar';
6
+ export * from './CardTile';
7
+ export * from './Row';
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ export * from './useActorContact';
@@ -0,0 +1,31 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { useMemo } from 'react';
6
+
7
+ import { type Database, Filter, Obj } from '@dxos/echo';
8
+ import { useQuery } from '@dxos/echo-react';
9
+ import { EID } from '@dxos/keys';
10
+ import { type Actor, Person } from '@dxos/types';
11
+
12
+ // TODO(burdon): Factor out lazy update pattern.
13
+ export const useActorContact = (db?: Database.Database, actor?: Actor.Actor): EID.EID | undefined => {
14
+ // Don't bother querying the space if there is already a reference to the contact.
15
+ const isLinked = !!actor?.contact;
16
+ const contacts = useQuery(isLinked ? undefined : db, Filter.type(Person.Person));
17
+ const existingContact = useMemo(
18
+ () => contacts.find((contact) => contact.emails?.find((email) => email.value === actor?.email)),
19
+ [contacts, actor?.email],
20
+ );
21
+
22
+ return useMemo(
23
+ () =>
24
+ actor?.contact
25
+ ? EID.tryParse(actor.contact.uri)
26
+ : existingContact
27
+ ? EID.parse(Obj.getURI(existingContact))
28
+ : undefined,
29
+ [actor?.contact, existingContact],
30
+ );
31
+ };
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  //
2
- // Copyright 2023 DXOS.org
2
+ // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
5
  export * from './components';
6
- export * from './theme';
6
+ export * from './hooks';
7
+ export * from './util';
@@ -0,0 +1,19 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import { type Resource } from '@dxos/react-ui';
6
+
7
+ export const translationKey = '@dxos/react-ui-card';
8
+
9
+ export const translations = [
10
+ {
11
+ 'en-US': {
12
+ [translationKey]: {
13
+ 'show-contact.label': 'Show contact',
14
+ 'create-contact.label': 'Create contact',
15
+ 'remove-attendee.label': 'Remove attendee',
16
+ },
17
+ },
18
+ },
19
+ ] as const satisfies Resource[];
package/src/util.ts ADDED
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ /** Deterministic string hash (djb2), used to map a name to a stable avatar hue. */
6
+ export const hashString = (str?: string): number =>
7
+ str ? Math.abs(str.split('').reduce((hash, char) => (hash << 5) + hash + char.charCodeAt(0), 0)) : 0;
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # @dxos/react-ui-card
2
-
3
- Opinionated card component.
@@ -1,200 +0,0 @@
1
- // packages/ui/react-ui-card/src/components/Card.tsx
2
- import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
3
- import { Circle, DotsSixVertical, DotsThreeVertical } from "@phosphor-icons/react";
4
- import React, { forwardRef } from "react";
5
- import { useDensityContext, DropdownMenu } from "@dxos/react-ui";
6
-
7
- // packages/ui/react-ui-card/src/theme.ts
8
- import { getSize, attentionSurface, mx } from "@dxos/react-ui-theme";
9
- var defaults = {
10
- width: "min-w-[256px]"
11
- };
12
- var cardRoot = ({ grow, square, rounded = true }, ...etc) => mx("flex flex-col group/card overflow-hidden", grow && "h-full", defaults.width, attentionSurface, "shadow", rounded && "rounded", square && "aspect-square", ...etc);
13
- var cardHeader = ({ floating }, ...etc) => mx(floating ? "relative" : "flex w-full shrink-0 justify-between overflow-hidden items-center py-1", ...etc);
14
- var cardTitle = ({ center }, ...etc) => mx("grow overflow-hidden truncate first:pl-4 last:pr-4", center && "text-center", ...etc);
15
- var cardDragHandle = ({ density, position }, ...etc) => mx("flex shrink-0 items-center justify-center select-none", density === "fine" ? getSize(8) : getSize(10), position === "left" && "absolute top-1 left-1", position === "right" && "absolute top-1 right-1", position && "text-black group-hover/card:bg-white group-hover/card:bg-opacity-80", ...etc);
16
- var cardDragHandleIcon = (_, ...etc) => mx(getSize(5), "cursor-pointer", ...etc);
17
- var cardMenu = ({ density, position }, ...etc) => mx("flex shrink-0 items-center justify-center", density === "fine" ? getSize(8) : getSize(10), position === "left" && "absolute top-1 left-1", position === "right" && "absolute top-1 right-1", position && "text-black group-hover/card:bg-white group-hover/card:bg-opacity-80", ...etc);
18
- var cardMenuIcon = (_, ...etc) => mx(getSize(5), "cursor-pointer");
19
- var cardBody = ({ density, gutter, noPadding }, ...etc) => mx(
20
- "grow overflow-auto",
21
- !noPadding && "px-2",
22
- // TODO(burdon): Create density-specific constants.
23
- gutter && (density === "fine" ? "pl-0 ml-[32px]" : "pl-0 ml-[40px]"),
24
- ...etc
25
- );
26
- var cardMedia = ({ contain }, ...etc) => mx(
27
- "grow max-h-[300px]",
28
- // Hide broken image links (e.g., if offline).
29
- "before:content-['\xA0'] before:block before:w-full before:h-full before:border-0",
30
- contain ? "object-contain" : "object-cover",
31
- ...etc
32
- );
33
- var cardTheme = {
34
- root: cardRoot,
35
- header: cardHeader,
36
- title: cardTitle,
37
- dragHandle: cardDragHandle,
38
- dragHandleIcon: cardDragHandleIcon,
39
- menu: cardMenu,
40
- menuIcon: cardMenuIcon,
41
- body: cardBody,
42
- media: cardMedia
43
- };
44
-
45
- // packages/ui/react-ui-card/src/components/Card.tsx
46
- var CardRoot = /* @__PURE__ */ forwardRef(({ grow, square, noPadding, classNames, children, ...props }, forwardedRef) => {
47
- var _effect = _useSignals();
48
- try {
49
- return /* @__PURE__ */ React.createElement("div", {
50
- ...props,
51
- ref: forwardedRef,
52
- className: cardRoot({
53
- grow,
54
- square,
55
- noPadding
56
- }, classNames)
57
- }, children);
58
- } finally {
59
- _effect.f();
60
- }
61
- });
62
- var CardHeader = ({ floating, classNames, children, ...props }) => {
63
- var _effect = _useSignals();
64
- try {
65
- return /* @__PURE__ */ React.createElement("div", {
66
- ...props,
67
- className: cardHeader({
68
- floating
69
- }, classNames)
70
- }, children);
71
- } finally {
72
- _effect.f();
73
- }
74
- };
75
- var CardTitle = ({ center, title, classNames, ...props }) => {
76
- var _effect = _useSignals();
77
- try {
78
- return /* @__PURE__ */ React.createElement("div", {
79
- ...props,
80
- className: cardTitle({
81
- center
82
- }, classNames)
83
- }, title);
84
- } finally {
85
- _effect.f();
86
- }
87
- };
88
- var CardDragHandle = ({ position, classNames, ...props }) => {
89
- var _effect = _useSignals();
90
- try {
91
- const density = useDensityContext();
92
- return /* @__PURE__ */ React.createElement("div", {
93
- ...props,
94
- className: cardDragHandle({
95
- density,
96
- position
97
- }, classNames)
98
- }, /* @__PURE__ */ React.createElement(DotsSixVertical, {
99
- className: cardDragHandleIcon({})
100
- }));
101
- } finally {
102
- _effect.f();
103
- }
104
- };
105
- var CardEndcap = ({ Icon = Circle, position, classNames, ...props }) => {
106
- var _effect = _useSignals();
107
- try {
108
- const density = useDensityContext();
109
- return /* @__PURE__ */ React.createElement("div", {
110
- ...props,
111
- className: cardMenu({
112
- density,
113
- position
114
- }, classNames)
115
- }, /* @__PURE__ */ React.createElement(Icon, {
116
- className: cardMenuIcon({})
117
- }));
118
- } finally {
119
- _effect.f();
120
- }
121
- };
122
- var CardMenu = /* @__PURE__ */ forwardRef(({ children, position, classNames, ...props }, forwardRef2) => {
123
- var _effect = _useSignals();
124
- try {
125
- const density = useDensityContext();
126
- return /* @__PURE__ */ React.createElement("div", {
127
- ...props,
128
- className: cardMenu({
129
- density,
130
- position
131
- }, classNames),
132
- ref: forwardRef2
133
- }, /* @__PURE__ */ React.createElement(DropdownMenu.Root, null, /* @__PURE__ */ React.createElement(DropdownMenu.Trigger, {
134
- asChild: true
135
- }, /* @__PURE__ */ React.createElement(DotsThreeVertical, {
136
- className: cardMenuIcon({})
137
- })), /* @__PURE__ */ React.createElement(DropdownMenu.Content, null, /* @__PURE__ */ React.createElement(DropdownMenu.Viewport, null, children))));
138
- } finally {
139
- _effect.f();
140
- }
141
- });
142
- var CardBody = ({ gutter, classNames, children, ...props }) => {
143
- var _effect = _useSignals();
144
- try {
145
- const density = useDensityContext();
146
- return /* @__PURE__ */ React.createElement("div", {
147
- ...props,
148
- className: cardBody({
149
- density,
150
- gutter
151
- }, classNames)
152
- }, children);
153
- } finally {
154
- _effect.f();
155
- }
156
- };
157
- var CardMedia = ({ src, contain, classNames, ...props }) => {
158
- var _effect = _useSignals();
159
- try {
160
- return /* @__PURE__ */ React.createElement("div", {
161
- className: "flex grow overflow-hidden"
162
- }, /* @__PURE__ */ React.createElement("img", {
163
- ...props,
164
- className: cardMedia({
165
- contain
166
- }, classNames),
167
- src
168
- }));
169
- } finally {
170
- _effect.f();
171
- }
172
- };
173
- var Card = {
174
- Root: CardRoot,
175
- Header: CardHeader,
176
- DragHandle: CardDragHandle,
177
- Endcap: CardEndcap,
178
- Menu: CardMenu,
179
- Title: CardTitle,
180
- Body: CardBody,
181
- Media: CardMedia
182
- };
183
- export {
184
- Card,
185
- CardBody,
186
- CardHeader,
187
- CardMedia,
188
- CardTitle,
189
- cardBody,
190
- cardDragHandle,
191
- cardDragHandleIcon,
192
- cardHeader,
193
- cardMedia,
194
- cardMenu,
195
- cardMenuIcon,
196
- cardRoot,
197
- cardTheme,
198
- cardTitle
199
- };
200
- //# sourceMappingURL=index.mjs.map