neo.mjs 5.10.13 → 5.11.1

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 (40) hide show
  1. package/apps/ServiceWorker.mjs +2 -2
  2. package/examples/ServiceWorker.mjs +2 -2
  3. package/examples/component/dateSelector/MainContainer.mjs +36 -22
  4. package/examples/component/timer/MainContainer.mjs +71 -0
  5. package/examples/component/timer/MainContainerController.mjs +95 -0
  6. package/examples/component/timer/app.mjs +7 -0
  7. package/examples/component/timer/index.html +12 -0
  8. package/examples/component/timer/neo-config.json +7 -0
  9. package/examples/component/video/MainContainer.mjs +46 -0
  10. package/examples/component/video/MainContainerController.mjs +37 -0
  11. package/examples/component/video/app.mjs +7 -0
  12. package/examples/component/video/index.html +12 -0
  13. package/examples/component/video/neo-config.json +7 -0
  14. package/examples/layout/form/MainContainer.mjs +131 -0
  15. package/examples/layout/form/app.mjs +6 -0
  16. package/examples/layout/form/index.html +11 -0
  17. package/examples/layout/form/neo-config.json +6 -0
  18. package/examples/list/base/MainModel.mjs +3 -0
  19. package/examples/list/base/MainStore.mjs +6 -0
  20. package/examples/table/container/MainContainer.mjs +6 -0
  21. package/package.json +4 -4
  22. package/resources/scss/src/component/Timer.scss +115 -0
  23. package/resources/scss/src/component/Video.scss +31 -0
  24. package/resources/scss/src/layout/Form.scss +27 -0
  25. package/resources/scss/theme-dark/component/Timer.scss +14 -0
  26. package/resources/scss/theme-dark/component/Video.scss +11 -0
  27. package/resources/scss/theme-light/component/Timer.scss +15 -0
  28. package/resources/scss/theme-light/component/Video.scss +11 -0
  29. package/src/DefaultConfig.mjs +2 -2
  30. package/src/component/DateSelector.mjs +44 -10
  31. package/src/component/Timer.mjs +328 -0
  32. package/src/component/Video.mjs +165 -0
  33. package/src/form/field/Date.mjs +42 -0
  34. package/src/layout/Flexbox.mjs +21 -0
  35. package/src/layout/Form.mjs +140 -0
  36. package/src/list/Base.mjs +11 -1
  37. package/src/selection/ListModel.mjs +3 -1
  38. package/src/table/Container.mjs +17 -0
  39. package/src/table/header/Button.mjs +36 -9
  40. package/src/table/header/Toolbar.mjs +26 -1
