Skip to content

Commit ba3251b

Browse files
committed
Notes: Refactor and extract offset calculation logic
1 parent ad255e8 commit ba3251b

6 files changed

Lines changed: 408 additions & 223 deletions

File tree

packages/editor/src/components/collab-sidebar/add-comment.js

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import clsx from 'clsx';
51
/**
62
* WordPress dependencies
73
*/
84
import { __ } from '@wordpress/i18n';
95
import { useSelect, useDispatch } from '@wordpress/data';
10-
import {
11-
__experimentalHStack as HStack,
12-
__experimentalVStack as VStack,
13-
} from '@wordpress/components';
6+
import { __experimentalHStack as HStack } from '@wordpress/components';
147
import {
158
store as blockEditorStore,
169
privateApis as blockEditorPrivateApis,
@@ -22,6 +15,7 @@ import {
2215
import { unlock } from '../../lock-unlock';
2316
import CommentAuthorInfo from './comment-author-info';
2417
import CommentForm from './comment-form';
18+
import { FloatingContainer } from './floating-container';
2519
import { focusCommentThread, noop } from './utils';
2620
import { store as editorStore } from '../../store';
2721

@@ -31,9 +25,7 @@ export function AddComment( {
3125
onSubmit,
3226
commentSidebarRef,
3327
reflowComments = noop,
34-
isFloating = false,
35-
y,
36-
refs,
28+
floating,
3729
} ) {
3830
const { clientId } = useSelect( ( select ) => {
3931
const { getSelectedBlockClientId } = select( blockEditorStore );
@@ -60,23 +52,15 @@ export function AddComment( {
6052
}
6153

6254
return (
63-
<VStack
64-
className={ clsx(
65-
'editor-collab-sidebar-panel__thread is-selected',
66-
{
67-
'is-floating': isFloating,
68-
}
69-
) }
55+
<FloatingContainer
56+
floating={ floating }
57+
className="editor-collab-sidebar-panel__thread is-selected"
7058
spacing="3"
7159
tabIndex={ 0 }
7260
aria-label={ __( 'New note' ) }
7361
role="treeitem"
74-
ref={ isFloating ? refs.setFloating : undefined }
7562
style={
76-
isFloating
77-
? // Delay showing the floating note box until a Y position is known to prevent blink.
78-
{ top: y, opacity: ! y ? 0 : undefined }
79-
: undefined
63+
floating ? { opacity: ! floating.y ? 0 : undefined } : undefined
8064
}
8165
onBlur={ ( event ) => {
8266
// Don't deselect notes when the browser window/tab loses focus.
@@ -104,6 +88,6 @@ export function AddComment( {
10488
submitButtonText={ __( 'Add note' ) }
10589
labelText={ __( 'New note' ) }
10690
/>
107-
</VStack>
91+
</FloatingContainer>
10892
);
10993
}

packages/editor/src/components/collab-sidebar/comments.js

Lines changed: 28 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import {
1010
useState,
1111
RawHTML,
1212
useEffect,
13-
useCallback,
1413
useMemo,
1514
useRef,
1615
} from '@wordpress/element';
@@ -41,7 +40,8 @@ import { unlock } from '../../lock-unlock';
4140
import CommentAuthorInfo from './comment-author-info';
4241
import CommentForm from './comment-form';
4342
import { focusCommentThread, getCommentExcerpt } from './utils';
44-
import { useFloatingThread } from './hooks';
43+
import { useFloatingBoard, useFloatingThread } from './hooks';
44+
import { FloatingContainer } from './floating-container';
4545
import { AddComment } from './add-comment';
4646
import { store as editorStore } from '../../store';
4747

@@ -58,13 +58,7 @@ export function Comments( {
5858
isFloating = false,
5959
commentLastUpdated,
6060
} ) {
61-
const [ heights, setHeights ] = useState( {} );
62-
const [ boardOffsets, setBoardOffsets ] = useState( {} );
63-
const [ blockRefs, setBlockRefs ] = useState( {} );
64-
65-
const { setCanvasMinHeight, selectNote } = unlock(
66-
useDispatch( editorStore )
67-
);
61+
const { selectNote } = unlock( useDispatch( editorStore ) );
6862
const { selectBlock, toggleBlockSpotlight } = unlock(
6963
useDispatch( blockEditorStore )
7064
);
@@ -182,151 +176,11 @@ export function Comments( {
182176
}
183177
}, [ noteFocused, selectedNote, selectNote, commentSidebarRef ] );
184178

185-
// Recalculate floating comment thread offsets whenever the heights change.
186-
useEffect( () => {
187-
/**
188-
* Calculate the y offsets for all comment threads. Account for potentially
189-
* overlapping threads and adjust their positions accordingly.
190-
*/
191-
const calculateAllOffsets = () => {
192-
const offsets = {};
193-
194-
if ( ! isFloating ) {
195-
return { offsets, minHeight: 0 };
196-
}
197-
198-
// Find the index of the selected thread.
199-
const selectedThreadIndex = threads.findIndex(
200-
( t ) => t.id === selectedNote
201-
);
202-
203-
const breakIndex =
204-
selectedThreadIndex === -1 ? 0 : selectedThreadIndex;
205-
206-
// If there is a selected thread, push threads above up and threads below down.
207-
const selectedThreadData = threads[ breakIndex ];
208-
209-
if (
210-
! selectedThreadData ||
211-
! blockRefs[ selectedThreadData.id ]
212-
) {
213-
return { offsets, minHeight: 0 };
214-
}
215-
216-
let blockElement = blockRefs[ selectedThreadData.id ];
217-
let blockRect = blockElement?.getBoundingClientRect();
218-
const selectedThreadTop = blockRect?.top || 0;
219-
const selectedThreadHeight = heights[ selectedThreadData.id ] || 0;
220-
221-
offsets[ selectedThreadData.id ] = -16;
222-
223-
let previousThreadData = {
224-
threadTop: selectedThreadTop - 16,
225-
threadHeight: selectedThreadHeight,
226-
};
227-
228-
// Process threads after the selected thread, offsetting any overlapping
229-
// threads downward.
230-
for ( let i = breakIndex + 1; i < threads.length; i++ ) {
231-
const thread = threads[ i ];
232-
if ( ! blockRefs[ thread.id ] ) {
233-
continue;
234-
}
235-
236-
blockElement = blockRefs[ thread.id ];
237-
blockRect = blockElement?.getBoundingClientRect();
238-
const threadTop = blockRect?.top || 0;
239-
const threadHeight = heights[ thread.id ] || 0;
240-
241-
let additionalOffset = -16;
242-
243-
// Check if the thread overlaps with the previous one.
244-
const previousBottom =
245-
previousThreadData.threadTop +
246-
previousThreadData.threadHeight;
247-
if ( threadTop < previousBottom + 16 ) {
248-
// Shift down by the difference plus a margin to avoid overlap.
249-
additionalOffset = previousBottom - threadTop + 20;
250-
}
251-
252-
offsets[ thread.id ] = additionalOffset;
253-
254-
// Update for next iteration.
255-
previousThreadData = {
256-
threadTop: threadTop + additionalOffset,
257-
threadHeight,
258-
};
259-
}
260-
261-
// Process threads before the selected thread, offsetting any overlapping
262-
// threads upward.
263-
let nextThreadData = {
264-
threadTop: selectedThreadTop - 16,
265-
};
266-
267-
for ( let i = selectedThreadIndex - 1; i >= 0; i-- ) {
268-
const thread = threads[ i ];
269-
if ( ! blockRefs[ thread.id ] ) {
270-
continue;
271-
}
272-
273-
blockElement = blockRefs[ thread.id ];
274-
blockRect = blockElement?.getBoundingClientRect();
275-
const threadTop = blockRect?.top || 0;
276-
const threadHeight = heights[ thread.id ] || 0;
277-
278-
let additionalOffset = -16;
279-
280-
// Calculate the bottom position of this thread with default offset.
281-
const threadBottom = threadTop + threadHeight;
282-
283-
// Check if this thread's bottom would overlap with the next thread's top.
284-
if ( threadBottom > nextThreadData.threadTop ) {
285-
// Shift up by the difference plus a margin to avoid overlap.
286-
additionalOffset =
287-
nextThreadData.threadTop -
288-
threadTop -
289-
threadHeight -
290-
20;
291-
}
292-
293-
offsets[ thread.id ] = additionalOffset;
294-
295-
// Update for next iteration (going upward).
296-
nextThreadData = {
297-
threadTop: threadTop + additionalOffset,
298-
};
299-
}
300-
301-
let editorMinHeight = 0;
302-
// Take the calculated top of the final note plus its height as the editor min height.
303-
const lastThread = threads[ threads.length - 1 ];
304-
if ( blockRefs[ lastThread.id ] ) {
305-
const lastBlockElement = blockRefs[ lastThread.id ];
306-
const lastBlockRect = lastBlockElement?.getBoundingClientRect();
307-
const lastThreadTop = lastBlockRect?.top || 0;
308-
const lastThreadHeight = heights[ lastThread.id ] || 0;
309-
const lastThreadOffset = offsets[ lastThread.id ] || 0;
310-
editorMinHeight =
311-
lastThreadTop + lastThreadHeight + lastThreadOffset + 32;
312-
}
313-
314-
return { offsets, minHeight: editorMinHeight };
315-
};
316-
const { offsets: newOffsets, minHeight } = calculateAllOffsets();
317-
if ( Object.keys( newOffsets ).length > 0 ) {
318-
setBoardOffsets( newOffsets );
319-
}
320-
// Ensure the editor has enough height to scroll to all notes.
321-
setCanvasMinHeight( minHeight );
322-
}, [
323-
heights,
324-
blockRefs,
325-
isFloating,
179+
const { boardOffsets, registerThread, reportHeight } = useFloatingBoard( {
326180
threads,
327-
selectedNote,
328-
setCanvasMinHeight,
329-
] );
181+
selectedNoteId: selectedNote,
182+
isFloating,
183+
} );
330184

331185
const handleThreadNavigation = ( event, thread, isSelected ) => {
332186
if ( event.defaultPrevented ) {
@@ -393,10 +247,6 @@ export function Comments( {
393247
}
394248
};
395249

396-
const setBlockRef = useCallback( ( id, blockRef ) => {
397-
setBlockRefs( ( prev ) => ( { ...prev, [ id ]: blockRef } ) );
398-
}, [] );
399-
400250
const hasThreads = Array.isArray( threads ) && threads.length > 0;
401251
// A special case for `template-locked` mode - https://github.com/WordPress/gutenberg/pull/72646.
402252
if ( ! hasThreads && ! isFloating ) {
@@ -426,11 +276,17 @@ export function Comments( {
426276
isSelected={ selectedNote === thread.id }
427277
commentSidebarRef={ commentSidebarRef }
428278
reflowComments={ reflowComments }
429-
isFloating={ isFloating }
430-
calculatedOffset={ boardOffsets[ thread.id ] ?? 0 }
431-
setHeights={ setHeights }
432-
setBlockRef={ setBlockRef }
433-
commentLastUpdated={ commentLastUpdated }
279+
floating={
280+
isFloating
281+
? {
282+
calculatedOffset:
283+
boardOffsets[ thread.id ] ?? 0,
284+
reportHeight,
285+
registerThread,
286+
commentLastUpdated,
287+
}
288+
: undefined
289+
}
434290
onKeyDown={ ( event ) =>
435291
handleThreadNavigation(
436292
event,
@@ -452,13 +308,10 @@ function Thread( {
452308
isSelected,
453309
commentSidebarRef,
454310
reflowComments,
455-
isFloating,
456-
calculatedOffset,
457-
setHeights,
458-
setBlockRef,
459-
commentLastUpdated,
311+
floating,
460312
onKeyDown,
461313
} ) {
314+
const isFloating = !! floating;
462315
const { toggleBlockHighlight, selectBlock, toggleBlockSpotlight } = unlock(
463316
useDispatch( blockEditorStore )
464317
);
@@ -474,11 +327,11 @@ function Thread( {
474327
);
475328
const { y, refs } = useFloatingThread( {
476329
thread,
477-
calculatedOffset,
478-
setHeights,
479-
setBlockRef,
330+
calculatedOffset: floating?.calculatedOffset ?? 0,
331+
reportHeight: floating?.reportHeight,
332+
registerThread: floating?.registerThread,
480333
selectedThread: selectedNote,
481-
commentLastUpdated,
334+
commentLastUpdated: floating?.commentLastUpdated,
482335
} );
483336
const isKeyboardTabbingRef = useRef( false );
484337

@@ -571,18 +424,16 @@ function Thread( {
571424
onSubmit={ onAddReply }
572425
commentSidebarRef={ commentSidebarRef }
573426
reflowComments={ reflowComments }
574-
isFloating={ isFloating }
575-
y={ y }
576-
refs={ refs }
427+
floating={ { y, refs } }
577428
/>
578429
);
579430
}
580431

581432
return (
582-
<VStack
433+
<FloatingContainer
434+
floating={ isFloating ? { y, refs } : undefined }
583435
className={ clsx( 'editor-collab-sidebar-panel__thread', {
584436
'is-selected': isSelected,
585-
'is-floating': isFloating,
586437
} ) }
587438
id={ `comment-thread-${ thread.id }` }
588439
spacing="3"
@@ -607,8 +458,6 @@ function Thread( {
607458
role="treeitem"
608459
aria-label={ ariaLabel }
609460
aria-expanded={ isSelected }
610-
ref={ isFloating ? refs.setFloating : undefined }
611-
style={ isFloating ? { top: y } : undefined }
612461
>
613462
<Button
614463
className="editor-collab-sidebar-panel__skip-to-comment"
@@ -759,7 +608,7 @@ function Thread( {
759608
{ __( 'Back to block' ) }
760609
</Button>
761610
) }
762-
</VStack>
611+
</FloatingContainer>
763612
);
764613
}
765614

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import clsx from 'clsx';
5+
6+
/**
7+
* WordPress dependencies
8+
*/
9+
import { __experimentalVStack as VStack } from '@wordpress/components';
10+
11+
export function FloatingContainer( {
12+
floating,
13+
className,
14+
style,
15+
children,
16+
...props
17+
} ) {
18+
const isFloating = !! floating;
19+
return (
20+
<VStack
21+
className={ clsx( className, { 'is-floating': isFloating } ) }
22+
ref={ isFloating ? floating.refs.setFloating : undefined }
23+
style={ isFloating ? { top: floating.y, ...style } : style }
24+
{ ...props }
25+
>
26+
{ children }
27+
</VStack>
28+
);
29+
}

0 commit comments

Comments
 (0)