synstate-dom 0.4.0 → 0.6.0
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 +5 -2
- package/dist/bind/bind-props.d.mts +4 -4
- package/dist/bind/bind-props.d.mts.map +1 -1
- package/dist/bind/bind-props.mjs +109 -39
- package/dist/bind/bind-props.mjs.map +1 -1
- package/dist/constant/constant.d.mts +2 -2
- package/dist/constant/derive.mjs.map +1 -1
- package/dist/constant/view.d.mts +3 -3
- package/dist/control/for.d.mts +2 -2
- package/dist/control/for.mjs.map +1 -1
- package/dist/control/if.d.mts +2 -2
- package/dist/control/longest-increasing-subsequence.d.mts +3 -3
- package/dist/control/match-optional.d.mts +4 -4
- package/dist/control/match-result.d.mts +5 -6
- package/dist/control/match-result.d.mts.map +1 -1
- package/dist/control/match-result.mjs +0 -1
- package/dist/control/match-result.mjs.map +1 -1
- package/dist/control/mount-switch.d.mts +5 -5
- package/dist/control/portal.d.mts +2 -2
- package/dist/index.d.mts +2 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/internal/as-constant.d.mts +4 -4
- package/dist/internal/bind-value.d.mts.map +1 -1
- package/dist/internal/bind-value.mjs +7 -8
- package/dist/internal/bind-value.mjs.map +1 -1
- package/dist/internal/dom-tables.d.mts.map +1 -1
- package/dist/internal/dom-tables.mjs +3 -0
- package/dist/internal/dom-tables.mjs.map +1 -1
- package/dist/jsx/child.d.mts +2 -2
- package/dist/jsx/element-props.d.mts +17 -4
- package/dist/jsx/element-props.d.mts.map +1 -1
- package/dist/jsx/jsx.mjs +1 -1
- package/dist/jsx/jsx.mjs.map +1 -1
- package/dist/jsx/mountable.d.mts +4 -4
- package/dist/jsx-dev-runtime.d.mts +2 -2
- package/dist/keyboard/index.d.mts +2 -0
- package/dist/keyboard/index.d.mts.map +1 -0
- package/dist/keyboard/index.mjs +2 -0
- package/dist/keyboard/index.mjs.map +1 -0
- package/dist/keyboard/is-ime-key-down.d.mts +11 -0
- package/dist/keyboard/is-ime-key-down.d.mts.map +1 -0
- package/dist/keyboard/is-ime-key-down.mjs +15 -0
- package/dist/keyboard/is-ime-key-down.mjs.map +1 -0
- package/dist/scope/index.d.mts +2 -0
- package/dist/scope/index.d.mts.map +1 -0
- package/dist/scope/index.mjs +2 -0
- package/dist/scope/index.mjs.map +1 -0
- package/dist/scope/scope-effects.d.mts +25 -0
- package/dist/scope/scope-effects.d.mts.map +1 -0
- package/dist/scope/scope-effects.mjs +43 -0
- package/dist/scope/scope-effects.mjs.map +1 -0
- package/package.json +9 -9
- package/src/bind/bind-props.mts +169 -66
- package/src/constant/constant.mts +2 -2
- package/src/constant/derive.mts +4 -4
- package/src/constant/view.mts +3 -3
- package/src/control/for.mts +10 -10
- package/src/control/if.mts +2 -2
- package/src/control/longest-increasing-subsequence.mts +3 -3
- package/src/control/match-optional.mts +4 -4
- package/src/control/match-result.mts +5 -6
- package/src/control/mount-switch.mts +5 -5
- package/src/control/portal.mts +2 -2
- package/src/index.mts +2 -0
- package/src/internal/as-constant.mts +4 -4
- package/src/internal/bind-value.mts +7 -13
- package/src/internal/dom-tables.mts +3 -0
- package/src/jsx/child.mts +2 -2
- package/src/jsx/element-props.mts +41 -4
- package/src/jsx/jsx.mts +5 -5
- package/src/jsx/mountable.mts +4 -4
- package/src/jsx-dev-runtime.mts +2 -2
- package/src/keyboard/index.mts +1 -0
- package/src/keyboard/is-ime-key-down.mts +20 -0
- package/src/keyboard/is-ime-key-down.test.mts +42 -0
- package/src/scope/index.mts +1 -0
- package/src/scope/scope-effects.mts +60 -0
- package/src/scope/scope-effects.test.mts +89 -0
|
@@ -29,8 +29,8 @@ export type ClassObject = ReadonlyRecord<string, Bindable<boolean>>;
|
|
|
29
29
|
export type StyleObject = ReadonlyRecord<string, Bindable<string | undefined>>;
|
|
30
30
|
/**
|
|
31
31
|
* The props of an HTML element: the common ones, one `Bindable` per writable
|
|
32
|
-
* primitive DOM property of `E` (`id`, `disabled`, `value`, `tabIndex`, …),
|
|
33
|
-
*
|
|
32
|
+
* primitive DOM property of `E` (`id`, `disabled`, `value`, `tabIndex`, …), and
|
|
33
|
+
* one handler per event of `HTMLElementEventMap`.
|
|
34
34
|
*
|
|
35
35
|
* An attribute, `aria-*` and `data-*` included, also takes `undefined`, which
|
|
36
36
|
* removes it: a value that comes and goes is an observable of `T | undefined`
|
|
@@ -71,8 +71,21 @@ type ReflectedKey<E, K extends keyof E> = string extends K ? never : number exte
|
|
|
71
71
|
type IsWritable<E, K extends keyof E> = TypeEq<StrictPick<E, K>, Readonly<StrictPick<E, K>>> extends true ? false : true;
|
|
72
72
|
/**
|
|
73
73
|
* Writable primitive properties that are not attributes, or that are props of
|
|
74
|
-
* their own (`class`, `style`).
|
|
74
|
+
* their own (`class`, `style`). The binding would pass them to `setAttribute`,
|
|
75
|
+
* which makes an attribute nothing reads (#2934): the parts of `<a>`'s `href`
|
|
76
|
+
* (`hash` … `username`), the text of `<a>`, `<option>`, `<script>` and
|
|
77
|
+
* `<title>` (`text`), media playback state (`currentTime` … `volume`), and form
|
|
78
|
+
* control state (`returnValue` … `length`), and the obsolete `ch` and `chOff`
|
|
79
|
+
* of table cells, whose attributes are `char` and `charoff`. A property written as a property is
|
|
80
|
+
* in `propertyNames`, and one whose attribute is spelt differently in
|
|
81
|
+
* `attributeNameOf`. Checked in Chromium by setting the attribute and reading
|
|
82
|
+
* the property back.
|
|
83
|
+
*
|
|
84
|
+
* The keys are left out of every element, so a name that is an attribute on one
|
|
85
|
+
* element and not on another goes by the common case: `<body>`'s obsolete
|
|
86
|
+
* `text` attribute is left out with the rest, and `<style>`'s `disabled`, which
|
|
87
|
+
* has no attribute, stays in with every other `disabled`.
|
|
75
88
|
*/
|
|
76
|
-
type ExcludedKey = 'className' | 'innerHTML' | 'innerText' | 'nodeValue' | 'outerHTML' | 'outerText' | 'scrollLeft' | 'scrollTop' | 'textContent';
|
|
89
|
+
type ExcludedKey = 'className' | 'innerHTML' | 'innerText' | 'nodeValue' | 'outerHTML' | 'outerText' | 'scrollLeft' | 'scrollTop' | 'textContent' | 'hash' | 'host' | 'hostname' | 'password' | 'pathname' | 'port' | 'protocol' | 'search' | 'username' | 'text' | 'currentTime' | 'defaultPlaybackRate' | 'playbackRate' | 'preservesPitch' | 'volume' | 'returnValue' | 'selectedIndex' | 'selectionDirection' | 'selectionEnd' | 'selectionStart' | 'valueAsNumber' | 'length' | 'ch' | 'chOff';
|
|
77
90
|
export {};
|
|
78
91
|
//# sourceMappingURL=element-props.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element-props.d.mts","sourceRoot":"","sources":["../../src/jsx/element-props.mts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"element-props.d.mts","sourceRoot":"","sources":["../../src/jsx/element-props.mts"],"names":[],"mappings":"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"}
|
package/dist/jsx/jsx.mjs
CHANGED
|
@@ -23,6 +23,7 @@ export const createMountable = (type, props, key) => {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
export const Fragment = (props) => mountable(() => mountChild(props.children ?? []));
|
|
26
|
+
const isRefCallback = (value) => typeof value === 'function';
|
|
26
27
|
const mountElement = (tag, props) => {
|
|
27
28
|
const element = svgTagNameSet.has(tag)
|
|
28
29
|
? document.createElementNS(svgNamespace, tag)
|
|
@@ -42,5 +43,4 @@ const mountElement = (tag, props) => {
|
|
|
42
43
|
};
|
|
43
44
|
const svgNamespace = 'http://www.w3.org/2000/svg';
|
|
44
45
|
const svgTagNameSet = new Set(svgTagNames);
|
|
45
|
-
const isRefCallback = (value) => typeof value === 'function';
|
|
46
46
|
//# sourceMappingURL=jsx.mjs.map
|
package/dist/jsx/jsx.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsx.mjs","sourceRoot":"","sources":["../../src/jsx/jsx.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAkB,MAAM,iBAAiB,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAiD,EACjD,KAAQ,EACR,GAAa,EACF,EAAE;IACb,
|
|
1
|
+
{"version":3,"file":"jsx.mjs","sourceRoot":"","sources":["../../src/jsx/jsx.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAkB,MAAM,iBAAiB,CAAC;AAE5D;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAiD,EACjD,KAAQ,EACR,GAAa,EACF,EAAE;IACb,6EAA6E;IAC7E,sEAAsE;IACtE,MAAM,YAAY,GAAG,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAY,CAAC;IAE9E,OAAO,OAAO,IAAI,KAAK,QAAQ;QAC7B,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAqC,EAAa,EAAE,CAC3E,SAAS,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;AAEpD,MAAM,aAAa,GAAG,CAAC,KAAc,EAAuC,EAAE,CAC5E,OAAO,KAAK,KAAK,UAAU,CAAC;AAE9B,MAAM,YAAY,GAAG,CAAC,GAAW,EAAE,KAAc,EAAW,EAAE;IAC5D,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QACpC,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,YAAY,EAAE,GAAG,CAAC;QAC7C,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAEhC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACrB,CAAC;IAED,wEAAwE;IACxE,IAAI,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChD,CAAC;IAED,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE1B,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAElD,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC"}
|
package/dist/jsx/mountable.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mountableTag } from '../internal/index.mjs';
|
|
2
2
|
/**
|
|
3
|
-
* What a JSX expression evaluates to: a description that creates DOM nodes
|
|
4
|
-
*
|
|
3
|
+
* What a JSX expression evaluates to: a description that creates DOM nodes when
|
|
4
|
+
* mounted, not the nodes themselves (SPEC.md 6.1).
|
|
5
5
|
*
|
|
6
6
|
* The children of a JSX element are evaluated before the element, so a
|
|
7
7
|
* description that created its nodes at once would create both branches of
|
|
@@ -10,8 +10,8 @@ import { mountableTag } from '../internal/index.mjs';
|
|
|
10
10
|
* It is an object rather than a function: a props object with a `then` prop
|
|
11
11
|
* holding a function would be taken for a thenable.
|
|
12
12
|
*
|
|
13
|
-
* The same description may be mounted any number of times; each mount
|
|
14
|
-
*
|
|
13
|
+
* The same description may be mounted any number of times; each mount creates
|
|
14
|
+
* new nodes.
|
|
15
15
|
*/
|
|
16
16
|
export type Mountable = Readonly<{
|
|
17
17
|
[mountableTag]: true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The module `"jsxImportSource": "synstate-dom"` makes TypeScript import for
|
|
3
|
-
* `"jsx": "react-jsxdev"`. The extra arguments (`key`, source location) are
|
|
4
|
-
*
|
|
3
|
+
* `"jsx": "react-jsxdev"`. The extra arguments (`key`, source location) are not
|
|
4
|
+
* used.
|
|
5
5
|
*/
|
|
6
6
|
export { Fragment, createMountable as jsxDEV, type JSX } from './jsx/index.mjs';
|
|
7
7
|
//# sourceMappingURL=jsx-dev-runtime.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/keyboard/index.mts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/keyboard/index.mts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { StrictPick } from 'ts-type-forge';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a key down is an input method's rather than the field's: one sent
|
|
4
|
+
* while a composition is going on, or the Enter or Escape that confirmed or
|
|
5
|
+
* cancelled one, which Safari sends after `compositionend`, told from the
|
|
6
|
+
* field's by its key code alone. Other keys with that key code are the field's:
|
|
7
|
+
* a virtual keyboard on Android sends it for the keys that type text, and its
|
|
8
|
+
* arrow keys may carry it too.
|
|
9
|
+
*/
|
|
10
|
+
export declare const isImeKeyDown: (event: StrictPick<KeyboardEvent, 'isComposing' | 'key' | 'keyCode'>) => boolean;
|
|
11
|
+
//# sourceMappingURL=is-ime-key-down.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-ime-key-down.d.mts","sourceRoot":"","sources":["../../src/keyboard/is-ime-key-down.mts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,UAChB,UAAU,CAAC,aAAa,EAAE,aAAa,GAAG,KAAK,GAAG,SAAS,CAAC,KAClE,OAI8B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-ime-key-down.mjs","sourceRoot":"","sources":["../../src/keyboard/is-ime-key-down.mts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,KAAmE,EAC1D,EAAE,CACX,KAAK,CAAC,WAAW;IACjB,CAAC,CAAC,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC;QAChD,6GAA6G;QAC7G,KAAK,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC;AAElC,6DAA6D;AAC7D,MAAM,UAAU,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/scope/index.mts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/scope/index.mts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Observable } from 'synstate';
|
|
2
|
+
import type { StrictPick } from 'ts-type-forge';
|
|
3
|
+
/**
|
|
4
|
+
* Calls `next` with every value of `source`, the current one first, until the
|
|
5
|
+
* current scope is disposed — what a component that is called once does in
|
|
6
|
+
* place of an effect.
|
|
7
|
+
*/
|
|
8
|
+
export declare const subscribeInScope: <A>(source: Observable<A>, next: (value: A) => void) => void;
|
|
9
|
+
/** Listens to `type` on `target` until the current scope is disposed. */
|
|
10
|
+
export declare const listenInScope: (target: Readonly<StrictPick<EventTarget, 'addEventListener' | 'removeEventListener'>>, type: string, listener: (event: Event) => void, options?: Readonly<{
|
|
11
|
+
capture?: boolean;
|
|
12
|
+
passive?: boolean;
|
|
13
|
+
}>) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Makes the function that runs work once the update under way has finished:
|
|
16
|
+
* in a microtask, so before the next paint, with the DOM bound to the update
|
|
17
|
+
* already written — and nothing written to a state while another is still
|
|
18
|
+
* propagating, which synstate warns of. Work for a scope disposed by then is
|
|
19
|
+
* dropped.
|
|
20
|
+
*
|
|
21
|
+
* Also what runs after the first mount: a component's nodes are attached to the
|
|
22
|
+
* document only once the whole description they are in is mounted.
|
|
23
|
+
*/
|
|
24
|
+
export declare const createAfterUpdate: () => ((work: () => void) => void);
|
|
25
|
+
//# sourceMappingURL=scope-effects.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-effects.d.mts","sourceRoot":"","sources":["../../src/scope/scope-effects.mts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,UACxB,UAAU,CAAC,CAAC,CAAC,QACf,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KACvB,IAMF,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,aAAa,WAChB,QAAQ,CACd,UAAU,CAAC,WAAW,EAAE,kBAAkB,GAAG,qBAAqB,CAAC,CACpE,QACK,MAAM,YACF,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,YACtB,QAAQ,CAAC;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,KAC3D,IAMF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAAO,CAAC,CAAC,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,CAc/D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { onScopeDispose } from 'synstate';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const subscribeInScope = (source, next) => {
|
|
8
|
+
const subscription = source.subscribe(next);
|
|
9
|
+
onScopeDispose(() => {
|
|
10
|
+
subscription.unsubscribe();
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const listenInScope = (target, type, listener, options) => {
|
|
15
|
+
target.addEventListener(type, listener, options);
|
|
16
|
+
onScopeDispose(() => {
|
|
17
|
+
target.removeEventListener(type, listener, options);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
export const createAfterUpdate = () => {
|
|
31
|
+
let mut_alive = true;
|
|
32
|
+
onScopeDispose(() => {
|
|
33
|
+
mut_alive = false;
|
|
34
|
+
});
|
|
35
|
+
return (work) => {
|
|
36
|
+
queueMicrotask(() => {
|
|
37
|
+
if (mut_alive) {
|
|
38
|
+
work();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=scope-effects.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-effects.mjs","sourceRoot":"","sources":["../../src/scope/scope-effects.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAmB,MAAM,UAAU,CAAC;AAG3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,MAAqB,EACrB,IAAwB,EAClB,EAAE;IACR,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAE5C,cAAc,CAAC,GAAG,EAAE;QAClB,YAAY,CAAC,WAAW,EAAE,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,MAEC,EACD,IAAY,EACZ,QAAgC,EAChC,OAA4D,EACtD,EAAE;IACR,MAAM,CAAC,gBAAgB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEjD,cAAc,CAAC,GAAG,EAAE;QAClB,MAAM,CAAC,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAiC,EAAE;IAClE,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,cAAc,CAAC,GAAG,EAAE;QAClB,SAAS,GAAG,KAAK,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,EAAE,EAAE;QACd,cAAc,CAAC,GAAG,EAAE;YAClB,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,EAAE,CAAC;YACT,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "synstate-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Compiler-free fine-grained UI library that binds synstate observables directly to the DOM",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -44,21 +44,21 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@vitest/browser-playwright": "5.0.5",
|
|
47
|
-
"eslint-config-typed": "
|
|
48
|
-
"eslint-plugin-ts-data-forge": "0.10.
|
|
49
|
-
"eslint-plugin-ts-fortress": "1.3.
|
|
50
|
-
"eslint-plugin-ts-type-forge": "0.8.
|
|
47
|
+
"eslint-config-typed": "7.3.1",
|
|
48
|
+
"eslint-plugin-ts-data-forge": "0.10.8",
|
|
49
|
+
"eslint-plugin-ts-fortress": "1.3.12",
|
|
50
|
+
"eslint-plugin-ts-type-forge": "0.8.17",
|
|
51
51
|
"playwright": "1.64.1",
|
|
52
52
|
"prettier": "3.9.10",
|
|
53
53
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
54
54
|
"prettier-plugin-packagejson": "3.0.2",
|
|
55
|
-
"synstate": "6.1
|
|
56
|
-
"ts-data-forge": "14.10.
|
|
55
|
+
"synstate": "6.2.1",
|
|
56
|
+
"ts-data-forge": "14.10.2",
|
|
57
57
|
"ts-repo-utils": "10.7.1"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
|
-
"synstate": ">=6.1
|
|
61
|
-
"ts-data-forge": ">=14.10.
|
|
60
|
+
"synstate": ">=6.2.1",
|
|
61
|
+
"ts-data-forge": ">=14.10.2"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=22.23.4",
|
package/src/bind/bind-props.mts
CHANGED
|
@@ -8,12 +8,13 @@ import {
|
|
|
8
8
|
propertyNames,
|
|
9
9
|
viewTag,
|
|
10
10
|
} from '../internal/index.mjs';
|
|
11
|
+
import { listenInScope } from '../scope/index.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* Binds every prop of an intrinsic element except `ref` and `children`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Binds every prop of an intrinsic element except `ref` and `children` (SPEC.md
|
|
15
|
+
* 7): `class`, `style`, event handlers, the DOM properties of `propertyNames`,
|
|
16
|
+
* and attributes for the rest. Subscriptions and listeners are released when
|
|
17
|
+
* the current scope is disposed.
|
|
17
18
|
*/
|
|
18
19
|
export const bindProps = (
|
|
19
20
|
element: Element,
|
|
@@ -24,43 +25,42 @@ export const bindProps = (
|
|
|
24
25
|
}
|
|
25
26
|
};
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (key === 'style') {
|
|
39
|
-
bindStyle(element, value);
|
|
28
|
+
/**
|
|
29
|
+
* `true` writes an empty attribute, and `false`, `undefined` and `null` remove
|
|
30
|
+
* it.
|
|
31
|
+
*/
|
|
32
|
+
const writeAttribute = (
|
|
33
|
+
element: Element,
|
|
34
|
+
name: string,
|
|
35
|
+
value: unknown,
|
|
36
|
+
): void => {
|
|
37
|
+
if (isBoolean(value)) {
|
|
38
|
+
element.toggleAttribute(name, value);
|
|
40
39
|
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
if (
|
|
45
|
-
|
|
43
|
+
if (value === undefined || value === null) {
|
|
44
|
+
element.removeAttribute(name);
|
|
46
45
|
|
|
47
46
|
return;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Reflect.set(element, key, v);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
49
|
+
element.setAttribute(name, unknownToString(value));
|
|
50
|
+
};
|
|
57
51
|
|
|
58
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Every value is a `Bindable` at run time: what is neither an observable nor a
|
|
54
|
+
* view is a constant. An omitted prop is `undefined`, which is not.
|
|
55
|
+
*/
|
|
56
|
+
const isBindable = (value: unknown): value is Bindable<unknown> =>
|
|
57
|
+
value !== undefined && value !== null;
|
|
59
58
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
/** `class` and `style` in their object form: neither an observable nor a view. */
|
|
60
|
+
const isPlainObject = (
|
|
61
|
+
value: unknown,
|
|
62
|
+
): value is ReadonlyRecord<string, unknown> =>
|
|
63
|
+
isRecord(value) && !isObservable(value) && !hasKey(value, viewTag);
|
|
64
64
|
|
|
65
65
|
const bindClass = (element: Element, value: Bindable<unknown>): void => {
|
|
66
66
|
if (!isPlainObject(value)) {
|
|
@@ -116,59 +116,162 @@ const bindStyle = (element: Element, value: Bindable<unknown>): void => {
|
|
|
116
116
|
}
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
):
|
|
124
|
-
|
|
119
|
+
/**
|
|
120
|
+
* The radio buttons of `input`'s group: those of the same `name` and form in
|
|
121
|
+
* the same document or shadow root. Any other input is a group of its own.
|
|
122
|
+
*/
|
|
123
|
+
const radioGroupOf = (input: HTMLInputElement): readonly HTMLInputElement[] => {
|
|
124
|
+
const root = input.getRootNode();
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
input.type !== 'radio' ||
|
|
128
|
+
input.name === '' ||
|
|
129
|
+
!(root instanceof Document || root instanceof ShadowRoot)
|
|
130
|
+
) {
|
|
131
|
+
return [input];
|
|
132
|
+
}
|
|
125
133
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
return Array.from(
|
|
135
|
+
root.querySelectorAll<HTMLInputElement>(
|
|
136
|
+
`input[type="radio"][name="${CSS.escape(input.name)}"]`,
|
|
137
|
+
),
|
|
138
|
+
).filter((radio) => radio.form === input.form);
|
|
129
139
|
};
|
|
130
140
|
|
|
131
141
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
142
|
+
* The controlled properties of an element, created with the `change` listener
|
|
143
|
+
* that sets them back. The listener is a capturing one, which runs before the
|
|
144
|
+
* element's own handlers, so no handler on the element can stop it.
|
|
134
145
|
*/
|
|
135
|
-
const
|
|
136
|
-
element
|
|
137
|
-
name: string,
|
|
138
|
-
value: unknown,
|
|
139
|
-
): void => {
|
|
140
|
-
if (isBoolean(value)) {
|
|
141
|
-
element.toggleAttribute(name, value);
|
|
146
|
+
const controlledOf = (element: HTMLInputElement): Controlled => {
|
|
147
|
+
const existing = controlled.get(element);
|
|
142
148
|
|
|
143
|
-
|
|
149
|
+
if (existing !== undefined) {
|
|
150
|
+
return existing;
|
|
144
151
|
}
|
|
145
152
|
|
|
146
|
-
|
|
147
|
-
|
|
153
|
+
const mut_restorers = new Map<ControlledKey, () => void>();
|
|
154
|
+
|
|
155
|
+
const created: Controlled = {
|
|
156
|
+
add: (key, restore) => {
|
|
157
|
+
mut_restorers.set(key, restore);
|
|
158
|
+
},
|
|
159
|
+
restore: () => {
|
|
160
|
+
for (const restore of mut_restorers.values()) {
|
|
161
|
+
restore();
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
} as const;
|
|
165
|
+
|
|
166
|
+
const onChange = (): void => {
|
|
167
|
+
requestAnimationFrame(() => {
|
|
168
|
+
for (const input of radioGroupOf(element)) {
|
|
169
|
+
controlled.get(input)?.restore();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
};
|
|
148
173
|
|
|
149
|
-
|
|
150
|
-
}
|
|
174
|
+
controlled.set(element, created);
|
|
151
175
|
|
|
152
|
-
element
|
|
153
|
-
};
|
|
176
|
+
listenInScope(element, 'change', onChange, { capture: true });
|
|
154
177
|
|
|
155
|
-
|
|
178
|
+
onScopeDispose(() => {
|
|
179
|
+
controlled.delete(element);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return created;
|
|
183
|
+
};
|
|
156
184
|
|
|
157
185
|
/**
|
|
158
|
-
*
|
|
159
|
-
*
|
|
186
|
+
* `checked` and `indeterminate` are controlled (SPEC.md 7.2): after a `change`,
|
|
187
|
+
* the element is drawn as the bound values again, so a change its owner did not
|
|
188
|
+
* take undoes itself. This waits for the next frame, after every handler of the
|
|
189
|
+
* event, the ancestors' included, has seen the user's change, and before it is
|
|
190
|
+
* painted. Checking a radio button unchecks the others of its group with no
|
|
191
|
+
* event on them, so a change on one sets back every bound button of the group.
|
|
160
192
|
*/
|
|
161
|
-
const
|
|
162
|
-
|
|
193
|
+
const bindControlled = (
|
|
194
|
+
element: HTMLInputElement,
|
|
195
|
+
key: ControlledKey,
|
|
196
|
+
value: Bindable<unknown>,
|
|
197
|
+
): void => {
|
|
198
|
+
const mut_bound: { current: unknown } = { current: undefined };
|
|
163
199
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
bindValue(value, (v) => {
|
|
201
|
+
mut_bound.current = v;
|
|
202
|
+
|
|
203
|
+
Reflect.set(element, key, v);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
controlledOf(element).add(key, () => {
|
|
207
|
+
if (Reflect.get(element, key) !== mut_bound.current) {
|
|
208
|
+
Reflect.set(element, key, mut_bound.current);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
type ControlledKey = 'checked' | 'indeterminate';
|
|
214
|
+
|
|
215
|
+
type Controlled = Readonly<{
|
|
216
|
+
add: (key: ControlledKey, restore: () => void) => void;
|
|
217
|
+
/** Writes every bound property back to the element. */
|
|
218
|
+
restore: () => void;
|
|
219
|
+
}>;
|
|
220
|
+
|
|
221
|
+
const isControlledKey = (key: string): key is ControlledKey =>
|
|
222
|
+
key === 'checked' || key === 'indeterminate';
|
|
223
|
+
|
|
224
|
+
/** The elements bound by {@link bindControlled}. */
|
|
225
|
+
const controlled = new WeakMap<HTMLInputElement, Controlled>();
|
|
169
226
|
|
|
170
227
|
/** `onClick`, `onPointerDown`: `on` and an upper-case letter. */
|
|
171
228
|
const isEventKey = (key: string): boolean => /^on[A-Z]/u.test(key);
|
|
172
229
|
|
|
173
230
|
const isEventHandler = (value: unknown): value is EventListener =>
|
|
174
231
|
typeof value === 'function';
|
|
232
|
+
|
|
233
|
+
const bindProp = (element: Element, key: string, value: unknown): void => {
|
|
234
|
+
if (key === 'children' || key === 'ref' || !isBindable(value)) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (key === 'class') {
|
|
239
|
+
bindClass(element, value);
|
|
240
|
+
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (key === 'style') {
|
|
245
|
+
bindStyle(element, value);
|
|
246
|
+
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (isEventKey(key) && isEventHandler(value)) {
|
|
251
|
+
listenInScope(element, key.slice(2).toLowerCase(), value);
|
|
252
|
+
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (isControlledKey(key) && element instanceof HTMLInputElement) {
|
|
257
|
+
bindControlled(element, key, value);
|
|
258
|
+
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (propertyNameSet.has(key)) {
|
|
263
|
+
bindValue(value, (v) => {
|
|
264
|
+
Reflect.set(element, key, v);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const name = attributeNameOf[key] ?? key;
|
|
271
|
+
|
|
272
|
+
bindValue(value, (v) => {
|
|
273
|
+
writeAttribute(element, name, v);
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const propertyNameSet: ReadonlySet<string> = new Set(propertyNames);
|
|
@@ -6,8 +6,8 @@ import { asConstant } from '../internal/index.mjs';
|
|
|
6
6
|
* A value that is written once and never updated. A brand that exists only in
|
|
7
7
|
* the type system: at run time it is the value itself.
|
|
8
8
|
*
|
|
9
|
-
* A slot that takes data accepts a `Constant<T>` but not a plain `T`, so that
|
|
10
|
-
*
|
|
9
|
+
* A slot that takes data accepts a `Constant<T>` but not a plain `T`, so that a
|
|
10
|
+
* value read once and never updated (`count$.getSnapshot().value`) is a type
|
|
11
11
|
* error rather than a binding that silently stays stale.
|
|
12
12
|
*/
|
|
13
13
|
export type Constant<T> = Brand<T, 'synstate-dom/Constant'>;
|
package/src/constant/derive.mts
CHANGED
|
@@ -22,10 +22,10 @@ export const derive = <const BS extends NonEmptyTuple<Bindable<unknown>>, R>(
|
|
|
22
22
|
bindables: BS,
|
|
23
23
|
fn: (...values: BindableValues<BS>) => R,
|
|
24
24
|
): Bindable<R> => {
|
|
25
|
-
// `fn` is called with the value of each of `bindables`, position by
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
25
|
+
// `fn` is called with the value of each of `bindables`, position by position:
|
|
26
|
+
// the constants themselves, or their observables' values. The checker cannot
|
|
27
|
+
// follow a position through `Arr.map`, which carries one element type for the
|
|
28
|
+
// whole tuple.
|
|
29
29
|
// eslint-disable-next-line total-functions/no-unsafe-type-assertion -- see above
|
|
30
30
|
const project = fn as (...values: readonly unknown[]) => R;
|
|
31
31
|
|
package/src/constant/view.mts
CHANGED
|
@@ -5,9 +5,9 @@ import { viewTag } from '../internal/index.mjs';
|
|
|
5
5
|
* An observable and a function to apply to each of its values, bound without
|
|
6
6
|
* creating a node in the graph. Made by {@link view}.
|
|
7
7
|
*
|
|
8
|
-
* The source's value type is hidden behind `open`, which hands the pair back
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The source's value type is hidden behind `open`, which hands the pair back to
|
|
9
|
+
* a callback that is generic in it: whoever opens a view sees a source and a
|
|
10
|
+
* function that agree with each other, with no assertion on either side.
|
|
11
11
|
*/
|
|
12
12
|
export type View<T> = Readonly<{
|
|
13
13
|
[viewTag]: true;
|