@f-ewald/components 0.2.0 → 0.2.1

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.
@@ -3475,7 +3475,7 @@
3475
3475
  "declarations": [
3476
3476
  {
3477
3477
  "kind": "class",
3478
- "description": "Button (or link styled as one) with an optional leading icon, in three\nvisual weights. Set `href` to render an `<a>` instead of a `<button>` —\nsame styling either way — for cross-page navigation that should look like\nan action button; a disabled/busy link stays a real `<a>` with\n`aria-disabled` + `pointer-events: none` rather than losing its href.\nPut the icon in the `icon` slot and the label in the default slot.",
3478
+ "description": "Button (or link styled as one) with an optional leading icon, in three\nvisual weights. Set `href` to render an `<a>` instead of a `<button>` —\nsame styling either way — for cross-page navigation that should look like\nan action button; a disabled/busy link stays a real `<a>` with\n`aria-disabled` + `pointer-events: none` rather than losing its href.\nPut the icon in the `icon` slot and the label in the default slot.\n\nForm-associated (`type=\"submit\"`/`\"reset\"`): the actual `<button>` lives in\nthis element's shadow root, which native HTML form association does not\ncross into from an ancestor light-DOM `<form>`. `type=\"submit\"`/`\"reset\"`\nis instead wired through `ElementInternals.form` — the same mechanism\n`address-autocomplete` uses to associate with an ancestor form.",
3479
3479
  "name": "UiButton",
3480
3480
  "slots": [
3481
3481
  {
@@ -3488,6 +3488,20 @@
3488
3488
  }
3489
3489
  ],
3490
3490
  "members": [
3491
+ {
3492
+ "kind": "field",
3493
+ "name": "formAssociated",
3494
+ "type": {
3495
+ "text": "boolean"
3496
+ },
3497
+ "static": true,
3498
+ "default": "true"
3499
+ },
3500
+ {
3501
+ "kind": "field",
3502
+ "name": "#internals",
3503
+ "privacy": "private"
3504
+ },
3491
3505
  {
3492
3506
  "kind": "field",
3493
3507
  "name": "variant",
@@ -3537,6 +3551,12 @@
3537
3551
  "default": "false",
3538
3552
  "description": "Shows a spinner in place of the icon slot and disables the control.",
3539
3553
  "attribute": "busy"
3554
+ },
3555
+ {
3556
+ "kind": "method",
3557
+ "name": "_onClick",
3558
+ "privacy": "private",
3559
+ "description": "Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively."
3540
3560
  }
3541
3561
  ],
3542
3562
  "attributes": [
@@ -8,11 +8,19 @@ export type ButtonVariant = "primary" | "secondary" | "danger";
8
8
  * `aria-disabled` + `pointer-events: none` rather than losing its href.
9
9
  * Put the icon in the `icon` slot and the label in the default slot.
10
10
  *
11
+ * Form-associated (`type="submit"`/`"reset"`): the actual `<button>` lives in
12
+ * this element's shadow root, which native HTML form association does not
13
+ * cross into from an ancestor light-DOM `<form>`. `type="submit"`/`"reset"`
14
+ * is instead wired through `ElementInternals.form` — the same mechanism
15
+ * `address-autocomplete` uses to associate with an ancestor form.
16
+ *
11
17
  * @element ui-button
12
18
  * @slot icon - Optional leading icon (e.g. an inline SVG).
13
19
  * @slot - Button label.
14
20
  */
15
21
  export declare class UiButton extends LitElement {
22
+ #private;
23
+ static formAssociated: boolean;
16
24
  static styles: import("lit").CSSResult[];
17
25
  /** Visual weight. */
18
26
  variant: ButtonVariant;
@@ -24,6 +32,8 @@ export declare class UiButton extends LitElement {
24
32
  disabled: boolean;
25
33
  /** Shows a spinner in place of the icon slot and disables the control. */
26
34
  busy: boolean;
35
+ /** Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively. */
36
+ private _onClick;
27
37
  render(): import("lit-html").TemplateResult<1>;
28
38
  }
29
39
  declare global {
@@ -1 +1 @@
1
- {"version":3,"file":"ui-button.d.ts","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE/D;;;;;;;;;;;GAWG;AACH,qBACa,QAAS,SAAQ,UAAU;IACtC,OAAgB,MAAM,4BA6DpB;IAEF,qBAAqB;IACT,OAAO,EAAE,aAAa,CAAa;IAC/C,oEAAoE;IACxD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,wDAAwD;IAC5C,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAY;IAC3D,wCAAwC;IACX,QAAQ,UAAS;IAC9C,0EAA0E;IAC7C,IAAI,UAAS;IAEjC,MAAM,yCAmBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
1
+ {"version":3,"file":"ui-button.d.ts","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAK5C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBACa,QAAS,SAAQ,UAAU;;IACtC,MAAM,CAAC,cAAc,UAAQ;IAG7B,OAAgB,MAAM,4BA6DpB;IAEF,qBAAqB;IACT,OAAO,EAAE,aAAa,CAAa;IAC/C,oEAAoE;IACxD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAQ;IACvC,wDAAwD;IAC5C,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAY;IAC3D,wCAAwC;IACX,QAAQ,UAAS;IAC9C,0EAA0E;IAC7C,IAAI,UAAS;IAE1C,qHAAqH;IACrH,OAAO,CAAC,QAAQ;IAKP,MAAM,yCAmBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,WAAW,EAAE,QAAQ,CAAC;KACvB;CACF"}
package/dist/ui-button.js CHANGED
@@ -16,6 +16,12 @@ import { tokens } from "./tokens.js";
16
16
  * `aria-disabled` + `pointer-events: none` rather than losing its href.
17
17
  * Put the icon in the `icon` slot and the label in the default slot.
18
18
  *
19
+ * Form-associated (`type="submit"`/`"reset"`): the actual `<button>` lives in
20
+ * this element's shadow root, which native HTML form association does not
21
+ * cross into from an ancestor light-DOM `<form>`. `type="submit"`/`"reset"`
22
+ * is instead wired through `ElementInternals.form` — the same mechanism
23
+ * `address-autocomplete` uses to associate with an ancestor form.
24
+ *
19
25
  * @element ui-button
20
26
  * @slot icon - Optional leading icon (e.g. an inline SVG).
21
27
  * @slot - Button label.
@@ -23,6 +29,7 @@ import { tokens } from "./tokens.js";
23
29
  let UiButton = class UiButton extends LitElement {
24
30
  constructor() {
25
31
  super(...arguments);
32
+ this.#internals = this.attachInternals();
26
33
  /** Visual weight. */
27
34
  this.variant = "primary";
28
35
  /** Renders an `<a href="...">` instead of a `<button>` when set. */
@@ -34,6 +41,8 @@ let UiButton = class UiButton extends LitElement {
34
41
  /** Shows a spinner in place of the icon slot and disables the control. */
35
42
  this.busy = false;
36
43
  }
44
+ static { this.formAssociated = true; }
45
+ #internals;
37
46
  static { this.styles = [
38
47
  tokens,
39
48
  css `
@@ -96,6 +105,13 @@ let UiButton = class UiButton extends LitElement {
96
105
  }
97
106
  `,
98
107
  ]; }
108
+ /** Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively. */
109
+ _onClick() {
110
+ if (this.type === "submit")
111
+ this.#internals.form?.requestSubmit();
112
+ else if (this.type === "reset")
113
+ this.#internals.form?.reset();
114
+ }
99
115
  render() {
100
116
  const classes = `btn ${this.variant}`;
101
117
  const isDisabled = this.disabled || this.busy;
@@ -109,7 +125,7 @@ let UiButton = class UiButton extends LitElement {
109
125
  `;
110
126
  }
111
127
  return html `
112
- <button class=${classes} type=${this.type} ?disabled=${isDisabled}>
128
+ <button class=${classes} type="button" ?disabled=${isDisabled} @click=${this._onClick}>
113
129
  <span class="spin" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>
114
130
  <slot name="icon" ?hidden=${this.busy}></slot>
115
131
  <slot></slot>
@@ -1 +1 @@
1
- {"version":3,"file":"ui-button.js","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;;;;;;;GAWG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,UAAU;IAAjC;;QAgEL,qBAAqB;QACT,YAAO,GAAkB,SAAS,CAAC;QAC/C,oEAAoE;QACxD,SAAI,GAAkB,IAAI,CAAC;QACvC,wDAAwD;QAC5C,SAAI,GAAkC,QAAQ,CAAC;QAC3D,wCAAwC;QACX,aAAQ,GAAG,KAAK,CAAC;QAC9C,0EAA0E;QAC7C,SAAI,GAAG,KAAK,CAAC;IAsB5C,CAAC;aA9FiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DF;KACF,AA7DqB,CA6DpB;IAaO,MAAM;QACb,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;QAC9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;mBACE,OAAO,SAAS,IAAI,CAAC,IAAI,kBAAkB,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;uCACpD,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;sCAChC,IAAI,CAAC,IAAI;;;OAGxC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAA;sBACO,OAAO,SAAS,IAAI,CAAC,IAAI,cAAc,UAAU;qCAClC,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;oCAChC,IAAI,CAAC,IAAI;;;KAGxC,CAAC;IACJ,CAAC;CACF,CAAA;AA9Ba;IAAX,QAAQ,EAAE;yCAAoC;AAEnC;IAAX,QAAQ,EAAE;sCAA4B;AAE3B;IAAX,QAAQ,EAAE;sCAAgD;AAE9B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;0CAAkB;AAEjB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;sCAAc;AAzE/B,QAAQ;IADpB,aAAa,CAAC,WAAW,CAAC;GACd,QAAQ,CA+FpB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\";\n\n/**\n * Button (or link styled as one) with an optional leading icon, in three\n * visual weights. Set `href` to render an `<a>` instead of a `<button>` —\n * same styling either way — for cross-page navigation that should look like\n * an action button; a disabled/busy link stays a real `<a>` with\n * `aria-disabled` + `pointer-events: none` rather than losing its href.\n * Put the icon in the `icon` slot and the label in the default slot.\n *\n * @element ui-button\n * @slot icon - Optional leading icon (e.g. an inline SVG).\n * @slot - Button label.\n */\n@customElement(\"ui-button\")\nexport class UiButton extends LitElement {\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 0.9rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 500;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n cursor: pointer;\n border: 1px solid transparent;\n text-decoration: none;\n box-sizing: border-box;\n }\n .btn.primary {\n background: var(--ui-primary, #4f46e5);\n color: #fff;\n }\n .btn.primary:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n .btn.secondary {\n background: none;\n border-color: var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n }\n .btn.secondary:hover:not(:disabled) {\n border-color: var(--ui-text-muted, #64748b);\n }\n .btn.danger {\n background: var(--ui-danger, #dc2626);\n color: #fff;\n }\n .btn.danger:hover:not(:disabled) {\n background: var(--ui-danger-hover, #b91c1c);\n }\n .btn:disabled,\n .btn[aria-disabled=\"true\"] {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n }\n .spin {\n display: inline-flex;\n animation: spin 0.8s linear infinite;\n }\n .spin[hidden] {\n display: none;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n `,\n ];\n\n /** Visual weight. */\n @property() variant: ButtonVariant = \"primary\";\n /** Renders an `<a href=\"...\">` instead of a `<button>` when set. */\n @property() href: string | null = null;\n /** Native button `type`. Ignored when `href` is set. */\n @property() type: \"button\" | \"submit\" | \"reset\" = \"button\";\n /** Disables the control and dims it. */\n @property({ type: Boolean }) disabled = false;\n /** Shows a spinner in place of the icon slot and disables the control. */\n @property({ type: Boolean }) busy = false;\n\n override render() {\n const classes = `btn ${this.variant}`;\n const isDisabled = this.disabled || this.busy;\n if (this.href) {\n return html`\n <a class=${classes} href=${this.href} aria-disabled=${isDisabled ? \"true\" : \"false\"}>\n <span class=\"spin\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </a>\n `;\n }\n return html`\n <button class=${classes} type=${this.type} ?disabled=${isDisabled}>\n <span class=\"spin\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ui-button\": UiButton;\n }\n}\n"]}
1
+ {"version":3,"file":"ui-button.js","sourceRoot":"","sources":["../src/ui-button.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC;;;;;;;;;;;;;;;;;GAiBG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,UAAU;IAAjC;;QAGL,eAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAgEpC,qBAAqB;QACT,YAAO,GAAkB,SAAS,CAAC;QAC/C,oEAAoE;QACxD,SAAI,GAAkB,IAAI,CAAC;QACvC,wDAAwD;QAC5C,SAAI,GAAkC,QAAQ,CAAC;QAC3D,wCAAwC;QACX,aAAQ,GAAG,KAAK,CAAC;QAC9C,0EAA0E;QAC7C,SAAI,GAAG,KAAK,CAAC;IA4B5C,CAAC;aAvGQ,mBAAc,GAAG,IAAI,AAAP,CAAQ;IAE7B,UAAU,CAA0B;aACpB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0DF;KACF,AA7DqB,CA6DpB;IAaF,qHAAqH;IAC7G,QAAQ;QACd,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC;aAC7D,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAChE,CAAC;IAEQ,MAAM;QACb,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC;QAC9C,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;mBACE,OAAO,SAAS,IAAI,CAAC,IAAI,kBAAkB,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;uCACpD,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;sCAChC,IAAI,CAAC,IAAI;;;OAGxC,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAA;sBACO,OAAO,4BAA4B,UAAU,WAAW,IAAI,CAAC,QAAQ;qCACtD,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC,EAAE,CAAC;oCAChC,IAAI,CAAC,IAAI;;;KAGxC,CAAC;IACJ,CAAC;CACF,CAAA;AApCa;IAAX,QAAQ,EAAE;yCAAoC;AAEnC;IAAX,QAAQ,EAAE;sCAA4B;AAE3B;IAAX,QAAQ,EAAE;sCAAgD;AAE9B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;0CAAkB;AAEjB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;sCAAc;AA5E/B,QAAQ;IADpB,aAAa,CAAC,WAAW,CAAC;GACd,QAAQ,CAwGpB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { iconArrowPath } from \"./icons.js\";\nimport { tokens } from \"./tokens.js\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\";\n\n/**\n * Button (or link styled as one) with an optional leading icon, in three\n * visual weights. Set `href` to render an `<a>` instead of a `<button>` —\n * same styling either way — for cross-page navigation that should look like\n * an action button; a disabled/busy link stays a real `<a>` with\n * `aria-disabled` + `pointer-events: none` rather than losing its href.\n * Put the icon in the `icon` slot and the label in the default slot.\n *\n * Form-associated (`type=\"submit\"`/`\"reset\"`): the actual `<button>` lives in\n * this element's shadow root, which native HTML form association does not\n * cross into from an ancestor light-DOM `<form>`. `type=\"submit\"`/`\"reset\"`\n * is instead wired through `ElementInternals.form` — the same mechanism\n * `address-autocomplete` uses to associate with an ancestor form.\n *\n * @element ui-button\n * @slot icon - Optional leading icon (e.g. an inline SVG).\n * @slot - Button label.\n */\n@customElement(\"ui-button\")\nexport class UiButton extends LitElement {\n static formAssociated = true;\n\n #internals = this.attachInternals();\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-flex;\n }\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n border-radius: var(--ui-radius-sm, 0.25rem);\n padding: 0.5rem 0.9rem;\n font-size: var(--ui-font-size-sm, 0.75rem);\n font-weight: 500;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif);\n cursor: pointer;\n border: 1px solid transparent;\n text-decoration: none;\n box-sizing: border-box;\n }\n .btn.primary {\n background: var(--ui-primary, #4f46e5);\n color: #fff;\n }\n .btn.primary:hover:not(:disabled) {\n background: var(--ui-primary-hover, #4338ca);\n }\n .btn.secondary {\n background: none;\n border-color: var(--ui-border, #e2e8f0);\n color: var(--ui-text, #0f172a);\n }\n .btn.secondary:hover:not(:disabled) {\n border-color: var(--ui-text-muted, #64748b);\n }\n .btn.danger {\n background: var(--ui-danger, #dc2626);\n color: #fff;\n }\n .btn.danger:hover:not(:disabled) {\n background: var(--ui-danger-hover, #b91c1c);\n }\n .btn:disabled,\n .btn[aria-disabled=\"true\"] {\n opacity: 0.6;\n cursor: default;\n pointer-events: none;\n }\n .spin {\n display: inline-flex;\n animation: spin 0.8s linear infinite;\n }\n .spin[hidden] {\n display: none;\n }\n @keyframes spin {\n to {\n transform: rotate(360deg);\n }\n }\n `,\n ];\n\n /** Visual weight. */\n @property() variant: ButtonVariant = \"primary\";\n /** Renders an `<a href=\"...\">` instead of a `<button>` when set. */\n @property() href: string | null = null;\n /** Native button `type`. Ignored when `href` is set. */\n @property() type: \"button\" | \"submit\" | \"reset\" = \"button\";\n /** Disables the control and dims it. */\n @property({ type: Boolean }) disabled = false;\n /** Shows a spinner in place of the icon slot and disables the control. */\n @property({ type: Boolean }) busy = false;\n\n /** Drives submit/reset on the ancestor form via ElementInternals, since a shadow-DOM button can't do it natively. */\n private _onClick() {\n if (this.type === \"submit\") this.#internals.form?.requestSubmit();\n else if (this.type === \"reset\") this.#internals.form?.reset();\n }\n\n override render() {\n const classes = `btn ${this.variant}`;\n const isDisabled = this.disabled || this.busy;\n if (this.href) {\n return html`\n <a class=${classes} href=${this.href} aria-disabled=${isDisabled ? \"true\" : \"false\"}>\n <span class=\"spin\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </a>\n `;\n }\n return html`\n <button class=${classes} type=\"button\" ?disabled=${isDisabled} @click=${this._onClick}>\n <span class=\"spin\" ?hidden=${!this.busy}>${iconArrowPath(14)}</span>\n <slot name=\"icon\" ?hidden=${this.busy}></slot>\n <slot></slot>\n </button>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ui-button\": UiButton;\n }\n}\n"]}
package/docs/ui-button.md CHANGED
@@ -7,6 +7,12 @@ an action button; a disabled/busy link stays a real `<a>` with
7
7
  `aria-disabled` + `pointer-events: none` rather than losing its href.
8
8
  Put the icon in the `icon` slot and the label in the default slot.
9
9
 
10
+ Form-associated (`type="submit"`/`"reset"`): the actual `<button>` lives in
11
+ this element's shadow root, which native HTML form association does not
12
+ cross into from an ancestor light-DOM `<form>`. `type="submit"`/`"reset"`
13
+ is instead wired through `ElementInternals.form` — the same mechanism
14
+ `address-autocomplete` uses to associate with an ancestor form.
15
+
10
16
  ## Install
11
17
 
12
18
  ```js
package/llms.txt CHANGED
@@ -398,6 +398,12 @@ an action button; a disabled/busy link stays a real `<a>` with
398
398
  `aria-disabled` + `pointer-events: none` rather than losing its href.
399
399
  Put the icon in the `icon` slot and the label in the default slot.
400
400
 
401
+ Form-associated (`type="submit"`/`"reset"`): the actual `<button>` lives in
402
+ this element's shadow root, which native HTML form association does not
403
+ cross into from an ancestor light-DOM `<form>`. `type="submit"`/`"reset"`
404
+ is instead wired through `ElementInternals.form` — the same mechanism
405
+ `address-autocomplete` uses to associate with an ancestor form.
406
+
401
407
  Import: `import "@f-ewald/components/ui-button.js";`
402
408
 
403
409
  Properties: `variant` (attribute `variant`) : ButtonVariant, default "primary"; `href` (attribute `href`) : string | null, default null; `type` (attribute `type`) : "button" | "submit" | "reset", default "button"; `disabled` (attribute `disabled`) : boolean, default false; `busy` (attribute `busy`) : boolean, default false
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@f-ewald/components",
3
3
  "private": false,
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "description": "A collection of universally usable web components for various tasks.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",