neo.mjs 8.15.0 → 8.17.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/apps/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +2 -2
- package/resources/scss/src/grid/Container.scss +12 -68
- package/resources/scss/src/grid/Scrollbar.scss +21 -0
- package/resources/scss/src/grid/View.scss +29 -2
- package/src/DefaultConfig.mjs +2 -2
- package/src/draggable/toolbar/SortZone.mjs +58 -23
- package/src/grid/Container.mjs +33 -14
- package/src/grid/Scrollbar.mjs +118 -0
- package/src/grid/View.mjs +1 -1
- package/src/main/addon/ScrollSync.mjs +90 -0
- package/src/worker/App.mjs +17 -0
package/apps/ServiceWorker.mjs
CHANGED
package/apps/portal/index.html
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "8.
|
3
|
+
"version": "8.17.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -60,7 +60,7 @@
|
|
60
60
|
"neo-jsdoc": "1.0.1",
|
61
61
|
"neo-jsdoc-x": "1.0.5",
|
62
62
|
"postcss": "^8.5.1",
|
63
|
-
"sass": "^1.
|
63
|
+
"sass": "^1.84.0",
|
64
64
|
"siesta-lite": "5.5.2",
|
65
65
|
"url": "^0.11.4",
|
66
66
|
"webpack": "^5.97.1",
|
@@ -4,74 +4,18 @@
|
|
4
4
|
overflow-y : hidden;
|
5
5
|
overscroll-behavior: none;
|
6
6
|
position : relative;
|
7
|
-
}
|
8
|
-
|
9
|
-
.neo-grid-container {
|
10
|
-
border : 1px solid var(--grid-container-border-color);
|
11
|
-
border-spacing : 0;
|
12
|
-
color : var(--grid-container-color);
|
13
|
-
font-size : 13px;
|
14
|
-
font-weight : 400;
|
15
|
-
height : 100%;
|
16
|
-
line-height : 19px;
|
17
|
-
overscroll-behavior: none;
|
18
|
-
overflow-y : hidden;
|
19
|
-
position : absolute;
|
20
|
-
width : 100%;
|
21
|
-
|
22
|
-
.neo-grid-row {
|
23
|
-
display: flex;
|
24
|
-
width : 100%;
|
25
|
-
}
|
26
|
-
|
27
|
-
.neo-grid-header-toolbar .neo-grid-row:nth-child(1) .neo-grid-header-cell {
|
28
|
-
z-index : 10;
|
29
|
-
}
|
30
|
-
|
31
|
-
.neo-grid-body {
|
32
|
-
display : block;
|
33
|
-
max-height: 300px; // an initial dummy value to reduce the flickering until the real height is available
|
34
|
-
overflow-y: scroll;
|
35
|
-
}
|
36
|
-
|
37
|
-
.neo-grid-row:last-child {
|
38
|
-
.neo-grid-cell {
|
39
|
-
border-bottom-width: 0 !important;
|
40
|
-
}
|
41
|
-
}
|
42
|
-
|
43
|
-
.neo-grid-row.neo-even {
|
44
|
-
.neo-grid-cell {
|
45
|
-
background-color: var(--grid-container-cell-background-color-even);
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
.neo-grid-header-cell {
|
50
|
-
position: sticky;
|
51
|
-
top : 0;
|
52
|
-
z-index : 5;
|
53
|
-
}
|
54
|
-
|
55
|
-
.neo-grid-cell, .neo-grid-header-cell {
|
56
|
-
border-bottom: 1px solid var(--grid-container-border-color);
|
57
|
-
border-right : 1px solid var(--grid-container-border-color);
|
58
|
-
height : inherit;
|
59
|
-
min-width : 300px;
|
60
|
-
|
61
|
-
&:last-child {
|
62
|
-
border-right-width : 0;
|
63
|
-
}
|
64
|
-
|
65
|
-
&.neo-locked {
|
66
|
-
left: 0;
|
67
|
-
}
|
68
|
-
}
|
69
7
|
|
70
|
-
.neo-grid-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
8
|
+
.neo-grid-container {
|
9
|
+
border : 1px solid var(--grid-container-border-color);
|
10
|
+
border-spacing : 0;
|
11
|
+
color : var(--grid-container-color);
|
12
|
+
font-size : 13px;
|
13
|
+
font-weight : 400;
|
14
|
+
height : 100%;
|
15
|
+
line-height : 19px;
|
16
|
+
overscroll-behavior: none;
|
17
|
+
overflow-y : hidden;
|
18
|
+
position : absolute;
|
19
|
+
width : 100%;
|
76
20
|
}
|
77
21
|
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.neo-grid-scrollbar {
|
2
|
+
bottom : 1px;
|
3
|
+
opacity : 0;
|
4
|
+
overflow-y : scroll;
|
5
|
+
position : absolute;
|
6
|
+
right : 0;
|
7
|
+
top : 31px; // header-toolbar height
|
8
|
+
transition : opacity 1s ease-out;
|
9
|
+
width : 16px;
|
10
|
+
z-index : 2;
|
11
|
+
|
12
|
+
&:hover {
|
13
|
+
opacity: 1;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
.neo-grid-wrapper:has(.neo-grid-view.neo-is-scrolling) {
|
18
|
+
.neo-grid-scrollbar {
|
19
|
+
opacity: 1;
|
20
|
+
}
|
21
|
+
}
|
@@ -4,17 +4,19 @@
|
|
4
4
|
overflow-x : hidden;
|
5
5
|
overflow-y : auto;
|
6
6
|
position : relative;
|
7
|
+
scrollbar-width: none;
|
7
8
|
|
8
9
|
&:focus {
|
9
10
|
outline: none;
|
10
11
|
}
|
11
12
|
|
12
|
-
.neo-grid-
|
13
|
+
.neo-grid-stretcher {
|
13
14
|
height : 1px;
|
14
15
|
position : absolute;
|
15
16
|
top : 0;
|
16
17
|
visibility : hidden;
|
17
18
|
width : 1px;
|
19
|
+
z-index : 1001; // Above the column dragProxy element
|
18
20
|
}
|
19
21
|
}
|
20
22
|
|
@@ -52,6 +54,13 @@
|
|
52
54
|
|
53
55
|
.neo-grid-row {
|
54
56
|
position: absolute;
|
57
|
+
width : 100%;
|
58
|
+
|
59
|
+
&:last-child {
|
60
|
+
.neo-grid-cell {
|
61
|
+
border-bottom-width: 0 !important;
|
62
|
+
}
|
63
|
+
}
|
55
64
|
|
56
65
|
&:hover {
|
57
66
|
.neo-grid-cell {
|
@@ -59,6 +68,12 @@
|
|
59
68
|
}
|
60
69
|
}
|
61
70
|
|
71
|
+
&.neo-even {
|
72
|
+
.neo-grid-cell {
|
73
|
+
background-color: var(--grid-container-cell-background-color-even);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
62
77
|
// selection.RowModel
|
63
78
|
&.neo-selected {
|
64
79
|
.neo-grid-cell {
|
@@ -68,7 +83,19 @@
|
|
68
83
|
}
|
69
84
|
|
70
85
|
.neo-grid-cell {
|
71
|
-
|
86
|
+
align-items : center;
|
87
|
+
background-color: var(--grid-container-cell-background-color);
|
88
|
+
border-bottom : 1px solid var(--grid-container-border-color);
|
89
|
+
border-right : 1px solid var(--grid-container-border-color);
|
90
|
+
display : flex;
|
91
|
+
height : inherit;
|
92
|
+
padding : 2px 10px 2px;
|
93
|
+
position : absolute;
|
94
|
+
width : fit-content;
|
95
|
+
|
96
|
+
&:last-child {
|
97
|
+
border-right-width : 0;
|
98
|
+
}
|
72
99
|
|
73
100
|
// selection.CellModel
|
74
101
|
&.neo-selected {
|
package/src/DefaultConfig.mjs
CHANGED
@@ -262,12 +262,12 @@ const DefaultConfig = {
|
|
262
262
|
useVdomWorker: true,
|
263
263
|
/**
|
264
264
|
* buildScripts/injectPackageVersion.mjs will update this value
|
265
|
-
* @default '8.
|
265
|
+
* @default '8.17.0'
|
266
266
|
* @memberOf! module:Neo
|
267
267
|
* @name config.version
|
268
268
|
* @type String
|
269
269
|
*/
|
270
|
-
version: '8.
|
270
|
+
version: '8.17.0'
|
271
271
|
};
|
272
272
|
|
273
273
|
Object.assign(DefaultConfig, {
|
@@ -82,6 +82,12 @@ class SortZone extends DragZone {
|
|
82
82
|
startIndex: -1
|
83
83
|
}
|
84
84
|
|
85
|
+
/**
|
86
|
+
* @member {Boolean} isOverDragging=false
|
87
|
+
* @protected
|
88
|
+
*/
|
89
|
+
isOverDragging = false
|
90
|
+
|
85
91
|
/**
|
86
92
|
* Override this method for class extensions (e.g. tab.header.Toolbar)
|
87
93
|
* @param {Number} fromIndex
|
@@ -150,52 +156,70 @@ class SortZone extends DragZone {
|
|
150
156
|
* @param {Object} data
|
151
157
|
*/
|
152
158
|
async onDragMove(data) {
|
153
|
-
//
|
159
|
+
// The method can trigger before we got the client rects from the main thread
|
154
160
|
if (!this.itemRects || this.isScrolling) {
|
155
161
|
return
|
156
162
|
}
|
157
163
|
|
158
164
|
let me = this,
|
159
|
-
moveFactor = 0.55, // we can not use 0.5, since items would jump back & forth
|
160
165
|
index = me.currentIndex,
|
161
166
|
{itemRects} = me,
|
162
167
|
maxItems = itemRects.length - 1,
|
163
168
|
reversed = me.reversedLayoutDirection,
|
164
|
-
delta,
|
169
|
+
delta, isOverDragging, isOverDraggingEnd, isOverDraggingStart, itemHeightOrWidth, moveFactor;
|
165
170
|
|
166
171
|
if (me.sortDirection === 'horizontal') {
|
167
|
-
delta
|
168
|
-
|
172
|
+
delta = data.clientX + me.scrollLeft - me.offsetX - itemRects[index].left;
|
173
|
+
isOverDraggingEnd = data.clientX > me.boundaryContainerRect.right;
|
174
|
+
isOverDraggingStart = data.clientX < me.boundaryContainerRect.left;
|
175
|
+
itemHeightOrWidth = 'width'
|
169
176
|
} else {
|
170
|
-
delta
|
171
|
-
|
177
|
+
delta = data.clientY + me.scrollTop - me.offsetY - itemRects[index].top;
|
178
|
+
isOverDraggingEnd = data.clientY > me.boundaryContainerRect.bottom;
|
179
|
+
isOverDraggingStart = data.clientY < me.boundaryContainerRect.top;
|
180
|
+
itemHeightOrWidth = 'height'
|
172
181
|
}
|
173
182
|
|
174
|
-
|
175
|
-
|
183
|
+
isOverDragging = isOverDraggingEnd || isOverDraggingStart;
|
184
|
+
moveFactor = isOverDragging ? 0.02 : 0.55; // We can not use 0.5, since items would jump back & forth
|
185
|
+
|
186
|
+
if (isOverDraggingStart) {
|
187
|
+
if (index > 0) {
|
176
188
|
me.currentIndex--;
|
189
|
+
await me.scrollToIndex();
|
190
|
+
me.switchItems(index, me.currentIndex)
|
191
|
+
}
|
192
|
+
}
|
177
193
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
194
|
+
else if (isOverDraggingEnd) {
|
195
|
+
if (index < maxItems) {
|
196
|
+
me.currentIndex++;
|
197
|
+
await me.scrollToIndex();
|
198
|
+
me.switchItems(index, me.currentIndex)
|
199
|
+
}
|
200
|
+
}
|
183
201
|
|
202
|
+
else if (index > 0 && (!reversed && delta < 0 || reversed && delta > 0)) {
|
203
|
+
if (Math.abs(delta) > itemRects[index - 1][itemHeightOrWidth] * moveFactor) {
|
204
|
+
me.currentIndex--;
|
184
205
|
me.switchItems(index, me.currentIndex)
|
185
206
|
}
|
186
207
|
}
|
187
208
|
|
188
209
|
else if (index < maxItems && (!reversed && delta > 0 || reversed && delta < 0)) {
|
189
|
-
if (Math.abs(delta) > itemRects[index + 1][
|
210
|
+
if (Math.abs(delta) > itemRects[index + 1][itemHeightOrWidth] * moveFactor) {
|
190
211
|
me.currentIndex++;
|
212
|
+
me.switchItems(index, me.currentIndex)
|
213
|
+
}
|
214
|
+
}
|
191
215
|
|
192
|
-
|
193
|
-
me.isScrolling = true;
|
194
|
-
await me.owner.scrollToIndex?.(me.currentIndex, itemRects[me.currentIndex]);
|
195
|
-
me.isScrolling = false
|
196
|
-
}
|
216
|
+
me.isOverDragging = isOverDragging && me.currentIndex !== 0 && me.currentIndex !== maxItems;
|
197
217
|
|
198
|
-
|
218
|
+
if (me.isOverDragging) {
|
219
|
+
await me.timeout(30); // wait for 1 frame
|
220
|
+
|
221
|
+
if (me.isOverDragging) {
|
222
|
+
await me.onDragMove(data)
|
199
223
|
}
|
200
224
|
}
|
201
225
|
}
|
@@ -227,7 +251,7 @@ class SortZone extends DragZone {
|
|
227
251
|
startIndex : index
|
228
252
|
});
|
229
253
|
|
230
|
-
await me.dragStart(data); //
|
254
|
+
await me.dragStart(data); // We do not want to trigger the super class call here
|
231
255
|
|
232
256
|
owner.items.forEach((item, index) => {
|
233
257
|
indexMap[index] = index;
|
@@ -244,7 +268,7 @@ class SortZone extends DragZone {
|
|
244
268
|
ownerStyle.height = `${itemRects[0].height}px`;
|
245
269
|
ownerStyle.width = `${itemRects[0].width}px`;
|
246
270
|
|
247
|
-
//
|
271
|
+
// The only reason we are adjusting the toolbar style is that there is no min height or width present.
|
248
272
|
// removing items from the layout could trigger a change in size.
|
249
273
|
owner.style = ownerStyle;
|
250
274
|
|
@@ -277,6 +301,17 @@ class SortZone extends DragZone {
|
|
277
301
|
}
|
278
302
|
}
|
279
303
|
|
304
|
+
/**
|
305
|
+
* @returns {Promise<void>}
|
306
|
+
*/
|
307
|
+
async scrollToIndex() {
|
308
|
+
let me = this;
|
309
|
+
|
310
|
+
me.isScrolling = true;
|
311
|
+
await me.owner.scrollToIndex?.(me.currentIndex, me.itemRects[me.currentIndex]);
|
312
|
+
me.isScrolling = false
|
313
|
+
}
|
314
|
+
|
280
315
|
/**
|
281
316
|
* @param {Number} index1
|
282
317
|
* @param {Number} index2
|
package/src/grid/Container.mjs
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import BaseContainer from '../container/Base.mjs';
|
2
2
|
import ClassSystemUtil from '../util/ClassSystem.mjs';
|
3
|
+
import GridScrollbar from './Scrollbar.mjs';
|
3
4
|
import GridView from './View.mjs';
|
4
5
|
import Store from '../data/Store.mjs';
|
5
6
|
import * as header from './header/_export.mjs';
|
@@ -70,6 +71,11 @@ class GridContainer extends BaseContainer {
|
|
70
71
|
* @member {Number} rowHeight_=32
|
71
72
|
*/
|
72
73
|
rowHeight_: 32,
|
74
|
+
/**
|
75
|
+
* @member {Neo.grid.Scrollbar|null} scrollbar=null
|
76
|
+
* @protected
|
77
|
+
*/
|
78
|
+
scrollbar: null,
|
73
79
|
/**
|
74
80
|
* @member {Boolean} showHeaderFilters_=false
|
75
81
|
*/
|
@@ -98,7 +104,7 @@ class GridContainer extends BaseContainer {
|
|
98
104
|
*/
|
99
105
|
items: null,
|
100
106
|
/**
|
101
|
-
* @member {Object} _vdom
|
107
|
+
* @member {Object} _vdom
|
102
108
|
*/
|
103
109
|
_vdom:
|
104
110
|
{cls: ['neo-grid-wrapper'], cn: [
|
@@ -134,8 +140,8 @@ class GridContainer extends BaseContainer {
|
|
134
140
|
construct(config) {
|
135
141
|
super.construct(config);
|
136
142
|
|
137
|
-
let me
|
138
|
-
{rowHeight, store} = me;
|
143
|
+
let me = this,
|
144
|
+
{appName, rowHeight, store, windowId} = me;
|
139
145
|
|
140
146
|
me.headerToolbarId = Neo.getId('grid-header-toolbar');
|
141
147
|
me.viewId = Neo.getId('grid-view');
|
@@ -147,13 +153,26 @@ class GridContainer extends BaseContainer {
|
|
147
153
|
sortable : me.sortable,
|
148
154
|
...me.headerToolbarConfig
|
149
155
|
}, {
|
150
|
-
module: GridView,
|
151
|
-
|
156
|
+
module : GridView,
|
157
|
+
flex : 1,
|
158
|
+
gridContainer: me,
|
159
|
+
id : me.viewId,
|
152
160
|
rowHeight,
|
153
161
|
store,
|
154
162
|
...me.viewConfig
|
155
163
|
}];
|
156
164
|
|
165
|
+
me.scrollbar = Neo.create({
|
166
|
+
module : GridScrollbar,
|
167
|
+
appName,
|
168
|
+
parentId: me.id,
|
169
|
+
rowHeight,
|
170
|
+
store,
|
171
|
+
windowId
|
172
|
+
});
|
173
|
+
|
174
|
+
me.vdom.cn.push(me.scrollbar.createVdomReference())
|
175
|
+
|
157
176
|
me.vdom.id = me.getWrapperId();
|
158
177
|
|
159
178
|
me.createColumns(me.columns);
|
@@ -219,15 +238,10 @@ class GridContainer extends BaseContainer {
|
|
219
238
|
*/
|
220
239
|
async addResizeObserver(mounted) {
|
221
240
|
let me = this,
|
222
|
-
ResizeObserver = Neo.main?.addon?.ResizeObserver,
|
223
241
|
{windowId} = me,
|
242
|
+
ResizeObserver = await Neo.currentWorker.getAddon('ResizeObserver', windowId),
|
224
243
|
resizeParams = {id: me.id, windowId};
|
225
244
|
|
226
|
-
if (!ResizeObserver) {
|
227
|
-
await Neo.Main.importAddon({name: 'ResizeObserver', windowId});
|
228
|
-
ResizeObserver = Neo.main.addon.ResizeObserver
|
229
|
-
}
|
230
|
-
|
231
245
|
if (mounted) {
|
232
246
|
ResizeObserver.register(resizeParams);
|
233
247
|
await me.passSizeToView()
|
@@ -505,12 +519,17 @@ class GridContainer extends BaseContainer {
|
|
505
519
|
|
506
520
|
/**
|
507
521
|
* @param {Object} data
|
522
|
+
* @param {Number} data.scrollLeft
|
523
|
+
* @param {Object} data.target
|
508
524
|
*/
|
509
|
-
onScroll({scrollLeft}) {
|
525
|
+
onScroll({scrollLeft, target}) {
|
510
526
|
let me = this;
|
511
527
|
|
512
|
-
|
513
|
-
|
528
|
+
// We must ignore events for grid-scrollbar
|
529
|
+
if (target.id.includes('grid-container')) {
|
530
|
+
me.headerToolbar.scrollLeft = scrollLeft;
|
531
|
+
me.view.scrollPosition = {x: scrollLeft, y: me.view.scrollPosition.y}
|
532
|
+
}
|
514
533
|
}
|
515
534
|
|
516
535
|
/**
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import Component from '../component/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* @class Neo.grid.Scrollbar
|
5
|
+
* @extends Neo.component.Base
|
6
|
+
*/
|
7
|
+
class GridScrollbar extends Component {
|
8
|
+
static config = {
|
9
|
+
/**
|
10
|
+
* @member {String} className='Neo.grid.Scrollbar'
|
11
|
+
* @protected
|
12
|
+
*/
|
13
|
+
className: 'Neo.grid.Scrollbar',
|
14
|
+
/**
|
15
|
+
* @member {String} ntype='grid-scrollbar'
|
16
|
+
* @protected
|
17
|
+
*/
|
18
|
+
ntype: 'grid-scrollbar',
|
19
|
+
/**
|
20
|
+
* @member {String[]} baseCls=['neo-grid-scrollbar']
|
21
|
+
* @protected
|
22
|
+
*/
|
23
|
+
baseCls: ['neo-grid-scrollbar'],
|
24
|
+
/**
|
25
|
+
* Number in px
|
26
|
+
* @member {Number} rowHeight_=0
|
27
|
+
*/
|
28
|
+
rowHeight_: 0,
|
29
|
+
/**
|
30
|
+
* @member {Neo.data.Store|null} store_=null
|
31
|
+
*/
|
32
|
+
store_: null,
|
33
|
+
/**
|
34
|
+
* @member {Object} _vdom
|
35
|
+
*/
|
36
|
+
_vdom:
|
37
|
+
{cn: [
|
38
|
+
{cls: ['neo-grid-scrollbar-content']}
|
39
|
+
]}
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @param {Boolean} mounted
|
44
|
+
* @protected
|
45
|
+
*/
|
46
|
+
async addScrollSync(mounted) {
|
47
|
+
let me = this,
|
48
|
+
{windowId} = me,
|
49
|
+
ScrollSync = await Neo.currentWorker.getAddon('ScrollSync', windowId),
|
50
|
+
params = {id: me.id, windowId};
|
51
|
+
|
52
|
+
if (mounted) {
|
53
|
+
ScrollSync.register({
|
54
|
+
fromId: me.parent.view.vdom.id,
|
55
|
+
toId : me.id,
|
56
|
+
...params
|
57
|
+
})
|
58
|
+
} else {
|
59
|
+
ScrollSync.unregister(params)
|
60
|
+
}
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Triggered after the mounted config got changed
|
65
|
+
* @param {Boolean} value
|
66
|
+
* @param {Boolean} oldValue
|
67
|
+
* @protected
|
68
|
+
*/
|
69
|
+
afterSetMounted(value, oldValue) {
|
70
|
+
super.afterSetMounted(value, oldValue);
|
71
|
+
oldValue !== undefined && this.addScrollSync(value)
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Triggered after the rowHeight config got changed
|
76
|
+
* @param {Number} value
|
77
|
+
* @param {Number} oldValue
|
78
|
+
* @protected
|
79
|
+
*/
|
80
|
+
afterSetRowHeight(value, oldValue) {
|
81
|
+
value > 0 && this.updateScrollHeight()
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Triggered after the store config got changed
|
86
|
+
* @param {Neo.data.Store|null} value
|
87
|
+
* @param {Neo.data.Store|null} oldValue
|
88
|
+
* @protected
|
89
|
+
*/
|
90
|
+
afterSetStore(value, oldValue) {
|
91
|
+
if (value) {
|
92
|
+
let me = this;
|
93
|
+
|
94
|
+
value.on({
|
95
|
+
load : me.updateScrollHeight,
|
96
|
+
scope: me
|
97
|
+
});
|
98
|
+
|
99
|
+
value.getCount() > 0 && me.updateScrollHeight()
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
/**
|
104
|
+
*
|
105
|
+
*/
|
106
|
+
updateScrollHeight() {
|
107
|
+
let me = this,
|
108
|
+
countRecords = me.store.getCount(),
|
109
|
+
{rowHeight} = me;
|
110
|
+
|
111
|
+
if (countRecords > 0 && rowHeight > 0) {
|
112
|
+
me.vdom.cn[0].height = `${(countRecords + 1) * rowHeight}px`;
|
113
|
+
me.update()
|
114
|
+
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
export default Neo.setupClass(GridScrollbar);
|
package/src/grid/View.mjs
CHANGED
@@ -0,0 +1,90 @@
|
|
1
|
+
import Base from './Base.mjs';
|
2
|
+
import DomAccess from '../DomAccess.mjs';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Syncs the scroll state of 2 DOM nodes
|
6
|
+
* @class Neo.main.addon.ScrollSync
|
7
|
+
* @extends Neo.main.addon.Base
|
8
|
+
*/
|
9
|
+
class ScrollSync extends Base {
|
10
|
+
static config = {
|
11
|
+
/**
|
12
|
+
* @member {String} className='Neo.main.addon.ScrollSync'
|
13
|
+
* @protected
|
14
|
+
*/
|
15
|
+
className: 'Neo.main.addon.ScrollSync',
|
16
|
+
/**
|
17
|
+
* Remote method access for other workers
|
18
|
+
* @member {Object} remote
|
19
|
+
* @protected
|
20
|
+
*/
|
21
|
+
remote: {
|
22
|
+
app: [
|
23
|
+
'register',
|
24
|
+
'unregister'
|
25
|
+
]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* @param {String} fromId
|
31
|
+
* @param {String} toId
|
32
|
+
* @param {String} direction
|
33
|
+
*/
|
34
|
+
addScrollListener(fromId, toId, direction) {
|
35
|
+
DomAccess.getElement(fromId)?.addEventListener('scroll', this.onScroll.bind(this, toId, direction))
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @param {String} toId
|
40
|
+
* @param {String} direction
|
41
|
+
* @param {Event} event
|
42
|
+
*/
|
43
|
+
onScroll(toId, direction, event) {
|
44
|
+
let node = DomAccess.getElement(toId),
|
45
|
+
{scrollLeft, scrollTop} = event.target;
|
46
|
+
|
47
|
+
if (node) {
|
48
|
+
if (direction === 'both') {
|
49
|
+
node.scrollTo({
|
50
|
+
behavior: 'instant',
|
51
|
+
left : scrollLeft,
|
52
|
+
top : scrollTop
|
53
|
+
})
|
54
|
+
} else if (direction === 'horizontal') {
|
55
|
+
node.scrollLeft = scrollLeft
|
56
|
+
} else if (direction === 'vertical') {
|
57
|
+
node.scrollTop = scrollTop
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* @param {Object} data
|
64
|
+
* @param {String} direction='vertical' 'horizontal', 'vertical' or 'both'
|
65
|
+
* @param {String} fromId
|
66
|
+
* @param {String} id The owner id (e.g. component id)
|
67
|
+
* @param {String} toId
|
68
|
+
* @param {Boolean} twoWay=true Sync the target's scroll state back to the source node
|
69
|
+
*/
|
70
|
+
register({direction='vertical', fromId, id, toId, twoWay=true}) {
|
71
|
+
console.log('register', direction, fromId, id, toId, twoWay);
|
72
|
+
|
73
|
+
let me = this;
|
74
|
+
|
75
|
+
me.addScrollListener(fromId, toId, direction);
|
76
|
+
|
77
|
+
if (twoWay) {
|
78
|
+
me.addScrollListener(toId, fromId, direction)
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* @param {Object} data
|
84
|
+
*/
|
85
|
+
unregister(data) {
|
86
|
+
console.log('unregister', data)
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
export default Neo.setupClass(ScrollSync);
|
package/src/worker/App.mjs
CHANGED
@@ -242,6 +242,23 @@ class App extends Base {
|
|
242
242
|
})
|
243
243
|
}
|
244
244
|
|
245
|
+
/**
|
246
|
+
* Convenience shortcut to lazy-load main thread addons, in case they are not imported yet
|
247
|
+
* @param {String} name
|
248
|
+
* @param {Number} windowId
|
249
|
+
* @returns {Promise<Neo.main.addon.Base>} The namespace of the addon to use via remote method access
|
250
|
+
*/
|
251
|
+
async getAddon(name, windowId) {
|
252
|
+
let addon = Neo.main?.addon?.[name];
|
253
|
+
|
254
|
+
if (!addon) {
|
255
|
+
await Neo.Main.importAddon({name, windowId});
|
256
|
+
addon = Neo.main.addon[name]
|
257
|
+
}
|
258
|
+
|
259
|
+
return addon
|
260
|
+
}
|
261
|
+
|
245
262
|
/**
|
246
263
|
* Get configs of any app realm based Neo instance from main
|
247
264
|
* @param {Object} data
|