@dxos/react-ui-thread 0.8.4-main.e8ec1fe → 0.8.4-main.effb148878

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 (34) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/browser/index.mjs +120 -197
  3. package/dist/lib/browser/index.mjs.map +4 -4
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/translations.mjs +25 -0
  6. package/dist/lib/browser/translations.mjs.map +7 -0
  7. package/dist/types/src/Message/Message.d.ts +2 -2
  8. package/dist/types/src/Message/Message.d.ts.map +1 -1
  9. package/dist/types/src/Message/Message.stories.d.ts +11 -11
  10. package/dist/types/src/Message/Message.stories.d.ts.map +1 -1
  11. package/dist/types/src/Thread/Comments.stories.d.ts +13 -13
  12. package/dist/types/src/Thread/Comments.stories.d.ts.map +1 -1
  13. package/dist/types/src/Thread/Thread.d.ts +25 -11
  14. package/dist/types/src/Thread/Thread.d.ts.map +1 -1
  15. package/dist/types/src/Thread/Thread.stories.d.ts +16 -12
  16. package/dist/types/src/Thread/Thread.stories.d.ts.map +1 -1
  17. package/dist/types/src/index.d.ts +0 -2
  18. package/dist/types/src/index.d.ts.map +1 -1
  19. package/dist/types/src/testing.d.ts.map +1 -1
  20. package/dist/types/src/translations.d.ts +12 -12
  21. package/dist/types/src/translations.d.ts.map +1 -1
  22. package/dist/types/src/types.d.ts +1 -0
  23. package/dist/types/src/types.d.ts.map +1 -1
  24. package/dist/types/tsconfig.tsbuildinfo +1 -1
  25. package/package.json +37 -28
  26. package/src/Message/Message.stories.tsx +6 -6
  27. package/src/Message/Message.tsx +16 -19
  28. package/src/Thread/Comments.stories.tsx +52 -34
  29. package/src/Thread/Thread.stories.tsx +10 -10
  30. package/src/Thread/Thread.tsx +22 -20
  31. package/src/index.ts +0 -3
  32. package/src/testing.tsx +4 -7
  33. package/src/translations.ts +11 -11
  34. package/src/types.ts +1 -0
package/package.json CHANGED
@@ -1,56 +1,65 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-thread",
3
- "version": "0.8.4-main.e8ec1fe",
3
+ "version": "0.8.4-main.effb148878",
4
4
  "description": "A set of components to use with threads, including comments and messages.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
- "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
11
+ "license": "FSL-1.1-Apache-2.0",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
15
+ "imports": {
16
+ "#translations": "./src/translations.ts"
17
+ },
11
18
  "exports": {
12
19
  ".": {
13
20
  "source": "./src/index.ts",
14
21
  "types": "./dist/types/src/index.d.ts",
15
22
  "browser": "./dist/lib/browser/index.mjs"
23
+ },
24
+ "./translations": {
25
+ "source": "./src/translations.ts",
26
+ "types": "./dist/types/src/translations.d.ts",
27
+ "browser": "./dist/lib/browser/translations.mjs",
28
+ "node": "./dist/lib/node-esm/translations.mjs"
16
29
  }
17
30
  },
18
31
  "types": "dist/types/src/index.d.ts",
19
- "typesVersions": {
20
- "*": {}
21
- },
22
32
  "files": [
23
33
  "dist",
24
34
  "src"
25
35
  ],
26
36
  "dependencies": {
27
- "@preact-signals/safe-react": "^0.9.0",
28
37
  "@radix-ui/react-primitive": "2.0.2",
29
- "date-fns": "^3.3.1",
30
- "@dxos/react-ui-editor": "0.8.4-main.e8ec1fe",
31
- "@dxos/util": "0.8.4-main.e8ec1fe"
38
+ "date-fns": "^3.6.0",
39
+ "@dxos/react-ui-editor": "0.8.4-main.effb148878",
40
+ "@dxos/util": "0.8.4-main.effb148878",
41
+ "@dxos/ui-editor": "0.8.4-main.effb148878"
32
42
  },
