@odoo/owl 3.0.0-alpha.20 → 3.0.0-alpha.22
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/README.md +100 -110
- package/dist/compile_templates.mjs +84 -80
- package/dist/owl-devtools.zip +0 -0
- package/dist/owl.cjs.js +1612 -1473
- package/dist/owl.es.js +1612 -1473
- package/dist/owl.iife.js +1612 -1473
- package/dist/owl.iife.min.js +1 -1
- package/dist/types/compiler/code_generator.d.ts +1 -2
- package/dist/types/compiler/inline_expressions.d.ts +7 -24
- package/dist/types/compiler/parser.d.ts +3 -8
- package/dist/types/owl.d.ts +33 -27
- package/dist/types/runtime/blockdom/attributes.d.ts +2 -0
- package/dist/types/runtime/component_node.d.ts +2 -0
- package/dist/types/runtime/plugin_manager.d.ts +2 -0
- package/dist/types/runtime/reactivity/computations.d.ts +1 -0
- package/dist/types/runtime/rendering/scheduler.d.ts +1 -1
- package/dist/types/runtime/rendering/template_helpers.d.ts +2 -2
- package/dist/types/runtime/types.d.ts +3 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +25 -2
- package/dist/compiler.js +0 -2371
- package/dist/owl.cjs.runtime.js +0 -4070
- package/dist/owl.es.runtime.js +0 -4026
- package/dist/owl.iife.runtime.js +0 -4074
- package/dist/owl.iife.runtime.min.js +0 -1
- package/dist/types/common/types.d.ts +0 -1
- package/dist/types/runtime/cancellableContext.d.ts +0 -15
- package/dist/types/runtime/cancellablePromise.d.ts +0 -15
- package/dist/types/runtime/error_handling.d.ts +0 -13
- package/dist/types/runtime/executionContext.d.ts +0 -0
- package/dist/types/runtime/fibers.d.ts +0 -37
- package/dist/types/runtime/listOperation.d.ts +0 -1
- package/dist/types/runtime/model.d.ts +0 -48
- package/dist/types/runtime/plugins.d.ts +0 -36
- package/dist/types/runtime/portal.d.ts +0 -12
- package/dist/types/runtime/reactivity/async_computed.d.ts +0 -1
- package/dist/types/runtime/reactivity/derived.d.ts +0 -7
- package/dist/types/runtime/reactivity/reactivity.d.ts +0 -46
- package/dist/types/runtime/reactivity/signals.d.ts +0 -30
- package/dist/types/runtime/reactivity/state.d.ts +0 -48
- package/dist/types/runtime/reactivity.d.ts +0 -57
- package/dist/types/runtime/relationalModel/discussModel.d.ts +0 -19
- package/dist/types/runtime/relationalModel/discussModelTypes.d.ts +0 -22
- package/dist/types/runtime/relationalModel/field.d.ts +0 -20
- package/dist/types/runtime/relationalModel/model.d.ts +0 -59
- package/dist/types/runtime/relationalModel/modelData.d.ts +0 -18
- package/dist/types/runtime/relationalModel/modelRegistry.d.ts +0 -3
- package/dist/types/runtime/relationalModel/modelUtils.d.ts +0 -4
- package/dist/types/runtime/relationalModel/store.d.ts +0 -16
- package/dist/types/runtime/relationalModel/types.d.ts +0 -83
- package/dist/types/runtime/relationalModel/util.d.ts +0 -1
- package/dist/types/runtime/relationalModel/web/WebDataPoint.d.ts +0 -25
- package/dist/types/runtime/relationalModel/web/WebRecord.d.ts +0 -131
- package/dist/types/runtime/relationalModel/web/WebStaticList.d.ts +0 -63
- package/dist/types/runtime/relationalModel/web/webModel.d.ts +0 -5
- package/dist/types/runtime/relationalModel/web/webModelTypes.d.ts +0 -139
- package/dist/types/runtime/scheduler.d.ts +0 -21
- package/dist/types/runtime/signals.d.ts +0 -19
- package/dist/types/runtime/suspense.d.ts +0 -5
- package/dist/types/runtime/task.d.ts +0 -12
- package/dist/types/runtime/template_helpers.d.ts +0 -58
- package/dist/types/utils/registry.d.ts +0 -15
package/README.md
CHANGED
|
@@ -1,149 +1,139 @@
|
|
|
1
|
-
<h1 align="center">🦉 <a href="https://odoo.github.io/owl/">Owl
|
|
1
|
+
<h1 align="center">🦉 <a href="https://odoo.github.io/owl/">Owl</a> 🦉</h1>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<strong>A modern, lightweight UI framework for applications that scale</strong>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
[](https://www.gnu.org/licenses/lgpl-3.0)
|
|
4
8
|
[](https://badge.fury.io/js/@odoo%2Fowl)
|
|
5
9
|
[](https://www.npmjs.com/package/@odoo/owl)
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
---
|
|
8
12
|
|
|
9
|
-
**
|
|
13
|
+
> **Owl 3.0.0 Alpha:** This is an alpha release. The API and features are subject to change without notice.
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
---
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
[Odoo](https://www.odoo.com/) for its products. Owl is a modern
|
|
15
|
-
framework, written in Typescript, taking the best ideas from React and Vue in a
|
|
16
|
-
simple and consistent way. Owl's main features are:
|
|
17
|
+
## Try it now
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- asynchronous rendering
|
|
19
|
+
The fastest way to discover Owl is the **[online playground](https://odoo.github.io/owl/playground)**.
|
|
20
|
+
It features interactive examples, a live editor, and showcases all major features:
|
|
21
|
+
reactivity, components, plugins, and more. It also includes **guided tutorials**
|
|
22
|
+
and is the recommended way to learn about Owl.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
underlying virtual DOM, integrates beautifully with hooks, and the rendering is
|
|
26
|
-
asynchronous.
|
|
24
|
+
## What is Owl?
|
|
27
25
|
|
|
28
|
-
|
|
26
|
+
Owl is a modern UI framework (~20kb gzipped, zero dependencies) written in TypeScript,
|
|
27
|
+
built by [Odoo](https://www.odoo.com/). It powers Odoo's web client, one of the largest
|
|
28
|
+
open-source business applications, but is equally suited for small projects and prototypes.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
- [changelog](CHANGELOG.md) (from Owl 1.x to 2.x),
|
|
32
|
-
- [playground](https://odoo.github.io/owl/playground)
|
|
30
|
+
Key features:
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
- **Signal-based reactivity** — Explicit, composable, and debuggable state management
|
|
33
|
+
- **Plugin system** — Type-safe, composable sharing of state and services
|
|
34
|
+
- **Class-based components** — Familiar OOP patterns with ES6 classes
|
|
35
|
+
- **Declarative templates** — XML templates with a clean syntax
|
|
36
|
+
- **Async rendering** — Concurrent mode for smooth user experiences
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
## Quick Example
|
|
37
39
|
|
|
38
40
|
```javascript
|
|
39
|
-
|
|
41
|
+
import { Component, signal, computed, mount, xml } from "@odoo/owl";
|
|
40
42
|
|
|
41
|
-
class
|
|
43
|
+
class TodoList extends Component {
|
|
42
44
|
static template = xml`
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
<input placeholder="Add todo..." t-on-keydown="this.onKeydown"/>
|
|
46
|
+
<ul>
|
|
47
|
+
<t t-foreach="this.todos()" t-as="todo" t-key="todo.id">
|
|
48
|
+
<li t-att-class="{ done: todo.done }">
|
|
49
|
+
<input type="checkbox" t-model="todo.done"/>
|
|
50
|
+
<t t-out="todo.text"/>
|
|
51
|
+
</li>
|
|
52
|
+
</t>
|
|
53
|
+
</ul>
|
|
54
|
+
<p t-if="this.remaining() > 0">
|
|
55
|
+
<t t-out="this.remaining()"/> item(s) remaining
|
|
56
|
+
</p>`;
|
|
57
|
+
|
|
58
|
+
todos = signal.Array([
|
|
59
|
+
{ id: 1, text: "Learn Owl", done: false },
|
|
60
|
+
{ id: 2, text: "Build something", done: false },
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
remaining = computed(() => this.todos().filter((t) => !t.done).length);
|
|
64
|
+
|
|
65
|
+
onKeydown(ev) {
|
|
66
|
+
if (ev.key === "Enter" && ev.target.value) {
|
|
67
|
+
this.todos.push({
|
|
68
|
+
id: Date.now(),
|
|
69
|
+
text: ev.target.value,
|
|
70
|
+
done: false,
|
|
71
|
+
});
|
|
72
|
+
ev.target.value = "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
48
75
|
}
|
|
49
76
|
|
|
50
|
-
|
|
51
|
-
static template = xml`
|
|
52
|
-
<span>Hello Owl</span>
|
|
53
|
-
<Counter increment="2"/>`;
|
|
54
|
-
|
|
55
|
-
static components = { Counter };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
mount(Root, document.body);
|
|
77
|
+
mount(TodoList, document.body);
|
|
59
78
|
```
|
|
60
79
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
More interesting examples can be found on the
|
|
66
|
-
[playground](https://odoo.github.io/owl/playground) application.
|
|
80
|
+
This example demonstrates Owl's reactivity: `todos` is a signal, `remaining`
|
|
81
|
+
is a computed value that updates automatically, and the UI reacts to changes
|
|
82
|
+
without manual subscription management.
|
|
67
83
|
|
|
68
84
|
## Documentation
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
The documentation below is for **Owl 3**. For the Owl 2 documentation, see the
|
|
87
|
+
[owl-2.x branch](https://github.com/odoo/owl/tree/owl-2.x).
|
|
71
88
|
|
|
72
|
-
|
|
89
|
+
### Getting Started
|
|
73
90
|
|
|
74
|
-
- [
|
|
75
|
-
- [
|
|
76
|
-
- [
|
|
91
|
+
- **[Playground](https://odoo.github.io/owl/playground)** — Interactive examples and live coding
|
|
92
|
+
- [Tutorial: Getting Started](https://odoo.github.io/owl/playground#getting_started) — Learn Owl fundamentals step by step
|
|
93
|
+
- [Tutorial: Todo List](https://odoo.github.io/owl/playground#todo_list) — Build a full TodoMVC app
|
|
94
|
+
- [Tutorial: Hibou OS](https://odoo.github.io/owl/playground#hibou_os) — Build a desktop-like interface
|
|
77
95
|
|
|
78
96
|
### Reference
|
|
79
97
|
|
|
80
|
-
- [
|
|
81
|
-
- [App](doc/reference/app.md)
|
|
82
|
-
- [Component](doc/reference/component.md)
|
|
83
|
-
- [
|
|
84
|
-
- [
|
|
85
|
-
- [
|
|
86
|
-
- [
|
|
87
|
-
- [
|
|
88
|
-
- [
|
|
89
|
-
- [
|
|
90
|
-
- [
|
|
91
|
-
- [
|
|
92
|
-
- [
|
|
93
|
-
- [
|
|
94
|
-
- [
|
|
95
|
-
- [
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- [
|
|
100
|
-
- [
|
|
101
|
-
- [
|
|
102
|
-
- [
|
|
103
|
-
- [Sub components](doc/reference/component.md#sub-components)
|
|
104
|
-
- [Sub templates](doc/reference/templates.md#sub-templates)
|
|
105
|
-
- [Templates (Qweb)](doc/reference/templates.md)
|
|
106
|
-
- [Translations](doc/reference/translations.md)
|
|
107
|
-
- [Utils](doc/reference/utils.md)
|
|
108
|
-
|
|
109
|
-
### Other Topics
|
|
110
|
-
|
|
111
|
-
- [Notes On Owl Architecture](doc/miscellaneous/architecture.md)
|
|
98
|
+
- [API Reference](doc/readme.md) — A complete list of everything exported by the Owl library
|
|
99
|
+
- [App](doc/reference/app.md) — Configure and mount an Owl application to the DOM
|
|
100
|
+
- [Component](doc/reference/component.md) — Define components with lifecycle methods and static properties
|
|
101
|
+
- [Error Handling](doc/reference/error_handling.md) — Catch and recover from errors in components
|
|
102
|
+
- [Event Handling](doc/reference/event_handling.md) — Handle DOM events with t-on directives
|
|
103
|
+
- [Form Bindings](doc/reference/form_bindings.md) — Bind form inputs to reactive state with t-model
|
|
104
|
+
- [Hooks](doc/reference/hooks.md) — Use lifecycle hooks and other built-in hooks in components
|
|
105
|
+
- [Plugins](doc/reference/plugins.md) — Share state and services across components with type-safe plugins
|
|
106
|
+
- [Props](doc/reference/props.md) — Pass data to child components with validation and defaults
|
|
107
|
+
- [Reactivity](doc/reference/reactivity.md) — Manage state with signals, computed values, and reactive objects
|
|
108
|
+
- [Refs](doc/reference/refs.md) — Access DOM elements from components with t-ref
|
|
109
|
+
- [Resources and Registries](doc/reference/resources_and_registries.md) — Ordered reactive collections for shared data
|
|
110
|
+
- [Slots](doc/reference/slots.md) — Compose components with named and dynamic slot content
|
|
111
|
+
- [Template Syntax](doc/reference/template_syntax.md) — Write XML templates with QWeb directives
|
|
112
|
+
- [Translations](doc/reference/translations.md) — Translate templates and dynamic strings
|
|
113
|
+
- [Types Validation](doc/reference/types_validation.md) — Validate data structures at runtime with a declarative schema
|
|
114
|
+
|
|
115
|
+
### Misc
|
|
116
|
+
|
|
117
|
+
- [Owl 3.x Release Notes](release_notes.md) — Complete guide to all changes in Owl 3
|
|
118
|
+
- [Design Principles](doc/miscellaneous/design_principles.md)
|
|
119
|
+
- [Why we built Owl](doc/miscellaneous/why_owl.md)
|
|
120
|
+
- [Architecture Notes](doc/miscellaneous/architecture.md)
|
|
112
121
|
- [Comparison with React/Vue](doc/miscellaneous/comparison.md)
|
|
113
|
-
- [Why did Odoo build Owl?](doc/miscellaneous/why_owl.md)
|
|
114
|
-
- [Changelog (from owl 1.x to 2.x)](CHANGELOG.md)
|
|
115
|
-
- [Notes on compiled templates](doc/miscellaneous/compiled_template.md)
|
|
116
|
-
- [Owl devtools extension](doc/tools/devtools.md)
|
|
117
122
|
|
|
118
|
-
##
|
|
123
|
+
## Installation
|
|
119
124
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```
|
|
125
|
+
```bash
|
|
123
126
|
npm install @odoo/owl
|
|
124
127
|
```
|
|
125
|
-
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
|
126
|
-
|
|
127
|
-
- [owl](https://github.com/odoo/owl/releases/latest)
|
|
128
|
-
|
|
129
|
-
## Installing Owl devtools
|
|
130
|
-
|
|
131
|
-
The Owl devtools browser extension is also available in the [release](https://github.com/odoo/owl/releases/latest):
|
|
132
|
-
Unzip the owl-devtools.zip file and follow the instructions depending on your browser:
|
|
133
|
-
|
|
134
|
-
### Chrome
|
|
135
128
|
|
|
136
|
-
|
|
137
|
-
Select the devtools-chrome folder and that's it, your extension is active!
|
|
138
|
-
There is a convenient refresh button on the extension card (still on the same admin page) to update your code.
|
|
139
|
-
Do note that if you got some problems, you may need to completly remove and reload the extension to completly refresh the extension.
|
|
129
|
+
Or download directly: [latest release](https://github.com/odoo/owl/releases/latest)
|
|
140
130
|
|
|
141
|
-
|
|
142
|
-
Go to the address about:debugging#/runtime/this-firefox and click on `Load temporary Add-on...`.
|
|
143
|
-
Select any file in the devtools-firefox folder and that's it, your extension is active!
|
|
144
|
-
Here, you can use the reload button to refresh the extension.
|
|
131
|
+
## Devtools
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
|
|
133
|
+
The Owl devtools extension helps debug your applications with component tree
|
|
134
|
+
inspection, state visualization, and performance profiling. Download it from
|
|
135
|
+
the [releases page](https://github.com/odoo/owl/releases/latest).
|
|
148
136
|
|
|
137
|
+
## License
|
|
149
138
|
|
|
139
|
+
Owl is released under the [LGPL v3](https://www.gnu.org/licenses/lgpl-3.0) license.
|
|
@@ -229,11 +229,18 @@ const isRightSeparator = (token) => token && (token.type === "RIGHT_BRACE" || to
|
|
|
229
229
|
* the arrow operator, then we add the current (or some previous tokens) token to
|
|
230
230
|
* the list of variables so it does not get replaced by a lookup in the context
|
|
231
231
|
*/
|
|
232
|
-
|
|
232
|
+
// Leading spaces are trimmed during tokenization, so they need to be added back for some values
|
|
233
|
+
const paddedValues = new Map([["in ", " in "]]);
|
|
234
|
+
/**
|
|
235
|
+
* Processes a javascript expression: compiles variable lookups and detects
|
|
236
|
+
* top-level arrow functions with their free variables, all in a single pass.
|
|
237
|
+
*/
|
|
238
|
+
function processExpr(expr) {
|
|
233
239
|
const localVars = new Set();
|
|
234
240
|
const tokens = tokenize(expr);
|
|
235
241
|
let i = 0;
|
|
236
242
|
let stack = []; // to track last opening (, [ or {
|
|
243
|
+
let topLevelArrowIndex = -1;
|
|
237
244
|
while (i < tokens.length) {
|
|
238
245
|
let token = tokens[i];
|
|
239
246
|
let prevToken = tokens[i - 1];
|
|
@@ -274,18 +281,21 @@ function compileExprToArray(expr) {
|
|
|
274
281
|
token.value = token.replace((expr) => compileExpr(expr));
|
|
275
282
|
}
|
|
276
283
|
if (nextToken && nextToken.type === "OPERATOR" && nextToken.value === "=>") {
|
|
284
|
+
if (stack.length === 0) {
|
|
285
|
+
topLevelArrowIndex = i + 1;
|
|
286
|
+
}
|
|
277
287
|
if (token.type === "RIGHT_PAREN") {
|
|
278
288
|
let j = i - 1;
|
|
279
289
|
while (j > 0 && tokens[j].type !== "LEFT_PAREN") {
|
|
280
290
|
if (tokens[j].type === "SYMBOL" && tokens[j].originalValue) {
|
|
281
291
|
tokens[j].value = tokens[j].originalValue;
|
|
282
|
-
localVars.add(tokens[j].value);
|
|
292
|
+
localVars.add(tokens[j].value);
|
|
283
293
|
}
|
|
284
294
|
j--;
|
|
285
295
|
}
|
|
286
296
|
}
|
|
287
297
|
else {
|
|
288
|
-
localVars.add(token.value);
|
|
298
|
+
localVars.add(token.value);
|
|
289
299
|
}
|
|
290
300
|
}
|
|
291
301
|
if (isVar) {
|
|
@@ -306,14 +316,24 @@ function compileExprToArray(expr) {
|
|
|
306
316
|
token.isLocal = true;
|
|
307
317
|
}
|
|
308
318
|
}
|
|
309
|
-
|
|
319
|
+
// Collect free variables from arrow function body
|
|
320
|
+
let freeVariables = null;
|
|
321
|
+
if (topLevelArrowIndex !== -1) {
|
|
322
|
+
freeVariables = [];
|
|
323
|
+
const seen = new Set();
|
|
324
|
+
for (let i = topLevelArrowIndex + 1; i < tokens.length; i++) {
|
|
325
|
+
const t = tokens[i];
|
|
326
|
+
if (t.varName && !t.isLocal && t.varName !== "this" && !seen.has(t.varName)) {
|
|
327
|
+
seen.add(t.varName);
|
|
328
|
+
freeVariables.push(t.varName);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const compiled = tokens.map((t) => paddedValues.get(t.value) || t.value).join("");
|
|
333
|
+
return { expr: compiled, freeVariables };
|
|
310
334
|
}
|
|
311
|
-
// Leading spaces are trimmed during tokenization, so they need to be added back for some values
|
|
312
|
-
const paddedValues = new Map([["in ", " in "]]);
|
|
313
335
|
function compileExpr(expr) {
|
|
314
|
-
return
|
|
315
|
-
.map((t) => paddedValues.get(t.value) || t.value)
|
|
316
|
-
.join("");
|
|
336
|
+
return processExpr(expr).expr;
|
|
317
337
|
}
|
|
318
338
|
const INTERP_REGEXP = /\{\{.*?\}\}|\#\{.*?\}/g;
|
|
319
339
|
function replaceDynamicParts(s, replacer) {
|
|
@@ -332,8 +352,11 @@ const zero = Symbol("zero");
|
|
|
332
352
|
const whitespaceRE = /\s+/g;
|
|
333
353
|
// using a non-html document so that <inner/outer>HTML serializes as XML instead
|
|
334
354
|
// of HTML (as we will parse it as xml later)
|
|
335
|
-
|
|
336
|
-
|
|
355
|
+
let xmlDoc;
|
|
356
|
+
if (typeof document !== "undefined") {
|
|
357
|
+
xmlDoc = document.implementation.createDocument(null, null, null);
|
|
358
|
+
}
|
|
359
|
+
const MODS = new Set(["stop", "capture", "prevent", "self", "synthetic", "passive"]);
|
|
337
360
|
let nextDataIds = {};
|
|
338
361
|
function generateId(prefix = "") {
|
|
339
362
|
nextDataIds[prefix] = (nextDataIds[prefix] || 0) + 1;
|
|
@@ -693,8 +716,6 @@ class CodeGenerator {
|
|
|
693
716
|
return this.compileTTranslation(ast, ctx);
|
|
694
717
|
case 16 /* ASTType.TTranslationContext */:
|
|
695
718
|
return this.compileTTranslationContext(ast, ctx);
|
|
696
|
-
case 17 /* ASTType.TPortal */:
|
|
697
|
-
return this.compileTPortal(ast, ctx);
|
|
698
719
|
}
|
|
699
720
|
}
|
|
700
721
|
compileDebug(ast, ctx) {
|
|
@@ -780,7 +801,8 @@ class CodeGenerator {
|
|
|
780
801
|
hoistedExpr = `(ctx,${bareArrowMatch[1]})=>${rest}`;
|
|
781
802
|
}
|
|
782
803
|
else {
|
|
783
|
-
|
|
804
|
+
this.helpers.add("callHandler");
|
|
805
|
+
hoistedExpr = `(ctx, ev) => callHandler(${compiled}, ctx, ev)`;
|
|
784
806
|
}
|
|
785
807
|
const id = generateId("hdlr_fn");
|
|
786
808
|
this.staticDefs.push({ id, expr: hoistedExpr });
|
|
@@ -858,11 +880,22 @@ class CodeGenerator {
|
|
|
858
880
|
// t-model
|
|
859
881
|
let tModelSelectedExpr;
|
|
860
882
|
if (ast.model) {
|
|
861
|
-
const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, } = ast.model;
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
883
|
+
const { hasDynamicChildren, expr, eventType, shouldNumberize, shouldTrim, targetAttr, specialInitTargetAttr, isProxy, } = ast.model;
|
|
884
|
+
let readExpr;
|
|
885
|
+
let writeExpr;
|
|
886
|
+
if (isProxy) {
|
|
887
|
+
const expression = compileExpr(expr);
|
|
888
|
+
readExpr = expression;
|
|
889
|
+
writeExpr = (value) => `${expression} = ${value}`;
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
const exprId = generateId("expr");
|
|
893
|
+
const expression = compileExpr(expr);
|
|
894
|
+
this.helpers.add("modelExpr");
|
|
895
|
+
this.define(exprId, `modelExpr(${expression})`);
|
|
896
|
+
readExpr = `${exprId}()`;
|
|
897
|
+
writeExpr = (value) => `${exprId}.set(${value})`;
|
|
898
|
+
}
|
|
866
899
|
let idx;
|
|
867
900
|
if (specialInitTargetAttr) {
|
|
868
901
|
let targetExpr = targetAttr in attrs && `'${attrs[targetAttr]}'`;
|
|
@@ -873,23 +906,23 @@ class CodeGenerator {
|
|
|
873
906
|
targetExpr = compileExpr(dynamicTgExpr);
|
|
874
907
|
}
|
|
875
908
|
}
|
|
876
|
-
idx = block.insertData(`${
|
|
909
|
+
idx = block.insertData(`${readExpr} === ${targetExpr}`, "prop");
|
|
877
910
|
attrs[`block-property-${idx}`] = specialInitTargetAttr;
|
|
878
911
|
}
|
|
879
912
|
else if (hasDynamicChildren) {
|
|
880
913
|
const bValueId = generateId("bValue");
|
|
881
914
|
tModelSelectedExpr = `${bValueId}`;
|
|
882
|
-
this.define(tModelSelectedExpr,
|
|
915
|
+
this.define(tModelSelectedExpr, readExpr);
|
|
883
916
|
}
|
|
884
917
|
else {
|
|
885
|
-
idx = block.insertData(
|
|
918
|
+
idx = block.insertData(readExpr, "prop");
|
|
886
919
|
attrs[`block-property-${idx}`] = targetAttr;
|
|
887
920
|
}
|
|
888
921
|
this.helpers.add("toNumber");
|
|
889
922
|
let valueCode = `ev.target.${targetAttr}`;
|
|
890
923
|
valueCode = shouldTrim ? `${valueCode}.trim()` : valueCode;
|
|
891
924
|
valueCode = shouldNumberize ? `toNumber(${valueCode})` : valueCode;
|
|
892
|
-
const handler = `[(ctx, ev) => { ${
|
|
925
|
+
const handler = `[(ctx, ev) => { ${writeExpr(valueCode)}; }, ctx]`;
|
|
893
926
|
idx = block.insertData(handler, "hdlr");
|
|
894
927
|
attrs[`block-handler-${idx}`] = eventType;
|
|
895
928
|
}
|
|
@@ -1182,12 +1215,11 @@ class CodeGenerator {
|
|
|
1182
1215
|
if (ast.context) {
|
|
1183
1216
|
const dynCtxVar = generateId("ctx");
|
|
1184
1217
|
this.addLine(`const ${dynCtxVar} = ${compileExpr(ast.context)};`);
|
|
1185
|
-
if (
|
|
1186
|
-
ctxExpr = `Object.assign({
|
|
1218
|
+
if (attrs.length) {
|
|
1219
|
+
ctxExpr = `Object.assign({this: ${dynCtxVar}}, ${ctxString})`;
|
|
1187
1220
|
}
|
|
1188
1221
|
else {
|
|
1189
|
-
|
|
1190
|
-
ctxExpr = `Object.assign({}, ${dynCtxVar}, ${thisCtx}${attrs.length ? ", " + ctxString : ""})`;
|
|
1222
|
+
ctxExpr = `{this: ${dynCtxVar}}`;
|
|
1191
1223
|
}
|
|
1192
1224
|
}
|
|
1193
1225
|
else {
|
|
@@ -1332,9 +1364,29 @@ class CodeGenerator {
|
|
|
1332
1364
|
let { block } = ctx;
|
|
1333
1365
|
// props
|
|
1334
1366
|
const hasSlotsProp = "slots" in (ast.props || {});
|
|
1335
|
-
const props =
|
|
1336
|
-
|
|
1337
|
-
|
|
1367
|
+
const props = [];
|
|
1368
|
+
const propList = [];
|
|
1369
|
+
for (let p in ast.props || {}) {
|
|
1370
|
+
let [name, suffix] = p.split(".");
|
|
1371
|
+
if (suffix) {
|
|
1372
|
+
// .alike, .bind, .translate — delegate to formatProp, no propList entry
|
|
1373
|
+
props.push(this.formatProp(p, ast.props[p], ast.propsTranslationCtx, ctx.translationCtx));
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1376
|
+
const { expr: compiledValue, freeVariables } = processExpr(ast.props[p]);
|
|
1377
|
+
const propName = /^[a-z_]+$/i.test(name) ? name : `'${name}'`;
|
|
1378
|
+
props.push(`${propName}: ${compiledValue || undefined}`);
|
|
1379
|
+
if (freeVariables) {
|
|
1380
|
+
for (const varName of freeVariables) {
|
|
1381
|
+
const syntheticKey = `\x01${name}.${varName}`;
|
|
1382
|
+
propList.push(`"${syntheticKey}"`);
|
|
1383
|
+
props.push(`"${syntheticKey}": ctx['${varName}']`);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
else {
|
|
1387
|
+
propList.push(`"${name}"`);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1338
1390
|
// slots
|
|
1339
1391
|
let slotDef = "";
|
|
1340
1392
|
if (ast.slots) {
|
|
@@ -1391,13 +1443,6 @@ class CodeGenerator {
|
|
|
1391
1443
|
keyArg = `${ctx.tKeyExpr} + ${keyArg}`;
|
|
1392
1444
|
}
|
|
1393
1445
|
let id = generateId("comp");
|
|
1394
|
-
const propList = [];
|
|
1395
|
-
for (let p in ast.props || {}) {
|
|
1396
|
-
let [name, suffix] = p.split(".");
|
|
1397
|
-
if (!suffix) {
|
|
1398
|
-
propList.push(`"${name}"`);
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
1446
|
this.helpers.add("createComponent");
|
|
1402
1447
|
this.staticDefs.push({
|
|
1403
1448
|
id,
|
|
@@ -1502,26 +1547,6 @@ class CodeGenerator {
|
|
|
1502
1547
|
}
|
|
1503
1548
|
return null;
|
|
1504
1549
|
}
|
|
1505
|
-
compileTPortal(ast, ctx) {
|
|
1506
|
-
this.helpers.add("Portal");
|
|
1507
|
-
let { block } = ctx;
|
|
1508
|
-
const name = this.compileInNewTarget("slot", ast.content, ctx);
|
|
1509
|
-
let id = generateId("comp");
|
|
1510
|
-
this.helpers.add("createComponent");
|
|
1511
|
-
this.staticDefs.push({
|
|
1512
|
-
id,
|
|
1513
|
-
expr: `createComponent(app, null, false, true, false, false)`,
|
|
1514
|
-
});
|
|
1515
|
-
const target = compileExpr(ast.target);
|
|
1516
|
-
const key = this.generateComponentKey();
|
|
1517
|
-
const blockString = `${id}({target: ${target},slots: {'default': {__render: ${name}.bind(this), __ctx: ctx}}}, ${key}, node, ctx, Portal)`;
|
|
1518
|
-
if (block) {
|
|
1519
|
-
this.insertAnchor(block);
|
|
1520
|
-
}
|
|
1521
|
-
block = this.createBlock(block, "multi", ctx);
|
|
1522
|
-
this.insertBlock(blockString, block, { ...ctx, forceNewBlock: false });
|
|
1523
|
-
return block.varName;
|
|
1524
|
-
}
|
|
1525
1550
|
}
|
|
1526
1551
|
|
|
1527
1552
|
/**
|
|
@@ -1593,7 +1618,6 @@ function parseNode(node, ctx) {
|
|
|
1593
1618
|
parseTDebugLog(node, ctx) ||
|
|
1594
1619
|
parseTForEach(node, ctx) ||
|
|
1595
1620
|
parseTIf(node, ctx) ||
|
|
1596
|
-
parseTPortal(node, ctx) ||
|
|
1597
1621
|
parseTTranslation(node, ctx) ||
|
|
1598
1622
|
parseTTranslationContext(node, ctx) ||
|
|
1599
1623
|
parseTCall(node, ctx) ||
|
|
@@ -1740,6 +1764,7 @@ function parseDOMNode(node, ctx) {
|
|
|
1740
1764
|
const hasTrimMod = attr.includes(".trim");
|
|
1741
1765
|
const hasLazyMod = hasTrimMod || attr.includes(".lazy");
|
|
1742
1766
|
const hasNumberMod = attr.includes(".number");
|
|
1767
|
+
const hasProxyMod = attr.includes(".proxy");
|
|
1743
1768
|
const eventType = isRadioInput ? "click" : isSelect || hasLazyMod ? "change" : "input";
|
|
1744
1769
|
model = {
|
|
1745
1770
|
expr: value,
|
|
@@ -1749,6 +1774,7 @@ function parseDOMNode(node, ctx) {
|
|
|
1749
1774
|
hasDynamicChildren: false,
|
|
1750
1775
|
shouldTrim: hasTrimMod,
|
|
1751
1776
|
shouldNumberize: hasNumberMod,
|
|
1777
|
+
isProxy: hasProxyMod,
|
|
1752
1778
|
};
|
|
1753
1779
|
if (isSelect) {
|
|
1754
1780
|
// don't pollute the original ctx
|
|
@@ -2220,28 +2246,6 @@ function parseTTranslationContext(node, ctx) {
|
|
|
2220
2246
|
return wrapInTTranslationContextAST(result, translationCtx);
|
|
2221
2247
|
}
|
|
2222
2248
|
// -----------------------------------------------------------------------------
|
|
2223
|
-
// Portal
|
|
2224
|
-
// -----------------------------------------------------------------------------
|
|
2225
|
-
function parseTPortal(node, ctx) {
|
|
2226
|
-
if (!node.hasAttribute("t-portal")) {
|
|
2227
|
-
return null;
|
|
2228
|
-
}
|
|
2229
|
-
const target = node.getAttribute("t-portal");
|
|
2230
|
-
node.removeAttribute("t-portal");
|
|
2231
|
-
const content = parseNode(node, ctx);
|
|
2232
|
-
if (!content) {
|
|
2233
|
-
return {
|
|
2234
|
-
type: 0 /* ASTType.Text */,
|
|
2235
|
-
value: "",
|
|
2236
|
-
};
|
|
2237
|
-
}
|
|
2238
|
-
return {
|
|
2239
|
-
type: 17 /* ASTType.TPortal */,
|
|
2240
|
-
target,
|
|
2241
|
-
content,
|
|
2242
|
-
};
|
|
2243
|
-
}
|
|
2244
|
-
// -----------------------------------------------------------------------------
|
|
2245
2249
|
// helpers
|
|
2246
2250
|
// -----------------------------------------------------------------------------
|
|
2247
2251
|
/**
|
package/dist/owl-devtools.zip
CHANGED
|
Binary file
|