@odoo/owl 2.1.3 → 2.2.1

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/dist/owl.iife.js CHANGED
@@ -125,14 +125,16 @@
125
125
  }
126
126
  const node = "node" in params ? params.node : params.fiber.node;
127
127
  const fiber = "fiber" in params ? params.fiber : node.fiber;
128
- // resets the fibers on components if possible. This is important so that
129
- // new renderings can be properly included in the initial one, if any.
130
- let current = fiber;
131
- do {
132
- current.node.fiber = current;
133
- current = current.parent;
134
- } while (current);
135
- fibersInError.set(fiber.root, error);
128
+ if (fiber) {
129
+ // resets the fibers on components if possible. This is important so that
130
+ // new renderings can be properly included in the initial one, if any.
131
+ let current = fiber;
132
+ do {
133
+ current.node.fiber = current;
134
+ current = current.parent;
135
+ } while (current);
136
+ fibersInError.set(fiber.root, error);
137
+ }
136
138
  const handled = _handleError(node, error);
137
139
  if (!handled) {
138
140
  console.warn(`[Owl] Unhandled error. Destroying the root component`);
@@ -178,11 +180,21 @@
178
180
  }
179
181
  function attrsSetter(attrs) {
180
182
  if (isArray(attrs)) {
181
- setAttribute.call(this, attrs[0], attrs[1]);
183
+ if (attrs[0] === "class") {
184
+ setClass.call(this, attrs[1]);
185
+ }
186
+ else {
187
+ setAttribute.call(this, attrs[0], attrs[1]);
188
+ }
182
189
  }
183
190
  else {
184
191
  for (let k in attrs) {
185
- setAttribute.call(this, k, attrs[k]);
192
+ if (k === "class") {
193
+ setClass.call(this, attrs[k]);
194
+ }
195
+ else {
196
+ setAttribute.call(this, k, attrs[k]);
197
+ }
186
198
  }
187
199
  }
188
200
  }
@@ -194,7 +206,12 @@
194
206
  if (val === oldAttrs[1]) {
195
207
  return;
196
208
  }
197
- setAttribute.call(this, name, val);
209
+ if (name === "class") {
210
+ updateClass.call(this, val, oldAttrs[1]);
211
+ }
212
+ else {
213
+ setAttribute.call(this, name, val);
214
+ }
198
215
  }
