@dxos/react-ui-thread 0.8.4-main.406dc2a → 0.8.4-main.52d7546f51
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 +129 -175
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/Message/Message.d.ts.map +1 -1
- package/dist/types/src/Message/Message.stories.d.ts +1 -1
- package/dist/types/src/Thread/Comments.stories.d.ts +1 -1
- package/dist/types/src/Thread/Comments.stories.d.ts.map +1 -1
- package/dist/types/src/Thread/Thread.d.ts +1 -1
- package/dist/types/src/Thread/Thread.d.ts.map +1 -1
- package/dist/types/src/Thread/Thread.stories.d.ts +1 -1
- package/dist/types/src/testing.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 +25 -22
- package/src/Message/Message.stories.tsx +2 -2
- package/src/Message/Message.tsx +4 -4
- package/src/Thread/Comments.stories.tsx +55 -29
- package/src/Thread/Thread.stories.tsx +3 -3
- package/src/Thread/Thread.tsx +4 -4
- package/src/testing.tsx +7 -6
- package/src/translations.ts +1 -1
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/react-ui-thread",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.52d7546f51",
|
|
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
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
7
11
|
"license": "MIT",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
10
14
|
"type": "module",
|
|
11
15
|
"exports": {
|
|
12
16
|
".": {
|
|
@@ -24,33 +28,32 @@
|
|
|
24
28
|
"src"
|
|
25
29
|
],
|
|
26
30
|
"dependencies": {
|
|
27
|
-
"@preact-signals/safe-react": "^0.9.0",
|
|
28
31
|
"@radix-ui/react-primitive": "2.0.2",
|
|
29
32
|
"date-fns": "^3.3.1",
|
|
30
|
-
"@dxos/react-ui-editor": "0.8.4-main.
|
|
31
|
-
"@dxos/
|
|
33
|
+
"@dxos/react-ui-editor": "0.8.4-main.52d7546f51",
|
|
34
|
+
"@dxos/ui-editor": "0.8.4-main.52d7546f51",
|
|
35
|
+
"@dxos/util": "0.8.4-main.52d7546f51"
|
|
32
36
|
},
|
|
33
37
|
"devDependencies": {
|
|
34
|
-
"@types/react": "~19.2.
|
|
35
|
-
"@types/react-dom": "~19.2.
|
|
36
|
-
"react": "~19.2.
|
|
37
|
-
"react-dom": "~19.2.
|
|
38
|
+
"@types/react": "~19.2.7",
|
|
39
|
+
"@types/react-dom": "~19.2.3",
|
|
40
|
+
"react": "~19.2.3",
|
|
41
|
+
"react-dom": "~19.2.3",
|
|
38
42
|
"vite": "7.1.9",
|
|
39
|
-
"@dxos/echo": "0.8.4-main.
|
|
40
|
-
"@dxos/echo-db": "0.8.4-main.
|
|
41
|
-
"@dxos/
|
|
42
|
-
"@dxos/
|
|
43
|
-
"@dxos/
|
|
44
|
-
"@dxos/
|
|
45
|
-
"@dxos/
|
|
46
|
-
"@dxos/
|
|
47
|
-
"@dxos/storybook-utils": "0.8.4-main.406dc2a"
|
|
43
|
+
"@dxos/echo": "0.8.4-main.52d7546f51",
|
|
44
|
+
"@dxos/echo-db": "0.8.4-main.52d7546f51",
|
|
45
|
+
"@dxos/keys": "0.8.4-main.52d7546f51",
|
|
46
|
+
"@dxos/log": "0.8.4-main.52d7546f51",
|
|
47
|
+
"@dxos/react-ui": "0.8.4-main.52d7546f51",
|
|
48
|
+
"@dxos/random": "0.8.4-main.52d7546f51",
|
|
49
|
+
"@dxos/ui-theme": "0.8.4-main.52d7546f51",
|
|
50
|
+
"@dxos/storybook-utils": "0.8.4-main.52d7546f51"
|
|
48
51
|
},
|
|
49
52
|
"peerDependencies": {
|
|
50
|
-
"react": "
|
|
51
|
-
"react-dom": "
|
|
52
|
-
"@dxos/react-ui": "0.8.4-main.
|
|
53
|
-
"@dxos/
|
|
53
|
+
"react": "~19.2.3",
|
|
54
|
+
"react-dom": "~19.2.3",
|
|
55
|
+
"@dxos/react-ui": "0.8.4-main.52d7546f51",
|
|
56
|
+
"@dxos/ui-theme": "0.8.4-main.52d7546f51"
|
|
54
57
|
},
|
|
55
58
|
"publishConfig": {
|
|
56
59
|
"access": "public"
|
|
@@ -7,7 +7,7 @@ import React, { useState } from 'react';
|
|
|
7
7
|
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
9
9
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
10
|
-
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/
|
|
10
|
+
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
|
|
11
11
|
|
|
12
12
|
import { type MessageEntity, MessageStoryText } from '../testing';
|
|
13
13
|
import { Thread } from '../Thread';
|
|
@@ -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()],
|
|
43
43
|
parameters: {
|
|
44
44
|
layout: 'fullscreen',
|
|
45
45
|
translations,
|
package/src/Message/Message.tsx
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
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,
|
|
11
|
-
import {
|
|
9
|
+
import { type UseTextEditorProps, useTextEditor } from '@dxos/react-ui-editor';
|
|
10
|
+
import { keymap, listener } from '@dxos/ui-editor';
|
|
11
|
+
import { focusRing, mx } from '@dxos/ui-theme';
|
|
12
12
|
import { hexToEmoji, hexToHue, isTruthy } from '@dxos/util';
|
|
13
13
|
|
|
14
14
|
import { translationKey } from '../translations';
|
|
@@ -149,7 +149,7 @@ export const MessageTextbox = ({
|
|
|
149
149
|
extensions: [
|
|
150
150
|
keymap.of(keyBindings({ onSend, onClear })),
|
|
151
151
|
listener({
|
|
152
|
-
onFocus: (focusing) => {
|
|
152
|
+
onFocus: ({ focusing }) => {
|
|
153
153
|
if (focusing) {
|
|
154
154
|
onEditorFocus?.();
|
|
155
155
|
}
|
|
@@ -5,28 +5,28 @@
|
|
|
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 {
|
|
9
|
-
import {
|
|
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
12
|
import { faker } from '@dxos/random';
|
|
14
|
-
import {
|
|
13
|
+
import { Icon, IconButton, useThemeContext } from '@dxos/react-ui';
|
|
15
14
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
15
|
+
import { useTextEditor } from '@dxos/react-ui-editor';
|
|
16
16
|
import {
|
|
17
17
|
type Comment,
|
|
18
18
|
type CommentsOptions,
|
|
19
|
+
type EditorView,
|
|
19
20
|
type Range,
|
|
20
|
-
automerge,
|
|
21
21
|
comments,
|
|
22
22
|
createBasicExtensions,
|
|
23
23
|
createThemeExtensions,
|
|
24
|
+
documentId,
|
|
24
25
|
listener,
|
|
25
26
|
scrollThreadIntoView,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
} from '@dxos/
|
|
29
|
-
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
|
|
27
|
+
setComments,
|
|
28
|
+
} from '@dxos/ui-editor';
|
|
29
|
+
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
|
|
30
30
|
|
|
31
31
|
import { MessageBody, MessageHeading, MessageRoot, MessageTextbox } from '../Message';
|
|
32
32
|
import { type MessageEntity } from '../testing';
|
|
@@ -38,13 +38,31 @@ faker.seed(101);
|
|
|
38
38
|
|
|
39
39
|
const authorId = PublicKey.random().toHex();
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated This hook will be removed in future versions. Use the new comment sync extension instead.
|
|
43
|
+
* Update comments state field.
|
|
44
|
+
*/
|
|
45
|
+
const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (view) {
|
|
48
|
+
// Check same document.
|
|
49
|
+
// NOTE: Hook might be called before editor state is updated.
|
|
50
|
+
if (id === view.state.facet(documentId)) {
|
|
51
|
+
view.dispatch({
|
|
52
|
+
effects: setComments.of({ id, comments: comments ?? [] }),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
41
59
|
//
|
|
42
60
|
// Editor
|
|
43
61
|
//
|
|
44
62
|
|
|
45
63
|
const Editor: FC<{
|
|
46
64
|
id?: string;
|
|
47
|
-
|
|
65
|
+
initialValue: string;
|
|
48
66
|
comments: Comment[];
|
|
49
67
|
selected?: string;
|
|
50
68
|
onCreateComment: CommentsOptions['onCreate'];
|
|
@@ -53,7 +71,7 @@ const Editor: FC<{
|
|
|
53
71
|
onSelectComment: CommentsOptions['onSelect'];
|
|
54
72
|
}> = ({
|
|
55
73
|
id = 'test',
|
|
56
|
-
|
|
74
|
+
initialValue,
|
|
57
75
|
selected: selectedValue,
|
|
58
76
|
comments: commentRanges,
|
|
59
77
|
onCreateComment,
|
|
@@ -67,11 +85,10 @@ const Editor: FC<{
|
|
|
67
85
|
const { parentRef, view } = useTextEditor(
|
|
68
86
|
() => ({
|
|
69
87
|
id,
|
|
70
|
-
initialValue:
|
|
88
|
+
initialValue: initialValue,
|
|
71
89
|
extensions: [
|
|
72
90
|
createBasicExtensions(),
|
|
73
91
|
createThemeExtensions({ themeMode }),
|
|
74
|
-
automerge(createDocAccessor(item, ['content'])),
|
|
75
92
|
comments({
|
|
76
93
|
id,
|
|
77
94
|
onCreate: onCreateComment,
|
|
@@ -81,7 +98,7 @@ const Editor: FC<{
|
|
|
81
98
|
}),
|
|
82
99
|
],
|
|
83
100
|
}),
|
|
84
|
-
[id,
|
|
101
|
+
[id, initialValue, themeMode],
|
|
85
102
|
);
|
|
86
103
|
useComments(view, id, commentRanges);
|
|
87
104
|
useEffect(() => {
|
|
@@ -114,19 +131,21 @@ const StoryThread: FC<{
|
|
|
114
131
|
onSelect: () => void;
|
|
115
132
|
onResolve: () => void;
|
|
116
133
|
}> = ({ thread, selected, onSelect, onResolve }) => {
|
|
117
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
118
134
|
const { themeMode } = useThemeContext();
|
|
135
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
136
|
+
|
|
119
137
|
// TODO(wittjosiah): This is a hack to reset the editor after a message is sent.
|
|
120
|
-
const [
|
|
121
|
-
const rerenderEditor = () => _setCount((count) => count + 1);
|
|
138
|
+
const [state, setState] = useState({});
|
|
122
139
|
const messageRef = useRef('');
|
|
123
140
|
const extensions = useMemo(
|
|
124
141
|
() => [
|
|
125
142
|
createBasicExtensions({ placeholder: 'Enter comment' }),
|
|
126
143
|
createThemeExtensions({ themeMode }),
|
|
127
|
-
listener({
|
|
144
|
+
listener({
|
|
145
|
+
onChange: ({ text }) => (messageRef.current = text),
|
|
146
|
+
}),
|
|
128
147
|
],
|
|
129
|
-
[themeMode,
|
|
148
|
+
[themeMode, state],
|
|
130
149
|
);
|
|
131
150
|
|
|
132
151
|
const [autoFocus, setAutoFocus] = useState(false);
|
|
@@ -142,7 +161,7 @@ const StoryThread: FC<{
|
|
|
142
161
|
|
|
143
162
|
const handleCreateMessage = () => {
|
|
144
163
|
if (messageRef.current?.length) {
|
|
145
|
-
const message =
|
|
164
|
+
const message = Obj.make(TestSchema.Expando, {
|
|
146
165
|
timestamp: new Date().toISOString(),
|
|
147
166
|
sender: { identityKey: authorId },
|
|
148
167
|
text: messageRef.current,
|
|
@@ -151,7 +170,7 @@ const StoryThread: FC<{
|
|
|
151
170
|
|
|
152
171
|
messageRef.current = '';
|
|
153
172
|
setAutoFocus(true);
|
|
154
|
-
|
|
173
|
+
setState({});
|
|
155
174
|
}
|
|
156
175
|
};
|
|
157
176
|
|
|
@@ -185,20 +204,27 @@ const StoryThread: FC<{
|
|
|
185
204
|
onSend={handleCreateMessage}
|
|
186
205
|
/>
|
|
187
206
|
<Thread.Status />
|
|
188
|
-
<
|
|
189
|
-
|
|
190
|
-
|
|
207
|
+
<IconButton
|
|
208
|
+
variant='ghost'
|
|
209
|
+
icon='ph--check--regular'
|
|
210
|
+
iconOnly
|
|
211
|
+
classNames='pli-1'
|
|
212
|
+
label='Resolve'
|
|
213
|
+
onClick={onResolve}
|
|
214
|
+
/>
|
|
191
215
|
</div>
|
|
192
216
|
</Thread.Root>
|
|
193
217
|
);
|
|
194
218
|
};
|
|
195
219
|
|
|
196
|
-
|
|
220
|
+
type SidebarProps = {
|
|
197
221
|
threads: StoryCommentThread[];
|
|
198
222
|
selected?: string;
|
|
199
223
|
onSelect: (thread: string) => void;
|
|
200
224
|
onResolve: (thread: string) => void;
|
|
201
|
-
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const Sidebar = ({ threads, selected, onSelect, onResolve }: SidebarProps) => {
|
|
202
228
|
// Sort by y-position.
|
|
203
229
|
const sortedThreads = useMemo(() => {
|
|
204
230
|
const sorted = [...threads];
|
|
@@ -232,7 +258,7 @@ type StoryProps = {
|
|
|
232
258
|
};
|
|
233
259
|
|
|
234
260
|
const DefaultStory = ({ text, autoCreate }: StoryProps) => {
|
|
235
|
-
const [item] = useState(
|
|
261
|
+
const [item] = useState(() => Obj.make(TestSchema.Expando, { content: text ?? '' }));
|
|
236
262
|
const [threads, setThreads] = useState<StoryCommentThread[]>([]);
|
|
237
263
|
const [selected, setSelected] = useState<string>();
|
|
238
264
|
|
|
@@ -326,7 +352,7 @@ const DefaultStory = ({ text, autoCreate }: StoryProps) => {
|
|
|
326
352
|
<main className='fixed inset-0 grid grid-cols-[1fr_24rem]'>
|
|
327
353
|
<div role='none' className='max-bs-full overflow-y-auto p-4'>
|
|
328
354
|
<Editor
|
|
329
|
-
|
|
355
|
+
initialValue={item.content}
|
|
330
356
|
selected={selected}
|
|
331
357
|
comments={comments}
|
|
332
358
|
onCreateComment={handleCreateComment}
|
|
@@ -352,7 +378,7 @@ const meta = {
|
|
|
352
378
|
title: 'ui/react-ui-thread/Comments',
|
|
353
379
|
component: StoryThread as any,
|
|
354
380
|
render: DefaultStory,
|
|
355
|
-
decorators: [withTheme],
|
|
381
|
+
decorators: [withTheme()],
|
|
356
382
|
parameters: {
|
|
357
383
|
translations,
|
|
358
384
|
layout: 'fullscreen',
|
|
@@ -8,8 +8,8 @@ import React, { useMemo, useRef, useState } from 'react';
|
|
|
8
8
|
import { PublicKey } from '@dxos/keys';
|
|
9
9
|
import { faker } from '@dxos/random';
|
|
10
10
|
import { withTheme } from '@dxos/react-ui/testing';
|
|
11
|
-
import { createBasicExtensions, createThemeExtensions } from '@dxos/
|
|
12
|
-
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/
|
|
11
|
+
import { createBasicExtensions, createThemeExtensions } from '@dxos/ui-editor';
|
|
12
|
+
import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
|
|
13
13
|
|
|
14
14
|
import { MessageRoot, MessageTextbox } from '../Message';
|
|
15
15
|
import { type MessageEntity, MessageStoryText } from '../testing';
|
|
@@ -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()],
|
|
89
89
|
parameters: {
|
|
90
90
|
layout: 'fullscreen',
|
|
91
91
|
translations,
|
package/src/Thread/Thread.tsx
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
import React, { type ComponentPropsWithRef, forwardRef } from 'react';
|
|
6
6
|
|
|
7
7
|
import { Icon, type ThemedClassName, useTranslation } from '@dxos/react-ui';
|
|
8
|
-
import { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/
|
|
8
|
+
import { hoverableControlItem, hoverableFocusedWithinControls, mx } from '@dxos/ui-theme';
|
|
9
9
|
|
|
10
10
|
import { translationKey } from '../translations';
|
|
11
11
|
import type { ThreadEntity } from '../types';
|
|
12
12
|
|
|
13
|
-
// TODO(burdon):
|
|
13
|
+
// TODO(burdon): Avoid exporting fragments.
|
|
14
14
|
export const threadLayout = 'is-full place-self-start grid grid-cols-[var(--rail-size)_1fr]';
|
|
15
15
|
|
|
16
16
|
//
|
|
@@ -45,10 +45,10 @@ const ThreadRoot = forwardRef<HTMLDivElement, ThreadRootProps>(
|
|
|
45
45
|
// Heading
|
|
46
46
|
//
|
|
47
47
|
|
|
48
|
-
type ThreadHeaderProps =
|
|
48
|
+
type ThreadHeaderProps = ComponentPropsWithRef<'div'> & { detached?: boolean };
|
|
49
49
|
|
|
50
50
|
const ThreadHeader = forwardRef<HTMLParagraphElement, ThreadHeaderProps>(
|
|
51
|
-
({
|
|
51
|
+
({ children, detached, ...props }, forwardedRef) => {
|
|
52
52
|
return (
|
|
53
53
|
<>
|
|
54
54
|
<div role='none' className='flex items-center justify-center text-description'>
|
package/src/testing.tsx
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import { hoverableControlItem, hoverableControls, hoverableFocusedWithinControls, mx } from '@dxos/
|
|
7
|
+
import { IconButton } from '@dxos/react-ui';
|
|
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';
|
|
@@ -25,13 +25,14 @@ export const MessageStoryText = ({ text, onDelete, authorName, timestamp }: Mess
|
|
|
25
25
|
<>
|
|
26
26
|
<MessageHeading authorName={authorName} timestamp={timestamp}>
|
|
27
27
|
{onDelete && (
|
|
28
|
-
<
|
|
28
|
+
<IconButton
|
|
29
|
+
icon='ph--x--regular'
|
|
30
|
+
iconOnly
|
|
31
|
+
label='Delete'
|
|
29
32
|
variant='ghost'
|
|
30
33
|
classNames={['p-1.5 min-bs-0 mie-1 items-start transition-opacity', hoverableControlItem]}
|
|
31
34
|
onClick={() => onDelete()}
|
|
32
|
-
|
|
33
|
-
<Icon icon='ph--x--regular' />
|
|
34
|
-
</Button>
|
|
35
|
+
/>
|
|
35
36
|
)}
|
|
36
37
|
</MessageHeading>
|
|
37
38
|
<MessageBody>{text}</MessageBody>
|