@things-factory/lite-menu 8.0.0-beta.1 → 8.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/lite-menu",
3
- "version": "8.0.0-beta.1",
3
+ "version": "8.0.0-beta.4",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -40,14 +40,14 @@
40
40
  "@operato/shell": "^8.0.0-beta",
41
41
  "@operato/styles": "^8.0.0-beta",
42
42
  "@operato/utils": "^8.0.0-beta",
43
- "@things-factory/apptool-base": "^8.0.0-beta.1",
44
- "@things-factory/auth-base": "^8.0.0-beta.1",
45
- "@things-factory/board-service": "^8.0.0-beta.1",
46
- "@things-factory/board-ui": "^8.0.0-beta.1",
47
- "@things-factory/more-base": "^8.0.0-beta.1",
48
- "@things-factory/personalization": "^8.0.0-beta.1",
49
- "@things-factory/setting-base": "^8.0.0-beta.1",
50
- "@things-factory/utils": "^8.0.0-beta.1"
43
+ "@things-factory/apptool-base": "^8.0.0-beta.4",
44
+ "@things-factory/auth-base": "^8.0.0-beta.4",
45
+ "@things-factory/board-service": "^8.0.0-beta.4",
46
+ "@things-factory/board-ui": "^8.0.0-beta.4",
47
+ "@things-factory/more-base": "^8.0.0-beta.4",
48
+ "@things-factory/personalization": "^8.0.0-beta.4",
49
+ "@things-factory/setting-base": "^8.0.0-beta.4",
50
+ "@things-factory/utils": "^8.0.0-beta.4"
51
51
  },
52
- "gitHead": "36c494e587640c1490318ef7b95adab02606e0c2"
52
+ "gitHead": "d83d12ed4ba07177dff1dac26e37be347d156b43"
53
53
  }
