UNPKG

4.42 kBPlain TextView Raw
1import {camelCase} from 'lodash'
2import {SerializeOptions, Serializable, Collection, CollectionBuilder} from './StructureNodes'
3import {defaultSchema, SchemaType} from './parts/Schema'
4import {ChildResolver} from './ChildResolver'
5import {DocumentListBuilder} from './DocumentList'
6import {SerializeError, HELP_URL} from './SerializeError'
7import {Partial} from './Partial'
8import {ListBuilder} from './List'
9import {EditorBuilder} from './Editor'
10import {ComponentBuilder} from './Component'
11
12type UnserializedListItemChild = Collection | CollectionBuilder | ChildResolver
13
14type ListItemChild = Collection | ChildResolver | undefined
15
16interface ListItemSerializeOptions extends SerializeOptions {
17 titleIsOptional?: boolean
18}
19
20interface ListItemDisplayOptions {
21 showIcon?: boolean
22}
23
24export interface ListItemInput {
25 id: string
26 title?: string
27 icon?: Function
28 child?: ListItemChild
29 displayOptions?: ListItemDisplayOptions
30 schemaType?: SchemaType | string
31}
32
33export interface ListItem {
34 id: string
35 type: string
36 title?: string
37 icon?: Function
38 child?: ListItemChild
39 displayOptions?: ListItemDisplayOptions
40 schemaType?: SchemaType
41}
42
43export interface UnserializedListItem {
44 id: string
45 title: string
46 icon?: Function
47 child?: UnserializedListItemChild
48 displayOptions?: ListItemDisplayOptions
49 schemaType?: SchemaType | string
50}
51
52type PartialListItem = Partial<UnserializedListItem>
53
54export class ListItemBuilder implements Serializable {
55 protected spec: PartialListItem
56
57 constructor(spec?: ListItemInput) {
58 this.spec = spec ? spec : {}
59 }
60
61 id(id: string) {
62 return this.clone({id})
63 }
64
65 getId() {
66 return this.spec.id
67 }
68
69 title(title: string) {
70 return this.clone({title, id: this.spec.id || camelCase(title)})
71 }
72
73 getTitle() {
74 return this.spec.title
75 }
76
77 icon(icon: Function): ListItemBuilder {
78 return this.clone({icon})
79 }
80
81 showIcon(enabled: boolean) {
82 return this.clone({
83 displayOptions: {...(this.spec.displayOptions || {}), showIcon: enabled}
84 })
85 }
86
87 getShowIcon() {
88 return this.spec.displayOptions ? this.spec.displayOptions.showIcon : undefined
89 }
90
91 getIcon() {
92 return this.spec.icon
93 }
94
95 child(child: UnserializedListItemChild): ListItemBuilder {
96 return this.clone({child})
97 }
98
99 getChild() {
100 return this.spec.child
101 }
102
103 schemaType(schemaType: SchemaType | string): ListItemBuilder {
104 return this.clone({schemaType})
105 }
106
107 getSchemaType() {
108 return this.spec.schemaType
109 }
110
111 serialize(options: ListItemSerializeOptions = {path: []}): ListItem {
112 const {id, title, child} = this.spec
113 if (typeof id !== 'string' || !id) {
114 throw new SerializeError(
115 '`id` is required for list items',
116 options.path,
117 options.index
118 ).withHelpUrl(HELP_URL.ID_REQUIRED)
119 }
120
121 if (!options.titleIsOptional && (typeof title !== 'string' || !title)) {
122 throw new SerializeError('`title` is required for list items', options.path, id).withHelpUrl(
123 HELP_URL.TITLE_REQUIRED
124 )
125 }
126
127 let schemaType = this.spec.schemaType
128 if (typeof schemaType === 'string') {
129 const type: SchemaType = defaultSchema.get(schemaType)
130 if (!type) {
131 throw new SerializeError(
132 `Could not find type "${schemaType}" in schema`,
133 options.path,
134 id
135 ).withHelpUrl(HELP_URL.SCHEMA_TYPE_NOT_FOUND)
136 }
137
138 schemaType = type
139 }
140
141 const serializeOptions = {path: options.path.concat(id), hint: 'child'}
142 let listChild =
143 child instanceof ComponentBuilder ||
144 child instanceof DocumentListBuilder ||
145 child instanceof ListBuilder ||
146 child instanceof EditorBuilder
147 ? child.serialize(serializeOptions)
148 : child
149
150 // In the case of a function, create a bound version that will pass the correct serialize
151 // context, so we may lazily resolve it at some point in the future without losing context
152 if (typeof listChild === 'function') {
153 const originalChild = listChild
154 listChild = (itemId, options) => {
155 return originalChild(itemId, {...options, serializeOptions})
156 }
157 }
158
159 return {...this.spec, schemaType, child: listChild, id, title, type: 'listItem'}
160 }
161
162 clone(withSpec?: PartialListItem): ListItemBuilder {
163 const builder = new ListItemBuilder()
164 builder.spec = {...this.spec, ...(withSpec || {})}
165 return builder
166 }
167}