targetj 1.0.224 → 1.0.226

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
@@ -1,4 +1,4 @@
1
- # TargetJS: UI Development as a Sequence
1
+ # TargetJS: State as Destination, UI as Sequence
2
2
 
3
3
  **[targetjs.io](https://targetjs.io)**
4
4
  [![MIT LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/livetrails/targetjs/blob/main/LICENSE)
@@ -10,19 +10,31 @@ TargetJS is a high-performance JavaScript UI framework with ultra-compact syntax
10
10
  It can be used as a full-featured framework or as a lightweight library alongside other frameworks. It is also a highly performant web framework, as shown in the [framework benchmark](https://krausest.github.io/js-framework-benchmark/current.html).
11
11
 
12
12
 
13
- ## The Philosophy Behind TargetJS
13
+ ## What problems TargetJS solves
14
14
 
15
- Traditional frameworks model the UI as a function of state: change state, re-render the UI. When state changes from A to B, the UI immediately jumps to B. The framework doesn’t naturally represent the *journey* from A to B. But modern, rich user experiences are journeys, not jumps. They are built on sequences that unfold over time. For example:
15
+ **UI frameworks model the final result, not transition**
16
16
 
17
- > Click Animate button Chain secondary animation Fetch data Render listAnimate itemsPauseAnimate an important item
17
+ Traditional frameworks model the UI as a function of state: change state, re-render the UI. When state changes from A B, the UI immediately jumps to B. The framework doesn’t naturally represent the journey from A B. But modern, rich user experiences are more like: A transition B.
18
18
 
19
- TargetJS is built for this reality. Instead of managing complex flags, your code structure mirrors these sequences directly.
19
+ TargetJS treats state as a destination. Values are not only assigned. They can be approached over time through configurable steps. This makes transitions a native part of state change rather than an afterthought. TargetJS also delivers CSS-level transition efficiency.
20
20
 
21
- It achieves this through Targets. A Target is a self-contained unit that merges data (fields) and logic (methods) into a single reactive block. Each Target has its own internal state, timing, and lifecycle, acting like a living cell within your app. By simply ordering them in your code, you create complex asynchronous workflows without async/await or .then() chains.
21
+ **Fragmentation across multiple mental models**
22
22
 
23
- In addition, efficient animation is built directly into the framework using the Web Animations API, delivering CSS-level efficiency.
23
+ In many applications, state, animation, events, loading, timing, and callbacks are all handled through separate concepts or APIs. This creates glue code and a mental split between them.
24
24
 
25
- By adopting a compact style, TargetJS makes the journey from A to B efficient and explicit, with significantly less code than traditional frameworks.
25
+ TargetJS unifies them under one concept and one model. Methods and fields are unified and both become reactive units with their own state, lifecycle, timing, execution conditions, looping, and callbacks. This shifts fields from passive values to active participants, reducing boilerplate and keeping application logic consolidated.
26
+
27
+ **UI sequences are difficult to trace in code**
28
+
29
+ UIs often follow sequences like this:
30
+
31
+ Click → animate button → fetch data → render results → animate items → highlight one item
32
+
33
+ In traditional code, that sequence is often scattered across different places such as event handlers, effects, promises, and callbacks.
34
+
35
+ TargetJS code order and target reactivity allow the implementation to more closely mirror the actual UI sequence.
36
+
37
+ With its compact style, TargetJS makes the journey from A → B efficient and explicit, with significantly less code than traditional frameworks.
26
38
 
27
39
  ## ⚡ Quick Start (30 Seconds)
28
40
 
@@ -48,17 +60,39 @@ App({
48
60
  }).mount("#app");
49
61
  ```
50
62
 
51
- ## Understanding TargetJS Syntax
63
+ ## Targets
64
+
65
+ In TargetJS, targets are the fundamental unit of behavior.
66
+ Methods, properties, and objects are internally transformed into targets that the framework schedules and executes.
67
+
52
68
 
53
- These symbols tell the framework **when** a target should run.
69
+ ### Execution Syntax
54
70
 
55
- | Symbol | Name | Behavior |
56
- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------|
57
- | `name` | Standard | Runs immediately in the order it appears. |
58
- | `name$` | Reactive | Runs every time the previous sibling target runs. |
59
- | `name$$` | Deferred | Runs only after the entire preceding target chain including children, animations, and API calls has fully completed. |
60
- | `_name` | Inactive | Does not run automatically. Trigger it manually via `.activateTarget()`. |
61
-
71
+ Target names can include special symbols that determine **when they execute**.
72
+
73
+ | Symbol | Name | Behavior |
74
+ |------|------|------|
75
+ | `name` | Standard | Runs immediately in the order it appears. |
76
+ | `name$` | Reactive | Runs every time the previous sibling target runs. |
77
+ | `name$$` | Deferred | Runs only after the entire preceding target chain (including children, animations, and API calls) completes. |
78
+ | `_name` | Inactive | Does not run automatically. Trigger it manually via `.activateTarget()`. |
79
+
80
+
81
+ ### Target Controls
82
+
83
+ A target can also be defined as an object with optional controls that manage its lifecycle and execution.
84
+
85
+ | Property | Description |
86
+ |------|------|
87
+ | `value` | The data or function that determines the target's state. |
88
+ | `steps` | Turns a value change into an animation. |
89
+ | `interval` | Delay (ms) between steps or executions. |
90
+ | `cycles` | Number of times the target repeats. |
91
+ | `loop` | Boolean form of repetition for continuous execution. |
92
+ | `enabledOn` | Determines whether the target is enabled for execution. |
93
+ | `easing` | Predefined easing function controlling how values update over steps. |
94
+ | `onComplete` | Callback triggered when this target (and its children) finishes. |
95
+ | `onValueChange` | Callback triggered when the target emits a new value. |
62
96
 
63
97
  ## Examples: Like Button → Animated Like (in 3 Steps)
64
98
 
@@ -114,14 +148,9 @@ App({
114
148
  heart$$: { // Wait for the button animation to finish, THEN add and animate the heart.
115
149
  html: "♥", color: "crimson", fontSize: 20,
116
150
  fly() {
117
- const cx = this.getCenterX(), cy = this.getCenterY();
118
- this.setTarget({
119
- opacity: { value: [0, 1, 1, 0.8, 0.1], steps: 20 },
120
- scale: { value: [0.8, 1.4, 1.1, 0.9, 0.8], steps: 20 },
121
- rotate: { value: [0, 12, -8, 6, 0], steps: 20 },
122
- x: { value: [cx, cx + 22, cx - 16, cx + 10, cx], steps: 30 },
123
- y: { value: [cy - 8, cy - 70, cy - 90, cy - 120, cy - 150], steps: 30 }
124
- });
151
+ const cx = (this.parent.getWidth() - this.getWidth()) / 2;
152
+ this.setTarget('x', { value: [cx, cx + 22, cx - 16, cx + 10, cx ], steps: 50, cycles: 2 }); // Repeat it twice
153
+ this.setTarget('y', { value: [0, -120], steps: 400 });
125
154
  }
126
155
  }
127
156
  }).mount("#likeButton");
@@ -152,14 +181,9 @@ App({
152
181
  heart$$: {
153
182
  html: "♥", color: "crimson", fontSize: 20,
154
183
  fly() {
155
- const cx = this.getCenterX(), cy = this.getCenterY();
156
- this.setTarget({
157
- opacity: { value: [0, 1, 1, 0.8, 0.1], steps: 20 },
158
- scale: { value: [0.8, 1.4, 1.1, 0.9, 0.8], steps: 20 },
159
- rotate: { value: [0, 12, -8, 6, 0], steps: 20 },
160
- x: { value: [cx, cx + 22, cx - 16, cx + 10, cx], steps: 30 },
161
- y: { value: [cy - 8, cy - 70, cy - 90, cy - 120, cy - 150], steps: 30 }
162
- });
184
+ const cx = (this.parent.getWidth() - this.getWidth()) / 2;
185
+ this.setTarget('x', { value: [cx, cx + 22, cx - 16, cx + 10, cx ], steps: 50, cycles: 2 }); // Repeat it twice
186
+ this.setTarget('y', { value: [0, -120], steps: 400 });
163
187
  }
164
188
  },
165
189
  fetch$$: { method: "POST", id: 123, url: "/api/like" }, // Wait for the heart to finish, THEN fetch
@@ -182,7 +206,6 @@ Each target has its own state and lifecycle. Targets execute automatically in th
182
206
  1. Deeper Examples:
183
207
  - [Loading Five Users Example](#loading-five-users-example)
184
208
  - [Infinite Loading and Scrolling Example](#infinite-loading-and-scrolling-example)
185
- 1. [Target Methods](#target-methods)
186
209
  1. [Special Target Names](#special-target-names)
187
210
  1. [How to Debug in TargetJS](#how-to-debug-in-targetjs)
188
211
  1. [Documentation](#documentation)
@@ -334,7 +357,7 @@ App({
334
357
  gap: 10,
335
358
  fetch: {
336
359
  interval: 1000,
337
- cycles: 4,
360
+ cycles: 5,
338
361
  value(i) { return `https://targetjs.io/api/randomUser?id=user${i}`; }
339
362
  },
340
363
  child$() {
@@ -411,7 +434,7 @@ App({
411
434
  },
412
435
  wave$$: {
413
436
  interval: 30,
414
- cycles() { return this.visibleChildren.length - 1; },
437
+ cycles() { return this.visibleChildren.length; },
415
438
  value(i) {
416
439
  const child = this.visibleChildren[i];
417
440
  child.setTarget("scale", { value: [1, 1.06, 1], steps: 18 });
@@ -431,46 +454,42 @@ App({
431
454
  ```
432
455
  ---
433
456
 
434
- ## Technical Reference
435
-
436
- ### Target Methods
437
-
438
- Every target can be an object with these optional controls:
439
-
440
- 1. **value**
441
- The data or function that determines the target's state.
457
+ ## Special Target Names
442
458
 
443
- 1. **steps**
444
- Turns a value change into an animation (e.g., steps: 20).
459
+ Some target names have built-in meaning and interact directly with the DOM, layout system, or browser events.
460
+ Because these behaviors are expressed as targets, they still participate in the same execution system and dependency flows as any other target.
445
461
 
446
- 1. **interval**
447
- The delay (ms) between steps or executions.
462
+ **Styles**
448
463
 
449
- 1. **cycles**
450
- How many times to repeat the target.
464
+ These targets update CSS properties and transforms:
451
465
 
452
- 1. **onComplete**
453
- Callback when this target (and its children) finishes.
466
+ - `width`, `height`
467
+ - `opacity`
468
+ - `x`, `y`, `z`
469
+ - `rotate`, `rotateX`, `rotateY`, `rotateZ`
470
+ - `scale`
471
+ - `backgroundColor`, `color`
454
472
 
455
- 1. **enabledOn**
456
- Determines whether the target is enabled for execution.
473
+ These can be animated simply by adding `steps`.
457
474
 
458
- 1. **loop**
459
- Managed the repetition of target execution. Similar to `cycles` but uses boolean instead.
475
+ **Structure**
460
476
 
461
- 1. **easing**
462
- A string that defines a predefined easing function that controls how the actual value is updated in relation to the steps.
477
+ These targets define the structure of the interface:
463
478
 
464
- 1. **onValueChange**
465
- This callback is triggered when `value` emits a new value.
479
+ - `children` or `addChildren` – adds new children each time the target executes
480
+ - `html` inner HTML content, often simple text
481
+ - `element` – specify the DOM element type (e.g., `div`, `canvas`)
466
482
 
467
- ### Special Target Names
483
+ **Events**
468
484
 
469
- TargetJS maps directly to the DOM for zero-friction styling. For example:
485
+ These targets respond to browser events:
470
486
 
471
- - **Styles**: `width`, `height`, `opacity`, `x`, `y`, `rotate`, `scale`, `backgroundColor`.
472
- - **Structure**: `html`, `children`, `element`, `domHolder`.
473
- - **Events**: `onClick`, `onScroll`, `onKey`, `onVisibleChildrenChange`, `onResize`.
487
+ - `onClick`
488
+ - `onScroll`
489
+ - `onKey`
490
+ - `onResize`
491
+ - `onEnter` / `onLeave`
492
+ - `onVisibleChildrenChange`
474
493
 
475
494
  ## How to Debug in TargetJS
476
495
 
package/build/Bracket.js CHANGED
@@ -157,9 +157,28 @@ var Bracket = exports.Bracket = /*#__PURE__*/function (_TModel) {
157
157
  }, {
158
158
  key: "shouldCalculateChildren",
159
159
  value: function shouldCalculateChildren() {
160
- var result = this.isVisible() && this.getDirtyLayout() !== false || this.currentStatus === 'new' || this.isNowVisible;
161
- this.currentStatus = undefined;
162
- return result;
160
+ if (this.currentStatus === 'new' || this.isNowVisible) {
161
+ var visibleChild = this.getChildren().find(function (child) {
162
+ return child.calcVisibility();
163
+ });
164
+ this.currentStatus = undefined;
165
+ if (visibleChild) {
166
+ this.currentStatus = 'new';
167
+ var parent = this.parent;
168
+ while (parent && parent.type === 'BI') {
169
+ parent.currentStatus = 'new';
170
+ parent = parent.parent;
171
+ }
172
+ }
173
+ return true;
174
+ }
175
+ if (this.getDirtyLayout() === false) {
176
+ return false;
177
+ }
178
+ if (this.isVisible()) {
179
+ return true;
180
+ }
181
+ return false;
163
182
  }
164
183
  }, {
165
184
  key: "getDirtyLayout",
@@ -124,16 +124,11 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
124
124
  value: function getTModelKey(tmodel, targetName) {
125
125
  return "".concat(document.URL, "_").concat(tmodel.oid, "_").concat(targetName);
126
126
  }
127
- }, {
128
- key: "getLoadTargetName",
129
- value: function getLoadTargetName(targetName) {
130
- return "load-".concat(targetName);
131
- }
132
127
  }, {
133
128
  key: "addToTModelKeyMap",
134
129
  value: function addToTModelKeyMap(tmodel, targetName, fetchId, cacheId) {
135
130
  var key = this.getTModelKey(tmodel, targetName);
136
- var loadTargetName = this.getLoadTargetName(targetName);
131
+ var loadTargetName = _TUtil.TUtil.getLoadTargetName(targetName);
137
132
  var loadingComplete = this.isLoadingComplete(tmodel, targetName);
138
133
  if (loadingComplete || !this.tmodelKeyMap[key]) {
139
134
  var _this$tmodelKeyMap, _this$tmodelKeyMap$ke;
@@ -215,7 +210,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
215
210
  if (!modelEntry) {
216
211
  return false;
217
212
  }
218
- var loadTargetName = this.getLoadTargetName(targetName);
213
+ var loadTargetName = _TUtil.TUtil.getLoadTargetName(targetName);
219
214
  var targetValue = tmodel.val(loadTargetName);
220
215
  return Array.isArray(targetValue) && _TUtil.TUtil.isDefined(targetValue[modelEntry.activeIndex]);
221
216
  }
@@ -227,7 +222,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
227
222
  if (!tmodelEntry || tmodelEntry.accessIndex >= tmodelEntry.resultCount) {
228
223
  return undefined;
229
224
  }
230
- var loadTargetName = this.getLoadTargetName(prevTargetName);
225
+ var loadTargetName = _TUtil.TUtil.getLoadTargetName(prevTargetName);
231
226
  var targetValue = tmodel.val(loadTargetName);
232
227
  var result;
233
228
  if (targetValue) {
@@ -290,7 +285,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
290
285
  targetName = _ref.targetName;
291
286
  var key = _this4.getTModelKey(tmodel, targetName);
292
287
  var tmodelEntry = _this4.tmodelKeyMap[key];
293
- var loadTargetName = _this4.getLoadTargetName(targetName);
288
+ var loadTargetName = _TUtil.TUtil.getLoadTargetName(targetName);
294
289
  if (!tmodelEntry || !tmodelEntry.fetchMap[fetchId]) {
295
290
  return;
296
291
  }
@@ -300,12 +295,12 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
300
295
  }));
301
296
  var targetResults = tmodel.val(loadTargetName);
302
297
  if (targetResults) {
303
- if (!targetResults[fetchEntry.order]) {
298
+ if (!_TUtil.TUtil.isDefined(targetResults[fetchEntry.order])) {
304
299
  tmodelEntry.resultCount++;
305
300
  }
306
301
  targetResults[fetchEntry.order] = res.result;
307
302
  }
308
- tmodel.val(targetName, (targetResults === null || targetResults === void 0 ? void 0 : targetResults.length) === 1 && tmodel.getTargetCycles(targetName) <= 1 ? targetResults[0] : targetResults);
303
+ tmodel.val(targetName, res.result);
309
304
  var newStatus = _this4.calculateTargetStatus(tmodel, targetName);
310
305
  tmodel.setTargetStatus(targetName, newStatus);
311
306
  tmodel.setLastUpdate(targetName);
@@ -332,7 +327,7 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
332
327
  targetName = _ref2.targetName;
333
328
  var key = _this5.getTModelKey(tmodel, targetName);
334
329
  var tmodelEntry = _this5.tmodelKeyMap[key];
335
- var loadTargetName = _this5.getLoadTargetName(targetName);
330
+ var loadTargetName = _TUtil.TUtil.getLoadTargetName(targetName);
336
331
  if (!tmodelEntry || !tmodelEntry.fetchMap[fetchId]) {
337
332
  return;
338
333
  }
@@ -345,12 +340,12 @@ var LoadingManager = exports.LoadingManager = /*#__PURE__*/function () {
345
340
  };
346
341
  var targetResults = tmodel.val(loadTargetName);
347
342
  if (targetResults) {
348
- if (!targetResults[fetchEntry.order]) {
343
+ if (!_TUtil.TUtil.isDefined(targetResults[fetchEntry.order])) {
349
344
  tmodelEntry.resultCount++;
350
345
  }
351
346
  targetResults[fetchEntry.order] = res;
352
347
  }
353
- tmodel.val(targetName, (targetResults === null || targetResults === void 0 ? void 0 : targetResults.length) === 1 && tmodel.getTargetCycles(targetName) <= 1 ? targetResults[0] : targetResults);
348
+ tmodel.val(targetName, res);
354
349
  tmodelEntry.errorCount++;
355
350
  _this5.callOnErrorHandler(tmodel, targetName);
356
351
  var newStatus = _this5.calculateTargetStatus(tmodel, targetName);
@@ -236,9 +236,6 @@ var LocationManager = exports.LocationManager = /*#__PURE__*/function () {
236
236
  if (child.isDomIsland()) {
237
237
  _this.domIslandSet.add(child);
238
238
  }
239
- if (child.shouldBeBracketed() && !_TUtil.TUtil.isDefined(child.getDomParent().targets['onWindowScroll'])) {
240
- child.getDomParent().addTarget('onWindowScroll', '');
241
- }
242
239
  viewport.setLocation();
243
240
  if (viewport.isOverflow()) {
244
241
  viewport.overflow();
package/build/TModel.js CHANGED
@@ -134,6 +134,7 @@ var TModel = exports.TModel = /*#__PURE__*/function (_BaseModel) {
134
134
  this.deletedChildren.push(child);
135
135
  this.removeFromUpdatingChildren(child);
136
136
  this.removeFromActiveChildren(child);
137
+ this.removeFromAnimatingChildren(child);
137
138
  this.childrenUpdateFlag = true;
138
139
  (0, _App.getLocationManager)().calcChildren(this);
139
140
  this.markLayoutDirty('removeChild');
@@ -501,6 +502,11 @@ var TModel = exports.TModel = /*#__PURE__*/function (_BaseModel) {
501
502
  }
502
503
  return parentValue;
503
504
  }
505
+ }, {
506
+ key: "getLoadedItems",
507
+ value: function getLoadedItems(targetName) {
508
+ return this.val(_TUtil.TUtil.getLoadTargetName(targetName));
509
+ }
504
510
  }, {
505
511
  key: "delVal",
506
512
  value: function delVal(key) {
@@ -226,7 +226,7 @@ var TModelManager = exports.TModelManager = /*#__PURE__*/function () {
226
226
  }, {
227
227
  key: "needsRerender",
228
228
  value: function needsRerender(tmodel) {
229
- if (tmodel.hasDom() && _TUtil.TUtil.isDefined(tmodel.getHtml()) && (tmodel.$dom.html() !== tmodel.getHtml() || tmodel.$dom.textOnly !== tmodel.isTextOnly())) {
229
+ if (tmodel.hasDom() && _TUtil.TUtil.isDefined(tmodel.getHtml()) && (tmodel.$dom.innerHTML() !== tmodel.getHtml() || tmodel.$dom.textOnly !== tmodel.isTextOnly())) {
230
230
  return true;
231
231
  }
232
232
  return false;
package/build/TUtil.js CHANGED
@@ -165,6 +165,11 @@ var TUtil = exports.TUtil = /*#__PURE__*/function () {
165
165
  value: function capitalizeFirstLetter(val) {
166
166
  return val.charAt(0).toUpperCase() + val.slice(1);
167
167
  }
168
+ }, {
169
+ key: "getLoadTargetName",
170
+ value: function getLoadTargetName(targetName) {
171
+ return "load-".concat(targetName);
172
+ }
168
173
  }, {
169
174
  key: "formatNum",
170
175
  value: function formatNum(num, precision) {
@@ -405,6 +405,9 @@ var TargetUtil = exports.TargetUtil = /*#__PURE__*/function () {
405
405
  if (tmodel.isTargetImperative(targetName)) {
406
406
  continue;
407
407
  }
408
+ if (tmodel.activatedTargets.indexOf(targetName) >= 0) {
409
+ return "activated targets";
410
+ }
408
411
  if (tmodel.hasUpdatingImperativeTargets(targetName)) {
409
412
  return tmodel.oid + "." + targetName + ": " + tmodel.getUpdatingImperativeTargets(targetName);
410
413
  }