@@ -1,146 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/menu/ox-menu-part.js'
3
-
4
- import gql from 'graphql-tag'
5
- import { html } from 'lit'
6
-
7
- import { store, InheritedValueType, clientSettingStore } from '@operato/shell'
8
- import { client } from '@operato/graphql'
9
- import { appendViewpart, toggleOverlay, TOOL_POSITION, VIEWPART_POSITION } from '@operato/layout'
10
- import { isMobileDevice } from '@operato/utils'
11
-
12
- import { APPEND_APP_TOOL, REMOVE_APP_TOOL } from '@things-factory/apptool-base/client'
13
-
14
- export const UPDATE_ADDON_MENUS = 'UPDATE_ADDON_MENUS'
15
- export const UPDATE_MENU_TEMPLATE = 'UPDATE_MENU_TEMPLATE'
16
-
17
- var HAMBURGER
18
-
19
- export async function setupMenuPart(options) {
20
- var {
21
- hovering = isMobileDevice() ? true : false,
22
- slotTemplate,
23
- portraitSlotTemplate,
24
- landscapeSlotTemplate,
25
- position = VIEWPART_POSITION.NAVBAR
26
- } = options || {}
27
-
28
- const { hovering: hoveringSetting } = (await clientSettingStore.get('lite-menu'))?.value || {}
29
- if (hoveringSetting !== undefined) {
30
- hovering = hoveringSetting
31
- }
32
-
33
- const orientation = position == VIEWPART_POSITION.HEADERBAR ? 'landscape' : 'portrait'
34
- const orientatedSlotTemplate =
35
- (orientation == 'landscape' ? landscapeSlotTemplate : portraitSlotTemplate) || slotTemplate || html``
36
-
37
- appendViewpart({
38
- name: 'ox-menu-part',
39
- viewpart: {
40
- show: !hovering,
41
- resizable: true,
42
- hovering: isMobileDevice() ? true : hovering,
43
- template: html` <ox-menu-part .orientation=${orientation}>${orientatedSlotTemplate}</ox-menu-part> `
44
- },
45
- position
46
- })
47
-
48
- // hovering이 true거나 모바일 일때는 계속 햄버거 버튼 표시
49
- if ((hovering || isMobileDevice()) && orientation == 'portrait') {
50
- if (!HAMBURGER) {
51
- HAMBURGER = {
52
- name: 'hamburger',
53
- template: html`
54
- <md-icon
55
- @click=${e =>
56
- toggleOverlay('ox-menu-part', {
57
- backdrop: true
58
- })}
59
- >view_headline</md-icon
60
- >
61
- `,
62
- position: TOOL_POSITION.FRONT_END
63
- }
64
-
65
- store.dispatch({
66
- /* incase of mobile : add hamburger tool */
67
- type: APPEND_APP_TOOL,
68
- tool: HAMBURGER
69
- })
70
- }
71
- } else {
72
- if (HAMBURGER) {
73
- store.dispatch({
74
- type: REMOVE_APP_TOOL,
75
- name: 'hamburger'
76
- })
77
-
78
- HAMBURGER = null
79
- }
80
- }
81
- }
82
-
83
- export async function updateMenuTemplate(template) {
84
- await fetchAddonMenus()
85
-
86
- store.dispatch({
87
- type: UPDATE_MENU_TEMPLATE,
88
- template
89
- })
90
- }
91
-
92
- export async function fetchAddonMenus() {
93
- var applicationName = (document.querySelector('meta[name="application-name"]') as HTMLMetaElement)?.content
94
-
95
- var liteMenus = (
96
- await client.query({
97
- query: gql`
98
- query ($filters: [Filter!], $inherited: InheritedValueType) {
99
- liteMenus: myLiteMenus(filters: $filters, inherited: $inherited) {
100
- items {
101
- id
102
- name
103
- label
104
- description
105
- appName
106
- parent
107
- rank
108
- active
109
- type
110
- value
111
- icon
112
- help
113
- }
114
- total
115
- }
116
- }
117
- `,
118
- variables: {
119
- filters: [
120
- {
121
- name: 'active',
122
- operator: 'eq',
123
- value: true
124
- },
125
- {
126
- name: 'appName',
127
- operator: 'in',
128
- value: ['', applicationName]
129
- }
130
- ],
131
- sortings: [
132
- {
133
- name: 'rank',
134
- desc: false
135
- }
136
- ],
137
- inherited: InheritedValueType.Include
138
- }
139
- })
140
- ).data.liteMenus.items
141
-
142
- store.dispatch({
143
- type: UPDATE_ADDON_MENUS,
144
- addon: liteMenus.filter(menu => !menu.appName || menu.appName === applicationName)
145
- })
146
- }
@@ -1,16 +0,0 @@
1
- import '@material/web/icon/icon.js'
2
- import '@operato/i18n/ox-i18n.js'
3
- import './route'
4
-
5
- import { store } from '@operato/shell'
6
-
7
- import { registerEditor as registerGristEditor } from '@operato/data-grist'
8
- import { OxGristEditorI18nLabel } from '@operato/grist-editor/ox-grist-editor-i18n-label.js'
9
-
10
- import liteMenu from './reducers/lite-menu'
11
-
12
- export default function bootstrap() {
13
- registerGristEditor('i18n-label', OxGristEditorI18nLabel)
14
-
15
- store.addReducers({ liteMenu })
16
- }
package/client/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './actions/lite-menu'
2
- export * from './lite-menu-setting-let'
@@ -1,86 +0,0 @@
1
- import '@operato/i18n/ox-i18n.js'
2
- import '@material/web/checkbox/checkbox.js'
3
-
4
- import { css, html, LitElement } from 'lit'
5
- import { customElement, query, property, state } from 'lit/decorators.js'
6
-
7
- import { clientSettingStore } from '@operato/shell'
8
- import { i18next, localize } from '@operato/i18n'
9
-
10
- import { setupMenuPart } from './actions/lite-menu'
11
-
12
- @customElement('lite-menu-setting-let')
13
- export class LiteMenuSettingLet extends localize(i18next)(LitElement) {
14
- static styles = [
15
- css`
16
- label {
17
- display: flex;
18
- gap: 10px;
19
- align-items: center;
20
-
21
- font: var(--label-font);
22
- color: var(--md-sys-color-on-primary-container);
23
- text-transform: var(--label-text-transform);
24
- }
25
- `
26
- ]
27
-
28
- @property({ type: Boolean }) hovering?: boolean
29
-
30
- render() {
31
- const checked = this.hovering === true
32
- const indeterminate = this.hovering === undefined
33
-
34
- return html`
35
- <setting-let>
36
- <ox-i18n slot="title" msgid="title.lite-menu-setting"></ox-i18n>
37
-
38
- <div slot="content">
39
- <label>
40
- <md-checkbox
41
- id="hovering"
42
- @change=${e => this.onChange(e)}
43
- ?checked=${checked}
44
- ?indeterminate=${indeterminate}
45
- ></md-checkbox>
46
- hovering
47
- </label>
48
- </div>
49
- </setting-let>
50
- `
51
- }
52
-
53
- async firstUpdated() {
54
- this.hovering = ((await clientSettingStore.get('lite-menu'))?.value || {}).hovering
55
- }
56
-
57
- async onChange(e: Event) {
58
- if (this.hovering === true) {
59
- this.hovering = false
60
- } else if (this.hovering === false) {
61
- this.hovering = undefined
62
- } else {
63
- this.hovering = true
64
- }
65
-
66
- const { hovering: valueFromStore } = (await clientSettingStore.get('lite-menu'))?.value || {}
67
- if (this.hovering === valueFromStore) {
68
- return
69
- }
70
-
71
- try {
72
- await clientSettingStore.put({
73
- key: 'lite-menu',
74
- value: {
75
- hovering: this.hovering
76
- }
77
- })
78
- } catch (e) {
79
- console.error(e)
80
- }
81
-
82
- setupMenuPart({
83
- hovering: this.hovering
84
- })
85
- }
86
- }
@@ -1,558 +0,0 @@
1
- import '@operato/data-grist/ox-grist.js'
2
- import '@operato/data-grist/ox-filters-form.js'
3
- import '@operato/data-grist/ox-sorters-control.js'
4
- import '@operato/data-grist/ox-record-creator.js'
5
- import '@operato/popup/ox-popup.js'
6
- import '@operato/context/ox-context-page-toolbar.js'
7
-
8
- import gql from 'graphql-tag'
9
- import { css, html } from 'lit'
10
- import { customElement, property, query, state } from 'lit/decorators.js'
11
- import { connect } from 'pwa-helpers/connect-mixin.js'
12
-
13
- import { DataGrist, FetchOption, getEditor, getRenderer } from '@operato/data-grist'
14
- import { client } from '@operato/graphql'
15
- import { i18next, localize } from '@operato/i18n'
16
- import { PageView, store } from '@operato/shell'
17
- import { CommonGristStyles, ScrollbarStyles, CommonHeaderStyles } from '@operato/styles'
18
- import { isMobileDevice } from '@operato/utils'
19
-
20
- import { getLanguages } from '@things-factory/auth-base/dist-client'
21
- import { p13n } from '@operato/p13n'
22
-
23
- import { fetchAddonMenus } from '../actions/lite-menu'
24
-
25
- const applicationName = (document.querySelector('meta[name="application-name"]') as HTMLMetaElement)?.content
26
-
27
- @customElement('addon-menu-setting')
28
- class AddonMenuSetting extends connect(store)(p13n(localize(i18next)(PageView))) {
29
- static styles = [
30
- ScrollbarStyles,
31
- CommonGristStyles,
32
- CommonHeaderStyles,
33
- css`
34
- :host {
35
- display: flex;
36
- overflow-x: hidden;
37
- flex-direction: column;
38
-
39
- width: 100%;
40
- }
41
-
42
- .header {
43
- grid-template-areas: 'filters actions';
44
- }
45
-
46
- ox-grist {
47
- overflow-y: auto;
48
- flex: 1;
49
- }
50
- `
51
- ]
52
-
53
- @state() config: any
54
- @state() mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
55
-
56
- @state() menus: any[] = []
57
-
58
- @query('ox-grist') private grist!: DataGrist
59
-
60
- get context() {
61
- return {
62
- title: i18next.t('text.addon-menu management'),
63
- search: {
64
- handler: (search: string) => {
65
- this.grist.searchText = search
66
- },
67
- value: this.grist?.searchText || ''
68
- },
69
- filter: {
70
- handler: () => {
71
- this.grist.toggleHeadroom()
72
- }
73
- },
74
- actions: [
75
- {
76
- title: i18next.t('button.refresh'),
77
- action: () => {
78
- this.onReload()
79
- },
80
- icon: 'refresh'
81
- },
82
- {
83
- title: i18next.t('button.delete'),
84
- action: () => {
85
- this.onDelete()
86
- },
87
- icon: 'delete',
88
- emphasis: {
89
- danger: true
90
- }
91
- },
92
- {
93
- title: i18next.t('button.save'),
94
- action: () => {
95
- this.onCommit()
96
- },
97
- icon: 'save'
98
- }
99
- ],
100
- exportable: {
101
- accept: ['json'],
102
- name: 'lite-menu-list',
103
- data: () => {
104
- return this.grist.data
105
- }
106
- },
107
- toolbar: false
108
- }
109
- }
110
-
111
- render() {
112
- return html`
113
- <ox-grist
114
- .mode=${this.mode}
115
- .config=${this.config}
116
- .fetchHandler=${this.fetchHandler}
117
- .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}
118
- >
119
- <div slot="headroom" class="header">
120
- <div class="filters">
121
- <ox-filters-form autofocus without-search></ox-filters-form>
122
- </div>
123
- <ox-context-page-toolbar class="actions" .context=${this.context}> </ox-context-page-toolbar>
124
- </div>
125
-
126
- <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
127
- </ox-grist>
128
- `
129
- }
130
-
131
- stateChanged(state) {
132
- this.menus = state.liteMenu?.menus
133
- }
134
-
135
- async onCommit() {
136
- var grist = this.grist
137
-
138
- var modifiedList = grist.dirtyRecords.filter(record => record['__dirty__'] == 'M')
139
- var addedList = grist.dirtyRecords.filter(record => record['__dirty__'] == '+')
140
-
141
- await Promise.all(
142
- modifiedList.map(async record => {
143
- var id = record.__origin__.id
144
- var patch = {
145
- name: record.name,
146
- description: record.description,
147
- appName: record.appName,
148
- parent: record.parent,
149
- rank: record.rank,
150
- active: record.active,
151
- role: record.role,
152
- labels: record.labels,
153
- type: record.type,
154
- value: record.value,
155
- icon: record.icon,
156
- help: record.help
157
- }
158
-
159
- if (record.type == 'group') {
160
- patch = {
161
- ...patch,
162
- parent: ''
163
- }
164
- }
165
-
166
- return await client.mutate({
167
- mutation: gql`
168
- mutation ($id: String!, $patch: LiteMenuPatch!) {
169
- updateLiteMenu(id: $id, patch: $patch) {
170
- id
171
- name
172
- }
173
- }
174
- `,
175
- variables: {
176
- id,
177
- patch: patch
178
- }
179
- })
180
- })
181
- )
182
-
183
- await Promise.all(
184
- addedList.map(async record => {
185
- var liteMenu = {
186
- name: record.name,
187
- description: record.description,
188
- appName: record.appName,
189
- parent: record.parent,
190
- rank: record.rank,
191
- active: record.active,
192
- role: record.role,
193
- type: record.type,
194
- value: record.value,
195
- icon: record.icon,
196
- help: record.help
197
- }
198
-
199
- return await client.mutate({
200
- mutation: gql`
201
- mutation ($liteMenu: NewLiteMenu!) {
202
- createLiteMenu(liteMenu: $liteMenu) {
203
- id
204
- name
205
- }
206
- }
207
- `,
208
- variables: {
209
- liteMenu
210
- }
211
- })
212
- })
213
- )
214
-
215
- grist.fetch()
216
- fetchAddonMenus() /* update menu-system */
217
- }
218
-
219
- onReload() {
220
- this.grist.fetch()
221
- }
222
-
223
- async onDelete() {
224
- var grist = this.grist
225
-
226
- var deletedList = grist.selected
227
-
228
- await Promise.all(
229
- deletedList.map(async record => {
230
- var id = record.id
231
-
232
- return await client.mutate({
233
- mutation: gql`
234
- mutation ($id: String!) {
235
- deleteLiteMenu(id: $id)
236
- }
237
- `,
238
- variables: {
239
- id
240
- }
241
- })
242
- })
243
- )
244
-
245
- grist.fetch()
246
- fetchAddonMenus() /* update menu-system */
247
- }
248
-
249
- async pageInitialized(lifecycle) {
250
- this.config = {
251
- list: {
252
- thumbnail: 'value',
253
- fields: ['name', 'description'],
254
- details: ['type', 'parent', 'rank', 'updatedAt']
255
- },
256
- columns: [
257
- {
258
- type: 'gutter',
259
- gutterName: 'dirty',
260
- fixed: true
261
- },
262
- {
263
- type: 'gutter',
264
- gutterName: 'sequence',
265
- fixed: true
266
- },
267
- {
268
- type: 'gutter',
269
- gutterName: 'row-selector',
270
- multiple: true,
271
- fixed: true
272
- },
273
- {
274
- type: 'string',
275
- name: 'id',
276
- hidden: true
277
- },
278
- {
279
- type: 'string',
280
- name: 'name',
281
- header: i18next.t('field.name'),
282
- fixed: true,
283
- record: {
284
- editable: true
285
- },
286
- filter: 'search',
287
- sortable: true,
288
- width: 120
289
- },
290
- {
291
- type: 'i18n-label',
292
- name: 'labels',
293
- record: {
294
- align: 'left',
295
- editable: true,
296
- renderer: (value, column, record, rowIndex, field) =>
297
- html`<span>${value && typeof value == 'object' ? value[i18next.language] : record['name']}</span>`,
298
- options: {
299
- objectified: true,
300
- languages: await getLanguages(),
301
- displayColumn: 'label'
302
- }
303
- },
304
- header: i18next.t('field.i18n-label'),
305
- sortable: true,
306
- filter: 'search',
307
- width: 120
308
- },
309
- {
310
- type: 'string',
311
- name: 'description',
312
- header: i18next.t('field.description'),
313
- record: {
314
- align: 'left',
315
- editable: true
316
- },
317
- filter: 'search',
318
- width: 200
319
- },
320
- {
321
- type: 'select',
322
- name: 'appName',
323
- header: i18next.t('field.app-name'),
324
- label: true,
325
- record: {
326
- align: 'left',
327
- editable: true,
328
- options: ['', applicationName]
329
- },
330
- filter: {
331
- options: ['', applicationName],
332
- multiple: false,
333
- operator: 'eq'
334
- },
335
- width: 200
336
- },
337
- {
338
- type: 'select',
339
- name: 'parent',
340
- label: true,
341
- header: i18next.t('field.parent-menu'),
342
- record: {
343
- editable: true,
344
- options: () => {
345
- const menus = this.menus.filter(menu => menu.type !== 'group')
346
- return ['', ...menus.map(menu => menu.name)]
347
- }
348
- },
349
- sortable: true,
350
- width: 120
351
- },
352
- {
353
- type: 'number',
354
- name: 'rank',
355
- label: true,
356
- header: i18next.t('field.rank'),
357
- record: {
358
- align: 'right',
359
- editable: true
360
- },
361
- sortable: true,
362
- width: 60
363
- },
364
- {
365
- type: 'select',
366
- name: 'type',
367
- label: true,
368
- header: i18next.t('field.type'),
369
- record: {
370
- editable: true,
371
- options: ['', 'group', 'page', 'board', 'interactive-board']
372
- },
373
- filter: {
374
- options: ['', 'group', 'page', 'board', 'interactive-board'],
375
- multiple: false,
376
- operator: 'eq'
377
- },
378
- width: 80
379
- },
380
- {
381
- type: 'string',
382
- name: 'value',
383
- header: i18next.t('field.value'),
384
- record: {
385
- editable: true,
386
- editor: function (value, column, record, rowIndex, field) {
387
- var type = record.type !== 'board' && record.type !== 'interactive-board' ? 'string' : 'board'
388
- return getEditor(type)(value, column, record, rowIndex, field)
389
- },
390
- renderer: function (value, column, record, rowIndex, field) {
391
- var type = record.type !== 'board' && record.type !== 'interactive-board' ? 'string' : 'board'
392
- return getRenderer(type)(value, column, record, rowIndex, field)
393
- }
394
- },
395
- width: 140
396
- },
397
- {
398
- type: 'string',
399
- name: 'icon',
400
- header: i18next.t('field.icon'),
401
- record: {
402
- editable: true
403
- },
404
- sortable: false,
405
- width: 120
406
- },
407
- {
408
- type: 'boolean',
409
- name: 'active',
410
- label: true,
411
- header: i18next.t('field.active'),
412
- record: {
413
- editable: true
414
- },
415
- filter: true,
416
- sortable: true,
417
- width: 60
418
- },
419
- {
420
- type: 'string',
421
- name: 'help',
422
- label: true,
423
- header: i18next.t('field.help'),
424
- record: {
425
- editable: true
426
- },
427
- width: 200
428
- },
429
- {
430
- type: 'resource-object',
431
- name: 'role',
432
- label: true,
433
- header: i18next.t('field.required role'),
434
- record: {
435
- editable: true,
436
- options: {
437
- title: i18next.t('title.lookup role'),
438
- queryName: 'roles'
439
- }
440
- },
441
- width: 200
442
- },
443
- {
444
- type: 'datetime',
445
- name: 'updatedAt',
446
- header: i18next.t('field.updated_at'),
447
- width: 180
448
- },
449
- {
450
- type: 'datetime',
451
- name: 'createdAt',
452
- header: i18next.t('field.created_at'),
453
- width: 180
454
- }
455
- ],
456
- rows: {
457
- selectable: {
458
- multiple: true
459
- },
460
- handlers: {
461
- click: 'select-row-toggle'
462
- }
463
- },
464
- sorters: [
465
- {
466
- name: 'parent',
467
- desc: false
468
- },
469
- {
470
- name: 'rank',
471
- desc: false
472
- }
473
- ],
474
- pagination: {
475
- pages: [20, 30, 50, 100, 200]
476
- }
477
- }
478
- }
479
-
480
- async pageUpdated(changes, lifecycle) {
481
- if (this.active) {
482
- await this.updateComplete
483
-
484
- this.grist.fetch()
485
- }
486
- }
487
-
488
- async fetchHandler({ filters = [], page, limit, sortings = [] }: FetchOption) {
489
- const response = (
490
- await client.query({
491
- query: gql`
492
- query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
493
- responses: liteMenus(filters: $filters, pagination: $pagination, sortings: $sortings) {
494
- items {
495
- id
496
- name
497
- label
498
- description
499
- help
500
- appName
501
- parent
502
- rank
503
- type
504
- value
505
- board {
506
- id
507
- name
508
- description
509
- thumbnail
510
- }
511
- icon
512
- active
513
- role {
514
- id
515
- name
516
- description
517
- }
518
- labels
519
- createdAt
520
- updatedAt
521
- creator {
522
- id
523
- name
524
- }
525
- updater {
526
- id
527
- name
528
- }
529
- }
530
- total
531
- }
532
- }
533
- `,
534
- variables: {
535
- filters: [
536
- ...filters,
537
- {
538
- name: 'appName',
539
- operator: 'in',
540
- value: ['', applicationName]
541
- }
542
- ],
543
- pagination: { page, limit },
544
- sortings
545
- }
546
- })
547
- ).data.responses
548
-
549
- return {
550
- records: response.items,
551
- total: response.total
552
- }
553
- }
554
-
555
- liteMenuCreationCallback(liteMenu) {
556
- console.warn('not implemented yet.')
557
- }
558
- }
@@ -1,83 +0,0 @@
1
- import cloneDeep from 'lodash-es/cloneDeep'
2
-
3
- const INITIAL_STATE = {
4
- template: [],
5
- addon: [],
6
- menus: []
7
- }
8
-
9
- function buildMenus(template, addon) {
10
- var menus = cloneDeep(template || [])
11
- addon = addon || []
12
-
13
- /* make group by parent */
14
- const groups =
15
- addon
16
- .filter(menu => menu.active)
17
- .sort((menu1, menu2) => (menu1.parent || '').localeCompare(menu2.parent || '') || menu1.rank - menu2.rank)
18
- .reduce((arr, menu) => {
19
- let lastparent = arr.length > 0 && arr[arr.length - 1][0].parent
20
-
21
- if (!lastparent || lastparent !== menu.parent) {
22
- arr.push([menu])
23
- } else {
24
- arr[arr.length - 1].push(menu)
25
- }
26
-
27
- return arr
28
- }, []) || []
29
-
30
- /* iterate group to put menu into parent menu */
31
- groups.forEach(group => {
32
- group.forEach(menu => {
33
- const { parent, name, rank, label, type, value, icon, help } = menu
34
-
35
- const parentMenu = parent && menus.find(m => m.name === parent)
36
- if (parentMenu && !parentMenu.menus) {
37
- parentMenu.menus = []
38
- }
39
- const children = parentMenu ? parentMenu.menus : menus
40
- children.push({
41
- name,
42
- label,
43
- rank,
44
- type,
45
- path:
46
- type === 'page'
47
- ? value
48
- : type === 'board'
49
- ? `board-viewer/${value ?? ''}?title=${label || name}${help ? '&help=' + help : ''}`
50
- : type === 'interactive-board'
51
- ? `board-viewer/${value ?? ''}?interactive=true&title=${label || name}${help ? '&help=' + help : ''}`
52
- : '',
53
- icon: icon || (type === 'page' ? 'menu' : type === 'board' || type === 'interactive-board' ? 'dashboard' : ''),
54
- help
55
- })
56
- })
57
- })
58
-
59
- return menus
60
- }
61
-
62
- const liteMenu = (state = INITIAL_STATE, action) => {
63
- switch (action.type) {
64
- case 'UPDATE_MENU_TEMPLATE':
65
- return {
66
- ...state,
67
- template: action.template,
68
- menus: buildMenus(action.template, state.addon)
69
- }
70
-
71
- case 'UPDATE_ADDON_MENUS':
72
- return {
73
- ...state,
74
- addon: action.addon,
75
- menus: buildMenus(state.template, action.addon)
76
- }
77
-
78
- default:
79
- return state
80
- }
81
- }
82
-
83
- export default liteMenu
package/client/route.ts DELETED
@@ -1,7 +0,0 @@
1
- export default function route(page) {
2
- switch (page) {
3
- case 'addon-menu-setting':
4
- import('./pages/addon-menu-setting')
5
- return page
6
- }
7
- }
@@ -1,13 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "experimentalDecorators": true,
5
- "skipLibCheck": true,
6
- "strict": true,
7
- "declaration": true,
8
- "module": "esnext",
9
- "outDir": "../dist-client",
10
- "baseUrl": "./"
11
- },
12
- "include": ["./**/*"]
13
- }
package/server/index.ts DELETED
@@ -1 +0,0 @@
1
- export * from './service'
@@ -1,18 +0,0 @@
1
- /* EXPORT ENTITY TYPES */
2
- export * from './lite-menu/lite-menu'
3
-
4
- /* IMPORT ENTITIES AND RESOLVERS */
5
- import { entities as LiteMenuEntities, resolvers as LiteMenuResolvers } from './lite-menu'
6
-
7
- export const entities = [
8
- /* ENTITIES */
9
- ...LiteMenuEntities,
10
- ]
11
-
12
-
13
- export const schema = {
14
- resolverClasses: [
15
- /* RESOLVER CLASSES */
16
- ...LiteMenuResolvers,
17
- ]
18
- }
@@ -1,6 +0,0 @@
1
- import { LiteMenu } from './lite-menu'
2
- import { LiteMenuQuery } from './lite-menu-query'
3
- import { LiteMenuMutation } from './lite-menu-mutation'
4
-
5
- export const entities = [LiteMenu]
6
- export const resolvers = [LiteMenuQuery, LiteMenuMutation]
@@ -1,54 +0,0 @@
1
- import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
2
-
3
- import { getRepository } from '@things-factory/shell'
4
-
5
- import { LiteMenu } from './lite-menu'
6
- import { LiteMenuPatch, NewLiteMenu } from './lite-menu-type'
7
-
8
- @Resolver(LiteMenu)
9
- export class LiteMenuMutation {
10
- @Directive('@privilege(category: "menu", privilege: "mutation", domainOwnerGranted: true)')
11
- @Mutation(returns => LiteMenu, { description: 'To create new LiteMenu' })
12
- async createLiteMenu(@Arg('liteMenu') liteMenu: NewLiteMenu, @Ctx() context: ResolverContext): Promise<LiteMenu> {
13
- const { domain, user } = context.state
14
-
15
- return await getRepository(LiteMenu).save({
16
- domain,
17
- creator: user,
18
- updater: user,
19
- ...liteMenu
20
- })
21
- }
22
-
23
- @Directive('@privilege(category: "menu", privilege: "mutation", domainOwnerGranted: true)')
24
- @Mutation(returns => LiteMenu, { description: 'To modify LiteMenu information' })
25
- async updateLiteMenu(
26
- @Arg('id') id: string,
27
- @Arg('patch') patch: LiteMenuPatch,
28
- @Ctx() context: ResolverContext
29
- ): Promise<LiteMenu> {
30
- const { domain, user } = context.state
31
-
32
- const repository = getRepository(LiteMenu)
33
- const liteMenu = await repository.findOne({
34
- where: { domain: { id: domain.id }, id }
35
- })
36
-
37
- return await repository.save({
38
- domain,
39
- creater: user,
40
- ...liteMenu,
41
- ...patch,
42
- updater: user
43
- })
44
- }
45
-
46
- @Directive('@privilege(category: "menu", privilege: "mutation", domainOwnerGranted: true)')
47
- @Mutation(returns => Boolean, { description: 'To delete LiteMenu' })
48
- async deleteLiteMenu(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
49
- const { domain } = context.state
50
-
51
- await getRepository(LiteMenu).delete({ domain: { id: domain.id }, id })
52
- return true
53
- }
54
- }
@@ -1,110 +0,0 @@
1
- import { In, Brackets } from 'typeorm'
2
- import { Arg, Args, Ctx, FieldResolver, Query, Resolver, Root } from 'type-graphql'
3
-
4
- import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
5
- import { User, Role } from '@things-factory/auth-base'
6
- import { Board } from '@things-factory/board-service'
7
-
8
- import { LiteMenu } from './lite-menu'
9
- import { LiteMenuList } from './lite-menu-type'
10
-
11
- @Resolver(LiteMenu)
12
- export class LiteMenuQuery {
13
- @Query(returns => LiteMenu, { description: 'To fetch a LiteMenu' })
14
- async liteMenu(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<LiteMenu> {
15
- const { domain } = context.state
16
- const repository = getRepository(LiteMenu)
17
-
18
- var liteMenu: any = repository.findOne({
19
- where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },
20
- relations: ['domain', 'creator', 'updater']
21
- })
22
-
23
- return liteMenu
24
- }
25
-
26
- @Query(returns => LiteMenuList, { description: 'To fetch multiple LiteMenus' })
27
- async liteMenus(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<LiteMenuList> {
28
- const { domain } = context.state
29
-
30
- const queryBuilder = getQueryBuilderFromListParams({
31
- repository: getRepository(LiteMenu),
32
- params,
33
- domain,
34
- searchables: ['name', 'description']
35
- })
36
- var [items, total] = await queryBuilder.getManyAndCount()
37
-
38
- return { items, total }
39
- }
40
-
41
- @Query(returns => LiteMenuList, { description: 'To fetch my own LiteMenus' })
42
- async myLiteMenus(
43
- @Args(type => ListParam) params: ListParam,
44
- @Ctx() context: ResolverContext
45
- ): Promise<LiteMenuList> {
46
- const { domain, user } = context.state
47
-
48
- const me = await getRepository(User).findOne({
49
- where: { id: user.id },
50
- relations: ['roles']
51
- })
52
-
53
- const roles = me.roles
54
- .filter(role => role.domainId === domain.id || (domain.parentId && role.domainId === domain.parentId))
55
- .map(role => role.id)
56
-
57
- const [items, total] = await getQueryBuilderFromListParams({
58
- repository: getRepository(LiteMenu),
59
- params,
60
- domain,
61
- alias: 'menu',
62
- searchables: ['name', 'description']
63
- })
64
- .andWhere('menu.active = :active', { active: true })
65
- .andWhere(
66
- new Brackets(qb => {
67
- qb.where('menu.role IN (:...roles)').orWhere('menu.role is null')
68
- })
69
- )
70
- .setParameter('roles', roles)
71
- .getManyAndCount()
72
-
73
- return { items, total }
74
- }
75
-
76
- @FieldResolver(type => String)
77
- async label(@Root() liteMenu: LiteMenu, @Ctx() context: ResolverContext): Promise<String> {
78
- const { lng } = context.state
79
- const { labels, name } = liteMenu
80
-
81
- return (lng && labels && labels[lng]) || name
82
- }
83
-
84
- @FieldResolver(type => Board)
85
- async board(@Root() liteMenu: LiteMenu) {
86
- if ((liteMenu.type === 'board' || liteMenu.type === 'interactive-board') && liteMenu.value) {
87
- return await getRepository(Board).findOneBy({ id: liteMenu.value })
88
- }
89
- }
90
-
91
- @FieldResolver(type => Role)
92
- async role(@Root() liteMenu: LiteMenu) {
93
- return liteMenu.roleId && (await getRepository(Role).findOneBy({ id: liteMenu.roleId }))
94
- }
95
-
96
- @FieldResolver(type => Domain)
97
- async domain(@Root() liteMenu: LiteMenu) {
98
- return liteMenu.domainId && (await getRepository(Domain).findOneBy({ id: liteMenu.domainId }))
99
- }
100
-
101
- @FieldResolver(type => User)
102
- async updater(@Root() liteMenu: LiteMenu): Promise<User> {
103
- return liteMenu.updaterId && (await getRepository(User).findOneBy({ id: liteMenu.updaterId }))
104
- }
105
-
106
- @FieldResolver(type => User)
107
- async creator(@Root() liteMenu: LiteMenu): Promise<User> {
108
- return liteMenu.creatorId && (await getRepository(User).findOneBy({ id: liteMenu.creatorId }))
109
- }
110
- }
@@ -1,92 +0,0 @@
1
- import { Field, InputType, Int, ObjectType } from 'type-graphql'
2
-
3
- import { ScalarObject, ObjectRef } from '@things-factory/shell'
4
-
5
- import { LiteMenu } from './lite-menu'
6
-
7
- @InputType()
8
- export class NewLiteMenu {
9
- @Field()
10
- name: string
11
-
12
- @Field({ nullable: true })
13
- description?: string
14
-
15
- @Field({ nullable: true })
16
- appName?: string
17
-
18
- @Field({ nullable: true })
19
- parent?: string
20
-
21
- @Field(type => Int, { nullable: true })
22
- rank?: number
23
-
24
- @Field({ nullable: true })
25
- type?: string
26
-
27
- @Field({ nullable: true })
28
- icon?: string
29
-
30
- @Field({ nullable: true })
31
- value?: string
32
-
33
- @Field({ nullable: true })
34
- active?: boolean
35
-
36
- @Field(type => ObjectRef, { nullable: true })
37
- role?: ObjectRef
38
-
39
- @Field(type => ScalarObject, { nullable: true })
40
- labels?: any // { [lng: string]: string }
41
-
42
- @Field({ nullable: true })
43
- help?: string
44
- }
45
-
46
- @InputType()
47
- export class LiteMenuPatch {
48
- @Field({ nullable: true })
49
- name: string
50
-
51
- @Field({ nullable: true })
52
- description?: string
53
-
54
- @Field({ nullable: true })
55
- appName?: string
56
-
57
- @Field({ nullable: true })
58
- parent?: string
59
-
60
- @Field(type => Int, { nullable: true })
61
- rank?: number
62
-
63
- @Field({ nullable: true })
64
- type?: string
65
-
66
- @Field({ nullable: true })
67
- icon?: string
68
-
69
- @Field({ nullable: true })
70
- value?: string
71
-
72
- @Field({ nullable: true })
73
- active?: boolean
74
-
75
- @Field(type => ObjectRef, { nullable: true })
76
- role?: ObjectRef
77
-
78
- @Field(type => ScalarObject, { nullable: true })
79
- labels?: any // { [lng: string]: string }
80
-
81
- @Field({ nullable: true })
82
- help?: string
83
- }
84
-
85
- @ObjectType()
86
- export class LiteMenuList {
87
- @Field(type => [LiteMenu])
88
- items: LiteMenu[]
89
-
90
- @Field(type => Int)
91
- total: number
92
- }
@@ -1,149 +0,0 @@
1
- import { Field, ID, Int, ObjectType } from 'type-graphql'
2
- import {
3
- Column,
4
- CreateDateColumn,
5
- Entity,
6
- Index,
7
- ManyToOne,
8
- PrimaryGeneratedColumn,
9
- RelationId,
10
- UpdateDateColumn
11
- } from 'typeorm'
12
-
13
- import { User, Role } from '@things-factory/auth-base'
14
- import { Board } from '@things-factory/board-service'
15
- import { Domain, ScalarObject } from '@things-factory/shell'
16
- import { config } from '@things-factory/env'
17
-
18
- const ORMCONFIG = config.get('ormconfig', {})
19
- const DATABASE_TYPE = ORMCONFIG.type
20
-
21
- @Entity()
22
- @Index('ix_lite_menus_0', (liteMenu: LiteMenu) => [liteMenu.domain, liteMenu.name, liteMenu.type, liteMenu.appName], {
23
- unique: true
24
- })
25
- @Index('ix_lite_menus_1', (liteMenu: LiteMenu) => [liteMenu.domain, liteMenu.appName, liteMenu.parent, liteMenu.rank], {
26
- unique: true
27
- })
28
- @ObjectType({ description: 'Entity for LiteMenu' })
29
- export class LiteMenu {
30
- @PrimaryGeneratedColumn('uuid')
31
- @Field(type => ID)
32
- readonly id: string
33
-
34
- @ManyToOne(type => Domain)
35
- @Field(type => Domain)
36
- domain?: Domain
37
-
38
- @RelationId((liteMenu: LiteMenu) => liteMenu.domain)
39
- domainId?: string
40
-
41
- @Column()
42
- @Field()
43
- name: string
44
-
45
- @Column({
46
- nullable: true
47
- })
48
- @Field({ nullable: true })
49
- description?: string
50
-
51
- @Column({
52
- nullable: true,
53
- default: ''
54
- })
55
- @Field({ nullable: true })
56
- appName?: string
57
-
58
- @Column({
59
- nullable: true,
60
- default: ''
61
- })
62
- @Field({ nullable: true })
63
- parent?: string
64
-
65
- @Column({
66
- nullable: true
67
- })
68
- @Field(type => Int, { nullable: true })
69
- rank?: number
70
-
71
- @Column({
72
- nullable: true
73
- })
74
- @Field({ nullable: true })
75
- type?: string
76
-
77
- @Column({
78
- nullable: true
79
- })
80
- @Field({ nullable: true })
81
- value?: string
82
-
83
- @Column({
84
- nullable: true
85
- })
86
- @Field({ nullable: true })
87
- icon?: string
88
-
89
- @Column({
90
- nullable: true
91
- })
92
- @Field({ nullable: true })
93
- active?: boolean
94
-
95
- @Field(type => Board, { nullable: true })
96
- board?: Board
97
-
98
- @ManyToOne(type => Role)
99
- @Field(type => Role, { nullable: true })
100
- role?: Role
101
-
102
- @RelationId((liteMenu: LiteMenu) => liteMenu.role)
103
- roleId?: string
104
-
105
- @Column('simple-json', { nullable: true })
106
- @Field(type => ScalarObject, { nullable: true, description: 'Settings for multilingual titles' })
107
- labels?: any // { [lng: string]: string }
108
-
109
- @Column({
110
- nullable: true,
111
- type:
112
- DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
113
- ? 'longtext'
114
- : DATABASE_TYPE == 'oracle'
115
- ? 'clob'
116
- : DATABASE_TYPE == 'mssql'
117
- ? 'nvarchar'
118
- : 'varchar',
119
- length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
120
- })
121
- @Field({ nullable: true })
122
- help?: string
123
-
124
- @CreateDateColumn()
125
- @Field({ nullable: true, description: 'Settings for help contents' })
126
- createdAt?: Date
127
-
128
- @UpdateDateColumn()
129
- @Field({ nullable: true })
130
- updatedAt?: Date
131
-
132
- @ManyToOne(type => User, {
133
- nullable: true
134
- })
135
- @Field(type => User, { nullable: true })
136
- creator?: User
137
-
138
- @RelationId((liteMenu: LiteMenu) => liteMenu.creator)
139
- creatorId?: string
140
-
141
- @ManyToOne(type => User, {
142
- nullable: true
143
- })
144
- @Field(type => User, { nullable: true })
145
- updater?: User
146
-
147
- @RelationId((liteMenu: LiteMenu) => liteMenu.updater)
148
- updaterId?: string
149
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": false,
5
- "module": "commonjs",
6
- "outDir": "../dist-server",
7
- "baseUrl": "./"
8
- },
9
- "include": ["./**/*"]
10
- }