@livenetworks/ashlar 2.1.12 โ 2.1.13
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 +25 -2
- package/components/COMPONENTS.md +82 -78
- package/components/ln-core/README.md +25 -69
- package/components/ln-core/index.js +1 -1
- package/components/ln-core/reactive.js +0 -59
- package/components/ln-filter/ln-filter.js +1 -1
- package/components/ln-filter/src/ln-filter.js +3 -1
- package/package.json +95 -85
- package/theme/README.md +73 -0
- package/theme/components/_table.scss +1 -2
- package/theme/config/_density.scss +104 -191
- package/theme/config/mixins/_avatar.scss +2 -2
- package/theme/config/mixins/_card.scss +2 -5
- package/theme/config/mixins/_form.scss +26 -39
- package/theme/config/mixins/_nav.scss +4 -4
- package/theme/config/mixins/_spacing.scss +4 -4
- package/theme/config/mixins/_table.scss +9 -33
- package/theme/tailwind/components.css +1661 -0
- package/theme/tailwind/entry.css +3 -0
- package/theme/tailwind/theme.css +23 -0
- package/web-types.json +1 -1
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ Combine autonomous components directly in semantic HTML โ **zero initializatio
|
|
|
107
107
|
|
|
108
108
|
### 1. ๐๏ธ Dual-Core Architecture (SSR + Client-Side SPA)
|
|
109
109
|
- **Backend-First Progressive HTML Enhancement (SSR):** Your backend (Laravel, Go, Django, Rails) delivers indexable, semantic HTML. Elements auto-activate via attributes (`data-ln-modal`, `data-ln-filter`, `data-ln-table`).
|
|
110
|
-
- **Client-Side Single-Page Applications (SPA / JSON Mode):** Built-in client router (`ln-router`), compound hash-state codec (`hash.js`), 3-tier local storage (`ln-data-store`), and microtask-batched
|
|
110
|
+
- **Client-Side Single-Page Applications (SPA / JSON Mode):** Built-in client router (`ln-router`), compound hash-state codec (`hash.js`), 3-tier local storage (`ln-data-store`), and microtask-batched state rendering (`createBatcher`, `renderList`) when client JSON APIs are required.
|
|
111
111
|
|
|
112
112
|
### 2. ๐ค AI-Native & MCP Surface
|
|
113
113
|
- **Machine-Readable Component Corpus (`docs-mcp/`):** Schema-validated documentation served directly to AI agent workflows via Model Context Protocol (MCP).
|
|
@@ -184,7 +184,7 @@ Deep architectural blueprints detailing the engine driving `ln-ashlar`.
|
|
|
184
184
|
| ๐ **[Architecture Philosophy](docs/architecture/philosophy.md)** | The DOM-First engineering manifesto detailing computing cycles, framework EOL risks, performance, security, **and full 17-category comparison tables**. |
|
|
185
185
|
| ๐ **[Data Flow Architecture](docs/architecture/data-flow.md)** | Rules governing how data moves. Splits responsibilities into **four isolated concerns**: Data (`ln-data-store` + `ln-data-coordinator`), Render (`ln-table`), Submit (`ln-form`), and Validate (`ln-validate`). Details the **parallel fan-out write pipeline** with Mermaid sequence diagrams. |
|
|
186
186
|
| ๐งญ **[3-Tier Local-First Storage Specs](docs/architecture/data-store-architecture.md)** | Technical reference for decoupling storage caches (`ln-data-store`), network gateways (`ln-*-connector`), and data transformations (`ln-mapper`). |
|
|
187
|
-
| โก **[Reactive Architecture Reference](docs/reactive.md)** | Blueprint on how components manage
|
|
187
|
+
| โก **[Reactive Architecture Reference](docs/reactive.md)** | Blueprint on how components manage state using microtask batching (`createBatcher`), DOM bindings (`fill`, `renderList`), and attribute-to-state bridges. |
|
|
188
188
|
| ๐ **[Design System Specifications](docs/architecture/reference.md)** | Complete styling specs covering CSS Custom Properties, layout grids, buttons, responsive breakpoints, typography hierarchies, dark mode theming, and icons. |
|
|
189
189
|
| ๐ก๏ธ **[Security & Threat Mitigation](docs/architecture/security.md)** | Deep architectural security analysis covering Web Crypto API encryption-at-rest, strict CSP compliance (no dynamic eval), sensitive DOM attribute protections, and the same-origin AJAX fragment trust boundary. |
|
|
190
190
|
| ๐ง **[Architect Overview Guide](docs/architecture/overview.md)** | Master developer guide introducing the DOM-First doctrine, component inventories, override architectures, and new project integration pipelines. |
|
|
@@ -270,6 +270,29 @@ For detailed manual instructions, properties, attributes, and events of individu
|
|
|
270
270
|
|
|
271
271
|
---
|
|
272
272
|
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## ๐จ Dual CSS Engine Architecture
|
|
276
|
+
|
|
277
|
+
`ln-ashlar` offers dual-engine visual styling with 100% design parity across all components and layouts:
|
|
278
|
+
|
|
279
|
+
1. **Native Ashlar SCSS:** Class-free semantic HTML styling and design tokens. Optimized for minimal CSSOM invalidation by eliminating mixin selector explosions and `:not(:has())` bottlenecks.
|
|
280
|
+
2. **Tailwind CSS v4 Engine:** Utility-first stylesheet compiled directly with Tailwind v4 `@theme` tokens (`npm run build:tailwind`).
|
|
281
|
+
|
|
282
|
+
See [Theme Architecture & SCSS Guidelines](theme/README.md) for details.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## ๐งช Automated Testing & Performance Suite
|
|
287
|
+
|
|
288
|
+
`ln-ashlar` includes an automated headless verification and benchmarking harness comparing both CSS engines:
|
|
289
|
+
|
|
290
|
+
- **Visual & Design Suite (`npm run test:visual`):** Validates responsive layout, horizontal overflow, and reactive UI across all 67 demo admin pages.
|
|
291
|
+
- **JS Performance Suite (`npm run test:perf`):** Benchmarks execution latency, DOM node churn, and JS heap memory across 46 interactive JS components.
|
|
292
|
+
- **Historical Comparison (`npm run test:perf:compare`):** Tracks performance trends across git commits and alerts on performance regressions (`>20%`).
|
|
293
|
+
|
|
294
|
+
See [Headless Testing Documentation](tests/headless/README.md) for full usage and options.
|
|
295
|
+
|
|
273
296
|
## ๐ฅ๏ธ Interactive Demo Site
|
|
274
297
|
|
|
275
298
|
`ln-ashlar` ships with a complete local dashboard environment inside `demo/admin/`. The demo is itself a project consuming `ln-ashlar`, showing real-world layout structures, component setups, and customized semantic SCSS:
|
package/components/COMPONENTS.md
CHANGED
|
@@ -69,7 +69,7 @@ Every component is an IIFE (Immediately Invoked Function Expression). Shared hel
|
|
|
69
69
|
|
|
70
70
|
```javascript
|
|
71
71
|
import { dispatch } from '../ln-core';
|
|
72
|
-
import {
|
|
72
|
+
import { createBatcher } from '../ln-core';
|
|
73
73
|
|
|
74
74
|
(function () {
|
|
75
75
|
const DOM_SELECTOR = 'data-ln-{name}';
|
|
@@ -96,8 +96,6 @@ import { deepReactive, createBatcher } from '../ln-core';
|
|
|
96
96
|
| `fillTemplate(clone, data)` | Replace `{{ key }}` text-node placeholders in cloned template with `data[key]` values |
|
|
97
97
|
| `buildDict(root, selector)` | Read hidden i18n elements once at init, return plain object, remove from DOM |
|
|
98
98
|
| `renderList(container, items, tpl, keyFn, fillFn, tag)` | Keyed list rendering with DOM reuse |
|
|
99
|
-
| `reactiveState(initial, onChange)` | Shallow Proxy โ onChange(prop, value, old) per set |
|
|
100
|
-
| `deepReactive(obj, onChange)` | Deep Proxy โ onChange() on any nested change |
|
|
101
99
|
| `createBatcher(renderFn, afterRender)` | Coalesce multiple sync state changes into one render |
|
|
102
100
|
|
|
103
101
|
Import only what the component needs. Vite tree-shakes unused exports.
|
|
@@ -213,80 +211,86 @@ function _domObserver() {
|
|
|
213
211
|
|
|
214
212
|
---
|
|
215
213
|
|
|
216
|
-
## Reactive Rendering Pattern
|
|
217
|
-
|
|
218
|
-
When a component has **multiple internal state properties** that
|
|
219
|
-
|
|
220
|
-
### The
|
|
221
|
-
|
|
222
|
-
| Helper | Role |
|
|
223
|
-
|--------|------|
|
|
224
|
-
| `
|
|
225
|
-
| `
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
function () {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.state.key
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
214
|
+
## Reactive Rendering Pattern
|
|
215
|
+
|
|
216
|
+
When a component has **multiple internal state properties** or frequent updates that drive DOM updates, use `createBatcher` + `fill()` from ln-core. This eliminates manual querySelector chains and coalesces multiple synchronous state changes into a single DOM update.
|
|
217
|
+
|
|
218
|
+
### The core primitives
|
|
219
|
+
|
|
220
|
+
| Helper | Role |
|
|
221
|
+
|--------|------|
|
|
222
|
+
| `createBatcher(renderFn, afterRender)` | Coalesces sync state changes into one `renderFn` call via `queueMicrotask` |
|
|
223
|
+
| `fill(root, data)` | Writes state to DOM via `data-ln-field`, `data-ln-attr`, `data-ln-show`, `data-ln-class` |
|
|
224
|
+
|
|
225
|
+
### Pattern
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
import { createBatcher, fill, dispatch } from '../ln-core';
|
|
229
|
+
|
|
230
|
+
function _component(dom) {
|
|
231
|
+
this.dom = dom;
|
|
232
|
+
const self = this;
|
|
233
|
+
|
|
234
|
+
this.state = { key: null, value: null };
|
|
235
|
+
|
|
236
|
+
this.queueRender = createBatcher(
|
|
237
|
+
function () {
|
|
238
|
+
if (!self._destroyed) {
|
|
239
|
+
self._render();
|
|
240
|
+
}
|
|
241
|
+
}, // runs after current sync block
|
|
242
|
+
function () {
|
|
243
|
+
if (!self._destroyed) {
|
|
244
|
+
self._afterRender();
|
|
245
|
+
}
|
|
246
|
+
} // runs after render โ dispatch events here
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
_component.prototype._render = function () {
|
|
251
|
+
fill(this.dom, this.state);
|
|
252
|
+
// additional DOM logic derived from state
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
_component.prototype._afterRender = function () {
|
|
256
|
+
dispatch(this.dom, 'ln-component:changed', { key: this.state.key });
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
_component.prototype.update = function (key, value) {
|
|
260
|
+
this.state.key = key;
|
|
261
|
+
this.state.value = value;
|
|
262
|
+
this.queueRender(); // Multiple sync updates trigger exactly ONE render pass
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
When multiple properties change in the same sync block:
|
|
267
|
+
|
|
268
|
+
```javascript
|
|
269
|
+
this.state.key = 'status';
|
|
270
|
+
this.state.value = 'active';
|
|
271
|
+
this.queueRender();
|
|
272
|
+
// โ microtask fires โ _render() once โ _afterRender() once
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### `fill()` โ limitations
|
|
276
|
+
|
|
277
|
+
`fill()` sets `textContent` for `data-ln-field` โ it does **not** set `.value` on `<input>`, `<textarea>`, or `<select>`. For form elements, set `.value` manually after `fill()`.
|
|
278
|
+
|
|
279
|
+
### When to use
|
|
280
|
+
|
|
281
|
+
Use `createBatcher` when a component has **frequent or multiple state updates** (e.g. search input, filter checkboxes, sorting), and multiple updates can occur in the same synchronous cycle.
|
|
282
|
+
|
|
283
|
+
**Don't use it for:**
|
|
284
|
+
- Single boolean state โ `this.isOpen = true; dom.classList.toggle(...)` is simpler
|
|
285
|
+
- Event-driven rendering โ components that receive data via `set-data` events (ln-table)
|
|
286
|
+
- One-shot renders โ clone template + fill once, no ongoing state
|
|
287
|
+
|
|
288
|
+
### Used in
|
|
289
|
+
|
|
290
|
+
Used in `ln-search`, `ln-filter`, `ln-list`, and `ln-data-coordinator` for microtask render coalescing.
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
290
294
|
## CustomEvent Communication
|
|
291
295
|
|
|
292
296
|
Components do NOT know about each other. Communication ONLY via CustomEvent.
|
|
@@ -985,7 +989,7 @@ The component calls `_ensureDefaultItemTemplate()` at the top of `_initUpload()`
|
|
|
985
989
|
|
|
986
990
|
| Component | Pattern | Data Attr | Description |
|
|
987
991
|
|-----------|---------|-----------|------|
|
|
988
|
-
| ln-core | Shared module | โ | cloneTemplate, cloneTemplateScoped, dispatch, dispatchCancelable, fill, fillTemplate, renderList, buildDict, guardBody, findElements,
|
|
992
|
+
| ln-core | Shared module | โ | cloneTemplate, cloneTemplateScoped, dispatch, dispatchCancelable, fill, fillTemplate, renderList, buildDict, guardBody, findElements, createBatcher, createWindowCache |
|
|
989
993
|
| ln-toggle | Instance | `data-ln-toggle` | Generic toggle (sidebar, collapse) |
|
|
990
994
|
| ln-accordion | Instance | `data-ln-accordion` | Wrapper โ only one toggle open at a time |
|
|
991
995
|
| ln-tabs | Instance | `data-ln-tabs` | Tab navigation โ URL hash (anchor triggers) or localStorage persist (button triggers) |
|
|
@@ -13,7 +13,7 @@ ln-core exposes helpers in these categories:
|
|
|
13
13
|
- **DOM binding** โ declarative data-attribute-driven rendering (`fill`, `renderList`, `fillTemplate`)
|
|
14
14
|
- **Forms & Inputs** โ serialize/populate by field `name`, intercept input value getters/setters (`interceptValueProperty`)
|
|
15
15
|
- **Target & Event Predicates** โ `shouldIgnoreClick`, `isTargetDisabled`, `isUsableTarget`, `isEditableTarget`
|
|
16
|
-
- **Reactivity** โ
|
|
16
|
+
- **Reactivity & Batching** โ microtask-coalesced render batcher (`createBatcher`)
|
|
17
17
|
- **Windowing** โ sliding-window sparse cache for server-side virtualization (`createWindowCache`)
|
|
18
18
|
- **Layout & Positioning** โ viewport-aware positioning (`computePlacement`), measurement (`measureHidden`)
|
|
19
19
|
- **Hash codec** โ namespaced URL fragment state (`hashParse`, `hashGet`, `hashSet`, `hashSortEncode`, `hashSortDecode`, `hashFilterEncode`, `hashFilterDecode`, `resolveHashNamespace`)
|
|
@@ -542,37 +542,6 @@ fetch('/tpl.html').then(html => {
|
|
|
542
542
|
|
|
543
543
|
## reactive.js
|
|
544
544
|
|
|
545
|
-
### reactiveState(initial, onChange)
|
|
546
|
-
|
|
547
|
-
Shallow `Proxy` โ fires `onChange(prop, value, old)` on every property set. No-op if value hasn't changed.
|
|
548
|
-
|
|
549
|
-
```js
|
|
550
|
-
const state = reactiveState({ searchTerm: '', sortCol: -1 }, function (prop, value, old) {
|
|
551
|
-
batchRender();
|
|
552
|
-
});
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
- Returns a Proxy wrapping a shallow copy of `initial`
|
|
556
|
-
- Only tracks direct property assignments (not nested objects)
|
|
557
|
-
- Used by `ln-filter` for flat state
|
|
558
|
-
|
|
559
|
-
### deepReactive(obj, onChange)
|
|
560
|
-
|
|
561
|
-
Deep `Proxy` โ recursively wraps nested objects and arrays. Fires `onChange()` (no args) on any nested mutation.
|
|
562
|
-
|
|
563
|
-
```js
|
|
564
|
-
const state = deepReactive({ toasts: [], files: [] }, function () {
|
|
565
|
-
batchRender();
|
|
566
|
-
});
|
|
567
|
-
state.toasts.push({ title: 'Hello' }); // triggers onChange
|
|
568
|
-
```
|
|
569
|
-
|
|
570
|
-
- Wraps existing nested objects on creation
|
|
571
|
-
- Auto-wraps newly assigned objects/arrays
|
|
572
|
-
- `deleteProperty` also triggers `onChange`
|
|
573
|
-
- Internal `Symbol('deepReactive')` flag prevents double-wrapping
|
|
574
|
-
- Used by `ln-toast`, `ln-upload` for complex state
|
|
575
|
-
|
|
576
545
|
### createBatcher(renderFn, afterRender)
|
|
577
546
|
|
|
578
547
|
Coalesce multiple synchronous state changes into a single `renderFn` call via `queueMicrotask`.
|
|
@@ -586,43 +555,28 @@ const batchRender = createBatcher(
|
|
|
586
555
|
|
|
587
556
|
- Multiple calls to `schedule()` within the same microtask result in one `renderFn` call
|
|
588
557
|
- Optional `afterRender` callback fires after each render
|
|
589
|
-
- Pattern: `
|
|
590
|
-
|
|
591
|
-
---
|
|
592
|
-
|
|
593
|
-
## Reactive Rendering Pattern
|
|
594
|
-
|
|
595
|
-
The three reactive primitives (`reactiveState` / `deepReactive` +
|
|
596
|
-
`createBatcher` + `fill`) are designed to be used together. They are
|
|
597
|
-
the canonical ln-ashlar render loop:
|
|
598
|
-
|
|
599
|
-
1. **State mutation.** Component code assigns to a property on a
|
|
600
|
-
reactive proxy (`this.state.value = 'x'`). The proxy fires its
|
|
601
|
-
onChange callback.
|
|
602
|
-
2. **Microtask queue.** The onChange is wired to a `createBatcher`
|
|
603
|
-
schedule function. Any number of synchronous assignments in the
|
|
604
|
-
same sync block schedule a single microtask.
|
|
605
|
-
3. **One render.** When the microtask fires, the batcher calls
|
|
606
|
-
`renderFn` exactly once. `renderFn` typically calls `fill(this.dom,
|
|
607
|
-
this.state)` (or `renderList` for keyed collections) to project
|
|
608
|
-
state into the DOM, then the optional `afterRender` callback fires
|
|
609
|
-
(commonly used to dispatch a `ln-{component}:rendered` event).
|
|
610
|
-
|
|
611
|
-
Two assignments on the same tick produce one DOM update. External
|
|
612
|
-
callers that need to set several properties at once do not need a
|
|
613
|
-
`batch()` helper โ the batcher handles it.
|
|
614
|
-
|
|
615
|
-
The pattern is: **state mutation โ microtask queue โ one render**.
|
|
616
|
-
Never wire a reactive onChange directly to `_render()` without a
|
|
617
|
-
batcher between them โ you will get one render per assignment, and
|
|
618
|
-
the DOM will thrash.
|
|
619
|
-
|
|
620
|
-
For the full decision matrix (`reactiveState` vs `deepReactive`, when
|
|
621
|
-
to use this pattern at all, anti-patterns), see
|
|
622
|
-
[Reactive Rendering Pattern in components/COMPONENTS.md](../../components/COMPONENTS.md#reactive-rendering-pattern).
|
|
558
|
+
- Pattern: used in `ln-search`, `ln-filter`, `ln-list`, `ln-data-coordinator` to debounce/coalesce state and render operations
|
|
623
559
|
|
|
624
560
|
---
|
|
625
561
|
|
|
562
|
+
## Reactive Rendering Pattern
|
|
563
|
+
|
|
564
|
+
The `createBatcher` primitive and DOM rendering helpers (`fill`, `renderList`) are designed to be used together as the canonical ln-ashlar microtask render loop:
|
|
565
|
+
|
|
566
|
+
1. **State mutation.** Component updates internal data properties (e.g. `this.state.key = 'val'`).
|
|
567
|
+
2. **Microtask queue.** The component triggers the `schedule()` function returned by `createBatcher`. Any number of synchronous assignments in the same sync block schedule a single microtask.
|
|
568
|
+
3. **One render.** When the microtask fires, the batcher calls `renderFn` exactly once. `renderFn` typically calls `fill(this.dom, this.state)` (or `renderList` for keyed collections) to project state into the DOM, then the optional `afterRender` callback fires (commonly used to dispatch a `ln-{component}:rendered` or changed event).
|
|
569
|
+
|
|
570
|
+
Two state assignments on the same tick produce one DOM update:
|
|
571
|
+
|
|
572
|
+
The pattern is: **state mutation โ microtask queue โ one render**.
|
|
573
|
+
Never wire sync state updates directly to immediate DOM writes when multiple attributes or filter parameters can change simultaneously โ batching prevents layout thrashing.
|
|
574
|
+
|
|
575
|
+
For further details, see
|
|
576
|
+
[Reactive Rendering Pattern in components/COMPONENTS.md](../../components/COMPONENTS.md#reactive-rendering-pattern).
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
626
580
|
## window-cache.js
|
|
627
581
|
|
|
628
582
|
### createWindowCache(config)
|
|
@@ -981,7 +935,7 @@ Returns `true` if the element is an `<input>`, `<textarea>`, `<select>`, or `[co
|
|
|
981
935
|
## Typical Usage Pattern
|
|
982
936
|
|
|
983
937
|
```js
|
|
984
|
-
import { dispatch, dispatchCancelable, guardBody, findElements,
|
|
938
|
+
import { dispatch, dispatchCancelable, guardBody, findElements, createBatcher, fill, renderList } from '../ln-core';
|
|
985
939
|
|
|
986
940
|
(function () {
|
|
987
941
|
const DOM_SELECTOR = 'data-ln-example';
|
|
@@ -997,8 +951,10 @@ import { dispatch, dispatchCancelable, guardBody, findElements, deepReactive, cr
|
|
|
997
951
|
const self = this;
|
|
998
952
|
|
|
999
953
|
// Reactive state + batched rendering
|
|
1000
|
-
|
|
1001
|
-
this.
|
|
954
|
+
this.state = { items: [] };
|
|
955
|
+
this.batchRender = createBatcher(function () {
|
|
956
|
+
if (!self._destroyed) self._render();
|
|
957
|
+
});
|
|
1002
958
|
|
|
1003
959
|
return this;
|
|
1004
960
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { cloneTemplate, cloneTemplateScoped, dispatch, dispatchCancelable, requestData, fill, lnFill, fillTemplate, renderList, buildDict, guardBody, findElements, isVisible, shouldIgnoreClick, isTargetDisabled, isUsableTarget, isEditableTarget, serializeForm, populateForm, resolveFormMethod, getLocale, readValue, detectValueType, compareValues, registerComponent, shouldInterceptLink, buildUrl, getHeaders, parseHeaders, registerDataMapper, getDataMapper, interceptValueProperty, registerLocaleFallback, getLocaleFallback, holdInit, releaseInit, pendingCount, queueBoot, ensureLocaleObserver, observeAttributes, observeLifecycle, withoutLifecycle, setDebugSink, setPersistSink, isDevMode, hasActiveDebug } from './helpers.js';
|
|
2
|
-
export {
|
|
2
|
+
export { createBatcher } from './reactive.js';
|
|
3
3
|
export { createWindowCache } from './window-cache.js';
|
|
4
4
|
export { hashParse, hashGet, hashSet, hashLinkClick, resolveHashNamespace, hashSortEncode, hashSortDecode, hashFilterEncode, hashFilterDecode } from './hash.js';
|
|
5
5
|
export { queryParse, queryGet, querySet, resolveQueryParam, querySortEncode, querySortDecode, queryFilterEncode, queryFilterDecode } from './query.js';
|
|
@@ -1,62 +1,3 @@
|
|
|
1
|
-
// โโโ Shallow Reactive โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
2
|
-
|
|
3
|
-
export function reactiveState(initial, onChange) {
|
|
4
|
-
return new Proxy(Object.assign({}, initial), {
|
|
5
|
-
set(target, prop, value) {
|
|
6
|
-
const old = target[prop];
|
|
7
|
-
if (old === value) return true;
|
|
8
|
-
target[prop] = value;
|
|
9
|
-
onChange(prop, value, old);
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// โโโ Deep Reactive โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
16
|
-
|
|
17
|
-
const PROXY_FLAG = Symbol('deepReactive');
|
|
18
|
-
|
|
19
|
-
export function deepReactive(obj, onChange) {
|
|
20
|
-
function wrap(target) {
|
|
21
|
-
if (target === null || typeof target !== 'object') return target;
|
|
22
|
-
if (target[PROXY_FLAG]) return target;
|
|
23
|
-
|
|
24
|
-
// Recursively wrap existing nested objects/arrays
|
|
25
|
-
const keys = Object.keys(target);
|
|
26
|
-
for (let i = 0; i < keys.length; i++) {
|
|
27
|
-
const val = target[keys[i]];
|
|
28
|
-
if (val !== null && typeof val === 'object') {
|
|
29
|
-
target[keys[i]] = wrap(val);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return new Proxy(target, {
|
|
34
|
-
get(t, prop) {
|
|
35
|
-
if (prop === PROXY_FLAG) return true;
|
|
36
|
-
return t[prop];
|
|
37
|
-
},
|
|
38
|
-
set(t, prop, value) {
|
|
39
|
-
const old = t[prop];
|
|
40
|
-
if (value !== null && typeof value === 'object') {
|
|
41
|
-
value = wrap(value);
|
|
42
|
-
}
|
|
43
|
-
t[prop] = value;
|
|
44
|
-
if (old !== value) onChange();
|
|
45
|
-
return true;
|
|
46
|
-
},
|
|
47
|
-
deleteProperty(t, prop) {
|
|
48
|
-
if (prop in t) {
|
|
49
|
-
delete t[prop];
|
|
50
|
-
onChange();
|
|
51
|
-
}
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return wrap(obj);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
1
|
// โโโ Render Batcher โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
61
2
|
|
|
62
3
|
export function createBatcher(renderFn, afterRender) {
|