wallace 0.12.2 → 0.14.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 CHANGED
@@ -16,7 +16,7 @@ const ComponentPrototype = {
16
16
 
17
17
  /*
18
18
  COMPILER_MODS:
19
- if useFlags is false this is deleted.
19
+ if useFlags is false this is deleted, so `_u` can be renamed to `update`.
20
20
  */
21
21
  update: function () {
22
22
  this._u(0, this._l);
@@ -100,7 +100,7 @@ const ComponentBase = {
100
100
  prototype: ComponentPrototype
101
101
  };
102
102
 
103
- if (wallaceConfig.flags.useStubs) {
103
+ if (wallaceConfig.flags.allowStubs) {
104
104
  ComponentBase.stubs = {};
105
105
  }
106
106
 
@@ -110,7 +110,7 @@ if (wallaceConfig.flags.useStubs) {
110
110
  * @param {function} BaseComponentFunction - A Component definition function to inherit bits from.
111
111
  * @returns the ComponentFunction with bits added.
112
112
  */
113
- export function initConstructor(ComponentFunction, BaseComponentFunction) {
113
+ export const initConstructor = (ComponentFunction, BaseComponentFunction) => {
114
114
  const proto = (ComponentFunction.prototype = Object.create(
115
115
  BaseComponentFunction.prototype,
116
116
  {
@@ -120,59 +120,55 @@ export function initConstructor(ComponentFunction, BaseComponentFunction) {
120
120
  }
121
121
  ));
122
122
 
123
- if (wallaceConfig.flags.useMethods) {
123
+ if (wallaceConfig.flags.allowMethods) {
124
124
  Object.defineProperty(ComponentFunction, "methods", {
125
- set: function (value) {
126
- Object.assign(proto, value);
127
- },
128
- get: function () {
129
- return proto;
130
- }
125
+ set: value => Object.assign(proto, value),
126
+ get: () => proto
131
127
  });
132
128
  } else {
133
129
  if (process.env.NODE_ENV !== "production") {
134
130
  Object.defineProperty(ComponentFunction, "name", {
135
131
  set: function (value) {
136
132
  throw new Error(
137
- 'Flag "useMethods" must be set to true in the config for this feature.'
133
+ 'Flag "allowMethods" must be set to true in the config for this feature.'
138
134
  );
139
135
  },
140
136
  get: function () {
141
137
  throw new Error(
142
- 'Flag "useMethods" must be set to true in the config for this feature.'
138
+ 'Flag "allowMethods" must be set to true in the config for this feature.'
143
139
  );
144
140
  }
145
141
  });
146
142
  }
147
143
  }
148
144
 
149
- if (wallaceConfig.flags.useStubs) {
145
+ if (wallaceConfig.flags.allowStubs) {
150
146
  ComponentFunction.stubs = Object.assign({}, BaseComponentFunction.stubs);
151
147
  }
152
148
 
153
149
  return ComponentFunction;
154
- }
150
+ };
155
151
 
156
- export function defineComponent(html, watches, queries, contructor, inheritFrom) {
152
+ export const defineComponent = (html, watches, queries, contructor, inheritFrom) => {
157
153
  const ComponentDefinition = initConstructor(contructor, inheritFrom || ComponentBase);
158
154
  const proto = ComponentDefinition.prototype;
159
155
  throwAway.innerHTML = html;
160
156
  proto._w = watches;
161
157
  proto._q = queries;
162
158
  proto._t = throwAway.content.firstChild;
163
- if (wallaceConfig.flags.useBase) {
159
+ if (wallaceConfig.flags.allowBase) {
164
160
  proto.base = ComponentPrototype;
165
161
  } else {
166
162
  if (process.env.NODE_ENV !== "production") {
167
163
  Object.defineProperty(proto, "base", {
168
164
  set: function (value) {
169
165
  throw new Error(
170
- 'Flag "useBase" must be set to true in the config for this feature.'
166
+ 'Flag "allowBase" must be set to true in the config for this feature.'
171
167
  );
172
168
  },
173
169
  get: function () {
174
170
  throw new Error(
175
- 'Flag "useBase" must be set to true in the config for this feature.'
171
+ 'Flag "allowBase" must be set to true in the config for this feature.'
176
172
  );
177
173
  }
178
174
  });
@@ -180,4 +176,4 @@ export function defineComponent(html, watches, queries, contructor, inheritFrom)
180
176
  }
181
177
 
182
178
  return ComponentDefinition;
183
- }
179
+ };
@@ -1,5 +1,5 @@
1
- export function createComponent(ComponentFunction, props, ctrl) {
1
+ export const createComponent = (ComponentFunction, props, ctrl) => {
2
2
  const component = new ComponentFunction();
3
3
  component.render(props, ctrl);
4
4
  return component;
5
- }
5
+ };
package/lib/detacher.js CHANGED
@@ -1,28 +1,25 @@
1
+ import { countOffset } from "./offsetter";
1
2
  /**
2
3
  * Deals with conditionally detaching elements with the if directive.
3
4
  */
4
5
  export function Detacher(i, e, s) {
5
6
  this.i = i; // the initial element index.
6
- this.e = e; // the stash key of the map of detached nodes.
7
- this.s = s; // the parent element key.
7
+ this.e = e; // the parent element key.
8
+ this.s = s; // the stash key of the map of detached nodes.
8
9
  }
9
10
 
10
11
  Detacher.prototype.apply = function (element, shouldBeVisible, elements, stash) {
11
- let adjustedIndex;
12
12
  const index = this.i,
13
13
  parent = elements[this.e],
14
- detachedElements = stash[this.s],
15
- detachedElement = detachedElements[index];
16
- if (shouldBeVisible && detachedElement) {
17
- adjustedIndex =
18
- index -
19
- Object.keys(detachedElements).filter(function (k) {
20
- return k < index && detachedElements[k];
21
- }).length;
22
- parent.insertBefore(detachedElement, parent.childNodes[adjustedIndex]);
23
- detachedElements[index] = null;
24
- } else if (!shouldBeVisible && !detachedElement) {
14
+ offsetTracker = stash[this.s];
15
+ let ownOffset = offsetTracker.get(index) || 0;
16
+ if (shouldBeVisible && ownOffset === -1) {
17
+ const adjustedIndex = countOffset(offsetTracker, index);
18
+ parent.insertBefore(element, parent.childNodes[adjustedIndex]);
19
+ ownOffset = 0;
20
+ } else if (!shouldBeVisible && ownOffset === 0) {
25
21
  parent.removeChild(element);
26
- detachedElements[index] = element;
22
+ ownOffset = -1;
27
23
  }
24
+ offsetTracker.set(index, ownOffset);
28
25
  };
package/lib/extend.js CHANGED
@@ -11,7 +11,7 @@ import { initConstructor } from "./component";
11
11
  *
12
12
  * So it should never be called with 2nd arg in real life.
13
13
  */
14
- export function extendComponent(base, componentDef) {
14
+ export const extendComponent = (base, componentDef) => {
15
15
  // This function call will have been replaced if 2nd arg is a valid component func.
16
16
  // and therefore we would not receive it.
17
17
  if (componentDef)
@@ -19,4 +19,4 @@ export function extendComponent(base, componentDef) {
19
19
  return initConstructor(function () {
20
20
  base.call(this);
21
21
  }, base);
22
- }
22
+ };
package/lib/index.js CHANGED
@@ -7,9 +7,9 @@ export { nestComponent } from "./nestComponent";
7
7
  export { saveRef } from "./refs";
8
8
  export { savePart } from "./part";
9
9
  export { KeyedRepeater } from "./repeaters/keyed";
10
- export { KeyedFnRepeater } from "./repeaters/keyedFn";
11
10
  export { SequentialRepeater } from "./repeaters/sequential";
12
11
  export { Router, route } from "./router";
13
12
  export { getStub } from "./stubs";
13
+ export { toDateString } from "./toDateString";
14
14
  export { findElement, onEvent } from "./utils";
15
15
  export { watch, protect } from "./watch";
package/lib/mount.js CHANGED
@@ -7,11 +7,11 @@ import { replaceNode } from "./utils";
7
7
  * @param {callable} componentDefinition The class of Component to create
8
8
  * @param {object} props The props to pass to the component (optional)
9
9
  */
10
- export function mount(elementOrId, componentDefinition, props, ctrl) {
10
+ export const mount = (elementOrId, componentDefinition, props, ctrl) => {
11
11
  const component = new componentDefinition();
12
12
  component.render(props, ctrl);
13
13
  const element =
14
14
  typeof elementOrId === "string" ? document.getElementById(elementOrId) : elementOrId;
15
15
  replaceNode(element, component.el);
16
16
  return component;
17
- }
17
+ };
@@ -1,8 +1,8 @@
1
1
  import { findElement, replaceNode } from "./utils";
2
2
 
3
- export function nestComponent(rootElement, path, componentDefinition) {
3
+ export const nestComponent = (rootElement, path, componentDefinition) => {
4
4
  const el = findElement(rootElement, path),
5
5
  child = new componentDefinition();
6
6
  replaceNode(el, child.el);
7
7
  return child;
8
- }
8
+ };
@@ -0,0 +1,8 @@
1
+ export function countOffset(offsetTracker, index) {
2
+ let offset = 0;
3
+ for (let [key, value] of offsetTracker.entries()) {
4
+ if (key >= index) break;
5
+ offset += value;
6
+ }
7
+ return index + offset;
8
+ }
package/lib/part.js CHANGED
@@ -11,7 +11,7 @@ Part.prototype.update = function () {
11
11
  /**
12
12
  * Saves a reference to a part of a component so it can be updated independently.
13
13
  */
14
- export function savePart(node, component, parts, name, start, end) {
14
+ export const savePart = (node, component, parts, name, start, end) => {
15
15
  parts[name] = new Part(component, start, end);
16
16
  return node;
17
- }
17
+ };
package/lib/refs.js CHANGED
@@ -2,6 +2,4 @@
2
2
  * Saves a reference to a node (element or nested component)
3
3
  * Returns the node.
4
4
  */
5
- export function saveRef(node, refs, name) {
6
- return (refs[name] = node);
7
- }
5
+ export const saveRef = (node, refs, name) => (refs[name] = node);
@@ -1,16 +1,30 @@
1
- // WARNING: Code here is near duplicated in keyedFn.
1
+ import { countOffset } from "../offsetter";
2
2
 
3
3
  /**
4
4
  * Repeats nested components, reusing items based on key.
5
5
  *
6
- * @param {function} componentDefinition - The ComponentDefinition to create.
7
- * @param {string} keyName - The name of the key property.
6
+ * COMPILER_MODS:
7
+ * if allowRepeaterSiblings is false the last two parameters are removed.
8
8
  */
9
- export function KeyedRepeater(componentDefinition, keyName) {
10
- this.def = componentDefinition;
11
- this.keyName = keyName;
12
- this.keys = []; // array of keys as last set.
13
- this.pool = {}; // pool of component instances.
9
+ export function KeyedRepeater(
10
+ componentDefinition,
11
+ pool,
12
+ key,
13
+ adjustmentTracker,
14
+ initialIndex
15
+ ) {
16
+ this.d = componentDefinition;
17
+ this.k = []; // array of keys as last set.
18
+ this.p = pool; // pool of component instances. Must be a Map.
19
+ if (typeof key === "function") {
20
+ this.f = key;
21
+ } else {
22
+ this.n = key;
23
+ }
24
+ if (wallaceConfig.flags.allowRepeaterSiblings) {
25
+ this.a = adjustmentTracker;
26
+ this.i = initialIndex;
27
+ }
14
28
  }
15
29
 
16
30
  /**
@@ -22,20 +36,27 @@ export function KeyedRepeater(componentDefinition, keyName) {
22
36
  * @param {any} ctrl - The parent item's controller.
23
37
  */
24
38
  KeyedRepeater.prototype.patch = function (e, items, ctrl) {
25
- const pool = this.pool,
26
- componentDefinition = this.def,
27
- keyName = this.keyName,
39
+ const pool = this.p,
40
+ componentDefinition = this.d,
41
+ keyName = this.n,
42
+ keyFn = this.f,
43
+ useKeyName = keyName !== undefined,
28
44
  childNodes = e.childNodes,
29
45
  itemsLength = items.length,
30
- previousKeys = this.keys,
46
+ previousKeys = this.k,
31
47
  previousKeysLength = previousKeys.length,
32
48
  newKeys = [],
33
49
  previousKeysSet = new Set(previousKeys),
34
50
  frag = document.createDocumentFragment();
51
+
35
52
  let item,
36
53
  el,
37
- key,
54
+ itemKey,
38
55
  component,
56
+ initialIndex,
57
+ offsetTracker,
58
+ endAnchor = null,
59
+ adjustment = 0,
39
60
  anchor = null,
40
61
  fragAnchor = null,
41
62
  untouched = true,
@@ -43,39 +64,59 @@ KeyedRepeater.prototype.patch = function (e, items, ctrl) {
43
64
  offset = previousKeysLength - itemsLength,
44
65
  i = itemsLength - 1;
45
66
 
67
+ if (wallaceConfig.flags.allowRepeaterSiblings) {
68
+ offsetTracker = this.a;
69
+ initialIndex = this.i;
70
+ if (offsetTracker) {
71
+ adjustment = countOffset(offsetTracker, initialIndex);
72
+ endAnchor = childNodes[previousKeysLength + adjustment] || null;
73
+ anchor = endAnchor;
74
+ untouched = false;
75
+ }
76
+ }
77
+
46
78
  // Working backwards saves us having to track moves.
47
79
  while (i >= 0) {
48
80
  item = items[i];
49
- key = item[keyName];
50
- component = pool[key] || (pool[key] = new componentDefinition());
81
+ itemKey = useKeyName ? item[keyName] : keyFn(item);
82
+ if (!(component = pool.get(itemKey))) {
83
+ component = new componentDefinition();
84
+ pool.set(itemKey, component);
85
+ }
51
86
  component.render(item, ctrl);
52
87
  el = component.el;
53
- if (untouched && !previousKeysSet.has(key)) {
88
+ if (untouched && !previousKeysSet.has(itemKey)) {
54
89
  frag.insertBefore(el, fragAnchor);
55
90
  fragAnchor = el;
56
91
  append = true;
57
92
  offset++;
58
93
  } else {
59
- if (key !== previousKeys[i + offset]) {
94
+ if (itemKey !== previousKeys[i + offset]) {
60
95
  e.insertBefore(el, anchor);
61
96
  untouched = false;
62
97
  }
63
98
  anchor = el;
64
99
  }
65
- newKeys.push(key);
66
- previousKeysSet.delete(key);
100
+ newKeys.push(itemKey);
101
+ previousKeysSet.delete(itemKey);
67
102
  i--;
68
103
  }
69
104
 
70
105
  if (append) {
71
- e.appendChild(frag);
106
+ e.insertBefore(frag, endAnchor);
72
107
  }
73
108
 
74
109
  let toStrip = previousKeysSet.size;
75
110
  while (toStrip > 0) {
76
- e.removeChild(childNodes[0]);
111
+ e.removeChild(childNodes[adjustment]);
77
112
  toStrip--;
78
113
  }
79
114
 
80
- this.keys = newKeys.reverse();
115
+ this.k = newKeys.reverse();
116
+
117
+ if (wallaceConfig.flags.allowRepeaterSiblings) {
118
+ if (offsetTracker) {
119
+ offsetTracker.set(initialIndex, itemsLength - 1);
120
+ }
121
+ }
81
122
  };
@@ -1,32 +1,60 @@
1
+ import { countOffset } from "../offsetter";
2
+
1
3
  /**
2
- * Repeats nested components, yielding from its pool sequentially.
4
+ * Repeats nested components, yielding from the pool sequentially.
3
5
  *
4
- * @param {componentDefinition} componentDefinition - The ComponentDefinition to create.
6
+ * COMPILER_MODS:
7
+ * if allowRepeaterSiblings is false the last two parameters are removed.
5
8
  */
6
- export function SequentialRepeater(componentDefinition) {
7
- this.def = componentDefinition;
8
- this.pool = []; // pool of component instances
9
- this.count = 0; // Child element count
9
+ export function SequentialRepeater(
10
+ componentDefinition,
11
+ pool,
12
+ adjustmentTracker,
13
+ initialIndex
14
+ ) {
15
+ this.d = componentDefinition;
16
+ this.p = pool; // pool of component instances. Must be an Array.
17
+ this.c = 0; // Child count
18
+ if (wallaceConfig.flags.allowRepeaterSiblings) {
19
+ this.a = adjustmentTracker;
20
+ this.i = initialIndex;
21
+ }
10
22
  }
11
23
 
12
24
  /**
13
25
  * Updates the element's childNodes to match the items.
14
26
  * Performance is important.
15
27
  *
16
- * @param {DOMElement} e - The DOM element to patch.
28
+ * @param {DOMElement} parent - The DOM element to patch.
17
29
  * @param {Array} items - Array of items which will be passed as props.
18
30
  * @param {any} ctrl - The parent item's controller.
19
31
  */
20
- SequentialRepeater.prototype.patch = function (e, items, ctrl) {
21
- const pool = this.pool,
22
- componentDefinition = this.def,
23
- childNodes = e.childNodes,
32
+ SequentialRepeater.prototype.patch = function (parent, items, ctrl) {
33
+ const pool = this.p,
34
+ componentDefinition = this.d,
35
+ previousChildCount = this.c,
24
36
  itemsLength = items.length,
25
- childElementCount = this.count;
37
+ childNodes = parent.childNodes;
38
+
26
39
  let i = 0,
40
+ offset = 0,
27
41
  component,
42
+ nextElement,
43
+ initialIndex,
44
+ offsetTracker,
45
+ endOfRange = previousChildCount,
28
46
  poolCount = pool.length;
29
47
 
48
+ if (wallaceConfig.flags.allowRepeaterSiblings) {
49
+ initialIndex = this.i;
50
+ offsetTracker = this.a;
51
+ if (offsetTracker) {
52
+ // The repeat element has siblings
53
+ offset = countOffset(offsetTracker, initialIndex);
54
+ endOfRange += offset;
55
+ nextElement = childNodes[endOfRange] || null;
56
+ }
57
+ }
30
58
  while (i < itemsLength) {
31
59
  if (i < poolCount) {
32
60
  component = pool[i];
@@ -36,15 +64,21 @@ SequentialRepeater.prototype.patch = function (e, items, ctrl) {
36
64
  poolCount++;
37
65
  }
38
66
  component.render(items[i], ctrl);
39
- if (i >= childElementCount) {
40
- e.appendChild(component.el);
67
+ if (i >= previousChildCount) {
68
+ parent.insertBefore(component.el, nextElement);
41
69
  }
42
70
  i++;
43
71
  }
44
- this.count = itemsLength;
45
- let lastIndex = childNodes.length - 1;
46
- const keepIndex = itemsLength - 1;
47
- for (let i = lastIndex; i > keepIndex; i--) {
48
- e.removeChild(childNodes[i]);
72
+ this.c = itemsLength;
73
+
74
+ let removeAtIndex = offset + previousChildCount - 1;
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) {
80
+ if (offsetTracker) {
81
+ offsetTracker.set(initialIndex, itemsLength - 1);
82
+ }
49
83
  }
50
84
  };
package/lib/router.js CHANGED
@@ -17,9 +17,8 @@ const converters = {
17
17
  date: v => new Date(v)
18
18
  };
19
19
 
20
- export function route(path, componentDef, converter, cleanup) {
21
- return new Route(path, componentDef, converter, cleanup);
22
- }
20
+ export const route = (path, componentDef, converter, cleanup) =>
21
+ new Route(path, componentDef, converter, cleanup);
23
22
 
24
23
  export const Router = () => <div></div>;
25
24
 
package/lib/stubs.js CHANGED
@@ -1,6 +1,4 @@
1
1
  /**
2
2
  * Gets a stub by name.
3
3
  */
4
- export function getStub(component, name) {
5
- return component.constructor.stubs[name];
6
- }
4
+ export const getStub = (component, name) => component.constructor.stubs[name];
@@ -0,0 +1 @@
1
+ export const toDateString = date => date.toISOString().slice(0, 10);
package/lib/types.d.ts CHANGED
@@ -204,6 +204,7 @@ const TaskList = (tasks) => (
204
204
  Notes:
205
205
 
206
206
  - You cannot use nest on the root element.
207
+ - You cannot use `if` on a nested element, only `show` and `hide`.
207
208
 
208
209
  ## 4. Repeating
209
210
 
@@ -239,8 +240,8 @@ const TaskList = (tasks) => (
239
240
 
240
241
  Notes:
241
242
 
242
- - You cannot repeat on the root element.
243
- - Repeat must be the only child element under its parent.
243
+ - You cannot use repeat on the root element.
244
+ - You cannot toggle visibility on the repeat element.
244
245
 
245
246
  ## 5. Directives
246
247
 
@@ -386,9 +387,7 @@ const TaskList: Uses<iTask[]> = (tasks) => (
386
387
  <div>
387
388
  First task:
388
389
  <Task.nest props={tasks[0]} />
389
- <div>
390
- <Task.repeat items={tasks.slice(1)} />
391
- </div>
390
+ <Task.repeat items={tasks.slice(1)} />
392
391
  </div>
393
392
  );
394
393
 
@@ -526,11 +525,12 @@ watchedObj[0] = 'foo'; // throws error.
526
525
  You can toggle flags in your babel config to disable certain features for cutting edge
527
526
  performance and bundle size:
528
527
 
529
- 1. useBase - enables use of `base` in components.
530
- 2. useControllers - enables use of `ctrl` in components.
531
- 3. useMethods - adds the `methods` helper to components.
532
- 4. useParts - enables use of parts.
533
- 5. useStubs - enables the use of stubs.
528
+ 1. allowBase - enables use of `base` in components.
529
+ 2. allowCtrl - enables use of `ctrl` in components.
530
+ 3. allowMethods - adds the `methods` helper to components.
531
+ 4. allowParts - enables use of parts.
532
+ 5. allowRepeaterSiblings - allows repeaters to have siblings.
533
+ 6. allowStubs - enables the use of stubs.
534
534
 
535
535
  These flags default to true, unless you specify `flags` in the plugin config, in which
536
536
  case they default to false and you need to explicitly enable those you want:
@@ -543,8 +543,8 @@ module.exports = {
543
543
  "babel-plugin-wallace",
544
544
  {
545
545
  flags: {
546
- useControllers: true,
547
- useStubs: false
546
+ allowCtrl: true,
547
+ allowStubs: false
548
548
  },
549
549
  }
550
550
  ],
@@ -841,46 +841,29 @@ interface DirectiveAttributes extends AllDomEvents {
841
841
  /**
842
842
  * ## Wallace directive: bind
843
843
  *
844
- * Sets up two-way binding:
845
- *
846
- * 1. It uses the expression as the element's value.
847
- * 2. It assigns the value back to the expression when the element's `change` event
848
- * fires.
849
- *
850
- * So this:
844
+ * Sets up two way binding between an input and data:
851
845
  *
852
846
  * ```
853
- * const MyComponent = ({name}) => (
854
- * <input type="text" bind={name}/>
855
- * );
847
+ * <input type="text" bind={name} />
856
848
  * ```
857
849
  *
858
850
  * Is the equivalent of this:
859
851
  *
860
- *```
861
- * const MyComponent = ({name}, {event}) => (
862
- * <input type="text" onChange={name = event.target.value} value={name}/>
863
- * );
864
852
  * ```
865
- *
866
- * In the case of a checkbox it uses `checked` instead of `value`, so is the equivalent of this:
853
+ * <input type="text" value={name} onChange={name = event.target.value} />
854
+ * ```
855
+ * By default it watches the `change` event, but you can specify a different one using
856
+ * the `event` directive:
867
857
  *
868
858
  * ```
869
- * const MyComponent = ({done}, {event}) => (
870
- * <input type="checkbox" onChange={done = event.target.checked} checked={done}/>
871
- * );
859
+ * <input type="text" bind={name} event:keyup />
872
860
  * ```
873
861
  *
874
- * By defaults it listens to the `change` event, but you can specify a different one:
862
+ * By default it binds to `value` but you can set a different property:
875
863
  *
876
864
  *```
877
- * const MyComponent = ({name}) => (
878
- * <input type="text" bind:KeyUp={name} />
879
- * );
865
+ * <input type="number" bind:valueAsNumber={name} />
880
866
  * ```
881
- *
882
- * Note that destructured props are converted to member expressions, so these examples
883
- * work even though it looks like you're setting a local variable.
884
867
  */
885
868
  bind?: MustBeExpression;
886
869
 
@@ -923,6 +906,19 @@ interface DirectiveAttributes extends AllDomEvents {
923
906
  */
924
907
  ctrl?: any;
925
908
 
909
+ /**
910
+ * ## Wallace directive: event
911
+ *
912
+ *
913
+ * Must be used with the `bind` directive, and causes it do watch a different event
914
+ * (the default is `change`)
915
+ *
916
+ * ```
917
+ * <input type="text" bind={name} event:keyup />
918
+ * ```
919
+ */
920
+ event?: string;
921
+
926
922
  /**
927
923
  * ## Wallace directive: fixed
928
924
  *
@@ -1078,7 +1074,6 @@ declare namespace JSX {
1078
1074
  * <MyComponent.repeat items={arrayOfProps} key="id"/>
1079
1075
  * <MyComponent.repeat items={arrayOfProps} key={(i) => i.id}/>
1080
1076
  * ```
1081
- * Note that repeated components may not have siblings.
1082
1077
  *
1083
1078
  * Available Wallace directives:
1084
1079
  *
@@ -1087,6 +1082,7 @@ declare namespace JSX {
1087
1082
  * - `class:xyz` defines a set of classes to be toggled.
1088
1083
  * - `css` shorthand for `fixed:class`.
1089
1084
  * - `ctrl` specifies ctrl for nested/repeated components.
1085
+ * - `event` changes the event which `bind` reacts to.
1090
1086
  * - `fixed:xyz` sets a attribute from an expression at definition.
1091
1087
  * - `hide` sets an element or component's hidden property.
1092
1088
  * - `html` Set the element's `innnerHTML` property.
package/lib/watch.js CHANGED
@@ -9,12 +9,13 @@ export function watch(target, callback) {
9
9
  const handler = {
10
10
  get(target, key) {
11
11
  if (key == "isProxy") return true;
12
- const prop = target[key];
13
- if (typeof prop == "undefined") return;
14
- if (typeof prop === "object") return new Proxy(prop, handler);
12
+ const prop = target[key],
13
+ propType = typeof prop;
14
+ if (propType == "undefined") return;
15
+ if (propType === "object") return new Proxy(prop, handler);
15
16
  if (
16
- Array.isArray(target) &&
17
17
  typeof target[key] === "function" &&
18
+ Array.isArray(target) &&
18
19
  MUTATING_METHODS.includes(key)
19
20
  ) {
20
21
  return (...args) => {
@@ -23,6 +24,9 @@ export function watch(target, callback) {
23
24
  return result;
24
25
  };
25
26
  }
27
+ if (target instanceof Date && propType === "function") {
28
+ return prop.bind(target);
29
+ }
26
30
  return prop;
27
31
  },
28
32
  set(target, key, value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallace",
3
- "version": "0.12.2",
3
+ "version": "0.14.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.12.2",
18
+ "babel-plugin-wallace": "^0.14.0",
19
19
  "browserify": "^17.0.1"
20
20
  },
21
- "gitHead": "7625a9b2fb654b6007a7ae97388a8c98995a49fe"
21
+ "gitHead": "94e0750d51118bf80ff69065fdb39a0f0f52dfc7"
22
22
  }
@@ -1,81 +0,0 @@
1
- // WARNING: Code here is near duplicated in keyedFn.
2
-
3
- /**
4
- * Repeats nested components, reusing items based on key.
5
- *
6
- * @param {function} componentDefinition - The ComponentDefinition to create.
7
- * @param {function} keyFn - A function which obtains the key.
8
- */
9
- export function KeyedFnRepeater(componentDefinition, keyFn) {
10
- this.def = componentDefinition;
11
- this.keyFn = keyFn;
12
- this.keys = []; // array of keys as last set.
13
- this.pool = {}; // pool of component instances.
14
- }
15
-
16
- /**
17
- * Updates the element's childNodes to match the items.
18
- * Performance is important.
19
- *
20
- * @param {DOMElement} e - The DOM element to patch.
21
- * @param {Array} items - Array of items which will be passed as props.
22
- * @param {any} ctrl - The parent item's controller.
23
- */
24
- KeyedFnRepeater.prototype.patch = function (e, items, ctrl) {
25
- const pool = this.pool,
26
- componentDefinition = this.def,
27
- keyFn = this.keyFn,
28
- childNodes = e.childNodes,
29
- itemsLength = items.length,
30
- previousKeys = this.keys,
31
- previousKeysLength = previousKeys.length,
32
- newKeys = [],
33
- previousKeysSet = new Set(previousKeys),
34
- frag = document.createDocumentFragment();
35
- let item,
36
- el,
37
- key,
38
- component,
39
- anchor = null,
40
- fragAnchor = null,
41
- untouched = true,
42
- append = false,
43
- offset = previousKeysLength - itemsLength,
44
- i = itemsLength - 1;
45
-
46
- // Working backwards saves us having to track moves.
47
- while (i >= 0) {
48
- item = items[i];
49
- key = keyFn(item);
50
- component = pool[key] || (pool[key] = new componentDefinition());
51
- component.render(item, ctrl);
52
- el = component.el;
53
- if (untouched && !previousKeysSet.has(key)) {
54
- frag.insertBefore(el, fragAnchor);
55
- fragAnchor = el;
56
- append = true;
57
- offset++;
58
- } else {
59
- if (key !== previousKeys[i + offset]) {
60
- e.insertBefore(el, anchor);
61
- untouched = false;
62
- }
63
- anchor = el;
64
- }
65
- newKeys.push(key);
66
- previousKeysSet.delete(key);
67
- i--;
68
- }
69
-
70
- if (append) {
71
- e.appendChild(frag);
72
- }
73
-
74
- let toStrip = previousKeysSet.size;
75
- while (toStrip > 0) {
76
- e.removeChild(childNodes[0]);
77
- toStrip--;
78
- }
79
-
80
- this.keys = newKeys.reverse();
81
- };