@operato/data-grist 1.12.7 → 1.12.8

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 (38) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/src/configure/config-builder.js +15 -0
  3. package/dist/src/configure/config-builder.js.map +1 -1
  4. package/dist/src/configure/tree-option-builder.js +13 -2
  5. package/dist/src/configure/tree-option-builder.js.map +1 -1
  6. package/dist/src/configure/zero-config.js +1 -3
  7. package/dist/src/configure/zero-config.js.map +1 -1
  8. package/dist/src/data-grist.d.ts +2 -5
  9. package/dist/src/data-grist.js +42 -100
  10. package/dist/src/data-grist.js.map +1 -1
  11. package/dist/src/data-manipulator.d.ts +11 -0
  12. package/dist/src/data-manipulator.js +93 -12
  13. package/dist/src/data-manipulator.js.map +1 -1
  14. package/dist/src/renderers/ox-grist-renderer-tree.js +3 -3
  15. package/dist/src/renderers/ox-grist-renderer-tree.js.map +1 -1
  16. package/dist/src/types.d.ts +2 -0
  17. package/dist/src/types.js.map +1 -1
  18. package/dist/stories/fixed-column.stories.js +5 -10
  19. package/dist/stories/fixed-column.stories.js.map +1 -1
  20. package/dist/stories/grist-modes.stories.js +1 -0
  21. package/dist/stories/grist-modes.stories.js.map +1 -1
  22. package/dist/stories/tree-column-with-checkbox.stories.js +5 -2
  23. package/dist/stories/tree-column-with-checkbox.stories.js.map +1 -1
  24. package/dist/stories/tree-column.stories.js +3 -1
  25. package/dist/stories/tree-column.stories.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +2 -2
  28. package/src/configure/config-builder.ts +18 -1
  29. package/src/configure/tree-option-builder.ts +16 -2
  30. package/src/configure/zero-config.ts +1 -3
  31. package/src/data-grist.ts +70 -124
  32. package/src/data-manipulator.ts +119 -13
  33. package/src/renderers/ox-grist-renderer-tree.ts +3 -3
  34. package/src/types.ts +2 -1
  35. package/stories/fixed-column.stories.ts +5 -10
  36. package/stories/grist-modes.stories.ts +1 -0
  37. package/stories/tree-column-with-checkbox.stories.ts +5 -2
  38. package/stories/tree-column.stories.ts +3 -1
