@srfnstack/fntags 1.2.8 → 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 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—text, attributes, or styles—for high-performance updates.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srfnstack/fntags",
3
- "version": "1.2.8",
3
+ "version": "1.4.0",
4
4
  "author": "Robert Kempton <r@snow87.com>",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/srfnstack/fntags",
package/src/fnroute.d.mts CHANGED
@@ -40,13 +40,25 @@ export function routeSwitch(...children: (any | Node)[]): Node | (() => Node);
40
40
  */
41
41
  export function fnlink(...children: (any | Node)[]): HTMLAnchorElement;
42
42
  /**
43
- * A function to navigate to the specified route
43
+ * A function to navigate to the specified route.
44
+ *
45
+ * Navigating to the current path with only a different hash or query (for example from /docs to /docs#faq)
46
+ * and no new context is not a route change: the history entry is updated and the hash target is scrolled
47
+ * into view, but the route is not re-rendered and no route events are emitted.
44
48
  * @param {string} route The route to navigate to
45
- * @param {any} context Data related to the route change
49
+ * @param {any} [context] Data related to the route change
46
50
  * @param {boolean} replace Whether to replace the state or push it. pushState is used by default.
47
51
  * @param {boolean} silent Prevent route change events from being emitted for this route change
48
52
  */
49
53
  export function goTo(route: string, context?: any, replace?: boolean, silent?: boolean): void;
54
+ /**
55
+ * Scroll the element whose id matches the hash into view. If the element does not exist yet, for example because
56
+ * the route's content is a promise that has not resolved, wait for it to be added to the document and scroll then.
57
+ * With no hash, scroll to the top of the page.
58
+ * @param {string|null|undefined} hash The hash to scroll to, with or without the leading #
59
+ * @param {number} [timeout] How long to wait for the element to appear, in milliseconds
60
+ */
61
+ export function scrollToHash(hash: string | null | undefined, timeout?: number): void;
50
62
  /**
51
63
  * Listen for routing events
52
64
  * @param {string} event a string event to listen for
@@ -1 +1 @@
1
- {"version":3,"file":"fnroute.d.mts","sourceRoot":"","sources":["fnroute.mjs"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,mCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,cAAc,CAoB1B;AAED;;;;;GAKG;AACH,yCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,IAAI,GAAC,CAAC,MAAI,IAAI,CAAC,CAyB3B;AAoBD;;;;GAIG;AACH,oCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,iBAAiB,CAuB7B;AAED;;;;;;GAMG;AACH,4BALW,MAAM,YACN,GAAG,YACH,OAAO,WACP,OAAO,QA4CjB;AAiED;;;;;;GAMG;AACH,iCALW,MAAM,iBAGL,MAAI,IAAI,CAanB;AAED;;;GAGG;AACH,sCAFW,MAAM,QAOhB;AAxFD;;;GAGG;AAEH;;;GAGG;AACH,6BAFU,OAAO,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,CAEf;AAEzC;;;GAGG;AAEH;;;GAGG;AACH,wBAFU,OAAO,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,CAO/C;AAMJ;;GAEG;AACH;;;GAGG;AACH,gCAFU,UAAU,CAEgC;AACpD;;;GAGG;AACH,+BAFU,UAAU,CAE8B;AAClD;;;GAGG;AACH,kCAFU,UAAU,CAEoC;;;;;;;;wBAnC3C;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAC;yBAmBrD,MAAM"}
1
+ {"version":3,"file":"fnroute.d.mts","sourceRoot":"","sources":["fnroute.mjs"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,mCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,cAAc,CAoB1B;AAED;;;;;GAKG;AACH,yCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,IAAI,GAAC,CAAC,MAAI,IAAI,CAAC,CAyB3B;AAoBD;;;;GAIG;AACH,oCAHW,CAAC,MAAO,IAAI,CAAC,EAAE,GACb,iBAAiB,CAuB7B;AAED;;;;;;;;;;GAUG;AACH,4BALW,MAAM,YACN,GAAG,YACH,OAAO,WACP,OAAO,QA+CjB;AAID;;;;;;GAMG;AACH,mCAHW,MAAM,GAAC,IAAI,GAAC,SAAS,YACrB,MAAM,QAiChB;AAiED;;;;;;GAMG;AACH,iCALW,MAAM,iBAGL,MAAI,IAAI,CAanB;AAED;;;GAGG;AACH,sCAFW,MAAM,QAOhB;AAxFD;;;GAGG;AAEH;;;GAGG;AACH,6BAFU,OAAO,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,CAEf;AAEzC;;;GAGG;AAEH;;;GAGG;AACH,wBAFU,OAAO,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,CAO/C;AAMJ;;GAEG;AACH;;;GAGG;AACH,gCAFU,UAAU,CAEgC;AACpD;;;GAGG;AACH,+BAFU,UAAU,CAE8B;AAClD;;;GAGG;AACH,kCAFU,UAAU,CAEoC;;;;;;;;wBAnC3C;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAC;yBAmBrD,MAAM"}
package/src/fnroute.mjs CHANGED
@@ -106,7 +106,7 @@ function updatePathParameters () {
106
106
  * @returns {HTMLAnchorElement} An anchor element that will navigate to the specified route when clicked
107
107
  */
