@operato/data-grist 1.12.6 → 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 (44) hide show
  1. package/CHANGELOG.md +19 -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-grid/data-grid-body.js +2 -2
  9. package/dist/src/data-grid/data-grid-body.js.map +1 -1
  10. package/dist/src/data-grist.d.ts +2 -5
  11. package/dist/src/data-grist.js +46 -100
  12. package/dist/src/data-grist.js.map +1 -1
  13. package/dist/src/data-manipulator.d.ts +11 -0
  14. package/dist/src/data-manipulator.js +93 -12
  15. package/dist/src/data-manipulator.js.map +1 -1
  16. package/dist/src/renderers/ox-grist-renderer-tree.js +3 -3
  17. package/dist/src/renderers/ox-grist-renderer-tree.js.map +1 -1
  18. package/dist/src/types.d.ts +2 -0
  19. package/dist/src/types.js.map +1 -1
  20. package/dist/stories/fixed-column.stories.js +5 -10
  21. package/dist/stories/fixed-column.stories.js.map +1 -1
  22. package/dist/stories/grist-modes.stories.js +1 -0
  23. package/dist/stories/grist-modes.stories.js.map +1 -1
  24. package/dist/stories/tree-column-with-checkbox.stories.js +5 -2
  25. package/dist/stories/tree-column-with-checkbox.stories.js.map +1 -1
  26. package/dist/stories/tree-column.stories copy.d.ts +26 -0
  27. package/dist/stories/tree-column.stories copy.js +326 -0
  28. package/dist/stories/tree-column.stories copy.js.map +1 -0
  29. package/dist/stories/tree-column.stories.js +3 -1
  30. package/dist/stories/tree-column.stories.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +5 -5
  33. package/src/configure/config-builder.ts +18 -1
  34. package/src/configure/tree-option-builder.ts +16 -2
  35. package/src/configure/zero-config.ts +1 -3
  36. package/src/data-grid/data-grid-body.ts +2 -2
  37. package/src/data-grist.ts +74 -124
  38. package/src/data-manipulator.ts +119 -13
  39. package/src/renderers/ox-grist-renderer-tree.ts +3 -3
  40. package/src/types.ts +2 -1
  41. package/stories/fixed-column.stories.ts +5 -10
  42. package/stories/grist-modes.stories.ts +1 -0
  43. package/stories/tree-column-with-checkbox.stories.ts +5 -2
  44. 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 => {
@@ -688,6 +615,11 @@ export class DataGrist extends LitElement implements DataConsumer {
688
615
  delete copied.__changes__
689
616
  delete copied.__dirtyfields__
690
617
  delete copied.__origin__
618
+ delete copied.__depth__
619
+ delete copied.__expanded__
620
+ delete copied.__check_in_tree__
621
+ delete copied.__children__
622
+ delete copied.__typename
691
623
 
692
624
  return copied
693
625
  })
@@ -717,18 +649,6 @@ export class DataGrist extends LitElement implements DataConsumer {
717
649
  }
718
650
  }
719
651
 
720
- traverse(record: GristRecord, __depth__: number = 0): GristRecord[] {
721
- const children = record[this.childrenProperty!] as GristRecord[]
722
-
723
- record.__depth__ = __depth__
724
-
725
- if (record.__expanded__ && children) {
726
- return [record].concat(...children.map(child => this.traverse(child, __depth__ + 1)))
727
- } else {
728
- return [record]
729
- }
730
- }
731
-
732
652
  /**
733
653
  * Forced internal data to be reflected on the screen
734
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.
@@ -736,22 +656,7 @@ export class DataGrist extends LitElement implements DataConsumer {
736
656
  * @method
737
657
  */
738
658
  refresh() {
739
- /*
740
- - TODO 여기에서 TREE 형태 데이터의 접고, 펴는 것을 재구성한다.
741
- - 동적으로 서브항목을 fetch 하는 기능은 제공하지 않는다.
742
-
743
- 1. 빈배열에서 시작한다.
744
- 2. 기존 배열을 traverse하면서, collapsed 여부에 따라서, 자식의 포함여부를 결정하고 준비한 배열에 하나씩 추가한다.
745
-
746
- */
747
- const { records } = this._data
748
- const toplevelRecords = records.filter(
749
- record => !record.__depth__
750
- ) /* __depth__ 가 설정되지 않았거나, 0 인 경우만 수집 */
751
- this._data = {
752
- ...this._data,
753
- records: ([] as GristRecord[]).concat(...toplevelRecords.map(record => this.traverse(record)))
754
- }
659
+ this.grist.refresh()
755
660
  }
756
661
 
757
662
  /**
@@ -769,15 +674,24 @@ export class DataGrist extends LitElement implements DataConsumer {
769
674
  records = []
770
675
  } = this.data || ZERO_PAGINATION
771
676
 
677
+ const { childrenProperty, expanded } = this.compiledConfig.tree
678
+
772
679
  /* 원본 데이타를 남기고, 복사본(_data)을 사용한다. */
773
- records = ([] as GristRecord[]).concat(...records.map(record => this.traverse(record)))
774
- records = records.map((record, idx) => {
775
- return {
776
- ...record,
777
- __seq__: this.mode == 'GRID' ? (page - 1) * limit + idx + 1 : idx + 1,
778
- __origin__: record
779
- }
780
- })
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
+ }
781
695
 
782
696
  this._data = {
783
697
  limit,
@@ -790,6 +704,43 @@ export class DataGrist extends LitElement implements DataConsumer {
790
704
  this.snapshotTaker?.take(true)
791
705
  }
792
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
+
793
744
  checkDirties() {
794
745
  const records = this.dirtyRecords
795
746
  const { columns = [] } = this.compiledConfig || {}
@@ -815,7 +766,7 @@ export class DataGrist extends LitElement implements DataConsumer {
815
766
  }
816
767
  }
817
768
 
818
- this._data = { ...this._data }
769
+ this._data = { ...this.dirtyData }
819
770
 
820
771
  this.snapshotTaker?.touch()
821
772
  }
@@ -833,9 +784,9 @@ export class DataGrist extends LitElement implements DataConsumer {
833
784
  .forEach(column => {
834
785
  cloned[column.name] = record[column.name]
835
786
  })
836
- const rowIndex = this._data.records.findIndex(rec => rec === record)
787
+ const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
837
788
 
838
- this._data.records.splice(rowIndex + 1, 0, cloned)
789
+ this.dirtyData.records.splice(rowIndex + 1, 0, cloned)
839
790
  })
840
791
 
841
792
  this.checkDirties()
@@ -847,11 +798,10 @@ export class DataGrist extends LitElement implements DataConsumer {
847
798
  records.forEach(record => {
848
799
  if (dirty) {
849
800
  record.__dirty__ = '-'
850
- } else {
851
801
  }
852
802
 
853
- const rowIndex = this._data.records.findIndex(rec => rec === record)
854
- this._data.records.splice(rowIndex, 1)
803
+ const rowIndex = this.dirtyData.records.findIndex(rec => rec === record)
804
+ this.dirtyData.records.splice(rowIndex, 1)
855
805
  })
856
806
 
857
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