neo.mjs 6.13.0 → 6.15.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.
Files changed (78) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/apps/covid/neo-config.json +1 -1
  3. package/apps/covid/view/HeaderContainer.mjs +2 -2
  4. package/apps/form/view/pages/Page10.mjs +2 -2
  5. package/apps/portal/view/ViewportController.mjs +5 -4
  6. package/apps/portal/view/home/ContentBox.mjs +80 -0
  7. package/apps/portal/view/home/MainContainer.mjs +51 -15
  8. package/apps/portal/view/learn/ContentTreeList.mjs +10 -3
  9. package/apps/portal/view/learn/MainContainerController.mjs +37 -5
  10. package/apps/portal/view/learn/MainContainerModel.mjs +51 -7
  11. package/apps/portal/view/learn/PageContainer.mjs +21 -9
  12. package/apps/sharedcovid/neo-config.json +1 -1
  13. package/apps/sharedcovid/view/HeaderContainer.mjs +2 -2
  14. package/examples/ServiceWorker.mjs +2 -2
  15. package/examples/button/base/MainContainer.mjs +2 -2
  16. package/examples/button/split/MainContainer.mjs +2 -2
  17. package/examples/component/process/MainContainer.mjs +3 -3
  18. package/examples/component/process/realWorldExample/MainContainer.mjs +15 -15
  19. package/examples/component/toast/MainContainer.mjs +4 -4
  20. package/examples/dialog/DemoDialog.mjs +4 -4
  21. package/examples/fieldWithPrefix/MainContainer.mjs +4 -5
  22. package/examples/form/field/{select → combobox}/MainContainer.mjs +7 -10
  23. package/examples/form/field/{select → combobox}/MainModel.mjs +4 -4
  24. package/examples/form/field/{select → combobox}/MainStore.mjs +2 -2
  25. package/examples/form/field/{select → combobox}/app.mjs +1 -1
  26. package/examples/form/field/{select → combobox}/index.html +1 -1
  27. package/examples/form/field/{select → combobox}/neo-config.json +1 -1
  28. package/examples/tableFiltering/MainContainer.mjs +2 -2
  29. package/package.json +4 -4
  30. package/resources/data/deck/learnneo/pages/2023-10-14T19-25-08-153Z.md +16 -1
  31. package/resources/data/deck/learnneo/pages/ComponentsAndContainers.md +180 -0
  32. package/resources/data/deck/learnneo/pages/Config.md +11 -4
  33. package/resources/data/deck/learnneo/pages/DescribingTheUI.md +6 -0
  34. package/resources/data/deck/learnneo/pages/Earthquakes.md +36 -9
  35. package/resources/data/deck/learnneo/pages/Events.md +55 -43
  36. package/resources/data/deck/learnneo/pages/GuideEvents.md +9 -8
  37. package/resources/data/deck/learnneo/pages/References.md +14 -7
  38. package/resources/data/deck/learnneo/pages/TodoList.md +241 -0
  39. package/resources/data/deck/learnneo/pages/WhyNeo-Quick.md +6 -11
  40. package/resources/data/deck/learnneo/tree.json +2 -0
  41. package/resources/scss/src/apps/portal/home/ContentBox.scss +26 -0
  42. package/resources/scss/src/apps/portal/home/MainContainer.scss +4 -12
  43. package/resources/scss/src/apps/portal/learn/MainContainer.scss +0 -7
  44. package/resources/scss/src/apps/portal/learn/PageContainer.scss +35 -0
  45. package/resources/scss/src/apps/portal/learn/PageSectionsPanel.scss +8 -0
  46. package/resources/scss/src/form/field/{Select.scss → ComboBox.scss} +8 -8
  47. package/resources/scss/theme-dark/form/field/ComboBox.scss +9 -0
  48. package/resources/scss/theme-light/form/field/ComboBox.scss +9 -0
  49. package/resources/scss/theme-neo-light/form/field/ComboBox.scss +9 -0
  50. package/src/DefaultConfig.mjs +2 -2
  51. package/src/component/Base.mjs +16 -1
  52. package/src/container/Viewport.mjs +5 -2
  53. package/src/controller/Application.mjs +12 -1
  54. package/src/controller/Base.mjs +15 -4
  55. package/src/controller/Component.mjs +5 -1
  56. package/src/core/Observable.mjs +62 -22
  57. package/src/form/field/Base.mjs +21 -9
  58. package/src/form/field/Chip.mjs +3 -3
  59. package/src/form/field/Color.mjs +3 -3
  60. package/src/form/field/{Select.mjs → ComboBox.mjs} +179 -130
  61. package/src/form/field/Country.mjs +3 -3
  62. package/src/form/field/Text.mjs +7 -10
  63. package/src/form/field/_export.mjs +2 -2
  64. package/src/main/DomEvents.mjs +2 -1
  65. package/src/main/addon/MonacoEditor.mjs +2 -2
  66. package/src/main/addon/Navigator.mjs +27 -24
  67. package/src/selection/ListModel.mjs +13 -14
  68. package/src/selection/Model.mjs +10 -10
  69. package/src/toolbar/Paging.mjs +2 -2
  70. package/src/util/HashHistory.mjs +1 -0
  71. package/src/worker/App.mjs +5 -1
  72. package/src/worker/Manager.mjs +14 -7
  73. package/test/components/app.mjs +1 -1
  74. package/test/components/files/form/field/{Select.mjs → ComboBox.mjs} +9 -7
  75. package/test/components/siesta.js +1 -1
  76. package/resources/scss/theme-dark/form/field/Select.scss +0 -9
  77. package/resources/scss/theme-light/form/field/Select.scss +0 -9
  78. package/resources/scss/theme-neo-light/form/field/Select.scss +0 -9
