@cobdfamily/clf-core 7.0.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.
Files changed (44) hide show
  1. package/README.md +250 -0
  2. package/dist/_variables.scss +973 -0
  3. package/dist/components/cobd-embed.d.ts +4 -0
  4. package/dist/components/cobd-embed.js +163 -0
  5. package/dist/components/cobd-nav.d.ts +1 -0
  6. package/dist/components/cobd-nav.js +383 -0
  7. package/dist/components/cobd-support.d.ts +26 -0
  8. package/dist/components/cobd-support.js +296 -0
  9. package/dist/components/font-scale-toggle.d.ts +9 -0
  10. package/dist/components/font-scale-toggle.js +159 -0
  11. package/dist/components/forms/checkbox.d.ts +1 -0
  12. package/dist/components/forms/checkbox.js +118 -0
  13. package/dist/components/forms/common.d.ts +17 -0
  14. package/dist/components/forms/common.js +137 -0
  15. package/dist/components/forms/index.d.ts +5 -0
  16. package/dist/components/forms/index.js +13 -0
  17. package/dist/components/forms/select.d.ts +1 -0
  18. package/dist/components/forms/select.js +132 -0
  19. package/dist/components/forms/submit.d.ts +1 -0
  20. package/dist/components/forms/submit.js +78 -0
  21. package/dist/components/forms/textarea.d.ts +1 -0
  22. package/dist/components/forms/textarea.js +95 -0
  23. package/dist/components/forms/textfield.d.ts +1 -0
  24. package/dist/components/forms/textfield.js +125 -0
  25. package/dist/components/index.d.ts +7 -0
  26. package/dist/components/index.js +33 -0
  27. package/dist/components/theme-toggle.d.ts +10 -0
  28. package/dist/components/theme-toggle.js +130 -0
  29. package/dist/i18n/chrome.json +94 -0
  30. package/dist/navs/cobd.ca.json +83 -0
  31. package/dist/navs/more-cobd.json +60 -0
  32. package/dist/navs.d.ts +27 -0
  33. package/dist/navs.js +193 -0
  34. package/dist/theming/font-scale-paint.d.ts +0 -0
  35. package/dist/theming/font-scale-paint.js +32 -0
  36. package/dist/theming/init.d.ts +1 -0
  37. package/dist/theming/init.js +19 -0
  38. package/dist/theming/runtime.d.ts +22 -0
  39. package/dist/theming/runtime.js +333 -0
  40. package/dist/tokens.css +972 -0
  41. package/dist/tokens.json +97 -0
  42. package/dist/tokens.scss +95 -0
  43. package/package.json +123 -0
  44. package/src/navs/schema.json +64 -0