33
43
  "devDependencies": {
34
- "@types/react": "~19.2.2",
35
- "@types/react-dom": "~19.2.2",
36
- "react": "~19.2.0",
37
- "react-dom": "~19.2.0",
38
- "vite": "7.1.9",
39
- "@dxos/echo": "0.8.4-main.e8ec1fe",
40
- "@dxos/echo-db": "0.8.4-main.e8ec1fe",
41
- "@dxos/keys": "0.8.4-main.e8ec1fe",
42
- "@dxos/log": "0.8.4-main.e8ec1fe",
43
- "@dxos/live-object": "0.8.4-main.e8ec1fe",
44
- "@dxos/react-ui": "0.8.4-main.e8ec1fe",
45
- "@dxos/storybook-utils": "0.8.4-main.e8ec1fe",
46
- "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe",
47
- "@dxos/random": "0.8.4-main.e8ec1fe"
44
+ "@types/react": "~19.2.7",
45
+ "@types/react-dom": "~19.2.3",
46
+ "react": "~19.2.3",
47
+ "react-dom": "~19.2.3",
48
+ "vite": "^8.0.14",
49
+ "@dxos/echo": "0.8.4-main.effb148878",
50
+ "@dxos/keys": "0.8.4-main.effb148878",
51
+ "@dxos/echo-db": "0.8.4-main.effb148878",
52
+ "@dxos/random": "0.8.4-main.effb148878",
53
+ "@dxos/react-ui": "0.8.4-main.effb148878",
54
+ "@dxos/log": "0.8.4-main.effb148878",
55
+ "@dxos/storybook-utils": "0.8.4-main.effb148878",
56
+ "@dxos/ui-theme": "0.8.4-main.effb148878"
48
57
  },
49
58
  "peerDependencies": {
50
- "react": "^19.0.0",
51
- "react-dom": "^19.0.0",
52
- "@dxos/react-ui": "0.8.4-main.e8ec1fe",
53
- "@dxos/react-ui-theme": "0.8.4-main.e8ec1fe"
59
+ "react": "~19.2.3",
60
+ "react-dom": "~19.2.3",
61
+ "@dxos/react-ui": "0.8.4-main.effb148878",
62
+ "@dxos/ui-theme": "0.8.4-main.effb148878"
54
63
  },
55
64
  "publishConfig": {
56
65
  "access": "public"
@@ -6,13 +6,13 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useState } from 'react';
7
7
 
8
8
  import { PublicKey } from '@dxos/keys';
9
- import { withTheme } from '@dxos/react-ui/testing';
10
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
11
+
12
+ import { translations } from '#translations';
11
13
 
12
14
  import { type MessageEntity, MessageStoryText } from '../testing';
13
15
  import { Thread } from '../Thread';
14
- import { translations } from '../translations';
15
-
16
16
  import { MessageRoot } from './Message';
17
17
 
18
18
  const DefaultStory = () => {
@@ -25,7 +25,7 @@ const DefaultStory = () => {
25
25
  });
26
26
 
