@ktbsh/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +165 -0
  2. package/dist/custom-elements.json +1427 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +12 -0
  5. package/dist/react/alert.d.ts +20 -0
  6. package/dist/react/alert.js +30 -0
  7. package/dist/react/badge.d.ts +17 -0
  8. package/dist/react/badge.js +30 -0
  9. package/dist/react/box.d.ts +22 -0
  10. package/dist/react/box.js +30 -0
  11. package/dist/react/breadcrumb.d.ts +17 -0
  12. package/dist/react/breadcrumb.js +30 -0
  13. package/dist/react/button.d.ts +20 -0
  14. package/dist/react/button.js +30 -0
  15. package/dist/react/checkbox.d.ts +22 -0
  16. package/dist/react/checkbox.js +30 -0
  17. package/dist/react/container.d.ts +18 -0
  18. package/dist/react/container.js +30 -0
  19. package/dist/react/field.d.ts +20 -0
  20. package/dist/react/field.js +30 -0
  21. package/dist/react/heading.d.ts +19 -0
  22. package/dist/react/heading.js +30 -0
  23. package/dist/react/input.d.ts +25 -0
  24. package/dist/react/input.js +30 -0
  25. package/dist/react/label.d.ts +18 -0
  26. package/dist/react/label.js +30 -0
  27. package/dist/react/link.d.ts +21 -0
  28. package/dist/react/link.js +30 -0
  29. package/dist/react/modal.d.ts +18 -0
  30. package/dist/react/modal.js +30 -0
  31. package/dist/react/nav.d.ts +17 -0
  32. package/dist/react/nav.js +30 -0
  33. package/dist/react/pagination.d.ts +19 -0
  34. package/dist/react/pagination.js +30 -0
  35. package/dist/react/progress.d.ts +19 -0
  36. package/dist/react/progress.js +30 -0
  37. package/dist/react/radio.d.ts +22 -0
  38. package/dist/react/radio.js +30 -0
  39. package/dist/react/select.d.ts +22 -0
  40. package/dist/react/select.js +30 -0
  41. package/dist/react/skeleton.d.ts +19 -0
  42. package/dist/react/skeleton.js +30 -0
  43. package/dist/react/spinner.d.ts +18 -0
  44. package/dist/react/spinner.js +30 -0
  45. package/dist/react/stack.d.ts +21 -0
  46. package/dist/react/stack.js +30 -0
  47. package/dist/react/switch.d.ts +21 -0
  48. package/dist/react/switch.js +30 -0
  49. package/dist/react/tab-panel.d.ts +18 -0
  50. package/dist/react/tab-panel.js +30 -0
  51. package/dist/react/tab.d.ts +19 -0
  52. package/dist/react/tab.js +30 -0
  53. package/dist/react/tabs.d.ts +17 -0
  54. package/dist/react/tabs.js +30 -0
  55. package/dist/react/text.d.ts +19 -0
  56. package/dist/react/text.js +30 -0
  57. package/dist/react/textarea.d.ts +24 -0
  58. package/dist/react/textarea.js +30 -0
  59. package/dist/react/toast.d.ts +19 -0
  60. package/dist/react/toast.js +30 -0
  61. package/dist/themes/dark.css +52 -0
  62. package/dist/themes/light.css +53 -0
  63. package/dist/tokens/index.d.ts +7 -0
  64. package/dist/tokens/index.js +7 -0
  65. package/dist/tokens/semantic.d.ts +209 -0
  66. package/dist/tokens/semantic.js +102 -0
  67. package/dist/types/index.d.ts +15 -0
  68. package/dist/types/index.js +2 -0
  69. package/dist/vanilla/alert.d.ts +2 -0
  70. package/dist/vanilla/alert.js +134 -0
  71. package/dist/vanilla/alert.src.js +257 -0
  72. package/dist/vanilla/badge.d.ts +2 -0
  73. package/dist/vanilla/badge.js +89 -0
  74. package/dist/vanilla/badge.src.js +212 -0
  75. package/dist/vanilla/box.d.ts +2 -0
  76. package/dist/vanilla/box.js +100 -0
  77. package/dist/vanilla/box.src.js +270 -0
  78. package/dist/vanilla/breadcrumb.d.ts +2 -0
  79. package/dist/vanilla/breadcrumb.js +93 -0
  80. package/dist/vanilla/breadcrumb.src.js +255 -0
  81. package/dist/vanilla/button.d.ts +2 -0
  82. package/dist/vanilla/button.js +135 -0
  83. package/dist/vanilla/button.src.js +260 -0
  84. package/dist/vanilla/checkbox.d.ts +2 -0
  85. package/dist/vanilla/checkbox.js +96 -0
  86. package/dist/vanilla/checkbox.src.js +296 -0
  87. package/dist/vanilla/container.d.ts +2 -0
  88. package/dist/vanilla/container.js +79 -0
  89. package/dist/vanilla/container.src.js +222 -0
  90. package/dist/vanilla/field.d.ts +2 -0
  91. package/dist/vanilla/field.js +113 -0
  92. package/dist/vanilla/field.src.js +278 -0
  93. package/dist/vanilla/heading.d.ts +2 -0
  94. package/dist/vanilla/heading.js +75 -0
  95. package/dist/vanilla/heading.src.js +241 -0
  96. package/dist/vanilla/input.d.ts +2 -0
  97. package/dist/vanilla/input.js +121 -0
  98. package/dist/vanilla/input.src.js +326 -0
  99. package/dist/vanilla/label.d.ts +2 -0
  100. package/dist/vanilla/label.js +75 -0
  101. package/dist/vanilla/label.src.js +232 -0
  102. package/dist/vanilla/link.d.ts +2 -0
  103. package/dist/vanilla/link.js +96 -0
  104. package/dist/vanilla/link.src.js +272 -0
  105. package/dist/vanilla/modal.d.ts +2 -0
  106. package/dist/vanilla/modal.js +125 -0
  107. package/dist/vanilla/modal.src.js +251 -0
  108. package/dist/vanilla/nav.d.ts +2 -0
  109. package/dist/vanilla/nav.js +94 -0
  110. package/dist/vanilla/nav.src.js +214 -0
  111. package/dist/vanilla/pagination.d.ts +2 -0
  112. package/dist/vanilla/pagination.js +114 -0
  113. package/dist/vanilla/pagination.src.js +262 -0
  114. package/dist/vanilla/progress.d.ts +2 -0
  115. package/dist/vanilla/progress.js +108 -0
  116. package/dist/vanilla/progress.src.js +244 -0
  117. package/dist/vanilla/radio.d.ts +2 -0
  118. package/dist/vanilla/radio.js +104 -0
  119. package/dist/vanilla/radio.src.js +326 -0
  120. package/dist/vanilla/select.d.ts +2 -0
  121. package/dist/vanilla/select.js +104 -0
  122. package/dist/vanilla/select.src.js +332 -0
  123. package/dist/vanilla/skeleton.d.ts +2 -0
  124. package/dist/vanilla/skeleton.js +99 -0
  125. package/dist/vanilla/skeleton.src.js +240 -0
  126. package/dist/vanilla/spinner.d.ts +2 -0
  127. package/dist/vanilla/spinner.js +108 -0
  128. package/dist/vanilla/spinner.src.js +222 -0
  129. package/dist/vanilla/stack.d.ts +2 -0
  130. package/dist/vanilla/stack.js +83 -0
  131. package/dist/vanilla/stack.src.js +259 -0
  132. package/dist/vanilla/switch.d.ts +2 -0
  133. package/dist/vanilla/switch.js +166 -0
  134. package/dist/vanilla/switch.src.js +299 -0
  135. package/dist/vanilla/tab-panel.d.ts +2 -0
  136. package/dist/vanilla/tab-panel.js +80 -0
  137. package/dist/vanilla/tab-panel.src.js +227 -0
  138. package/dist/vanilla/tab.d.ts +2 -0
  139. package/dist/vanilla/tab.js +99 -0
  140. package/dist/vanilla/tab.src.js +253 -0
  141. package/dist/vanilla/tabs.d.ts +2 -0
  142. package/dist/vanilla/tabs.js +66 -0
  143. package/dist/vanilla/tabs.src.js +237 -0
  144. package/dist/vanilla/text.d.ts +2 -0
  145. package/dist/vanilla/text.js +74 -0
  146. package/dist/vanilla/text.src.js +232 -0
  147. package/dist/vanilla/textarea.d.ts +2 -0
  148. package/dist/vanilla/textarea.js +97 -0
  149. package/dist/vanilla/textarea.src.js +316 -0
  150. package/dist/vanilla/toast.d.ts +2 -0
  151. package/dist/vanilla/toast.js +120 -0
  152. package/dist/vanilla/toast.src.js +245 -0
  153. package/package.json +111 -0