@@ -0,0 +1,328 @@
1
+ import Component from './Base.mjs';
2
+ import NeoArray from '../util/Array.mjs';
3
+
4
+ /**
5
+ * @class Neo.component.Timer
6
+ * @extends Neo.component.Base
7
+ */
8
+ class Timer extends Component {
9
+ static config = {
10
+ /**
11
+ * @member {String} className='Neo.component.Timer'
12
+ * @protected
13
+ */
14
+ className: 'Neo.component.Timer',
15
+ /**
16
+ * @member {String} ntype='timer'
17
+ * @protected
18
+ */
19
+ ntype: 'timer',
20
+ /**
21
+ * CSS selectors to apply to the root level node of this component
22
+ * @member {String[]} baseCls=['timer']
23
+ */
24
+ baseCls: ['neo-timer'],
25
+ /**
26
+ * End color of the circle. If not set, it uses the css default
27
+ * @member {Number|String} colorEnd_=null
28
+ */
29
+ colorEnd_: null,
30
+ /**
31
+ * Start color of the circle. If not set, it uses the css default
32
+ * @member {Number|String} colorStart_=null
33
+ */
34
+ colorStart_: null,
35
+ /**
36
+ * Start time. This might be '5m', '30s' or milliseconds as number
37
+ * @member {Number|String} duration_='5m'
38
+ */
39
+ duration_: '10m',
40
+ /**
41
+ * Defines height and min-width. This can be a number in px or a string.
42
+ * @member {Number|String} dimensions_='6rem'
43
+ */
44
+ dimensions_: '8em',
45
+ /**
46
+ * Helper to keep running smooth at minimum cost
47
+ * @member {Object} timer={}
48
+ * @member {Number|null} timer.currentSecond =null // run only once per second
49
+ * @member {Number|null} timer.intervalId =null // setInterval id
50
+ * @member {Boolean} timer.running =false// keeps track if timer/entry is up
51
+ * @member {Number|null} timer.startTime =null // calc the current progress
52
+ */
53
+ timer: {
54
+ currentSecond: null,
55
+ intervalId : null,
56
+ running : false,
57
+ startTime : null
58
+ },
59
+ /**
60
+ * The vdom markup for this component.
61
+ * @member {Object} vdom={}
62
+ */
63
+ vdom: {
64
+ cn: [
65
+ {cls: 'countdown', cn : [
66
+ {tag: 'svg', cls: 'clock', viewBox: "-50 -50 100 100", strokeWidth: "10", cn: [
67
+ {tag: 'circle', r: 45},
68
+ {tag: 'circle', r: 45,pathLength: 1}
69
+ ]},
70
+ {cls: ['flip-card'], cn : [
71
+ {cls: 'flip-card-inner enter-mask', cn : [
72
+ {cls: 'flip-card-front', cn : [
73
+ {tag: 'input', cls: 'enter-time'},
74
+ {tag: 'button',cls: 'fa fa-play'}
75
+ ]},
76
+ {cls: 'flip-card-back', cn : [
77
+ {cls : 'runner', html: '00:00'}
78
+ ]}
79
+ ]}
80
+ ]}
81
+ ]}
82
+ ]
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @param {Object} config
88
+ */
89
+ construct(config) {
90
+ super.construct(config);
91
+
92
+ let me = this;
93
+
94
+ me.addDomListeners([
95
+ {click: me.onTimerClick, delegate: 'flip-card-back'},
96
+ {click: me.onTimerClick, delegate: 'fa fa-play'},
97
+ {input: me.onTimerInput, delegate: 'enter-time'},
98
+ {focusout: me.onTimerInput, delegate: 'enter-time'},
99
+ {keydown: me.onFieldKeyDown, delegate: 'enter-time'}
100
+ ]);
101
+ }
102
+
103
+ /**
104
+ * Triggered after the dimensions config got changed
105
+ * @param {Number|String} value
106
+ * @param {Number|String} oldValue
107
+ * @protected
108
+ */
109
+ afterSetDimensions(value, oldValue) {
110
+ if (typeof value === 'number') {
111
+ value = value + 'px';
112
+ }
113
+ this.updateProperties({dimensions: value});
114
+ }
115
+
116
+ /**
117
+ * Triggered after the colorStart config got changed
118
+ * @param {String|null} value
119
+ * @param {String|null} oldValue
120
+ * @protected
121
+ */
122
+ afterSetColorStart(value, oldValue) {
123
+ if (!value) return;
124
+ this.updateProperties({colorStart: value});
125
+ }
126
+
127
+ /**
128
+ * Triggered after the colorEnd config got changed
129
+ * @param {String|null} value
130
+ * @param {String|null} oldValue
131
+ * @protected
132
+ */
133
+ afterSetColorEnd(value, oldValue) {
134
+ if (!value) return;
135
+ this.updateProperties({colorEnd: value});
136
+ }
137
+
138
+ /**
139
+ * Triggered before the duration config got changed
140
+ * @param {Number|String} value
141
+ * @param {Number|String} oldValue
142
+ * @protected
143
+ */
144
+ beforeSetDuration(value, oldValue) {
145
+ const me = this;
146
+
147
+ me.updateInputField(value)
148
+
149
+ if (typeof value === 'string') {
150
+ const durationType = value.at(-1);
151
+
152
+ if (durationType === 'm') {
153
+ value = value.split('m')[0] * 60 * 1000;
154
+ } else if (durationType === 's') {
155
+ value = value.split('s')[0] * 1000;
156
+ }
157
+ }
158
+
159
+ me.updateProperties({full: value});
160
+
161
+ return value;
162
+ }
163
+
164
+ /**
165
+ * Check if Enter was pressed
166
+ * @param {Object} data
167
+ */
168
+ onFieldKeyDown(data) {
169
+ const me = this;
170
+
171
+ if (data.key === 'Enter') {
172
+ me.duration = me.timer.entry;
173
+ me.onTimerClick();
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Click on Play or Timer
179
+ */
180
+ onTimerClick() {
181
+ const me = this;
182
+
183
+ // If the timer is running, stop and clear it
184
+ if (me.timer.intervalId) {
185
+ me.toggleTimer(false);
186
+ me.resetTimer();
187
+ } else {
188
+ // prepare
189
+ me.timer.startTime = new Date().getTime();
190
+
191
+ me.timer.intervalId = setInterval(function () {
192
+ const startTime = me.timer.startTime,
193
+ curTime = new Date().getTime(),
194
+ totalTime = me.duration,
195
+ endTime = startTime + totalTime;
196
+
197
+ if (curTime > endTime) {
198
+ me.toggleTimer(false);
199
+ me.resetTimer();
200
+ } else {
201
+ const milliseconds = endTime - curTime,
202
+ secondsLeft = Math.floor(milliseconds / 1000);
203
+ let secondsNow = secondsLeft % 60,
204
+ minutesNow = Math.floor(secondsLeft / 60)
205
+
206
+ // Ensure this does not run 10 times a second
207
+ if (secondsNow !== me.timer.currentSecond) {
208
+ me.timer.currentSecond = secondsNow;
209
+
210
+ secondsNow = secondsNow.toString().padStart(2, '0');
211
+ minutesNow = minutesNow.toString().padStart(2, '0');
212
+
213
+ me.updateTimer(`${minutesNow}:${secondsNow}`);
214
+ me.updateProperties({current: milliseconds});
215
+ me.toggleTimer(true);
216
+ }
217
+ }
218
+ }, 100);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * On change event of the textfield
224
+ * @param {Object} data
225
+ */
226
+ onTimerInput(data) {
227
+ const me = this;
228
+
229
+ if (data.value) {
230
+ me.timer.entry = data.value
231
+ } else {
232
+ me.duration = me.timer.entry;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Reset the properties, timer and remove Interval
238
+ */
239
+ resetTimer() {
240
+ const me = this;
241
+
242
+ me.updateProperties({current: ''});
243
+ me.updateTimer('00:00');
244
+
245
+ clearInterval(me.timer.intervalId);
246
+ delete me.timer.intervalId;
247
+ }
248
+
249
+ /**
250
+ * Flip over the timer face
251
+ * @param {Boolean} doShow
252
+ */
253
+ toggleTimer(doShow) {
254
+ if(this.running === doShow) return;
255
+
256
+ let me = this,
257
+ turnFn = doShow ? 'add' : 'remove',
258
+ vdom = me.vdom,
259
+ flipCard = vdom.cn[0].cn[1];
260
+
261
+ me.running = doShow;
262
+
263
+ flipCard.cls = flipCard.cls || [];
264
+
265
+ NeoArray[turnFn](flipCard.cls, 'turn');
266
+ me.vdom = vdom;
267
+ }
268
+
269
+ /**
270
+ * Write to the input field
271
+ * @param {String} value
272
+ */
273
+ updateInputField(value) {
274
+ let me = this,
275
+ vdom = me.vdom,
276
+ inputField = vdom.cn[0].cn[1].cn[0].cn[0].cn[0];
277
+
278
+ inputField.value = value;
279
+ }
280
+
281
+ /**
282
+ * Update the timer, typically once per second
283
+ * @param {String} value
284
+ */
285
+ updateTimer(value) {
286
+ let me = this,
287
+ vdom = me.vdom,
288
+ timer = vdom.cn[0].cn[1].cn[0].cn[1].cn[0];
289
+
290
+ timer.innerHTML = value;
291
+ me.vdom = vdom;
292
+ }
293
+
294
+ /**
295
+ * Update the css properties
296
+ * - current amount of seconds left
297
+ * - full amount of time
298
+ * - size of the timer
299
+ * @param {Object} properties
300
+ */
301
+ updateProperties(properties) {
302
+ // Neo.setCssVariable({key: '--neo-timer-full', value: '\'200\''});
303
+ let me = this,
304
+ style = me.style;
305
+
306
+ if (properties.current !== undefined) {
307
+ style['--neo-timer-current'] = `${properties.current}!important`;
308
+ }
309
+ if (properties.full !== undefined) {
310
+ style['--neo-timer-full'] = `${properties.full}!important`;
311
+ }
312
+ if (properties.colorEnd !== undefined) {
313
+ style['--timer-color-end'] = `${properties.colorEnd}!important`;
314
+ }
315
+ if (properties.colorStart !== undefined) {
316
+ style['--timer-color-start'] = `${properties.colorStart}!important`;
317
+ }
318
+ if (properties.dimensions !== undefined) {
319
+ style['--timer-dimension'] = `${properties.dimensions}!important`;
320
+ }
321
+
322
+ me.style = style;
323
+ }
324
+ }
325
+
326
+ Neo.applyClassConfig(Timer);
327
+
328
+ export default Timer;
@@ -0,0 +1,165 @@
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='neo-video'
21
+ * @protected
22
+ */
23
+ ntype: 'video',
24
+ /*
25
+ * @member {[String]} cls=['neo-video']
26
+ */
27
+ baseCls: ['neo-video'],
28
+ /*
29
+ * @member {String} url=null
30
+ * @public
31
+ */
32
+ url_: null,
33
+
34
+ /**
35
+ * Current state of the video
36
+ * @member {boolean} playing=false
37
+ */
38
+ playing_: false,
39
+ /**
40
+ * Type of the video
41
+ * @member {boolean} type='video/mp4'
42
+ */
43
+ type: 'video/mp4',
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
+ /**
69
+ * construct is earlier in component life cicle than init
70
+ *
71
+ * @param config
72
+ */
73
+ construct(config) {
74
+ super.construct(config);
75
+
76
+ console.log(this);
77
+
78
+ let me = this,
79
+ domListeners = me.domListeners;
80
+
81
+ domListeners.push({
82
+ click : me.play,
83
+ delegate: '.neo-video-ghost'
84
+ }, {
85
+ click : me.pause,
86
+ delegate: '.neo-video-media'
87
+ });
88
+
89
+ me.domListeners = domListeners;
90
+ }
91
+
92
+ /**
93
+ * beforeSetPlaying autgen by playing_
94
+ *
95
+ * @param {Boolean} value
96
+ * @param {Boolean} oldValue
97
+ * @returns {Boolean}
98
+ */
99
+ beforeSetPlaying(value, oldValue) {
100
+ if (!Neo.isBoolean(value)) {
101
+ return oldValue;
102
+ }
103
+
104
+ return value;
105
+ }
106
+
107
+ /**
108
+ * afterSetPlaying - run the event listeners
109
+ * @param {Boolean} value
110
+ * @param {Boolean} oldValue
111
+ */
112
+ afterSetPlaying(value, oldValue) {
113
+ let vdom = this.vdom,
114
+ media = VDomUtil.getFlags(vdom, 'media')[0],
115
+ ghost = VDomUtil.getFlags(vdom, 'ghost')[0];
116
+
117
+ ghost.removeDom = value;
118
+ media.removeDom = !value;
119
+
120
+ this.vdom = vdom;
121
+ }
122
+
123
+ /**
124
+ * afterSetUrl
125
+ * Add a source element into the video element containing the url
126
+ *
127
+ * @param {String} value
128
+ * @param {String|null} oldValue
129
+ */
130
+ afterSetUrl(value, oldValue) {
131
+ if (!value) {
132
+ return;
133
+ }
134
+
135
+ const me = this;
136
+ let vdom = me.vdom,
137
+ media = vdom.cn[1];
138
+
139
+ media.cn = [{
140
+ tag : 'source',
141
+ src : value,
142
+ type: me.type
143
+ }];
144
+
145
+ me.vdom = vdom;
146
+ }
147
+
148
+ /**
149
+ * Clicked ghost
150
+ */
151
+ play() {
152
+ this.playing = true;
153
+ }
154
+
155
+ /**
156
+ * Clicked media
157
+ */
158
+ pause() {
159
+ this.playing = false;
160
+ }
161
+ }
162
+
163
+ Neo.applyClassConfig(Video);
164
+
165
+ export default Video;
@@ -42,6 +42,14 @@ class DateField extends Picker {
42
42
  * @member {String} inputType='date'
43
43
  */
44
44
  inputType: 'date',
45
+ /**
46
+ * @member {String|null} maxValue_=null
47
+ */
48
+ maxValue_: null,
49
+ /**
50
+ * @member {String|null} minValue_=null
51
+ */
52
+ minValue_: null,
45
53
  /**
46
54
  * @member {Number} pickerHeight=225
47
55
  */
@@ -79,6 +87,8 @@ class DateField extends Picker {
79
87
 
80
88
  me.dateSelector = Neo.create(DateSelector, {
81
89
  dayNameFormat: 'short',
90
+ maxValue : me.maxValue,
91
+ minValue : me.minValue,
82
92
  value : me.value || DateUtil.convertToyyyymmdd(new Date()),
83
93
  ...me.dateSelectorConfig
84
94
  });
@@ -93,6 +103,38 @@ class DateField extends Picker {
93
103
  });
94
104
  }
95
105
 
106
+ /**
107
+ * Triggered after the maxValue config got changed
108
+ * @param {Text} value
109
+ * @param {Text} oldValue
110
+ * @protected
111
+ */
112
+ afterSetMaxValue(value, oldValue) {
113
+ let me = this;
114
+
115
+ me.changeInputElKey('max', value);
116
+
117
+ if (me.dateSelector) {
118
+ me.dateSelector.maxValue = value
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Triggered after the minValue config got changed
124
+ * @param {Text} value
125
+ * @param {Text} oldValue
126
+ * @protected
127
+ */
128
+ afterSetMinValue(value, oldValue) {
129
+ let me = this;
130
+
131
+ me.changeInputElKey('max', value);
132
+
133
+ if (me.dateSelector) {
134
+ me.dateSelector.minValue = value
135
+ }
136
+ }
137
+
96
138
  /**
97
139
  * Triggered after the value config got changed
98
140
  * @param {String} value
@@ -56,6 +56,11 @@ class Flexbox extends Base {
56
56
  * @member {String|null} direction_=null
57
57
  */
58
58
  direction_: null,
59
+ /**
60
+ * flex css allows gap. This adds it to the component style
61
+ * @member {String} gap_=null
62
+ */
63
+ gap_: null,
59
64
  /**
60
65
  * Valid values: 'center', 'end', 'start', null
61
66
  * @member {String|null} pack_=null
@@ -93,6 +98,22 @@ class Flexbox extends Base {
93
98
  oldValue && this.updateInputValue(value, oldValue, 'direction');
94
99
  }
95
100
 
101
+ /**
102
+ * Updates the Container style to add a gap to display:flex
103
+ * @param {String|null} value
104
+ * @param {String|null} oldValue
105
+ * @protected
106
+ */
107
+ afterSetGap(value, oldValue) {
108
+ if (!value && !oldValue) return;
109
+
110
+ let item = Neo.getComponent(this.containerId),
111
+ style = item.wrapperStyle;
112
+
113
+ style.gap = value;
114
+ item.wrapperStyle = style;
115
+ }
116
+
96
117
  /**
97
118
  * Updates the Container CSS cls after "pack" gets changed
98
119
  * @param {String|null} value