targetj 1.0.202 → 1.0.203
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 +119 -209
- package/build/$Dom.js +10 -0
- package/build/App.js +25 -14
- package/build/BaseModel.js +24 -22
- package/build/Bracket.js +31 -19
- package/build/BracketGenerator.js +2 -0
- package/build/EventListener.js +43 -6
- package/build/LocationManager.js +377 -205
- package/build/PageManager.js +4 -2
- package/build/RunScheduler.js +132 -92
- package/build/SearchUtil.js +3 -3
- package/build/TModel.js +59 -39
- package/build/TModelManager.js +4 -7
- package/build/TModelUtil.js +25 -0
- package/build/TUtil.js +29 -20
- package/build/TargetData.js +20 -8
- package/build/TargetUtil.js +1 -0
- package/build/Viewport.js +81 -13
- package/dist/targetjs.js +1 -1
- package/dist/targetjs.js.gz +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,42 +1,40 @@
|
|
|
1
|
-
# TargetJS:
|
|
1
|
+
# TargetJS: JavaScript UI Framework for Simplified Development and Enhanced User Experience
|
|
2
2
|
|
|
3
3
|
**[targetjs.io](https://targetjs.io)**
|
|
4
4
|
[](https://github.com/livetrails/targetjs/blob/main/LICENSE)
|
|
5
5
|
[](https://github.com/livetrails/targetjs/stargazers)
|
|
6
6
|
[](https://www.npmjs.com/package/targetj)
|
|
7
7
|
|
|
8
|
-
TargetJS is a modern JavaScript UI framework that simplifies front-end development
|
|
8
|
+
TargetJS is a modern JavaScript UI framework that simplifies front-end development with a code-ordered reactivity model and a compact, Rebol-like syntax. It provides a unified solution for key aspects like UI rendering, animations, APIs, state management, and event handling.
|
|
9
9
|
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).
|
|
10
10
|
|
|
11
11
|
## The Philosophy Behind TargetJS
|
|
12
12
|
|
|
13
|
-
Frameworks often promise simplicity, but frequently require extensive boilerplate and libraries as they inherit the same software approach rooted in early programming models and force it to fit
|
|
13
|
+
Frameworks often promise simplicity, but frequently require extensive boilerplate and libraries as they inherit the same software approach rooted in early programming models and try to force it to fit UI development by adding more complexity. User interfaces are dynamic and asynchronous and require a different paradigm.
|
|
14
14
|
|
|
15
15
|
TargetJS adopts a new approach. First, it unifies class methods and fields into a single construct called targets. Each target is given state, lifecycles, timing, iterations, and the autonomy to execute mimicking the behavior of living cells. Targets are essentially self-contained, intelligent blocks of code.
|
|
16
16
|
|
|
17
|
-
The second challenge is making these targets
|
|
17
|
+
The second challenge is making these targets fit and work together especially since UI operations are highly asynchronous. Instead of relying on traditional method calls and callbacks that don't address asynchronous nature well, TargetJS allows targets to react to the execution or completion of preceding targets. A subsequent target can run independently, execute whenever the previous one does, or wait until the previous target completes. Targets stack together like Lego pieces. It can address complex asynchronous workflow while remaining easy to understand.
|
|
18
18
|
|
|
19
|
-
For example, setting a value can implicitly define an animation, where the current value iteratively progresses until it reaches the new value. When the animation completes, the next target might initiate a fetch API call. Once the data is received, it can trigger another target that creates 10 new elements, each with its own animation and API call. A subsequent target can then be set to run only after all elements have completed their tasks.
|
|
19
|
+
For example, setting a value can implicitly define an animation, where the current value iteratively progresses until it reaches the new value. When the animation completes, the next target might initiate a fetch API call. Once the data is received, it can trigger another target that creates 10 new elements, each with its own animation and API call. A subsequent target can then be set to run only after all 10 elements have completed their tasks. Targets simply react and chain together based on how the code is written.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Furthermore, TargetJS adopts a Rebol-like style to make the code more compact.
|
|
22
22
|
|
|
23
23
|
## Key Innovations and Concepts
|
|
24
24
|
|
|
25
|
-
1.
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
28
|
-
4. Code-Ordered Execution: Targets are chained top to bottom and the execution flow generally follows the order in which the code is written.
|
|
29
|
-
5. Autonomous Methods: Methods in TargetJS are not directly callable. Instead, they are designed to execute themselves or react dynamically to the execution or completion of preedings targets. This enables declarative programming that inherently supports asynchronous operations without explicit plumbing like using async/await keywords.
|
|
30
|
-
6. Compactness: TargetJS allows developers to achieve interactive UIs with significantly less code.
|
|
25
|
+
1. Reactive targets: A new construct called “targets” unifies class methods and fields. Targets are self-contained units of code with their own state, lifecycles, and timing. They are designed to execute themselves or react dynamically to the run or completion of preceding targets. This enables the declarative programming of complex asynchronous flows without explicit callbacks.
|
|
26
|
+
2. All-in-One solution: Offers a unified approach to UI rendering, API integration, state management, event handling, and animation.
|
|
27
|
+
3. Code-ordered execution, Rebol-like style: less code and more readable code.
|
|
31
28
|
|
|
29
|
+
---
|
|
32
30
|
|
|
33
|
-
## Examples:
|
|
31
|
+
## Examples: Like Button → Animated Like with 4 Async Ops (in 7 Steps)
|
|
34
32
|
|
|
35
|
-
|
|
33
|
+
The example demonstrates how to run four asynchronous operations in a strict sequential sequence, where each step waits for the previous ones to complete. Any restart or delay in an operation delays the ones that follow. This is to showcase managing async operations rather than focusing on the user experience. We will show the example in 7 steps.
|
|
36
34
|
|
|
37
|
-
|
|
35
|
+
### 1) Like button (view only)
|
|
38
36
|
|
|
39
|
-
**What this shows:** One object defines a UI element without separate HTML/CSS. Static targets map directly to DOM styles/attributes.
|
|
37
|
+
**What this shows:** One object defines a UI element without separate HTML/CSS. Static targets map directly to DOM styles/attributes. You can still use CSS if wanted.
|
|
40
38
|
|
|
41
39
|
<img src="https://targetjs.io/img/likeButton.png" width="130" />
|
|
42
40
|
|
|
@@ -57,7 +55,7 @@ App({
|
|
|
57
55
|
---
|
|
58
56
|
|
|
59
57
|
|
|
60
|
-
|
|
58
|
+
### 2) Animation
|
|
61
59
|
|
|
62
60
|
**What this shows:** A mount-time animation that scales and changes the background over 12 steps, with 12ms pauses between steps. Targets without (`$`, `$$`, `_`) execute immediately in the order they are defined.
|
|
63
61
|
|
|
@@ -78,7 +76,7 @@ App({
|
|
|
78
76
|
});
|
|
79
77
|
```
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
### 3) Click → animation (imperative `setTarget`)
|
|
82
80
|
|
|
83
81
|
**What this shows:** Clicking plays the animations from the previous step using imperative `setTarget`.
|
|
84
82
|
|
|
@@ -103,9 +101,9 @@ App({
|
|
|
103
101
|
|
|
104
102
|
---
|
|
105
103
|
|
|
106
|
-
|
|
104
|
+
### 4) Sequencing with `$$`: Adding a small heart after click animation (first async op)
|
|
107
105
|
|
|
108
|
-
**What this shows:** A `$$` target (deferred) runs only after all prior targets finish (including `onClick()` and its animations). Here it adds a new heart element and runs its fly motion only once the click sequence has completed.
|
|
106
|
+
**What this shows:** A `$$` target (deferred) runs only after all prior targets finish (including `onClick()` and its animations). Here it adds a new heart element and runs its fly motion only once the click sequence has completed. Repeated clicks will delay adding the heart.
|
|
109
107
|
|
|
110
108
|
<img src="https://targetjs.io/img/likeButton7.gif" width="130" />
|
|
111
109
|
|
|
@@ -139,9 +137,9 @@ App({
|
|
|
139
137
|
|
|
140
138
|
---
|
|
141
139
|
|
|
142
|
-
|
|
140
|
+
### 5) Another `$$`: Adding a big heart (second async op)
|
|
143
141
|
|
|
144
|
-
**What this shows:** Deferred addition of a new element using $$. `bigHeart$$` waits for `heart$$` to complete
|
|
142
|
+
**What this shows:** Deferred addition of a new element using $$. `bigHeart$$` waits for `heart$$` and the click sequence to complete their animation, then adds a larger heart and runs its own happy animation.
|
|
145
143
|
|
|
146
144
|
<img src="https://targetjs.io/img/likeButton8.gif" width="130" />
|
|
147
145
|
|
|
@@ -174,7 +172,7 @@ App({
|
|
|
174
172
|
},
|
|
175
173
|
bigHeart$$: {
|
|
176
174
|
html: "♥", color: "blue", fontSize: 100,
|
|
177
|
-
|
|
175
|
+
happyFly() {
|
|
178
176
|
const cx = this.getCenterX(), cy = this.getCenterY();
|
|
179
177
|
this.setTarget({
|
|
180
178
|
opacity: { list: [0, 1, 1, 0.85, 0.6, 0.1] },
|
|
@@ -190,9 +188,9 @@ App({
|
|
|
190
188
|
|
|
191
189
|
---
|
|
192
190
|
|
|
193
|
-
|
|
191
|
+
### 6) `fetch$$` (third async op)
|
|
194
192
|
|
|
195
|
-
**What this shows:** Networking is just another target. The POST happens **only after** all prior visual steps complete, since the target is postfixed with `$$`.
|
|
193
|
+
**What this shows:** Networking is just another target. The POST happens **only after** all prior visual steps complete, since the target is postfixed with `$$`. Similarly, repeated clicks delay `fetch$$`.
|
|
196
194
|
|
|
197
195
|
```javascript
|
|
198
196
|
App({
|
|
@@ -204,9 +202,9 @@ App({
|
|
|
204
202
|
|
|
205
203
|
---
|
|
206
204
|
|
|
207
|
-
|
|
205
|
+
### 7) Final version
|
|
208
206
|
|
|
209
|
-
**What this shows:** A Like button that consolidates the previous steps into a single component. After the POST completes, a cleanup `removeHearts$$` target runs to remove the two heart elements. The button also includes basic accessibility (role, tabIndex, and Enter to activate).
|
|
207
|
+
**What this shows:** A Like button that consolidates the previous steps into a single component. After the POST completes, a cleanup `removeHearts$$` target (fourth async op) runs to remove the two heart elements. The button also includes basic accessibility (role, tabIndex, and Enter to activate). Demo: [Like button](https://targetj.io/examples/quick.html).
|
|
210
208
|
|
|
211
209
|
<img src="https://targetjs.io/img/likeButton9.gif" width="130" />
|
|
212
210
|
|
|
@@ -241,7 +239,7 @@ App({
|
|
|
241
239
|
|
|
242
240
|
bigHeart$$: {
|
|
243
241
|
html: "♥", color: "blue", fontSize: 100,
|
|
244
|
-
|
|
242
|
+
happyFly() {
|
|
245
243
|
const cx = this.getCenterX(), cy = this.getCenterY();
|
|
246
244
|
this.setTarget({
|
|
247
245
|
opacity: { list: [0, 1, 1, 0.85, 0.6, 0.1] },
|
|
@@ -258,90 +256,30 @@ App({
|
|
|
258
256
|
}
|
|
259
257
|
});
|
|
260
258
|
```
|
|
261
|
-
|
|
262
|
-
Or in HTML (no JavaScript required), using tg- attributes that mirror object literal keys:
|
|
263
|
-
|
|
264
|
-
```html
|
|
265
|
-
<div
|
|
266
|
-
id="likeButton"
|
|
267
|
-
tg-width="220"
|
|
268
|
-
tg-height="60"
|
|
269
|
-
tg-lineHeight="60"
|
|
270
|
-
tg-textAlign="center"
|
|
271
|
-
tg-borderRadius="10"
|
|
272
|
-
tg-background="#f5f5f5"
|
|
273
|
-
tg-cursor="pointer"
|
|
274
|
-
tg-userSelect="none"
|
|
275
|
-
tg-role="button"
|
|
276
|
-
tg-html="♡ Like"
|
|
277
|
-
tg-tabIndex="0"
|
|
278
|
-
tg-onClick="function() {
|
|
279
|
-
this.setTarget('scale', { list: [1.2, 1] }, 12, 12);
|
|
280
|
-
this.setTarget('background', { list: ['#ffe8ec', '#f5f5f5'] }, 12, 12);
|
|
281
|
-
this.setTarget('html', '♥ Liked');
|
|
282
|
-
}"
|
|
283
|
-
tg-heart$$="{
|
|
284
|
-
html: '♥',
|
|
285
|
-
color: 'crimson',
|
|
286
|
-
fontSize: 20,
|
|
287
|
-
fly() {
|
|
288
|
-
const cx = this.getCenterX(), cy = this.getCenterY();
|
|
289
|
-
this.setTarget({
|
|
290
|
-
opacity: { list: [0, 1, 1, 0.8, 0.1] },
|
|
291
|
-
scale: { list: [0.8, 1.4, 1.1, 0.9, 0.8] },
|
|
292
|
-
rotate: { list: [0, 12, -8, 6, 0] },
|
|
293
|
-
x: { list: [cx, cx + 22, cx - 16, cx + 10, cx] },
|
|
294
|
-
y: { list: [cy - 8, cy - 70, cy - 90, cy - 120, cy - 150] }
|
|
295
|
-
}, 20);
|
|
296
|
-
}
|
|
297
|
-
}"
|
|
298
|
-
tg-bigHeart$$="{
|
|
299
|
-
html: '♥',
|
|
300
|
-
color: 'blue',
|
|
301
|
-
fontSize: 100,
|
|
302
|
-
fly() {
|
|
303
|
-
const cx = this.getCenterX(), cy = this.getCenterY();
|
|
304
|
-
this.setTarget({
|
|
305
|
-
opacity: { list: [0, 1, 1, 0.85, 0.6, 0.1] },
|
|
306
|
-
scale: { list: [0.4, 1.9, 1.2, 1.6, 1.0, 0.95] },
|
|
307
|
-
rotate: { list: [0, 4, -3, 4, -2, 0] },
|
|
308
|
-
x: { list: [cx, cx + 14, cx + 10, cx - 6, cx - 14, cx] },
|
|
309
|
-
y: { list: [cy, cy - 30, cy - 55, cy - 80, cy - 100, cy - 130] }
|
|
310
|
-
}, 30);
|
|
311
|
-
}
|
|
312
|
-
}"
|
|
313
|
-
tg-fetch$$='{"method":"POST","id":123,"url":"/api/like"}'
|
|
314
|
-
tg-removeHearts$$="function() { this.removeAll(); }"
|
|
315
|
-
tg-onKey="function(e) { if (e.key === 'Enter') this.activateTarget('onClick'); }"
|
|
316
|
-
/></div>
|
|
317
|
-
```
|
|
318
|
-
|
|
319
259
|
---
|
|
320
260
|
|
|
321
|
-
|
|
261
|
+
### Final takeaway
|
|
322
262
|
|
|
323
|
-
-
|
|
324
|
-
$ reacts to the previous step; $$ defers until all prior steps finish. Animations, API calls, and child creation are all the same kind of thing: targets.
|
|
325
|
-
So complex flows read top-to-bottom.
|
|
263
|
+
- Instead of wiring callbacks and effects, you write a sequence of targets. `$$` defers until all prior steps finish. Animations, API calls, and child creation are all the same kind of thing: targets.
|
|
326
264
|
- Minimal plumbing yet full control to manage a flow of complex asynchronous operations.
|
|
327
265
|
|
|
328
266
|
## Table of Contents
|
|
329
267
|
|
|
330
268
|
1. [Targets: The Building Blocks of TargetJS](#targets-the-building-blocks-of-targetjs)
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
- [Loading
|
|
269
|
+
1. [Understanding TargetJS Syntax: Reactive Postfixes](#understanding-targetjs-syntax-reactive-postfixes)
|
|
270
|
+
1. [📦 Installation](#-installation)
|
|
271
|
+
1. [What Problems Does TargetJS Solve?](#what-problems-does-targetjs-solve)
|
|
272
|
+
1. More Examples:
|
|
273
|
+
- [Loading Five Users Example](#loading-two-users-example)
|
|
336
274
|
- [Infinite Loading and Scrolling Example](#infinite-loading-and-scrolling-example)
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
275
|
+
1. [Target Methods](#target-methods)
|
|
276
|
+
1. [Target Variables](#target-variables)
|
|
277
|
+
1. [Special Target Names](#special-target-names)
|
|
278
|
+
1. [How to Debug in TargetJS](#how-to-debug-in-targetjs)
|
|
279
|
+
1. [Documentation](#documentation)
|
|
280
|
+
1. [License](#license)
|
|
281
|
+
1. [Contact](#contact)
|
|
282
|
+
1. [💖 Support TargetJS](#-support-targetjs)
|
|
345
283
|
|
|
346
284
|
## Targets: The Building Blocks of TargetJS
|
|
347
285
|
|
|
@@ -355,33 +293,48 @@ Targets provide a unified interface for both class methods and fields. Each Targ
|
|
|
355
293
|
|
|
356
294
|
## Understanding TargetJS Syntax: Reactive Postfixes
|
|
357
295
|
|
|
358
|
-
TargetJS
|
|
296
|
+
TargetJS defines reactive behaviors using the `$` and `$$` postfixes on target names, unifying asynchronous operations such as API calls, animations, timers, and UI transitions. Although this convention may seem a bit cryptic at first, it offers a compact syntax.
|
|
359
297
|
|
|
360
298
|
**`$` Postfix (Immediate Reactivity):**
|
|
361
299
|
|
|
362
300
|
A target name ending with a single `$` (e.g., `height$`) indicates that this target will execute every time its immediately preceding target runs or emits a new value. If the preceding target involves an asynchronous operation like an API call, the reactive target activates when the response is received. If there are multiple API calls made, `$` postfix ensures that the target reacts to the first API result when it becomes available, then the second, and so on, maintaining a strict, code-ordered sequence of operations.
|
|
363
301
|
|
|
364
|
-
**`$$` Postfix (
|
|
302
|
+
**`$$` Postfix (Deferred Reactivity):**
|
|
365
303
|
|
|
366
|
-
A target name ending with a double `$$` (e.g., `fetch$$`) will activate only after
|
|
304
|
+
A target name ending with a double `$$` (e.g., `fetch$$`) will activate only after all the preceding targets have fully and comprehensively completed all of their operations. This includes:
|
|
367
305
|
|
|
368
306
|
- The successful resolution of any timed sequences, such as animations.
|
|
369
307
|
- The completion and return of results from all associated API calls.
|
|
370
308
|
- The finalization of all tasks, animations, and API calls initiated by any dependent child targets that were themselves triggered by a preceding target.
|
|
371
309
|
|
|
310
|
+
---
|
|
372
311
|
|
|
373
312
|
## **📦 Installation**
|
|
374
313
|
|
|
314
|
+
**Via package manager**
|
|
315
|
+
|
|
316
|
+
Install TargetJS via npm (note: there's no "s" at the end):
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
npm install targetj
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Then import it into your JavaScript code:
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
import { App } from "targetj";
|
|
326
|
+
```
|
|
327
|
+
|
|
375
328
|
**Via CDN**
|
|
376
329
|
|
|
377
|
-
Add the following `<script>` tag to your HTML to load TargetJS from a CDN (only
|
|
330
|
+
Add the following `<script>` tag to your HTML to load TargetJS from a CDN (only 67KB):
|
|
378
331
|
|
|
379
332
|
```html
|
|
380
333
|
<script src="https://unpkg.com/targetj@latest/dist/targetjs.js"></script>
|
|
381
334
|
```
|
|
382
335
|
|
|
383
336
|
This will add `TargetJS` to the global `window` object, making it accessible throughout your JavaScript such as `TargetJS.App(YourApp)`.
|
|
384
|
-
You can also use it directly in your HTML with
|
|
337
|
+
You can also use it directly in your HTML with `tg-` attributes:
|
|
385
338
|
|
|
386
339
|
```html
|
|
387
340
|
<div
|
|
@@ -391,36 +344,25 @@ You can also use it directly in your HTML with custom attributes:
|
|
|
391
344
|
</div>
|
|
392
345
|
```
|
|
393
346
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
Install TargetJS via npm (note: there's no "s" at the end):
|
|
397
|
-
|
|
398
|
-
```bash
|
|
399
|
-
npm install targetj
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
Then import it into your JavaScript code:
|
|
403
|
-
|
|
404
|
-
```javascript
|
|
405
|
-
import { App } from "targetj";
|
|
406
|
-
```
|
|
347
|
+
---
|
|
407
348
|
|
|
408
349
|
## What Problems Does TargetJS Solve?
|
|
409
350
|
|
|
410
351
|
TargetJS addresses several common pain points in front-end development:
|
|
411
352
|
|
|
412
|
-
1. **Scattered State Management:** Many frameworks require separate libraries or complex patterns for state management. In TargetJS, state management is inherently handled
|
|
413
|
-
2. **Complexity of Asynchronous Operations:** Traditional JavaScript often involves complex handling of asynchronous operations (Promises, callbacks, `async/await`). TargetJS addresses this by providing a
|
|
414
|
-
3. **Disjointed Development Workflow:** Developers often juggle multiple tools and concepts (UI libraries, animation libraries,
|
|
415
|
-
4. **Rigid Static Layer of HTML:** Many frameworks use HTML as the primary medium for generating the user interface. TargetJS makes JavaScript the primary driver
|
|
416
|
-
5. **Boilerplate and Verbosity:** TargetJS aims to reduce boilerplate code. The code is compact and follows a predictable execution flow.
|
|
353
|
+
1. **Scattered State Management:** Many frameworks require separate libraries or complex patterns for state management. In TargetJS, state management is inherently handled through its core concept of “targets”.
|
|
354
|
+
2. **Complexity of Asynchronous Operations:** Traditional JavaScript often involves complex handling of asynchronous operations (Promises, callbacks, `async/await`). TargetJS addresses this by providing a declarative reactive targets and synchronous execution flow.
|
|
355
|
+
3. **Disjointed Development Workflow:** Developers often juggle multiple tools and concepts (UI libraries, animation libraries, event handlers). TargetJS provides a unified solution.
|
|
356
|
+
4. **Rigid Static Layer of HTML:** Many frameworks use HTML as the primary medium for generating the user interface. TargetJS makes JavaScript the primary driver.
|
|
357
|
+
5. **Boilerplate and Verbosity:** TargetJS aims to reduce boilerplate code. The code is compact and follows a predictable execution flow.
|
|
417
358
|
6. **Difficult Animation Control:** TargetJS makes animations first-class citizens with fine-grained control.
|
|
418
|
-
7. **Complicated execution flow:** TargetJS execution flow follows the order the code is written.
|
|
419
359
|
8. **Performance Bottlenecks with Large Lists:** TargetJS optimizes rendering for large lists by using a tree structure that renders only the visible branches.
|
|
420
|
-
|
|
360
|
+
|
|
361
|
+
---
|
|
362
|
+
|
|
421
363
|
## More Examples
|
|
422
364
|
|
|
423
|
-
|
|
365
|
+
### Loading Five Users Example
|
|
424
366
|
|
|
425
367
|
In this example, we load five separate users and display five boxes, each containing a user's name and email.
|
|
426
368
|
|
|
@@ -461,7 +403,7 @@ App({
|
|
|
461
403
|
});
|
|
462
404
|
```
|
|
463
405
|
|
|
464
|
-
It can also be written using a target’s `cycles` and `
|
|
406
|
+
It can also be written using a target’s `cycles` and `interval` properties/methods to fetch users at intervals instead of in a single batch. In this example, we set interval to 1000, making the API call once every second.
|
|
465
407
|
|
|
466
408
|
<img src="https://targetjs.io/img/fetch-5-users2.gif" width="130" />
|
|
467
409
|
|
|
@@ -484,7 +426,15 @@ App({
|
|
|
484
426
|
|
|
485
427
|
### Infinite Loading and Scrolling Example
|
|
486
428
|
|
|
487
|
-
In this advanced example, we
|
|
429
|
+
In this advanced example, we implement an infinite-scrolling application. The asynchronous flow that we want to achieve:
|
|
430
|
+
1. Insert new items, and animate their width and background.
|
|
431
|
+
2. On animation completion, trigger the API request for the visible items.
|
|
432
|
+
3. Await for all API responses of all the items and then populate their names.
|
|
433
|
+
|
|
434
|
+
This example could use other flows such as calling the API as soon as items are added, or populating each item’s name as its result arrives instead of waiting for all visible items.
|
|
435
|
+
All of these are doable, but we chose the above to showcase a more complex, sequential async flow.
|
|
436
|
+
|
|
437
|
+
**Explanation:**
|
|
488
438
|
|
|
489
439
|
- children: `children` is a special target that adds several items to the container's children each time it is executed. The `onVisibleChildrenChange` event function detects changes in the visible children and activates the `children` target to add new items that fill the gaps.
|
|
490
440
|
|
|
@@ -494,15 +444,13 @@ In this advanced example, we demonstrate an infinite scrolling application where
|
|
|
494
444
|
|
|
495
445
|
TargetJS employs a tree-like structure to track visible branches, optimizing the scroller performance.
|
|
496
446
|
|
|
497
|
-
We use the TModel class instead of a plain object to demonstrate how it can provide additional functionality and control. A plain object would also have worked in this example.
|
|
498
447
|
|
|
499
448
|
<img src="https://targetjs.io/img/infiniteScrolling.gif" width="130" />
|
|
500
449
|
|
|
501
450
|
```javascript
|
|
502
451
|
import { App, TModel, getEvents, fetch, getScreenWidth, getScreenHeight } from "targetj";
|
|
503
452
|
|
|
504
|
-
App(
|
|
505
|
-
domHolder: true,
|
|
453
|
+
App({
|
|
506
454
|
preventDefault: true,
|
|
507
455
|
containerOverflowMode: "always",
|
|
508
456
|
children() {
|
|
@@ -540,7 +488,7 @@ App(new TModel("scroller", {
|
|
|
540
488
|
width: getScreenWidth,
|
|
541
489
|
height: getScreenHeight,
|
|
542
490
|
onResize: 'width'
|
|
543
|
-
})
|
|
491
|
+
});
|
|
544
492
|
```
|
|
545
493
|
|
|
546
494
|
We can reduce the number of API calls by triggering them only after scrolling stops as follows:
|
|
@@ -558,50 +506,7 @@ We can reduce the number of API calls by triggering them only after scrolling st
|
|
|
558
506
|
}
|
|
559
507
|
}
|
|
560
508
|
```
|
|
561
|
-
|
|
562
|
-
Finally, in HTML:
|
|
563
|
-
|
|
564
|
-
```HTML
|
|
565
|
-
<div
|
|
566
|
-
id="scroller"
|
|
567
|
-
tg-domHolder="true"
|
|
568
|
-
tg-preventDefault="true"
|
|
569
|
-
tg-containerOverflowMode="always"
|
|
570
|
-
tg-width="return TargetJS.getScreenWidth();"
|
|
571
|
-
tg-height="return TargetJS.getScreenHeight();"
|
|
572
|
-
tg-children="function() {
|
|
573
|
-
const childrenCount = this.getChildren().length;
|
|
574
|
-
return Array.from({ length: 20 }, (_, i) => ({
|
|
575
|
-
width: [{list: [100, 250, 100]}, 50],
|
|
576
|
-
x$() { return this.getCenterX(); },
|
|
577
|
-
background: [{ list: ['#FCE961', '#B388FF'] }, 15, 15],
|
|
578
|
-
height: 48,
|
|
579
|
-
color: '#C2FC61',
|
|
580
|
-
textAlign: 'center',
|
|
581
|
-
lineHeight: 48,
|
|
582
|
-
bottomMargin: 2,
|
|
583
|
-
html: childrenCount + i
|
|
584
|
-
}));
|
|
585
|
-
}"
|
|
586
|
-
tg-load$$="function() {
|
|
587
|
-
this.visibleChildren.filter(child => !child.loaded).forEach(child => {
|
|
588
|
-
child.loaded = true;
|
|
589
|
-
TargetJS.fetch(this, `https://targetjs.io/api/randomUser?id=${child.oid}`);
|
|
590
|
-
});
|
|
591
|
-
}"
|
|
592
|
-
tg-populate$$="function() {
|
|
593
|
-
if (this.prevTargetValue) {
|
|
594
|
-
this.prevTargetValue.forEach(data => this.getChildByOid(data.id).setTarget('html', data.name));
|
|
595
|
-
}
|
|
596
|
-
}"
|
|
597
|
-
tg-onScroll="function() {
|
|
598
|
-
this.setTarget('scrollTop', Math.max(0, this.getScrollTop() + TargetJS.getEvents().deltaY()));
|
|
599
|
-
}"
|
|
600
|
-
tg-onVisibleChildrenChange="function() {
|
|
601
|
-
return !this.visibleChildren.length || this.getLastChild().getY() < this.getHeight() ? 'children' : 'load$$';
|
|
602
|
-
}"
|
|
603
|
-
></div>
|
|
604
|
-
```
|
|
509
|
+
---
|
|
605
510
|
|
|
606
511
|
## Target Methods
|
|
607
512
|
|
|
@@ -609,60 +514,61 @@ All methods and properties are optional, but they play integral roles in making
|
|
|
609
514
|
|
|
610
515
|
1. **value**
|
|
611
516
|
If defined, value is the primary target method that will be executed. The target value will be calculated based on the result of this method.
|
|
517
|
+
`Value` can also be defined as a property.
|
|
612
518
|
|
|
613
|
-
|
|
614
|
-
It indicates that the target is in an inactive state and must be activated by an event or other targets.
|
|
615
|
-
|
|
616
|
-
3. **active**
|
|
617
|
-
This is only a property. It indicates whether the target is ready for execution. When set to false, it behaves similarly to a `_ `prefix. By default, all targets are active, so setting it to true is unnecessary.
|
|
618
|
-
|
|
619
|
-
15. **Postfix `$` to the target name**
|
|
519
|
+
1. **Postfix `$` to the target name** (Reactive):
|
|
620
520
|
A target name ending with $ indicates that it will be only activated when the preceding target is executed. If the preceding target involves API calls, it will be activated
|
|
621
521
|
each time an API response is received, while ensuring the order of API calls is enforced. This means it will remain inactive until the first API result is received,
|
|
622
522
|
then the second, and so on.
|
|
623
523
|
|
|
624
|
-
|
|
625
|
-
A target name ending with `$$` indicates
|
|
524
|
+
1. **Postfix `$$` to the target name** (Deferred):
|
|
525
|
+
A target name ending with `$$` indicates that it will be activated only after all the preceding target has completed, along with all its imperative targets,
|
|
626
526
|
and after all API results have been received.
|
|
627
527
|
|
|
628
|
-
|
|
528
|
+
1. **Prefix `_` to the target name** (Inactive):
|
|
529
|
+
It indicates that the target is in an inactive state and must be activated by an event or other targets explicitly.
|
|
530
|
+
|
|
531
|
+
1. **enabledOn**
|
|
629
532
|
Determines whether the target is eligible for execution. If enabledOn() returns false, the target remains active until it is enabled and gets executed.
|
|
630
533
|
|
|
631
|
-
|
|
534
|
+
1. **loop**
|
|
632
535
|
Controls the repetition of target execution. If loop() returns true, the target will continue to execute indefinitely. It can also be defined as a boolean instead of a method.
|
|
633
536
|
|
|
634
|
-
|
|
635
|
-
It works similarly to `loop`, but it specifies an explicit number of repetitions. It can also be combined with `loop`, in which case, once the specified cycles complete, they will rerun as long as `loop` returns true.
|
|
537
|
+
1. **cycles**
|
|
538
|
+
It works similarly to `loop`, but it specifies an explicit number of repetitions. It can also be combined with `loop`, in which case, once the specified cycles complete, they will rerun as long as `loop` returns true. In other words, `loop` functions as an outer loop for `cycles`.
|
|
636
539
|
|
|
637
|
-
|
|
540
|
+
1. **interval**
|
|
638
541
|
It specifies the pause between each target execution or each actual value update when steps are defined.
|
|
639
542
|
|
|
640
|
-
|
|
543
|
+
1. **steps**
|
|
641
544
|
By default, the actual value is updated immediately after the target value. The steps option allows the actual value to be updated in iterations specified by the number of steps.
|
|
642
545
|
|
|
643
|
-
|
|
546
|
+
1. **easing**
|
|
644
547
|
An easing function that operates when steps are defined. It controls how the actual value is updated in relation to the steps.
|
|
645
548
|
|
|
646
|
-
|
|
647
|
-
This callback is triggered whenever there is a change returned by the target method
|
|
549
|
+
1. **onValueChange**
|
|
550
|
+
This callback is triggered whenever there is a change returned by the target method/property `value`.
|
|
648
551
|
|
|
649
|
-
|
|
552
|
+
1. **onStepsEnd**
|
|
650
553
|
This method is invoked only after the final step of updating the actual value is completed, assuming the target has a defined steps value.
|
|
651
554
|
|
|
652
|
-
|
|
555
|
+
1. **onImperativeStep**
|
|
653
556
|
This callback tracks the progress of imperative targets defined within a declarative target. If there are multiple imperative targets, this method is called at each step,
|
|
654
557
|
identifiable by their target name. You can also use `on${targetName}Step` to track individual targets with their own callbacks. For example, `onWidthStep()` is called on each update of the `width` target.
|
|
655
558
|
|
|
656
|
-
|
|
559
|
+
1. **onImperativeEnd**
|
|
657
560
|
Similar to `onImperativeStep`, but it is triggered when an imperative target completes. If multiple targets are expected to complete, you can use `on${targetName}End` instead. For example, `onWidthEnd` is called when the `width` target gets completed.
|
|
658
561
|
|
|
659
|
-
|
|
660
|
-
This is only property. It defines the initial value of the actual value.
|
|
562
|
+
1. **initialValue**
|
|
563
|
+
This is only a property. It defines the initial value of the actual value.
|
|
564
|
+
|
|
565
|
+
1. **active**
|
|
566
|
+
This is only a property. It indicates whether the target is ready for execution. When set to false, it behaves similarly to a `_` prefix. By default, all targets are active, so setting it to true is unnecessary.
|
|
661
567
|
|
|
662
|
-
|
|
568
|
+
1. **onSuccess**
|
|
663
569
|
An optional callback for targets that make API calls. It will be invoked for each API response received.
|
|
664
570
|
|
|
665
|
-
|
|
571
|
+
1. **onError**
|
|
666
572
|
Similar to the `onSuccess` but it will be invoked on every error.
|
|
667
573
|
|
|
668
574
|
## Target Variables
|
|
@@ -696,6 +602,7 @@ In addition to styles and attribute names, we have the following special names:
|
|
|
696
602
|
|
|
697
603
|
1. **html**: Sets the content of the object, interpreted as text by default.
|
|
698
604
|
2. **children**: Adds new items to the parent each time it executes. Items can be either plain objects or instances of TModel for greater control.
|
|
605
|
+
3. **Child**: Similar to `children` but adds only one item.
|
|
699
606
|
4. **css**: A string that sets the CSS of the object.
|
|
700
607
|
5. **element**: Sets the HTML tag of the object, defaulting to `div`.
|
|
701
608
|
6. **shouldBeBracketed**: A boolean flag that, when set to true (the default), enables the creation of an optimization tree for a container with more items than the `bracketThreshold` (another target with a default value of 10). This optimization ensures only the visible branch receives updates and get executed.
|
|
@@ -755,13 +662,13 @@ Here are all the event targets:
|
|
|
755
662
|
TargetJS provides built-in debugging tools:
|
|
756
663
|
|
|
757
664
|
```bash
|
|
758
|
-
TargetJS.tApp.stop(); //
|
|
759
|
-
TargetJS.tApp.start(); //
|
|
760
|
-
TargetJS.tApp.throttle; //
|
|
761
|
-
TargetJS.tApp.debugLevel = 1; //
|
|
665
|
+
TargetJS.tApp.stop(); // Stop the application.
|
|
666
|
+
TargetJS.tApp.start(); // Restart the application
|
|
667
|
+
TargetJS.tApp.throttle = 0; // Slow down execution (milliseconds between cycles)
|
|
668
|
+
TargetJS.tApp.debugLevel = 1; // Log cycle execution
|
|
762
669
|
```
|
|
763
670
|
- Use `t()` in the browser console to find an object by its oid.
|
|
764
|
-
- Use `t(oid).bug()` to inspect all the vital
|
|
671
|
+
- Use `t(oid).bug()` to inspect all the vital properties.
|
|
765
672
|
- Use `t(oid).logTree()` to inspect the UI structure.
|
|
766
673
|
|
|
767
674
|
## Documentation
|
|
@@ -774,6 +681,9 @@ Distributed under the MIT License. See LICENSE for more information.
|
|
|
774
681
|
Ahmad Wasfi - wasfi2@gmail.com
|
|
775
682
|
|
|
776
683
|
## 💖 Support TargetJS
|
|
684
|
+
|
|
685
|
+
If you would like to show some appreciation:
|
|
686
|
+
|
|
777
687
|
- ⭐ Star this repo on GitHub to show your support!
|
|
778
688
|
- 🐛 Report issues & suggest features.
|
|
779
689
|
- 📢 Share TargetJS with your network.
|
package/build/$Dom.js
CHANGED
|
@@ -403,6 +403,16 @@ var $Dom = exports.$Dom = /*#__PURE__*/function () {
|
|
|
403
403
|
value: function findFirstByTag(tagName) {
|
|
404
404
|
return $Dom.findFirstByTag(tagName, this.element);
|
|
405
405
|
}
|
|
406
|
+
}, {
|
|
407
|
+
key: "getScrollTop",
|
|
408
|
+
value: function getScrollTop() {
|
|
409
|
+
return this.element.scrollTop;
|
|
410
|
+
}
|
|
411
|
+
}, {
|
|
412
|
+
key: "getScrollLeft",
|
|
413
|
+
value: function getScrollLeft() {
|
|
414
|
+
return this.element.scrollLeft;
|
|
415
|
+
}
|
|
406
416
|
}], [{
|
|
407
417
|
key: "createTemplate",
|
|
408
418
|
value: function createTemplate(html) {
|
package/build/App.js
CHANGED
|
@@ -130,20 +130,31 @@ var AppFn = function AppFn() {
|
|
|
130
130
|
}
|
|
131
131
|
}, _callee2);
|
|
132
132
|
}));
|
|
133
|
-
my.reset = function () {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
133
|
+
my.reset = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
|
|
134
|
+
return _regeneratorRuntime().wrap(function _callee3$(_context3) {
|
|
135
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
136
|
+
case 0:
|
|
137
|
+
my.manager.getVisibles().forEach(function (tmodel) {
|
|
138
|
+
tmodel.transformMap = {};
|
|
139
|
+
tmodel.styleMap = {};
|
|
140
|
+
tmodel.allStyleTargetList.forEach(function (key) {
|
|
141
|
+
if (_TUtil.TUtil.isDefined(tmodel.val(key))) {
|
|
142
|
+
tmodel.addToStyleTargetList(key);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
_context3.next = 3;
|
|
147
|
+
return my.runScheduler.resetRuns();
|
|
148
|
+
case 3:
|
|
149
|
+
my.manager.clearAll();
|
|
150
|
+
my.locationManager.clear();
|
|
151
|
+
_SearchUtil.SearchUtil.clear();
|
|
152
|
+
case 6:
|
|
153
|
+
case "end":
|
|
154
|
+
return _context3.stop();
|
|
155
|
+
}
|
|
156
|
+
}, _callee3);
|
|
157
|
+
}));
|
|
147
158
|
my.isRunning = function () {
|
|
148
159
|
return my.runningFlag;
|
|
149
160
|
};
|