@firsthandjs/dom 0.1.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/LICENSE +21 -0
- package/README.md +29 -0
- package/dist/adapter.d.ts +29 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/attributes.d.ts +34 -0
- package/dist/attributes.d.ts.map +1 -0
- package/dist/chunk-6OTA5OKE.js +1 -0
- package/dist/component.d.ts +99 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/dev.d.ts +13 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.prod.d.ts +4 -0
- package/dist/dev.prod.d.ts.map +1 -0
- package/dist/events.d.ts +21 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/insert.d.ts +59 -0
- package/dist/insert.d.ts.map +1 -0
- package/dist/internal.d.ts +22 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +1 -0
- package/dist/list.d.ts +18 -0
- package/dist/list.d.ts.map +1 -0
- package/dist/portal.d.ts +12 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/props.d.ts +38 -0
- package/dist/props.d.ts.map +1 -0
- package/dist/render.d.ts +13 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/template.d.ts +17 -0
- package/dist/template.d.ts.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Firsthand contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @firsthandjs/dom
|
|
2
|
+
|
|
3
|
+
DOM parts, components, portals and keyed lists — and a re-export of
|
|
4
|
+
`@firsthandjs/core`, so applications have one import site.
|
|
5
|
+
|
|
6
|
+
**Documentation:** [guide](https://github.com/firsthandjs/firsthand/blob/main/docs/guide/03-components.md) · [API reference](https://github.com/firsthandjs/firsthand/blob/main/docs/reference/dom.md) · [all docs](https://github.com/firsthandjs/firsthand/blob/main/docs/README.md)
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { component, render, signal } from '@firsthandjs/dom';
|
|
10
|
+
|
|
11
|
+
const Counter = component(() => {
|
|
12
|
+
const count = signal(0);
|
|
13
|
+
return <button onClick={() => count.value++}>{count.value}</button>;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
render(() => <Counter />);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Pair it with [`@firsthandjs/compiler`](../compiler) at build time: static markup
|
|
20
|
+
becomes a cloned `<template>` and every dynamic expression becomes a specialised
|
|
21
|
+
DOM part.
|
|
22
|
+
|
|
23
|
+
`@firsthandjs/dom/internal` is the compiler/runtime protocol. It is public on
|
|
24
|
+
purpose — compiled output is code you could have written by hand — but it is not
|
|
25
|
+
covered by semantic versioning in the same way the main entry point is.
|
|
26
|
+
|
|
27
|
+
Full documentation: the [repository README](../../README.md).
|
|
28
|
+
|
|
29
|
+
MIT licensed.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element types this framework does not own.
|
|
3
|
+
*
|
|
4
|
+
* A component from another framework is a function, and TSX is happy to write
|
|
5
|
+
* it as an element. Firsthand cannot run it — there is nothing here that knows
|
|
6
|
+
* what a React element is, and putting that knowledge in this package would
|
|
7
|
+
* make every application pay for a framework it may not use.
|
|
8
|
+
*
|
|
9
|
+
* So this is a seam and nothing else: one slot for an adapter, one cache, and
|
|
10
|
+
* an error that names the fix. `@firsthandjs/react/auto` fills the slot; anything
|
|
11
|
+
* else can fill it the same way. No name in this file mentions React.
|
|
12
|
+
*/
|
|
13
|
+
import type { Component } from './component.js';
|
|
14
|
+
/** Turns a foreign component into a Firsthand one. Called once per target. */
|
|
15
|
+
export type ComponentAdapter = (target: (props: never) => unknown) => Component<never>;
|
|
16
|
+
/**
|
|
17
|
+
* Installs the adapter used for element types that are not Firsthand components.
|
|
18
|
+
*
|
|
19
|
+
* Call it once, at startup. `null` removes it again, which is what a test does
|
|
20
|
+
* between cases.
|
|
21
|
+
*/
|
|
22
|
+
export declare function setComponentAdapter(next: ComponentAdapter | null): void;
|
|
23
|
+
/** Thrown for an element type this framework cannot run. */
|
|
24
|
+
export declare class FirsthandComponentError extends Error {
|
|
25
|
+
constructor(name: string);
|
|
26
|
+
}
|
|
27
|
+
/** Resolves a foreign element type, or says why it cannot be resolved. */
|
|
28
|
+
export declare function adapt(target: object): Component<never>;
|
|
29
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC;AAcvF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAKvE;AAED,4DAA4D;AAC5D,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,IAAI,EAAE,MAAM;CAQzB;AAED,0EAA0E;AAC1E,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAYtD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Specialised setters for the attribute-shaped parts of a template.
|
|
3
|
+
*
|
|
4
|
+
* Each kind has its own function rather than one generic `setAttribute(node,
|
|
5
|
+
* name, value)` dispatcher, because the compiler already knows which kind it
|
|
6
|
+
* emitted (ADR-0009). Nothing here inspects a value to decide what it is,
|
|
7
|
+
* except where the public contract genuinely allows several shapes (`class`
|
|
8
|
+
* and `style`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function setAttribute(node: Element, name: string, value: unknown): void;
|
|
11
|
+
export declare function setAttributeNS(node: Element, ns: string, name: string, value: unknown): void;
|
|
12
|
+
/** Writes a DOM property, which is what keeps object props out of strings. */
|
|
13
|
+
export declare function setProperty(node: Element, name: string, value: unknown): void;
|
|
14
|
+
/** A boolean DOM property such as `disabled` or `checked`. */
|
|
15
|
+
export declare function setBoolean(node: Element, name: string, value: unknown): void;
|
|
16
|
+
/** `class` as a string, replacing whatever was there. */
|
|
17
|
+
export declare function setClass(node: Element, value: unknown): void;
|
|
18
|
+
/**
|
|
19
|
+
* `class` as a record of `{ name: enabled }`, toggling only what changed.
|
|
20
|
+
*
|
|
21
|
+
* `previous` is the record from the last run; the caller keeps it, so nothing
|
|
22
|
+
* is allocated here.
|
|
23
|
+
*/
|
|
24
|
+
export declare function setClassList(node: Element, value: Record<string, unknown>, previous: Record<string, unknown> | undefined): void;
|
|
25
|
+
/** `style` as a string. */
|
|
26
|
+
export declare function setStyle(node: ElementCSSInlineStyle, value: unknown): void;
|
|
27
|
+
/**
|
|
28
|
+
* `style` as an object, diffed per property.
|
|
29
|
+
*
|
|
30
|
+
* Rewriting `cssText` would discard properties set elsewhere and force a full
|
|
31
|
+
* re-parse of the declaration; setting only what changed does not.
|
|
32
|
+
*/
|
|
33
|
+
export declare function setStyleObject(node: ElementCSSInlineStyle, value: Record<string, string | number | null | undefined>, previous: Record<string, string | number | null | undefined> | undefined): void;
|
|
34
|
+
//# sourceMappingURL=attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributes.d.ts","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAM9E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAM5F;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAE7E;AAED,8DAA8D;AAC9D,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAE5E;AAED,yDAAyD;AACzD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAM5D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAC5C,IAAI,CAeN;AAED,2BAA2B;AAC3B,wBAAgB,QAAQ,CAAC,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAM1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,qBAAqB,EAC3B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EACzD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,SAAS,GACvE,IAAI,CAmBN"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var k=null,C=new WeakMap;function $n(n){k=n,C=new WeakMap}var x=class extends Error{constructor(t){super(`${t} is not a Firsthand component. If it is a React component, import '@firsthandjs/react/auto' once at startup to render React components directly, or wrap it with fromReact(). Otherwise, declare it with component().`),this.name="FirsthandComponentError"}};function $(n){let t=C.get(n);if(t!==void 0)return t;if(k===null){let o=typeof n=="function"&&n.name!==""?n.name:"The value";throw new x(o)}let e=k(n);return C.set(n,e),e}import{bind as rn,getOwner as j,onCleanup as sn,runWithOwner as ln}from"@firsthandjs/core";var dn=3;function L(n){return typeof n.nodeType=="number"}var H=Symbol("firsthand.part");function jn(n){return{[H]:!0,anchor:document.createTextNode(""),thunk:n,owner:j()}}function D(n){return H in n}function M(n,t,e=null){if(typeof t!="function"){p(n,e,null,t);return}let o=null;rn(()=>{let r=t();if(typeof r=="function"){o=w(o),M(n,r,e);return}o=p(n,e,o,r)}),sn(()=>{o=w(o)})}function p(n,t,e,o){let r=typeof o;if(o==null||r==="boolean")return w(e);if(r==="string"||r==="number"){let i=String(o);return e!==null&&!Array.isArray(e)&&e.nodeType===dn?(e.data=i,e):v(n,t,e,document.createTextNode(i))}if(r==="function")return p(n,t,e,o());if(Array.isArray(o)){let i=[],s=[];if(W(o,i,s),i.length===0)return w(e);un(n,t,e===null?[]:Array.isArray(e)?e:[e],i);for(let d=0;d<s.length;d++){let l=s[d];ln(l.owner??j(),()=>{M(n,l.thunk,l.anchor)})}return i}return D(o)?p(n,t,e,[o]):L(o)?v(n,t,e,o):p(n,t,e,String(o))}function W(n,t,e){for(let o=0;o<n.length;o++){let r=n[o];if(typeof r=="object"&&r!==null&&D(r)){t.push(r.anchor),e.push(r);continue}for(;typeof r=="function";)r=r();r==null||typeof r=="boolean"||(Array.isArray(r)?W(r,t,e):typeof r=="object"&&L(r)?t.push(r):t.push(document.createTextNode(String(r))))}}function T(n){let t=n.parentNode;t!==null&&t.removeChild(n)}function w(n){if(n!==null)if(Array.isArray(n))for(let t=0;t<n.length;t++)T(n[t]);else T(n);return null}function v(n,t,e,o){return e===o?o:e!==null&&!Array.isArray(e)?(n.replaceChild(o,e),o):(w(e),n.insertBefore(o,t),o)}function un(n,t,e,o){let r=0,i=0,s=e.length-1,d=o.length-1;for(;r<=s&&i<=d&&e[r]===o[i];)r++,i++;for(;r<=s&&i<=d&&e[s]===o[d];)s--,d--;let l=d+1<o.length?o[d+1]:t;if(r>s){for(let u=i;u<=d;u++)n.insertBefore(o[u],l);return}if(i>d){for(let u=r;u<=s;u++)n.removeChild(e[u]);return}let c=new Map;for(let u=r;u<=s;u++)c.set(e[u],u);let a=d-i+1,f=new Int32Array(a).fill(-1);for(let u=0;u<a;u++){let g=o[i+u],O=c.get(g);O!==void 0&&(f[u]=O,c.delete(g))}for(let u of c.keys())n.removeChild(u);let m=cn(f),b=m.length-1,S=l;for(let u=a-1;u>=0;u--){let g=o[i+u];b>=0&&m[b]===u?b--:n.insertBefore(g,S),S=g}}function cn(n){let t=n.length,e=new Int32Array(t).fill(-1),o=[];for(let s=0;s<t;s++){let d=n[s];if(d===-1)continue;let l=0,c=o.length;for(;l<c;){let a=l+c>>1;n[o[a]]<d?l=a+1:c=a}l>0&&(e[s]=o[l-1]),o[l]=s}let r=[],i=o.length>0?o[o.length-1]:-1;for(;i!==-1;)r.push(i),i=e[i];return r.reverse()}import{signal as an,createOwner as I,disposeOwner as fn,getOwner as F,handleError as V,setOwner as h,untrack as _}from"@firsthandjs/core";var z=Symbol.for("firsthand.component"),pn=0,B="firsthand";function Vn(n){B=n}function _n(n,t,e,o){let i=Object.assign(s=>mn(i,s),{[z]:!0,setup:n,options:t,id:e??`c${String(++pn)}`,tag:void 0});return Object.defineProperty(i,"name",{configurable:!0,value:o??(n.name===""?"Component":n.name)}),e===void 0&&(`${i.name}`,void 0),(t?.tag!==void 0||t?.shadow===!0)&&wn(i,typeof t.tag=="string"?t.tag:void 0),i}function zn(n){return typeof n=="function"&&z in n}function mn(n,t){if(Object.freeze(t),n.setup===void 0)return $(n)(t);if(n.tag!==void 0)return bn(n,t);let e=I(F()),o=h(e);try{let r=_(()=>n.setup(t));return h(o),r}catch(r){return h(o),V(r,e),null}}function gn(n){let t=n.name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[^a-zA-Z0-9-]/g,"-").toLowerCase();return`${B}-${t}`}function wn(n,t){let e=n;if(e.tag!==void 0)return e.tag;let o=t??gn(e);return customElements.get(o)!==void 0&&(o=`${o}-${e.id.replace(/[^a-zA-Z0-9]/g,"").toLowerCase()}`),e.tag=o,customElements.define(o,hn(e)),o}function hn(n){let t=n.options?.shadow===!0,e=n.options?.attributes,o=e!==void 0?Object.keys(e):[];return class extends HTMLElement{static observedAttributes=o;constructor(){super(),this.$owner=null,this.$props=null,this.$attrs=null,this.$standalone=!1}connectedCallback(){this.$owner===null&&(this.$props===null&&(this.$standalone=!0,this.$attrs={},this.$props=yn(this,e,this.$attrs)),K(this,n,t))}disconnectedCallback(){!this.$standalone||this.$owner===null||(fn(this.$owner),this.$owner=null)}attributeChangedCallback(i,s,d){let l=this.$attrs?.[i];l!==void 0&&(l.value=e[i]?.(d))}}}function yn(n,t,e){let o={};if(t!==void 0)for(let r in t){let i=an(t[r](n.getAttribute(r)));e[r]=i,Object.defineProperty(o,r,{enumerable:!0,get:()=>i.value})}return Object.freeze(o)}function K(n,t,e){let o=I(F());n.$owner=o;let r=h(o);try{let i=e?n.attachShadow({mode:"open"}):n,s=_(()=>t.setup(n.$props));p(i,null,null,s)}catch(i){V(i,o)}finally{h(r)}}function bn(n,t){let e=document.createElement(n.tag);return e.$props=t,e.$owner===null&&K(e,n,n.options?.shadow===!0),e}import{batch as kn,createOwner as q,disposeOwner as Cn,getOwner as xn,setOwner as E,signal as Z,untrack as Y}from"@firsthandjs/core";function Gn(n,t,e){let o=q(xn()),r=new Map;return()=>{let i=n(),s=new Map,d=[];return kn(()=>{for(let l=0;l<i.length;l++){let c=i[l],a=Y(()=>t(c,l));if(s.has(a)){`${String(a)}`;continue}let f=r.get(a);f===void 0?f=En(o,c,l,e):(r.delete(a),f.item.value=c,f.index.value=l),s.set(a,f);for(let m=0;m<f.nodes.length;m++)d.push(f.nodes[m])}for(let l of r.values())Cn(l.owner)}),r=s,d}}function En(n,t,e,o){let r=q(n),i=Z(t),s=Z(e),d=E(r),l;try{l=Y(()=>o(i,s)),E(d)}catch(a){throw E(d),a}let c=[];return G(l,c),{owner:r,item:i,index:s,nodes:c}}function G(n,t){if(!(n==null||typeof n=="boolean")){if(Array.isArray(n)){for(let e=0;e<n.length;e++)G(n[e],t);return}if(typeof n=="object"&&typeof n.nodeType=="number"){t.push(n);return}t.push(document.createTextNode(String(n)))}}var Rn=new Set(["click","dblclick","contextmenu","input","beforeinput","change","keydown","keyup","keypress","pointerdown","pointerup","pointermove","mousedown","mouseup","mouseover","mouseout","submit","focusin","focusout","touchstart","touchend"]),X=new Set;function R(n){return`$firsthand$${n}`}function Nn(n){let t=R(n.type),e=n.composedPath(),o=null;Object.defineProperty(n,"currentTarget",{configurable:!0,get:()=>o});for(let r=0;r<e.length;r++){let i=e[r],s=i[t];if(s!==void 0&&(o=i,s.call(i,n),n.cancelBubble)||i===document)break}o=null}function y(n,t,e,o){if(o!==void 0||!Rn.has(t)){n.addEventListener(t,e,o===!0?void 0:o);return}n[R(t)]=e,X.has(t)||(X.add(t),document.addEventListener(t,Nn))}function Jn(n,t){n[R(t)]=void 0}var An={class:"className",for:"htmlFor"};function A(n,t,e){e==null||e===!1?n.removeAttribute(t):n.setAttribute(t,e===!0?"":String(e))}function Un(n,t,e,o){o==null||o===!1?n.removeAttributeNS(t,e):n.setAttributeNS(t,e,o===!0?"":String(o))}function P(n,t,e){n[An[t]??t]=e}function ne(n,t,e){n[t]=!!e}function Q(n,t){t==null?n.removeAttribute("class"):n.className=String(t)}function U(n,t,e){let o=n.classList;if(e!==void 0)for(let r in e)r in t||o.remove(r);for(let r in t){let i=!!t[r];(e===void 0||!!e[r]!==i)&&o.toggle(r,i)}}function nn(n,t){t==null?n.style.cssText="":n.style.cssText=String(t)}function en(n,t,e){let o=n.style;if(e!==void 0)for(let r in e)r in t||o.removeProperty(N(r));for(let r in t){let i=t[r];(e===void 0||e[r]!==i)&&(i==null?o.removeProperty(N(r)):o.setProperty(N(r),String(i)))}}var Pn=/[A-Z]/g,J=new Map;function N(n){let t=J.get(n);return t===void 0&&(t=n.startsWith("--")?n:n.replace(Pn,e=>`-${e.toLowerCase()}`),J.set(n,t)),t}function Sn(n,t,e){if(t==="class"||t==="className"){e!==null&&typeof e=="object"?U(n,e,on(n,"class",e)):Q(n,e);return}if(t==="style"){e!==null&&typeof e=="object"?en(n,e,on(n,"style",e)):nn(n,e);return}if(t==="ref"){e(n);return}if(t.startsWith("prop:")){P(n,t.slice(5),e);return}if(t.startsWith("attr:")){A(n,t.slice(5),e);return}if(t.startsWith("on")&&t.length>2&&/[A-Z:]/.test(t[2])){On(n,t,e);return}if(t in n&&typeof e!="string"){P(n,t,e);return}A(n,t,e)}function On(n,t,e){let o=t.slice(2).split(":"),r=o[0]==="",i=r?o[1]:o[0].toLowerCase(),s=o[r?2:1];s===void 0?y(n,i,e):s==="native"?y(n,i,e,!0):y(n,i,e,{[s]:!0})}function re(n,t){for(let e in t)e!=="children"&&Sn(n,e,t[e])}function ie(...n){let t={};for(let e=0;e<n.length;e++){let o=n[e];for(let r of Object.keys(o))Object.defineProperty(t,r,{enumerable:!0,configurable:!0,get:()=>o[r]})}return t}function se(n,t){let e={};for(let o of Object.keys(n))t.includes(o)||Object.defineProperty(e,o,{enumerable:!0,get:()=>n[o]});return Object.freeze(e)}var tn=new WeakMap;function on(n,t,e){let o=tn.get(n);o===void 0&&(o={},tn.set(n,o));let r=o[t];return o[t]=e,r}export{$n as a,x as b,jn as c,M as d,p as e,un as f,z as g,Vn as h,_n as i,zn as j,mn as k,wn as l,Gn as m,y as n,Jn as o,A as p,Un as q,P as r,ne as s,Q as t,U as u,nn as v,en as w,Sn as x,re as y,ie as z,se as A};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { ReadonlyProps } from '@firsthandjs/core';
|
|
2
|
+
import { type DynamicChild } from './insert.js';
|
|
3
|
+
/** Marks a value as a Firsthand component; used by the JSX runtime and compiler. */
|
|
4
|
+
export declare const COMPONENT: unique symbol;
|
|
5
|
+
export interface ComponentOptions {
|
|
6
|
+
/** Attach a shadow root to the element host. Implies `tag` (ADR-0007). */
|
|
7
|
+
shadow?: boolean;
|
|
8
|
+
/** Host this component in a real custom element (ADR-0003). */
|
|
9
|
+
tag?: true | string;
|
|
10
|
+
/** Explicit attribute codecs for vanilla-HTML consumers of the element. */
|
|
11
|
+
attributes?: Readonly<Record<string, AttributeCodec>>;
|
|
12
|
+
}
|
|
13
|
+
/** Converts an attribute string to a prop value. `null` means "absent". */
|
|
14
|
+
export type AttributeCodec = (raw: string | null) => unknown;
|
|
15
|
+
/**
|
|
16
|
+
* Anything a component or a dynamic expression may produce.
|
|
17
|
+
*
|
|
18
|
+
* `JSX.Element` is an alias of this, so TSX and the runtime agree on what a
|
|
19
|
+
* view is instead of one of them describing the other loosely.
|
|
20
|
+
*
|
|
21
|
+
* `DynamicChild` is in the union because a deferred child position is a real
|
|
22
|
+
* thing a view can be: it is what the compiler emits for a dynamic child of a
|
|
23
|
+
* fragment, and what a component returns when it renders something that has to
|
|
24
|
+
* be bound after it is placed — a route outlet, for instance.
|
|
25
|
+
*/
|
|
26
|
+
export type View = Node | string | number | boolean | null | undefined | DynamicChild | readonly View[];
|
|
27
|
+
/**
|
|
28
|
+
* Props as a *caller* may write them.
|
|
29
|
+
*
|
|
30
|
+
* An optional prop accepts an explicit `undefined`. Under
|
|
31
|
+
* `exactOptionalPropertyTypes` it otherwise would not, and `count={maybe}`
|
|
32
|
+
* — where `maybe` is `number | undefined` — would be an error although
|
|
33
|
+
* "absent" and "undefined" mean exactly the same thing to the component that
|
|
34
|
+
* receives it. Required props are untouched.
|
|
35
|
+
*/
|
|
36
|
+
export type PropsArgument<P> = {
|
|
37
|
+
[K in keyof P]: undefined extends P[K] ? P[K] | undefined : P[K];
|
|
38
|
+
};
|
|
39
|
+
export interface Component<P> {
|
|
40
|
+
/**
|
|
41
|
+
* Creates an instance.
|
|
42
|
+
*
|
|
43
|
+
* This is a real call: `<Counter initial={1} />` compiles to
|
|
44
|
+
* `createComponent(Counter, ...)`, and calling `Counter({ initial: 1 })`
|
|
45
|
+
* does the same thing. The signature is also what makes a component a valid
|
|
46
|
+
* TSX element type without any JSX-namespace special-casing.
|
|
47
|
+
*/
|
|
48
|
+
(props: PropsArgument<P>): View;
|
|
49
|
+
readonly [COMPONENT]: true;
|
|
50
|
+
readonly setup: (props: ReadonlyProps<P>) => View;
|
|
51
|
+
readonly options: ComponentOptions | undefined;
|
|
52
|
+
/** Stable build id from the compiler; never `Function.name` (ADR-0004). */
|
|
53
|
+
readonly id: string;
|
|
54
|
+
/** Original identifier, for devtools and error messages. */
|
|
55
|
+
readonly name: string;
|
|
56
|
+
/** Registered custom element name, once `defineElement` has run. */
|
|
57
|
+
tag: string | undefined;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Sets the prefix for custom element names. Call once, at startup.
|
|
61
|
+
*
|
|
62
|
+
* `setElementPrefix('acme')` makes `UserCard` register as `<acme-user-card>`.
|
|
63
|
+
* The prefix is the only thing an application has to say about element names:
|
|
64
|
+
* the rest is derived from the binding the component was assigned to.
|
|
65
|
+
*/
|
|
66
|
+
export declare function setElementPrefix(value: string): void;
|
|
67
|
+
/**
|
|
68
|
+
* Declares a component.
|
|
69
|
+
*
|
|
70
|
+
* The setup function runs exactly once per instance. The component's identity
|
|
71
|
+
* is the binding you assign it to — there is no name string to repeat, and no
|
|
72
|
+
* reliance on `Function.name`, which minifiers rewrite (ADR-0004).
|
|
73
|
+
*/
|
|
74
|
+
export declare function component<P>(setup: (props: ReadonlyProps<P>) => View, options?: ComponentOptions, id?: string, name?: string): Component<P>;
|
|
75
|
+
export declare function isComponent(value: unknown): value is Component<never>;
|
|
76
|
+
/**
|
|
77
|
+
* Instantiates a component.
|
|
78
|
+
*
|
|
79
|
+
* `props` arrives exactly as the caller built it: object references are
|
|
80
|
+
* preserved, nothing is serialised or copied, and dynamic props are accessor
|
|
81
|
+
* properties so that reading them subscribes to whatever the parent read
|
|
82
|
+
* (ADR-0005). The object itself is frozen, so top-level props have no writable
|
|
83
|
+
* slots; the values it points at are the caller's own and are never touched.
|
|
84
|
+
*
|
|
85
|
+
* Setup runs untracked: a component created inside a conditional part must not
|
|
86
|
+
* subscribe that part to everything its setup happens to read.
|
|
87
|
+
*/
|
|
88
|
+
export declare function createComponent<P>(target: Component<P>, props: P): View;
|
|
89
|
+
/** `UserCard` -> `acme-user-card`, using the prefix `setElementPrefix` holds. */
|
|
90
|
+
export declare function tagNameFor(target: Component<unknown>): string;
|
|
91
|
+
/**
|
|
92
|
+
* Registers a component as a real custom element.
|
|
93
|
+
*
|
|
94
|
+
* This is the opt-in half of ADR-0003: components are hostless by default
|
|
95
|
+
* because an upgraded custom element costs a constructor call, an upgrade
|
|
96
|
+
* reaction and an extra node per instance.
|
|
97
|
+
*/
|
|
98
|
+
export declare function defineElement(target: Component<never>, tag?: string): string;
|
|
99
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D,oFAAoF;AACpF,eAAO,MAAM,SAAS,EAAE,OAAO,MAA0C,CAAC;AAE1E,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;CACvD;AAED,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC;AAE7D;;;;;;;;;;GAUG;AACH,MAAM,MAAM,IAAI,GACd,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,IAAI,EAAE,CAAC;AAEvF;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI;KAC5B,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;CACjE,CAAC;AAEF,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B;;;;;;;OAOG;IACH,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC/C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,EACxC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,EAAE,CAAC,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS,CAAC,CAAC,CAAC,CAgCd;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,KAAK,CAAC,CAErE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CA4BvE;AAcD,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,MAAM,CAM7D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAc5E"}
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development-only diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* There is deliberately no `if (DEV)` guard at any call site: the production
|
|
5
|
+
* build aliases this module to `dev.prod.ts`, whose functions have empty bodies
|
|
6
|
+
* and are removed by the minifier. That keeps the production bundle free of
|
|
7
|
+
* diagnostics without creating branches that can never be taken in tests — see
|
|
8
|
+
* ARCHITECTURE section 8, item 6.
|
|
9
|
+
*/
|
|
10
|
+
export declare function devWarn(message: string): void;
|
|
11
|
+
/** Warns at most once for a given key, so a repeated mistake is not spam. */
|
|
12
|
+
export declare function devWarnOnce(key: string, message: string): void;
|
|
13
|
+
//# sourceMappingURL=dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../src/dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAID,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK9D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.prod.d.ts","sourceRoot":"","sources":["../src/dev.prod.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEhE"}
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native events with delegation (ADR-0012).
|
|
3
|
+
*
|
|
4
|
+
* There is no synthetic event: handlers receive the browser's own `Event`.
|
|
5
|
+
* For the bubbling types below, one real listener per type is installed on the
|
|
6
|
+
* document and dispatch walks the real `composedPath()`, so mounting 100 000
|
|
7
|
+
* rows registers zero listeners.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Attaches an event handler.
|
|
11
|
+
*
|
|
12
|
+
* `options` forces a direct listener: capture, once and passive cannot be
|
|
13
|
+
* expressed through delegation, and forcing one is also the documented escape
|
|
14
|
+
* hatch when a handler must run before a listener attached further up.
|
|
15
|
+
*/
|
|
16
|
+
export declare function on(node: Element, type: string, handler: (event: Event) => void, options?: AddEventListenerOptions | true): void;
|
|
17
|
+
/** Removes a delegated handler. Direct listeners die with their node. */
|
|
18
|
+
export declare function off(node: Element, type: string): void;
|
|
19
|
+
/** Test-only: forgets which document listeners were installed. */
|
|
20
|
+
export declare function resetDelegation(): void;
|
|
21
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAkEH;;;;;;GAMG;AACH,wBAAgB,EAAE,CAChB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,EAC/B,OAAO,CAAC,EAAE,uBAAuB,GAAG,IAAI,GACvC,IAAI,CAUN;AAED,yEAAyE;AACzE,wBAAgB,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,kEAAkE;AAClE,wBAAgB,eAAe,IAAI,IAAI,CAKtC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@firsthandjs/dom` — DOM parts, components, portals and keyed lists.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the reactive core so that applications have one import site.
|
|
5
|
+
*/
|
|
6
|
+
export { component, defineElement, setElementPrefix, createComponent } from './component.js';
|
|
7
|
+
export { setComponentAdapter, FirsthandComponentError } from './adapter.js';
|
|
8
|
+
export type { ComponentAdapter } from './adapter.js';
|
|
9
|
+
export type { Component, ComponentOptions, AttributeCodec, View } from './component.js';
|
|
10
|
+
export { render } from './render.js';
|
|
11
|
+
export { portal } from './portal.js';
|
|
12
|
+
export { list } from './list.js';
|
|
13
|
+
export { on, off } from './events.js';
|
|
14
|
+
export { mergeProps } from './props.js';
|
|
15
|
+
export { signal, computed, effect, batch, untrack, onCleanup, createRoot, catchError, runWithOwner, createContext, provide, useContext, FirsthandContextError, FirsthandCycleError, FirsthandReadonlyError, } from '@firsthandjs/core';
|
|
16
|
+
export type { CellOptions, Context, DeepReadonly, Dispose, ReadonlyCell, ReadonlyProps, Signal, } from '@firsthandjs/core';
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EACL,MAAM,EACN,QAAQ,EACR,MAAM,EACN,KAAK,EACL,OAAO,EACP,SAAS,EACT,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EACb,OAAO,EACP,UAAU,EACV,qBAAqB,EACrB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,WAAW,EACX,OAAO,EACP,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,MAAM,GACP,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as d,b as i,e as p,h as s,i as m,k as f,l as a,m as c,n as u,o as C,z as x}from"./chunk-6OTA5OKE.js";import{createRoot as y,onCleanup as h,untrack as N}from"@firsthandjs/core";function g(e,r=document.body){let o;return y(t=>{o=t;let n=null;n=p(r,null,null,N(e)),h(()=>{p(r,null,n,null)})}),o}import{onCleanup as E}from"@firsthandjs/core";function b(e,r){let o=[];l(e,o);for(let t=0;t<o.length;t++)r.appendChild(o[t]);return E(()=>{for(let t=0;t<o.length;t++){let n=o[t];n.parentNode?.removeChild(n)}}),null}function l(e,r){if(!(e==null||typeof e=="boolean")){if(Array.isArray(e)){for(let o=0;o<e.length;o++)l(e[o],r);return}if(typeof e=="object"&&typeof e.nodeType=="number"){r.push(e);return}r.push(document.createTextNode(String(e)))}}import{signal as z,computed as B,effect as G,batch as H,untrack as I,onCleanup as J,createRoot as K,catchError as L,runWithOwner as M,createContext as Q,provide as U,useContext as X,FirsthandContextError as Y,FirsthandCycleError as Z,FirsthandReadonlyError as _}from"@firsthandjs/core";export{i as FirsthandComponentError,Y as FirsthandContextError,Z as FirsthandCycleError,_ as FirsthandReadonlyError,H as batch,L as catchError,m as component,B as computed,f as createComponent,Q as createContext,K as createRoot,a as defineElement,G as effect,c as list,x as mergeProps,C as off,u as on,J as onCleanup,b as portal,U as provide,g as render,M as runWithOwner,d as setComponentAdapter,s as setElementPrefix,z as signal,I as untrack,X as useContext};
|
package/dist/insert.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Owner } from '@firsthandjs/core';
|
|
2
|
+
/** What a child part currently owns in the DOM. */
|
|
3
|
+
export type ChildSlot = Node | Node[] | null;
|
|
4
|
+
declare const PART: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* A dynamic child inside an array, carrying the scope it was written in.
|
|
7
|
+
*
|
|
8
|
+
* A fragment has no element of its own, so its children cannot be bound when
|
|
9
|
+
* they are created: there is no parent to insert into yet. Deferring the whole
|
|
10
|
+
* child to insertion time would evaluate it under whoever performs the
|
|
11
|
+
* insertion — the wrong scope, which loses context and disposal, and which does
|
|
12
|
+
* not make it reactive at all.
|
|
13
|
+
*
|
|
14
|
+
* So a fragment's dynamic child is emitted as one of these: an anchor that
|
|
15
|
+
* takes its place in the array, plus the expression and the owner it belongs
|
|
16
|
+
* to. Once the array is in the DOM, each one is bound through `insert` under
|
|
17
|
+
* its own owner, exactly as a child of a real element would be.
|
|
18
|
+
*/
|
|
19
|
+
export interface DynamicChild {
|
|
20
|
+
readonly [PART]: true;
|
|
21
|
+
readonly anchor: Text;
|
|
22
|
+
readonly thunk: () => unknown;
|
|
23
|
+
readonly owner: Owner | null;
|
|
24
|
+
}
|
|
25
|
+
/** Marks a dynamic child of a fragment. Emitted by the compiler. */
|
|
26
|
+
export declare function part(thunk: () => unknown): DynamicChild;
|
|
27
|
+
/**
|
|
28
|
+
* Binds a dynamic child position.
|
|
29
|
+
*
|
|
30
|
+
* `value` is a thunk when the compiler could not prove the expression constant.
|
|
31
|
+
* The thunk is evaluated once inside a tracking scope; if it read nothing
|
|
32
|
+
* reactive, no effect is retained (ADR-0009).
|
|
33
|
+
*/
|
|
34
|
+
export declare function insert(parent: Node, value: unknown, marker?: Node | null): void;
|
|
35
|
+
/** Applies one value to a child slot and returns the slot's new contents. */
|
|
36
|
+
export declare function applyChild(parent: Node, marker: Node | null, current: ChildSlot, value: unknown): ChildSlot;
|
|
37
|
+
/**
|
|
38
|
+
* Reconciles two node lists in place, by node identity.
|
|
39
|
+
*
|
|
40
|
+
* Keyed lists reuse their rows' DOM nodes across reorders, so identity is
|
|
41
|
+
* exactly the right key here: a row that survived is the same node, and only
|
|
42
|
+
* nodes that genuinely moved are touched.
|
|
43
|
+
*
|
|
44
|
+
* The algorithm is a common-prefix/suffix trim, then a longest-increasing-
|
|
45
|
+
* subsequence over the surviving nodes, moving only the ones outside it — the
|
|
46
|
+
* provably minimal number of `insertBefore` calls.
|
|
47
|
+
*
|
|
48
|
+
* This is the outcome of the comparison ADR-0010 required, not an assumption:
|
|
49
|
+
* three candidates were measured over ten operations at two sizes, with the
|
|
50
|
+
* order rotated per repetition and correctness asserted on every run. LIS came
|
|
51
|
+
* out ahead overall (1.02 against 1.17 for the two-ended scan and 1.49 for the
|
|
52
|
+
* naive baseline) and decisively where moves are few and far apart — a swap at
|
|
53
|
+
* 10 000 rows costs it 2 moves instead of 9 997. It loses one case: a full
|
|
54
|
+
* reverse, where the subsequence is length 1 and the analysis buys nothing.
|
|
55
|
+
* That trade is published in `benchmarks/results/reconcilers.json`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function reconcile(parent: Node, marker: Node | null, a: Node[], b: Node[]): void;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=insert.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insert.d.ts","sourceRoot":"","sources":["../src/insert.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAExF,mDAAmD;AACnD,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC;AAQ7C,QAAA,MAAM,IAAI,EAAE,OAAO,MAAiC,CAAC;AAErD;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC9B;AAED,oEAAoE;AACpE,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,OAAO,GAAG,YAAY,CAEvD;AAMD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,IAAI,GAAG,IAAW,GAAG,IAAI,CA4BrF;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CACxB,MAAM,EAAE,IAAI,EACZ,MAAM,EAAE,IAAI,GAAG,IAAI,EACnB,OAAO,EAAE,SAAS,EAClB,KAAK,EAAE,OAAO,GACb,SAAS,CAqDX;AAmED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAqEvF"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compiler/runtime protocol (ARCHITECTURE section 1.1).
|
|
3
|
+
*
|
|
4
|
+
* The compiler has no privileged access to runtime internals: it emits calls
|
|
5
|
+
* against exactly this surface, which is also importable by hand. That is what
|
|
6
|
+
* makes "no benchmark-only runtime" an enforceable property rather than a
|
|
7
|
+
* promise — the benchmark, the examples and the tests all go through here.
|
|
8
|
+
*
|
|
9
|
+
* Breaking changes to this surface bump `PROTOCOL_VERSION`, and the compiler
|
|
10
|
+
* emits a version assertion so a mismatched pair fails loudly at build time.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PROTOCOL_VERSION = 1;
|
|
13
|
+
export { template, path } from './template.js';
|
|
14
|
+
export { insert, applyChild, reconcile, part } from './insert.js';
|
|
15
|
+
export type { DynamicChild, ChildSlot } from './insert.js';
|
|
16
|
+
export { setAttribute, setAttributeNS, setProperty, setBoolean, setClass, setClassList, setStyle, setStyleObject, } from './attributes.js';
|
|
17
|
+
export { on, off } from './events.js';
|
|
18
|
+
export { applyProp, spread, mergeProps, rest } from './props.js';
|
|
19
|
+
export { bind } from '@firsthandjs/core';
|
|
20
|
+
export { list } from './list.js';
|
|
21
|
+
export { createComponent, isComponent, COMPONENT } from './component.js';
|
|
22
|
+
//# sourceMappingURL=internal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EACL,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,QAAQ,EACR,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/internal.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{A as L,c as l,d as s,e as i,f as a,g as m,j as d,k as f,m as c,n as u,o as x,p as N,q as y,r as C,s as h,t as b,u as O,v as S,w as P,x as T,y as g,z as E}from"./chunk-6OTA5OKE.js";function j(r,o=!1){let e;return()=>{if(e===void 0){let t=document.createElement("template");t.innerHTML=r,e=o?t.content:t.content.firstChild}return e.cloneNode(!0)}}function A(r,...o){let e=r;for(let t=0;t<o.length;t++){let n=e.firstChild;for(let p=o[t];p>0;p--)n=n.nextSibling;e=n}return e}import{bind as k}from"@firsthandjs/core";var M=1;export{m as COMPONENT,M as PROTOCOL_VERSION,i as applyChild,T as applyProp,k as bind,f as createComponent,s as insert,d as isComponent,c as list,E as mergeProps,x as off,u as on,l as part,A as path,a as reconcile,L as rest,N as setAttribute,y as setAttributeNS,h as setBoolean,b as setClass,O as setClassList,C as setProperty,S as setStyle,P as setStyleObject,g as spread,j as template};
|
package/dist/list.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReadonlyCell } from '@firsthandjs/core';
|
|
2
|
+
/**
|
|
3
|
+
* A keyed list part.
|
|
4
|
+
*
|
|
5
|
+
* `list()` is called while the template is being built, so it captures the
|
|
6
|
+
* component's owner. Rows are created under a dedicated child of that owner and
|
|
7
|
+
* therefore survive the list's own re-evaluation — unlike everything created
|
|
8
|
+
* directly inside the enclosing effect, which is cleared on each run.
|
|
9
|
+
*
|
|
10
|
+
* A row that keeps its key keeps its DOM nodes, its owner and its component
|
|
11
|
+
* state across reorders; only its `item` and `index` cells are updated, so a
|
|
12
|
+
* reorder touches exactly the parts that read the index and nothing else.
|
|
13
|
+
*
|
|
14
|
+
* The returned thunk produces the row nodes in order; `insert` reconciles them
|
|
15
|
+
* by node identity (see `insert.ts` and ADR-0010).
|
|
16
|
+
*/
|
|
17
|
+
export declare function list<T>(each: () => readonly T[], keyOf: (item: T, index: number) => unknown, render: (item: ReadonlyCell<T>, index: ReadonlyCell<number>) => unknown): () => Node[];
|
|
18
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../src/list.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,YAAY,EAElB,MAAM,mBAAmB,CAAC;AAU3B;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,IAAI,CAAC,CAAC,EACpB,IAAI,EAAE,MAAM,SAAS,CAAC,EAAE,EACxB,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,EAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,OAAO,GACtE,MAAM,IAAI,EAAE,CAuCd"}
|
package/dist/portal.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders `view` into `target`, keeping its logical owner where it was written.
|
|
3
|
+
*
|
|
4
|
+
* `view` is an ordinary expression, so it has already been created under the
|
|
5
|
+
* current scope by the time `portal` sees it. Context, disposal, reactive
|
|
6
|
+
* dependencies, event handling and error ownership all follow the owner tree
|
|
7
|
+
* (ADR-0008), so moving the nodes changes nothing but their physical position.
|
|
8
|
+
*
|
|
9
|
+
* The call contributes nothing at its own position in the tree.
|
|
10
|
+
*/
|
|
11
|
+
export declare function portal(view: unknown, target: ParentNode): null;
|
|
12
|
+
//# sourceMappingURL=portal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../src/portal.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAa9D"}
|
package/dist/props.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generic property applier.
|
|
3
|
+
*
|
|
4
|
+
* The compiler emits specialised setters wherever it knows the kind of a part,
|
|
5
|
+
* so this function is only reached by `class`/`style` (which genuinely accept
|
|
6
|
+
* several shapes), by spread attributes, and by the runtime JSX path. It is
|
|
7
|
+
* deliberately not on any hot path the compiler controls.
|
|
8
|
+
*/
|
|
9
|
+
export declare function applyProp(node: Element, name: string, value: unknown): void;
|
|
10
|
+
/**
|
|
11
|
+
* `onClick`, `onClick:capture`, and `on:sl-change` for a literal event name.
|
|
12
|
+
*
|
|
13
|
+
* The camel-case form lowercases, which is right for every DOM event and
|
|
14
|
+
* wrong for the ones component libraries invent: Shoelace dispatches
|
|
15
|
+
* `sl-change`, Vaadin `value-changed`, and no casing of an identifier
|
|
16
|
+
* produces a hyphen. `on:` takes what follows verbatim, so those work without
|
|
17
|
+
* a ref and an `addEventListener` by hand.
|
|
18
|
+
*/
|
|
19
|
+
export declare function applyEvent(node: Element, name: string, value: unknown): void;
|
|
20
|
+
/** Applies a spread of props to an element. */
|
|
21
|
+
export declare function spread(node: Element, props: Record<string, unknown>): void;
|
|
22
|
+
/**
|
|
23
|
+
* Merges prop sources without flattening their accessors.
|
|
24
|
+
*
|
|
25
|
+
* `{...a} {...b}` must not snapshot: every key becomes a getter that delegates
|
|
26
|
+
* to the source, so a reactive prop stays reactive through a spread.
|
|
27
|
+
*/
|
|
28
|
+
export declare function mergeProps(...sources: Record<string, unknown>[]): Record<string, unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* The remaining props, as live reads.
|
|
31
|
+
*
|
|
32
|
+
* `component(({ a, ...rest }) => ...)` compiles to a call to this. The result
|
|
33
|
+
* delegates every key back to the original props object, so a prop that
|
|
34
|
+
* changes is still seen through `rest` — copying the values instead would
|
|
35
|
+
* reintroduce exactly the snapshot the rewrite exists to remove.
|
|
36
|
+
*/
|
|
37
|
+
export declare function rest(props: Record<string, unknown>, omit: readonly string[]): Record<string, unknown>;
|
|
38
|
+
//# sourceMappingURL=props.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["../src/props.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CA0C3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAa5E;AAED,+CAA+C;AAC/C,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAM1E;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazF;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAWzB"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Dispose } from '@firsthandjs/core';
|
|
2
|
+
import type { View } from './component.js';
|
|
3
|
+
/**
|
|
4
|
+
* Mounts a view and returns its disposer.
|
|
5
|
+
*
|
|
6
|
+
* The container defaults to `document.body`, so an application needs no
|
|
7
|
+
* element lookup and no cast to start. Everything the view creates belongs to
|
|
8
|
+
* this root, so `dispose()` unsubscribes every effect, runs every cleanup and
|
|
9
|
+
* removes exactly the nodes that were inserted — not the container's other
|
|
10
|
+
* children.
|
|
11
|
+
*/
|
|
12
|
+
export declare function render(view: () => View, container?: ParentNode): Dispose;
|
|
13
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAG3C;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,SAAS,GAAE,UAA0B,GAAG,OAAO,CAWvF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static markup is parsed once into a `<template>` and cloned per instance
|
|
3
|
+
* (ADR-0009). The compiler emits one `template()` call per distinct markup
|
|
4
|
+
* shape at module scope; parsing is deferred to the first instance, so a module
|
|
5
|
+
* that is imported but never rendered costs nothing.
|
|
6
|
+
*/
|
|
7
|
+
export type TemplateFactory = () => Node;
|
|
8
|
+
export declare function template(html: string, isFragment?: boolean): TemplateFactory;
|
|
9
|
+
/**
|
|
10
|
+
* Descends to a node by a compile-time-known chain of child indices.
|
|
11
|
+
*
|
|
12
|
+
* This is how dynamic positions are located: no `querySelector`, no marker
|
|
13
|
+
* attributes, no scanning. The compiler knows the shape of the template, so it
|
|
14
|
+
* emits the path.
|
|
15
|
+
*/
|
|
16
|
+
export declare function path(root: Node, ...indices: readonly number[]): Node;
|
|
17
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC;AAEzC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,eAAe,CAU1E;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAUpE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@firsthandjs/dom",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DOM parts, templates, components, portals and keyed lists for Firsthand.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./internal": {
|
|
14
|
+
"types": "./dist/internal.d.ts",
|
|
15
|
+
"default": "./dist/internal.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@firsthandjs/core": "0.1.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20.11.0"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public",
|
|
33
|
+
"provenance": true
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/firsthandjs/firsthand.git",
|
|
38
|
+
"directory": "packages/dom"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/firsthandjs/firsthand/issues"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://github.com/firsthandjs/firsthand#readme",
|
|
44
|
+
"keywords": [
|
|
45
|
+
"reactive",
|
|
46
|
+
"signals",
|
|
47
|
+
"fine-grained",
|
|
48
|
+
"web-components",
|
|
49
|
+
"ui",
|
|
50
|
+
"framework",
|
|
51
|
+
"jsx",
|
|
52
|
+
"no-virtual-dom"
|
|
53
|
+
]
|
|
54
|
+
}
|