1 | import {camelCase} from 'lodash'
|
2 | import {StructureNode, SerializeOptions, Serializable, Child} from './StructureNodes'
|
3 | import {Layout, layoutOptions} from './Layout'
|
4 | import {MenuItem, MenuItemBuilder, maybeSerializeMenuItem} from './MenuItem'
|
5 | import {MenuItemGroup, MenuItemGroupBuilder, maybeSerializeMenuItemGroup} from './MenuItemGroup'
|
6 | import {IntentChecker} from './Intent'
|
7 | import {SerializeError} from './SerializeError'
|
8 |
|
9 | function noChildResolver() {
|
10 | return undefined
|
11 | }
|
12 |
|
13 | export interface ListDisplayOptions {
|
14 | showIcons?: boolean
|
15 | }
|
16 |
|
17 | export interface BaseGenericList extends StructureNode {
|
18 | defaultLayout?: Layout
|
19 | canHandleIntent?: IntentChecker
|
20 | displayOptions?: ListDisplayOptions
|
21 | child: Child
|
22 | }
|
23 |
|
24 |
|
25 | export interface GenericList extends BaseGenericList {
|
26 | type: string
|
27 | menuItems: MenuItem[]
|
28 | menuItemGroups: MenuItemGroup[]
|
29 | }
|
30 |
|
31 |
|
32 | export interface BuildableGenericList extends Partial<BaseGenericList> {
|
33 | menuItems?: (MenuItem | MenuItemBuilder)[]
|
34 | menuItemGroups?: (MenuItemGroup | MenuItemGroupBuilder)[]
|
35 | }
|
36 |
|
37 |
|
38 | export interface GenericListInput extends StructureNode {
|
39 | id: string
|
40 | title: string
|
41 | menuItems?: (MenuItem | MenuItemBuilder)[]
|
42 | menuItemGroups?: (MenuItemGroup | MenuItemGroupBuilder)[]
|
43 | defaultLayout?: Layout
|
44 | canHandleIntent?: IntentChecker
|
45 | child?: Child
|
46 | }
|
47 |
|
48 | export abstract class GenericListBuilder<L extends BuildableGenericList, ConcreteImpl>
|
49 | implements Serializable {
|
50 | protected spec: L = {} as L
|
51 |
|
52 | id(id: string) {
|
53 | return this.clone({id})
|
54 | }
|
55 |
|
56 | getId() {
|
57 | return this.spec.id
|
58 | }
|
59 |
|
60 | title(title: string) {
|
61 | return this.clone({title, id: this.spec.id || camelCase(title)})
|
62 | }
|
63 |
|
64 | getTitle() {
|
65 | return this.spec.title
|
66 | }
|
67 |
|
68 | defaultLayout(defaultLayout: Layout) {
|
69 | return this.clone({defaultLayout})
|
70 | }
|
71 |
|
72 | getDefaultLayout() {
|
73 | return this.spec.defaultLayout
|
74 | }
|
75 |
|
76 | menuItems(menuItems: (MenuItem | MenuItemBuilder)[]) {
|
77 | return this.clone({menuItems})
|
78 | }
|
79 |
|
80 | getMenuItems() {
|
81 | return this.spec.menuItems
|
82 | }
|
83 |
|
84 | menuItemGroups(menuItemGroups: (MenuItemGroup | MenuItemGroupBuilder)[]) {
|
85 | return this.clone({menuItemGroups})
|
86 | }
|
87 |
|
88 | getMenuItemGroups() {
|
89 | return this.spec.menuItemGroups
|
90 | }
|
91 |
|
92 | child(child: Child) {
|
93 | return this.clone({child})
|
94 | }
|
95 |
|
96 | getChild() {
|
97 | return this.spec.child
|
98 | }
|
99 |
|
100 | canHandleIntent(canHandleIntent: IntentChecker) {
|
101 | return this.clone({canHandleIntent})
|
102 | }
|
103 |
|
104 | getCanHandleIntent() {
|
105 | return this.spec.canHandleIntent
|
106 | }
|
107 |
|
108 | showIcons(enabled: boolean) {
|
109 | return this.clone({
|
110 | displayOptions: {...(this.spec.displayOptions || {}), showIcons: enabled}
|
111 | })
|
112 | }
|
113 |
|
114 | getShowIcons() {
|
115 | return this.spec.displayOptions ? this.spec.displayOptions.showIcons : undefined
|
116 | }
|
117 |
|
118 | serialize(options: SerializeOptions = {path: []}): GenericList {
|
119 | const id = this.spec.id || ''
|
120 | const path = options.path
|
121 |
|
122 | const defaultLayout = this.spec.defaultLayout
|
123 | if (defaultLayout && !layoutOptions.includes(defaultLayout)) {
|
124 | throw new SerializeError(
|
125 | `\`layout\` must be one of ${layoutOptions.map(item => `"${item}"`).join(', ')}`,
|
126 | path,
|
127 | id || options.index,
|
128 | this.spec.title
|
129 | )
|
130 | }
|
131 |
|
132 | return {
|
133 | id,
|
134 | title: this.spec.title,
|
135 | type: 'genericList',
|
136 | defaultLayout,
|
137 | child: this.spec.child || noChildResolver,
|
138 | canHandleIntent: this.spec.canHandleIntent,
|
139 | displayOptions: this.spec.displayOptions,
|
140 | menuItems: (this.spec.menuItems || []).map((item, i) =>
|
141 | maybeSerializeMenuItem(item, i, path)
|
142 | ),
|
143 | menuItemGroups: (this.spec.menuItemGroups || []).map((item, i) =>
|
144 | maybeSerializeMenuItemGroup(item, i, path)
|
145 | )
|
146 | }
|
147 | }
|
148 |
|
149 | clone(withSpec?: object) {
|
150 | const builder = new (this.constructor as {new (): ConcreteImpl})()
|
151 | return builder
|
152 | }
|
153 | }
|
154 |
|
\ | No newline at end of file |