neo.mjs 5.1.5 → 5.1.7

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/README.md +1 -1
  2. package/apps/ServiceWorker.mjs +2 -2
  3. package/examples/ServiceWorker.mjs +2 -2
  4. package/examples/button/base/MainContainer.mjs +0 -1
  5. package/examples/button/split/MainContainer.mjs +11 -4
  6. package/examples/dialog/DemoDialog.mjs +1 -0
  7. package/examples/toolbar/breadcrumb/view/MainContainer.mjs +78 -26
  8. package/examples/toolbar/breadcrumb/view/MainContainerController.mjs +26 -0
  9. package/examples/tree/MainContainer.mjs +1 -1
  10. package/package.json +2 -2
  11. package/resources/scss/src/button/Split.scss +3 -3
  12. package/resources/scss/src/calendar/view/YearComponent.scss +1 -1
  13. package/resources/scss/src/component/DateSelector.scss +1 -1
  14. package/resources/scss/src/dialog/Base.scss +6 -4
  15. package/resources/scss/src/form/field/Search.scss +2 -2
  16. package/resources/scss/src/form/field/Select.scss +0 -1
  17. package/resources/scss/src/form/field/trigger/Base.scss +1 -0
  18. package/resources/scss/src/form/field/trigger/Clear.scss +1 -1
  19. package/resources/scss/src/list/Base.scss +1 -1
  20. package/resources/scss/src/plugin/Resizable.scss +2 -2
  21. package/resources/scss/src/toolbar/Breadcrumb.scss +1 -1
  22. package/resources/scss/src/tree/List.scss +2 -2
  23. package/resources/scss/theme-dark/form/field/CheckBox.scss +1 -1
  24. package/resources/scss/theme-light/button/Base.scss +1 -1
  25. package/resources/scss/theme-light/form/field/CheckBox.scss +1 -1
  26. package/src/DefaultConfig.mjs +2 -2
  27. package/src/Neo.mjs +10 -6
  28. package/src/button/Split.mjs +73 -9
  29. package/src/component/Base.mjs +19 -3
  30. package/src/component/Toast.mjs +13 -13
  31. package/src/dialog/Base.mjs +20 -3
  32. package/src/dialog/header/Toolbar.mjs +1 -1
  33. package/src/form/field/Picker.mjs +18 -1
  34. package/src/form/field/Select.mjs +22 -9
  35. package/src/list/Base.mjs +10 -1
  36. package/src/toolbar/Base.mjs +38 -35
  37. package/src/toolbar/Breadcrumb.mjs +99 -18
  38. package/src/util/HashHistory.mjs +7 -5
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/neomjs-logo-250.png">
2
+ <img height="250" width="250" src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/logo_rounded.svg">
3
3
  </p>
4
4
 
5
5
  <p align="center">
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.1.5'
23
+ * @member {String} version='5.1.7'
24
24
  */
25
- version: '5.1.5'
25
+ version: '5.1.7'
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='5.1.5'
23
+ * @member {String} version='5.1.7'
24
24
  */
25
- version: '5.1.5'
25
+ version: '5.1.7'
26
26
  }
27
27
 
28
28
  /**
@@ -13,7 +13,6 @@ import TextField from '../../../src/form/field/Text.mjs';
13
13
  class MainContainer extends ConfigurationViewport {
14
14
  static config = {
15
15
  className : 'Neo.examples.button.base.MainContainer',
16
- autoMount : true,
17
16
  configItemLabelWidth: 160,
18
17
  configItemWidth : 280,
19
18
  layout : {ntype: 'hbox', align: 'stretch'}
@@ -1,7 +1,8 @@
1
- import SplitButton from '../../../src/button/Split.mjs';
1
+ import CheckBox from '../../../src/form/field/CheckBox.mjs';
2
2
  import ConfigurationViewport from '../../ConfigurationViewport.mjs';
3
3
  import Radio from '../../../src/form/field/Radio.mjs';
4
4
  import NumberField from '../../../src/form/field/Number.mjs';
5
+ import SplitButton from '../../../src/button/Split.mjs';
5
6
  import TextField from '../../../src/form/field/Text.mjs';
6
7
 
7
8
  /**
@@ -11,9 +12,8 @@ import TextField from '../../../src/form/field/Text.mjs';
11
12
  class MainContainer extends ConfigurationViewport {
12
13
  static config = {
13
14
  className : 'Neo.examples.button.split.MainContainer',
14
- autoMount : true,
15
- configItemLabelWidth: 110,
16
- configItemWidth : 230,
15
+ configItemLabelWidth: 160,
16
+ configItemWidth : 280,
17
17
  layout : {ntype: 'hbox', align: 'stretch'}
18
18
  }
19
19
 
@@ -29,10 +29,17 @@ class MainContainer extends ConfigurationViewport {
29
29
  minValue : 30,
30
30
  stepSize : 5,
31
31
  value : me.exampleComponent.height
32
+ }, {
33
+ module : CheckBox,
34
+ checked : me.exampleComponent.hideTriggerButton,
35
+ labelText: 'hideTriggerButton',
36
+ listeners: {change: me.onConfigChange.bind(me, 'hideTriggerButton')},
37
+ style : {marginTop: '10px'}
32
38
  }, {
33
39
  module : TextField, // todo: selectField with options
34
40
  labelText : 'iconCls',
35
41
  listeners : {change: me.onConfigChange.bind(me, 'iconCls')},
42
+ style : {marginTop: '10px'},
36
43
  value : me.exampleComponent.iconCls
37
44
  }, {
38
45
  module : TextField, // todo: colorPickerField
@@ -7,6 +7,7 @@ import Dialog from '../../src/dialog/Base.mjs';
7
7
  class DemoDialog extends Dialog {
8
8
  static config = {
9
9
  className: 'Neo.examples.dialog.DemoWindow',
10
+ title : 'My Dialog',
10
11
 
11
12
  wrapperStyle: {
12
13
  height: '40%',
@@ -1,6 +1,6 @@
1
- import BreadcrumbToolbar from '../../../../src/toolbar/Breadcrumb.mjs';
2
- import Store from '../../../../src/data/Store.mjs';
3
- import Viewport from '../../../../src/container/Viewport.mjs';
1
+ import BreadcrumbToolbar from '../../../../src/toolbar/Breadcrumb.mjs';
2
+ import MainContainerController from './MainContainerController.mjs';
3
+ import Viewport from '../../../../src/container/Viewport.mjs';
4
4
 
5
5
  /**
6
6
  * @class Neo.examples.toolbar.breadcrumb.view.MainContainer
@@ -13,6 +13,10 @@ class MainContainer extends Viewport {
13
13
  * @protected
14
14
  */
