mono-jsx 0.6.8 → 0.6.10

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
@@ -268,9 +268,7 @@ function Button(this: FC<{ count: 0 }>, props: { role: string }) {
268
268
  }
269
269
  ```
270
270
 
271
- ### Using `<form>` Element
272
-
273
- mono-jsx supports `<form>` elements with the `action` attribute. The `action` attribute can be a string URL or a function that accepts a `FormData` object. The function will be called on form submission, and the `FormData` object will contain the form data.
271
+ mono-jsx allows you to use a function as the value of the `action` attribute of the `<form>` element. The function will be called on form submission, and the `FormData` object will contain the form data.
274
272
 
275
273
  ```tsx
276
274
  function App() {
@@ -415,11 +413,7 @@ function App(this: FC<{ input: string }>) {
415
413
  return (
416
414
  <div>
417
415
  <h1>{this.computed(() => this.input + "!")}</h1>
418
-
419
- <form action={(fd) => this.input = fd.get("input") as string}>
420
- <input type="text" name="input" value={"" + this.input} />
421
- <button type="submit">Submit</button>
422
- </form>
416
+ <input type="text" $value={this.input} />
423
417
  </div>
424
418
  )
425
419
  }
@@ -534,6 +528,21 @@ function App(this: FC<{ lang: "en" | "zh" | "🙂" }>) {
534
528
  }
535
529
  ```
536
530
 
531
+ ### Form Input Bindings with `$value` Attribute
532
+
533
+ You can use the `$value` attribute to bind a signal to the value of a form input element. The `$value` attribute is a two-way data binding, which means that when the input value changes, the signal will be updated, and when the signal changes, the input value will be updated.
534
+
535
+ ```tsx
536
+ function App(this: FC<{ value: string }>) {
537
+ this.value = "Welcome to mono-jsx";
538
+ this.effect(() => {
539
+ console.log("value changed:", this.value);
540
+ });
541
+ // return <input value={this.value} oninput={e => this.value = e.target.value} />;
542
+ return <input $value={this.value} />;
543
+ }
544
+ ```
545
+
537
546
  ### Limitation of Signals
538
547
 
539
548
  1\. Arrow function are non-stateful components.
package/jsx-runtime.mjs CHANGED
@@ -12,10 +12,10 @@ var ROUTER = 512;
12
12
  var EVENT_JS = `{var w=window;w.$emit=(e,f,s)=>f.call(w.$signals?.(s)??e.target,e.type==="mount"?e.target:e);w.$onsubmit=(e,f,s)=>{e.preventDefault();f.call(w.$signals?.(s)??e.target,new FormData(e.target),e)};}`;
13
13
  var CX_JS = `{var n=e=>typeof e=="string"?e:typeof e=="object"&&e!==null?Array.isArray(e)?e.map(n).filter(Boolean).join(" "):Object.entries(e).filter(([,t])=>!!t).map(([t])=>t).join(" "):"";window.$cx=n;}`;
14
14
  var STYLE_JS = `{var a=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;var l=e=>typeof e=="string";var u=e=>e.replace(/[a-z][A-Z]/g,r=>r.charAt(0)+"-"+r.charAt(1).toLowerCase());var p=e=>{let r=[],t=[],n=new g;for(let[o,s]of Object.entries(e))switch(o.charCodeAt(0)){case 58:t.push(null,o+"{"+c(s)+"}");break;case 64:t.push(o+"{",null,"{"+c(s)+"}}");break;case 38:t.push(null,o.slice(1)+"{"+c(s)+"}");break;default:r.push([o,s])}return r.length>0&&(n.inline=c(r)),t.length>0&&(n.css=t),n},f=(e,r)=>{let{inline:t,css:n}=p(r);if(n){let o="data-css-",s="["+o+(Date.now()+Math.random()).toString(36).replace(".","")+"]";document.head.appendChild(document.createElement("style")).textContent=(t?s+"{"+t+"}":"")+n.map(i=>i===null?s:i).join(""),e.getAttributeNames().forEach(i=>i.startsWith(o)&&e.removeAttribute(i)),e.setAttribute(s.slice(1,-1),"")}else t&&e.setAttribute("style",t)},c=e=>{if(typeof e=="object"&&e!==null){let r="";for(let[t,n]of Array.isArray(e)?e:Object.entries(e))if(l(n)||typeof n=="number"){let o=u(t),s=typeof n=="number"?a.test(t)?""+n:n+"px":""+n;r+=(r?";":"")+o+":"+(o==="content"?JSON.stringify(s):s)}return r}return""},g=(()=>{function e(){}return e.prototype=Object.freeze(Object.create(null)),e})();window.$applyStyle=f;}`;
15
- var RENDER_ATTR_JS = `{var s=(l,t,r)=>{let n=l.parentElement;return n.tagName==="M-GROUP"&&(n=n.previousElementSibling),()=>{let e=r();e===!1||e===null||e===void 0?n.removeAttribute(t):typeof e=="object"&&e!==null&&(t==="class"||t==="style"||t==="props")?t==="class"?n.setAttribute(t,$cx(e)):t==="style"?$applyStyle(n,e):n.setAttribute(t,JSON.stringify(e)):n.setAttribute(t,e===!0?"":e)}};window.$renderAttr=s;}`;
16
- var RENDER_TOGGLE_JS = `{var s=(n,l)=>{let e;return()=>{if(!e){let t=n.firstElementChild;t&&t.tagName==="TEMPLATE"&&t.hasAttribute("m-slot")?e=[...t.content.childNodes]:e=[...n.childNodes]}n.replaceChildren(...l()?e:[])}};window.$renderToggle=s;}`;
17
- var RENDER_SWITCH_JS = `{var u=(s,d)=>{let r,i=s.getAttribute("value"),t,l,o=e=>t.get(e)??t.set(e,[]).get(e);return()=>{if(!t){t=new Map,l=[];for(let e of s.childNodes)if(e.nodeType===1&&e.tagName==="TEMPLATE"&&e.hasAttribute("m-slot")){for(let n of e.content.childNodes)n.nodeType===1&&n.hasAttribute("slot")?o(n.getAttribute("slot")).push(n):l.push(n);e.remove()}else i?o(i).push(e):l.push(e)}r=""+d(),s.replaceChildren(...t.has(r)?t.get(r):l)}};window.$renderSwitch=u;}`;
18
- var SIGNALS_JS = `{let m;const h=window,b=new Map,y=new Map,f=n=>y.get(n)??y.set(n,S(n)).get(n),k=()=>Object.create(null),d=(n,e)=>n.getAttribute(e),S=n=>{const e=k(),t=(c,r)=>{e[c]=r},s=new Map,a=(c,r)=>{let i=s.get(c);return i||(i=new Set,s.set(c,i)),i.add(r),()=>{i.delete(r),i.size===0&&s.delete(c)}},o=new Proxy(k(),{get:(c,r)=>document.querySelector("[data-ref='"+n+":"+r+"']")});return new Proxy(e,{get:(c,r,i)=>{switch(r){case"$init":return t;case"$watch":return a;case"app":return f(0);case"refs":return o;default:return m?.(n,r),Reflect.get(c,r,i)}},set:(c,r,i,l)=>{if(i!==Reflect.get(c,r,l)){const u=s.get(r);return u&&queueMicrotask(()=>u.forEach(g=>g())),Reflect.set(c,r,i,l)}return!1}})},$=(n,e,t)=>{switch(e){case"toggle":return $renderToggle(n,t);case"switch":return $renderSwitch(n,t);case"html":return()=>n.innerHTML=""+t()}return e&&e.length>2&&e.startsWith("[")&&e.endsWith("]")?$renderAttr(n,e.slice(1,-1),t):()=>n.textContent=""+t()},v=n=>{const e=n.indexOf(":");return e>0?[Number(n.slice(0,e)),n.slice(e+1)]:null},p=async n=>{const e=n();return e!==void 0?e:(await new Promise(t=>setTimeout(t,0)),p(n))},E=(n,e)=>customElements.define(n,class extends HTMLElement{disposes=[];connectedCallback(){e(this)}disconnectedCallback(){this.disposes.forEach(t=>t()),this.disposes.length=0}});E("m-signal",n=>{const e=Number(d(n,"scope")),t=f(e),s=d(n,"key");if(s)n.disposes.push(t.$watch(s,$(n,d(n,"mode"),()=>t[s])));else{const a=Number(d(n,"computed"));p(()=>b.get(e*1e9+a)).then(([o,c])=>{const r=$(n,d(n,"mode"),o.bind(t));c.forEach(i=>{const[l,u]=v(i);n.disposes.push(f(l).$watch(u,r))})})}}),E("m-effect",n=>{const{disposes:e}=n,t=Number(d(n,"scope")),s=Number(d(n,"n")),a=new Array(s);e.push(()=>{a.forEach(o=>typeof o=="function"&&o()),a.length=0});for(let o=0;o<s;o++){const c="$ME_"+t+"_"+o;p(()=>h[c]).then(r=>{const i=[],l=f(t),u=()=>{a[o]=r.call(l)};m=(g,w)=>i.push([g,w]),u(),m=void 0;for(const[g,w]of i)e.push(f(g).$watch(w,u))},()=>{})}}),h.$MS=(n,e)=>{const[t,s]=v(n);f(t).$init(s,e)},h.$MC=(n,e,t,s)=>{b.set(n*1e9+e,[t,s])},h.$patch=(n,...e)=>{for(const[t,...s]of e){const a=s.pop();let o=n;for(const c of s)o=o[c];o[a]=t}return n},h.$signals=n=>n!==void 0?f(n):void 0;}`;
15
+ var RENDER_ATTR_JS = `{var s=(l,n,r)=>{let t=l.parentElement;return t.tagName==="M-GROUP"&&(t=t.previousElementSibling),()=>{let e=r();e===!1||e===null||e===void 0?t.removeAttribute(n):typeof e=="object"&&e!==null?n==="class"?t.setAttribute(n,$cx(e)):n==="style"?$applyStyle(t,e):t.setAttribute(n,JSON.stringify(e)):n==="value"?t.value=String(e):t.setAttribute(n,e===!0?"":e)}};window.$renderAttr=s;}`;
16
+ var RENDER_TOGGLE_JS = `{var r=(n,l)=>{let e;return()=>{if(!e){let t=n.firstElementChild;t&&t.tagName==="TEMPLATE"&&t.hasAttribute("m-slot")?e=[...t.content.childNodes]:e=[...n.childNodes]}n.replaceChildren(...l()?e:[])}};window.$renderToggle=r;}`;
17
+ var RENDER_SWITCH_JS = `{var u=(r,d)=>{let s,i=r.getAttribute("value"),t,l,o=e=>t.get(e)??t.set(e,[]).get(e);return()=>{if(!t){t=new Map,l=[];for(let e of r.childNodes)if(e.nodeType===1&&e.tagName==="TEMPLATE"&&e.hasAttribute("m-slot")){for(let n of e.content.childNodes)n.nodeType===1&&n.hasAttribute("slot")?o(n.getAttribute("slot")).push(n):l.push(n);e.remove()}else i?o(i).push(e):l.push(e)}s=""+d(),r.replaceChildren(...t.has(s)?t.get(s):l)}};window.$renderSwitch=u;}`;
18
+ var SIGNALS_JS = `{let m;const h=window,b=new Map,k=new Map,l=n=>k.get(n)??k.set(n,M(n)).get(n),y=()=>Object.create(null),f=(n,e)=>n.getAttribute(e),M=n=>{const e=y(),t=(r,c)=>{e[r]=c},s=new Map,a=(r,c)=>{let o=s.get(r);return o||(o=new Set,s.set(r,o)),o.add(c),()=>{o.delete(c),o.size===0&&s.delete(r)}},i=new Proxy(y(),{get:(r,c)=>document.querySelector("[data-ref='"+n+":"+c+"']")});return new Proxy(e,{get:(r,c,o)=>{switch(c){case"$init":return t;case"$watch":return a;case"app":return l(0);case"refs":return i;default:return m?.(n,c),Reflect.get(r,c,o)}},set:(r,c,o,d)=>{if(o!==Reflect.get(r,c,d)){const u=s.get(c);return u&&queueMicrotask(()=>u.forEach(g=>g())),Reflect.set(r,c,o,d)}return!1}})},$=(n,e,t)=>{switch(e){case"toggle":return $renderToggle(n,t);case"switch":return $renderSwitch(n,t);case"html":return()=>n.innerHTML=""+t()}return e&&e.length>2&&e.startsWith("[")&&e.endsWith("]")?$renderAttr(n,e.slice(1,-1),t):()=>n.textContent=""+t()},E=n=>{const e=n.indexOf(":");return e>0?[Number(n.slice(0,e)),n.slice(e+1)]:null},p=async n=>{const e=n();return e!==void 0?e:(await new Promise(t=>setTimeout(t,0)),p(n))},S=n=>{typeof n=="function"&&n()},v=(n,e)=>customElements.define(n,class extends HTMLElement{disposes=[];connectedCallback(){e(this)}disconnectedCallback(){this.disposes.forEach(t=>t()),this.disposes.length=0}});v("m-signal",n=>{const e=Number(f(n,"scope")),t=l(e),s=f(n,"key");if(s)n.disposes.push(t.$watch(s,$(n,f(n,"mode"),()=>t[s])));else{const a=Number(f(n,"computed"));p(()=>b.get(e*1e9+a)).then(([i,r])=>{const c=$(n,f(n,"mode"),i.bind(t));r.forEach(o=>{const[d,u]=E(o);n.disposes.push(l(d).$watch(u,c))})})}}),v("m-effect",n=>{const{disposes:e}=n,t=Number(f(n,"scope")),s=Number(f(n,"n")),a=new Array(s);e.push(()=>{a.forEach(S),a.length=0});for(let i=0;i<s;i++){const r="$ME_"+t+"_"+i;p(()=>h[r]).then(c=>{const o=[],d=l(t),u=()=>{S(a[i]),a[i]=c.call(d)};m=(g,w)=>o.push([g,w]),u(),m=void 0;for(const[g,w]of o)e.push(l(g).$watch(w,u))},()=>{})}}),h.$MS=(n,e)=>{const[t,s]=E(n);l(t).$init(s,e)},h.$MC=(n,e,t,s)=>{b.set(n*1e9+e,[t,s])},h.$patch=(n,...e)=>{for(const[t,...s]of e){const a=s.pop();let i=n;for(const r of s)i=i[r];i[a]=t}return n},h.$signals=n=>n!==void 0?l(n):void 0;}`;
19
19
  var SUSPENSE_JS = `{const i=new Map,o=e=>e.getAttribute("chunk-id"),l=(e,t)=>customElements.define(e,class extends HTMLElement{connectedCallback(){t(this)}});l("m-portal",e=>{i.set(o(e),e)}),l("m-chunk",e=>{setTimeout(()=>{const t=o(e),n=i.get(t),s=e.firstChild?.content.childNodes;n&&(e.hasAttribute("next")?s&&n.before(...s):(e.hasAttribute("done")?n.remove():s&&n.replaceWith(...s),i.delete(t)),e.remove())})});}`;
20
20
  var LAZY_JS = `{const r=document,o=(t,s)=>t.getAttribute(s),i=(t,s=[])=>t.replaceChildren(...s);customElements.define("m-component",class extends HTMLElement{static observedAttributes=["name","props"];#t;#e;#i;#n;#s;async#r(){if(!this.#t){i(this);return}const t={"x-component":this.#t,"x-props":this.#e||"{}","x-flags":$FLAGS},s=new AbortController;this.#n?.abort(),this.#n=s,i(this,this.#i);const e=await fetch(location.href,{headers:t,signal:s.signal});if(!e.ok)throw i(this),new Error("Failed to fetch component '"+this.#t+"'");const[h,n]=await e.json();this.innerHTML=h,n&&(r.body.appendChild(r.createElement("script")).textContent=n)}get name(){return this.#t??null}set name(t){t&&t!==this.#t&&(this.#t=t,this.refresh())}get props(){return this.#e?JSON.parse(this.#e):void 0}set props(t){const s=typeof t=="string"?t:JSON.stringify(t);s&&s!==this.#e&&(this.#e=s,this.refresh())}connectedCallback(){setTimeout(()=>{if(!this.#i){const t=o(this,"props");this.#t=o(this,"name"),this.#e=t?.startsWith("base64,")?atob(t.slice(7)):void 0,this.#i=[...this.childNodes]}this.#r()})}disconnectedCallback(){i(this,this.#i),this.#n?.abort(),this.#n=void 0,this.#s&&clearTimeout(this.#s),this.#s=void 0}attributeChangedCallback(t,s,e){this.#t&&e&&(t==="name"?this.name=e:t==="props"&&(this.props=e))}refresh(){this.#s&&clearTimeout(this.#s),this.#s=setTimeout(()=>{this.#s=void 0,this.#r()},50)}});}`;
21
21
  var ROUTER_JS = `{const a=document,n=location,l=t=>t.split("#",1)[0],c=t=>l(t)===l(n.href);customElements.define("m-router",class extends HTMLElement{#t;#e;#s;#i;async#o(t){const s=new AbortController,e={"x-route":"true","x-flags":$FLAGS};this.#i?.abort(),this.#i=s;const i=await fetch(t,{headers:e,signal:s.signal});if(i.status===404)return this.replaceChildren(...this.#t),!0;if(!i.ok)throw this.replaceChildren(),new Error("Failed to fetch route: "+i.status+" "+i.statusText);const[o,r]=await i.json();this.innerHTML=o,r&&(a.body.appendChild(a.createElement("script")).textContent=r)}#n(){a.querySelectorAll("nav a").forEach(t=>{const{href:s,classList:e}=t,i=t.closest("nav")?.getAttribute("data-active-class")??"active";c(s)?e.add(i):e.remove(i)})}navigate(t,s){const e=new URL(t,n.href);if(e.origin!==n.origin){n.href=t;return}c(e.href)||this.#a(t,s)}async#a(t,s){const e=await this.#o(t);s?.replace?history.replaceState({},"",t):history.pushState({},"",t),e&&typeof $signals<"u"&&($signals(0).url=new URL(t)),this.#n(),window.scrollTo(0,0)}connectedCallback(){setTimeout(()=>{if(!this.#t)if(this.getAttribute("status")==="404")this.#t=[...this.childNodes];else{this.#t=[];for(const t of this.childNodes)if(t.nodeType===1&&t.tagName==="TEMPLATE"&&t.hasAttribute("m-slot")){this.#t.push(...t.content.childNodes),t.remove();break}}}),this.#e=t=>{if(t.defaultPrevented||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey||!(t.target instanceof HTMLAnchorElement))return;const{download:s,href:e,rel:i,target:o}=t.target;s||i==="external"||o==="_blank"||!e.startsWith(n.origin)||(t.preventDefault(),this.navigate(e))},this.#s=()=>this.#a(n.href),addEventListener("popstate",this.#s),a.addEventListener("click",this.#e),setTimeout(()=>this.#n())}disconnectedCallback(){removeEventListener("popstate",this.#s),a.removeEventListener("click",this.#e),this.#i?.abort(),this.#i=void 0,this.#e=void 0,this.#s=void 0}});}`;
@@ -135,7 +135,7 @@ var $signal = Symbol.for("mono.signal");
135
135
  var $vnode = Symbol.for("jsx.vnode");
136
136
 
137
137
  // version.ts
138
- var VERSION = "0.6.7";
138
+ var VERSION = "0.6.9";
139
139
 
140
140
  // render.ts
141
141
  var cdn = "https://raw.esm.sh";
@@ -325,7 +325,6 @@ async function render(node, options, write, writeJS, componentMode) {
325
325
  request,
326
326
  signals,
327
327
  routeFC,
328
- eager: componentMode,
329
328
  flags: { scope: 0, chunk: 0, runtime: 0 },
330
329
  mcs: new IdGenManagerImpl(),
331
330
  mfs: new IdGenManagerImpl()
@@ -359,7 +358,9 @@ async function render(node, options, write, writeJS, componentMode) {
359
358
  js += 'window.$FLAGS="' + scope + "|" + chunk + "|" + runtimeFlag + '";';
360
359
  }
361
360
  if (rc.mfs.size > 0) {
362
- js += rc.mfs.toJS((scope, seq, fn) => "function $MF_" + scope + "_" + seq + "(){(" + fn.toString() + ").apply(this,arguments)};");
361
+ js += rc.mfs.toJS(
362
+ (scope, seq, fn) => "function $MF_" + scope + "_" + seq + "(){(" + (fn.str ?? String(fn)) + ").apply(this,arguments)};"
363
+ );
363
364
  rc.mfs.clear();
364
365
  }
365
366
  if (hasEffect) {
@@ -384,7 +385,7 @@ async function render(node, options, write, writeJS, componentMode) {
384
385
  if (rc.mcs.size > 0) {
385
386
  js += rc.mcs.toJS((scope, seq, signal) => {
386
387
  const { compute, deps } = signal[$signal].key;
387
- return "$MC(" + scope + "," + seq + ",function(){return(" + compute.toString() + ").call(this)}," + stringify([...deps.values()]) + ");";
388
+ return "$MC(" + scope + "," + seq + ",function(){return(" + String(compute) + ").call(this)}," + stringify([...deps.values()]) + ");";
388
389
  });
389
390
  rc.mcs.clear();
390
391
  }
@@ -406,6 +407,14 @@ async function render(node, options, write, writeJS, componentMode) {
406
407
  runtimeFlag = runtime;
407
408
  }
408
409
  }
410
+ if (componentMode) {
411
+ const [tag, props] = node;
412
+ if (typeof tag === "function") {
413
+ await renderFC(rc, tag, props, true);
414
+ await finalize();
415
+ return;
416
+ }
417
+ }
409
418
  await renderNode(rc, node);
410
419
  if (rc.flags.scope > 0 && !componentMode) {
411
420
  markSignals(rc, signals.app);
@@ -650,7 +659,7 @@ async function renderNode(rc, node, stripSlotProp) {
650
659
  const write2 = (chunk) => {
651
660
  attrModifiers += chunk;
652
661
  };
653
- renderSignal({ ...rc, write: write2 }, signalValue, [propName]);
662
+ renderSignal({ ...rc, write: write2 }, signalValue, [propName === "$value" ? "value" : propName]);
654
663
  rc.flags.runtime |= RENDER_ATTR;
655
664
  }
656
665
  buffer += attr;
@@ -693,32 +702,31 @@ function renderAttr(rc, attrName, attrValue, stripSlotProp) {
693
702
  let attr = "";
694
703
  let addonHtml = "";
695
704
  let signalValue;
705
+ let scopeId = rc.fcCtx?.scopeId;
696
706
  if (isObject(attrValue)) {
697
707
  let signal;
698
708
  if (isSignal(attrValue)) {
699
709
  signal = attrValue;
700
710
  } else {
701
- const { fcCtx } = rc;
702
- if (fcCtx) {
711
+ if (scopeId) {
703
712
  const deps = /* @__PURE__ */ new Set();
704
713
  const patches = [];
705
714
  const staticProps = traverseProps(attrValue, (path, value) => {
706
715
  const { scope, key } = value[$signal];
707
716
  if (isString(key)) {
708
717
  patches.push([
709
- (scope !== fcCtx.scopeId ? "$signals(" + scope + ")" : "this") + "[" + stringify(key) + "]",
718
+ (scope !== scopeId ? "$signals(" + scope + ")" : "this") + "[" + stringify(key) + "]",
710
719
  ...path
711
720
  ].join(","));
712
721
  deps.add(scope + ":" + key);
713
722
  } else {
714
- patches.push(["(" + key.compute.toString() + ")(),", ...path].join(","));
723
+ patches.push(["(" + String(key.compute) + ")(),", ...path].join(","));
715
724
  for (const dep of key.deps) {
716
725
  deps.add(dep);
717
726
  }
718
727
  }
719
728
  });
720
729
  if (patches.length > 0) {
721
- const { scopeId } = fcCtx;
722
730
  const compute = "()=>$patch(" + stringify(staticProps) + ",[" + patches.join("],[") + "])";
723
731
  signal = Signal(scopeId, { compute, deps }, staticProps);
724
732
  }
@@ -765,7 +773,7 @@ function renderAttr(rc, attrName, attrValue, stripSlotProp) {
765
773
  } else {
766
774
  const refId = rc.fcCtx.refs++;
767
775
  const effects = signals[Symbol.for("effects")];
768
- effects.push("()=>(" + attrValue.toString() + ')(this.refs["' + refId + '"])');
776
+ effects.push("()=>(" + String(attrValue) + ')(this.refs["' + refId + '"])');
769
777
  attr = " data-ref=" + toAttrStringLit(rc.fcCtx.scopeId + ":" + refId);
770
778
  }
771
779
  } else if (attrValue instanceof Ref) {
@@ -774,8 +782,8 @@ function renderAttr(rc, attrName, attrValue, stripSlotProp) {
774
782
  break;
775
783
  case "action":
776
784
  if (typeof attrValue === "function") {
777
- const scopeId = rc.fcCtx?.scopeId;
778
- attr = ' onsubmit="$onsubmit(event,$MF_' + (scopeId ?? 0) + "_" + rc.mfs.gen(attrValue, scopeId) + toStr(scopeId, (i) => "," + i) + ')"';
785
+ const scopeId2 = rc.fcCtx?.scopeId;
786
+ attr = ' onsubmit="$onsubmit(event,$MF_' + (scopeId2 ?? 0) + "_" + rc.mfs.gen(attrValue, scopeId2) + toStr(scopeId2, (i) => "," + i) + ')"';
779
787
  } else if (isString(attrValue)) {
780
788
  attr = " action=" + toAttrStringLit(attrValue);
781
789
  }
@@ -785,9 +793,20 @@ function renderAttr(rc, attrName, attrValue, stripSlotProp) {
785
793
  attr = " slot=" + toAttrStringLit(attrValue);
786
794
  }
787
795
  break;
796
+ case "$value":
797
+ attr = " value=" + toAttrStringLit(String(attrValue));
798
+ if (signalValue) {
799
+ const { key } = signalValue[$signal];
800
+ if (isString(key)) {
801
+ const fn = () => {
802
+ };
803
+ fn.str = "e=>this[" + toAttrStringLit(key) + "]=e.target.value";
804
+ attr += ' oninput="$emit(event,$MF_' + (scopeId ?? 0) + "_" + rc.mfs.gen(fn, scopeId) + toStr(scopeId, (i) => "," + i) + ')"';
805
+ }
806
+ }
807
+ break;
788
808
  default:
789
809
  if (attrName.startsWith("on") && typeof attrValue === "function") {
790
- const scopeId = rc.fcCtx?.scopeId;
791
810
  attr = " " + escapeHTML(attrName.toLowerCase()) + '="$emit(event,$MF_' + (scopeId ?? 0) + "_" + rc.mfs.gen(attrValue, scopeId) + toStr(scopeId, (i) => "," + i) + ')"';
792
811
  } else if (attrValue === false || attrValue === null || attrValue === void 0) {
793
812
  } else {
@@ -799,7 +818,7 @@ function renderAttr(rc, attrName, attrValue, stripSlotProp) {
799
818
  }
800
819
  return [attr, addonHtml, signalValue];
801
820
  }
802
- async function renderFC(rc, fc, props) {
821
+ async function renderFC(rc, fc, props, eager) {
803
822
  const { write } = rc;
804
823
  const { children } = props;
805
824
  const scopeId = ++rc.flags.scope;
@@ -810,7 +829,7 @@ async function renderFC(rc, fc, props) {
810
829
  const v = fc.call(signals, props);
811
830
  if (isObject(v) && !isVNode(v)) {
812
831
  if (v instanceof Promise) {
813
- if (rc.eager || (props.rendering ?? fc.rendering) === "eager") {
832
+ if (eager || (props.rendering ?? fc.rendering) === "eager") {
814
833
  await renderNode({ ...rc, fcCtx }, await v);
815
834
  markSignals(rc, signals);
816
835
  } else {
@@ -832,7 +851,7 @@ async function renderFC(rc, fc, props) {
832
851
  }));
833
852
  }
834
853
  } else if (Symbol.asyncIterator in v) {
835
- if (rc.eager || (props.rendering ?? fc.rendering) === "eager") {
854
+ if (eager || (props.rendering ?? fc.rendering) === "eager") {
836
855
  for await (const c of v) {
837
856
  await renderNode({ ...rc, fcCtx }, c);
838
857
  }
@@ -950,7 +969,7 @@ function createSignals(scopeId, appSignals, context = new NullProtoObj(), reques
950
969
  return Signal(scopeId, { compute, deps }, value);
951
970
  };
952
971
  const markEffect = (effect) => {
953
- effects.push(effect.toString());
972
+ effects.push(String(effect));
954
973
  };
955
974
  const mark = ({ signals: signals2, write }) => {
956
975
  if (effects.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
4
4
  "description": "`<html>` as a `Response`.",
5
5
  "type": "module",
6
6
  "module": "./index.mjs",
package/types/html.d.ts CHANGED
@@ -185,7 +185,7 @@ export namespace HTML {
185
185
  src?: string;
186
186
  step?: number | string;
187
187
  type?: InputType;
188
- value?: string | ReadonlyArray<string> | number;
188
+ value?: string | number | ReadonlyArray<string | number>;
189
189
  width?: number | string;
190
190
  /**
191
191
  * Turns a <button> or <input> element into a popover control button; takes the ID of the popover element to control as its value.
@@ -197,8 +197,15 @@ export namespace HTML {
197
197
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Popover_API
198
198
  */
199
199
  popovertargetaction?: "hide" | "show" | "toggle";
200
- // for type="search"
200
+ /**
201
+ * For type="search"
202
+ */
201
203
  onSearch?: EventHandler<Event, T>;
204
+ /**
205
+ * @mono-jsx: Two-way binding prop that automatically creates value and oninput attributes for signal binding
206
+ * Similar to Vue's v-model
207
+ */
208
+ $value?: string | number | ReadonlyArray<string | number>;
202
209
  }
203
210
 
204
211
  interface OptionAttributes<T extends EventTarget> extends GlobalAttributes<T> {
@@ -216,7 +223,12 @@ export namespace HTML {
216
223
  name?: string;
217
224
  required?: boolean;
218
225
  size?: number;
219
- value?: string | ReadonlyArray<string> | number;
226
+ value?: string | number | ReadonlyArray<string | number>;
227
+ /**
228
+ * @mono-jsx: Two-way binding prop that automatically creates value and oninput attributes for signal binding
229
+ * Similar to Vue's v-model
230
+ */
231
+ $value?: string | number | ReadonlyArray<string | number>;
220
232
  }
221
233
 
222
234
  interface TextareaAttributes<T extends EventTarget> extends GlobalAttributes<T> {
@@ -235,6 +247,11 @@ export namespace HTML {
235
247
  value?: string;
236
248
  wrap?: string;
237
249
  onChange?: EventHandler<Event, T>;
250
+ /**
251
+ * @mono-jsx: Two-way binding prop that automatically creates value and oninput attributes for signal binding
252
+ * Similar to Vue's v-model
253
+ */
254
+ $value?: string;
238
255
  }
239
256
 
240
257
  interface ButtonAttributes<T extends EventTarget> extends GlobalAttributes<T> {
package/types/mono.d.ts CHANGED
@@ -3,15 +3,37 @@ import type * as CSS from "./css.d.ts";
3
3
  type D9 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
4
4
  type D100 = 0 | D9 | `${D9}${0 | D9}` | 100;
5
5
 
6
- export interface BaseCSSProperties extends CSS.Properties<string | number> {}
6
+ export interface BaseCSSProperties extends CSS.Properties<string | number> {
7
+ /**
8
+ * The field-sizing CSS property enables you to control the sizing behavior of elements that are given a default preferred size, such as form control elements. This property enables you to override the default sizing behavior, allowing form controls to adjust in size to fit their contents.
9
+ * @see https://developer.mozilla.org/docs/Web/CSS/field-sizing
10
+ */
11
+ fieldSizing?: "fixed" | "content";
12
+ /**
13
+ * The view-transition-class CSS property provides the selected elements with an identifying class (a <custom-ident>), providing an additional method of styling the view transitions for those elements.
14
+ * @see https://developer.mozilla.org/docs/Web/CSS/view-transition-class
15
+ */
16
+ "view-transition-class"?: string;
17
+ /**
18
+ * The view-transition-name CSS property specifies the view transition snapshot that selected elements will participate in, which enables an element to be animated separately from the rest of the page during a view transition.
19
+ * @see https://developer.mozilla.org/docs/Web/CSS/view-transition-name
20
+ */
21
+ "view-transition-name"?: string;
22
+ }
7
23
 
8
24
  export interface AtRuleCSSProperties {
9
25
  [key: `@container${" " | "("}${string}`]: BaseCSSProperties;
10
26
  [key: `@media${" " | "("}${string}`]: BaseCSSProperties;
11
- [key: `@supports${" " | "("}${string}`]: BaseCSSProperties;
12
27
  [key: `@keyframes ${string}`]: {
13
28
  [key in "from" | "to" | `${D100}%`]?: BaseCSSProperties;
14
29
  };
30
+ [key: `@supports${" " | "("}${string}`]: BaseCSSProperties;
31
+ "@view-transition"?: {
32
+ /**
33
+ * Specifies the effect this at-rule will have on the document's view transition behavior.
34
+ */
35
+ navigation?: "auto" | "none";
36
+ };
15
37
  }
16
38
 
17
39
  export interface PseudoCSSProperties {