/** @jsxRuntime classic */ /** @jsx jsx */ import { jsx, Stack, useTheme, Text } from '@keystone-ui/core' import { memo, type ReactNode, useContext, useId, useMemo } from 'react' import { FieldDescription } from '@keystone-ui/fields' import { ButtonContext } from '@keystone-ui/button' import { type FieldGroupMeta, type FieldMeta } from '../../types' import { type Value } from '.' type RenderFieldProps = { field: FieldMeta value: unknown itemValue: unknown onChange?(value: (value: Value) => Value): void autoFocus?: boolean forceValidation?: boolean } const RenderField = memo(function RenderField ({ field, value, itemValue, autoFocus, forceValidation, onChange, }: RenderFieldProps) { return ( { if (onChange === undefined) return undefined return value => { onChange(val => ({ ...val, [field.controller.path]: { kind: 'value', value } })) } }, [onChange, field.controller.path])} value={value} itemValue={itemValue} autoFocus={autoFocus} forceValidation={forceValidation} /> ) }) type FieldsProps = { fields: Record groups?: FieldGroupMeta[] value: Value fieldModes?: Record | null fieldPositions?: Record | null forceValidation: boolean position?: 'form' | 'sidebar' invalidFields: ReadonlySet onChange(value: (value: Value) => Value): void } export function Fields ({ fields, value, fieldModes = null, fieldPositions = null, forceValidation, invalidFields, position = 'form', groups = [], onChange, }: FieldsProps) { const renderedFields = Object.fromEntries( Object.keys(fields).map((fieldKey, index) => { const field = fields[fieldKey] const val = value[fieldKey] const fieldMode = fieldModes === null ? 'edit' : fieldModes[fieldKey] const fieldPosition = fieldPositions === null ? 'form' : fieldPositions[fieldKey] if (fieldMode === 'hidden') return [fieldKey, null] if (fieldPosition !== position) return [fieldKey, null] if (val.kind === 'error') { return [ fieldKey,
{field.label}: {val.errors[0].message}
, ] } return [ fieldKey, , ] }) ) const rendered: ReactNode[] = [] const fieldGroups = new Map() for (const group of groups) { const state = { group, rendered: false } for (const field of group.fields) { fieldGroups.set(field.path, state) } } for (const field of Object.values(fields)) { const fieldKey = field.path if (fieldGroups.has(fieldKey)) { const groupState = fieldGroups.get(field.path)! if (groupState.rendered) { continue } groupState.rendered = true const { group } = groupState const renderedFieldsInGroup = group.fields.map(field => renderedFields[field.path]) if (renderedFieldsInGroup.every(field => field === null)) { continue } rendered.push( {renderedFieldsInGroup} ) continue } if (renderedFields[fieldKey] === null) { continue } rendered.push(renderedFields[fieldKey]) } return ( {rendered.length === 0 ? 'There are no fields that you can read or edit' : rendered} ) } function FieldGroup (props: { label: string, description: string | null, children: ReactNode }) { const descriptionId = useId() const labelId = useId() const theme = useTheme() const buttonSize = 24 const { useButtonStyles, useButtonTokens, defaults } = useContext(ButtonContext) const buttonStyles = useButtonStyles({ tokens: useButtonTokens(defaults) }) const divider = (
) return (
above css={{ ...buttonStyles, 'summary:focus &': buttonStyles[':focus'], padding: 0, height: buttonSize, width: buttonSize, 'details[open] &': { transform: 'rotate(90deg)', }, }} > {downChevron}
{divider} {props.label}
{divider}
{props.description !== null && ( {props.description} )} {props.children}
) } const downChevron = ( )