neo.mjs 5.11.0 → 5.12.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='5.11.0'
23
+ * @member {String} version='5.12.0'
24
24
  */
25
- version: '5.11.0'
25
+ version: '5.12.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='5.11.0'
23
+ * @member {String} version='5.12.0'
24
24
  */
25
- version: '5.11.0'
25
+ version: '5.12.0'
26
26
  }
27
27
 
28
28
  /**
@@ -174,15 +174,26 @@ class MainContainer extends ConfigurationViewport {
174
174
  return Neo.create({
175
175
  module : Button,
176
176
  badgeText: 'Badge',
177
+ handler : data => console.log('button click =>', data.component.id),
177
178
  height : 50,
178
179
  iconCls : 'fa fa-home',
179
180
  text : 'Hello World',
180
181
  ui : 'primary',
181
182
  width : 150,
182
183
 
183
- handler: (data) => {
184
- console.log('button click =>', data.component.id);
185
- }
184
+ menu: [{
185
+ handler: data => console.log('menu item click =>', data.component.id),
186
+ iconCls: 'fa fa-home',
187
+ text : 'Item 1'
188
+ }, {
189
+ handler: data => console.log('menu item click =>', data.component.id),
190
+ iconCls: 'fa fa-user',
191
+ text : 'Item 2'
192
+ }, {
193
+ handler: data => console.log('menu item click =>', data.component.id),
194
+ iconCls: 'fa fa-play',
195
+ text : 'Item 3'
196
+ }]
186
197
 
187
198
  /*tooltips: [{
188
199
  text: 'Hello World Tooltip'
@@ -15,11 +15,12 @@ class MainContainer extends ConfigurationViewport {
15
15
  }
16
16
 
17
17
  createConfigurationComponents() {
18
- let me = this;
18
+ let me = this,
19
+ exampleComponent = me.exampleComponent;
19
20
 
20
21
  return [{
21
22
  module : Radio,
22
- checked : me.exampleComponent.currentDate.getMonth() === 0,
23
+ checked : exampleComponent.currentDate.getMonth() === 0,
23
24
  hideValueLabel: false,
24
25
  labelText : 'currentDate (month)',
25
26
  listeners : {change: me.onMonthRadioChange.bind(me, 0)},
@@ -27,7 +28,7 @@ class MainContainer extends ConfigurationViewport {
27
28
  valueLabelText: 'Jan'
28
29
  }, {
29
30
  module : Radio,
30
- checked : me.exampleComponent.currentDate.getMonth() === 1,
31
+ checked : exampleComponent.currentDate.getMonth() === 1,
31
32
  hideValueLabel: false,
32
33
  labelText : '',
33
34
  listeners : {change: me.onMonthRadioChange.bind(me, 1)},
@@ -35,7 +36,7 @@ class MainContainer extends ConfigurationViewport {
35
36
  valueLabelText: 'Feb'
36
37
  }, {
37
38
  module : Radio,
38
- checked : me.exampleComponent.currentDate.getMonth() === 2,
39
+ checked : exampleComponent.currentDate.getMonth() === 2,
39
40
  hideValueLabel: false,
40
41
  labelText : '',
41
42
  listeners : {change: me.onMonthRadioChange.bind(me, 2)},
@@ -43,7 +44,7 @@ class MainContainer extends ConfigurationViewport {
43
44
  valueLabelText: 'Mar'
44
45
  }, {
45
46
  module : Radio,
46
- checked : me.exampleComponent.currentDate.getMonth() === 3,
47
+ checked : exampleComponent.currentDate.getMonth() === 3,
47
48
  hideValueLabel: false,
48
49
  labelText : '',
49
50
  listeners : {change: me.onMonthRadioChange.bind(me, 3)},
@@ -51,7 +52,7 @@ class MainContainer extends ConfigurationViewport {
51
52
  valueLabelText: 'Apr'
52
53
  }, {
53
54
  module : Radio,
54
- checked : me.exampleComponent.currentDate.getFullYear() === 2021,
55
+ checked : exampleComponent.currentDate.getFullYear() === 2021,
55
56
  hideValueLabel: false,
56
57
  labelText : 'currentDate (year)',
57
58
  listeners : {change: me.onYearRadioChange.bind(me, 2021)},
@@ -60,7 +61,7 @@ class MainContainer extends ConfigurationViewport {
60
61
  valueLabelText: '2021'
61
62
  }, {
62
63
  module : Radio,
63
- checked : me.exampleComponent.currentDate.getFullYear() === 2020,
64
+ checked : exampleComponent.currentDate.getFullYear() === 2020,
64
65
  hideValueLabel: false,
65
66
  labelText : '',
66
67
  listeners : {change: me.onYearRadioChange.bind(me, 2020)},
@@ -68,7 +69,7 @@ class MainContainer extends ConfigurationViewport {
68
69
  valueLabelText: '2020'
69
70
  }, {
70
71
  module : Radio,
71
- checked : me.exampleComponent.currentDate.getFullYear() === 2019,
72
+ checked : exampleComponent.currentDate.getFullYear() === 2019,
72
73
  hideValueLabel: false,
73
74
  labelText : '',
74
75
  listeners : {change: me.onYearRadioChange.bind(me, 2019)},
@@ -76,7 +77,7 @@ class MainContainer extends ConfigurationViewport {
76
77
  valueLabelText: '2019'
77
78
  }, {
78
79
  module : Radio,
79
- checked : me.exampleComponent.currentDate.getFullYear() === 2018,
80
+ checked : exampleComponent.currentDate.getFullYear() === 2018,
80
81
  hideValueLabel: false,
81
82
  labelText : '',
82
83
  listeners : {change: me.onYearRadioChange.bind(me, 2018)},
@@ -84,7 +85,7 @@ class MainContainer extends ConfigurationViewport {
84
85
  valueLabelText: '2018'
85
86
  }, {
86
87
  module : Radio,
87
- checked : me.exampleComponent.dayNameFormat === 'narrow',
88
+ checked : exampleComponent.dayNameFormat === 'narrow',
88
89
  hideValueLabel: false,
89
90
  labelText : 'dayNameFormat',
90
91
  listeners : {change: me.onRadioChange.bind(me, 'dayNameFormat', 'narrow')},
@@ -93,7 +94,7 @@ class MainContainer extends ConfigurationViewport {
93
94
  valueLabelText: 'narrow'
94
95
  }, {
95
96
  module : Radio,
96
- checked : me.exampleComponent.dayNameFormat === 'short',
97
+ checked : exampleComponent.dayNameFormat === 'short',
97
98
  hideValueLabel: false,
98
99
  labelText : '',
99
100
  listeners : {change: me.onRadioChange.bind(me, 'dayNameFormat', 'short')},
@@ -101,55 +102,55 @@ class MainContainer extends ConfigurationViewport {
101
102
  valueLabelText: 'short'
102
103
  }, {
103
104
  module : Radio,
104
- checked : me.exampleComponent.dayNameFormat === 'long',
105
+ checked : exampleComponent.dayNameFormat === 'long',
105
106
  hideValueLabel: false,
106
107
  labelText : '',
107
108
  listeners : {change: me.onRadioChange.bind(me, 'dayNameFormat', 'long')},
108
109
  name : 'dayNameFormat',
109
110
  valueLabelText: 'long'
111
+ }, {
112
+ module : NumberField,
113
+ clearable : true,
114
+ labelText : 'height',
115
+ listeners : {change: me.onConfigChange.bind(me, 'height')},
116
+ maxValue : 800,
117
+ minValue : 230,
118
+ stepSize : 10,
119
+ style : {marginTop: '10px'},
120
+ value : exampleComponent.height
110
121
  }, {
111
122
  module : DateField,
112
123
  labelText : 'maxValue',
113
124
  listeners : {change: me.onConfigChange.bind(me, 'maxValue')},
114
125
  matchPickerWidth: false,
115
- style : {marginTop: '10px'},
116
- value : me.exampleComponent.maxValue
126
+ value : exampleComponent.maxValue
117
127
  }, {
118
128
  module : DateField,
119
129
  labelText : 'minValue',
120
130
  listeners : {change: me.onConfigChange.bind(me, 'minValue')},
121
131
  matchPickerWidth: false,
122
- value : me.exampleComponent.minValue
123
- }, {
124
- module : NumberField,
125
- clearable : true,
126
- labelText : 'height',
127
- listeners : {change: me.onConfigChange.bind(me, 'height')},
128
- maxValue : 800,
129
- minValue : 230,
130
- stepSize : 10,
131
- value : me.exampleComponent.height
132
+ value : exampleComponent.minValue
132
133
  }, {
133
134
  module : CheckBox,
134
- checked : me.exampleComponent.showCellBorders,
135
+ checked : exampleComponent.showCellBorders,
135
136
  labelText: 'showCellBorders',
136
137
  listeners: {change: me.onConfigChange.bind(me, 'showCellBorders')},
137
138
  style : {marginTop: '10px'}
138
139
  }, {
139
140
  module : CheckBox,
140
- checked : me.exampleComponent.showDisabledDays,
141
+ checked : exampleComponent.showDisabledDays,
141
142
  labelText: 'showDisabledDays',
142
143
  listeners: {change: me.onConfigChange.bind(me, 'showDisabledDays')},
143
144
  style : {marginTop: '10px'}
144
145
  }, {
145
146
  module : CheckBox,
146
- checked : me.exampleComponent.useAnimations,
147
+ checked : exampleComponent.useAnimations,
147
148
  labelText: 'useAnimations',
148
149
  listeners: {change: me.onConfigChange.bind(me, 'useAnimations')},
149
150
  style : {marginTop: '10px'}
150
151
  }, {
151
152
  module : Radio,
152
- checked : me.exampleComponent.weekStartDay === 6,
153
+ checked : exampleComponent.weekStartDay === 6,
153
154
  hideValueLabel: false,
154
155
  labelText : 'weekStartDay',
155
156
  listeners : {change: me.onRadioChange.bind(me, 'weekStartDay', 6)},
@@ -158,7 +159,7 @@ class MainContainer extends ConfigurationViewport {
158
159
  valueLabelText: '6 (Saturday)'
159
160
  }, {
160
161
  module : Radio,
161
- checked : me.exampleComponent.weekStartDay === 0,
162
+ checked : exampleComponent.weekStartDay === 0,
162
163
  hideValueLabel: false,
163
164
  labelText : '',
164
165
  listeners : {change: me.onRadioChange.bind(me, 'weekStartDay', 0)},
@@ -166,7 +167,7 @@ class MainContainer extends ConfigurationViewport {
166
167
  valueLabelText: '0 (Sunday)'
167
168
  }, {
168
169
  module : Radio,
169
- checked : me.exampleComponent.weekStartDay === 1,
170
+ checked : exampleComponent.weekStartDay === 1,
170
171
  hideValueLabel: false,
171
172
  labelText : '',
172
173
  listeners : {change: me.onRadioChange.bind(me, 'weekStartDay', 1)},
@@ -181,7 +182,7 @@ class MainContainer extends ConfigurationViewport {
181
182
  minValue : 240,
182
183
  stepSize : 10,
183
184
  style : {marginTop: '10px'},
184
- value : me.exampleComponent.width
185
+ value : exampleComponent.width
185
186
  }];
186
187
  }
187
188
 
@@ -0,0 +1,46 @@
1
+ import Viewport from '../../../src/container/Viewport.mjs';
2
+ import Video from '../../../src/component/Video.mjs';
3
+ import Panel from '../../../src/container/Panel.mjs';
4
+ import MainContainerController from "./MainContainerController.mjs";
5
+
6
+ /**
7
+ * @class Neo.examples.component.timer.MainContainer
8
+ * @extends Neo.examples.Viewport
9
+ */
10
+ class MainContainer extends Viewport {
11
+ static config = {
12
+ className: 'Neo.examples.component.timer.MainContainer',
13
+
14
+ controller: MainContainerController,
15
+
16
+ items : [{
17
+ ntype: 'panel',
18
+ headers: [{
19
+ dock: 'top',
20
+ items: [{
21
+ ntype: 'component',
22
+ html : '<h1>Video Demo</h1>',
23
+ flex : 'none',
24
+ style: {textAlign: 'center'}
25
+ }, {
26
+ ntype: 'component', flex: 1
27
+ }, {
28
+ reference: 'theme-button',
29
+ iconCls : 'fa fa-sun',
30
+ handler : 'onToggleTheme',
31
+ style : {height: '100%',padding: '0 40px',borderWidth: 0,borderLeftWidth: '1px',borderRadius: 0}
32
+ }]
33
+ }],
34
+ items: [{
35
+ module : Video,
36
+ url : 'https://video-ssl.itunes.apple.com/itunes-assets/Video125/v4/a0/57/54/a0575426-dd8e-2d25-bdf3-139702870b50/mzvf_786190431362224858.640x464.h264lc.U.p.m4v',
37
+ flag : 'video-component',
38
+ minHeight: 400,
39
+ }]
40
+ }]
41
+ }
42
+ }
43
+
44
+ Neo.applyClassConfig(MainContainer);
45
+
46
+ export default MainContainer;
@@ -0,0 +1,37 @@
1
+ import ComponentController from '../../../src/controller/Component.mjs';
2
+ import Toast from '../../../src/component/Toast.mjs';
3
+
4
+ /**
5
+ * @class Neo.examples.component.toast.MainContainerController
6
+ * @extends Neo.controller.Component
7
+ */
8
+ class MainContainerController extends ComponentController {
9
+ static config = {
10
+ /**
11
+ * @member {String} className='Neo.examples.component.toast.MainContainerController'
12
+ * @protected
13
+ */
14
+ className: 'Neo.examples.component.toast.MainContainerController'
15
+ }
16
+
17
+ theme = 'light';
18
+
19
+ /**
20
+ * @param {Object} config
21
+ */
22
+ onToggleTheme(config) {
23
+ const add = (this.theme === 'light') ? 'dark' : 'light',
24
+ remove = add === 'light' ? 'dark' : 'light',
25
+ themeButton = this.getReference('theme-button'),
26
+ buttonIcon = add === 'light' ? 'sun' : 'moon';
27
+
28
+ this.theme = add;
29
+
30
+ Neo.main.DomAccess.setBodyCls({remove: ['neo-theme-' + remove] , add: ['neo-theme-' + add]});
31
+ themeButton.iconCls = 'fa fa-' + buttonIcon;
32
+ }
33
+ }
34
+
35
+ Neo.applyClassConfig(MainContainerController);
36
+
37
+ export default MainContainerController;
@@ -0,0 +1,7 @@
1
+ import MainContainer from './MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'Neo.examples.component.video'
6
+ });
7
+
@@ -0,0 +1,12 @@
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 Video</title>
7
+ </head>
8
+ </head>
9
+ <body>
10
+ <script src="../../../src/MicroLoader.mjs" type="module"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,7 @@
1
+ {
2
+ "appPath" : "examples/component/video/app.mjs",
3
+ "basePath" : "../../../",
4
+ "environment" : "development",
5
+ "mainPath" : "./Main.mjs",
6
+ "mainThreadAddons": ["Stylesheet", "ScrollSync"]
7
+ }
@@ -12,6 +12,9 @@ class MainModel extends Model {
12
12
  fields: [{
13
13
  name: 'country',
14
14
  type: 'String'
15
+ }, {
16
+ name: 'disabled',
17
+ type: 'Boolean'
15
18
  }, {
16
19
  name: 'firstname',
17
20
  type: 'String'
@@ -13,31 +13,37 @@ class MainStore extends Store {
13
13
 
14
14
  data: [{
15
15
  country : 'Germany',
16
+ disabled : false,
16
17
  firstname: 'Tobias',
17
18
  githubId : 'tobiu',
18
19
  lastname : 'Uhlig'
19
20
  }, {
20
21
  country : 'USA',
22
+ disabled : true,
21
23
  firstname: 'Rich',
22
24
  githubId : 'rwaters',
23
25
  lastname : 'Waters'
24
26
  }, {
25
27
  country : 'Germany',
28
+ disabled : false,
26
29
  firstname: 'Nils',
27
30
  githubId : 'mrsunshine',
28
31
  lastname : 'Dehl'
29
32
  }, {
30
33
  country : 'USA',
34
+ disabled : false,
31
35
  firstname: 'Gerard',
32
36
  githubId : 'camtnbikerrwc',
33
37
  lastname : 'Horan'
34
38
  }, {
35
39
  country : 'Slovakia',
40
+ disabled : false,
36
41
  firstname: 'Jozef',
37
42
  githubId : 'jsakalos',
38
43
  lastname : 'Sakalos'
39
44
  }, {
40
45
  country : 'Germany',
46
+ disabled : false,
41
47
  firstname: 'Bastian',
42
48
  githubId : 'bhaustein',
43
49
  lastname : 'Haustein'
@@ -1,3 +1,4 @@
1
+ import Button from '../../../src/button/Base.mjs';
1
2
  import CellColumnModel from '../../../src/selection/table/CellColumnModel.mjs';
2
3
  import CellColumnRowModel from '../../../src/selection/table/CellColumnRowModel.mjs';
3
4
  import CellModel from '../../../src/selection/table/CellModel.mjs';
@@ -84,6 +85,9 @@ class MainContainer extends ConfigurationViewport {
84
85
  }];
85
86
  }
86
87
 
88
+ /**
89
+ * @returns {Neo.table.Container}
90
+ */
87
91
  createExampleComponent() {
88
92
  return Neo.create(TableContainer, {
89
93
  autoRender : false,
@@ -95,10 +99,30 @@ class MainContainer extends ConfigurationViewport {
95
99
  {dataField: 'firstname', text: 'Firstname'},
96
100
  {dataField: 'lastname', text: 'Lastname'},
97
101
  {dataField: 'githubId', text: 'Github Id'},
98
- {dataField: 'country', text: 'Country'}
102
+ {dataField: 'country', text: 'Country'},
103
+ {
104
+ renderer: data => {
105
+ let button = Neo.create({
106
+ module : Button,
107
+ appName : this.appName,
108
+ handler : this.editButtonHandler,
109
+ parentId: 'myTableStoreContainer',
110
+ text : 'Edit'
111
+ });
112
+
113
+ return button.vdom
114
+ }
115
+ }
99
116
  ]
100
117
  });
101
118
  }
119
+
120
+ /**
121
+ * @param {Object} data
122
+ */
123
+ editButtonHandler(data) {
124
+ console.log(data)
125
+ }
102
126
  }
103
127
 
104
128
  Neo.applyClassConfig(MainContainer);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.11.0",
3
+ "version": "5.12.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -52,12 +52,12 @@
52
52
  "envinfo": "^7.10.0",
53
53
  "fs-extra": "^11.1.1",
54
54
  "highlightjs-line-numbers.js": "^2.8.0",
55
- "inquirer": "^9.2.7",
55
+ "inquirer": "^9.2.8",
56
56
  "neo-jsdoc": "^1.0.1",
57
57
  "neo-jsdoc-x": "^1.0.5",
58
- "postcss": "^8.4.25",
59
- "sass": "^1.63.6",
60
- "webpack": "^5.88.1",
58
+ "postcss": "^8.4.27",
59
+ "sass": "^1.64.1",
60
+ "webpack": "^5.88.2",
61
61
  "webpack-cli": "^5.1.4",
62
62
  "webpack-dev-server": "4.15.1",
63
63
  "webpack-hook-plugin": "^1.0.7",
@@ -0,0 +1,31 @@
1
+ .neo-video {
2
+ position: relative;
3
+
4
+ & > * {
5
+ position: absolute;
6
+ inset: 0;
7
+ }
8
+
9
+ .neo-video-ghost {
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+
14
+ height: 100%;
15
+ width: 100%;
16
+ background-color: v(video-ghost-color);
17
+
18
+ .fa-solid {
19
+ height: v(video-ghost-size);
20
+ width: v(video-ghost-size);
21
+ font-size: v(video-ghost-size);
22
+ cursor: pointer;
23
+ }
24
+ }
25
+
26
+ .neo-video-media {
27
+ height: 100%;
28
+ width: 100%;
29
+ background: black;
30
+ }
31
+ }
@@ -0,0 +1,11 @@
1
+ $neoMap: map-merge($neoMap, (
2
+ 'video-ghost-color': rgb(50, 50, 50),
3
+ 'video-ghost-size' : 30px
4
+ ));
5
+
6
+ @if $useCssVars == true {
7
+ :root .neo-theme-dark { // .neo-video
8
+ --video-ghost-color: #{neo(video-ghost-color)};
9
+ --video-ghost-size: #{neo(video-ghost-size)};
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ $neoMap: map-merge($neoMap, (
2
+ 'video-ghost-color': rgba(50, 50, 50, 0.3),
3
+ 'video-ghost-size' : 30px
4
+ ));
5
+
6
+ @if $useCssVars == true {
7
+ :root .neo-theme-light { // .neo-video
8
+ --video-ghost-color: #{neo(video-ghost-color)};
9
+ --video-ghost-size: #{neo(video-ghost-size)};
10
+ }
11
+ }
@@ -236,12 +236,12 @@ const DefaultConfig = {
236
236
  useVdomWorker: true,
237
237
  /**
238
238
  * buildScripts/injectPackageVersion.mjs will update this value
239
- * @default '5.11.0'
239
+ * @default '5.12.0'
240
240
  * @memberOf! module:Neo
241
241
  * @name config.version
242
242
  * @type String
243
243
  */
244
- version: '5.11.0'
244
+ version: '5.12.0'
245
245
  };
246
246
 
247
247
  Object.assign(DefaultConfig, {
package/src/Main.mjs CHANGED
@@ -48,6 +48,7 @@ class Main extends core.Base {
48
48
  app: [
49
49
  'alert',
50
50
  'editRoute',
51
+ 'getByPath',
51
52
  'getWindowData',
52
53
  'redirectTo',
53
54
  'setNeoConfig',
@@ -150,6 +151,18 @@ class Main extends core.Base {
150
151
  window.location.hash = hashArr.join('&');
151
152
  }
152
153
 
154
+ /**
155
+ * Request specific accessible window attributes by path into the app worker.
156
+ * Keep in mind that this excludes anything DOM related or instances.
157
+ * Example: Neo.Main.getByPath({path: 'navigator.language'}).then(data => {})
158
+ * @param {Object} data
159
+ * @param {String} data.path
160
+ * @returns {*}
161
+ */
162
+ getByPath(data) {
163
+ return Neo.nsWithArrays(data.path)
164
+ }
165
+
153
166
  /**
154
167
  * window.screen is not spreadable
155
168
  * @returns {Object}
@@ -385,7 +398,7 @@ class Main extends core.Base {
385
398
  }
386
399
 
387
400
  /**
388
- * Change the location.hash value
401
+ * Change a Neo.config from the app worker
389
402
  * @param {Object} data
390
403
  * @param {String} data.key
391
404
  * @param {*} data.value
@@ -437,7 +450,7 @@ class Main extends core.Base {
437
450
  * @param {String} data.windowFeatures
438
451
  * @param {String} data.windowName
439
452
  * @return {Boolean}
440
- */
453
+ */
441
454
  windowOpen(data) {
442
455
  let openedWindow = window.open(data.url, data.windowName, data.windowFeatures),
443
456
  success = !!openedWindow;
@@ -78,6 +78,10 @@ class Base extends Component {
78
78
  * @member {String} iconPosition_='left'
79
79
  */
80
80
  iconPosition_: 'left',
81
+ /**
82
+ * @member {Object[]|null} menu_=null
83
+ */
84
+ menu_: null,
81
85
  /**
82
86
  * The pressed state of the Button
83
87
  * @member {Boolean} pressed_=false
@@ -236,6 +240,29 @@ class Base extends Component {
236
240
  this.cls = cls;
237
241
  }
238
242
 
243
+ /**
244
+ * Triggered after the menu config got changed
245
+ * @param {Object[]|null} value
246
+ * @param {Object[]|null} oldValue
247
+ * @protected
248
+ */
249
+ afterSetMenu(value, oldValue) {
250
+ if (value) {
251
+ import('../menu/List.mjs').then(module => {
252
+ let list = Neo.create({
253
+ module : module.default,
254
+ appName : this.appName,
255
+ floating: true,
256
+ items : value
257
+ });
258
+
259
+ list.render(true);
260
+
261
+ console.log('afterSetMenu', list);
262
+ })
263
+ }
264
+ }
265
+
239
266
  /**
240
267
  * Triggered after the pressed config got changed
241
268
  * @param {Boolean} value
@@ -0,0 +1,160 @@
1
+ import BaseComponent from '../component/Base.mjs';
2
+ import VDomUtil from '../util/VDom.mjs';
3
+
4
+ /**
5
+ * @class Neo.component.Video
6
+ * @extends Neo.component.Base
7
+ *
8
+ * @example
9
+ * ntype: 'video',
10
+ * url: 'https://video-ssl.itunes.apple.com/itunes-assets/Video125/v4/a0/57/54/a0575426-dd8e-2d25-bdf3-139702870b50/mzvf_786190431362224858.640x464.h264lc.U.p.m4v'
11
+ */
12
+ class Video extends BaseComponent {
13
+ static config = {
14
+ /*
15
+ * @member {String} className='Neo.component.Video'
16
+ * @protected
17
+ */
18
+ className: 'Neo.component.Video',
19
+ /*
20
+ * @member {String} ntype='video'
21
+ * @protected
22
+ */
23
+ ntype: 'video',
24
+ /*
25
+ * @member {String[]} baseCls=['neo-video']
26
+ */
27
+ baseCls: ['neo-video'],
28
+ /**
29
+ * Current state of the video
30
+ * @member {Boolean} playing=false
31
+ */
32
+ playing_: false,
33
+ /**
34
+ * Type of the video
35
+ * @member {Boolean} type='video/mp4'
36
+ */
37
+ type: 'video/mp4',
38
+ /*
39
+ * @member {String} url=null
40
+ */
41
+ url_: null,
42
+ /**
43
+ * @member {Object} _vdom
44
+ */
45
+ _vdom: {
46
+ cn: [{
47
+ flag: 'ghost',
48
+ cls : ['neo-video-ghost'],
49
+ cn : [{
50
+ // The <i> tag defines a part of text in an alternate voice or mood. The content inside is typically displayed in italic.
51
+ tag: 'i',
52
+ cls: ['fa-solid', 'fa-circle-play']
53
+ }]
54
+ }, {
55
+ // Neo specific configs
56
+ tag : 'video',
57
+ flag : 'media',
58
+ removeDom: true,
59
+ cls : ['neo-video-media'],
60
+ // dom attributes
61
+ autoplay: true,
62
+ controls: true
63
+ }]
64
+ }
65
+ }
66
+
67
+ /**
68
+ * @param {Object} config
69
+ */
70
+ construct(config) {
71
+ super.construct(config);
72
+
73
+ let me = this,
74
+ domListeners = me.domListeners;
75
+
76
+ domListeners.push({
77
+ click : me.play,
78
+ delegate: '.neo-video-ghost'
79
+ }, {
80
+ click : me.pause,
81
+ delegate: '.neo-video-media'
82
+ });
83
+
84
+ me.domListeners = domListeners;
85
+ }
86
+
87
+ /**
88
+ * beforeSetPlaying autgen by playing_
89
+ *
90
+ * @param {Boolean} value
91
+ * @param {Boolean} oldValue
92
+ * @returns {Boolean}
93
+ */
94
+ beforeSetPlaying(value, oldValue) {
95
+ if (!Neo.isBoolean(value)) {
96
+ return oldValue;
97
+ }
98
+
99
+ return value;
100
+ }
101
+
102
+ /**
103
+ * afterSetPlaying - run the event listeners
104
+ * @param {Boolean} value
105
+ * @param {Boolean} oldValue
106
+ */
107
+ afterSetPlaying(value, oldValue) {
108
+ let vdom = this.vdom,
109
+ media = VDomUtil.getFlags(vdom, 'media')[0],
110
+ ghost = VDomUtil.getFlags(vdom, 'ghost')[0];
111
+
112
+ ghost.removeDom = value;
113
+ media.removeDom = !value;
114
+
115
+ this.vdom = vdom;
116
+ }
117
+
118
+ /**
119
+ * afterSetUrl
120
+ * Add a source element into the video element containing the url
121
+ *
122
+ * @param {String} value
123
+ * @param {String|null} oldValue
124
+ */
125
+ afterSetUrl(value, oldValue) {
126
+ if (!value) {
127
+ return;
128
+ }
129
+
130
+ const me = this;
131
+ let vdom = me.vdom,
132
+ media = vdom.cn[1];
133
+
134
+ media.cn = [{
135
+ tag : 'source',
136
+ src : value,
137
+ type: me.type
138
+ }];
139
+
140
+ me.vdom = vdom;
141
+ }
142
+
143
+ /**
144
+ * Clicked ghost
145
+ */
146
+ play() {
147
+ this.playing = true;
148
+ }
149
+
150
+ /**
151
+ * Clicked media
152
+ */
153
+ pause() {
154
+ this.playing = false;
155
+ }
156
+ }
157
+
158
+ Neo.applyClassConfig(Video);
159
+
160
+ export default Video;
@@ -86,12 +86,12 @@ class Fetch extends Base {
86
86
 
87
87
  /**
88
88
  * @param {Object|String} url
89
- * @param {Object} config
89
+ * @param {Object} config={}
90
90
  * @param {String} method
91
91
  * @param {Object} [data]
92
92
  * @returns {Promise<any>}
93
93
  */
94
- request(url, config, method, data) {
94
+ request(url, config={}, method, data) {
95
95
  if (!Neo.isString(url)) {
96
96
  config = url;
97
97
  url = config.url;
@@ -320,14 +320,7 @@ class Picker extends Text {
320
320
  * @param {Object} data
321
321
  */
322
322
  onInputClick(data) {
323
- let me = this;
324
-
325
- if (!me.editable) {
326
- me.togglePicker();
327
-
328
- // stay in sync to the trigger-click logic
329
- !me.pickerIsMounted && me.focus()
330
- }
323
+ !this.editable && this.togglePicker()
331
324
  }
332
325
 
333
326
  /**
@@ -981,10 +981,10 @@ class Text extends Base {
981
981
 
982
982
  /**
983
983
  * Calls focus() on the inputEl node instead
984
- * @param {String} id=this.id
984
+ * @param {String} id
985
985
  * @override
986
986
  */
987
- focus(id=this.id) {
987
+ focus(id) {
988
988
  super.focus(this.getInputElId())
989
989
  }
990
990
 
package/src/list/Base.mjs CHANGED
@@ -37,6 +37,12 @@ class Base extends Component {
37
37
  * @member {String[]} baseCls=['neo-list']
38
38
  */
39
39
  baseCls: ['neo-list'],
40
+ /**
41
+ * An optional record field to make items non-clickable and visually greyed out.
42
+ * The field expects the Boolean type.
43
+ * @member {String} disabledField='disabled'
44
+ */
45
+ disabledField: 'disabled',
40
46
  /**
41
47
  * @member {Boolean} disableSelection_=false
42
48
  */
@@ -373,10 +379,14 @@ class Base extends Component {
373
379
 
374
380
  if (!me.disableSelection && selectionModel) {
375
381
  if (selectionModel.isSelected(itemId)) {
376
- cls.push(selectionModel.selectedCls);
382
+ cls.push(selectionModel.selectedCls)
377
383
  }
378
384
  }
379
385
 
386
+ if (record[me.disabledField]) {
387
+ cls.push('neo-disabled')
388
+ }
389
+
380
390
  item = {
381
391
  tag : isHeader ? 'dt' : me.itemTagName,
382
392
  cls,
@@ -105,7 +105,9 @@ class DomEvent extends Base {
105
105
  }
106
106
 
107
107
  if (!preventFire) {
108
- // console.log(Neo.get(id));
108
+ // multiple listeners would change the reference of data.component
109
+ data = Neo.clone(data, true);
110
+
109
111
  data.component = component;
110
112
  listener.fn.apply(listener.scope || globalThis, [data]);
111
113
 
@@ -83,9 +83,11 @@ class ListModel extends Model {
83
83
  if (item) {
84
84
  recordId = view.getItemRecordId(item);
85
85
  index = store.indexOf(recordId) + step;
86
+ record = store.getAt(index);
86
87
 
87
- while (store.getAt(index)?.isHeader === true) {
88
+ while (record?.[view.disabledField] === true || record?.isHeader === true) {
88
89
  index += step;
90
+ record = store.getAt(index)
89
91
  }
90
92
 
91
93
  if (index < 0) {
@@ -59,12 +59,10 @@ class View extends Component {
59
59
  i = 0,
60
60
  vdom = me.vdom,
61
61
  cellCls, cellId, config, column, dockLeftMargin, dockRightMargin, id, index, j, rendererOutput,
62
- record, rendererValue, selectedRows, trCls;
62
+ record, rendererType, rendererValue, selectedRows, trCls;
63
63
 
64
64
  me.recordVnodeMap = {}; // remove old data
65
65
 
66
- // console.log('createViewData', me.id, inputData);
67
-
68
66
  if (container.selectionModel.ntype === 'selection-table-rowmodel') {
69
67
  selectedRows = container.selectionModel.items || [];
70
68
  }
@@ -113,17 +111,29 @@ class View extends Component {
113
111
  value : rendererValue
114
112
  });
115
113
 
116
- cellCls = rendererOutput?.cls || ['neo-table-cell'];
117
-
118
- if (column.align !== 'left') {
119
- cellCls.push('neo-' + column.align);
114
+ cellCls = ['neo-table-cell'];
115
+ rendererType = Neo.typeOf(rendererOutput);
116
+
117
+ switch (rendererType) {
118
+ case 'Object': {
119
+ if (rendererOutput.cls && rendererOutput.html) {
120
+ cellCls.push(...rendererOutput.cls);
121
+ } else {
122
+ rendererOutput = [rendererOutput];
123
+ }
124
+ break;
125
+ }
126
+ case 'String': {
127
+ rendererOutput = {
128
+ cls : cellCls,
129
+ html: rendererOutput?.toString()
130
+ };
131
+ break;
132
+ }
120
133
  }
121
134
 
122
- if (!Neo.isObject(rendererOutput)) {
123
- rendererOutput = {
124
- cls : cellCls,
125
- html: rendererOutput?.toString()
126
- };
135
+ if (column.align !== 'left') {
136
+ cellCls.push('neo-' + column.align)
127
137
  }
128
138
 
129
139
  // todo: remove the else part as soon as all tables use stores (examples table)
@@ -134,14 +144,19 @@ class View extends Component {
134
144
  }
135
145
 
136
146
  config = {
137
- tag : 'td',
138
- id : cellId,
139
- cls : cellCls,
140
- innerHTML: rendererOutput.html || '',
141
- style : rendererOutput.style || {},
142
- tabIndex : '-1'
147
+ tag : 'td',
148
+ id : cellId,
149
+ cls : cellCls,
150
+ style : rendererOutput.style || {},
151
+ tabIndex: '-1'
143
152
  };
144
153
 
154
+ if (rendererType === 'String') {
155
+ config.innerHTML = rendererOutput.html || ''
156
+ } else {
157
+ config.cn = rendererOutput
158
+ }
159
+
145
160
  if (column.dock) {
146
161
  config.cls = ['neo-locked', ...config.cls || []];
147
162