neo.mjs 8.16.0 → 8.17.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.16.0'
23
+ * @member {String} version='8.17.1'
24
24
  */
25
- version: '8.16.0'
25
+ version: '8.17.1'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2025-02-06",
19
+ "datePublished": "2025-02-07",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -107,7 +107,7 @@ class FooterContainer extends Container {
107
107
  }, {
108
108
  module: Component,
109
109
  cls : ['neo-version'],
110
- html : 'v8.16.0'
110
+ html : 'v8.17.1'
111
111
  }]
112
112
  }],
113
113
  /**
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='8.16.0'
23
+ * @member {String} version='8.17.1'
24
24
  */
25
- version: '8.16.0'
25
+ version: '8.17.1'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "8.16.0",
3
+ "version": "8.17.1",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -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-cell {
71
- align-items : center;
72
- background-color: var(--grid-container-cell-background-color);
73
- display : flex;
74
- padding : 2px 10px 2px;
75
- width : fit-content;
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-scrollbar {
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,12 +54,25 @@
52
54
 
53
55
  .neo-grid-row {
54
56
  position: absolute;
57
+ width : 100%;
55
58
 
56
- &:hover {
57
- .neo-grid-cell {
58
- background-color: var(--grid-cell-background-color-hover);
59
- }
60
- }
59
+ &:last-child {
60
+ .neo-grid-cell {
61
+ border-bottom-width: 0 !important;
62
+ }
63
+ }
64
+
65
+ &.neo-even {
66
+ .neo-grid-cell {
67
+ background-color: var(--grid-container-cell-background-color-even);
68
+ }
69
+ }
70
+
71
+ &:hover {
72
+ .neo-grid-cell {
73
+ background-color: var(--grid-cell-background-color-hover);
74
+ }
75
+ }
61
76
 
62
77
  // selection.RowModel
63
78
  &.neo-selected {
@@ -68,7 +83,19 @@
68
83
  }
69
84
 
70
85
  .neo-grid-cell {
71
- position: absolute;
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 {
@@ -262,12 +262,12 @@ const DefaultConfig = {
262
262
  useVdomWorker: true,
263
263
  /**
264
264
  * buildScripts/injectPackageVersion.mjs will update this value
265
- * @default '8.16.0'
265
+ * @default '8.17.1'
266
266
  * @memberOf! module:Neo
267
267
  * @name config.version
268
268
  * @type String
269
269
  */
270
- version: '8.16.0'
270
+ version: '8.17.1'
271
271
  };
272
272
 
273
273
  Object.assign(DefaultConfig, {
@@ -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={cls:['neo-grid-wrapper'],cn:[{cn:[]}]}
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 = this,
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
- id : me.viewId,
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
- me.headerToolbar.scrollLeft = scrollLeft;
513
- me.view.scrollPosition = {x: scrollLeft, y: me.view.scrollPosition.y}
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
@@ -126,7 +126,7 @@ class GridView extends Component {
126
126
  _vdom:
127
127
  {tabIndex: '-1', cn: [
128
128
  {cn: []},
129
- {cls: 'neo-grid-scrollbar'}
129
+ {cls: 'neo-grid-stretcher'}
130
130
  ]}
131
131
  }
132
132
 
@@ -0,0 +1,88 @@
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
+ let me = this;
72
+
73
+ me.addScrollListener(fromId, toId, direction);
74
+
75
+ if (twoWay) {
76
+ me.addScrollListener(toId, fromId, direction)
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {Object} data
82
+ */
83
+ unregister(data) {
84
+ console.log('unregister', data)
85
+ }
86
+ }
87
+
88
+ export default Neo.setupClass(ScrollSync);
@@ -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