@joist/templating 4.2.4-next.3 → 4.2.4-next.4

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 CHANGED
@@ -20,7 +20,7 @@ import { bind } from "@joist/templating";
20
20
 
21
21
  class MyElement extends HTMLElement {
22
22
  @bind()
23
- accessor myProperty: string;
23
+ accessor myProperty = "";
24
24
  }
25
25
  ```
26
26
 
@@ -36,33 +36,13 @@ class MyElement extends HTMLElement {
36
36
  @observe()
37
37
  assessor value = "Hello World";
38
38
 
39
- @bind((instance) => instance.value.toUpperCase())
39
+ @bind({
40
+ compute: (i) => i.value.toUpperCase()
41
+ })
40
42
  accessor formattedValue = "";
41
43
  }
42
44
  ```
43
45
 
44
- ### Token System (`token.ts`)
45
-
46
- The `JToken` class handles parsing and evaluation of binding expressions. It supports:
47
-
48
- NOTE: Most of the time you will not be using this yourself.
49
-
50
- - Simple property bindings: `propertyName`
51
- - Nested property access: `user.profile.name`
52
- - Negation operator: `!isVisible`
53
- - Array access: `items.0.name`
54
-
55
- Example usage:
56
-
57
- ```typescript
58
- const token = new JToken("user.name");
59
- const value = token.readTokenValueFrom(context);
60
-
61
- // With negation
62
- const negatedToken = new JToken("!isVisible");
63
- const isHidden = negatedToken.readTokenValueFrom(context);
64
- ```
65
-
66
46
  ## Built-in Template Elements
67
47
 
68
48
  Joist provides several built-in template elements for common templating needs:
@@ -82,7 +62,7 @@ Displays a bound value as text content:
82
62
  <j-val bind="user.profile.address.city"></j-val>
83
63
 
84
64
  <!-- With array access -->