15
15
  className: 'Neo.examples.toolbar.breadcrumb.view.MainContainer',
16
+ /**
17
+ * @member {Neo.controller.Component} controller=MainContainerController
18
+ */
19
+ controller: MainContainerController,
16
20
  /**
17
21
  * @member {Object[]} items
18
22
  */
@@ -20,38 +24,86 @@ class MainContainer extends Viewport {
20
24
  module : BreadcrumbToolbar,
21
25
  activeKey: 2,
22
26
  flex : 'none',
27
+ reference: 'breadcrumb-toolbar',
23
28
 
24
29
  store: {
25
- module: Store,
26
-
27
- model: {
28
- fields: [{
29
- name: 'id',
30
- type: 'Integer'
31
- }, {
32
- name: 'name',
33
- type: 'String'
34
- }, {
35
- name: 'parentId',
36
- type: 'Integer'
37
- }, {
38
- name: 'route',
39
- type: 'String'
40
- }]
41
- },
42
-
43
30
  data: [
44
- {id: 1, name: 'Home', parentId: null, route: '/home/'},
45
- {id: 2, name: 'Accessibility', parentId: 1, route: '/home/accessibility/'},
46
- {id: 3, name: 'Imprint', parentId: 1, route: '/home/imprint/'},
47
- {id: 4, name: 'News', parentId: 1, route: '/home/news/'},
31
+ {id: 1, name: 'Home', parentId: null, route: '/home/'},
32
+ {id: 2, name: 'Accessibility', parentId: 1, route: '/home/accessibility/'},
33
+ {id: 3, name: 'Imprint', parentId: 1, route: '/home/imprint/'},
34
+ {id: 4, name: 'News', parentId: 1, route: '/home/news/'},
35
+ {id: 5, name: 'Forms', parentId: null, route: '/forms/'},
36
+ {id: 6, name: 'Form 1', parentId: 5, route: '/forms/form1/'},
37
+ {id: 7, name: 'Page 1', parentId: 6, route: '/forms/form1/page1/'},
38
+ {id: 8, name: 'Page 2', parentId: 6, route: '/forms/form1/page2/'},
39
+ {id: 9, name: 'Form 2', parentId: 5, route: '/forms/form2/'},
40
+ {id: 10, name: 'Page 1', parentId: 9, route: '/forms/form2/page1/'},
41
+ {id: 11, name: 'Page 2', parentId: 9, route: '/forms/form2/page2/'}
48
42
  ]
49
43
  }
44
+ }, {
45
+ ntype: 'label',
46
+ style: {marginTop: '40px'},
47
+ text : 'Navigate to'
48
+ }, {
49
+ ntype : 'toolbar',
50
+ flex : 'none',
51
+ layout: {ntype: 'vbox', align: 'start'},
52
+
53
+ itemDefaults: {
54
+ ntype : 'button',
55
+ handler: 'onActiveKeyButtonClick',
56
+ style : {marginTop: '10px'}
57
+ },
58
+
59
+ items: [{
60
+ activeKey: 1,
61
+ text : 'Home'
62
+ }, {
63
+ activeKey: 2,
64
+ style : {marginLeft: '20px', marginTop: '10px'},
65
+ text : 'Accessibility'
66
+ }, {
67
+ activeKey: 3,
68
+ style : {marginLeft: '20px', marginTop: '10px'},
69
+ text : 'Imprint'
70
+ }, {
71
+ activeKey: 4,
72
+ style : {marginLeft: '20px', marginTop: '10px'},
73
+ text : 'News'
74
+ }, {
75
+ activeKey: 5,
76
+ text : 'Forms'
77
+ }, {
78
+ activeKey: 6,
79
+ style : {marginLeft: '20px', marginTop: '10px'},
80
+ text : 'Form 1'
81
+ }, {
82
+ activeKey: 7,
83
+ style : {marginLeft: '40px', marginTop: '10px'},
84
+ text : 'Page 1'
85
+ }, {
86
+ activeKey: 8,
87
+ style : {marginLeft: '40px', marginTop: '10px'},
88
+ text : 'Page 2'
89
+ }, {
90
+ activeKey: 9,
91
+ style : {marginLeft: '20px', marginTop: '10px'},
92
+ text : 'Form 2'
93
+ }, {
94
+ activeKey: 10,
95
+ style : {marginLeft: '40px', marginTop: '10px'},
96
+ text : 'Page 1'
97
+ }, {
98
+ activeKey: 11,
99
+ style : {marginLeft: '40px', marginTop: '10px'},
100
+ text : 'Page 2'
101
+ }]
50
102
  }],
51
103
  /**
52
104
  * @member {Object} layout={ntype:'vbox',align:'stretch'}
53
105
  */
54
- layout: {ntype: 'vbox', align: 'stretch'},
106
+ layout: {ntype: 'vbox', align: 'start'},
55
107
  /**
56
108
  * @member {Object} style={padding:'20px'}
57
109
  */
