Skip to content

Commit a977c1c

Browse files
committed
fix: up/down arrow selections
1 parent 0216710 commit a977c1c

File tree

7 files changed

+63
-64
lines changed

7 files changed

+63
-64
lines changed

packages/ui/src/elements/TreeView/NestedSectionsTable/Row/index.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ interface DivTableRowProps {
5252
shiftKey: boolean
5353
}
5454
}) => void
55-
openItemIDs?: Set<number | string>
55+
openItemKeys?: Set<ItemKey>
5656
rowItem: SectionRow
5757
segmentWidth: number
5858
selectedItemKeys: Set<ItemKey>
@@ -83,7 +83,7 @@ export const Row: React.FC<DivTableRowProps> = ({
8383
onRowDrag,
8484
onRowKeyPress,
8585
onSelectionChange,
86-
openItemIDs,
86+
openItemKeys,
8787
rowItem,
8888
segmentWidth,
8989
selectedItemKeys,
@@ -226,7 +226,7 @@ export const Row: React.FC<DivTableRowProps> = ({
226226
</div>
227227
) : (
228228
<ChevronIcon
229-
direction={openItemIDs?.has(rowItem.rowID) ? 'down' : 'right'}
229+
direction={openItemKeys?.has(rowItem.rowID) ? 'down' : 'right'}
230230
/>
231231
)}
232232
</Button>

packages/ui/src/elements/TreeView/NestedSectionsTable/TableSection/index.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ interface RenderTableSectionProps {
3636
shiftKey: boolean
3737
}
3838
}) => void
39-
openItemIDs?: Set<number | string>
39+
openItemKeys?: Set<ItemKey>
4040
parentItems?: SectionRow[]
4141
rowIndexOffset?: number
4242
rows: SectionRow[]
@@ -64,7 +64,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
6464
onRowDrag,
6565
onRowKeyPress,
6666
onSelectionChange,
67-
openItemIDs,
67+
openItemKeys,
6868
parentItems = [],
6969
rowIndexOffset = 0,
7070
rows,
@@ -76,7 +76,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
7676
// Helper to count all rows recursively, only counting visible (open) rows
7777
const countRows = (items: SectionRow[]): number => {
7878
return items.reduce((count, item) => {
79-
const isOpen = openItemIDs?.has(item.rowID)
79+
const isOpen = openItemKeys?.has(item.rowID)
8080
return count + 1 + (item.rows && isOpen ? countRows(item.rows) : 0)
8181
}, 0)
8282
}
@@ -86,7 +86,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
8686
let offset = rowIndexOffset
8787
for (let i = 0; i < index; i++) {
8888
offset += 1
89-
const isOpen = openItemIDs?.has(rows[i].rowID)
89+
const isOpen = openItemKeys?.has(rows[i].rowID)
9090
if (rows[i].rows && isOpen) {
9191
offset += countRows(rows[i].rows || [])
9292
}
@@ -99,7 +99,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
9999
{rows.map((rowItem, sectionRowIndex: number) => {
100100
const absoluteRowIndex = getAbsoluteRowIndex(sectionRowIndex)
101101
const isLastRow = rows.length - 1 === sectionRowIndex
102-
const hasNestedRows = Boolean(rowItem?.rows?.length) && openItemIDs?.has(rowItem.rowID)
102+
const hasNestedRows = Boolean(rowItem?.rows?.length) && openItemKeys?.has(rowItem.rowID)
103103
const isRowAtRootLevel = level === 0 || (isLastRow && isLastRowOfRoot)
104104

105105
// Calculate drop target items based on position in hierarchy
@@ -158,7 +158,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
158158
onRowDrag={onRowDrag}
159159
onRowKeyPress={onRowKeyPress}
160160
onSelectionChange={onSelectionChange}
161-
openItemIDs={openItemIDs}
161+
openItemKeys={openItemKeys}
162162
rowItem={rowItem}
163163
segmentWidth={segmentWidth}
164164
selectedItemKeys={selectedItemKeys}
@@ -186,7 +186,7 @@ export const TableSection: React.FC<RenderTableSectionProps> = ({
186186
onRowDrag={onRowDrag}
187187
onRowKeyPress={onRowKeyPress}
188188
onSelectionChange={onSelectionChange}
189-
openItemIDs={openItemIDs}
189+
openItemKeys={openItemKeys}
190190
parentItems={[...parentItems, rowItem]}
191191
rowIndexOffset={absoluteRowIndex + 1}
192192
rows={rowItem.rows}

packages/ui/src/elements/TreeView/NestedSectionsTable/index.tsx

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
1515
dropContextName,
1616
hoveredRowID,
1717
isDragging = false,
18-
isRowFocusable = () => true,
18+
isRowFocusable,
1919
loadingRowIDs,
2020
onDroppableHover,
2121
onEnter,
2222
onEscape,
2323
onRowDrag,
2424
onSelectAll,
25-
openItemIDs,
25+
openItemKeys,
2626
sections,
2727
segmentWidth = DEFAULT_SEGMENT_WIDTH,
2828
selectedItemKeys,
@@ -42,13 +42,13 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
4242
let count = 0
4343
for (const row of rows) {
4444
count++
45-
if (row.rows && openItemIDs?.has(row.rowID)) {
45+
if (row.rows && openItemKeys?.has(row.rowID)) {
4646
count += countVisibleRows(row.rows)
4747
}
4848
}
4949
return count
5050
},
51-
[openItemIDs],
51+
[openItemKeys],
5252
)
5353

5454
const totalVisibleRows = React.useMemo(
@@ -70,7 +70,7 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
7070
return row
7171
}
7272
currentIndex++
73-
if (row.rows && openItemIDs?.has(row.rowID)) {
73+
if (row.rows && openItemKeys?.has(row.rowID)) {
7474
const found = findRow(row.rows)
7575
if (found) {
7676
return found
@@ -82,7 +82,7 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
8282

8383
return findRow(sections)
8484
},
85-
[sections, openItemIDs],
85+
[sections, openItemKeys],
8686
)
8787

8888
// Get visual index from row ID
@@ -99,7 +99,7 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
9999
return currentIndex
100100
}
101101
currentIndex++
102-
if (row.rows && openItemIDs?.has(row.rowID)) {
102+
if (row.rows && openItemKeys?.has(row.rowID)) {
103103
const found = findIndex(row.rows)
104104
if (found !== -1) {
105105
return found
@@ -111,7 +111,7 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
111111

112112
return sections ? findIndex(sections) : -1
113113
},
114-
[sections, openItemIDs],
114+
[sections, openItemKeys],
115115
)
116116

117117
// Get the current focused row index for passing down to Row components
@@ -207,13 +207,6 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
207207
const currentIndex = getIndexFromRowID(row.rowID)
208208
let nextIndex = currentIndex + direction
209209

210-
if (shiftKey) {
211-
onSelectionChange({
212-
itemKey: row.rowID,
213-
options: { ctrlKey, metaKey, shiftKey },
214-
})
215-
}
216-
217210
// Find next focusable row
218211
while (nextIndex >= 0 && nextIndex < totalVisibleRows) {
219212
const nextRow = getRowAtVisibleIndex(nextIndex)
@@ -349,7 +342,7 @@ export const NestedSectionsTable: React.FC<NestedSectionsTableProps> = ({
349342
onRowDrag={onRowDrag}
350343
onRowKeyPress={handleRowKeyPress}
351344
onSelectionChange={onSelectionChange}
352-
openItemIDs={openItemIDs}
345+
openItemKeys={openItemKeys}
353346
parentItems={[]}
354347
rowIndexOffset={0}
355348
rows={sections}

packages/ui/src/elements/TreeView/NestedSectionsTable/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export type NestedSectionsTableProps = {
2828
onEscape: () => void
2929
onRowDrag?: (params: { event: PointerEvent; item: null | SectionRow }) => void
3030
onSelectAll: () => void
31-
openItemIDs?: Set<number | string>
31+
openItemKeys?: Set<ItemKey>
3232
sections: SectionRow[]
3333
segmentWidth?: number
3434
selectedItemKeys: Set<ItemKey>

packages/ui/src/elements/TreeView/TreeViewTable/index.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useDndMonitor } from '@dnd-kit/core'
66
import React from 'react'
77
import { toast } from 'sonner'
88

9-
import type { SectionRow } from '../NestedSectionsTable/types.js'
9+
import type { ItemKey, SectionRow } from '../NestedSectionsTable/types.js'
1010

1111
import { useTranslation } from '../../../providers/Translation/index.js'
1212
import { useTreeView } from '../../../providers/TreeView/index.js'
@@ -28,7 +28,7 @@ export function TreeViewTable() {
2828
loadingRowIDs,
2929
moveItems,
3030
onItemSelection,
31-
openItemIDs,
31+
openItemKeys,
3232
selectAll,
3333
selectedItemKeys,
3434
toggleRow,
@@ -46,13 +46,6 @@ export function TreeViewTable() {
4646
[items, i18n.language],
4747
)
4848

49-
const selectedRowIDs = React.useMemo(() => {
50-
return Array.from(selectedItemKeys).map((key) => {
51-
const doc = items.find((d) => d.itemKey === key)
52-
return doc?.value.id || ''
53-
})
54-
}, [selectedItemKeys, items])
55-
5649
// Handle drag/drop events
5750
const onDragEnd = React.useCallback(
5851
async (event: DragEndEvent) => {
@@ -65,14 +58,24 @@ export function TreeViewTable() {
6558
'targetItem' in event.over.data.current
6659
) {
6760
const selectedItems = getSelectedItems()
68-
const docIDs = selectedItems.map((doc) => doc.value.id)
61+
const { docsToMove, itemKeys } = selectedItems.reduce(
62+
(acc, doc) => {
63+
acc.itemKeys.add(doc.itemKey)
64+
acc.docsToMove.push({
65+
relationTo: doc.relationTo,
66+
value: doc.value.id,
67+
})
68+
return acc
69+
},
70+
{ docsToMove: [], itemKeys: new Set<ItemKey>() },
71+
)
6972
const targetItem = event.over.data.current.targetItem
7073
const targetID = targetItem?.rowID
7174

7275
// Validate: prevent moving a parent into its own descendant
73-
const invalidTargets = new Set<number | string>()
74-
docIDs.forEach((id) => {
75-
const descendants = getAllDescendantIDs({ itemIDs: [id], items })
76+
const invalidTargets = new Set<ItemKey>()
77+
itemKeys.forEach((itemKey) => {
78+
const descendants = getAllDescendantIDs({ itemKeys: new Set([itemKey]), items })
7679
descendants.forEach((descendantID) => invalidTargets.add(descendantID))
7780
})
7881
if (targetID && invalidTargets.has(targetID)) {
@@ -82,7 +85,7 @@ export function TreeViewTable() {
8285

8386
try {
8487
await moveItems({
85-
docIDs,
88+
docsToMove,
8689
parentID: targetID,
8790
})
8891
} catch (error) {
@@ -144,7 +147,6 @@ export function TreeViewTable() {
144147
const isCurrentlySelected = selectedItemKeys.has(dragItemDoc.itemKey)
145148

146149
if (!isCurrentlySelected) {
147-
const index = items.findIndex((d) => d.itemKey === dragItemDoc.itemKey)
148150
onItemSelection({
149151
eventOptions: {
150152
ctrlKey: event.ctrlKey || event.metaKey,
@@ -159,8 +161,8 @@ export function TreeViewTable() {
159161
)
160162

161163
const unfocusableIDs = React.useMemo(() => {
162-
return getAllDescendantIDs({ itemIDs: selectedRowIDs, items })
163-
}, [selectedRowIDs, items])
164+
return getAllDescendantIDs({ itemKeys: selectedItemKeys, items })
165+
}, [selectedItemKeys, items])
164166

165167
const isRowFocusable = React.useCallback(
166168
(row: SectionRow) => {
@@ -198,7 +200,7 @@ export function TreeViewTable() {
198200
onEscape={clearSelections}
199201
onRowDrag={onRowDrag}
200202
onSelectAll={handleSelectAll}
201-
openItemIDs={openItemIDs}
203+
openItemKeys={openItemKeys}
202204
sections={sections}
203205
selectedItemKeys={selectedItemKeys}
204206
targetParentID={targetParentID}

packages/ui/src/elements/TreeView/utils/getAllDescendantIDs.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import type { TreeViewItem } from 'payload/shared'
22

3+
import type { ItemKey } from './itemsToSectionRows.js'
4+
35
type GetAllDescendantIDsArgs = {
46
/**
5-
* IDs of the items to get descendants for
7+
* Item Keys of the items to get descendants for
68
*/
7-
itemIDs: (number | string)[]
9+
itemKeys: Set<ItemKey>
810
/**
911
* All items in the tree
1012
*/
@@ -31,24 +33,21 @@ type GetAllDescendantIDsArgs = {
3133
* getAllDescendantIDs({ itemID: ['A', 'D'], items: documents })
3234
* // Returns Set { 'A', 'B', 'C', 'D' }
3335
*/
34-
export function getAllDescendantIDs({
35-
itemIDs,
36-
items,
37-
}: GetAllDescendantIDsArgs): Set<number | string> {
38-
const result = new Set<number | string>()
36+
export function getAllDescendantIDs({ itemKeys, items }: GetAllDescendantIDsArgs): Set<ItemKey> {
37+
const result = new Set<ItemKey>()
3938

40-
const collectDescendants = (parentID: number | string) => {
39+
const collectDescendants = (parentItemKey: ItemKey) => {
4140
items.forEach((doc) => {
42-
if (doc.value.parentID === parentID) {
43-
result.add(doc.value.id)
41+
if (doc.parentItemKey === parentItemKey) {
42+
result.add(doc.itemKey)
4443
// Recursively collect this child's descendants
45-
collectDescendants(doc.value.id)
44+
collectDescendants(doc.itemKey)
4645
}
4746
})
4847
}
4948

50-
itemIDs.forEach((id) => {
51-
collectDescendants(id)
49+
itemKeys.forEach((itemKey) => {
50+
collectDescendants(itemKey)
5251
})
5352

5453
return result

packages/ui/src/providers/TreeView/index.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ export type TreeViewContextValue = {
3232
itemKeysToMove?: Set<TreeViewItemKey>
3333
items?: TreeViewItem[]
3434
loadingRowIDs: Set<number | string>
35-
moveItems: (args: { docIDs: (number | string)[]; parentID?: number | string }) => Promise<void>
35+
moveItems: (args: {
36+
docsToMove: { relationTo: string; value: number | string }[]
37+
parentID?: number | string
38+
}) => Promise<void>
3639
onItemSelection: (args: {
3740
eventOptions?: {
3841
ctrlKey?: boolean
@@ -41,7 +44,7 @@ export type TreeViewContextValue = {
4144
}
4245
item: TreeViewItem
4346
}) => void
44-
openItemIDs: Set<number | string>
47+
openItemKeys: Set<ItemKey>
4548
parentFieldName: string
4649
refineTreeViewData: (args: { query?: TreeViewQueryParams; updateURL: boolean }) => void
4750
search: string
@@ -63,7 +66,7 @@ const Context = React.createContext<TreeViewContextValue>({
6366
loadingRowIDs: new Set<number | string>(),
6467
moveItems: () => Promise.resolve(undefined),
6568
onItemSelection: () => undefined,
66-
openItemIDs: new Set<number | string>(),
69+
openItemKeys: new Set<ItemKey>(),
6770
parentFieldName: '_parentDoc',
6871
refineTreeViewData: () => undefined,
6972
search: '',
@@ -240,11 +243,13 @@ export function TreeViewProvider({
240243

241244
const moveItems: TreeViewContextValue['moveItems'] = React.useCallback(
242245
async (args) => {
243-
const { docIDs, parentID } = args
244-
if (!docIDs.length) {
246+
const { docsToMove, parentID } = args
247+
if (!docsToMove.length) {
245248
return
246249
}
247250

251+
const docIDs = docsToMove.map((d) => d.value)
252+
248253
// Optimistically update local documents
249254
setItems((prevDocs) =>
250255
prevDocs.map((doc) =>
@@ -402,7 +407,7 @@ export function TreeViewProvider({
402407
loadingRowIDs,
403408
moveItems,
404409
onItemSelection,
405-
openItemIDs: openItemKeys,
410+
openItemKeys,
406411
parentFieldName,
407412
refineTreeViewData,
408413
search,

0 commit comments

Comments
 (0)