wallace 0.14.0 → 0.15.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/lib/component.js +42 -33
- package/lib/createComponent.js +6 -2
- package/lib/detacher.js +61 -15
- package/lib/index.js +1 -1
- package/lib/mount.js +9 -5
- package/lib/offsetter.js +3 -4
- package/lib/part.js +4 -4
- package/lib/repeaters/keyed.js +99 -85
- package/lib/repeaters/nester.js +29 -0
- package/lib/repeaters/sequential.js +77 -57
- package/lib/router.js +10 -7
- package/lib/types.d.ts +7 -6
- package/lib/utils.js +4 -8
- package/package.json +3 -3
- package/lib/nestComponent.js +0 -8
package/lib/component.js
CHANGED
|
@@ -2,21 +2,15 @@ const throwAway = document.createElement("template");
|
|
|
2
2
|
const NO_LOOKUP = "__";
|
|
3
3
|
|
|
4
4
|
const ComponentPrototype = {
|
|
5
|
-
/*
|
|
6
|
-
COMPILER_MODS:
|
|
7
|
-
if useController is false:
|
|
8
|
-
- param `ctrl` is removed.
|
|
9
|
-
- `this.ctrl = ctrl` is removed.
|
|
10
|
-
*/
|
|
11
|
-
render: function (props, ctrl) {
|
|
5
|
+
render: function (props, /* #INCLUDE-IF: allowCtrl */ ctrl) {
|
|
12
6
|
this.props = props;
|
|
13
|
-
this.ctrl = ctrl;
|
|
7
|
+
/* #INCLUDE-IF: allowCtrl */ this.ctrl = ctrl;
|
|
14
8
|
this.update();
|
|
15
9
|
},
|
|
16
10
|
|
|
17
11
|
/*
|
|
18
12
|
COMPILER_MODS:
|
|
19
|
-
if
|
|
13
|
+
if allowParts is false this is deleted, so `_u` can be renamed to `update`.
|
|
20
14
|
*/
|
|
21
15
|
update: function () {
|
|
22
16
|
this._u(0, this._l);
|
|
@@ -24,18 +18,20 @@ const ComponentPrototype = {
|
|
|
24
18
|
|
|
25
19
|
/*
|
|
26
20
|
COMPILER_MODS:
|
|
27
|
-
if
|
|
21
|
+
if allowParts is false:
|
|
28
22
|
- gets renamed to `update`
|
|
29
23
|
- parameters are removed
|
|
30
24
|
- add `i = 0, il = this._l` to variable declarator.
|
|
31
25
|
*/
|
|
32
26
|
_u: function (i, il) {
|
|
33
|
-
let watch, element, displayToggle, detacher, lookupTrue, shouldBeVisible;
|
|
27
|
+
let watch, element, displayToggle, detacher, query, lookupTrue, shouldBeVisible;
|
|
34
28
|
|
|
35
29
|
const watches = this._w,
|
|
36
30
|
props = this.props,
|
|
37
31
|
previous = this._p,
|
|
38
|
-
elements = this._e
|
|
32
|
+
elements = this._e,
|
|
33
|
+
lookups = this._q,
|
|
34
|
+
stash = this._s;
|
|
39
35
|
/*
|
|
40
36
|
Watches is an array of objects with keys:
|
|
41
37
|
e: the element index (number)
|
|
@@ -57,11 +53,14 @@ const ComponentPrototype = {
|
|
|
57
53
|
displayToggle = watch.v;
|
|
58
54
|
shouldBeVisible = true;
|
|
59
55
|
if (displayToggle) {
|
|
60
|
-
|
|
61
|
-
shouldBeVisible = displayToggle.r ? lookupTrue : !lookupTrue;
|
|
56
|
+
query = displayToggle.q;
|
|
62
57
|
detacher = displayToggle.d;
|
|
58
|
+
if (query !== undefined) {
|
|
59
|
+
lookupTrue = !!lookups[query](props, this);
|
|
60
|
+
shouldBeVisible = displayToggle.r ? lookupTrue : !lookupTrue;
|
|
61
|
+
}
|
|
63
62
|
if (detacher) {
|
|
64
|
-
detacher.apply(element, shouldBeVisible, elements,
|
|
63
|
+
detacher.apply(element, shouldBeVisible, elements, stash);
|
|
65
64
|
} else {
|
|
66
65
|
element.hidden = !shouldBeVisible;
|
|
67
66
|
}
|
|
@@ -74,10 +73,10 @@ const ComponentPrototype = {
|
|
|
74
73
|
callbacks = watch.c;
|
|
75
74
|
for (let key in callbacks) {
|
|
76
75
|
if (key === NO_LOOKUP) {
|
|
77
|
-
callbacks[key](element, props, this);
|
|
76
|
+
callbacks[key](element, props, this, stash);
|
|
78
77
|
} else {
|
|
79
78
|
const oldValue = prev[key],
|
|
80
|
-
newValue =
|
|
79
|
+
newValue = lookups[key](props, this);
|
|
81
80
|
if (oldValue !== newValue) {
|
|
82
81
|
callbacks[key](element, props, this, newValue);
|
|
83
82
|
prev[key] = newValue;
|
|
@@ -87,23 +86,23 @@ const ComponentPrototype = {
|
|
|
87
86
|
}
|
|
88
87
|
i++;
|
|
89
88
|
}
|
|
89
|
+
},
|
|
90
|
+
/* #INCLUDE-IF: allowDismount */ dismount: function () {
|
|
91
|
+
let i = 0,
|
|
92
|
+
stash = this._s,
|
|
93
|
+
dismountKeys = this._d;
|
|
94
|
+
while (i < dismountKeys.length) {
|
|
95
|
+
stash[dismountKeys[i]].dismount();
|
|
96
|
+
i++;
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
};
|
|
92
100
|
|
|
93
|
-
Object.defineProperty(ComponentPrototype, "hidden", {
|
|
94
|
-
set: function (value) {
|
|
95
|
-
this.el.hidden = value;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
101
|
const ComponentBase = {
|
|
100
|
-
prototype: ComponentPrototype
|
|
102
|
+
prototype: ComponentPrototype,
|
|
103
|
+
/* #INCLUDE-IF: allowStubs */ stubs: {}
|
|
101
104
|
};
|
|
102
105
|
|
|
103
|
-
if (wallaceConfig.flags.allowStubs) {
|
|
104
|
-
ComponentBase.stubs = {};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
106
|
/**
|
|
108
107
|
*
|
|
109
108
|
* @param {function} ComponentFunction - The Component definition function to add bits to.
|
|
@@ -127,7 +126,7 @@ export const initConstructor = (ComponentFunction, BaseComponentFunction) => {
|
|
|
127
126
|
});
|
|
128
127
|
} else {
|
|
129
128
|
if (process.env.NODE_ENV !== "production") {
|
|
130
|
-
Object.defineProperty(ComponentFunction, "
|
|
129
|
+
Object.defineProperty(ComponentFunction, "methods", {
|
|
131
130
|
set: function (value) {
|
|
132
131
|
throw new Error(
|
|
133
132
|
'Flag "allowMethods" must be set to true in the config for this feature.'
|
|
@@ -142,20 +141,30 @@ export const initConstructor = (ComponentFunction, BaseComponentFunction) => {
|
|
|
142
141
|
}
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
144
|
+
/* #INCLUDE-IF: allowStubs */
|
|
145
|
+
ComponentFunction.stubs = Object.assign({}, BaseComponentFunction.stubs);
|
|
148
146
|
|
|
149
147
|
return ComponentFunction;
|
|
150
148
|
};
|
|
151
149
|
|
|
152
|
-
export const defineComponent = (
|
|
150
|
+
export const defineComponent = (
|
|
151
|
+
html,
|
|
152
|
+
watches,
|
|
153
|
+
queries,
|
|
154
|
+
contructor,
|
|
155
|
+
/* #INCLUDE-IF: allowDismount */ dismountKeys,
|
|
156
|
+
inheritFrom
|
|
157
|
+
) => {
|
|
153
158
|
const ComponentDefinition = initConstructor(contructor, inheritFrom || ComponentBase);
|
|
154
159
|
const proto = ComponentDefinition.prototype;
|
|
155
160
|
throwAway.innerHTML = html;
|
|
156
161
|
proto._w = watches;
|
|
157
162
|
proto._q = queries;
|
|
158
163
|
proto._t = throwAway.content.firstChild;
|
|
164
|
+
if (wallaceConfig.flags.allowDismount) {
|
|
165
|
+
ComponentDefinition.pool = proto._c = []; // The shared component pool.
|
|
166
|
+
proto._d = dismountKeys; // Keys of stashed elements to dismount.
|
|
167
|
+
}
|
|
159
168
|
if (wallaceConfig.flags.allowBase) {
|
|
160
169
|
proto.base = ComponentPrototype;
|
|
161
170
|
} else {
|
package/lib/createComponent.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
export const createComponent = (
|
|
1
|
+
export const createComponent = (
|
|
2
|
+
ComponentFunction,
|
|
3
|
+
props,
|
|
4
|
+
/* #INCLUDE-IF: allowCtrl */ ctrl
|
|
5
|
+
) => {
|
|
2
6
|
const component = new ComponentFunction();
|
|
3
|
-
component.render(props, ctrl);
|
|
7
|
+
component.render(props, /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
4
8
|
return component;
|
|
5
9
|
};
|
package/lib/detacher.js
CHANGED
|
@@ -1,25 +1,71 @@
|
|
|
1
1
|
import { countOffset } from "./offsetter";
|
|
2
2
|
/**
|
|
3
|
-
* Deals with conditionally detaching elements
|
|
3
|
+
* Deals with conditionally detaching single elements or components.
|
|
4
|
+
* A Detacher is assigned to a watch, not a component.
|
|
4
5
|
*/
|
|
5
|
-
export function Detacher(
|
|
6
|
-
this.i =
|
|
7
|
-
this.e =
|
|
8
|
-
this.s =
|
|
6
|
+
export function Detacher(initialIndex, parentElementKey, offsetTrackerStashKey) {
|
|
7
|
+
this.i = initialIndex;
|
|
8
|
+
this.e = parentElementKey;
|
|
9
|
+
this.s = offsetTrackerStashKey;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/*
|
|
13
|
+
This is very tricky bit of code.
|
|
14
|
+
|
|
15
|
+
It is affected by:
|
|
16
|
+
- initialIndex as passed into Detacher constructor.
|
|
17
|
+
- offsetTracker's initial value.
|
|
18
|
+
|
|
19
|
+
It take either:
|
|
20
|
+
- an element, which starts attached.
|
|
21
|
+
- a nester, whose element isn't attached to begin with.
|
|
22
|
+
|
|
23
|
+
Which affects how the insertion position is calculated.
|
|
24
|
+
|
|
25
|
+
About the offsetTracker:
|
|
26
|
+
|
|
27
|
+
The offset tracker is a map which stores a number against the initialIndex of each
|
|
28
|
+
conditionally displayed element, nester or repeater.
|
|
29
|
+
|
|
30
|
+
That number serves two purposes:
|
|
31
|
+
1. For elements & nesters it indicates whether the element needs inserted or removed,
|
|
32
|
+
if any.
|
|
33
|
+
2. For all cases, it is used to calculate the position at which new itsems should be
|
|
34
|
+
inserted.
|
|
35
|
+
|
|
36
|
+
Where it gets tricky is that the initial DOM will include elements, but not nester or
|
|
37
|
+
repeater elements.
|
|
38
|
+
|
|
39
|
+
For elements an offset of 0 means it is attached (and is the default if not set) and -1
|
|
40
|
+
indicates it is detached, and tells elements after it that they need to adjust their
|
|
41
|
+
insertion index by that much.
|
|
42
|
+
|
|
43
|
+
For nesters it is also 0 or -1, but the default is -1, which is set in the constructor.
|
|
44
|
+
|
|
45
|
+
For repeaters the offset is the number of items last rendered -1, because 0 items means
|
|
46
|
+
no elements inserted. However the repeaters never go through this function, so we can
|
|
47
|
+
assume the value here is always 0 or -1.
|
|
48
|
+
|
|
49
|
+
*/
|
|
50
|
+
Detacher.prototype.apply = function (elementOrNester, shouldBeVisible, elements, stash) {
|
|
12
51
|
const index = this.i,
|
|
13
|
-
parent = elements[this.e],
|
|
14
52
|
offsetTracker = stash[this.s];
|
|
15
53
|
let ownOffset = offsetTracker.get(index) || 0;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
parent
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
54
|
+
|
|
55
|
+
if ((shouldBeVisible && ownOffset < 0) || (!shouldBeVisible && ownOffset === 0)) {
|
|
56
|
+
var parent = elements[this.e],
|
|
57
|
+
element =
|
|
58
|
+
elementOrNester instanceof HTMLElement
|
|
59
|
+
? elementOrNester
|
|
60
|
+
: elementOrNester.get().el;
|
|
61
|
+
if (shouldBeVisible) {
|
|
62
|
+
var adjustedIndex = countOffset(offsetTracker, index);
|
|
63
|
+
parent.insertBefore(element, parent.childNodes[adjustedIndex]);
|
|
64
|
+
ownOffset = 0;
|
|
65
|
+
} else {
|
|
66
|
+
parent.removeChild(element);
|
|
67
|
+
ownOffset = -1;
|
|
68
|
+
}
|
|
69
|
+
offsetTracker.set(index, ownOffset);
|
|
23
70
|
}
|
|
24
|
-
offsetTracker.set(index, ownOffset);
|
|
25
71
|
};
|
package/lib/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export { Detacher } from "./detacher";
|
|
|
3
3
|
export { extendComponent } from "./extend";
|
|
4
4
|
export { mount } from "./mount";
|
|
5
5
|
export { createComponent } from "./createComponent";
|
|
6
|
-
export {
|
|
6
|
+
export { Nester } from "./repeaters/nester";
|
|
7
7
|
export { saveRef } from "./refs";
|
|
8
8
|
export { savePart } from "./part";
|
|
9
9
|
export { KeyedRepeater } from "./repeaters/keyed";
|
package/lib/mount.js
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { replaceNode } from "./utils";
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Creates and mounts a component onto an element.
|
|
5
3
|
*
|
|
6
4
|
* @param {any} elementOrId Either a string representing an id, or an element.
|
|
7
5
|
* @param {callable} componentDefinition The class of Component to create
|
|
8
6
|
* @param {object} props The props to pass to the component (optional)
|
|
7
|
+
* @param {ctrl} props The ctrl to pass to the component (optional)
|
|
9
8
|
*/
|
|
10
|
-
export const mount = (
|
|
9
|
+
export const mount = (
|
|
10
|
+
elementOrId,
|
|
11
|
+
componentDefinition,
|
|
12
|
+
props,
|
|
13
|
+
/* #INCLUDE-IF: allowCtrl */ ctrl
|
|
14
|
+
) => {
|
|
11
15
|
const component = new componentDefinition();
|
|
12
|
-
component.render(props, ctrl);
|
|
16
|
+
component.render(props, /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
13
17
|
const element =
|
|
14
18
|
typeof elementOrId === "string" ? document.getElementById(elementOrId) : elementOrId;
|
|
15
|
-
|
|
19
|
+
element.parentNode.replaceChild(component.el, element);
|
|
16
20
|
return component;
|
|
17
21
|
};
|
package/lib/offsetter.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const countOffset = (offsetTracker, index) => {
|
|
2
2
|
let offset = 0;
|
|
3
3
|
for (let [key, value] of offsetTracker.entries()) {
|
|
4
|
-
if (key
|
|
5
|
-
offset += value;
|
|
4
|
+
if (key < index) offset += value;
|
|
6
5
|
}
|
|
7
6
|
return index + offset;
|
|
8
|
-
}
|
|
7
|
+
};
|
package/lib/part.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
function Part(component, start, end) {
|
|
2
|
-
this.
|
|
3
|
-
this.
|
|
4
|
-
this.
|
|
2
|
+
this.c = component;
|
|
3
|
+
this.s = start;
|
|
4
|
+
this.e = end;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
Part.prototype.update = function () {
|
|
8
|
-
this.
|
|
8
|
+
this.c._u(this.s, this.e);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
/**
|
package/lib/repeaters/keyed.js
CHANGED
|
@@ -2,121 +2,135 @@ import { countOffset } from "../offsetter";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Repeats nested components, reusing items based on key.
|
|
5
|
-
*
|
|
6
|
-
* COMPILER_MODS:
|
|
7
|
-
* if allowRepeaterSiblings is false the last two parameters are removed.
|
|
8
5
|
*/
|
|
9
6
|
export function KeyedRepeater(
|
|
10
7
|
componentDefinition,
|
|
11
|
-
pool,
|
|
12
8
|
key,
|
|
13
|
-
adjustmentTracker,
|
|
14
|
-
initialIndex
|
|
9
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ adjustmentTracker,
|
|
10
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ initialIndex
|
|
15
11
|
) {
|
|
16
12
|
this.d = componentDefinition;
|
|
13
|
+
/* #INCLUDE-IF: allowDismount */ this.s = componentDefinition.pool;
|
|
14
|
+
this.p = new Map();
|
|
17
15
|
this.k = []; // array of keys as last set.
|
|
18
|
-
this.p = pool; // pool of component instances. Must be a Map.
|
|
19
16
|
if (typeof key === "function") {
|
|
20
17
|
this.f = key;
|
|
21
18
|
} else {
|
|
22
19
|
this.n = key;
|
|
23
20
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.i = initialIndex;
|
|
27
|
-
}
|
|
21
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ this.a = adjustmentTracker;
|
|
22
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ this.i = initialIndex;
|
|
28
23
|
}
|
|
29
24
|
|
|
30
25
|
/**
|
|
31
26
|
* Updates the element's childNodes to match the items.
|
|
32
27
|
* Performance is important.
|
|
33
|
-
*
|
|
34
|
-
* @param {DOMElement} e - The DOM element to patch.
|
|
35
|
-
* @param {Array} items - Array of items which will be passed as props.
|
|
36
|
-
* @param {any} ctrl - The parent item's controller.
|
|
37
28
|
*/
|
|
38
|
-
KeyedRepeater.prototype
|
|
39
|
-
|
|
40
|
-
componentDefinition = this.d,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
29
|
+
KeyedRepeater.prototype = {
|
|
30
|
+
patch: function (parent, items, /* #INCLUDE-IF: allowCtrl */ ctrl) {
|
|
31
|
+
const componentDefinition = this.d,
|
|
32
|
+
/* #INCLUDE-IF: allowDismount */ sharedPool = this.s,
|
|
33
|
+
ownPool = this.p,
|
|
34
|
+
keyName = this.n,
|
|
35
|
+
keyFn = this.f,
|
|
36
|
+
useKeyName = keyName !== undefined,
|
|
37
|
+
childNodes = parent.childNodes,
|
|
38
|
+
itemsLength = items.length,
|
|
39
|
+
previousKeys = this.k,
|
|
40
|
+
previousKeysLength = previousKeys.length,
|
|
41
|
+
newKeys = [],
|
|
42
|
+
previousKeysSet = new Set(previousKeys),
|
|
43
|
+
frag = document.createDocumentFragment();
|
|
51
44
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
45
|
+
let item,
|
|
46
|
+
el,
|
|
47
|
+
itemKey,
|
|
48
|
+
component,
|
|
49
|
+
initialIndex,
|
|
50
|
+
offsetTracker,
|
|
51
|
+
endAnchor = null,
|
|
52
|
+
adjustment = 0,
|
|
53
|
+
anchor = null,
|
|
54
|
+
fragAnchor = null,
|
|
55
|
+
untouched = true,
|
|
56
|
+
append = false,
|
|
57
|
+
offset = previousKeysLength - itemsLength,
|
|
58
|
+
i = itemsLength - 1;
|
|
66
59
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
60
|
+
if (wallaceConfig.flags.allowRepeaterSiblings) {
|
|
61
|
+
offsetTracker = this.a;
|
|
62
|
+
initialIndex = this.i;
|
|
63
|
+
if (offsetTracker) {
|
|
64
|
+
adjustment = countOffset(offsetTracker, initialIndex);
|
|
65
|
+
endAnchor = childNodes[previousKeysLength + adjustment] || null;
|
|
66
|
+
anchor = endAnchor;
|
|
67
|
+
untouched = false;
|
|
68
|
+
}
|
|
75
69
|
}
|
|
76
|
-
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
71
|
+
// Working backwards saves us having to track moves.
|
|
72
|
+
while (i >= 0) {
|
|
73
|
+
item = items[i];
|
|
74
|
+
itemKey = useKeyName ? item[keyName] : keyFn(item);
|
|
75
|
+
if (!(component = ownPool.get(itemKey))) {
|
|
76
|
+
if (wallaceConfig.flags.allowDismount) {
|
|
77
|
+
component = sharedPool.pop() || new componentDefinition();
|
|
78
|
+
} else {
|
|
79
|
+
component = new componentDefinition();
|
|
80
|
+
}
|
|
81
|
+
ownPool.set(itemKey, component);
|
|
82
|
+
}
|
|
83
|
+
component.render(item, /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
84
|
+
el = component.el;
|
|
85
|
+
if (untouched && !previousKeysSet.has(itemKey)) {
|
|
86
|
+
frag.insertBefore(el, fragAnchor);
|
|
87
|
+
fragAnchor = el;
|
|
88
|
+
append = true;
|
|
89
|
+
offset++;
|
|
90
|
+
} else {
|
|
91
|
+
if (itemKey !== previousKeys[i + offset]) {
|
|
92
|
+
parent.insertBefore(el, anchor);
|
|
93
|
+
untouched = false;
|
|
94
|
+
}
|
|
95
|
+
anchor = el;
|
|
97
96
|
}
|
|
98
|
-
|
|
97
|
+
newKeys.push(itemKey);
|
|
98
|
+
previousKeysSet.delete(itemKey);
|
|
99
|
+
i--;
|
|
99
100
|
}
|
|
100
|
-
newKeys.push(itemKey);
|
|
101
|
-
previousKeysSet.delete(itemKey);
|
|
102
|
-
i--;
|
|
103
|
-
}
|
|
104
101
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
if (append) {
|
|
103
|
+
parent.insertBefore(frag, endAnchor);
|
|
104
|
+
}
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
let toStrip = previousKeysSet.size;
|
|
107
|
+
while (toStrip > 0) {
|
|
108
|
+
parent.removeChild(childNodes[adjustment]);
|
|
109
|
+
toStrip--;
|
|
110
|
+
}
|
|
114
111
|
|
|
115
|
-
|
|
112
|
+
this.k = newKeys.reverse();
|
|
116
113
|
|
|
117
|
-
|
|
114
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */
|
|
118
115
|
if (offsetTracker) {
|
|
119
116
|
offsetTracker.set(initialIndex, itemsLength - 1);
|
|
120
117
|
}
|
|
118
|
+
|
|
119
|
+
/* #INCLUDE-IF: allowDismount */
|
|
120
|
+
for (const keyToRemove of previousKeysSet) {
|
|
121
|
+
component = ownPool.get(keyToRemove);
|
|
122
|
+
sharedPool.push(component);
|
|
123
|
+
component.dismount();
|
|
124
|
+
ownPool.delete(keyToRemove);
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
/* #INCLUDE-IF: allowDismount */ dismount: function () {
|
|
128
|
+
const pool = this.p,
|
|
129
|
+
sharedPool = this.s;
|
|
130
|
+
for (const [key, value] of pool.entries()) {
|
|
131
|
+
sharedPool.push(value);
|
|
132
|
+
value.dismount();
|
|
133
|
+
}
|
|
134
|
+
pool.clear();
|
|
121
135
|
}
|
|
122
136
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function Nester(componentDefinition) {
|
|
2
|
+
this.d = componentDefinition;
|
|
3
|
+
this.i = null;
|
|
4
|
+
/* #INCLUDE-IF: allowDismount */ this.s = componentDefinition.pool;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
Nester.prototype = {
|
|
8
|
+
send: function (props, ctrl) {
|
|
9
|
+
this.get().render(props, ctrl);
|
|
10
|
+
},
|
|
11
|
+
get: function () {
|
|
12
|
+
if (!this.i) {
|
|
13
|
+
if (wallaceConfig.flags.allowDismount) {
|
|
14
|
+
this.i = this.s.pop() || new this.d();
|
|
15
|
+
} else {
|
|
16
|
+
this.i = new this.d();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return this.i;
|
|
20
|
+
},
|
|
21
|
+
/* #INCLUDE-IF: allowDismount */ dismount: function () {
|
|
22
|
+
const i = this.i;
|
|
23
|
+
if (i) {
|
|
24
|
+
i.dismount();
|
|
25
|
+
this.s.push(i);
|
|
26
|
+
this.i = null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
@@ -2,83 +2,103 @@ import { countOffset } from "../offsetter";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Repeats nested components, yielding from the pool sequentially.
|
|
5
|
-
*
|
|
6
|
-
* COMPILER_MODS:
|
|
7
|
-
* if allowRepeaterSiblings is false the last two parameters are removed.
|
|
8
5
|
*/
|
|
9
6
|
export function SequentialRepeater(
|
|
10
7
|
componentDefinition,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
initialIndex
|
|
8
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ adjustmentTracker,
|
|
9
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ initialIndex
|
|
14
10
|
) {
|
|
15
11
|
this.d = componentDefinition;
|
|
16
|
-
|
|
12
|
+
/* #INCLUDE-IF: allowDismount */ this.s = componentDefinition.pool;
|
|
13
|
+
this.p = [];
|
|
17
14
|
this.c = 0; // Child count
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
this.i = initialIndex;
|
|
21
|
-
}
|
|
15
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ this.a = adjustmentTracker;
|
|
16
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */ this.i = initialIndex;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
19
|
/**
|
|
25
20
|
* Updates the element's childNodes to match the items.
|
|
26
21
|
* Performance is important.
|
|
27
|
-
*
|
|
28
|
-
* @param {DOMElement} parent - The DOM element to patch.
|
|
29
|
-
* @param {Array} items - Array of items which will be passed as props.
|
|
30
|
-
* @param {any} ctrl - The parent item's controller.
|
|
31
22
|
*/
|
|
32
|
-
SequentialRepeater.prototype
|
|
33
|
-
|
|
34
|
-
componentDefinition = this.d,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
SequentialRepeater.prototype = {
|
|
24
|
+
patch: function (parent, items, /* #INCLUDE-IF: allowCtrl */ ctrl) {
|
|
25
|
+
const componentDefinition = this.d,
|
|
26
|
+
/* #INCLUDE-IF: allowDismount */ sharedPool = this.s,
|
|
27
|
+
previousChildCount = this.c,
|
|
28
|
+
pool = this.p,
|
|
29
|
+
itemsLength = items.length,
|
|
30
|
+
childNodes = parent.childNodes;
|
|
38
31
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
let i = 0,
|
|
33
|
+
offset = 0,
|
|
34
|
+
component,
|
|
35
|
+
nextElement,
|
|
36
|
+
initialIndex,
|
|
37
|
+
offsetTracker,
|
|
38
|
+
endOfRange = previousChildCount,
|
|
39
|
+
poolCount = pool.length,
|
|
40
|
+
originalPoolCount = poolCount;
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
if (wallaceConfig.flags.allowRepeaterSiblings) {
|
|
43
|
+
initialIndex = this.i;
|
|
44
|
+
offsetTracker = this.a;
|
|
45
|
+
if (offsetTracker) {
|
|
46
|
+
// The repeat element has siblings
|
|
47
|
+
offset = countOffset(offsetTracker, initialIndex);
|
|
48
|
+
endOfRange += offset;
|
|
49
|
+
nextElement = childNodes[endOfRange] || null;
|
|
50
|
+
}
|
|
56
51
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
52
|
+
while (i < itemsLength) {
|
|
53
|
+
if (i < poolCount) {
|
|
54
|
+
component = pool[i];
|
|
55
|
+
} else {
|
|
56
|
+
if (wallaceConfig.flags.allowDismount) {
|
|
57
|
+
component = sharedPool.pop() || new componentDefinition();
|
|
58
|
+
} else {
|
|
59
|
+
component = new componentDefinition();
|
|
60
|
+
}
|
|
61
|
+
pool.push(component);
|
|
62
|
+
poolCount++;
|
|
63
|
+
}
|
|
64
|
+
component.render(items[i], /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
65
|
+
if (i >= previousChildCount) {
|
|
66
|
+
parent.insertBefore(component.el, nextElement);
|
|
67
|
+
}
|
|
68
|
+
i++;
|
|
65
69
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
this.c = itemsLength;
|
|
71
|
+
|
|
72
|
+
let removeAtIndex = offset + previousChildCount - 1;
|
|
73
|
+
const stopAtIndex = offset + itemsLength - 1;
|
|
74
|
+
for (let i = removeAtIndex; i > stopAtIndex; i--) {
|
|
75
|
+
parent.removeChild(childNodes[i]);
|
|
69
76
|
}
|
|
70
|
-
i++;
|
|
71
|
-
}
|
|
72
|
-
this.c = itemsLength;
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
const stopatIndex = offset + itemsLength - 1;
|
|
76
|
-
for (let i = removeAtIndex; i > stopatIndex; i--) {
|
|
77
|
-
parent.removeChild(childNodes[i]);
|
|
78
|
-
}
|
|
79
|
-
if (wallaceConfig.flags.allowRepeaterSiblings) {
|
|
78
|
+
/* #INCLUDE-IF: allowRepeaterSiblings */
|
|
80
79
|
if (offsetTracker) {
|
|
81
80
|
offsetTracker.set(initialIndex, itemsLength - 1);
|
|
82
81
|
}
|
|
82
|
+
|
|
83
|
+
/* #INCLUDE-IF: allowDismount */
|
|
84
|
+
while (originalPoolCount > itemsLength) {
|
|
85
|
+
component = pool.pop();
|
|
86
|
+
sharedPool.push(component);
|
|
87
|
+
component.dismount();
|
|
88
|
+
originalPoolCount--;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
/* #INCLUDE-IF: allowDismount */ dismount: function () {
|
|
92
|
+
let component,
|
|
93
|
+
pool = this.p,
|
|
94
|
+
poolCount = pool.length,
|
|
95
|
+
sharedPool = this.s;
|
|
96
|
+
while (poolCount > 0) {
|
|
97
|
+
component = pool.pop();
|
|
98
|
+
sharedPool.push(component);
|
|
99
|
+
component.dismount();
|
|
100
|
+
poolCount--;
|
|
101
|
+
}
|
|
102
|
+
pool.length = 0;
|
|
83
103
|
}
|
|
84
104
|
};
|
package/lib/router.js
CHANGED
|
@@ -22,8 +22,8 @@ export const route = (path, componentDef, converter, cleanup) =>
|
|
|
22
22
|
|
|
23
23
|
export const Router = () => <div></div>;
|
|
24
24
|
|
|
25
|
-
Router.
|
|
26
|
-
render(props, ctrl) {
|
|
25
|
+
Object.assign(Router.prototype, {
|
|
26
|
+
render(props, /* #INCLUDE-IF: allowCtrl */ ctrl) {
|
|
27
27
|
const defaultError = (error, router) => (router.el.innerHTML = error.message);
|
|
28
28
|
this.error = props.error || defaultError;
|
|
29
29
|
this.current = null;
|
|
@@ -33,7 +33,7 @@ Router.methods = {
|
|
|
33
33
|
this.el.setAttribute(k, props.atts[k]);
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
|
-
this.base.render.call(this, props, ctrl);
|
|
36
|
+
this.base.render.call(this, props, /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
37
37
|
},
|
|
38
38
|
async onHashChange() {
|
|
39
39
|
const path = location.hash.slice(1) || "",
|
|
@@ -45,7 +45,10 @@ Router.methods = {
|
|
|
45
45
|
while (i < len) {
|
|
46
46
|
let route = routes[i];
|
|
47
47
|
if ((routeData = route.match(path))) {
|
|
48
|
-
const component = await route.getComponent(
|
|
48
|
+
const component = await route.getComponent(
|
|
49
|
+
routeData,
|
|
50
|
+
/* #INCLUDE-IF: allowCtrl */ this.ctrl
|
|
51
|
+
);
|
|
49
52
|
this.current && this.current.cleanup();
|
|
50
53
|
this.mount(component);
|
|
51
54
|
this.current = route;
|
|
@@ -62,7 +65,7 @@ Router.methods = {
|
|
|
62
65
|
this.el.textContent = "";
|
|
63
66
|
this.el.appendChild(component.el);
|
|
64
67
|
}
|
|
65
|
-
};
|
|
68
|
+
});
|
|
66
69
|
|
|
67
70
|
export function Route(path, def, convert, cleanup) {
|
|
68
71
|
this.chunks = path
|
|
@@ -96,12 +99,12 @@ Route.prototype = {
|
|
|
96
99
|
}
|
|
97
100
|
return { args, params: new URLSearchParams(query), url };
|
|
98
101
|
},
|
|
99
|
-
async getComponent(routeData, ctrl) {
|
|
102
|
+
async getComponent(routeData, /* #INCLUDE-IF: allowCtrl */ ctrl) {
|
|
100
103
|
if (!this.component) {
|
|
101
104
|
this.component = new this.def();
|
|
102
105
|
}
|
|
103
106
|
const props = await this._convert(routeData);
|
|
104
|
-
this.component.render(props, ctrl);
|
|
107
|
+
this.component.render(props, /* #INCLUDE-IF: allowCtrl */ ctrl);
|
|
105
108
|
return this.component;
|
|
106
109
|
},
|
|
107
110
|
/**
|
package/lib/types.d.ts
CHANGED
|
@@ -525,12 +525,13 @@ watchedObj[0] = 'foo'; // throws error.
|
|
|
525
525
|
You can toggle flags in your babel config to disable certain features for cutting edge
|
|
526
526
|
performance and bundle size:
|
|
527
527
|
|
|
528
|
-
1. allowBase -
|
|
529
|
-
2. allowCtrl -
|
|
530
|
-
3.
|
|
531
|
-
4.
|
|
532
|
-
5.
|
|
533
|
-
6.
|
|
528
|
+
1. `allowBase` - allows use of `base` in components.
|
|
529
|
+
2. `allowCtrl` - allows use of `ctrl` in components.
|
|
530
|
+
3. `allowDismount` - allows components to handle dismounting.
|
|
531
|
+
4. `allowMethods` - allows use of `methods` helper to components.
|
|
532
|
+
5. `allowParts` - allows use of parts.
|
|
533
|
+
6. `allowRepeaterSiblings` - allows repeaters to have siblings.
|
|
534
|
+
7. `allowStubs` - allows use of stubs.
|
|
534
535
|
|
|
535
536
|
These flags default to true, unless you specify `flags` in the plugin config, in which
|
|
536
537
|
case they default to false and you need to explicitly enable those you want:
|
package/lib/utils.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const findElement = (rootElement, path) => {
|
|
2
2
|
let node = rootElement;
|
|
3
3
|
for (let i = 0; i < path.length; i++) {
|
|
4
4
|
node = node.childNodes[path[i]];
|
|
5
5
|
}
|
|
6
6
|
return node;
|
|
7
|
-
}
|
|
7
|
+
};
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
nodeToReplace.parentNode.replaceChild(newNode, nodeToReplace);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function onEvent(element, eventName, callback) {
|
|
9
|
+
export const onEvent = (element, eventName, callback) => {
|
|
14
10
|
element.addEventListener(eventName, callback);
|
|
15
11
|
return element;
|
|
16
|
-
}
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"author": "Andrew Buchan",
|
|
5
5
|
"description": "An insanely small, fast, intuitive and extendable front-end framework",
|
|
6
6
|
"homepage": "https://wallace.js.org",
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"test": "jest --clearCache && jest"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"babel-plugin-wallace": "^0.
|
|
18
|
+
"babel-plugin-wallace": "^0.15.0",
|
|
19
19
|
"browserify": "^17.0.1"
|
|
20
20
|
},
|
|
21
|
-
"gitHead": "
|
|
21
|
+
"gitHead": "87a69fe8a8f3b8dd20e66eb0d125998658272931"
|
|
22
22
|
}
|
package/lib/nestComponent.js
DELETED