@things-factory/lite-menu 8.0.5 → 9.0.0-beta.10

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.
@@ -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]