@things-factory/reference-app 3.7.1 → 3.7.6

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.
@@ -0,0 +1,437 @@
1
+ import '@operato/popup'
2
+ import '@operato/data-grist'
3
+
4
+ import { css, html } from 'lit-element'
5
+ import { i18next, localize } from '@things-factory/i18n-base'
6
+
7
+ import { PageView } from '@things-factory/shell'
8
+
9
+ class GristModePage extends localize(i18next)(PageView) {
10
+ static get styles() {
11
+ return css`
12
+ :host {
13
+ display: flex;
14
+
15
+ width: 100%;
16
+
17
+ --grid-record-emphasized-background-color: red;
18
+ --grid-record-emphasized-color: yellow;
19
+ }
20
+
21
+ ox-grist {
22
+ flex: 1;
23
+ overflow-y: auto;
24
+
25
+ --grid-record-emphasized-background-color: red;
26
+ --grid-record-emphasized-color: yellow;
27
+ }
28
+
29
+ #headroom {
30
+ display: flex;
31
+ flex-direction: column;
32
+ background-color: var(--primary-color);
33
+ height: 200px;
34
+ align-items: center;
35
+ justify-content: center;
36
+ color: var(--theme-white-color);
37
+ }
38
+
39
+ #modes > * {
40
+ padding: var(--padding-narrow);
41
+ font-size: 1.5em;
42
+ opacity: 0.7;
43
+ }
44
+
45
+ #modes > mwc-icon[active] {
46
+ border: 1px solid var(--status-warning-color);
47
+ border-radius: 9px;
48
+ background-color: rgba(0, 0, 0, 0.3);
49
+ opacity: 1;
50
+ }
51
+
52
+ #filters {
53
+ position: absolute;
54
+ left: 10px;
55
+ bottom: 10px;
56
+ width: calc(100% - 20px);
57
+ }
58
+ `
59
+ }
60
+
61
+ static get properties() {
62
+ return {
63
+ config: Object,
64
+ data: Object,
65
+ mode: String
66
+ }
67
+ }
68
+
69
+ get context() {
70
+ return {
71
+ title: 'Grist Modes and Headroom',
72
+ printable: true
73
+ }
74
+ }
75
+
76
+ get grist() {
77
+ return this.shadowRoot.querySelector('ox-grist')
78
+ }
79
+
80
+ render() {
81
+ const mode = this.mode || 'CARD'
82
+
83
+ return html`
84
+ <ox-grist .config=${this.config} .mode=${mode} auto-fetch .fetchHandler=${this.fetchHandler}>
85
+ <div slot="headroom" id="headroom">
86
+ <h1>HEAD ROOM AREA</h1>
87
+ <div id="modes">
88
+ <mwc-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>view_list</mwc-icon>
89
+ <mwc-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>menu</mwc-icon>
90
+ <mwc-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</mwc-icon>
91
+ </div>
92
+ <div id="tailer">
93
+ <span
94
+ @click=${() => {
95
+ this.grist.reset()
96
+ }}
97
+ >
98
+ Reset
99
+ </span>
100
+ |
101
+ <span
102
+ @click=${() => {
103
+ this.grist.fetch(true)
104
+ }}
105
+ >
106
+ Fetch
107
+ </span>
108
+ </div>
109
+
110
+ <div id="filters">
111
+ <mwc-icon
112
+ @click=${e => {
113
+ const target = e.currentTarget
114
+ this.renderRoot.querySelector('ox-popup-list').open({
115
+ left: target.offsetLeft,
116
+ top: target.offsetTop + target.offsetHeight
117
+ })
118
+ }}
119
+ >sort</mwc-icon
120
+ >
121
+ <mwc-icon
122
+ @click=${e => {
123
+ const target = e.currentTarget
124
+ this.renderRoot.querySelector('ox-popup-list').open({
125
+ left: target.offsetLeft,
126
+ top: target.offsetTop + target.offsetHeight
127
+ })
128
+ }}
129
+ >more_horiz</mwc-icon
130
+ >
131
+ <mwc-icon
132
+ @click=${e => {
133
+ const target = e.currentTarget
134
+ this.renderRoot.querySelector('ox-popup-list').open({
135
+ left: target.offsetLeft,
136
+ top: target.offsetTop + target.offsetHeight
137
+ })
138
+ }}
139
+ >sort</mwc-icon
140
+ >
141
+
142
+ <ox-popup-list alive-on-select>
143
+ <div
144
+ option
145
+ @click=${function (e) {
146
+ const icon = e.currentTarget.querySelector('mwc-icon')
147
+ icon.innerHTML = icon.innerHTML == 'check' ? '' : 'check'
148
+ }}
149
+ >
150
+ <mwc-icon slot="icon" style="width: 20px;height: 20px;"></mwc-icon>
151
+ <span>click me to toggle</span>
152
+ </div>
153
+ <div
154
+ option
155
+ @click=${function (e) {
156
+ const icon = e.currentTarget.querySelector('mwc-icon')
157
+ icon.innerHTML = icon.innerHTML == 'check' ? '' : 'check'
158
+ }}
159
+ >
160
+ <mwc-icon slot="icon" style="width: 20px;height: 20px;"></mwc-icon>
161
+ <span>click me to toggle</span>
162
+ </div>
163
+ </ox-popup-list>
164
+ </div>
165
+ </div>
166
+ </ox-grist>
167
+ `
168
+ }
169
+
170
+ async fetchHandler({ page, limit, sorters = [] }) {
171
+ var total = 120993
172
+ var start = (page - 1) * limit
173
+
174
+ await new Promise(resolve => setTimeout(resolve, 500))
175
+
176
+ return {
177
+ total,
178
+ records: Array(limit * page > total ? total % limit : limit)
179
+ .fill()
180
+ .map((item, idx) => {
181
+ return {
182
+ id: idx,
183
+ name: idx % 2 ? `shnam-${start + idx + 1}` : `heartyoh-${start + idx + 1}`,
184
+ description: idx % 2 ? `hatiolab manager-${start + idx + 1}` : `hatiosea manager-${start + idx + 1}`,
185
+ email: idx % 2 ? `shnam-${start + idx + 1}@gmail.com` : `heartyoh-${start + idx + 1}@gmail.com`,
186
+ active: Math.round(Math.random() * 2) % 2 ? true : false,
187
+ barcode: idx % 2 ? `1234567890${start + idx + 1}` : `0987654321${start + idx + 1}`,
188
+ company:
189
+ idx % 2
190
+ ? {
191
+ id: '2',
192
+ name: 'HatioLAB',
193
+ description: `경기도 성남시-${start + idx + 1}`
194
+ }
195
+ : {
196
+ id: '3',
197
+ name: 'HatioSEA',
198
+ description: `말레이시아 세티아알람-${start + idx + 1}`
199
+ },
200
+ image:
201
+ idx % 2
202
+ ? `http://www.hatiolab.com/assets/img/operato-biz3.png`
203
+ : `http://www.hatiolab.com/assets/img/thingsboard-30.png`,
204
+ role: ['admin', 'worker', 'tester'][idx % 3],
205
+ color: idx % 2 ? `#87f018` : `#180f87`,
206
+ rate: Math.round(Math.random() * 100),
207
+ dynamicType: ['text', 'email', 'checkbox', 'color', 'progress', 'barcode'][idx % 5],
208
+ dynamicValue: ['abcdefghijkl', 'heartyoh@hatiolab.com', 'true', 'orange', '50', '1234567890'][idx % 5],
209
+ homepage:
210
+ idx % 2 ? `http://hatiolab.com/${start + idx + 1}` : `http://deadpool.hatiolab.com/${start + idx + 1}`,
211
+ json5: {
212
+ abc: 'abc',
213
+ value: 123
214
+ },
215
+ createdAt: Date.now(),
216
+ updatedAt: Date.now()
217
+ }
218
+ })
219
+ }
220
+ }
221
+
222
+ get gristConfig() {
223
+ return {
224
+ list: {
225
+ thumbnail: function (record, rowIndex) {
226
+ return html` <img src=${record.image} style="width: 100%; height: 100%;" /> `
227
+ },
228
+ fields: ['name', 'description'],
229
+ details: ['role', 'email']
230
+ },
231
+ columns: [
232
+ {
233
+ type: 'gutter',
234
+ gutterName: 'dirty'
235
+ },
236
+ {
237
+ type: 'gutter',
238
+ gutterName: 'sequence'
239
+ },
240
+ {
241
+ type: 'gutter',
242
+ gutterName: 'row-selector',
243
+ multiple: true
244
+ },
245
+ {
246
+ type: 'gutter',
247
+ gutterName: 'button',
248
+ icon: 'edit',
249
+ handlers: {
250
+ click: 'record-view'
251
+ }
252
+ },
253
+ {
254
+ type: 'string',
255
+ name: 'id',
256
+ hidden: true
257
+ },
258
+ {
259
+ type: 'link',
260
+ name: 'name',
261
+ label: true,
262
+ header: 'name',
263
+ record: {
264
+ editable: true,
265
+ options: {
266
+ // href: 'http://hatiolab.com',
267
+ href: function (column, record, rowIndex) {
268
+ return record['homepage']
269
+ }
270
+ // target: '_blank'
271
+ }
272
+ },
273
+ sortable: true,
274
+ width: 120
275
+ },
276
+ {
277
+ type: 'string',
278
+ name: 'description',
279
+ header: 'description',
280
+ record: {
281
+ editable: true,
282
+ align: 'left'
283
+ },
284
+ width: 200,
285
+ handlers: {
286
+ dblclick: (columns, data, column, record, rowIndex) => {
287
+ alert(`${column.name} ${record[column.name]}, row : ${rowIndex}`)
288
+ }
289
+ }
290
+ },
291
+ {
292
+ type: 'email',
293
+ name: 'email',
294
+ label: true,
295
+ header: 'email',
296
+ record: {
297
+ editable: true
298
+ },
299
+ sortable: true,
300
+ width: 130,
301
+ validation: function (after, before, record, column) {
302
+ if (after.indexOf('@') == -1) {
303
+ document.dispatchEvent(
304
+ new CustomEvent('notify', {
305
+ detail: {
306
+ type: 'error',
307
+ message: `invalid value - ${after}`
308
+ }
309
+ })
310
+ )
311
+ return false
312
+ }
313
+ return true
314
+ }
315
+ },
316
+ {
317
+ type: 'boolean',
318
+ name: 'active',
319
+ header: 'active',
320
+ record: {
321
+ editable: true
322
+ },
323
+ handlers: {
324
+ dblclick: () => {
325
+ const grist = document.querySelector('ox-grist')
326
+ console.log(grist.dirtyRecords)
327
+ }
328
+ },
329
+ sortable: true,
330
+ width: 60
331
+ },
332
+ {
333
+ type: 'select',
334
+ name: 'role',
335
+ label: true,
336
+ header: 'role',
337
+ record: {
338
+ options: ['admin', 'worker', 'tester'],
339
+ editable: true
340
+ },
341
+ sortable: true,
342
+ width: 120
343
+ },
344
+ {
345
+ type: 'color',
346
+ name: 'color',
347
+ header: 'color',
348
+ record: {
349
+ editable: true
350
+ },
351
+ sortable: true,
352
+ width: 50
353
+ },
354
+ {
355
+ type: 'float',
356
+ name: 'rate',
357
+ header: 'rate',
358
+ record: {
359
+ align: 'right',
360
+ editable: true
361
+ },
362
+ sortable: true,
363
+ width: 50
364
+ },
365
+ {
366
+ type: 'json5',
367
+ name: 'json5',
368
+ header: 'JSON5',
369
+ width: 200
370
+ },
371
+ {
372
+ type: 'datetime',
373
+ name: 'updatedAt',
374
+ header: 'updated at',
375
+ record: {
376
+ editable: true
377
+ },
378
+ sortable: true,
379
+ width: 180
380
+ },
381
+ {
382
+ type: 'datetime',
383
+ name: 'createdAt',
384
+ header: 'created at',
385
+ record: {
386
+ editable: true
387
+ },
388
+ sortable: true,
389
+ width: 180
390
+ }
391
+ ],
392
+ rows: {
393
+ selectable: {
394
+ multiple: true
395
+ },
396
+ handlers: {
397
+ click: 'select-row-toggle'
398
+ },
399
+ classifier: function (record, rowIndex) {
400
+ const rate = record['rate']
401
+ const emphasized =
402
+ rate < 10 ? ['black', 'white'] : rate < 25 ? ['yellow', 'blue'] : rate < 40 ? ['cyan', 'red'] : undefined
403
+ return {
404
+ emphasized
405
+ }
406
+ }
407
+ },
408
+ sorters: [
409
+ {
410
+ name: 'name',
411
+ desc: true
412
+ },
413
+ {
414
+ name: 'email'
415
+ }
416
+ ],
417
+ pagination: {
418
+ pages: [20, 30, 50, 100, 200]
419
+ }
420
+ }
421
+ }
422
+
423
+ pageUpdated(changes, lifecycle) {
424
+ if (this.active) {
425
+ this.grist.fetch()
426
+ }
427
+ }
428
+
429
+ pageInitialized() {
430
+ this.config = this.gristConfig
431
+
432
+ this.page = 1
433
+ this.limit = 50
434
+ }
435
+ }
436
+
437
+ window.customElements.define('grist-mode-page', GristModePage)
@@ -1,7 +1,7 @@
1
1
  import { css, html } from 'lit-element'
