Skip to content

Commit

Permalink
fix(core): resolveSchemaTypeForPath updates
Browse files Browse the repository at this point in the history
  • Loading branch information
hermanwikner committed Jun 28, 2024
1 parent 9c48028 commit c9dbafe
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 43 deletions.
9 changes: 7 additions & 2 deletions packages/sanity/src/core/form/field/actions/pasteAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import {useCallback} from 'react'
import {defineDocumentFieldAction} from '../../../config/document/fieldActions/define'
import {useTranslation} from '../../../i18n'
import {useCopyPasteAction} from '../../../studio/copyPaste/useCopyPasteAction'
import {useGetFormValue} from '../../contexts/GetFormValue'
import {type FormDocumentValue} from '../../types/formDocumentValue'
import {defineActionItem} from './define'

export const pasteAction = defineDocumentFieldAction({
name: 'pasteField',
useAction({path}) {
const {t} = useTranslation('copy-paste')
const getFormValue = useGetFormValue()

const isDocument = path.length === 0

Expand All @@ -19,11 +22,13 @@ export const pasteAction = defineDocumentFieldAction({

const {onPaste} = useCopyPasteAction()

const value = getFormValue([]) as FormDocumentValue

const onAction = useCallback(() => {
onPaste(path, {
onPaste(path, value, {
context: {source: isDocument ? 'documentFieldAction' : 'fieldAction'},
})
}, [onPaste, path, isDocument])
}, [onPaste, path, value, isDocument])

return defineActionItem({
type: 'action',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,58 +1,81 @@
import {
type ArraySchemaType,
isIndexSegment,
isKeySegment,
isReferenceSchemaType,
isObjectSchemaType,
type ObjectField,
type ObjectSchemaType,
type Path,
type SchemaType,
} from '@sanity/types'
import {fromString, toString} from '@sanity/util/paths'
import {type FormDocumentValue, getValueAtPath} from 'sanity'

function getFieldTypeByName(type: SchemaType, fieldName: string): SchemaType | undefined {
if (!('fields' in type)) {
return undefined
import {getItemType} from '../../form/store/utils/getItemType'

export function getSchemaField(
schemaType: SchemaType,
fieldPath: string,
): ObjectField<SchemaType> | undefined {
if (!fieldPath) return undefined

const paths = fromString(fieldPath)
const firstPath = paths[0]

if (firstPath && isObjectSchemaType(schemaType)) {
const field = schemaType?.fields?.find((f) => f.name === firstPath)

if (field) {
const nextPath = toString(paths.slice(1))

if (nextPath) {
return getSchemaField(field.type, nextPath)
}

return field
}
}

const fieldType = type.fields.find((field) => field.name === fieldName)
return fieldType ? fieldType.type : undefined
return undefined
}

export function resolveSchemaTypeForPath(baseType: SchemaType, path: Path): SchemaType | undefined {
const pathSegments = path
export function resolveSchemaTypeForPath(
baseType: SchemaType,
path: Path,
documentValue: FormDocumentValue | undefined,
): SchemaType | undefined {
if (!baseType) return undefined

let current: SchemaType | undefined = baseType
for (const segment of pathSegments) {
if (!current) {
return undefined
}
let currentField: ObjectSchemaType | ArraySchemaType<unknown> | SchemaType = baseType

if (typeof segment === 'string') {
current = getFieldTypeByName(current, segment)
continue
}
path.forEach((segment, index) => {
const currentPath = path.slice(0, index + 1)
const isArrayItem = isKeySegment(segment) || isIndexSegment(segment)

const isArrayAccessor = isKeySegment(segment) || isIndexSegment(segment)
if (!isArrayAccessor || current.jsonType !== 'array') {
return undefined
}
if (isArrayItem) {
// We know that the currentField is an array schema type
// if the current segment is an array item.
const arraySchemaType = currentField as ArraySchemaType<unknown>

const [memberType, otherType] = current.of || []
if (otherType || !memberType) {
// Can't figure out the type without knowing the value
return undefined
}
// Get the value of the array item at the current path
const itemValue = getValueAtPath(documentValue, currentPath) as unknown[]

if (!isReferenceSchemaType(memberType)) {
current = memberType
continue
}
// Get the schema type of the array item
const item = getItemType(arraySchemaType, itemValue)

const [refType, otherRefType] = memberType.to || []
if (otherRefType || !refType) {
// Can't figure out the type without knowing the value
return undefined
if (item) {
currentField = item as ObjectSchemaType

return
}
}

current = refType
}
const nextField = getSchemaField(currentField, toString(currentPath)) as ObjectSchemaType

if (nextField.type) {
currentField = nextField.type as ObjectSchemaType
}
})

return current
return currentField
}
17 changes: 13 additions & 4 deletions packages/sanity/src/core/studio/copyPaste/useCopyPasteAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import {transferValue, type TransferValueOptions} from './valueTransfer'

interface CopyPasteHookValue {
onCopy: (path: Path, value: FormDocumentValue | undefined, options?: CopyOptions) => Promise<void>
onPaste: (targetPath: Path, options?: PasteOptions) => Promise<void>
onPaste: (
targetPath: Path,
value: FormDocumentValue | undefined,
options?: PasteOptions,
) => Promise<void>
onChange: ((event: PatchEvent) => void) | undefined
}

Expand Down Expand Up @@ -70,7 +74,7 @@ export function useCopyPasteAction(): CopyPasteHookValue {
return
}

const schemaTypeAtPath = resolveSchemaTypeForPath(schemaType, path)
const schemaTypeAtPath = resolveSchemaTypeForPath(schemaType, path, value)

if (!schemaTypeAtPath) {
toast.push({
Expand Down Expand Up @@ -146,9 +150,13 @@ export function useCopyPasteAction(): CopyPasteHookValue {
)

const onPaste = useCallback(
async (targetPath: Path, options?: PasteOptions) => {
async (targetPath: Path, value: FormDocumentValue | undefined, options?: PasteOptions) => {
const {schemaType: targetDocumentSchemaType} = getDocumentMeta()!
const targetSchemaType = resolveSchemaTypeForPath(targetDocumentSchemaType!, targetPath)!
const targetSchemaType = resolveSchemaTypeForPath(
targetDocumentSchemaType!,
targetPath,
value,
)!

const clipboardItem = await getClipboardItem()

Expand Down Expand Up @@ -186,6 +194,7 @@ export function useCopyPasteAction(): CopyPasteHookValue {
const sourceSchemaType = resolveSchemaTypeForPath(
sourceDocumentSchemaType,
item.documentPath,
value,
)

if (!sourceSchemaType) {
Expand Down

0 comments on commit c9dbafe

Please sign in to comment.