Skip to content

Commit ae3abb8

Browse files
committed
Some document fixes and better document test
1 parent c84e2a2 commit ae3abb8

File tree

22 files changed

+197
-176
lines changed

22 files changed

+197
-176
lines changed

browser/data-browser/src/chunks/RTE/CollaborativeEditor.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ export default function CollaborativeEditor({
159159
title: 'Resource',
160160
id: 'resource',
161161
icon: FaLink,
162-
command: ({ range }) =>
163-
editor
162+
command: ({ range, editor: internalEditor }) =>
163+
internalEditor
164164
.chain()
165165
.focus()
166166
.deleteRange(range)
@@ -171,11 +171,11 @@ export default function CollaborativeEditor({
171171
title: 'Data Table',
172172
id: 'data-table',
173173
icon: FaTable,
174-
command: ({ range }) => {
174+
command: ({ range, editor: internalEditor }) => {
175175
showNewResourceUI(dataBrowser.classes.table, resource.subject, {
176176
skipNavigation: true,
177177
onCreated: table => {
178-
editor
178+
internalEditor
179179
.chain()
180180
.focus()
181181
.deleteRange(range)
@@ -255,7 +255,7 @@ export default function CollaborativeEditor({
255255
},
256256
},
257257
},
258-
[canWrite],
258+
[canWrite, drive],
259259
);
260260

261261
useEffect(() => {

browser/data-browser/src/chunks/RTE/ColorMenu.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { MdFormatColorFill, MdFormatColorText } from 'react-icons/md';
44
import { useLocalStorage } from '@hooks/useLocalStorage';
55
import styled from 'styled-components';
66
import { transition } from '@helpers/transition';
7-
import { useState, useRef } from 'react';
7+
import { useState, useRef, useEffect } from 'react';
88
import { useEditorState } from '@tiptap/react';
99
import { FaPencil } from 'react-icons/fa6';
1010
import { desaturate, readableColor, setLightness } from 'polished';
@@ -83,6 +83,11 @@ export const ColorMenu: React.FC = () => {
8383
event.preventDefault();
8484
};
8585

86+
useEffect(() => {
87+
// The bubble menu might need to be repositioned if this component is shown.
88+
editor.commands.setMeta('bubbleMenu', 'updatePosition');
89+
}, [editor]);
90+
8691
return (
8792
<Column>
8893
<Row center>

browser/data-browser/src/chunks/RTE/FullBubbleMenu.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { useEditorState } from '@tiptap/react';
1212
import { ToggleButton } from './ToggleButton';
1313
import { useState } from 'react';
1414
import { ColorMenu } from './ColorMenu';
15-
import { flushSync } from 'react-dom';
1615

1716
export const FullBubbleMenu: React.FC = () => {
1817
const editor = useTipTapEditor();
@@ -55,12 +54,8 @@ export const FullBubbleMenu: React.FC = () => {
5554
<BubbleMenu
5655
extraItems={<>{colorMenuOpen && <ColorMenu />}</>}
5756
onShow={() => {
58-
flushSync(() => {
59-
const style = editor.getAttributes('textStyle');
60-
setColorMenuOpen(!!style.color || !!style.backgroundColor);
61-
62-
editor.commands.setMeta('bubbleMenu', 'updatePosition');
63-
});
57+
const style = editor.getAttributes('textStyle');
58+
setColorMenuOpen(!!style.color || !!style.backgroundColor);
6459
}}
6560
>
6661
<Separator />

browser/data-browser/src/chunks/RTE/ResourceExtension/ResourceExtention.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const buildResourceSuggestion = (
3838
drive: string,
3939
): Partial<SuggestionOptions> => ({
4040
items: async ({ query }: { query: string }): Promise<SuggestionItem[]> => {
41-
const results = await store.search(query, {
41+
const results = await store.search(query.toLowerCase(), {
4242
limit: 10,
4343
// Including the results could lead to weird behavior when the document itself is returned from the server.
4444
include: false,
@@ -53,11 +53,7 @@ export const buildResourceSuggestion = (
5353
icon: getIconForClass(r.getClasses()[0]),
5454
command: ({ editor, range }) => {
5555
const subject = r.subject;
56-
const textBeforeQuery = getTextBeforeQuery(editor, range);
57-
58-
// If there is text before the query we are in not in a block context and the resource should be inserted inline.
59-
const isBlockContext = textBeforeQuery.length === 0;
60-
56+
const isBlockContext = getIsBlockContext(editor, range);
6157
const command = editor.chain().focus().deleteRange(range);
6258

6359
if (isBlockContext) {
@@ -72,17 +68,12 @@ export const buildResourceSuggestion = (
7268
render: createRenderFunction<SuggestionItem>(container),
7369
});
7470

75-
const getTextBeforeQuery = (editor: Editor, range: Range) => {
71+
const getIsBlockContext = (editor: Editor, range: Range) => {
7672
const { from } = range;
7773

78-
const queryText = editor.state.doc.textBetween(range.from, range.to);
79-
8074
// Resolve the position and the parent node
8175
const $pos = editor.state.doc.resolve(from);
82-
const parentNode = $pos.parent;
83-
84-
// Calculate the offset within the parent node where the query starts
85-
const startOfQueryOffset = $pos.parentOffset - queryText.length;
8676

87-
return parentNode.textContent.substring(0, startOfQueryOffset).trim();
77+
// Text offset tells us the distance to a previous node. This is 0 if there is no previous node meaning we are in a block context.
78+
return $pos.textOffset === 0;
8879
};

browser/data-browser/src/chunks/RTE/SlashMenu/CommandList.tsx

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { styled } from 'styled-components';
1010
import { ScrollArea } from '../../../components/ScrollArea';
1111
import type { SuggestionItem } from '../types';
1212
import { useOnValueChange } from '@helpers/useOnValueChange';
13+
import { Column } from '@components/Row';
1314

1415
export type CommandListRefType = {
1516
onKeyDown: (event: KeyboardEvent) => boolean;
@@ -82,24 +83,26 @@ export const CommandList = forwardRef<CommandListRefType, CommandListProps>(
8283
);
8384

8485
return (
85-
<ScrollingList type='hover'>
86-
{items.length === 0 && <div>No results found</div>}
87-
{items.map((item, index) => {
88-
const Icon = item.icon;
89-
90-
return (
91-
<ListItemButton
92-
key={item.id}
93-
id={buildItemId(compId, index)}
94-
onClick={() => selectItem(index)}
95-
onMouseEnter={() => setSelectedIndex(index)}
96-
active={selectedIndex === index}
97-
>
98-
<Icon />
99-
{item.title}
100-
</ListItemButton>
101-
);
102-
})}
86+
<ScrollingList type='hover' data-testid='rte-command-list'>
87+
<ContainedColumn gap='0'>
88+
{items.length === 0 && <div>No results found</div>}
89+
{items.map((item, index) => {
90+
const Icon = item.icon;
91+
92+
return (
93+
<ListItemButton
94+
key={item.id}
95+
id={buildItemId(compId, index)}
96+
onClick={() => selectItem(index)}
97+
onMouseEnter={() => setSelectedIndex(index)}
98+
active={selectedIndex === index}
99+
>
100+
<Icon />
101+
<span>{item.title}</span>
102+
</ListItemButton>
103+
);
104+
})}
105+
</ContainedColumn>
103106
</ScrollingList>
104107
);
105108
},
@@ -134,4 +137,19 @@ const ListItemButton = styled.button<{ active: boolean }>`
134137
gap: 1ch;
135138
padding: 0.5rem;
136139
border-radius: ${p => p.theme.radius};
140+
max-width: 60ch;
141+
overflow: hidden;
142+
143+
& > svg {
144+
min-width: 1rem;
145+
flex-basis: 1rem;
146+
}
147+
148+
& > span {
149+
overflow: hidden;
150+
text-overflow: ellipsis;
151+
white-space: nowrap;
152+
}
137153
`;
154+
155+
const ContainedColumn = styled(Column)``;

browser/data-browser/src/chunks/RTE/useYSync.ts

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,30 @@ export function useYSync(
1818
const awareness = new awarenessProtocol.Awareness(doc);
1919

2020
useEffect(() => {
21-
awareness.on(
22-
'update',
23-
({ added, updated, removed }: AwarenessUpdate, origin: string) => {
24-
if (origin !== 'local') {
25-
// Only send local updates to the server.
26-
return;
27-
}
21+
const handleAwarenessUpdate = (
22+
{ added, updated, removed }: AwarenessUpdate,
23+
origin: string,
24+
) => {
25+
if (origin !== 'local') {
26+
// Only send local updates to the server.
27+
return;
28+
}
2829

29-
const changedClients = [...updated, ...added, ...removed];
30+
const changedClients = [...updated, ...added, ...removed];
3031

31-
const encodedUpdate = awarenessProtocol.encodeAwarenessUpdate(
32-
awareness,
33-
changedClients,
34-
);
32+
const encodedUpdate = awarenessProtocol.encodeAwarenessUpdate(
33+
awareness,
34+
changedClients,
35+
);
3536

36-
store.broadcastYSyncUpdate(resource.subject, property, {
37-
awarenessUpdate: encodedUpdate,
38-
});
39-
},
40-
);
37+
store.broadcastYSyncUpdate(resource.subject, property, {
38+
awarenessUpdate: encodedUpdate,
39+
});
40+
};
41+
42+
awareness.on('update', handleAwarenessUpdate);
4143

42-
return store.subscribeYSync(
44+
const unsubYSync = store.subscribeYSync(
4345
resource.subject,
4446
property,
4547
({ awarenessUpdate, docUpdate }) => {
@@ -56,6 +58,11 @@ export function useYSync(
5658
}
5759
},
5860
);
61+
62+
return () => {
63+
awareness.off('update', handleAwarenessUpdate);
64+
unsubYSync();
65+
};
5966
}, [awareness, resource.subject, property, store, doc]);
6067

6168
useEffect(() => {

browser/data-browser/src/components/Main.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
transitionName,
77
} from '../helpers/transitionName';
88
import { ViewTransitionProps } from '../helpers/ViewTransitionProps';
9-
import { MAIN_CONTAINER } from '../helpers/containers';
109
import Parent from './Parent';
1110
import { useResource } from '@tomic/react';
1211
import { CalculatedPageHeight } from '../globalCssVars';
@@ -35,7 +34,6 @@ export function Main({
3534
}
3635

3736
const StyledMain = memo(styled.main<ViewTransitionProps>`
38-
container: ${MAIN_CONTAINER} / inline-size;
3937
${p => transitionName(RESOURCE_PAGE_TRANSITION_TAG, p.subject)};
4038
height: calc(
4139
${CalculatedPageHeight.var()} - ${p => p.theme.heights.breadCrumbBar}
@@ -45,7 +43,7 @@ const StyledMain = memo(styled.main<ViewTransitionProps>`
4543
${p => p.theme.heights.breadCrumbBar} + ${p => p.theme.size(2)}
4644
);
4745
48-
width: 100%;
46+
width: 100cqw;
4947
5048
@media (prefers-reduced-motion: no-preference) {
5149
scroll-behavior: smooth;

browser/data-browser/src/components/Navigation.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { CalculatedPageHeight } from '../globalCssVars';
1717
import { AISidebarContextProvider } from './AI/AISidebarContext';
1818
import { AISidebarContainer } from './AI/AISidebarContainer';
1919
import { HideInPrint } from './HideInPrint';
20+
import { MAIN_CONTAINER } from '@helpers/containers';
2021

2122
export const NAVBAR_HEIGHT = '2.5rem';
2223

@@ -71,6 +72,7 @@ interface ContentProps {
7172
const Content = styled.div<ContentProps>`
7273
display: block;
7374
flex: 1;
75+
container: ${MAIN_CONTAINER} / inline-size;
7476
`;
7577

7678
/** Persistently shown navigation bar */

browser/data-browser/src/components/Parent.tsx

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,31 +34,28 @@ function Parent({ resource }: ParentProps): JSX.Element {
3434

3535
return (
3636
<ParentWrapper aria-label='Breadcrumbs'>
37-
<Row fullWidth center gap='initial'>
38-
{parent ? (
39-
<NestedParent subject={parent} depth={0} />
40-
) : (
41-
<DriveMismatch subject={resource.subject} />
42-
)}
37+
{!parent && <DriveMismatch subject={resource.subject} />}
38+
<BreadcrumbRow center gap='initial'>
39+
{parent && <NestedParent subject={parent} depth={0} />}
4340
<BreadCrumbCurrent>{resource.title}</BreadCrumbCurrent>
44-
<Spacer />
45-
<ButtonArea>
46-
{enableAI && (
47-
<IconButton
48-
title='Toggle AI panel'
49-
variant={IconButtonVariant.Magic}
50-
onClick={() => setIsOpen(prev => !prev)}
51-
>
52-
<AIIcon />
53-
</IconButton>
54-
)}
55-
<ResourceContextMenu
56-
isMainMenu
57-
subject={resource.subject}
58-
trigger={MenuBarDropdownTrigger}
59-
/>
60-
</ButtonArea>
61-
</Row>
41+
</BreadcrumbRow>
42+
<Spacer />
43+
<ButtonArea>
44+
{enableAI && (
45+
<IconButton
46+
title='Toggle AI panel'
47+
variant={IconButtonVariant.Magic}
48+
onClick={() => setIsOpen(prev => !prev)}
49+
>
50+
<AIIcon />
51+
</IconButton>
52+
)}
53+
<ResourceContextMenu
54+
isMainMenu
55+
subject={resource.subject}
56+
trigger={MenuBarDropdownTrigger}
57+
/>
58+
</ButtonArea>
6259
</ParentWrapper>
6360
);
6461
}
@@ -159,7 +156,7 @@ const BreadCrumbBase = css`
159156
white-space: nowrap;
160157
overflow: hidden;
161158
text-overflow: ellipsis;
162-
max-width: 50ch;
159+
min-width: 0;
163160
`;
164161

165162
const BreadCrumbCurrent = styled.span`
@@ -169,7 +166,7 @@ const BreadCrumbCurrent = styled.span`
169166
const Breadcrumb = styled.a`
170167
${BreadCrumbBase}
171168
align-self: center;
172-
cursor: 'pointer';
169+
cursor: pointer;
173170
text-decoration: none;
174171
border-radius: ${p => p.theme.radius};
175172
@@ -183,6 +180,16 @@ const Breadcrumb = styled.a`
183180
}
184181
`;
185182

183+
const BreadcrumbRow = styled(Row)`
184+
flex-shrink: 1;
185+
min-width: 0;
186+
overflow: hidden;
187+
max-width: 80vw;
188+
& > * {
189+
min-width: 0;
190+
}
191+
`;
192+
186193
const Spacer = styled.span`
187194
flex: 1;
188195
`;

0 commit comments

Comments
 (0)