neo.mjs 3.0.3 → 3.0.4

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.
@@ -1,8 +1,10 @@
1
- import CheckBox from '../../../src/form/field/CheckBox.mjs';
2
- import List from './List.mjs';
3
- import MainStore from './MainStore.mjs';
4
- import Toolbar from '../../../src/container/Toolbar.mjs';
5
- import Viewport from '../../../src/container/Viewport.mjs';
1
+ import CheckBox from '../../../src/form/field/CheckBox.mjs';
2
+ import List from './List.mjs';
3
+ import MainStore from './MainStore.mjs';
4
+ import NumberField from '../../../src/form/field/Number.mjs';
5
+ import TextField from '../../../src/form/field/Text.mjs';
6
+ import Toolbar from '../../../src/container/Toolbar.mjs';
7
+ import Viewport from '../../../src/container/Viewport.mjs';
6
8
 
7
9
  /**
8
10
  * @class Neo.examples.list.animate.MainContainer
@@ -54,6 +56,27 @@ class MainContainer extends Viewport {
54
56
  listeners : {change: me.changeIsOnlineFilter.bind(me)},
55
57
  style : {marginLeft: '50px'}
56
58
  }]
59
+ }, {
60
+ module : TextField,
61
+ flex : 'none',
62
+ labelText : 'Search',
63
+ labelWidth: 60,
64
+ listeners : {change: me.changeNameFilter.bind(me)},
65
+ style : {marginLeft: '10px'},
66
+ width : 262
67
+ }, {
68
+ module : NumberField,
69
+ clearToOriginalValue: true,
70
+ flex : 'none',
71
+ labelText : 'Transition Duration',
72
+ labelWidth : 150,
73
+ listeners : {change: me.changeTransitionDuration.bind(me)},
74
+ maxValue : 5000,
75
+ minValue : 100,
76
+ stepSize : 100,
77
+ style : {marginLeft: '10px'},
78
+ value : 3000,
79
+ width : 262
57
80
  }, {
58
81
  module: List,
59
82
  store : MainStore
@@ -69,6 +92,15 @@ class MainContainer extends Viewport {
69
92
  store.getFilter('isOnline').disabled = !data.value;
70
93
  }
71
94
 
95
+ /**
96
+ * @param {Object} data
97
+ */
98
+ changeNameFilter(data) {
99
+ let store = this.down({module: List}).store;
100
+
101
+ store.getFilter('name').value = data.value;
102
+ }
103
+
72
104
  /**
73
105
  * @param {String} property
74
106
  * @param {Object} data
@@ -97,6 +129,13 @@ class MainContainer extends Viewport {
97
129
 
98
130
  me.sortBy = property;
99
131
  }
132
+
133
+ /**
134
+ * @param {Object} data
135
+ */
136
+ changeTransitionDuration(data) {
137
+ this.down({module: List}).getPlugin('animate').transitionDuration = data.value;
138
+ }
100
139
  }
101
140
 
102
141
  Neo.applyClassConfig(MainContainer);
@@ -17,6 +17,23 @@ class MainStore extends Store {
17
17
  disabled : true,
18
18
  property : 'isOnline',
19
19
  value : true
20
+ }, {
21
+ property : 'name',
22
+ value : null,
23
+
24
+ filterBy: opts => {
25
+ let record = opts.item,
26
+ value = opts.value;
27
+
28
+ if (value) {
29
+ return !(
30
+ record.firstname.toLowerCase().includes(value) ||
31
+ record.lastname .toLowerCase().includes(value)
32
+ );
33
+ }
34
+
35
+ return false;
36
+ }
20
37
  }],
21
38
 