108
108
  export function fnlink (...children) {
109
- let context = null
109
+ let context
110
110
  if (children[0] && children[0].context) {
111
111
  context = children[0].context
112
112
  }
@@ -129,21 +129,36 @@ export function fnlink (...children) {
129
129
  }
130
130
 
131
131
  /**
132
- * A function to navigate to the specified route
132
+ * A function to navigate to the specified route.
133
+ *
134
+ * Navigating to the current path with only a different hash or query (for example from /docs to /docs#faq)
135
+ * and no new context is not a route change: the history entry is updated and the hash target is scrolled
136
+ * into view, but the route is not re-rendered and no route events are emitted.
133
137
  * @param {string} route The route to navigate to
134
- * @param {any} context Data related to the route change
138
+ * @param {any} [context] Data related to the route change
135
139
  * @param {boolean} replace Whether to replace the state or push it. pushState is used by default.
136
140
  * @param {boolean} silent Prevent route change events from being emitted for this route change
137
141
  */
138
- export function goTo (route, context = {}, replace = false, silent = false) {
142
+ export function goTo (route, context, replace = false, silent = false) {
139
143
  const newPath = window.location.origin + makePath(route)
144
+ const hash = newPath.indexOf('#') > -1 ? newPath.split('#')[1] : null
145
+ const oldPathState = pathState()
146
+ const currentPath = ensureOnlyLeadingSlash(route.split(/[#?]/)[0])
140
147
 
141
- const patch = {
142
- currentPath: route.split(/[#?]/)[0],
143
- context
148
+ if (currentPath === oldPathState.currentPath && context === undefined) {
149
+ if (replace) {
150
+ history.replaceState({}, route, newPath)
151
+ } else {
152
+ history.pushState({}, route, newPath)
153
+ }
154
+ scrollToHash(hash)
155
+ return
144
156
  }
145
157
 
146
- const oldPathState = pathState()
158
+ const patch = {
159
+ currentPath,
160
+ context: context === undefined ? {} : context
161
+ }
147
162
  const newPathState = Object.assign({}, oldPathState, patch)
148
163
  if (!silent) {
149
164
  try {
@@ -160,25 +175,59 @@ export function goTo (route, context = {}, replace = false, silent = false) {
160
175
  }
161
176
 
162
177
  setTimeout(() => {
163
- pathState.assign({
164
- currentPath: route.split(/[#?]/)[0],
165
- context
166
- })
178
+ pathState.assign(patch)
167
179
  if (!silent) {
168
180
  emit(afterRouteChange, newPathState, oldPathState)
169
181
  }
170
- if (newPath.indexOf('#') > -1) {
171
- const el = document.getElementById(decodeURIComponent(newPath.split('#')[1]))
172
- el && el.scrollIntoView()
173
- } else {
174
- window.scrollTo(0, 0)
175
- }
182
+ scrollToHash(hash)
176
183
  if (!silent) {
177
184
  emit(routeChangeComplete, newPathState, oldPathState)
178
185
  }
179
186
  })
180
187
  }
181
188
 
189
+ let cancelPendingScroll = null
190
+
191
+ /**
192
+ * Scroll the element whose id matches the hash into view. If the element does not exist yet, for example because
193
+ * the route's content is a promise that has not resolved, wait for it to be added to the document and scroll then.
194
+ * With no hash, scroll to the top of the page.
195
+ * @param {string|null|undefined} hash The hash to scroll to, with or without the leading #
196
+ * @param {number} [timeout] How long to wait for the element to appear, in milliseconds
197
+ */
198
+ export function scrollToHash (hash, timeout = 5000) {
199
+ if (cancelPendingScroll) {
200
+ cancelPendingScroll()
201
+ }
202
+ const id = hash ? decodeURIComponent(hash.startsWith('#') ? hash.slice(1) : hash) : ''
203
+ if (!id) {
204
+ window.scrollTo(0, 0)
205
+ return
206
+ }
207
+ const scrollIfPresent = () => {
208
+ const el = document.getElementById(id)
209
+ if (el) {
210
+ el.scrollIntoView()
211
+ }
212
+ return !!el
213
+ }
214
+ if (scrollIfPresent() || typeof MutationObserver === 'undefined') {
215
+ return
216
+ }
217
+ const observer = new MutationObserver(() => {
218
+ if (scrollIfPresent()) {
219
+ cancelPendingScroll()
220
+ }
221
+ })
222
+ const timer = setTimeout(() => cancelPendingScroll(), timeout)
223
+ cancelPendingScroll = () => {
224
+ observer.disconnect()
225
+ clearTimeout(timer)
226
+ cancelPendingScroll = null
227
+ }
228
+ observer.observe(document.documentElement, { childList: true, subtree: true })
229
+ }
230
+
182
231
  const ensureOnlyLeadingSlash = (part) => removeTrailingSlash(part.startsWith('/') ? part : '/' + part)
183
232
 
184
233
  const removeTrailingSlash = part => part.endsWith('/') && part.length > 1 ? part.slice(0, -1) : part
@@ -280,6 +329,12 @@ window.addEventListener(
280
329
  const patch = {
281
330
  currentPath: ensureOnlyLeadingSlash(window.location.pathname.replace(new RegExp('^' + pathState().rootPath), '')) || '/'
282
331
  }
332
+ // Clicking <a href="#section"> and moving back/forward between hash entries fire popstate without
333
+ // changing the path. That is not a route change, so leave the rendered route alone and let the
334
+ // browser handle scrolling to the fragment.
335
+ if (patch.currentPath === oldPathState.currentPath) {
336
+ return
337
+ }
283
338
  const newPathState = Object.assign({}, oldPathState, patch)
284
339
  try {
285
340
  emit(beforeRouteChange, newPathState, oldPathState)
@@ -294,6 +349,12 @@ window.addEventListener(
294
349
  }
295
350
  )
296
351
 
352
+ // On initial load the browser only scrolls to the hash if the target already exists. When the target is inside
353
+ // a route whose content has not rendered yet, wait for it the same way goTo does.
354
+ if (window.location.hash && !document.getElementById(decodeURIComponent(window.location.hash.slice(1)))) {
355
+ scrollToHash(window.location.hash)
356
+ }
357
+
297
358
  const makePath = path => (pathState().rootPath === '/' ? '' : pathState().rootPath) + ensureOnlyLeadingSlash(path)
298
359
 
299
360
  const makePathPattern = (path, absolute) => {
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 — it replaces the element on every update and the focused input will lose focus mid-keystroke. Bind the `value` attribute with bindAttr instead.
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.
@@ -72,11 +92,40 @@ export function h<T extends HTMLElement | SVGElement>(tag: string, ...children:
72
92
  */
73
93
  export function fnstate<T>(initialValue: T, mapKey?: (T: any) => any): FnState<T>;
74
94
  /**
75
- * Convert non objects (objects are assumed to be nodes) to text nodes and allow promises to resolve to nodes
95
+ * Convert non objects (objects are assumed to be nodes) to text nodes and allow promises to resolve to nodes.
96
+ * Arrays are flattened at any depth into a DocumentFragment.
76
97
  * @param {any} node The node to render
77
98
  * @returns {Node} The rendered node
78
99
  */
79
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;
80
129
  /**
81
130
  * Check if the given value is an object that can be used as attributes
82
131
  * @param {any} val The value to check
@@ -136,9 +185,19 @@ export function hmrRoot(container: HTMLElement, appFn: (() => Node) | Node): HTM
136
185
  * Create a compiled template function. The returned function takes a single object that contains the properties
137
186
  * defined in the template.
138
187
  *
139
- * This allows fast rendering by pre-creating a dom element with the entire template structure then cloning and populating
140
- * the clone with data from the provided context. This avoids the work of having to re-execute the tag functions
141
- * one by one and can speed up situations where a similar element is created many times.
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.
142
201
  *
143
202
  * You cannot bind state to the initial template. If you attempt to, the state will be read, but the elements will
144
203
  * not be updated when the state changes because they will not be bound to the cloned element.
@@ -156,7 +215,7 @@ export type FnStateObj<T> = {
156
215
  /**
157
216
  * Bind this state to the given element function. This causes the element to be replaced when the state changes.
158
217
  * If called with no parameters, the state's value will be rendered as an element.
159
- * 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.
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.
160
219
  */
161
220
  bindAs: (element?: (newValue: T, oldValue: T) => (Node | any)) => Node;
162
221
  /**
@@ -165,6 +224,15 @@ export type FnStateObj<T> = {
165
224
  * If the current value is not an array, this will behave the same as bindAs.
166
225
  */
167
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;
168
236
  /**
169
237
  * Bind to a property of an object stored in this state instead of the state itself.
170
238
  * Shortcut for `mystate.bindAs((current)=> current[prop])`
@@ -221,6 +289,25 @@ export type FnStateObj<T> = {
221
289
  */
222
290
  isFnState: boolean;
223
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
+ };
224
311
  /**
225
312
  * A container for a state value that can be bound to.
226
313
  */
@@ -1 +1 @@
1
- {"version":3,"file":"fntags.d.mts","sourceRoot":"","sources":["fntags.mjs"],"names":[],"mappings":"AAcA;;;;;GAKG;AACH,+BAFW,OAAO,QAIjB;AAQD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,2DALW,MAAM,eACH,CAAC,IAAI,MAAO,CAAC,OA6C1B;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;;GAGG;AAEH;;;;;;;;;;GAUG;AACH,iEAPgB,GAAG,cAqIlB;AA2kBD;;;;GAIG;AACH,iCAHW,GAAG,GACD,IAAI,CAuBhB;AAqFD;;;;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;AAED;;;;;;;;;;;;;;;GAeG;AACH,qDAJkB,IAAI,iBACH,IAAI,CA+DtB;;;;;;;;;;kCAliCmC,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;;;;;qBAG9E,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;;;;;sDAKqB,CAAC,KAAG,CAAC"}
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"}