UNPKG

@sanity/assist

Version:

You create the instructions; Sanity AI Assist does the rest.

135 lines (121 loc) 4.49 kB
/* eslint-disable max-depth */ import { type ArrayOfObjectsFormNode, type ArrayOfObjectsItemMember, type ArrayOfPrimitivesFormNode, type DocumentFormNode, type FieldsetState, isObjectSchemaType, type ObjectFormNode, type Path, pathToString, type SchemaType, } from 'sanity' const DEFAULT_MAX_DEPTH = 8 const ABSOLUTE_MAX_DEPTH = 50 export interface ConditionalMemberState { path: string hidden: boolean readOnly: boolean } interface ConditionalMemberInnerState extends ConditionalMemberState { conditional: boolean } /** * This is used to statically determine the state of the functions on the server-side. * Paths which has a schema with conditional config should be considered hidden: true and/or readOnly: true * Only conditional paths are included, as static props can be determined from schema. * * Returns paths that has conditional hidden or readOnly schema config (function) and that. * Form-state does not contain hidden members. * * Note: * * If a parent path is hidden, no child paths are included * * If a parent path is readOnly, no child paths are included * * If a path is hidden, it is not included; only conditionally visible paths will be returned, with hidden: false */ export function getConditionalMembers( docState: DocumentFormNode, maxDepth = DEFAULT_MAX_DEPTH, ): ConditionalMemberState[] { const doc: ConditionalMemberInnerState = { path: '', hidden: false, readOnly: !!docState.readOnly, conditional: isConditional(docState.schemaType), } return ( [doc, ...extractConditionalPaths(docState, Math.min(maxDepth, ABSOLUTE_MAX_DEPTH))] .filter((v) => v.conditional) // eslint-disable-next-line @typescript-eslint/no-unused-vars .map(({conditional, ...state}) => ({...state})) ) } function isConditional(schemaType: SchemaType) { return typeof schemaType.hidden === 'function' || typeof schemaType.readOnly === 'function' } function conditionalState(memberState: { path: Path schemaType: SchemaType readOnly?: boolean }): ConditionalMemberInnerState { return { path: pathToString(memberState.path), readOnly: !!memberState.readOnly, hidden: false, // if its in members, its not hidden conditional: isConditional(memberState.schemaType), } } function extractConditionalPaths( node: ObjectFormNode | FieldsetState, maxDepth: number, ): ConditionalMemberInnerState[] { if (node.path.length >= maxDepth) { return [] } return node.members.reduce<ConditionalMemberInnerState[]>((acc, member) => { if (member.kind === 'error') { return acc } if (member.kind === 'field') { const schemaType = member.field.schemaType if (schemaType.jsonType === 'object') { const innerFields = member.field.readOnly ? [] : extractConditionalPaths(member.field as ObjectFormNode, maxDepth) return [...acc, conditionalState(member.field), ...innerFields] } else if (schemaType.jsonType === 'array') { const array = member.field as ArrayOfObjectsFormNode | ArrayOfPrimitivesFormNode let arrayPaths: ConditionalMemberInnerState[] = [] const isObjectsArray = array.members.some( (m) => m.kind === 'item' && isObjectSchemaType(m.item.schemaType), ) if (!array.readOnly) { for (const arrayMember of array.members) { if (arrayMember.kind === 'error') { continue } const innerFields = isObjectsArray && !arrayMember.item.readOnly ? extractConditionalPaths((arrayMember as ArrayOfObjectsItemMember).item, maxDepth) : [] arrayPaths = [...arrayPaths, conditionalState(arrayMember.item), ...innerFields] } } return [...acc, conditionalState(array), ...arrayPaths] } return [...acc, conditionalState(member.field)] } else if (member.kind === 'fieldSet') { const conditionalFieldset = !!(node as ObjectFormNode).schemaType?.fieldsets?.some( (f) => !f.single && f.name === member.fieldSet.name && typeof f.hidden === 'function', ) const innerFields = extractConditionalPaths(member.fieldSet, maxDepth).map((f) => ({ ...f, // if fieldset is conditional, visible fields must also be considered conditional conditional: conditionalFieldset ?? f.conditional, })) return [...acc, ...innerFields] } return acc }, []) }