@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.es.js CHANGED
@@ -122,14 +122,16 @@ function handleError(params) {
122
122
  }
123
123
  const node = "node" in params ? params.node : params.fiber.node;
124
124
  const fiber = "fiber" in params ? params.fiber : node.fiber;
125
- // resets the fibers on components if possible. This is important so that
126
- // new renderings can be properly included in the initial one, if any.
127
- let current = fiber;
128
- do {
129
- current.node.fiber = current;
130
- current = current.parent;
131
- } while (current);
132
- fibersInError.set(fiber.root, error);
125
+ if (fiber) {
126
+ // resets the fibers on components if possible. This is important so that
127
+ // new renderings can be properly included in the initial one, if any.
128
+ let current = fiber;
129
+ do {
130
+ current.node.fiber = current;
131
+ current = current.parent;
132
+ } while (current);
133
+ fibersInError.set(fiber.root, error);
134
+ }
133
135
  const handled = _handleError(node, error);
134
136
  if (!handled) {
135
137
  console.warn(`[Owl] Unhandled error. Destroying the root component`);
@@ -175,11 +177,21 @@ function createAttrUpdater(attr) {
175
177
  }
176
178
  function attrsSetter(attrs) {
177
179
  if (isArray(attrs)) {
178
- setAttribute.call(this, attrs[0], attrs[1]);
180
+ if (attrs[0] === "class") {
181
+ setClass.call(this, attrs[1]);
182
+ }
183
+ else {
184
+ setAttribute.call(this, attrs[0], attrs[1]);
185
+ }
179
186
  }
180
187
  else {
181
188
  for (let k in attrs) {
182
- setAttribute.call(this, k, attrs[k]);
189
+ if (k === "class") {
190
+ setClass.call(this, attrs[k]);
191
+ }
192
+ else {
193
+ setAttribute.call(this, k, attrs[k]);
194
+ }
183
195
  }
184
196
  }
185
197
  }
@@ -191,7 +203,12 @@ function attrsUpdater(attrs, oldAttrs) {
191
203
  if (val === oldAttrs[1]) {
192
204
  return;
193
205
  }
194
- setAttribute.call(this, name, val);
206
+ if (name === "class") {
207
+ updateClass.call(this, val, oldAttrs[1]);
208
+ }
209
+ else {
210
+ setAttribute.call(this, name, val);
211
+ }
195
212
  }
196
213
  else {
197
214
  removeAttribute.call(this, oldAttrs[0]);
@@ -201,13 +218,23 @@ function attrsUpdater(attrs, oldAttrs) {
201
218
  else {
202
219
  for (let k in oldAttrs) {
203
220
  if (!(k in attrs)) {
204
- removeAttribute.call(this, k);
221
+ if (k === "class") {
222
+ updateClass.call(this, "", oldAttrs[k]);
223
+ }
224
+ else {
225
+ removeAttribute.call(this, k);
226
+ }
205
227
  }
206
228
  }
207
229
  for (let k in attrs) {
208
230
  const val = attrs[k];
209
231
  if (val !== oldAttrs[k]) {
210
- setAttribute.call(this, k, val);
232
+ if (k === "class") {
233
+ updateClass.call(this, val, oldAttrs[k]);
234
+ }
235
+ else {
236
+ setAttribute.call(this, k, val);
237
+ }
211
238
  }
212
239
  }
213
240
  }
@@ -286,20 +313,13 @@ function updateClass(val, oldVal) {
286
313
  * @returns a batched version of the original callback
287
314
  */
288
315
  function batched(callback) {
289
- let called = false;
290
- return async () => {
291
- // This await blocks all calls to the callback here, then releases them sequentially
292
- // in the next microtick. This line decides the granularity of the batch.
293
- await Promise.resolve();
294
- if (!called) {
295
- called = true;
296
- // wait for all calls in this microtick to fall through before resetting "called"
297
- // so that only the first call to the batched function calls the original callback.
298
- // Schedule this before calling the callback so that calls to the batched function
299
- // within the callback will proceed only after resetting called to false, and have
300
- // a chance to execute the callback again
301
- Promise.resolve().then(() => (called = false));
302
- callback();
316
+ let scheduled = false;
317
+ return async (...args) => {
318
+ if (!scheduled) {
319
+ scheduled = true;
320
+ await Promise.resolve();
321
+ scheduled = false;
322
+ callback(...args);
303
323
  }
304
324
  };
305
325
  }
@@ -1632,8 +1652,7 @@ function cancelFibers(fibers) {
1632
1652
  let node = fiber.node;
1633
1653
  fiber.render = throwOnRender;
1634
1654
  if (node.status === 0 /* NEW */) {
1635
- node.destroy();
1636
- delete node.parent.children[node.parentKey];
1655
+ node.cancel();
1637
1656
  }
1638
1657
  node.fiber = null;
1639
1658
  if (fiber.bdom) {
@@ -2360,6 +2379,9 @@ class ComponentNode {
2360
2379
  }
2361
2380
  }
2362
2381
  async render(deep) {
2382
+ if (this.status >= 2 /* CANCELLED */) {
2383
+ return;
2384
+ }
2363
2385
  let current = this.fiber;
2364
2386
  if (current && (current.root.locked || current.bdom === true)) {
2365
2387
  await Promise.resolve();
@@ -2385,7 +2407,7 @@ class ComponentNode {
2385
2407
  this.fiber = fiber;
2386
2408
  this.app.scheduler.addFiber(fiber);
2387
2409
  await Promise.resolve();
2388
- if (this.status === 2 /* DESTROYED */) {
2410
+ if (this.status >= 2 /* CANCELLED */) {
2389
2411
  return;
2390
2412
  }
2391
2413
  // We only want to actually render the component if the following two
@@ -2403,6 +2425,18 @@ class ComponentNode {
2403
2425
  fiber.render();
2404
2426
  }
2405
2427
  }
2428
+ cancel() {
2429
+ this._cancel();
2430
+ delete this.parent.children[this.parentKey];
2431
+ this.app.scheduler.scheduleDestroy(this);
2432
+ }
2433
+ _cancel() {
2434
+ this.status = 2 /* CANCELLED */;
2435
+ const children = this.children;
2436
+ for (let childKey in children) {
2437
+ children[childKey]._cancel();
2438
+ }
2439
+ }
2406
2440
  destroy() {
2407
2441
  let shouldRemove = this.status === 1 /* MOUNTED */;
2408
2442
  this._destroy();
@@ -2430,7 +2464,7 @@ class ComponentNode {
2430
2464
  this.app.handleError({ error: e, node: this });
2431
2465
  }
2432
2466
  }
2433
- this.status = 2 /* DESTROYED */;
2467
+ this.status = 3 /* DESTROYED */;
2434
2468
  }
2435
2469
  async updateAndRender(props, parentFiber) {
2436
2470
  this.nextProps = props;
@@ -2963,12 +2997,22 @@ function prepareList(collection) {
2963
2997
  keys = collection;
2964
2998
  values = collection;
2965
2999
  }
2966
- else if (collection) {
2967
- values = Object.keys(collection);
2968
- keys = Object.values(collection);
3000
+ else if (collection instanceof Map) {
3001
+ keys = [...collection.keys()];
3002
+ values = [...collection.values()];
3003
+ }
3004
+ else if (collection && typeof collection === "object") {
3005
+ if (Symbol.iterator in collection) {
3006
+ keys = [...collection];
3007
+ values = keys;
3008
+ }
3009
+ else {
3010
+ values = Object.keys(collection);
3011
+ keys = Object.values(collection);
3012
+ }
2969
3013
  }
2970
3014
  else {
2971
- throw new OwlError("Invalid loop expression");
3015
+ throw new OwlError(`Invalid loop expression: "${collection}" is not iterable`);
2972
3016
  }
2973
3017
  const n = values.length;
2974
3018
  return [keys, values, n, new Array(n)];
@@ -3875,6 +3919,10 @@ class CodeGenerator {
3875
3919
  })
3876
3920
  .join("");
3877
3921
  }
3922
+ translate(str) {
3923
+ const match = translationRE.exec(str);
3924
+ return match[1] + this.translateFn(match[2]) + match[3];
3925
+ }
3878
3926
  /**
3879
3927
  * @returns the newly created block name, if any
3880
3928
  */
@@ -3952,8 +4000,7 @@ class CodeGenerator {
3952
4000
  let { block, forceNewBlock } = ctx;
3953
4001
  let value = ast.value;
3954
4002
  if (value && ctx.translate !== false) {
3955
- const match = translationRE.exec(value);
3956
- value = match[1] + this.translateFn(match[2]) + match[3];
4003
+ value = this.translate(value);
3957
4004
  }
3958
4005
  if (!ctx.inPreTag) {
3959
4006
  value = value.replace(whitespaceRE, " ");
@@ -4494,11 +4541,12 @@ class CodeGenerator {
4494
4541
  else {
4495
4542
  let value;
4496
4543
  if (ast.defaultValue) {
4544
+ const defaultValue = ctx.translate ? this.translate(ast.defaultValue) : ast.defaultValue;
4497
4545
  if (ast.value) {
4498
- value = `withDefault(${expr}, \`${ast.defaultValue}\`)`;
4546
+ value = `withDefault(${expr}, \`${defaultValue}\`)`;
4499
4547
  }
4500
4548
  else {
4501
- value = `\`${ast.defaultValue}\``;
4549
+ value = `\`${defaultValue}\``;
4502
4550
  }
4503
4551
  }
4504
4552
  else {
@@ -4791,10 +4839,10 @@ function parseNode(node, ctx) {
4791
4839
  parseTCall(node, ctx) ||
4792
4840
  parseTCallBlock(node) ||
4793
4841
  parseTEscNode(node, ctx) ||
4842
+ parseTOutNode(node, ctx) ||
4794
4843
  parseTKey(node, ctx) ||
4795
4844
  parseTTranslation(node, ctx) ||
4796
4845
  parseTSlot(node, ctx) ||
4797
- parseTOutNode(node, ctx) ||
4798
4846
  parseComponent(node, ctx) ||
4799
4847
  parseDOMNode(node, ctx) ||
4800
4848
  parseTSetNode(node, ctx) ||
@@ -4879,10 +4927,10 @@ function parseDOMNode(node, ctx) {
4879
4927
  let model = null;
4880
4928
  for (let attr of nodeAttrsNames) {
4881
4929
  const value = node.getAttribute(attr);
4882
- if (attr.startsWith("t-on")) {
4883
- if (attr === "t-on") {
4884
- throw new OwlError("Missing event name with t-on directive");
4885
- }
4930
+ if (attr === "t-on" || attr === "t-on-") {
4931
+ throw new OwlError("Missing event name with t-on directive");
4932
+ }
4933
+ if (attr.startsWith("t-on-")) {
4886
4934
  on = on || {};
4887
4935
  on[attr.slice(5)] = value;
4888
4936
  }
@@ -4907,10 +4955,8 @@ function parseDOMNode(node, ctx) {
4907
4955
  const typeAttr = node.getAttribute("type");
4908
4956
  const isInput = tagName === "input";
4909
4957
  const isSelect = tagName === "select";
4910
- const isTextarea = tagName === "textarea";
4911
4958
  const isCheckboxInput = isInput && typeAttr === "checkbox";
4912
4959
  const isRadioInput = isInput && typeAttr === "radio";
4913
- const isOtherInput = isInput && !isCheckboxInput && !isRadioInput;
4914
4960
  const hasLazyMod = attr.includes(".lazy");
4915
4961
  const hasNumberMod = attr.includes(".number");
4916
4962
  const hasTrimMod = attr.includes(".trim");
@@ -4922,8 +4968,8 @@ function parseDOMNode(node, ctx) {
4922
4968
  specialInitTargetAttr: isRadioInput ? "checked" : null,
4923
4969
  eventType,
4924
4970
  hasDynamicChildren: false,
4925
- shouldTrim: hasTrimMod && (isOtherInput || isTextarea),
4926
- shouldNumberize: hasNumberMod && (isOtherInput || isTextarea),
4971
+ shouldTrim: hasTrimMod,
4972
+ shouldNumberize: hasNumberMod,
4927
4973
  };
4928
4974
  if (isSelect) {
4929
4975
  // don't pollute the original ctx
@@ -4986,9 +5032,6 @@ function parseTEscNode(node, ctx) {
4986
5032
  content: [tesc],
4987
5033
  };
4988
5034
  }
4989
- if (ast.type === 11 /* TComponent */) {
4990
- throw new OwlError("t-esc is not supported on Component nodes");
4991
- }
4992
5035
  return tesc;
4993
5036
  }
4994
5037
  // -----------------------------------------------------------------------------
@@ -5430,19 +5473,21 @@ function normalizeTIf(el) {
5430
5473
  *
5431
5474
  * @param el the element containing the tree that should be normalized
5432
5475
  */
5433
- function normalizeTEsc(el) {
5434
- const elements = [...el.querySelectorAll("[t-esc]")].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5435
- for (const el of elements) {
5436
- if (el.childNodes.length) {
5437
- throw new OwlError("Cannot have t-esc on a component that already has content");
5438
- }
5439
- const value = el.getAttribute("t-esc");
5440
- el.removeAttribute("t-esc");
5441
- const t = el.ownerDocument.createElement("t");
5442
- if (value != null) {
5443
- t.setAttribute("t-esc", value);
5476
+ function normalizeTEscTOut(el) {
5477
+ for (const d of ["t-esc", "t-out"]) {
5478
+ const elements = [...el.querySelectorAll(`[${d}]`)].filter((el) => el.tagName[0] === el.tagName[0].toUpperCase() || el.hasAttribute("t-component"));
5479
+ for (const el of elements) {
5480
+ if (el.childNodes.length) {
5481
+ throw new OwlError(`Cannot have ${d} on a component that already has content`);
5482
+ }
5483
+ const value = el.getAttribute(d);
5484
+ el.removeAttribute(d);
5485
+ const t = el.ownerDocument.createElement("t");
5486
+ if (value != null) {
5487
+ t.setAttribute(d, value);
5488
+ }
5489
+ el.appendChild(t);
5444
5490
  }
5445
- el.appendChild(t);
5446
5491
  }
5447
5492
  }
5448
5493
  /**
@@ -5453,7 +5498,7 @@ function normalizeTEsc(el) {
5453
5498
  */
5454
5499
  function normalizeXML(el) {
5455
5500
  normalizeTIf(el);
5456
- normalizeTEsc(el);
5501
+ normalizeTEscTOut(el);
5457
5502
  }
5458
5503
  /**
5459
5504
  * Parses an XML string into an XML document, throwing errors on parser errors
@@ -5506,7 +5551,7 @@ function compile(template, options = {}) {
5506
5551
  }
5507
5552
 
5508
5553
  // do not modify manually. This file is generated by the release script.
5509
- const version = "2.1.2";
5554
+ const version = "2.2";
5510
5555
 
5511
5556
  // -----------------------------------------------------------------------------
5512
5557
  // Scheduler
@@ -5516,11 +5561,18 @@ class Scheduler {
5516
5561
  this.tasks = new Set();
5517
5562
  this.frame = 0;
5518
5563
  this.delayedRenders = [];
5564
+ this.cancelledNodes = new Set();
5519
5565
  this.requestAnimationFrame = Scheduler.requestAnimationFrame;
5520
5566
  }
5521
5567
  addFiber(fiber) {
5522
5568
  this.tasks.add(fiber.root);
5523
5569
  }
5570
+ scheduleDestroy(node) {
5571
+ this.cancelledNodes.add(node);
5572
+ if (this.frame === 0) {
5573
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5574
+ }
5575
+ }
5524
5576
  /**
5525
5577
  * Process all current tasks. This only applies to the fibers that are ready.
5526
5578
  * Other tasks are left unchanged.
@@ -5530,21 +5582,28 @@ class Scheduler {
5530
5582
  let renders = this.delayedRenders;
5531
5583
  this.delayedRenders = [];
5532
5584
  for (let f of renders) {
5533
- if (f.root && f.node.status !== 2 /* DESTROYED */ && f.node.fiber === f) {
5585
+ if (f.root && f.node.status !== 3 /* DESTROYED */ && f.node.fiber === f) {
5534
5586
  f.render();
5535
5587
  }
5536
5588
  }
5537
5589
  }
5538
5590
  if (this.frame === 0) {
5539
- this.frame = this.requestAnimationFrame(() => {
5540
- this.frame = 0;
5541
- this.tasks.forEach((fiber) => this.processFiber(fiber));
5542
- for (let task of this.tasks) {
5543
- if (task.node.status === 2 /* DESTROYED */) {
5544
- this.tasks.delete(task);
5545
- }
5546
- }
5547
- });
5591
+ this.frame = this.requestAnimationFrame(() => this.processTasks());
5592
+ }
5593
+ }
5594
+ processTasks() {
5595
+ this.frame = 0;
5596
+ for (let node of this.cancelledNodes) {
5597
+ node._destroy();
5598
+ }
5599
+ this.cancelledNodes.clear();
5600
+ for (let task of this.tasks) {
5601
+ this.processFiber(task);
5602
+ }
5603
+ for (let task of this.tasks) {
5604
+ if (task.node.status === 3 /* DESTROYED */) {
5605
+ this.tasks.delete(task);
5606
+ }
5548
5607
  }
5549
5608
  }
5550
5609
  processFiber(fiber) {
@@ -5557,7 +5616,7 @@ class Scheduler {
5557
5616
  this.tasks.delete(fiber);
5558
5617
  return;
5559
5618
  }
5560
- if (fiber.node.status === 2 /* DESTROYED */) {
5619
+ if (fiber.node.status === 3 /* DESTROYED */) {
5561
5620
  this.tasks.delete(fiber);
5562
5621
  return;
5563
5622
  }
@@ -5585,6 +5644,8 @@ window.__OWL_DEVTOOLS__ || (window.__OWL_DEVTOOLS__ = {
5585
5644
  apps: new Set(),
5586
5645
  Fiber: Fiber,
5587
5646
  RootFiber: RootFiber,
5647
+ toRaw: toRaw,
5648
+ reactive: reactive,
5588
5649
  });
5589
5650
  class App extends TemplateSet {
5590
5651
  constructor(Root, config = {}) {
@@ -5779,9 +5840,11 @@ function status(component) {
5779
5840
  switch (component.__owl__.status) {
5780
5841
  case 0 /* NEW */:
5781
5842
  return "new";
5843
+ case 2 /* CANCELLED */:
5844
+ return "cancelled";
5782
5845
  case 1 /* MOUNTED */:
5783
5846
  return "mounted";
5784
- case 2 /* DESTROYED */:
5847
+ case 3 /* DESTROYED */:
5785
5848
  return "destroyed";
5786
5849
  }
5787
5850
  }
@@ -5837,8 +5900,9 @@ function useChildSubEnv(envExtension) {
5837
5900
  * will run a cleanup function before patching and before unmounting the
5838
5901
  * the component.
5839
5902
  *
5840
- * @param {Effect} effect the effect to run on component mount and/or patch
5841
- * @param {()=>any[]} [computeDependencies=()=>[NaN]] a callback to compute
5903
+ * @template T
5904
+ * @param {Effect<T>} effect the effect to run on component mount and/or patch
5905
+ * @param {()=>T} [computeDependencies=()=>[NaN]] a callback to compute
5842
5906
  * dependencies that will decide if the effect needs to be cleaned up and
5843
5907
  * run again. If the dependencies did not change, the effect will not run
5844
5908
  * again. The default value returns an array containing only NaN because
@@ -5920,6 +5984,6 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
5920
5984
  export { App, Component, EventBus, OwlError, __info__, blockDom, loadFile, markRaw, markup, mount, onError, onMounted, onPatched, onRendered, onWillDestroy, onWillPatch, onWillRender, onWillStart, onWillUnmount, onWillUpdateProps, reactive, status, toRaw, useChildSubEnv, useComponent, useEffect, useEnv, useExternalListener, useRef, useState, useSubEnv, validate, validateType, whenReady, xml };
5921
5985
 
5922
5986
 
5923
- __info__.date = '2023-04-29T07:45:54.333Z';
5924
- __info__.hash = 'aabb755';
5987
+ __info__.date = '2023-07-19T13:22:24.480Z';
5988
+ __info__.hash = '2e07799';
5925
5989
  __info__.url = 'https://github.com/odoo/owl';