@@ -0,0 +1,35 @@
1
+ .learn-content-container {
2
+ // align-items: center !important;
3
+ // padding: 0 3rem;
4
+ overflow: scroll;
5
+
6
+ .content-bottom-toolbar {
7
+ gap : 8px;
8
+ padding: 0 3rem;
9
+
10
+ .neo-button {
11
+ flex : 1 !important;
12
+ height : 75px;
13
+ justify-content: flex-start;
14
+
15
+ .neo-button-text, .neo-button-glyph {
16
+ padding-top: 16px;
17
+ }
18
+ }
19
+
20
+ .content-bottom-toolbar-previous:before {
21
+ content : 'Previous Page';
22
+ font-family : var(--core-fontfamily-sans);
23
+ position : absolute;
24
+ top : var(--cmp-button-spacinghorizontal);
25
+ left : var(--cmp-button-spacinghorizontal);
26
+ }
27
+ .content-bottom-toolbar-next:before {
28
+ content : 'Next Page';
29
+ font-family : var(--core-fontfamily-sans);
30
+ position : absolute;
31
+ top : var(--cmp-button-spacinghorizontal);
32
+ right : var(--cmp-button-spacinghorizontal);
33
+ }
34
+ }
35
+ }
@@ -1,6 +1,14 @@
1
1
  .portal-page-sections-panel.neo-panel {
2
2
  border: none; // reset the default 1px
3
3
 
4
+ @media only screen and (max-width: 1200px) {
5
+ display: none;
6
+ }
7
+
8
+ .neo-list .neo-list-item {
9
+ white-space: normal;
10
+ }
11
+
4
12
  .neo-panel-header-toolbar {
5
13
  border : none; // reset the default 1px
6
14
  border-bottom: 1px solid #f2f2f2;;
@@ -1,4 +1,4 @@
1
- .neo-selectfield {
1
+ .neo-combobox {
2
2
  .neo-input-field-wrapper {
3
3
  flex : 1 1 30px;
4
4
  position: relative;
@@ -14,9 +14,9 @@
14
14
 
15
15
  .neo-typeahead-input {
16
16
  border-color: transparent;
17
- color : var(--selectfield-input-hint-color);
17
+ color : var(--combobox-input-hint-color);
18
18
  font : var(--textfield-input-font);
19
- opacity : var(--selectfield-input-hint-opacity);
19
+ opacity : var(--combobox-input-hint-opacity);
20
20
  }
21
21
  }
22
22
 
@@ -26,20 +26,20 @@
26
26
  text-align: left;
27
27
 
28
28
  .neo-highlight-search {
29
- background-color: var(--selectfield-list-search-background-color);
30
- color : var(--selectfield-list-search-color) !important;
29
+ background-color: var(--combobox-list-search-background-color);
30
+ color : var(--combobox-list-search-color) !important;
31
31
  }
32
32
 
33
33
  .neo-selected {
34
34
  .neo-highlight-search {
35
- color: var(--selectfield-list-search-color-selected) !important;
35
+ color: var(--combobox-list-search-color-selected) !important;
36
36
  }
37
37
  }
38
38
  }
39
39
 
40
40
  .neo-picker-container {
41
- border : var(--selectfield-picker-container-border);
42
- border-radius: var(--selectfield-picker-container-border-radius);
41
+ border : var(--combobox-picker-container-border);
42
+ border-radius: var(--combobox-picker-container-border-radius);
43
43
  font-weight : 400;
44
44
  }
45
45
  }
@@ -0,0 +1,9 @@
1
+ :root .neo-theme-dark { // .neo-combobox
2
+ --combobox-input-hint-color : #bbb;
3
+ --combobox-input-hint-opacity : 0.6;
4
+ --combobox-list-search-background-color : inherit;
5
+ --combobox-list-search-color : #fff;
6
+ --combobox-list-search-color-selected : #000;
7
+ --combobox-picker-container-border : 1px solid #bbb;
8
+ --combobox-picker-container-border-radius: 0;
9
+ }
@@ -0,0 +1,9 @@
1
+ :root .neo-theme-light { // .neo-combobox
2
+ --combobox-input-hint-color : #333;
3
+ --combobox-input-hint-opacity : 0.6;
4
+ --combobox-list-search-background-color : inherit;
5
+ --combobox-list-search-color : red;
6
+ --combobox-list-search-color-selected : red;
7
+ --combobox-picker-container-border : 1px solid #1c60a0;
8
+ --combobox-picker-container-border-radius: 3px;
9
+ }
@@ -0,0 +1,9 @@
1
+ :root .neo-theme-neo-light { // .neo-combobox
2
+ --combobox-input-hint-color : #333;
3
+ --combobox-input-hint-opacity : 0.6;
4
+ --combobox-list-search-background-color : inherit;
5
+ --combobox-list-search-color : red;
6
+ --combobox-list-search-color-selected : red;
7
+ --combobox-picker-container-border : 1px solid #1c60a0;
8
+ --combobox-picker-container-border-radius: 3px;
9
+ }
@@ -260,12 +260,12 @@ const DefaultConfig = {
260
260
  useVdomWorker: true,
261
261
  /**
262
262
  * buildScripts/injectPackageVersion.mjs will update this value
263
- * @default '6.12.1'
263
+ * @default '6.15.0'
264
264
  * @memberOf! module:Neo
265
265
  * @name config.version
266
266
  * @type String
267
267
  */
268
- version: '6.12.1'
268
+ version: '6.15.0'
269
269
  };
270
270
 
271
271
  Object.assign(DefaultConfig, {
@@ -933,6 +933,20 @@ class Base extends CoreBase {
933
933
  this.changeVdomRootKey('width', value)
934
934
  }
935
935
 
936
+ /**
937
+ * Triggered after the windowId config got changed
938
+ * @param {Number|null} value
939
+ * @param {Number|null} oldValue
940
+ * @protected
941
+ */
942
+ afterSetWindowId(value, oldValue) {
943
+ let controller = this.controller;
944
+
945
+ if (controller && value) {
946
+ controller.windowId = value
947
+ }
948
+ }
949
+
936
950
  /**
937
951
  * Triggered after the wrapperCls config got changed
938
952
  * @param {String[]|null} value
@@ -1119,7 +1133,8 @@ class Base extends CoreBase {
1119
1133
 
1120
1134
  if (value) {
1121
1135
  return ClassSystemUtil.beforeSetInstance(value, null, {
1122
- component: this
1136
+ component: this,
1137
+ windowId : this.windowId
1123
1138
  })
1124
1139
  }
1125
1140
 
@@ -39,9 +39,12 @@ class Viewport extends Container {
39
39
  onConstructed() {
40
40
  super.onConstructed();
41
41
 
42
+ let {appName, windowId} = this;
43
+
42
44
  this.applyBodyCls && Neo.main.DomAccess.applyBodyCls({
43
- appName: this.appName,
44
- cls : ['neo-body-viewport']
45
+ appName,
46
+ cls: ['neo-body-viewport'],
47
+ windowId
45
48
  })
46
49
  }
47
50
  }
@@ -20,6 +20,11 @@ class Application extends Base {
20
20
  * @protected
21
21
  */
22
22
  className: 'Neo.controller.Application',
23
+ /**
24
+ * @member {String} ntype='application'
25
+ * @protected
26
+ */
27
+ ntype: 'application',
23
28
  /**
24
29
  * @member {String|null} appThemeFolder=null
25
30
  */
@@ -50,7 +55,11 @@ class Application extends Base {
50
55
  * @member {Boolean} rendering=false
51
56
  * @protected
52
57
  */
53
- rendering: false
58
+ rendering: false,
59
+ /**
60
+ * @member {Number|null} windowId=null
61
+ */
62
+ windowId: null
54
63
  }
55
64
 
56
65
  /**
@@ -66,6 +75,8 @@ class Application extends Base {
66
75
 
67
76
  let me = this;
68
77
 
78
+ me.windowId = Neo.config.windowId;
79
+
69
80
  Neo.apps = Neo.apps || {};
70
81
 
71
82
  Neo.apps[me.name] = me;
@@ -95,12 +95,18 @@ class Base extends CoreBase {
95
95
  /**
96
96
  *
97
97
  */
98
- onConstructed() {
99
- let currentHash = HashHistory.first(),
100
- defaultHash = this.defaultHash;
98
+ async onConstructed() {
99
+ let me = this,
100
+ currentHash = HashHistory.first(),
101
+ defaultHash = me.defaultHash;
102
+
103
+ // get outside the construction chain => a related cmp & vm has to be constructed too
104
+ await me.timeout(1);
101
105
 
102
106
  if (currentHash) {
103
- this.onHashChange(currentHash, null)
107
+ if (currentHash.windowId === me.windowId) {
108
+ await me.onHashChange(currentHash, null)
109
+ }
104
110
  } else {
105
111
  /*
106
112
  * worker.App: onLoadApplication() will push config.hash into the HashHistory with a 5ms delay.
@@ -117,6 +123,11 @@ class Base extends CoreBase {
117
123
  * @param {Object} oldValue
118
124
  */
119
125
  async onHashChange(value, oldValue) {
126
+ // We only want to trigger hash changes for the same browser window (SharedWorker context)
127
+ if (value.windowId !== this.windowId) {
128
+ return
129
+ }
130
+
120
131
  let me = this,
121
132
  counter = 0,
122
133
  hasRouteBeenFound = false,
@@ -30,7 +30,11 @@ class Component extends Base {
30
30
  * @member {Object} references=null
31
31
  * @protected
32
32
  */
33
- references: null
33
+ references: null,
34
+ /**
35
+ * @member {Number|null} windowId=null
36
+ */
37
+ windowId: null
34
38
  }
35
39
 
36
40
  /**
@@ -29,28 +29,46 @@ class Observable extends Base {
29
29
  * @param {Object} [scope]
30
30
  * @param {String} [eventId]
31
31
  * @param {Object} [data]
32
- * @param {Number} [order]
32
+ * @param {Number|String} [order]
33
33
  * @returns {String|null} eventId null in case an object gets passed as the name (multiple ids)
34
34
  */
35
35
  addListener(name, opts, scope, eventId, data, order) {
36
36
  let me = this,
37
+ delay = 0,
37
38
  nameObject = typeof name === 'object',
39
+ once = false,
38
40
  listener, existing, eventConfig;
39
41
 
40
42
  if (nameObject) {
43
+ if (name.hasOwnProperty('delay')) {
44
+ delay = name.delay;
45
+ delete name.delay
46
+ }
47
+
48
+ if (name.hasOwnProperty('once')) {
49
+ once = name.once;
50
+ delete name.once
51
+ }
52
+
41
53
  if (name.hasOwnProperty('scope')) {
42
54
  scope = name.scope;
43
- delete name.scope;
55
+ delete name.scope
44
56
  }
45
57
 
46
58
  Object.entries(name).forEach(([key, value]) => {
47
- me.addListener(key, value, scope);
48
- });
59
+ if (Neo.isObject(value)) {
60
+ me.addListener(key, {delay, once, scope, ...value})
61
+ } else {
62
+ me.addListener(key, {delay, fn: value, once, scope})
63
+ }
64
+ })
49
65
  } else if (typeof opts === 'object') {
50
- scope = scope || opts.scope;
51
- listener = opts.fn;
52
- order = order || opts.order;
66
+ delay = delay || opts.delay;
53
67
  eventId = eventId || opts.eventId;
68
+ listener = opts.fn;
69
+ once = once || opts.once;
70
+ order = order || opts.order;
71
+ scope = scope || opts.scope;
54
72
  } else if (typeof opts === 'function') {
55
73
  listener = opts;
56
74
  } else if (typeof opts === 'string') {
@@ -61,10 +79,12 @@ class Observable extends Base {
61
79
 
62
80
  if (!nameObject) {
63
81
  eventConfig = {
64
- fn: listener,
65
- scope,
66
82
  data,
67
- id: eventId || Neo.getId('event')
83
+ delay,
84
+ fn: listener,
85
+ id: eventId || Neo.getId('event'),
86
+ once,
87
+ scope
68
88
  };
69
89
 
70
90
  if (existing = me.listeners?.[name]) {
@@ -85,10 +105,10 @@ class Observable extends Base {
85
105
  me.listeners[name] = [eventConfig];
86
106
  }
87
107
 
88
- return eventConfig.id;
108
+ return eventConfig.id
89
109
  }
90
110
 
91
- return null;
111
+ return null
92
112
  }
93
113
 
94
114
  /**
@@ -101,11 +121,22 @@ class Observable extends Base {
101
121
  callback(fn, scope=this, args) {
102
122
  if (fn) {
103
123
  const handler = this.resolveCallback(fn, scope);
104
-
105
- handler.fn.apply(handler.scope, args);
124
+ handler.fn.apply(handler.scope, args)
106
125
  }
107
126
  }
108
127
 
128
+ /**
129
+ * Internal helper method for events which use the delay option
130
+ * @param {Object} cb
131
+ * @param {Array} args
132
+ * @param {Number} delay
133
+ */
134
+ delayedCallback(cb, args, delay) {
135
+ setTimeout(() => {
136
+ cb.fn.apply(cb.scope, args)
137
+ }, delay)
138
+ }
139
+
109
140
  /**
110
141
  * @param name
111
142
  */
@@ -113,29 +144,38 @@ class Observable extends Base {
113
144
  let me = this,
114
145
  args = [].slice.call(arguments, 1),
115
146
  listeners = me.listeners,
116
- handler, handlers, i, len;
147
+ delay, handler, handlers, i, len;
117
148
 
118
149
  if (listeners && listeners[name]) {
119
150
  handlers = [...listeners[name]];
120
- len = handlers.length;
151
+ len = handlers.length;
121
152
 
122
153
  for (i = 0; i < len; i++) {
123
154
  handler = handlers[i];
155
+ delay = handler.delay;
124
156
 
125
- // Resolve function name on the scope (oe me), or, if it starts with 'up.'
157
+ // Resolve function name on the scope (or me), or, if it starts with 'up.'
126
158
  // look in the ownership hierarchy from me.
127
159
  const cb = me.resolveCallback(handler.fn, handler.scope || me);
128
160
 
129
- // remove the listener, in case the scope no longer exists
161
+ // remove the listener if the scope no longer exists
130
162
  if (cb.scope && !cb.scope.id) {
131
- listeners[name].splice(i, 1);
163
+ listeners[name].splice(i, 1)
132
164
  } else {
133
165
  if (!me.suspendEvents) {
134
166
  // Object event format. Inject firer reference in as 'source'
135
- if (args.length === 1 && typeof(args[0]) === 'object') {
136
- args[0].source = me.id;
167
+ if (args.length === 1 && Neo.isObject(args[0])) {
168
+ args[0].source = me.id
169
+ }
170
+
171
+ // remove the listener if it has the once flag
172
+ handler.once && listeners[name].splice(i, 1)
173
+
174
+ if (Neo.isNumber(delay) && delay > 0) {
175
+ me.delayedCallback(cb, handler.data ? args.concat(handler.data) : args, delay)
176
+ } else {
177
+ cb.fn.apply(cb.scope, handler.data ? args.concat(handler.data) : args)
137
178
  }
138
- cb.fn.apply(cb.scope, handler.data ? args.concat(handler.data) : args);
139
179
  }
140
180
  }
141
181
  }
@@ -81,6 +81,17 @@ class Base extends Component {
81
81
  * @member {String|null} formGroupString=null
82
82
  */
83
83
  formGroupString = null
84
+ /**
85
+ * Base implementation to check if the fields value has changed.
86
+ * Can get overridden in superclasses.
87
+ * @returns {Boolean}
88
+ */
89
+ get isDirty() {
90
+ let originalValue = this.originalConfig.value,
91
+ value = this.value;
92
+
93
+ return value !== originalValue && Neo.isEmpty(value) !== Neo.isEmpty(originalValue)
94
+ }
84
95
  /**
85
96
  * An internal cache for formGroup(s) and the field name
86
97
  * @member {String|null} path=null
@@ -96,7 +107,7 @@ class Base extends Component {
96
107
  let cls = this.cls;
97
108
 
98
109
  NeoArray.toggle(cls, 'neo-is-touched', value);
99
- this.cls = cls;
110
+ this.cls = cls
100
111
  }
101
112
 
102
113
  /**
@@ -118,7 +129,7 @@ class Base extends Component {
118
129
  */
119
130
  afterSetRole(value, oldValue) {
120
131
  this.getInputEl().role = value;
121
- this.update();
132
+ this.update()
122
133
  }
123
134
 
124
135
  /**
@@ -142,7 +153,7 @@ class Base extends Component {
142
153
  returnValue;
143
154
 
144
155
  if (me.formGroupString) {
145
- return me.formGroupString;
156
+ return me.formGroupString
146
157
  }
147
158
 
148
159
  value && group.push(value);
@@ -178,9 +189,9 @@ class Base extends Component {
178
189
  let me = this;
179
190
 
180
191
  if (value || Neo.isBoolean(value) || value === 0) {
181
- me.getInputEl()[key] = value;
192
+ me.getInputEl()[key] = value
182
193
  } else {
183
- delete me.getInputEl()[key];
194
+ delete me.getInputEl()[key]
184
195
  }
185
196
 
186
197
  !silent && me.update()
@@ -295,14 +306,14 @@ class Base extends Component {
295
306
  * @returns {*}
296
307
  */
297
308
  getValue() {
298
- return this.value;
309
+ return this.value
299
310
  }
300
311
 
301
312
  /**
302
313
  * @returns {Boolean}
303
314
  */
304
315
  isValid() {
305
- return true;
316
+ return true
306
317
  }
307
318
 
308
319
  /**
@@ -350,7 +361,8 @@ class Base extends Component {
350
361
  * @param {*} value=null
351
362
  */
352
363
  reset(value=null) {
353
- this.value = value;
364
+ this.originalConfig.value = value;
365
+ this.value = value
354
366
  }
355
367
 
356
368
  /**
@@ -359,7 +371,7 @@ class Base extends Component {
359
371
  * @returns {Boolean} Returns true in case there are no client-side errors
360
372
  */
361
373
  validate(silent=true) {
362
- return true;
374
+ return true
363
375
  }
364
376
  }
365
377
 
@@ -1,10 +1,10 @@
1
- import Select from './Select.mjs';
1
+ import ComboBox from './ComboBox.mjs';
2
2
 
3
3
  /**
4
4
  * @class Neo.form.field.Chip
5
- * @extends Neo.form.field.Select
5
+ * @extends Neo.form.field.ComboBox
6
6
  */
7
- class Chip extends Select {
7
+ class Chip extends ComboBox {
8
8
  static config = {
9
9
  /**
10
10
  * @member {String} className='Neo.form.field.Chip'
@@ -1,12 +1,12 @@
1
1
  import ColorList from '../../list/Color.mjs'
2
- import Select from './Select.mjs';
2
+ import ComboBox from './ComboBox.mjs';
3
3
  import VDomUtil from '../../util/VDom.mjs';
4
4
 
5
5
  /**
6
6
  * @class Neo.form.field.Color
7
- * @extends Neo.form.field.Select
7
+ * @extends Neo.form.field.ComboBox
8
8
  */
9
- class Color extends Select {
9
+ class Color extends ComboBox {
10
10
  static config = {
11
11
  /**
12
12
  * @member {String} className='Neo.form.field.Color'