85
- <j-val bind="items[0].name"></j-val>
65
+ <j-val bind="items.0.name"></j-val>
86
66
  ```
87
67
 
88
68
  ### Conditional Rendering (`j-if`)
@@ -104,6 +84,38 @@ Conditionally renders content based on a boolean expression:
104
84
  </template>
105
85
  </j-if>
106
86
 
87
+ <!-- With comparison operators -->
88
+ <j-if bind="status == active">
89
+ <template>
90
+ <div>Status is active</div>
91
+ </template>
92
+ </j-if>
93
+
94
+ <j-if bind="status != active">
95
+ <template>
96
+ <div>Status is not active</div>
97
+ </template>
98
+ </j-if>
99
+
100
+ <j-if bind="count > 5">
101
+ <template>
102
+ <div>Count is greater than 5</div>
103
+ </template>
104
+ </j-if>
105
+
106
+ <j-if bind="score < 100">
107
+ <template>
108
+ <div>Score is less than 100</div>
109
+ </template>
110
+ </j-if>
111
+
112
+ <!-- With nested paths -->
113
+ <j-if bind="user.score > 100">
114
+ <template>
115
+ <div>User's score is above 100</div>
116
+ </template>
117
+ </j-if>
118
+
107
119
  <!-- With else template -->
108
120
  <j-if bind="isLoggedIn">
109
121
  <template>
@@ -119,6 +131,12 @@ The `j-if` element supports:
119
131
 
120
132
  - Boolean expressions for conditional rendering
121
133
  - Negation operator (`!`) for inverse conditions
134
+ - Comparison operators:
135
+ - Equality (`==`): `status == active`
136
+ - Inequality (`!=`): `status != active`
137
+ - Greater than (`>`): `count > 5`
138
+ - Less than (`<`): `score < 100`
139
+ - Nested property paths: `user.score > 100`
122
140
  - Optional `else` template for fallback content
123
141
  - Automatic cleanup of removed content
124
142
 
@@ -266,133 +284,3 @@ The `j-async` element supports:
266
284
  1. **Binding Not Updating**
267
285
 
268
286
  - Check if the property is decorated with `@bind()`
269
- - Verify the binding expression is correct
270
- - Ensure the property is being updated correctly
271
-
272
- 2. **List Rendering Issues**
273
-
274
- - Verify the `key` attribute is unique and stable
275
- - Check if the list items are properly structured
276
- - Ensure the binding expression matches the data structure
277
-
278
- 3. **Async State Problems**
279
- - Verify the Promise is properly resolved/rejected
280
- - Check if all required templates are present
281
- - Ensure error handling is implemented
282
-
283
- ## Manual Value Handling
284
-
285
- You can manually handle value requests and updates by listening for the `joist::value` event. This is useful when you need more control over the binding process or want to implement custom binding logic:
286
-
287
- ```typescript
288
- class MyElement extends HTMLElement {
289
- connectedCallback() {
290
- // Listen for value requests
291
- this.addEventListener("joist::value", (e) => {
292
- const token = e.token;
293
-
294
- // Handle the value request
295
- if (token.bindTo === "myValue") {
296
- // Update the value
297
- e.update({
298
- oldValue: this.myValue,
299
- newValue: this.myValue,
300
- });
301
- }
302
- });
303
- }
304
- }
305
- ```
306
-
307
- ## Complete Example
308
-
309
- Here's a complete todo application in a single component:
310
-
311
- ```typescript
312
- import { bind } from "@joist/templating";
313
- import { element, html, css, listen, query } from "@joist/element";
314
-
315
- interface Todo {
316
- id: string;
317
- text: string;
318
- }
319
-
320
- @element({
321
- tagName: "todo-list",
322
- shadowDom: [
323
- css`
324
- :host {
325
- display: block;
326
- max-width: 600px;
327
- margin: 2rem auto;
328
- }
329
- .form {
330
- display: flex;
331
- gap: 1rem;
332
- }
333
- .todo-item {
334
- align-items: center;
335
- display: flex;
336
- gap: 0.5rem;
337
- margin: 0.5rem 0;
338
- }
339
- .todo-text {
340
- flex: 1;
341
- }
342
- `,
343
- html`
344
- <form class="form">
345
- <input type="text" placeholder="What needs to be done?" />
346
- <button type="submit">Add</button>
347
- </form>
348
-
349
- <j-if bind="!todos.length">
350
- <template>
351
- <p>No todos yet!</p>
352
- </template>
353
- </j-if>
354
-
355
- <j-for id="todos" bind="todos" key="id">
356
- <template>
357
- <div class="todo-item">
358
- <j-val class="todo-text" bind="each.value.text"></j-val>
359
-
360
- <j-bind attrs="data-id:each.value.id">
361
- <button>×</button>
362
- </j-bind>
363
- </div>
364
- </template>
365
- </j-for>
366
-
367
- <j-val bind="todos.length"></j-val> remaining
368
- `,
369
- ],
370
- })
371
- export class TodoList extends HTMLElement {
372
- @bind()
373
- accessor todos: Todo[] = [];
374
-
375
- #nextId = 1;
376
- #input = query("input");
377
-
378
- @listen("submit", "form")
379
- onSubmit(e: SubmitEvent) {
380
- e.preventDefault();
381
-
382
- const input = this.#input();
383
-
384
- this.todos = [...this.todos, { id: String(this.#nextId++), text: input.value.trim() }];
385
-
386
- input.value = "";
387
- }
388
-
389
- @listen("click", "#todos")
390
- onDelete(e: Event) {
391
- if (e.target instanceof HTMLButtonElement) {
392
- const id = Number(e.target.dataset.id);
393
-
394
- this.todos = this.todos.filter((todo) => todo.id !== id);
395
- }
396
- }
397
- }
398
- ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joist/templating",
3
- "version": "4.2.4-next.3",
3
+ "version": "4.2.4-next.4",
4
4
  "type": "module",
5
5
  "main": "./target/lib.js",
6
6
  "module": "./target/lib.js",
@@ -88,3 +88,179 @@ it("should switch between if and else templates", () => {
88
88
 
89
89
  assert.equal(element.textContent?.trim(), "If Content");
90
90
  });
91
+
92
+ it("should handle equality comparison", () => {
93
+ const element = fixtureSync(html`
94
+ <div
95
+ @joist::value=${(e: JoistValueEvent) => {
96
+ e.update({ oldValue: null, newValue: { status: "active" } });
97
+ }}
98
+ >
99
+ <j-if bind="example.status == active">
100
+ <template>Status is Active</template>
101
+ </j-if>
102
+ </div>
103
+ `);
104
+
105
+ assert.equal(element.textContent?.trim(), "Status is Active");
106
+ });
107
+
108
+ it("should handle greater than comparison", () => {
109
+ const element = fixtureSync(html`
110
+ <div
111
+ @joist::value=${(e: JoistValueEvent) => {
112
+ e.update({ oldValue: null, newValue: { count: 10 } });
113
+ }}
114
+ >
115
+ <j-if bind="example.count > 5">
116
+ <template>Count is Greater Than 5</template>
117
+ </j-if>
118
+ </div>
119
+ `);
120
+
121
+ assert.equal(element.textContent?.trim(), "Count is Greater Than 5");
122
+ });
123
+
124
+ it("should handle less than comparison", () => {
125
+ const element = fixtureSync(html`
126
+ <div
127
+ @joist::value=${(e: JoistValueEvent) => {
128
+ e.update({ oldValue: null, newValue: { score: 75 } });
129
+ }}
130
+ >
131
+ <j-if bind="example.score < 100">
132
+ <template>Score is Less Than 100</template>
133
+ </j-if>
134
+ </div>
135
+ `);
136
+
137
+ assert.equal(element.textContent?.trim(), "Score is Less Than 100");
138
+ });
139
+
140
+ it("should handle nested path comparisons", () => {
141
+ const element = fixtureSync(html`
142
+ <div
143
+ @joist::value=${(e: JoistValueEvent) => {
144
+ e.update({ oldValue: null, newValue: { user: { score: 150 } } });
145
+ }}
146
+ >
147
+ <j-if bind="example.user.score > 100">
148
+ <template>User Score is Above 100</template>
149
+ </j-if>
150
+ </div>
151
+ `);
152
+
153
+ assert.equal(element.textContent?.trim(), "User Score is Above 100");
154
+ });
155
+
156
+ it("should handle negated comparisons", () => {
157
+ const element = fixtureSync(html`
158
+ <div
159
+ @joist::value=${(e: JoistValueEvent) => {
160
+ e.update({ oldValue: null, newValue: { status: "inactive" } });
161
+ }}
162
+ >
163
+ <j-if bind="!example.status == active">
164
+ <template>Status is Not Active</template>
165
+ </j-if>
166
+ </div>
167
+ `);
168
+
169
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
170
+ });
171
+
172
+ it("should handle string number comparisons", () => {
173
+ const element = fixtureSync(html`
174
+ <div
175
+ @joist::value=${(e: JoistValueEvent) => {
176
+ e.update({ oldValue: null, newValue: { count: "10" } });
177
+ }}
178
+ >
179
+ <j-if bind="example.count > 5">
180
+ <template>String Count is Greater Than 5</template>
181
+ </j-if>
182
+ </div>
183
+ `);
184
+
185
+ assert.equal(element.textContent?.trim(), "String Count is Greater Than 5");
186
+ });
187
+
188
+ it("should handle undefined values in comparisons", () => {
189
+ const element = fixtureSync(html`
190
+ <div
191
+ @joist::value=${(e: JoistValueEvent) => {
192
+ e.update({ oldValue: null, newValue: { count: undefined } });
193
+ }}
194
+ >
195
+ <j-if bind="example.count > 5">
196
+ <template>Count is Greater Than 5</template>
197
+ </j-if>
198
+ </div>
199
+ `);
200
+
201
+ assert.equal(element.textContent?.trim(), "");
202
+ });
203
+
204
+ it("should handle not equal comparison", () => {
205
+ const element = fixtureSync(html`
206
+ <div
207
+ @joist::value=${(e: JoistValueEvent) => {
208
+ e.update({ oldValue: null, newValue: { status: "inactive" } });
209
+ }}
210
+ >
211
+ <j-if bind="example.status != active">
212
+ <template>Status is Not Active</template>
213
+ </j-if>
214
+ </div>
215
+ `);
216
+
217
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
218
+ });
219
+
220
+ it("should handle not equal comparison with matching value", () => {
221
+ const element = fixtureSync(html`
222
+ <div
223
+ @joist::value=${(e: JoistValueEvent) => {
224
+ e.update({ oldValue: null, newValue: { status: "active" } });
225
+ }}
226
+ >
227
+ <j-if bind="example.status != active">
228
+ <template>Status is Not Active</template>
229
+ </j-if>
230
+ </div>
231
+ `);
232
+
233
+ assert.equal(element.textContent?.trim(), "");
234
+ });
235
+
236
+ it("should handle not equal comparison with string numbers", () => {
237
+ const element = fixtureSync(html`
238
+ <div
239
+ @joist::value=${(e: JoistValueEvent) => {
240
+ e.update({ oldValue: null, newValue: { count: "10" } });
241
+ }}
242
+ >
243
+ <j-if bind="example.count != 5">
244
+ <template>Count is Not 5</template>
245
+ </j-if>
246
+ </div>
247
+ `);
248
+
249
+ assert.equal(element.textContent?.trim(), "Count is Not 5");
250
+ });
251
+
252
+ it("should handle not equal comparison with undefined", () => {
253
+ const element = fixtureSync(html`
254
+ <div
255
+ @joist::value=${(e: JoistValueEvent) => {
256
+ e.update({ oldValue: null, newValue: { status: undefined } });
257
+ }}
258
+ >
259
+ <j-if bind="example.status != active">
260
+ <template>Status is Not Active</template>
261
+ </j-if>
262
+ </div>
263
+ `);
264
+
265
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
266
+ });
@@ -33,6 +33,45 @@ describe("JToken", () => {
33
33
  const token = new JToken("!example.token");
34
34
  assert.equal(token.bindTo, "example");
35
35
  });
36
+
37
+ it("should parse equals operator value", () => {
38
+ const token = new JToken("example==value");
39
+ assert.equal(token.equalsValue, "value");
40
+ });
41
+
42
+ it("should handle equals operator with negation", () => {
43
+ const token = new JToken("!example == value");
44
+ assert.equal(token.equalsValue, "value");
45
+ assert.isTrue(token.isNegated);
46
+ });
47
+
48
+ it("should handle equals operator with nested paths", () => {
49
+ const token = new JToken("example.nested == value");
50
+ assert.equal(token.equalsValue, "value");
51
+ assert.deepEqual(token.path, ["nested"]);
52
+ });
53
+
54
+ it("should parse greater than operator value", () => {
55
+ const token = new JToken("example > 5");
56
+ assert.equal(token.gtValue, "5");
57
+ });
58
+
59
+ it("should parse less than operator value", () => {
60
+ const token = new JToken("example < 10");
61
+ assert.equal(token.ltValue, "10");
62
+ });
63
+
64
+ it("should handle greater than operator with negation", () => {
65
+ const token = new JToken("!example > 5");
66
+ assert.equal(token.gtValue, "5");
67
+ assert.isTrue(token.isNegated);
68
+ });
69
+
70
+ it("should handle less than operator with nested paths", () => {
71
+ const token = new JToken("example.count < 10");
72
+ assert.equal(token.ltValue, "10");
73
+ assert.deepEqual(token.path, ["count"]);
74
+ });
36
75
  });
37
76
 
38
77
  describe("readTokenValueFrom", () => {
@@ -64,5 +103,102 @@ describe("JToken", () => {
64
103
 
65
104
  assert.equal(value, 2);
66
105
  });
106
+
107
+ it("should return true when equals against primative", () => {
108
+ const token = new JToken("example == active");
109
+ const value = token.readTokenValueFrom<boolean>("active");
110
+ assert.isTrue(value);
111
+ });
112
+
113
+ it("should return true when equals comparison matches", () => {
114
+ const token = new JToken("example.status==active");
115
+ const obj = { status: "active" };
116
+ const value = token.readTokenValueFrom<boolean>(obj);
117
+ assert.isTrue(value);
118
+ });
119
+
120
+ it("should return false when equals comparison does not match", () => {
121
+ const token = new JToken("example.status==active");
122
+ const obj = { status: "inactive" };
123
+ const value = token.readTokenValueFrom<boolean>(obj);
124
+ assert.isFalse(value);
125
+ });
126
+
127
+ it("should handle equals comparison with numbers", () => {
128
+ const token = new JToken("example.count == 5");
129
+ const obj = { count: 5 };
130
+ const value = token.readTokenValueFrom<boolean>(obj);
131
+ assert.isTrue(value);
132
+ });
133
+
134
+ it("should handle equals comparison with nested paths", () => {
135
+ const token = new JToken("example.user.status == active");
136
+ const obj = { user: { status: "active" } };
137
+ const value = token.readTokenValueFrom<boolean>(obj);
138
+ assert.isTrue(value);
139
+ });
140
+
141
+ it("should handle equals comparison with undefined values", () => {
142
+ const token = new JToken("example.status == active");
143
+ const obj = { status: undefined };
144
+ const value = token.readTokenValueFrom<boolean>(obj);
145
+ assert.isFalse(value);
146
+ });
147
+
148
+ it("should return true when greater than comparison matches", () => {
149
+ const token = new JToken("example.count > 5");
150
+ const obj = { count: 10 };
151
+ const value = token.readTokenValueFrom<boolean>(obj);
152
+ assert.isTrue(value);
153
+ });
154
+
155
+ it("should return false when greater than comparison does not match", () => {
156
+ const token = new JToken("example.count > 5");
157
+ const obj = { count: 3 };
158
+ const value = token.readTokenValueFrom<boolean>(obj);
159
+ assert.isFalse(value);
160
+ });
161
+
162
+ it("should return true when less than comparison matches", () => {
163
+ const token = new JToken("example.count < 10");
164
+ const obj = { count: 5 };
165
+ const value = token.readTokenValueFrom<boolean>(obj);
166
+ assert.isTrue(value);
167
+ });
168
+
169
+ it("should return false when less than comparison does not match", () => {
170
+ const token = new JToken("example.count < 10");
171
+ const obj = { count: 15 };
172
+ const value = token.readTokenValueFrom<boolean>(obj);
173
+ assert.isFalse(value);
174
+ });
175
+
176
+ it("should handle greater than comparison with string numbers", () => {
177
+ const token = new JToken("example.count > 5");
178
+ const obj = { count: "10" };
179
+ const value = token.readTokenValueFrom<boolean>(obj);
180
+ assert.isTrue(value);
181
+ });
182
+
183
+ it("should handle less than comparison with string numbers", () => {
184
+ const token = new JToken("example.count < 10");
185
+ const obj = { count: "5" };
186
+ const value = token.readTokenValueFrom<boolean>(obj);
187
+ assert.isTrue(value);
188
+ });
189
+
190
+ it("should handle greater than comparison with undefined values", () => {
191
+ const token = new JToken("example.count > 5");
192
+ const obj = { count: undefined };
193
+ const value = token.readTokenValueFrom<boolean>(obj);
194
+ assert.isFalse(value);
195
+ });
196
+
197
+ it("should handle less than comparison with undefined values", () => {
198
+ const token = new JToken("example.count < 10");
199
+ const obj = { count: undefined };
200
+ const value = token.readTokenValueFrom<boolean>(obj);
201
+ assert.isFalse(value);
202
+ });
67
203
  });
68
204
  });
