@operato/data-grist 2.0.0-alpha.86 → 2.0.0-alpha.88

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 (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/src/data-grid/data-grid-header.js +9 -0
  3. package/dist/src/data-grid/data-grid-header.js.map +1 -1
  4. package/dist/src/data-grid/data-grid.js +3 -1
  5. package/dist/src/data-grid/data-grid.js.map +1 -1
  6. package/dist/src/data-grist.js +1 -0
  7. package/dist/src/data-grist.js.map +1 -1
  8. package/dist/stories/accumulator.stories.js +2 -2
  9. package/dist/stories/accumulator.stories.js.map +1 -1
  10. package/dist/stories/default-filters.stories.js +1 -1
  11. package/dist/stories/default-filters.stories.js.map +1 -1
  12. package/dist/stories/dynamic-editable.stories.js +28 -0
  13. package/dist/stories/dynamic-editable.stories.js.map +1 -1
  14. package/dist/stories/fixed-column.stories.js +0 -1
  15. package/dist/stories/fixed-column.stories.js.map +1 -1
  16. package/dist/stories/grid-setting.stories.d.ts +36 -0
  17. package/dist/stories/grid-setting.stories.js +517 -0
  18. package/dist/stories/grid-setting.stories.js.map +1 -0
  19. package/dist/stories/grist-modes.stories.js +0 -1
  20. package/dist/stories/grist-modes.stories.js.map +1 -1
  21. package/dist/stories/group-header.stories.js +0 -1
  22. package/dist/stories/group-header.stories.js.map +1 -1
  23. package/dist/stories/textarea.stories.js +1 -1
  24. package/dist/stories/textarea.stories.js.map +1 -1
  25. package/dist/stories/tree-column-with-checkbox.stories.js +1 -2
  26. package/dist/stories/tree-column-with-checkbox.stories.js.map +1 -1
  27. package/dist/stories/tree-column.stories.js +0 -1
  28. package/dist/stories/tree-column.stories.js.map +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +5 -5
  31. package/src/data-grid/data-grid-header.ts +9 -0
  32. package/src/data-grid/data-grid.ts +3 -1
  33. package/src/data-grist.ts +1 -0
  34. package/stories/accumulator.stories.ts +2 -2
  35. package/stories/default-filters.stories.ts +1 -1
  36. package/stories/dynamic-editable.stories.ts +28 -0
  37. package/stories/fixed-column.stories.ts +0 -1
  38. package/stories/grid-setting.stories.ts +553 -0
  39. package/stories/grist-modes.stories.ts +0 -1
  40. package/stories/group-header.stories.ts +0 -1
  41. package/stories/textarea.stories.ts +1 -1
  42. package/stories/tree-column-with-checkbox.stories.ts +1 -2
  43. package/stories/tree-column.stories.ts +0 -1
@@ -249,7 +249,7 @@ const Template: Story<ArgTypes> = ({ config, mode = 'GRID', urlParamsSensitive =
249
249
  flex-direction: row;
250
250
  align-items: center;
251
251
  padding: var(--padding-default) var(--padding-wide);
252
- background-color: var(--theme-white-color);
252
+ background-color: unset;
253
253
  box-shadow: var(--box-shadow);
254
254
 
255
255
  --md-icon-size: 24px;
@@ -295,7 +295,7 @@ const Template: Story<ArgTypes> = ({ config, mode = 'GRID', urlParamsSensitive =
295
295
  }
296
296
 
297
297
  #add {
298
- width: 50px;
298
+ margin-left: auto;
299
299
  text-align: right;
300
300
  }
301
301
 
@@ -161,7 +161,7 @@ const Template: Story<ArgTypes> = ({ headerFilter }: ArgTypes) =>
161
161
  flex-direction: row;
162
162
  align-items: center;
163
163
  padding: var(--padding-default) var(--padding-wide);
164
- background-color: var(--theme-white-color);
164
+ background-color: unset;
165
165
  box-shadow: var(--box-shadow);
166
166
 
167
167
  --md-icon-size: 24px;
@@ -272,6 +272,34 @@ const Template: Story<ArgTypes> = ({ headerFilter }: ArgTypes) =>
272
272
  margin-right: var(--margin-default);
273
273
  }
274
274
 
275
+ #add {
276
+ text-align: right;
277
+ }
278
+
279
+ #add button {
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+
284
+ background-color: var(--primary-color);
285
+ border: 0;
286
+ border-radius: 50%;
287
+ padding: 5px;
288
+ width: 32px;
289
+ height: 32px;
290
+ cursor: pointer;
291
+ }
292
+
293
+ #add button:hover {
294
+ background-color: var(--focus-background-color);
295
+ box-shadow: var(--box-shadow);
296
+ }
297
+
298
+ #add button md-icon {
299
+ font-size: 1.5em;
300
+ color: var(--theme-white-color);
301
+ }
302
+
275
303
  @media only screen and (max-width: 460px) {
276
304
  #filters {
277
305
  flex-direction: column;
@@ -420,7 +420,6 @@ const Template: Story<ArgTypes> = ({ config }: ArgTypes) =>
420
420
  }
