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.
@@ -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
+ }
@@ -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
@@ -0,0 +1,3 @@
1
+ import DocumentsPane from './DocumentsPane'
2
+
3
+ export default DocumentsPane
package/src/parts.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Tells Typescript to skip typechecking imported parts
2
+ declare module "part:@sanity/*";
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ const {showIncompatiblePluginDialog} = require('@sanity/incompatible-plugin')
2
+ const {name, version} = require('./package.json')
3
+
4
+ export default showIncompatiblePluginDialog({
5
+ name: name,
6
+ versions: {
7
+ v3: version,
8
+ v2: '^1.1.0',
9
+ },
10
+ })