neo.mjs 3.0.1 → 3.0.2

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.
@@ -32,13 +32,15 @@ class List extends BaseList {
32
32
  * @returns {Object|Object[]|String} Either a config object to assign to the item, a vdom cn array or a html string
33
33
  */
34
34
  createItemContent(record, index) {
35
+ let id = this.getItemId(record.id);
36
+
35
37
  return [
36
- {cls: ['neo-list-item-content'], cn: [
37
- {tag: 'img', src: `../../../resources/examples/${record.image}`},
38
- {cls: ['neo-list-item-text'], cn: [
39
- {html: record.firstname},
40
- {cls: ['neo-lastname'], html: record.lastname},
41
- {cls: ['neo-is-online'], removeDom: !record.isOnline}
38
+ {cls: ['neo-list-item-content'], id: `${id}__content`, cn: [
39
+ {tag: 'img', id: `${id}__image`, src: `../../../resources/examples/${record.image}`},
40
+ {cls: ['neo-list-item-text'], id: `${id}__content_wrapper`, cn: [
41
+ {html: record.firstname, id: `${id}__firstname`},
42
+ {cls: ['neo-lastname'], id: `${id}__lastname`, html: record.lastname},
43
+ {cls: ['neo-is-online'], id: `${id}__isonline`, removeDom: !record.isOnline}
42
44
  ]}
43
45
  ]}
44
46
  ];
@@ -12,7 +12,8 @@ class MainContainer extends Viewport {
12
12
  static getConfig() {return {
13
13
  className: 'Neo.examples.list.animate.MainContainer',
14
14
  autoMount: true,
15
- layout : {ntype: 'vbox', align: 'stretch'}
15
+ layout : {ntype: 'vbox', align: 'stretch'},
16
+ sortBy : 'firstname'
16
17
  }}
17
18
 
18
19
  /**
@@ -36,11 +37,16 @@ class MainContainer extends Viewport {
36
37
  ntype: 'label',
37
38
  text : 'Sort by'
38
39
  }, {
39
- handler: me.changeSorting.bind(me, 'firstname'),
40
- text : 'Firstname'
40
+ field : 'firstname',
41
+ handler : me.changeSorting.bind(me, 'firstname'),
42
+ iconCls : 'fas fa-arrow-circle-up',
43
+ iconPosition: 'right',
44
+ text : 'Firstname'
41
45
  }, {
42
- handler: me.changeSorting.bind(me, 'lastname'),
43
- text : 'Lastname'
46
+ field : 'lastname',
47
+ handler : me.changeSorting.bind(me, 'lastname'),
48
+ iconPosition: 'right',
49
+ text : 'Lastname'
44
50
  }, {
45
51
  module : CheckBox,
46
52
  labelText : 'Is online',
@@ -64,13 +70,32 @@ class MainContainer extends Viewport {
64
70
  }
65
71
 
66
72
  /**
67
- * @param {String} field
73
+ * @param {String} property
68
74
  * @param {Object} data
69
75
  */
70
- changeSorting(field, data) {
71
- let store = this.down({module: List}).store;
76
+ changeSorting(property, data) {
77
+ let me = this,
78
+ buttonFirstName = me.down({field: 'firstname'}),
79
+ buttonLastName = me.down({field: 'lastname'}),
80
+ direction = 'ASC',
81
+ store = me.down({module: List}).store,
82
+ sorter = store.sorters[0],
83
+ button;
84
+
85
+ button = property === 'firstname' ? buttonFirstName : buttonLastName;
86
+
87
+ if (property === me.sortBy) {
88
+ direction = sorter.direction === 'ASC' ? 'DESC' : 'ASC';
89
+ }
90
+
91
+ button.iconCls = `fas fa-arrow-circle-${direction === 'ASC' ? 'up' : 'down'}`;
92
+
93
+ button = button === buttonFirstName ? buttonLastName : buttonFirstName;
94
+ button.iconCls = null;
95
+
96
+ sorter.set({direction, property});
72
97
 
73
- store.sorters[0].property = field;
98
+ me.sortBy = property;
74
99
  }
75
100
  }
76
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "repository": {
6
6
  "type": "git",
@@ -17,7 +17,7 @@
17
17
  border : 1px solid #1c60a0;
18
18
  border-radius : 6px;
19
19
  opacity : .9;
20
- transition : transform .5s ease-in-out;
20
+ transition : opacity .5s ease-in-out, transform .5s ease-in-out;
21
21
  }
22
22
 
23
23
  .neo-list-item-content {
@@ -469,11 +469,13 @@ class Base extends CoreBase {
469
469
  }
470
470
 
471
471
  /**
472
+ *
473
+ * @param {Object[]} items=this._items
474
+ * @param {Boolean} silent=false
472
475
  * @protected
473
476
  */
474
- doSort() {
477
+ doSort(items=this._items, silent=false) {
475
478
  let me = this,
476
- items = me._items,
477
479
  previousItems = [...items],
478
480
  sorters = me.sorters,
479
481
  sortDirections = me.sortDirections,
@@ -558,7 +560,7 @@ class Base extends CoreBase {
558
560
 
559
561
  me[isSorted] = countSorters > 0;
560
562
 
561
- if (me[updatingIndex] === 0) {
563
+ if (!silent && me[updatingIndex] === 0) {
562
564
  me.fire('sort', {
563
565
  items: me._items,
564
566
  previousItems,
@@ -607,6 +609,8 @@ class Base extends CoreBase {
607
609
  i = 0,
608
610
  countItems = items.length,
609
611
  filteredItems = [],
612
+ needsSorting = false,
613
+ oldItems = [...me._items],
610
614
  config, isIncluded, item, j, tmpItems;
611
615
 
612
616
  for (; i < countAllFilters; i++) {
@@ -616,6 +620,10 @@ class Base extends CoreBase {
616
620
  }
617
621
 
618
622
  if (countFilters === 0 && me.allItems) {
623
+ if (me.sorters.length > 0) {
624
+ needsSorting = true;
625
+ }
626
+
619
627
  me.clear();
620
628
 
621
629
  me.items = [...me.allItems._items];
@@ -682,7 +690,16 @@ class Base extends CoreBase {
682
690
 
683
691
  me[isFiltered] = countFilters !== 0;
684
692
 
685
- me.fire('filter', me);
693
+ if (needsSorting) {
694
+ me.doSort(me.items, true);
695
+ }
696
+
697
+ me.fire('filter', {
698
+ isFiltered: me[isFiltered],
699
+ items : me.items,
700
+ oldItems,
701
+ scope : me
702
+ });
686
703
  }
687
704
 
688
705
  /**
@@ -107,6 +107,10 @@ class Observable extends Base {
107
107
  for (i = 0; i < len; i++) {
108
108
  eventConfig = events[i];
109
109
 
110
+ if (!Neo.isFunction(eventConfig.fn)) {
111
+ eventConfig.fn = eventConfig.scope[eventConfig.fn];
112
+ }
113
+
110
114
  eventConfig.fn.apply(eventConfig.scope || me, eventConfig.data ? args.concat(eventConfig.data) : args);
111
115
  }
112
116
  }
package/src/list/Base.mjs CHANGED
@@ -191,9 +191,9 @@ class Base extends Component {
191
191
  let me = this;
192
192
 
193
193
  value?.on({
194
- filter : me.onStoreFilter,
195
- load : me.onStoreLoad,
196
- recordChange: me.onStoreRecordChange,
194
+ filter : 'onStoreFilter',
195
+ load : 'onStoreLoad',
196
+ recordChange: 'onStoreRecordChange',
197
197
  scope : me
198
198
  });
199
199
 
@@ -39,7 +39,12 @@ class Animate extends Base {
39
39
  * Read only
40
40
  * @member {Number|null} rows=null
41
41
  */
42
- rows: null
42
+ rows: null,
43
+ /**
44
+ * Time in ms. Please ensure to match the CSS based value, in case you change the default.
45
+ * @member {Number} transitionDuration=500
46
+ */
47
+ transitionDuration: 500
43
48
  }}
44
49
 
45
50
  /**
@@ -48,14 +53,16 @@ class Animate extends Base {
48
53
  construct(config) {
49
54
  super.construct(config);
50
55
 
51
- let me = this;
56
+ let me = this,
57
+ owner = me.owner;
52
58
 
53
59
  me.adjustCreateItem();
54
60
 
55
- me.owner.store.on({
56
- filter: me.onFilter,
57
- sort : me.onSort,
58
- scope : me
61
+ owner.onStoreFilter = me.onStoreFilter.bind(me);
62
+
63
+ owner.store.on({
64
+ sort : me.onSort,
65
+ scope: me
59
66
  });
60
67
  }
61
68
 
@@ -77,27 +84,19 @@ class Animate extends Base {
77
84
  * @returns {Object}
78
85
  */
79
86
  createItem(me, record, index) {
80
- let item = me.ownerCreateItem(record, index),
81
- itemHeight = me.itemHeight,
82
- itemWidth = me.itemWidth,
83
- margin = me.itemMargin,
84
- style = item.style || {},
85
- column, row, x, y;
87
+ let item = me.ownerCreateItem(record, index),
88
+ position = me.getItemPosition(record, index),
89
+ style = item.style || {};
86
90
 
87
91
  if (!me.ownerRect) {
88
92
  return null;
89
93
  }
90
94
 
91
- column = index % me.columns;
92
- row = Math.floor(index / me.columns);
93
- x = column * (margin + itemWidth) + margin;
94
- y = row * (margin + itemHeight) + margin;
95
-
96
95
  Object.assign(style, {
97
- height : `${itemHeight}px`,
96
+ height : `${me.itemHeight}px`,
98
97
  position : 'absolute',
99
- transform: `translate(${x}px, ${y}px)`,
100
- width : `${itemWidth}px`
98
+ transform: `translate(${position.x}px, ${position.y}px)`,
99
+ width : `${me.itemWidth}px`
101
100
  });
102
101
 
103
102
  item.style = style;
@@ -106,10 +105,20 @@ class Animate extends Base {
106
105
  }
107
106
 
108
107
  /**
109
- * @param {Object} data
108
+ *
109
+ * @param {Object} record
110
+ * @param {Number} index
111
+ * @returns {{x: Number, y: Number}}
110
112
  */
111
- onFilter(data) {
112
- console.log('onFilter', data);
113
+ getItemPosition(record, index) {
114
+ let me = this,
115
+ column = index % me.columns,
116
+ margin = me.itemMargin,
117
+ row = Math.floor(index / me.columns),
118
+ x = column * (margin + me.itemWidth) + margin,
119
+ y = row * (margin + me.itemHeight) + margin;
120
+
121
+ return {x, y};
113
122
  }
114
123
 
115
124
  /**
@@ -167,6 +176,76 @@ class Animate extends Base {
167
176
  }
168
177
  }
169
178
  }
179
+
180
+ /**
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
186
+ */
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
+ });
241
+
242
+ owner.vdom = vdom;
243
+
244
+ setTimeout(() => {
245
+ owner.createItems();
246
+ }, me.transitionDuration);
247
+ }, 50);
248
+ }
170
249
  }
171
250
 
172
251
  Neo.applyClassConfig(Animate);