@srfnstack/fntags 1.3.0 → 1.4.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 +1 -1
- package/package.json +1 -1
- package/src/fntags.d.mts +91 -5
- package/src/fntags.d.mts.map +1 -1
- package/src/fntags.mjs +2279 -564
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ fntags allows you to build complex, interactive web apps using standard JavaScri
|
|
|
13
13
|
## Why fntags?
|
|
14
14
|
|
|
15
15
|
- **No Build Step**: Import the framework directly from a CDN or your file system. No Webpack, no Babel, no headaches.
|
|
16
|
-
- **Granular State**: Bind only what needs to change
|
|
16
|
+
- **Granular State**: Bind only what needs to change (text, attributes, or styles) for high-performance updates.
|
|
17
17
|
- **Standards Based**: Just standard ES6 JavaScript and HTML5. Zero magic syntax to learn.
|
|
18
18
|
- **Effortless Debugging**: In fntags, there is no black box. Errors produce clean stack traces that point exactly to your source code.
|
|
19
19
|
- **TypeScript Support**: Includes TypeScript definitions out of the box. No need to install separate `@types` packages.
|
package/package.json
CHANGED
package/src/fntags.d.mts
CHANGED
|
@@ -13,10 +13,17 @@ export function setDevMode(on: boolean): void;
|
|
|
13
13
|
*
|
|
14
14
|
* Any attribute starting with 'on' that is a function is added as an event listener with the 'on' removed.
|
|
15
15
|
* i.e. { onclick: fn } gets added to the element as element.addEventListener('click', fn)
|
|
16
|
+
* An event attribute can also be an array [fn, arg], which calls fn.call(element, arg, event).
|
|
17
|
+
* For event types passed to delegateEvents, the handler is stored on the element and run by a single
|
|
18
|
+
* listener on document instead (see delegateEvents for how ordering differs).
|
|
16
19
|
*
|
|
17
20
|
* The style attribute can be an object and the properties of the object will be added as style properties to the element.
|
|
18
21
|
* i.e. { style: { color: blue } } becomes element.style.color = blue
|
|
19
22
|
*
|
|
23
|
+
* An attribute value of null, undefined or '' means no attribute: it is removed if present and never written,
|
|
24
|
+
* so { class: '' } produces an element without a class attribute. The value attribute and boolean attributes
|
|
25
|
+
* (disabled, checked, ...) are exempt and keep their property semantics.
|
|
26
|
+
*
|
|
20
27
|
* The rest of the arguments will be considered children of this element and appended to it in the same order as passed.
|
|
21
28
|
*
|
|
22
29
|
* @template {HTMLElement|SVGElement} T
|
|
@@ -31,10 +38,16 @@ export function h<T extends HTMLElement | SVGElement>(tag: string, ...children:
|
|
|
31
38
|
* @typedef FnStateObj A container for a state value that can be bound to.
|
|
32
39
|
* @property {(element?: (newValue: T, oldValue: T)=>(Node|any))=>Node} bindAs Bind this state to the given element function. This causes the element to be replaced when the state changes.
|
|
33
40
|
* If called with no parameters, the state's value will be rendered as an element.
|
|
34
|
-
* Do not wrap form inputs (input, textarea, select) in bindAs
|
|
41
|
+
* Do not wrap form inputs (input, textarea, select) in bindAs: it replaces the element on every update and the focused input will lose focus mid-keystroke. Bind the `value` attribute with bindAttr instead.
|
|
35
42
|
* @property {(parent: (()=>(Node|any))|any|Node, element: (childState: FnState)=>(Node|any))=>Node} bindChildren Bind the values of this state to the given element.
|
|
36
43
|
* Values are items/elements of an array.
|
|
37
44
|
* If the current value is not an array, this will behave the same as bindAs.
|
|
45
|
+
* @property {(parent: (()=>(Node|any))|Node, template: (context: any)=>Node, options?: BindRowsOptions)=>Node} bindRows Bind the values of this state
|
|
46
|
+
* to rows stamped from an fntemplate whose template has a single root element. `values(item, selected)` maps an item to the template's slots and
|
|
47
|
+
* runs at mount, then only when the item's reference changes or the row gains or loses selection; only changed slots are written. Slots named in
|
|
48
|
+
* `handlers` are event slots, called as handler(event, item) with the row's current item through one shared function per name.
|
|
49
|
+
* Rows keep no per-row state or subscriptions, so use this for rows that are a pure function of item and selection, and bindChildren for rows
|
|
50
|
+
* with their own state or nested reactivity. Items are compared by reference: update them immutably.
|
|
38
51
|
* @property {(prop: string)=>Node} bindProp Bind to a property of an object stored in this state instead of the state itself.
|
|
39
52
|
* Shortcut for `mystate.bindAs((current)=> current[prop])`
|
|
40
53
|
* @property {(attribute?: (newValue: T, oldValue: T)=>(string|any))=>any} bindAttr Bind attribute values to state changes.
|
|
@@ -55,6 +68,13 @@ export function h<T extends HTMLElement | SVGElement>(tag: string, ...children:
|
|
|
55
68
|
* @property {(subscriber: (newState: T, oldState: T)=>void) => void} subscribe Register a callback that will be executed whenever the state is changed
|
|
56
69
|
* @property {boolean} isFnState A flag to indicate that this is a fnstate object
|
|
57
70
|
*/
|
|
71
|
+
/**
|
|
72
|
+
* @typedef BindRowsOptions Options for bindRows.
|
|
73
|
+
* @property {(item: any, selected: any)=>Object<string, any>} [values] Map an item and the currently selected key to the template's slot values.
|
|
74
|
+
* Defaults to the item itself. Return a fresh object: it is kept and compared slot by slot against the next result.
|
|
75
|
+
* A slot value may also be a bind getter (bindAs, bindAttr, bindStyle) or a node; bindings created here are cleaned up when values re-runs or the row is removed.
|
|
76
|
+
* @property {Object<string, (event: Event, item: any)=>any>} [handlers] Event handlers by slot name. The named slots must be on* attributes.
|
|
77
|
+
*/
|
|
58
78
|
/**
|
|
59
79
|
* @template T The type of data stored in the state container
|
|
60
80
|
* @typedef {FnStateObj<T> & ((newState?: T)=>T)} FnState A container for a state value that can be bound to.
|
|
@@ -78,6 +98,34 @@ export function fnstate<T>(initialValue: T, mapKey?: (T: any) => any): FnState<T
|
|
|
78
98
|
* @returns {Node} The rendered node
|
|
79
99
|
*/
|
|
80
100
|
export function renderNode(node: any): Node;
|
|
101
|
+
/**
|
|
102
|
+
* Opt in to event delegation for the given event types. After this call, an `on<type>` handler on an element
|
|
103
|
+
* created by h(), or by an fntemplate compiled after this call, is stored on the element as a property
|
|
104
|
+
* (`element.__fnon_<type>`) instead of being added with addEventListener, and one listener per type is added to
|
|
105
|
+
* `document`. This saves a listener, and usually a closure, per element, which adds up in long lists.
|
|
106
|
+
*
|
|
107
|
+
* Semantics and caveats:
|
|
108
|
+
* - Only elements created after the call are delegated. Elements that already exist keep their listeners, and
|
|
109
|
+
* templates compiled before the call keep using addEventListener for every instance.
|
|
110
|
+
* - Delegated handlers run when the event bubbles up to `document`, after every native listener on the target
|
|
111
|
+
* and its ancestors has run. Calling stopPropagation() in a native listener therefore prevents delegated
|
|
112
|
+
* handlers from running, and a delegated handler cannot stop a native listener on an ancestor.
|
|
113
|
+
* - Among delegated handlers the order is innermost first, like native bubbling: the walk follows the event's
|
|
114
|
+
* composed path from the target up to `document`. stopPropagation() (or stopImmediatePropagation()) in a
|
|
115
|
+
* delegated handler stops the walk. Removing an element during dispatch does not shorten the walk, because
|
|
116
|
+
* the path is fixed when the event is dispatched, as it is for native listeners.
|
|
117
|
+
* - Inside a delegated handler `this` and `e.currentTarget` are the element the handler belongs to.
|
|
118
|
+
* - Elements must be connected to the document (directly or through open shadow roots) for handlers to run.
|
|
119
|
+
* - Events that do not bubble (focus, blur, mouseenter, mouseleave, load, scroll, ...) are never delegated:
|
|
120
|
+
* they are skipped with a dev-mode warning and keep using addEventListener.
|
|
121
|
+
* - Delegation cannot be turned off again for a type.
|
|
122
|
+
*
|
|
123
|
+
* Any `on<type>` attribute, delegated or not, also accepts `[fn, arg]`, which calls `fn.call(element, arg, event)`,
|
|
124
|
+
* so every row of a list can share one handler function instead of allocating a closure per row.
|
|
125
|
+
*
|
|
126
|
+
* @param {string|string[]} types the event types to delegate, e.g. ['click', 'input']
|
|
127
|
+
*/
|
|
128
|
+
export function delegateEvents(types: string | string[]): void;
|
|
81
129
|
/**
|
|
82
130
|
* Check if the given value is an object that can be used as attributes
|
|
83
131
|
* @param {any} val The value to check
|
|
@@ -137,9 +185,19 @@ export function hmrRoot(container: HTMLElement, appFn: (() => Node) | Node): HTM
|
|
|
137
185
|
* Create a compiled template function. The returned function takes a single object that contains the properties
|
|
138
186
|
* defined in the template.
|
|
139
187
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
188
|
+
* The template function receives a context function. Calling `ctx('prop')` returns a placeholder that can be used
|
|
189
|
+
* as a child, as an attribute value, or as a style value. The template is rendered once to a dom tree; each
|
|
190
|
+
* instantiation clones that tree and fills the placeholders from the context object, which avoids re-executing
|
|
191
|
+
* the tag functions and can speed up situations where a similar element is created many times.
|
|
192
|
+
*
|
|
193
|
+
* Child placeholders are empty text nodes in the template. A primitive context value is written into the text node
|
|
194
|
+
* in place. A bindAs/bindSelect getter whose current node is a text node adopts the placeholder as its live node,
|
|
195
|
+
* so state changes keep updating the clone. A function value is evaluated like renderNode does. Nodes, arrays and
|
|
196
|
+
* promises replace the placeholder with their rendered node. As with h(), an attribute value of '', null or undefined
|
|
197
|
+
* (including the initial value of a bound attribute) means no attribute.
|
|
198
|
+
*
|
|
199
|
+
* The locations of all placeholders are compiled once into a single firstChild/nextSibling walk over the clone,
|
|
200
|
+
* which stops at the last bound node, so instantiation does not depend on the size of the unbound parts of the tree.
|
|
143
201
|
*
|
|
144
202
|
* You cannot bind state to the initial template. If you attempt to, the state will be read, but the elements will
|
|
145
203
|
* not be updated when the state changes because they will not be bound to the cloned element.
|
|
@@ -157,7 +215,7 @@ export type FnStateObj<T> = {
|
|
|
157
215
|
/**
|
|
158
216
|
* Bind this state to the given element function. This causes the element to be replaced when the state changes.
|
|
159
217
|
* If called with no parameters, the state's value will be rendered as an element.
|
|
160
|
-
* Do not wrap form inputs (input, textarea, select) in bindAs
|
|
218
|
+
* Do not wrap form inputs (input, textarea, select) in bindAs: it replaces the element on every update and the focused input will lose focus mid-keystroke. Bind the `value` attribute with bindAttr instead.
|
|
161
219
|
*/
|
|
162
220
|
bindAs: (element?: (newValue: T, oldValue: T) => (Node | any)) => Node;
|
|
163
221
|
/**
|
|
@@ -166,6 +224,15 @@ export type FnStateObj<T> = {
|
|
|
166
224
|
* If the current value is not an array, this will behave the same as bindAs.
|
|
167
225
|
*/
|
|
168
226
|
bindChildren: (parent: (() => (Node | any)) | any | Node, element: (childState: any) => (Node | any)) => Node;
|
|
227
|
+
/**
|
|
228
|
+
* Bind the values of this state
|
|
229
|
+
* to rows stamped from an fntemplate whose template has a single root element. `values(item, selected)` maps an item to the template's slots and
|
|
230
|
+
* runs at mount, then only when the item's reference changes or the row gains or loses selection; only changed slots are written. Slots named in
|
|
231
|
+
* `handlers` are event slots, called as handler(event, item) with the row's current item through one shared function per name.
|
|
232
|
+
* Rows keep no per-row state or subscriptions, so use this for rows that are a pure function of item and selection, and bindChildren for rows
|
|
233
|
+
* with their own state or nested reactivity. Items are compared by reference: update them immutably.
|
|
234
|
+
*/
|
|
235
|
+
bindRows: (parent: (() => (Node | any)) | Node, template: (context: any) => Node, options?: BindRowsOptions) => Node;
|
|
169
236
|
/**
|
|
170
237
|
* Bind to a property of an object stored in this state instead of the state itself.
|
|
171
238
|
* Shortcut for `mystate.bindAs((current)=> current[prop])`
|
|
@@ -222,6 +289,25 @@ export type FnStateObj<T> = {
|
|
|
222
289
|
*/
|
|
223
290
|
isFnState: boolean;
|
|
224
291
|
};
|
|
292
|
+
/**
|
|
293
|
+
* Options for bindRows.
|
|
294
|
+
*/
|
|
295
|
+
export type BindRowsOptions = {
|
|
296
|
+
/**
|
|
297
|
+
* Map an item and the currently selected key to the template's slot values.
|
|
298
|
+
* Defaults to the item itself. Return a fresh object: it is kept and compared slot by slot against the next result.
|
|
299
|
+
* A slot value may also be a bind getter (bindAs, bindAttr, bindStyle) or a node; bindings created here are cleaned up when values re-runs or the row is removed.
|
|
300
|
+
*/
|
|
301
|
+
values?: (item: any, selected: any) => {
|
|
302
|
+
[x: string]: any;
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* Event handlers by slot name. The named slots must be on* attributes.
|
|
306
|
+
*/
|
|
307
|
+
handlers?: {
|
|
308
|
+
[x: string]: (event: Event, item: any) => any;
|
|
309
|
+
};
|
|
310
|
+
};
|
|
225
311
|
/**
|
|
226
312
|
* A container for a state value that can be bound to.
|
|
227
313
|
*/
|
package/src/fntags.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fntags.d.mts","sourceRoot":"","sources":["fntags.mjs"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fntags.d.mts","sourceRoot":"","sources":["../../../../../../../../Users/r/fntags/.claude/worktrees/agent-a4e6d365c3389d8c1/src/fntags.mjs"],"names":[],"mappings":"AAiCA;;;;;GAKG;AACH,+BAFW,OAAO,QAIjB;AAoBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,2DALW,MAAM,eACH,CAAC,IAAI,MAAO,CAAC,OAM1B;AAuGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH;;;;;;GAMG;AAEH;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,iEAPgB,GAAG,cA0ClB;AA84CD;;;;;GAKG;AACH,iCAHW,GAAG,GACD,IAAI,CAwChB;AA+DD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,sCAFW,MAAM,GAAC,MAAM,EAAE,QAiBzB;AAoHD;;;;GAIG;AACH,6BAHW,GAAG,GACD,OAAO,CAInB;AAED;;;;GAIG;AACH,mCAHW,GAAG,GACF,MAAM,CAIjB;AAED;;;;;;;;;;GAUG;AACH,kEALW,MAAM,GAAC,MAAM,OACb,MAAM,YACN,MAAM,EAAE,GAAC,IAAI,EAAE,KAezB;AAOD;;;;;;;;GAQG;AACH,wCAJW,MAAM,0BAiBhB;AAED;;;;;;;;;GASG;AACH,oCALW,MAAM,gBACN,GAAG,eACE,GAAG,KAAK,MAAM,OAc7B;AAED;;;;;;;;GAQG;AACH,mCAJW,WAAW,SACX,CAAC,MAAI,IAAI,CAAC,GAAC,IAAI,GACb,WAAW,CAWvB;AAsHD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qDAJkB,IAAI,iBACH,IAAI,CA6HtB;;;;;;;;;;kCA9mEmC,CAAC,YAAY,CAAC,KAAG,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;;;2BAG/C,CAAC,MAAI,CAAC,IAAI,GAAC,GAAG,CAAC,CAAC,GAAC,GAAG,GAAC,IAAI,gCAAkC,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;;;;;;uBAG5E,CAAC,MAAI,CAAC,IAAI,GAAC,GAAG,CAAC,CAAC,GAAC,IAAI,sBAAsB,GAAG,KAAG,IAAI,YAAY,eAAe,KAAG,IAAI;;;;;qBAMzF,MAAM,KAAG,IAAI;;;;;sCAEI,CAAC,YAAY,CAAC,KAAG,CAAC,MAAM,GAAC,GAAG,CAAC,KAAG,GAAG;;;;mCAEvC,CAAC,YAAY,CAAC,KAAG,MAAM,KAAK,MAAM;;;;yCAC7B,GAAG,KAAG,CAAC,IAAI,GAAC,GAAG,CAAC,KAAG,IAAI;;;;+CACrB,GAAG,KAAG,CAAC,MAAM,GAAC,GAAG,CAAC,KAAG,GAAG;;;;;;kBAC7C,GAAG,KAAG,IAAI;;;;cAGhB,MAAK,GAAG;;;;;qBACC,CAAC,KAAG,IAAI;;;;;;oBAEV,MAAM,KAAG,GAAG;;;;oBAGZ,MAAM,SAAS,GAAG,mBAAmB,OAAO,KAAG,IAAI;;;;uCAClC,CAAC,YAAY,CAAC,KAAG,IAAI,KAAK,IAAI;;;;eACtD,OAAO;;;;;;;;;;;oBAKA,GAAG,YAAY,GAAG;YAAU,MAAM,GAAE,GAAG;;;;;;6BAGvB,KAAK,QAAQ,GAAG,KAAG,GAAG;;;;;;sDAKjB,CAAC,KAAG,CAAC"}
|