@sanity/cross-dataset-duplicator 1.5.0 → 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 +42 -61
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +720 -492
- package/dist/index.js.map +1 -1
- package/package.json +38 -77
- package/dist/index.d.mts +0 -81
- package/dist/index.mjs +0 -654
- package/dist/index.mjs.map +0 -1
- package/sanity.json +0 -8
- package/src/actions/DuplicateToAction.tsx +0 -34
- package/src/components/CrossDatasetDuplicator.tsx +0 -93
- package/src/components/CrossDatasetDuplicatorAction.tsx +0 -13
- package/src/components/CrossDatasetDuplicatorTool.tsx +0 -17
- package/src/components/Duplicator.tsx +0 -568
- package/src/components/DuplicatorQuery.tsx +0 -144
- package/src/components/DuplicatorWrapper.tsx +0 -67
- package/src/components/Feedback.tsx +0 -18
- package/src/components/ResetSecret.tsx +0 -30
- package/src/components/SelectButtons.tsx +0 -84
- package/src/components/StatusBadge.tsx +0 -111
- package/src/context/ConfigProvider.tsx +0 -30
- package/src/helpers/constants.ts +0 -12
- package/src/helpers/getDocumentsInArray.ts +0 -86
- package/src/helpers/index.ts +0 -19
- package/src/index.ts +0 -5
- package/src/plugin.tsx +0 -31
- package/src/tool/index.ts +0 -14
- package/src/types/index.ts +0 -27
- package/v2-incompatible.js +0 -11
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import {useState, useEffect} from 'react'
|
|
2
|
-
import {Grid, Card, Container, Button} from '@sanity/ui'
|
|
3
|
-
import {SanityDocument, useClient} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import type {DuplicatorProps} from './Duplicator'
|
|
6
|
-
import Duplicator from './Duplicator'
|
|
7
|
-
|
|
8
|
-
export default function DuplicatorWrapper(props: DuplicatorProps) {
|
|
9
|
-
const {docs, token, pluginConfig, onDuplicated} = props
|
|
10
|
-
const [inbound, setInbound] = useState<SanityDocument[]>([])
|
|
11
|
-
const {follow = [], apiVersion} = pluginConfig
|
|
12
|
-
|
|
13
|
-
// Make the first mode the default if there's only one
|
|
14
|
-
const [mode, setMode] = useState<'inbound' | 'outbound'>(
|
|
15
|
-
follow.length === 1 ? follow[0] : `outbound`
|
|
16
|
-
)
|
|
17
|
-
const client = useClient({apiVersion})
|
|
18
|
-
|
|
19
|
-
// "Inbound" will start with all documents that reference the first one
|
|
20
|
-
// And then you can gather "Outbound" references thereafter
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
;(async () => {
|
|
23
|
-
if (follow.includes(`inbound`)) {
|
|
24
|
-
const inboundReferences = await client.fetch(`*[references($id)]`, {id: docs[0]._id})
|
|
25
|
-
setInbound([...props.docs, ...inboundReferences])
|
|
26
|
-
}
|
|
27
|
-
})()
|
|
28
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
29
|
-
}, [])
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Container>
|
|
33
|
-
{follow.length > 1 && (follow.includes(`inbound`) || follow.includes(`outbound`)) ? (
|
|
34
|
-
<Card paddingX={4} paddingBottom={4} marginBottom={4} borderBottom>
|
|
35
|
-
<Grid columns={2} gap={4}>
|
|
36
|
-
{follow.includes(`outbound`) ? (
|
|
37
|
-
<Button
|
|
38
|
-
mode="ghost"
|
|
39
|
-
tone="primary"
|
|
40
|
-
selected={mode === 'outbound'}
|
|
41
|
-
onClick={() => setMode('outbound')}
|
|
42
|
-
text="Outbound"
|
|
43
|
-
/>
|
|
44
|
-
) : null}
|
|
45
|
-
{follow.includes(`inbound`) ? (
|
|
46
|
-
<Button
|
|
47
|
-
mode="ghost"
|
|
48
|
-
tone="primary"
|
|
49
|
-
selected={mode === 'inbound'}
|
|
50
|
-
onClick={() => setMode('inbound')}
|
|
51
|
-
disabled={inbound.length === 0}
|
|
52
|
-
text={inbound.length > 0 ? `Inbound (${inbound.length})` : 'No inbound references'}
|
|
53
|
-
/>
|
|
54
|
-
) : null}
|
|
55
|
-
</Grid>
|
|
56
|
-
</Card>
|
|
57
|
-
) : null}
|
|
58
|
-
<Duplicator
|
|
59
|
-
docs={mode === 'outbound' ? docs : inbound}
|
|
60
|
-
token={token}
|
|
61
|
-
// draftIds={[]}
|
|
62
|
-
pluginConfig={pluginConfig}
|
|
63
|
-
onDuplicated={onDuplicated}
|
|
64
|
-
/>
|
|
65
|
-
</Container>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import {Card, Text} from '@sanity/ui'
|
|
3
|
-
import type {BadgeTone} from '@sanity/ui'
|
|
4
|
-
|
|
5
|
-
type FeedbackProps = {
|
|
6
|
-
children?: React.ReactNode
|
|
7
|
-
tone?: BadgeTone
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export default function Feedback(props: FeedbackProps) {
|
|
11
|
-
const {children, tone = `caution`} = props
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<Card padding={3} radius={2} shadow={1} tone={tone}>
|
|
15
|
-
<Text size={1}>{children}</Text>
|
|
16
|
-
</Card>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {useCallback} from 'react'
|
|
2
|
-
import {useClient} from 'sanity'
|
|
3
|
-
import {Button, Flex} from '@sanity/ui'
|
|
4
|
-
|
|
5
|
-
import {SECRET_NAMESPACE} from '../helpers/constants'
|
|
6
|
-
|
|
7
|
-
type ResetSecretProps = {
|
|
8
|
-
apiVersion: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export default function ResetSecret({apiVersion}: ResetSecretProps) {
|
|
12
|
-
const client = useClient({apiVersion})
|
|
13
|
-
|
|
14
|
-
const handleClick = useCallback(() => {
|
|
15
|
-
client.delete({query: `*[_id == "secrets.${SECRET_NAMESPACE}"]`})
|
|
16
|
-
}, [client])
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Flex align="center" justify="flex-end" paddingX={[2, 2, 2, 5]} paddingY={5}>
|
|
20
|
-
<Button
|
|
21
|
-
text="Reset Secret"
|
|
22
|
-
onClick={handleClick}
|
|
23
|
-
mode="ghost"
|
|
24
|
-
tone="critical"
|
|
25
|
-
fontSize={1}
|
|
26
|
-
padding={2}
|
|
27
|
-
/>
|
|
28
|
-
</Flex>
|
|
29
|
-
)
|
|
30
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import {useState, useEffect} from 'react'
|
|
2
|
-
import {Button, Card, Flex} from '@sanity/ui'
|
|
3
|
-
|
|
4
|
-
import {PayloadItem} from './Duplicator'
|
|
5
|
-
import {isAssetId} from '@sanity/asset-utils'
|
|
6
|
-
|
|
7
|
-
const buttons = [`All`, `None`, null, `New`, `Existing`, `Older`, null, `Documents`, `Assets`]
|
|
8
|
-
|
|
9
|
-
type Action = 'ALL' | 'NONE' | 'NEW' | 'EXISTING' | 'OLDER' | 'ASSETS' | 'DOCUMENTS'
|
|
10
|
-
|
|
11
|
-
type SelectButtonsProps = {
|
|
12
|
-
payload: PayloadItem[]
|
|
13
|
-
setPayload: Function
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export default function SelectButtons(props: SelectButtonsProps) {
|
|
17
|
-
const {payload, setPayload} = props
|
|
18
|
-
const [disabledActions, setDisabledActions] = useState<Action[]>([])
|
|
19
|
-
|
|
20
|
-
// Set intiial disabled button
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (!disabledActions?.length && payload.every((item) => item.include)) {
|
|
23
|
-
setDisabledActions([`ALL`])
|
|
24
|
-
}
|
|
25
|
-
}, [disabledActions?.length, payload])
|
|
26
|
-
|
|
27
|
-
function handleSelectButton(action?: Action) {
|
|
28
|
-
if (!action || !payload.length) return
|
|
29
|
-
|
|
30
|
-
const newPayload = [...payload]
|
|
31
|
-
|
|
32
|
-
switch (action) {
|
|
33
|
-
case 'ALL':
|
|
34
|
-
newPayload.map((item) => (item.include = true))
|
|
35
|
-
break
|
|
36
|
-
case 'NONE':
|
|
37
|
-
newPayload.map((item) => (item.include = false))
|
|
38
|
-
break
|
|
39
|
-
case 'NEW':
|
|
40
|
-
newPayload.map((item) => (item.include = Boolean(item.status === 'CREATE')))
|
|
41
|
-
break
|
|
42
|
-
case 'EXISTING':
|
|
43
|
-
newPayload.map((item) => (item.include = Boolean(item.status === 'EXISTS')))
|
|
44
|
-
break
|
|
45
|
-
case 'OLDER':
|
|
46
|
-
newPayload.map((item) => (item.include = Boolean(item.status === 'OVERWRITE')))
|
|
47
|
-
break
|
|
48
|
-
case 'ASSETS':
|
|
49
|
-
newPayload.map((item) => (item.include = isAssetId(item.doc._id)))
|
|
50
|
-
break
|
|
51
|
-
case 'DOCUMENTS':
|
|
52
|
-
newPayload.map((item) => (item.include = !isAssetId(item.doc._id)))
|
|
53
|
-
break
|
|
54
|
-
default:
|
|
55
|
-
break
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
setDisabledActions([action])
|
|
59
|
-
setPayload(newPayload)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<Card padding={1} radius={3} shadow={1}>
|
|
64
|
-
<Flex gap={2} wrap="wrap">
|
|
65
|
-
{buttons.map((action, actionIndex) =>
|
|
66
|
-
action ? (
|
|
67
|
-
<Button
|
|
68
|
-
key={action}
|
|
69
|
-
fontSize={1}
|
|
70
|
-
mode="bleed"
|
|
71
|
-
padding={2}
|
|
72
|
-
text={action}
|
|
73
|
-
disabled={disabledActions.includes(action.toUpperCase() as Action)}
|
|
74
|
-
onClick={() => handleSelectButton(action.toUpperCase() as Action)}
|
|
75
|
-
/>
|
|
76
|
-
) : (
|
|
77
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
78
|
-
<Card key={`divider-${actionIndex}`} borderLeft />
|
|
79
|
-
)
|
|
80
|
-
)}
|
|
81
|
-
</Flex>
|
|
82
|
-
</Card>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import {InfoOutlineIcon} from '@sanity/icons'
|
|
2
|
-
import {Box, Text, Badge, Tooltip} from '@sanity/ui'
|
|
3
|
-
import type {BadgeTone} from '@sanity/ui'
|
|
4
|
-
|
|
5
|
-
type StatusTones = {
|
|
6
|
-
EXISTS: BadgeTone
|
|
7
|
-
OVERWRITE: BadgeTone
|
|
8
|
-
UPDATE: BadgeTone
|
|
9
|
-
CREATE: BadgeTone
|
|
10
|
-
UNPUBLISHED: BadgeTone
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const documentTones: StatusTones = {
|
|
14
|
-
EXISTS: `primary`,
|
|
15
|
-
OVERWRITE: `critical`,
|
|
16
|
-
UPDATE: `caution`,
|
|
17
|
-
CREATE: `positive`,
|
|
18
|
-
UNPUBLISHED: `caution`,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const assetTones: StatusTones = {
|
|
22
|
-
EXISTS: `critical`,
|
|
23
|
-
OVERWRITE: `critical`,
|
|
24
|
-
UPDATE: `critical`,
|
|
25
|
-
CREATE: `positive`,
|
|
26
|
-
UNPUBLISHED: `default`,
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export type MessageTypes = {
|
|
30
|
-
EXISTS: string
|
|
31
|
-
OVERWRITE: string
|
|
32
|
-
UPDATE: string
|
|
33
|
-
CREATE: string
|
|
34
|
-
UNPUBLISHED: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const documentMessages: MessageTypes = {
|
|
38
|
-
// Only happens once document is copied the first time, and _updatedAt is the same
|
|
39
|
-
EXISTS: `This document already exists at the Destination with the same ID with the same Updated time.`,
|
|
40
|
-
// Is true immediately after transaction as _updatedAt is updated by API after mutation
|
|
41
|
-
// Is also true if the document at the destination has been manually modified
|
|
42
|
-
// Presently, the plugin doesn't actually compare the two documents
|
|
43
|
-
OVERWRITE: `A newer version of this document exists at the Destination, and it will be overwritten with this version.`,
|
|
44
|
-
// Document at destination is older
|
|
45
|
-
UPDATE: `An older version of this document exists at the Destination, and it will be overwritten with this version.`,
|
|
46
|
-
// Document at destination doesn't exist
|
|
47
|
-
CREATE: `This document will be created at the destination.`,
|
|
48
|
-
UNPUBLISHED: `A Draft version of this Document exists in this Dataset, but only the Published version will be duplicated to the destination.`,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const assetMessages: MessageTypes = {
|
|
52
|
-
EXISTS: `This Asset already exists at the Destination`,
|
|
53
|
-
OVERWRITE: `This Asset already exists at the Destination`,
|
|
54
|
-
UPDATE: `This Asset already exists at the Destination`,
|
|
55
|
-
CREATE: `This Asset does not yet exist at the Destination`,
|
|
56
|
-
UNPUBLISHED: ``,
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const assetStatus: MessageTypes = {
|
|
60
|
-
EXISTS: `RE-UPLOAD`,
|
|
61
|
-
OVERWRITE: `RE-UPLOAD`,
|
|
62
|
-
UPDATE: `RE-UPLOAD`,
|
|
63
|
-
CREATE: `UPLOAD`,
|
|
64
|
-
UNPUBLISHED: ``,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
type StatusBadgeProps = {
|
|
68
|
-
isAsset: boolean
|
|
69
|
-
status?: keyof MessageTypes
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export default function StatusBadge(props: StatusBadgeProps) {
|
|
73
|
-
const {status, isAsset} = props
|
|
74
|
-
|
|
75
|
-
if (!status) {
|
|
76
|
-
return null
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const badgeTone = isAsset ? assetTones[status] : documentTones[status]
|
|
80
|
-
|
|
81
|
-
if (!badgeTone) {
|
|
82
|
-
return (
|
|
83
|
-
<Badge muted padding={2} fontSize={1} mode="outline">
|
|
84
|
-
Checking...
|
|
85
|
-
</Badge>
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const badgeText = isAsset ? assetMessages[status] : documentMessages[status]
|
|
90
|
-
const badgeStatus = isAsset ? assetStatus[status] : status
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Tooltip
|
|
94
|
-
content={
|
|
95
|
-
<Box padding={3} style={{maxWidth: 200}}>
|
|
96
|
-
<Text size={1}>{badgeText}</Text>
|
|
97
|
-
</Box>
|
|
98
|
-
}
|
|
99
|
-
fallbackPlacements={['right', 'left']}
|
|
100
|
-
placement="top"
|
|
101
|
-
portal
|
|
102
|
-
>
|
|
103
|
-
<Badge muted padding={3} fontSize={1} tone={badgeTone} mode="outline">
|
|
104
|
-
{badgeStatus}
|
|
105
|
-
<Box marginLeft={2} display={'inline-block'} as="span">
|
|
106
|
-
<InfoOutlineIcon />
|
|
107
|
-
</Box>
|
|
108
|
-
</Badge>
|
|
109
|
-
</Tooltip>
|
|
110
|
-
)
|
|
111
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {useContext} from 'react'
|
|
2
|
-
import {createContext} from 'react'
|
|
3
|
-
import {LayoutProps} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {DEFAULT_CONFIG} from '../helpers/constants'
|
|
6
|
-
import {PluginConfig} from '../types'
|
|
7
|
-
|
|
8
|
-
const CrossDatasetDuplicatorContext = createContext(DEFAULT_CONFIG)
|
|
9
|
-
|
|
10
|
-
type ConfigProviderProps = LayoutProps & {pluginConfig: Required<PluginConfig>}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Plugin config context hook from the Cross Dataset Duplicator plugin
|
|
14
|
-
* @public
|
|
15
|
-
*/
|
|
16
|
-
export function useCrossDatasetDuplicatorConfig() {
|
|
17
|
-
const pluginConfig = useContext(CrossDatasetDuplicatorContext)
|
|
18
|
-
|
|
19
|
-
return pluginConfig
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function ConfigProvider(props: ConfigProviderProps) {
|
|
23
|
-
const {pluginConfig, ...rest} = props
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<CrossDatasetDuplicatorContext.Provider value={pluginConfig}>
|
|
27
|
-
{props.renderDefault(rest)}
|
|
28
|
-
</CrossDatasetDuplicatorContext.Provider>
|
|
29
|
-
)
|
|
30
|
-
}
|
package/src/helpers/constants.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {PluginConfig} from '../types'
|
|
2
|
-
|
|
3
|
-
export const SECRET_NAMESPACE = `CrossDatasetDuplicator`
|
|
4
|
-
|
|
5
|
-
export const DEFAULT_CONFIG: Required<PluginConfig> = {
|
|
6
|
-
apiVersion: '2025-02-19',
|
|
7
|
-
tool: true,
|
|
8
|
-
types: [],
|
|
9
|
-
filter: '',
|
|
10
|
-
follow: ['outbound'],
|
|
11
|
-
queries: [],
|
|
12
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import {extractWithPath} from '@sanity/mutator'
|
|
2
|
-
import {SanityClient, SanityDocument} from 'sanity'
|
|
3
|
-
import {PluginConfig} from '../types'
|
|
4
|
-
|
|
5
|
-
type OptionsBag = {
|
|
6
|
-
fetchIds: string[]
|
|
7
|
-
client: SanityClient
|
|
8
|
-
pluginConfig: PluginConfig
|
|
9
|
-
currentIds?: Set<string> | null
|
|
10
|
-
projection?: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Recursively fetch Documents from an array of _id's and their references
|
|
14
|
-
// Heavy use of Set is to avoid recursively querying for id's already in the payload
|
|
15
|
-
export async function getDocumentsInArray(options: OptionsBag): Promise<SanityDocument[]> {
|
|
16
|
-
const {fetchIds, client, pluginConfig, currentIds, projection} = options
|
|
17
|
-
const collection: SanityDocument[] = []
|
|
18
|
-
|
|
19
|
-
// Find initial docs
|
|
20
|
-
const filter = ['_id in $fetchIds', pluginConfig.filter].filter(Boolean).join(' && ')
|
|
21
|
-
const query = `*[${filter}]${projection ?? ``}`
|
|
22
|
-
const data: SanityDocument[] = await client.fetch(query, {
|
|
23
|
-
fetchIds: fetchIds ?? [],
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
if (!data?.length) {
|
|
27
|
-
return []
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const localCurrentIds = currentIds ?? new Set<string>()
|
|
31
|
-
|
|
32
|
-
// Find new ids in the returned data
|
|
33
|
-
// Unless we started with an empty set, get the _ids from the data
|
|
34
|
-
const newDataIds = new Set<string>(
|
|
35
|
-
data
|
|
36
|
-
.map((dataDoc) => dataDoc._id)
|
|
37
|
-
.filter((id) => (currentIds?.size ? !localCurrentIds.has(id) : Boolean(id)))
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
if (newDataIds.size) {
|
|
41
|
-
collection.push(...data)
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
localCurrentIds.add(...newDataIds)
|
|
44
|
-
|
|
45
|
-
// Check new data for more references
|
|
46
|
-
await Promise.all(
|
|
47
|
-
data.map(async (doc) => {
|
|
48
|
-
const expr = `.._ref`
|
|
49
|
-
const references: string[] = extractWithPath(expr, doc).map((ref) => ref.value as string)
|
|
50
|
-
|
|
51
|
-
if (references.length) {
|
|
52
|
-
// Find references not already in the Collection
|
|
53
|
-
const newReferenceIds = new Set<string>(
|
|
54
|
-
references.filter((ref) => !localCurrentIds.has(ref))
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
if (newReferenceIds.size) {
|
|
58
|
-
// Recursive query for new documents
|
|
59
|
-
const referenceDocs = await getDocumentsInArray({
|
|
60
|
-
fetchIds: Array.from(newReferenceIds),
|
|
61
|
-
currentIds: localCurrentIds,
|
|
62
|
-
client,
|
|
63
|
-
pluginConfig,
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
if (referenceDocs?.length) {
|
|
67
|
-
collection.push(...referenceDocs)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Create a unique array of objects from collection
|
|
76
|
-
// Set() wasn't working for unique id's ¯\_(ツ)_/¯
|
|
77
|
-
const uniqueCollection = collection.filter(Boolean).reduce((acc: SanityDocument[], cur) => {
|
|
78
|
-
if (acc.some((doc) => doc._id === cur._id)) {
|
|
79
|
-
return acc
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return [...acc, cur]
|
|
83
|
-
}, [])
|
|
84
|
-
|
|
85
|
-
return uniqueCollection
|
|
86
|
-
}
|
package/src/helpers/index.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {CSSProperties} from 'react'
|
|
2
|
-
|
|
3
|
-
export function createInitialMessage(docCount = 0, refsCount = 0): string {
|
|
4
|
-
const message = [
|
|
5
|
-
docCount === 1 ? `This Document contains` : `These ${docCount} Documents contain`,
|
|
6
|
-
refsCount === 1 ? `1 Reference.` : `${refsCount} References.`,
|
|
7
|
-
refsCount === 1 ? `That Document` : `Those Documents`,
|
|
8
|
-
`may have References too. If referenced Documents do not exist at the target Destination, this transaction will fail.`,
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
return message.join(` `)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const stickyStyles = (isDarkMode = true): CSSProperties => ({
|
|
15
|
-
position: 'sticky',
|
|
16
|
-
top: 0,
|
|
17
|
-
zIndex: 100,
|
|
18
|
-
backgroundColor: isDarkMode ? `rgba(10,10,10,0.95)` : `rgba(255,255,255,0.95)`,
|
|
19
|
-
})
|
package/src/index.ts
DELETED
package/src/plugin.tsx
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import {definePlugin} from 'sanity'
|
|
2
|
-
|
|
3
|
-
import {DuplicateToAction} from './actions/DuplicateToAction'
|
|
4
|
-
import {ConfigProvider} from './context/ConfigProvider'
|
|
5
|
-
import {DEFAULT_CONFIG} from './helpers/constants'
|
|
6
|
-
import {crossDatasetDuplicatorTool} from './tool'
|
|
7
|
-
import {PluginConfig} from './types'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Plugin: Cross Dataset Duplicator
|
|
11
|
-
* @public
|
|
12
|
-
*/
|
|
13
|
-
export const crossDatasetDuplicator = definePlugin<Partial<PluginConfig> | void>((config = {}) => {
|
|
14
|
-
const pluginConfig = {...DEFAULT_CONFIG, ...config}
|
|
15
|
-
const {types} = pluginConfig
|
|
16
|
-
|
|
17
|
-
return {
|
|
18
|
-
name: '@sanity/cross-dataset-duplicator',
|
|
19
|
-
tools: (prev) => (pluginConfig.tool ? [...prev, crossDatasetDuplicatorTool()] : prev),
|
|
20
|
-
studio: {
|
|
21
|
-
components: {
|
|
22
|
-
layout: (props) => ConfigProvider({...props, pluginConfig}),
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
document: {
|
|
26
|
-
actions: (prev, {schemaType}) => {
|
|
27
|
-
return types && types.includes(schemaType) ? [...prev, DuplicateToAction] : prev
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
}
|
|
31
|
-
})
|
package/src/tool/index.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type {Tool} from 'sanity'
|
|
2
|
-
import {LaunchIcon} from '@sanity/icons'
|
|
3
|
-
|
|
4
|
-
import {CrossDatasetDuplicatorTool, MultiToolConfig} from '../components/CrossDatasetDuplicatorTool'
|
|
5
|
-
|
|
6
|
-
export const crossDatasetDuplicatorTool = (): Tool<MultiToolConfig> => ({
|
|
7
|
-
title: 'Duplicator',
|
|
8
|
-
name: 'duplicator',
|
|
9
|
-
icon: LaunchIcon,
|
|
10
|
-
component: CrossDatasetDuplicatorTool,
|
|
11
|
-
options: {
|
|
12
|
-
docs: [],
|
|
13
|
-
},
|
|
14
|
-
})
|
package/src/types/index.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import {SanityDocument} from 'sanity'
|
|
2
|
-
|
|
3
|
-
type PreDefinedQuery = {
|
|
4
|
-
label: string
|
|
5
|
-
query: string
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Plugin configuration
|
|
9
|
-
* @public
|
|
10
|
-
*/
|
|
11
|
-
export interface PluginConfig {
|
|
12
|
-
apiVersion?: string
|
|
13
|
-
tool?: boolean
|
|
14
|
-
types?: string[]
|
|
15
|
-
filter?: string
|
|
16
|
-
follow?: ('inbound' | 'outbound')[]
|
|
17
|
-
queries?: PreDefinedQuery[]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Cross Dataset Duplicator document action props
|
|
22
|
-
* @public
|
|
23
|
-
*/
|
|
24
|
-
export type CrossDatasetDuplicatorActionProps = {
|
|
25
|
-
docs: SanityDocument[]
|
|
26
|
-
onDuplicated?: () => Promise<void>
|
|
27
|
-
}
|
package/v2-incompatible.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
|
|
2
|
-
const {name, version, sanityExchangeUrl} = require('./package.json')
|
|
3
|
-
|
|
4
|
-
export default showIncompatiblePluginDialog({
|
|
5
|
-
name: name,
|
|
6
|
-
versions: {
|
|
7
|
-
v3: version,
|
|
8
|
-
v2: undefined,
|
|
9
|
-
},
|
|
10
|
-
sanityExchangeUrl,
|
|
11
|
-
})
|