@things-factory/menu-ui 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.
@@ -1,200 +0,0 @@
1
- import '../viewparts/menu-bar'
2
- import '../viewparts/menu-tile-list'
3
-
4
- import gql from 'graphql-tag'
5
- import { css, html } from 'lit'
6
- import { customElement, property, state } from 'lit/decorators.js'
7
- import { connect } from 'pwa-helpers/connect-mixin.js'
8
-
9
- import { navigate, PageView, store } from '@operato/shell'
10
- import { client } from '@operato/graphql'
11
- import { ScrollbarStyles } from '@operato/styles'
12
- import { pulltorefresh } from '@operato/pull-to-refresh'
13
- import { swipe } from '@things-factory/utils/src/index.js'
14
-
15
- @customElement('menu-list-page')
16
- class MenuListPage extends connect(store)(PageView) {
17
- static styles = [
18
- ScrollbarStyles,
19
- css`
20
- :host {
21
- display: flex;
22
- flex-direction: column;
23
-
24
- overflow: hidden;
25
- }
26
-
27
- menu-bar {
28
- z-index: 1;
29
- }
30
-
31
- menu-tile-list {
32
- flex: 1;
33
- overflow-y: auto;
34
- }
35
- `
36
- ]
37
-
38
- @property({ type: String }) menuId?: string
39
- @property({ type: Array }) menus: any[] = []
40
- @property({ type: Object }) routingTypes: any
41
- @property({ type: Array }) favorites: any[] = []
42
- @property({ type: Object }) user: any
43
- @property({ type: Boolean }) showSpinner: boolean = false
44
-
45
- @state() private page?: string
46
-
47
- private getMenus: any
48
-
49
- render() {
50
- return html`
51
- <menu-bar .menus=${this.menus} .menuId=${this.menuId}></menu-bar>
52
-
53
- <menu-tile-list
54
- .menus=${this.menus}
55
- .routingTypes=${this.routingTypes}
56
- .menuId=${this.menuId}
57
- .favorites=${this.favorites}
58
- .showSpinner=${this.showSpinner}
59
- ></menu-tile-list>
60
- `
61
- }
62
-
63
- get context() {
64
- return {
65
- title: 'Menu'
66
- }
67
- }
68
-
69
- async fetchMenus() {
70
- const response = await client.query({
71
- query: gql`
72
- query {
73
- userMenus {
74
- id
75
- name
76
- role {
77
- id
78
- name
79
- description
80
- }
81
- children {
82
- id
83
- name
84
- routingType
85
- idField
86
- titleField
87
- resourceName
88
- resourceUrl
89
- template
90
- role {
91
- id
92
- name
93
- description
94
- }
95
- }
96
- }
97
- }
98
- `
99
- })
100
-
101
- return response.data.userMenus
102
- }
103
-
104
- async updated(changes) {
105
- if (changes.has('user')) {
106
- if (this.user && this.user.email) {
107
- this.refresh()
108
- }
109
- }
110
- }
111
-
112
- stateChanged(state) {
113
- this.page = state.route.page
114
- this.routingTypes = state.menu.routingTypes
115
- this.menuId = state.route.resourceId
116
- this.favorites = state.favorite.favorites
117
- this.user = state.auth.user
118
-
119
- var provider = state.menu.provider
120
- this.getMenus = typeof provider === 'function' ? provider.bind(this) : this.fetchMenus
121
- }
122
-
123
- async refresh() {
124
- this.showSpinner = true
125
- this.menus = await this.getMenus()
126
- this.showSpinner = false
127
- }
128
-
129
- async firstUpdated() {
130
- var list = this.renderRoot.querySelector('menu-tile-list') as HTMLElement
131
-
132
- pulltorefresh({
133
- container: this.renderRoot,
134
- scrollable: list,
135
- refresh: () => {
136
- return this.refresh()
137
- }
138
- })
139
-
140
- swipe({
141
- container: this.renderRoot.querySelector('menu-tile-list'),
142
- animates: {
143
- dragging: async (d, opts) => {
144
- var currentIndex = Number(this.menuId)
145
- var isHome = this.menuId === '' || this.menuId === undefined
146
-
147
- if ((d > 0 && isHome) || (d < 0 && currentIndex >= this.menus.length - 1)) {
148
- /* TODO blocked gesture */
149
- return false
150
- }
151
-
152
- list.style.transform = `translate3d(${d}px, 0, 0)`
153
- },
154
- aborting: async opts => {
155
- list.style.transition = 'transform 0.3s'
156
- list.style.transform = `translate3d(0, 0, 0)`
157
-
158
- setTimeout(() => {
159
- list.style.transition = ''
160
- }, 300)
161
- },
162
- swiping: async (d, opts) => {
163
- var currentIndex = Number(this.menuId)
164
- var isHome = this.menuId === '' || this.menuId === undefined
165
-
166
- if ((d > 0 && isHome) || (d < 0 && currentIndex >= this.menus.length - 1)) {
167
- list.style.transform = `translate3d(0, 0, 0)`
168
-
169
- return
170
- }
171
-
172
- list.style.transition = 'transform 0.3s'
173
- list.style.transform = `translate3d(${d < 0 ? '-100%' : '100%'}, 0, 0)`
174
-
175
- setTimeout(() => {
176
- if (isHome) {
177
- navigate(`${this.page}/0`)
178
- } else if (d > 0 && currentIndex == 0) {
179
- navigate(`${this.page}`)
180
- } else {
181
- navigate(`${this.page}/${currentIndex + (d < 0 ? 1 : -1)}`)
182
- }
183
-
184
- list.style.transition = ''
185
- list.style.transform = `translate3d(${d < 0 ? '100%' : '-100%'}, 0, 0)`
186
-
187
- requestAnimationFrame(() => {
188
- list.style.transition = 'transform 0.3s'
189
- list.style.transform = `translate3d(0, 0, 0)`
190
- })
191
- }, 300)
192
- }
193
- }
194
- })
195
-
196
- await this.updateComplete
197
-
198
- this.refresh()
199
- }
200
- }
@@ -1,374 +0,0 @@
1
- import '@things-factory/form-ui'
2
- import '@operato/data-grist'
3
-
4
- import gql from 'graphql-tag'
5
- import { css, html, LitElement } from 'lit'
6
- import { customElement, property, query } from 'lit/decorators.js'
7
-
8
- import { DataGrist, FetchOption, getEditor, getRenderer } from '@operato/data-grist'
9
- import { i18next, localize } from '@operato/i18n'
10
- import { client } from '@operato/graphql'
11
- import { ScrollbarStyles } from '@operato/styles'
12
- import { isMobileDevice } from '@operato/utils'
13
- import { gqlBuilder } from '@things-factory/utils'
14
-
15
- import { OxPrompt } from '@operato/popup/ox-prompt.js'
16
-
17
- @customElement('menu-management-detail')
18
- class MenuManagementDetail extends localize(i18next)(LitElement) {
19
- static styles = [
20
- ScrollbarStyles,
21
- css`
22
- :host {
23
- display: flex;
24
- flex-direction: column;
25
- overflow: hidden;
26
- background-color: var(--md-sys-color-surface);
27
- }
28
- search-form {
29
- overflow: visible;
30
- }
31
- .grist {
32
- display: flex;
33
- flex-direction: column;
34
- flex: 1;
35
- overflow-y: auto;
36
- }
37
- ox-grist {
38
- overflow-y: hidden;
39
- flex: 1;
40
- }
41
- .button-container {
42
- padding: 10px 0 12px 0;
43
- text-align: center;
44
- }
45
- .button-container > button {
46
- background-color: var(--button-background-color);
47
- border: var(--button-border);
48
- border-radius: var(--button-border-radius);
49
- margin: var(--button-margin);
50
- padding: var(--button-padding);
51
- color: var(--button-color);
52
- font: var(--button-font);
53
- text-transform: var(--button-text-transform);
54
- }
55
- .button-container > button:hover,
56
- .button-container > button:active {
57
- background-color: var(--button-background-focus-color);
58
- }
59
- `
60
- ]
61
-
62
- @property({ type: String }) menuId?: string
63
- @property({ type: Object }) searchFields: any
64
- @property({ type: Object }) config: any
65
-
66
- @query('ox-grist') dataGrist!: DataGrist
67
- @query('search-form') searchForm!: HTMLFormElement
68
-
69
- render() {
70
- return html`
71
- <search-form .fields=${this.searchFields} @submit=${e => this.dataGrist.fetch()}></search-form>
72
-
73
- <div class="grist">
74
- <ox-grist
75
- .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
76
- .config=${this.config}
77
- .fetchHandler=${this.fetchHandler.bind(this)}
78
- ></ox-grist>
79
- </div>
80
-
81
- <div class="button-container">
82
- <button @click=${this.save}>${i18next.t('button.save')}</button>
83
- <button @click=${this.delete}>${i18next.t('button.delete')}</button>
84
- </div>
85
- `
86
- }
87
-
88
- firstUpdated() {
89
- this.searchFields = [
90
- {
91
- name: 'name',
92
- label: i18next.t('field.name'),
93
- type: 'text',
94
- props: { searchOper: 'i_like' }
95
- },
96
- {
97
- name: 'description',
98
- label: i18next.t('field.description'),
99
- type: 'text',
100
- props: { searchOper: 'i_like' }
101
- },
102
- {
103
- name: 'template',
104
- label: i18next.t('field.template'),
105
- type: 'text',
106
- props: { searchOper: 'i_like' }
107
- },
108
- {
109
- name: 'category',
110
- label: i18next.t('field.category'),
111
- type: 'text',
112
- props: { searchOper: 'i_like' }
113
- },
114
- {
115
- name: 'resourceUrl',
116
- label: i18next.t('field.resource_url'),
117
- type: 'text',
118
- props: { searchOper: 'i_like' }
119
- },
120
- {
121
- name: 'hiddenFlag',
122
- label: i18next.t('field.hidden_flag'),
123
- type: 'checkbox',
124
- props: { searchOper: 'eq' },
125
- attrs: ['indeterminate']
126
- }
127
- ]
128
-
129
- this.config = {
130
- rows: { selectable: { multiple: true } },
131
- pagination: { infinite: true },
132
- columns: [
133
- { type: 'gutter', gutterName: 'dirty' },
134
- { type: 'gutter', gutterName: 'sequence' },
135
- { type: 'gutter', gutterName: 'row-selector', multiple: true },
136
- {
137
- type: 'string',
138
- name: 'name',
139
- header: i18next.t('field.name'),
140
- record: { editable: true, align: 'left' },
141
- sortable: true,
142
- width: 150
143
- },
144
- {
145
- type: 'integer',
146
- name: 'rank',
147
- header: i18next.t('field.rank'),
148
- record: { editable: true },
149
- sortable: true,
150
- width: 80
151
- },
152
- {
153
- type: 'select',
154
- name: 'resourceType',
155
- header: i18next.t('field.type'),
156
- record: {
157
- editable: true,
158
- options: ['', 'board']
159
- },
160
- width: 80
161
- },
162
- {
163
- type: 'string',
164
- name: 'resourceId',
165
- header: i18next.t('field.value'),
166
- record: {
167
- editable: true,
168
- editor: function (value, column, record, rowIndex, field) {
169
- const type = record.resourceType || 'string'
170
- if (value && type !== 'board') {
171
- delete record.resourceId
172
- value = ''
173
- }
174
- return getEditor(type)(value, column, record, rowIndex, field)
175
- },
176
- renderer: function (value, column, record, rowIndex, field) {
177
- const type = record.resourceType || 'string'
178
- if (value && type !== 'board') {
179
- delete record.resourceId
180
- value = ''
181
- }
182
- return getRenderer(type)(value, column, record, rowIndex, field)
183
- }
184
- },
185
- width: 140
186
- },
187
- {
188
- type: 'string',
189
- name: 'description',
190
- header: i18next.t('field.description'),
191
- record: { editable: true, align: 'left' },
192
- sortable: true,
193
- width: 200
194
- },
195
- {
196
- type: 'string',
197
- name: 'template',
198
- header: i18next.t('field.template'),
199
- record: { editable: true, align: 'left' },
200
- sortable: true,
201
- width: 160
202
- },
203
- {
204
- type: 'string',
205
- name: 'resourceUrl',
206
- header: i18next.t('field.resource_url'),
207
- record: { editable: true, align: 'left' },
208
- sortable: true,
209
- width: 160
210
- },
211
- {
212
- type: 'boolean',
213
- name: 'hiddenFlag',
214
- header: i18next.t('field.hidden_flag'),
215
- record: { editable: true },
216
- sortable: true,
217
- width: 80
218
- },
219
- {
220
- type: 'resource-object',
221
- name: 'role',
222
- label: true,
223
- header: i18next.t('field.required role'),
224
- record: {
225
- editable: true,
226
- options: {
227
- title: i18next.t('title.lookup role'),
228
- queryName: 'roles'
229
- }
230
- },
231
- width: 200
232
- },
233
- {
234
- type: 'datetime',
235
- name: 'updatedAt',
236
- header: i18next.t('field.updated_at'),
237
- record: { editable: false },
238
- sortable: true,
239
- width: 150
240
- },
241
- {
242
- type: 'object',
243
- name: 'updater',
244
- header: i18next.t('field.updater'),
245
- record: { editable: false },
246
- sortable: true,
247
- width: 150
248
- }
249
- ]
250
- }
251
- }
252
-
253
- async fetchHandler({ page, limit, sorters = [{ name: 'rank' }, { name: 'name' }] }: FetchOption) {
254
- const response = await client.query({
255
- query: gql`
256
- query {
257
- menus(${gqlBuilder.buildArgs({
258
- filters: [...this.searchForm.queryFilters, { name: 'parentId', operator: 'eq', value: this.menuId }],
259
- pagination: { page, limit },
260
- sortings: sorters
261
- })}) {
262
- items {
263
- id
264
- name
265
- rank
266
- description
267
- category
268
- template
269
- resourceUrl
270
- resourceType
271
- resourceId
272
- hiddenFlag
273
- role {
274
- id
275
- name
276
- description
277
- }
278
- updatedAt
279
- updater{
280
- id
281
- name
282
- description
283
- }
284
- }
285
- total
286
- }
287
- }
288
- `
289
- })
290
-
291
- return {
292
- total: response.data.menus.total || 0,
293
- records: response.data.menus.items || []
294
- }
295
- }
296
-
297
- async save() {
298
- const patches = this.getPatches()
299
- if (patches && patches.length) {
300
- const response = await client.query({
301
- query: gql`
302
- mutation {
303
- updateMultipleMenu(${gqlBuilder.buildArgs({
304
- patches
305
- })}) {
306
- name
307
- }
308
- }
309
- `
310
- })
311
-
312
- if (!response.errors) this.dataGrist.fetch()
313
- } else {
314
- OxPrompt.open({
315
- title: i18next.t('text.nothing_changed'),
316
- text: i18next.t('text.there_is_nothing_to_save')
317
- })
318
- }
319
- }
320
-
321
- async delete() {
322
- const ids = this.dataGrist.selected.map(record => record.id)
323
- if (ids && ids.length > 0) {
324
- if (
325
- await OxPrompt.open({
326
- type: 'warning',
327
- title: i18next.t('button.delete'),
328
- text: i18next.t('text.are_you_sure'),
329
- confirmButton: { text: i18next.t('button.delete') },
330
- cancelButton: { text: i18next.t('button.cancel') }
331
- })
332
- ) {
333
- const response = await client.query({
334
- query: gql`
335
- mutation {
336
- deleteMenus(${gqlBuilder.buildArgs({ ids })})
337
- }
338
- `
339
- })
340
-
341
- if (!response.errors) {
342
- this.dataGrist.fetch()
343
- }
344
- }
345
- } else {
346
- OxPrompt.open({
347
- title: i18next.t('text.nothing_selected'),
348
- text: i18next.t('text.there_is_nothing_to_delete')
349
- })
350
- }
351
- }
352
-
353
- getPatches() {
354
- let patches = this.dataGrist.dirtyRecords
355
- if (patches && patches.length) {
356
- patches = patches.map(menu => {
357
- let patchField: any = menu.id ? { id: menu.id } : {}
358
- patchField = { ...patchField, routingType: menu.routingType, menuType: menu.menuType }
359
- const dirtyFields = menu.__dirtyfields__
360
- for (let key in dirtyFields) {
361
- patchField[key] = dirtyFields[key].after
362
- }
363
- patchField.parent = { id: this.menuId }
364
- patchField.routingType = patchField.routingType || 'STATIC'
365
- patchField.menuType = patchField.menuType || 'SCREEN'
366
- patchField.cuFlag = menu.__dirty__
367
-
368
- return patchField
369
- })
370
- }
371
-
372
- return patches
373
- }
374
- }