1 | import {camelCase} from 'lodash'
|
2 | import {SerializeOptions, StructureNode, Serializable} from './StructureNodes'
|
3 | import {SerializeError, HELP_URL} from './SerializeError'
|
4 | import {MenuItem, MenuItemBuilder, maybeSerializeMenuItem} from './MenuItem'
|
5 | import {MenuItemGroup, MenuItemGroupBuilder, maybeSerializeMenuItemGroup} from './MenuItemGroup'
|
6 |
|
7 | export interface Component extends StructureNode {
|
8 | component: Function
|
9 | menuItems: MenuItem[]
|
10 | menuItemGroups: MenuItemGroup[]
|
11 | }
|
12 |
|
13 | export interface ComponentInput extends StructureNode {
|
14 | component: Function
|
15 | menuItems?: (MenuItem | MenuItemBuilder)[]
|
16 | menuItemGroups?: (MenuItemGroup | MenuItemGroupBuilder)[]
|
17 | }
|
18 |
|
19 | export interface BuildableComponent extends Partial<StructureNode> {
|
20 | component?: Function
|
21 | menuItems?: (MenuItem | MenuItemBuilder)[]
|
22 | menuItemGroups?: (MenuItemGroup | MenuItemGroupBuilder)[]
|
23 | }
|
24 |
|
25 | interface ReactComponent extends Function {
|
26 | displayName?: string
|
27 | }
|
28 |
|
29 | function getFunctionName(fn: ReactComponent) {
|
30 | return typeof fn.displayName === 'string' ? fn.displayName : fn.name
|
31 | }
|
32 |
|
33 | export class ComponentBuilder implements Serializable {
|
34 | protected spec: BuildableComponent
|
35 |
|
36 | constructor(spec?: ComponentInput) {
|
37 | this.spec = spec ? spec : {}
|
38 | }
|
39 |
|
40 | id(id: string) {
|
41 | return this.clone({id})
|
42 | }
|
43 |
|
44 | getId() {
|
45 | return this.spec.id
|
46 | }
|
47 |
|
48 | title(title: string) {
|
49 | return this.clone({title, id: this.spec.id || camelCase(title)})
|
50 | }
|
51 |
|
52 | getTitle() {
|
53 | return this.spec.title
|
54 | }
|
55 |
|
56 | component(component: Function) {
|
57 | return this.clone({component, id: this.spec.id || getFunctionName(component)})
|
58 | }
|
59 |
|
60 | getComponent() {
|
61 | return this.spec.component
|
62 | }
|
63 |
|
64 | menuItems(menuItems: (MenuItem | MenuItemBuilder)[]) {
|
65 | return this.clone({menuItems})
|
66 | }
|
67 |
|
68 | getMenuItems() {
|
69 | return this.spec.menuItems
|
70 | }
|
71 |
|
72 | menuItemGroups(menuItemGroups: (MenuItemGroup | MenuItemGroupBuilder)[]) {
|
73 | return this.clone({menuItemGroups})
|
74 | }
|
75 |
|
76 | getMenuItemGroups() {
|
77 | return this.spec.menuItemGroups
|
78 | }
|
79 |
|
80 | serialize(options: SerializeOptions = {path: []}): Component {
|
81 | const {id, title, component} = this.spec
|
82 | if (!id) {
|
83 | throw new SerializeError(
|
84 | '`id` is required for `component` structure item',
|
85 | options.path,
|
86 | options.index
|
87 | ).withHelpUrl(HELP_URL.ID_REQUIRED)
|
88 | }
|
89 |
|
90 | if (!component) {
|
91 | throw new SerializeError(
|
92 | '`component` is required for `component` structure item',
|
93 | options.path,
|
94 | options.index
|
95 | ).withHelpUrl(HELP_URL.ID_REQUIRED)
|
96 | }
|
97 |
|
98 | return {
|
99 | id,
|
100 | title,
|
101 | type: 'component',
|
102 | component,
|
103 | menuItems: (this.spec.menuItems || []).map((item, i) =>
|
104 | maybeSerializeMenuItem(item, i, options.path)
|
105 | ),
|
106 | menuItemGroups: (this.spec.menuItemGroups || []).map((item, i) =>
|
107 | maybeSerializeMenuItemGroup(item, i, options.path)
|
108 | )
|
109 | }
|
110 | }
|
111 |
|
112 | clone(withSpec?: BuildableComponent): ComponentBuilder {
|
113 | const builder = new ComponentBuilder()
|
114 | builder.spec = {...this.spec, ...(withSpec || {})}
|
115 | return builder
|
116 | }
|
117 | }
|
118 |
|
\ | No newline at end of file |