neo.mjs 6.2.0 → 6.3.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.2.0'
23
+ * @member {String} version='6.3.0'
24
24
  */
25
- version: '6.2.0'
25
+ version: '6.3.0'
26
26
  }
27
27
 
28
28
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.2.0'
23
+ * @member {String} version='6.3.0'
24
24
  */
25
- version: '6.2.0'
25
+ version: '6.3.0'
26
26
  }
27
27
 
28
28
  /**
@@ -0,0 +1,58 @@
1
+ import ConfigurationViewport from '../ConfigurationViewport.mjs';
2
+
3
+ import Component from '../../src/component/Base.mjs';
4
+ import PopoverPlugin from '../../src/plugin/Popover.mjs';
5
+ import Button from '../../src/button/Base.mjs';
6
+
7
+ /**
8
+ * @class Neo.examples.popover.MainContainer
9
+ * @extends Neo.examples.ConfigurationViewport
10
+ */
11
+ class MainContainer extends ConfigurationViewport {
12
+ static config = {
13
+ className : 'Neo.examples.popover.MainContainer',
14
+ autoMount : true,
15
+ configItemLabelWidth: 100,
16
+ configItemWidth : 230,
17
+ layout : {ntype: 'hbox', align: 'stretch'},
18
+ cls : ['examples-container-accordion']
19
+ }
20
+
21
+
22
+ /**
23
+ * @returns {*}
24
+ */
25
+ createExampleComponent() {
26
+ return Neo.ntype({
27
+ ntype : 'container',
28
+ height: '30%',
29
+ layout: {ntype: 'vbox', align: 'center', pack: 'center'},
30
+ items : [{
31
+ module: Component,
32
+ height: 10
33
+ }, {
34
+ module : Button,
35
+ width : 200,
36
+ text : 'Click Me',
37
+ plugins: [{
38
+ module: PopoverPlugin,
39
+ align : 'bc-tc',
40
+ items : [{
41
+ ntype : 'panel',
42
+ headers: [{
43
+ dock: 'top',
44
+ html: 'HEADER'
45
+ }],
46
+ items : [{
47
+ html: 'This is a comment about the button'
48
+ }]
49
+ }]
50
+ }]
51
+ }]
52
+ });
53
+ }
54
+ }
55
+
56
+ Neo.applyClassConfig(MainContainer);
57
+
58
+ export default MainContainer;
@@ -2,5 +2,5 @@ import MainContainer from './MainContainer.mjs';
2
2
 
3
3
  export const onStart = () => Neo.app({
4
4
  mainView: MainContainer,
5
- name : 'Neo.examples.treeSelectionModel'
5
+ name : 'Neo.examples.popover'
6
6
  });
@@ -0,0 +1,13 @@
1
+ {
2
+ "appPath" : "examples/popover/app.mjs",
3
+ "basePath" : "../../",
4
+ "environment": "development",
5
+ "mainPath" : "./Main.mjs",
6
+ "themes" : ["neo-theme-dark", "neo-theme-light"],
7
+ "mainThreadAddons": [
8
+ "DragDrop",
9
+ "ScrollSync",
10
+ "Stylesheet",
11
+ "Popover"
12
+ ]
13
+ }
@@ -57,7 +57,7 @@
57
57
  "id": 8,
58
58
  "name": "V01 . At vero eos et accusam",
59
59
  "parentId": 7,
60
- "collapsed": true,
60
+ "collapsed": false,
61
61
  "isLeaf": false,
62
62
  "content": "Auswählliste",
63
63
  "iconCls": "fa fa-chevron-down color-red"
@@ -10,12 +10,12 @@ import ViewController from '../../src/controller/Component.mjs';
10
10
  import ViewModel from '../../src/model/Component.mjs';
11
11
 
12
12
  /**
13
- * @class Neo.examples.treeSelectionModel.MainContainer
13
+ * @class Neo.examples.treeAccordion.MainContainer
14
14
  * @extends Neo.examples.ConfigurationViewport
15
15
  */
