UNPKG

3.98 kBPlain TextView Raw
1import {getExtendedProjection} from './util/getExtendedProjection'
2import {SchemaType, defaultSchema} from './parts/Schema'
3import {getSortIcon} from './parts/Icon'
4import {Intent} from './Intent'
5import {Partial} from './Partial'
6import {SortItem, Ordering, DEFAULT_ORDERING_OPTIONS} from './Sort'
7import {SerializeOptions, Serializable, SerializePath} from './StructureNodes'
8import {SerializeError, HELP_URL} from './SerializeError'
9
10const SortIcon = getSortIcon()
11
12export function maybeSerializeMenuItem(
13 item: MenuItem | MenuItemBuilder,
14 index: number,
15 path: SerializePath
16): MenuItem {
17 return item instanceof MenuItemBuilder ? item.serialize({path, index}) : item
18}
19
20type ShowAsAction = {
21 whenCollapsed: boolean
22}
23
24export interface MenuItem {
25 title: string
26 action?: string | Function
27 intent?: Intent
28 group?: string
29 icon?: Function
30 params?: object
31 showAsAction?: boolean | ShowAsAction
32}
33
34export type PartialMenuItem = Partial<MenuItem>
35
36export class MenuItemBuilder implements Serializable {
37 protected spec: PartialMenuItem
38
39 constructor(spec?: MenuItem) {
40 this.spec = spec ? spec : {}
41 }
42
43 action(action: string | Function): MenuItemBuilder {
44 return this.clone({action})
45 }
46
47 getAction() {
48 return this.spec.action
49 }
50
51 intent(intent: Intent): MenuItemBuilder {
52 return this.clone({intent})
53 }
54
55 getIntent() {
56 return this.spec.intent
57 }
58
59 title(title: string): MenuItemBuilder {
60 return this.clone({title})
61 }
62
63 getTitle() {
64 return this.spec.title
65 }
66
67 group(group: string): MenuItemBuilder {
68 return this.clone({group})
69 }
70
71 getGroup() {
72 return this.spec.group
73 }
74
75 icon(icon: Function): MenuItemBuilder {
76 return this.clone({icon})
77 }
78
79 getIcon() {
80 return this.spec.icon
81 }
82
83 params(params: object): MenuItemBuilder {
84 return this.clone({params})
85 }
86
87 getParams() {
88 return this.spec.params
89 }
90
91 showAsAction(showAsAction: boolean | ShowAsAction): MenuItemBuilder {
92 return this.clone({showAsAction})
93 }
94
95 getShowAsAction() {
96 return this.spec.showAsAction
97 }
98
99 serialize(options: SerializeOptions = {path: []}): MenuItem {
100 const {title, action, intent} = this.spec
101 if (!title) {
102 const hint = typeof action === 'string' ? `action: "${action}"` : undefined
103 throw new SerializeError(
104 '`title` is required for menu item',
105 options.path,
106 options.index,
107 hint
108 ).withHelpUrl(HELP_URL.TITLE_REQUIRED)
109 }
110
111 if (!action && !intent) {
112 throw new SerializeError(
113 `\`action\` or \`intent\` required for menu item with title ${this.spec.title}`,
114 options.path,
115 options.index,
116 `"${title}"`
117 ).withHelpUrl(HELP_URL.ACTION_OR_INTENT_REQUIRED)
118 }
119
120 if (intent && action) {
121 throw new SerializeError(
122 'cannot set both `action` AND `intent`',
123 options.path,
124 options.index,
125 `"${title}"`
126 ).withHelpUrl(HELP_URL.ACTION_AND_INTENT_MUTUALLY_EXCLUSIVE)
127 }
128
129 return {...this.spec, title}
130 }
131
132 clone(withSpec?: PartialMenuItem): MenuItemBuilder {
133 const builder = new MenuItemBuilder()
134 builder.spec = {...this.spec, ...(withSpec || {})}
135 return builder
136 }
137}
138
139export interface SortMenuItem extends MenuItem {
140 params: {
141 by: SortItem[]
142 }
143}
144
145export function getOrderingMenuItem(ordering: Ordering, extendedProjection?: string) {
146 return new MenuItemBuilder()
147 .group('sorting')
148 .title(`Sort by ${ordering.title}`)
149 .icon(SortIcon)
150 .action('setSortOrder')
151 .params({by: ordering.by, extendedProjection})
152}
153
154export function getOrderingMenuItemsForSchemaType(typeName: SchemaType | string) {
155 const type = typeof typeName === 'string' ? defaultSchema.get(typeName) : typeName
156
157 return (type.orderings
158 ? type.orderings.concat(DEFAULT_ORDERING_OPTIONS)
159 : DEFAULT_ORDERING_OPTIONS
160 ).map((ordering: Ordering) =>
161 getOrderingMenuItem(ordering, getExtendedProjection(type, ordering.by))
162 )
163}