@excom/super-form 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.
Files changed (35) hide show
  1. package/.rush/temp/chunked-rush-logs/super-form.apply-exports.chunks.jsonl +1 -0
  2. package/.rush/temp/chunked-rush-logs/super-form.build_docs.chunks.jsonl +1 -0
  3. package/.rush/temp/chunked-rush-logs/super-form.build_package-metas.chunks.jsonl +1 -0
  4. package/.rush/temp/operation/apply-exports/all.log +1 -0
  5. package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
  6. package/.rush/temp/operation/apply-exports/state.json +3 -0
  7. package/.rush/temp/operation/build_docs/all.log +1 -0
  8. package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
  9. package/.rush/temp/operation/build_docs/state.json +3 -0
  10. package/.rush/temp/operation/build_package-metas/all.log +1 -0
  11. package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
  12. package/.rush/temp/operation/build_package-metas/state.json +3 -0
  13. package/.rush/temp/shrinkwrap-deps.json +3 -0
  14. package/config/rig.json +5 -0
  15. package/index.ts +17 -0
  16. package/package.json +46 -0
  17. package/rush-logs/super-form.apply-exports.cache.log +1 -0
  18. package/rush-logs/super-form.apply-exports.log +1 -0
  19. package/rush-logs/super-form.build_docs.cache.log +1 -0
  20. package/rush-logs/super-form.build_docs.log +1 -0
  21. package/rush-logs/super-form.build_package-metas.cache.log +1 -0
  22. package/rush-logs/super-form.build_package-metas.log +1 -0
  23. package/super-form.ts +100 -0
  24. package/support/custom-elements.json +107 -0
  25. package/support/demos/external-trigger.html +43 -0
  26. package/support/demos/simple.html +31 -0
  27. package/support/dist-docs/super-form.md +212 -0
  28. package/support/docs/README.md +61 -0
  29. package/support/package-meta.json +224 -0
  30. package/support/tests/__snapshots__/external-trigger.view.test.ts.snap +23 -0
  31. package/support/tests/__snapshots__/simple.view.test.ts.snap +23 -0
  32. package/support/tests/external-trigger.view.test.ts +57 -0
  33. package/support/tests/simple.view.test.ts +63 -0
  34. package/support/tests/super-form.test.ts +319 -0
  35. package/tsconfig.json +5 -0
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 39.44651799999997
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 57.36623900000018
3
+ }
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
@@ -0,0 +1 @@
1
+ {"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "nonCachedDurationMs": 106.06559299999981
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "../../packages/super-form": "../../packages/super-form:fgUo1XbKfCrQYlJAtIvTnfsOnmy+P0W0FMZOE7XPyBk=:"
3
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json",
3
+ "rigPackageName": "@excom/heft-rig",
4
+ "rigProfile": "default"
5
+ }
package/index.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { SuperForm } from "./super-form";
2
+
3
+ SuperForm.define();
4
+
5
+ export { SuperForm };
6
+
7
+ type T_HTMLSuperFormElement = typeof SuperForm.CustomElement;
8
+ declare global {
9
+ interface HTMLSuperFormElement extends T_HTMLSuperFormElement {}
10
+ interface Window {
11
+ HTMLSuperFormElement: HTMLSuperFormElement;
12
+ }
13
+ interface HTMLElementTagNameMap {
14
+ "super-form": HTMLSuperFormElement;
15
+ }
16
+ }
17
+ export type { HTMLSuperFormElement };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@excom/super-form",
3
+ "version": "0.1.0",
4
+ "description": "<super-form> custom element",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=24.13.0"
8
+ },
9
+ "type": "module",
10
+ "dependencies": {
11
+ "@excom/fetchable-element": "^0.1.0",
12
+ "@excom/neutron": "^0.1.0"
13
+ },
14
+ "peerDependencies": {},
15
+ "devDependencies": {
16
+ "@excom/heft-rig": "^0.1.0",
17
+ "@excom/kit-logger": "^0.1.0",
18
+ "@excom/quark": "^0.1.0",
19
+ "@excom/quark-sheet": "^0.1.0"
20
+ },
21
+ "repository": {
22
+ "url": "excom-dev/nucleus",
23
+ "directory": "packages/super-form"
24
+ },
25
+ "homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/super-form/support/docs/README.md",
26
+ "bugs": "https://github.com/excom-dev/nucleus/issues",
27
+ "keywords": [
28
+ "super-form",
29
+ "neutron",
30
+ "custom-elements"
31
+ ],
32
+ "excom": {
33
+ "packageType": "kit-element"
34
+ },
35
+ "scripts": {
36
+ "build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
37
+ "build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
38
+ "format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
39
+ "test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
40
+ "coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
41
+ "dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
42
+ "preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
43
+ "build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
44
+ "build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
45
+ }
46
+ }
@@ -0,0 +1 @@
1
+ Caching has been disabled for this project's "apply-exports" command.
@@ -0,0 +1 @@
1
+ Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
@@ -0,0 +1 @@
1
+ This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
@@ -0,0 +1 @@
1
+ Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
package/super-form.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { FetchableElement } from "@excom/fetchable-element";
2
+ import { Neutron, TEvent } from "@excom/neutron";
3
+
4
+ export type FetchArgs = [url: string, requestInit: RequestInit];
5
+
6
+ export type SuperFormSubmitEvent = TEvent & {
7
+ type: "super-form-submit";
8
+ detail: FetchArgs;
9
+ };
10
+
11
+ /** Native form `submit` this element intercepts, not a Neutron emit. */
12
+ export type SuperFormNativeSubmitEvent = SubmitEvent & {
13
+ type: "submit";
14
+ bubbles: true;
15
+ cancelable: true;
16
+ composed: false;
17
+ };
18
+
19
+ /**
20
+ * Turns a plain `<form>` into a zero-JS `fetch()` submit — intercepts
21
+ * `submit`, converts the form's fields into a JSON payload (see
22
+ * `formToJson()` below), and sends it to the form's `action` (or
23
+ * `api-url` / `api-method`, if no `form-ref` form is found). Composes
24
+ * `FetchableElement`, so `is-loading` / `is-success` / `is-error` / `provision`
25
+ * and every request-building attribute (headers, credentials, …) are
26
+ * inherited — see that package's docs for the full list.
27
+ *
28
+ * Form field `name`s become JSON keys: dot-separated names nest
29
+ * (`address.city` → `{ address: { city } }`) and a trailing `[]` collects
30
+ * same-named fields into an array (`tags[]` → `{ tags: [...] }`).
31
+ *
32
+ * @summary Zero-JS `fetch()` submit for a plain `<form>`.
33
+ *
34
+ * @fires super-form-submit - Internal — dispatched whenever a submit is
35
+ * about to run (real `submit` or the `--submit` command). Built by
36
+ * `getFetchArgs()`. Cancelable; default action calls `doFetch()`.
37
+ * @type SuperFormSubmitEvent
38
+ * @listens submit - The default action of the `<form>` matched by
39
+ * `form-ref` (or any descendant `<form>`); prevented, then converted
40
+ * into `super-form-submit`.
41
+ * @type SuperFormNativeSubmitEvent
42
+ * @command --submit - Submits programmatically (`<button command="--submit"
43
+ * commandfor="…">`) — the only option when `form-ref` points to a form
44
+ * that isn't a descendant, since this element can't hear its `submit`
45
+ * event directly.
46
+ * @default-action super-form-submit - Calls `doFetch(url, requestInit)`
47
+ * with the event's detail.
48
+ *
49
+ * @example
50
+ * <super-form>
51
+ * <form action="/api/signup" method="post">
52
+ * <input name="email" type="email" required>
53
+ * <button type="submit">Sign up</button>
54
+ * </form>
55
+ * </super-form>
56
+ */
57
+ export const SuperForm = Neutron.compose([
58
+ FetchableElement,
59
+ Neutron({
60
+ tag: "super-form",
61
+ props: {
62
+ /**
63
+ * @option
64
+ * CSS selector for the `<form>` to intercept. The form's
65
+ * `action` / `method` / `enctype` take priority over `api-url` /
66
+ * `api-method` below. Must be a descendant to be heard directly —
67
+ * point elsewhere and invoke the `--submit` command instead.
68
+ * @default :scope form
69
+ * @values <CSS Selector>
70
+ */
71
+ formRef: {
72
+ type: String,
73
+ // Non-descendant form: this element won't hear `submit`; invoke `--submit`
74
+ defaultValue: () => ":scope form",
75
+ },
76
+ /**
77
+ * @option
78
+ * HTTP method used when the form itself has no `method`.
79
+ * @default POST
80
+ */
81
+ apiMethod: {
82
+ type: String,
83
+ defaultValue: () => "POST",
84
+ },
85
+ },
86
+ }),
87
+ ])
88
+ .onEvent("submit", ({ getFetchArgs }, e) => {
89
+ // Block the native submit
90
+ e.preventDefault();
91
+ return {
92
+ emit: ["super-form-submit", { detail: [getFetchArgs()] }],
93
+ };
94
+ })
95
+ .onCommand("--submit", ({ getFetchArgs }) => ({
96
+ emit: ["super-form-submit", { detail: [getFetchArgs()] }],
97
+ }))
98
+ .onEventDefault("super-form-submit", (_, { detail }) => ({
99
+ doFetch: [detail[0], detail[1]],
100
+ }));
@@ -0,0 +1,107 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "modules": [
4
+ {
5
+ "kind": "javascript-module",
6
+ "path": "super-form.ts",
7
+ "declarations": [
8
+ {
9
+ "kind": "class",
10
+ "name": "SuperForm",
11
+ "customElement": true,
12
+ "tagName": "super-form",
13
+ "mixins": [
14
+ {
15
+ "name": "FetchableElement",
16
+ "package": "@excom/fetchable-element"
17
+ }
18
+ ],
19
+ "summary": "Zero-JS `fetch()` submit for a plain `<form>`.",
20
+ "description": "Native form `submit` this element intercepts, not a Neutron emit.",
21
+ "attributes": [
22
+ {
23
+ "name": "form-ref",
24
+ "type": {
25
+ "text": "string"
26
+ },
27
+ "description": "CSS selector for the `<form>` to intercept. The form's `action` / `method` / `enctype` take priority over `api-url` / `api-method` below. Must be a descendant to be heard directly — point elsewhere and invoke the `--submit` command instead.",
28
+ "fieldName": "formRef",
29
+ "default": ":scope form",
30
+ "values": [
31
+ "<CSS Selector>"
32
+ ]
33
+ }
34
+ ],
35
+ "members": [
36
+ {
37
+ "kind": "field",
38
+ "name": "formRef",
39
+ "type": {
40
+ "text": "string"
41
+ },
42
+ "privacy": "public",
43
+ "readonly": false,
44
+ "description": "CSS selector for the `<form>` to intercept. The form's `action` / `method` / `enctype` take priority over `api-url` / `api-method` below. Must be a descendant to be heard directly — point elsewhere and invoke the `--submit` command instead.",
45
+ "default": ":scope form",
46
+ "_neutron": {
47
+ "surface": "option"
48
+ }
49
+ }
50
+ ],
51
+ "events": [
52
+ {
53
+ "name": "super-form-submit",
54
+ "description": "Internal — dispatched whenever a submit is about to run (real `submit` or the `--submit` command). Built by `getFetchArgs()`. Cancelable; default action calls `doFetch()`.",
55
+ "type": {
56
+ "text": "SuperFormSubmitEvent",
57
+ "expanded": "CustomEvent & { type: \"super-form-submit\"; detail: [url: string, requestInit: RequestInit]; bubbles: true; cancelable: true; composed: true }"
58
+ }
59
+ }
60
+ ],
61
+ "_neutron": {
62
+ "listens": [
63
+ {
64
+ "name": "submit",
65
+ "description": "The default action of the `<form>` matched by `form-ref` (or any descendant `<form>`); prevented, then converted into `super-form-submit`.",
66
+ "type": {
67
+ "text": "SuperFormNativeSubmitEvent",
68
+ "expanded": "SubmitEvent & { type: \"submit\"; bubbles: true; cancelable: true; composed: false; }"
69
+ }
70
+ }
71
+ ],
72
+ "commands": [
73
+ {
74
+ "name": "--submit",
75
+ "description": "Submits programmatically (`<button command=\"--submit\" commandfor=\"…\">`) — the only option when `form-ref` points to a form that isn't a descendant, since this element can't hear its `submit` event directly."
76
+ }
77
+ ],
78
+ "defaultActions": [
79
+ {
80
+ "name": "super-form-submit",
81
+ "description": "Calls `doFetch(url, requestInit)` with the event's detail."
82
+ }
83
+ ]
84
+ }
85
+ }
86
+ ],
87
+ "exports": [
88
+ {
89
+ "kind": "js",
90
+ "name": "SuperForm",
91
+ "declaration": {
92
+ "name": "SuperForm",
93
+ "module": "super-form.ts"
94
+ }
95
+ },
96
+ {
97
+ "kind": "custom-element-definition",
98
+ "name": "super-form",
99
+ "declaration": {
100
+ "name": "SuperForm",
101
+ "module": "super-form.ts"
102
+ }
103
+ }
104
+ ]
105
+ }
106
+ ]
107
+ }
@@ -0,0 +1,43 @@
1
+ <section>
2
+ <super-form id="demo-super-form-external" class="tag-article grid">
3
+ <form action="/api/echo" method="post">
4
+ <label>
5
+ Nickname
6
+ <input type="text" name="nickname" placeholder="John" required>
7
+ </label>
8
+ </form>
9
+ <ul></ul>
10
+ <ul></ul>
11
+ <template id="demo-super-form-item">
12
+ <li class="tag-code"></li>
13
+ </template>
14
+ </super-form>
15
+ <button type="button" command="--submit" commandfor="demo-super-form-external">
16
+ Save (outside the form)
17
+ </button>
18
+ <style>
19
+ #demo-super-form-external-trigger { max-width: none; }
20
+ #demo-super-form-external-trigger super-form {
21
+ &[is-loading] {
22
+ opacity: 0.5;
23
+ border: 1px dashed yellow;
24
+ }
25
+ &[is-success] { border: 1px solid green; }
26
+ &[is-error] {
27
+ border: 1px solid red;
28
+ &::before { content: "An error occurred."; }
29
+ }
30
+ ul:first-of-type::before { content: "Submitted JSON:"; }
31
+ ul:last-of-type::before { content: "Response data:"; }
32
+ li { display: block; }
33
+ }
34
+ </style>
35
+ <quark-sheet>
36
+ super-form[is-success] {
37
+ $res: prop("provision");
38
+ li { content: "#{index}: #{item}"; }
39
+ ul:first-of-type { content: iterate($res.body.json, "#demo-super-form-item"); }
40
+ ul:last-of-type { content: iterate($res, "#demo-super-form-item"); }
41
+ }
42
+ </quark-sheet>
43
+ </section>
@@ -0,0 +1,31 @@
1
+ <section>
2
+ <super-form>
3
+ <form action="/api/echo" method="post">
4
+ <input type="text" name="name" placeholder="Your name" required>
5
+ <button type="submit">Submit</button>
6
+ </form>
7
+ <h4></h4>
8
+ <span></span>
9
+ </super-form>
10
+ <quark-sheet>
11
+ super-form[is-success] {
12
+ $res: prop("provision");
13
+ h4 { content: "Echoed name: #{$res.body.json.name}"; }
14
+ }
15
+ super-form {
16
+ $res: prop("provision");
17
+ span { content: "HTTP status code: #{$res.status or ""}"; }
18
+ }
19
+ </quark-sheet>
20
+ <style>
21
+ #demo-super-form-simple super-form[is-loading] {
22
+ animation: pulse 1s linear infinite;
23
+ pointer-events: none;
24
+ }
25
+ @keyframes pulse {
26
+ 0% { opacity: 0.2; }
27
+ 50% { opacity: 0.6; }
28
+ 100% { opacity: 0.2; }
29
+ }
30
+ </style>
31
+ </section>
@@ -0,0 +1,212 @@
1
+ # super-form
2
+
3
+ Submit AJAX requests with HTML forms. Pair it with Quark to render the response.
4
+
5
+
6
+ ```html
7
+ <section>
8
+ <super-form>
9
+ <form action="/api/echo" method="post">
10
+ <input type="text" name="name" placeholder="Your name" required>
11
+ <button type="submit">Submit</button>
12
+ </form>
13
+ <h4></h4>
14
+ <span></span>
15
+ </super-form>
16
+ <quark-sheet>
17
+ super-form[is-success] {
18
+ $res: prop("provision");
19
+ h4 { content: "Echoed name: #{$res.body.json.name}"; }
20
+ }
21
+ super-form {
22
+ $res: prop("provision");
23
+ span { content: "HTTP status code: #{$res.status or ""}"; }
24
+ }
25
+ </quark-sheet>
26
+ <style>
27
+ #demo-super-form-simple super-form[is-loading] {
28
+ animation: pulse 1s linear infinite;
29
+ pointer-events: none;
30
+ }
31
+ @keyframes pulse {
32
+ 0% { opacity: 0.2; }
33
+ 50% { opacity: 0.6; }
34
+ 100% { opacity: 0.2; }
35
+ }
36
+ </style>
37
+ </section>
38
+ ```
39
+
40
+
41
+ ## Features
42
+
43
+ - **Makes AJAX requests** JSON payload is built from each input's `name` and `type` attributes
44
+ - **Progressively enhanced** Doesn't replace the native `form`; it enhances it. Everything you know about `form` and `input` still applies.
45
+ - **Provides data** Use Quark to render the response
46
+ - **Submit command** `--submit` submits programmatically (`<button command="--submit" commandfor="…">`)
47
+ - **Highly configurable** Headers, credentials, redirect, etc
48
+
49
+ ## Installation
50
+
51
+
52
+ `@excom/super-form` v0.1.0
53
+
54
+ ```bash
55
+ pnpm add @excom/super-form
56
+ ```
57
+
58
+ ```bash
59
+ npm install @excom/super-form
60
+ ```
61
+
62
+ ```bash
63
+ yarn add @excom/super-form
64
+ ```
65
+
66
+ ### Import
67
+
68
+ ```ts
69
+ import "@excom/super-form";
70
+ ```
71
+
72
+
73
+
74
+ ## Usage
75
+
76
+ Just wrap a regular form. Form fields become a JSON payload via their `name`: dot-separated names nest, and a trailing `[]` collects same-named fields into an array. `input[type]` determines the type conversion.
77
+
78
+ ```html
79
+ <super-form>
80
+ <form action="/api/signup" method="post">
81
+ <input name="isAvailable" type="checkbox"> <!-- -> { isAvailable: true } -->
82
+ <input name="address.city" value="Anytown"> <!-- -> { address: { city: "Anytown" } } -->
83
+ <input name="tags[]" value="smart">
84
+ <input name="tags[]" value="kind"> <!-- -> { tags: ["smart", "kind"] } -->
85
+ <button type="submit">Sign up</button>
86
+ </form>
87
+ </super-form>
88
+ ```
89
+
90
+ Hook the lifecycle state with CSS:
91
+
92
+ ```css
93
+ super-form[is-loading] { /* form currently submitting, show loading spinner */ }
94
+ super-form[is-error]::before { content: "An error occurred." }
95
+ ```
96
+
97
+ Or Quark:
98
+
99
+ ```quark
100
+ super-form[is-success] {
101
+ $res: prop("provision").body;
102
+ span { content: $res.json.email; }
103
+ }
104
+ ```
105
+
106
+ ### API Reference
107
+
108
+
109
+ #### Attributes
110
+
111
+ | Name | Surface | Type | Default | Values | Description | Inherited from |
112
+ | --- | --- | --- | --- | --- | --- | --- |
113
+ | `form-ref` | option | `string` | `":scope form"` | `<CSS Selector>` | CSS selector for the `<form>` to intercept. The form's `action` / `method` / `enctype` take priority over `api-url` / `api-method` below. Must be a descendant to be heard directly — point elsewhere and invoke the `--submit` command instead. | |
114
+ | `has-body` | option | `boolean` | | | Force a request body even for methods that don't imply one (`GET` / `HEAD`). Already implied for `POST` / `PUT` / `PATCH`. | `@excom/fetchable-element` |
115
+ | `api-url` | option | `string` | `""` | | Endpoint URL. When the request has no body, the JSON payload (from `form-ref` or custom `doFetch()` args) is merged in as query params instead. | `@excom/fetchable-element` |
116
+ | `api-method` | option | `string` | `"GET"` | | HTTP method. Always uppercased before the request is sent. | `@excom/fetchable-element` |
117
+ | `header-accept` | option | `string` | `"application/json"` | | `Accept` request header. | `@excom/fetchable-element` |
118
+ | `header-content-type` | option | `string` | `"application/json"` | | `Content-Type` request header. Dropped entirely when the request has no body. | `@excom/fetchable-element` |
119
+ | `header-cache-control` | option | `string` | | | `Cache-Control` request header. Unset by default (browser default caching applies). | `@excom/fetchable-element` |
120
+ | `fetch-redirect` | option | `string` | | `"follow"` \| `"error"` \| `"manual"` | `RequestInit.redirect` mode. Unset defers to the browser default (`follow`). | `@excom/fetchable-element` |
121
+ | `fetch-credentials` | option | `string` | `"include"` | `"omit"` \| `"same-origin"` \| `"include"` | `RequestInit.credentials` mode. | `@excom/fetchable-element` |
122
+ | `is-loading` | state | `boolean` | | | A request is currently in flight. | `@excom/fetchable-element` |
123
+ | `is-success` | state | `boolean` | | | The most recent request resolved successfully. Mutually exclusive with `is-error`. | `@excom/fetchable-element` |
124
+ | `is-error` | state | `boolean` | | | The most recent request failed (non-2xx status, network error, or a thrown error other than `AbortError`). Fires with the `error` event. | `@excom/fetchable-element` |
125
+
126
+ #### Provision
127
+
128
+ | Name | Type | Description | Inherited from |
129
+ | --- | --- | --- | --- |
130
+ | `provision` | `FetchResponse` (`{ bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }`) | Response payload on success, or error payload on failure. Success shape: `{ status, statusText, ok, headers, url, redirected, bodyUsed, type, body }`. Failure shape is either that same response shape (server responded with an error status) or `{ message, stack }` (request never completed). Not reflected as an attribute. | `@excom/fetchable-element` |
131
+
132
+ #### Fires
133
+
134
+ | Name | Type | Description | Inherited from |
135
+ | --- | --- | --- | --- |
136
+ | `super-form-submit` | `SuperFormSubmitEvent` (`CustomEvent & { type: "super-form-submit"; detail: [url: string, requestInit: RequestInit]; bubbles: true; cancelable: true; composed: true }`) | Internal — dispatched whenever a submit is about to run (real `submit` or the `--submit` command). Built by `getFetchArgs()`. Cancelable; default action calls `doFetch()`. | |
137
+ | `super-form-loading` | `FetchableLoadingEvent` (`CustomEvent & { type: "{tag}-loading"; detail: void; bubbles: true; cancelable: true; composed: true }`) | Dispatched immediately before the request is sent. | `@excom/fetchable-element` |
138
+ | `super-form-success` | `FetchableSuccessEvent` (`CustomEvent & { type: "{tag}-success"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched when the request resolves successfully. `event.detail` is the parsed response (see `provision`). | `@excom/fetchable-element` |
139
+ | `super-form-error` | `FetchableErrorEvent` (`CustomEvent & { type: "{tag}-error"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; } \| { message: string; stack?: string }; bubbles: true; cancelable: true; composed: true }`) | Dispatched when the request fails — non-2xx status, network error, or a thrown error. `event.detail` is the error payload (see `provision`). Not dispatched for aborted requests. | `@excom/fetchable-element` |
140
+
141
+ #### Listens for
142
+
143
+ | Name | Type | Description |
144
+ | --- | --- | --- |
145
+ | `submit` | `SuperFormNativeSubmitEvent` (`SubmitEvent & { type: "submit"; bubbles: true; cancelable: true; composed: false; }`) | The default action of the `<form>` matched by `form-ref` (or any descendant `<form>`); prevented, then converted into `super-form-submit`. |
146
+
147
+ #### Commands
148
+
149
+ | Command | Action |
150
+ | --- | --- |
151
+ | `--submit` | Submits programmatically (`<button command="--submit" commandfor="…">`) — the only option when `form-ref` points to a form that isn't a descendant, since this element can't hear its `submit` event directly. |
152
+
153
+ #### Default actions
154
+
155
+ | Event | Default behavior (unless preventDefault() is called) |
156
+ | --- | --- |
157
+ | `super-form-submit` | Calls `doFetch(url, requestInit)` with the event's detail. |
158
+
159
+
160
+
161
+ ### Examples
162
+
163
+ #### Comprehensive
164
+
165
+ This example shows loading state, error state, rendering, and triggering from outside the form.
166
+
167
+
168
+ ```html
169
+ <section>
170
+ <super-form id="demo-super-form-external" class="tag-article grid">
171
+ <form action="/api/echo" method="post">
172
+ <label>
173
+ Nickname
174
+ <input type="text" name="nickname" placeholder="John" required>
175
+ </label>
176
+ </form>
177
+ <ul></ul>
178
+ <ul></ul>
179
+ <template id="demo-super-form-item">
180
+ <li class="tag-code"></li>
181
+ </template>
182
+ </super-form>
183
+ <button type="button" command="--submit" commandfor="demo-super-form-external">
184
+ Save (outside the form)
185
+ </button>
186
+ <style>
187
+ #demo-super-form-external-trigger { max-width: none; }
188
+ #demo-super-form-external-trigger super-form {
189
+ &[is-loading] {
190
+ opacity: 0.5;
191
+ border: 1px dashed yellow;
192
+ }
193
+ &[is-success] { border: 1px solid green; }
194
+ &[is-error] {
195
+ border: 1px solid red;
196
+ &::before { content: "An error occurred."; }
197
+ }
198
+ ul:first-of-type::before { content: "Submitted JSON:"; }
199
+ ul:last-of-type::before { content: "Response data:"; }
200
+ li { display: block; }
201
+ }
202
+ </style>
203
+ <quark-sheet>
204
+ super-form[is-success] {
205
+ $res: prop("provision");
206
+ li { content: "#{index}: #{item}"; }
207
+ ul:first-of-type { content: iterate($res.body.json, "#demo-super-form-item"); }
208
+ ul:last-of-type { content: iterate($res, "#demo-super-form-item"); }
209
+ }
210
+ </quark-sheet>
211
+ </section>
212
+ ```