16
16
  class MainContainer extends ConfigurationViewport {
17
17
  static config = {
18
- className : 'Neo.examples.treeSelectionModel.MainContainer',
18
+ className : 'Neo.examples.treeAccordion.MainContainer',
19
19
  autoMount : true,
20
20
  configItemLabelWidth: 100,
21
21
  configItemWidth : 230,
@@ -46,6 +46,14 @@ class MainContainer extends ConfigurationViewport {
46
46
  listeners : {change: me.onConfigChange.bind(me, 'firstParentIsVisible')},
47
47
  style : {marginTop: '10px'},
48
48
  valueLabelText: 'firstParentIsVisible'
49
+ }, {
50
+ module : CheckBox,
51
+ checked : treeList.showIcon,
52
+ hideLabel : true,
53
+ hideValueLabel: false,
54
+ listeners : {change: me.onConfigChange.bind(me, 'showIcon')},
55
+ style : {marginTop: '10px'},
56
+ valueLabelText: 'showIcon'
49
57
  }, {
50
58
  module : NumberField,
51
59
  clearable: true,
@@ -89,7 +97,7 @@ class MainContainer extends ConfigurationViewport {
89
97
  },
90
98
 
91
99
  autoLoad: true,
92
- url : '../../examples/treeSelectionModel/tree.json'
100
+ url : '../../examples/treeAccordion/tree.json'
93
101
  });
94
102
 
95
103
  return Neo.ntype({
@@ -135,7 +143,7 @@ class MainContainer extends ConfigurationViewport {
135
143
  }, {
136
144
  module: Panel,
137
145
  height: 150,
138
- width : '100%',
146
+ flex : 1,
139
147
 
140
148
  itemDefaults: {
141
149
  style: {
@@ -0,0 +1,6 @@
1
+ import MainContainer from './MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'Neo.examples.treeAccordion'
6
+ });
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta charset="UTF-8">
6
+ <title>Neo Tree</title>
7
+ </head>
8
+ <body>
9
+ <script src="../../src/MicroLoader.mjs" type="module"></script>
10
+ </body>
11
+ </html>
@@ -1,5 +1,5 @@
1
1
  {
2
- "appPath" : "examples/treeSelectionModel/app.mjs",
2
+ "appPath" : "examples/treeAccordion/app.mjs",
3
3
  "basePath" : "../../",
4
4
  "environment": "development",
5
5
  "mainPath" : "./Main.mjs",
@@ -0,0 +1,112 @@
1
+ [
2
+ {
3
+ "id": 1,
4
+ "name": "ROOT",
5
+ "parentId": null,
6
+ "collapsed": false,
7
+ "isLeaf": false
8
+ },
9
+ {
10
+ "id": 2,
11
+ "name": "Lorem ipsum dolor sit amet, consetetur sadipscing elitr,",
12
+ "parentId": 1,
13
+ "isLeaf": true,
14
+ "content": "Textfeld",
15
+ "iconCls": "fa fa-t color-blue"
16
+ },
17
+ {
18
+ "id": 3,
19
+ "name": "Sed diam nonumy",
20
+ "parentId": 1,
21
+ "isLeaf": true,
22
+ "content": "Textfeld",
23
+ "iconCls": "fa fa-t color-blue"
24
+ },
25
+ {
26
+ "id": 4,
27
+ "name": "Eirmod",
28
+ "parentId": 1,
29
+ "isLeaf": true,
30
+ "content": "Textfeld",
31
+ "iconCls": "fa fa-t color-blue"
32
+ },
33
+ {
34
+ "id": 5,
35
+ "name": "Tempor",
36
+ "parentId": 1,
37
+ "isLeaf": true,
38
+ "content": "Textfeld",
39
+ "iconCls": "fa fa-t color-blue"
40
+ },
41
+ {
42
+ "id": 6,
43
+ "name": "Invidunt ut labore",
44
+ "parentId": 1,
45
+ "isLeaf": true,
46
+ "content": "Textfeld",
47
+ "iconCls": "fa fa-t color-blue"
48
+ },
49
+ {
50
+ "id": 7,
51
+ "name": "Dolore magna aliquyam",
52
+ "parentId": null,
53
+ "collapsed": false,
54
+ "isLeaf": false
55
+ },
56
+ {
57
+ "id": 8,
58
+ "name": "V01 . At vero eos et accusam",
59
+ "parentId": 7,
60
+ "collapsed": false,
61
+ "isLeaf": false,
62
+ "content": "Auswählliste",
63
+ "iconCls": "fa fa-chevron-down color-red"
64
+ },
65
+ {
66
+ "id": 9,
67
+ "name": "Justo 1",
68
+ "parentId": 8,
69
+ "isLeaf": true,
70
+ "content": "Child",
71
+ "iconCls": "fa-regular fa-square-check color-green"
72
+ },
73
+ {
74
+ "id": 10,
75
+ "name": "Justo 2",
76
+ "parentId": 8,
77
+ "isLeaf": true,
78
+ "content": "Child",
79
+ "iconCls": "fa-regular fa-square-check color-green"
80
+ },
81
+ {
82
+ "id": 11,
83
+ "name": "Rebum",
84
+ "parentId": null,
85
+ "collapsed": false,
86
+ "isLeaf": false
87
+ },
88
+ {
89
+ "id": 12,
90
+ "name": "F0801 - Stet ",
91
+ "parentId": 11,
92
+ "isLeaf": true,
93
+ "content": "Datumfeld",
94
+ "iconCls": "fa-solid fa-calendar-days color-yellow"
95
+ },
96
+ {
97
+ "id": 13,
98
+ "name": "F0802 - Kasd",
99
+ "parentId": 11,
100
+ "isLeaf": true,
101
+ "content": "Datumfeld",
102
+ "iconCls": "fa-solid fa-calendar-days color-yellow"
103
+ },
104
+ {
105
+ "id": 14,
106
+ "name": "E30 - Gubergren",
107
+ "parentId": 11,
108
+ "isLeaf": true,
109
+ "content": "Mehrfachauswahl",
110
+ "iconCls": "fa-regular fa-square-check color-green"
111
+ }
112
+ ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -0,0 +1,111 @@
1
+ [popover] {
2
+ inset: unset;
3
+ border-radius: 5px;
4
+ box-shadow: 2px 2px 7px #111;
5
+ background-color: #222;
6
+
7
+ //&.neo-popover[popover] {
8
+ // display: revert;
9
+ //}
10
+
11
+ &.bc-tc {
12
+ position-fallback: --bc-tc;
13
+ translate: -50%;
14
+ margin: .5rem 0;
15
+ }
16
+ &.tc-bc {
17
+ position-fallback: --tc-bc;
18
+ translate: -50%;
19
+ margin: .5rem 0;
20
+ }
21
+ &.tl-tr {
22
+ position-fallback: --tl-tr;
23
+ margin: 0 .5rem;
24
+ }
25
+ &.tr-tl {
26
+ position-fallback: --tl-tr;
27
+ margin: 0 .5rem;
28
+ }
29
+ &.cl-cr {
30
+ position-fallback: --cl-cr;
31
+ margin: 0 .25rem;
32
+ translate: 0 -50%;
33
+ }
34
+ &.cr-cl {
35
+ position-fallback: --cr-cl;
36
+ margin: 0 .25rem;
37
+ translate: 0 -50%;
38
+ }
39
+ }
40
+
41
+ @position-fallback --bc-tc {
42
+ @try {
43
+ bottom: anchor(top);
44
+ left: anchor(center);
45
+ }
46
+
47
+ @try {
48
+ top: anchor(bottom);
49
+ left: anchor(center);
50
+ }
51
+ }
52
+
53
+ @position-fallback --tc-bc {
54
+ @try {
55
+ top: anchor(bottom);
56
+ left: anchor(center);
57
+ }
58
+
59
+ @try {
60
+ bottom: anchor(top);
61
+ left: anchor(center);
62
+ }
63
+ }
64
+
65
+ @position-fallback --tl-tr {
66
+ @try {
67
+ top: anchor(top);
68
+ left: anchor(right);
69
+ }
70
+
71
+ @try {
72
+ top: anchor(top);
73
+ right: anchor(left);
74
+ }
75
+ }
76
+
77
+ @position-fallback --tr-tl {
78
+ @try {
79
+ top: anchor(top);
80
+ right: anchor(left);
81
+ }
82
+
83
+ @try {
84
+ top: anchor(top);
85
+ left: anchor(right);
86
+ }
87
+ }
88
+
89
+ @position-fallback --cr-cl {
90
+ @try {
91
+ top: anchor(center);
92
+ right: anchor(left);
93
+ }
94
+
95
+ @try {
96
+ top: anchor(center);
97
+ left: anchor(right);
98
+ }
99
+ }
100
+
101
+ @position-fallback --cl-cr {
102
+ @try {
103
+ top: anchor(center);
104
+ left: anchor(right);
105
+ }
106
+
107
+ @try {
108
+ top: anchor(center);
109
+ right: anchor(left);
110
+ }
111
+ }
@@ -236,12 +236,12 @@ const DefaultConfig = {
236
236
  useVdomWorker: true,
237
237
  /**
238
238
  * buildScripts/injectPackageVersion.mjs will update this value
239
- * @default '6.2.0'
239
+ * @default '6.3.0'
240
240
  * @memberOf! module:Neo
241
241
  * @name config.version
242
242
  * @type String
243
243
  */
244
- version: '6.2.0'
244
+ version: '6.3.0'
245
245
  };
246
246
 
247
247
  Object.assign(DefaultConfig, {
@@ -135,6 +135,7 @@ class Container extends BaseContainer {
135
135
  */
136
136
  async getValues() {
137
137
  let fields = await this.getFields(),
138
+ Radio = Neo.form.field.Radio,
138
139
  values = {},
139
140
  itemName, key, ns, nsArray, value;
140
141
 
@@ -156,12 +157,23 @@ class Container extends BaseContainer {
156
157
  ns = values
157
158
  }
158
159
 
160
+ // Ensuring that Radios will not return arrays
161
+ if (Radio && item instanceof Radio) {
162
+ // Only overwrite an existing value with a checked value
163
+ if (Object.hasOwn(ns, key)) {
164
+ if (value !== item.uncheckedValue) {
165
+ ns[key] = value
166
+ }
167
+ } else {
168
+ ns[key] = value
169
+ }
170
+ }
159
171
  /*
160
172
  * CheckBoxes need custom logic
161
173
  * => we only want to pass the uncheckedValue in case the field does not belong to a group
162
174
  * (multiple fields using the same name)
163
175
  */
164
- if (Object.hasOwn(ns, key) && value !== undefined) {
176
+ else if (Object.hasOwn(ns, key) && value !== undefined) {
165
177
  if (ns[key] === item.uncheckedValue) {
166
178
  ns[key] = []
167
179
  } else if (!Array.isArray(ns[key])) {
@@ -76,9 +76,7 @@ class Base extends Component {
76
76
  * @param {*} oldValue
77
77
  */
78
78
  afterSetValue(value, oldValue) {
79
- if (oldValue !== undefined) {
80
- this.fireChangeEvent(value, oldValue)
81
- }
79
+ oldValue !== undefined && this.fireChangeEvent(value, oldValue)
82
80
  }
83
81
 
84
82
  /**
@@ -134,27 +132,42 @@ class Base extends Component {
134
132
  */
135
133
  fireChangeEvent(value, oldValue) {
136
134
  let me = this,
137
- FormContainer = Neo.form?.Container;
135
+ FormContainer = Neo.form?.Container,
136
+ opts = {component: me, oldValue, value};
138
137
 
139
- me.fire('change', {
140
- component: me,
141
- oldValue,
142
- value
143
- });
138
+ if (Neo.isFunction(me.getGroupValue)) {
139
+ opts.groupValue = me.getGroupValue()
140
+ }
141
+
142
+ me.fire('change', opts);
144
143
 
145
144
  if (!me.suspendEvents) {
146
145
  ComponentManager.getParents(me).forEach(parent => {
147
146
  if (FormContainer && parent instanceof FormContainer) {
148
- parent.fire('fieldChange', {
149
- component: me,
150
- oldValue,
151
- value
152
- })
147
+ parent.fire('fieldChange', opts)
153
148
  }
154
149
  })
155
150
  }
156
151
  }
157
152
 
153
+ /**
154
+ * Forms in neo can be nested. This method will return the closest parent which is a form.Container or null.
155
+ * @returns {Neo.form.Container|null}
156
+ */
157
+ getClosestForm() {
158
+ let me = this,
159
+ FormContainer = Neo.form?.Container,
160
+ parent;
161
+
162
+ for (parent of ComponentManager.getParents(me)) {
163
+ if (FormContainer && parent instanceof FormContainer) {
164
+ return parent
165
+ }
166
+ }
167
+
168
+ return null
169
+ }
170
+
158
171
  /**
159
172
  * Override this method as needed
160
173
  * @returns {Object|null}
@@ -213,15 +226,17 @@ class Base extends Component {
213
226
  super.onFocusLeave?.(data);
214
227
 
215
228
  let me = this,
216
- FormContainer = Neo.form?.Container;
229
+ FormContainer = Neo.form?.Container,
230
+ opts = {...data, component: me, value: me.getValue()};
231
+
232
+ if (Neo.isFunction(me.getGroupValue)) {
233
+ opts.groupValue = me.getGroupValue()
234
+ }
217
235
 
218
236
  if (!me.suspendEvents) {
219
237
  ComponentManager.getParents(me).forEach(parent => {
220
238
  if (FormContainer && parent instanceof FormContainer) {
221
- parent.fire('fieldFocusLeave', {
222
- ...data,
223
- component: me
224
- })
239
+ parent.fire('fieldFocusLeave', opts)
225
240
  }
226
241
  })
227
242
  }
@@ -194,7 +194,7 @@ class CheckBox extends Base {
194
194
  me.update();
195
195
 
196
196
  if (oldValue !== undefined) {
197
- me.fireChangeEvent(me.getValue(), null)
197
+ me.fireChangeEvent(me.getValue(), me.getOldValue())
198
198
  }
199
199
  }
200
200
 
@@ -433,6 +433,23 @@ class CheckBox extends Base {
433
433
  return true
434
434
  }
435
435
 
436
+ /**
437
+ * @returns {String[]}
438
+ */
439
+ getGroupValue() {
440
+ let form = this.getClosestForm(),
441
+ fields = ComponentManager.find({path: this.getPath()}),
442
+ value = [];
443
+
444
+ fields.forEach(field => {
445
+ if (field.checked && field.getClosestForm() === form) {
446
+ NeoArray.add(value, field.value)
447
+ }
448
+ });
449
+
450
+ return value
451
+ }
452
+
436
453
  /**
437
454
  * @returns {String}
438
455
  */
@@ -462,6 +479,17 @@ class CheckBox extends Base {
462
479
  }
463
480
 
464
481
  /**
482
+ * Counterpart to getValue(), returning the uncheckedValue if checked
483
+ * @returns {String|null}
484
+ */
485
+ getOldValue() {
486
+ let me = this;
487
+
488
+ return me.checked ? me.uncheckedValue : me.value
489
+ }
490
+
491
+ /**
492
+ * Returns this.value if checked, otherwise this.uncheckedValue
465
493
  * @returns {String|null}
466
494
  */
467
495
  getValue() {
@@ -482,7 +510,7 @@ class CheckBox extends Base {
482
510
  */
483
511
  isValid() {
484
512
  this.validate(true); // silent
485
-
513
+
486
514
  return this.error ? false : super.isValid()
487
515
  }
488
516
 
@@ -45,10 +45,22 @@ class Radio extends CheckBox {
45
45
  }
46
46
 
47
47
  /**
48
- * @returns {String|null}
48
+ * Radios should only fire change & fieldChange events if checked.
49
+ * If there was just 1 radio, you can not uncheck it.
50
+ * @param {*} value
51
+ * @param {*} oldValue
49
52
  */
50
- getValue() {
51
- return this.checked ? this.value : undefined
53
+ fireChangeEvent(value, oldValue) {
54
+ this.checked && super.fireChangeEvent(value, oldValue)
55
+ }
56
+
57
+ /**
58
+ * @returns {String[]}
59
+ */
60
+ getGroupValue() {
61
+ let value = super.getGroupValue();
62
+
63
+ return value.length > 0 ? value[0] : []
52
64
  }
53
65
 
54
66
  /**
@@ -0,0 +1,75 @@
1
+ import Base from '../../core/Base.mjs';
2
+ import DomAccess from '../DomAccess.mjs'
3
+ /**
4
+ * Addon for Popover
5
+ * @class Neo.main.addon.Popover
6
+ * @extends Neo.core.Base
7
+ * @singleton
8
+ */
9
+ class Popover extends Base {
10
+ static config = {
11
+ /**
12
+ * @member {String} className='Neo.main.addon.Popover'
13
+ * @protected
14
+ */
15
+ className: 'Neo.main.addon.Popover',
16
+ /**
17
+ * Remote method access for other workers
18
+ * @member {Object} remote={app: [//...]}
19
+ * @protected
20
+ */
21
+ remote: {
22
+ app: [
23
+ 'hide',
24
+ 'show',
25
+ 'toggle'
26
+ ]
27
+ },
28
+ /**
29
+ * @member {Boolean} singleton=true
30
+ * @protected
31
+ */
32
+ singleton: true
33
+ }
34
+
35
+ /**
36
+ * @param {Object} data
37
+ * @param {String} data.id
38
+ * @returns {Boolean}
39
+ */
40
+ hide(data) {
41
+ this.getPopover(data.id).hidePopover();
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * @param {Object} data
47
+ * @param {String} data.id
48
+ * @returns {Boolean}
49
+ */
50
+ show(data) {
51
+ this.getPopover(data.id).showPopover();
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * @param {Object} data
57
+ * @param {String} data.id
58
+ * @returns {Boolean}
59
+ */
60
+ toggle(data) {
61
+ this.getPopover(data.id).togglePopover();
62
+ return true;
63
+ }
64
+
65
+ getPopover(parentId) {
66
+ const parent = document.getElementById(parentId),
67
+ popover = document.getElementById(parent.getAttribute('popovertarget'));
68
+
69
+ return popover;
70
+ }
71
+ }
72
+
73
+ let instance = Neo.applyClassConfig(Popover);
74
+
75
+ export default instance;
package/src/menu/List.mjs CHANGED
@@ -357,9 +357,12 @@ class List extends BaseList {
357
357
 
358
358
  record.handler?.call(me, record);
359
359
 
360
- if (me.hideOnLeafItemClick && !record.items) {
361
- me.unmount()
362
- }
360
+ record.route && Neo.Main.setRoute({
361
+ appName: me.appName,
362
+ value : record.route
363
+ });
364
+
365
+ me.hideOnLeafItemClick && !record.items && me.unmount();
363
366
 
364
367
  if (record.items) {
365
368
  submenu = me.subMenuMap?.[me.getMenuMapId(recordId)];
@@ -33,6 +33,9 @@ class Model extends BaseModel {
33
33
  }, {
34
34
  name: 'items', // optional
35
35
  type: 'Array'
36
+ }, {
37
+ name: 'route',
38
+ type: 'String'
36
39
  }, {
37
40
  name: 'text',
38
41
  type: 'String'
@@ -7,6 +7,20 @@ import VDomUtil from "../util/VDom.mjs";
7
7
  /**
8
8
  * @class Neo.tree.Accordion
9
9
  * @extends Neo.tree.List
10
+ *
11
+ * Accordion Store expects the following fields
12
+ *
13
+ * [
14
+ * iconCls, // can be defined in fields:icon
15
+ * content, // can be defined in fields:text
16
+ * name, // can be defined in fields:header
17
+ *
18
+ * collapsed, // collapsed state for non-leaf-items
19
+ * isLeaf, // defines it item is leaf-item
20
+ * id, // defines item id
21
+ * parentId // leaf or sub-items need a parentId
22
+ * ]
23
+ *
10
24
  */
11
25
  class AccordionTree extends TreeList {
12
26
  static config = {
@@ -25,19 +39,24 @@ class AccordionTree extends TreeList {
25
39
  */
26
40
  baseCls: ['neo-tree-list'],
27
41
  /**
28
- * @member {Boolean} showCollapseExpandAllIcons=true
42
+ * Set to false to hide the initial root item
43
+ * @member {Boolean} firstParentIsVisible=true
29
44
  */
30
- showCollapseExpandAllIcons: false,
45
+ firstParentIsVisible_: true,
46
+ /**
47
+ * Define the field names for the store to show header, text and icon
48
+ * @member {Object} fields={header:'name',icon:'iconCls',text:'content'}
49
+ */
50
+ fields: {
51
+ header: 'name',
52
+ icon : 'iconCls',
53
+ text : 'content'
54
+ },
31
55
  /**
32
56
  * Set to false will auto expand root parent items and disallow collapsing
33
57
  * @member {Boolean} rootParentIsCollapsible=false
34
58
  */
35
59
  rootParentsAreCollapsible_: false,
36
- /**
37
- * Set to false to hide the initial root item
38
- * @member {Boolean} firstParentIsVisible=true
39
- */
40
- firstParentIsVisible_: true,
41
60
  /**
42
61
  * Currently selected item, which is bindable
43
62
  * @member {Record[]|null} selection=null
@@ -50,6 +69,15 @@ class AccordionTree extends TreeList {
50
69
  * bind : {html: data => data.selection[0].name}
51
70
  */
52
71
  selection_: null,
72
+ /**
73
+ * Set to false will hide the icons for all leaf items
74
+ * @member {Boolean} showIcon=true
75
+ */
76
+ showIcon_: true,
77
+ /**
78
+ * @member {Boolean} showCollapseExpandAllIcons=true
79
+ */
80
+ showCollapseExpandAllIcons: false,
53
81
  /**
54
82
  * @member {Object} _vdom
55
83
  */
@@ -114,6 +142,31 @@ class AccordionTree extends TreeList {
114
142
  }
115
143
  }
116
144
 
145
+ /**
146
+ * Called when changing showIcon
147
+ * Changes the display of the icons
148
+ *
149
+ * @param {Boolean} value
150
+ * @param {Boolean} oldValue
151
+ */
152
+ afterSetShowIcon(value, oldValue) {
153
+ const me = this,
154
+ store = me.store,
155
+ hide = !value;
156
+
157
+ store.items.forEach((record) => {
158
+ const itemId = me.getItemId(record[me.getKeyProperty()]),
159
+ vdom = me.getVdomChild(itemId),
160
+ itemVdom = VDomUtil.getByFlag(vdom, 'icon');
161
+
162
+ if (record.isLeaf) {
163
+ itemVdom.removeDom = hide;
164
+ }
165
+ })
166
+
167
+ me.update()
168
+ }
169
+
117
170
  /**
118
171
  * Triggered before the selectionModel config gets changed.
119
172
  * @param {Neo.selection.Model} value
@@ -211,10 +264,11 @@ class AccordionTree extends TreeList {
211
264
  cls,
212
265
  id,
213
266
  cn : [{
267
+ flag : 'icon',
214
268
  tag : 'span',
215
- cls : ['neo-accordion-item-icon', item.iconCls],
216
- id : id + '__item',
217
- removeDom: !item.isLeaf
269
+ cls : ['neo-accordion-item-icon', item[me.fields.icon]],
270
+ id : id + '__icon',
271
+ removeDom: (!item.isLeaf || !me.showIcon)
218
272
  }, {
219
273
  cls : [itemCls + '-content'],
220
274
  id : id + '__item-content',
@@ -224,13 +278,13 @@ class AccordionTree extends TreeList {
224
278
  tag : 'span',
225
279
  cls : [itemCls + '-content-header'],
226
280
  id : id + '__item-content-header',
227
- innerHTML: item.name
281
+ innerHTML: item[me.fields.header]
228
282
  }, {
229
283
  flag : 'content',
230
284
  tag : 'span',
231
285
  cls : [itemCls + '-content-text'],
232
286
  id : id + '__item-content-text',
233
- innerHTML: item.content
287
+ innerHTML: item[me.fields.text]
234
288
  }]
235
289
  }],
236
290
  style: {
@@ -273,7 +327,6 @@ class AccordionTree extends TreeList {
273
327
  const me = this,
274
328
  selectionModel = me.selectionModel,
275
329
  itemId = item.id,
276
- // ! todo make it String
277
330
  id = Number(itemId.split('__')[1]),
278
331
  record = me.store.get(id);
279
332
 
@@ -354,11 +407,11 @@ class AccordionTree extends TreeList {
354
407
 
355
408
  /**
356
409
  * Update a record
357
- * @param {Object} data
358
- * @param {Object[]} data.fields
359
- * @param {Number} data.index
410
+ * @param {Object} data
411
+ * @param {Object[]} data.fields
412
+ * @param {Number} data.index
360
413
  * @param {Neo.data.Model} data.model
361
- * @param {Record} data.record
414
+ * @param {Record} data.record
362
415
  */
363
416
  onStoreRecordChange(data) {
364
417
  let me = this,