@@ -0,0 +1,260 @@
1
+
2
+ import { render, html } from 'uhtml';
3
+
4
+ const sheet = new CSSStyleSheet();
5
+ sheet.replaceSync(":host {\n--kb-color-bg-canvas: #ffffff;\n--kb-color-bg-surface: #f6f8fa;\n--kb-color-bg-subtle: #eef1f4;\n--kb-color-bg-inverse: #1f2328;\n--kb-color-fg-default: #1f2328;\n--kb-color-fg-muted: #656d76;\n--kb-color-fg-subtle: #8c959f;\n--kb-color-fg-on-accent: #ffffff;\n--kb-color-fg-on-inverse: #ffffff;\n--kb-color-border-default: #d0d7de;\n--kb-color-border-muted: #e6ebf0;\n--kb-color-border-focus: #0969da;\n--kb-color-accent-default: #0969da;\n--kb-color-accent-hover: #0860ca;\n--kb-color-accent-subtle: #ddf4ff;\n--kb-color-danger-default: #cf222e;\n--kb-color-danger-subtle: #ffebe9;\n--kb-color-success-default: #1a7f37;\n--kb-color-success-subtle: #dafbe1;\n--kb-color-warning-default: #9a6700;\n--kb-color-warning-subtle: #fff8c5;\n--kb-space-2xs: 2px;\n--kb-space-xs: 4px;\n--kb-space-sm: 8px;\n--kb-space-md: 16px;\n--kb-space-lg: 24px;\n--kb-space-xl: 32px;\n--kb-space-2xl: 48px;\n--kb-radius-sm: 4px;\n--kb-radius-md: 8px;\n--kb-radius-lg: 12px;\n--kb-radius-full: 9999px;\n--kb-font-family-sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n--kb-font-size-sm: 0.875rem;\n--kb-font-size-md: 1rem;\n--kb-font-size-lg: 1.125rem;\n--kb-font-size-xl: 1.25rem;\n--kb-font-weight-regular: 400;\n--kb-font-weight-medium: 500;\n--kb-font-weight-semibold: 600;\n--kb-line-height-tight: 1.25;\n--kb-line-height-normal: 1.5;\n--kb-shadow-sm: 0 1px 2px rgba(31, 35, 40, 0.08);\n--kb-shadow-md: 0 4px 12px rgba(31, 35, 40, 0.1);\n--kb-focus-ring: 0 0 0 3px rgba(9, 105, 218, 0.35);\n}\n\n\n :host {\n display: inline-block;\n --kitbash-btn-padding-y: var(--kb-space-sm);\n --kitbash-btn-padding-x: var(--kb-space-md);\n --kitbash-btn-radius: var(--kb-radius-sm);\n --kitbash-btn-font: var(--kb-font-size-md);\n --kitbash-btn-focus-ring: var(--kb-focus-ring);\n font-family: var(--kb-font-family-sans);\n }\n :host([size=\"sm\"]) {\n --kitbash-btn-padding-y: var(--kb-space-xs);\n --kitbash-btn-padding-x: var(--kb-space-sm);\n --kitbash-btn-font: var(--kb-font-size-sm);\n }\n :host([size=\"lg\"]) {\n --kitbash-btn-padding-y: var(--kb-space-md);\n --kitbash-btn-padding-x: var(--kb-space-lg);\n --kitbash-btn-font: var(--kb-font-size-lg);\n }\n button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--kb-space-xs);\n box-sizing: border-box;\n min-height: 2.25rem;\n min-width: 2.25rem;\n padding: var(--kitbash-btn-padding-y) var(--kitbash-btn-padding-x);\n border-width: 1px;\n border-style: solid;\n border-radius: var(--kitbash-btn-radius);\n cursor: pointer;\n font-size: var(--kitbash-btn-font);\n font-family: inherit;\n font-weight: var(--kb-font-weight-medium);\n line-height: var(--kb-line-height-tight);\n }\n :host([size=\"sm\"]) button {\n min-height: 2rem;\n min-width: 2rem;\n }\n :host([size=\"lg\"]) button {\n min-height: 2.75rem;\n min-width: 2.75rem;\n }\n button:focus-visible {\n outline: none;\n box-shadow: var(--kitbash-btn-focus-ring);\n }\n button:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n .primary {\n background-color: var(--kitbash-btn-bg, var(--kb-color-accent-default));\n color: var(--kitbash-btn-color, var(--kb-color-fg-on-accent));\n border-color: var(--kitbash-btn-border-color, transparent);\n }\n .secondary {\n background-color: var(--kitbash-btn-bg, var(--kb-color-bg-subtle));\n color: var(--kitbash-btn-color, var(--kb-color-fg-default));\n border-color: var(\n --kitbash-btn-border-color,\n var(--kb-color-border-default)\n );\n }\n .ghost {\n background-color: var(--kitbash-btn-bg, transparent);\n color: var(--kitbash-btn-color, var(--kb-color-fg-default));\n border-color: var(--kitbash-btn-border-color, transparent);\n }\n .danger {\n background-color: var(--kitbash-btn-bg, var(--kb-color-danger-default));\n color: var(--kitbash-btn-color, var(--kb-color-fg-on-accent));\n border-color: var(--kitbash-btn-border-color, transparent);\n }\n ");
6
+
7
+ export class KitbashButton extends HTMLElement {
8
+
9
+ static get observedAttributes() {
10
+ return ["variant","size","disabled","type"];
11
+ }
12
+
13
+ static get propTypes() {
14
+ return {"variant":"String","size":"String","disabled":"Boolean","type":"String"};
15
+ }
16
+
17
+
18
+ get variant() {
19
+ return this._props['variant'];
20
+ }
21
+ set variant(val) {
22
+ // External / framework property writes: re-render, do not emit kitbash-change
23
+ // (caller already knows the value they set).
24
+ this._assignProp('variant', val);
25
+ this.update();
26
+ }
27
+
28
+ get size() {
29
+ return this._props['size'];
30
+ }
31
+ set size(val) {
32
+ // External / framework property writes: re-render, do not emit kitbash-change
33
+ // (caller already knows the value they set).
34
+ this._assignProp('size', val);
35
+ this.update();
36
+ }
37
+
38
+ get disabled() {
39
+ return this._props['disabled'];
40
+ }
41
+ set disabled(val) {
42
+ // External / framework property writes: re-render, do not emit kitbash-change
43
+ // (caller already knows the value they set).
44
+ this._assignProp('disabled', val);
45
+ this.update();
46
+ }
47
+
48
+ get type() {
49
+ return this._props['type'];
50
+ }
51
+ set type(val) {
52
+ // External / framework property writes: re-render, do not emit kitbash-change
53
+ // (caller already knows the value they set).
54
+ this._assignProp('type', val);
55
+ this.update();
56
+ }
57
+
58
+
59
+ constructor() {
60
+ super();
61
+ this.attachShadow({ mode: 'open', delegatesFocus: true });
62
+
63
+ this.shadowRoot.adoptedStyleSheets = [sheet];
64
+
65
+ this._defaults = {"variant":"primary","size":"md","disabled":false,"type":"button"};
66
+
67
+ this._state = {};
68
+ this._props = { ...this._defaults };
69
+ this._eventHandlers = { 'click button': function(e, { props }) {
70
+ if (props.disabled)
71
+ return;
72
+ const type = props.type === "submit" || props.type === "reset" ? props.type : "button";
73
+ if (type === "button")
74
+ return;
75
+ const form = e.currentTarget.getRootNode().host?.closest?.("form");
76
+ if (!form)
77
+ return;
78
+ if (type === "submit")
79
+ form.requestSubmit();
80
+ else
81
+ form.reset();
82
+ } };
83
+ this._eventCleanup = [];
84
+ this._renderFn = function({ props, html }) {
85
+ const variant = props.variant === "secondary" || props.variant === "ghost" || props.variant === "danger" ? props.variant : "primary", type = props.type === "submit" || props.type === "reset" ? props.type : "button";
86
+ return html`
87
+ <button
88
+ part="button-root"
89
+ class=${variant}
90
+ type=${type}
91
+ ?disabled=${props.disabled}
92
+ >
93
+ <slot></slot>
94
+ </button>
95
+ `;
96
+ };
97
+ this._reflecting = false;
98
+ }
99
+
100
+ connectedCallback() {
101
+ // Initial attribute values are parsed by attributeChangedCallback before connectedCallback fires
102
+ this.update();
103
+
104
+ }
105
+
106
+ disconnectedCallback() {
107
+ this.cleanupEvents();
108
+ }
109
+
110
+ attributeChangedCallback(name, oldValue, newValue) {
111
+ // Skip when we caused the attribute write ourselves (avoids double update)
112
+ if (this._reflecting) return;
113
+ if (oldValue === newValue) return;
114
+
115
+ const type = this.constructor.propTypes[name];
116
+
117
+ let parsedValue = newValue;
118
+ if (newValue === null || newValue === undefined) {
119
+ parsedValue = this._defaults[name];
120
+ } else if (type === 'Number' && newValue !== '') {
121
+ // Match property coercion: empty string is not Number('') → 0
122
+ parsedValue = Number(newValue);
123
+ } else if (type === 'Boolean') {
124
+ parsedValue = newValue !== null && newValue !== 'false';
125
+ }
126
+
127
+ this._props[name] = parsedValue;
128
+
129
+ this.update();
130
+ }
131
+
132
+ /** Coerce + reflect a single prop; no re-render (caller decides). */
133
+ _assignProp(key, val) {
134
+ const type = this.constructor.propTypes[key];
135
+ let next = val;
136
+ if (type === 'Boolean') {
137
+ next = val === true || val === '';
138
+ } else if (type === 'Number' && val !== null && val !== undefined && val !== '') {
139
+ next = Number(val);
140
+ }
141
+ this._props[key] = next;
142
+
143
+ // Only reflect primitives — objects/arrays stay as properties (no [object Object] attrs)
144
+ const isPrimitive =
145
+ next === null ||
146
+ next === undefined ||
147
+ typeof next === 'string' ||
148
+ typeof next === 'number' ||
149
+ typeof next === 'boolean';
150
+
151
+ this._reflecting = true;
152
+ try {
153
+ if (type === 'Boolean') {
154
+ if (next) this.setAttribute(key, '');
155
+ else this.removeAttribute(key);
156
+ } else if (!isPrimitive) {
157
+ // property-only; drop stale attribute if any
158
+ this.removeAttribute(key);
159
+ } else if (next === null || next === undefined) {
160
+ this.removeAttribute(key);
161
+ } else {
162
+ this.setAttribute(key, String(next));
163
+ }
164
+ } finally {
165
+ this._reflecting = false;
166
+ }
167
+
168
+ }
169
+
170
+ _emitChange() {
171
+ // Shallow-copy both bags so listeners cannot mutate internal state/props via event.detail
172
+ this.dispatchEvent(new CustomEvent('kitbash-change', {
173
+ detail: {
174
+ state: { ...this._state },
175
+ props: { ...this._props },
176
+ },
177
+ bubbles: true,
178
+ composed: true
179
+ }));
180
+ }
181
+
182
+ /**
183
+ * Batch props and/or state: one uhtml update, one kitbash-change.
184
+ * Prefer this in event handlers (e.g. controlled inputs).
185
+ */
186
+ commit(patch) {
187
+ const p = patch || {};
188
+ if (p.props) {
189
+ const allowed = this.constructor.propTypes || {};
190
+ for (const key of Object.keys(p.props)) {
191
+ // Only declared props — ignore typos / stray keys
192
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) continue;
193
+ this._assignProp(key, p.props[key]);
194
+ }
195
+ }
196
+ if (p.state) {
197
+ this._state = { ...this._state, ...p.state };
198
+ }
199
+ this.update();
200
+ this._emitChange();
201
+ }
202
+
203
+ setState(newState) {
204
+ this.commit({ state: newState });
205
+ }
206
+
207
+ setProps(nextProps) {
208
+ this.commit({ props: nextProps });
209
+ }
210
+
211
+ _handlerCtx() {
212
+ // Snapshots so handlers/render cannot mutate internal bags by accident
213
+ return {
214
+ props: { ...this._props },
215
+ state: { ...this._state },
216
+ setState: this.setState.bind(this),
217
+ setProps: this.setProps.bind(this),
218
+ commit: this.commit.bind(this),
219
+ };
220
+ }
221
+
222
+ cleanupEvents() {
223
+ this._eventCleanup.forEach(cleanup => cleanup());
224
+ this._eventCleanup = [];
225
+ }
226
+
227
+ update() {
228
+ // Run the uhtml DOM diffing render cycle
229
+ render(this.shadowRoot, this._renderFn({
230
+ ...this._handlerCtx(),
231
+ html
232
+ }));
233
+ this.cleanupEvents();
234
+ this.bindEvents();
235
+
236
+ }
237
+
238
+
239
+
240
+ bindEvents() {
241
+ Object.keys(this._eventHandlers).forEach(eventSelector => {
242
+ const parts = eventSelector.split(' ');
243
+ const eventName = parts[0];
244
+ const selector = parts.slice(1).join(' ');
245
+
246
+ const targets = selector ? this.shadowRoot.querySelectorAll(selector) : [this];
247
+ targets.forEach(target => {
248
+ const handler = (e) => {
249
+ this._eventHandlers[eventSelector](e, this._handlerCtx());
250
+ };
251
+ target.addEventListener(eventName, handler);
252
+ this._eventCleanup.push(() => target.removeEventListener(eventName, handler));
253
+ });
254
+ });
255
+ }
256
+ }
257
+
258
+ if (!customElements.get('kitbash-button')) {
259
+ customElements.define('kitbash-button', KitbashButton);
260
+ }
@@ -0,0 +1,2 @@
1
+ /** Side-effect import — registers the custom element. */
2
+ export {};
@@ -0,0 +1,96 @@
1
+ var d=(_,U,B,Y,I)=>{let W=B.length,J=U.length,z=W,O=0,$=0,X=null;while(O<J||$<z)if(J===O){let Q=z<W?$?Y(B[$-1],-0).nextSibling:Y(B[z],0):I;while($<z)_.insertBefore(Y(B[$++],1),Q)}else if(z===$)while(O<J){if(!X||!X.has(U[O]))_.removeChild(Y(U[O],-1));O++}else if(U[O]===B[$])O++,$++;else if(U[J-1]===B[z-1])J--,z--;else if(U[O]===B[z-1]&&B[$]===U[J-1]){let Q=Y(U[--J],-0).nextSibling;_.insertBefore(Y(B[$++],1),Y(U[O++],-0).nextSibling),_.insertBefore(Y(B[--z],1),Q),U[J]=B[z]}else{if(!X){X=new Map;let Q=$;while(Q<z)X.set(B[Q],Q++)}if(X.has(U[O])){let Q=X.get(U[O]);if($<Q&&Q<z){let Z=O,K=1;while(++Z<J&&Z<z&&X.get(U[Z])===Q+K)K++;if(K>Q-$){let D=Y(U[O],0);while($<Q)_.insertBefore(Y(B[$++],1),D)}else _.replaceChild(Y(B[$++],1),Y(U[O++],-1))}else O++}else _.removeChild(Y(U[O++],-1))}return B};var{isArray:E}=Array,{getPrototypeOf:R1,getOwnPropertyDescriptor:D1}=Object;var i="http://www.w3.org/2000/svg",q=[],N=()=>document.createRange(),M=(_,U,B)=>{return _.set(U,B),B},m=(_,U)=>{let B;do B=D1(_,U);while(!B&&(_=R1(_)));return B},o=(_,U)=>U.reduceRight(M1,_),M1=(_,U)=>_.childNodes[U];var s=1;var r=8;var l=11;var{setPrototypeOf:T1}=Object,n=(_)=>{function U(B){return T1(B,new.target.prototype)}return U.prototype=_.prototype,U};var T,j=(_,U,B)=>{if(!T)T=N();if(B)T.setStartAfter(_);else T.setStartBefore(_);return T.setEndAfter(U),T.deleteContents(),_};var w=({firstChild:_,lastChild:U},B)=>j(_,U,B),t=!1,V=(_,U)=>t&&_.nodeType===l?1/U<0?U?w(_,!0):_.lastChild:U?_.valueOf():_.firstChild:_,a=(_)=>document.createComment(_);class x extends n(DocumentFragment){#U=a("<>");#B=a("</>");#_=q;constructor(_){super(_);this.replaceChildren(...[this.#U,..._.childNodes,this.#B]),t=!0}get firstChild(){return this.#U}get lastChild(){return this.#B}get parentNode(){return this.#U.parentNode}remove(){w(this,!1)}replaceWith(_){w(this,!0).replaceWith(_)}valueOf(){let{parentNode:_}=this;if(_===this){if(this.#_===q)this.#_=[...this.childNodes]}else{if(_){let{firstChild:U,lastChild:B}=this;this.#_=[U];while(U!==B)this.#_.push(U=U.nextSibling)}this.replaceChildren(...this.#_)}return this}}var e=(_,U,B)=>_.setAttribute(U,B),P=(_,U)=>_.removeAttribute(U),H1=(_,U)=>{for(let B in U){let Y=U[B],I=B==="role"?B:`aria-${B}`;if(Y==null)P(_,I);else e(_,I,Y)}return U},C,P1=(_,U,B)=>{if(B=B.slice(1),!C)C=new WeakMap;let Y=C.get(_)||M(C,_,{}),I=Y[B];if(I&&I[0])_.removeEventListener(B,...I);if(I=E(U)?U:[U,!1],Y[B]=I,I[0])_.addEventListener(B,...I);return U};var G=(_,U)=>{let{t:B,n:Y}=_,I=!1;switch(typeof U){case"object":if(U!==null){(Y||B).replaceWith(_.n=U.valueOf());break}case"undefined":I=!0;default:if(B.data=I?"":U,Y)_.n=null,Y.replaceWith(B);break}return U},F1=(_,U)=>f(_,U,U==null?"class":"className"),A1=(_,U)=>{let{dataset:B}=_;for(let Y in U)if(U[Y]==null)delete B[Y];else B[Y]=U[Y];return U},k=(_,U,B)=>_[B]=U,E1=(_,U,B)=>k(_,U,B.slice(1)),f=(_,U,B)=>U==null?(P(_,B),U):k(_,U,B),_1=(_,U)=>(typeof U==="function"?U(_):U.current=_,U),y=(_,U,B)=>(U==null?P(_,B):e(_,B,U),U),N1=(_,U)=>U==null?f(_,U,"style"):k(_.style,U,"cssText"),j1=(_,U,B)=>(_.toggleAttribute(B.slice(1),U),U),R=(_,U,B)=>{let{length:Y}=U;if(_.data=`[${Y}]`,Y)return d(_.parentNode,B,U,V,_);switch(B.length){case 1:B[0].remove();case 0:break;default:j(V(B[0],0),V(B.at(-1),-0),!1);break}return q},U1=new Map([["aria",H1],["class",F1],["data",A1],["ref",_1],["style",N1]]),B1=(_,U,B)=>{switch(U[0]){case".":return E1;case"?":return j1;case"@":return P1;default:return B||"ownerSVGElement"in _?U==="ref"?_1:y:U1.get(U)||(U in _?U.startsWith("on")?k:m(_,U)?.set?f:y:y)}},Y1=(_,U)=>(_.textContent=U==null?"":U,U);var H=(_,U,B)=>({a:_,b:U,c:B}),z1=(_,U)=>({b:_,c:U}),I1=(_,U,B,Y)=>({v:q,u:_,t:U,n:B,c:Y}),L=()=>H(null,null,q);var b=(_)=>(U,B)=>{let{a:Y,b:I,c:W}=_(U,B),J=document.importNode(Y,!0),z=q;if(I!==q){z=[];for(let O,$,X=0;X<I.length;X++){let{a:Q,b:Z,c:K}=I[X],D=Q===$?O:O=o(J,$=Q);z[X]=I1(Z,D,K,Z===R?[]:Z===G?L():null)}}return z1(W?J.firstChild:new x(J),z)};var J1=/^(?:plaintext|script|style|textarea|title|xmp)$/i,O1=/^(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)$/i;var V1=/<([a-zA-Z0-9]+[a-zA-Z0-9:._-]*)([^>]*?)(\/?)>/g,C1=/([^\s\\>"'=]+)\s*=\s*(['"]?)\x01/g,k1=/[\x01\x02]/g,Q1=(_,U,B)=>{let Y=0;return _.join("\x01").trim().replace(V1,(I,W,J,z)=>`<${W}${J.replace(C1,"\x02=$2$1").trimEnd()}${z?B||O1.test(W)?" /":`></${W}`:""}>`).replace(k1,(I)=>I==="\x01"?`<!--${U+Y++}-->`:U+Y++)};var S=document.createElement("template"),g,h,W1=(_,U)=>{if(U){if(!g)g=document.createElementNS(i,"svg"),h=N(),h.selectNodeContents(g);return h.createContextualFragment(_)}S.innerHTML=_;let{content:B}=S;return S=S.cloneNode(!1),B};var c=(_)=>{let U=[],B;while(B=_.parentNode)U.push(U.indexOf.call(B.childNodes,_)),_=B;return U},X1=()=>document.createTextNode(""),S1=(_,U,B)=>{let Y=W1(Q1(_,F,B),B),{length:I}=_,W=q;if(I>1){let O=[],$=document.createTreeWalker(Y,129),X=0,Q=`${F}${X++}`;W=[];while(X<I){let Z=$.nextNode();if(Z.nodeType===r){if(Z.data===Q){let K=E(U[X-1])?R:G;if(K===G)O.push(Z);W.push(H(c(Z),K,null)),Q=`${F}${X++}`}}else{let K;while(Z.hasAttribute(Q)){if(!K)K=c(Z);let D=Z.getAttribute(Q);W.push(H(K,B1(Z,D,B),D)),P(Z,Q),Q=`${F}${X++}`}if(!B&&J1.test(Z.localName)&&Z.textContent.trim()===`<!--${Q}-->`)W.push(H(K||c(Z),Y1,null)),Q=`${F}${X++}`}}for(X=0;X<O.length;X++)O[X].replaceWith(X1())}let{childNodes:J}=Y,{length:z}=J;if(z<1)z=1,Y.appendChild(X1());else if(z===1&&I!==1&&J[0].nodeType!==s)z=0;return M($1,_,H(Y,W,z===1))},$1=new WeakMap,F="isµ",v=(_)=>(U,B)=>$1.get(U)||S1(U,B,_);var w1=b(v(!1)),x1=b(v(!0)),p=(_,{s:U,t:B,v:Y})=>{if(_.a!==B){let{b:I,c:W}=(U?x1:w1)(B,Y);_.a=B,_.b=I,_.c=W}for(let{c:I}=_,W=0;W<I.length;W++){let J=Y[W],z=I[W];switch(z.u){case R:z.v=R(z.t,y1(z.c,J),z.v);break;case G:let O=J instanceof A?p(z.c||(z.c=L()),J):(z.c=null,J);if(O!==z.v)z.v=G(z,O);break;default:if(J!==z.v)z.v=z.u(z.t,J,z.n,z.v);break}}return _.b},y1=(_,U)=>{let B=0,{length:Y}=U;if(Y<_.length)_.splice(Y);for(;B<Y;B++){let I=U[B];if(I instanceof A)U[B]=p(_[B]||(_[B]=L()),I);else _[B]=null}return U};class A{constructor(_,U,B){this.s=_,this.t=U,this.v=B}toDOM(_=L()){return p(_,this)}}var Z1=new WeakMap,u=(_,U)=>{let B=Z1.get(_)||M(Z1,_,L()),{b:Y}=B;if(Y!==(typeof U==="function"?U():U).toDOM(B))_.replaceChildren(B.b.valueOf());return _};/*! (c) Andrea Giammarchi - MIT */var K1=(_)=>(U,...B)=>new A(_,U,B),q1=K1(!1),C_=K1(!0);var G1=new CSSStyleSheet;G1.replaceSync(`:host {
2
+ --kb-color-bg-canvas: #ffffff;
3
+ --kb-color-bg-surface: #f6f8fa;
4
+ --kb-color-bg-subtle: #eef1f4;
5
+ --kb-color-bg-inverse: #1f2328;
6
+ --kb-color-fg-default: #1f2328;
7
+ --kb-color-fg-muted: #656d76;
8
+ --kb-color-fg-subtle: #8c959f;
9
+ --kb-color-fg-on-accent: #ffffff;
10
+ --kb-color-fg-on-inverse: #ffffff;
11
+ --kb-color-border-default: #d0d7de;
12
+ --kb-color-border-muted: #e6ebf0;
13
+ --kb-color-border-focus: #0969da;
14
+ --kb-color-accent-default: #0969da;
15
+ --kb-color-accent-hover: #0860ca;
16
+ --kb-color-accent-subtle: #ddf4ff;
17
+ --kb-color-danger-default: #cf222e;
18
+ --kb-color-danger-subtle: #ffebe9;
19
+ --kb-color-success-default: #1a7f37;
20
+ --kb-color-success-subtle: #dafbe1;
21
+ --kb-color-warning-default: #9a6700;
22
+ --kb-color-warning-subtle: #fff8c5;
23
+ --kb-space-2xs: 2px;
24
+ --kb-space-xs: 4px;
25
+ --kb-space-sm: 8px;
26
+ --kb-space-md: 16px;
27
+ --kb-space-lg: 24px;
28
+ --kb-space-xl: 32px;
29
+ --kb-space-2xl: 48px;
30
+ --kb-radius-sm: 4px;
31
+ --kb-radius-md: 8px;
32
+ --kb-radius-lg: 12px;
33
+ --kb-radius-full: 9999px;
34
+ --kb-font-family-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
35
+ --kb-font-size-sm: 0.875rem;
36
+ --kb-font-size-md: 1rem;
37
+ --kb-font-size-lg: 1.125rem;
38
+ --kb-font-size-xl: 1.25rem;
39
+ --kb-font-weight-regular: 400;
40
+ --kb-font-weight-medium: 500;
41
+ --kb-font-weight-semibold: 600;
42
+ --kb-line-height-tight: 1.25;
43
+ --kb-line-height-normal: 1.5;
44
+ --kb-shadow-sm: 0 1px 2px rgba(31, 35, 40, 0.08);
45
+ --kb-shadow-md: 0 4px 12px rgba(31, 35, 40, 0.1);
46
+ --kb-focus-ring: 0 0 0 3px rgba(9, 105, 218, 0.35);
47
+ }
48
+
49
+
50
+ :host {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ gap: var(--kb-space-sm);
54
+ font-family: var(--kb-font-family-sans);
55
+ font-size: var(--kb-font-size-md);
56
+ color: var(--kb-color-fg-default);
57
+ }
58
+ input {
59
+ width: 1.125rem;
60
+ height: 1.125rem;
61
+ min-width: 1.125rem;
62
+ min-height: 1.125rem;
63
+ margin: 0;
64
+ accent-color: var(--kb-color-accent-default);
65
+ cursor: pointer;
66
+ }
67
+ input:focus-visible {
68
+ outline: none;
69
+ box-shadow: var(--kb-focus-ring);
70
+ border-radius: var(--kb-radius-sm);
71
+ }
72
+ input:disabled {
73
+ cursor: not-allowed;
74
+ opacity: 0.55;
75
+ }
76
+ input[aria-invalid="true"] {
77
+ outline: 1px solid var(--kb-color-danger-default);
78
+ }
79
+ .label {
80
+ line-height: var(--kb-line-height-normal);
81
+ }
82
+ `);class L1 extends HTMLElement{static formAssociated=!0;static get observedAttributes(){return["name","value","checked","disabled","required","invalid"]}static get propTypes(){return{name:"String",value:"String",checked:"Boolean",disabled:"Boolean",required:"Boolean",invalid:"Boolean"}}get name(){return this._props.name}set name(_){this._assignProp("name",_),this.update()}get value(){return this._props.value}set value(_){this._assignProp("value",_),this.update()}get checked(){return this._props.checked}set checked(_){this._assignProp("checked",_),this.update()}get disabled(){return this._props.disabled}set disabled(_){this._assignProp("disabled",_),this.update()}get required(){return this._props.required}set required(_){this._assignProp("required",_),this.update()}get invalid(){return this._props.invalid}set invalid(_){this._assignProp("invalid",_),this.update()}constructor(){super();this.attachShadow({mode:"open",delegatesFocus:!0}),this._internals=this.attachInternals(),this.shadowRoot.adoptedStyleSheets=[G1],this._defaults={name:"",value:"on",checked:!1,disabled:!1,required:!1,invalid:!1},this._state={},this._props={...this._defaults},this._eventHandlers={"change input":function(_,{commit:U}){let B=_.target;U({props:{checked:B.checked}})}},this._eventCleanup=[],this._renderFn=function({props:_,html:U}){return U`
83
+ <input
84
+ part="checkbox-root"
85
+ type="checkbox"
86
+ name=${_.name}
87
+ value=${_.value}
88
+ .checked=${_.checked}
89
+ ?disabled=${_.disabled}
90
+ ?required=${_.required}
91
+ aria-invalid=${_.invalid?"true":"false"}
92
+ />
93
+ <span class="label" part="checkbox-label">
94
+ <slot></slot>
95
+ </span>
96
+ `},this._reflecting=!1}connectedCallback(){if(this.update(),this.value!==void 0)this._internals.setFormValue(this.value==null?null:String(this.value))}disconnectedCallback(){this.cleanupEvents()}attributeChangedCallback(_,U,B){if(this._reflecting)return;if(U===B)return;let Y=this.constructor.propTypes[_],I=B;if(B===null||B===void 0)I=this._defaults[_];else if(Y==="Number"&&B!=="")I=Number(B);else if(Y==="Boolean")I=B!==null&&B!=="false";if(this._props[_]=I,_==="value"&&this._internals)this._internals.setFormValue(I==null?null:String(I));this.update()}_assignProp(_,U){let B=this.constructor.propTypes[_],Y=U;if(B==="Boolean")Y=U===!0||U==="";else if(B==="Number"&&U!==null&&U!==void 0&&U!=="")Y=Number(U);this._props[_]=Y;let I=Y===null||Y===void 0||typeof Y==="string"||typeof Y==="number"||typeof Y==="boolean";this._reflecting=!0;try{if(B==="Boolean")if(Y)this.setAttribute(_,"");else this.removeAttribute(_);else if(!I)this.removeAttribute(_);else if(Y===null||Y===void 0)this.removeAttribute(_);else this.setAttribute(_,String(Y))}finally{this._reflecting=!1}if(_==="value"&&this._internals)this._internals.setFormValue(Y==null?null:String(Y))}_emitChange(){this.dispatchEvent(new CustomEvent("kitbash-change",{detail:{state:{...this._state},props:{...this._props}},bubbles:!0,composed:!0}))}commit(_){let U=_||{};if(U.props){let B=this.constructor.propTypes||{};for(let Y of Object.keys(U.props)){if(!Object.prototype.hasOwnProperty.call(B,Y))continue;this._assignProp(Y,U.props[Y])}}if(U.state)this._state={...this._state,...U.state};this.update(),this._emitChange()}setState(_){this.commit({state:_})}setProps(_){this.commit({props:_})}_handlerCtx(){return{props:{...this._props},state:{...this._state},setState:this.setState.bind(this),setProps:this.setProps.bind(this),commit:this.commit.bind(this)}}cleanupEvents(){this._eventCleanup.forEach((_)=>_()),this._eventCleanup=[]}update(){u(this.shadowRoot,this._renderFn({...this._handlerCtx(),html:q1})),this.cleanupEvents(),this.bindEvents(),this.syncFormState()}syncFormState(){let _={},U="";if(this._props.invalid)_.customError=!0,U="Invalid field.";else if(this._props.required&&!this._props.value)_.valueMissing=!0,U="This field is required.";if(Object.keys(_).length>0)this._internals.setValidity(_,U);else this._internals.setValidity({})}bindEvents(){Object.keys(this._eventHandlers).forEach((_)=>{let U=_.split(" "),B=U[0],Y=U.slice(1).join(" ");(Y?this.shadowRoot.querySelectorAll(Y):[this]).forEach((W)=>{let J=(z)=>{this._eventHandlers[_](z,this._handlerCtx())};W.addEventListener(B,J),this._eventCleanup.push(()=>W.removeEventListener(B,J))})})}}if(!customElements.get("kitbash-checkbox"))customElements.define("kitbash-checkbox",L1);export{L1 as KitbashCheckbox};
@@ -0,0 +1,296 @@
1
+
2
+ import { render, html } from 'uhtml';
3
+
4
+ const sheet = new CSSStyleSheet();
5
+ sheet.replaceSync(":host {\n--kb-color-bg-canvas: #ffffff;\n--kb-color-bg-surface: #f6f8fa;\n--kb-color-bg-subtle: #eef1f4;\n--kb-color-bg-inverse: #1f2328;\n--kb-color-fg-default: #1f2328;\n--kb-color-fg-muted: #656d76;\n--kb-color-fg-subtle: #8c959f;\n--kb-color-fg-on-accent: #ffffff;\n--kb-color-fg-on-inverse: #ffffff;\n--kb-color-border-default: #d0d7de;\n--kb-color-border-muted: #e6ebf0;\n--kb-color-border-focus: #0969da;\n--kb-color-accent-default: #0969da;\n--kb-color-accent-hover: #0860ca;\n--kb-color-accent-subtle: #ddf4ff;\n--kb-color-danger-default: #cf222e;\n--kb-color-danger-subtle: #ffebe9;\n--kb-color-success-default: #1a7f37;\n--kb-color-success-subtle: #dafbe1;\n--kb-color-warning-default: #9a6700;\n--kb-color-warning-subtle: #fff8c5;\n--kb-space-2xs: 2px;\n--kb-space-xs: 4px;\n--kb-space-sm: 8px;\n--kb-space-md: 16px;\n--kb-space-lg: 24px;\n--kb-space-xl: 32px;\n--kb-space-2xl: 48px;\n--kb-radius-sm: 4px;\n--kb-radius-md: 8px;\n--kb-radius-lg: 12px;\n--kb-radius-full: 9999px;\n--kb-font-family-sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n--kb-font-size-sm: 0.875rem;\n--kb-font-size-md: 1rem;\n--kb-font-size-lg: 1.125rem;\n--kb-font-size-xl: 1.25rem;\n--kb-font-weight-regular: 400;\n--kb-font-weight-medium: 500;\n--kb-font-weight-semibold: 600;\n--kb-line-height-tight: 1.25;\n--kb-line-height-normal: 1.5;\n--kb-shadow-sm: 0 1px 2px rgba(31, 35, 40, 0.08);\n--kb-shadow-md: 0 4px 12px rgba(31, 35, 40, 0.1);\n--kb-focus-ring: 0 0 0 3px rgba(9, 105, 218, 0.35);\n}\n\n\n :host {\n display: inline-flex;\n align-items: center;\n gap: var(--kb-space-sm);\n font-family: var(--kb-font-family-sans);\n font-size: var(--kb-font-size-md);\n color: var(--kb-color-fg-default);\n }\n input {\n width: 1.125rem;\n height: 1.125rem;\n min-width: 1.125rem;\n min-height: 1.125rem;\n margin: 0;\n accent-color: var(--kb-color-accent-default);\n cursor: pointer;\n }\n input:focus-visible {\n outline: none;\n box-shadow: var(--kb-focus-ring);\n border-radius: var(--kb-radius-sm);\n }\n input:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n }\n input[aria-invalid=\"true\"] {\n outline: 1px solid var(--kb-color-danger-default);\n }\n .label {\n line-height: var(--kb-line-height-normal);\n }\n ");
6
+
7
+ export class KitbashCheckbox extends HTMLElement {
8
+ static formAssociated = true;
9
+ static get observedAttributes() {
10
+ return ["name","value","checked","disabled","required","invalid"];
11
+ }
12
+
13
+ static get propTypes() {
14
+ return {"name":"String","value":"String","checked":"Boolean","disabled":"Boolean","required":"Boolean","invalid":"Boolean"};
15
+ }
16
+
17
+
18
+ get name() {
19
+ return this._props['name'];
20
+ }
21
+ set name(val) {
22
+ // External / framework property writes: re-render, do not emit kitbash-change
23
+ // (caller already knows the value they set).
24
+ this._assignProp('name', val);
25
+ this.update();
26
+ }
27
+
28
+ get value() {
29
+ return this._props['value'];
30
+ }
31
+ set value(val) {
32
+ // External / framework property writes: re-render, do not emit kitbash-change
33
+ // (caller already knows the value they set).
34
+ this._assignProp('value', val);
35
+ this.update();
36
+ }
37
+
38
+ get checked() {
39
+ return this._props['checked'];
40
+ }
41
+ set checked(val) {
42
+ // External / framework property writes: re-render, do not emit kitbash-change
43
+ // (caller already knows the value they set).
44
+ this._assignProp('checked', val);
45
+ this.update();
46
+ }
47
+
48
+ get disabled() {
49
+ return this._props['disabled'];
50
+ }
51
+ set disabled(val) {
52
+ // External / framework property writes: re-render, do not emit kitbash-change
53
+ // (caller already knows the value they set).
54
+ this._assignProp('disabled', val);
55
+ this.update();
56
+ }
57
+
58
+ get required() {
59
+ return this._props['required'];
60
+ }
61
+ set required(val) {
62
+ // External / framework property writes: re-render, do not emit kitbash-change
63
+ // (caller already knows the value they set).
64
+ this._assignProp('required', val);
65
+ this.update();
66
+ }
67
+
68
+ get invalid() {
69
+ return this._props['invalid'];
70
+ }
71
+ set invalid(val) {
72
+ // External / framework property writes: re-render, do not emit kitbash-change
73
+ // (caller already knows the value they set).
74
+ this._assignProp('invalid', val);
75
+ this.update();
76
+ }
77
+
78
+
79
+ constructor() {
80
+ super();
81
+ this.attachShadow({ mode: 'open', delegatesFocus: true });
82
+ this._internals = this.attachInternals();
83
+ this.shadowRoot.adoptedStyleSheets = [sheet];
84
+
85
+ this._defaults = {"name":"","value":"on","checked":false,"disabled":false,"required":false,"invalid":false};
86
+
87
+ this._state = {};
88
+ this._props = { ...this._defaults };
89
+ this._eventHandlers = { 'change input': function(e, { commit }) {
90
+ const target = e.target;
91
+ commit({
92
+ props: { checked: target.checked }
93
+ });
94
+ } };
95
+ this._eventCleanup = [];
96
+ this._renderFn = function({ props, html }) {
97
+ return html`
98
+ <input
99
+ part="checkbox-root"
100
+ type="checkbox"
101
+ name=${props.name}
102
+ value=${props.value}
103
+ .checked=${props.checked}
104
+ ?disabled=${props.disabled}
105
+ ?required=${props.required}
106
+ aria-invalid=${props.invalid ? "true" : "false"}
107
+ />
108
+ <span class="label" part="checkbox-label">
109
+ <slot></slot>
110
+ </span>
111
+ `;
112
+ };
113
+ this._reflecting = false;
114
+ }
115
+
116
+ connectedCallback() {
117
+ // Initial attribute values are parsed by attributeChangedCallback before connectedCallback fires
118
+ this.update();
119
+ if (this.value !== undefined) this._internals.setFormValue(this.value == null ? null : String(this.value));
120
+ }
121
+
122
+ disconnectedCallback() {
123
+ this.cleanupEvents();
124
+ }
125
+
126
+ attributeChangedCallback(name, oldValue, newValue) {
127
+ // Skip when we caused the attribute write ourselves (avoids double update)
128
+ if (this._reflecting) return;
129
+ if (oldValue === newValue) return;
130
+
131
+ const type = this.constructor.propTypes[name];
132
+
133
+ let parsedValue = newValue;
134
+ if (newValue === null || newValue === undefined) {
135
+ parsedValue = this._defaults[name];
136
+ } else if (type === 'Number' && newValue !== '') {
137
+ // Match property coercion: empty string is not Number('') → 0
138
+ parsedValue = Number(newValue);
139
+ } else if (type === 'Boolean') {
140
+ parsedValue = newValue !== null && newValue !== 'false';
141
+ }
142
+
143
+ this._props[name] = parsedValue;
144
+ if (name === 'value' && this._internals) this._internals.setFormValue(parsedValue == null ? null : String(parsedValue));
145
+ this.update();
146
+ }
147
+
148
+ /** Coerce + reflect a single prop; no re-render (caller decides). */
149
+ _assignProp(key, val) {
150
+ const type = this.constructor.propTypes[key];
151
+ let next = val;
152
+ if (type === 'Boolean') {
153
+ next = val === true || val === '';
154
+ } else if (type === 'Number' && val !== null && val !== undefined && val !== '') {
155
+ next = Number(val);
156
+ }
157
+ this._props[key] = next;
158
+
159
+ // Only reflect primitives — objects/arrays stay as properties (no [object Object] attrs)
160
+ const isPrimitive =
161
+ next === null ||
162
+ next === undefined ||
163
+ typeof next === 'string' ||
164
+ typeof next === 'number' ||
165
+ typeof next === 'boolean';
166
+
167
+ this._reflecting = true;
168
+ try {
169
+ if (type === 'Boolean') {
170
+ if (next) this.setAttribute(key, '');
171
+ else this.removeAttribute(key);
172
+ } else if (!isPrimitive) {
173
+ // property-only; drop stale attribute if any
174
+ this.removeAttribute(key);
175
+ } else if (next === null || next === undefined) {
176
+ this.removeAttribute(key);
177
+ } else {
178
+ this.setAttribute(key, String(next));
179
+ }
180
+ } finally {
181
+ this._reflecting = false;
182
+ }
183
+
184
+ if (key === 'value' && this._internals) {
185
+ this._internals.setFormValue(next == null ? null : String(next));
186
+ }
187
+ }
188
+
189
+ _emitChange() {
190
+ // Shallow-copy both bags so listeners cannot mutate internal state/props via event.detail
191
+ this.dispatchEvent(new CustomEvent('kitbash-change', {
192
+ detail: {
193
+ state: { ...this._state },
194
+ props: { ...this._props },
195
+ },
196
+ bubbles: true,
197
+ composed: true
198
+ }));
199
+ }
200
+
201
+ /**
202
+ * Batch props and/or state: one uhtml update, one kitbash-change.
203
+ * Prefer this in event handlers (e.g. controlled inputs).
204
+ */
205
+ commit(patch) {
206
+ const p = patch || {};
207
+ if (p.props) {
208
+ const allowed = this.constructor.propTypes || {};
209
+ for (const key of Object.keys(p.props)) {
210
+ // Only declared props — ignore typos / stray keys
211
+ if (!Object.prototype.hasOwnProperty.call(allowed, key)) continue;
212
+ this._assignProp(key, p.props[key]);
213
+ }
214
+ }
215
+ if (p.state) {
216
+ this._state = { ...this._state, ...p.state };
217
+ }
218
+ this.update();
219
+ this._emitChange();
220
+ }
221
+
222
+ setState(newState) {
223
+ this.commit({ state: newState });
224
+ }
225
+
226
+ setProps(nextProps) {
227
+ this.commit({ props: nextProps });
228
+ }
229
+
230
+ _handlerCtx() {
231
+ // Snapshots so handlers/render cannot mutate internal bags by accident
232
+ return {
233
+ props: { ...this._props },
234
+ state: { ...this._state },
235
+ setState: this.setState.bind(this),
236
+ setProps: this.setProps.bind(this),
237
+ commit: this.commit.bind(this),
238
+ };
239
+ }
240
+
241
+ cleanupEvents() {
242
+ this._eventCleanup.forEach(cleanup => cleanup());
243
+ this._eventCleanup = [];
244
+ }
245
+
246
+ update() {
247
+ // Run the uhtml DOM diffing render cycle
248
+ render(this.shadowRoot, this._renderFn({
249
+ ...this._handlerCtx(),
250
+ html
251
+ }));
252
+ this.cleanupEvents();
253
+ this.bindEvents();
254
+ this.syncFormState();
255
+ }
256
+
257
+
258
+ syncFormState() {
259
+ const flags = {};
260
+ let msg = '';
261
+ if (this._props.invalid) {
262
+ flags.customError = true;
263
+ msg = 'Invalid field.';
264
+ } else if (this._props.required && !this._props.value) {
265
+ flags.valueMissing = true;
266
+ msg = 'This field is required.';
267
+ }
268
+ if (Object.keys(flags).length > 0) {
269
+ this._internals.setValidity(flags, msg);
270
+ } else {
271
+ this._internals.setValidity({});
272
+ }
273
+ }
274
+
275
+
276
+ bindEvents() {
277
+ Object.keys(this._eventHandlers).forEach(eventSelector => {
278
+ const parts = eventSelector.split(' ');
279
+ const eventName = parts[0];
280
+ const selector = parts.slice(1).join(' ');
281
+
282
+ const targets = selector ? this.shadowRoot.querySelectorAll(selector) : [this];
283
+ targets.forEach(target => {
284
+ const handler = (e) => {
285
+ this._eventHandlers[eventSelector](e, this._handlerCtx());
286
+ };
287
+ target.addEventListener(eventName, handler);
288
+ this._eventCleanup.push(() => target.removeEventListener(eventName, handler));
289
+ });
290
+ });
291
+ }
292
+ }
293
+
294
+ if (!customElements.get('kitbash-checkbox')) {
295
+ customElements.define('kitbash-checkbox', KitbashCheckbox);
296
+ }
@@ -0,0 +1,2 @@
1
+ /** Side-effect import — registers the custom element. */
2
+ export {};