@dxos/react-ui-thread 0.8.3 → 0.8.4-main.1da679c

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-thread",
3
- "version": "0.8.3",
3
+ "version": "0.8.4-main.1da679c",
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",
@@ -10,6 +10,7 @@
10
10
  "type": "module",
11
11
  "exports": {
12
12
  ".": {
13
+ "source": "./src/index.ts",
13
14
  "types": "./dist/types/src/index.d.ts",
14
15
  "browser": "./dist/lib/browser/index.mjs"
15
16
  }
@@ -26,32 +27,30 @@
26
27
  "@preact-signals/safe-react": "^0.9.0",
27
28
  "@radix-ui/react-primitive": "2.0.2",
28
29
  "date-fns": "^3.3.1",
29
- "@dxos/react-ui-editor": "0.8.3",
30
- "@dxos/util": "0.8.3"
30
+ "@dxos/react-ui-editor": "0.8.4-main.1da679c",
31
+ "@dxos/util": "0.8.4-main.1da679c"
31
32
  },
32
33
  "devDependencies": {
33
- "@phosphor-icons/react": "^2.1.5",
34
34
  "@types/react": "~18.2.0",
35
35
  "@types/react-dom": "~18.2.0",
36
36
  "react": "~18.2.0",
37
37
  "react-dom": "~18.2.0",
38
- "vite": "5.4.7",
39
- "@dxos/echo-schema": "0.8.3",
40
- "@dxos/live-object": "0.8.3",
41
- "@dxos/echo-db": "0.8.3",
42
- "@dxos/log": "0.8.3",
43
- "@dxos/random": "0.8.3",
44
- "@dxos/keys": "0.8.3",
45
- "@dxos/react-ui": "0.8.3",
46
- "@dxos/react-ui-theme": "0.8.3",
47
- "@dxos/storybook-utils": "0.8.3"
38
+ "vite": "7.1.1",
39
+ "@dxos/echo-db": "0.8.4-main.1da679c",
40
+ "@dxos/echo-schema": "0.8.4-main.1da679c",
41
+ "@dxos/keys": "0.8.4-main.1da679c",
42
+ "@dxos/live-object": "0.8.4-main.1da679c",
43
+ "@dxos/random": "0.8.4-main.1da679c",
44
+ "@dxos/react-ui-theme": "0.8.4-main.1da679c",
45
+ "@dxos/storybook-utils": "0.8.4-main.1da679c",
46
+ "@dxos/react-ui": "0.8.4-main.1da679c",
47
+ "@dxos/log": "0.8.4-main.1da679c"
48
48
  },
49
49
  "peerDependencies": {
50
- "@phosphor-icons/react": "^2.1.5",
51
50
  "react": "~18.2.0",
52
51
  "react-dom": "~18.2.0",
53
- "@dxos/react-ui": "0.8.3",
54
- "@dxos/react-ui-theme": "0.8.3"
52
+ "@dxos/react-ui": "0.8.4-main.1da679c",
53
+ "@dxos/react-ui-theme": "0.8.4-main.1da679c"
55
54
  },
56
55
  "publishConfig": {
57
56
  "access": "public"
@@ -4,16 +4,18 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
8
  import React, { useState } from 'react';
8
9
 
9
10
  import { PublicKey } from '@dxos/keys';
10
11
  import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
11
12
  import { withLayout, withTheme } from '@dxos/storybook-utils';
12
13
 
14
+ import { type MessageEntity, MessageStoryText } from '../testing';
15
+ import { Thread } from '../Thread';
16
+ import { translations } from '../translations';
17
+
13
18
  import { MessageRoot } from './Message';
14
- import { threadLayout } from '../Thread';
15
- import { ThreadStoryContainer, MessageStoryText, type MessageEntity } from '../testing';
16
- import translations from '../translations';
17
19
 
18
20
  const DefaultStory = () => {
19
21
  const [identityKey] = useState(PublicKey.random());
@@ -25,22 +27,26 @@ const DefaultStory = () => {
25
27
  });
26
28
 
27
29
  return (
28
- <ThreadStoryContainer>
29
- <div className={threadLayout}>
30
+ <div className='mli-auto is-96 overflow-y-auto'>
31
+ <Thread.Root id='t1'>
30
32
  <MessageRoot {...message} classNames={[hoverableControls, hoverableFocusedWithinControls]}>
31
33
  <MessageStoryText {...message} onDelete={() => console.log('delete')} />
32
34
  </MessageRoot>
33
- </div>
34
- </ThreadStoryContainer>
35
+ </Thread.Root>
36
+ </div>
35
37
  );
36
38
  };
