@sanity/orderable-document-list 1.2.2 → 1.2.3

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.
@@ -1,14 +1,14 @@
1
- import {useEffect, useState, useMemo, useCallback, CSSProperties} from 'react'
1
+ import {useEffect, useState, useMemo, useCallback, type 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
4
  import type {PatchOperations} from 'sanity'
5
5
  import {usePaneRouter} from 'sanity/structure'
6
6
 
7
- import Document from './Document'
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 {SanityDocumentWithOrder} from './types'
11
+ import type {SanityDocumentWithOrder} from './types'
12
12
 
13
13
  interface ListSetting {
14
14
  isDuplicate: boolean
@@ -25,30 +25,26 @@ export interface DraggableListProps {
25
25
 
26
26
  const getItemStyle = (
27
27
  draggableStyle: CSSProperties | undefined,
28
- itemIsUpdating: boolean
28
+ itemIsUpdating: boolean,
29
29
  ): CSSProperties => ({
30
30
  userSelect: 'none',
31
- transition: `opacity 500ms ease-in-out`,
31
+ transition: 'opacity 500ms ease-in-out',
32
32
  opacity: itemIsUpdating ? 0.2 : 1,
33
- pointerEvents: itemIsUpdating ? `none` : undefined,
33
+ pointerEvents: itemIsUpdating ? 'none' : undefined,
34
34
  ...draggableStyle,
35
35
  })
36
36
 
37
37
  const cardTone = (settings: ListSetting) => {
38
38
  const {isDuplicate, isGhosting, isDragging, isSelected} = settings
39
39
 
40
- if (isGhosting) return `transparent`
41
- if (isDragging || isSelected) return `primary`
42
- if (isDuplicate) return `caution`
40
+ if (isGhosting) return 'transparent'
41
+ if (isDragging || isSelected) return 'primary'
42
+ if (isDuplicate) return 'caution'
43
43
 
44
44
  return undefined
45
45
  }
46
46
 
47
- export default function DraggableList({
48
- data,
49
- listIsUpdating,
50
- setListIsUpdating,
51
- }: DraggableListProps) {
47
+ export function DraggableList({data, listIsUpdating, setListIsUpdating}: DraggableListProps) {
52
48
  const toast = useToast()
53
49
  const router = usePaneRouter()
54
50
  const {groupIndex, routerPanesState} = router
@@ -64,7 +60,7 @@ export default function DraggableList({
64
60
  /* eslint-disable-next-line react-hooks/exhaustive-deps */
65
61
  }, [data])
66
62
 
67
- const [draggingId, setDraggingId] = useState(``)
63
+ const [draggingId, setDraggingId] = useState('')
68
64
  const [selectedIds, setSelectedIds] = useState<string[]>(currentDoc ? [currentDoc] : [])
69
65
 
70
66
  const clearSelected = useCallback(() => setSelectedIds([]), [setSelectedIds])
@@ -114,7 +110,7 @@ export default function DraggableList({
114
110
 
115
111
  return setSelectedIds(updatedIds)
116
112
  },
117
- [setSelectedIds, orderedData, selectedIds]
113
+ [setSelectedIds, orderedData, selectedIds],
118
114
  )
119
115
 
120
116
  const client = useSanityClient()
@@ -125,35 +121,36 @@ export default function DraggableList({
125
121
 
126
122
  patches.forEach(([docId, ops]) => transaction.patch(docId, ops))
127
123
 
128
- await transaction
129
- .commit()
130
- .then((updated) => {
131
- clearSelected()
132
- setDraggingId(``)
133
- setListIsUpdating(false)
134
- toast.push({
135
- title: `${
136
- updated.results.length === 1 ? `1 Document` : `${updated.results.length} Documents`
137
- } Reordered`,
138
- status: `success`,
139
- description: message,
140
- })
124
+ try {
125
+ const updated = await transaction.commit({
126
+ visibility: 'async',
127
+ tag: 'orderable-document-list.reorder',
141
128
  })
142
- .catch(() => {
143
- setDraggingId(``)
144
- setListIsUpdating(false)
145
- toast.push({
146
- title: `Reordering failed`,
147
- status: `error`,
148
- })
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',
149
145
  })
146
+ }
150
147
  },
151
- [client, setDraggingId, clearSelected, setListIsUpdating, toast]
148
+ [client, setDraggingId, clearSelected, setListIsUpdating, toast],
152
149
  )
153
150
 
154
151
  const handleDragEnd = useCallback(
155
152
  (result: DropResult | undefined, entities: SanityDocumentWithOrder[]) => {
156
- setDraggingId(``)
153
+ setDraggingId('')
157
154
 
158
155
  const {source, destination, draggableId} = result ?? {}
159
156
 
@@ -190,7 +187,7 @@ export default function DraggableList({
190
187
  transactPatches(patches, message)
191
188
  }
192
189
  },
193
- [selectedIds, setDraggingId, setSelectedIds, transactPatches, setListIsUpdating]
190
+ [selectedIds, setDraggingId, setSelectedIds, transactPatches, setListIsUpdating],
194
191
  )
195
192
 
196
193
  const handleDragStart = useCallback(
@@ -203,7 +200,7 @@ export default function DraggableList({
203
200
 
204
201
  setDraggingId(id)
205
202
  },
206
- [selectedIds, clearSelected, setDraggingId]
203
+ [selectedIds, clearSelected, setDraggingId],
207
204
  )
208
205
 
209
206
  // Move one document up or down one place, by fake invoking the drag function
@@ -217,7 +214,7 @@ export default function DraggableList({
217
214
 
218
215
  return handleDragEnd(result as DropResult, entities)
219
216
  },
220
- [handleDragEnd]
217
+ [handleDragEnd],
221
218
  )
222
219
 
223
220
  const onWindowKeyDown = useCallback(
@@ -226,7 +223,7 @@ export default function DraggableList({
226
223
  clearSelected()
227
224
  }
228
225
  },
229
- [clearSelected]
226
+ [clearSelected],
230
227
  )
231
228
 
232
229
  useEffect(() => {
@@ -248,7 +245,7 @@ export default function DraggableList({
248
245
 
249
246
  const onDragEnd = useCallback(
250
247
  (result: DropResult) => handleDragEnd(result, orderedData),
251
- [orderedData, handleDragEnd]
248
+ [orderedData, handleDragEnd],
252
249
  )
253
250
 
254
251
  return (
@@ -282,7 +279,7 @@ export default function DraggableList({
282
279
  {...innerProvided.dragHandleProps}
283
280
  style={
284
281
  isDisabled
285
- ? {opacity: 0.2, pointerEvents: `none`}
282
+ ? {opacity: 0.2, pointerEvents: 'none'}
286
283
  : getItemStyle(innerProvided.draggableProps.style, isUpdating)
287
284
  }
288
285
  >
@@ -1,7 +1,7 @@
1
- import React from 'react'
1
+ import {createContext} from 'react'
2
2
 
3
3
  export interface OrderableContextValue {
4
4
  showIncrements?: boolean
5
5
  }
6
6
 
7
- export const OrderableContext = React.createContext<OrderableContextValue>({})
7
+ export const OrderableContext = createContext<OrderableContextValue>({})
@@ -1,8 +1,8 @@
1
1
  import {Component} from 'react'
2
2
 
3
- import {SanityClient} from '@sanity/client'
3
+ import type {SanityClient} from '@sanity/client'
4
4
  import type {ToastParams} from '@sanity/ui'
5
- import DocumentListWrapper from './DocumentListWrapper'
5
+ import {DocumentListWrapper} from './DocumentListWrapper'
6
6
  import {resetOrder} from './helpers/resetOrder'
7
7
 
8
8
  export interface OrderableDocumentListProps {
@@ -20,7 +20,7 @@ interface State {
20
20
  }
21
21
 
22
22
  // Must use a Class Component here so the actionHandlers can be called
23
- export default class OrderableDocumentList extends Component<OrderableDocumentListProps, State> {
23
+ export class OrderableDocumentList extends Component<OrderableDocumentListProps, State> {
24
24
  constructor(props: OrderableDocumentListProps) {
25
25
  super(props)
26
26
  this.state = {
@@ -1,9 +1,9 @@
1
1
  import {GenerateIcon, SortIcon} from '@sanity/icons'
2
2
  import type {ConfigContext} from 'sanity'
3
3
 
4
- import {ComponentType} from 'react'
4
+ import type {ComponentType} from 'react'
5
5
  import {StructureBuilder, type ListItem, type MenuItem} from 'sanity/structure'
6
- import OrderableDocumentList from '../OrderableDocumentList'
6
+ import {OrderableDocumentList} from '../OrderableDocumentList'
7
7
  import {API_VERSION} from '../helpers/constants'
8
8
 
9
9
  export interface OrderableListConfig {
@@ -42,7 +42,7 @@ export function orderableDocumentListDeskItem(config: OrderableListConfig): List
42
42
  S.menuItem()
43
43
  .title(`Create new ${typeTitle}`)
44
44
  .intent({type: 'create', params: {type}})
45
- .serialize()
45
+ .serialize(),
46
46
  )
47
47
  }
48
48
  return S.listItem()
@@ -73,8 +73,8 @@ export function orderableDocumentListDeskItem(config: OrderableListConfig): List
73
73
  .action(`showIncrements`)
74
74
  .serialize(),
75
75
  ],
76
- }
77
- )
76
+ },
77
+ ),
78
78
  )
79
79
  .serialize()
80
80
  }
@@ -1,7 +1,7 @@
1
1
  import {type ConfigContext, defineField} from 'sanity'
2
2
  import {API_VERSION, ORDER_FIELD_NAME} from '../helpers/constants'
3
- import initialRank from '../helpers/initialRank'
4
- import {NewItemPosition} from '../types'
3
+ import {initialRank} from '../helpers/initialRank'
4
+ import type {NewItemPosition} from '../types'
5
5
 
6
6
  export type SchemaContext = Omit<ConfigContext, 'schema' | 'currentUser' | 'client'>
7
7
 
@@ -16,7 +16,7 @@ export const orderRankField = (config: RankFieldConfig) => {
16
16
  `
17
17
  type must be provided.
18
18
  Example: orderRankField({type: 'category'})
19
- `
19
+ `,
20
20
  )
21
21
  }
22
22
 
@@ -33,7 +33,8 @@ export const orderRankField = (config: RankFieldConfig) => {
33
33
 
34
34
  const lastDocOrderRank = await getClient({apiVersion: API_VERSION}).fetch(
35
35
  `*[_type == $type]|order(@[$order] ${direction})[0][$order]`,
36
- {type, order: ORDER_FIELD_NAME}
36
+ {type, order: ORDER_FIELD_NAME},
37
+ {tag: 'orderable-document-list.last-doc-order-rank'},
37
38
  )
38
39
  return initialRank(lastDocOrderRank, newItemPosition)
39
40
  },
@@ -1,4 +1,4 @@
1
- import {SortOrdering} from 'sanity'
1
+ import type {SortOrdering} from 'sanity'
2
2
  import {ORDER_FIELD_NAME} from '../helpers/constants'
3
3
 
4
4
  export const orderRankOrdering: SortOrdering = {
@@ -1,6 +1,6 @@
1
- import {useClient} from 'sanity'
1
+ import {type SanityClient, useClient} from 'sanity'
2
2
  import {API_VERSION} from './constants'
3
3
 
4
- export function useSanityClient() {
4
+ export function useSanityClient(): SanityClient {
5
5
  return useClient({apiVersion: API_VERSION})
6
6
  }
@@ -3,9 +3,9 @@ import {NewItemPosition} from '../types'
3
3
 
4
4
  // Use in initial value field by passing in the rank value of the last document
5
5
  // If not value passed, generate a sensibly low rank
6
- export default function initialRank(
6
+ export function initialRank(
7
7
  compareRankValue = ``,
8
- newItemPosition: NewItemPosition = 'after'
8
+ newItemPosition: NewItemPosition = 'after',
9
9
  ): string {
10
10
  const compareRank = compareRankValue ? LexoRank.parse(compareRankValue) : LexoRank.min()
11
11
  const rank =
@@ -1,7 +1,7 @@
1
1
  import {LexoRank} from 'lexorank'
2
2
  import type {PatchOperations} from 'sanity'
3
3
 
4
- import {SanityDocumentWithOrder} from '../types'
4
+ import type {SanityDocumentWithOrder} from '../types'
5
5
  import {ORDER_FIELD_NAME} from './constants'
6
6
 
7
7
  export interface MaifestArgs {
@@ -48,10 +48,10 @@ export const reorderDocuments = ({
48
48
  const isMovingUp = startIndex > endIndex
49
49
  const selectedItems = entities.filter((item) => selectedIds.includes(item._id))
50
50
  const message = [
51
- `Moved`,
52
- selectedItems.length === 1 ? `1 document` : `${selectedItems.length} documents`,
53
- isMovingUp ? `up` : `down`,
54
- `from position`,
51
+ 'Moved',
52
+ selectedItems.length === 1 ? '1 document' : `${selectedItems.length} documents`,
53
+ isMovingUp ? 'up' : 'down',
54
+ 'from position',
55
55
  `${startIndex + 1} to ${endIndex + 1}`,
56
56
  ].join(' ')
57
57
 
@@ -99,7 +99,7 @@ export const reorderDocuments = ({
99
99
 
100
100
  return {all: [...acc.all, cur], selected: acc.selected}
101
101
  },
102
- {all: [], selected: []}
102
+ {all: [], selected: []},
103
103
  )
104
104
 
105
105
  const patches = selected.flatMap((doc) => {
@@ -115,9 +115,9 @@ export const reorderDocuments = ({
115
115
  ]
116
116
 
117
117
  // If it's a draft, we need to patch the published document as well
118
- if (doc._id.startsWith(`drafts.`) && doc.hasPublished) {
118
+ if (doc._id.startsWith('drafts.') && doc.hasPublished) {
119
119
  docPatches.push([
120
- doc._id.replace(`drafts.`, ``),
120
+ doc._id.replace('drafts.', ''),
121
121
  {
122
122
  set: {
123
123
  [ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME],
@@ -1,31 +1,36 @@
1
1
  import {LexoRank} from 'lexorank'
2
- import {SanityClient} from '@sanity/client'
2
+ import type {MultipleMutationResult, SanityClient} from '@sanity/client'
3
3
  import {ORDER_FIELD_NAME} from './constants'
4
+
4
5
  // Function to wipe and re-do ordering with LexoRank
5
6
  // Will at least attempt to start with the current order
6
- export async function resetOrder(type = ``, client: SanityClient) {
7
+ export async function resetOrder(
8
+ type: string,
9
+ client: SanityClient,
10
+ ): Promise<MultipleMutationResult | null> {
7
11
  const query = `*[_type == $type]|order(@[$order] asc)._id`
8
12
  const queryParams = {type, order: ORDER_FIELD_NAME}
9
- const documents = await client.fetch(query, queryParams)
13
+ const documentIds = await client.fetch<Array<string>>(query, queryParams, {
14
+ tag: 'orderable-document-list.reset-order',
15
+ })
10
16
 
11
- if (!documents.length) {
17
+ if (documentIds.length === 0) {
12
18
  return null
13
19
  }
14
20
 
15
- const transaction = client.transaction()
16
21
  let aLexoRank = LexoRank.min()
17
22
 
18
- for (let index = 0; index < documents.length; index += 1) {
23
+ const transaction = documentIds.reduce((trx, documentId) => {
19
24
  // Generate next rank before even the first document so there's room to move!
20
25
  aLexoRank = aLexoRank.genNext().genNext()
21
26
 
22
- transaction.patch(documents[index], {
27
+ return trx.patch(documentId, {
23
28
  set: {[ORDER_FIELD_NAME]: aLexoRank.toString()},
24
29
  })
25
- }
30
+ }, client.transaction())
26
31
 
27
- return transaction
28
- .commit()
29
- .then((update) => update)
30
- .catch((err) => err)
32
+ return transaction.commit({
33
+ visibility: 'async',
34
+ tag: 'orderable-document-list.reset-order',
35
+ })
31
36
  }