pawajs 2.4.0-beta.1 → 2.4.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -1
- package/LICENSE +1 -1
- package/README.md +216 -70
- package/examples/Text.js +5 -0
- package/examples/index.html +64 -0
- package/examples/index.js +31 -0
- package/index.d.ts +394 -356
- package/index.js +64 -1413
- package/package.json +1 -1
- package/src/builtIn/Active.js +67 -0
- package/src/builtIn/viewTransition.js +42 -0
- package/src/component/index.js +194 -0
- package/src/component/utils.js +358 -0
- package/src/control-flow/attribute.js +83 -0
- package/src/control-flow/await.js +78 -0
- package/src/control-flow/for.js +220 -0
- package/src/control-flow/if.js +127 -0
- package/src/control-flow/key.js +87 -0
- package/src/control-flow/ref.js +34 -0
- package/src/control-flow/state.js +26 -0
- package/src/control-flow/template.js +25 -0
- package/src/control-flow/text.js +58 -0
- package/src/control-flow/timers.js +26 -0
- package/src/control-flow/utils.js +42 -0
- package/src/dev/index.js +67 -0
- package/src/dev/plugin.js +24 -0
- package/src/dev/tools.js +89 -0
- package/src/event/index.js +212 -0
- package/src/global.js +93 -0
- package/src/graph/graph.js +156 -0
- package/src/graph/index.js +150 -0
- package/src/graph/setup.js +28 -0
- package/src/hooks/index.js +217 -0
- package/src/hooks/registerComponent.js +101 -0
- package/src/hooks/state.js +295 -0
- package/src/hooks/utils.js +35 -0
- package/src/pawaElement.js +159 -0
- package/src/reactive.js +251 -0
- package/{server.js → src/server/index.js} +6 -4
- package/src/store.js +3 -0
- package/src/utils.js +56 -0
- package/cdn/index.html +0 -25
- package/cdn/index.js +0 -23
- package/merger/for.js +0 -203
- package/merger/if.js +0 -135
- package/merger/key.js +0 -117
- package/merger/switch.js +0 -134
- package/normal/For.js +0 -30
- package/normal/If.js +0 -29
- package/normal/Key.js +0 -27
- package/normal/Switch.js +0 -29
- package/normal/component.js +0 -419
- package/normal/template.js +0 -33
- package/pawaComponent.js +0 -72
- package/pawaElement.js +0 -504
- package/power.js +0 -667
- package/reactive.js +0 -190
- package/resumer.js +0 -19
- package/server.d.ts +0 -13
- package/utils.js +0 -309
package/CHANGELOG.md
CHANGED
|
@@ -23,4 +23,5 @@ CHANGELOG.md
|
|
|
23
23
|
+ version 2.0.1 -2.0.2 made runEffect to be global or component capability
|
|
24
24
|
+ version 2.0.3 integrating HMR system
|
|
25
25
|
+ version 2.0.4 experimental of hmr
|
|
26
|
-
+ version 2.0.5 - 2.0.6 experimental of hmr
|
|
26
|
+
+ version 2.0.5 - 2.0.6 experimental of hmr
|
|
27
|
+
+2.4.0.beta.4 new pawajs Rewrite
|
package/LICENSE
CHANGED
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -5,22 +5,27 @@ pawajs - power the web (reactivity and html runtime)
|
|
|
5
5
|
|
|
6
6
|
**A lightweight and reactive JavaScript library for building modern web interfaces with a simple, declarative syntax.**
|
|
7
7
|
|
|
8
|
-
PawaJS (reactive web runtime) is a JavaScript library designed for building dynamic user interfaces. It combines a component-based architecture and progressive enhancement with a powerful reactivity system no v-dom. Its intuitive, directive-based templating feels familiar and makes it easy to create interactive applications, from simple widgets to complex single-page apps. With built-in support for server-side rendering using pawa-ssr (server rendering runtime) and pawajs-continue (
|
|
8
|
+
PawaJS (reactive web runtime) is a JavaScript library designed for building dynamic user interfaces. It combines a component-based architecture and progressive enhancement with a powerful reactivity system, no v-dom. Its intuitive, directive-based templating feels familiar and makes it easy to create interactive applications, from simple widgets to complex single-page apps. With built-in support for server-side rendering using pawa-ssr (server rendering runtime) and pawajs-continue (continuity runtime), PawaJS is equipped for performance and scalability.
|
|
9
9
|
|
|
10
|
+
PawaJS is a **reactive web runtime** for HTML-first UIs: components, directives, and fine-grained `$state` updates with **no virtual DOM**.
|
|
11
|
+
|
|
12
|
+
On the client it powers widgets through SPAs. With **pawa-ssr** and **pawajs-continue** it uses **selective continue** (SCP): the server sends HTML plus a sparse index of reactive areas; the client continues only those areas. **Static components are stripped** from that index so they are not re-executed on the client.
|
|
10
13
|
🌐 **Website:** [pawajs.vercel.app](https://pawajs.vercel.app)
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
## Features
|
|
15
18
|
|
|
16
|
-
- **Declarative Rendering:** Use a clean, HTML-based template syntax with powerful directives (`if`, `for-each`, `on-event`, etc.) to describe your UI.
|
|
19
|
+
- **Declarative Rendering:** Use a clean, HTML-based template syntax with powerful directives (`if`, `for-each`, `await`, `on-event`, etc.) to describe your UI.
|
|
17
20
|
- **Reactive State Management:** Effortlessly create reactive state that automatically updates the DOM when it changes using the `$state` utility.
|
|
18
|
-
- **Component-Based Architecture:** Build encapsulated components with just
|
|
19
|
-
- **Async
|
|
21
|
+
- **Component-Based Architecture:** Build encapsulated components with just a JS function that manages its own state, making your code more reusable and maintainable. Components are synchronous by default — no async component mode to reason about.
|
|
22
|
+
- **First-Class Async Handling:** Fetch and await data directly in your templates with the `await` / `as-fallback` / `as-catch` directives — no special component wiring required.
|
|
20
23
|
- **Efficient List Rendering:** Render lists of data with the `for-each` directive, including support for keyed updates for optimal performance.
|
|
21
24
|
- **Lifecycle Hooks:** Tap into a component's lifecycle with `mount` and `unmount` directives, or the `runEffect` hook for more complex side effects.
|
|
22
25
|
- **Context API:** Pass data through the component tree without having to pass props down manually at every level.
|
|
23
|
-
|
|
26
|
+
|
|
27
|
+
- **Selective continue (SSR):** `pawa-ssr` emits HTML + SCP; `pawajs-continue` binds reactive regions only. Static UI stays plain HTML (no client re-execution).
|
|
28
|
+
---
|
|
24
29
|
- **Plugin System:** Extend PawaJS's core functionality with custom directives and lifecycle behaviors.
|
|
25
30
|
|
|
26
31
|
---
|
|
@@ -33,7 +38,7 @@ Install PawaJS into your project using npm:
|
|
|
33
38
|
npm install pawajs
|
|
34
39
|
```
|
|
35
40
|
CDN
|
|
36
|
-
|
|
41
|
+
|
|
37
42
|
```html
|
|
38
43
|
<!doctype html>
|
|
39
44
|
<html lang="en">
|
|
@@ -86,7 +91,7 @@ CDN
|
|
|
86
91
|
|
|
87
92
|
OR
|
|
88
93
|
|
|
89
|
-
Then, you can import it into your application
|
|
94
|
+
Then, you can import it into your application through npm:
|
|
90
95
|
|
|
91
96
|
```javascript
|
|
92
97
|
import { pawaStartApp, $state, RegisterComponent, html } from 'pawajs';
|
|
@@ -162,7 +167,7 @@ pawaStartApp(appElement);
|
|
|
162
167
|
|
|
163
168
|
### State Management with `$state`
|
|
164
169
|
|
|
165
|
-
The `$state` function is the heart of PawaJS's reactivity. It creates a reactive object whose `value` property can be read and written to. Any changes to `.value` will automatically trigger updates in the parts of your application that depend on it (fine-
|
|
170
|
+
The `$state` function is the heart of PawaJS's reactivity. It creates a reactive object whose `value` property can be read and written to. Any changes to `.value` will automatically trigger updates in the parts of your application that depend on it (fine-grained) — no component re-rendering or diffing. `$state` can be global when used outside a component. You can also declare inline state directly in HTML or a template with `state-*="any js value"`.
|
|
166
171
|
|
|
167
172
|
```html
|
|
168
173
|
<!--- number-->
|
|
@@ -187,48 +192,37 @@ name.value = 'PawaJS'; // The UI will update automatically
|
|
|
187
192
|
const user = $state({ name: 'Alex', loggedIn: false });
|
|
188
193
|
user.value.loggedIn = true; // This is also reactive
|
|
189
194
|
|
|
190
|
-
// Persist state to localStorage
|
|
195
|
+
// Persist state to localStorage — wrap the $state call in useStorage(),
|
|
191
196
|
// The state will be saved under the key 'session' and reloaded on page refresh.
|
|
192
|
-
const session = $state({ id: null }, 'session');
|
|
197
|
+
const session = useStorage(() => $state({ id: null }), 'session');
|
|
193
198
|
|
|
194
199
|
// Compute state - must be used inside a component
|
|
195
|
-
//
|
|
200
|
+
// whenever count changes, the state updates
|
|
196
201
|
const doubleCount=$state(()=>count.value * 2,[count])
|
|
197
202
|
|
|
198
203
|
//AutoCompute
|
|
199
|
-
// Any function that uses any reactive state for reactive bindings or
|
|
204
|
+
// Any function that uses any reactive state for reactive bindings or directives becomes a computed function
|
|
200
205
|
const doubleCount=()=>count.value * 2
|
|
206
|
+
|
|
207
|
+
// Schedule a batch of state updates through the frame-budgeted scheduler
|
|
208
|
+
// instead of the default immediate/microtask path — useful for grouping a
|
|
209
|
+
// burst of updates so they get spread across animation frames rather than
|
|
210
|
+
// forced through synchronously in one go.
|
|
211
|
+
schedule(() => {
|
|
212
|
+
count.value++
|
|
213
|
+
name.value = 'Updated'
|
|
214
|
+
})
|
|
201
215
|
```
|
|
202
216
|
|
|
203
217
|
### Components
|
|
204
218
|
|
|
205
|
-
Components are the building blocks of your application. In PawaJS, a component is a JavaScript function that returns an HTML template string or
|
|
219
|
+
Components are the building blocks of your application. In PawaJS, a component is a **synchronous** JavaScript function that returns an HTML template string, or nothing.
|
|
206
220
|
|
|
207
221
|
- **Defining:** Create a function that returns a template or not.
|
|
208
222
|
- **Registering:** Use `RegisterComponent(MyComponent)` to make it available globally. In HTML, you can then use it as `<my-component>`.
|
|
209
223
|
- **`useInsert`:** To make variables, state, and functions from your component's setup available in its template, pass them in an object to `useInsert()`.
|
|
210
224
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
PawaJS supports async components ( `useAsync()` hook) out of the box. You can define a component as an `async` function, allowing you to perform asynchronous operations (like fetching data) before the component renders.
|
|
214
|
-
wrap any pawajs hook with $async after any await call
|
|
215
|
-
|
|
216
|
-
```javascript
|
|
217
|
-
const UserProfile = async () => {
|
|
218
|
-
// Async hook store the current component instance for $async to use
|
|
219
|
-
const {$async}=useAsync()
|
|
220
|
-
// The component will wait for this promise to resolve before rendering
|
|
221
|
-
const data = await fetch('/api/user').then(res => res.json());
|
|
222
|
-
const user = $async(()=>$state(data);)
|
|
223
|
-
$async(()=>useInsert({user}))
|
|
224
|
-
|
|
225
|
-
return html`
|
|
226
|
-
<div class="profile">
|
|
227
|
-
<h1>@{user.value.name}</h1>
|
|
228
|
-
</div>
|
|
229
|
-
`;
|
|
230
|
-
}
|
|
231
|
-
```
|
|
225
|
+
Components are always synchronous — there's no separate "async component" mode to learn. If a component needs to fetch or await data, reach for the [`await` directive](#await--as-fallback--as-catch) inside its template instead.
|
|
232
226
|
|
|
233
227
|
### Templating
|
|
234
228
|
|
|
@@ -249,7 +243,7 @@ PawaJS uses a simple `@{...}` syntax to embed dynamic JavaScript expressions dir
|
|
|
249
243
|
Directives are special attributes that apply reactive behavior to DOM elements.
|
|
250
244
|
|
|
251
245
|
#### `state-*`
|
|
252
|
-
|
|
246
|
+
Create inline state for the element and its children.
|
|
253
247
|
|
|
254
248
|
```html
|
|
255
249
|
<div state-count="0">
|
|
@@ -258,7 +252,6 @@ create inline state for the element and children
|
|
|
258
252
|
|
|
259
253
|
```
|
|
260
254
|
|
|
261
|
-
|
|
262
255
|
#### `if` / `else` / `else-if`
|
|
263
256
|
For conditional rendering.
|
|
264
257
|
|
|
@@ -273,36 +266,15 @@ For conditional rendering.
|
|
|
273
266
|
Please log in to continue.
|
|
274
267
|
</div>
|
|
275
268
|
```
|
|
276
|
-
#### `switch` / `case` / `default`
|
|
277
|
-
switch conditional rendering.
|
|
278
269
|
|
|
279
|
-
```html
|
|
280
|
-
<div switch="user.value.type" case="'admin'">
|
|
281
|
-
Welcome back, @{user.name.value}!
|
|
282
|
-
</div>
|
|
283
|
-
<div case="'guest">
|
|
284
|
-
You are browsing as a guest.
|
|
285
|
-
</div>
|
|
286
|
-
<div default>
|
|
287
|
-
Please log in to continue.
|
|
288
|
-
</div>
|
|
289
|
-
```
|
|
290
270
|
#### `key`
|
|
291
|
-
|
|
271
|
+
Re-renders the element/component when the value it watches changes.
|
|
292
272
|
|
|
293
273
|
```html
|
|
294
274
|
<user-component key="user.value.type"></user-component>
|
|
295
275
|
|
|
296
276
|
```
|
|
297
277
|
|
|
298
|
-
#### `is-exit`
|
|
299
|
-
makes pawajs engine to wait before removing the element until the animation/transition is done.
|
|
300
|
-
|
|
301
|
-
```html
|
|
302
|
-
<div if="user.value.loggedIn" class="user-card @{user.value.isActive ? 'active' : 'inactive'}" is-exit>
|
|
303
|
-
<input value="@{user.value.name}" on-input="user.value.value = e.target.value">
|
|
304
|
-
</div>
|
|
305
|
-
```
|
|
306
278
|
#### `for-each`
|
|
307
279
|
For rendering lists from an array. Use `for-key` to give each element a unique identity, which helps PawaJS optimize rendering.
|
|
308
280
|
|
|
@@ -314,7 +286,35 @@ For rendering lists from an array. Use `for-key` to give each element a unique i
|
|
|
314
286
|
</ul>
|
|
315
287
|
```
|
|
316
288
|
|
|
317
|
-
#### `
|
|
289
|
+
#### `await` / `as-fallback` / `as-catch`
|
|
290
|
+
For handling asynchronous data directly in a template — no async component wiring needed. The `await` attribute must point to a **function that returns a promise**, not an already-invoked call.
|
|
291
|
+
|
|
292
|
+
The resolved value is exposed in scope under `res` by default (the caught error under `error`), or under whatever name you give with `as`.
|
|
293
|
+
|
|
294
|
+
```html
|
|
295
|
+
<div await="fetchUser">
|
|
296
|
+
<p>Welcome, @{res.name}</p>
|
|
297
|
+
</div>
|
|
298
|
+
<div as-fallback>
|
|
299
|
+
<p>Loading...</p>
|
|
300
|
+
</div>
|
|
301
|
+
<div as-catch>
|
|
302
|
+
<p>Failed to load: @{error.message}</p>
|
|
303
|
+
</div>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Use `as` to bind the resolved (or caught) value under a custom name:
|
|
307
|
+
|
|
308
|
+
```html
|
|
309
|
+
<div await="fetchUser" as="user">
|
|
310
|
+
<p>Welcome, @{user.name}</p>
|
|
311
|
+
</div>
|
|
312
|
+
<div as-catch as="err">
|
|
313
|
+
<p>Failed to load: @{err.message}</p>
|
|
314
|
+
</div>
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
#### `on-<event>..chainModifiers`
|
|
318
318
|
modifiers - 'self','prevent','once','capture' etc
|
|
319
319
|
For handling DOM events.
|
|
320
320
|
|
|
@@ -322,19 +322,46 @@ For handling DOM events.
|
|
|
322
322
|
<button on-click="addTodo()">Add Todo</button>
|
|
323
323
|
<input on-input="newTodoText.value = e.target.value" />
|
|
324
324
|
```
|
|
325
|
+
|
|
325
326
|
#### `out-<event>.chainModifiers`
|
|
326
327
|
modifiers - 'self','prevent','once','capture' etc
|
|
327
|
-
For handling
|
|
328
|
+
For handling events fired outside the element.
|
|
328
329
|
|
|
329
330
|
```html
|
|
330
331
|
<button out-click.once="console.log('outside')">Add Todo</button>
|
|
331
332
|
```
|
|
332
333
|
|
|
334
|
+
#### `after-[ms]` / `every-[ms]`
|
|
335
|
+
Run a callback once after a delay or repeatedly on an interval. The expression is evaluated with the current render context and the timer is cleaned up when the component unmounts.
|
|
336
|
+
|
|
337
|
+
```html
|
|
338
|
+
<div after-[2000]="console.log('done after 2s')"></div>
|
|
339
|
+
<div every-[1000]="count.value++"></div>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
This is the runtime timer directive path used by the graph scheduler and is cleaned up automatically on unmount.
|
|
343
|
+
|
|
344
|
+
### Direct element property updates (JSA) - JavaScript Attribute
|
|
345
|
+
PawaJS also supports direct property assignment (javascript element attribute) with the `@` shorthand on an element. This avoids needing to write a full `on-*` event handler when you just want to set a DOM property or style field.
|
|
346
|
+
|
|
347
|
+
```html
|
|
348
|
+
<div @style.color="red" @inner-text="inner text"></div>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Nested properties are also supported:
|
|
352
|
+
|
|
353
|
+
```html
|
|
354
|
+
<div @style.background-color="tomato" @dataset.role="admin"></div>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The same shorthand works for native properties like `@value`, `@checked`, `@textContent`, and `@innerHTML`.
|
|
358
|
+
|
|
333
359
|
### Component Props
|
|
334
360
|
|
|
335
361
|
You can pass data from a parent component to a child component using props. To declare a prop, prefix the attribute with a colon (`:`).
|
|
336
|
-
|
|
337
|
-
For rest props pass `--` to the element that needs the attributes.
|
|
362
|
+
|
|
363
|
+
Children are **not** passed as a prop in PawaJS — they're passed as a `<slot>`, the same way native web components handle content projection. For rest props, pass `--` to the element that needs the attributes.
|
|
364
|
+
|
|
338
365
|
**Parent Component (`app.js`)**
|
|
339
366
|
```javascript
|
|
340
367
|
// ...
|
|
@@ -342,13 +369,15 @@ const message = $state('This is a message from the parent!');
|
|
|
342
369
|
useInsert({ message });
|
|
343
370
|
|
|
344
371
|
return html`
|
|
345
|
-
<todo-list title="My Todo List" :message="message.value" class="to the rest prop">
|
|
372
|
+
<todo-list title="My Todo List" :message="message.value" class="to the rest prop">
|
|
373
|
+
<p>Children go in here</p>
|
|
374
|
+
</todo-list>
|
|
346
375
|
`;
|
|
347
376
|
```
|
|
348
377
|
|
|
349
378
|
**Child Component (`todo-list.js`)**
|
|
350
379
|
```javascript
|
|
351
|
-
export const TodoList = ({ title, message
|
|
380
|
+
export const TodoList = ({ title, message }) => {
|
|
352
381
|
// Props are passed as functions that return the reactive value
|
|
353
382
|
useInsert({ title, message });
|
|
354
383
|
|
|
@@ -356,7 +385,7 @@ export const TodoList = ({ title, message,children }) => {
|
|
|
356
385
|
<div -->
|
|
357
386
|
<h2>@{title()}</h2>
|
|
358
387
|
<p>@{message()}</p>
|
|
359
|
-
|
|
388
|
+
<slot></slot>
|
|
360
389
|
</div>
|
|
361
390
|
`;
|
|
362
391
|
}
|
|
@@ -374,6 +403,106 @@ useValidateComponent(TodoList, {
|
|
|
374
403
|
});
|
|
375
404
|
```
|
|
376
405
|
|
|
406
|
+
### Slots
|
|
407
|
+
|
|
408
|
+
A component's `<slot>` is where the caller's children land. A plain `<slot></slot>` (or a `<slot name="default">`) catches every child the caller passed that wasn't targeted at a specific named slot — this is what `TodoList` above uses.
|
|
409
|
+
|
|
410
|
+
You can have **more than one slot**, each with its own `name`, to project different pieces of content into different parts of a component's template. On the caller's side, target a named slot with a `<template slot="name">` wrapper:
|
|
411
|
+
|
|
412
|
+
**Parent (`app.js`)**
|
|
413
|
+
```javascript
|
|
414
|
+
return html`
|
|
415
|
+
<card-panel>
|
|
416
|
+
<template slot="header">
|
|
417
|
+
<h2>Project status</h2>
|
|
418
|
+
</template>
|
|
419
|
+
|
|
420
|
+
<p>This is the default slot content — the body of the card.</p>
|
|
421
|
+
|
|
422
|
+
<template slot="footer">
|
|
423
|
+
<button on-click="dismiss()">Dismiss</button>
|
|
424
|
+
</template>
|
|
425
|
+
</card-panel>
|
|
426
|
+
`;
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Child (`card-panel.js`)**
|
|
430
|
+
```javascript
|
|
431
|
+
export const CardPanel = () => {
|
|
432
|
+
return html`
|
|
433
|
+
<div class="card">
|
|
434
|
+
<header>
|
|
435
|
+
<slot name="header"></slot>
|
|
436
|
+
</header>
|
|
437
|
+
<section class="card-body">
|
|
438
|
+
<slot></slot>
|
|
439
|
+
</section>
|
|
440
|
+
<footer>
|
|
441
|
+
<slot name="footer"></slot>
|
|
442
|
+
</footer>
|
|
443
|
+
</div>
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Anything the caller passes that isn't wrapped in a `<template slot="...">` falls through to the unnamed default slot — so plain, un-templated children and a `<template slot="...">` block can be mixed freely on the same call site, as in the example above.
|
|
449
|
+
|
|
450
|
+
### Passing a string prop with `<template prop="...">`
|
|
451
|
+
|
|
452
|
+
Sometimes you want to pass a chunk of raw markup or text to a component as a **prop value** (a string), not as projected content. Use `<template prop="name">` for that — its inner HTML becomes the string value of that prop, available on the child the same way any other prop is.
|
|
453
|
+
|
|
454
|
+
```javascript
|
|
455
|
+
// Parent
|
|
456
|
+
return html`
|
|
457
|
+
<alert-box>
|
|
458
|
+
<template prop="message">Something went wrong.</template>
|
|
459
|
+
</alert-box>
|
|
460
|
+
`;
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
```javascript
|
|
464
|
+
// Child (alert-box.js)
|
|
465
|
+
export const AlertBox = ({ message }) => {
|
|
466
|
+
useInsert({ message });
|
|
467
|
+
return html`
|
|
468
|
+
<div class="alert">@{message()}</div>
|
|
469
|
+
`;
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
A component's root element can also **become** whatever the caller passed, instead of wrapping it, by marking either the component's own root with `aschild` or letting a top-level `<slot>` take over as the root — useful when a component shouldn't add an extra wrapping element (e.g. a `Button` component that should render as the caller's own `<a>` if one is passed). In that case, the two elements' attributes are merged rather than one replacing the other — `class`/`style` are concatenated, everything else is kept from both (with collisions resolved by prefixing).
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## Built-in Components
|
|
478
|
+
|
|
479
|
+
PawaJS ships two built-in components for common structural and animation needs.
|
|
480
|
+
|
|
481
|
+
### `Active`
|
|
482
|
+
|
|
483
|
+
Controls whether its content is visible and reactive, toggling `display: none` and pausing/resuming the effects underneath it based on a `show` prop. Useful for tabs, panels, or any content that should stop doing reactive work while it's hidden.
|
|
484
|
+
|
|
485
|
+
```html
|
|
486
|
+
<active :show="isOpen.value">
|
|
487
|
+
<expensive-panel></expensive-panel>
|
|
488
|
+
</active>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### `Transition`
|
|
492
|
+
|
|
493
|
+
Wraps its content with enter/exit animation. When the content is added to the DOM, `Transition` plays the `enter` animation; when it's about to be removed (e.g. an `if` flipping false), it plays `exit` first and waits for it to finish before the element is actually taken out — so removal-triggering content never just vanishes mid-animation.
|
|
494
|
+
|
|
495
|
+
`enter` and `exit` take the same frame arguments as `el.animation()` — either an array of class names or an array of inline style strings — and `duration` controls how long each takes (defaults to `300`ms).
|
|
496
|
+
|
|
497
|
+
```html
|
|
498
|
+
<transition
|
|
499
|
+
:enter="['opacity-0 scale-95', 'opacity-100 scale-100']"
|
|
500
|
+
:exit="['opacity-100 scale-100', 'opacity-0 scale-95']"
|
|
501
|
+
:duration="250">
|
|
502
|
+
<div if="user.value.loggedIn">Welcome back!</div>
|
|
503
|
+
</transition>
|
|
504
|
+
```
|
|
505
|
+
|
|
377
506
|
---
|
|
378
507
|
|
|
379
508
|
## API Reference
|
|
@@ -381,17 +510,34 @@ useValidateComponent(TodoList, {
|
|
|
381
510
|
### Core Functions
|
|
382
511
|
- `pawaStartApp(rootElement, initialContext)`: Initializes the PawaJS application on a given root DOM element.
|
|
383
512
|
- `RegisterComponent(...components)`: Registers one or more components to be used in templates.
|
|
384
|
-
-
|
|
385
|
-
- `
|
|
513
|
+
- `Graph(parentGraph?)`: Creates a render graph node for a component or section of the DOM tree. This is the low-level runtime model used to track child nodes, lifecycle hooks, and reactive state.
|
|
514
|
+
- `PawaRender(graph, contexts?)`: Creates a render driver and returns `{ render, renderGraph, setContext }`. It walks a DOM subtree, resolves directives, mounts components, and attaches the current context graph to each node.
|
|
515
|
+
- `setComponentGraph(graph)` / `getComponentGraph()`: Set or read the active component graph from the current rendering context.
|
|
516
|
+
- `$state(initialValue, section?)`: Creates a new reactive state object. Used inside or outside a component (module export). `section` is used for compute dependencies (pass an array) or for naming an async batch — not for localStorage persistence.
|
|
517
|
+
- `useStorage(createCall, name)`: Wraps a `$state` creation call so its value is persisted to `localStorage` under `name` and restored on reload.
|
|
518
|
+
- `schedule(callback)`: Runs any state updates made inside `callback` through the frame-budgeted scheduler instead of the default immediate path — useful for batching a burst of updates across animation frames.
|
|
519
|
+
- `Plugin(plugin)`: Registers a plugin to extend PawaJS functionality (e.g., Routers, Global Stores).
|
|
386
520
|
- `html`: A tagged template literal for syntax highlighting and potential future optimizations.
|
|
387
521
|
|
|
388
522
|
### Component Hooks
|
|
389
523
|
- `useInsert(object)`: Exposes data and functions from a component's setup to its template.
|
|
390
|
-
- `runEffect(callback, dependencies?)`: Runs a side effect after or before the component renders, and re-runs it when its dependencies change
|
|
524
|
+
- `runEffect(callback, dependencies?)`: Runs a side effect after or before the component renders, and re-runs it when its dependencies change. Used inside or outside a component.
|
|
391
525
|
- `useContext(contextObject)` & `setContext()`: A mechanism for providing and consuming data throughout a component tree.
|
|
392
526
|
- `useRef()`: Creates a reference object that can be attached to a DOM element using the `ref` directive.
|
|
393
527
|
- `useValidateComponent(Component, rules)`: Defines validation rules for a component's props.
|
|
394
|
-
|
|
528
|
+
|
|
529
|
+
### Low-level Render Graph
|
|
530
|
+
PawaJS keeps internal render metadata in a graph object attached to the DOM node tree. That graph tracks parent/child relationships, mount hooks, enter/exit transitions, storage for context, and the current render state. This is the runtime structure behind directives like `if`, `for-each`, `key`, `await`, and component mounting.
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
import { Graph, PawaRender } from 'pawajs';
|
|
534
|
+
|
|
535
|
+
const graph = Graph();
|
|
536
|
+
const { render, renderGraph } = PawaRender(graph, { user: { name: 'Pawa' } });
|
|
537
|
+
render(document.getElementById('app'));
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
The graph is not usually something app code needs to manipulate directly, but it is the internal foundation for the library's reactive rendering pipeline and is exposed for advanced runtime integration and debugging.
|
|
395
541
|
|
|
396
542
|
---
|
|
397
543
|
|
package/examples/Text.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Document</title>
|
|
7
|
+
<style>
|
|
8
|
+
.view{
|
|
9
|
+
width: 200px;
|
|
10
|
+
height: 200px;
|
|
11
|
+
background-color: aquamarine;
|
|
12
|
+
}
|
|
13
|
+
.views{
|
|
14
|
+
width: 150px;
|
|
15
|
+
height: 150px;
|
|
16
|
+
background-color: gold;
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
</head>
|
|
20
|
+
<body>
|
|
21
|
+
<div id="app">
|
|
22
|
+
<div>
|
|
23
|
+
<h1>Hello PawaJS</h1>
|
|
24
|
+
<text></text>
|
|
25
|
+
</div>
|
|
26
|
+
<transition
|
|
27
|
+
|
|
28
|
+
:duration="3000"
|
|
29
|
+
:enter="['transform:scale(0)','transform:scale(1)']"
|
|
30
|
+
:exit="['transform:scale(1)','transform:scale(0)']"
|
|
31
|
+
>
|
|
32
|
+
<div class="view" state-name="''">
|
|
33
|
+
<active :show="user.value">
|
|
34
|
+
<div #style.color="@{user.value?'red':'blue'}">@{name.value}</div>
|
|
35
|
+
</active>
|
|
36
|
+
<input @value="@{name.value}" on-input="name.value=e.target.value"/>
|
|
37
|
+
</div>
|
|
38
|
+
</transition>
|
|
39
|
+
|
|
40
|
+
<template for-each="item, i in items.value"
|
|
41
|
+
for-key="{{item.id}}"
|
|
42
|
+
>
|
|
43
|
+
<div #style.color="@{user.value?'red':'blue'}">@{item.name}</div>
|
|
44
|
+
<div on-click="remove(item.id)">Enter</div>
|
|
45
|
+
</template>
|
|
46
|
+
|
|
47
|
+
<div on-click="set()">From template</div>
|
|
48
|
+
<div await="promise()">
|
|
49
|
+
<h4>This is Promise</h4>
|
|
50
|
+
</div>
|
|
51
|
+
<div as-fallback>
|
|
52
|
+
<span>Loading...</span>
|
|
53
|
+
</div>
|
|
54
|
+
<div as-catch>Error something wrong</div>
|
|
55
|
+
<counter user="user">
|
|
56
|
+
<div>
|
|
57
|
+
<span @inner-html="ALLWELL"></span>
|
|
58
|
+
<span>MY OWN Counter</span>
|
|
59
|
+
</div>
|
|
60
|
+
</counter>
|
|
61
|
+
</div>
|
|
62
|
+
<script type="module" src="./index.js"></script>
|
|
63
|
+
</body>
|
|
64
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { pawaStartApp,$state, RegisterComponent, useValidateComponent, runEffect, useInnerContext, useInsert, forwardProps, Transition, Active } from "../index.js";
|
|
2
|
+
import { schedule, useStorage } from "../src/hooks/state.js";
|
|
3
|
+
const user=useStorage(()=>$state(false),'user')
|
|
4
|
+
const set=()=>{
|
|
5
|
+
schedule(()=>user.value=!user.value)
|
|
6
|
+
}
|
|
7
|
+
const items=$state([
|
|
8
|
+
{name:'james',id:938449},{name:'joy',id:736497},{name:'josh',id:997287}
|
|
9
|
+
])
|
|
10
|
+
const remove=(key)=>{
|
|
11
|
+
items.value=items.value.filter(n => n.id !== key)
|
|
12
|
+
}
|
|
13
|
+
const Counter=({...props})=>{
|
|
14
|
+
|
|
15
|
+
useInsert({data:'users'})
|
|
16
|
+
return `
|
|
17
|
+
<slot></slot>`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const promise=()=>new Promise((r)=>{
|
|
21
|
+
setTimeout(() => {
|
|
22
|
+
r({
|
|
23
|
+
user:'Allwell',
|
|
24
|
+
age:24
|
|
25
|
+
})
|
|
26
|
+
}, 6000);
|
|
27
|
+
})
|
|
28
|
+
RegisterComponent(Counter,Transition,Active)
|
|
29
|
+
RegisterComponent.lazy('Text',()=>import('./Text.js'))
|
|
30
|
+
pawaStartApp(document.getElementById('app'),{user,set,items,remove,promise})
|
|
31
|
+
|