@sigx/lynx-runtime 0.4.6 → 0.4.7
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/dist/nodeOps.d.ts +8 -0
- package/dist/nodeOps.js +50 -0
- package/dist/shadow-element.d.ts +1 -0
- package/dist/shadow-element.js +10 -0
- package/package.json +2 -2
package/dist/nodeOps.d.ts
CHANGED
|
@@ -10,5 +10,13 @@ import type { RendererOptions } from '@sigx/runtime-core/internals';
|
|
|
10
10
|
import { ShadowElement } from './shadow-element.js';
|
|
11
11
|
export type LynxNode = ShadowElement;
|
|
12
12
|
export type LynxElement = ShadowElement;
|
|
13
|
+
/**
|
|
14
|
+
* Test-only: clear the module-level per-element maps above. They are keyed by
|
|
15
|
+
* element id, so a test suite that recycles ids via `resetShadowState()`
|
|
16
|
+
* without this reset would resolve stale slots from earlier tests (and skip
|
|
17
|
+
* pushing SET_EVENT for "already registered" events). Production code never
|
|
18
|
+
* recycles ids, so this is never called outside tests.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resetNodeOpsState(): void;
|
|
13
21
|
export declare function resolveClass(el: ShadowElement): string;
|
|
14
22
|
export declare const nodeOps: RendererOptions<ShadowElement, ShadowElement>;
|
package/dist/nodeOps.js
CHANGED
|
@@ -55,6 +55,18 @@ const elementEventSigns = new Map();
|
|
|
55
55
|
// to bindEvent:tap), we keep one sign in __AddEvent and dispatch to all handlers from
|
|
56
56
|
// a multi-handler wrapper in the BG registry.
|
|
57
57
|
const nativeEventSlots = new Map();
|
|
58
|
+
/**
|
|
59
|
+
* Test-only: clear the module-level per-element maps above. They are keyed by
|
|
60
|
+
* element id, so a test suite that recycles ids via `resetShadowState()`
|
|
61
|
+
* without this reset would resolve stale slots from earlier tests (and skip
|
|
62
|
+
* pushing SET_EVENT for "already registered" events). Production code never
|
|
63
|
+
* recycles ids, so this is never called outside tests.
|
|
64
|
+
*/
|
|
65
|
+
export function resetNodeOpsState() {
|
|
66
|
+
sentWorklets.clear();
|
|
67
|
+
elementEventSigns.clear();
|
|
68
|
+
nativeEventSlots.clear();
|
|
69
|
+
}
|
|
58
70
|
// ---------------------------------------------------------------------------
|
|
59
71
|
// Style normalisation — numeric values → 'Npx' (Lynx requires units)
|
|
60
72
|
// ---------------------------------------------------------------------------
|
|
@@ -229,8 +241,19 @@ export const nodeOps = {
|
|
|
229
241
|
}
|
|
230
242
|
let slot = elSlots.get(nativeKey);
|
|
231
243
|
if (!slot) {
|
|
244
|
+
// Record what the user typed so the `value` patch branch below can
|
|
245
|
+
// tell a model echo apart from a programmatic write (#143).
|
|
246
|
+
const trackInputValue = event.name === 'input'
|
|
247
|
+
&& (el.type === 'input' || el.type === 'textarea');
|
|
232
248
|
// First handler for this native event — register with Lynx.
|
|
233
249
|
const sign = register((data) => {
|
|
250
|
+
if (trackInputValue) {
|
|
251
|
+
const v = data?.detail?.value;
|
|
252
|
+
// Normalize to a string ('' for nullish) — the `value` patch
|
|
253
|
+
// branch compares and stores the same representation, and
|
|
254
|
+
// setValue only ever pushes strings.
|
|
255
|
+
el._lastInputValue = v == null ? '' : String(v);
|
|
256
|
+
}
|
|
234
257
|
// Dispatch to all handlers registered for this slot.
|
|
235
258
|
const s = elSlots.get(nativeKey);
|
|
236
259
|
if (s) {
|
|
@@ -293,6 +316,33 @@ export const nodeOps = {
|
|
|
293
316
|
else if (key === 'id') {
|
|
294
317
|
pushOp(OP.SET_ID, el.id, nextValue);
|
|
295
318
|
}
|
|
319
|
+
else if (key === 'value' && (el.type === 'input' || el.type === 'textarea')) {
|
|
320
|
+
pushOp(OP.SET_PROP, el.id, key, nextValue);
|
|
321
|
+
// The native field treats the `value` attribute as initial-only once
|
|
322
|
+
// the user has edited it — programmatic writes (clear-on-send, editor
|
|
323
|
+
// toolbar inserts) must additionally go through the element's
|
|
324
|
+
// `setValue` UI method or the visible text never changes (#143).
|
|
325
|
+
// Skip during initial mount (`el.parent == null`: props are patched
|
|
326
|
+
// before insertion, and the attribute covers the initial value) — NOT
|
|
327
|
+
// by `_prevValue == null`, which would also skip legitimate post-mount
|
|
328
|
+
// transitions like value={null} → value={'text'}. Also skip the model
|
|
329
|
+
// echo (the re-render caused by the user's own typing, where the new
|
|
330
|
+
// value is exactly what the input event just reported) so cursor/IME
|
|
331
|
+
// composition isn't disturbed while typing.
|
|
332
|
+
// Normalize to a string ('' for nullish) on BOTH sides of the
|
|
333
|
+
// comparison — `value` is typed string on input/textarea but user code
|
|
334
|
+
// can write a number/boolean by mistake, and setValue is a native
|
|
335
|
+
// text-field method that expects a string. Any other representation
|
|
336
|
+
// would desync `_lastInputValue` from the native field and re-invoke
|
|
337
|
+
// redundantly on later renders.
|
|
338
|
+
const next = nextValue == null ? '' : String(nextValue);
|
|
339
|
+
if (el.parent != null && next !== el._lastInputValue) {
|
|
340
|
+
pushOp(OP.INVOKE_UI_METHOD, el.id, 'setValue', { value: next });
|
|
341
|
+
// The programmatic write replaces whatever the user had typed; track
|
|
342
|
+
// it so the next echo comparison stays correct.
|
|
343
|
+
el._lastInputValue = next;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
296
346
|
else {
|
|
297
347
|
pushOp(OP.SET_PROP, el.id, key, nextValue);
|
|
298
348
|
}
|
package/dist/shadow-element.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class ShadowElement {
|
|
|
19
19
|
_vShowHidden: boolean;
|
|
20
20
|
_baseClass: string;
|
|
21
21
|
_transitionClasses: Set<string>;
|
|
22
|
+
_lastInputValue: string | undefined;
|
|
22
23
|
constructor(type: string, forceId?: number);
|
|
23
24
|
insertBefore(child: ShadowElement, anchor: ShadowElement | null): void;
|
|
24
25
|
removeChild(child: ShadowElement): void;
|
package/dist/shadow-element.js
CHANGED
|
@@ -23,6 +23,16 @@ export class ShadowElement {
|
|
|
23
23
|
// Class management for Transition support.
|
|
24
24
|
_baseClass = '';
|
|
25
25
|
_transitionClasses = new Set();
|
|
26
|
+
// Last text known to be in the native <input>/<textarea>: recorded from the
|
|
27
|
+
// native input event by nodeOps, and updated by post-mount programmatic
|
|
28
|
+
// writes that go through the setValue UI method. Used to tell a model echo
|
|
29
|
+
// (signal update caused by typing) apart from a programmatic write
|
|
30
|
+
// (clear-on-send, toolbar insert) — only the latter must be pushed back to
|
|
31
|
+
// the native field. Always stored as a string ('' for nullish, matching
|
|
32
|
+
// what setValue pushes); `undefined` until the first input event or
|
|
33
|
+
// setValue-pushing write (the first-render `value` attribute does NOT
|
|
34
|
+
// initialize it).
|
|
35
|
+
_lastInputValue = undefined;
|
|
26
36
|
constructor(type, forceId) {
|
|
27
37
|
this.id = forceId !== undefined ? forceId : ShadowElement.nextId++;
|
|
28
38
|
this.type = type;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigx/lynx-runtime",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "Lynx renderer for SignalX (background thread)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@sigx/runtime-core": "^0.4.8",
|
|
43
43
|
"@sigx/reactivity": "^0.4.8",
|
|
44
|
-
"@sigx/lynx-runtime-internal": "^0.4.
|
|
44
|
+
"@sigx/lynx-runtime-internal": "^0.4.7"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@lynx-js/types": "*"
|