@@ -0,0 +1,26 @@
1
+ import Controller from '../../../../src/controller/Component.mjs';
2
+
3
+ /**
4
+ * @class Neo.examples.toolbar.breadcrumb.view.MainContainerController
5
+ * @extends Neo.controller.Component
6
+ */
7
+ class MainContainerController extends Controller {
8
+ static config = {
9
+ /**
10
+ * @member {String} className='Neo.examples.toolbar.breadcrumb.view.MainContainerController'
11
+ * @protected
12
+ */
13
+ className: 'Neo.examples.toolbar.breadcrumb.view.MainContainerController'
14
+ }
15
+
16
+ /**
17
+ * @param {Object} data
18
+ */
19
+ onActiveKeyButtonClick(data) {
20
+ this.getReference('breadcrumb-toolbar').activeKey = data.component.activeKey;
21
+ }
22
+ }
23
+
24
+ Neo.applyClassConfig(MainContainerController);
25
+
26
+ export default MainContainerController;
@@ -121,7 +121,7 @@ class MainContainer extends ConfigurationViewport {
121
121
  * @param {Object} opts
122
122
  */
123
123
  onLeafNodesOnlyChange(opts) {
124
- let dragZone = this.exampleComponent.dragZone;
124
+ let dragZone = this.exampleComponent.down({module: ApiTreeList}).dragZone;
125
125
 
126
126
  if (dragZone) {
127
127
  dragZone.leafNodesOnly = opts.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.1.5",
3
+ "version": "5.1.7",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "homepage": "https://neomjs.github.io/pages/",
43
43
  "dependencies": {
44
- "@fortawesome/fontawesome-free": "^6.2.1",
44
+ "@fortawesome/fontawesome-free": "^6.3.0",
45
45
  "@material/mwc-button": "^0.27.0",
46
46
  "@material/mwc-textfield": "^0.27.0",
47
47
  "autoprefixer": "^10.4.13",
@@ -8,9 +8,9 @@
8
8
  border : none;
9
9
  margin : 0;
10
10
  padding: .5em;
11
+ }
11
12
 
12
- &:last-child {
13
- border-left: v(button-border-width) solid v(button-border-color);
14
- }
13
+ .neo-trigger-button {
14
+ border-left: v(button-border-width) solid v(button-border-color);
15
15
  }
16
16
  }
@@ -56,7 +56,7 @@
56
56
 
57
57
  &:before {
58
58
  color : v(dateselector-nav-button-color);
59
- font-family: "Font Awesome 5 Free";
59
+ font-family: var(--fa-style-family-classic);
60
60
  font-weight: 900;
61
61
  transition : color 150ms cubic-bezier(0.4, 0, 0.2, 1);
62
62
  }
@@ -185,7 +185,7 @@
185
185
 
186
186
  &:before {
187
187
  color : v(dateselector-nav-button-color);
188
- font-family: "Font Awesome 5 Free";
188
+ font-family: var(--fa-style-family-classic);
189
189
  font-size : 18px;
190
190
  font-weight: 900;
191
191
  transition : color 150ms cubic-bezier(0.4, 0, 0.2, 1);
@@ -60,10 +60,6 @@
60
60
  border : none;
61
61
  border-bottom: 1px solid v(dialog-border-color);
62
62
 
63
- &.neo-draggable {
64
- cursor: move;
65
- }
66
-
67
63
  .neo-button {
68
64
  border : 0;
69
65
  margin-right: 0.3em;
@@ -81,12 +77,18 @@
81
77
 
82
78
  .neo-panel-header-text {
83
79
  color : v(dialog-header-color);
80
+ overflow : hidden;
84
81
  pointer-events: none;
82
+ text-overflow : ellipsis;
85
83
  user-drag : none; // not supported yet
86
84
  user-select : none;
87
85
 
88
86
  -webkit-user-drag: none;
89
87
  }
88
+
89
+ &.neo-draggable {
90
+ cursor: move;
91
+ }
90
92
  }
91
93
  }
92
94
  }
@@ -3,7 +3,7 @@
3
3
  color : v(searchfield-glyph-color);
4
4
  content : "\f002";
5
5
  display : inline-block;
6
- font-family: "Font Awesome 5 Free";
6
+ font-family: var(--fa-style-family-classic);
7
7
  font-size : 14px;
8
8
  font-weight: 900;
9
9
  padding : 7px;
@@ -16,4 +16,4 @@
16
16
  padding-left: 25px;
17
17
  }
18
18
  }
19
- }
19
+ }
@@ -19,7 +19,6 @@
19
19
  border-color: transparent;
20
20
  color : v(selectfield-input-hint-color);
21
21
  font : v(textfield-input-font);
22
- min-height : 25px;
23
22
  opacity : v(selectfield-input-hint-opacity);
24
23
  }
25
24
  }
@@ -4,6 +4,7 @@
4
4
  color : v(trigger-color);
5
5
  cursor : pointer;
6
6
  display : flex;
7
+ flex-shrink : 0;
7
8
  font-size : 14px;
8
9
  height : v(textfield-input-height); // has to be 1px smaller to not overlap the bottom border on hover
9
10
  justify-content: center;
@@ -16,4 +16,4 @@
16
16
  border-left-color: transparent;
17
17
  width : 0;
18
18
  }
19
- }
19
+ }
@@ -20,7 +20,7 @@
20
20
  color : v(list-item-glyph-color);
21
21
  content : "\f0c8";
22
22
  display : inline-block;
23
- font-family: "Font Awesome 5 Free";
23
+ font-family: var(--fa-style-family-classic);
24
24
  width : 25px;
25
25
  }
26
26
 
@@ -6,7 +6,7 @@
6
6
  align-items : center;
7
7
  color : #bbb;
8
8
  display : flex;
9
- font-family : "Font Awesome 5 Free";
9
+ font-family : var(--fa-style-family-classic);
10
10
  font-weight : 600;
11
11
  height : 10px;
12
12
  justify-content: center;
@@ -110,4 +110,4 @@
110
110
  transform: rotate(270deg);
111
111
  }
112
112
  }
113
- }
113
+ }
@@ -7,7 +7,7 @@
7
7
  color : v(button-text-color);
8
8
  content : "\f105";
9
9
  display : block;
10
- font-family : "Font Awesome 5 Free";
10
+ font-family : var(--fa-style-family-classic);
11
11
  font-weight : 600;
12
12
  position : absolute;
13
13
  right : -20px;
@@ -48,7 +48,7 @@
48
48
  color : v(list-item-glyph-color); // todo: tree-list var