421
421
 
422
422
  #add {
423
- width: 50px;
424
423
  text-align: right;
425
424
  }
426
425
 
@@ -0,0 +1,553 @@
1
+ import '../src/index.js'
2
+ import '../src/filters/filters-form.js'
3
+ import '../src/sorters/sorters-control.js'
4
+ import '../src/record-view/record-creator.js'
5
+ import '@operato/popup/ox-popup-list.js'
6
+ import '@material/web/icon/icon.js'
7
+
8
+ import { html, TemplateResult } from 'lit'
9
+
10
+ import {
11
+ ColumnConfig,
12
+ FetchHandler,
13
+ GristClassifier,
14
+ GristEventHandlerSet,
15
+ GristRecord,
16
+ ValidationCallback
17
+ } from '../src/types.js'
18
+
19
+ const fetchHandler: FetchHandler = async ({ page, limit }) => {
20
+ var total = 120993
21
+ var start = (page! - 1) * limit!
22
+
23
+ await new Promise(resolve => setTimeout(resolve, 500))
24
+
25
+ return {
26
+ total,
27
+ records: Array(limit! * page! > total ? total % limit! : limit)
28
+ .fill('')
29
+ .map((item, idx) => {
30
+ return {
31
+ id: String(idx),
32
+ name: idx % 2 ? `shnam-${start + idx + 1}` : `heartyoh-${start + idx + 1}`,
33
+ description: idx % 2 ? `hatiolabmanager${start + idx + 1}1234567890` : `hatiosea manager-${start + idx + 1}`,
34
+ email: idx % 2 ? `shnam-${start + idx + 1}@gmail.com` : `heartyoh-${start + idx + 1}@gmail.com`,
35
+ active: Math.round(Math.random() * 2) % 2 ? true : false,
36
+ barcode: idx % 2 ? `1234567890${start + idx + 1}` : `0987654321${start + idx + 1}`,
37
+ company:
38
+ idx % 2
39
+ ? {
40
+ id: '2',
41
+ name: 'HatioLAB',
42
+ description: `경기도 성남시-${start + idx + 1}`
43
+ }
44
+ : {
45
+ id: '3',
46
+ name: 'HatioSEA',
47
+ description: `말레이시아 세티아알람-${start + idx + 1}`
48
+ },
49
+ thumbnail:
50
+ idx % 4 === 0
51
+ ? '' /* no source */
52
+ : idx % 4 === 1
53
+ ? `http://www.hatiolab.com/assets/img/operato-biz3.png`
54
+ : idx % 4 === 2
55
+ ? `http://www.hatiolab.com/assets/img/thingsboard-30.png`
56
+ : `http://www.hatiolab.com/wrong-url.png` /* wrong source */,
57
+ role: ['admin', 'worker', 'tester'][idx % 3],
58
+ color: idx % 2 ? `#87f018` : `#180f87`,
59
+ rate: Math.round(Math.random() * 100),
60
+ dynamicType: ['text', 'email', 'checkbox', 'color', 'progress', 'barcode'][idx % 5],
61
+ dynamicValue: ['abcdefghijkl', 'heartyoh@hatiolab.com', 'true', 'orange', '50', '1234567890'][idx % 5],
62
+ homepage:
63
+ idx % 2 ? `http://hatiolab.com/${start + idx + 1}` : `http://deadpool.hatiolab.com/${start + idx + 1}`,
64
+ json5: {
65
+ abc: 'abc',
66
+ value: 123
67
+ },
68
+ createdAt: Date.now(),
69
+ updatedAt: Date.now()
70
+ }
71
+ })
72
+ }
73
+ }
74
+
75
+ const config = {
76
+ list: {
77
+ thumbnail: 'thumbnail',
78
+ fields: ['name', 'description'],
79
+ details: ['role', 'email']
80
+ },
81
+ columns: [
82
+ {
83
+ type: 'gutter',
84
+ gutterName: 'dirty'
85
+ },
86
+ {
87
+ type: 'gutter',
88
+ gutterName: 'sequence'
89
+ },
90
+ {
91
+ type: 'gutter',
92
+ gutterName: 'row-selector',
93
+ multiple: true
94
+ },
95
+ {
96
+ type: 'gutter',
97
+ gutterName: 'button',
98
+ icon: 'edit',
99
+ title: 'edit',
100
+ handlers: {
101
+ click: function () {
102
+ console.log('clicked')
103
+ }
104
+ }
105
+ },
106
+ {
107
+ type: 'gutter',
108
+ gutterName: 'button',
109
+ icon: 'add',
110
+ title: 'add',
111
+ handlers: {
112
+ click: 'record-copy'
113
+ }
114
+ },
115
+ {
116
+ type: 'gutter',
117
+ gutterName: 'button',
118
+ icon: 'arrow_downward',
119
+ title: 'download',
120
+ handlers: {
121
+ click: 'move-down'
122
+ }
123
+ },
124
+ {
125
+ type: 'string',
126
+ name: 'id',
127
+ hidden: true
128
+ },
129
+ {
130
+ type: 'link',
131
+ name: 'name',
132
+ label: true,
133
+ fixed: true,
134
+ header: 'name',
135
+ record: {
136
+ editable: true,
137
+ options: {
138
+ // href: 'http://hatiolab.com',
139
+ href: function (column: ColumnConfig, record: GristRecord, rowIndex: number) {
140
+ return record['homepage']
141
+ },
142
+ target: '_blank'
143
+ }
144
+ },
145
+ filter: 'search',
146
+ sortable: true,
147
+ width: 120
148
+ },
149
+ {
150
+ type: 'string',
151
+ name: 'description',
152
+ header: 'description',
153
+ filter: 'search',
154
+ record: {
155
+ editable: true,
156
+ align: 'left'
157
+ },
158
+ width: 200,
159
+ handlers: {
160
+ click: (columns, data, column, record, rowIndex, target) => {
161
+ alert(`${column!.name} ${record![column!.name]}, row : ${rowIndex}`)
162
+ }
163
+ } as GristEventHandlerSet
164
+ },
165
+ {
166
+ type: 'email',
167
+ name: 'email',
168
+ label: true,
169
+ header: 'email',
170
+ record: {
171
+ editable: true
172
+ },
173
+ filter: 'search',
174
+ sortable: true,
175
+ width: 130,
176
+ validation: function (after, before, record, column) {
177
+ if (after.indexOf('@') == -1) {
178
+ document.dispatchEvent(
179
+ new CustomEvent('notify', {
180
+ detail: {
181
+ type: 'error',
182
+ message: `invalid value - ${after}`
183
+ }
184
+ })
185
+ )
186
+ return false
187
+ }
188
+ return true
189
+ } as ValidationCallback
190
+ },
191
+ {
192
+ type: 'boolean',
193
+ name: 'active',
194
+ header: 'active',
195
+ record: {
196
+ editable: true
197
+ },
198
+ filter: true,
199
+ handlers: {
200
+ dblclick: () => {
201
+ const grist = document.querySelector('ox-grist') as any
202
+ console.log(grist!.dirtyRecords)
203
+ }
204
+ },
205
+ sortable: true,
206
+ width: 60
207
+ },
208
+ {
209
+ type: 'select',
210
+ name: 'role',
211
+ label: true,
212
+ header: 'role',
213
+ record: {
214
+ options: ['', 'admin', 'worker', 'tester'],
215
+ editable: true
216
+ },
217
+ filter: true,
218
+ sortable: true,
219
+ width: 120
220
+ },
221
+ {
222
+ type: 'color',
223
+ name: 'color',
224
+ header: 'color',
225
+ record: {
226
+ editable: true
227
+ },
228
+ sortable: true,
229
+ width: 50
230
+ },
231
+ {
232
+ type: 'float',
233
+ name: 'rate',
234
+ header: 'rate',
235
+ record: {
236
+ align: 'right',
237
+ editable: true,
238
+ defaultValue: 10000.1
239
+ },
240
+ filter: {
241
+ operator: 'between',
242
+ value: [1, 100]
243
+ },
244
+ sortable: true,
245
+ width: 50
246
+ },
247
+ {
248
+ type: 'json5',
249
+ name: 'json5',
250
+ header: 'JSON5',
251
+ width: 200
252
+ },
253
+ {
254
+ type: 'image',
255
+ name: 'thumbnail',
256
+ header: 'thumbnail',
257
+ record: {
258
+ editable: true
259
+ },
260
+ width: 120
261
+ },
262
+ {
263
+ type: 'datetime',
264
+ name: 'updatedAt',
265
+ header: 'updated at',
266
+ record: {
267
+ editable: true,
268
+ defaultValue: {
269
+ name: 'now'
270
+ }
271
+ },
272
+ filter: {
273
+ operator: 'between',
274
+ type: 'datetime',
275
+ value: [
276
+ {
277
+ name: 'today',
278
+ params: {
279
+ type: 'datetime'
280
+ }
281
+ },
282
+ {
283
+ name: 'now',
284
+ params: {
285
+ type: 'datetime',
286
+ relativeDays: 1
287
+ }
288
+ }
289
+ ]
290
+ },
291
+ sortable: true,
292
+ width: 180
293
+ },
294
+ {
295
+ type: 'datetime',
296
+ name: 'createdAt',
297
+ header: 'created at',
298
+ record: {
299
+ editable: false
300
+ },
301
+ sortable: true,
302
+ width: 180
303
+ }
304
+ ],
305
+ rows: {
306
+ selectable: {
307
+ multiple: true
308
+ },
309
+ handlers: {
310
+ focus: 'select-row-toggle'
311
+ },
312
+ classifier: function (record, rowIndex) {
313
+ const rate = record['rate']
314
+ const emphasized =
315
+ rate < 10 ? ['black', 'white'] : rate < 25 ? ['yellow', 'blue'] : rate < 40 ? ['cyan', 'red'] : undefined
316
+ return {
317
+ emphasized
318
+ }
319
+ } as GristClassifier
320
+ },
321
+ sorters: [
322
+ {
323
+ name: 'name',
324
+ desc: true
325
+ },
326
+ {
327
+ name: 'email'
328
+ }
329
+ ],
330
+ pagination: {
331
+ pages: [20, 30, 50, 100, 200]
332
+ }
333
+ }
334
+
335
+ export default {
336
+ title: 'grid setting',
337
+ component: 'ox-grist',
338
+ argTypes: {
339
+ config: { control: 'object' },
340
+ mode: { control: 'select', options: ['GRID', 'LIST', 'CARD'] },
341
+ urlParamsSensitive: { control: 'boolean' }
342
+ }
343
+ }
344
+
345
+ interface Story<T> {
346
+ (args: T): TemplateResult
347
+ args?: Partial<T>
348
+ argTypes?: Record<string, unknown>
349
+ }
350
+
351
+ interface ArgTypes {
352
+ config: object
353
+ mode: string
354
+ urlParamsSensitive: boolean
355
+ fetchHandler: object
356
+ }
357
+
358
+ const Template: Story<ArgTypes> = ({ config, mode = 'GRID', urlParamsSensitive = false, fetchHandler }: ArgTypes) =>
359
+ html` <link
360
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL@20..48,100..700,0..1"
361
+ rel="stylesheet"
362
+ />
363
+ <link
364
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL@20..48,100..700,0..1"
365
+ rel="stylesheet"
366
+ />
367
+ <link
368
+ href="https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL@20..48,100..700,0..1"
369
+ rel="stylesheet"
370
+ />
371
+
372
+ <link href="/themes/app-theme.css" rel="stylesheet" />
373
+ <link href="/themes/oops-theme.css" rel="stylesheet" />
374
+ <link href="/themes/grist-theme.css" rel="stylesheet" />
375
+
376
+ <style>
377
+ ox-filters-form {
378
+ --input-gap-vertical: 8px;
379
+ --input-gap-horizontal: 16px;
380
+
381
+ --ox-filters-input-placeholder-color: var(--primary-color);
382
+
383
+ --ox-filters-input-border: 1px solid rgba(0, 0, 0, 0.2);
384
+ --ox-filters-input-focus-border: 1px solid var(--primary-color);
385
+ --ox-filters-input-font: normal 14px var(--theme-font);
386
+ --ox-filters-input-color: var(--primary-text-color);
387
+ --ox-filters-input-focus-color: var(--primary-color);
388
+ --ox-filters-label-font: normal 14px var(--theme-font);
389
+ --ox-filters-label-color: var(--primary-text-color);
390
+
391
+ --ox-filters-form-gap: var(--input-gap-vertical, 8px) var(--input-gap-horizontal, 16px);
392
+ --ox-filters-input-padding: 6px 2px;
393
+ }
394
+ </style>
395
+
396
+ <style>
397
+ ox-grist {
398
+ width: 100%;
399
+ height: 600px;
400
+ }
401
+
402
+ [slot='headroom'] {
403
+ display: flex;
404
+ flex-direction: row;
405
+ align-items: center;
406
+ padding: var(--padding-default) var(--padding-wide);
407
+ background-color: var(--theme-white-color);
408
+ box-shadow: var(--box-shadow);
409
+
410
+ --md-icon-size: 24px;
411
+ }
412
+ #sorters md-icon,
413
+ #modes md-icon {
414
+ --md-icon-size: 18px;
415
+ }
416
+ #sorters {
417
+ margin-left: auto;
418
+ margin-right: var(--margin-default);
419
+ padding-left: var(--padding-narrow);
420
+ border-bottom: var(--border-dark-color);
421
+ position: relative;
422
+ color: var(--secondary-color);
423
+ font-size: var(--fontsize-default);
424
+ user-select: none;
425
+ }
426
+
427
+ #sorters > * {
428
+ padding: var(--padding-narrow);
429
+ vertical-align: middle;
430
+ }
431
+
432
+ #modes > * {
433
+ padding: var(--padding-narrow);
434
+ opacity: 0.5;
435
+ color: var(--primary-text-color);
436
+ cursor: pointer;
437
+ }
438
+
439
+ #modes > md-icon[active] {
440
+ border-radius: 9px;
441
+ background-color: rgba(var(--primary-color-rgb), 0.05);
442
+ opacity: 1;
443
+ color: var(--secondary-text-color);
444
+ cursor: default;
445
+ }
446
+
447
+ #modes > md-icon:hover {
448
+ opacity: 1;
449
+ color: var(--secondary-text-color);
450
+ }
451
+
452
+ #add {
453
+ text-align: right;
454
+ }
455
+
456
+ #add button {
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+
461
+ background-color: var(--primary-color);
462
+ border: 0;
463
+ border-radius: 50%;
464
+ padding: 5px;
465
+ width: 32px;
466
+ height: 32px;
467
+ cursor: pointer;
468
+ }
469
+
470
+ #add button:hover {
471
+ background-color: var(--focus-background-color);
472
+ box-shadow: var(--box-shadow);
473
+ }
474
+
475
+ #add button md-icon {
476
+ font-size: 1.5em;
477
+ color: var(--theme-white-color);
478
+ }
479
+
480
+ #filters {
481
+ display: flex;
482
+ justify-content: center;
483
+ align-items: center;
484
+ }
485
+
486
+ #filters * {
487
+ margin-right: var(--margin-default);
488
+ }
489
+
490
+ [slot='setting'] {
491
+ --md-icon-size: 18px;
492
+ }
493
+
494
+ @media only screen and (max-width: 460px) {
495
+ #filters {
496
+ flex-direction: column;
497
+ }
498
+
499
+ #modes {
500
+ display: none;
501
+ }
502
+ }
503
+ </style>
504
+
505
+ <ox-grist
506
+ .config=${config}
507
+ .mode=${mode}
508
+ .fetchHandler=${fetchHandler}
509
+ ?url-params-sensitive=${urlParamsSensitive}
510
+ @filters-change=${(e: Event) => console.log('filters', (e.target as any).filters)}
511
+ >
512
+ <div slot="headroom">
513
+ <div id="filters">
514
+ <ox-filters-form autofocus></ox-filters-form>
515
+ </div>
516
+
517
+ <div id="sorters">
518
+ Sort
519
+ <md-icon
520
+ @click=${(e: Event) => {
521
+ const target = e.currentTarget as HTMLElement
522
+ ;(target.closest('#sorters')!.querySelector('#sorter-control') as any).open({
523
+ right: 0,
524
+ top: target.offsetTop + target.offsetHeight
525
+ })
526
+ }}
527
+ >expand_more</md-icon
528
+ >
529
+ <ox-popup id="sorter-control">
530
+ <ox-sorters-control> </ox-sorters-control>
531
+ </ox-popup>
532
+ </div>
533
+
534
+ <div id="modes">
535
+ <md-icon @click=${() => (mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
536
+ <md-icon @click=${() => (mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
537
+ <md-icon @click=${() => (mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
538
+ </div>
539
+
540
+ <ox-record-creator id="add" light-popup>
541
+ <button><md-icon>add</md-icon></button>
542
+ </ox-record-creator>
543
+ </div>
544
+
545
+ <md-icon slot="setting" @click=${(e: MouseEvent) => alert('setting clicked')}>tune</md-icon>
546
+ </ox-grist>`
547
+
548
+ export const Regular = Template.bind({})
549
+ Regular.args = {
550
+ config,
551
+ fetchHandler,
552
+ mode: 'GRID'
553
+ }
@@ -450,7 +450,6 @@ const Template: Story<ArgTypes> = ({ config, mode = 'GRID', urlParamsSensitive =
450
450
  }
451
451
 
452
452
  #add {
453
- width: 50px;
454
453
  text-align: right;
455
454
  }
456
455
 
@@ -450,7 +450,6 @@ const Template: Story<ArgTypes> = ({ config }: ArgTypes) =>
450
450
  }
451
451
 
452
452
  #add {
453
- width: 50px;
454
453
  text-align: right;
455
454
  }
456
455
 
@@ -279,7 +279,7 @@ const Template: Story<ArgTypes> = ({ config, mode = 'GRID', urlParamsSensitive =
279
279
  }
280
280
 
281
281
  #add {
282
- width: 50px;
282
+ margin-left: auto;
283
283
  text-align: right;
284
284
  }
285
285
 
@@ -309,11 +309,10 @@ const Template: Story<ArgTypes> = ({ config }: ArgTypes) =>
309
309
  }
310
310
 
311
311
  #add {
312
- width: 50px;
313
312
  text-align: right;
314
313
  }
315
314
 
316
- #add #add button {
315
+ #add button {
317
316
  display: flex;
318
317
  align-items: center;
319
318
  justify-content: center;
@@ -308,7 +308,6 @@ const Template: Story<ArgTypes> = ({ config }: ArgTypes) =>
308
308
  }
309
309
 
310
310
  #add {
311
- width: 50px;
312
311
  text-align: right;
313
312
  }
314
313