targetj 1.0.241 → 1.0.243

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
@@ -7,7 +7,7 @@
7
7
  [![Stars](https://img.shields.io/github/stars/livetrails/targetjs.svg)](https://github.com/livetrails/targetjs/stargazers)
8
8
  [![npm version](https://img.shields.io/npm/v/targetj.svg)](https://www.npmjs.com/package/targetj)
9
9
 
10
- TargetJS is a JavaScript UI framework that replaces the "State → Render" model with "State → Transition → Render". It also lets code order directly define the UI sequence. It unifies UI, animations, API calls, event handling, and state into self-contained "Targets" that stack together like intelligent Lego pieces using Code-Ordered Reactivity.
10
+ TargetJS is a JavaScript UI framework that replaces the "State → Render" model with "State → Transition → Render". It also lets code order directly define the UI sequence. It unifies UI, animations, API calls, event handling, and state into self-contained "Targets" that stack together like Lego pieces using Code-Ordered Reactivity.
11
11
 
12
12
  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).
13
13
 
@@ -40,11 +40,11 @@ With its compact style, TargetJS makes the journey from A → B explicit and eff
40
40
 
41
41
  ## 🚀 Why TargetJS?
42
42
 
43
- 1. Unified State: State isn't "elsewhere". It is built into every Target.
44
- 2. Animation by Default: High-performance animations are baked into the logic.
45
- 3. Ultra-Compact: Write 30% to 70% less code than standard frameworks.
46
- 4. UI as Sequence: Code describes the UI story from top to bottom, exactly how the user experiences the interaction: "When this finishes, do that."
47
- 5. Zero Boilerplate Async: Targets can handle waiting for asynchronous operations automatically.
43
+ 1. Unified State: One single state. Transitions are state too.
44
+ 1. UI as Sequence: Code describes the UI story from top to bottom, just like the user experiences the interaction: "When this finishes, do that."
45
+ 1. Ultra-Compact: Minimal code, with no coordination variables.
46
+ 1. Zero Boilerplate Async: Targets handle waiting for nested asynchronous operations automatically.
47
+ 1. Animation by Default: High-performance animations are baked into the logic.
48
48
 
49
49
  ## ⚡ Quick Start (30 Seconds)
50
50
 
@@ -54,19 +54,31 @@ With its compact style, TargetJS makes the journey from A → B explicit and eff
54
54
  npm install targetj
55
55
  ```
56
56
 
57
- **2. The "Hello World" Sequence**
57
+ **2. Example**
58
58
 
59
- This creates a blue box that grows, then turns red, and then logs "Hello World" in order.
59
+ This creates the following sequential sequence: appear bounce → move → turn red → log. Notice how the code follows the UI sequence. There is only one state, and the animation transitions are part of that state.
60
60
 
61
61
  ```javascript
62
62
  import { App } from "targetj";
63
63
 
64
64
  App({
65
- backgroundColor: 'blue', // Starts immediately
66
- width: { value: [100, 200], steps: 100, interval: 8 }, // Starts immediately: animate width from 100px to 200px in 100 steps with 8 ms interval per step.
67
- height: { value: [100, 200], steps: 100, interval: 8 }, // Starts immediately: animate height.
68
- backgroundColor$$: { value: 'red', steps: 100, interval: 8 }, // Wait ($$) for width/height to finish
69
- done$$() { console.log("Hello World!"); } // 3. Waits ($$) for the background color, width/height to finish
65
+ width: 100,
66
+ height: 100,
67
+ backgroundColor: "blue",
68
+
69
+ // Starts immediately: bounce in.
70
+ scale: { value: [0.5, 1.2, 1], steps: 24, interval: 12 },
71
+
72
+ // Waits for scale to finish, then moves right.
73
+ x$$: { value: [0, 180], steps: 40, interval: 8 },
74
+
75
+ // Waits for x to finish, then turns red.
76
+ backgroundColor$$: { value: "crimson", steps: 30, interval: 8 },
77
+
78
+ // Waits for the color change to finish.
79
+ done$$() {
80
+ console.log("Sequence complete");
81
+ }
70
82
  }).mount("#app");
71
83
  ```
72
84
 
@@ -80,11 +92,12 @@ Methods and properties both are internally transformed into targets that the fra
80
92
  A target can:
81
93
  - execute a method
82
94
  - hold a value
83
- - move toward that value over time
84
- - wait for previous targets
95
+ - move toward a new value over time
96
+ - pause while moving toward a value
97
+ - wait for previous targets to complete
85
98
  - react when previous targets update
86
99
  - fetch data
87
- - react to an event
100
+ - respond to events
88
101
  - create children
89
102
  - run callbacks
90
103
  - control its own lifecycle
@@ -101,13 +114,13 @@ A target can also be defined as an object with optional controls that manage its
101
114
  | `steps` | Turns a value change into an animation. |
102
115
  | `interval` | Delay (ms) between steps or executions. |
103
116
  | `cycles` | Number of times the target repeats. |
104
- | `loop` | Boolean form of repetition for continuous execution. |
117
+ | `loop` | Controls repetition, either actively for continuous execution or passively when the value changes. |
105
118
  | `active` | Boolean property controlling when `value` is executed. |
106
119
  | `enabledOn` | Determines whether the target is enabled for execution. |
120
+ | `pauseOn` | Pauses execution while the target is in progress. |
107
121
  | `easing` | Predefined easing function controlling how values update over steps. |
108
122
  | `onComplete` | Callback triggered when this target (and its children) finishes. |
109
123
  | `onValueChange` | Callback triggered when the target emits a new value. |
110
- | `onChange` | Callback triggered when the target emits a new value. |
111
124
  | `on<PropertyName>Step` | Callback triggered on every step of a specific property. |
112
125
 
113
126
  ### Compact Execution Syntax
@@ -222,7 +235,7 @@ App({
222
235
 
223
236
  ### Summary
224
237
 
225
- Each target has its own state and lifecycle. Targets execute automatically in the order they are written. `$$` defers execution until all prior sibling targets (including their children) are fully complete. Animations, API calls, event handling, and child creation are all treated uniformly as targets. Complex asynchronous flows can be structured by organizing work into parent and child targets. In addition, targets provide built-in capabilities such as `onComplete` callbacks, `enabledOn`, looping with delays, and more. This also makes the code more compact, as it avoids using extra variables to track progress and reduces the need for loops and conditional statements.
238
+ Each target has its own state and lifecycle. Targets execute automatically in the order they are written. `$$` defers execution until all prior sibling targets (including their children) are fully complete. Animations, API calls, event handling, and child creation are all treated uniformly as targets. Complex asynchronous flows can be structured by organizing work into parent and child targets. In addition, targets provide built-in capabilities such as `onComplete` callbacks, `pauseOn`, `enabledOn`, looping with delays, and more. This also makes the code more compact, as it avoids using extra variables to track progress and reduces the need for loops and conditional statements.
226
239
 
227
240
  ---
228
241
 
@@ -399,13 +412,13 @@ In this advanced example, we implement an infinite-scrolling application.
399
412
 
400
413
  * `photo` and `userName` each add a `div` element inside every item, serving as placeholders for the photo and user name.
401
414
 
402
- * `pause$$` delays the execution of all targets that follow it by 100 ms.
415
+ * `pause$$` delays the execution of all targets that follow it by 300 ms. It also has `pauseOn: 'hidden'`, which means the delay is paused if the item becomes invisible, such as when the user scrolls down quickly. This also puts all following `$$` targets on hold, so `fetch$$` will not execute until the item becomes visible again.
403
416
 
404
417
  * `fetch$$` retrieves the user’s details.
405
418
 
406
419
  * `reveal$$` executes after `fetch$$`, revealing the user name and populating the photo with a random color.
407
420
 
408
- * `wave$$` executes only after all preceding children have completed their targets, giving each user item a coordinated animation.
421
+ * `wave` executes when all visible children have completed their targets or when scrolling ends, giving each user item a coordinated animation.
409
422
 
410
423
  TargetJS employs a tree-like structure to track visible branches, optimizing scroller performance.
411
424
 
@@ -421,61 +434,88 @@ import { App, getEvents, getScreenWidth, getScreenHeight } from "targetj";
421
434
 
422
435
  App({
423
436
  preventDefault: true,
424
- width: 250,
437
+ width: 300,
425
438
  height() { return getScreenHeight(); },
426
439
  x() { return (getScreenWidth() - this.getWidth()) / 2; },
427
440
  containerOverflowMode: "always",
441
+ canDeleteDom: false,
442
+ overflow: 'scroll',
443
+ onWindowScroll: true,
428
444
  addChildren: {
429
- waitForChildren: 'visible',
430
- value() {
431
- return Array.from({ length: 10 }, (_, i) => ({
432
- height: 56,
433
- width() { return this.parent.getWidth(); },
434
- bottomMargin: 8,
435
- borderRadius: 12,
436
- backgroundColor: "white",
437
- boxShadow: "0 8px 20px rgba(0,0,0,0.08)",
438
- photo: {
439
- x: 10, y: 10, width: 34, height: 34,
440
- borderRadius: "50%",
441
- backgroundColor: "#ddd"
442
- },
443
- userName: {
444
- x: 60, y: 10, width: 180, height: 30,
445
- overflow: "hidden",
446
- borderRadius: 5,
447
- backgroundColor: "#ddd"
448
- },
449
- pause$$: { interval: 100 },
450
- fetch$$: "https://targetjs.io/api/randomUser",
451
- reveal$$() {
452
- const userName = this.getChild("userName");
453
- userName.setTarget("html", this.val("fetch$$").name);
454
- userName.setTarget("backgroundColor", { value: "white", steps: 20 });
455
- this.getChild("photo").setTarget("backgroundColor", { value: "#" + Math.random().toString(16).slice(-6), steps: 20 });
456
- },
457
- }));
458
- }
445
+ value() {
446
+ return Array.from({length: 10}, (_, i) => ({
447
+ height: 56,
448
+ width() { return this.parent.getWidth() - 30; },
449
+ bottomMargin: 8,
450
+ borderRadius: 12,
451
+ backgroundColor: 'white',
452
+ boxShadow: "0 8px 20px rgba(0,0,0,0.08)",
453
+ pause: {
454
+ interval() { return getEvents().isWindowScrolling() ? 600 : 0; },
455
+ pauseOn: 'hidden'
456
+ },
457
+ photo$$: {
458
+ x: 10,
459
+ y: 10,
460
+ width:34,
461
+ height:34,
462
+ borderRadius: '50%',
463
+ backgroundColor: '#ddd'
464
+ },
465
+ userName$$: {
466
+ x: 60,
467
+ y: 10,
468
+ width: 180,
469
+ height: 30,
470
+ overflow: 'hidden',
471
+ borderRadius: 5,
472
+ backgroundColor: '#ddd'
473
+ },
474
+ pause$$: {
475
+ interval: 300,
476
+ pauseOn: 'hidden'
477
+ },
478
+ fetch$$: "https://targetjs.io/api/randomUser",
479
+ reveal$$() {
480
+ const userName = this.getChild('userName$$');
481
+ userName.setTarget('html', this.val('fetch$$').name);
482
+ userName.setTarget('backgroundColor', { value: 'white', steps: 20 });
483
+ this.getChild('photo$$').setTarget('backgroundColor', { value: '#' + Math.random().toString(16).slice(-6), steps: 20 });
484
+ }
485
+ }));
486
+ },
487
+ onVisibleComplete() {
488
+ if (!getEvents().isWindowScrolling()) {
489
+ this.activateTarget('wave');
490
+ }
491
+ }
459
492
  },
460
- wave$$: {
461
- interval: 30,
462
- cycles() { return this.visibleChildren.length; },
463
- value(i) {
464
- const child = this.visibleChildren[i];
465
- if (child) {
466
- child.setTarget("scale", { value: [1, 1.06, 1], steps: 18 });
467
- child.setTarget("opacity", { value: [1, 0.92, 1], steps: 18 });
468
- }
469
- }
493
+ onWindowScrollTopEnd() {
494
+ if (this.isTargetVisibleTreeComplete('addChildren')) {
495
+ this.activateTarget('wave');
496
+ }
470
497
  },
471
- onScroll() {
472
- this.setTarget("scrollTop", Math.max(0, this.getScrollTop() + getEvents().deltaY()));
498
+ wave: {
499
+ active: false,
500
+ interval: 30,
501
+ cycles() { return this.visibleChildren.length; },
502
+ value(i) {
503
+ const child = this.visibleChildren[i];
504
+ if (child) {
505
+ child.setTarget("scale", { value: [1, 1.06, 1], steps: 18 });
506
+ child.setTarget("opacity", { value: [1, 0.7, 1], steps: 18 });
507
+ }
508
+ }
473
509
  },
474
510
  onVisibleChildrenChange() {
475
- const visibleCount = this.visibleChildren.length;
476
- if (getEvents().dir() !== "up" && visibleCount * 64 < this.getHeight()) {
477
- this.activateTarget("addChildren");
478
- }
511
+ if (!this.visibleChildren.length) {
512
+ return this.activateTarget("addChildren");
513
+ }
514
+ const scrollTop = this.$dom?.getScrollTop() ?? 0;
515
+ const lastY = this.getLastChild().getY() - scrollTop - 500;
516
+ if (lastY <= this.getHeight()) {
517
+ this.activateTarget("addChildren");
518
+ }
479
519
  }
480
520
  }).mount("#userList");
481
521
  ```
@@ -529,7 +569,7 @@ TargetJS.tApp.throttle = 0; // Slow down execution (milliseconds between cycles)
529
569
  TargetJS.tApp.debugLevel = 1; // Log cycle execution
530
570
  ```
531
571
  - Use `t(id)` in the browser console to find an object by its element id.
532
- - Use `t(id).bug()` to inspect all the vital properties.
572
+ - Use `t(id).debug()` to inspect all the vital properties.
533
573
  - Use `t(id).logTree()` to inspect the UI structure.
534
574
 
535
575
  ## Documentation
@@ -146,9 +146,9 @@ var AnimationManager = /*#__PURE__*/function () {
146
146
  needsFireOnStep: true,
147
147
  hooks: hooks
148
148
  };
149
- tmodel.addToAnimatingMap(_originalKey);
150
149
  var targetValue = tmodel.targetValues[_originalKey];
151
150
  if (targetValue) {
151
+ tmodel.addToAnimatingMap(_originalKey);
152
152
  targetValue.status = !targetValue.snapAnimation ? 'updating' : targetValue.status;
153
153
  }
154
154
  this.recordMap.set(recId, rec);
@@ -973,9 +973,7 @@ var BaseModel = /*#__PURE__*/function () {
973
973
  var _step2$value = _slicedToArray(_step2.value, 1),
974
974
  key = _step2$value[0];
975
975
  if (this.targetValues[key]) {
976
- this.targetValues[key].status = 'done';
977
- this.removeFromUpdatingTargets(key);
978
- this.removeFromActiveTargets(key);
976
+ this.addTargetToStatusList(key);
979
977
  }
980
978
  }
981
979
  } catch (err) {
package/build/Bracket.js CHANGED
@@ -131,16 +131,6 @@ var Bracket = /*#__PURE__*/function (_TModel) {
131
131
  value: function getRealParent() {
132
132
  return this.realParent;
133
133
  }
134
- }, {
135
- key: "getRightMargin",
136
- value: function getRightMargin() {
137
- return 0;
138
- }
139
- }, {
140
- key: "getBottomMargin",
141
- value: function getBottomMargin() {
142
- return 0;
143
- }
144
134
  }, {
145
135
  key: "calcAbsolutePosition",
146
136
  value: function calcAbsolutePosition(x, y) {
@@ -348,7 +348,8 @@ var EventListener = /*#__PURE__*/function () {
348
348
  _this$currentHandlers5,
349
349
  _this$currentHandlers7,
350
350
  _this$currentHandlers8,
351
- _this7 = this;
351
+ _this7 = this,
352
+ _tmodel3;
352
353
  if (!event) {
353
354
  return;
354
355
  }
@@ -553,7 +554,7 @@ var EventListener = /*#__PURE__*/function () {
553
554
  _this7.queueWindowScrollEndEvent('x', tmodel);
554
555
  _this7.queueWindowScrollEndEvent('y', tmodel);
555
556
  _this7.windowScrollEndTimer = 0;
556
- }, 120);
557
+ }, 450);
557
558
  if (isWindowEvent) {
558
559
  this.windowEpoch++;
559
560
  this.windowScrollX = window.scrollX | 0;
@@ -563,16 +564,10 @@ var EventListener = /*#__PURE__*/function () {
563
564
  });
564
565
  } else {
565
566
  tmodel.markLayoutDirty('container-scroll-event');
566
- clearTimeout(tmodel.scrollEndTimer);
567
- tmodel.scrollEndTimer = setTimeout(function () {
568
- _this7.queueWindowScrollEndEvent('x', tmodel, 'container');
569
- _this7.queueWindowScrollEndEvent('y', tmodel, 'container');
570
- tmodel.scrollEndTimer = 0;
571
- }, 120);
572
567
  }
573
568
  break;
574
569
  }
575
- getRunScheduler().schedule(0, "".concat(originalName, "-").concat(eventName, "-").concat((event.target.tagName || '').toUpperCase()));
570
+ getRunScheduler().schedule(0, "".concat((_tmodel3 = tmodel) === null || _tmodel3 === void 0 ? void 0 : _tmodel3.oid, "-").concat(originalName, "-").concat(eventName, "-").concat((event.target.tagName || '').toUpperCase()));
576
571
  }
577
572
  }, {
578
573
  key: "queueScrollEndEvent",
@@ -97,7 +97,7 @@ var RunScheduler = /*#__PURE__*/function () {
97
97
  key: "run",
98
98
  value: function () {
99
99
  var _run = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee2(delay, runId) {
100
- var handedOff;
100
+ var handedOff, _this$delayProcess;
101
101
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
102
102
  while (1) switch (_context2.prev = _context2.next) {
103
103
  case 0:
@@ -121,51 +121,54 @@ var RunScheduler = /*#__PURE__*/function () {
121
121
  this.runningFlag = true;
122
122
  this.runStartTime = TUtil.now();
123
123
  handedOff = false;
124
- _context2.prev = 10;
124
+ if (tApp.debugLevel === 1) {
125
+ TUtil.log(true)("Request from: ".concat(runId, " delay: ").concat(delay, " runningStep:").concat(this.runningStep, " dom:").concat(this.domProcessing, " runs:").concat(this.nextRuns.length, " D:").concat((_this$delayProcess = this.delayProcess) === null || _this$delayProcess === void 0 ? void 0 : _this$delayProcess.delay, " events:").concat(getEvents().eventQueue.length));
126
+ }
127
+ _context2.prev = 11;
125
128
  if (!(this.phase === 0)) {
126
- _context2.next = 17;
129
+ _context2.next = 18;
127
130
  break;
128
131
  }
129
132
  getEvents().captureEvents();
130
133
  tApp.targetManager.applyTargetValues(tRoot());
131
- _context2.next = 16;
134
+ _context2.next = 17;
132
135
  return getLocationManager().calculateAll();
133
- case 16:
134
- this.phase = 1;
135
136
  case 17:
137
+ this.phase = 1;
138
+ case 18:
136
139
  if (!(TUtil.now() - this.runStartTime > FRAME_BUDGET_MS)) {
137
- _context2.next = 21;
140
+ _context2.next = 22;
138
141
  break;
139
142
  }
140
143
  handedOff = true;
141
144
  this.requestNextSlice(runId);
142
145
  return _context2.abrupt("return");
143
- case 21:
146
+ case 22:
144
147
  if (this.phase === 1) {
145
148
  this.runningStep = tApp.manager.analyze();
146
149
  this.phase = 2;
147
150
  }
148
151
  if (!(TUtil.now() - this.runStartTime > FRAME_BUDGET_MS)) {
149
- _context2.next = 26;
152
+ _context2.next = 27;
150
153
  break;
151
154
  }
152
155
  handedOff = true;
153
156
  this.requestNextSlice(runId);
154
157
  return _context2.abrupt("return");
155
- case 26:
158
+ case 27:
156
159
  if (this.phase === 2) {
157
160
  getLocationManager().calculateActivated();
158
161
  tApp.events.resetEventsOnTimeout();
159
162
  this.phase = 3;
160
163
  }
161
164
  if (!(TUtil.now() - this.runStartTime > FRAME_BUDGET_MS)) {
162
- _context2.next = 31;
165
+ _context2.next = 32;
163
166
  break;
164
167
  }
165
168
  handedOff = true;
166
169
  this.requestNextSlice(runId);
167
170
  return _context2.abrupt("return");
168
- case 31:
171
+ case 32:
169
172
  if (this.phase === 3) {
170
173
  if (this.runningStep >= 0) {
171
174
  if (this.domProcessing === 0) {
@@ -181,24 +184,24 @@ var RunScheduler = /*#__PURE__*/function () {
181
184
  if (this.domProcessing === 0) {
182
185
  this.needsRerun();
183
186
  }
184
- _context2.next = 40;
187
+ _context2.next = 41;
185
188
  break;
186
- case 36:
187
- _context2.prev = 36;
188
- _context2.t0 = _context2["catch"](10);
189
+ case 37:
190
+ _context2.prev = 37;
191
+ _context2.t0 = _context2["catch"](11);
189
192
  this.phase = 0;
190
193
  throw _context2.t0;
191
- case 40:
192
- _context2.prev = 40;
194
+ case 41:
195
+ _context2.prev = 41;
193
196
  if (!handedOff && this.domProcessing === 0) {
194
197
  this.runningFlag = false;
195
198
  }
196
- return _context2.finish(40);
197
- case 43:
199
+ return _context2.finish(41);
200
+ case 44:
198
201
  case "end":
199
202
  return _context2.stop();
200
203
  }
201
- }, _callee2, this, [[10, 36, 40, 43]]);
204
+ }, _callee2, this, [[11, 37, 41, 44]]);
202
205
  }));
203
206
  function run(_x, _x2) {
204
207
  return _run.apply(this, arguments);
@@ -295,8 +298,8 @@ var RunScheduler = /*#__PURE__*/function () {
295
298
  }, {
296
299
  key: "clearDelayProcess",
297
300
  value: function clearDelayProcess() {
298
- var _this$delayProcess;
299
- if ((_this$delayProcess = this.delayProcess) !== null && _this$delayProcess !== void 0 && _this$delayProcess.timeoutId) {
301
+ var _this$delayProcess2;
302
+ if ((_this$delayProcess2 = this.delayProcess) !== null && _this$delayProcess2 !== void 0 && _this$delayProcess2.timeoutId) {
300
303
  clearTimeout(this.delayProcess.timeoutId);
301
304
  this.delayProcess.timeoutId = undefined;
302
305
  }