mono-jsx 0.8.0-beta.13 → 0.8.0-beta.15

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
@@ -1103,12 +1103,12 @@ async function Login(this: FC) {
1103
1103
  You can also return regular HTML elements from the route form post response. The `formslot` element is used to
1104
1104
  mark the position where the returned HTML elements will be inserted.
1105
1105
 
1106
- - `<formslot mode="insertbefore" />`: Insert HTML before the `formslot` element.
1106
+ - `<formslot mode="replaceChildren" />`: Replace the `formslot` element's children with the HTML. This is the default mode.
1107
1107
  - `<formslot mode="insertafter" />`: Insert HTML after the `formslot` element.
1108
- - `<formslot mode="replace" />`: Replace the `formslot` children. This is the default mode.
1108
+ - `<formslot mode="insertbefore" />`: Insert HTML before the `formslot` element.
1109
1109
 
1110
1110
  ```tsx
1111
- function FormSlot(this: FC) {
1111
+ function MyFormPage(this: FC) {
1112
1112
  if (this.form) {
1113
1113
  const message = this.form.get("message") as string | null;
1114
1114
  if (!message) {
@@ -1117,12 +1117,30 @@ function FormSlot(this: FC) {
1117
1117
  return <p>{message}</p>
1118
1118
  }
1119
1119
  return (
1120
- <form route>
1121
- {/* <- new message will be inserted here */}
1122
- <formslot mode="insertbefore" />
1123
- <input type="text" name="message" placeholder="Type Message..." style={{ ":invalid": { borderColor: "red" } }} />
1124
- <button type="submit">Send</button>
1125
- </form>
1120
+ <form route>
1121
+ {/* <- new message will be inserted here */}
1122
+ <formslot mode="insertbefore" />
1123
+ <input type="text" name="message" placeholder="Type Message..." style={{ ":invalid": { borderColor: "red" } }} />
1124
+ <button type="submit">Send</button>
1125
+ </form>
1126
+ )
1127
+ }
1128
+ ```
1129
+
1130
+ You can also use the `name` attribute to specify the name of the formslot element. And you can use the `slot` attribute to specify the name of the slot to insert the HTML into.
1131
+
1132
+ ```tsx
1133
+ function MyFormPage(this: FC) {
1134
+ if (this.form) {
1135
+ return <p slot="message">Hello, world!</p>
1136
+ }
1137
+ return (
1138
+ <div>
1139
+ <formslot name="message" />
1140
+ <form route>
1141
+ <button type="submit">Send</button>
1142
+ </form>
1143
+ </div>
1126
1144
  )
1127
1145
  }
1128
1146
  ```
package/jsx-runtime.mjs CHANGED
@@ -30,7 +30,7 @@ var SIGNALS_JS = `{let m;const w=window,p=document,y=new Map,k=new Map,l=n=>k.ge
30
30
  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())})});}`;
31
31
  var COMPONENT_JS = `{const e=document,a=(t,s)=>t.getAttribute(s);customElements.define("m-component",class extends HTMLElement{static observedAttributes=["name","props"];#t;#s;#r;#h;#i;#e=new Map;#a=!0;async#l(){if(!this.#t){this.#n("");return}const t=this.#s||"{}",s=this.#t+t,i={"x-component":this.#t,"x-props":t,"x-flags":$FLAGS},n=new AbortController;if(this.#h?.abort(),this.#h=n,this.#e.has(s)){this.#n(this.#e.get(s));return}this.#r?.length&&this.#n(this.#r);const r=await fetch(location.href,{headers:i,signal:n.signal});if(!r.ok)throw this.#n(""),new Error("Failed to fetch component '"+this.#t+"'");const[h,o]=await r.json();this.#e.set(s,h),this.#n(h),o&&(e.body.appendChild(e.createElement("script")).textContent=o)}#n(t){const s=()=>typeof t=="string"?this.innerHTML=t:this.replaceChildren(...t);this.hasAttribute("vt")&&e.startViewTransition&&!this.#a?e.startViewTransition(s):s(),this.#a=!1}get name(){return this.#t??null}set name(t){t&&t!==this.#t&&(this.#t=t,this.#o())}get props(){return this.#s?JSON.parse(this.#s):void 0}set props(t){const s=typeof t=="string"?t:JSON.stringify(t);s&&s!==this.#s&&(this.#s=s,this.#o())}attributeChangedCallback(t,s,i){this.#t&&i&&(t==="name"?this.name=i:t==="props"&&(this.props=i))}connectedCallback(){setTimeout(()=>{if(!this.#r){const t=a(this,"props");this.#t=a(this,"name"),this.#s=t?.startsWith("base64,")?atob(t.slice(7)):void 0,this.#r=[...this.childNodes]}this.#l()})}disconnectedCallback(){this.#e.clear(),this.#h?.abort(),this.#h=void 0,this.#i&&clearTimeout(this.#i),this.#i=void 0}#o(){this.#i&&clearTimeout(this.#i),this.#i=setTimeout(()=>{this.#i=void 0,this.#l()},50)}refresh(){this.#t&&this.#e.delete(this.#t+(this.#s||"{}")),this.#o()}});}`;
32
32
  var ROUTER_JS = `{const n=document,a=location,o=t=>t.split("#",1)[0],l=t=>o(t)===o(a.href);customElements.define("m-router",class extends HTMLElement{#t;#s;#i;#n;#e=new Map;#r=!0;async#c(t){const s=new AbortController,i={"x-route":"true","x-flags":$FLAGS};this.#n?.abort(),this.#n=s;const e=await fetch(t,{headers:i,signal:s.signal});if(e.status===404)return null;if(!e.ok)throw this.replaceChildren(),new Error("Failed to fetch route: "+e.status+" "+e.statusText);return e.json()}#a(t){const s=()=>typeof t=="string"?this.innerHTML=t:this.replaceChildren(...t);this.hasAttribute("vt")&&n.startViewTransition&&!this.#r?n.startViewTransition(s):s(),this.#r=!1}#o(){n.querySelectorAll("nav a").forEach(t=>{const{href:s,classList:i}=t,e=t.closest("nav")?.getAttribute("data-active-class")??"active";l(s)?i.add(e):i.remove(e)})}async#l(t,s){const i=this.#c(t).then(e=>{if(e){const[r,c]=e;return this.#e.set(t,r),this.#a(r),c}else this.#e.delete(t),this.#a(this.#t??[]),typeof $signals<"u"&&($signals(0).url=new URL(t))});this.#e.has(t)?this.#a(this.#e.get(t)):await i,history[s?.replace?"replaceState":"pushState"]({},"",t),this.#o(),window.scrollTo(0,0),i.then(e=>{e&&(n.body.appendChild(n.createElement("script")).textContent=e)})}navigate(t,s){const i=new URL(t,a.href);if(i.origin!==a.origin){a.href=t;return}l(i.href)||this.#l(t,s)}connectedCallback(){setTimeout(()=>{if(!this.#t)if(this.hasAttribute("fallback"))this.removeAttribute("fallback"),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.#s=t=>{if(t.defaultPrevented||t.altKey||t.ctrlKey||t.metaKey||t.shiftKey||!(t.target instanceof HTMLAnchorElement))return;const{download:s,href:i,rel:e,target:r}=t.target;s||e==="external"||r==="_blank"||!i.startsWith(a.origin)||(t.preventDefault(),this.navigate(i))},this.#i=()=>this.#l(a.href),addEventListener("popstate",this.#i),n.addEventListener("click",this.#s),setTimeout(()=>this.#o()),globalThis.$router=this}disconnectedCallback(){removeEventListener("popstate",this.#i),n.removeEventListener("click",this.#s),delete globalThis.$router,this.#n?.abort(),this.#n=void 0,this.#e.clear(),this.#s=void 0,this.#i=void 0}});}`;
33
- var FORM_JS = `{const m=(t,e)=>t.setCustomValidity(e),l=(t,e,i)=>t.insertAdjacentHTML(e,i);customElements.define("m-invalid",class extends HTMLElement{connectedCallback(){const t=this.getAttribute("for"),e=this.closest("form"),i=this.textContent;if(t&&e&&i)for(const r of t.split(",")){const s=e.elements.namedItem(r.trim());if(s){const o=()=>{s.removeEventListener("input",o),m(s,"")};s.addEventListener("input",o),m(s,i),s.focus()}}this.remove()}}),window.$onrfs=async t=>{t.preventDefault();const e=t.target;if(!e.checkValidity())return;const i=new FormData(e),r=[...e.elements];for(const n of r)n._disabled=n.disabled,n.disabled=!0;const s=await fetch(location.href,{method:"POST",headers:{"x-route-form":"true","x-flags":$FLAGS},body:i}),[o,d]=await s.json(),a=e.querySelector("m-formslot");for(const n of r)n.disabled=n._disabled,delete n._disabled;if(a)switch(a.getAttribute("mode")){case"insertbefore":l(a,"beforebegin",o);break;case"insertafter":l(a,"afterend",o);break;default:a.innerHTML=o}else l(e,"beforeend",o);setTimeout(()=>{r.some(n=>!n.validity.valid)||e.reset()},0),d&&(document.body.appendChild(document.createElement("script")).textContent=d)};}`;
33
+ var FORM_JS = `{customElements.define("m-invalid",class extends HTMLElement{connectedCallback(){const s=this.getAttribute("for"),t=this.closest("form"),i=this.textContent;if(s&&t&&i)for(const l of s.split(",")){const o=t.elements.namedItem(l.trim());if(o){const r=()=>{o.removeEventListener("input",r),o.setCustomValidity("")};o.addEventListener("input",r),o.setCustomValidity(i),o.focus()}}this.remove()}}),window.$onrfs=async s=>{s.preventDefault();const t=s.target;if(!t.checkValidity())return;const i=new FormData(t),l=[...t.elements];for(const e of l)e._disabled=e.disabled,e.disabled=!0;const o=await fetch(location.href,{method:"POST",headers:{"x-route-form":"true","x-flags":$FLAGS},body:i}),[r,m]=await o.json(),f=document.createElement("template"),u=new Map;f.innerHTML=r;for(const e of l)e.disabled=e._disabled,delete e._disabled;for(const e of f.content.childNodes){if(e.nodeType===1){const n=e,a=n.getAttribute("formslot"),d=a?'m-formslot[name="'+a+'"]':"m-formslot",c=a?t.querySelector(d)??document.querySelector(d):t.querySelector(d);if(c){c.innerHTML="",u.set(n,c);continue}}t.appendChild(e)}for(const[e,n]of u)switch(n.getAttribute("mode")){case"insertbefore":n.before(e);break;case"insertafter":n.after(e);break;default:n.appendChild(e)}setTimeout(()=>{l.some(e=>!e.validity.valid)||t.reset()},0),m&&(document.body.appendChild(document.createElement("script")).textContent=m)};}`;
34
34
 
35
35
  // runtime/utils.ts
36
36
  var regexpIsNonDimensional = /^(-|f[lo].*[^se]$|g.{5,}[^ps]$|z|o[pr]|(W.{5})?[lL]i.*(t|mp)$|an|(bo|s).{4}Im|sca|m.{6}[ds]|ta|c.*[st]$|wido|ini)/;
@@ -176,7 +176,7 @@ var $vnode = Symbol.for("jsx.vnode");
176
176
  var $setup = Symbol.for("mono.setup");
177
177
 
178
178
  // version.ts
179
- var VERSION = "0.8.0-beta.13";
179
+ var VERSION = "0.8.0-beta.15";
180
180
 
181
181
  // render.ts
182
182
  var cdn = "https://raw.esm.sh";
@@ -745,7 +745,9 @@ async function renderNode(rc, node, stripSlotProp) {
745
745
  const now = Date.now();
746
746
  const value = cache.get(key);
747
747
  if (value && (!value.expires || value.expires > now)) {
748
+ write("<!-- cached -->");
748
749
  write(value.html);
750
+ write("<!-- /cached -->");
749
751
  } else {
750
752
  let buf = "";
751
753
  await renderChildren(
@@ -778,16 +780,19 @@ async function renderNode(rc, node, stripSlotProp) {
778
780
  }
779
781
  case "invalid":
780
782
  case "formslot": {
781
- const { children, for: forProp, mode } = props;
783
+ const { children, name, mode, for: forProp } = props;
782
784
  let buf = "<m-" + tag;
785
+ if (isString(name)) {
786
+ buf += " name=" + toAttrStringLit(name);
787
+ }
788
+ if (isString(mode)) {
789
+ buf += " mode=" + toAttrStringLit(mode);
790
+ }
783
791
  if (isString(forProp)) {
784
792
  buf += " for=" + toAttrStringLit(forProp) + " hidden";
785
793
  } else if (tag === "invalid") {
786
794
  break;
787
795
  }
788
- if (isString(mode)) {
789
- buf += " mode=" + toAttrStringLit(mode);
790
- }
791
796
  buf += ">";
792
797
  if (children) {
793
798
  await renderChildren({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx",
3
- "version": "0.8.0-beta.13",
3
+ "version": "0.8.0-beta.15",
4
4
  "description": "`<html>` as a `Response`.",
5
5
  "type": "module",
6
6
  "module": "./index.mjs",
package/types/jsx.d.ts CHANGED
@@ -18,6 +18,11 @@ export interface BaseAttributes {
18
18
  * The `slot` attribute assigns a slot in a [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM) shadow tree to an element: An element with a `slot` attribute is assigned to the slot created by the `<slot>` element whose name attribute's value matches that slot attribute's value.
19
19
  */
20
20
  slot?: string;
21
+ /**
22
+ * The `formslot` attribute assigns a named formslot element to an element.
23
+ * @mono-jsx
24
+ */
25
+ formslot?: string;
21
26
  /**
22
27
  * The `portal` attribute is used to mount the component to a specified DOM element.
23
28
  * @mono-jsx
package/types/mono.d.ts CHANGED
@@ -151,9 +151,17 @@ export interface Elements {
151
151
  */
152
152
  formslot: BaseAttributes & {
153
153
  /**
154
- * The insert position of the formslot.
154
+ * The name of the formslot element.
155
155
  */
156
- mode?: "insertbefore" | "insertafter" | "replace";
156
+ name?: string;
157
+ /**
158
+ * The insert mode of the formslot.
159
+ * - "insertbefore": Insert HTML before the formslot element.
160
+ * - "insertafter": Insert HTML after the formslot element.
161
+ * - "replaceChildren": Replace the formslot element's children with the HTML.
162
+ * @default "replaceChildren"
163
+ */
164
+ mode?: "insertbefore" | "insertafter" | "replaceChildren";
157
165
  };
158
166
  }
159
167