package/src/data-grist.ts CHANGED
@@ -114,7 +114,6 @@ export class DataGrist extends LitElement implements DataConsumer {
114
114
  private orginPaddingTop?: string
115
115
  private originMarginTop?: string
116
116
  private lastLocation: { origin?: string; pathname?: string; search?: string } = {}
117
- private childrenProperty?: string
118
117
 
119
118
  private popstateEventHandler: EventListener = ((e: Event) => {
120
119
  const { origin, pathname, search } = window.location
@@ -241,8 +240,6 @@ export class DataGrist extends LitElement implements DataConsumer {
241
240
  })
242
241
  )
243
242
 
244
- this.childrenProperty = this.compiledConfig.tree.childrenProperty
245
-
246
243
  this.pagination = this.compiledConfig.pagination || {}
247
244
 
248
245
  if (!this.urlParamsSensitive) {
@@ -319,13 +316,7 @@ export class DataGrist extends LitElement implements DataConsumer {
319
316
  const empty = !this._showSpinner && this._data.records.length == 0
320
317
 
321
318
  return html`
322
- <div
323
- id="wrap"
324
- @keydown=${(e: KeyboardEvent) => this.onKeydown(e)}
325
- @collapsed=${(e: CustomEvent) => this.onCollapsed(e)}
326
- @expanded=${(e: CustomEvent) => this.onExpanded(e)}
327
- @check-in-tree=${(e: CustomEvent) => this.onCheckInTree(e)}
328
- >
319
+ <div id="wrap" @keydown=${(e: KeyboardEvent) => this.onKeydown(e)}>
329
320
  ${this.mode == 'GRID'
330
321
  ? html`
331
322
  <ox-grid
@@ -381,86 +372,22 @@ export class DataGrist extends LitElement implements DataConsumer {
381
372
  }
382
373
  }
383
374
 
384
- private onCollapsed(e: CustomEvent) {
385
- const record = e.detail
386
- record.__expanded__ = false
387
-
388
- this.refresh()
389
- }
390
-
391
- private onExpanded(e: CustomEvent) {
392
- const record = e.detail
393
- record.__expanded__ = true
394
-
395
- this.refresh()
396
- }
397
-
398
- private onCheckInTree(e: CustomEvent) {
399
- const self = this
400
-
401
- function walkTreeCheckedUpdate(record: GristRecord, checked: 'checked' | 'unchecked') {
402
- const children = record[self.childrenProperty!] as GristRecord[]
403
-
404
- children?.forEach(child => walkTreeCheckedUpdate(child, checked))
405
- record.__check_in_tree__ = checked
406
- record.__selected__ = checked == 'checked'
407
- }
408
-
409
- function updateCheckedAll(record: GristRecord) {
410
- /* 자식들의 checked 상태로 record의 checked 상태를 수정한다. */
411
- var children = record[self.childrenProperty!] as GristRecord[]
412
-
413
- if (!children || children.length == 0) {
414
- return
415
- }
416
-
417
- children.forEach(child => updateCheckedAll(child))
418
-
419
- var checked: 'checked' | 'half-checked' | 'unchecked' | undefined
420
-
421
- children.forEach(child => {
422
- const { __check_in_tree__ } = child
423
-
424
- if (__check_in_tree__ == 'half-checked') {
425
- checked = 'half-checked'
426
- } else if (__check_in_tree__ == 'checked') {
427
- checked = checked == 'checked' || !checked ? 'checked' : 'half-checked'
428
- } else {
429
- checked = checked == 'unchecked' || !checked ? 'unchecked' : 'half-checked'
430
- }
431
- })
432
-
433
- record.__check_in_tree__ = checked
434
- record.__selected__ = checked == 'checked'
435
- }
436
-
437
- e.stopPropagation()
438
-
439
- const record = e.detail
440
- var checked = record.__check_in_tree__
441
-
442
- walkTreeCheckedUpdate(record, !checked || checked == 'unchecked' ? 'checked' : 'unchecked')
443
- const toplevelRecords = this._data.records.filter(
444
- record => !record.__depth__
445
- ) /* __depth__ 가 설정되지 않았거나, 0 인 경우만 수집 */
446
-
447
- toplevelRecords.forEach(record => updateCheckedAll(record))
448
-
449
- this.refresh()
450
- }
451
-
452
375
  get state() {
453
- return JSON.stringify(this._data)
376
+ return JSON.stringify(this.dirtyData)
454
377
  }
455
378
 
456
379
  undo() {
457
- if (!this.timeCapsule?.backwardable) return
380
+ if (!this.timeCapsule?.backwardable) {
381
+ return
382
+ }
458
383
 
459
384
  this._data = JSON.parse(this.timeCapsule?.backward())
460
385
  }
461
386
 
462
387
  redo() {
463
- if (!this.timeCapsule?.forwardable) return
388
+ if (!this.timeCapsule?.forwardable) {
389
+ return
390
+ }
464
391
 
465
392
  this._data = JSON.parse(this.timeCapsule?.forward())
466
393
  }
@@ -541,7 +468,7 @@ export class DataGrist extends LitElement implements DataConsumer {
541
468
  var { records } = this.data || []
542
469
  var selectedRecords = this.selectedRecords || []
543
470
 
544
- var _records = this._data.records
471
+ var _records = this.dirtyData.records
545
472
 
546
473
  /* 원본데이타에서 index를 찾아서, 복사본 데이타의 selected를 설정한다. */
547
474
  selectedRecords.forEach(selected => {
@@ -691,6 +618,7 @@ export class DataGrist extends LitElement implements DataConsumer {
691
618
  delete copied.__depth__
692
619
  delete copied.__expanded__
693
620
  delete copied.__check_in_tree__
621
+ delete copied.__children__
694
622
  delete copied.__typename
695
623
 
696
624
  return copied
@@ -721,18 +649,6 @@ export class DataGrist extends LitElement implements DataConsumer {
721
649
  }
722
650
  }
723
651
 
724
- traverse(record: GristRecord, __depth__: number = 0): GristRecord[] {
725
- const children = record[this.childrenProperty!] as GristRecord[]
726
-
727
- record.__depth__ = __depth__
728
-
729
- if (record.__expanded__ && children) {
730
- return [record].concat(...children.map(child => this.traverse(child, __depth__ + 1)))
731
- } else {
732
- return [record]
733
- }
734
- }
735
-
736
652
  /**
737
653
  * Forced internal data to be reflected on the screen
738
654
  * Data changing through a normal method is automatically reflected on the screen, so it is a method that does not need to be used in general.
@@ -740,22 +656,7 @@ export class DataGrist extends LitElement implements DataConsumer {
740
656
  * @method
741
657
  */
742
658
  refresh() {
743
- /*
744
- - TODO 여기에서 TREE 형태 데이터의 접고, 펴는 것을 재구성한다.
745
- - 동적으로 서브항목을 fetch 하는 기능은 제공하지 않는다.
746
-
747
- 1. 빈배열에서 시작한다.
748
- 2. 기존 배열을 traverse하면서, collapsed 여부에 따라서, 자식의 포함여부를 결정하고 준비한 배열에 하나씩 추가한다.
749
-
750
- */
751
- const { records } = this._data
752
- const toplevelRecords = records.filter(
753
- record => !record.__depth__
754
- ) /* __depth__ 가 설정되지 않았거나, 0 인 경우만 수집 */
755
- this._data = {
756
- ...this._data,
757
- records: ([] as GristRecord[]).concat(...toplevelRecords.map(record => this.traverse(record)))
758
- }
659
+ this.grist.refresh()
759
660
  }
760
661
 
761
662
  /**
@@ -773,15 +674,24 @@ export class DataGrist extends LitElement implements DataConsumer {
773
674
  records = []
774
675
  } = this.data || ZERO_PAGINATION
775
676
 
677
+ const { childrenProperty, expanded } = this.compiledConfig.tree
678
+
776
679
  /* 원본 데이타를 남기고, 복사본(_data)을 사용한다. */
777
- records = ([] as GristRecord[]).concat(...records.map(record => this.traverse(record)))
778
- records = records.map((record, idx) => {
779
- return {
780
- ...record,
781
- __seq__: this.mode == 'GRID' ? (page - 1) * limit + idx + 1 : idx + 1,
782
- __origin__: record
783
- }
784
- })
680
+ records = ([] as GristRecord[]).concat(
681
+ ...records.map((record, idx) =>
682
+ this.traverseReset(
683
+ record,
684
+ this.mode == 'GRID' ? (page - 1) * limit + idx + 1 : idx + 1,
685
+ 0,
686
+ childrenProperty,
687
+ expanded as () => boolean
688
+ )
689
+ )
690
+ )
691
+
692
+ if (childrenProperty) {
693
+ records = ([] as GristRecord[]).concat(...records.map(record => this.traverseExpanded(record)))
694
+ }
785
695
 
786
696
  this._data = {
787
697
  limit,
@@ -794,6 +704,43 @@ export class DataGrist extends LitElement implements DataConsumer {
794
704
  this.snapshotTaker?.take(true)
795
705
  }
796
706
 
707
+ private traverseReset(
708
+ record: GristRecord,
709
+ seq: number,
710
+ __depth__: number,
711
+ childrenProperty: string | undefined,
712
+ expanded: () => boolean
713
+ ): GristRecord {
714
+ const copied = {
715
+ ...record,
716
+ __seq__: seq,
717
+ __origin__: record
718
+ }
719
+
720
+ if (childrenProperty) {
721
+ const children: GristRecord[] = record[childrenProperty!]
722
+
723
+ const __expanded__ = (expanded as Function)(record)
724
+ const __children__ = (children || []).map(child =>
725
+ this.traverseReset(child, seq, __depth__ + 1, childrenProperty, expanded)
726
+ )
727
+
728
+ Object.assign(copied, { __depth__, __children__, __expanded__ })
729
+ }
730
+
731
+ return copied
732
+ }
733
+
734
+ private traverseExpanded(record: GristRecord): GristRecord[] {
735
+ const { __expanded__, __children__ = [] } = record
736
+
737
+ if (__expanded__ && __children__.length > 0) {
738
+ return [record].concat(...__children__.map(child => this.traverseExpanded(child)))
739
+ } else {
740
+ return [record]
741
+ }
742
+ }
743
+
797
744
  checkDirties() {
798
745
  const records = this.dirtyRecords
799
746
  const { columns = [] } = this.compiledConfig || {}
@@ -819,7 +766,7 @@ export class DataGrist extends LitElement implements DataConsumer {
819
766
  }
820
767
  }
821
768
 
822
- this._data = { ...this._data }
769
+ this._data = { ...this.dirtyData }
823
770
 
824
771
  this.snapshotTaker?.touch()
825
772
  }
@@ -837,9 +784,9 @@ export class DataGrist extends LitElement implements DataConsumer {
837
784
  .forEach(column => {
838
785
  cloned[column.name] = record[column.name]
839
786
  })
840
- const rowIndex = this._data.records.findIndex(rec => rec === record)
787
+ const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
841
788
 
842
- this._data.records.splice(rowIndex + 1, 0, cloned)
789
+ this.dirtyData.records.splice(rowIndex + 1, 0, cloned)
843
790
  })
844
791
 
845
792
  this.checkDirties()
@@ -851,11 +798,10 @@ export class DataGrist extends LitElement implements DataConsumer {
851
798
  records.forEach(record => {
852
799
  if (dirty) {
853
800
  record.__dirty__ = '-'
854
- } else {
855
801
  }
856
802
 
857
- const rowIndex = this._data.records.findIndex(rec => rec === record)
858
- this._data.records.splice(rowIndex, 1)
803
+ const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
804
+ this.dirtyData.records.splice(rowIndex, 1)
859
805
  })
860
806
 
861
807
  this.checkDirties()
@@ -1,4 +1,4 @@
1
- import { LitElement } from 'lit'
1
+ import { LitElement, PropertyValues } from 'lit'
2
2
  import { property } from 'lit/decorators.js'
3
3
 
4
4
  import { ZERO_CONFIG, ZERO_DATA } from './configure/zero-config'
@@ -62,8 +62,19 @@ export class DataManipulator extends LitElement {
62
62
 
63
63
  this.onRecordChanged(record['__origin__'], row, null)
64
64
  })
65
+
66
+ /* tree processing */
67
+ this.addEventListener('collapsed', (e: Event) => this.onCollapsed(e as CustomEvent))
68
+ this.addEventListener('expanded', (e: Event) => this.onExpanded(e as CustomEvent))
69
+ this.addEventListener('check-in-tree', (e: Event) => this.onCheckInTree(e as CustomEvent))
65
70
  }
66
71
 
72
+ // updated(changes: PropertyValues<this>) {
73
+ // if (changes.has('data')) {
74
+ // this.refresh()
75
+ // }
76
+ // }
77
+
67
78
  onFieldChange({
68
79
  after,
69
80
  before,
@@ -169,19 +180,11 @@ export class DataManipulator extends LitElement {
169
180
  } else {
170
181
  let beforeDirty = beforeRecord['__dirty__']
171
182
  if (beforeDirty == '+') {
172
- /* 기존에 새로 생성된 레코드가 있었으며 계속 수정중이다. */
173
- afterRecord = {
174
- ...beforeRecord,
175
- ...recordData,
176
- __dirty__: '+'
177
- }
183
+ /* 기존에 새로 생성된 레코드가 있었으며 계속 수정중이다.(레코드 레퍼런스를 유지해야한다) */
184
+ afterRecord = Object.assign(beforeRecord, recordData, { __dirty__: '+' })
178
185
  } else {
179
- /* 기존에 레코드가 있었으며 계속 수정중이다. */
180
- afterRecord = {
181
- ...beforeRecord,
182
- ...recordData,
183
- __dirty__: 'M'
184
- }
186
+ /* 기존에 레코드가 있었으며 계속 수정중이다.(레코드 레퍼런스를 유지해야한다) */
187
+ afterRecord = Object.assign(beforeRecord, recordData, { __dirty__: 'M' })
185
188
  }
186
189
  }
187
190
  }
@@ -209,4 +212,107 @@ export class DataManipulator extends LitElement {
209
212
 
210
213
  this.requestUpdate()
211
214
  }
215
+
216
+ onCollapsed(e: CustomEvent) {
217
+ const record = e.detail
218
+ record.__expanded__ = false
219
+
220
+ this.refresh()
221
+ }
222
+
223
+ onExpanded(e: CustomEvent) {
224
+ const record = e.detail
225
+ record.__expanded__ = true
226
+
227
+ this.refresh()
228
+ }
229
+
230
+ onCheckInTree(e: CustomEvent) {
231
+ const self = this
232
+
233
+ function walkTreeCheckedUpdate(record: GristRecord, checked: 'checked' | 'unchecked') {
234
+ const children = record.__children__
235
+
236
+ children?.forEach(child => walkTreeCheckedUpdate(child, checked))
237
+ record.__check_in_tree__ = checked
238
+ record.__selected__ = checked == 'checked'
239
+ }
240
+
241
+ function updateCheckedAll(record: GristRecord) {
242
+ /* 자식들의 checked 상태로 record의 checked 상태를 수정한다. */
243
+ const children = record.__children__
244
+
245
+ if (!children || children.length == 0) {
246
+ return
247
+ }
248
+
249
+ children.forEach(child => updateCheckedAll(child))
250
+
251
+ var checked: 'checked' | 'half-checked' | 'unchecked' | undefined
252
+
253
+ children.forEach(child => {
254
+ const { __check_in_tree__ } = child
255
+
256
+ if (__check_in_tree__ == 'half-checked') {
257
+ checked = 'half-checked'
258
+ } else if (__check_in_tree__ == 'checked') {
259
+ checked = checked == 'checked' || !checked ? 'checked' : 'half-checked'
260
+ } else {
261
+ checked = checked == 'unchecked' || !checked ? 'unchecked' : 'half-checked'
262
+ }
263
+ })
264
+
265
+ record.__check_in_tree__ = checked
266
+ record.__selected__ = checked == 'checked'
267
+ }
268
+
269
+ e.stopPropagation()
270
+
271
+ const record = e.detail
272
+ var checked = record.__check_in_tree__
273
+
274
+ walkTreeCheckedUpdate(record, !checked || checked == 'unchecked' ? 'checked' : 'unchecked')
275
+ const toplevelRecords = this.data.records.filter(
276
+ record => !record.__depth__
277
+ ) /* __depth__ 가 설정되지 않았거나, 0 인 경우만 수집 */
278
+
279
+ toplevelRecords.forEach(record => updateCheckedAll(record))
280
+
281
+ this.refresh()
282
+ }
283
+
284
+ /**
285
+ * Forced internal data to be reflected on the screen
286
+ * Data changing through a normal method is automatically reflected on the screen, so it is a method that does not need to be used in general.
287
+ * Therefore, it will be deprecated.
288
+ * @method
289
+ */
290
+ refresh() {
291
+ /*
292
+ - TODO 여기에서 TREE 형태 데이터의 접고, 펴는 것을 재구성한다.
293
+ - 동적으로 서브항목을 fetch 하는 기능은 제공하지 않는다.
294
+
295
+ 1. 빈배열에서 시작한다.
296
+ 2. 기존 배열을 traverseRefresh하면서, collapsed 여부에 따라서, 자식의 포함여부를 결정하고 준비한 배열에 하나씩 추가한다.
297
+
298
+ */
299
+ const { records } = this.data
300
+ const toplevelRecords = records.filter(
301
+ record => !record.__depth__
302
+ ) /* __depth__ 가 설정되지 않았거나, 0 인 경우만 수집 */
303
+ this.data = {
304
+ ...this.data,
305
+ records: ([] as GristRecord[]).concat(...toplevelRecords.map(record => this.traverseRefresh(record)))
306
+ }
307
+ }
308
+
309
+ private traverseRefresh(record: GristRecord): GristRecord[] {
310
+ const { __expanded__, __children__ = [] } = record
311
+
312
+ if (__expanded__ && __children__.length > 0) {
313
+ return [record].concat(...__children__.map(child => this.traverseRefresh(child)))
314
+ } else {
315
+ return [record]
316
+ }
317
+ }
212
318
  }
@@ -121,10 +121,10 @@ export class OxGristRendererTree extends OxGristRenderer {
121
121
  @state() private expanded?: boolean = false
122
122
 
123
123
  get rendererTemplate() {
124
- var { childrenProperty = 'children', selectable = false } = this.column.record.options || {}
125
- var { [childrenProperty]: children } = this.record
124
+ var { selectable = false } = this.column.record.options || {}
125
+ var { __children__ } = this.record
126
126
 
127
- const expandable = children && children.length > 0
127
+ const expandable = __children__ && __children__.length > 0
128
128
 
129
129
  return html`
130
130
  <div wrap>
package/src/types.ts CHANGED
@@ -237,6 +237,7 @@ export type ListConfig = {
237
237
 
238
238
  export type TreeConfig = {
239
239
  childrenProperty?: string
240
+ expanded?: boolean | ((x: GristRecord) => boolean)
240
241
  }
241
242
 
242
243
  export type ImexConfig = {
@@ -291,7 +292,7 @@ export type GristRecord = {
291
292
  __collapsed__?: boolean
292
293
  __depth__?: number
293
294
  __check_in_tree__?: 'checked' | 'half-checked' | 'unchecked'
294
- // __children__?: GristRecord[]
295
+ __children__?: GristRecord[]
295
296
  [key: string]: any
296
297
  }
297
298
 
@@ -81,19 +81,16 @@ const config = {
81
81
  columns: [
82
82
  {
83
83
  type: 'gutter',
84
- gutterName: 'dirty',
85
- fixed: true
84
+ gutterName: 'dirty'
86
85
  },
87
86
  {
88
87
  type: 'gutter',
89
- gutterName: 'sequence',
90
- fixed: true
88
+ gutterName: 'sequence'
91
89
  },
92
90
  {
93
91
  type: 'gutter',
94
92
  gutterName: 'row-selector',
95
- multiple: true,
96
- fixed: true
93
+ multiple: true
97
94
  },
98
95
  {
99
96
  type: 'gutter',
@@ -104,8 +101,7 @@ const config = {
104
101
  click: function () {
105
102
  console.log('clicked')
106
103
  }
107
- },
108
- fixed: true
104
+ }
109
105
  },
110
106
  {
111
107
  type: 'gutter',
@@ -114,8 +110,7 @@ const config = {
114
110
  title: 'add',
115
111
  handlers: {
116
112
  click: 'record-copy'
117
- },
118
- fixed: true
113
+ }
119
114
  },
120
115
  {
121
116
  type: 'gutter',
@@ -130,6 +130,7 @@ const config = {
130
130
  type: 'link',
131
131
  name: 'name',
132
132
  label: true,
133
+ fixed: true,
133
134
  header: 'name',
134
135
  record: {
135
136
  editable: true,
@@ -93,8 +93,7 @@ const config = {
93
93
  record: {
94
94
  editable: false,
95
95
  options: {
96
- selectable: true /* with checkbox */,
97
- childrenProperty: 'children'
96
+ selectable: true /* with checkbox */
98
97
  }
99
98
  },
100
99
  filter: 'search',
@@ -185,6 +184,10 @@ const config = {
185
184
  ],
186
185
  pagination: {
187
186
  pages: [20, 30, 50, 100, 200]
187
+ },
188
+ tree: {
189
+ childrenProperty: 'children',
190
+ expanded: true
188
191
  }
189
192
  }
190
193
 
@@ -93,7 +93,6 @@ const config = {
93
93
  record: {
94
94
  editable: false,
95
95
  options: {
96
- childrenProperty: 'children',
97
96
  selectable: false
98
97
  }
99
98
  },
@@ -185,6 +184,9 @@ const config = {
185
184
  ],
186
185
  pagination: {
187
186
  pages: [20, 30, 50, 100, 200]
187
+ },
188
+ tree: {
189
+ childrenProperty: 'children'
188
190
  }
189
191
  }
190
192