@joist/templating 4.2.4-next.4 → 4.2.4-next.5
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 +0 -9
- package/package.json +1 -1
- package/src/lib/bind.test.ts +72 -0
- package/src/lib/bind.ts +7 -4
- package/src/lib/elements/async.element.ts +2 -2
- package/src/lib/elements/bind.element.test.ts +2 -2
- package/src/lib/elements/bind.element.ts +4 -4
- package/src/lib/elements/for.element.ts +16 -9
- package/src/lib/elements/if.element.test.ts +272 -238
- package/src/lib/elements/if.element.ts +5 -5
- package/src/lib/elements/scope.ts +1 -1
- package/src/lib/elements/value.element.ts +8 -4
- package/src/lib/events.ts +5 -4
- package/src/lib/{token.test.ts → expression.test.ts} +51 -51
- package/src/lib/{token.ts → expression.ts} +20 -11
- package/target/lib/bind.js +6 -4
- package/target/lib/bind.js.map +1 -1
- package/target/lib/bind.test.js +76 -0
- package/target/lib/bind.test.js.map +1 -0
- package/target/lib/elements/async.element.js +2 -2
- package/target/lib/elements/async.element.js.map +1 -1
- package/target/lib/elements/bind.element.d.ts +2 -2
- package/target/lib/elements/bind.element.js +3 -3
- package/target/lib/elements/bind.element.js.map +1 -1
- package/target/lib/elements/bind.element.test.js +2 -2
- package/target/lib/elements/bind.element.test.js.map +1 -1
- package/target/lib/elements/for.element.d.ts +1 -1
- package/target/lib/elements/for.element.js +13 -7
- package/target/lib/elements/for.element.js.map +1 -1
- package/target/lib/elements/if.element.js +5 -5
- package/target/lib/elements/if.element.js.map +1 -1
- package/target/lib/elements/if.element.test.js +12 -210
- package/target/lib/elements/if.element.test.js.map +1 -1
- package/target/lib/elements/scope.js +1 -1
- package/target/lib/elements/scope.js.map +1 -1
- package/target/lib/elements/value.element.js +6 -4
- package/target/lib/elements/value.element.js.map +1 -1
- package/target/lib/events.d.ts +4 -3
- package/target/lib/events.js +3 -3
- package/target/lib/events.js.map +1 -1
- package/target/lib/{token.d.ts → expression.d.ts} +2 -2
- package/target/lib/{token.js → expression.js} +6 -3
- package/target/lib/expression.js.map +1 -0
- package/target/lib/expression.test.d.ts +1 -0
- package/target/lib/{token.test.js → expression.test.js} +52 -52
- package/target/lib/expression.test.js.map +1 -0
- package/target/lib/token.js.map +0 -1
- package/target/lib/token.test.js.map +0 -1
- /package/target/lib/{token.test.d.ts → bind.test.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@ The Joist templating system provides a powerful and flexible way to handle data
|
|
|
7
7
|
- [Core Components](#core-components)
|
|
8
8
|
- [Built-in Template Elements](#built-in-template-elements)
|
|
9
9
|
- [Complete Example](#complete-example)
|
|
10
|
-
- [Troubleshooting](#troubleshooting)
|
|
11
10
|
|
|
12
11
|
## Core Components
|
|
13
12
|
|
|
@@ -276,11 +275,3 @@ The `j-async` element supports:
|
|
|
276
275
|
- Promise handling with automatic state transitions
|
|
277
276
|
- Loading, success, and error templates
|
|
278
277
|
- State object with typed data and error fields
|
|
279
|
-
|
|
280
|
-
## Troubleshooting
|
|
281
|
-
|
|
282
|
-
### Common Issues
|
|
283
|
-
|
|
284
|
-
1. **Binding Not Updating**
|
|
285
|
-
|
|
286
|
-
- Check if the property is decorated with `@bind()`
|
package/package.json
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { assert } from "chai";
|
|
2
|
+
import { bind } from "./bind.js";
|
|
3
|
+
import { JoistValueEvent } from "./events.js";
|
|
4
|
+
import { JExpression } from "./expression.js";
|
|
5
|
+
|
|
6
|
+
describe("bind decorator", () => {
|
|
7
|
+
class TestElement extends HTMLElement {
|
|
8
|
+
@bind()
|
|
9
|
+
accessor value = "initial";
|
|
10
|
+
|
|
11
|
+
@bind({ alwaysUpdate: true })
|
|
12
|
+
accessor alwaysUpdateValue = "initial";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
customElements.define("test-element", TestElement);
|
|
16
|
+
|
|
17
|
+
it("should initialize with default value", () => {
|
|
18
|
+
const element = new TestElement();
|
|
19
|
+
assert.equal(element.value, "initial");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should update value and trigger binding", async () => {
|
|
23
|
+
const element = new TestElement();
|
|
24
|
+
let oldValue: unknown = null;
|
|
25
|
+
let newValue: unknown = null;
|
|
26
|
+
|
|
27
|
+
element.dispatchEvent(
|
|
28
|
+
new JoistValueEvent(new JExpression("value"), (update) => {
|
|
29
|
+
oldValue = update.oldValue;
|
|
30
|
+
newValue = update.newValue;
|
|
31
|
+
}),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
assert.equal(oldValue, null);
|
|
35
|
+
assert.equal(newValue, "initial");
|
|
36
|
+
|
|
37
|
+
element.value = "updated";
|
|
38
|
+
|
|
39
|
+
await Promise.resolve();
|
|
40
|
+
|
|
41
|
+
assert.equal(oldValue, "initial");
|
|
42
|
+
assert.equal(newValue, "updated");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should trigger binding on every change with alwaysUpdate option", async () => {
|
|
46
|
+
const element = new TestElement();
|
|
47
|
+
let bindingCount = 0;
|
|
48
|
+
let oldValue: unknown;
|
|
49
|
+
let newValue: unknown;
|
|
50
|
+
|
|
51
|
+
element.dispatchEvent(
|
|
52
|
+
new JoistValueEvent(new JExpression("alwaysUpdateValue"), (update) => {
|
|
53
|
+
bindingCount++;
|
|
54
|
+
oldValue = update.oldValue;
|
|
55
|
+
newValue = update.newValue;
|
|
56
|
+
}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
assert.equal(bindingCount, 1);
|
|
60
|
+
assert.equal(oldValue, null);
|
|
61
|
+
assert.equal(newValue, "initial");
|
|
62
|
+
|
|
63
|
+
// Change some other value in the model
|
|
64
|
+
element.value = "something else";
|
|
65
|
+
|
|
66
|
+
await Promise.resolve();
|
|
67
|
+
|
|
68
|
+
assert.equal(bindingCount, 2);
|
|
69
|
+
assert.equal(oldValue, "initial");
|
|
70
|
+
assert.equal(newValue, "initial");
|
|
71
|
+
});
|
|
72
|
+
});
|
package/src/lib/bind.ts
CHANGED
|
@@ -18,7 +18,7 @@ export function bind<This extends HTMLElement, Value>(opts: BindOpts<This, Value
|
|
|
18
18
|
return {
|
|
19
19
|
init(value) {
|
|
20
20
|
this.addEventListener("joist::value", (e) => {
|
|
21
|
-
if (e.
|
|
21
|
+
if (e.expression.bindTo === ctx.name) {
|
|
22
22
|
const instanceMeta = instanceMetadataStore.read<This>(this);
|
|
23
23
|
|
|
24
24
|
e.stopPropagation();
|
|
@@ -27,14 +27,16 @@ export function bind<This extends HTMLElement, Value>(opts: BindOpts<This, Value
|
|
|
27
27
|
oldValue: null,
|
|
28
28
|
newValue: ctx.access.get(this),
|
|
29
29
|
alwaysUpdate: opts.alwaysUpdate,
|
|
30
|
+
firstChange: true,
|
|
30
31
|
});
|
|
31
32
|
|
|
33
|
+
const name = ctx.name as keyof This;
|
|
34
|
+
|
|
32
35
|
instanceMeta.bindings.add((changes) => {
|
|
33
|
-
const
|
|
34
|
-
const change = changes.get(key);
|
|
36
|
+
const change = changes.get(name);
|
|
35
37
|
|
|
36
38
|
if (change) {
|
|
37
|
-
e.update({ ...change, alwaysUpdate: opts.alwaysUpdate });
|
|
39
|
+
e.update({ ...change, alwaysUpdate: opts.alwaysUpdate, firstChange: false });
|
|
38
40
|
} else if (opts.alwaysUpdate) {
|
|
39
41
|
const value = ctx.access.get(this);
|
|
40
42
|
|
|
@@ -42,6 +44,7 @@ export function bind<This extends HTMLElement, Value>(opts: BindOpts<This, Value
|
|
|
42
44
|
oldValue: value,
|
|
43
45
|
newValue: value,
|
|
44
46
|
alwaysUpdate: opts.alwaysUpdate,
|
|
47
|
+
firstChange: false,
|
|
45
48
|
});
|
|
46
49
|
}
|
|
47
50
|
});
|
|
@@ -2,7 +2,7 @@ import { attr, element, queryAll, css, html } from "@joist/element";
|
|
|
2
2
|
|
|
3
3
|
import { bind } from "../bind.js";
|
|
4
4
|
import { JoistValueEvent } from "../events.js";
|
|
5
|
-
import {
|
|
5
|
+
import { JExpression } from "../expression.js";
|
|
6
6
|
|
|
7
7
|
declare global {
|
|
8
8
|
interface HTMLElementTagNameMap {
|
|
@@ -52,7 +52,7 @@ export class JoistAsyncElement extends HTMLElement {
|
|
|
52
52
|
success: templates.find((t) => t.hasAttribute("success")),
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
const token = new
|
|
55
|
+
const token = new JExpression(this.bind);
|
|
56
56
|
|
|
57
57
|
this.dispatchEvent(
|
|
58
58
|
new JoistValueEvent(token, ({ newValue, oldValue }) => {
|
|
@@ -9,14 +9,14 @@ it("should pass props to child", () => {
|
|
|
9
9
|
const element = fixtureSync(html`
|
|
10
10
|
<div
|
|
11
11
|
@joist::value=${(e: JoistValueEvent) => {
|
|
12
|
-
if (e.
|
|
12
|
+
if (e.expression.bindTo === "href") {
|
|
13
13
|
e.update({
|
|
14
14
|
oldValue: null,
|
|
15
15
|
newValue: "$foo",
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
if (e.
|
|
19
|
+
if (e.expression.bindTo === "target") {
|
|
20
20
|
e.update({
|
|
21
21
|
oldValue: null,
|
|
22
22
|
newValue: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { attr, element, css, html } from "@joist/element";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { JExpression } from "../expression.js";
|
|
4
4
|
import { JoistValueEvent } from "../events.js";
|
|
5
5
|
|
|
6
6
|
declare global {
|
|
@@ -9,7 +9,7 @@ declare global {
|
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export class JAttrToken extends
|
|
12
|
+
export class JAttrToken extends JExpression {
|
|
13
13
|
mapTo: string;
|
|
14
14
|
|
|
15
15
|
constructor(binding: string) {
|
|
@@ -80,14 +80,14 @@ export class JoistBindElement extends HTMLElement {
|
|
|
80
80
|
.filter((b) => b);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
#dispatch(token:
|
|
83
|
+
#dispatch(token: JExpression, write: (value: unknown) => void) {
|
|
84
84
|
this.dispatchEvent(
|
|
85
85
|
new JoistValueEvent(token, ({ newValue, oldValue, alwaysUpdate }) => {
|
|
86
86
|
if (newValue === oldValue && !alwaysUpdate) {
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
let valueToWrite = token.
|
|
90
|
+
let valueToWrite = token.readBoundValueFrom(newValue);
|
|
91
91
|
|
|
92
92
|
if (token.isNegated) {
|
|
93
93
|
valueToWrite = !valueToWrite;
|
|
@@ -2,7 +2,7 @@ import { attr, element, query, css, html } from "@joist/element";
|
|
|
2
2
|
|
|
3
3
|
import { bind } from "../bind.js";
|
|
4
4
|
import { JoistValueEvent } from "../events.js";
|
|
5
|
-
import {
|
|
5
|
+
import { JExpression } from "../expression.js";
|
|
6
6
|
|
|
7
7
|
declare global {
|
|
8
8
|
interface HTMLElementTagNameMap {
|
|
@@ -31,7 +31,7 @@ export class JForScope<T = unknown> extends HTMLElement {
|
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
@attr()
|
|
34
|
-
accessor key
|
|
34
|
+
accessor key: unknown;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
@element({
|
|
@@ -64,7 +64,7 @@ export class JositForElement extends HTMLElement {
|
|
|
64
64
|
currentScope = currentScope.nextElementSibling;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const token = new
|
|
67
|
+
const token = new JExpression(this.bind);
|
|
68
68
|
|
|
69
69
|
this.dispatchEvent(
|
|
70
70
|
new JoistValueEvent(token, ({ newValue, oldValue }) => {
|
|
@@ -98,11 +98,15 @@ export class JositForElement extends HTMLElement {
|
|
|
98
98
|
|
|
99
99
|
let index = 0;
|
|
100
100
|
for (const value of this.#items) {
|
|
101
|
-
|
|
101
|
+
let key: unknown = index;
|
|
102
|
+
|
|
103
|
+
if (keyProperty && hasProperty(value, keyProperty)) {
|
|
104
|
+
key = value[keyProperty];
|
|
105
|
+
}
|
|
102
106
|
|
|
103
107
|
const scope = new JForScope();
|
|
104
108
|
scope.append(document.importNode(templateContent, true));
|
|
105
|
-
scope.key =
|
|
109
|
+
scope.key = key;
|
|
106
110
|
scope.each = { position: index + 1, index, value };
|
|
107
111
|
|
|
108
112
|
fragment.appendChild(scope);
|
|
@@ -118,13 +122,16 @@ export class JositForElement extends HTMLElement {
|
|
|
118
122
|
updateItems(): void {
|
|
119
123
|
const template = this.#template();
|
|
120
124
|
const leftoverScopes = new Map<unknown, JForScope>(this.#scopes);
|
|
121
|
-
const keyProperty = this.key;
|
|
125
|
+
const keyProperty = this.key;
|
|
122
126
|
|
|
123
127
|
let index = 0;
|
|
124
128
|
|
|
125
129
|
for (const value of this.#items) {
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
let key: unknown = index;
|
|
131
|
+
|
|
132
|
+
if (keyProperty && hasProperty(value, keyProperty)) {
|
|
133
|
+
key = value[keyProperty];
|
|
134
|
+
}
|
|
128
135
|
|
|
129
136
|
let scope = leftoverScopes.get(key);
|
|
130
137
|
|
|
@@ -138,7 +145,7 @@ export class JositForElement extends HTMLElement {
|
|
|
138
145
|
|
|
139
146
|
// Only update if values have changed
|
|
140
147
|
if (scope.key !== key || scope.each.value !== value) {
|
|
141
|
-
scope.key =
|
|
148
|
+
scope.key = key;
|
|
142
149
|
scope.each = { position: index + 1, index, value };
|
|
143
150
|
}
|
|
144
151
|
|