@f-ewald/components 1.16.0 → 1.18.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/README.md +2 -0
- package/custom-elements.json +620 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/mapbox-map.d.ts +59 -0
- package/dist/mapbox-map.d.ts.map +1 -0
- package/dist/mapbox-map.js +144 -0
- package/dist/mapbox-map.js.map +1 -0
- package/dist/range-slider.d.ts +48 -0
- package/dist/range-slider.d.ts.map +1 -0
- package/dist/range-slider.js +240 -0
- package/dist/range-slider.js.map +1 -0
- package/dist/split-hero.d.ts +26 -0
- package/dist/split-hero.d.ts.map +1 -0
- package/dist/split-hero.js +132 -0
- package/dist/split-hero.js.map +1 -0
- package/dist/ui-checkbox.d.ts +11 -3
- package/dist/ui-checkbox.d.ts.map +1 -1
- package/dist/ui-checkbox.js +31 -2
- package/dist/ui-checkbox.js.map +1 -1
- package/docs/mapbox-map.md +62 -0
- package/docs/range-slider.md +59 -0
- package/docs/split-hero.md +55 -0
- package/docs/ui-checkbox.md +9 -1
- package/llms.txt +100 -2
- package/package.json +3 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
/**
|
|
3
|
+
* Full-viewport split layout: a user-supplied photo fills one half, the
|
|
4
|
+
* default slot (typically a sign-in/sign-up form) fills the other. Below the
|
|
5
|
+
* shared 48rem breakpoint the photo becomes a blurred, full-bleed backdrop
|
|
6
|
+
* behind a solid content card instead of disappearing outright.
|
|
7
|
+
*
|
|
8
|
+
* Give the host a height the same way as `app-shell` (e.g. `height: 100vh`).
|
|
9
|
+
*
|
|
10
|
+
* @element split-hero
|
|
11
|
+
* @slot - Form or other content for the non-image half.
|
|
12
|
+
*/
|
|
13
|
+
export declare class SplitHero extends LitElement {
|
|
14
|
+
/** URL of the image filling the visual half; omit to render content full-width. */
|
|
15
|
+
src: string;
|
|
16
|
+
/** Accessible alternative text for the image; leave empty for a decorative photo. */
|
|
17
|
+
alt: string;
|
|
18
|
+
static styles: import("lit").CSSResult[];
|
|
19
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
20
|
+
}
|
|
21
|
+
declare global {
|
|
22
|
+
interface HTMLElementTagNameMap {
|
|
23
|
+
"split-hero": SplitHero;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=split-hero.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-hero.d.ts","sourceRoot":"","sources":["../src/split-hero.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C;;;;;;;;;;GAUG;AACH,qBACa,SAAU,SAAQ,UAAU;IACvC,mFAAmF;IACvE,GAAG,SAAM;IAErB,qFAAqF;IACzE,GAAG,SAAM;IAErB,OAAgB,MAAM,4BAiFpB;IAEO,MAAM,yCASd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,SAAS,CAAC;KACzB;CACF"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { LitElement, css, html } from "lit";
|
|
8
|
+
import { customElement, property } from "lit/decorators.js";
|
|
9
|
+
import { tokens } from "./tokens.js";
|
|
10
|
+
/**
|
|
11
|
+
* Full-viewport split layout: a user-supplied photo fills one half, the
|
|
12
|
+
* default slot (typically a sign-in/sign-up form) fills the other. Below the
|
|
13
|
+
* shared 48rem breakpoint the photo becomes a blurred, full-bleed backdrop
|
|
14
|
+
* behind a solid content card instead of disappearing outright.
|
|
15
|
+
*
|
|
16
|
+
* Give the host a height the same way as `app-shell` (e.g. `height: 100vh`).
|
|
17
|
+
*
|
|
18
|
+
* @element split-hero
|
|
19
|
+
* @slot - Form or other content for the non-image half.
|
|
20
|
+
*/
|
|
21
|
+
let SplitHero = class SplitHero extends LitElement {
|
|
22
|
+
constructor() {
|
|
23
|
+
super(...arguments);
|
|
24
|
+
/** URL of the image filling the visual half; omit to render content full-width. */
|
|
25
|
+
this.src = "";
|
|
26
|
+
/** Accessible alternative text for the image; leave empty for a decorative photo. */
|
|
27
|
+
this.alt = "";
|
|
28
|
+
}
|
|
29
|
+
static { this.styles = [
|
|
30
|
+
tokens,
|
|
31
|
+
css `
|
|
32
|
+
:host {
|
|
33
|
+
display: flex;
|
|
34
|
+
block-size: 100%;
|
|
35
|
+
font-family: var(
|
|
36
|
+
--ui-font,
|
|
37
|
+
ui-sans-serif,
|
|
38
|
+
system-ui,
|
|
39
|
+
sans-serif,
|
|
40
|
+
"Apple Color Emoji",
|
|
41
|
+
"Segoe UI Emoji",
|
|
42
|
+
"Segoe UI Symbol",
|
|
43
|
+
"Noto Color Emoji"
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
.visual {
|
|
47
|
+
flex: 1 1 0;
|
|
48
|
+
min-width: 0;
|
|
49
|
+
margin: 0;
|
|
50
|
+
background: var(--ui-surface-muted, #f8fafc);
|
|
51
|
+
}
|
|
52
|
+
.visual img {
|
|
53
|
+
display: block;
|
|
54
|
+
width: 100%;
|
|
55
|
+
height: 100%;
|
|
56
|
+
object-fit: cover;
|
|
57
|
+
}
|
|
58
|
+
.content {
|
|
59
|
+
position: relative;
|
|
60
|
+
flex: 1 1 0;
|
|
61
|
+
min-width: 0;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
justify-content: center;
|
|
65
|
+
box-sizing: border-box;
|
|
66
|
+
padding: 2rem;
|
|
67
|
+
overflow-y: auto;
|
|
68
|
+
background: var(--ui-surface, #ffffff);
|
|
69
|
+
color: var(--ui-text, #0f172a);
|
|
70
|
+
}
|
|
71
|
+
.backdrop {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
.content-inner {
|
|
75
|
+
position: relative;
|
|
76
|
+
z-index: 1;
|
|
77
|
+
width: 100%;
|
|
78
|
+
max-width: 25rem;
|
|
79
|
+
}
|
|
80
|
+
@media (max-width: 48rem) {
|
|
81
|
+
.visual {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
.content {
|
|
85
|
+
padding: 1.5rem;
|
|
86
|
+
}
|
|
87
|
+
.backdrop {
|
|
88
|
+
display: block;
|
|
89
|
+
position: absolute;
|
|
90
|
+
inset: 0;
|
|
91
|
+
z-index: 0;
|
|
92
|
+
width: 100%;
|
|
93
|
+
height: 100%;
|
|
94
|
+
object-fit: cover;
|
|
95
|
+
filter: blur(2.5rem);
|
|
96
|
+
pointer-events: none;
|
|
97
|
+
}
|
|
98
|
+
.content-inner.card {
|
|
99
|
+
background: var(--ui-surface, #ffffff);
|
|
100
|
+
border-radius: var(--ui-radius, 0.5rem);
|
|
101
|
+
box-shadow: var(
|
|
102
|
+
--ui-shadow-lg,
|
|
103
|
+
0 20px 25px -5px rgb(0 0 0 / 0.1),
|
|
104
|
+
0 8px 10px -6px rgb(0 0 0 / 0.1)
|
|
105
|
+
);
|
|
106
|
+
padding: 1.5rem;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`,
|
|
110
|
+
]; }
|
|
111
|
+
render() {
|
|
112
|
+
const hasPhoto = Boolean(this.src);
|
|
113
|
+
return html `
|
|
114
|
+
${hasPhoto ? html `<figure class="visual"><img src=${this.src} alt=${this.alt} /></figure>` : ""}
|
|
115
|
+
<div class="content">
|
|
116
|
+
${hasPhoto ? html `<img class="backdrop" src=${this.src} alt="" aria-hidden="true" />` : ""}
|
|
117
|
+
<div class="content-inner ${hasPhoto ? "card" : ""}"><slot></slot></div>
|
|
118
|
+
</div>
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
__decorate([
|
|
123
|
+
property()
|
|
124
|
+
], SplitHero.prototype, "src", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
property()
|
|
127
|
+
], SplitHero.prototype, "alt", void 0);
|
|
128
|
+
SplitHero = __decorate([
|
|
129
|
+
customElement("split-hero")
|
|
130
|
+
], SplitHero);
|
|
131
|
+
export { SplitHero };
|
|
132
|
+
//# sourceMappingURL=split-hero.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"split-hero.js","sourceRoot":"","sources":["../src/split-hero.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,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;GAUG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,UAAU;IAAlC;;QACL,mFAAmF;QACvE,QAAG,GAAG,EAAE,CAAC;QAErB,qFAAqF;QACzE,QAAG,GAAG,EAAE,CAAC;IA+FvB,CAAC;aA7FiB,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8EF;KACF,AAjFqB,CAiFpB;IAEO,MAAM;QACb,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,OAAO,IAAI,CAAA;QACP,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,mCAAmC,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE;;UAE3F,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,6BAA6B,IAAI,CAAC,GAAG,+BAA+B,CAAC,CAAC,CAAC,EAAE;oCAC9D,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;;KAErD,CAAC;IACJ,CAAC;CACF,CAAA;AAlGa;IAAX,QAAQ,EAAE;sCAAU;AAGT;IAAX,QAAQ,EAAE;sCAAU;AALV,SAAS;IADrB,aAAa,CAAC,YAAY,CAAC;GACf,SAAS,CAoGrB","sourcesContent":["import { LitElement, css, html } from \"lit\";\nimport { customElement, property } from \"lit/decorators.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * Full-viewport split layout: a user-supplied photo fills one half, the\n * default slot (typically a sign-in/sign-up form) fills the other. Below the\n * shared 48rem breakpoint the photo becomes a blurred, full-bleed backdrop\n * behind a solid content card instead of disappearing outright.\n *\n * Give the host a height the same way as `app-shell` (e.g. `height: 100vh`).\n *\n * @element split-hero\n * @slot - Form or other content for the non-image half.\n */\n@customElement(\"split-hero\")\nexport class SplitHero extends LitElement {\n /** URL of the image filling the visual half; omit to render content full-width. */\n @property() src = \"\";\n\n /** Accessible alternative text for the image; leave empty for a decorative photo. */\n @property() alt = \"\";\n\n static override styles = [\n tokens,\n css`\n :host {\n display: flex;\n block-size: 100%;\n font-family: var(\n --ui-font,\n ui-sans-serif,\n system-ui,\n sans-serif,\n \"Apple Color Emoji\",\n \"Segoe UI Emoji\",\n \"Segoe UI Symbol\",\n \"Noto Color Emoji\"\n );\n }\n .visual {\n flex: 1 1 0;\n min-width: 0;\n margin: 0;\n background: var(--ui-surface-muted, #f8fafc);\n }\n .visual img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n .content {\n position: relative;\n flex: 1 1 0;\n min-width: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n padding: 2rem;\n overflow-y: auto;\n background: var(--ui-surface, #ffffff);\n color: var(--ui-text, #0f172a);\n }\n .backdrop {\n display: none;\n }\n .content-inner {\n position: relative;\n z-index: 1;\n width: 100%;\n max-width: 25rem;\n }\n @media (max-width: 48rem) {\n .visual {\n display: none;\n }\n .content {\n padding: 1.5rem;\n }\n .backdrop {\n display: block;\n position: absolute;\n inset: 0;\n z-index: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n filter: blur(2.5rem);\n pointer-events: none;\n }\n .content-inner.card {\n background: var(--ui-surface, #ffffff);\n border-radius: var(--ui-radius, 0.5rem);\n box-shadow: var(\n --ui-shadow-lg,\n 0 20px 25px -5px rgb(0 0 0 / 0.1),\n 0 8px 10px -6px rgb(0 0 0 / 0.1)\n );\n padding: 1.5rem;\n }\n }\n `,\n ];\n\n override render() {\n const hasPhoto = Boolean(this.src);\n return html`\n ${hasPhoto ? html`<figure class=\"visual\"><img src=${this.src} alt=${this.alt} /></figure>` : \"\"}\n <div class=\"content\">\n ${hasPhoto ? html`<img class=\"backdrop\" src=${this.src} alt=\"\" aria-hidden=\"true\" />` : \"\"}\n <div class=\"content-inner ${hasPhoto ? \"card\" : \"\"}\"><slot></slot></div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"split-hero\": SplitHero;\n }\n}\n"]}
|
package/dist/ui-checkbox.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LitElement, type PropertyValues } from "lit";
|
|
1
|
+
import { LitElement, type PropertyValues, type TemplateResult } from "lit";
|
|
2
2
|
/**
|
|
3
3
|
* A form-associated boolean checkbox, usable standalone or inside a native
|
|
4
4
|
* `<form>`. Submits `name=on` when checked (matching native
|
|
@@ -8,7 +8,11 @@ import { LitElement, type PropertyValues } from "lit";
|
|
|
8
8
|
* Renders a native `<input type="checkbox">` wrapped in a `<label>`, styled
|
|
9
9
|
* via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)
|
|
10
10
|
* rather than styling the native input directly; the checkbox itself renders
|
|
11
|
-
* at `1rem`, matching the existing radio-input convention.
|
|
11
|
+
* at `1rem`, matching the existing radio-input convention. An optional
|
|
12
|
+
* pre-rendered `icon` (matching `form-select`'s per-option icon convention)
|
|
13
|
+
* renders between the box and the label, inside the same clickable `<label>`
|
|
14
|
+
* — for a row that pairs a checkbox with an icon/swatch and needs the whole
|
|
15
|
+
* row, icon included, to stay one click target.
|
|
12
16
|
*
|
|
13
17
|
* @element ui-checkbox
|
|
14
18
|
* @fires change - The checkbox was toggled by the user, in either direction;
|
|
@@ -30,6 +34,10 @@ export declare class UiCheckbox extends LitElement {
|
|
|
30
34
|
name: string;
|
|
31
35
|
/** Visible label text rendered next to the box. */
|
|
32
36
|
label: string;
|
|
37
|
+
/** Pre-rendered icon template displayed between the box and the label, e.g. `iconPencil(14)` from this package's icon set. */
|
|
38
|
+
icon: TemplateResult | null;
|
|
39
|
+
/** Square icon size in pixels — 14 (inline icon size) by default. */
|
|
40
|
+
iconSize: number;
|
|
33
41
|
private _formDisabled;
|
|
34
42
|
protected willUpdate(_changed: PropertyValues): void;
|
|
35
43
|
protected updated(changed: PropertyValues): void;
|
|
@@ -39,7 +47,7 @@ export declare class UiCheckbox extends LitElement {
|
|
|
39
47
|
formDisabledCallback(disabled: boolean): void;
|
|
40
48
|
/** Restores the checked state from saved form state (bfcache/autofill). */
|
|
41
49
|
formStateRestoreCallback(state: string | File | FormData | null): void;
|
|
42
|
-
render():
|
|
50
|
+
render(): TemplateResult<1>;
|
|
43
51
|
}
|
|
44
52
|
declare global {
|
|
45
53
|
interface HTMLElementTagNameMap {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-checkbox.d.ts","sourceRoot":"","sources":["../src/ui-checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"ui-checkbox.d.ts","sourceRoot":"","sources":["../src/ui-checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAK/F;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,UAAW,SAAQ,UAAU;;IACxC,MAAM,CAAC,cAAc,UAAQ;IAE7B,OAAgB,MAAM,4BA4EpB;IAEF,kCAAkC;IACL,OAAO,UAAS;IAC7C,8EAA8E;IACjD,aAAa,UAAS;IACnD,2EAA2E;IAC9C,QAAQ,UAAS;IAC9C,wEAAwE;IAC3C,QAAQ,UAAS;IAC9C,kEAAkE;IACtD,IAAI,SAAM;IACtB,mDAAmD;IACvC,KAAK,SAAM;IACvB,8HAA8H;IAC9F,IAAI,EAAE,cAAc,GAAG,IAAI,CAAQ;IACnE,qEAAqE;IACzC,QAAQ,SAAM;IAEjC,OAAO,CAAC,aAAa,CAAS;IAWvC,UAAmB,UAAU,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAE5D;IAED,UAAmB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAQxD;IAED,kFAAkF;IAClF,iBAAiB,IAAI,IAAI,CAGxB;IAED,qDAAqD;IACrD,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED,2EAA2E;IAC3E,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,IAAI,CAGrE;IAwCQ,MAAM,sBAyBd;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
|
package/dist/ui-checkbox.js
CHANGED
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { LitElement, css, html } from "lit";
|
|
7
|
+
import { LitElement, css, html, nothing } from "lit";
|
|
8
8
|
import { customElement, property, state } from "lit/decorators.js";
|
|
9
9
|
import { ref } from "lit/directives/ref.js";
|
|
10
10
|
import { tokens } from "./tokens.js";
|
|
@@ -17,7 +17,11 @@ import { tokens } from "./tokens.js";
|
|
|
17
17
|
* Renders a native `<input type="checkbox">` wrapped in a `<label>`, styled
|
|
18
18
|
* via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)
|
|
19
19
|
* rather than styling the native input directly; the checkbox itself renders
|
|
20
|
-
* at `1rem`, matching the existing radio-input convention.
|
|
20
|
+
* at `1rem`, matching the existing radio-input convention. An optional
|
|
21
|
+
* pre-rendered `icon` (matching `form-select`'s per-option icon convention)
|
|
22
|
+
* renders between the box and the label, inside the same clickable `<label>`
|
|
23
|
+
* — for a row that pairs a checkbox with an icon/swatch and needs the whole
|
|
24
|
+
* row, icon included, to stay one click target.
|
|
21
25
|
*
|
|
22
26
|
* @element ui-checkbox
|
|
23
27
|
* @fires change - The checkbox was toggled by the user, in either direction;
|
|
@@ -38,6 +42,10 @@ let UiCheckbox = class UiCheckbox extends LitElement {
|
|
|
38
42
|
this.name = "";
|
|
39
43
|
/** Visible label text rendered next to the box. */
|
|
40
44
|
this.label = "";
|
|
45
|
+
/** Pre-rendered icon template displayed between the box and the label, e.g. `iconPencil(14)` from this package's icon set. */
|
|
46
|
+
this.icon = null;
|
|
47
|
+
/** Square icon size in pixels — 14 (inline icon size) by default. */
|
|
48
|
+
this.iconSize = 14;
|
|
41
49
|
this._formDisabled = false;
|
|
42
50
|
this.#internals = this.attachInternals();
|
|
43
51
|
this.#defaultChecked = false;
|
|
@@ -91,6 +99,16 @@ let UiCheckbox = class UiCheckbox extends LitElement {
|
|
|
91
99
|
.required-mark {
|
|
92
100
|
color: var(--ui-danger, #dc2626);
|
|
93
101
|
}
|
|
102
|
+
.checkbox-icon {
|
|
103
|
+
display: inline-flex;
|
|
104
|
+
width: var(--checkbox-icon-size);
|
|
105
|
+
height: var(--checkbox-icon-size);
|
|
106
|
+
flex: 0 0 var(--checkbox-icon-size);
|
|
107
|
+
}
|
|
108
|
+
.checkbox-icon > svg {
|
|
109
|
+
width: 100%;
|
|
110
|
+
height: 100%;
|
|
111
|
+
}
|
|
94
112
|
.sr-only {
|
|
95
113
|
position: absolute;
|
|
96
114
|
width: 1px;
|
|
@@ -189,6 +207,11 @@ let UiCheckbox = class UiCheckbox extends LitElement {
|
|
|
189
207
|
?required=${this.required}
|
|
190
208
|
@change=${(e) => this.#onChange(e)}
|
|
191
209
|
/>
|
|
210
|
+
${this.icon
|
|
211
|
+
? html `<span class="checkbox-icon" style=${`--checkbox-icon-size: ${this.iconSize}px`} aria-hidden="true"
|
|
212
|
+
>${this.icon}</span
|
|
213
|
+
>`
|
|
214
|
+
: nothing}
|
|
192
215
|
<span
|
|
193
216
|
>${this.label}${this.required
|
|
194
217
|
? html `<span class="required-mark" aria-hidden="true"> *</span><span class="sr-only">
|
|
@@ -218,6 +241,12 @@ __decorate([
|
|
|
218
241
|
__decorate([
|
|
219
242
|
property()
|
|
220
243
|
], UiCheckbox.prototype, "label", void 0);
|
|
244
|
+
__decorate([
|
|
245
|
+
property({ attribute: false })
|
|
246
|
+
], UiCheckbox.prototype, "icon", void 0);
|
|
247
|
+
__decorate([
|
|
248
|
+
property({ type: Number })
|
|
249
|
+
], UiCheckbox.prototype, "iconSize", void 0);
|
|
221
250
|
__decorate([
|
|
222
251
|
state()
|
|
223
252
|
], UiCheckbox.prototype, "_formDisabled", void 0);
|
package/dist/ui-checkbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui-checkbox.js","sourceRoot":"","sources":["../src/ui-checkbox.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAuB,MAAM,KAAK,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,GAAG,EAAE,MAAM,uBAAuB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;;;;;;;;;;;;GAcG;AAEI,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,UAAU;IAAnC;;QAuEL,kCAAkC;QACL,YAAO,GAAG,KAAK,CAAC;QAC7C,8EAA8E;QACjD,kBAAa,GAAG,KAAK,CAAC;QACnD,2EAA2E;QAC9C,aAAQ,GAAG,KAAK,CAAC;QAC9C,wEAAwE;QAC3C,aAAQ,GAAG,KAAK,CAAC;QAC9C,kEAAkE;QACtD,SAAI,GAAG,EAAE,CAAC;QACtB,mDAAmD;QACvC,UAAK,GAAG,EAAE,CAAC;QAEN,kBAAa,GAAG,KAAK,CAAC;QAEvC,eAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACpC,oBAAe,GAAG,KAAK,CAAC;QACxB,aAAQ,GAA4B,IAAI,CAAC;QAuEzC,gBAAW,GAAG,CAAC,EAAuB,EAAQ,EAAE;YAC9C,IAAI,CAAC,QAAQ,GAAI,EAAmC,IAAI,IAAI,CAAC;YAC7D,IAAI,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;QACtE,CAAC,CAAC;IAuBJ,CAAC;aAxLQ,mBAAc,GAAG,IAAI,AAAP,CAAQ;aAEb,WAAM,GAAG;QACvB,MAAM;QACN,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+DF;KACF,AAlEqB,CAkEpB;IAiBF,UAAU,CAA0B;IACpC,eAAe,CAAS;IACxB,QAAQ,CAAiC;IAEzC,+EAA+E;IAC/E,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,aAAa,CAAC;IAC7C,CAAC;IAEkB,UAAU,CAAC,QAAwB;QACpD,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC;IAC5D,CAAC;IAEkB,OAAO,CAAC,OAAuB;QAChD,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,cAAc,EAAE,CAAC;QACzE,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YACjH,IAAI,CAAC,aAAa,EAAE,CAAC;QACvB,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClD,IAAI,CAAC,QAAQ,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;QACnD,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,iBAAiB;QACf,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;IAC7B,CAAC;IAED,qDAAqD;IACrD,oBAAoB,CAAC,QAAiB;QACpC,IAAI,CAAC,aAAa,GAAG,QAAQ,CAAC;IAChC,CAAC;IAED,2EAA2E;IAC3E,wBAAwB,CAAC,KAAsC;QAC7D,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO;QACtC,IAAI,CAAC,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC;IAChC,CAAC;IAED,cAAc;QACZ,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,aAAa;QACX,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;YAChC,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,WAAW,CACzB,EAAE,YAAY,EAAE,IAAI,EAAE,EACtB,oCAAoC,EACpC,IAAI,CAAC,QAAQ,IAAI,SAAS,CAC3B,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,SAAS,CAAC,CAAQ;QAChB,MAAM,KAAK,GAAG,CAAC,CAAC,MAA0B,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;QAC3B,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAChG,CAAC;IACJ,CAAC;IAED,WAAW,CAGT;IAEO,MAAM;QACb,OAAO,IAAI,CAAA;;;YAGH,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;;qBAEZ,IAAI,CAAC,OAAO;sBACX,IAAI,CAAC,WAAW;sBAChB,IAAI,CAAC,QAAQ;oBACf,CAAC,CAAQ,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;;;aAGtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,QAAQ;YAC3B,CAAC,CAAC,IAAI,CAAA;;kBAEA;YACN,CAAC,CAAC,EAAE;;;KAGX,CAAC;IACJ,CAAC;CACF,CAAA;AAjH8B;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;2CAAiB;AAEhB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;iDAAuB;AAEtB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;4CAAkB;AAEjB;IAA5B,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;4CAAkB;AAElC;IAAX,QAAQ,EAAE;wCAAW;AAEV;IAAX,QAAQ,EAAE;yCAAY;AAEN;IAAhB,KAAK,EAAE;iDAA+B;AApF5B,UAAU;IADtB,aAAa,CAAC,aAAa,CAAC;GAChB,UAAU,CAyLtB","sourcesContent":["import { LitElement, css, html, type PropertyValues } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { ref } from \"lit/directives/ref.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A form-associated boolean checkbox, usable standalone or inside a native\n * `<form>`. Submits `name=on` when checked (matching native\n * `<input type=\"checkbox\">` semantics) and participates fully in form\n * `reset()`, ancestor `<fieldset disabled>`, and `required` validity.\n *\n * Renders a native `<input type=\"checkbox\">` wrapped in a `<label>`, styled\n * via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)\n * rather than styling the native input directly; the checkbox itself renders\n * at `1rem`, matching the existing radio-input convention.\n *\n * @element ui-checkbox\n * @fires change - The checkbox was toggled by the user, in either direction;\n * detail: `{ checked }`. Programmatic `checked` assignments do not fire it.\n */\n@customElement(\"ui-checkbox\")\nexport class UiCheckbox extends LitElement {\n static formAssociated = true;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-block;\n }\n .checkbox {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n min-height: 2rem;\n cursor: pointer;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");\n font-size: var(--ui-font-size-sm, 0.75rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text, #0f172a);\n }\n .checkbox input {\n width: 1rem;\n height: 1rem;\n margin: 0;\n accent-color: var(--ui-primary, #4f46e5);\n cursor: pointer;\n }\n .checkbox:has(input:focus-visible) {\n outline: none;\n }\n .checkbox:has(input:focus-visible) input {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n border-radius: var(--ui-radius-sm, 0.25rem);\n }\n .checkbox:has(input:disabled) {\n cursor: not-allowed;\n opacity: 0.6;\n }\n .checkbox:has(input:disabled) input {\n cursor: not-allowed;\n }\n .required-mark {\n color: var(--ui-danger, #dc2626);\n }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n @media (forced-colors: active) {\n .checkbox:has(input:focus-visible) input {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n .checkbox:has(input:disabled) {\n color: GrayText;\n opacity: 1;\n }\n }\n `,\n ];\n\n /** Whether the box is checked. */\n @property({ type: Boolean }) checked = false;\n /** Visual \"partial selection\" state; cleared on the next user interaction. */\n @property({ type: Boolean }) indeterminate = false;\n /** Disables interaction; merged with an ancestor `<fieldset disabled>`. */\n @property({ type: Boolean }) disabled = false;\n /** Marks the control invalid via `ElementInternals` while unchecked. */\n @property({ type: Boolean }) required = false;\n /** Form field name; submitted as `name=on` only while checked. */\n @property() name = \"\";\n /** Visible label text rendered next to the box. */\n @property() label = \"\";\n\n @state() private _formDisabled = false;\n\n #internals = this.attachInternals();\n #defaultChecked = false;\n #inputEl: HTMLInputElement | null = null;\n\n /** Whether the host or an ancestor fieldset currently disables the control. */\n get #isDisabled(): boolean {\n return this.disabled || this._formDisabled;\n }\n\n protected override willUpdate(_changed: PropertyValues): void {\n if (!this.hasUpdated) this.#defaultChecked = this.checked;\n }\n\n protected override updated(changed: PropertyValues): void {\n if (changed.has(\"checked\") || changed.has(\"name\")) this.#syncFormValue();\n if (changed.has(\"checked\") || changed.has(\"required\") || changed.has(\"disabled\") || changed.has(\"_formDisabled\")) {\n this.#syncValidity();\n }\n if (changed.has(\"indeterminate\") && this.#inputEl) {\n this.#inputEl.indeterminate = this.indeterminate;\n }\n }\n\n /** Restores the checked state captured at first render, per the form contract. */\n formResetCallback(): void {\n this.checked = this.#defaultChecked;\n this.indeterminate = false;\n }\n\n /** Mirrors an ancestor fieldset's disabled state. */\n formDisabledCallback(disabled: boolean): void {\n this._formDisabled = disabled;\n }\n\n /** Restores the checked state from saved form state (bfcache/autofill). */\n formStateRestoreCallback(state: string | File | FormData | null): void {\n if (typeof state !== \"string\") return;\n this.checked = state === \"on\";\n }\n\n #syncFormValue(): void {\n if (!this.name || !this.checked) {\n this.#internals.setFormValue(null);\n return;\n }\n this.#internals.setFormValue(\"on\");\n }\n\n #syncValidity(): void {\n if (this.#isDisabled) {\n this.#internals.setValidity({});\n return;\n }\n if (this.required && !this.checked) {\n this.#internals.setValidity(\n { valueMissing: true },\n \"Please check this box to continue.\",\n this.#inputEl ?? undefined,\n );\n } else {\n this.#internals.setValidity({});\n }\n }\n\n #onChange(e: Event): void {\n const input = e.target as HTMLInputElement;\n this.checked = input.checked;\n this.indeterminate = false;\n this.dispatchEvent(\n new CustomEvent(\"change\", { detail: { checked: this.checked }, bubbles: true, composed: true }),\n );\n }\n\n #onInputRef = (el: Element | undefined): void => {\n this.#inputEl = (el as HTMLInputElement | undefined) ?? null;\n if (this.#inputEl) this.#inputEl.indeterminate = this.indeterminate;\n };\n\n override render() {\n return html`\n <label class=\"checkbox\">\n <input\n ${ref(this.#onInputRef)}\n type=\"checkbox\"\n .checked=${this.checked}\n ?disabled=${this.#isDisabled}\n ?required=${this.required}\n @change=${(e: Event) => this.#onChange(e)}\n />\n <span\n >${this.label}${this.required\n ? html`<span class=\"required-mark\" aria-hidden=\"true\"> *</span><span class=\"sr-only\">\n (required)</span\n >`\n : \"\"}</span\n >\n </label>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ui-checkbox\": UiCheckbox;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui-checkbox.js","sourceRoot":"","sources":["../src/ui-checkbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, css, html, nothing, type PropertyValues, type TemplateResult } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { ref } from \"lit/directives/ref.js\";\nimport { tokens } from \"./tokens.js\";\n\n/**\n * A form-associated boolean checkbox, usable standalone or inside a native\n * `<form>`. Submits `name=on` when checked (matching native\n * `<input type=\"checkbox\">` semantics) and participates fully in form\n * `reset()`, ancestor `<fieldset disabled>`, and `required` validity.\n *\n * Renders a native `<input type=\"checkbox\">` wrapped in a `<label>`, styled\n * via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)\n * rather than styling the native input directly; the checkbox itself renders\n * at `1rem`, matching the existing radio-input convention. An optional\n * pre-rendered `icon` (matching `form-select`'s per-option icon convention)\n * renders between the box and the label, inside the same clickable `<label>`\n * — for a row that pairs a checkbox with an icon/swatch and needs the whole\n * row, icon included, to stay one click target.\n *\n * @element ui-checkbox\n * @fires change - The checkbox was toggled by the user, in either direction;\n * detail: `{ checked }`. Programmatic `checked` assignments do not fire it.\n */\n@customElement(\"ui-checkbox\")\nexport class UiCheckbox extends LitElement {\n static formAssociated = true;\n\n static override styles = [\n tokens,\n css`\n :host {\n display: inline-block;\n }\n .checkbox {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n min-height: 2rem;\n cursor: pointer;\n font-family: var(--ui-font, ui-sans-serif, system-ui, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");\n font-size: var(--ui-font-size-sm, 0.75rem);\n line-height: var(--ui-line-height-tight, 1.25);\n color: var(--ui-text, #0f172a);\n }\n .checkbox input {\n width: 1rem;\n height: 1rem;\n margin: 0;\n accent-color: var(--ui-primary, #4f46e5);\n cursor: pointer;\n }\n .checkbox:has(input:focus-visible) {\n outline: none;\n }\n .checkbox:has(input:focus-visible) input {\n outline: none;\n box-shadow: var(--ui-focus-ring, 0 0 0 3px rgb(79 70 229 / 0.35));\n border-radius: var(--ui-radius-sm, 0.25rem);\n }\n .checkbox:has(input:disabled) {\n cursor: not-allowed;\n opacity: 0.6;\n }\n .checkbox:has(input:disabled) input {\n cursor: not-allowed;\n }\n .required-mark {\n color: var(--ui-danger, #dc2626);\n }\n .checkbox-icon {\n display: inline-flex;\n width: var(--checkbox-icon-size);\n height: var(--checkbox-icon-size);\n flex: 0 0 var(--checkbox-icon-size);\n }\n .checkbox-icon > svg {\n width: 100%;\n height: 100%;\n }\n .sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n border: 0;\n }\n @media (forced-colors: active) {\n .checkbox:has(input:focus-visible) input {\n outline: 2px solid CanvasText;\n outline-offset: 2px;\n box-shadow: none;\n }\n .checkbox:has(input:disabled) {\n color: GrayText;\n opacity: 1;\n }\n }\n `,\n ];\n\n /** Whether the box is checked. */\n @property({ type: Boolean }) checked = false;\n /** Visual \"partial selection\" state; cleared on the next user interaction. */\n @property({ type: Boolean }) indeterminate = false;\n /** Disables interaction; merged with an ancestor `<fieldset disabled>`. */\n @property({ type: Boolean }) disabled = false;\n /** Marks the control invalid via `ElementInternals` while unchecked. */\n @property({ type: Boolean }) required = false;\n /** Form field name; submitted as `name=on` only while checked. */\n @property() name = \"\";\n /** Visible label text rendered next to the box. */\n @property() label = \"\";\n /** Pre-rendered icon template displayed between the box and the label, e.g. `iconPencil(14)` from this package's icon set. */\n @property({ attribute: false }) icon: TemplateResult | null = null;\n /** Square icon size in pixels — 14 (inline icon size) by default. */\n @property({ type: Number }) iconSize = 14;\n\n @state() private _formDisabled = false;\n\n #internals = this.attachInternals();\n #defaultChecked = false;\n #inputEl: HTMLInputElement | null = null;\n\n /** Whether the host or an ancestor fieldset currently disables the control. */\n get #isDisabled(): boolean {\n return this.disabled || this._formDisabled;\n }\n\n protected override willUpdate(_changed: PropertyValues): void {\n if (!this.hasUpdated) this.#defaultChecked = this.checked;\n }\n\n protected override updated(changed: PropertyValues): void {\n if (changed.has(\"checked\") || changed.has(\"name\")) this.#syncFormValue();\n if (changed.has(\"checked\") || changed.has(\"required\") || changed.has(\"disabled\") || changed.has(\"_formDisabled\")) {\n this.#syncValidity();\n }\n if (changed.has(\"indeterminate\") && this.#inputEl) {\n this.#inputEl.indeterminate = this.indeterminate;\n }\n }\n\n /** Restores the checked state captured at first render, per the form contract. */\n formResetCallback(): void {\n this.checked = this.#defaultChecked;\n this.indeterminate = false;\n }\n\n /** Mirrors an ancestor fieldset's disabled state. */\n formDisabledCallback(disabled: boolean): void {\n this._formDisabled = disabled;\n }\n\n /** Restores the checked state from saved form state (bfcache/autofill). */\n formStateRestoreCallback(state: string | File | FormData | null): void {\n if (typeof state !== \"string\") return;\n this.checked = state === \"on\";\n }\n\n #syncFormValue(): void {\n if (!this.name || !this.checked) {\n this.#internals.setFormValue(null);\n return;\n }\n this.#internals.setFormValue(\"on\");\n }\n\n #syncValidity(): void {\n if (this.#isDisabled) {\n this.#internals.setValidity({});\n return;\n }\n if (this.required && !this.checked) {\n this.#internals.setValidity(\n { valueMissing: true },\n \"Please check this box to continue.\",\n this.#inputEl ?? undefined,\n );\n } else {\n this.#internals.setValidity({});\n }\n }\n\n #onChange(e: Event): void {\n const input = e.target as HTMLInputElement;\n this.checked = input.checked;\n this.indeterminate = false;\n this.dispatchEvent(\n new CustomEvent(\"change\", { detail: { checked: this.checked }, bubbles: true, composed: true }),\n );\n }\n\n #onInputRef = (el: Element | undefined): void => {\n this.#inputEl = (el as HTMLInputElement | undefined) ?? null;\n if (this.#inputEl) this.#inputEl.indeterminate = this.indeterminate;\n };\n\n override render() {\n return html`\n <label class=\"checkbox\">\n <input\n ${ref(this.#onInputRef)}\n type=\"checkbox\"\n .checked=${this.checked}\n ?disabled=${this.#isDisabled}\n ?required=${this.required}\n @change=${(e: Event) => this.#onChange(e)}\n />\n ${this.icon\n ? html`<span class=\"checkbox-icon\" style=${`--checkbox-icon-size: ${this.iconSize}px`} aria-hidden=\"true\"\n >${this.icon}</span\n >`\n : nothing}\n <span\n >${this.label}${this.required\n ? html`<span class=\"required-mark\" aria-hidden=\"true\"> *</span><span class=\"sr-only\">\n (required)</span\n >`\n : \"\"}</span\n >\n </label>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"ui-checkbox\": UiCheckbox;\n }\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# `<mapbox-map>`
|
|
2
|
+
|
|
3
|
+
A thin, generic wrapper around a `mapboxgl.Map` — construction, access
|
|
4
|
+
token, style loading/switching, and container resizing only. It carries no
|
|
5
|
+
domain logic: no layer registry, no click-handler system, no markers or
|
|
6
|
+
popups. A consumer registers its own sources/layers/handlers against the
|
|
7
|
+
`mapboxgl.Map` instance handed back on `map-ready`, the same instance
|
|
8
|
+
`mapbox-map` continues to own (this component never calls `map.remove()`
|
|
9
|
+
except on disconnect, so a consumer's own registrations survive style
|
|
10
|
+
reloads exactly as they would using `mapboxgl.Map` directly).
|
|
11
|
+
|
|
12
|
+
Deliberately does not construct the map until `styleUrl` is a non-empty
|
|
13
|
+
string — if a consumer knows the desired style only after an async
|
|
14
|
+
lookup (e.g. a saved user preference), delay setting `styleUrl` rather
|
|
15
|
+
than setting a default and swapping it later, which would visibly flash
|
|
16
|
+
the wrong basemap before the real one loads.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import "@f-ewald/components/mapbox-map.js";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<mapbox-map
|
|
28
|
+
access-token="pk.your-token"
|
|
29
|
+
style-url="mapbox://styles/mapbox/light-v11"
|
|
30
|
+
></mapbox-map>
|
|
31
|
+
<script type="module">
|
|
32
|
+
document.querySelector("mapbox-map").addEventListener("map-ready", (e) => {
|
|
33
|
+
const map = e.detail.map; // the underlying mapboxgl.Map
|
|
34
|
+
map.addSource("mine", { type: "geojson", data: "/mine.geojson" });
|
|
35
|
+
map.addLayer({ id: "mine", type: "circle", source: "mine", paint: { "circle-color": "#4f46e5" } });
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Attributes / properties
|
|
41
|
+
|
|
42
|
+
| Property | Attribute | Type | Default | Description |
|
|
43
|
+
| --- | --- | --- | --- | --- |
|
|
44
|
+
| `accessToken` | `access-token` | `string` | `""` | Mapbox access token. Required before the map can be constructed. |
|
|
45
|
+
| `styleUrl` | `style-url` | `string` | `""` | Style URL (e.g. `mapbox://styles/mapbox/light-v11`). The map is not constructed until this is a non-empty string — see the class doc. |
|
|
46
|
+
| `center` | _(JS property only)_ | `[number, number]` | `[0, 0]` | Initial center as `[lng, lat]`. Only read at construction time. |
|
|
47
|
+
| `zoom` | `zoom` | `number` | `0` | Initial zoom level. Only read at construction time. |
|
|
48
|
+
|
|
49
|
+
## Events
|
|
50
|
+
|
|
51
|
+
| Event | Description |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| `map-style-reloaded` | A subsequent `styleUrl` change finished loading its new style (sources/layers registered by a consumer via the `map-ready` instance do not survive a style change and must be re-registered — same behavior as calling `map.setStyle()` directly); detail: `{ map: mapboxgl.Map }`. |
|
|
54
|
+
| `map-ready` | The map (and, if `styleUrl` changed before the initial load finished, its final requested style) has finished loading; detail: `{ map: mapboxgl.Map }`. |
|
|
55
|
+
|
|
56
|
+
## Slots
|
|
57
|
+
|
|
58
|
+
_None._
|
|
59
|
+
|
|
60
|
+
## CSS custom properties
|
|
61
|
+
|
|
62
|
+
_None._
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# `<range-slider>`
|
|
2
|
+
|
|
3
|
+
A form-associated numeric range slider, usable standalone or inside a
|
|
4
|
+
native `<form>`. Wraps a native `<input type="range">` (kept for its free
|
|
5
|
+
keyboard, drag, and screen-reader support) restyled to match this
|
|
6
|
+
package's track/fill visual language (`stat-meter`, `percent-bar-chart`)
|
|
7
|
+
instead of the browser-default appearance. Purely a value control — no
|
|
8
|
+
built-in label; wrap in `form-field` for a labeled field, or render a
|
|
9
|
+
value readout next to it (see the playground example), matching
|
|
10
|
+
`autocomplete-input`/`form-select`.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import "@f-ewald/components/range-slider.js";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<range-slider min="100" max="5000" step="50" value="1000"></range-slider>
|
|
22
|
+
<script type="module">
|
|
23
|
+
document.querySelector("range-slider").addEventListener("input", (e) => {
|
|
24
|
+
console.log(e.detail.value);
|
|
25
|
+
});
|
|
26
|
+
</script>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Attributes / properties
|
|
30
|
+
|
|
31
|
+
| Property | Attribute | Type | Default | Description |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `min` | `min` | `number` | `0` | Minimum value. |
|
|
34
|
+
| `max` | `max` | `number` | `100` | Maximum value. |
|
|
35
|
+
| `step` | `step` | `number` | `1` | Step increment. |
|
|
36
|
+
| `value` | `value` | `number` | `0` | Current value. |
|
|
37
|
+
| `disabled` | `disabled` | `boolean` | `false` | Disables interaction; merged with an ancestor `<fieldset disabled>`. |
|
|
38
|
+
| `name` | `name` | `string` | `""` | Form field name. |
|
|
39
|
+
|
|
40
|
+
## Events
|
|
41
|
+
|
|
42
|
+
| Event | Description |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `input` | Fires continuously while dragging/typing; detail: `{ value }`. |
|
|
45
|
+
| `change` | Fires once the value is committed (drag released, arrow key released); detail: `{ value }`. |
|
|
46
|
+
|
|
47
|
+
## Slots
|
|
48
|
+
|
|
49
|
+
_None._
|
|
50
|
+
|
|
51
|
+
## CSS custom properties
|
|
52
|
+
|
|
53
|
+
| Custom property |
|
|
54
|
+
| --- |
|
|
55
|
+
| `--ui-focus-ring` |
|
|
56
|
+
| `--ui-primary` |
|
|
57
|
+
| `--ui-surface` |
|
|
58
|
+
| `--ui-surface-muted` |
|
|
59
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# `<split-hero>`
|
|
2
|
+
|
|
3
|
+
Full-viewport split layout: a user-supplied photo fills one half, the
|
|
4
|
+
default slot (typically a sign-in/sign-up form) fills the other. Below the
|
|
5
|
+
shared 48rem breakpoint the photo becomes a blurred, full-bleed backdrop
|
|
6
|
+
behind a solid content card instead of disappearing outright.
|
|
7
|
+
|
|
8
|
+
Give the host a height the same way as `app-shell` (e.g. `height: 100vh`).
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import "@f-ewald/components/split-hero.js";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<split-hero src="/photos/coast.jpg" alt="Coastal road" style="height: 100vh">
|
|
20
|
+
<form>
|
|
21
|
+
<h1>Sign in</h1>
|
|
22
|
+
<form-field label="Email"><input type="email" name="email" /></form-field>
|
|
23
|
+
<form-field label="Password"><input type="password" name="password" /></form-field>
|
|
24
|
+
<ui-button type="submit" variant="primary">Sign in</ui-button>
|
|
25
|
+
</form>
|
|
26
|
+
</split-hero>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Attributes / properties
|
|
30
|
+
|
|
31
|
+
| Property | Attribute | Type | Default | Description |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `src` | `src` | `string` | `""` | URL of the image filling the visual half; omit to render content full-width. |
|
|
34
|
+
| `alt` | `alt` | `string` | `""` | Accessible alternative text for the image; leave empty for a decorative photo. |
|
|
35
|
+
|
|
36
|
+
## Events
|
|
37
|
+
|
|
38
|
+
_None._
|
|
39
|
+
|
|
40
|
+
## Slots
|
|
41
|
+
|
|
42
|
+
| Slot | Description |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `(default)` | Form or other content for the non-image half. |
|
|
45
|
+
|
|
46
|
+
## CSS custom properties
|
|
47
|
+
|
|
48
|
+
| Custom property |
|
|
49
|
+
| --- |
|
|
50
|
+
| `--ui-font` |
|
|
51
|
+
| `--ui-radius` |
|
|
52
|
+
| `--ui-shadow-lg` |
|
|
53
|
+
| `--ui-surface` |
|
|
54
|
+
| `--ui-surface-muted` |
|
|
55
|
+
| `--ui-text` |
|
package/docs/ui-checkbox.md
CHANGED
|
@@ -8,7 +8,11 @@ A form-associated boolean checkbox, usable standalone or inside a native
|
|
|
8
8
|
Renders a native `<input type="checkbox">` wrapped in a `<label>`, styled
|
|
9
9
|
via `:has()` on the wrapping label (matching `radio-pills`/`radio-cards`)
|
|
10
10
|
rather than styling the native input directly; the checkbox itself renders
|
|
11
|
-
at `1rem`, matching the existing radio-input convention.
|
|
11
|
+
at `1rem`, matching the existing radio-input convention. An optional
|
|
12
|
+
pre-rendered `icon` (matching `form-select`'s per-option icon convention)
|
|
13
|
+
renders between the box and the label, inside the same clickable `<label>`
|
|
14
|
+
— for a row that pairs a checkbox with an icon/swatch and needs the whole
|
|
15
|
+
row, icon included, to stay one click target.
|
|
12
16
|
|
|
13
17
|
## Install
|
|
14
18
|
|
|
@@ -21,6 +25,8 @@ import "@f-ewald/components/ui-checkbox.js";
|
|
|
21
25
|
```html
|
|
22
26
|
<ui-checkbox label="Subscribe to updates"></ui-checkbox>
|
|
23
27
|
<ui-checkbox name="terms" label="I agree to the terms" required></ui-checkbox>
|
|
28
|
+
<!-- .icon is set programmatically (a pre-rendered TemplateResult), not an attribute -->
|
|
29
|
+
<ui-checkbox label="Show list view"></ui-checkbox>
|
|
24
30
|
```
|
|
25
31
|
|
|
26
32
|
## Attributes / properties
|
|
@@ -33,6 +39,8 @@ import "@f-ewald/components/ui-checkbox.js";
|
|
|
33
39
|
| `required` | `required` | `boolean` | `false` | Marks the control invalid via `ElementInternals` while unchecked. |
|
|
34
40
|
| `name` | `name` | `string` | `""` | Form field name; submitted as `name=on` only while checked. |
|
|
35
41
|
| `label` | `label` | `string` | `""` | Visible label text rendered next to the box. |
|
|
42
|
+
| `icon` | _(JS property only)_ | `TemplateResult | null` | `null` | Pre-rendered icon template displayed between the box and the label, e.g. `iconPencil(14)` from this package's icon set. |
|
|
43
|
+
| `iconSize` | `iconSize` | `number` | `14` | Square icon size in pixels — 14 (inline icon size) by default. |
|
|
36
44
|
|
|
37
45
|
## Events
|
|
38
46
|
|