neo.mjs 8.22.0 → 8.24.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.
- package/README.md +5 -0
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FeatureSection.mjs +4 -4
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/home/MainContainer.mjs +7 -7
- package/apps/portal/view/home/parts/MainNeo.mjs +35 -4
- package/examples/ConfigurationViewport.mjs +21 -8
- package/examples/ServiceWorker.mjs +2 -2
- package/examples/component/magicmovetext/MainContainer.mjs +92 -0
- package/examples/component/magicmovetext/app.mjs +6 -0
- package/examples/component/magicmovetext/index.html +11 -0
- package/examples/component/magicmovetext/neo-config.json +6 -0
- package/examples/grid/bigData/GridContainer.mjs +3 -0
- package/package.json +2 -2
- package/resources/scss/src/apps/portal/home/parts/MainNeo.scss +30 -0
- package/resources/scss/src/component/MagicMoveText.scss +39 -0
- package/resources/scss/src/examples/ConfigurationViewport.scss +27 -0
- package/src/DefaultConfig.mjs +2 -2
- package/src/component/MagicMoveText.mjs +494 -0
- package/src/data/Store.mjs +12 -0
- package/src/grid/Container.mjs +78 -25
- package/src/grid/View.mjs +89 -74
- package/src/grid/header/Toolbar.mjs +2 -3
- package/src/selection/grid/CellModel.mjs +8 -4
- package/src/selection/grid/RowModel.mjs +2 -2
- package/resources/scss/src/examples/ConfigurationPanel.scss +0 -25
@@ -0,0 +1,494 @@
|
|
1
|
+
import Component from '../component/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Deeply inspired by https://github.com/yangshun 's video on LinkedIn
|
5
|
+
* as well as Apple's Keynote Magic Move effect
|
6
|
+
* @class Neo.component.MagicMoveText
|
7
|
+
* @extends Neo.component.Base
|
8
|
+
*/
|
9
|
+
class MagicMoveText extends Component {
|
10
|
+
static config = {
|
11
|
+
/**
|
12
|
+
* @member {String} className='Neo.component.MagicMoveText'
|
13
|
+
* @protected
|
14
|
+
*/
|
15
|
+
className: 'Neo.component.MagicMoveText',
|
16
|
+
/**
|
17
|
+
* @member {String} ntype='magic-move-text'
|
18
|
+
* @protected
|
19
|
+
*/
|
20
|
+
ntype: 'magic-move-text',
|
21
|
+
/**
|
22
|
+
* @member {Boolean} autoCycle_=true
|
23
|
+
*/
|
24
|
+
autoCycle_: true,
|
25
|
+
/**
|
26
|
+
* @member {Number} autoCycleInterval_=2000
|
27
|
+
*/
|
28
|
+
autoCycleInterval_: 2000,
|
29
|
+
/**
|
30
|
+
* @member {String[]} baseCls=['neo-magic-move-text']
|
31
|
+
* @protected
|
32
|
+
*/
|
33
|
+
baseCls: ['neo-magic-move-text'],
|
34
|
+
/**
|
35
|
+
* @member {String|null} colorMove=null
|
36
|
+
*/
|
37
|
+
colorMove: null,
|
38
|
+
/**
|
39
|
+
* @member {String|null} colorFadeIn=null
|
40
|
+
*/
|
41
|
+
colorFadeIn: null,
|
42
|
+
/**
|
43
|
+
* @member {String|null} colorFadeOut=null
|
44
|
+
*/
|
45
|
+
colorFadeOut: null,
|
46
|
+
/**
|
47
|
+
* @member {String[]|null} cycleTexts=null
|
48
|
+
*/
|
49
|
+
cycleTexts: null,
|
50
|
+
/**
|
51
|
+
* @member {String} fontFamily_='Helvetica Neue'
|
52
|
+
*/
|
53
|
+
fontFamily_: 'Helvetica Neue',
|
54
|
+
/**
|
55
|
+
* @member {String} text_=null
|
56
|
+
*/
|
57
|
+
text_: null,
|
58
|
+
/**
|
59
|
+
* Time in ms for the fadeIn, fadeOut and move character OPs
|
60
|
+
* @member {Number} transitionTime_=500
|
61
|
+
*/
|
62
|
+
transitionTime_: 500,
|
63
|
+
/**
|
64
|
+
* @member {Object} _vdom
|
65
|
+
*/
|
66
|
+
_vdom:
|
67
|
+
{style: {}, cn: [
|
68
|
+
{cls: ['neo-content'], cn: []},
|
69
|
+
{cls: ['neo-measure-element-wrapper'], removeDom: true, cn: [
|
70
|
+
{cls: ['neo-measure-element'], cn:[]}
|
71
|
+
]}
|
72
|
+
]}
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* @member {Object[]} chars=[]
|
77
|
+
* @protected
|
78
|
+
*/
|
79
|
+
chars = []
|
80
|
+
/**
|
81
|
+
* @member {Object[]} charsVdom=[]
|
82
|
+
* @protected
|
83
|
+
*/
|
84
|
+
charsVdom = []
|
85
|
+
/**
|
86
|
+
* @member {Number} contentHeight=0
|
87
|
+
* @protected
|
88
|
+
*/
|
89
|
+
contentHeight = 0
|
90
|
+
/**
|
91
|
+
* @member {Number} contentWidth=0
|
92
|
+
* @protected
|
93
|
+
*/
|
94
|
+
contentWidth = 0
|
95
|
+
/**
|
96
|
+
* @member {Number} currentIndex=0
|
97
|
+
* @protected
|
98
|
+
*/
|
99
|
+
currentIndex = 0
|
100
|
+
/**
|
101
|
+
* We do not need the first event to trigger logic, since afterSetMounted() handles this
|
102
|
+
* @member {Boolean} initialResizeEvent=true
|
103
|
+
* @protected
|
104
|
+
*/
|
105
|
+
initialResizeEvent = true
|
106
|
+
/**
|
107
|
+
* @member {Number|null} intervalId=null
|
108
|
+
* @protected
|
109
|
+
*/
|
110
|
+
intervalId = null
|
111
|
+
/**
|
112
|
+
* Internal flag which gets set to true while the animated char transitions are running
|
113
|
+
* @member {Boolean} isTransitioning=false
|
114
|
+
* @protected
|
115
|
+
*/
|
116
|
+
isTransitioning = false
|
117
|
+
/**
|
118
|
+
* @member {Object} measureCache={}
|
119
|
+
* @protected
|
120
|
+
*/
|
121
|
+
measureCache = {}
|
122
|
+
/**
|
123
|
+
* @member {Object[]} previousChars=[]
|
124
|
+
* @protected
|
125
|
+
*/
|
126
|
+
previousChars = []
|
127
|
+
/**
|
128
|
+
* @member {Object} measureElement
|
129
|
+
* @protected
|
130
|
+
*/
|
131
|
+
get measureElement() {
|
132
|
+
return this.measureWrapper.cn[0]
|
133
|
+
}
|
134
|
+
/**
|
135
|
+
* @member {Object} measureElement
|
136
|
+
* @protected
|
137
|
+
*/
|
138
|
+
get measureWrapper() {
|
139
|
+
return this.vdom.cn[1]
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* @param {Object} config
|
144
|
+
*/
|
145
|
+
construct(config) {
|
146
|
+
super.construct(config);
|
147
|
+
|
148
|
+
let me = this;
|
149
|
+
|
150
|
+
me.addDomListeners({
|
151
|
+
resize: me.onResize,
|
152
|
+
scope : me
|
153
|
+
})
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* @param {Boolean} mounted
|
158
|
+
* @protected
|
159
|
+
*/
|
160
|
+
async addResizeObserver(mounted) {
|
161
|
+
let {id, windowId} = this,
|
162
|
+
ResizeObserver = await Neo.currentWorker.getAddon('ResizeObserver', windowId);
|
163
|
+
|
164
|
+
ResizeObserver[mounted ? 'register' : 'unregister']({id, windowId});
|
165
|
+
|
166
|
+
if (mounted) {
|
167
|
+
this.initialResizeEvent = true
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Triggered after the autoCycle config got changed
|
173
|
+
* @param {Boolean} value
|
174
|
+
* @param {Boolean} oldValue
|
175
|
+
* @protected
|
176
|
+
*/
|
177
|
+
afterSetAutoCycle(value, oldValue) {
|
178
|
+
this.mounted && this.startAutoCycle(value)
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Triggered after the autoCycleInterval config got changed
|
183
|
+
* @param {Number} value
|
184
|
+
* @param {Number} oldValue
|
185
|
+
* @protected
|
186
|
+
*/
|
187
|
+
afterSetAutoCycleInterval(value, oldValue) {
|
188
|
+
let me = this;
|
189
|
+
|
190
|
+
if (oldValue && me.mounted) {
|
191
|
+
me.startAutoCycle(false);
|
192
|
+
me.startAutoCycle()
|
193
|
+
}
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Triggered after the fontFamily config got changed
|
198
|
+
* @param {String} value
|
199
|
+
* @param {String} oldValue
|
200
|
+
* @protected
|
201
|
+
*/
|
202
|
+
afterSetFontFamily(value, oldValue) {
|
203
|
+
let me = this;
|
204
|
+
|
205
|
+
me.measureCache = {};
|
206
|
+
|
207
|
+
me.vdom.style.fontFamily = value;
|
208
|
+
me.update()
|
209
|
+
}
|
210
|
+
|
211
|
+
/**
|
212
|
+
* Triggered after the mounted config got changed
|
213
|
+
* @param {Boolean} value
|
214
|
+
* @param {Boolean} oldValue
|
215
|
+
* @protected
|
216
|
+
*/
|
217
|
+
afterSetMounted(value, oldValue) {
|
218
|
+
super.afterSetMounted(value, oldValue);
|
219
|
+
|
220
|
+
let me = this;
|
221
|
+
|
222
|
+
value && me.getDomRect().then(rect => {
|
223
|
+
me.contentHeight = rect.height;
|
224
|
+
me.contentWidth = rect.width;
|
225
|
+
|
226
|
+
me.autoCycle && me.startAutoCycle(value)
|
227
|
+
});
|
228
|
+
|
229
|
+
oldValue !== undefined && me.addResizeObserver(value)
|
230
|
+
}
|
231
|
+
|
232
|
+
/**
|
233
|
+
* Triggered after the text config got changed
|
234
|
+
* @param {String} value
|
235
|
+
* @param {String} oldValue
|
236
|
+
* @returns {Promise<void>}
|
237
|
+
* @protected
|
238
|
+
*/
|
239
|
+
async afterSetText(value, oldValue) {
|
240
|
+
let me = this,
|
241
|
+
{measureElement} = me;
|
242
|
+
|
243
|
+
if (oldValue) {
|
244
|
+
me.previousChars = me.chars
|
245
|
+
}
|
246
|
+
|
247
|
+
if (value) {
|
248
|
+
me.chars = [];
|
249
|
+
measureElement.cn = [];
|
250
|
+
|
251
|
+
value?.split('').forEach(char => {
|
252
|
+
me.chars.push({name: char});
|
253
|
+
|
254
|
+
if (char === ' ') {
|
255
|
+
char = ' '
|
256
|
+
}
|
257
|
+
|
258
|
+
measureElement.cn.push({tag: 'span', html: char})
|
259
|
+
});
|
260
|
+
|
261
|
+
if (me.mounted) {
|
262
|
+
await me.measureChars()
|
263
|
+
}
|
264
|
+
|
265
|
+
await me.updateChars()
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Triggered after the transitionTime config got changed
|
271
|
+
* @param {Number} value
|
272
|
+
* @param {Number} oldValue
|
273
|
+
* @protected
|
274
|
+
*/
|
275
|
+
afterSetTransitionTime(value, oldValue) {
|
276
|
+
this.vdom.style['--neo-transition-time'] = value + 'ms';
|
277
|
+
this.update()
|
278
|
+
}
|
279
|
+
|
280
|
+
/**
|
281
|
+
* @param {String[]} letters
|
282
|
+
* @returns {Object[]}
|
283
|
+
* @protected
|
284
|
+
*/
|
285
|
+
createCharsVdom(letters) {
|
286
|
+
let me = this,
|
287
|
+
{chars} = me,
|
288
|
+
charsContainer = [],
|
289
|
+
char;
|
290
|
+
|
291
|
+
letters.forEach((letter, index) => {
|
292
|
+
if (letter !== null) {
|
293
|
+
char = chars[index];
|
294
|
+
|
295
|
+
charsContainer.push({
|
296
|
+
cls : ['neo-char'],
|
297
|
+
html : char.name,
|
298
|
+
style: {color: me.colorFadeIn, left: char.left, opacity: 0, top: char.top}
|
299
|
+
})
|
300
|
+
}
|
301
|
+
});
|
302
|
+
|
303
|
+
return charsContainer
|
304
|
+
}
|
305
|
+
|
306
|
+
/**
|
307
|
+
* @protected
|
308
|
+
*/
|
309
|
+
cycleText() {
|
310
|
+
let me = this;
|
311
|
+
|
312
|
+
me.text = me.cycleTexts[me.currentIndex];
|
313
|
+
me.currentIndex = (me.currentIndex + 1) % me.cycleTexts.length
|
314
|
+
}
|
315
|
+
|
316
|
+
/**
|
317
|
+
* @returns {Promise<void>}
|
318
|
+
* @protected
|
319
|
+
*/
|
320
|
+
async measureChars() {
|
321
|
+
let me = this,
|
322
|
+
{measureCache, measureElement, measureWrapper, text} = me,
|
323
|
+
parentRect, rects;
|
324
|
+
|
325
|
+
if (measureCache[text]) {
|
326
|
+
rects = [...measureCache[text]];
|
327
|
+
parentRect = rects.shift()
|
328
|
+
} else {
|
329
|
+
measureWrapper.style = {
|
330
|
+
height: me.contentHeight + 'px',
|
331
|
+
width : me.contentWidth + 'px'
|
332
|
+
};
|
333
|
+
|
334
|
+
delete measureWrapper.removeDom;
|
335
|
+
|
336
|
+
await me.promiseUpdate();
|
337
|
+
await me.timeout(20);
|
338
|
+
|
339
|
+
rects = await me.getDomRect([measureWrapper.id, ...measureElement.cn.map(node => node.id)]);
|
340
|
+
parentRect = rects.shift();
|
341
|
+
|
342
|
+
measureCache[text] = [parentRect, ...rects]
|
343
|
+
}
|
344
|
+
|
345
|
+
rects.forEach((rect, index) => {
|
346
|
+
me.chars[index].left = `${rect.left - parentRect.left}px`;
|
347
|
+
me.chars[index].top = `${rect.top - parentRect.top }px`;
|
348
|
+
});
|
349
|
+
|
350
|
+
measureWrapper.removeDom = true;
|
351
|
+
await me.promiseUpdate()
|
352
|
+
}
|
353
|
+
|
354
|
+
/**
|
355
|
+
* @param {Object} data
|
356
|
+
* @returns {Promise<void>}
|
357
|
+
* @protected
|
358
|
+
*/
|
359
|
+
async onResize({rect}) {
|
360
|
+
let me = this;
|
361
|
+
|
362
|
+
me.contentHeight = rect.height;
|
363
|
+
me.contentWidth = rect.width;
|
364
|
+
|
365
|
+
me.measureCache = {};
|
366
|
+
|
367
|
+
|
368
|
+
if (!me.initialResizeEvent) {
|
369
|
+
if (!me.isTransitioning) {
|
370
|
+
await me.measureChars();
|
371
|
+
|
372
|
+
me.charsVdom = me.createCharsVdom(me.chars.map(char => char.name))
|
373
|
+
}
|
374
|
+
} else {
|
375
|
+
me.initialResizeEvent = false
|
376
|
+
}
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* @param {Object} a
|
381
|
+
* @param {Object} b
|
382
|
+
* @returns {Number}
|
383
|
+
* @protected
|
384
|
+
*/
|
385
|
+
sortCharacters(a, b) {
|
386
|
+
let deltaTop = parseFloat(a.style.top) - parseFloat(b.style.top);
|
387
|
+
|
388
|
+
if (deltaTop !== 0) {
|
389
|
+
return deltaTop
|
390
|
+
}
|
391
|
+
|
392
|
+
return parseFloat(a.style.left) - parseFloat(b.style.left)
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* @param {Boolean} start=true
|
397
|
+
* @protected
|
398
|
+
*/
|
399
|
+
startAutoCycle(start=true) {
|
400
|
+
let me = this;
|
401
|
+
|
402
|
+
if (start) {
|
403
|
+
me.intervalId = setInterval(me.cycleText.bind(me), me.autoCycleInterval);
|
404
|
+
|
405
|
+
me.timeout(20).then(() => {me.cycleText()});
|
406
|
+
} else {
|
407
|
+
clearInterval(me.intervalId)
|
408
|
+
}
|
409
|
+
}
|
410
|
+
|
411
|
+
/**
|
412
|
+
* @returns {Promise<void>}
|
413
|
+
* @protected
|
414
|
+
*/
|
415
|
+
async updateChars() {
|
416
|
+
let me = this,
|
417
|
+
{chars, previousChars} = me,
|
418
|
+
charsContainer = me.vdom.cn[0],
|
419
|
+
letters = chars.map(char => char.name),
|
420
|
+
charNode, index;
|
421
|
+
|
422
|
+
me.isTransitioning = true;
|
423
|
+
|
424
|
+
if (me.charsVdom.length > 1) {
|
425
|
+
charsContainer.cn = me.charsVdom;
|
426
|
+
await me.promiseUpdate()
|
427
|
+
}
|
428
|
+
|
429
|
+
previousChars.forEach((previousChar, previousIndex) => {
|
430
|
+
index = letters.indexOf(previousChar.name);
|
431
|
+
|
432
|
+
if (index > -1) {
|
433
|
+
charNode = charsContainer.cn[previousIndex];
|
434
|
+
|
435
|
+
Object.assign(charNode.style, {
|
436
|
+
color: me.colorMove,
|
437
|
+
left : chars[index].left,
|
438
|
+
top : chars[index].top
|
439
|
+
});
|
440
|
+
|
441
|
+
letters[index] = null
|
442
|
+
} else {
|
443
|
+
charNode = charsContainer.cn[previousIndex];
|
444
|
+
|
445
|
+
charNode.flag = 'remove'
|
446
|
+
}
|
447
|
+
});
|
448
|
+
|
449
|
+
charsContainer.cn.push(...me.createCharsVdom(letters));
|
450
|
+
|
451
|
+
await me.promiseUpdate();
|
452
|
+
|
453
|
+
charsContainer.cn.forEach(charNode => {
|
454
|
+
if (charNode.flag === 'remove') {
|
455
|
+
charNode.style.color = me.colorFadeOut;
|
456
|
+
charNode.style.opacity = 0
|
457
|
+
} else {
|
458
|
+
delete charNode.style.opacity
|
459
|
+
}
|
460
|
+
});
|
461
|
+
|
462
|
+
await me.promiseUpdate();
|
463
|
+
await me.timeout(me.transitionTime);
|
464
|
+
|
465
|
+
charsContainer.cn.sort(me.sortCharacters);
|
466
|
+
|
467
|
+
index = charsContainer.cn.length - 1;
|
468
|
+
|
469
|
+
for (; index >= 0; index--) {
|
470
|
+
charNode = charsContainer.cn[index];
|
471
|
+
|
472
|
+
delete charNode.flag;
|
473
|
+
delete charNode.style.color;
|
474
|
+
|
475
|
+
if (charNode.style.opacity === 0) {
|
476
|
+
charsContainer.cn.splice(index, 1)
|
477
|
+
}
|
478
|
+
}
|
479
|
+
|
480
|
+
await me.promiseUpdate();
|
481
|
+
await me.timeout(200);
|
482
|
+
|
483
|
+
me.charsVdom = [...charsContainer.cn];
|
484
|
+
|
485
|
+
charsContainer.cn.length = 0;
|
486
|
+
|
487
|
+
charsContainer.cn.push({html: me.text});
|
488
|
+
await me.promiseUpdate();
|
489
|
+
|
490
|
+
me.isTransitioning = false
|
491
|
+
}
|
492
|
+
}
|
493
|
+
|
494
|
+
export default Neo.setupClass(MagicMoveText);
|
package/src/data/Store.mjs
CHANGED
@@ -330,6 +330,18 @@ class Store extends Base {
|
|
330
330
|
return this.keyProperty || this.model.keyProperty
|
331
331
|
}
|
332
332
|
|
333
|
+
/**
|
334
|
+
* Convenience shortcut to check for int based keyProperties
|
335
|
+
* @returns {String|null} lowercase value of the model field type
|
336
|
+
*/
|
337
|
+
getKeyType() {
|
338
|
+
let me = this,
|
339
|
+
{model} = me,
|
340
|
+
keyField = model?.getField(me.getKeyProperty());
|
341
|
+
|
342
|
+
return keyField?.type?.toLowerCase() || null
|
343
|
+
}
|
344
|
+
|
333
345
|
/**
|
334
346
|
* @param {Object} opts={}
|
335
347
|
* @param {Object} opts.data
|
package/src/grid/Container.mjs
CHANGED
@@ -197,6 +197,25 @@ class GridContainer extends BaseContainer {
|
|
197
197
|
})
|
198
198
|
}
|
199
199
|
|
200
|
+
/**
|
201
|
+
* @param {Boolean} mounted
|
202
|
+
* @protected
|
203
|
+
*/
|
204
|
+
async addResizeObserver(mounted) {
|
205
|
+
let me = this,
|
206
|
+
{windowId} = me,
|
207
|
+
ResizeObserver = await Neo.currentWorker.getAddon('ResizeObserver', windowId),
|
208
|
+
resizeParams = {id: me.id, windowId};
|
209
|
+
|
210
|
+
if (mounted) {
|
211
|
+
ResizeObserver.register(resizeParams);
|
212
|
+
await me.passSizeToView()
|
213
|
+
} else {
|
214
|
+
me.initialResizeEvent = true;
|
215
|
+
ResizeObserver.unregister(resizeParams)
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
200
219
|
/**
|
201
220
|
* Triggered after the cellEditing config got changed
|
202
221
|
* @param {Boolean} value
|
@@ -245,25 +264,6 @@ class GridContainer extends BaseContainer {
|
|
245
264
|
}
|
246
265
|
}
|
247
266
|
|
248
|
-
/**
|
249
|
-
* @param {Boolean} mounted
|
250
|
-
* @protected
|
251
|
-
*/
|
252
|
-
async addResizeObserver(mounted) {
|
253
|
-
let me = this,
|
254
|
-
{windowId} = me,
|
255
|
-
ResizeObserver = await Neo.currentWorker.getAddon('ResizeObserver', windowId),
|
256
|
-
resizeParams = {id: me.id, windowId};
|
257
|
-
|
258
|
-
if (mounted) {
|
259
|
-
ResizeObserver.register(resizeParams);
|
260
|
-
await me.passSizeToView()
|
261
|
-
} else {
|
262
|
-
me.initialResizeEvent = true;
|
263
|
-
ResizeObserver.unregister(resizeParams)
|
264
|
-
}
|
265
|
-
}
|
266
|
-
|
267
267
|
/**
|
268
268
|
* Triggered after the mounted config got changed
|
269
269
|
* @param {Boolean} value
|
@@ -522,7 +522,7 @@ class GridContainer extends BaseContainer {
|
|
522
522
|
if (!me.initialResizeEvent) {
|
523
523
|
await me.passSizeToView(true);
|
524
524
|
|
525
|
-
me.view.
|
525
|
+
me.view.updateMountedAndVisibleColumns();
|
526
526
|
|
527
527
|
await me.headerToolbar.passSizeToView()
|
528
528
|
} else {
|
@@ -537,16 +537,17 @@ class GridContainer extends BaseContainer {
|
|
537
537
|
* @param {Object} data.touches
|
538
538
|
*/
|
539
539
|
onScroll({scrollLeft, target, touches}) {
|
540
|
-
let me
|
540
|
+
let me = this,
|
541
|
+
{view} = me,
|
541
542
|
deltaY, lastTouchY;
|
542
543
|
|
543
544
|
// We must ignore events for grid-scrollbar
|
544
545
|
if (target.id.includes('grid-container')) {
|
545
546
|
me.headerToolbar.scrollLeft = scrollLeft;
|
546
|
-
|
547
|
+
view.scrollPosition = {x: scrollLeft, y: view.scrollPosition.y};
|
547
548
|
|
548
549
|
if (touches) {
|
549
|
-
if (!
|
550
|
+
if (!view.isTouchMoveOwner) {
|
550
551
|
me.isTouchMoveOwner = true
|
551
552
|
}
|
552
553
|
|
@@ -556,8 +557,8 @@ class GridContainer extends BaseContainer {
|
|
556
557
|
|
557
558
|
deltaY !== 0 && Neo.main.DomAccess.scrollTo({
|
558
559
|
direction: 'top',
|
559
|
-
id :
|
560
|
-
value :
|
560
|
+
id : view.vdom.id,
|
561
|
+
value : view.scrollPosition.y + deltaY
|
561
562
|
})
|
562
563
|
|
563
564
|
me.lastTouchY = lastTouchY
|
@@ -657,6 +658,58 @@ class GridContainer extends BaseContainer {
|
|
657
658
|
}
|
658
659
|
})
|
659
660
|
}
|
661
|
+
|
662
|
+
/**
|
663
|
+
* Used for keyboard navigation (selection models)
|
664
|
+
* @param {Number} index
|
665
|
+
* @param {Number} step
|
666
|
+
*/
|
667
|
+
scrollByColumns(index, step) {
|
668
|
+
let me = this,
|
669
|
+
{view} = me,
|
670
|
+
{columnPositions, containerWidth, mountedColumns, visibleColumns} = view,
|
671
|
+
countColumns = columnPositions.getCount(),
|
672
|
+
newIndex = index + step,
|
673
|
+
column, mounted, scrollPosition, visible;
|
674
|
+
|
675
|
+
if (newIndex >= countColumns) {
|
676
|
+
newIndex %= countColumns;
|
677
|
+
step = newIndex - index
|
678
|
+
}
|
679
|
+
|
680
|
+
while (newIndex < 0) {
|
681
|
+
newIndex += countColumns;
|
682
|
+
step += countColumns
|
683
|
+
}
|
684
|
+
|
685
|
+
mounted = newIndex >= mountedColumns[0] && newIndex <= mountedColumns[1];
|
686
|
+
|
687
|
+
// Not using >= or <=, since the first / last column might not be fully visible
|
688
|
+
visible = newIndex > visibleColumns[0] && newIndex < visibleColumns[1];
|
689
|
+
|
690
|
+
if (!visible) {
|
691
|
+
// Leaving the mounted area will re-calculate the visibleColumns for us
|
692
|
+
if (mounted) {
|
693
|
+
visibleColumns[0] += step;
|
694
|
+
visibleColumns[1] += step
|
695
|
+
}
|
696
|
+
|
697
|
+
column = columnPositions.getAt(newIndex);
|
698
|
+
|
699
|
+
if (step < 0) {
|
700
|
+
scrollPosition = column.x
|
701
|
+
} else {
|
702
|
+
scrollPosition = column.x - containerWidth + column.width
|
703
|
+
}
|
704
|
+
|
705
|
+
Neo.main.DomAccess.scrollTo({
|
706
|
+
direction: 'left',
|
707
|
+
id : me.id,
|
708
|
+
value : scrollPosition,
|
709
|
+
windowId : me.windowId
|
710
|
+
})
|
711
|
+
}
|
712
|
+
}
|
660
713
|
}
|
661
714
|
|
662
715
|
export default Neo.setupClass(GridContainer);
|