sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
234 lines (201 loc) • 8.35 kB
text/typescript
import {AddIcon} from '@sanity/icons'
import {type InitialValueTemplateItem} from 'sanity'
import {type BaseIntentParams, type IntentParams} from './Intent'
import {type MenuItem, MenuItemBuilder} from './MenuItem'
import {HELP_URL, SerializeError} from './SerializeError'
import {type Serializable, type SerializeOptions, type SerializePath} from './StructureNodes'
import {type StructureContext} from './types'
/**
* A `InitialValueTemplateItemBuilder` is used to build a document node with an initial value set.
*
* @public
*/
export class InitialValueTemplateItemBuilder implements Serializable<InitialValueTemplateItem> {
/** Initial Value template item option object. See {@link InitialValueTemplateItem} */
protected spec: Partial<InitialValueTemplateItem>
constructor(
/**
* Structure context. See {@link StructureContext}
*/
protected _context: StructureContext,
spec?: Partial<InitialValueTemplateItem>,
) {
this.spec = spec ? spec : {}
}
/** Set initial value template item builder ID
* @param id - initial value template item ID
* @returns initial value template item based on ID provided. See {@link InitialValueTemplateItemBuilder}
*/
id(id: string): InitialValueTemplateItemBuilder {
return this.clone({id})
}
/** Get initial value template item builder ID
* @returns initial value template item ID. See {@link InitialValueTemplateItem}
*/
getId(): Partial<InitialValueTemplateItem>['id'] {
return this.spec.id
}
/** Set initial value template item title
* @param title - initial value template item title
* @returns initial value template item based on title provided. See {@link InitialValueTemplateItemBuilder}
*/
title(title: string): InitialValueTemplateItemBuilder {
return this.clone({title})
}
/** Get initial value template item title
* @returns initial value template item title. See {@link InitialValueTemplateItem}
*/
getTitle(): Partial<InitialValueTemplateItem>['title'] {
return this.spec.title
}
/** Set initial value template item description
* @param description - initial value template item description
* @returns initial value template item builder based on description provided. See {@link InitialValueTemplateItemBuilder}
*/
description(description: string): InitialValueTemplateItemBuilder {
return this.clone({description})
}
/** Get initial value template item description
* @returns initial value template item description. See {@link InitialValueTemplateItem}
*/
getDescription(): Partial<InitialValueTemplateItem>['description'] {
return this.spec.description
}
/** Set initial value template ID
* @param templateId - initial value template item template ID
* @returns initial value template item based builder on template ID provided. See {@link InitialValueTemplateItemBuilder}
*/
templateId(templateId: string): InitialValueTemplateItemBuilder {
// Let's try to be a bit helpful and assign an ID from template ID if none is specified
const paneId = this.spec.id || templateId
return this.clone({
id: paneId,
templateId,
})
}
/** Get initial value template item template ID
* @returns initial value template item ID. See {@link InitialValueTemplateItem}
*/
getTemplateId(): Partial<InitialValueTemplateItem>['templateId'] {
return this.spec.templateId
}
/** Get initial value template item template parameters
* @param parameters - initial value template item parameters
* @returns initial value template item builder based on parameters provided. See {@link InitialValueTemplateItemBuilder}
*/
parameters(parameters: {[key: string]: any}): InitialValueTemplateItemBuilder {
return this.clone({parameters})
}
/** Get initial value template item template parameters
* @returns initial value template item parameters. See {@link InitialValueTemplateItem}
*/
getParameters(): Partial<InitialValueTemplateItem>['parameters'] {
return this.spec.parameters
}
/** Serialize initial value template item
* @param options - serialization options. See {@link SerializeOptions}
* @returns initial value template item object based on the path, index and hint provided in options. See {@link InitialValueTemplateItem}
*/
serialize({path = [], index, hint}: SerializeOptions = {path: []}): InitialValueTemplateItem {
const {spec, _context} = this
const {templates} = _context
if (typeof spec.id !== 'string' || !spec.id) {
throw new SerializeError(
'`id` is required for initial value template item nodes',
path,
index,
hint,
).withHelpUrl(HELP_URL.ID_REQUIRED)
}
if (!spec.templateId) {
throw new SerializeError(
'template id (`templateId`) is required for initial value template item nodes',
path,
spec.id,
hint,
).withHelpUrl(HELP_URL.ID_REQUIRED)
}
const template = templates.find((t) => t.id === spec.templateId)
if (!template) {
throw new SerializeError(
'template id (`templateId`) is required for initial value template item nodes',
path,
spec.id,
hint,
).withHelpUrl(HELP_URL.ID_REQUIRED)
}
return {
id: spec.id,
templateId: spec.id,
schemaType: template.schemaType,
type: 'initialValueTemplateItem',
description: spec.description || template.description,
title: spec.title || template.title,
subtitle: spec.subtitle,
icon: spec.icon || template.icon,
initialDocumentId: spec.initialDocumentId,
parameters: spec.parameters,
}
}
/** Clone generic view builder (allows for options overriding)
* @param withSpec - initial value template item builder options. See {@link InitialValueTemplateItemBuilder}
* @returns initial value template item builder based on the context and options provided. See {@link InitialValueTemplateItemBuilder}
*/
clone(withSpec: Partial<InitialValueTemplateItem> = {}): InitialValueTemplateItemBuilder {
const builder = new InitialValueTemplateItemBuilder(this._context)
builder.spec = {...this.spec, ...withSpec}
return builder
}
}
/** @internal */
export function defaultInitialValueTemplateItems(
context: StructureContext,
): InitialValueTemplateItemBuilder[] {
const {schema, getStructureBuilder, templates} = context
// Sort templates by their schema type, in order or definition
const typeNames = schema.getTypeNames()
const ordered = templates
// Don't list templates that require parameters
// TODO: this should use the new-document template items instead maybe?
.filter((tpl) => !tpl.parameters?.length)
.sort((a, b) => typeNames.indexOf(a.schemaType) - typeNames.indexOf(b.schemaType))
// Create actual template items out of the templates
return ordered.map((tpl) => getStructureBuilder().initialValueTemplateItem(tpl.id))
}
/** @internal */
export function maybeSerializeInitialValueTemplateItem(
item: InitialValueTemplateItem | InitialValueTemplateItemBuilder,
index: number,
path: SerializePath,
): InitialValueTemplateItem {
return item instanceof InitialValueTemplateItemBuilder ? item.serialize({path, index}) : item
}
/** @internal */
export function menuItemsFromInitialValueTemplateItems(
context: StructureContext,
templateItems: InitialValueTemplateItem[],
): MenuItem[] {
const {schema, templates} = context
return templateItems.map((item) => {
const template = templates.find((t) => t.id === item.templateId)
const title = item.title || template?.title || 'Create'
const params: BaseIntentParams = {}
if (template && template.schemaType) {
params.type = template.schemaType
}
if (item.templateId) {
params.template = item.templateId
}
const intentParams: IntentParams = item.parameters ? [params, item.parameters] : params
const schemaType = template && schema.get(template.schemaType)
const i18n = item.i18n || template?.i18n
let builder = new MenuItemBuilder(context)
.title(title)
.icon((template && template.icon) || schemaType?.icon || AddIcon)
.intent({type: 'create', params: intentParams})
if (i18n) {
builder = builder.i18n(i18n)
}
return builder.serialize()
})
}