sanity-plugin-documents-pane 1.0.0-v3-studio.1
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/LICENSE +21 -0
- package/README.md +105 -0
- package/lib/cjs/index.js +367 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/esm/index.js +355 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/types/index.d.ts +40 -0
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +97 -0
- package/sanity.json +8 -0
- package/src/Debug.tsx +27 -0
- package/src/Documents.tsx +99 -0
- package/src/DocumentsPane.tsx +60 -0
- package/src/Feedback.tsx +18 -0
- package/src/NewDocument.tsx +40 -0
- package/src/hooks/useListeningQuery.ts +66 -0
- package/src/index.ts +3 -0
- package/src/parts.d.ts +2 -0
- package/src/resolveInitialValueTemplates.ts +20 -0
- package/src/resolveParams.ts +44 -0
- package/src/types.ts +34 -0
- package/v2-incompatible.js +10 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {Stack} from '@sanity/ui'
|
|
3
|
+
|
|
4
|
+
import Documents from './Documents'
|
|
5
|
+
import Feedback from './Feedback'
|
|
6
|
+
import Debug from './Debug'
|
|
7
|
+
import {DocumentsPaneProps} from './types'
|
|
8
|
+
import resolveParams from './resolveParams'
|
|
9
|
+
import resolveInitialValueTemplates from './resolveInitialValueTemplates'
|
|
10
|
+
|
|
11
|
+
export default function DocumentsPane(props: DocumentsPaneProps) {
|
|
12
|
+
const {document, options} = props
|
|
13
|
+
const {
|
|
14
|
+
query,
|
|
15
|
+
params,
|
|
16
|
+
useDraft = false,
|
|
17
|
+
debug = false,
|
|
18
|
+
initialValueTemplates: initialValueTemplatesResolver,
|
|
19
|
+
} = options
|
|
20
|
+
|
|
21
|
+
if (useDraft && typeof params === 'function') {
|
|
22
|
+
return (
|
|
23
|
+
<Stack padding={4} space={5}>
|
|
24
|
+
<Feedback>
|
|
25
|
+
<code>useDraft</code> should not be <code>true</code> when supplying a function for
|
|
26
|
+
<code>params</code>
|
|
27
|
+
</Feedback>
|
|
28
|
+
{debug && <Debug query={query} />}
|
|
29
|
+
</Stack>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const paramValues = resolveParams({document, params, useDraft})
|
|
34
|
+
|
|
35
|
+
const initialValueTemplates = resolveInitialValueTemplates({
|
|
36
|
+
resolver: initialValueTemplatesResolver,
|
|
37
|
+
document,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
if (!paramValues) {
|
|
41
|
+
return (
|
|
42
|
+
<Stack padding={4} space={5}>
|
|
43
|
+
<Feedback>
|
|
44
|
+
Parameters for this query could not be resolved. This may mean the document does not yet
|
|
45
|
+
exist or is incomplete.
|
|
46
|
+
</Feedback>
|
|
47
|
+
{debug && <Debug query={query} />}
|
|
48
|
+
</Stack>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Documents
|
|
54
|
+
query={query}
|
|
55
|
+
params={paramValues}
|
|
56
|
+
debug={debug}
|
|
57
|
+
initialValueTemplates={initialValueTemplates}
|
|
58
|
+
/>
|
|
59
|
+
)
|
|
60
|
+
}
|
package/src/Feedback.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {Button, Card, Flex} from '@sanity/ui'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import {DocumentsPaneInitialValueTemplate} from './types'
|
|
4
|
+
import {ComposeIcon} from '@sanity/icons'
|
|
5
|
+
import {usePaneRouter} from 'sanity/desk'
|
|
6
|
+
import {uuid} from '@sanity/uuid'
|
|
7
|
+
|
|
8
|
+
interface NewDocumentProps {
|
|
9
|
+
initialValueTemplates: DocumentsPaneInitialValueTemplate[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function NewDocument(props: NewDocumentProps) {
|
|
13
|
+
const {initialValueTemplates = []} = props
|
|
14
|
+
const {ReferenceChildLink} = usePaneRouter()
|
|
15
|
+
|
|
16
|
+
if (!initialValueTemplates.length) return null
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Card borderBottom={true} padding={2}>
|
|
20
|
+
<Flex justify="flex-end" gap={1}>
|
|
21
|
+
{initialValueTemplates.map((template) => {
|
|
22
|
+
if (!template.template) {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
return (
|
|
26
|
+
<ReferenceChildLink
|
|
27
|
+
documentId={uuid()}
|
|
28
|
+
documentType={template.schemaType}
|
|
29
|
+
template={{id: template.template, params: template.parameters}}
|
|
30
|
+
parentRefPath={[]}
|
|
31
|
+
key={`${template.schemaType}-${template.template}`}
|
|
32
|
+
>
|
|
33
|
+
<Button icon={<ComposeIcon />} text={template.title} mode="bleed" as="span" />
|
|
34
|
+
</ReferenceChildLink>
|
|
35
|
+
)
|
|
36
|
+
})}
|
|
37
|
+
</Flex>
|
|
38
|
+
</Card>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {useEffect, useState, useRef} from 'react'
|
|
2
|
+
import {catchError, distinctUntilChanged} from 'rxjs/operators'
|
|
3
|
+
import isEqual from 'react-fast-compare'
|
|
4
|
+
import type {SanityDocument} from 'sanity'
|
|
5
|
+
import {useDocumentStore} from 'sanity/_unstable'
|
|
6
|
+
|
|
7
|
+
type Params = Record<string, string | number | boolean | string[]>
|
|
8
|
+
|
|
9
|
+
interface ListenQueryOptions {
|
|
10
|
+
tag?: string
|
|
11
|
+
apiVersion?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type ReturnShape = {
|
|
15
|
+
loading: boolean
|
|
16
|
+
error: boolean
|
|
17
|
+
data: SanityDocument[] | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type Observable = {
|
|
21
|
+
unsubscribe: () => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_PARAMS = {}
|
|
25
|
+
const DEFAULT_OPTIONS = {apiVersion: `v2022-05-09`}
|
|
26
|
+
|
|
27
|
+
export default function useListeningQuery(
|
|
28
|
+
query: string,
|
|
29
|
+
params: Params = DEFAULT_PARAMS,
|
|
30
|
+
options: ListenQueryOptions = DEFAULT_OPTIONS
|
|
31
|
+
): ReturnShape {
|
|
32
|
+
const [loading, setLoading] = useState(true)
|
|
33
|
+
const [error, setError] = useState(false)
|
|
34
|
+
const [data, setData] = useState<SanityDocument[] | null>(null)
|
|
35
|
+
const subscription = useRef<null | Observable>(null)
|
|
36
|
+
const documentStore = useDocumentStore()
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (query) {
|
|
40
|
+
subscription.current = documentStore
|
|
41
|
+
.listenQuery(query, params, options)
|
|
42
|
+
.pipe(
|
|
43
|
+
distinctUntilChanged(isEqual),
|
|
44
|
+
catchError((err) => {
|
|
45
|
+
console.error(err)
|
|
46
|
+
setError(err)
|
|
47
|
+
setLoading(false)
|
|
48
|
+
setData(null)
|
|
49
|
+
|
|
50
|
+
return err
|
|
51
|
+
})
|
|
52
|
+
)
|
|
53
|
+
.subscribe((documents: SanityDocument[]) => {
|
|
54
|
+
setData((current) => (isEqual(current, documents) ? current : documents))
|
|
55
|
+
setLoading(false)
|
|
56
|
+
setError(false)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return () => {
|
|
61
|
+
return subscription.current ? subscription.current.unsubscribe() : undefined
|
|
62
|
+
}
|
|
63
|
+
}, [query, params, options])
|
|
64
|
+
|
|
65
|
+
return {loading, error, data}
|
|
66
|
+
}
|
package/src/index.ts
ADDED
package/src/parts.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DocumentsPaneInitialValueTemplate,
|
|
3
|
+
DocumentsPaneInitialValueTemplateResolver,
|
|
4
|
+
DocumentVersionsCollection,
|
|
5
|
+
} from './types'
|
|
6
|
+
|
|
7
|
+
interface ResolveInitialValueTemplatesOptions {
|
|
8
|
+
resolver: DocumentsPaneInitialValueTemplateResolver | undefined
|
|
9
|
+
document: DocumentVersionsCollection
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function resolveInitialValueTemplates(
|
|
13
|
+
options: ResolveInitialValueTemplatesOptions
|
|
14
|
+
): DocumentsPaneInitialValueTemplate[] {
|
|
15
|
+
const {resolver, document} = options || {}
|
|
16
|
+
|
|
17
|
+
if (!resolver) return []
|
|
18
|
+
|
|
19
|
+
return resolver({document})
|
|
20
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {DocumentsPaneQueryParams, DocumentVersionsCollection} from './types'
|
|
2
|
+
import delve from 'dlv'
|
|
3
|
+
|
|
4
|
+
interface ResolveParamsOptions {
|
|
5
|
+
params?: DocumentsPaneQueryParams
|
|
6
|
+
document: DocumentVersionsCollection
|
|
7
|
+
useDraft: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ResolveParamsReturn = undefined | {[key: string]: string}
|
|
11
|
+
|
|
12
|
+
function defaultResolver(options: ResolveParamsOptions): {
|
|
13
|
+
[key: string]: string | undefined
|
|
14
|
+
} {
|
|
15
|
+
const {params, document, useDraft} = options
|
|
16
|
+
|
|
17
|
+
// params is optional
|
|
18
|
+
if (!params) return {}
|
|
19
|
+
|
|
20
|
+
// legacy useDraft behaviour
|
|
21
|
+
const doc = useDraft ? document.displayed : document.published
|
|
22
|
+
|
|
23
|
+
return Object.keys(params).reduce(
|
|
24
|
+
(acc, key) => ({
|
|
25
|
+
...acc,
|
|
26
|
+
[key]: delve(doc, params[key as keyof DocumentsPaneQueryParams]),
|
|
27
|
+
}),
|
|
28
|
+
{}
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function resolveParams(options: ResolveParamsOptions): ResolveParamsReturn {
|
|
33
|
+
const {params, document} = options
|
|
34
|
+
|
|
35
|
+
const resolvedParams = typeof params == 'function' ? params({document}) : defaultResolver(options)
|
|
36
|
+
|
|
37
|
+
// if any of the parameters are undefined, the query will error
|
|
38
|
+
// so return undefined so the UI can show a more appropriate message
|
|
39
|
+
if (Object.values(resolvedParams).includes(undefined)) return undefined
|
|
40
|
+
|
|
41
|
+
// Typescript can't tell that we've guarded against any value being undefined,
|
|
42
|
+
// so forcing the type
|
|
43
|
+
return resolvedParams as {[key: string]: string}
|
|
44
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import {SanityDocument} from '@sanity/client'
|
|
2
|
+
|
|
3
|
+
export interface DocumentVersionsCollection {
|
|
4
|
+
displayed: SanityDocument
|
|
5
|
+
published: SanityDocument
|
|
6
|
+
draft: SanityDocument
|
|
7
|
+
historical: SanityDocument
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// eslint-disable-next-line prettier/prettier
|
|
11
|
+
export type DocumentsPaneQueryParams = (params: {document: DocumentVersionsCollection}) => ({[key: string]: string}) | {[key: string]: string}
|
|
12
|
+
|
|
13
|
+
export interface DocumentsPaneInitialValueTemplate {
|
|
14
|
+
schemaType: string
|
|
15
|
+
template?: string
|
|
16
|
+
parameters?: {[key: string]: any}
|
|
17
|
+
title: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line prettier/prettier
|
|
21
|
+
export type DocumentsPaneInitialValueTemplateResolver = (params: {document: DocumentVersionsCollection}) => DocumentsPaneInitialValueTemplate[]
|
|
22
|
+
|
|
23
|
+
export type DocumentsPaneOptions = {
|
|
24
|
+
query: string
|
|
25
|
+
params?: DocumentsPaneQueryParams
|
|
26
|
+
debug?: boolean
|
|
27
|
+
useDraft?: boolean
|
|
28
|
+
initialValueTemplates?: DocumentsPaneInitialValueTemplateResolver
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type DocumentsPaneProps = {
|
|
32
|
+
document: DocumentVersionsCollection
|
|
33
|
+
options: DocumentsPaneOptions
|
|
34
|
+
}
|