27
27
  return (
28
- <div className='mli-auto is-96 overflow-y-auto'>
28
+ <div className='mx-auto w-96 overflow-y-auto'>
29
29
  <Thread.Root id='t1'>
30
30
  <MessageRoot {...message} classNames={[hoverableControls, hoverableFocusedWithinControls]}>
31
31
  <MessageStoryText {...message} onDelete={() => console.log('delete')} />
@@ -39,7 +39,7 @@ const meta = {
39
39
  title: 'ui/react-ui-thread/Message',
40
40
  component: MessageRoot as any,
41
41
  render: DefaultStory,
42
- decorators: [withTheme],
42
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
43
43
  parameters: {
44
44
  layout: 'fullscreen',
45
45
  translations,
@@ -5,20 +5,21 @@
5
5
  import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
6
6
  import React, { type ComponentPropsWithRef, type ComponentPropsWithoutRef, forwardRef } from 'react';
7
7
 
8
- // TODO(burdon): Remove dep.
9
8
  import { Avatar, type ThemedClassName, useTranslation } from '@dxos/react-ui';
10
- import { type UseTextEditorProps, keymap, listener, useTextEditor } from '@dxos/react-ui-editor';
11
- import { focusRing, mx } from '@dxos/react-ui-theme';
9
+ import { type UseTextEditorProps, useTextEditor } from '@dxos/react-ui-editor';
10
+ import { keymap, listener } from '@dxos/ui-editor';
11
+ import { mx } from '@dxos/ui-theme';
12
12
  import { hexToEmoji, hexToHue, isTruthy } from '@dxos/util';
13
13
 
14
- import { translationKey } from '../translations';
14
+ import { translationKey } from '#translations';
15
+
15
16
  import { type MessageMetadata } from '../types';
16
17
 
17
18
  const avatarSize = 7;
18
19
 
19
- export type MessageRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> &
20
- MessageMetadata &
21
- Partial<{ continues: boolean }>;
20
+ export type MessageRootProps = ThemedClassName<
21
+ ComponentPropsWithRef<'div'> & MessageMetadata & Partial<{ continues: boolean }>
22
+ >;
22
23
 
23
24
  // TODO(burdon): Show authorName on tooltip.
24
25
  export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
@@ -26,28 +27,25 @@ export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
26
27
  { authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps },
27
28
  forwardedRef,
28
29
  ) => {
30
+ // Must wrap the message since Avatar.Label may be used in the content.
29
31
  return (
30
- // Must wrap the message since Avatar.Label may be used in the content.
31
32
  <Avatar.Root>
32
33
  <div
33
- role='none'
34
34
  data-testid='thread.message'
35
35
  {...rootProps}
36
36
  className={mx('grid grid-cols-subgrid col-span-2', classNames)}
37
37
  ref={forwardedRef}
38
38
  >
39
- <div role='none' className='flex flex-col items-center gap-2 pbs-1'>
39
+ <div className='flex flex-col items-center gap-2 pt-1'>
40
40
  <Avatar.Content
41
41
  size={avatarSize}
42
42
  hue={authorAvatarProps?.hue || hexToHue(authorId ?? '0')}
43
43
  fallback={authorAvatarProps?.emoji || hexToEmoji(authorId ?? '0')}
44
44
  {...(authorImgSrc && { imgSrc: authorImgSrc })}
45
45
  />
46
- {continues && <div role='none' className='is-px grow bg-separator' />}
47
- </div>
48
- <div role='none' className='plb-1 min-is-0'>
49
- {children}
46
+ {continues && <div className='w-px grow bg-separator' />}
50
47
  </div>
48
+ <div className='py-1 min-w-0'>{children}</div>
51
49
  </div>
52
50
  </Avatar.Root>
53
51
  );
@@ -59,7 +57,7 @@ export type MessageHeadingProps = ThemedClassName<ComponentPropsWithoutRef<'div'
59
57
 
60
58
  export const MessageHeading = ({ children, classNames, timestamp, authorName, ...props }: MessageHeadingProps) => {
61
59
  return (
62
- <div role='none' {...props} className={mx('flex gap-2 items-start', classNames)}>
60
+ <div {...props} className={mx('flex gap-2 items-start', classNames)}>
63
61
  <p className='grow'>
64
62
  <MessageAuthorName authorName={authorName} />
65
63
  {timestamp && <MessageTime timestamp={timestamp} />}
@@ -74,7 +72,7 @@ export type MessageAuthorNameProps = Pick<MessageMetadata, 'authorName'>;
74
72
  export const MessageAuthorName = ({ authorName }: MessageAuthorNameProps) => {
75
73
  const { t } = useTranslation(translationKey);
76
74
  return (
77
- <Avatar.Label classNames='block truncate text-sm text-subdued'>{authorName ?? t('anonymous label')}</Avatar.Label>
75
+ <Avatar.Label classNames='block truncate text-sm text-subdued'>{authorName ?? t('anonymous.label')}</Avatar.Label>
78
76
  );
79
77
  };
80
78
 
@@ -84,7 +82,7 @@ export const MessageTime = ({ timestamp }: MessageTimeProps) => {
84
82
  const { dtLocale } = useTranslation(translationKey);
85
83
  const dt = timestamp ? new Date(timestamp) : undefined;
86
84
  return (
87
- <time className='block text-subdued text-xs pbe-0.5' dateTime={dt?.toISOString()}>
85
+ <time className='block text-subdued text-xs pb-0.5' dateTime={dt?.toISOString()}>
88
86
  {dt ? formatDistanceToNow(dt, { locale: dtLocale, addSuffix: true }) : ''}
89
87
  </time>
90
88
  );
@@ -165,9 +163,8 @@ export const MessageTextbox = ({
165
163
  return (
166
164
  <MessageRoot {...{ id, authorId, authorName, authorImgSrc, authorAvatarProps }} continues={false}>
167
165
  <div
168
- role='none'
169
166
  ref={parentRef}
170
- className={mx('plb-0.5 mie-1 rounded-sm', focusRing, disabled && 'opacity-50')}
167
+ className={mx('py-0.5 me-1 rounded-xs dx-focus-ring', disabled && 'opacity-50')}
171
168
  {...focusAttributes}
172
169
  />
173
170
  </MessageRoot>
@@ -5,46 +5,63 @@
5
5
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
7
7
 
8
- import { Expando } from '@dxos/echo/internal';
9
- import { live } from '@dxos/echo/internal';
10
- import { createDocAccessor, createObject } from '@dxos/echo-db';
8
+ import { Obj } from '@dxos/echo';
9
+ import { TestSchema } from '@dxos/echo/testing';
11
10
  import { PublicKey } from '@dxos/keys';
12
11
  import { log } from '@dxos/log';
13
- import { faker } from '@dxos/random';
12
+ import { random } from '@dxos/random';
14
13
  import { Icon, IconButton, useThemeContext } from '@dxos/react-ui';
15
- import { withTheme } from '@dxos/react-ui/testing';
14
+ import { useTextEditor } from '@dxos/react-ui-editor';
15
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
16
16
  import {
17
- type Comment,
18
17
  type CommentsOptions,
19
- type Range,
20
- automerge,
18
+ type EditorView,
21
19
  comments,
22
20
  createBasicExtensions,
23
21
  createThemeExtensions,
22
+ documentId,
24
23
  listener,
25
24
  scrollThreadIntoView,
26
- useComments,
27
- useTextEditor,
28
- } from '@dxos/react-ui-editor';
29
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
25
+ setComments,
26
+ } from '@dxos/ui-editor';
27
+ import { type Comment, type Range } from '@dxos/ui-editor/types';
28
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
29
+
30
+ import { translations } from '#translations';
30
31
 
31
32
  import { MessageBody, MessageHeading, MessageRoot, MessageTextbox } from '../Message';
32
33
  import { type MessageEntity } from '../testing';
33
- import { translations } from '../translations';
34
-
35
34
  import { Thread } from './Thread';
36
35
 
37
- faker.seed(101);
36
+ random.seed(101);
38
37
 
39
38
  const authorId = PublicKey.random().toHex();
40
39
 
40
+ /**
41
+ * @deprecated This hook will be removed in future versions. Use the new comment sync extension instead.
42
+ * Update comments state field.
43
+ */
44
+ const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
45
+ useEffect(() => {
46
+ if (view) {
47
+ // Check same document.
48
+ // NOTE: Hook might be called before editor state is updated.
49
+ if (id === view.state.facet(documentId)) {
50
+ view.dispatch({
51
+ effects: setComments.of({ id, comments: comments ?? [] }),
52
+ });
53
+ }
54
+ }
55
+ });
56
+ };
57
+
41
58
  //
42
59
  // Editor
43
60
  //
44
61
 
45
62
  const Editor: FC<{
46
63
  id?: string;
47
- item: Expando;
64
+ initialValue: string;
48
65
  comments: Comment[];
49
66
  selected?: string;
50
67
  onCreateComment: CommentsOptions['onCreate'];
@@ -53,7 +70,7 @@ const Editor: FC<{
53
70
  onSelectComment: CommentsOptions['onSelect'];
54
71
  }> = ({
55
72
  id = 'test',
56
- item,
73
+ initialValue,
57
74
  selected: selectedValue,
58
75
  comments: commentRanges,
59
76
  onCreateComment,
@@ -67,11 +84,10 @@ const Editor: FC<{
67
84
  const { parentRef, view } = useTextEditor(
68
85
  () => ({
69
86
  id,
70
- initialValue: item.content,
87
+ initialValue: initialValue,
71
88
  extensions: [
72
89
  createBasicExtensions(),
73
90
  createThemeExtensions({ themeMode }),
74
- automerge(createDocAccessor(item, ['content'])),
75
91
  comments({
76
92
  id,
77
93
  onCreate: onCreateComment,
@@ -81,7 +97,7 @@ const Editor: FC<{
81
97
  }),
82
98
  ],
83
99
  }),
84
- [id, item, themeMode],
100
+ [id, initialValue, themeMode],
85
101
  );
86
102
  useComments(view, id, commentRanges);
87
103
  useEffect(() => {
@@ -144,7 +160,7 @@ const StoryThread: FC<{
144
160
 
145
161
  const handleCreateMessage = () => {
146
162
  if (messageRef.current?.length) {
147
- const message = live(Expando, {
163
+ const message = Obj.make(TestSchema.Expando, {
148
164
  timestamp: new Date().toISOString(),
149
165
  sender: { identityKey: authorId },
150
166
  text: messageRef.current,
@@ -191,7 +207,7 @@ const StoryThread: FC<{
191
207
  variant='ghost'
192
208
  icon='ph--check--regular'
193
209
  iconOnly
194
- classNames='pli-1'
210
+ classNames='px-1'
195
211
  label='Resolve'
196
212
  onClick={onResolve}
197
213
  />
@@ -200,12 +216,14 @@ const StoryThread: FC<{
200
216
  );
201
217
  };
202
218
 
203
- const Sidebar: FC<{
219
+ type SidebarProps = {
204
220
  threads: StoryCommentThread[];
205
221
  selected?: string;
206
222
  onSelect: (thread: string) => void;
207
223
  onResolve: (thread: string) => void;
208
- }> = ({ threads, selected, onSelect, onResolve }) => {
224
+ };
225
+
226
+ const Sidebar = ({ threads, selected, onSelect, onResolve }: SidebarProps) => {
209
227
  // Sort by y-position.
210
228
  const sortedThreads = useMemo(() => {
211
229
  const sorted = [...threads];
@@ -233,13 +251,13 @@ const Sidebar: FC<{
233
251
  // Story container.
234
252
  //
235
253
 
236
- type StoryProps = {
254
+ type DefaultStoryProps = {
237
255
  text?: string;
238
256
  autoCreate?: boolean;
239
257
  };
240
258
 
241
- const DefaultStory = ({ text, autoCreate }: StoryProps) => {
242
- const [item] = useState(createObject(live(Expando, { content: text ?? '' })));
259
+ const DefaultStory = ({ text, autoCreate }: DefaultStoryProps) => {
260
+ const [item] = useState(() => Obj.make(TestSchema.Expando, { content: text ?? '' }));
243
261
  const [threads, setThreads] = useState<StoryCommentThread[]>([]);
244
262
  const [selected, setSelected] = useState<string>();
245
263
 
@@ -258,12 +276,12 @@ const DefaultStory = ({ text, autoCreate }: StoryProps) => {
258
276
  cursor,
259
277
  yPos: location ? Math.floor(location.top) : undefined,
260
278
  messages: autoCreate
261
- ? faker.helpers.multiple(
279
+ ? random.helpers.multiple(
262
280
  () => ({
263
281
  id: PublicKey.random().toHex(),
264
282
  timestamp: new Date().toISOString(),
265
283
  authorId: PublicKey.random().toHex(),
266
- text: faker.lorem.sentence(),
284
+ text: random.lorem.sentence(),
267
285
  }),
268
286
  { count: { min: 1, max: 3 } },
269
287
  )
@@ -331,9 +349,9 @@ const DefaultStory = ({ text, autoCreate }: StoryProps) => {
331
349
 
332
350
  return (
333
351
  <main className='fixed inset-0 grid grid-cols-[1fr_24rem]'>
334
- <div role='none' className='max-bs-full overflow-y-auto p-4'>
352
+ <div className='max-h-full overflow-y-auto p-4'>
335
353
  <Editor
336
- item={item}
354
+ initialValue={item.content}
337
355
  selected={selected}
338
356
  comments={comments}
339
357
  onCreateComment={handleCreateComment}
@@ -343,7 +361,7 @@ const DefaultStory = ({ text, autoCreate }: StoryProps) => {
343
361
  />
344
362
  </div>
345
363
 
346
- <div role='none' className='max-bs-full overflow-y-auto p-4'>
364
+ <div className='max-h-full overflow-y-auto p-4'>
347
365
  <Sidebar
348
366
  threads={visibleThreads}
349
367
  selected={selected}
@@ -359,7 +377,7 @@ const meta = {
359
377
  title: 'ui/react-ui-thread/Comments',
360
378
  component: StoryThread as any,
361
379
  render: DefaultStory,
362
- decorators: [withTheme],
380
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
363
381
  parameters: {
364
382
  translations,
365
383
  layout: 'fullscreen',
@@ -377,7 +395,7 @@ export const Default: Story = {
377
395
  text: str(
378
396
  '# Comments',
379
397
  '',
380
- str(...faker.helpers.multiple(() => [faker.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
398
+ str(...random.helpers.multiple(() => [random.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
381
399
  '',
382
400
  ),
383
401
  },
@@ -6,18 +6,18 @@ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
6
  import React, { useMemo, useRef, useState } from 'react';
7
7
 
8
8
  import { PublicKey } from '@dxos/keys';
9
- import { faker } from '@dxos/random';
10
- import { withTheme } from '@dxos/react-ui/testing';
11
- import { createBasicExtensions, createThemeExtensions } from '@dxos/react-ui-editor';
12
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
9
+ import { random } from '@dxos/random';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+ import { createBasicExtensions, createThemeExtensions } from '@dxos/ui-editor';
12
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
13
+
14
+ import { translations } from '#translations';
13
15
 
14
16
  import { MessageRoot, MessageTextbox } from '../Message';
15
17
  import { type MessageEntity, MessageStoryText } from '../testing';
16
- import { translations } from '../translations';
17
-
18
18
  import { Thread } from './Thread';
19
19
 
20
- faker.seed(1);
20
+ random.seed(1);
21
21
 
22
22
  const DefaultStory = () => {
23
23
  const [pending, setPending] = useState(false);
@@ -28,7 +28,7 @@ const DefaultStory = () => {
28
28
  id: `m${i + 1}`,
29
29
  timestamp: new Date().toISOString(),
30
30
  authorId: [identityKey1.toHex(), identityKey2.toHex()][i % 2],
31
- text: faker.lorem.paragraph(),
31
+ text: random.lorem.paragraph(),
32
32
  })),
33
33
  );
34
34
 
@@ -59,7 +59,7 @@ const DefaultStory = () => {
59
59
  };
60
60
 
61
61
  return (
62
- <div className='mli-auto is-96 overflow-y-auto'>
62
+ <div className='mx-auto w-96 overflow-y-auto'>
63
63
  <Thread.Root id='t1'>
64
64
  {messages.map((message) => (
65
65
  <MessageRoot key={message.id} classNames={[hoverableControls, hoverableFocusedWithinControls]} {...message}>
@@ -85,7 +85,7 @@ const meta = {
85
85
  title: 'ui/react-ui-thread/Thread',
86
86
  component: Thread.Root,
87
87
  render: DefaultStory,
88
- decorators: [withTheme],
88
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
89
89
  parameters: {
90
90
  layout: 'fullscreen',
91
91
  translations,
@@ -2,22 +2,27 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import React, { type ComponentPropsWithRef, forwardRef } from 'react';
5
+ import React, { ComponentPropsWithRef, forwardRef, PropsWithChildren } from 'react';
6
6
 
7
7
  import { Icon, type ThemedClassName, useTranslation } from '@dxos/react-ui';
8
- import { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
8
+ import { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/ui-theme';
9
+
10
+ import { translationKey } from '#translations';
9
11
 
10
- import { translationKey } from '../translations';
11
12
  import type { ThreadEntity } from '../types';
12
13
 
13
- // TODO(burdon): Why is this exported?
14
- export const threadLayout = 'is-full place-self-start grid grid-cols-[var(--rail-size)_1fr]';
14
+ // TODO(burdon): Avoid exporting fragments.
15
+ export const threadLayout = 'w-full place-self-start grid grid-cols-[var(--dx-rail-size)_1fr]';
15
16
 
16
17
  //
17
18
  // Root
18
19
  //
19
20
 
20
- type ThreadRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> & ThreadEntity & { current?: boolean };
21
+ type ThreadRootProps = ThemedClassName<
22
+ PropsWithChildren<
23
+ ThreadEntity & { current?: boolean } & Pick<ComponentPropsWithRef<'div'>, 'onClickCapture' | 'onFocusCapture'>
24
+ >
25
+ >;
21
26
 
22
27
  const ThreadRoot = forwardRef<HTMLDivElement, ThreadRootProps>(
23
28
  ({ current, children, classNames, ...props }, forwardedRef) => {
@@ -30,7 +35,7 @@ const ThreadRoot = forwardRef<HTMLDivElement, ThreadRootProps>(
30
35
  className={mx(
31
36
  threadLayout,
32
37
  hoverableFocusedWithinControls,
33
- 'bg-[var(--surface-bg)] current-related attention-surface [--controls-opacity:0]',
38
+ 'bg-[var(--surface-bg)] current-related dx-attention-surface [--controls-opacity:0]',
34
39
  classNames,
35
40
  )}
36
41
  ref={forwardedRef}
@@ -45,24 +50,21 @@ const ThreadRoot = forwardRef<HTMLDivElement, ThreadRootProps>(
45
50
  // Heading
46
51
  //
47
52
 
48
- type ThreadHeaderProps = ComponentPropsWithRef<'div'> & { detached?: boolean };
53
+ type ThreadHeaderProps = PropsWithChildren<{ detached?: boolean }>;
49
54
 
50
55
  const ThreadHeader = forwardRef<HTMLParagraphElement, ThreadHeaderProps>(
51
56
  ({ children, detached, ...props }, forwardedRef) => {
52
57
  return (
53
58
  <>
54
- <div role='none' className='flex items-center justify-center text-description'>
59
+ <div className='flex items-center justify-center text-description'>
55
60
  <Icon icon='ph--caret-double-right--regular' />
56
61
  </div>
57
- <div role='none' className='flex items-center overflow-hidden'>
62
+ <div className='flex items-center overflow-hidden'>
58
63
  <p
59
64
  role='heading'
60
65
  data-testid='thread.heading'
61
66
  {...props}
62
- className={mx(
63
- 'mie-2 text-description font-medium truncate italic',
64
- detached && 'line-through decoration-1',
65
- )}
67
+ className={mx('me-2 text-description font-medium truncate italic', detached && 'line-through decoration-1')}
66
68
  ref={forwardedRef}
67
69
  >
68
70
  {children}
@@ -77,7 +79,7 @@ const ThreadHeader = forwardRef<HTMLParagraphElement, ThreadHeaderProps>(
77
79
  // Status
78
80
  //
79
81
 
80
- type ThreadStatusProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
82
+ type ThreadStatusProps = ThemedClassName<PropsWithChildren> & {
81
83
  activity?: boolean;
82
84
  };
83
85
 
@@ -88,20 +90,20 @@ const ThreadStatus = forwardRef<HTMLDivElement, ThreadStatusProps>(
88
90
  <div
89
91
  {...props}
90
92
  className={mx(
91
- 'col-start-2 grid grid-cols-[min-content_1fr_max-content] pb-2 pie-2 text-xs text-description',
93
+ 'col-start-2 grid grid-cols-[min-content_1fr_max-content] pb-2 pe-2 text-xs text-description',
92
94
  classNames,
93
95
  )}
94
96
  ref={forwardedRef}
95
97
  >
96
98
  <Icon
97
99
  icon='ph--spinner--bold'
98
- classNames='is-6 bs-4 invisible data-[visible=show]:visible animate-spin-slow'
100
+ classNames='w-6 h-4 invisible data-[visible=show]:visible animate-spin-slow'
99
101
  data-visible={activity ? 'show' : 'hide'}
100
102
  />
101
- <span className='truncate min-is-0' aria-live='polite'>
103
+ <span className='truncate min-w-0' aria-live='polite'>
102
104
  {activity ? children : null}
103
105
  </span>
104
- <span className={mx('text-end', hoverableControlItem)}>{t('enter to send message')}</span>
106
+ <span className={mx('text-end', hoverableControlItem)}>{t('enter-to-send.message')}</span>
105
107
  </div>
106
108
  );
107
109
  },
@@ -113,4 +115,4 @@ export const Thread = {
113
115
  Status: ThreadStatus,
114
116
  };
115
117
 
116
- export type { ThreadRootProps, ThreadHeaderProps as ThreadHeadingProps, ThreadStatusProps };
118
+ export type { ThreadRootProps, ThreadHeaderProps, ThreadStatusProps };
package/src/index.ts CHANGED
@@ -2,9 +2,6 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { translations } from './translations';
6
-
7
5
  export * from './Thread';
8
6
  export * from './Message';
9
7
  export type * from './types';
10
- export { translations };
package/src/testing.tsx CHANGED
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
 
7
7
  import { IconButton } from '@dxos/react-ui';
8
- import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
8
+ import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/ui-theme';
9
9
 
10
10
  import { MessageBody, MessageHeading } from './Message';
11
11
  import { type MessageMetadata } from './types';
@@ -30,7 +30,7 @@ export const MessageStoryText = ({ text, onDelete, authorName, timestamp }: Mess
30
30
  iconOnly
31
31
  label='Delete'
32
32
  variant='ghost'
33
- classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}
33
+ classNames={['p-1.5 min-h-0 me-1 items-start transition-opacity', hoverableControlItem]}
34
34
  onClick={() => onDelete()}
35
35
  />
36
36
  )}
@@ -46,11 +46,8 @@ export type MessagePartProps<PartValue> = {
46
46
 
47
47
  export const MessageStoryBlock = <PartValue,>({ part }: MessagePartProps<PartValue>) => {
48
48
  return (
49
- <div
50
- role='none'
51
- className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}
52
- >
53
- <pre className='font-mono max-is-full overflow-x-auto col-span-3'>
49
+ <div className={mx('grid grid-cols-subgrid col-span-3', hoverableControls, hoverableFocusedWithinControls)}>
50
+ <pre className='font-mono max-w-full overflow-x-auto col-span-3'>
54
51
  <code>{JSON.stringify(part, undefined, 2)}</code>
55
52
  </pre>
56
53
  </div>