199
216
  else {
200
217
  removeAttribute.call(this, oldAttrs[0]);
@@ -204,13 +221,23 @@
204
221
  else {
205
222
  for (let k in oldAttrs) {
206
223
  if (!(k in attrs)) {
207
- removeAttribute.call(this, k);
224
+ if (k === "class") {
225
+ updateClass.call(this, "", oldAttrs[k]);
226
+ }
227
+ else {
228
+ removeAttribute.call(this, k);
229
+ }
208
230
  }
209
231
  }
210
232
  for (let k in attrs) {
211
233
  const val = attrs[k];
212
234
  if (val !== oldAttrs[k]) {
213
- setAttribute.call(this, k, val);
235
+ if (k === "class") {
236
+ updateClass.call(this, val, oldAttrs[k]);
237
+ }
238
+ else {
239
+ setAttribute.call(this, k, val);
240
+ }
214
241
  }
215
242
  }
216
243
  }
@@ -289,20 +316,13 @@
289
316
  * @returns a batched version of the original callback
290
317
  */
291
318
  function batched(callback) {
292
- let called = false;
293
- return async () => {
294
- // This await blocks all calls to the callback here, then releases them sequentially
295
- // in the next microtick. This line decides the granularity of the batch.
296
- await Promise.resolve();
297
- if (!called) {
298
- called = true;
299
- // wait for all calls in this microtick to fall through before resetting "called"
300
- // so that only the first call to the batched function calls the original callback.
301
- // Schedule this before calling the callback so that calls to the batched function
302
- // within the callback will proceed only after resetting called to false, and have
303
- // a chance to execute the callback again
304
- Promise.resolve().then(() => (called = false));
305
- callback();
319
+ let scheduled = false;
320
+ return async (...args) => {
321
+ if (!scheduled) {
322
+ scheduled = true;
323
+ await Promise.resolve();
324
+ scheduled = false;
325
+ callback(...args);
306
326
  }
307
327
  };
308
328
  }
@@ -1635,8 +1655,7 @@
1635
1655
  let node = fiber.node;
1636
1656
  fiber.render = throwOnRender;
1637
1657
  if (node.status === 0 /* NEW */) {
1638
- node.destroy();
1639
- delete node.parent.children[node.parentKey];
1658
+ node.cancel();
1640
1659
  }
1641
1660
  node.fiber = null;
1642
1661
  if (fiber.bdom) {
@@ -2363,6 +2382,9 @@
2363
2382
  }
2364
2383
  }
2365
2384
  async render(deep) {
2385
+ if (this.status >= 2 /* CANCELLED */) {
2386
+ return;
2387
+ }
2366
2388
  let current = this.fiber;
2367
2389
  if (current && (current.root.locked || current.bdom === true)) {
2368
2390
  await Promise.resolve();
@@ -2388,7 +2410,7 @@
2388
2410
  this.fiber = fiber;
2389
2411
  this.app.scheduler.addFiber(fiber);
2390
2412
  await Promise.resolve();
2391
- if (this.status === 2 /* DESTROYED */) {
2413
+ if (this.status >= 2 /* CANCELLED */) {
2392
2414
  return;
2393
2415
  }
2394
2416
  // We only want to actually render the component if the following two
@@ -2406,6 +2428,18 @@
2406
2428
  fiber.render();
2407
2429
  }
2408
2430
  }
2431
+ cancel() {
2432
+ this._cancel();
2433
+ delete this.parent.children[this.parentKey];
2434
+ this.app.scheduler.scheduleDestroy(this);
2435
+ }
2436
+ _cancel() {
2437
+ this.status = 2 /* CANCELLED */;
2438
+ const children = this.children;
2439
+ for (let childKey in children) {
2440
+ children[childKey]._cancel();
2441
+ }
2442
+ }
2409
2443
  destroy() {
2410
2444
  let shouldRemove = this.status === 1 /* MOUNTED */;
2411
2445
  this._destroy();
@@ -2433,7 +2467,7 @@
2433
2467
  this.app.handleError({ error: e, node: this });
2434
2468
  }
2435
2469
  }
2436
- this.status = 2 /* DESTROYED */;
2470
+ this.status = 3 /* DESTROYED */;
2437
2471
  }
2438
2472
  async updateAndRender(props, parentFiber) {
2439
2473
  this.nextProps = props;
@@ -2966,12 +3000,22 @@
2966
3000
  keys = collection;
2967
3001
  values = collection;
2968
3002
  }
2969
- else if (collection) {
2970
- values = Object.keys(collection);
2971
- keys = Object.values(collection);
3003
+ else if (collection instanceof Map) {
3004
+ keys = [...collection.keys()];
3005
+ values = [...collection.values()];
3006
+ }
3007
+ else if (collection && typeof collection === "object") {
3008
+ if (Symbol.iterator in collection) {
3009
+ keys = [...collection];
3010
+ values = keys;
3011
+ }
3012
+ else {
3013
+ values = Object.keys(collection);
3014
+ keys = Object.values(collection);
3015
+ }
2972
3016
  }
2973
3017
  else {
2974
- throw new OwlError("Invalid loop expression");
3018
+ throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
2975
3019
  }
2976
3020
  const n = values.length;
2977
3021
  return [keys, values, n, new Array(n)];
@@ -3878,6 +3922,10 @@
3878
3922
  })
3879
3923
  .join("");
3880
3924
  }
3925
+ translate(str) {
3926
+ const match = translationRE.exec(str);
3927
+ return match[1] + this.translateFn(match[2]) + match[3];
3928
+ }
3881
3929
  /**
3882
3930
  * @returns the newly created block name, if any
3883
3931
  */
@@ -3955,8 +4003,7 @@
3955
4003
  let { block, forceNewBlock } = ctx;
3956
4004
  let value = ast.value;
3957
4005
  if (value && ctx.translate !== false) {
3958
- const match = translationRE.exec(value);
3959
- value = match[1] + this.translateFn(match[2]) + match[3];
4006
+ value = this.translate(value);
3960
4007
  }
