@sanity/orderable-document-list 1.0.4 → 1.1.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/README.md +54 -51
- package/lib/index.esm.js +770 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +783 -1
- package/lib/index.js.map +1 -1
- package/package.json +40 -39
- package/src/Document.tsx +72 -40
- package/src/DocumentListQuery.tsx +75 -66
- package/src/DocumentListWrapper.tsx +10 -4
- package/src/DraggableList.tsx +37 -24
- package/src/OrderableDocumentList.tsx +3 -3
- package/src/desk-structure/orderableDocumentListDeskItem.ts +7 -3
- package/src/fields/orderRankOrdering.ts +1 -1
- package/src/helpers/reorderDocuments.ts +32 -10
- package/src/types.ts +3 -3
- package/v2-incompatible.js +2 -2
- package/src/Feedback.tsx +0 -12
- /package/lib/{src/index.d.ts → index.d.ts} +0 -0
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {useEffect, useMemo} from 'react'
|
|
2
2
|
import {useToast} from '@sanity/ui'
|
|
3
3
|
|
|
4
4
|
import {useSchema} from 'sanity'
|
|
5
|
-
import type
|
|
5
|
+
import {Box, type ToastParams} from '@sanity/ui'
|
|
6
|
+
import {Feedback} from 'sanity-plugin-utils'
|
|
6
7
|
import DocumentListQuery from './DocumentListQuery'
|
|
7
8
|
import {OrderableContext} from './OrderableContext'
|
|
8
9
|
|
|
9
10
|
import {ORDER_FIELD_NAME} from './helpers/constants'
|
|
10
|
-
import Feedback from './Feedback'
|
|
11
11
|
|
|
12
12
|
export interface DocumentListWrapperProps {
|
|
13
13
|
showIncrements: boolean
|
|
14
14
|
type: string
|
|
15
15
|
resetOrderTransaction: ToastParams
|
|
16
|
+
// eslint-disable-next-line react/require-default-props
|
|
16
17
|
filter?: string
|
|
18
|
+
// eslint-disable-next-line react/require-default-props
|
|
17
19
|
params?: Record<string, unknown>
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -88,7 +90,11 @@ export default function DocumentListWrapper({
|
|
|
88
90
|
}, [type, schema])
|
|
89
91
|
|
|
90
92
|
if (schemaIsInvalid) {
|
|
91
|
-
return
|
|
93
|
+
return (
|
|
94
|
+
<Box padding={2}>
|
|
95
|
+
<Feedback description={schemaIsInvalid} tone="caution" />
|
|
96
|
+
</Box>
|
|
97
|
+
)
|
|
92
98
|
}
|
|
93
99
|
|
|
94
100
|
return (
|
package/src/DraggableList.tsx
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {useEffect, useState, useMemo, useCallback, CSSProperties} from 'react'
|
|
2
2
|
import {DragDropContext, Draggable, Droppable, type DropResult} from '@hello-pangea/dnd'
|
|
3
3
|
import {Box, Card, useToast} from '@sanity/ui'
|
|
4
|
-
import {usePaneRouter} from 'sanity/desk'
|
|
5
4
|
import type {PatchOperations} from 'sanity'
|
|
5
|
+
import {usePaneRouter} from 'sanity/desk'
|
|
6
6
|
|
|
7
7
|
import Document from './Document'
|
|
8
8
|
import {reorderDocuments} from './helpers/reorderDocuments'
|
|
9
9
|
import {ORDER_FIELD_NAME} from './helpers/constants'
|
|
10
10
|
import {useSanityClient} from './helpers/client'
|
|
11
|
-
import {
|
|
11
|
+
import {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
|
+
}
|
|
12
25
|
|
|
13
26
|
const getItemStyle = (
|
|
14
27
|
draggableStyle: CSSProperties | undefined,
|
|
@@ -31,29 +44,16 @@ const cardTone = (settings: ListSetting) => {
|
|
|
31
44
|
return undefined
|
|
32
45
|
}
|
|
33
46
|
|
|
34
|
-
interface ListSetting {
|
|
35
|
-
isDuplicate: boolean
|
|
36
|
-
isGhosting: boolean
|
|
37
|
-
isDragging: boolean
|
|
38
|
-
isSelected: boolean
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface DraggableListProps {
|
|
42
|
-
data: SanityDocumentWithOrder[]
|
|
43
|
-
type: string
|
|
44
|
-
listIsUpdating: boolean
|
|
45
|
-
setListIsUpdating: (val: boolean) => void
|
|
46
|
-
}
|
|
47
|
-
|
|
48
47
|
export default function DraggableList({
|
|
49
48
|
data,
|
|
50
|
-
type,
|
|
51
49
|
listIsUpdating,
|
|
52
50
|
setListIsUpdating,
|
|
53
51
|
}: DraggableListProps) {
|
|
54
52
|
const toast = useToast()
|
|
55
53
|
const router = usePaneRouter()
|
|
56
|
-
const {
|
|
54
|
+
const {groupIndex, routerPanesState} = router
|
|
55
|
+
|
|
56
|
+
const currentDoc = routerPanesState[groupIndex + 1]?.[0]?.id || false
|
|
57
57
|
|
|
58
58
|
// Maintains local state order before transaction completes
|
|
59
59
|
const [orderedData, setOrderedData] = useState<SanityDocumentWithOrder[]>(data)
|
|
@@ -65,7 +65,7 @@ export default function DraggableList({
|
|
|
65
65
|
}, [data])
|
|
66
66
|
|
|
67
67
|
const [draggingId, setDraggingId] = useState(``)
|
|
68
|
-
const [selectedIds, setSelectedIds] = useState<string[]>([])
|
|
68
|
+
const [selectedIds, setSelectedIds] = useState<string[]>(currentDoc ? [currentDoc] : [])
|
|
69
69
|
|
|
70
70
|
const clearSelected = useCallback(() => setSelectedIds([]), [setSelectedIds])
|
|
71
71
|
|
|
@@ -82,10 +82,14 @@ export default function DraggableList({
|
|
|
82
82
|
// - update selected to just this one
|
|
83
83
|
// - open document
|
|
84
84
|
if (!selectMultiple && !selectAdditional) {
|
|
85
|
-
navigateIntent('edit', {id: clickedId, type})
|
|
86
85
|
return setSelectedIds([clickedId])
|
|
87
86
|
}
|
|
88
87
|
|
|
88
|
+
// If shift key was held, prevent default to avoid new window opening
|
|
89
|
+
if (selectMultiple) {
|
|
90
|
+
nativeEvent.preventDefault()
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
// Shift key was held, add id's between last selected and this one
|
|
90
94
|
// ...before adding this one
|
|
91
95
|
if (selectMultiple && !isSelected) {
|
|
@@ -110,7 +114,7 @@ export default function DraggableList({
|
|
|
110
114
|
|
|
111
115
|
return setSelectedIds(updatedIds)
|
|
112
116
|
},
|
|
113
|
-
[setSelectedIds,
|
|
117
|
+
[setSelectedIds, orderedData, selectedIds]
|
|
114
118
|
)
|
|
115
119
|
|
|
116
120
|
const client = useSanityClient()
|
|
@@ -267,6 +271,9 @@ export default function DraggableList({
|
|
|
267
271
|
const isDisabled = Boolean(!item[ORDER_FIELD_NAME])
|
|
268
272
|
const isDuplicate = duplicateOrders.includes(item[ORDER_FIELD_NAME])
|
|
269
273
|
const tone = cardTone({isDuplicate, isGhosting, isDragging, isSelected})
|
|
274
|
+
const selectedCount = selectedIds.length
|
|
275
|
+
|
|
276
|
+
const dragBadge = isDragging && selectedCount > 1 ? selectedCount : false
|
|
270
277
|
|
|
271
278
|
return (
|
|
272
279
|
<div
|
|
@@ -280,15 +287,21 @@ export default function DraggableList({
|
|
|
280
287
|
}
|
|
281
288
|
>
|
|
282
289
|
<Box paddingBottom={1}>
|
|
283
|
-
<Card
|
|
290
|
+
<Card
|
|
291
|
+
tone={tone}
|
|
292
|
+
shadow={isDragging ? 2 : undefined}
|
|
293
|
+
radius={2}
|
|
294
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
295
|
+
onClick={(e) => handleSelect(item._id, index, e.nativeEvent)}
|
|
296
|
+
>
|
|
284
297
|
<Document
|
|
285
298
|
doc={item}
|
|
286
299
|
entities={orderedData}
|
|
287
|
-
handleSelect={handleSelect}
|
|
288
300
|
increment={incrementIndex}
|
|
289
301
|
index={index}
|
|
290
302
|
isFirst={index === 0}
|
|
291
303
|
isLast={index === orderedData.length - 1}
|
|
304
|
+
dragBadge={dragBadge}
|
|
292
305
|
/>
|
|
293
306
|
</Card>
|
|
294
307
|
</Box>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {Component} from 'react'
|
|
2
2
|
|
|
3
3
|
import {SanityClient} from '@sanity/client'
|
|
4
4
|
import type {ToastParams} from '@sanity/ui'
|
|
@@ -8,8 +8,8 @@ import {resetOrder} from './helpers/resetOrder'
|
|
|
8
8
|
export interface OrderableDocumentListProps {
|
|
9
9
|
options: {
|
|
10
10
|
type: string
|
|
11
|
-
client: SanityClient
|
|
12
|
-
filter?: string
|
|
11
|
+
client: SanityClient
|
|
12
|
+
filter?: string
|
|
13
13
|
params?: Record<string, unknown>
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -2,7 +2,7 @@ import {GenerateIcon, SortIcon} from '@sanity/icons'
|
|
|
2
2
|
import type {ConfigContext} from 'sanity'
|
|
3
3
|
|
|
4
4
|
import {ComponentType} from 'react'
|
|
5
|
-
import {StructureBuilder} from 'sanity/desk'
|
|
5
|
+
import {StructureBuilder, type ListItem} from 'sanity/desk'
|
|
6
6
|
import OrderableDocumentList from '../OrderableDocumentList'
|
|
7
7
|
|
|
8
8
|
export interface OrderableListConfig {
|
|
@@ -16,7 +16,7 @@ export interface OrderableListConfig {
|
|
|
16
16
|
S: StructureBuilder
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function orderableDocumentListDeskItem(config: OrderableListConfig) {
|
|
19
|
+
export function orderableDocumentListDeskItem(config: OrderableListConfig): ListItem {
|
|
20
20
|
if (!config?.type || !config.context || !config.S) {
|
|
21
21
|
throw new Error(`
|
|
22
22
|
type, context and S (StructureBuilder) must be provided.
|
|
@@ -54,7 +54,11 @@ export function orderableDocumentListDeskItem(config: OrderableListConfig) {
|
|
|
54
54
|
.intent({type: 'create', params: {type}})
|
|
55
55
|
.serialize(),
|
|
56
56
|
S.menuItem().title(`Reset Order`).icon(GenerateIcon).action(`resetOrder`).serialize(),
|
|
57
|
-
S.menuItem()
|
|
57
|
+
S.menuItem()
|
|
58
|
+
.title(`Toggle Increments`)
|
|
59
|
+
.icon(SortIcon)
|
|
60
|
+
.action(`showIncrements`)
|
|
61
|
+
.serialize(),
|
|
58
62
|
],
|
|
59
63
|
})
|
|
60
64
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {LexoRank} from 'lexorank'
|
|
2
2
|
import type {PatchOperations} from 'sanity'
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {SanityDocumentWithOrder} from '../types'
|
|
5
5
|
import {ORDER_FIELD_NAME} from './constants'
|
|
6
6
|
|
|
7
7
|
export interface MaifestArgs {
|
|
@@ -20,6 +20,12 @@ export interface ReorderArgs {
|
|
|
20
20
|
destination: any
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
export interface ReorderReturn {
|
|
24
|
+
newOrder: SanityDocumentWithOrder[]
|
|
25
|
+
patches: [string, PatchOperations][]
|
|
26
|
+
message: any
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
function lexicographicalSort(a: SanityDocumentWithOrder, b: SanityDocumentWithOrder) {
|
|
24
30
|
if (!a[ORDER_FIELD_NAME] || !b[ORDER_FIELD_NAME]) {
|
|
25
31
|
return 0
|
|
@@ -36,14 +42,14 @@ export const reorderDocuments = ({
|
|
|
36
42
|
selectedIds,
|
|
37
43
|
source,
|
|
38
44
|
destination,
|
|
39
|
-
}: ReorderArgs) => {
|
|
45
|
+
}: ReorderArgs): ReorderReturn => {
|
|
40
46
|
const startIndex = source.index
|
|
41
47
|
const endIndex = destination.index
|
|
42
48
|
const isMovingUp = startIndex > endIndex
|
|
43
49
|
const selectedItems = entities.filter((item) => selectedIds.includes(item._id))
|
|
44
50
|
const message = [
|
|
45
51
|
`Moved`,
|
|
46
|
-
selectedItems.length === 1 ? `1
|
|
52
|
+
selectedItems.length === 1 ? `1 document` : `${selectedItems.length} documents`,
|
|
47
53
|
isMovingUp ? `up` : `down`,
|
|
48
54
|
`from position`,
|
|
49
55
|
`${startIndex + 1} to ${endIndex + 1}`,
|
|
@@ -96,15 +102,31 @@ export const reorderDocuments = ({
|
|
|
96
102
|
{all: [], selected: []}
|
|
97
103
|
)
|
|
98
104
|
|
|
99
|
-
const patches
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
const patches = selected.flatMap((doc) => {
|
|
106
|
+
const docPatches: [string, PatchOperations][] = [
|
|
107
|
+
[
|
|
108
|
+
doc._id,
|
|
109
|
+
{
|
|
110
|
+
set: {
|
|
111
|
+
[ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME],
|
|
112
|
+
},
|
|
105
113
|
},
|
|
106
|
-
|
|
114
|
+
],
|
|
107
115
|
]
|
|
116
|
+
|
|
117
|
+
// If it's a draft, we need to patch the published document as well
|
|
118
|
+
if (doc._id.startsWith(`drafts.`) && doc.hasPublished) {
|
|
119
|
+
docPatches.push([
|
|
120
|
+
doc._id.replace(`drafts.`, ``),
|
|
121
|
+
{
|
|
122
|
+
set: {
|
|
123
|
+
[ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
])
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return docPatches
|
|
108
130
|
})
|
|
109
131
|
|
|
110
132
|
// Safety-check to make sure everything is in order
|
package/src/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type {SanityDocument} from 'sanity'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {ORDER_FIELD_NAME} from './helpers/constants'
|
|
4
4
|
|
|
5
5
|
export interface SanityDocumentWithOrder extends SanityDocument {
|
|
6
|
-
|
|
6
|
+
[ORDER_FIELD_NAME]?: string
|
|
7
|
+
hasPublished?: boolean
|
|
7
8
|
}
|
|
8
|
-
|
package/v2-incompatible.js
CHANGED
package/src/Feedback.tsx
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import React, {PropsWithChildren} from 'react'
|
|
2
|
-
import {Box, Card, Text} from '@sanity/ui'
|
|
3
|
-
|
|
4
|
-
export default function Feedback({children}: PropsWithChildren<{}>) {
|
|
5
|
-
return (
|
|
6
|
-
<Box padding={3}>
|
|
7
|
-
<Card padding={4} radius={2} shadow={1} tone="caution">
|
|
8
|
-
<Text>{children}</Text>
|
|
9
|
-
</Card>
|
|
10
|
-
</Box>
|
|
11
|
-
)
|
|
12
|
-
}
|
|
File without changes
|