package/src/lib/token.ts CHANGED
@@ -1,36 +1,170 @@
1
+ type ComparisonOperator = "==" | "!=" | ">" | "<";
2
+
3
+ interface TokenParts {
4
+ path: string[];
5
+ value?: string;
6
+ operator?: ComparisonOperator;
7
+ }
8
+
9
+ /**
10
+ * JToken represents a token that can be used to extract and compare values from objects.
11
+ *
12
+ * Supported operators:
13
+ * - `==` : Equality comparison (e.g., "status==active")
14
+ * - `!=` : Inequality comparison (e.g., "status!=active")
15
+ * - `>` : Greater than comparison (e.g., "count>5")
16
+ * - `<` : Less than comparison (e.g., "count<10")
17
+ *
18
+ * Examples:
19
+ * ```typescript
20
+ * // Basic path access
21
+ * new JToken("user.name").readTokenValueFrom({ user: { name: "John" } }) // "John"
22
+ *
23
+ * // Equality comparison
24
+ * new JToken("status == active").readTokenValueFrom({ status: "active" }) // true
25
+ *
26
+ * // Inequality comparison
27
+ * new JToken("status != active").readTokenValueFrom({ status: "inactive" }) // true
28
+ *
29
+ * // Greater than comparison
30
+ * new JToken("count > 5").readTokenValueFrom({ count: 10 }) // true
31
+ *
32
+ * // Less than comparison
33
+ * new JToken("count < 10").readTokenValueFrom({ count: 5 }) // true
34
+ *
35
+ * // With negation
36
+ * new JToken("!status == active").readTokenValueFrom({ status: "inactive" }) // true
37
+ *
38
+ * // Nested paths
39
+ * new JToken("user.score > 100").readTokenValueFrom({ user: { score: 150 } }) // true
40
+ * ```
41
+ */
1
42
  export class JToken {
43
+ /** The raw token string as provided to the constructor */
2
44
  rawToken: string;
45
+ /** Whether the token is negated (starts with '!') */
3
46
  isNegated = false;
47
+ /** The first part of the path (before the first dot) */
4
48
  bindTo: string;
49
+ /** The remaining parts of the path (after the first dot) */
5
50
  path: string[] = [];
51
+ /** The value to compare against for equality (==) */
52
+ equalsValue: string | undefined;
53
+ /** The value to compare against for inequality (!=) */
54
+ notEqualsValue: string | undefined;
55
+ /** The value to compare against for greater than (>) */
56
+ gtValue: string | undefined;
57
+ /** The value to compare against for less than (<) */
58
+ ltValue: string | undefined;
6
59
 
60
+ /**
61
+ * Creates a new JToken instance.
62
+ * @param rawToken - The token string to parse. Can include operators (==, !=, >, <) and negation (!)
63
+ */
7
64
  constructor(rawToken: string) {
8
65
  this.rawToken = rawToken;
9
-
10
66
  this.isNegated = this.rawToken.startsWith("!");
11
67
 
12
- this.path = this.rawToken.split(".");
68
+ const { path, value, operator } = this.#parseToken();
69
+ this.path = path;
13
70
  this.bindTo = this.path.shift() ?? "";
14
71
  this.bindTo = this.bindTo.replaceAll("!", "");
72
+
73
+ // Set the appropriate comparison value based on the operator
74
+ switch (operator) {
75
+ case "==":
76
+ this.equalsValue = value;
77
+ break;
78
+ case "!=":
79
+ this.notEqualsValue = value;
80
+ break;
81
+ case ">":
82
+ this.gtValue = value;
83
+ break;
84
+ case "<":
85
+ this.ltValue = value;
86
+ break;
87
+ }
15
88
  }
16
89
 
90
+ /**
91
+ * Reads a value from the provided object using the token's path and performs any comparison.
92
+ * @param value - The object to read from
93
+ * @returns The value at the path, or the result of the comparison if an operator is present
94
+ * @template T - The expected return type
95
+ */
17
96
  readTokenValueFrom<T = unknown>(value: unknown): T {
18
- let pointer: any = value;
97
+ if (typeof value !== "object" && typeof value !== "string") {
98
+ return value as T;
99
+ }
100
+
101
+ const pathValue = this.#getValueAtPath(value);
102
+ return this.#performComparison(pathValue) as T;
103
+ }
104
+
105
+ /**
106
+ * Parses the raw token into its components.
107
+ * @returns An object containing the path parts and any comparison operator/value
108
+ */
109
+ #parseToken(): TokenParts {
110
+ const operators: ComparisonOperator[] = ["==", "!=", ">", "<"];
19
111
 
20
- if ((typeof value === "object" && value !== null) || typeof value === "string") {
21
- if (!this.path.length) {
22
- return pointer;
112
+ for (const operator of operators) {
113
+ if (this.rawToken.includes(operator)) {
114
+ const [tokenPart, value] = this.rawToken.split(operator).map((part) => part.trim());
115
+ return {
116
+ path: tokenPart.split("."),
117
+ value,
118
+ operator,
119
+ };
23
120
  }
121
+ }
24
122
 
25
- for (const part of this.path) {
26
- pointer = pointer[part];
123
+ return {
124
+ path: this.rawToken.split("."),
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Gets the value at the token's path in the provided object.
130
+ * @param value - The object to read from
131
+ * @returns The value at the path, or undefined if the path doesn't exist
132
+ */
133
+ #getValueAtPath(value: unknown): unknown {
134
+ if (!this.path.length) {
135
+ return value;
136
+ }
27
137
 
28
- if (pointer === undefined) {
29
- break;
30
- }
138
+ let pointer: any = value;
139
+ for (const part of this.path) {
140
+ pointer = pointer?.[part];
141
+ if (pointer === undefined) {
142
+ break;
31
143
  }
32
144
  }
33
145
 
34
146
  return pointer;
35
147
  }
148
+
149
+ /**
150
+ * Performs the comparison operation if an operator is present.
151
+ * @param value - The value to compare
152
+ * @returns The result of the comparison, or the original value if no operator is present
153
+ */
154
+ #performComparison(value: unknown): boolean | unknown {
155
+ if (this.equalsValue !== undefined) {
156
+ return String(value) === this.equalsValue;
157
+ }
158
+ if (this.notEqualsValue !== undefined) {
159
+ return String(value) !== this.notEqualsValue;
160
+ }
161
+ if (this.gtValue !== undefined) {
162
+ return Number(value) > Number(this.gtValue);
163
+ }
164
+ if (this.ltValue !== undefined) {
165
+ return Number(value) < Number(this.ltValue);
166
+ }
167
+
168
+ return value;
169
+ }
36
170
  }
@@ -75,4 +75,158 @@ it("should switch between if and else templates", () => {
75
75
  `);
76
76
  assert.equal(element.textContent?.trim(), "If Content");
77
77
  });
78
+ it("should handle equality comparison", () => {
79
+ const element = fixtureSync(html `
80
+ <div
81
+ @joist::value=${(e) => {
82
+ e.update({ oldValue: null, newValue: { status: "active" } });
83
+ }}
84
+ >
85
+ <j-if bind="example.status == active">
86
+ <template>Status is Active</template>
87
+ </j-if>
88
+ </div>
89
+ `);
90
+ assert.equal(element.textContent?.trim(), "Status is Active");
91
+ });
92
+ it("should handle greater than comparison", () => {
93
+ const element = fixtureSync(html `
94
+ <div
95
+ @joist::value=${(e) => {
96
+ e.update({ oldValue: null, newValue: { count: 10 } });
97
+ }}
98
+ >
99
+ <j-if bind="example.count > 5">
100
+ <template>Count is Greater Than 5</template>
101
+ </j-if>
102
+ </div>
103
+ `);
104
+ assert.equal(element.textContent?.trim(), "Count is Greater Than 5");
105
+ });
106
+ it("should handle less than comparison", () => {
107
+ const element = fixtureSync(html `
108
+ <div
109
+ @joist::value=${(e) => {
110
+ e.update({ oldValue: null, newValue: { score: 75 } });
111
+ }}
112
+ >
113
+ <j-if bind="example.score < 100">
114
+ <template>Score is Less Than 100</template>
115
+ </j-if>
116
+ </div>
117
+ `);
118
+ assert.equal(element.textContent?.trim(), "Score is Less Than 100");
119
+ });
120
+ it("should handle nested path comparisons", () => {
121
+ const element = fixtureSync(html `
122
+ <div
123
+ @joist::value=${(e) => {
124
+ e.update({ oldValue: null, newValue: { user: { score: 150 } } });
125
+ }}
126
+ >
127
+ <j-if bind="example.user.score > 100">
128
+ <template>User Score is Above 100</template>
129
+ </j-if>
130
+ </div>
131
+ `);
132
+ assert.equal(element.textContent?.trim(), "User Score is Above 100");
133
+ });
134
+ it("should handle negated comparisons", () => {
135
+ const element = fixtureSync(html `
136
+ <div
137
+ @joist::value=${(e) => {
138
+ e.update({ oldValue: null, newValue: { status: "inactive" } });
139
+ }}
140
+ >
141
+ <j-if bind="!example.status == active">
142
+ <template>Status is Not Active</template>
143
+ </j-if>
144
+ </div>
145
+ `);
146
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
147
+ });
148
+ it("should handle string number comparisons", () => {
149
+ const element = fixtureSync(html `
150
+ <div
151
+ @joist::value=${(e) => {
152
+ e.update({ oldValue: null, newValue: { count: "10" } });
153
+ }}
154
+ >
155
+ <j-if bind="example.count > 5">
156
+ <template>String Count is Greater Than 5</template>
157
+ </j-if>
158
+ </div>
159
+ `);
160
+ assert.equal(element.textContent?.trim(), "String Count is Greater Than 5");
161
+ });
162
+ it("should handle undefined values in comparisons", () => {
163
+ const element = fixtureSync(html `
164
+ <div
165
+ @joist::value=${(e) => {
166
+ e.update({ oldValue: null, newValue: { count: undefined } });
167
+ }}
168
+ >
169
+ <j-if bind="example.count > 5">
170
+ <template>Count is Greater Than 5</template>
171
+ </j-if>
172
+ </div>
173
+ `);
174
+ assert.equal(element.textContent?.trim(), "");
175
+ });
176
+ it("should handle not equal comparison", () => {
177
+ const element = fixtureSync(html `
178
+ <div
179
+ @joist::value=${(e) => {
180
+ e.update({ oldValue: null, newValue: { status: "inactive" } });
181
+ }}
182
+ >
183
+ <j-if bind="example.status != active">
184
+ <template>Status is Not Active</template>
185
+ </j-if>
186
+ </div>
187
+ `);
188
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
189
+ });
190
+ it("should handle not equal comparison with matching value", () => {
191
+ const element = fixtureSync(html `
192
+ <div
193
+ @joist::value=${(e) => {
194
+ e.update({ oldValue: null, newValue: { status: "active" } });
195
+ }}
196
+ >
197
+ <j-if bind="example.status != active">
198
+ <template>Status is Not Active</template>
199
+ </j-if>
200
+ </div>
201
+ `);
202
+ assert.equal(element.textContent?.trim(), "");
203
+ });
204
+ it("should handle not equal comparison with string numbers", () => {
205
+ const element = fixtureSync(html `
206
+ <div
207
+ @joist::value=${(e) => {
208
+ e.update({ oldValue: null, newValue: { count: "10" } });
209
+ }}
210
+ >
211
+ <j-if bind="example.count != 5">
212
+ <template>Count is Not 5</template>
213
+ </j-if>
214
+ </div>
215
+ `);
216
+ assert.equal(element.textContent?.trim(), "Count is Not 5");
217
+ });
218
+ it("should handle not equal comparison with undefined", () => {
219
+ const element = fixtureSync(html `
220
+ <div
221
+ @joist::value=${(e) => {
222
+ e.update({ oldValue: null, newValue: { status: undefined } });
223
+ }}
224
+ >
225
+ <j-if bind="example.status != active">
226
+ <template>Status is Not Active</template>
227
+ </j-if>
228
+ </div>
229
+ `);
230
+ assert.equal(element.textContent?.trim(), "Status is Not Active");
231
+ });
78
232
  //# sourceMappingURL=if.element.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"if.element.test.js","sourceRoot":"","sources":["../../../src/lib/elements/if.element.test.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAI9B,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC7D,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAA;;sBAEZ,CAAC,CAAkB,EAAE,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;;;;;;GAMJ,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;IAChE,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAA;;sBAEZ,CAAC,CAAkB,EAAE,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;;;;;;GAMJ,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAChD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAA;;sBAEZ,CAAC,CAAkB,EAAE,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;;;;;;GAMJ,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,iBAAiB,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC7D,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAA;;sBAEZ,CAAC,CAAkB,EAAE,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;;;;;;;GAOJ,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACrD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAA;;sBAEZ,CAAC,CAAkB,EAAE,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9C,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;;;;;;;GAOJ,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,YAAY,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"if.element.test.js","sourceRoot":"","sources":["../../../src/lib/elements/if.element.test.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,13 @@
1
1
  export declare class JToken {
2
+ #private;
2
3
  rawToken: string;
3
4
  isNegated: boolean;
4
5
  bindTo: string;
5
6
  path: string[];
7
+ equalsValue: string | undefined;
8
+ notEqualsValue: string | undefined;
9
+ gtValue: string | undefined;
10
+ ltValue: string | undefined;
6
11
  constructor(rawToken: string);
7
12
  readTokenValueFrom<T = unknown>(value: unknown): T;
8
13
  }
@@ -3,27 +3,82 @@ export class JToken {
3
3
  isNegated = false;
4
4
  bindTo;
5
5
  path = [];
6
+ equalsValue;
7
+ notEqualsValue;
8
+ gtValue;
9
+ ltValue;
6
10
  constructor(rawToken) {
7
11
  this.rawToken = rawToken;
8
12
  this.isNegated = this.rawToken.startsWith("!");
9
- this.path = this.rawToken.split(".");
13
+ const { path, value, operator } = this.#parseToken();
14
+ this.path = path;
10
15
  this.bindTo = this.path.shift() ?? "";
11
16
  this.bindTo = this.bindTo.replaceAll("!", "");
17
+ switch (operator) {
18
+ case "==":
19
+ this.equalsValue = value;
20
+ break;
21
+ case "!=":
22
+ this.notEqualsValue = value;
23
+ break;
24
+ case ">":
25
+ this.gtValue = value;
26
+ break;
27
+ case "<":
28
+ this.ltValue = value;
29
+ break;
30
+ }
12
31
  }
13
32
  readTokenValueFrom(value) {
14
- let pointer = value;
15
- if ((typeof value === "object" && value !== null) || typeof value === "string") {
16
- if (!this.path.length) {
17
- return pointer;
33
+ if (typeof value !== "object" && typeof value !== "string") {
34
+ return value;
35
+ }
36
+ const pathValue = this.#getValueAtPath(value);
37
+ return this.#performComparison(pathValue);
38
+ }
39
+ #parseToken() {
40
+ const operators = ["==", "!=", ">", "<"];
41
+ for (const operator of operators) {
42
+ if (this.rawToken.includes(operator)) {
43
+ const [tokenPart, value] = this.rawToken.split(operator).map((part) => part.trim());
44
+ return {
45
+ path: tokenPart.split("."),
46
+ value,
47
+ operator,
48
+ };
18
49
  }
19
- for (const part of this.path) {
20
- pointer = pointer[part];
21
- if (pointer === undefined) {
22
- break;
23
- }
50
+ }
51
+ return {
52
+ path: this.rawToken.split("."),
53
+ };
54
+ }
55
+ #getValueAtPath(value) {
56
+ if (!this.path.length) {
57
+ return value;
58
+ }
59
+ let pointer = value;
60
+ for (const part of this.path) {
61
+ pointer = pointer?.[part];
62
+ if (pointer === undefined) {
63
+ break;
24
64
  }
25
65
  }
26
66
  return pointer;
27
67
  }
68
+ #performComparison(value) {
69
+ if (this.equalsValue !== undefined) {
70
+ return String(value) === this.equalsValue;
71
+ }
72
+ if (this.notEqualsValue !== undefined) {
73
+ return String(value) !== this.notEqualsValue;
74
+ }
75
+ if (this.gtValue !== undefined) {
76
+ return Number(value) > Number(this.gtValue);
77
+ }
78
+ if (this.ltValue !== undefined) {
79
+ return Number(value) < Number(this.ltValue);
80
+ }
81
+ return value;
82
+ }
28
83
  }
29
84
  //# sourceMappingURL=token.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/lib/token.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,MAAM;IACjB,QAAQ,CAAS;IACjB,SAAS,GAAG,KAAK,CAAC;IAClB,MAAM,CAAS;IACf,IAAI,GAAa,EAAE,CAAC;IAEpB,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAEzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAE/C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,kBAAkB,CAAc,KAAc;QAC5C,IAAI,OAAO,GAAQ,KAAK,CAAC;QAEzB,IAAI,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC/E,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACtB,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC7B,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;gBAExB,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;oBAC1B,MAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/lib/token.ts"],"names":[],"mappings":"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"}
@@ -26,6 +26,38 @@ describe("JToken", () => {
26
26
  const token = new JToken("!example.token");
27
27
  assert.equal(token.bindTo, "example");
28
28
  });
29
+ it("should parse equals operator value", () => {
30
+ const token = new JToken("example==value");
31
+ assert.equal(token.equalsValue, "value");
32
+ });
33
+ it("should handle equals operator with negation", () => {
34
+ const token = new JToken("!example == value");
35
+ assert.equal(token.equalsValue, "value");
36
+ assert.isTrue(token.isNegated);
37
+ });
38
+ it("should handle equals operator with nested paths", () => {
39
+ const token = new JToken("example.nested == value");
40
+ assert.equal(token.equalsValue, "value");
41
+ assert.deepEqual(token.path, ["nested"]);
42
+ });
43
+ it("should parse greater than operator value", () => {
44
+ const token = new JToken("example > 5");
45
+ assert.equal(token.gtValue, "5");
46
+ });
47
+ it("should parse less than operator value", () => {
48
+ const token = new JToken("example < 10");
49
+ assert.equal(token.ltValue, "10");
50
+ });
51
+ it("should handle greater than operator with negation", () => {
52
+ const token = new JToken("!example > 5");
53
+ assert.equal(token.gtValue, "5");
54
+ assert.isTrue(token.isNegated);
55
+ });
56
+ it("should handle less than operator with nested paths", () => {
57
+ const token = new JToken("example.count < 10");
58
+ assert.equal(token.ltValue, "10");
59
+ assert.deepEqual(token.path, ["count"]);
60
+ });
29
61
  });
30
62
  describe("readTokenValueFrom", () => {
31
63
  it("should read the value from a nested object", () => {
@@ -51,6 +83,89 @@ describe("JToken", () => {
51
83
  const value = token.readTokenValueFrom("42");
52
84
  assert.equal(value, 2);
53
85
  });
86
+ it("should return true when equals against primative", () => {
87
+ const token = new JToken("example == active");
88
+ const value = token.readTokenValueFrom("active");
89
+ assert.isTrue(value);
90
+ });
91
+ it("should return true when equals comparison matches", () => {
92
+ const token = new JToken("example.status==active");
93
+ const obj = { status: "active" };
94
+ const value = token.readTokenValueFrom(obj);
95
+ assert.isTrue(value);
96
+ });
97
+ it("should return false when equals comparison does not match", () => {
98
+ const token = new JToken("example.status==active");
99
+ const obj = { status: "inactive" };
100
+ const value = token.readTokenValueFrom(obj);
101
+ assert.isFalse(value);
102
+ });
103
+ it("should handle equals comparison with numbers", () => {
104
+ const token = new JToken("example.count == 5");
105
+ const obj = { count: 5 };
106
+ const value = token.readTokenValueFrom(obj);
107
+ assert.isTrue(value);
108
+ });
109
+ it("should handle equals comparison with nested paths", () => {
110
+ const token = new JToken("example.user.status == active");
111
+ const obj = { user: { status: "active" } };
112
+ const value = token.readTokenValueFrom(obj);
113
+ assert.isTrue(value);
114
+ });
115
+ it("should handle equals comparison with undefined values", () => {
116
+ const token = new JToken("example.status == active");
117
+ const obj = { status: undefined };
118
+ const value = token.readTokenValueFrom(obj);
119
+ assert.isFalse(value);
120
+ });
121
+ it("should return true when greater than comparison matches", () => {
122
+ const token = new JToken("example.count > 5");
123
+ const obj = { count: 10 };
124
+ const value = token.readTokenValueFrom(obj);
125
+ assert.isTrue(value);
126
+ });
127
+ it("should return false when greater than comparison does not match", () => {
128
+ const token = new JToken("example.count > 5");
129
+ const obj = { count: 3 };
130
+ const value = token.readTokenValueFrom(obj);
131
+ assert.isFalse(value);
132
+ });
133
+ it("should return true when less than comparison matches", () => {
134
+ const token = new JToken("example.count < 10");
135
+ const obj = { count: 5 };
136
+ const value = token.readTokenValueFrom(obj);
137
+ assert.isTrue(value);
138
+ });
139
+ it("should return false when less than comparison does not match", () => {
140
+ const token = new JToken("example.count < 10");
141
+ const obj = { count: 15 };
142
+ const value = token.readTokenValueFrom(obj);
143
+ assert.isFalse(value);
144
+ });
145
+ it("should handle greater than comparison with string numbers", () => {
146
+ const token = new JToken("example.count > 5");
147
+ const obj = { count: "10" };
148
+ const value = token.readTokenValueFrom(obj);
149
+ assert.isTrue(value);
150
+ });
151
+ it("should handle less than comparison with string numbers", () => {
152
+ const token = new JToken("example.count < 10");
153
+ const obj = { count: "5" };
154
+ const value = token.readTokenValueFrom(obj);
155
+ assert.isTrue(value);
156
+ });
157
+ it("should handle greater than comparison with undefined values", () => {
158
+ const token = new JToken("example.count > 5");
159
+ const obj = { count: undefined };
160
+ const value = token.readTokenValueFrom(obj);
161
+ assert.isFalse(value);
162
+ });
163
+ it("should handle less than comparison with undefined values", () => {
164
+ const token = new JToken("example.count < 10");
165
+ const obj = { count: undefined };
166
+ const value = token.readTokenValueFrom(obj);
167
+ assert.isFalse(value);
168
+ });
54
169
  });
55
170
  });
56
171
  //# sourceMappingURL=token.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token.test.js","sourceRoot":"","sources":["../../src/lib/token.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token.test.js","sourceRoot":"","sources":["../../src/lib/token.test.ts"],"names":[],"mappings":"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"}