2
-
3
- import { getEditor, getRenderer } from '@things-factory/grist-ui'
2
+ import { getEditor, getRenderer } from '@operato/data-grist'
4
3
  import { i18next, localize } from '@things-factory/i18n-base'
4
+
5
5
  import { PageView } from '@things-factory/shell'
6
6
  import { isMobileDevice } from '@things-factory/utils'
7
7
 
@@ -17,7 +17,7 @@ class GristPage extends localize(i18next)(PageView) {
17
17
  --grid-record-emphasized-color: yellow;
18
18
  }
19
19
 
20
- data-grist {
20
+ ox-grist {
21
21
  width: 100%;
22
22
  height: 100%;
23
23
  }
@@ -39,16 +39,16 @@ class GristPage extends localize(i18next)(PageView) {
39
39
  }
40
40
 
41
41
  get grist() {
42
- return this.shadowRoot.querySelector('data-grist')
42
+ return this.shadowRoot.querySelector('ox-grist')
43
43
  }
44
44
 
45
45
  render() {
46
46
  return html`
47
- <data-grist
47
+ <ox-grist
48
48
  .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
49
49
  .config=${this.config}
50
50
  .fetchHandler=${this.fetchHandler}
51
- ></data-grist>
51
+ ></ox-grist>
52
52
  `
53
53
  }
54
54
 
@@ -0,0 +1,189 @@
1
+ import '@things-factory/barcode-ui'
2
+
3
+ import { css, html } from 'lit-element'
4
+ import { connect } from 'pwa-helpers/connect-mixin.js'
5
+
6
+ import { PageView, store } from '@things-factory/shell'
7
+
8
+ import { referencePageStyles } from './reference-page-styles'
9
+
10
+ class LabelScanPage extends connect(store)(PageView) {
11
+ static get styles() {
12
+ return [
13
+ referencePageStyles,
14
+ css`
15
+ :host {
16
+ display: block;
17
+ }
18
+
19
+ div {
20
+ overflow-wrap: anywhere;
21
+ }
22
+ `
23
+ ]
24
+ }
25
+
26
+ static get properties() {
27
+ return {
28
+ bcid: String,
29
+ value: String,
30
+ bcWidth: Number,
31
+ bcHeight: Number,
32
+ bcScale: Number,
33
+ padding: Number
34
+ }
35
+ }
36
+
37
+ get context() {
38
+ return {
39
+ title: 'Barcode Scan Input and Tag Component'
40
+ }
41
+ }
42
+
43
+ render() {
44
+ const bcid = this.bcid || 'code128'
45
+ const value = this.value || '213439240572'
46
+ const bcWidth = this.bcWidth || 30
47
+ const bcHeight = this.bcHeight || 30
48
+ const bcScale = this.bcScale || 3
49
+ const padding = this.padding || 5
50
+
51
+ return html`
52
+ <section>
53
+ <h2>Barcode Scanable Input</h2>
54
+ <p>
55
+ barcode-scanable-input 컴포넌트는 카메라 입력을 통해서 바코드를 스캔하고, 스캔한 결과를 입력 필드에 적용한다.
56
+ </p>
57
+ <barcode-scanable-input
58
+ name="barcode-input"
59
+ custom-input
60
+ @keypress=${e => {
61
+ if (e.keyCode === 13) {
62
+ e.preventDefault()
63
+ this.value = e.target.shadowRoot.querySelector('input').value
64
+ console.log('input completed.', e.target.value)
65
+ }
66
+ }}
67
+ ></barcode-scanable-input>
68
+ </section>
69
+
70
+ <section>
71
+ <h2>Input Field with Barcode Image</h2>
72
+ <p>barcode-input 컴포넌트는 입력필드에 입력된 결과를 바코드 이미지로도 보여준다.</p>
73
+
74
+ <label>bacode type</label>
75
+ <select
76
+ @change=${e => {
77
+ this.bcid = e.target.value
78
+ }}
79
+ .value=${bcid}
80
+ >
81
+ <option value="code39">code39</option>
82
+ <option value="code128" selected>code128</option>
83
+ <option value="qrcode">QR</option>
84
+ </select>
85
+
86
+ <label>bacode width</label>
87
+ <select
88
+ @change=${e => {
89
+ this.bcWidth = Number(e.target.value)
90
+ }}
91
+ .value=${bcWidth}
92
+ >
93
+ <option value="10">10</option>
94
+ <option value="30" selected>30</option>
95
+ <option value="50">50</option>
96
+ </select>
97
+
98
+ <label>bacode height</label>
99
+ <select
100
+ @change=${e => {
101
+ this.bcHeight = Number(e.target.value)
102
+ }}
103
+ .value=${bcHeight}
104
+ >
105
+ <option value="10">10</option>
106
+ <option value="30" selected>30</option>
107
+ <option value="50">50</option>
108
+ </select>
109
+
110
+ <label>bacode scale</label>
111
+ <select
112
+ @change=${e => {
113
+ this.bcScale = Number(e.target.value)
114
+ }}
115
+ .value=${bcScale}
116
+ >
117
+ <option value="2">2</option>
118
+ <option value="3" selected>3</option>
119
+ <option value="4">4</option>
120
+ </select>
121
+
122
+ <label>padding</label>
123
+ <select
124
+ @change=${e => {
125
+ this.padding = Number(e.target.value)
126
+ }}
127
+ .value=${padding}
128
+ >
129
+ <option value="3">3</option>
130
+ <option value="5" selected>5</option>
131
+ <option value="10">10</option>
132
+ </select>
133
+
134
+ <barcode-input
135
+ .bcid=${bcid}
136
+ .value=${value}
137
+ .bcWidth=${bcWidth}
138
+ .bcHeight=${bcHeight}
139
+ .bcScale=${bcScale}
140
+ @change=${e => {
141
+ this.value = e.target.value
142
+ }}
143
+ ></barcode-input>
144
+ </section>
145
+
146
+ <section>
147
+ <h2>Barcode Image Component</h2>
148
+ <p>
149
+ barcode-tag 컴포넌트는 바코드 이미지를 보여준다. 바코드 이미지를 클릭하면 이미지 파일을 다운로드 받을 수 있다.
150
+ </p>
151
+
152
+ <barcode-tag
153
+ .bcid=${bcid}
154
+ .value=${value}
155
+ .bcWidth=${bcWidth}
156
+ .bcHeight=${bcHeight}
157
+ .bcScale=${bcScale}
158
+ .padding=${padding}
159
+ ></barcode-tag>
160
+ </section>
161
+ `
162
+ }
163
+
164
+ updated(changes) {
165
+ /*
166
+ * If this page properties are changed, this callback will be invoked.
167
+ * This callback will be called back only when this page is activated.
168
+ */
169
+ if (changes.has('itemId') || changes.has('params')) {
170
+ /* do something */
171
+ }
172
+ }
173
+
174
+ stateChanged(state) {
175
+ /*
176
+ * application wide state changed
177
+ *
178
+ */
179
+ }
180
+
181
+ pageUpdated(changes, lifecycle, before) {
182
+ if (this.active) {
183
+ } else {
184
+ /* this page is deactivated */
185
+ }
186
+ }
187
+ }
188
+
189
+ customElements.define('label-scan-page', LabelScanPage)
@@ -1,6 +1,8 @@
1
- import { css, html } from 'lit-element'
1
+ import '@operato/data-grist'
2
2
 
3
+ import { css, html } from 'lit-element'
3
4
  import { i18next, localize } from '@things-factory/i18n-base'
5
+
4
6
  import { PageView } from '@things-factory/shell'
5
7
 
6
8
  class ReportPage extends localize(i18next)(PageView) {
@@ -12,7 +14,7 @@ class ReportPage extends localize(i18next)(PageView) {
12
14
  width: 100%;
13
15
  }
14
16
 
15
- data-report {
17
+ ox-report {
16
18
  width: 100%;
17
19
  height: 100%;
18
20
  }
@@ -37,11 +39,11 @@ class ReportPage extends localize(i18next)(PageView) {
37
39
  }
38
40
 
39
41
  get report() {
40
- return this.shadowRoot.querySelector('data-report')
42
+ return this.shadowRoot.querySelector('ox-report')
41
43
  }
42
44
 
43
45
  render() {
44
- return html` <data-report .config=${this.config} .fetchHandler=${this.fetchHandler}></data-report> `
46
+ return html` <ox-report .config=${this.config} .fetchHandler=${this.fetchHandler}></ox-report> `
45
47
  }
46
48
 
47
49
  async fetchHandler({ page, /*limit,*/ sorters = [] }) {