@sanity/orderable-document-list 1.5.1 → 2.0.0
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/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +669 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -83
- package/README.md +0 -218
- package/lib/index.cjs +0 -581
- package/lib/index.cjs.map +0 -1
- package/lib/index.d.cts +0 -65
- package/lib/index.d.ts +0 -65
- package/lib/index.js +0 -589
- package/lib/index.js.map +0 -1
- package/sanity.json +0 -8
- package/src/Document.tsx +0 -138
- package/src/DocumentListQuery.tsx +0 -95
- package/src/DocumentListWrapper.tsx +0 -112
- package/src/DraggableList.tsx +0 -316
- package/src/OrderableContext.ts +0 -7
- package/src/OrderableDocumentList.tsx +0 -81
- package/src/desk-structure/orderableDocumentListDeskItem.ts +0 -84
- package/src/fields/orderRankField.ts +0 -43
- package/src/fields/orderRankOrdering.ts +0 -8
- package/src/helpers/__tests__/getFilteredDeduppedDocs.test.ts +0 -327
- package/src/helpers/__tests__/initialRank.test.ts +0 -20
- package/src/helpers/__tests__/parseOrderRank.test.ts +0 -39
- package/src/helpers/__tests__/reorderDocuments.test.ts +0 -45
- package/src/helpers/client.ts +0 -8
- package/src/helpers/constants.ts +0 -4
- package/src/helpers/getFilteredDedupedDocs.ts +0 -103
- package/src/helpers/initialRank.ts +0 -16
- package/src/helpers/parseOrderRank.ts +0 -21
- package/src/helpers/query.ts +0 -50
- package/src/helpers/reorderDocuments.ts +0 -133
- package/src/helpers/resetOrder.ts +0 -48
- package/src/index.ts +0 -7
- package/src/types.ts +0 -10
- package/v2-incompatible.js +0 -11
package/src/DraggableList.tsx
DELETED
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
import {useEffect, useState, useMemo, useCallback, type CSSProperties} from 'react'
|
|
2
|
-
import {DragDropContext, Draggable, Droppable, type DropResult} from '@hello-pangea/dnd'
|
|
3
|
-
import {Box, Card, useToast} from '@sanity/ui'
|
|
4
|
-
import type {PatchOperations} from 'sanity'
|
|
5
|
-
import {usePaneRouter} from 'sanity/structure'
|
|
6
|
-
|
|
7
|
-
import {Document} from './Document'
|
|
8
|
-
import {reorderDocuments} from './helpers/reorderDocuments'
|
|
9
|
-
import {ORDER_FIELD_NAME} from './helpers/constants'
|
|
10
|
-
import {useSanityClient} from './helpers/client'
|
|
11
|
-
import type {SanityDocumentWithOrder} from './types'
|
|
12
|
-
|
|
13
|
-
interface ListSetting {
|
|
14
|
-
isDuplicate: boolean
|
|
15
|
-
isGhosting: boolean
|
|
16
|
-
isDragging: boolean
|
|
17
|
-
isSelected: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface DraggableListProps {
|
|
21
|
-
data: SanityDocumentWithOrder[]
|
|
22
|
-
listIsUpdating: boolean
|
|
23
|
-
setListIsUpdating: (val: boolean) => void
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const getItemStyle = (
|
|
27
|
-
draggableStyle: CSSProperties | undefined,
|
|
28
|
-
itemIsUpdating: boolean,
|
|
29
|
-
): CSSProperties => ({
|
|
30
|
-
userSelect: 'none',
|
|
31
|
-
transition: 'opacity 500ms ease-in-out',
|
|
32
|
-
opacity: itemIsUpdating ? 0.2 : 1,
|
|
33
|
-
pointerEvents: itemIsUpdating ? 'none' : undefined,
|
|
34
|
-
...draggableStyle,
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
const cardTone = (settings: ListSetting) => {
|
|
38
|
-
const {isDuplicate, isGhosting, isDragging, isSelected} = settings
|
|
39
|
-
|
|
40
|
-
if (isGhosting) return 'transparent'
|
|
41
|
-
if (isDragging || isSelected) return 'primary'
|
|
42
|
-
if (isDuplicate) return 'caution'
|
|
43
|
-
|
|
44
|
-
return undefined
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function DraggableList({data, listIsUpdating, setListIsUpdating}: DraggableListProps) {
|
|
48
|
-
const toast = useToast()
|
|
49
|
-
const router = usePaneRouter()
|
|
50
|
-
const {groupIndex, routerPanesState} = router
|
|
51
|
-
|
|
52
|
-
const currentDoc = routerPanesState[groupIndex + 1]?.[0]?.id || false
|
|
53
|
-
|
|
54
|
-
// Maintains local state order before transaction completes
|
|
55
|
-
const [orderedData, setOrderedData] = useState<SanityDocumentWithOrder[]>(data)
|
|
56
|
-
|
|
57
|
-
// Update local state when documents change from an outside source
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (!listIsUpdating) setOrderedData(data)
|
|
60
|
-
/* eslint-disable-next-line react-hooks/exhaustive-deps */
|
|
61
|
-
}, [data])
|
|
62
|
-
|
|
63
|
-
const [draggingId, setDraggingId] = useState('')
|
|
64
|
-
const [selectedIds, setSelectedIds] = useState<string[]>(currentDoc ? [currentDoc] : [])
|
|
65
|
-
|
|
66
|
-
const clearSelected = useCallback(() => setSelectedIds([]), [setSelectedIds])
|
|
67
|
-
|
|
68
|
-
const handleSelect = useCallback(
|
|
69
|
-
(clickedId: string, index: number, nativeEvent: MouseEvent) => {
|
|
70
|
-
const isSelected = selectedIds.includes(clickedId)
|
|
71
|
-
const selectMultiple = nativeEvent.shiftKey
|
|
72
|
-
const isUsingWindows = navigator.appVersion.indexOf('Win') !== -1
|
|
73
|
-
const selectAdditional = isUsingWindows ? nativeEvent.ctrlKey : nativeEvent.metaKey
|
|
74
|
-
|
|
75
|
-
let updatedIds = []
|
|
76
|
-
|
|
77
|
-
// No modifier keys pressed during click:
|
|
78
|
-
// - update selected to just this one
|
|
79
|
-
// - open document
|
|
80
|
-
if (!selectMultiple && !selectAdditional) {
|
|
81
|
-
return setSelectedIds([clickedId])
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// If shift key was held, prevent default to avoid new window opening
|
|
85
|
-
if (selectMultiple) {
|
|
86
|
-
nativeEvent.preventDefault()
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Shift key was held, add id's between last selected and this one
|
|
90
|
-
// ...before adding this one
|
|
91
|
-
if (selectMultiple && !isSelected) {
|
|
92
|
-
const lastSelectedId = selectedIds[selectedIds.length - 1]
|
|
93
|
-
const lastSelectedIndex = orderedData.findIndex((item) => item._id === lastSelectedId)
|
|
94
|
-
|
|
95
|
-
const firstSelected = index < lastSelectedIndex ? index : lastSelectedIndex
|
|
96
|
-
const lastSelected = index > lastSelectedIndex ? index : lastSelectedIndex
|
|
97
|
-
|
|
98
|
-
const betweenIds = orderedData
|
|
99
|
-
.filter((item, itemIndex) => itemIndex > firstSelected && itemIndex < lastSelected)
|
|
100
|
-
.map((item) => item._id)
|
|
101
|
-
|
|
102
|
-
updatedIds = [...selectedIds, ...betweenIds, clickedId]
|
|
103
|
-
} else if (isSelected) {
|
|
104
|
-
// Toggle off a single id
|
|
105
|
-
updatedIds = selectedIds.filter((id) => id !== clickedId)
|
|
106
|
-
} else {
|
|
107
|
-
// Toggle on a single id
|
|
108
|
-
updatedIds = [...selectedIds, clickedId]
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return setSelectedIds(updatedIds)
|
|
112
|
-
},
|
|
113
|
-
[setSelectedIds, orderedData, selectedIds],
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
const client = useSanityClient()
|
|
117
|
-
|
|
118
|
-
const transactPatches = useCallback(
|
|
119
|
-
async (patches: [string, PatchOperations][], message: string) => {
|
|
120
|
-
const transaction = client.transaction()
|
|
121
|
-
|
|
122
|
-
patches.forEach(([docId, ops]) => transaction.patch(docId, ops))
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
const updated = await transaction.commit({
|
|
126
|
-
visibility: 'sync',
|
|
127
|
-
tag: 'orderable-document-list.reorder',
|
|
128
|
-
})
|
|
129
|
-
clearSelected()
|
|
130
|
-
setDraggingId('')
|
|
131
|
-
setListIsUpdating(false)
|
|
132
|
-
toast.push({
|
|
133
|
-
title: `${
|
|
134
|
-
updated.results.length === 1 ? '1 document' : `${updated.results.length} documents`
|
|
135
|
-
} reordered`,
|
|
136
|
-
status: 'success',
|
|
137
|
-
description: message,
|
|
138
|
-
})
|
|
139
|
-
} catch (err) {
|
|
140
|
-
setDraggingId('')
|
|
141
|
-
setListIsUpdating(false)
|
|
142
|
-
toast.push({
|
|
143
|
-
title: 'Reordering failed',
|
|
144
|
-
status: 'error',
|
|
145
|
-
})
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
[client, setDraggingId, clearSelected, setListIsUpdating, toast],
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
const handleDragEnd = useCallback(
|
|
152
|
-
(result: DropResult | undefined, entities: SanityDocumentWithOrder[]) => {
|
|
153
|
-
setDraggingId('')
|
|
154
|
-
|
|
155
|
-
const {source, destination, draggableId} = result ?? {}
|
|
156
|
-
|
|
157
|
-
// Don't do anything if nothing changed
|
|
158
|
-
if (source?.index === destination?.index) return
|
|
159
|
-
|
|
160
|
-
// Don't do anything if we don't have the entitites
|
|
161
|
-
if (!entities?.length || !draggableId) return
|
|
162
|
-
|
|
163
|
-
// A document can be dragged without being one-of-many-selected
|
|
164
|
-
const effectedIds = selectedIds?.length ? selectedIds : [draggableId]
|
|
165
|
-
|
|
166
|
-
// Don't do anything if we don't have ids to effect
|
|
167
|
-
if (!effectedIds?.length) return
|
|
168
|
-
|
|
169
|
-
// Update state to update styles + prevent data refetching
|
|
170
|
-
setListIsUpdating(true)
|
|
171
|
-
setSelectedIds(effectedIds)
|
|
172
|
-
|
|
173
|
-
const {newOrder, patches, message} = reorderDocuments({
|
|
174
|
-
entities,
|
|
175
|
-
selectedIds: effectedIds,
|
|
176
|
-
source,
|
|
177
|
-
destination,
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
// Update local state
|
|
181
|
-
if (newOrder?.length) {
|
|
182
|
-
setOrderedData(newOrder)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Transact new order patches
|
|
186
|
-
if (patches?.length) {
|
|
187
|
-
transactPatches(patches, message)
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
[selectedIds, setDraggingId, setSelectedIds, transactPatches, setListIsUpdating],
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
const handleDragStart = useCallback(
|
|
194
|
-
(start: {draggableId: string}) => {
|
|
195
|
-
const id = start.draggableId
|
|
196
|
-
const selected = selectedIds.includes(id)
|
|
197
|
-
|
|
198
|
-
// if dragging an item that is not selected - unselect all items
|
|
199
|
-
if (!selected) clearSelected()
|
|
200
|
-
|
|
201
|
-
setDraggingId(id)
|
|
202
|
-
},
|
|
203
|
-
[selectedIds, clearSelected, setDraggingId],
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
// Move one document up or down one place, by fake invoking the drag function
|
|
207
|
-
const incrementIndex = useCallback(
|
|
208
|
-
(shiftFrom: number, shiftTo: number, id: string, entities: SanityDocumentWithOrder[]) => {
|
|
209
|
-
const result = {
|
|
210
|
-
draggableId: id,
|
|
211
|
-
source: {index: shiftFrom},
|
|
212
|
-
destination: {index: shiftTo},
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return handleDragEnd(result as DropResult, entities)
|
|
216
|
-
},
|
|
217
|
-
[handleDragEnd],
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
const onWindowKeyDown = useCallback(
|
|
221
|
-
(event: KeyboardEvent) => {
|
|
222
|
-
if (event.key === 'Escape') {
|
|
223
|
-
clearSelected()
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
[clearSelected],
|
|
227
|
-
)
|
|
228
|
-
|
|
229
|
-
useEffect(() => {
|
|
230
|
-
window.addEventListener('keydown', onWindowKeyDown)
|
|
231
|
-
|
|
232
|
-
return () => {
|
|
233
|
-
window.removeEventListener('keydown', onWindowKeyDown)
|
|
234
|
-
}
|
|
235
|
-
}, [onWindowKeyDown])
|
|
236
|
-
|
|
237
|
-
// Find all items with duplicate order field
|
|
238
|
-
const duplicateOrders = useMemo(() => {
|
|
239
|
-
if (!orderedData.length) return []
|
|
240
|
-
|
|
241
|
-
const orderField = orderedData.map((item) => item[ORDER_FIELD_NAME])
|
|
242
|
-
|
|
243
|
-
return orderField.filter((item, index) => orderField.indexOf(item) !== index)
|
|
244
|
-
}, [orderedData])
|
|
245
|
-
|
|
246
|
-
const onDragEnd = useCallback(
|
|
247
|
-
(result: DropResult) => handleDragEnd(result, orderedData),
|
|
248
|
-
[orderedData, handleDragEnd],
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
return (
|
|
252
|
-
<DragDropContext onDragStart={handleDragStart} onDragEnd={onDragEnd}>
|
|
253
|
-
<Droppable droppableId="documentSortZone">
|
|
254
|
-
{(provided) => (
|
|
255
|
-
<div {...provided.droppableProps} ref={provided.innerRef}>
|
|
256
|
-
{orderedData.map((item, index) => (
|
|
257
|
-
<Draggable
|
|
258
|
-
key={`${item._id}-${item[ORDER_FIELD_NAME]}`}
|
|
259
|
-
draggableId={item._id}
|
|
260
|
-
index={index}
|
|
261
|
-
// onClick={(event) => handleDraggableClick(event, provided, snapshot)}
|
|
262
|
-
>
|
|
263
|
-
{(innerProvided, innerSnapshot) => {
|
|
264
|
-
const isSelected = selectedIds.includes(item._id)
|
|
265
|
-
const isDragging = innerSnapshot.isDragging
|
|
266
|
-
const isGhosting = Boolean(!isDragging && draggingId && isSelected)
|
|
267
|
-
const isUpdating = listIsUpdating && isSelected
|
|
268
|
-
const isDisabled = Boolean(!item[ORDER_FIELD_NAME])
|
|
269
|
-
const isDuplicate = duplicateOrders.includes(item[ORDER_FIELD_NAME])
|
|
270
|
-
const tone = cardTone({isDuplicate, isGhosting, isDragging, isSelected})
|
|
271
|
-
const selectedCount = selectedIds.length
|
|
272
|
-
|
|
273
|
-
const dragBadge = isDragging && selectedCount > 1 ? selectedCount : false
|
|
274
|
-
|
|
275
|
-
return (
|
|
276
|
-
<div
|
|
277
|
-
ref={innerProvided.innerRef}
|
|
278
|
-
{...innerProvided.draggableProps}
|
|
279
|
-
{...innerProvided.dragHandleProps}
|
|
280
|
-
style={
|
|
281
|
-
isDisabled
|
|
282
|
-
? {opacity: 0.2, pointerEvents: 'none'}
|
|
283
|
-
: getItemStyle(innerProvided.draggableProps.style, isUpdating)
|
|
284
|
-
}
|
|
285
|
-
>
|
|
286
|
-
<Box paddingBottom={1}>
|
|
287
|
-
<Card
|
|
288
|
-
tone={tone}
|
|
289
|
-
shadow={isDragging ? 2 : undefined}
|
|
290
|
-
radius={2}
|
|
291
|
-
// eslint-disable-next-line react/jsx-no-bind
|
|
292
|
-
onClick={(e) => handleSelect(item._id, index, e.nativeEvent)}
|
|
293
|
-
>
|
|
294
|
-
<Document
|
|
295
|
-
doc={item}
|
|
296
|
-
entities={orderedData}
|
|
297
|
-
increment={incrementIndex}
|
|
298
|
-
index={index}
|
|
299
|
-
isFirst={index === 0}
|
|
300
|
-
isLast={index === orderedData.length - 1}
|
|
301
|
-
dragBadge={dragBadge}
|
|
302
|
-
/>
|
|
303
|
-
</Card>
|
|
304
|
-
</Box>
|
|
305
|
-
</div>
|
|
306
|
-
)
|
|
307
|
-
}}
|
|
308
|
-
</Draggable>
|
|
309
|
-
))}
|
|
310
|
-
{provided.placeholder}
|
|
311
|
-
</div>
|
|
312
|
-
)}
|
|
313
|
-
</Droppable>
|
|
314
|
-
</DragDropContext>
|
|
315
|
-
)
|
|
316
|
-
}
|
package/src/OrderableContext.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import {Component} from 'react'
|
|
2
|
-
|
|
3
|
-
import type {SanityClient} from '@sanity/client'
|
|
4
|
-
import type {ToastParams} from '@sanity/ui'
|
|
5
|
-
import {DocumentListWrapper} from './DocumentListWrapper'
|
|
6
|
-
import {resetOrder} from './helpers/resetOrder'
|
|
7
|
-
|
|
8
|
-
export interface OrderableDocumentListProps {
|
|
9
|
-
options: {
|
|
10
|
-
type: string
|
|
11
|
-
client: SanityClient
|
|
12
|
-
filter?: string
|
|
13
|
-
params?: Record<string, unknown>
|
|
14
|
-
currentVersion?: string
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface State {
|
|
19
|
-
showIncrements: boolean
|
|
20
|
-
resetOrderTransaction: ToastParams
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Must use a Class Component here so the actionHandlers can be called
|
|
24
|
-
export class OrderableDocumentList extends Component<OrderableDocumentListProps, State> {
|
|
25
|
-
constructor(props: OrderableDocumentListProps) {
|
|
26
|
-
super(props)
|
|
27
|
-
this.state = {
|
|
28
|
-
showIncrements: false,
|
|
29
|
-
resetOrderTransaction: {},
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
actionHandlers = {
|
|
34
|
-
showIncrements: () => {
|
|
35
|
-
this.setState((state) => ({
|
|
36
|
-
showIncrements: !state.showIncrements,
|
|
37
|
-
}))
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
resetOrder: async () => {
|
|
41
|
-
this.setState(() => ({
|
|
42
|
-
resetOrderTransaction: {
|
|
43
|
-
status: `info`,
|
|
44
|
-
title: `Reordering started...`,
|
|
45
|
-
closable: true,
|
|
46
|
-
},
|
|
47
|
-
}))
|
|
48
|
-
|
|
49
|
-
const update = await resetOrder(this.props.options)
|
|
50
|
-
|
|
51
|
-
const reorderWasSuccessful = update?.results?.length
|
|
52
|
-
|
|
53
|
-
this.setState(() => ({
|
|
54
|
-
resetOrderTransaction: {
|
|
55
|
-
status: reorderWasSuccessful ? `success` : `info`,
|
|
56
|
-
title: reorderWasSuccessful
|
|
57
|
-
? `Reordered ${update.results.length === 1 ? `Document` : `Documents`}`
|
|
58
|
-
: `Reordering failed`,
|
|
59
|
-
closable: true,
|
|
60
|
-
},
|
|
61
|
-
}))
|
|
62
|
-
},
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
render() {
|
|
66
|
-
const type = this?.props?.options?.type
|
|
67
|
-
if (!type) {
|
|
68
|
-
return null
|
|
69
|
-
}
|
|
70
|
-
return (
|
|
71
|
-
<DocumentListWrapper
|
|
72
|
-
filter={this?.props?.options?.filter}
|
|
73
|
-
params={this?.props?.options?.params}
|
|
74
|
-
type={type}
|
|
75
|
-
showIncrements={this.state.showIncrements}
|
|
76
|
-
resetOrderTransaction={this.state.resetOrderTransaction}
|
|
77
|
-
currentVersion={this?.props?.options?.currentVersion}
|
|
78
|
-
/>
|
|
79
|
-
)
|
|
80
|
-
}
|
|
81
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import {GenerateIcon, SortIcon} from '@sanity/icons'
|
|
2
|
-
import type {ConfigContext} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import type {ComponentType} from 'react'
|
|
5
|
-
import {StructureBuilder, type ListItem, type MenuItem} from 'sanity/structure'
|
|
6
|
-
import {OrderableDocumentList} from '../OrderableDocumentList'
|
|
7
|
-
import {API_VERSION} from '../helpers/constants'
|
|
8
|
-
|
|
9
|
-
export interface OrderableListConfig {
|
|
10
|
-
type: string
|
|
11
|
-
id?: string
|
|
12
|
-
title?: string
|
|
13
|
-
icon?: ComponentType
|
|
14
|
-
params?: Record<string, unknown>
|
|
15
|
-
filter?: string
|
|
16
|
-
menuItems?: MenuItem[]
|
|
17
|
-
createIntent?: boolean
|
|
18
|
-
context: ConfigContext
|
|
19
|
-
S: StructureBuilder
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function orderableDocumentListDeskItem(config: OrderableListConfig): ListItem {
|
|
23
|
-
if (!config?.type || !config.context || !config.S) {
|
|
24
|
-
throw new Error(`
|
|
25
|
-
type, context and S (StructureBuilder) must be provided.
|
|
26
|
-
context and S are available when configuring structure.
|
|
27
|
-
Example: orderableDocumentListDeskItem({type: 'category'})
|
|
28
|
-
`)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const {type, filter, menuItems = [], createIntent, params, title, icon, id, context, S} = config
|
|
32
|
-
const {schema, getClient} = context
|
|
33
|
-
// 'perspectiveStack' may not exist on ConfigContext in some versions
|
|
34
|
-
const perspectiveStack = (context as any).perspectiveStack || []
|
|
35
|
-
const client = getClient({apiVersion: API_VERSION})
|
|
36
|
-
// the first position in the perspective stack is the current version
|
|
37
|
-
const currentVersion = perspectiveStack[0]
|
|
38
|
-
|
|
39
|
-
const listTitle = title ?? `Orderable ${type}`
|
|
40
|
-
const listId = id ?? `orderable-${type}`
|
|
41
|
-
const listIcon = icon ?? SortIcon
|
|
42
|
-
const typeTitle = schema.get(type)?.title ?? type
|
|
43
|
-
|
|
44
|
-
if (createIntent !== false) {
|
|
45
|
-
menuItems.push(
|
|
46
|
-
S.menuItem()
|
|
47
|
-
.title(`Create new ${typeTitle}`)
|
|
48
|
-
.intent({type: 'create', params: {type}})
|
|
49
|
-
.serialize(),
|
|
50
|
-
)
|
|
51
|
-
}
|
|
52
|
-
return S.listItem()
|
|
53
|
-
.title(listTitle)
|
|
54
|
-
.id(listId)
|
|
55
|
-
.icon(listIcon)
|
|
56
|
-
.schemaType(type)
|
|
57
|
-
.child(
|
|
58
|
-
Object.assign(
|
|
59
|
-
S.documentTypeList(type)
|
|
60
|
-
.canHandleIntent(() => !!createIntent)
|
|
61
|
-
.serialize(),
|
|
62
|
-
{
|
|
63
|
-
// Prevents the component from re-rendering when switching documents
|
|
64
|
-
__preserveInstance: true,
|
|
65
|
-
// Prevents the component from NOT re-rendering when switching listItems
|
|
66
|
-
key: listId,
|
|
67
|
-
|
|
68
|
-
type: 'component',
|
|
69
|
-
component: OrderableDocumentList,
|
|
70
|
-
options: {type, filter, params, client, currentVersion},
|
|
71
|
-
menuItems: [
|
|
72
|
-
...menuItems,
|
|
73
|
-
S.menuItem().title(`Reset Order`).icon(GenerateIcon).action(`resetOrder`).serialize(),
|
|
74
|
-
S.menuItem()
|
|
75
|
-
.title(`Toggle Increments`)
|
|
76
|
-
.icon(SortIcon)
|
|
77
|
-
.action(`showIncrements`)
|
|
78
|
-
.serialize(),
|
|
79
|
-
],
|
|
80
|
-
},
|
|
81
|
-
),
|
|
82
|
-
)
|
|
83
|
-
.serialize()
|
|
84
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import {type ConfigContext, defineField, FieldDefinition, type StringDefinition} from 'sanity'
|
|
2
|
-
import {API_VERSION, ORDER_FIELD_NAME} from '../helpers/constants'
|
|
3
|
-
import {initialRank} from '../helpers/initialRank'
|
|
4
|
-
import type {NewItemPosition} from '../types'
|
|
5
|
-
|
|
6
|
-
export type SchemaContext = Omit<ConfigContext, 'schema' | 'currentUser' | 'client'>
|
|
7
|
-
|
|
8
|
-
export interface RankFieldConfig
|
|
9
|
-
extends Partial<Omit<StringDefinition, 'name' | 'type' | 'initialValue'>> {
|
|
10
|
-
type: string
|
|
11
|
-
newItemPosition?: NewItemPosition
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const orderRankField = (config: RankFieldConfig): FieldDefinition<'string'> => {
|
|
15
|
-
if (!config?.type) {
|
|
16
|
-
throw new Error(
|
|
17
|
-
`
|
|
18
|
-
type must be provided.
|
|
19
|
-
Example: orderRankField({type: 'category'})
|
|
20
|
-
`,
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const {type, newItemPosition = 'after', ...rest} = config
|
|
25
|
-
return defineField({
|
|
26
|
-
title: 'Order Rank',
|
|
27
|
-
readOnly: true,
|
|
28
|
-
hidden: true,
|
|
29
|
-
...rest,
|
|
30
|
-
name: ORDER_FIELD_NAME,
|
|
31
|
-
type: 'string',
|
|
32
|
-
initialValue: async (p, {getClient}) => {
|
|
33
|
-
const direction = newItemPosition === 'before' ? 'asc' : 'desc'
|
|
34
|
-
|
|
35
|
-
const lastDocOrderRank = await getClient({apiVersion: API_VERSION}).fetch(
|
|
36
|
-
`*[_type == $type]|order(@[$order] ${direction})[0][$order]`,
|
|
37
|
-
{type, order: ORDER_FIELD_NAME},
|
|
38
|
-
{tag: 'orderable-document-list.last-doc-order-rank'},
|
|
39
|
-
)
|
|
40
|
-
return initialRank(lastDocOrderRank, newItemPosition)
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
}
|