@things-factory/board-ui 8.0.0-beta.8 → 8.0.0

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.
Files changed (55) hide show
  1. package/client/apptools/favorite-tool.ts +124 -0
  2. package/client/board-list/board-tile-list.ts +272 -0
  3. package/client/board-list/group-bar-styles.ts +63 -0
  4. package/client/board-list/group-bar.ts +99 -0
  5. package/client/board-list/play-group-bar.ts +88 -0
  6. package/client/board-provider.ts +92 -0
  7. package/client/bootstrap.ts +39 -0
  8. package/client/data-grist/board-editor.ts +113 -0
  9. package/client/data-grist/board-renderer.ts +134 -0
  10. package/client/data-grist/color-map-editor.ts +17 -0
  11. package/client/data-grist/color-ranges-editor.ts +17 -0
  12. package/client/graphql/board-template.ts +141 -0
  13. package/client/graphql/board.ts +273 -0
  14. package/client/graphql/favorite-board.ts +25 -0
  15. package/client/graphql/group.ts +138 -0
  16. package/client/graphql/index.ts +6 -0
  17. package/client/graphql/my-board.ts +25 -0
  18. package/client/graphql/play-group.ts +189 -0
  19. package/client/index.ts +10 -0
  20. package/client/pages/attachment-list-page.ts +142 -0
  21. package/client/pages/board-list-page.ts +603 -0
  22. package/client/pages/board-modeller-page.ts +288 -0
  23. package/client/pages/board-player-by-name-page.ts +29 -0
  24. package/client/pages/board-player-page.ts +241 -0
  25. package/client/pages/board-template/board-template-list-page.ts +248 -0
  26. package/client/pages/board-viewer-by-name-page.ts +24 -0
  27. package/client/pages/board-viewer-page.ts +271 -0
  28. package/client/pages/font-list-page.ts +31 -0
  29. package/client/pages/play-list-page.ts +400 -0
  30. package/client/pages/printable-board-viewer-page.ts +54 -0
  31. package/client/pages/theme/theme-editors.ts +56 -0
  32. package/client/pages/theme/theme-list-page.ts +313 -0
  33. package/client/pages/things-scene-components-with-tools.import +0 -0
  34. package/client/pages/things-scene-components.import +0 -0
  35. package/client/route.ts +51 -0
  36. package/client/setting-let/board-view-setting-let.ts +68 -0
  37. package/client/themes/board-theme.css +77 -0
  38. package/client/things-scene-import.d.ts +4 -0
  39. package/client/viewparts/board-basic-info.ts +646 -0
  40. package/client/viewparts/board-info-link.ts +56 -0
  41. package/client/viewparts/board-info.ts +85 -0
  42. package/client/viewparts/board-template-builder.ts +134 -0
  43. package/client/viewparts/board-versions.ts +172 -0
  44. package/client/viewparts/group-info-basic.ts +267 -0
  45. package/client/viewparts/group-info-import.ts +132 -0
  46. package/client/viewparts/group-info.ts +87 -0
  47. package/client/viewparts/index.ts +3 -0
  48. package/client/viewparts/link-builder.ts +210 -0
  49. package/client/viewparts/play-group-info-basic.ts +268 -0
  50. package/client/viewparts/play-group-info-link.ts +46 -0
  51. package/client/viewparts/play-group-info.ts +81 -0
  52. package/dist-client/tsconfig.tsbuildinfo +1 -1
  53. package/dist-server/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +19 -19
  55. package/server/index.ts +0 -0
