neo.mjs 10.0.0-beta.3 → 10.0.0-beta.5
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/.github/RELEASE_NOTES/v10.0.0-beta.4.md +41 -0
- package/ServiceWorker.mjs +2 -2
- package/apps/portal/index.html +1 -1
- package/apps/portal/view/ViewportController.mjs +1 -1
- package/apps/portal/view/home/FooterContainer.mjs +1 -1
- package/apps/portal/view/learn/MainContainerController.mjs +6 -6
- package/examples/button/effect/MainContainer.mjs +207 -0
- package/examples/button/effect/app.mjs +6 -0
- package/examples/button/effect/index.html +11 -0
- package/examples/button/effect/neo-config.json +6 -0
- package/learn/guides/{Collections.md → datahandling/Collections.md} +6 -6
- package/learn/guides/datahandling/Grids.md +621 -0
- package/learn/guides/{Records.md → datahandling/Records.md} +4 -3
- package/learn/guides/{StateProviders.md → datahandling/StateProviders.md} +146 -1
- package/learn/guides/fundamentals/DeclarativeVDOMWithEffects.md +166 -0
- package/learn/guides/fundamentals/ExtendingNeoClasses.md +359 -0
- package/learn/guides/{Layouts.md → uibuildingblocks/Layouts.md} +40 -38
- package/learn/guides/{form_fields → userinteraction/form_fields}/ComboBox.md +3 -3
- package/learn/tree.json +64 -57
- package/package.json +3 -3
- package/src/DefaultConfig.mjs +2 -2
- package/src/Neo.mjs +244 -88
- package/src/button/Effect.mjs +435 -0
- package/src/collection/Base.mjs +35 -3
- package/src/component/Base.mjs +72 -61
- package/src/container/Base.mjs +28 -24
- package/src/controller/Base.mjs +87 -63
- package/src/core/Base.mjs +207 -33
- package/src/core/Compare.mjs +3 -13
- package/src/core/Config.mjs +230 -0
- package/src/core/ConfigSymbols.mjs +3 -0
- package/src/core/Effect.mjs +127 -0
- package/src/core/EffectBatchManager.mjs +68 -0
- package/src/core/EffectManager.mjs +38 -0
- package/src/core/Util.mjs +3 -18
- package/src/data/RecordFactory.mjs +22 -3
- package/src/grid/Container.mjs +8 -4
- package/src/grid/column/Component.mjs +1 -1
- package/src/state/Provider.mjs +343 -452
- package/src/state/createHierarchicalDataProxy.mjs +124 -0
- package/src/tab/header/EffectButton.mjs +75 -0
- package/src/util/Function.mjs +52 -5
- package/src/vdom/Helper.mjs +9 -10
- package/src/vdom/VNode.mjs +1 -1
- package/src/worker/App.mjs +0 -5
- package/test/siesta/siesta.js +32 -0
- package/test/siesta/tests/CollectionBase.mjs +10 -10
- package/test/siesta/tests/VdomHelper.mjs +22 -59
- package/test/siesta/tests/config/AfterSetConfig.mjs +100 -0
- package/test/siesta/tests/config/Basic.mjs +149 -0
- package/test/siesta/tests/config/CircularDependencies.mjs +166 -0
- package/test/siesta/tests/config/CustomFunctions.mjs +69 -0
- package/test/siesta/tests/config/Hierarchy.mjs +94 -0
- package/test/siesta/tests/config/MemoryLeak.mjs +92 -0
- package/test/siesta/tests/config/MultiLevelHierarchy.mjs +85 -0
- package/test/siesta/tests/core/Effect.mjs +131 -0
- package/test/siesta/tests/core/EffectBatching.mjs +322 -0
- package/test/siesta/tests/neo/MixinStaticConfig.mjs +138 -0
- package/test/siesta/tests/state/Provider.mjs +537 -0
- package/test/siesta/tests/state/createHierarchicalDataProxy.mjs +217 -0
- package/learn/guides/ExtendingNeoClasses.md +0 -331
- /package/learn/guides/{Tables.md → datahandling/Tables.md} +0 -0
- /package/learn/guides/{ApplicationBootstrap.md → fundamentals/ApplicationBootstrap.md} +0 -0
- /package/learn/guides/{ConfigSystemDeepDive.md → fundamentals/ConfigSystemDeepDive.md} +0 -0
- /package/learn/guides/{DeclarativeComponentTreesVsImperativeVdom.md → fundamentals/DeclarativeComponentTreesVsImperativeVdom.md} +0 -0
- /package/learn/guides/{InstanceLifecycle.md → fundamentals/InstanceLifecycle.md} +0 -0
- /package/learn/guides/{MainThreadAddons.md → fundamentals/MainThreadAddons.md} +0 -0
- /package/learn/guides/{Mixins.md → specificfeatures/Mixins.md} +0 -0
- /package/learn/guides/{MultiWindow.md → specificfeatures/MultiWindow.md} +0 -0
- /package/learn/guides/{PortalApp.md → specificfeatures/PortalApp.md} +0 -0
- /package/learn/guides/{ComponentsAndContainers.md → uibuildingblocks/ComponentsAndContainers.md} +0 -0
- /package/learn/guides/{CustomComponents.md → uibuildingblocks/CustomComponents.md} +0 -0
- /package/learn/guides/{WorkingWithVDom.md → uibuildingblocks/WorkingWithVDom.md} +0 -0
- /package/learn/guides/{Forms.md → userinteraction/Forms.md} +0 -0
- /package/learn/guides/{events → userinteraction/events}/CustomEvents.md +0 -0
- /package/learn/guides/{events → userinteraction/events}/DomEvents.md +0 -0
@@ -0,0 +1,435 @@
|
|
1
|
+
import Component from '../component/Base.mjs';
|
2
|
+
import Effect from '../core/Effect.mjs';
|
3
|
+
import NeoArray from '../util/Array.mjs';
|
4
|
+
|
5
|
+
/**
|
6
|
+
* @class Neo.button.Effect
|
7
|
+
* @extends Neo.component.Base
|
8
|
+
*/
|
9
|
+
class EffectButton extends Component {
|
10
|
+
/**
|
11
|
+
* Valid values for badgePosition
|
12
|
+
* @member {String[]} badgePositions=['bottom-left','bottom-right','top-left','top-right']
|
13
|
+
* @protected
|
14
|
+
* @static
|
15
|
+
*/
|
16
|
+
static badgePositions = ['bottom-left', 'bottom-right', 'top-left', 'top-right']
|
17
|
+
/**
|
18
|
+
* Valid values for iconPosition
|
19
|
+
* @member {String[]} iconPositions=['top','right','bottom','left']
|
20
|
+
* @protected
|
21
|
+
* @static
|
22
|
+
*/
|
23
|
+
static iconPositions = ['top', 'right', 'bottom', 'left']
|
24
|
+
|
25
|
+
static config = {
|
26
|
+
/**
|
27
|
+
* @member {String} className='Neo.button.Effect'
|
28
|
+
* @protected
|
29
|
+
*/
|
30
|
+
className: 'Neo.button.Effect',
|
31
|
+
/**
|
32
|
+
* @member {String} ntype='effect-button'
|
33
|
+
* @protected
|
34
|
+
*/
|
35
|
+
ntype: 'effect-button',
|
36
|
+
/**
|
37
|
+
* @member {String} badgePosition_='top-right'
|
38
|
+
*/
|
39
|
+
badgePosition_: 'top-right',
|
40
|
+
/**
|
41
|
+
* @member {String|null} badgeText_=null
|
42
|
+
*/
|
43
|
+
badgeText_: null,
|
44
|
+
/**
|
45
|
+
* @member {String[]} baseCls=['neo-button']
|
46
|
+
*/
|
47
|
+
baseCls: ['neo-button'],
|
48
|
+
/**
|
49
|
+
* @member {String[]} cls=[]
|
50
|
+
*/
|
51
|
+
cls: [],
|
52
|
+
/**
|
53
|
+
* false calls Neo.Main.setRoute()
|
54
|
+
* @member {Boolean} editRoute=true
|
55
|
+
*/
|
56
|
+
editRoute: true,
|
57
|
+
/**
|
58
|
+
* Shortcut for domListeners={click:handler}
|
59
|
+
* A string based value assumes that the handlerFn lives inside a controller.Component
|
60
|
+
* @member {Function|String|null} handler_=null
|
61
|
+
*/
|
62
|
+
handler_: null,
|
63
|
+
/**
|
64
|
+
* The scope (this pointer) inside the handler function.
|
65
|
+
* Points to the button instance by default.
|
66
|
+
* You can use 'this' as a string for convenience reasons
|
67
|
+
* @member {Object|String|null} handlerScope=null
|
68
|
+
*/
|
69
|
+
handlerScope: null,
|
70
|
+
/**
|
71
|
+
* The CSS class to use for an icon, e.g. 'fa fa-home'
|
72
|
+
* @member {String|null} [iconCls_=null]
|
73
|
+
*/
|
74
|
+
iconCls_: null,
|
75
|
+
/**
|
76
|
+
* The color to use for an icon, e.g. '#ff0000' [optional]
|
77
|
+
* @member {String|null} iconColor_=null
|
78
|
+
*/
|
79
|
+
iconColor_: null,
|
80
|
+
/**
|
81
|
+
* The position of the icon in case iconCls has a value.
|
82
|
+
* Valid values are: 'top', 'right', 'bottom', 'left'
|
83
|
+
* @member {String} iconPosition_='left'
|
84
|
+
*/
|
85
|
+
iconPosition_: 'left',
|
86
|
+
/**
|
87
|
+
* An array representing the configuration of the menu items.
|
88
|
+
*
|
89
|
+
* Or a configuration object which adds custom configuration to the menu to be
|
90
|
+
* created and includes an `items` property to define the menu items.
|
91
|
+
* @member {Object|Object[]|null} menu_=null
|
92
|
+
*/
|
93
|
+
menu_: null,
|
94
|
+
/**
|
95
|
+
* The pressed state of the Button
|
96
|
+
* @member {Boolean} pressed_=false
|
97
|
+
*/
|
98
|
+
pressed_: false,
|
99
|
+
/**
|
100
|
+
* Change the browser hash value on click.
|
101
|
+
* Use route for internal navigation and url for external links. Do not use both on the same instance.
|
102
|
+
* Transforms the button tag into an a tag [optional]
|
103
|
+
* @member {String|null} route_=null
|
104
|
+
*/
|
105
|
+
route_: null,
|
106
|
+
/**
|
107
|
+
* @member {String} tag='button'
|
108
|
+
*/
|
109
|
+
tag: 'button',
|
110
|
+
/**
|
111
|
+
* Transforms the button tag into an a tag [optional]
|
112
|
+
* @member {String|null} url_=null
|
113
|
+
*/
|
114
|
+
url_: null,
|
115
|
+
/**
|
116
|
+
* If url is set, applies the target attribute on the top level vdom node [optional]
|
117
|
+
* @member {String} urlTarget_='_blank'
|
118
|
+
*/
|
119
|
+
urlTarget_: '_blank',
|
120
|
+
/**
|
121
|
+
* True adds an expanding circle on click
|
122
|
+
* @member {Boolean} useRippleEffect_=true
|
123
|
+
*/
|
124
|
+
useRippleEffect_: true
|
125
|
+
}
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Time in ms for the ripple effect when clicking on the button.
|
129
|
+
* Only active if useRippleEffect is set to true.
|
130
|
+
* @member {Number} rippleEffectDuration=400
|
131
|
+
*/
|
132
|
+
rippleEffectDuration = 400
|
133
|
+
/**
|
134
|
+
* Internal flag to store the last setTimeout() id for ripple effect remove node callbacks
|
135
|
+
* @member {Number} #rippleTimeoutId=null
|
136
|
+
* @private
|
137
|
+
*/
|
138
|
+
#rippleTimeoutId = null
|
139
|
+
|
140
|
+
/**
|
141
|
+
* @param {Object} config
|
142
|
+
*/
|
143
|
+
construct(config) {
|
144
|
+
super.construct(config);
|
145
|
+
|
146
|
+
let me = this;
|
147
|
+
|
148
|
+
me.addDomListeners({
|
149
|
+
click: me.onClick,
|
150
|
+
scope: me
|
151
|
+
});
|
152
|
+
|
153
|
+
me.createVdomEffect()
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Final. Should not be overridden.
|
158
|
+
* This is the core reactive effect.
|
159
|
+
* @returns {Neo.core.Effect}
|
160
|
+
* @protected
|
161
|
+
*/
|
162
|
+
createVdomEffect() {
|
163
|
+
return new Effect({fn: () => {
|
164
|
+
// The effect's only job is to get the config and trigger an update.
|
165
|
+
this._vdom = this.getVdomConfig();
|
166
|
+
this.update();
|
167
|
+
}});
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* Builds the array of child nodes (the 'cn' property).
|
172
|
+
* Subclasses can override this to add or remove children.
|
173
|
+
* @returns {Object[]}
|
174
|
+
* @protected
|
175
|
+
*/
|
176
|
+
getVdomChildren() {
|
177
|
+
return [
|
178
|
+
// iconNode
|
179
|
+
{tag: 'span', cls: ['neo-button-glyph', ...this._iconCls || []], removeDom: !this.iconCls, style: {color: this.iconColor || null}},
|
180
|
+
// textNode
|
181
|
+
{tag: 'span', cls: ['neo-button-text'], id: `${this.id}__text`, removeDom: !this.text, text: this.text},
|
182
|
+
// badgeNode
|
183
|
+
{cls: ['neo-button-badge', 'neo-' + this.badgePosition], removeDom: !this.badgeText, text: this.badgeText},
|
184
|
+
// rippleWrapper
|
185
|
+
{cls: ['neo-button-ripple-wrapper'], removeDom: !this.useRippleEffect, cn: [{cls: ['neo-button-ripple']}]}
|
186
|
+
];
|
187
|
+
}
|
188
|
+
|
189
|
+
/**
|
190
|
+
* Builds the array of CSS classes for the root element.
|
191
|
+
* @returns {String[]}
|
192
|
+
* @protected
|
193
|
+
*/
|
194
|
+
getVdomCls() {
|
195
|
+
let vdomCls = [...this.baseCls, ...this.cls];
|
196
|
+
|
197
|
+
NeoArray.toggle(vdomCls, 'no-text', !this.text);
|
198
|
+
NeoArray.toggle(vdomCls, 'pressed', this.pressed);
|
199
|
+
vdomCls.push('icon-' + this.iconPosition);
|
200
|
+
|
201
|
+
return vdomCls;
|
202
|
+
}
|
203
|
+
|
204
|
+
/**
|
205
|
+
* Builds the top-level VDOM object.
|
206
|
+
* Subclasses can override this to add or modify root properties.
|
207
|
+
* @returns {Object}
|
208
|
+
* @protected
|
209
|
+
*/
|
210
|
+
getVdomConfig() {
|
211
|
+
let me = this,
|
212
|
+
link = !me.editRoute && me.route || me.url,
|
213
|
+
tag = link ? 'a' : 'button';
|
214
|
+
|
215
|
+
return {
|
216
|
+
tag,
|
217
|
+
cls : this.getVdomCls(),
|
218
|
+
href : link ? (link.startsWith('#') ? link : '#' + link) : null,
|
219
|
+
id : me.id,
|
220
|
+
style : me.style,
|
221
|
+
target : me.url ? me.urlTarget : null,
|
222
|
+
type : tag === 'button' ? 'button' : null,
|
223
|
+
cn : this.getVdomChildren()
|
224
|
+
};
|
225
|
+
}
|
226
|
+
|
227
|
+
/**
|
228
|
+
* Triggered after the menu config got changed
|
229
|
+
* @param {Object|Object[]|null} value
|
230
|
+
* @param {Object|Object[]|null} oldValue
|
231
|
+
* @protected
|
232
|
+
*/
|
233
|
+
afterSetMenu(value, oldValue) {
|
234
|
+
if (value) {
|
235
|
+
import('../menu/List.mjs').then(module => {
|
236
|
+
let me = this,
|
237
|
+
isArray = Array.isArray(value),
|
238
|
+
items = isArray ? value : value.items,
|
239
|
+
menuConfig = isArray ? {} : value,
|
240
|
+
stateProvider = me.getStateProvider(),
|
241
|
+
{appName, theme, windowId} = me,
|
242
|
+
|
243
|
+
config = Neo.merge({
|
244
|
+
module : module.default,
|
245
|
+
align : {edgeAlign: 't0-b0', target: me.id},
|
246
|
+
appName,
|
247
|
+
displayField : 'text',
|
248
|
+
floating : true,
|
249
|
+
hidden : true,
|
250
|
+
parentComponent: me,
|
251
|
+
theme,
|
252
|
+
windowId
|
253
|
+
}, menuConfig);
|
254
|
+
|
255
|
+
if (items) {
|
256
|
+
config.items = items
|
257
|
+
}
|
258
|
+
|
259
|
+
if (stateProvider) {
|
260
|
+
config.stateProvider = {parent: stateProvider}
|
261
|
+
}
|
262
|
+
|
263
|
+
me.menuList = Neo.create(config)
|
264
|
+
})
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
/**
|
269
|
+
* Triggered after the theme config got changed
|
270
|
+
* @param {String|null} value
|
271
|
+
* @param {String|null} oldValue
|
272
|
+
* @protected
|
273
|
+
*/
|
274
|
+
afterSetTheme(value, oldValue) {
|
275
|
+
super.afterSetTheme(value, oldValue);
|
276
|
+
|
277
|
+
let {menuList} = this;
|
278
|
+
|
279
|
+
if (menuList) {
|
280
|
+
menuList.theme = value
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
284
|
+
/**
|
285
|
+
* Triggered after the windowId config got changed
|
286
|
+
* @param {Number|null} value
|
287
|
+
* @param {Number|null} oldValue
|
288
|
+
* @protected
|
289
|
+
*/
|
290
|
+
afterSetWindowId(value, oldValue) {
|
291
|
+
super.afterSetWindowId(value, oldValue);
|
292
|
+
|
293
|
+
let {menuList} = this;
|
294
|
+
|
295
|
+
if (menuList) {
|
296
|
+
menuList.windowId = value
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
/**
|
301
|
+
* Converts the iconCls array into a string on beforeGet
|
302
|
+
* @returns {String}
|
303
|
+
* @protected
|
304
|
+
*/
|
305
|
+
beforeGetIconCls() {
|
306
|
+
let iconCls = this._iconCls;
|
307
|
+
|
308
|
+
if (Array.isArray(iconCls)) {
|
309
|
+
return iconCls.join(' ')
|
310
|
+
}
|
311
|
+
|
312
|
+
return iconCls
|
313
|
+
}
|
314
|
+
|
315
|
+
/**
|
316
|
+
* Triggered before the badgePosition config gets changed
|
317
|
+
* @param {String} value
|
318
|
+
* @param {String} oldValue
|
319
|
+
* @returns {String}
|
320
|
+
* @protected
|
321
|
+
*/
|
322
|
+
beforeSetBadgePosition(value, oldValue) {
|
323
|
+
return this.beforeSetEnumValue(value, oldValue, 'badgePosition')
|
324
|
+
}
|
325
|
+
|
326
|
+
/**
|
327
|
+
* Triggered before the iconCls config gets changed. Converts the string into an array if needed.
|
328
|
+
* @param {Array|String|null} value
|
329
|
+
* @param {Array|String|null} oldValue
|
330
|
+
* @returns {Array}
|
331
|
+
* @protected
|
332
|
+
*/
|
333
|
+
beforeSetIconCls(value, oldValue) {
|
334
|
+
if (value && !Array.isArray(value)) {
|
335
|
+
value = value.split(' ').filter(Boolean)
|
336
|
+
}
|
337
|
+
|
338
|
+
return value
|
339
|
+
}
|
340
|
+
|
341
|
+
/**
|
342
|
+
* Triggered before the iconPosition config gets changed
|
343
|
+
* @param {String} value
|
344
|
+
* @param {String} oldValue
|
345
|
+
* @protected
|
346
|
+
*/
|
347
|
+
beforeSetIconPosition(value, oldValue) {
|
348
|
+
return this.beforeSetEnumValue(value, oldValue, 'iconPosition')
|
349
|
+
}
|
350
|
+
|
351
|
+
/**
|
352
|
+
* @protected
|
353
|
+
*/
|
354
|
+
changeRoute() {
|
355
|
+
this.editRoute && Neo.Main.editRoute(this.route)
|
356
|
+
}
|
357
|
+
|
358
|
+
/**
|
359
|
+
* @param args
|
360
|
+
*/
|
361
|
+
destroy(...args) {
|
362
|
+
this.menuList?.destroy(true, false);
|
363
|
+
super.destroy(...args)
|
364
|
+
}
|
365
|
+
|
366
|
+
/**
|
367
|
+
* @param {Object} data
|
368
|
+
*/
|
369
|
+
onClick(data) {
|
370
|
+
let me = this;
|
371
|
+
|
372
|
+
me.bindCallback(me.handler, 'handler', me.handlerScope || me);
|
373
|
+
me.handler?.(data);
|
374
|
+
|
375
|
+
me.menu && me.toggleMenu();
|
376
|
+
me.route && me.changeRoute(); // only relevant for editRoute=true
|
377
|
+
me.useRippleEffect && me.showRipple(data)
|
378
|
+
}
|
379
|
+
|
380
|
+
/**
|
381
|
+
* @param {Object} data
|
382
|
+
*/
|
383
|
+
async showRipple(data) {
|
384
|
+
let me = this,
|
385
|
+
buttonRect = data.path[0].rect,
|
386
|
+
diameter = Math.max(buttonRect.height, buttonRect.width),
|
387
|
+
radius = diameter / 2,
|
388
|
+
rippleEffectDuration = me.rippleEffectDuration,
|
389
|
+
rippleWrapper = me.getVdomRoot().cn[3],
|
390
|
+
rippleEl = rippleWrapper.cn[0],
|
391
|
+
rippleTimeoutId;
|
392
|
+
|
393
|
+
rippleEl.style = Object.assign(rippleEl.style || {}, {
|
394
|
+
animation: 'none',
|
395
|
+
height : `${diameter}px`,
|
396
|
+
left : `${data.clientX - buttonRect.left - radius}px`,
|
397
|
+
top : `${data.clientY - buttonRect.top - radius}px`,
|
398
|
+
width : `${diameter}px`
|
399
|
+
});
|
400
|
+
|
401
|
+
delete rippleWrapper.removeDom;
|
402
|
+
me.update();
|
403
|
+
|
404
|
+
await me.timeout(1);
|
405
|
+
|
406
|
+
rippleEl.style.animation = `ripple ${rippleEffectDuration}ms linear`;
|
407
|
+
me.update();
|
408
|
+
|
409
|
+
me.#rippleTimeoutId = rippleTimeoutId = setTimeout(() => {
|
410
|
+
// we do not want to break animations when clicking multiple times
|
411
|
+
if (me.#rippleTimeoutId === rippleTimeoutId) {
|
412
|
+
me.#rippleTimeoutId = null;
|
413
|
+
|
414
|
+
rippleWrapper.removeDom = true;
|
415
|
+
me.update()
|
416
|
+
}
|
417
|
+
}, rippleEffectDuration)
|
418
|
+
}
|
419
|
+
|
420
|
+
/**
|
421
|
+
*
|
422
|
+
*/
|
423
|
+
async toggleMenu() {
|
424
|
+
let {menuList} = this,
|
425
|
+
hidden = !menuList.hidden;
|
426
|
+
|
427
|
+
menuList.hidden = hidden;
|
428
|
+
|
429
|
+
if (!hidden) {
|
430
|
+
await this.timeout(50)
|
431
|
+
}
|
432
|
+
}
|
433
|
+
}
|
434
|
+
|
435
|
+
export default Neo.setupClass(EffectButton);
|
package/src/collection/Base.mjs
CHANGED
@@ -48,6 +48,11 @@ class Collection extends Base {
|
|
48
48
|
* @member {Boolean} autoSort=true
|
49
49
|
*/
|
50
50
|
autoSort: true,
|
51
|
+
/**
|
52
|
+
* Stores the items.length of the items array in use
|
53
|
+
* @member {Number} count_=0
|
54
|
+
*/
|
55
|
+
count_: 0,
|
51
56
|
/**
|
52
57
|
* Use 'primitive' for default filters, use 'advanced' for filters using a filterBy method
|
53
58
|
* which need to iterate over other collection items
|
@@ -140,6 +145,17 @@ class Collection extends Base {
|
|
140
145
|
}
|
141
146
|
|
142
147
|
/**
|
148
|
+
* Triggered after the badgePosition config got changed
|
149
|
+
* @param {Number} value
|
150
|
+
* @param {Number} oldValue
|
151
|
+
* @protected
|
152
|
+
*/
|
153
|
+
afterSetCount(value, oldValue) {
|
154
|
+
this.fire('countChange', {oldValue, value})
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* Triggered after the filters config got changed
|
143
159
|
* @param {Array} value
|
144
160
|
* @param {Array} oldValue
|
145
161
|
* @protected
|
@@ -158,6 +174,7 @@ class Collection extends Base {
|
|
158
174
|
}
|
159
175
|
|
160
176
|
/**
|
177
|
+
* Triggered after the items config got changed
|
161
178
|
* @param {Array} value
|
162
179
|
* @param {Array} oldValue
|
163
180
|
* @protected
|
@@ -174,10 +191,13 @@ class Collection extends Base {
|
|
174
191
|
item = value[i];
|
175
192
|
me.map.set(item[keyProperty], item)
|
176
193
|
}
|
194
|
+
|
195
|
+
me.count = len
|
177
196
|
}
|
178
197
|
}
|
179
198
|
|
180
199
|
/**
|
200
|
+
* Triggered after the sorters config got changed
|
181
201
|
* @param {Array} value
|
182
202
|
* @param {Array} oldValue
|
183
203
|
* @protected
|
@@ -198,6 +218,7 @@ class Collection extends Base {
|
|
198
218
|
}
|
199
219
|
|
200
220
|
/**
|
221
|
+
* Triggered after the sourceId config got changed
|
201
222
|
* @param {Number|String} value
|
202
223
|
* @param {Number|String} oldValue
|
203
224
|
* @protected
|
@@ -433,13 +454,17 @@ class Collection extends Base {
|
|
433
454
|
filters = me._filters || [],
|
434
455
|
sorters = me._sorters || [];
|
435
456
|
|
457
|
+
// Ensure the keyProperty does not get lost.
|
458
|
+
config.keyProperty = me.keyProperty;
|
459
|
+
|
436
460
|
delete config.id;
|
437
461
|
delete config.filters;
|
438
462
|
delete config.items;
|
439
463
|
delete config.sorters;
|
440
464
|
|
441
465
|
if (me._items.length > 0) {
|
442
|
-
config.items
|
466
|
+
config.items = [...me._items];
|
467
|
+
config.count = config.items.length;
|
443
468
|
}
|
444
469
|
|
445
470
|
config.filters = [];
|
@@ -673,6 +698,8 @@ class Collection extends Base {
|
|
673
698
|
|
674
699
|
me.allItems = Neo.create(Collection, {
|
675
700
|
...Neo.clone(config, true, true),
|
701
|
+
id : me.id + '-all',
|
702
|
+
items : [...me._items], // Initialize with a shallow copy of current items
|
676
703
|
keyProperty: me.keyProperty,
|
677
704
|
sourceId : me.id
|
678
705
|
})
|
@@ -727,6 +754,8 @@ class Collection extends Base {
|
|
727
754
|
me.doSort(me.items, true)
|
728
755
|
}
|
729
756
|
|
757
|
+
me.count = me.items.length;
|
758
|
+
|
730
759
|
me.fire('filter', {
|
731
760
|
isFiltered: me[isFiltered],
|
732
761
|
items : me.items,
|
@@ -845,11 +874,12 @@ class Collection extends Base {
|
|
845
874
|
}
|
846
875
|
|
847
876
|
/**
|
848
|
-
* Returns the
|
877
|
+
* Returns the config value of this.count
|
849
878
|
* @returns {Number}
|
879
|
+
* @deprecated Use `this.count` directly instead.
|
850
880
|
*/
|
851
881
|
getCount() {
|
852
|
-
return this.
|
882
|
+
return this._count || 0 // skipping beforeGetCount() on purpose
|
853
883
|
}
|
854
884
|
|
855
885
|
/**
|
@@ -1238,6 +1268,8 @@ class Collection extends Base {
|
|
1238
1268
|
}
|
1239
1269
|
|
1240
1270
|
if (me[updatingIndex] === 0) {
|
1271
|
+
me.count = me._items.length;
|
1272
|
+
|
1241
1273
|
me.fire('mutate', {
|
1242
1274
|
addedItems : toAddArray,
|
1243
1275
|
preventBubbleUp: me.preventBubbleUp,
|