3961
4008
  if (!ctx.inPreTag) {
3962
4009
  value = value.replace(whitespaceRE, " ");
@@ -4497,11 +4544,12 @@
4497
4544
  else {
4498
4545
  let value;
4499
4546
  if (ast.defaultValue) {
4547
+ const defaultValue = ctx.translate ? this.translate(ast.defaultValue) : ast.defaultValue;
4500
4548
  if (ast.value) {
4501
- value = `withDefault(${expr}, \`${ast.defaultValue}\`)`;
4549
+ value = `withDefault(${expr}, \`${defaultValue}\`)`;
4502
4550
  }
4503
4551
  else {
4504
- value = `\`${ast.defaultValue}\``;
4552
+ value = `\`${defaultValue}\``;
4505
4553
  }
4506
4554
  }
4507
4555
  else {
@@ -4794,10 +4842,10 @@
4794
4842
  parseTCall(node, ctx) ||
4795
4843
  parseTCallBlock(node) ||
4796
4844
  parseTEscNode(node, ctx) ||
4845
+ parseTOutNode(node, ctx) ||
4797
4846
  parseTKey(node, ctx) ||
4798
4847
  parseTTranslation(node, ctx) ||
4799
4848
  parseTSlot(node, ctx) ||
4800
- parseTOutNode(node, ctx) ||
4801
4849
  parseComponent(node, ctx) ||
4802
4850
  parseDOMNode(node, ctx) ||
4803
4851
  parseTSetNode(node, ctx) ||
@@ -4882,10 +4930,10 @@
4882
4930
  let model = null;
4883
4931
  for (let attr of nodeAttrsNames) {
4884
4932
  const value = node.getAttribute(attr);
4885
- if (attr.startsWith("t-on")) {
4886
- if (attr === "t-on") {
4887
- throw new OwlError("Missing event name with t-on directive");
4888
- }
4933
+ if (attr === "t-on" || attr === "t-on-") {
4934
+ throw new OwlError("Missing event name with t-on directive");
4935
+ }
4936
+ if (attr.startsWith("t-on-")) {
4889
4937
  on = on || {};
4890
4938
  on[attr.slice(5)] = value;
4891
4939
  }
@@ -4910,10 +4958,8 @@
4910
4958
  const typeAttr = node.getAttribute("type");
4911
4959
  const isInput = tagName === "input";
4912
4960
  const isSelect = tagName === "select";
4913
- const isTextarea = tagName === "textarea";
4914
4961
  const isCheckboxInput = isInput && typeAttr === "checkbox";
4915
4962
  const isRadioInput = isInput && typeAttr === "radio";
4916
- const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
4917
4963
  const hasLazyMod = attr.includes(".lazy");
4918
4964
  const hasNumberMod = attr.includes(".number");
4919
4965
  const hasTrimMod = attr.includes(".trim");
@@ -4925,8 +4971,8 @@
4925
4971
  specialInitTargetAttr: isRadioInput ? "checked" : null,
4926
4972
  eventType,
4927
4973
  hasDynamicChildren: false,
4928
- shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
4929
- shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
4974
+ shouldTrim: hasTrimMod,
4975
+ shouldNumberize: hasNumberMod,
4930
4976
  };
4931
4977
  if (isSelect) {
4932
4978
  // don't pollute the original ctx
@@ -4989,9 +5035,6 @@
4989
5035
  content: [tesc],
4990
5036
  };
4991
5037
  }
4992
- if (ast.type === 11 /* TComponent */) {
4993
- throw new OwlError("t-esc is not supported on Component nodes");
4994
- }
4995
5038
  return tesc;
4996
5039
  }
4997
5040
  // -----------------------------------------------------------------------------
@@ -5433,19 +5476,21 @@
5433
5476
  *
5434
5477
  * @param el the element containing the tree that should be normalized
5435
5478
  */
5436
- function normalizeTEsc(el) {
5437
- const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5438
- for (const el of elements) {
5439
- if (el.childNodes.length) {
5440
- throw new OwlError("Cannot have t-esc on a component that already has content");
5441
- }
5442
- const value = el.getAttribute("t-esc");
5443
- el.removeAttribute("t-esc");
5444
- const t = el.ownerDocument.createElement("t");
5445
- if (value != null) {
5446
- t.setAttribute("t-esc", value);
5479
+ function normalizeTEscTOut(el) {
5480
+ for (const d of ["t-esc", "t-out"]) {
5481
+ const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5482
+ for (const el of elements) {
5483
+ if (el.childNodes.length) {
5484
+ throw new OwlError(`Cannot have ${d} on a component that already has content`);
5485
+ }
5486
+ const value = el.getAttribute(d);
5487
+ el.removeAttribute(d);
5488
+ const t = el.ownerDocument.createElement("t");
5489
+ if (value != null) {
5490
+ t.setAttribute(d, value);
5491
+ }
5492
+ el.appendChild(t);
5447
5493
  }
5448
- el.appendChild(t);
5449
5494
  }
5450
5495
  }
5451
5496
  /**
@@ -5456,7 +5501,7 @@
5456
5501
  */
5457
5502
  function normalizeXML(el) {
5458
5503
  normalizeTIf(el);
5459
- normalizeTEsc(el);
5504
+ normalizeTEscTOut(el);
5460
5505
  }
5461
5506
  /**
5462
5507
  * Parses an XML string into an XML document, throwing errors on parser errors
@@ -5509,7 +5554,7 @@
5509
5554
  }
5510
5555
 
5511
5556
  // do not modify manually. This file is generated by the release script.
5512
- const version = "2.1.2";
5557
+ const version = "2.2";
5513
5558
 
5514
5559
  // -----------------------------------------------------------------------------
5515
5560
  // Scheduler
@@ -5519,11 +5564,18 @@
5519
5564
  this.tasks = new Set();
5520
5565
  this.frame = 0;
5521
5566
  this.delayedRenders = [];
5567
+ this.cancelledNodes = new Set();
5522
5568
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
5523
5569
  }
5524
5570
  addFiber(fiber) {
5525
5571
  this.tasks.add(fiber.root);
5526
5572
  }
5573
+ scheduleDestroy(node) {
5574
+ this.cancelledNodes.add(node);
5575
+ if (this.frame === 0) {
5576
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5577
+ }
5578
+ }
5527
5579
  /**
5528
5580
  * Process all current tasks. This only applies to the fibers that are ready.
5529
5581
  * Other tasks are left unchanged.
@@ -5533,21 +5585,28 @@
5533
5585
  let renders = this.delayedRenders;
5534
5586
  this.delayedRenders = [];
5535
5587
  for (let f of renders) {
5536
- if (f.root && f.node.status !== 2 /* DESTROYED */ && f.node.fiber === f) {
5588
+ if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
5537
5589
  f.render();
5538
5590
  }
5539
5591
  }
5540
5592
  }
5541
5593
  if (this.frame === 0) {
5542
- this.frame = this.requestAnimationFrame(() => {
5543
- this.frame = 0;
5544
- this.tasks.forEach((fiber) => this.processFiber(fiber));
5545
- for (let task of this.tasks) {
5546
- if (task.node.status === 2 /* DESTROYED */) {
5547
- this.tasks.delete(task);
5548
- }
5549
- }
5550
- });
5594
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5595
+ }
5596
+ }
5597
+ processTasks() {
5598
+ this.frame = 0;
5599
+ for (let node of this.cancelledNodes) {
5600
+ node._destroy();
5601
+ }
5602
+ this.cancelledNodes.clear();
5603
+ for (let task of this.tasks) {
5604
+ this.processFiber(task);
5605
+ }
5606
+ for (let task of this.tasks) {
5607
+ if (task.node.status === 3 /* DESTROYED */) {
5608
+ this.tasks.delete(task);
5609
+ }
5551
5610
  }
5552
5611
  }
5553
5612
  processFiber(fiber) {
@@ -5560,7 +5619,7 @@
5560
5619
  this.tasks.delete(fiber);
5561
5620
  return;
5562
5621
  }
5563
- if (fiber.node.status === 2 /* DESTROYED */) {
5622
+ if (fiber.node.status === 3 /* DESTROYED */) {
5564
5623
  this.tasks.delete(fiber);
5565
5624
  return;
5566
5625
  }
@@ -5588,6 +5647,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5588
5647
  apps: new Set(),
5589
5648
  Fiber: Fiber,
5590
5649
  RootFiber: RootFiber,
5650
+ toRaw: toRaw,
5651
+ reactive: reactive,
5591
5652
  });