@@ -0,0 +1,400 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@material/web/fab/fab.js'
3
+
4
+ import '../viewparts/play-group-info'
5
+ import '../board-list/board-tile-list'
6
+ import '../board-list/play-group-bar'
7
+
8
+ import gql from 'graphql-tag'
9
+ import { css, html } from 'lit'
10
+ import { customElement, property, query, queryAll, state } from 'lit/decorators.js'
11
+ import { connect } from 'pwa-helpers/connect-mixin.js'
12
+
13
+ import { UPDATE_FAVORITES } from '@things-factory/fav-base/client'
14
+ import { openOverlay } from '@operato/layout'
15
+ import { navigate, PageView, store } from '@operato/shell'
16
+ import { client } from '@operato/graphql'
17
+ import { i18next, localize } from '@operato/i18n'
18
+ import { ScrollbarStyles } from '@operato/styles'
19
+ import { pulltorefresh } from '@operato/pull-to-refresh'
20
+ import { swipe, isMobileDevice } from '@things-factory/utils/src/index.js'
21
+
22
+ import {
23
+ createPlayGroup,
24
+ deletePlayGroup,
25
+ fetchPlayGroup,
26
+ fetchPlayGroupList,
27
+ leavePlayGroup,
28
+ updateBoard,
29
+ updatePlayGroup
30
+ } from '../graphql'
31
+
32
+ @customElement('play-list-page')
33
+ export class PlayListPage extends localize(i18next)(connect(store)(PageView)) {
34
+ static styles = [
35
+ ScrollbarStyles,
36
+ css`
37
+ :host {
38
+ display: flex;
39
+ flex-direction: column;
40
+ position: relative;
41
+
42
+ overflow: hidden;
43
+ }
44
+
45
+ board-tile-list {
46
+ flex: 1;
47
+ overflow-y: auto;
48
+ }
49
+
50
+ #play {
51
+ position: absolute;
52
+ bottom: 15px;
53
+ right: 16px;
54
+ text-decoration: auto;
55
+ }
56
+ `
57
+ ]
58
+
59
+ @property({ type: String }) groupId?: string
60
+ @property({ type: Array }) groups: any[] = []
61
+ @property({ type: Array }) boards: any[] = []
62
+ @property({ type: Array }) favorites: any[] = []
63
+
64
+ @query('board-tile-list') boardTileList!: HTMLElement
65
+
66
+ private page?: string
67
+
68
+ get context() {
69
+ var group = this.groups && this.groups.find(group => group.id === this.groupId)
70
+
71
+ return {
72
+ title: {
73
+ icon: 'video_library',
74
+ text: group ? group.name : 'Play List'
75
+ },
76
+ board_topmenu: true
77
+ }
78
+ }
79
+
80
+ render() {
81
+ return html`
82
+ <play-group-bar
83
+ .groups=${this.groups}
84
+ .groupId=${this.groupId}
85
+ target-page="play-list"
86
+ @info-play-group=${e => this.onInfoPlayGroup(e.detail)}
87
+ ></play-group-bar>
88
+
89
+ <board-tile-list
90
+ .favorites=${this.favorites}
91
+ .boards=${this.boards}
92
+ .group=${this.groupId}
93
+ @info-board=${e => this.onInfoBoard(e.detail)}
94
+ @delete-board=${e => this.onDeleteBoard(e.detail)}
95
+ @refresh-favorites=${e => this.refreshFavorites()}
96
+ @reordered=${e => this.onReorder(e.detail)}
97
+ reorderable
98
+ ></board-tile-list>
99
+
100
+ <a id="play" .href=${'board-player/' + this.groupId}>
101
+ <md-fab title="play">
102
+ <md-icon slot="icon">play_arrow</md-icon>
103
+ </md-fab>
104
+ </a>
105
+ `
106
+ }
107
+
108
+ async refresh() {
109
+ this.groups = (await fetchPlayGroupList()).playGroups.items
110
+
111
+ this.groups && (await this.refreshBoards())
112
+ }
113
+
114
+ async refreshBoards() {
115
+ if (!this.groups) {
116
+ await this.refresh()
117
+ return
118
+ }
119
+
120
+ if (!this.groupId) {
121
+ let groupId = this.groups && this.groups[0] && this.groups[0].id
122
+ var newURL = new URL(window.location.href)
123
+
124
+ newURL.pathname += `/${groupId}`
125
+
126
+ if (groupId) {
127
+ navigate(newURL.href, true)
128
+ }
129
+ return
130
+ }
131
+
132
+ if (this.groupId) {
133
+ var playGroup = (await fetchPlayGroup(this.groupId)).playGroup
134
+ this.boards = playGroup ? playGroup.boards : []
135
+ } else {
136
+ this.boards = []
137
+ }
138
+
139
+ this.updateContext()
140
+
141
+ var list = this.boardTileList
142
+
143
+ list.style.transition = ''
144
+ list.style.transform = `translate3d(0, 0, 0)`
145
+ }
146
+
147
+ async pageInitialized() {
148
+ this.refresh()
149
+ }
150
+
151
+ async pageUpdated(changes, lifecycle) {
152
+ if (this.active) {
153
+ this.page = lifecycle.page
154
+ this.groupId = lifecycle.resourceId
155
+
156
+ await this.updateComplete
157
+
158
+ this.refreshBoards()
159
+ }
160
+ }
161
+
162
+ stateChanged(state) {
163
+ this.favorites = state.favorite.favorites
164
+ }
165
+
166
+ firstUpdated() {
167
+ var list = this.boardTileList
168
+
169
+ pulltorefresh({
170
+ container: this.shadowRoot,
171
+ scrollable: list,
172
+ refresh: () => {
173
+ return this.refresh()
174
+ }
175
+ })
176
+
177
+ if (isMobileDevice()) {
178
+ swipe({
179
+ container: list,
180
+ animates: {
181
+ dragging: async (d, opts) => {
182
+ var groups = this.groups
183
+ var currentIndex = groups.findIndex(group => group.id == this.groupId)
184
+
185
+ if ((d > 0 && currentIndex <= 0) || (d < 0 && currentIndex >= groups.length - 1)) {
186
+ /* TODO blocked gesture */
187
+ return false
188
+ }
189
+
190
+ list.style.transform = `translate3d(${d}px, 0, 0)`
191
+ },
192
+ aborting: async opts => {
193
+ list.style.transition = 'transform 0.3s'
194
+ list.style.transform = `translate3d(0, 0, 0)`
195
+
196
+ setTimeout(() => {
197
+ list.style.transition = ''
198
+ }, 300)
199
+ },
200
+ swiping: async (d, opts) => {
201
+ var groups = this.groups
202
+ var currentIndex = groups.findIndex(group => group.id == this.groupId)
203
+
204
+ if ((d > 0 && currentIndex <= 0) || (d < 0 && currentIndex >= groups.length - 1)) {
205
+ list.style.transition = ''
206
+ list.style.transform = `translate3d(0, 0, 0)`
207
+ } else {
208
+ list.style.transition = 'transform 0.3s'
209
+ list.style.transform = `translate3d(${d < 0 ? '-100%' : '100%'}, 0, 0)`
210
+
211
+ navigate(`${this.page}/${groups[currentIndex + (d < 0 ? 1 : -1)].id}`)
212
+ }
213
+ }
214
+ }
215
+ })
216
+ }
217
+ }
218
+
219
+ async onInfoBoard(board) {
220
+ openOverlay('viewpart-info', {
221
+ template: html`
222
+ <board-info
223
+ .board=${board}
224
+ .groupId=${this.groupId}
225
+ @update-board=${e => this.onUpdateBoard(e.detail)}
226
+ @delete-board=${e => this.onDeleteBoard(e.detail)}
227
+ @join-playgroup=${e => this.onJoinPlayGroup(e.detail)}
228
+ @leave-playgroup=${e => this.onLeavePlayGroup(e.detail)}
229
+ ></board-info>
230
+ `
231
+ })
232
+ }
233
+
234
+ async onInfoPlayGroup(group) {
235
+ openOverlay('viewpart-info', {
236
+ template: html`
237
+ <play-group-info
238
+ .playGroup=${group}
239
+ @update-play-group=${e => this.onUpdatePlayGroup(e.detail)}
240
+ @delete-play-group=${e => this.onDeletePlayGroup(e.detail)}
241
+ @create-play-group=${e => this.onCreatePlayGroup(e.detail)}
242
+ ></play-group-info>
243
+ `
244
+ })
245
+ }
246
+
247
+ async onCreatePlayGroup(playGroup) {
248
+ try {
249
+ await createPlayGroup(playGroup)
250
+ this._notify('info', i18next.t('text.play-group created', { playGroup: playGroup.name }))
251
+ } catch (ex) {
252
+ this._notify('error', ex, ex)
253
+ }
254
+
255
+ this.refresh()
256
+ }
257
+
258
+ async onUpdatePlayGroup(playGroup) {
259
+ try {
260
+ await updatePlayGroup(playGroup)
261
+ this._notify('info', i18next.t('text.play-group updated', { playGroup: playGroup.name }))
262
+ } catch (ex) {
263
+ this._notify('error', ex, ex)
264
+ }
265
+
266
+ this.refresh()
267
+ }
268
+
269
+ async onDeletePlayGroup(playGroup) {
270
+ try {
271
+ await deletePlayGroup(playGroup.id)
272
+ this._notify('info', i18next.t('text.play-group deleted', { playGroup: playGroup.name }))
273
+ } catch (ex) {
274
+ this._notify('error', ex, ex)
275
+ }
276
+
277
+ this.refresh()
278
+ }
279
+
280
+ async onUpdateBoard(board) {
281
+ try {
282
+ await updateBoard(board)
283
+ this._notify('info', i18next.t('text.board updated', { board: board.name }))
284
+ } catch (ex) {
285
+ this._notify('error', ex, ex)
286
+ }
287
+
288
+ this.refreshBoards()
289
+ }
290
+
291
+ async onDeleteBoard(board) {
292
+ const playGroup = this.groups && this.groups.find(group => group.id === this.groupId)
293
+ await this.onLeavePlayGroup({ board, playGroup })
294
+ }
295
+
296
+ async onJoinPlayGroup({ board, playGroup }) {
297
+ try {
298
+ await client.mutate({
299
+ mutation: gql`
300
+ mutation JoinPlayGroup($id: String!, $boardIds: [String!]!) {
301
+ joinPlayGroup(id: $id, boardIds: $boardIds) {
302
+ id
303
+ }
304
+ }
305
+ `,
306
+ variables: {
307
+ id: playGroup.id,
308
+ boardIds: [board.id]
309
+ }
310
+ })
311
+
312
+ this._notify('info', i18next.t('text.joined into play-group', { board: board.name, playGroup: playGroup.name }))
313
+ } catch (ex) {
314
+ this._notify('error', ex, ex)
315
+ }
316
+
317
+ this.refresh()
318
+ }
319
+
320
+ async onLeavePlayGroup({ board, playGroup }) {
321
+ try {
322
+ await client.mutate({
323
+ mutation: gql`
324
+ mutation ($id: String!, $boardIds: [String!]!) {
325
+ leavePlayGroup(id: $id, boardIds: $boardIds) {
326
+ id
327
+ }
328
+ }
329
+ `,
330
+ variables: {
331
+ id: playGroup.id,
332
+ boardIds: [board.id]
333
+ }
334
+ })
335
+
336
+ this._notify('info', i18next.t('text.leaved from play-group', { board: board.name, playGroup: playGroup.name }))
337
+ } catch (ex) {
338
+ this._notify('error', ex, ex)
339
+ }
340
+
341
+ this.refresh()
342
+ }
343
+
344
+ async onReorder({ boardIds, groupId }) {
345
+ try {
346
+ const response = await client.mutate({
347
+ mutation: gql`
348
+ mutation ($id: String!, $boardIds: [String!]!) {
349
+ reorderPlayGroup(id: $id, boardIds: $boardIds) {
350
+ id
351
+ name
352
+ }
353
+ }
354
+ `,
355
+ variables: {
356
+ id: groupId,
357
+ boardIds
358
+ }
359
+ })
360
+
361
+ const playGroup = response.data.reorderPlayGroup
362
+
363
+ this._notify('info', i18next.t('text.reorder play-group', { playGroup: playGroup.name }))
364
+ } catch (ex) {
365
+ this._notify('error', ex, ex)
366
+ }
367
+
368
+ this.refresh()
369
+ }
370
+
371
+ async refreshFavorites() {
372
+ const response = await client.query({
373
+ query: gql`
374
+ query {
375
+ myFavorites {
376
+ id
377
+ routing
378
+ }
379
+ }
380
+ `
381
+ })
382
+
383
+ store.dispatch({
384
+ type: UPDATE_FAVORITES,
385
+ favorites: response.data.myFavorites.map(favorite => favorite.routing)
386
+ })
387
+ }
388
+
389
+ _notify(level: 'info' | 'warn' | 'error', message: any, ex?: any) {
390
+ document.dispatchEvent(
391
+ new CustomEvent('notify', {
392
+ detail: {
393
+ level,
394
+ message,
395
+ ex
396
+ }
397
+ })
398
+ )
399
+ }
400
+ }
@@ -0,0 +1,54 @@
1
+ import { BoardViewerPage } from './board-viewer-page'
2
+
3
+ function serialize(obj) {
4
+ var str = [] as string[]
5
+ for (var p in obj)
6
+ if (obj.hasOwnProperty(p)) {
7
+ str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]))
8
+ }
9
+ return str.join('&')
10
+ }
11
+
12
+ function readFileAsync(file) {
13
+ return new Promise((resolve, reject) => {
14
+ let reader = new FileReader()
15
+
16
+ reader.onload = () => {
17
+ resolve(reader.result)
18
+ }
19
+
20
+ reader.onerror = reject
21
+ reader.readAsDataURL(file)
22
+ })
23
+ }
24
+
25
+ export class PrintableBoardViewerPage extends BoardViewerPage {
26
+ get context() {
27
+ return {
28
+ ...super.context,
29
+ printable: {
30
+ accept: ['preview'],
31
+ content: async () => {
32
+ const image = await this.fetchImage()
33
+ await this.printTrick(image)
34
+
35
+ return this as any
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ async fetchImage() {
42
+ const modelId = this._boardId
43
+ const data = {
44
+ title: this.context.title
45
+ }
46
+ const queryString = serialize(data)
47
+ const url = `${location.origin}/screenshot/${modelId}${queryString ? '?' + queryString : ''}`
48
+
49
+ const response = await fetch(url)
50
+ return await readFileAsync(await response.blob())
51
+ }
52
+ }
53
+
54
+ customElements.define('printable-board-viewer-page', PrintableBoardViewerPage)
@@ -0,0 +1,56 @@
1
+ import { getEditor, getRenderer } from '@operato/data-grist'
2
+
3
+ export function buildThemeValueRecordConfig() {
4
+ return {
5
+ editor: function (value, column, record, rowIndex, field) {
6
+ return getEditor(record.type || 'text')(value, column, record, rowIndex, field)
7
+ },
8
+ renderer: function (value, column, record, rowIndex, field) {
9
+ return getRenderer(record.type || 'text')(value, column, record, rowIndex, field)
10
+ },
11
+ editable: true
12
+ }
13
+ }
14
+
15
+ function getEditorType(themeType: string): string {
16
+ switch (themeType) {
17
+ case 'legend':
18
+ case 'color-map':
19
+ return 'value-map'
20
+ case 'color-ranges':
21
+ return 'value-ranges'
22
+ case 'color':
23
+ return 'color'
24
+ default:
25
+ return 'text'
26
+ }
27
+ }
28
+
29
+ function getRendererType(themeType: string): string {
30
+ switch (themeType) {
31
+ case 'legend':
32
+ case 'color-map':
33
+ case 'color-ranges':
34
+ return 'json5'
35
+ case 'color':
36
+ return 'color'
37
+ default:
38
+ return 'text'
39
+ }
40
+ }
41
+
42
+ function getOptions(themeType: string, name: string): any {
43
+ switch (themeType) {
44
+ case 'color-map':
45
+ case 'color-ranges':
46
+ return {
47
+ name: `${themeType.toUpperCase()} : ${name}`,
48
+ valuetype: 'color'
49
+ }
50
+ case 'color':
51
+ default:
52
+ return {
53
+ name: `${themeType?.toUpperCase() || ''} : ${name}`
54
+ }
55
+ }
56
+ }