49
49
  content : "\f1f9";
50
50
  display : inline-block;
51
- font-family: "Font Awesome 5 Free";
51
+ font-family: var(--fa-style-family-classic);
52
52
  font-weight: 900;
53
53
  width : 25px;
54
54
  }
@@ -75,7 +75,7 @@
75
75
  .neo-treelist-menu-item-content:before {
76
76
  color : v(tree-list-menu-item-color);
77
77
  display : inline-block;
78
- font-family: "Font Awesome 5 Free";
78
+ font-family: var(--fa-style-family-classic);
79
79
  font-weight: 900;
80
80
  width : 25px;
81
81
  }
@@ -1,7 +1,7 @@
1
1
  $neoMap: map-merge($neoMap, (
2
2
  'checkboxfield-color' : #eee,
3
3
  'checkboxfield-color-checked' : #64B5F6,
4
- 'checkboxfield-icon-font-family': '"Font Awesome 5 Free"',
4
+ 'checkboxfield-icon-font-family': var(--fa-style-family-classic),
5
5
  'checkboxfield-icon-font-size' : 14px,
6
6
  'checkboxfield-label-top-margin': 0 0 3px
7
7
  ));
@@ -30,7 +30,7 @@ $neoMap: map-merge($neoMap, (
30
30
  'button-text-color' : #1c60a0,
31
31
  'button-text-color-active' : #1c60a0,
32
32
  'button-text-color-disabled' : #1c60a0,
33
- 'button-text-color-hover' : #bbb,
33
+ 'button-text-color-hover' : #1c60a0,
34
34
  'button-text-font-family' : #{neo(neo-font-family)},
35
35
  'button-text-font-size' : 11px,
36
36
  'button-text-font-weight' : 600,
@@ -1,7 +1,7 @@
1
1
  $neoMap: map-merge($neoMap, (
2
2
  'checkboxfield-color' : #aaa,
3
3
  'checkboxfield-color-checked' : #1c60a0,
4
- 'checkboxfield-icon-font-family': '"Font Awesome 5 Free"',
4
+ 'checkboxfield-icon-font-family': var(--fa-style-family-classic),
5
5
  'checkboxfield-icon-font-size' : 14px,
6
6
  'checkboxfield-label-top-margin': 0 0 3px
7
7
  ));
@@ -237,12 +237,12 @@ const DefaultConfig = {
237
237
  useVdomWorker: true,
238
238
  /**
239
239
  * buildScripts/injectPackageVersion.mjs will update this value
240
- * @default '5.1.5'
240
+ * @default '5.1.7'
241
241
  * @memberOf! module:Neo
242
242
  * @name config.version
243
243
  * @type String
244
244
  */
245
- version: '5.1.5'
245
+ version: '5.1.7'
246
246
  };
247
247
 
248
248
  Object.assign(DefaultConfig, {
package/src/Neo.mjs CHANGED
@@ -52,7 +52,7 @@ Neo = globalThis.Neo = Object.assign({
52
52
  ntypeMap = Neo.ntypeMap,
53
53
  proto = cls.prototype || cls,
54
54
  protos = [],
55
- cfg, config, ctor, overrides;
55
+ cfg, config, ctor, ntype;
56
56
 
57
57
  while (proto.__proto__) {
58
58
  ctor = proto.constructor;
@@ -74,7 +74,7 @@ Neo = globalThis.Neo = Object.assign({
74
74
  ctor = element.constructor;
75
75
 
76
76
  cfg = ctor.config || {};
77
-
77
+
78
78
  if (Neo.overwrites) {
79
79
  ctor.applyOverwrites(cfg);
80
80
  }
@@ -99,11 +99,15 @@ Neo = globalThis.Neo = Object.assign({
99
99
  });
100
100
 
101
101
  if (Object.hasOwn(cfg, 'ntype')) {
102
- if (Object.hasOwn(ntypeMap, cfg.ntype)) {
103
- throw new Error(`ntype conflict for '${cfg.ntype}' inside the classes:\n${ntypeMap[cfg.ntype]}\n${cfg.className}`);
102
+ ntype = cfg.ntype;
103
+
104
+ // Running the docs app inside a workspace can pull in the same classes from different roots,
105
+ // so we want to check for different class names as well
106
+ if (Object.hasOwn(ntypeMap, ntype) && cfg.className !== ntypeMap[ntype]) {
107
+ throw new Error(`ntype conflict for '${ntype}' inside the classes:\n${ntypeMap[ntype]}\n${cfg.className}`);
104
108
  }
105
109
 
106
- ntypeMap[cfg.ntype] = cfg.className;
110
+ ntypeMap[ntype] = cfg.className;
107
111
  }
108
112
 
109
113
  mixins = Object.hasOwn(config, 'mixins') && config.mixins || [];
@@ -396,7 +400,7 @@ Neo = globalThis.Neo = Object.assign({
396
400
  *
397
401
  * @memberOf module:Neo
398
402
  * @param {Array|String} names The class name string containing dots or an Array of the string parts
399
- * @param {Boolean} [create] Set create to true to create empty objects for non existing parts
403
+ * @param {Boolean} [create] Set create to true to create empty objects for non-existing parts
400
404
  * @param {Object} [scope] Set a different starting point as globalThis
401
405
  * @returns {Object} reference to the toplevel namespace
402
406
  */
@@ -18,6 +18,10 @@ class Split extends Button {
18
18
  * @protected
19
19
  */
20
20
  ntype: 'split-button',
21
+ /**
22
+ * @member {Boolean} hideTriggerButton_=false
23
+ */
24
+ hideTriggerButton_: false,
21
25
  /**
22
26
  * Read only, it will get created inside the ctor.
23
27
  * Use triggerButtonConfig to pass initial config for it.
@@ -63,11 +67,14 @@ class Split extends Button {
63
67
  me.triggerButton = Neo.create({
64
68
  module : Button,
65
69
  appName : me.appName,
70
+ cls : ['neo-trigger-button'],
66
71
  disabled: me.disabled,
67
72
  handler : me.splitButtonHandler,
73
+ hidden : me.hideTriggerButton,
68
74
  iconCls : me.triggerButtonIconCls,
69
- parentId: me.id,
75
+ parentId: me.vdom.id, // wrapper id
70
76
  pressed : me.pressed,
77
+ ui : me.ui,
71
78
  ...me.triggerButtonConfig
72
79
  });
73
80
 
@@ -75,6 +82,20 @@ class Split extends Button {
75
82
  me.update();
76
83
  }
77
84
 
85
+ /**
86
+ * Triggered after the appName config got changed
87
+ * @param {String|null} value
88
+ * @param {String|null} oldValue
89
+ * @protected
90
+ */
91
+ afterSetAppName(value, oldValue) {
92
+ if (this.triggerButton) {
93
+ this.triggerButton.appName = value;
94
+ }
95
+
96
+ super.afterSetAppName(value, oldValue);
97
+ }
98
+
78
99
  /**
79
100
  * Triggered after the disabled config got changed
80
101
  * @param {Boolean} value
@@ -82,15 +103,42 @@ class Split extends Button {
82
103
  * @protected
83
104
  */
84
105
  afterSetDisabled(value, oldValue) {
85
- let me = this;
106
+ let triggerButton = this.triggerButton;
86
107
 
87
- if (me.triggerButton) {
88
- me.triggerButton.disabled = value;
108
+ if (triggerButton) {
109
+ triggerButton.disabled = value;
89
110
  }
90
111
 
91
112
  super.afterSetDisabled(value, oldValue);
92
113
  }
93
114
 
115
+ /**
116
+ * Triggered after the id config got changed
117
+ * @param {String} value
118
+ * @param {String} oldValue
119
+ * @protected
120
+ */
121
+ afterSetId(value, oldValue) {
122
+ this.vdom.id = value + '__wrapper';
123
+
124
+ // silent vdom update, the super call will trigger the engine
125
+ super.afterSetId(value, oldValue);
126
+ }
127
+
128
+ /**
129
+ * Triggered after the hideTriggerButton config got changed
130
+ * @param {Boolean} value
131
+ * @param {Boolean} oldValue
132
+ * @protected
133
+ */
134
+ afterSetHideTriggerButton(value, oldValue) {
135
+ let triggerButton = this.triggerButton;
136
+
137
+ if (triggerButton) {
138
+ triggerButton.hidden = value;
139
+ }
140
+ }
141
+
94
142
  /**
95
143
  * Triggered after the pressed config got changed
96
144
  * @param {Boolean} value
@@ -98,10 +146,10 @@ class Split extends Button {
98
146
  * @protected
99
147
  */
100
148
  afterSetPressed(value, oldValue) {
101
- let me = this;
149
+ let triggerButton = this.triggerButton;
102
150
 
103
- if (me.triggerButton) {
104
- me.triggerButton.pressed = value;
151
+ if (triggerButton) {
152
+ triggerButton.pressed = value;
105
153
  }
106
154
 
107
155
  super.afterSetPressed(value, oldValue);
@@ -114,11 +162,27 @@ class Split extends Button {
114
162
  * @protected
115
163
  */
116
164
  afterSetTriggerButtonIconCls(value, oldValue) {
117
- if (this.triggerButton) {
118
- this.triggerButton.iconCls = value;
165
+ let triggerButton = this.triggerButton;
166
+
167
+ if (triggerButton) {
168
+ triggerButton.iconCls = value;
119
169
  }
120
170
  }
121
171
 
172
+ /**
173
+ * @param {String|null} value
174
+ * @param {String|null} oldValue
175
+ */
176
+ afterSetUi(value, oldValue) {
177
+ let triggerButton = this.triggerButton;
178
+
179
+ if (triggerButton) {
180
+ triggerButton.ui = value;
181
+ }
182
+
183
+ super.afterSetUi(value, oldValue);
184
+ }
185
+
122
186
  /**
123
187
  * @param {Boolean} [updateParentVdom=false]
124
188
  * @param {Boolean} [silent=false]
@@ -252,6 +252,12 @@ class Base extends CoreBase {
252
252
  * @protected
253
253
  */
254
254
  rendering_: false,
255
+ /**
256
+ * Specify a role tag attribute for the vdom root.
257
+ * See: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles
258
+ * @member {String|null} role_=null
259
+ */
260
+ role_: null,
255
261
  /**
256
262
  * Set this to true for bulk updates.
257
263
  * Ensure to set it back to false afterwards.
@@ -625,6 +631,16 @@ class Base extends CoreBase {
625
631
  }
626
632
  }
627
633
 
634
+ /**
635
+ * Triggered after the role config got changed
636
+ * @param {String|null} value
637
+ * @param {String|null} oldValue
638
+ * @protected
639
+ */
640
+ afterSetRole(value, oldValue) {
641
+ this.changeVdomRootKey('role', value);
642
+ }
643
+
628
644
  /**
629
645
  * Triggered after the style config got changed
630
646
  * @param {Object} value
@@ -1643,11 +1659,11 @@ class Base extends CoreBase {
1643
1659
  let me = this;
1644
1660
 
1645
1661
  if (me.hideMode !== 'visibility') {
1646
- me.vdom.removeDom = false;
1647
- me.update();
1662
+ delete me.vdom.removeDom;
1663
+ !me.silentVdomUpdate && me.render(true);
1648
1664
  } else {
1649
1665
  let style = me.style;
1650
- style.visibility = 'visible';
1666
+ delete style.visibility;
1651
1667
  me.style = style;
1652
1668
  }
1653
1669
 
@@ -1,6 +1,6 @@
1
1
  import Base from '../component/Base.mjs';
2
- import ToastManager from '../manager/Toast.mjs';
3
2
  import NeoArray from "../util/Array.mjs";
3
+ import ToastManager from '../manager/Toast.mjs';
4
4
 
5
5
  /**
6
6
  * @class Neo.component.Toast
@@ -21,18 +21,6 @@ import NeoArray from "../util/Array.mjs";
21
21
  })
22
22
  */
23
23
  class Toast extends Base {
24
- /**
25
- * Used by the ToastManager
26
- * @member {Boolean} running=false
27
- * @private
28
- */
29
- running = false
30
- /**
31
- * Timeout in ms after which the toast is removed
32
- * @member {Number} timeout=3000
33
- */
34
- timeout = 3000
35
-
36
24
  /**
37
25
  * Valid values for positions
38
26
  * @member {String[]} positions = ['tl','tc','tr','bl','bc','br']
@@ -122,6 +110,18 @@ class Toast extends Base {
122
110
  }]}
123
111
  }
124
112
 
113
+ /**
114
+ * Used by the ToastManager
115
+ * @member {Boolean} running=false
116
+ * @private
117
+ */
118
+ running = false
119
+ /**
120
+ * Timeout in ms after which the toast is removed
121
+ * @member {Number} timeout=3000
122
+ */
123
+ timeout = 3000
124
+
125
125
  /**
126
126
  * @param {Object} config
127
127
  */
@@ -113,9 +113,9 @@ class Base extends Panel {
113
113
  */
114
114
  resizablePluginConfig: null,
115
115
  /**
116
- * @member {String} title='Dialog Title'
116
+ * @member {String|null} title_=null
117
117
  */
118
- title: 'Dialog Title',
118
+ title_: null,
119
119
  /**
120
120
  * @member {Object} _vdom
121
121
  */
@@ -268,6 +268,18 @@ class Base extends Panel {
268
268
  });
269
269
  }
270
270
 
271
+ /**
272
+ * Triggered after the title config got changed
273
+ * @param {String|null} value
274
+ * @param {String|null} oldValue
275
+ * @protected
276
+ */
277
+ afterSetTitle(value, oldValue) {
278
+ if (this.headerToolbar) {
279
+ this.headerToolbar.title = value;
280
+ }
281
+ }
282
+
271
283
  /**
272
284
  *
273
285
  */
@@ -419,7 +431,12 @@ class Base extends Panel {
419
431
  maximize: me.maximize
420
432
  };
421
433
 
422
- map[data.action].call(me, data);
434
+ map[data.action]?.call(me, data);
435
+
436
+ me.fire('headerAction', {
437
+ dialog: me,
438
+ ...data
439
+ })
423
440
  }
424
441
 
425
442
  /**
@@ -47,7 +47,7 @@ class Toolbar extends Base {
47
47
  * @protected
48
48
  */
49
49
  afterSetTitle(value, oldValue) {
50
- oldValue && this.down({flag: 'title-label'})?.set({
50
+ this.down({flag: 'title-label'})?.set({
51
51
  removeDom: !value,
52
52
  text : value
53
53
  })
@@ -79,6 +79,11 @@ class Picker extends Text {
79
79
  * @member {Number|null} pickerWidth=100
80
80
  */
81
81
  pickerWidth: 100,
82
+ /**
83
+ * @member {Boolean} showPickerOnFocus=false
84
+ * @protected
85
+ */
86
+ showPickerOnFocus: false,
82
87
  /**
83
88
  * @member {Object|Object[]} triggers=[]
84
89
  * @protected
@@ -168,7 +173,7 @@ class Picker extends Text {
168
173
  id : me.getPickerId(),
169
174
  items : pickerComponent ? [pickerComponent] : [],
170
175
  maxHeight: me.pickerMaxHeight,
171
- vdom : {cn: [], tabIndex: -1},
176
+ vdom : {cn: [], 'aria-activedescendant': me.id, tabIndex: -1},
172
177
  width : me.pickerWidth,
173
178
  ...me.pickerConfig,
174
179
 
@@ -258,6 +263,18 @@ class Picker extends Text {
258
263
  me.pickerIsMounted = false;
259
264
  }
260
265
 
266
+ /**
267
+ * @param {Object} data
268
+ * @protected
269
+ */
270
+ onFocusEnter(data) {
271
+ super.onFocusEnter(data);
272
+
273
+ let me = this;
274
+
275
+ me.showPickerOnFocus && !me.pickerIsMounted && me.getClientRectsThenShow();
276
+ }
277
+
261
278
  /**
262
279
  * @param {Object} data
263
280
  * @protected
@@ -82,6 +82,10 @@ class Select extends Picker {
82
82
  * @protected
83
83
  */
84
84
  record: null,
85
+ /**
86
+ * @member {String|null} role='listbox'
87
+ */
88
+ role: 'listbox',
85
89
  /**
86
90
  * @member {Neo.data.Store|null} store_=null
87
91
  */
@@ -128,6 +132,7 @@ class Select extends Picker {
128
132
  module : List,
129
133
  appName : me.appName,
130
134
  displayField : me.displayField,
135
+ itemRole : 'option',
131
136
  parentId : me.id,
132
137
  selectionModel: {stayInList: false},
133
138
  store : me.store,
@@ -361,6 +366,21 @@ class Select extends Picker {
361
366
  return me.record?.[me.valueField] || me.value
362
367
  }
363
368
 
369
+ /**
370
+ * @param {Object} data
371
+ * @protected
372
+ */
373
+ handleKeyDownEnter(data) {
374
+ let me = this;
375
+
376
+ if (me.pickerIsMounted) {
377
+ me.selectListItem();
378
+ super.onKeyDownEnter(data)
379
+ } else {
380
+ super.onKeyDownEnter(data, me.selectListItem)
381
+ }
382
+ }
383
+
364
384
  /**
365
385
  * @param {Object} data
366
386
  * @protected
@@ -406,7 +426,7 @@ class Select extends Picker {
406
426
  * @protected
407
427
  */
408
428
  onKeyDownDown(data) {
409
- this.onKeyDownEnter(data)
429
+ this.handleKeyDownEnter(data)
410
430
  }
411
431
 
412
432
  /**
@@ -414,14 +434,7 @@ class Select extends Picker {
414
434
  * @protected
415
435
  */
416
436
  onKeyDownEnter(data) {
417
- let me = this;
418
-
419
- if (me.pickerIsMounted) {
420
- me.selectListItem();
421
- super.onKeyDownEnter(data)
422
- } else {
423
- super.onKeyDownEnter(data, me.selectListItem)
424
- }
437
+ this.handleKeyDownEnter(data);
425
438
  }
426
439
 
427
440
  /**
package/src/list/Base.mjs CHANGED
@@ -132,6 +132,11 @@ class Base extends Component {
132
132
  {tag: 'ul', cn: []}
133
133
  }
134
134
 
135
+ /**
136
+ * @member {String|null} itemRole=null
137
+ */
138
+ itemRole = null
139
+
135
140
  /**
136
141
  * @param {Object} config
137
142
  */
@@ -370,6 +375,10 @@ class Base extends Component {
370
375
  tabIndex: -1
371
376
  };
372
377
 
378
+ if (me.itemRole) {
379
+ item.role = me.itemRole;
380
+ }
381
+
373
382
  switch (Neo.typeOf(itemContent)) {
374
383
  case null: {
375
384
  return null;
@@ -545,7 +554,7 @@ class Base extends Component {
545
554
  let itemId = vnodeId.split('__')[1],
546
555
  model = this.store.model,
547
556
  keyField = model?.getField(model.keyProperty),
548
- keyType = keyField?.type.toLowerCase();
557
+ keyType = keyField?.type?.toLowerCase();
549
558
 
550
559
  if (keyType === 'integer' || keyType === 'number') {
551
560
  itemId = parseInt(itemId);
@@ -11,10 +11,10 @@ import NeoArray from '../util/Array.mjs';
11
11
  class Base extends Container {
12
12
  /**
13
13
  * Valid values for dock
14
- * @member {String[]} dockPositions=['top','right','bottom','left']
14
+ * @member {String[]} dockPositions=['top','right','bottom','left', null]
15
15
  * @static
16
16
  */
17
- static dockPositions = ['top', 'right', 'bottom', 'left']
17
+ static dockPositions = ['top', 'right', 'bottom', 'left', null]
18
18
 
19
19
  static config = {
20
20
  /**
@@ -32,9 +32,9 @@ class Base extends Container {
32
32
  */
33
33
  baseCls: ['neo-toolbar'],
34
34
  /**
35
- * @member {String} dock_='top'
35
+ * @member {String|null} dock_=null
36
36
  */
37
- dock_: 'top',
37
+ dock_: null,
38
38
  /**
39
39
  * @member {Object} itemDefaults={ntype:'button'}
40
40
  */
@@ -42,9 +42,9 @@ class Base extends Container {
42
42
  ntype: 'button'
43
43
  },
44
44
  /**
45
- * @member {Object} _layout={ntype: 'hbox', align: 'center', pack : 'start'}
45
+ * @member {Object} layout={ntype: 'hbox', align: 'center', pack : 'start'}
46
46
  */
47
- _layout: {
47
+ layout: {
48
48
  ntype: 'hbox',
49
49
  align: 'center',
50
50
  pack : 'start'
@@ -89,7 +89,7 @@ class Base extends Container {
89
89
  dockPositions = me.getStaticConfig('dockPositions');
90
90
 
91
91
  dockPositions.forEach(key => {
92
- NeoArray[key === value ? 'add' : 'remove'](cls, 'neo-dock-' + key);
92
+ key !== null && NeoArray[key === value ? 'add' : 'remove'](cls, 'neo-dock-' + key);
93
93
  });
94
94
 
95
95
  me.cls = cls;
@@ -154,36 +154,39 @@ class Base extends Container {
154
154
  * @returns {Object} layoutConfig
155
155
  */
156
156
  getLayoutConfig() {
157
- let layoutConfig;
158
-
159
- switch(this.dock) {
160
- case 'bottom':
161
- case 'top':
162
- layoutConfig = {
163
- ntype: 'hbox',
164
- align: 'center',
165
- pack : 'start'
166
- };
167
- break;
168
- case 'left':
169
- layoutConfig = {
170
- ntype : 'vbox',
171
- align : 'center',
172
- direction: 'column-reverse',
173
- pack : 'start'
174
- };
175
- break;
176
- case 'right':
177
- layoutConfig = {
178
- ntype : 'vbox',
179
- align : 'center',
180
- direction: 'column',
181
- pack : 'start'
182
- };
183
- break;
157
+ let me = this,
158
+ layoutConfig;
159
+
160
+ if (me.dock) {
161
+ switch(me.dock) {
162
+ case 'bottom':
163
+ case 'top':
164
+ layoutConfig = {
165
+ ntype: 'hbox',
166
+ align: 'center',
167
+ pack : 'start'
168
+ };
169
+ break;
170
+ case 'left':
171
+ layoutConfig = {
172
+ ntype : 'vbox',
173
+ align : 'center',
174
+ direction: 'column-reverse',
175
+ pack : 'start'
176
+ };
177
+ break;
178
+ case 'right':
179
+ layoutConfig = {
180
+ ntype : 'vbox',
181
+ align : 'center',
182
+ direction: 'column',
183
+ pack : 'start'
184
+ };
185
+ break;
186
+ }
184
187
  }
185
188
 
186
- return layoutConfig;
189
+ return layoutConfig || me.layout;
187
190
  }
188
191
  }
189
192
 
@@ -1,4 +1,5 @@
1
1
  import ClassSystemUtil from '../util/ClassSystem.mjs';
2
+ import HashHistory from '../util/HashHistory.mjs';
2
3
  import Store from '../data/Store.mjs';
3
4
  import Toolbar from '../toolbar/Base.mjs';
4
5
 
@@ -18,24 +19,58 @@ class Breadcrumb extends Toolbar {
18
19
  * @protected
19
20
  */
20
21
  ntype: 'breadcrumb-toolbar',
21
- /**
22
- * @member {String[]} baseCls=['neo-breadcrumb-toolbar','neo-toolbar']
23
- */
24
- baseCls: ['neo-breadcrumb-toolbar', 'neo-toolbar'],
25
22
  /**
26
23
  * @member {Number|String|null} activeKey_=null
27
24
  */
28
25
  activeKey_: null,
29
26
  /**
30
- * @member {Object[]} items
27
+ * @member {String[]} baseCls=['neo-breadcrumb-toolbar','neo-toolbar']
31
28
  */
32
- items: [],
29
+ baseCls: ['neo-breadcrumb-toolbar', 'neo-toolbar'],
33
30
  /**
34
31
  * @member {Neo.data.Store|Object} store_=null
35
32
  */
36
33
  store_: null
37
34
  }
38
35
 
36
+ /**
37
+ * @member {Object} defaultStoreConfig
38
+ */
39
+ defaultStoreConfig = {
40
+ module: Store,
41
+
42
+ model: {
43
+ fields: [{
44
+ name: 'id',
45
+ type: 'Integer'
46
+ }, {
47
+ name: 'name',
48
+ type: 'String'
49
+ }, {
50
+ name: 'parentId',
51
+ type: 'Integer'
52
+ }, {
53
+ name: 'route',
54
+ type: 'String'
55
+ }]
56
+ }
57
+ }
58
+ /**
59
+ * @member {Boolean} updateOnHashChange=true
60
+ */
61
+ updateOnHashChange = true
62
+
63
+ /**
64
+ * @param {Object} config
65
+ */
66
+ construct(config) {
67
+ super.construct(config);
68
+
69
+ let me = this;
70
+
71
+ me.updateOnHashChange && HashHistory.on('change', me.onHashChange, me);
72
+ }
73
+
39
74
  /**
40
75
  * Triggered after the activeKey config got changed
41
76
  * @param {Number|String|null} value
@@ -43,7 +78,16 @@ class Breadcrumb extends Toolbar {
43
78
  * @protected
44
79
  */
45
80
  afterSetActiveKey(value, oldValue) {
46
- this.store.getCount?.() > 0 && this.updateItems()
81
+ let me = this,
82
+ route;
83
+
84
+ if (value !== null && me.store.getCount?.() > 0) {
85
+ me.updateItems();
86
+
87
+ route = me.store.get(value)?.route;
88
+
89
+ route && Neo.Main.setRoute({value: route})
90
+ }
47
91
  }
48
92
 
49
93
  /**
@@ -53,14 +97,10 @@ class Breadcrumb extends Toolbar {
53
97
  * @protected
54
98
  */
55
99
  afterSetStore(value, oldValue) {
56
- let me = this;
57
-
58
100
  value.on({
59
- load: this.onStoreLoad,
60
- scope: me
61
- });
62
-
63
- value?.getCount() > 0 && me.onStoreLoad(value.items)
101
+ load : this.onStoreLoad,
102
+ scope: this
103
+ })
64
104
  }
65
105
 
66
106
  /**
@@ -72,7 +112,18 @@ class Breadcrumb extends Toolbar {
72
112
  */
73
113
  beforeSetStore(value, oldValue) {
74
114
  oldValue?.destroy();
75
- return ClassSystemUtil.beforeSetInstance(value, Store);
115
+ return ClassSystemUtil.beforeSetInstance(value, null, this.defaultStoreConfig);
116
+ }
117
+
118
+ /**
119
+ *
120
+ */
121
+ destroy(...args) {
122
+ let me = this;
123
+
124
+ me.updateOnHashChange && HashHistory.un('change', me.onHashChange, me);
125
+
126
+ super.destroy(...args);
76
127
  }
77
128
 
78
129
  /**
@@ -95,11 +146,25 @@ class Breadcrumb extends Toolbar {
95
146
  return items;
96
147
  }
97
148
 
149
+ /**
150
+ * @param {Object} value
151
+ * @param {Object} oldValue
152
+ */
153
+ onHashChange(value, oldValue) {
154
+ let hashString = value?.hashString,
155
+ store = this.store,
156
+ activeKey = hashString && store.findFirst({route: hashString})?.[store.keyProperty];
157
+
158
+ if (activeKey !== null) {
159
+ this.activeKey = activeKey;
160
+ }
161
+ }
162
+
98
163
  /**
99
164
  * @param {Object[]} items
100
165
  */
101
166
  onStoreLoad(items) {
102
- this.activeKey !== null && this.updateItems()
167
+ this.afterSetActiveKey(this.activeKey, null);
103
168
  }
104
169
 
105
170
  /**
@@ -114,23 +179,39 @@ class Breadcrumb extends Toolbar {
114
179
  newItems = [],
115
180
  config, item
116
181
 
182
+ me.silentVdomUpdate = true;
183
+
117
184
  for (; i < len; i++) {
118
185
  item = pathItems[i];
119
186
 
120
187
  config = {
188
+ disabled : i === len - 1,
121
189
  editRoute: false,
190
+ hidden : false,
122
191
  route : item.route,
123
192
  text : item.name
124
193
  };
125
194
 
126
195
  if (items[i]) {
127
- items[i].set(config);
196
+ items[i].setSilent(config)
128
197
  } else {
129
- newItems.push(config);
198
+ newItems.push(config)
130
199
  }
131
200
  }
132
201
 
202
+ len = items.length;
203
+
204
+ for (; i < len; i++) {
205
+ items[i].setSilent({
206
+ hidden: true
207
+ })
208
+ }
209
+
133
210
  newItems.length > 0 && me.add(newItems);
211
+
212
+ me.silentVdomUpdate = false;
213
+
214
+ me.update()
134
215
  }
135
216
  }
136
217
 
@@ -61,13 +61,15 @@ class HashHistory extends Base {
61
61
  let me = this,
62
62
  stack = me.stack;
63
63
 
64
- stack.unshift(data);
64
+ if (stack[0]?.hashString !== data.hashString) {
65
+ stack.unshift(data);
65
66
 
66
- if (stack.length > me.maxItems) {
67
- stack.pop();
68
- }
67
+ if (stack.length > me.maxItems) {
68
+ stack.pop();
69
+ }
69
70
 
70
- me.fire('change', data, stack[1] || null);
71
+ me.fire('change', data, stack[1] || null)
72
+ }
71
73
  }
72
74
  }
73
75