5592
5653
  class App extends TemplateSet {
5593
5654
  constructor(Root, config = {}) {
@@ -5782,9 +5843,11 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5782
5843
  switch (component.__owl__.status) {
5783
5844
  case 0 /* NEW */:
5784
5845
  return "new";
5846
+ case 2 /* CANCELLED */:
5847
+ return "cancelled";
5785
5848
  case 1 /* MOUNTED */:
5786
5849
  return "mounted";
5787
- case 2 /* DESTROYED */:
5850
+ case 3 /* DESTROYED */:
5788
5851
  return "destroyed";
5789
5852
  }
5790
5853
  }
@@ -5840,8 +5903,9 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5840
5903
  * will run a cleanup function before patching and before unmounting the
5841
5904
  * the component.
5842
5905
  *
5843
- * @param {Effect} effect the effect to run on component mount and/or patch
5844
- * @param {()=>any[]} [computeDependencies=()=>[NaN]] a callback to compute
5906
+ * @template T
5907
+ * @param {Effect<T>} effect the effect to run on component mount and/or patch
5908
+ * @param {()=>T} [computeDependencies=()=>[NaN]] a callback to compute
5845
5909
  * dependencies that will decide if the effect needs to be cleaned up and
5846
5910
  * run again. If the dependencies did not change, the effect will not run
5847
5911
  * again. The default value returns an array containing only NaN because
@@ -5959,8 +6023,8 @@ See https://github.com/odoo/owl/blob/${hash}/doc/reference/app.md#configuration
5959
6023
  Object.defineProperty(exports, '__esModule', { value: true });
5960
6024
 
5961
6025
 
5962
- __info__.date = '2023-04-29T07:45:54.333Z';
5963
- __info__.hash = 'aabb755';
6026
+ __info__.date = '2023-07-19T13:22:24.480Z';
6027
+ __info__.hash = '2e07799';
5964
6028
  __info__.url = 'https://github.com/odoo/owl';
5965
6029
 
5966
6030