22
39
  sorters: [{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "3.0.3",
3
+ "version": "3.0.4",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,6 @@
17
17
  border : 1px solid #1c60a0;
18
18
  border-radius : 6px;
19
19
  opacity : .9;
20
- transition : opacity .5s ease-in-out, transform .5s ease-in-out;
21
20
  }
22
21
 
23
22
  .neo-list-item-content {
@@ -40,7 +40,7 @@ class Filter extends Base {
40
40
  */
41
41
  disabled_: false,
42
42
  /**
43
- * Provide a custom filtering function, has a higher priority than property, operator & value
43
+ * Provide a custom filtering function which has a higher priority than property, operator & value
44
44
  * @member {Function|null} filterBy_=null
45
45
  */
46
46
  filterBy_: null,
@@ -173,7 +173,12 @@ class Filter extends Base {
173
173
  }
174
174
 
175
175
  if (me._filterBy) {
176
- return me.filterBy.call(me.scope || me, item, filteredItems, allItems);
176
+ return me.filterBy.call(me.scope || me, {
177
+ allItems,
178
+ filteredItems,
179
+ item,
180
+ value: me._value
181
+ });
177
182
  }
178
183
 
179
184
  if (me.includeEmptyValues && (me._value === null || Neo.isEmpty(me._value))) {
package/src/list/Base.mjs CHANGED
@@ -132,7 +132,7 @@ class Base extends Component {
132
132
  plugins.push({
133
133
  module : module.default,
134
134
  appName: me.appName,
135
- flag : 'animate',
135
+ id : 'animate',
136
136
  ...me.pluginAnimateConfig
137
137
  });
138
138
 
@@ -1,4 +1,6 @@
1
- import Base from '../../plugin/Base.mjs';
1
+ import Base from '../../plugin/Base.mjs';
2
+ import CssUtil from '../../util/Css.mjs';
3
+ import VdomUtil from '../../util/VDom.mjs';
2
4
 
3
5
  /**
4
6
  * @class Neo.list.plugin.Animate
@@ -42,9 +44,14 @@ class Animate extends Base {
42
44
  rows: null,
43
45
  /**
44
46
  * Time in ms. Please ensure to match the CSS based value, in case you change the default.
45
- * @member {Number} transitionDuration=500
47
+ * @member {Number} transitionDuration_=500
46
48
  */
47
- transitionDuration: 500
49
+ transitionDuration_: 2000,
50
+ /**
51
+ * The id of the setTimeout() call which gets triggered after a transition is done.
52
+ * @member {Number|null} transitionTimeoutId=null
53
+ */
54
+ transitionTimeoutId: null
48
55
  }}
49
56
 
50
57
  /**
@@ -61,7 +68,7 @@ class Animate extends Base {
61
68
  owner.onStoreFilter = me.onStoreFilter.bind(me);
62
69
 
63
70
  owner.store.on({
64
- sort : me.onSort,
71
+ sort : me.onStoreSort,
65
72
  scope: me
66
73
  });
67
74
  }
@@ -77,6 +84,25 @@ class Animate extends Base {
77
84
  owner.createItem = me.createItem.bind(owner, me);
78
85
  }
79
86
 
87
+ /**
88
+ * Triggered after the transitionDuration config got changed.
89
+ *
90
+ * We do not want to apply the style to each list item itself,
91
+ * so we are using Neo.util.Css
92
+ * @param {Boolean} value
93
+ * @param {Boolean} oldValue
94
+ * @protected
95
+ */
96
+ afterSetTransitionDuration(value, oldValue) {
97
+ Neo.isNumber(oldValue) && CssUtil.deleteRules(`#${this.owner.id} .neo-list-item`);
98
+
99
+ CssUtil.insertRules([
100
+ `#${this.owner.id} .neo-list-item {`,
101
+ `transition: opacity ${value}ms ease-in-out, transform ${value}ms ease-in-out`,
102
+ '}'
103
+ ].join(''));
104
+ }
105
+
80
106
  /**
81
107
  * @param {Neo.list.plugin.Animate} me
82
108
  * @param {Object} record
@@ -121,6 +147,24 @@ class Animate extends Base {
121
147
  return {x, y};
122
148
  }
123
149
 
150
+ /**
151
+ *
152
+ * @param {Object} obj
153
+ * @param {String[]} map
154
+ * @param {Boolean} intercept
155
+ * @returns {Number}
156
+ */
157
+ getItemIndex(obj, map, intercept) {
158
+ if (!intercept) {
159
+ return obj.index;
160
+ }
161
+
162
+ let owner = this.owner,
163
+ key = owner.store.keyProperty;
164
+
165
+ return map.indexOf(owner.getItemId(obj.record[key]));
166
+ }
167
+
124
168
  /**
125
169
  *
126
170
  */
@@ -136,13 +180,126 @@ class Animate extends Base {
136
180
  });
137
181
  }
138
182
 
183
+ /**
184
+ * @param {Object} data
185
+ * @param {Boolean} data.isFiltered
186
+ * @param {Object[]} data.items
187
+ * @param {Object[]} data.oldItems
188
+ * @param {Neo.data.Store} data.scope
189
+ */
190
+ onStoreFilter(data) {
191
+ let me = this,
192
+ owner = me.owner,
193
+ key = owner.store.keyProperty,
194
+ hasAddedItems = false,
195
+ addedItems = [],
196
+ movedItems = [],
197
+ removedItems = [],
198
+ transitionTimeoutId = me.transitionTimeoutId,
199
+ intercept = !!transitionTimeoutId,
200
+ vdom = owner.vdom,
201
+ index, item, map, position, vdomIndex;
202
+
203
+ if (transitionTimeoutId) {
204
+ clearTimeout(transitionTimeoutId);
205
+ me.transitionTimeoutId = null;
206
+ }
207
+
208
+ map = intercept ? vdom.cn.map(e => e.id) : [];
209
+
210
+ data.items.forEach((record, index) => {
211
+ item = {index, record};
212
+
213
+ if (!data.oldItems.includes(record)) {
214
+ // flag items which are still inside the DOM (running remove OP)
215
+ if (intercept && map.includes(owner.getItemId(record[key]))) {
216
+ item.reAdded = true;
217
+ }
218
+
219
+ addedItems.push(item);
220
+ } else {
221
+ movedItems.push(item);
222
+ }
223
+ });
224
+
225
+ data.oldItems.forEach((record, index) => {
226
+ if (!data.items.includes(record)) {
227
+ removedItems.push({index, record});
228
+ }
229
+ });
230
+
231
+ addedItems.forEach(obj => {
232
+ if (!obj.reAdded) {
233
+ index = me.getItemIndex(obj, map, intercept);
234
+
235
+ if (index > -1) {
236
+ hasAddedItems = true;
237
+
238
+ vdom.cn.splice(index, 0, me.createItem(me, obj.record, obj.index));
239
+
240
+ obj.item = vdom.cn[index];
241
+ obj.item.style.opacity = 0;
242
+ }
243
+ }
244
+ });
245
+
246
+ if (hasAddedItems) {
247
+ owner.vdom = vdom;
248
+ }
249
+
250
+ // ensure to get into the next animation frame
251
+ setTimeout(() => {
252
+ // get the latest version of the vdom, since this is a delayed callback
253
+ vdom = owner.vdom;
254
+
255
+ // new items are already added into the vdom, while old items are not yet removed
256
+ // => we need a map to ensure getting the correct index
257
+ map = vdom.cn.map(e => e.id);
258
+
259
+ addedItems.forEach(obj => {
260
+ index = me.getItemIndex(obj, map, intercept);
261
+
262
+ if (index > -1) {
263
+ // we can change the opacity for re-added items too => the vdom engine will ignore this
264
+ vdom.cn[index].style.opacity = 1;
265
+ }
266
+ });
267
+
268
+ movedItems.forEach(obj => {
269
+ index = me.getItemIndex(obj, map, true); // honor removed items, even without interceptions
270
+
271
+ if (index > -1) {
272
+ position = me.getItemPosition(obj.record, obj.index);
273
+
274
+ Object.assign(vdom.cn[index].style, {
275
+ opacity : 1,
276
+ transform: `translate(${position.x}px, ${position.y}px)`
277
+ });
278
+ }
279
+ });
280
+
281
+ removedItems.forEach(obj => {
282
+ index = me.getItemIndex(obj, map, intercept);
283
+
284
+ if (index > -1) {
285
+ obj.item = vdom.cn[index];
286
+ obj.item.style.opacity = 0;
287
+ }
288
+ });
289
+
290
+ owner.vdom = vdom;
291
+
292
+ me.triggerTransitionCallback();
293
+ }, 50);
294
+ }
295
+
139
296
  /**
140
297
  * @param {Object} data
141
298
  * @param {Object[]} data.items
142
299
  * @param {Object[]} data.previousItems
143
300
  * @param {Neo.data.Store} data.scope
144
301
  */
145
- onSort(data) {
302
+ onStoreSort(data) {
146
303
  let me = this,
147
304
  hasChange = false,
148
305
  keyProperty = data.scope.keyProperty,
@@ -178,73 +335,16 @@ class Animate extends Base {
178
335
  }
179
336
 
180
337
  /**
181
- * @param {Object} data
182
- * @param {Boolean} data.isFiltered
183
- * @param {Object[]} data.items
184
- * @param {Object[]} data.oldItems
185
- * @param {Neo.data.Store} data.scope
338
+ *
186
339
  */
187
- onStoreFilter(data) {
188
- let me = this,
189
- owner = me.owner,
190
- addedItems = [],
191
- movedItems = [],
192
- removedItems = [],
193
- vdom = owner.vdom,
194
- index, map, position;
195
-
196
- data.items.forEach((record, index) => {
197
- if (!data.oldItems.includes(record)) {
198
- addedItems.push({index, record});
199
- } else {
200
- movedItems.push({index, record});
201
- }
202
- });
203
-
204
- data.oldItems.forEach((record, index) => {
205
- if (!data.items.includes(record)) {
206
- removedItems.push({index, record});
207
- }
208
- });
209
-
210
- addedItems.forEach(obj => {
211
- vdom.cn.splice(obj.index, 0, me.createItem(me, obj.record, obj.index));
212
-
213
- obj.item = vdom.cn[obj.index];
214
- obj.item.style.opacity = 0;
215
- });
216
-
217
- owner.vdom = vdom;
218
-
219
- // ensure to get into the next animation frame
220
- setTimeout(() => {
221
- vdom = owner.vdom;
222
-
223
- addedItems.forEach(obj => {
224
- vdom.cn[obj.index].style.opacity = 1;
225
- });
226
-
227
- // new items are already added into the vdom, while old items are not yet removed
228
- // => we need a map to ensure getting the correct index
229
- map = vdom.cn.map(e => e.id);
230
-
231
- movedItems.forEach(obj => {
232
- index = map.indexOf(owner.getItemId(obj.record[owner.store.keyProperty]));
233
- position = me.getItemPosition(obj.record, obj.index);
234
- vdom.cn[index].style.transform = `translate(${position.x}px, ${position.y}px)`;
235
- });
236
-
237
- removedItems.forEach(obj => {
238
- obj.item = vdom.cn[obj.index];
239
- obj.item.style.opacity = 0;
240
- });
340
+ triggerTransitionCallback() {
341
+ let me = this;
241
342
 
242
- owner.vdom = vdom;
343
+ me.transitionTimeoutId = setTimeout(() => {
344
+ me.transitionTimeoutId = null;
243
345
 
244
- setTimeout(() => {
245
- owner.createItems();
246
- }, me.transitionDuration);
247
- }, 50);
346
+ me.owner.createItems();
347
+ }, me.transitionDuration);
248
348
  }
249
349
  }
250
350
 
@@ -8,6 +8,12 @@ import Base from '../../core/Base.mjs';
8
8
  * @singleton
9
9
  */
10
10
  class Stylesheet extends Base {
11
+ /**
12
+ * @member {String} dynamicStyleSheetId='neo-dynamic-stylesheet'
13
+ * @protected
14
+ */
15
+ dynamicStyleSheetId = 'neo-dynamic-stylesheet';
16
+
11
17
  static getConfig() {return {
12
18
  /**
13
19
  * @member {String} className='Neo.main.addon.Stylesheet'
@@ -23,6 +29,7 @@ class Stylesheet extends Base {
23
29
  app: [
24
30
  'addThemeFiles',
25
31
  'createStyleSheet',
32
+ 'deleteCssRules',
26
33
  'insertCssRules',
27
34
  'swapStyleSheet'
28
35
  ]
@@ -137,6 +144,32 @@ class Stylesheet extends Base {
137
144
  document.head.appendChild(link);
138
145
  }
139
146
 
147
+ /**
148
+ * @param {Object} data
149
+ * @param {Array} data.rules
150
+ * @protected
151
+ */
152
+ deleteCssRules(data) {
153
+ let styleEl = document.getElementById(this.dynamicStyleSheetId),
154
+ styleSheet = styleEl.sheet,
155
+ cssRules = styleSheet.cssRules,
156
+ i = 0,
157
+ len = data.rules.length,
158
+ j, rulesLen;
159
+
160
+ for (; i < len; i++) {
161
+ j = 0;
162
+ rulesLen = cssRules.length;
163
+
164
+ for (; j < rulesLen; j++) {
165
+ if (cssRules[j].selectorText === data.rules[i]) {
166
+ styleSheet.deleteRule(j);
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ }
172
+
140
173
  /**
141
174
  * @param {String} token
142
175
  * @returns {Boolean}
@@ -162,7 +195,7 @@ class Stylesheet extends Base {
162
195
  * @protected
163
196
  */
164
197
  insertCssRules(data) {
165
- let styleEl = document.getElementById('neoDynamicStyleSheet'),
198
+ let styleEl = document.getElementById(this.dynamicStyleSheetId),
166
199
  i = 0,
167
200
  len = data.rules.length,
168
201
  styleSheet;
@@ -170,7 +203,7 @@ class Stylesheet extends Base {
170
203
  if (!styleEl) {
171
204
  styleEl = document.createElement('style');
172
205
 
173
- styleEl.id = 'neoDynamicStyleSheet';
206
+ styleEl.id = this.dynamicStyleSheetId;
174
207
  document.head.appendChild(styleEl);
175
208
  }
176
209
 
@@ -33,7 +33,11 @@ class Base extends CoreBase {
33
33
 
34
34
  let me = this;
35
35
 
36
- me.owner.on('mounted', me.onOwnerMounted, me);
36
+ if (me.owner.mounted) {
37
+ me.onOwnerMounted();
38
+ } else {
39
+ me.owner.on('mounted', me.onOwnerMounted, me);
40
+ }
37
41
  }
38
42
 
39
43
  /**
package/src/util/Css.mjs CHANGED
@@ -14,15 +14,29 @@ class Css extends Base {
14
14
  }}
15
15
 
16
16
  /**
17
- * @param {Array} rules
17
+ * Pass the selectorText of the rules which you want to remove
18
+ * @param {String[]|String} rules
19
+ */
20
+ static deleteRules(rules) {
21
+ if (!Array.isArray(rules)) {
22
+ rules = [rules];
23
+ }
24
+
25
+ Neo.main.addon.Stylesheet.deleteCssRules({
26
+ rules: rules
27
+ });
28
+ }
29
+
30
+ /**
31
+ * @param {String[]|String} rules
18
32
  */
19
33
  static insertRules(rules) {
34
+ if (!Array.isArray(rules)) {
35
+ rules = [rules];
36
+ }
37
+
20
38
  Neo.main.addon.Stylesheet.insertCssRules({
21
39
  rules: rules
22
- }).then(function(data) {
23
- // console.log('inserted CSS rules', data);
24
- }).catch(function(err) {
25
- console.log('App: Got error attempting to insert CSS rules', err, rules);
26
40
  });
27
41
  }
28
42
  }