@@ -0,0 +1,137 @@
1
+ // Shared helpers for the form custom elements.
2
+ //
3
+ // - ionicPresent() -- same auto-detect as
4
+ // cobd-nav. Looking for <ion-app> in the DOM
5
+ // sidesteps the
6
+ // customElements-not-yet-defined race that an
7
+ // `customElements.get("ion-input")` check would
8
+ // hit under Angular Ionic's deferred bootstrap.
9
+ //
10
+ // - ensureBaseStyle() -- Light-DOM custom elements
11
+ // default to `display: inline`, which would
12
+ // collapse the rendered field labels + slots.
13
+ // Inject one global rule the first time any
14
+ // form element upgrades. Idempotent.
15
+ //
16
+ // - readBoolAttr() / readAttr() -- attribute-
17
+ // reading helpers that respect the HTML
18
+ // convention (presence-as-true for booleans,
19
+ // getAttribute() for strings) and default
20
+ // gracefully.
21
+ export function ionicPresent() {
22
+ return typeof document !== "undefined"
23
+ && document.querySelector("ion-app") !== null;
24
+ }
25
+ const BASE_STYLE_ID = "cobd-forms-base-style";
26
+ const BASE_STYLE = `
27
+ cobd-textfield, cobd-select, cobd-textarea,
28
+ cobd-checkbox, cobd-submit {
29
+ display: block;
30
+ }
31
+ .cobd-field-row {
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: var(--cobd-spacing-xs, 4px);
35
+ }
36
+ .cobd-field-help,
37
+ .cobd-field-error {
38
+ font-size: var(--cobd-typography-size-sm, 0.875rem);
39
+ line-height: var(--cobd-typography-line-default, 1.5);
40
+ }
41
+ .cobd-field-help {
42
+ color: var(--cobd-color-medium, #92949c);
43
+ }
44
+ .cobd-field-error {
45
+ color: var(--cobd-color-danger, #c5283b);
46
+ }
47
+ .cobd-required-asterisk {
48
+ color: var(--cobd-color-danger, #c5283b);
49
+ margin-left: 0.2em;
50
+ aria-hidden: true;
51
+ }
52
+ `;
53
+ export function ensureBaseStyle() {
54
+ if (typeof document === "undefined")
55
+ return;
56
+ if (document.getElementById(BASE_STYLE_ID))
57
+ return;
58
+ const style = document.createElement("style");
59
+ style.id = BASE_STYLE_ID;
60
+ style.textContent = BASE_STYLE;
61
+ document.head.appendChild(style);
62
+ }
63
+ export function readAttr(el, name) {
64
+ return el.hasAttribute(name) ? el.getAttribute(name) : null;
65
+ }
66
+ export function readBoolAttr(el, name) {
67
+ // HTML boolean attrs: present (even empty) =
68
+ // true; absent = false. Matches the rule
69
+ // browsers use for `disabled`, `required`,
70
+ // etc.
71
+ return el.hasAttribute(name);
72
+ }
73
+ export function readFieldDef(el, fallbackIdPrefix) {
74
+ const name = readAttr(el, "name") ?? "";
75
+ const explicitId = readAttr(el, "id");
76
+ const id = explicitId || name
77
+ || `${fallbackIdPrefix}-${Math.random().toString(36).slice(2, 8)}`;
78
+ return {
79
+ id,
80
+ name,
81
+ label: readAttr(el, "label") ?? "",
82
+ help: readAttr(el, "help"),
83
+ error: readAttr(el, "error"),
84
+ required: readBoolAttr(el, "required"),
85
+ };
86
+ }
87
+ export function buildLabel(def) {
88
+ const lbl = document.createElement("label");
89
+ lbl.setAttribute("for", def.id);
90
+ lbl.className = "cobd-field-label";
91
+ lbl.textContent = def.label;
92
+ if (def.required) {
93
+ const star = document.createElement("span");
94
+ star.className = "cobd-required-asterisk";
95
+ star.setAttribute("aria-hidden", "true");
96
+ star.textContent = "*";
97
+ lbl.appendChild(star);
98
+ const sr = document.createElement("span");
99
+ sr.className = "sr-only";
100
+ sr.textContent = " (required)";
101
+ lbl.appendChild(sr);
102
+ }
103
+ return lbl;
104
+ }
105
+ export function buildHelp(def) {
106
+ if (!def.help)
107
+ return null;
108
+ const help = document.createElement("div");
109
+ help.id = `${def.id}-help`;
110
+ help.className = "cobd-field-help";
111
+ help.innerHTML = def.help;
112
+ return help;
113
+ }
114
+ export function buildError(def) {
115
+ // Always render the error slot (even when
116
+ // empty) so consumers can swap text into it
117
+ // at runtime without restructuring the DOM.
118
+ const err = document.createElement("div");
119
+ err.id = `${def.id}-error`;
120
+ err.className = "cobd-field-error";
121
+ err.setAttribute("role", "alert");
122
+ err.setAttribute("aria-live", "polite");
123
+ if (def.error)
124
+ err.innerHTML = def.error;
125
+ return err;
126
+ }
127
+ // Compose aria-describedby pointing at help +
128
+ // error slots (whichever are present). The
129
+ // control's aria-describedby gets this string
130
+ // so screen readers announce them in order.
131
+ export function describedByValue(def) {
132
+ const parts = [];
133
+ if (def.help)
134
+ parts.push(`${def.id}-help`);
135
+ parts.push(`${def.id}-error`);
136
+ return parts.join(" ");
137
+ }
@@ -0,0 +1,5 @@
1
+ import "./textfield.js";
2
+ import "./textarea.js";
3
+ import "./select.js";
4
+ import "./checkbox.js";
5
+ import "./submit.js";
@@ -0,0 +1,13 @@
1
+ // Aggregate entry: importing this module registers
2
+ // every cobd-* form element as a side effect. For
3
+ // tree-shaking, each element is also exported on
4
+ // its own subpath:
5
+ //
6
+ // import "@cobdfamily/clf-core/components/forms/textfield";
7
+ // import "@cobdfamily/clf-core/components/forms/select";
8
+ // ...
9
+ import "./textfield.js";
10
+ import "./textarea.js";
11
+ import "./select.js";
12
+ import "./checkbox.js";
13
+ import "./submit.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,132 @@
1
+ // <cobd-select name="..." label="..." [value="..."]
2
+ // [help="..."] [error="..."] [required]>
3
+ // <option value="ferry">Ferry</option>
4
+ // <option value="lift">Lift</option>
5
+ // </cobd-select>
6
+ //
7
+ // Select with options as Light-DOM children. The
8
+ // child <option> elements are read on render and
9
+ // translated to <ion-select-option> when Ionic is
10
+ // present, kept as <option> when not.
11
+ //
12
+ // Form-associated like cobd-textfield.
13
+ import { ionicPresent, ensureBaseStyle, readAttr, readFieldDef, buildLabel, buildHelp, buildError, describedByValue, } from "./common.js";
14
+ function readOptions(host, initialValue) {
15
+ const out = [];
16
+ for (const opt of Array.from(host.querySelectorAll("option"))) {
17
+ const v = opt.getAttribute("value") ?? opt.textContent ?? "";
18
+ out.push({
19
+ value: v,
20
+ label: opt.textContent?.trim() ?? v,
21
+ selected: opt.hasAttribute("selected")
22
+ || v === initialValue,
23
+ disabled: opt.hasAttribute("disabled"),
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ class CobdSelect extends HTMLElement {
29
+ static get observedAttributes() {
30
+ return [
31
+ "name", "label", "value", "help",
32
+ "error", "required", "id",
33
+ ];
34
+ }
35
+ constructor() {
36
+ super();
37
+ this.snapshot = [];
38
+ this.rendered = false;
39
+ this.internals = this.attachInternals();
40
+ }
41
+ connectedCallback() {
42
+ ensureBaseStyle();
43
+ // Snapshot the option children once -- the
44
+ // render path replaceChildren()s the host,
45
+ // which would otherwise wipe them.
46
+ if (!this.rendered) {
47
+ this.snapshot = readOptions(this, readAttr(this, "value") ?? "");
48
+ this.render();
49
+ }
50
+ }
51
+ attributeChangedCallback() {
52
+ if (this.isConnected && this.rendered)
53
+ this.render();
54
+ }
55
+ render() {
56
+ const def = readFieldDef(this, "cobd-select");
57
+ const value = readAttr(this, "value") ?? "";
58
+ const options = this.snapshot.map(o => ({
59
+ ...o,
60
+ selected: o.value === value || o.selected,
61
+ }));
62
+ const row = document.createElement("div");
63
+ row.className = "cobd-field-row";
64
+ row.appendChild(buildLabel(def));
65
+ const control = ionicPresent()
66
+ ? this.buildIonSelect(def, options)
67
+ : this.buildPlainSelect(def, options);
68
+ row.appendChild(control);
69
+ const help = buildHelp(def);
70
+ if (help)
71
+ row.appendChild(help);
72
+ row.appendChild(buildError(def));
73
+ this.replaceChildren(row);
74
+ const initial = options.find(o => o.selected)?.value
75
+ ?? options[0]?.value ?? "";
76
+ this.internals.setFormValue(initial);
77
+ this.rendered = true;
78
+ }
79
+ buildIonSelect(def, options) {
80
+ const it = document.createElement("ion-select");
81
+ it.setAttribute("id", def.id);
82
+ if (def.name)
83
+ it.setAttribute("name", def.name);
84
+ it.setAttribute("label-placement", "stacked");
85
+ if (def.required)
86
+ it.setAttribute("required", "");
87
+ it.setAttribute("aria-describedby", describedByValue(def));
88
+ for (const opt of options) {
89
+ const o = document.createElement("ion-select-option");
90
+ o.setAttribute("value", opt.value);
91
+ if (opt.disabled)
92
+ o.setAttribute("disabled", "");
93
+ o.textContent = opt.label;
94
+ if (opt.selected) {
95
+ it.setAttribute("value", opt.value);
96
+ }
97
+ it.appendChild(o);
98
+ }
99
+ it.addEventListener("ionChange", () => this.syncFrom(it));
100
+ return it;
101
+ }
102
+ buildPlainSelect(def, options) {
103
+ const it = document.createElement("select");
104
+ it.id = def.id;
105
+ if (def.name)
106
+ it.name = def.name;
107
+ if (def.required)
108
+ it.required = true;
109
+ it.setAttribute("aria-describedby", describedByValue(def));
110
+ for (const opt of options) {
111
+ const o = document.createElement("option");
112
+ o.value = opt.value;
113
+ if (opt.disabled)
114
+ o.disabled = true;
115
+ o.textContent = opt.label;
116
+ if (opt.selected)
117
+ o.selected = true;
118
+ it.appendChild(o);
119
+ }
120
+ it.addEventListener("change", () => this.syncFrom(it));
121
+ return it;
122
+ }
123
+ syncFrom(el) {
124
+ const v = el.value;
125
+ const str = v == null ? "" : String(v);
126
+ this.internals.setFormValue(str);
127
+ }
128
+ }
129
+ CobdSelect.formAssociated = true;
130
+ if (!customElements.get("cobd-select")) {
131
+ customElements.define("cobd-select", CobdSelect);
132
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ // <cobd-submit label="Save"
2
+ // [action="/api/save"]
3
+ // [method="post"]>
4
+ //
5
+ // Form submit button. When the host wraps it in a
6
+ // <form>, the button submits that form (browser
7
+ // default). When the action/method attrs are set,
8
+ // the element wraps itself in its own <form> so
9
+ // the button stands alone -- useful for one-off
10
+ // "post and go" actions where the surrounding
11
+ // markup isn't already a form.
12
+ //
13
+ // Renders <ion-button> when Ionic is present, a
14
+ // plain <button> otherwise. The element is NOT
15
+ // form-associated for value purposes (a submit
16
+ // button has no submittable value of its own);
17
+ // it just provides the trigger.
18
+ import { ionicPresent, ensureBaseStyle, readAttr, } from "./common.js";
19
+ class CobdSubmit extends HTMLElement {
20
+ constructor() {
21
+ super(...arguments);
22
+ this.rendered = false;
23
+ }
24
+ static get observedAttributes() {
25
+ return ["label", "action", "method", "color"];
26
+ }
27
+ connectedCallback() {
28
+ ensureBaseStyle();
29
+ if (!this.rendered)
30
+ this.render();
31
+ }
32
+ attributeChangedCallback() {
33
+ if (this.isConnected && this.rendered)
34
+ this.render();
35
+ }
36
+ render() {
37
+ const label = readAttr(this, "label") ?? "Submit";
38
+ const action = readAttr(this, "action");
39
+ const method = (readAttr(this, "method")
40
+ ?? "post").toLowerCase();
41
+ const color = readAttr(this, "color") ?? "primary";
42
+ const button = ionicPresent()
43
+ ? this.buildIonButton(label, color)
44
+ : this.buildPlainButton(label);
45
+ // Standalone-form mode: if `action` is set,
46
+ // the consumer wants this button to submit
47
+ // its own form (rather than a surrounding
48
+ // one). Wrap the button in a <form>.
49
+ if (action) {
50
+ const form = document.createElement("form");
51
+ form.action = action;
52
+ form.method = method;
53
+ form.appendChild(button);
54
+ this.replaceChildren(form);
55
+ }
56
+ else {
57
+ this.replaceChildren(button);
58
+ }
59
+ this.rendered = true;
60
+ }
61
+ buildIonButton(label, color) {
62
+ const it = document.createElement("ion-button");
63
+ it.setAttribute("type", "submit");
64
+ it.setAttribute("color", color);
65
+ it.setAttribute("expand", "block");
66
+ it.textContent = label;
67
+ return it;
68
+ }
69
+ buildPlainButton(label) {
70
+ const it = document.createElement("button");
71
+ it.type = "submit";
72
+ it.textContent = label;
73
+ return it;
74
+ }
75
+ }
76
+ if (!customElements.get("cobd-submit")) {
77
+ customElements.define("cobd-submit", CobdSubmit);
78
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ // <cobd-textarea name="..." label="..."
2
+ // [value="..."] [placeholder="..."]
3
+ // [help="..."] [error="..."]
4
+ // [required] [rows="..."]>
5
+ //
6
+ // Same shape as <cobd-textfield>, multi-line.
7
+ // Form-associated. Ionic auto-detect.
8
+ import { ionicPresent, ensureBaseStyle, readAttr, readFieldDef, buildLabel, buildHelp, buildError, describedByValue, } from "./common.js";
9
+ class CobdTextarea extends HTMLElement {
10
+ static get observedAttributes() {
11
+ return [
12
+ "name", "label", "value", "placeholder",
13
+ "help", "error", "required", "rows", "id",
14
+ ];
15
+ }
16
+ constructor() {
17
+ super();
18
+ this.rendered = false;
19
+ this.internals = this.attachInternals();
20
+ }
21
+ connectedCallback() {
22
+ ensureBaseStyle();
23
+ if (!this.rendered)
24
+ this.render();
25
+ }
26
+ attributeChangedCallback() {
27
+ if (this.isConnected && this.rendered)
28
+ this.render();
29
+ }
30
+ render() {
31
+ const def = readFieldDef(this, "cobd-textarea");
32
+ const value = readAttr(this, "value") ?? "";
33
+ const placeholder = readAttr(this, "placeholder") ?? "";
34
+ const rows = readAttr(this, "rows") ?? "4";
35
+ const row = document.createElement("div");
36
+ row.className = "cobd-field-row";
37
+ row.appendChild(buildLabel(def));
38
+ const control = ionicPresent()
39
+ ? this.buildIonTextarea(def, value, placeholder, rows)
40
+ : this.buildPlainTextarea(def, value, placeholder, rows);
41
+ row.appendChild(control);
42
+ const help = buildHelp(def);
43
+ if (help)
44
+ row.appendChild(help);
45
+ row.appendChild(buildError(def));
46
+ this.replaceChildren(row);
47
+ this.internals.setFormValue(value);
48
+ this.rendered = true;
49
+ }
50
+ buildIonTextarea(def, value, placeholder, rows) {
51
+ const it = document.createElement("ion-textarea");
52
+ it.setAttribute("id", def.id);
53
+ if (def.name)
54
+ it.setAttribute("name", def.name);
55
+ it.setAttribute("label-placement", "stacked");
56
+ if (value)
57
+ it.setAttribute("value", value);
58
+ if (placeholder)
59
+ it.setAttribute("placeholder", placeholder);
60
+ if (rows)
61
+ it.setAttribute("rows", rows);
62
+ if (def.required)
63
+ it.setAttribute("required", "");
64
+ it.setAttribute("aria-describedby", describedByValue(def));
65
+ it.addEventListener("ionInput", () => this.syncFrom(it));
66
+ it.addEventListener("ionChange", () => this.syncFrom(it));
67
+ return it;
68
+ }
69
+ buildPlainTextarea(def, value, placeholder, rows) {
70
+ const it = document.createElement("textarea");
71
+ it.id = def.id;
72
+ if (def.name)
73
+ it.name = def.name;
74
+ if (placeholder)
75
+ it.placeholder = placeholder;
76
+ if (rows)
77
+ it.rows = Number(rows);
78
+ if (def.required)
79
+ it.required = true;
80
+ it.value = value;
81
+ it.setAttribute("aria-describedby", describedByValue(def));
82
+ it.addEventListener("input", () => this.syncFrom(it));
83
+ it.addEventListener("change", () => this.syncFrom(it));
84
+ return it;
85
+ }
86
+ syncFrom(el) {
87
+ const v = el.value;
88
+ const str = v == null ? "" : String(v);
89
+ this.internals.setFormValue(str);
90
+ }
91
+ }
92
+ CobdTextarea.formAssociated = true;
93
+ if (!customElements.get("cobd-textarea")) {
94
+ customElements.define("cobd-textarea", CobdTextarea);
95
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ // <cobd-textfield name="..." label="..." [type="..."]
2
+ // [value="..."] [placeholder="..."]
3
+ // [help="..."] [error="..."]
4
+ // [required] [maxlength="..."]>
5
+ //
6
+ // Attribute-driven, form-associated text input.
7
+ // Form-Associated Custom Elements API (ElementInternals)
8
+ // makes the element participate in <form> submission
9
+ // natively -- the form sees `name=value` on POST as if
10
+ // the element were a plain <input>.
11
+ //
12
+ // Render mode:
13
+ //
14
+ // Ionic present -> <ion-input> + custom label,
15
+ // help, error slots around it.
16
+ // Ionic absent -> plain <input> with the same
17
+ // semantic surround.
18
+ //
19
+ // In both modes the element follows the cobd-* design
20
+ // tokens via CSS variables on <html>; no inline
21
+ // colours.
22
+ import { ionicPresent, ensureBaseStyle, readAttr, readFieldDef, buildLabel, buildHelp, buildError, describedByValue, } from "./common.js";
23
+ const KNOWN_TYPES = new Set([
24
+ "text", "email", "password", "tel", "url",
25
+ "number", "search",
26
+ ]);
27
+ class CobdTextfield extends HTMLElement {
28
+ static get observedAttributes() {
29
+ return [
30
+ "name", "label", "type", "value",
31
+ "placeholder", "help", "error",
32
+ "required", "maxlength", "id",
33
+ ];
34
+ }
35
+ constructor() {
36
+ super();
37
+ this.rendered = false;
38
+ this.internals = this.attachInternals();
39
+ }
40
+ connectedCallback() {
41
+ ensureBaseStyle();
42
+ if (!this.rendered)
43
+ this.render();
44
+ }
45
+ attributeChangedCallback() {
46
+ if (this.isConnected && this.rendered)
47
+ this.render();
48
+ }
49
+ render() {
50
+ const def = readFieldDef(this, "cobd-textfield");
51
+ const type = (readAttr(this, "type") ?? "text").toLowerCase();
52
+ const safeType = KNOWN_TYPES.has(type) ? type : "text";
53
+ const value = readAttr(this, "value") ?? "";
54
+ const placeholder = readAttr(this, "placeholder") ?? "";
55
+ const maxlength = readAttr(this, "maxlength");
56
+ const row = document.createElement("div");
57
+ row.className = "cobd-field-row";
58
+ row.appendChild(buildLabel(def));
59
+ const control = ionicPresent()
60
+ ? this.buildIonInput(def, safeType, value, placeholder, maxlength)
61
+ : this.buildPlainInput(def, safeType, value, placeholder, maxlength);
62
+ row.appendChild(control);
63
+ const help = buildHelp(def);
64
+ if (help)
65
+ row.appendChild(help);
66
+ row.appendChild(buildError(def));
67
+ this.replaceChildren(row);
68
+ this.internals.setFormValue(value);
69
+ this.rendered = true;
70
+ }
71
+ buildIonInput(def, type, value, placeholder, maxlength) {
72
+ const it = document.createElement("ion-input");
73
+ it.setAttribute("id", def.id);
74
+ if (def.name)
75
+ it.setAttribute("name", def.name);
76
+ it.setAttribute("type", type);
77
+ it.setAttribute("label-placement", "stacked");
78
+ if (value)
79
+ it.setAttribute("value", value);
80
+ if (placeholder)
81
+ it.setAttribute("placeholder", placeholder);
82
+ if (maxlength)
83
+ it.setAttribute("maxlength", maxlength);
84
+ if (def.required)
85
+ it.setAttribute("required", "");
86
+ it.setAttribute("aria-describedby", describedByValue(def));
87
+ // ion-input fires ionInput / ionChange; mirror
88
+ // both to ElementInternals so <form> POST sees
89
+ // the latest value.
90
+ it.addEventListener("ionInput", () => this.syncValueFrom(it));
91
+ it.addEventListener("ionChange", () => this.syncValueFrom(it));
92
+ return it;
93
+ }
94
+ buildPlainInput(def, type, value, placeholder, maxlength) {
95
+ const it = document.createElement("input");
96
+ it.id = def.id;
97
+ if (def.name)
98
+ it.name = def.name;
99
+ it.type = type;
100
+ if (value)
101
+ it.value = value;
102
+ if (placeholder)
103
+ it.placeholder = placeholder;
104
+ if (maxlength)
105
+ it.maxLength = Number(maxlength);
106
+ if (def.required)
107
+ it.required = true;
108
+ it.setAttribute("aria-describedby", describedByValue(def));
109
+ it.addEventListener("input", () => this.syncValueFrom(it));
110
+ it.addEventListener("change", () => this.syncValueFrom(it));
111
+ return it;
112
+ }
113
+ syncValueFrom(el) {
114
+ // ion-input has its own `value` getter (number
115
+ // | string | null); HTMLInputElement is just
116
+ // string. Coerce uniformly.
117
+ const v = el.value;
118
+ const str = v == null ? "" : String(v);
119
+ this.internals.setFormValue(str);
120
+ }
121
+ }
122
+ CobdTextfield.formAssociated = true;
123
+ if (!customElements.get("cobd-textfield")) {
124
+ customElements.define("cobd-textfield", CobdTextfield);
125
+ }
@@ -0,0 +1,7 @@
1
+ import "../theming/init.js";
2
+ import "./theme-toggle.js";
3
+ import "./font-scale-toggle.js";
4
+ import "./cobd-nav.js";
5
+ import "./cobd-support.js";
6
+ import "./cobd-embed.js";
7
+ import "./forms/index.js";
@@ -0,0 +1,33 @@
1
+ // Side-effect bundle: importing this module
2
+ // registers every clf-core custom element AND
3
+ // boots the theming runtime. One import = one
4
+ // `<script type="module" src=".../components/
5
+ // index.js">` covers the whole element surface.
6
+ //
7
+ // Why this exists: most CLF consumers (Hugo, WP,
8
+ // Keycloak, Moodle, CiviCRM, static HTML) don't
9
+ // ship a JS module pipeline. The clf-factory
10
+ // chrome auto-loads this one bundle from the
11
+ // CDN, with one SRI integrity attr, and every
12
+ // element is then available globally inside
13
+ // authored body content. Consumers with a real
14
+ // bundler can keep importing per-element entry
15
+ // points (`@cobdfamily/clf-core/components/
16
+ // cobd-embed`, etc.) for tree-shaking; that
17
+ // surface is unchanged.
18
+ //
19
+ // Order matters for theming/init: it has to
20
+ // run before the toggles register, otherwise a
21
+ // toggle that ticks at definition time
22
+ // (touching the runtime singleton) wouldn't
23
+ // find the state machine initialized. Putting
24
+ // it first here is enough; everything below is
25
+ // a side-effect import so the order of
26
+ // definitions matches the order of imports.
27
+ import "../theming/init.js";
28
+ import "./theme-toggle.js";
29
+ import "./font-scale-toggle.js";
30
+ import "./cobd-nav.js";
31
+ import "./cobd-support.js";
32
+ import "./cobd-embed.js";
33
+ import "./forms/index.js";
@@ -0,0 +1,10 @@
1
+ export declare class CobdThemeToggle extends HTMLElement {
2
+ private button;
3
+ private slotEl;
4
+ private path;
5
+ private cleanup;
6
+ connectedCallback(): void;
7
+ disconnectedCallback(): void;
8
+ private cycle;
9
+ private render;
10
+ }