37
39
 
38
- export default {
40
+ const meta = {
39
41
  title: 'ui/react-ui-thread/Message',
40
- component: MessageRoot,
42
+ component: MessageRoot as any,
41
43
  render: DefaultStory,
42
44
  decorators: [withTheme, withLayout({ fullscreen: true })],
43
45
  parameters: { translations },
44
- };
46
+ } satisfies Meta<typeof DefaultStory>;
47
+
48
+ export default meta;
49
+
50
+ type Story = StoryObj<typeof meta>;
45
51
 
46
- export const Default = {};
52
+ export const Default: Story = {};
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
6
- import React, { type ComponentPropsWithRef, forwardRef, type ComponentPropsWithoutRef } from 'react';
6
+ import React, { type ComponentPropsWithRef, type ComponentPropsWithoutRef, forwardRef } from 'react';
7
7
 
8
8
  // TODO(burdon): Remove dep.
9
9
  import { Avatar, type ThemedClassName, useTranslation } from '@dxos/react-ui';
@@ -20,6 +20,7 @@ export type MessageRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> &
20
20
  MessageMetadata &
21
21
  Partial<{ continues: boolean }>;
22
22
 
23
+ // TODO(burdon): Show authorName on tooltip.
23
24
  export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
24
25
  (
25
26
  { authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps },
@@ -35,7 +36,7 @@ export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
35
36
  className={mx('grid grid-cols-subgrid col-span-2', classNames)}
36
37
  ref={forwardedRef}
37
38
  >
38
- <div role='none' className='flex flex-col items-center gap-2 pbs-2'>
39
+ <div role='none' className='flex flex-col items-center gap-2 pbs-1'>
39
40
  <Avatar.Content
40
41
  size={avatarSize}
41
42
  hue={authorAvatarProps?.hue || hexToHue(authorId ?? '0')}
@@ -4,7 +4,7 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { Check, Trash } from '@phosphor-icons/react';
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
8
  import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
9
9
 
10
10
  import { createDocAccessor, createObject } from '@dxos/echo-db';
@@ -13,7 +13,7 @@ import { PublicKey } from '@dxos/keys';
13
13
  import { live } from '@dxos/live-object';
14
14
  import { log } from '@dxos/log';
15
15
  import { faker } from '@dxos/random';
16
- import { Button, useThemeContext } from '@dxos/react-ui';
16
+ import { Button, Icon, useThemeContext } from '@dxos/react-ui';
17
17
  import {
18
18
  type Comment,
19
19
  type CommentsOptions,
@@ -30,10 +30,11 @@ import {
30
30
  import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
31
31
  import { withTheme } from '@dxos/storybook-utils';
32
32
 
33
- import { Thread, ThreadFooter, ThreadHeading } from './Thread';
34
33
  import { MessageBody, MessageHeading, MessageRoot, MessageTextbox } from '../Message';
35
34
  import { type MessageEntity } from '../testing';
36
- import translations from '../translations';
35
+ import { translations } from '../translations';
36
+
37
+ import { Thread } from './Thread';
37
38
 
38
39
  faker.seed(101);
39
40
 
@@ -157,17 +158,17 @@ const StoryThread: FC<{
157
158
  };
158
159
 
159
160
  return (
160
- <Thread current={selected} onClickCapture={onSelect}>
161
+ <Thread.Root current={selected} onClickCapture={onSelect}>
161
162
  <div className='col-start-2 flex gap-1 text-xs text-description'>
162
163
  <span>id:{thread.id.slice(0, 4)}</span>
163
164
  <span>y:{thread.yPos}</span>
164
165
  <span className='grow' />
165
- {!thread.cursor && <Trash />}
166
+ {!thread.cursor && <Icon icon='ph--trash--regular' />}
166
167
  </div>
167
168
 
168
- <ThreadHeading>
169
+ <Thread.Header>
169
170
  {thread.range?.from} &ndash; {thread.range?.to}
170
- </ThreadHeading>
171
+ </Thread.Header>
171
172
 
172
173
  {thread.messages.map((message) => (
173
174
  <MessageRoot key={message.id} classNames={[hoverableControls, hoverableFocusedWithinControls]} {...message}>
@@ -185,12 +186,12 @@ const StoryThread: FC<{
185
186
  onEditorFocus={onSelect}
186
187
  onSend={handleCreateMessage}
187
188
  />
188
- <ThreadFooter />
189
+ <Thread.Status />
189
190
  <Button variant='ghost' classNames='px-1' title='Resolve' onClick={onResolve}>
190
- <Check />
191
+ <Icon icon='ph--check--regular' />
191
192
  </Button>
192
193
  </div>
193
- </Thread>
194
+ </Thread.Root>
194
195
  );
195
196
  };
196
197
 
@@ -232,7 +233,7 @@ type StoryProps = {
232
233
  autoCreate?: boolean;
233
234
  };
234
235
 
235
- const Story = ({ text, autoCreate }: StoryProps) => {
236
+ const DefaultStory = ({ text, autoCreate }: StoryProps) => {
236
237
  const [item] = useState(createObject(live(Expando, { content: text ?? '' })));
237
238
  const [threads, setThreads] = useState<StoryCommentThread[]>([]);
238
239
  const [selected, setSelected] = useState<string>();
@@ -349,25 +350,27 @@ const Story = ({ text, autoCreate }: StoryProps) => {
349
350
  );
350
351
  };
351
352
 
352
- export default {
353
+ const meta = {
353
354
  title: 'ui/react-ui-thread/Comments',
354
- component: StoryThread,
355
+ component: StoryThread as any,
356
+ render: DefaultStory,
355
357
  decorators: [withTheme],
356
- render: (args: StoryProps) => <Story {...args} />,
357
358
  parameters: { translations, layout: 'fullscreen' },
358
- };
359
+ } satisfies Meta<typeof DefaultStory>;
359
360
 
360
- const str = (...lines: string[]) => lines.join('\n');
361
+ export default meta;
361
362
 
362
- const document = str(
363
- '# Comments',
364
- '',
365
- str(...faker.helpers.multiple(() => [faker.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
366
- '',
367
- );
363
+ type Story = StoryObj<typeof meta>;
364
+
365
+ const str = (...lines: string[]) => lines.join('\n');
368
366
 
369
- export const Default = {
367
+ export const Default: Story = {
370
368
  args: {
371
- text: document,
369
+ text: str(
370
+ '# Comments',
371
+ '',
372
+ str(...faker.helpers.multiple(() => [faker.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
373
+ '',
374
+ ),
372
375
  },
373
376
  };
@@ -4,6 +4,7 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
8
  import React, { useMemo, useRef, useState } from 'react';
8
9
 
9
10
  import { PublicKey } from '@dxos/keys';
@@ -12,10 +13,11 @@ import { createBasicExtensions, createThemeExtensions } from '@dxos/react-ui-edi
12
13
  import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
13
14
  import { withLayout, withTheme } from '@dxos/storybook-utils';
14
15
 
15
- import { Thread, ThreadFooter } from './Thread';
16
16
  import { MessageRoot, MessageTextbox } from '../Message';
17
- import { ThreadStoryContainer, MessageStoryText, type MessageEntity } from '../testing';
18
- import translations from '../translations';
17
+ import { type MessageEntity, MessageStoryText } from '../testing';
18
+ import { translations } from '../translations';
19
+
20
+ import { Thread } from './Thread';
19
21
 
20
22
  faker.seed(1);
21
23
 
@@ -59,13 +61,14 @@ const DefaultStory = () => {
59
61
  };
60
62
 
61
63
  return (
62
- <ThreadStoryContainer>
63
- <Thread id='t1'>
64
+ <div className='mli-auto is-96 overflow-y-auto'>
65
+ <Thread.Root id='t1'>
64
66
  {messages.map((message) => (
65
67
  <MessageRoot key={message.id} classNames={[hoverableControls, hoverableFocusedWithinControls]} {...message}>
66
68
  <MessageStoryText {...message} text={message.text} onDelete={() => console.log('delete')} />
67
69
  </MessageRoot>
68
70
  ))}
71
+
69
72
  <MessageTextbox
70
73
  id={String(_count)}
71
74
  authorId={identityKey1.toHex()}
@@ -73,18 +76,23 @@ const DefaultStory = () => {
73
76
  extensions={extensions}
74
77
  onSend={handleSend}
75
78
  />
76
- <ThreadFooter activity>Processing...</ThreadFooter>
77
- </Thread>
78
- </ThreadStoryContainer>
79
+
80
+ <Thread.Status activity>Processing...</Thread.Status>
81
+ </Thread.Root>
82
+ </div>
79
83
  );
80
84
  };
81
85
 
82
- export default {
86
+ const meta = {
83
87
  title: 'ui/react-ui-thread/Thread',
84
- component: Thread,
88
+ component: Thread.Root,
85
89
  render: DefaultStory,
86
90
  decorators: [withTheme, withLayout({ fullscreen: true })],
87
91
  parameters: { translations },
88
- };
92
+ } satisfies Meta<typeof Thread.Root>;
93
+
94
+ export default meta;
95
+
96
+ type Story = StoryObj<typeof meta>;
89
97
 
90
- export const Default = {};
98
+ export const Default: Story = {};
@@ -2,20 +2,24 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { CaretDoubleRight, Spinner } from '@phosphor-icons/react';
6
5
  import React, { type ComponentPropsWithRef, forwardRef } from 'react';
7
6
 
8
- import { type ThemedClassName, useTranslation } from '@dxos/react-ui';
7
+ import { Icon, type ThemedClassName, useTranslation } from '@dxos/react-ui';
9
8
  import { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
10
9
 
11
10
  import { translationKey } from '../translations';
12
11
  import type { ThreadEntity } from '../types';
13
12
 
14
- export type ThreadProps = ThemedClassName<ComponentPropsWithRef<'div'>> & ThreadEntity & { current?: boolean };
15
-
13
+ // TODO(burdon): Why is this exported?
16
14
  export const threadLayout = 'is-full place-self-start grid grid-cols-[var(--rail-size)_1fr]';
17
15
 
18
- export const Thread = forwardRef<HTMLDivElement, ThreadProps>(
16
+ //
17
+ // Root
18
+ //
19
+
20
+ type ThreadRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> & ThreadEntity & { current?: boolean };
21
+
22
+ const ThreadRoot = forwardRef<HTMLDivElement, ThreadRootProps>(
19
23
  ({ current, children, classNames, ...props }, forwardedRef) => {
20
24
  return (
21
25
  <div
@@ -26,8 +30,7 @@ export const Thread = forwardRef<HTMLDivElement, ThreadProps>(
26
30
  className={mx(
27
31
  threadLayout,
28
32
  hoverableFocusedWithinControls,
29
- 'pbs-2 pbe-4 bg-[var(--surface-bg)] border-separator first:border-bs-0 border-be',
30
- 'current-related attention-surface [--controls-opacity:0]',
33
+ 'bg-[var(--surface-bg)] current-related attention-surface [--controls-opacity:0]',
31
34
  classNames,
32
35
  )}
33
36
  ref={forwardedRef}
@@ -38,14 +41,18 @@ export const Thread = forwardRef<HTMLDivElement, ThreadProps>(
38
41
  },
39
42
  );
40
43
 
41
- export type ThreadHeadingProps = ThemedClassName<ComponentPropsWithRef<'div'>> & { detached?: boolean };
44
+ //
45
+ // Heading
46
+ //
47
+
48
+ type ThreadHeaderProps = ThemedClassName<ComponentPropsWithRef<'div'>> & { detached?: boolean };
42
49
 
43
- export const ThreadHeading = forwardRef<HTMLParagraphElement, ThreadHeadingProps>(
50
+ const ThreadHeader = forwardRef<HTMLParagraphElement, ThreadHeaderProps>(
44
51
  ({ classNames, children, detached, ...props }, forwardedRef) => {
45
52
  return (
46
53
  <>
47
54
  <div role='none' className='flex items-center justify-center text-description'>
48
- <CaretDoubleRight />
55
+ <Icon icon='ph--caret-double-right--regular' />
49
56
  </div>
50
57
  <div role='none' className='flex items-center overflow-hidden'>
51
58
  <p
@@ -66,11 +73,15 @@ export const ThreadHeading = forwardRef<HTMLParagraphElement, ThreadHeadingProps
66
73
  },
67
74
  );
68
75
 
69
- export type ThreadFooterProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
76
+ //
77
+ // Status
78
+ //
79
+
80
+ type ThreadStatusProps = ThemedClassName<ComponentPropsWithRef<'div'>> & {
70
81
  activity?: boolean;
71
82
  };
72
83
 
73
- export const ThreadFooter = forwardRef<HTMLDivElement, ThreadFooterProps>(
84
+ const ThreadStatus = forwardRef<HTMLDivElement, ThreadStatusProps>(
74
85
  ({ activity, classNames, children, ...props }, forwardedRef) => {
75
86
  const { t } = useTranslation(translationKey);
76
87
  return (
@@ -82,10 +93,10 @@ export const ThreadFooter = forwardRef<HTMLDivElement, ThreadFooterProps>(
82
93
  )}
83
94
  ref={forwardedRef}
84
95
  >
85
- <Spinner
86
- weight='bold'
96
+ <Icon
97
+ icon='ph--spinner--bold'
98
+ classNames='is-6 bs-4 invisible data-[visible=show]:visible animate-spin-slow'
87
99
  data-visible={activity ? 'show' : 'hide'}
88
- className='is-6 bs-4 invisible data-[visible=show]:visible animate-spin-slow'
89
100
  />
90
101
  <span className='truncate min-is-0' aria-live='polite'>
91
102
  {activity ? children : null}
@@ -95,3 +106,11 @@ export const ThreadFooter = forwardRef<HTMLDivElement, ThreadFooterProps>(
95
106
  );
96
107
  },
97
108
  );
109
+
110
+ export const Thread = {
111
+ Root: ThreadRoot,
112
+ Header: ThreadHeader,
113
+ Status: ThreadStatus,
114
+ };
115
+
116
+ export type { ThreadRootProps, ThreadHeaderProps as ThreadHeadingProps, ThreadStatusProps };
package/src/index.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import translations from './translations';
5
+ import { translations } from './translations';
6
6
 
7
7
  export * from './Thread';
8
8
  export * from './Message';
9
- export * from './types';
9
+ export type * from './types';
10
10
  export { translations };
package/src/testing.tsx CHANGED
@@ -2,10 +2,9 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { X } from '@phosphor-icons/react';
6
- import React, { type PropsWithChildren } from 'react';
5
+ import React from 'react';
7
6
 
8
- import { Button } from '@dxos/react-ui';
7
+ import { Button, Icon } from '@dxos/react-ui';
9
8
  import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/react-ui-theme';
10
9
 
11
10
  import { MessageBody, MessageHeading } from './Message';
@@ -31,7 +30,7 @@ export const MessageStoryText = ({ text, onDelete, authorName, timestamp }: Mess
31
30
  classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}
32
31
  onClick={() => onDelete()}
33
32
  >
34
- <X />
33
+ <Icon icon='ph--x--regular' />
35
34
  </Button>
36
35
  )}
37
36
  </MessageHeading>
@@ -56,7 +55,3 @@ export const MessageStoryBlock = <PartValue,>({ part }: MessagePartProps<PartVal
56
55
  </div>
57
56
  );
58
57
  };
59
-
60
- export const ThreadStoryContainer = ({ children }: PropsWithChildren) => {
61
- return <div className={mx('bg-white dark:bg-black mli-auto is-96 overflow-y-auto')}>{children}</div>;
62
- };
@@ -2,9 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- export const translationKey = 'thread';
5
+ import { type Resource } from '@dxos/react-ui';
6
6
 
7
- export default [
7
+ export const translationKey = 'react-ui-thread';
8
+
9
+ export const translations = [
8
10
  {
9
11
  'en-US': {
10
12
  [translationKey]: {
@@ -21,4 +23,4 @@ export default [
21
23
  },
22
24
  },
23
25
  },
24
- ];
26
+ ] as const satisfies Resource[];