@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.
@@ -1,19 +1,21 @@
1
- import React, {useEffect, useMemo} from 'react'
1
+ import {useEffect, useMemo} from 'react'
2
2
  import {useToast} from '@sanity/ui'
3
3
 
4
4
  import {useSchema} from 'sanity'
5
- import type {ToastParams} from '@sanity/ui'
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 <Feedback>{schemaIsInvalid}</Feedback>
93
+ return (
94
+ <Box padding={2}>
95
+ <Feedback description={schemaIsInvalid} tone="caution" />
96
+ </Box>
97
+ )
92
98
  }
93
99
 
94
100
  return (
@@ -1,14 +1,27 @@
1
- import React, {useEffect, useState, useMemo, useCallback, CSSProperties} from 'react'
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 { SanityDocumentWithOrder } from './types'
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 {navigateIntent} = router
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, navigateIntent, orderedData, selectedIds, type]
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 tone={tone} shadow={isDragging ? 2 : undefined} radius={2}>
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 React, {Component} from 'react'
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().title(`Toggle Increments`).icon(SortIcon).action(`showIncrements`).serialize(),
57
+ S.menuItem()
58
+ .title(`Toggle Increments`)
59
+ .icon(SortIcon)
60
+ .action(`showIncrements`)
61
+ .serialize(),
58
62
  ],
59
63
  })
60
64
  )
@@ -1,4 +1,4 @@
1
- import { SortOrdering } from 'sanity'
1
+ import {SortOrdering} from 'sanity'
2
2
  import {ORDER_FIELD_NAME} from '../helpers/constants'
3
3
 
4
4
  export const orderRankOrdering: SortOrdering = {
@@ -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 {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 Document` : `${selectedItems.length} Documents`,
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: [string, PatchOperations][] = selected.map((doc) => {
100
- return [
101
- doc._id,
102
- {
103
- set: {
104
- [ORDER_FIELD_NAME]: doc[ORDER_FIELD_NAME],
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 { ORDER_FIELD_NAME } from './helpers/constants'
3
+ import {ORDER_FIELD_NAME} from './helpers/constants'
4
4
 
5
5
  export interface SanityDocumentWithOrder extends SanityDocument {
6
- [ORDER_FIELD_NAME]?: string
6
+ [ORDER_FIELD_NAME]?: string
7
+ hasPublished?: boolean
7
8
  }
8
-
@@ -5,7 +5,7 @@ export default showIncompatiblePluginDialog({
5
5
  name: name,
6
6
  versions: {
7
7
  v3: version,
8
- v2: '^0.0.10',
8
+ v2: undefined,
9
9
  },
10
- sanityExchangeUrl
10
+ sanityExchangeUrl,
11
11
  })
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