1 | import {camelCase} from 'lodash'
|
2 | import {SerializeOptions, Serializable, Collection, CollectionBuilder} from './StructureNodes'
|
3 | import {defaultSchema, SchemaType} from './parts/Schema'
|
4 | import {ChildResolver} from './ChildResolver'
|
5 | import {DocumentListBuilder} from './DocumentList'
|
6 | import {SerializeError, HELP_URL} from './SerializeError'
|
7 | import {Partial} from './Partial'
|
8 | import {ListBuilder} from './List'
|
9 | import {EditorBuilder} from './Editor'
|
10 | import {ComponentBuilder} from './Component'
|
11 |
|
12 | type UnserializedListItemChild = Collection | CollectionBuilder | ChildResolver
|
13 |
|
14 | type ListItemChild = Collection | ChildResolver | undefined
|
15 |
|
16 | interface ListItemSerializeOptions extends SerializeOptions {
|
17 | titleIsOptional?: boolean
|
18 | }
|
19 |
|
20 | interface ListItemDisplayOptions {
|
21 | showIcon?: boolean
|
22 | }
|
23 |
|
24 | export interface ListItemInput {
|
25 | id: string
|
26 | title?: string
|
27 | icon?: Function
|
28 | child?: ListItemChild
|
29 | displayOptions?: ListItemDisplayOptions
|
30 | schemaType?: SchemaType | string
|
31 | }
|
32 |
|
33 | export 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 |
|
43 | export interface UnserializedListItem {
|
44 | id: string
|
45 | title: string
|
46 | icon?: Function
|
47 | child?: UnserializedListItemChild
|
48 | displayOptions?: ListItemDisplayOptions
|
49 | schemaType?: SchemaType | string
|
50 | }
|
51 |
|
52 | type PartialListItem = Partial<UnserializedListItem>
|
53 |
|
54 | export 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 |
|
151 |
|
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 | }
|