@prismicio/types-internal
Version:
Prismic types for Custom Types and Prismic Data
121 lines (101 loc) • 3.25 kB
text/typescript
import * as t from "io-ts"
import type { OnFieldFn } from "../../_internal/utils"
import { WidgetKey } from "../../common"
import { StringOrNull } from "../../validators"
import { NestableWidget } from "./nestable/NestableWidget"
export const GroupFieldType = "Group"
// We're obliged to use `t.Mixed` here, otherwise the factory would be
// referencing itself and TypeScript would infer its type as `any`
const createGroupConfig = <TWidgets extends t.Mixed>(widgets: TWidgets) =>
t.exact(
t.partial({
label: StringOrNull,
repeat: t.boolean,
fields: t.record(WidgetKey, widgets),
}),
)
// We're obliged to use `t.Mixed` here, otherwise the factory would be
// referencing itself and TypeScript would infer its type as `any`
const createGroup = <TWidgets extends t.Mixed>(widgets: TWidgets) =>
t.exact(
t.intersection([
t.type({
type: t.literal(GroupFieldType),
}),
t.partial({
fieldset: StringOrNull,
icon: t.string,
description: t.string,
config: createGroupConfig(widgets),
}),
]),
)
export const NestedGroupConfig = createGroupConfig(NestableWidget)
export type NestedGroupConfig = t.TypeOf<typeof NestedGroupConfig>
export const NestedGroup = createGroup(NestableWidget)
export type NestedGroup = t.TypeOf<typeof NestedGroup>
export const GroupConfig = createGroupConfig(
t.union([NestableWidget, NestedGroup]),
)
export type GroupConfig = t.TypeOf<typeof GroupConfig>
export const Group = createGroup(t.union([NestableWidget, NestedGroup]))
export type Group = t.TypeOf<typeof Group>
export function traverseNestedGroup(args: {
path: ReadonlyArray<string>
group: NestedGroup
onField: OnFieldFn<NestableWidget>
}): NestedGroup {
const { path: prevPath, group, onField } = args
if (!group.config?.fields) return group
let fields: Record<string, NestableWidget> | undefined
for (const [key, field] of Object.entries(group.config.fields)) {
const path = [...prevPath, key]
const newField = onField({
path,
key,
field,
})
if (field !== newField) {
if (!fields) fields = { ...group.config.fields }
fields[key] = newField
}
}
// returns the traversed model instead of a new one if it didn't change
return fields ? { ...group, config: { ...group.config, fields } } : group
}
export function traverseGroup(args: {
path: ReadonlyArray<string>
group: Group
onField: OnFieldFn<NestableWidget | NestedGroup>
}): Group {
const { path: prevPath, group, onField } = args
if (!group.config?.fields) return group
let fields: Record<string, NestableWidget | NestedGroup> | undefined
for (const [key, prevField] of Object.entries(group.config.fields)) {
const path = [...prevPath, key]
let field
switch (prevField.type) {
case "Group":
field = traverseNestedGroup({
path,
group: prevField,
onField: onField as OnFieldFn<NestableWidget>,
})
break
default:
field = prevField
break
}
const newField = onField({
path,
key,
field,
})
if (field !== newField) {
if (!fields) fields = { ...group.config.fields }
fields[key] = newField
}
}
// returns the traversed model instead of a new one if it didn't change
return fields ? { ...group, config: { ...group.config, fields } } : group
}