j-templates 7.0.98 → 7.0.99
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/package.json +3 -28
- package/LICENSE +0 -21
- package/README.md +0 -462
- package/lib/package.json +0 -13
- /package/{lib/DOM → DOM}/createAssignment.d.ts +0 -0
- /package/{lib/DOM → DOM}/createAssignment.js +0 -0
- /package/{lib/DOM → DOM}/createAttributeAssignment.d.ts +0 -0
- /package/{lib/DOM → DOM}/createAttributeAssignment.js +0 -0
- /package/{lib/DOM → DOM}/createEventAssignment.d.ts +0 -0
- /package/{lib/DOM → DOM}/createEventAssignment.js +0 -0
- /package/{lib/DOM → DOM}/createPropertyAssignment.d.ts +0 -0
- /package/{lib/DOM → DOM}/createPropertyAssignment.js +0 -0
- /package/{lib/DOM → DOM}/domNodeConfig.d.ts +0 -0
- /package/{lib/DOM → DOM}/domNodeConfig.js +0 -0
- /package/{lib/DOM → DOM}/elements.d.ts +0 -0
- /package/{lib/DOM → DOM}/elements.js +0 -0
- /package/{lib/DOM → DOM}/index.d.ts +0 -0
- /package/{lib/DOM → DOM}/index.js +0 -0
- /package/{lib/DOM → DOM}/svgElements.d.ts +0 -0
- /package/{lib/DOM → DOM}/svgElements.js +0 -0
- /package/{lib/Node → Node}/component.d.ts +0 -0
- /package/{lib/Node → Node}/component.js +0 -0
- /package/{lib/Node → Node}/component.types.d.ts +0 -0
- /package/{lib/Node → Node}/component.types.js +0 -0
- /package/{lib/Node → Node}/nodeConfig.d.ts +0 -0
- /package/{lib/Node → Node}/nodeConfig.js +0 -0
- /package/{lib/Node → Node}/vNode.d.ts +0 -0
- /package/{lib/Node → Node}/vNode.js +0 -0
- /package/{lib/Node → Node}/vNode.types.d.ts +0 -0
- /package/{lib/Node → Node}/vNode.types.js +0 -0
- /package/{lib/SYNTAX_PRIMER.md → SYNTAX_PRIMER.md} +0 -0
- /package/{lib/Store → Store}/Diff/diffAsync.d.ts +0 -0
- /package/{lib/Store → Store}/Diff/diffAsync.js +0 -0
- /package/{lib/Store → Store}/Diff/diffSync.d.ts +0 -0
- /package/{lib/Store → Store}/Diff/diffSync.js +0 -0
- /package/{lib/Store → Store}/Diff/diffTree.d.ts +0 -0
- /package/{lib/Store → Store}/Diff/diffTree.js +0 -0
- /package/{lib/Store → Store}/Diff/diffWorker.d.ts +0 -0
- /package/{lib/Store → Store}/Diff/diffWorker.js +0 -0
- /package/{lib/Store → Store}/Diff/workerQueue.d.ts +0 -0
- /package/{lib/Store → Store}/Diff/workerQueue.js +0 -0
- /package/{lib/Store → Store}/Store/store.d.ts +0 -0
- /package/{lib/Store → Store}/Store/store.js +0 -0
- /package/{lib/Store → Store}/Store/storeAsync.d.ts +0 -0
- /package/{lib/Store → Store}/Store/storeAsync.js +0 -0
- /package/{lib/Store → Store}/Store/storeSync.d.ts +0 -0
- /package/{lib/Store → Store}/Store/storeSync.js +0 -0
- /package/{lib/Store → Store}/Tree/observableNode.d.ts +0 -0
- /package/{lib/Store → Store}/Tree/observableNode.js +0 -0
- /package/{lib/Store → Store}/Tree/observableScope.d.ts +0 -0
- /package/{lib/Store → Store}/Tree/observableScope.js +0 -0
- /package/{lib/Store → Store}/index.d.ts +0 -0
- /package/{lib/Store → Store}/index.js +0 -0
- /package/{lib/Utils → Utils}/animation.d.ts +0 -0
- /package/{lib/Utils → Utils}/animation.js +0 -0
- /package/{lib/Utils → Utils}/asyncQueue.d.ts +0 -0
- /package/{lib/Utils → Utils}/asyncQueue.js +0 -0
- /package/{lib/Utils → Utils}/decorators.d.ts +0 -0
- /package/{lib/Utils → Utils}/decorators.js +0 -0
- /package/{lib/Utils → Utils}/emitter.d.ts +0 -0
- /package/{lib/Utils → Utils}/emitter.js +0 -0
- /package/{lib/Utils → Utils}/functions.d.ts +0 -0
- /package/{lib/Utils → Utils}/functions.js +0 -0
- /package/{lib/Utils → Utils}/index.d.ts +0 -0
- /package/{lib/Utils → Utils}/index.js +0 -0
- /package/{lib/Utils → Utils}/injector.d.ts +0 -0
- /package/{lib/Utils → Utils}/injector.js +0 -0
- /package/{lib/Utils → Utils}/json.d.ts +0 -0
- /package/{lib/Utils → Utils}/json.js +0 -0
- /package/{lib/Utils → Utils}/list.d.ts +0 -0
- /package/{lib/Utils → Utils}/list.js +0 -0
- /package/{lib/Utils → Utils}/router.d.ts +0 -0
- /package/{lib/Utils → Utils}/router.js +0 -0
- /package/{lib/Utils → Utils}/scheduling.d.ts +0 -0
- /package/{lib/Utils → Utils}/scheduling.js +0 -0
- /package/{lib/Utils → Utils}/thread.d.ts +0 -0
- /package/{lib/Utils → Utils}/thread.js +0 -0
- /package/{lib/Utils → Utils}/utils.types.d.ts +0 -0
- /package/{lib/Utils → Utils}/utils.types.js +0 -0
- /package/{lib/_not_used → _not_used}/array.d.ts +0 -0
- /package/{lib/_not_used → _not_used}/array.js +0 -0
- /package/{lib/_not_used → _not_used}/avlTree.d.ts +0 -0
- /package/{lib/_not_used → _not_used}/avlTree.js +0 -0
- /package/{lib/index.d.ts → index.d.ts} +0 -0
- /package/{lib/index.js → index.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "j-templates",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.99",
|
|
4
4
|
"description": "j-templates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "https://github.com/TypesInCode/jTemplates",
|
|
@@ -8,31 +8,6 @@
|
|
|
8
8
|
"keywords": [
|
|
9
9
|
""
|
|
10
10
|
],
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
],
|
|
14
|
-
"main": "lib/index.js",
|
|
15
|
-
"typings": "lib/index.d.ts",
|
|
16
|
-
"scripts": {
|
|
17
|
-
"clean": "rimraf lib",
|
|
18
|
-
"build": "npm run buildquick && node ./tools/copy-package-json.js",
|
|
19
|
-
"buildquick": "npm run clean && echo Using TypeScript && tsc --version && tsc --pretty",
|
|
20
|
-
"builddocs": "typedoc --out docs src/index.ts src/DOM/index.ts src/Utils/index.ts src/Store/index.ts && touch docs/.nojekyll",
|
|
21
|
-
"test": "vitest run",
|
|
22
|
-
"test_watch": "vitest --ui"
|
|
23
|
-
},
|
|
24
|
-
"devDependencies": {
|
|
25
|
-
"@fission-ai/openspec": "^1.1.1",
|
|
26
|
-
"@types/node": "^25.2.0",
|
|
27
|
-
"@vitest/ui": "^4.1.5",
|
|
28
|
-
"fs-extra": "^11.3.3",
|
|
29
|
-
"jsdom": "^27.4.0",
|
|
30
|
-
"rimraf": "^6.1.2",
|
|
31
|
-
"typedoc": "^0.28.16",
|
|
32
|
-
"typescript": "^5.9.3",
|
|
33
|
-
"vitest": "^4.1.5"
|
|
34
|
-
},
|
|
35
|
-
"engines": {
|
|
36
|
-
"node": ">=4.0.0"
|
|
37
|
-
}
|
|
11
|
+
"main": "./index.js",
|
|
12
|
+
"typings": "./index.d.ts"
|
|
38
13
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2017 TypesInCode
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,462 +0,0 @@
|
|
|
1
|
-
# j-templates
|
|
2
|
-
|
|
3
|
-
A type-safe, fine-grained reactive framework for building browser applications in TypeScript — with no compile step, no virtual DOM diffing, and no framework lock-in.
|
|
4
|
-
|
|
5
|
-
## Why j-templates?
|
|
6
|
-
|
|
7
|
-
- **Fine-grained reactivity** — Updates propagate at the property level, not the component level. Only the DOM nodes that depend on changed state are touched.
|
|
8
|
-
- **Zero compile step** — Pure TypeScript with experimental decorators. Works with any bundler (Vite, Webpack, Rollup).
|
|
9
|
-
- **Object identity preservation** — `@Computed` uses diff-based updates so downstream consumers receive the same reference when data hasn't structurally changed, enabling efficient DOM reuse without key-based reconciliation.
|
|
10
|
-
- **Proxy-based deep reactivity** — `@State` wraps objects and arrays in reactive proxies. Nested property writes and array mutations (`push`, `splice`, etc.) are tracked automatically.
|
|
11
|
-
- **Hierarchical dependency injection** — Components resolve dependencies through a parent-chain injector, making it easy to share services across a component tree.
|
|
12
|
-
- **Minimal dependencies** — Zero runtime dependencies. Only TypeScript as a dev dependency.
|
|
13
|
-
|
|
14
|
-
## Install
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm install j-templates
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Requires `experimentalDecorators`, `emitDecoratorMetadata`, and `useDefineForClassFields: false` in your `tsconfig.json`:
|
|
21
|
-
|
|
22
|
-
```json
|
|
23
|
-
{
|
|
24
|
-
"compilerOptions": {
|
|
25
|
-
"experimentalDecorators": true,
|
|
26
|
-
"emitDecoratorMetadata": true,
|
|
27
|
-
"useDefineForClassFields": false
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Hello World
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
import { Component } from "j-templates";
|
|
36
|
-
import { div } from "j-templates/DOM";
|
|
37
|
-
|
|
38
|
-
class HelloWorld extends Component {
|
|
39
|
-
Template() {
|
|
40
|
-
return div({}, () => "Hello world");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const helloWorld = Component.ToFunction("hello-world", HelloWorld);
|
|
45
|
-
Component.Attach(document.body, helloWorld({}));
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Module Structure
|
|
49
|
-
|
|
50
|
-
| Module | Import Path | Exports |
|
|
51
|
-
|---|---|---|
|
|
52
|
-
| Core | `j-templates` | `Component`, `scope`, `gate`, `peek`, `mapped` |
|
|
53
|
-
| DOM | `j-templates/DOM` | 70+ HTML element factories (`div`, `button`, `table`, …), `text`, `fragment` |
|
|
54
|
-
| Store | `j-templates/Store` | `StoreSync`, `StoreAsync`, `ObservableScope`, `ObservableNode` |
|
|
55
|
-
| Utils | `j-templates/Utils` | `@Value`, `@State`, `@Scope`, `@Computed`, `@ComputedAsync`, `@Watch`, `@Inject`, `@Destroy`, `Animation`, `IDestroyable` |
|
|
56
|
-
|
|
57
|
-
## Reactive State
|
|
58
|
-
|
|
59
|
-
### `@Value` — Primitive reactive property
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import { Component } from "j-templates";
|
|
63
|
-
import { Value } from "j-templates/Utils";
|
|
64
|
-
import { div, button } from "j-templates/DOM";
|
|
65
|
-
|
|
66
|
-
class Counter extends Component {
|
|
67
|
-
@Value() count = 0;
|
|
68
|
-
|
|
69
|
-
Template() {
|
|
70
|
-
return div({}, () => [
|
|
71
|
-
button({ on: { click: () => this.count-- } }, () => "-"),
|
|
72
|
-
div({}, () => this.count),
|
|
73
|
-
button({ on: { click: () => this.count++ } }, () => "+"),
|
|
74
|
-
]);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### `@State` — Deep reactive objects and arrays
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { State } from "j-templates/Utils";
|
|
83
|
-
|
|
84
|
-
class TodoList extends Component {
|
|
85
|
-
@State() items: string[] = [];
|
|
86
|
-
|
|
87
|
-
addItem(text: string) {
|
|
88
|
-
this.items.push(text); // Triggers reactive update automatically
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
removeItem(index: number) {
|
|
92
|
-
this.items.splice(index, 1); // Also tracked
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### `@Scope` — Computed value (new reference on change)
|
|
98
|
-
|
|
99
|
-
Best for cheap computations that return primitives or new arrays.
|
|
100
|
-
|
|
101
|
-
```typescript
|
|
102
|
-
import { Scope } from "j-templates/Utils";
|
|
103
|
-
|
|
104
|
-
class Stats extends Component {
|
|
105
|
-
@Value() count = 0;
|
|
106
|
-
|
|
107
|
-
@Scope()
|
|
108
|
-
get doubled() { return this.count * 2; }
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### `@Computed` — Computed value with object identity preservation
|
|
113
|
-
|
|
114
|
-
Best for expensive computations or when downstream consumers need stable references. Uses `StoreSync` + diff-based updates so the same object reference is returned when the structure hasn't changed.
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
import { Computed } from "j-templates/Utils";
|
|
118
|
-
|
|
119
|
-
class TodoApp extends Component {
|
|
120
|
-
@State() items: Todo[] = [];
|
|
121
|
-
|
|
122
|
-
@Computed()
|
|
123
|
-
get completedItems(): Todo[] {
|
|
124
|
-
return this.items.filter(t => t.done);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### `@Watch` — React to state changes
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { Watch, Value } from "j-templates/Utils";
|
|
133
|
-
|
|
134
|
-
class Logger extends Component {
|
|
135
|
-
@Value() message = "";
|
|
136
|
-
|
|
137
|
-
@Watch((self) => self.message)
|
|
138
|
-
onMessageChanged(newValue: string) {
|
|
139
|
-
console.log("message changed to:", newValue);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
## Template System
|
|
145
|
-
|
|
146
|
-
Element functions accept a config object and children:
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
div({
|
|
150
|
-
props: () => ({ className: "card" }), // Reactive DOM properties
|
|
151
|
-
attrs: { "aria-label": "card" }, // Static HTML attributes
|
|
152
|
-
on: { click: () => handleClick() }, // Event handlers
|
|
153
|
-
data: () => this.items, // Reactive data source
|
|
154
|
-
}, children)
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Reactive children
|
|
158
|
-
|
|
159
|
-
Pass a function as the second argument to make children reactive:
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
div({}, () => `Count: ${this.count}`) // Re-renders when count changes
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### List rendering with `data`
|
|
166
|
-
|
|
167
|
-
The `data` property drives iterative rendering. Each item is mapped to a child via a callback:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
tbody({ data: () => this.items }, (item: Todo) =>
|
|
171
|
-
tr({}, () => [
|
|
172
|
-
td({}, () => item.text),
|
|
173
|
-
td({}, () => item.done ? "Done" : "Pending"),
|
|
174
|
-
])
|
|
175
|
-
);
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Conditional rendering
|
|
179
|
-
|
|
180
|
-
Use ternary expressions inside reactive functions:
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
div({}, () => this.isLoading ? "Loading…" : "Content");
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
Or use `data` with a falsy value to hide all children (like `*ngIf`):
|
|
187
|
-
|
|
188
|
-
```typescript
|
|
189
|
-
// When this.items is null/undefined/false, all children are destroyed
|
|
190
|
-
ul({ data: () => this.items }, (item) =>
|
|
191
|
-
li({}, () => item.name),
|
|
192
|
-
);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
Use `fragment()` when you want conditional rendering **without a wrapper DOM node** — its children reconcile directly into the real ancestor:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
fragment({ data: () => this.isLoading }, () => div({}, () => "Loading"));
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Use `gate()` when the condition shares a children function with frequently-changing siblings — it only re-evaluates when the boolean flips:
|
|
202
|
-
|
|
203
|
-
```typescript
|
|
204
|
-
gate(() => this.isLoading) ? div({}, () => "Loading") : div({}, () => "Content");
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
### Fragment elements
|
|
208
|
-
|
|
209
|
-
`fragment()` creates a container with **no DOM node** — its children are inserted directly into the nearest real ancestor element. Use it when you need a reactive scope or a `data:` iteration but don't want an extra wrapper element in the DOM.
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
import { fragment } from "j-templates/DOM";
|
|
213
|
-
|
|
214
|
-
// Iteration with no wrapper node
|
|
215
|
-
fragment({ data: () => this.items }, (item) => div({}, () => item.name));
|
|
216
|
-
|
|
217
|
-
// Nested fragments flatten into the real ancestor
|
|
218
|
-
fragment({}, () => [
|
|
219
|
-
div({}, () => "OUTER"),
|
|
220
|
-
fragment({}, () => (this.showExtra ? div({}, () => "EXTRA") : div({}, () => "BASE"))),
|
|
221
|
-
]);
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
Key behaviors:
|
|
225
|
-
- **No DOM node** — a falsy `data:` value renders *nothing* (no empty wrapper box left behind).
|
|
226
|
-
- **`data:` behaves like any DOM element** — iterates arrays, wraps truthy scalars, collapses falsy values.
|
|
227
|
-
- **Nesting is fine** — fragments inside fragments flatten into the real ancestor.
|
|
228
|
-
- **Cannot be attached directly** — a fragment has no node to attach; wrap it in a real element (e.g. `div`) first.
|
|
229
|
-
|
|
230
|
-
## Component Architecture
|
|
231
|
-
|
|
232
|
-
Components are class-based with three generic type parameters:
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
class MyComponent extends Component<Data, Templates, Events>
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
- **`Data`** — Shape of data passed to the component factory
|
|
239
|
-
- **`Templates`** — Interface describing injectable template slots
|
|
240
|
-
- **`Events`** — Interface describing events the component fires
|
|
241
|
-
|
|
242
|
-
### Lifecycle
|
|
243
|
-
|
|
244
|
-
1. **Constructor** — Avoid overriding. Use `Bound()` instead.
|
|
245
|
-
2. **`Bound()`** — Called after the component is attached to the DOM. Initialize state, start watchers, fetch data here.
|
|
246
|
-
3. **`Template()`** — Return the component's virtual DOM.
|
|
247
|
-
4. **`Destroy()`** — Cleanup. Automatically destroys all `@Value`/`@State`/`@Scope`/`@Computed` scopes and calls `.Destroy()` on `@Destroy`-decorated properties.
|
|
248
|
-
|
|
249
|
-
### Creating and mounting components
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
// Convert class to factory function
|
|
253
|
-
const myComp = Component.ToFunction("my-comp", MyComponent);
|
|
254
|
-
|
|
255
|
-
// Mount to DOM
|
|
256
|
-
Component.Attach(document.body, myComp({ data: myData }));
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Component events
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
interface ButtonEvents {
|
|
263
|
-
click: { x: number; y: number };
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
class MyButton extends Component<void, void, ButtonEvents> {
|
|
267
|
-
Template() {
|
|
268
|
-
return button({
|
|
269
|
-
on: { click: (e) => this.Fire("click", { x: e.clientX, y: e.clientY }) },
|
|
270
|
-
}, () => "Click me");
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Component composition with templates
|
|
276
|
-
|
|
277
|
-
```typescript
|
|
278
|
-
interface RowTemplate<D> {
|
|
279
|
-
row: (data: D) => vNode;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
class DataTable<D> extends Component<{ items: D[] }, RowTemplate<D>> {
|
|
283
|
-
Template() {
|
|
284
|
-
return tbody({ data: () => this.Data.items }, (item: D) =>
|
|
285
|
-
this.Templates.row(item)
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Usage — inject the row template
|
|
291
|
-
const table = Component.ToFunction("data-table", DataTable);
|
|
292
|
-
table({
|
|
293
|
-
data: { items: myItems },
|
|
294
|
-
templates: { row: (item) => tr({}, () => td({}, () => item.name)) },
|
|
295
|
-
});
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Dependency Injection
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
import { Inject, Destroy } from "j-templates/Utils";
|
|
302
|
-
|
|
303
|
-
abstract class ApiService {
|
|
304
|
-
abstract fetch(): Promise<Data[]>;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
class DataView extends Component {
|
|
308
|
-
@Inject(ApiService) api!: ApiService;
|
|
309
|
-
@Destroy() subscription?: IDestroyable;
|
|
310
|
-
|
|
311
|
-
Bound() {
|
|
312
|
-
this.subscription = this.api.fetch();
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
Injectors are hierarchical — if a dependency isn't found on the current component, the framework walks up the component tree to parent injectors.
|
|
318
|
-
|
|
319
|
-
## Stores
|
|
320
|
-
|
|
321
|
-
### `StoreSync` — Synchronous diff-based store
|
|
322
|
-
|
|
323
|
-
```typescript
|
|
324
|
-
import { StoreSync } from "j-templates/Store";
|
|
325
|
-
|
|
326
|
-
const store = new StoreSync((item) => item.id); // Key function for identity
|
|
327
|
-
|
|
328
|
-
store.Write({ id: "1", name: "Alice" }); // Write & diff
|
|
329
|
-
store.Patch("1", { name: "Bob" }); // Deep merge patch
|
|
330
|
-
store.Push("1", "tags", "admin"); // Push to nested array
|
|
331
|
-
const user = store.Get<User>("1"); // Observable retrieval
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### `StoreAsync` — Asynchronous diff-based store
|
|
335
|
-
|
|
336
|
-
Same API as `StoreSync`, but all mutations return `Promise` and diff computation runs in a Web Worker to keep the main thread free.
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
import { StoreAsync } from "j-templates/Store";
|
|
340
|
-
|
|
341
|
-
const store = new StoreAsync((item) => item.id);
|
|
342
|
-
await store.Write({ id: "1", name: "Alice" });
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
## Observable Primitives
|
|
346
|
-
|
|
347
|
-
For advanced use cases, the reactive primitives are directly accessible:
|
|
348
|
-
|
|
349
|
-
### `ObservableScope` — Signal-like reactive scope
|
|
350
|
-
|
|
351
|
-
```typescript
|
|
352
|
-
import { ObservableScope } from "j-templates/Store";
|
|
353
|
-
|
|
354
|
-
const scope = ObservableScope.Create(() => count.value * 2);
|
|
355
|
-
const value = ObservableScope.Value(scope); // Read (registers dependency)
|
|
356
|
-
const peeked = ObservableScope.Peek(scope); // Read without registering
|
|
357
|
-
ObservableScope.Watch(scope, (v) => console.log(v));
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### `ObservableNode` — Proxy-based deep reactivity
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
import { ObservableNode } from "j-templates/Store";
|
|
364
|
-
|
|
365
|
-
const obj = ObservableNode.Create({ a: 1, b: { c: 2 } });
|
|
366
|
-
obj.a = 10; // Triggers reactive update on leaf scope
|
|
367
|
-
const raw = ObservableNode.Unwrap(obj); // { a: 10, b: { c: 2 } }
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### Inline computed scopes — `scope()`, `gate()`, `peek()`, `mapped()`
|
|
371
|
-
|
|
372
|
-
Memoized computed scopes you can create inline within a watch context (template functions, `@Scope` getters, etc.). All accept `() => T | Promise<T>`.
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
import { scope, gate, peek, mapped } from "j-templates";
|
|
376
|
-
|
|
377
|
-
// Full reactivity — emits on every recomputation
|
|
378
|
-
scope(() => this.Data.items);
|
|
379
|
-
|
|
380
|
-
// Emission gatekeeper — only emits when the value changes by ===
|
|
381
|
-
gate(() => this.count > 10);
|
|
382
|
-
|
|
383
|
-
// Read without subscribing — no dependency registered
|
|
384
|
-
peek(() => Date.now());
|
|
385
|
-
|
|
386
|
-
// Per-item scope (the mechanism `data:` uses internally)
|
|
387
|
-
mapped(item, (d) => /* ... */);
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
## Animation
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
import { Animation, AnimationType } from "j-templates/Utils";
|
|
394
|
-
|
|
395
|
-
const anim = new Animation(AnimationType.EaseIn, 1000, (next) => {
|
|
396
|
-
element.style.opacity = String(next);
|
|
397
|
-
});
|
|
398
|
-
anim.Animate(0, 1); // Fades from 0 to 1 over 1000ms
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
## API Quick Reference
|
|
402
|
-
|
|
403
|
-
### Decorators
|
|
404
|
-
|
|
405
|
-
| Decorator | Target | Description |
|
|
406
|
-
|---|---|---|
|
|
407
|
-
| `@Value()` | Property | Reactive primitive (number, string, boolean) |
|
|
408
|
-
| `@State()` | Property | Deep reactive object/array via proxy |
|
|
409
|
-
| `@Scope()` | Getter | Cached computed value; new reference on change |
|
|
410
|
-
| `@Computed()` | Getter | Cached computed with identity preservation via diff |
|
|
411
|
-
| `@ComputedAsync(default)` | Getter | Async `@Computed` backed by `StoreAsync` |
|
|
412
|
-
| `@Watch(scopeFn)` | Method | Auto-subscribe method to reactive value changes |
|
|
413
|
-
| `@Inject(type)` | Property | Dependency injection from component injector |
|
|
414
|
-
| `@Destroy()` | Property | Auto-calls `.Destroy()` on property when component is destroyed |
|
|
415
|
-
|
|
416
|
-
### Component API
|
|
417
|
-
|
|
418
|
-
| Method | Description |
|
|
419
|
-
|---|---|
|
|
420
|
-
| `Template()` | Return virtual DOM — override in subclass |
|
|
421
|
-
| `Bound()` | Lifecycle hook — called after DOM attachment |
|
|
422
|
-
| `Destroy()` | Lifecycle hook — cleanup (auto-cleans decorators) |
|
|
423
|
-
| `Fire(event, data)` | Emit a component event |
|
|
424
|
-
| `Component.ToFunction(name, Class)` | Convert class to factory function |
|
|
425
|
-
| `Component.Register(name, Class)` | Register as Web Component (custom element) |
|
|
426
|
-
| `Component.Attach(node, vNode)` | Mount a vNode to a real DOM node |
|
|
427
|
-
|
|
428
|
-
### Store API
|
|
429
|
-
|
|
430
|
-
| Method | StoreSync | StoreAsync |
|
|
431
|
-
|---|---|---|
|
|
432
|
-
| `Write(data, key?)` | Sync | `Promise` |
|
|
433
|
-
| `Patch(key, patch)` | Sync | `Promise` |
|
|
434
|
-
| `Push(key, ...data)` | Sync | `Promise` |
|
|
435
|
-
| `Splice(key, start, del?, ...items)` | Sync | `Promise` |
|
|
436
|
-
| `Get(key)` | Sync | Sync |
|
|
437
|
-
| `Destroy()` | — | Stops async queue |
|
|
438
|
-
|
|
439
|
-
## Documentation
|
|
440
|
-
|
|
441
|
-
### Tutorials
|
|
442
|
-
- [Getting Started](docs/tutorials/01-getting-started.md)
|
|
443
|
-
- [Your First Component](docs/tutorials/02-your-first-component.md)
|
|
444
|
-
- [Reactive State Basics](docs/tutorials/03-reactive-state-basics.md)
|
|
445
|
-
|
|
446
|
-
### Patterns
|
|
447
|
-
- [Components](docs/patterns/01-components.md) — Component class, lifecycle, data passing, template functions, events, composition
|
|
448
|
-
- [Reactivity](docs/patterns/02-reactivity.md) — ObservableScope, ObservableNode, stores, decorators (@Value, @State, @Scope, @Computed, @Watch)
|
|
449
|
-
- [Templates & Data](docs/patterns/03-templates-and-data.md) — DOM element functions, reactive bindings, data modeling, animation
|
|
450
|
-
- [Dependency Injection](docs/patterns/04-dependency-injection.md) — Injector hierarchy, @Inject, @Destroy, IDestroyable
|
|
451
|
-
|
|
452
|
-
### Reference
|
|
453
|
-
- [Syntax Primer v3](docs/SYNTAX_PRIMER_v3.md)
|
|
454
|
-
|
|
455
|
-
## Examples
|
|
456
|
-
|
|
457
|
-
- [Real-Time Dashboard](examples/real_time_dashboard/) — Full application example
|
|
458
|
-
- [Tutorial Project](examples/tutorial_project/) — Progressive tutorial series
|
|
459
|
-
|
|
460
|
-
## License
|
|
461
|
-
|
|
462
|
-
MIT
|
package/lib/package.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "j-templates",
|
|
3
|
-
"version": "7.0.98",
|
|
4
|
-
"description": "j-templates",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"repository": "https://github.com/TypesInCode/jTemplates",
|
|
7
|
-
"author": "Jay Landrum",
|
|
8
|
-
"keywords": [
|
|
9
|
-
""
|
|
10
|
-
],
|
|
11
|
-
"main": "./index.js",
|
|
12
|
-
"typings": "./index.d.ts"
|
|
13
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|