UNPKG

@prismicio/types-internal

Version:
216 lines (196 loc) 4.82 kB
import { either } from "fp-ts/" import { getOrElseW } from "fp-ts/lib/Either" import { pipe } from "fp-ts/lib/function" import * as t from "io-ts" import { StringOrNull } from "../../../validators" import ImageConstraint from "../shared/ImageConstraint" export const RichTextFieldType = "StructuredText" export const DEFAULT_OPTION = "paragraph" export type RichTextNodeType = typeof RichTextNodeType[keyof typeof RichTextNodeType] export const RichTextNodeType = { heading1: "heading1", heading2: "heading2", heading3: "heading3", heading4: "heading4", heading5: "heading5", heading6: "heading6", paragraph: "paragraph", strong: "strong", em: "em", preformatted: "preformatted", hyperlink: "hyperlink", image: "image", embed: "embed", list: "list-item", orderedList: "o-list-item", rtl: "rtl", } as const export const RichTextNodeTypeCodec = t.keyof({ [RichTextNodeType.heading1]: null, [RichTextNodeType.heading2]: null, [RichTextNodeType.heading3]: null, [RichTextNodeType.heading4]: null, [RichTextNodeType.heading5]: null, [RichTextNodeType.heading6]: null, [RichTextNodeType.paragraph]: null, [RichTextNodeType.strong]: null, [RichTextNodeType.em]: null, [RichTextNodeType.preformatted]: null, [RichTextNodeType.hyperlink]: null, [RichTextNodeType.image]: null, [RichTextNodeType.embed]: null, [RichTextNodeType.list]: null, [RichTextNodeType.orderedList]: null, [RichTextNodeType.rtl]: null, }) const RichTextOptions = new t.Type<string, string, unknown>( "RichTextOptions", (u: unknown): u is string => typeof u === "string", (u: unknown) => { return pipe( t.union([t.string, t.null]).decode(u), either.map((s: string | null) => { if (!s) return DEFAULT_OPTION const entries = s.split(",").map((e: string) => e.trim()) const filtered = entries.filter((entry) => { return getOrElseW(() => undefined)( RichTextNodeTypeCodec.decode(entry), ) }) if (!filtered.length) return DEFAULT_OPTION return filtered.join(",") }), ) }, (a) => a, ) const NoLabels = ( labels: | string | ReadonlyArray<string> | { [x: string]: ReadonlyArray<{ name: string }> } | null, ) => { if (!labels) return t.success([]) return } const LabelsAsObject = ( labels: | string | ReadonlyArray<string> | { [x: string]: ReadonlyArray<{ name: string }> } | null, ) => { if (labels instanceof Object) { const labelsObj = labels as { [x: string]: { name: string }[] } // empty labels if (!Object.entries(labelsObj).length) return t.success([]) // weird case labels with empty key as parent if (labelsObj[""]) { return t.success(labelsObj[""].map((l) => l.name)) } const convertedObjectToArray = Object.entries(labelsObj) .reduce<ReadonlyArray<string>>((acc, [, labelsEntries]) => { return acc.concat(labelsEntries.map((l) => l.name)) }, []) .filter(Boolean) return t.success(convertedObjectToArray) } return } const LabelsAsArray = ( labels: | string | ReadonlyArray<string> | { [x: string]: ReadonlyArray<{ name: string }> } | null, ) => { if (labels instanceof Array) { const isValidLabels = labels.reduce( (acc, l) => acc && typeof l === "string", true, ) if (isValidLabels) return t.success(labels) } return } const LabelsAsString = ( labels: | string | ReadonlyArray<string> | { [x: string]: ReadonlyArray<{ name: string }> } | null, ) => { if (typeof labels === "string") { return t.success([labels]) } return } const RichTextLabels = new t.Type<ReadonlyArray<string>, object, unknown>( "RichTextLabels", (u: unknown): u is ReadonlyArray<string> => { return u instanceof Array }, (u: unknown, context: t.Context) => { const legacyValidator = t.record( t.string, t.readonlyArray(t.record(t.literal("name"), t.string)), ) const validator = t.readonlyArray(t.string) return pipe( t.union([legacyValidator, validator, t.string, t.null]).decode(u), either.chain((labels) => { return ( NoLabels(labels) || LabelsAsArray(labels) || LabelsAsObject(labels) || LabelsAsString(labels) || t.failure(u, context) ) }), ) }, (res) => res, ) export const RichTextConfig = t.exact( t.partial({ label: StringOrNull, placeholder: t.string, useAsTitle: t.boolean, single: RichTextOptions, multi: RichTextOptions, imageConstraint: ImageConstraint, labels: RichTextLabels, allowTargetBlank: t.boolean, }), ) export type RichTextConfig = t.TypeOf<typeof RichTextConfig> export const RichText = t.exact( t.intersection([ t.type({ type: t.literal(RichTextFieldType), }), t.partial({ fieldset: StringOrNull, config: RichTextConfig, }), ]), ) export type RichText = t.TypeOf<typeof RichText>