@ktjs/mui 0.20.0 → 0.23.2

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.
@@ -1,17 +1,81 @@
1
1
  var __ktjs_mui__ = (function (exports) {
2
2
  'use strict';
3
3
 
4
+ // Shared constants
5
+ // Empty for now - can be extended with framework-wide constants
6
+ /**
7
+ * Mark the attribute as SVG to handle special cases during rendering.
8
+ */
9
+ const $defines = Object.defineProperties;
10
+ const $entries = Object.entries;
11
+ const $random$2 = Math.random;
12
+ const { get: $buttonDisabledGetter$2, set: $buttonDisabledSetter$2 } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
13
+ const parseStyle = (style) => {
14
+ if (!style) {
15
+ return '';
16
+ }
17
+ if (typeof style === 'string') {
18
+ return style;
19
+ }
20
+ if (style && typeof style === 'object') {
21
+ if (style.isKT) {
22
+ return parseStyle(style.value);
23
+ }
24
+ return $entries(style)
25
+ .map((entry) => {
26
+ const cssKey = entry[0].replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
27
+ return `${cssKey}:${entry[1]}`;
28
+ })
29
+ .join(';');
30
+ }
31
+ return '';
32
+ };
33
+
34
+ // String manipulation utilities
35
+ /**
36
+ * Default empty function
37
+ */
38
+ const $emptyFn = (() => true);
39
+
40
+ if (typeof Symbol === 'undefined') {
41
+ window.Symbol = function Symbol(description) {
42
+ return `@@SYMBOL_${description || ''}_${$random$2().toString(36).slice(2)}`;
43
+ };
44
+ }
45
+
46
+ // Shared utilities and cached native methods for kt.js framework
47
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.4' });
48
+
49
+ // Shared constants
50
+ // Empty for now - can be extended with framework-wide constants
51
+ /**
52
+ * Mark the attribute as SVG to handle special cases during rendering.
53
+ */
54
+ const SVG_ATTR_FLAG = '__kt_svg__';
55
+ /**
56
+ * Mark the attribute as MathML to handle special cases during rendering.
57
+ */
58
+ const MATHML_ATTR_FLAG = '__kt_mathml__';
59
+
4
60
  // Cached native methods for performance optimization
5
61
  const $isArray = Array.isArray;
6
- const $isThenable = (o) => typeof o === 'object' && o !== null && typeof o.then === 'function';
7
-
8
- // Error handling utilities
9
- const $throw = (message) => {
10
- throw new Error('@ktjs/shared: ' + message);
11
- };
62
+ const $random$1 = Math.random;
63
+ const $isThenable = (o) => typeof o?.then === 'function';
12
64
 
13
65
  // DOM manipulation utilities
14
66
  // # dom natives
67
+ const $isNode$1 = (x) => x?.nodeType > 0;
68
+ /**
69
+ * Safe replace `oldNode` With `newNode`
70
+ */
71
+ const $replaceNode$1 = (oldNode, newNode) => {
72
+ if ($isNode$1(oldNode) && $isNode$1(newNode)) {
73
+ if (newNode.contains(oldNode)) {
74
+ newNode.remove();
75
+ }
76
+ oldNode.replaceWith(newNode);
77
+ }
78
+ };
15
79
  /**
16
80
  * & Remove `bind` because it is shockingly slower than wrapper
17
81
  * & `window.document` is safe because it is not configurable and its setter is undefined
@@ -47,11 +111,28 @@ var __ktjs_mui__ = (function (exports) {
47
111
  $appendChild.call(this, fragment);
48
112
  }
49
113
  };
50
- const { get: $buttonDisabledGetter$2, set: $buttonDisabledSetter$2 } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
114
+ const { get: $buttonDisabledGetter$1, set: $buttonDisabledSetter$1 } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
115
+ /**
116
+ * Used for `k-model`
117
+ */
118
+ const applyModel = (element, valueRef, propName, eventName) => {
119
+ element[propName] = valueRef.value; // initialize
120
+ valueRef.addOnChange((newValue) => (element[propName] = newValue));
121
+ element.addEventListener(eventName, () => (valueRef.value = element[propName]));
122
+ };
123
+
124
+ if (typeof Symbol === 'undefined') {
125
+ window.Symbol = function Symbol(description) {
126
+ return `@@SYMBOL_${description || ''}_${$random$1().toString(36).slice(2)}`;
127
+ };
128
+ }
51
129
 
52
130
  // Shared utilities and cached native methods for kt.js framework
53
- // Re-export all utilities
54
- Object.defineProperty(window, '@ktjs/shared', { value: '0.20.0' });
131
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.4' });
132
+
133
+ const isKT$1 = (obj) => obj?.isKT;
134
+ const isRef = (obj) => obj?.ktType === 1 /* KTReactiveType.REF */;
135
+ const isComputed = (obj) => obj?.ktType === 2 /* KTReactiveType.COMPUTED */;
55
136
 
56
137
  const booleanHandler = (element, key, value) => {
57
138
  if (key in element) {
@@ -95,10 +176,25 @@ var __ktjs_mui__ = (function (exports) {
95
176
  };
96
177
 
97
178
  const defaultHandler = (element, key, value) => element.setAttribute(key, value);
179
+ const setElementStyle = (element, style) => {
180
+ if (typeof style === 'string') {
181
+ element.style.cssText = style;
182
+ return;
183
+ }
184
+ for (const key in style) {
185
+ element.style[key] = style[key];
186
+ }
187
+ };
98
188
  function attrIsObject(element, attr) {
99
189
  const classValue = attr.class || attr.className;
100
190
  if (classValue !== undefined) {
101
- element.setAttribute('class', classValue);
191
+ if (isKT$1(classValue)) {
192
+ element.setAttribute('class', classValue.value);
193
+ classValue.addOnChange((v) => element.setAttribute('class', v));
194
+ }
195
+ else {
196
+ element.setAttribute('class', classValue);
197
+ }
102
198
  }
103
199
  const style = attr.style;
104
200
  if (style) {
@@ -106,18 +202,34 @@ var __ktjs_mui__ = (function (exports) {
106
202
  element.setAttribute('style', style);
107
203
  }
108
204
  else if (typeof style === 'object') {
109
- for (const key in style) {
110
- element.style[key] = style[key];
205
+ if (isKT$1(style)) {
206
+ setElementStyle(element, style.value);
207
+ style.addOnChange((v) => setElementStyle(element, v));
111
208
  }
209
+ else {
210
+ setElementStyle(element, style);
211
+ }
212
+ }
213
+ }
214
+ if ('k-html' in attr) {
215
+ const html = attr['k-html'];
216
+ if (isKT$1(html)) {
217
+ element.innerHTML = html.value;
218
+ html.addOnChange((v) => (element.innerHTML = v));
219
+ }
220
+ else {
221
+ element.innerHTML = html;
112
222
  }
113
223
  }
114
224
  for (const key in attr) {
115
- if (key === 'class' ||
225
+ if (key === 'k-if' ||
226
+ key === 'k-model' ||
227
+ key === 'ref' ||
228
+ key === 'class' ||
116
229
  key === 'className' ||
117
230
  key === 'style' ||
118
231
  key === 'children' ||
119
- key === 'k-if' ||
120
- key === 'ref') {
232
+ key === 'k-html') {
121
233
  continue;
122
234
  }
123
235
  const o = attr[key];
@@ -127,7 +239,14 @@ var __ktjs_mui__ = (function (exports) {
127
239
  }
128
240
  // normal attributes
129
241
  else {
130
- (handlers[key] || defaultHandler)(element, key, o);
242
+ const handler = handlers[key] || defaultHandler;
243
+ if (isKT$1(o)) {
244
+ handler(element, key, o.value);
245
+ o.addOnChange((v) => handler(element, key, v));
246
+ }
247
+ else {
248
+ handler(element, key, o);
249
+ }
131
250
  }
132
251
  }
133
252
  }
@@ -139,17 +258,28 @@ var __ktjs_mui__ = (function (exports) {
139
258
  attrIsObject(element, attr);
140
259
  }
141
260
  else {
142
- throw new Error('kt.js: attr must be an object.');
261
+ throw new Error('[kt.js error] attr must be an object.');
143
262
  }
144
263
  }
145
264
 
265
+ const assureNode = (o) => ($isNode$1(o) ? o : document.createTextNode(o));
146
266
  function apdSingle(element, c) {
147
267
  // & JSX should ignore false, undefined, and null
148
268
  if (c === false || c === undefined || c === null) {
149
269
  return;
150
270
  }
151
- if (typeof c === 'object' && c !== null && 'isKT' in c) {
152
- $append.call(element, c.value);
271
+ if (isKT$1(c)) {
272
+ let node = assureNode(c.value);
273
+ $append.call(element, node);
274
+ c.addOnChange((newValue, oldValue) => {
275
+ if ($isNode$1(newValue) && $isNode$1(oldValue)) {
276
+ // & this case is handled automically in `class KTRef`
277
+ return;
278
+ }
279
+ const oldNode = node;
280
+ node = assureNode(newValue);
281
+ oldNode.replaceWith(node);
282
+ });
153
283
  }
154
284
  else {
155
285
  $append.call(element, c);
@@ -194,7 +324,34 @@ var __ktjs_mui__ = (function (exports) {
194
324
  }
195
325
  }
196
326
 
197
- const svgTempWrapper = document.createElement('div');
327
+ function applyKModel(element, valueRef) {
328
+ if (!isKT$1(valueRef)) {
329
+ console.warn('[kt.js warn]','k-model value must be a KTRef.');
330
+ return;
331
+ }
332
+ if (element instanceof HTMLInputElement) {
333
+ if (element.type === 'radio' || element.type === 'checkbox') {
334
+ applyModel(element, valueRef, 'checked', 'change');
335
+ }
336
+ else {
337
+ applyModel(element, valueRef, 'value', 'input');
338
+ }
339
+ }
340
+ else if (element instanceof HTMLSelectElement) {
341
+ applyModel(element, valueRef, 'value', 'change');
342
+ }
343
+ else if (element instanceof HTMLTextAreaElement) {
344
+ applyModel(element, valueRef, 'value', 'input');
345
+ }
346
+ else {
347
+ console.warn('[kt.js warn]','not supported element for k-model:');
348
+ }
349
+ }
350
+
351
+ const htmlCreator = (tag) => document.createElement(tag);
352
+ const svgCreator = (tag) => document.createElementNS('http://www.w3.org/2000/svg', tag);
353
+ const mathMLCreator = (tag) => document.createElementNS('http://www.w3.org/1998/Math/MathML', tag);
354
+ let creator = htmlCreator;
198
355
  /**
199
356
  * Create an enhanced HTMLElement.
200
357
  * - Only supports HTMLElements, **NOT** SVGElements or other Elements.
@@ -205,7 +362,7 @@ var __ktjs_mui__ = (function (exports) {
205
362
  * ## About
206
363
  * @package @ktjs/core
207
364
  * @author Kasukabe Tsumugi <futami16237@gmail.com>
208
- * @version 0.20.0 (Last Update: 2026.02.01 00:47:09.995)
365
+ * @version 0.26.9 (Last Update: 2026.02.09 00:03:34.324)
209
366
  * @license MIT
210
367
  * @link https://github.com/baendlorel/kt.js
211
368
  * @link https://baendlorel.github.io/ Welcome to my site!
@@ -214,42 +371,77 @@ var __ktjs_mui__ = (function (exports) {
214
371
  */
215
372
  const h = (tag, attr, content) => {
216
373
  if (typeof tag !== 'string') {
217
- $throw('tagName must be a string.');
374
+ throw new Error('[kt.js error] tagName must be a string.');
375
+ }
376
+ if (attr) {
377
+ if (SVG_ATTR_FLAG in attr) {
378
+ delete attr[SVG_ATTR_FLAG];
379
+ creator = svgCreator;
380
+ }
381
+ else if (MATHML_ATTR_FLAG in attr) {
382
+ delete attr[MATHML_ATTR_FLAG];
383
+ creator = mathMLCreator;
384
+ }
385
+ else {
386
+ creator = htmlCreator;
387
+ }
218
388
  }
219
389
  // * start creating the element
220
- const element = document.createElement(tag);
390
+ const element = creator(tag);
221
391
  // * Handle content
222
392
  applyAttr(element, attr);
223
393
  applyContent(element, content);
224
- if (tag === 'svg') {
225
- svgTempWrapper.innerHTML = element.outerHTML;
226
- return svgTempWrapper.firstChild;
394
+ if (typeof attr === 'object' && attr !== null && 'k-model' in attr) {
395
+ applyKModel(element, attr['k-model']);
227
396
  }
228
397
  return element;
229
398
  };
230
399
 
231
400
  const dummyRef = { value: null };
401
+ const create = (tag, props) => {
402
+ if (typeof tag === 'function') {
403
+ return tag(props);
404
+ }
405
+ else {
406
+ return h(tag, props, props.children);
407
+ }
408
+ };
409
+ const placeholder = () => document.createComment('k-if');
232
410
  /**
233
411
  * @param tag html tag or function component
234
412
  * @param props properties/attributes
235
413
  */
236
- function jsx(tag, props = {}) {
237
- const ref = props.ref?.isKT ? props.ref : dummyRef;
238
- let el;
239
- if ('k-if' in props && !props['k-if']) {
240
- // & make comment placeholder in case that ref might be redrawn later
241
- el = document.createComment('k-if');
242
- ref.value = el;
243
- return el;
244
- }
245
- // Handle function components
246
- if (typeof tag === 'function') {
247
- el = tag(props);
414
+ function jsx(tag, props) {
415
+ if (isComputed(props.ref)) {
416
+ throw new Error('[kt.js error] Cannot assign a computed value to an element.');
248
417
  }
249
- else {
250
- el = h(tag, props, props.children);
418
+ const maybeDummyRef = isRef(props.ref) ? props.ref : dummyRef;
419
+ let el;
420
+ if ('k-if' in props) {
421
+ const kif = props['k-if'];
422
+ let condition = kif; // assume boolean by default
423
+ // Handle reactive k-if
424
+ if (isKT$1(kif)) {
425
+ kif.addOnChange((newValue, oldValue) => {
426
+ if (newValue === oldValue) {
427
+ return;
428
+ }
429
+ const oldEl = el;
430
+ el = newValue ? create(tag, props) : placeholder();
431
+ $replaceNode$1(oldEl, el);
432
+ maybeDummyRef.value = el;
433
+ });
434
+ condition = kif.value;
435
+ }
436
+ if (!condition) {
437
+ // & make comment placeholder in case that ref might be redrawn later
438
+ el = placeholder();
439
+ maybeDummyRef.value = el;
440
+ return el;
441
+ }
251
442
  }
252
- ref.value = el;
443
+ el = create(tag, props);
444
+ maybeDummyRef.value = el;
253
445
  return el;
254
446
  }
255
447
  /**
@@ -258,48 +450,8 @@ var __ktjs_mui__ = (function (exports) {
258
450
  */
259
451
  const jsxs = jsx;
260
452
 
261
- // Cached native methods for performance optimization
262
- const $defines = Object.defineProperties;
263
-
264
- // String manipulation utilities
265
- /**
266
- * Default empty function
267
- */
268
- const $emptyFn = (() => true);
269
- const { get: $buttonDisabledGetter$1, set: $buttonDisabledSetter$1 } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
270
- // # DOM utilities
271
- const parseStyle = (style) => {
272
- if (typeof style === 'string') {
273
- return style;
274
- }
275
- if (style && typeof style === 'object') {
276
- return Object.entries(style)
277
- .map(([key, value]) => {
278
- // Convert camelCase to kebab-case
279
- const cssKey = key.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
280
- return `${cssKey}: ${value}`;
281
- })
282
- .join('; ');
283
- }
284
- return '';
285
- };
286
- const generateHandler = (props, key) => {
287
- const handler = props[key];
288
- if (typeof handler === 'function') {
289
- return handler;
290
- }
291
- else if (handler && typeof handler === 'object' && handler.isKT) {
292
- return (value) => (handler.value = value);
293
- }
294
- return $emptyFn;
295
- };
296
-
297
- // Shared utilities and cached native methods for kt.js framework
298
- // Re-export all utilities
299
- Object.defineProperty(window, '@ktjs/shared', { value: '0.20.0' });
300
-
301
453
  function Alert(props) {
302
- const { children, severity = 'info', variant = 'standard', icon, 'kt:close': onClose } = props;
454
+ const { children, severity = 'info', variant = 'standard', icon, 'on:close': onClose } = props;
303
455
  const classes = `mui-alert mui-alert-${severity} mui-alert-${variant} ${props.class ? props.class : ''}`;
304
456
  // Icon SVG paths for different severities
305
457
  const getIcon = () => {
@@ -324,205 +476,413 @@ var __ktjs_mui__ = (function (exports) {
324
476
  }
325
477
  };
326
478
  const alertIcon = getIcon();
327
- const alert = (jsxs("div", { class: classes, style: parseStyle(props.style), role: "alert", children: [alertIcon && jsx("div", { class: "mui-alert-icon-wrapper", children: alertIcon }), jsx("div", { class: "mui-alert-message", children: children }), onClose && (jsx("button", { class: "mui-alert-close", "on:click": onClose, "aria-label": "Close", children: jsx("svg", { viewBox: "0 0 24 24", width: "18px", height: "18px", children: jsx("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) }))] }));
479
+ const alert = (jsxs("div", { class: classes, style: parseStyle(props.style), role: "alert", children: [alertIcon && jsx("div", { class: "mui-alert-icon-wrapper", children: alertIcon }), jsx("div", { class: "mui-alert-message", children: children }), jsx("button", { "k-if": onClose, class: "mui-alert-close", "on:click": onClose, "aria-label": "Close", children: jsx("svg", { viewBox: "0 0 24 24", width: "18px", height: "18px", children: jsx("path", { fill: "currentColor", d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) }) })] }));
328
480
  return alert;
329
481
  }
330
482
 
483
+ // Shared constants
484
+ // Empty for now - can be extended with framework-wide constants
331
485
  /**
332
- * Button component - mimics MUI Button appearance and behavior
486
+ * Mark the attribute as SVG to handle special cases during rendering.
333
487
  */
334
- function Button(props) {
335
- let { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = $emptyFn, } = props;
336
- const classes = [
337
- 'mui-button',
338
- `mui-button-${variant}`,
339
- `mui-button-${variant}-${color}`,
340
- `mui-button-size-${size}`,
341
- fullWidth ? 'mui-button-fullwidth' : '',
342
- iconOnly ? 'mui-button-icon-only' : '',
343
- disabled ? 'mui-button-disabled' : '',
344
- props.class ? props.class : '',
345
- ].join(' ');
346
- const rippleContainer = jsx("span", { class: "mui-button-ripple" });
347
- const handleClick = (e) => {
348
- if (disabled) {
349
- e.preventDefault();
350
- return;
351
- }
352
- // Create ripple effect
353
- const button = e.currentTarget;
354
- if (rippleContainer) {
355
- const rect = button.getBoundingClientRect();
356
- const size = Math.max(rect.width, rect.height);
357
- const x = e.clientX - rect.left - size / 2;
358
- const y = e.clientY - rect.top - size / 2;
359
- const ripple = document.createElement('span');
360
- ripple.style.width = ripple.style.height = `${size}px`;
361
- ripple.style.left = `${x}px`;
362
- ripple.style.top = `${y}px`;
363
- ripple.classList.add('mui-button-ripple-effect');
364
- rippleContainer.appendChild(ripple);
365
- // Remove ripple after animation
366
- setTimeout(() => ripple.remove(), 600);
367
- }
368
- onClick(e);
369
- };
370
- const container = (jsxs("button", { class: classes, style: parseStyle(props.style), type: type, disabled: disabled, "on:click": handleClick, children: [startIcon && jsx("span", { class: "mui-button-start-icon", children: startIcon }), jsx("span", { class: "mui-button-label", children: children }), endIcon && jsx("span", { class: "mui-button-end-icon", children: endIcon }), rippleContainer] }));
371
- $defines(container, {
372
- disabled: {
373
- get: $buttonDisabledGetter$1,
374
- set: function (value) {
375
- $buttonDisabledSetter$1.call(this, value);
376
- this.classList.toggle('mui-button-disabled', value);
377
- },
378
- },
379
- });
380
- return container;
381
- }
488
+ const $is = Object.is;
489
+ const $random = Math.random;
382
490
 
491
+ // DOM manipulation utilities
492
+ // # dom natives
493
+ const $isNode = (x) => x?.nodeType > 0;
383
494
  /**
384
- * Checkbox component - mimics MUI Checkbox appearance and behavior
495
+ * Safe replace `oldNode` With `newNode`
385
496
  */
386
- function Checkbox(props) {
387
- const toggleIcon = (checked, indeterminate) => {
388
- if (indeterminate) {
389
- uncheckedIcon.style.display = 'none';
390
- checkedIcon.style.display = 'none';
391
- indeterminateIcon.style.display = '';
392
- }
393
- else {
394
- uncheckedIcon.style.display = checked ? 'none' : '';
395
- checkedIcon.style.display = checked ? '' : 'none';
396
- indeterminateIcon.style.display = 'none';
497
+ const $replaceNode = (oldNode, newNode) => {
498
+ if ($isNode(oldNode) && $isNode(newNode)) {
499
+ if (newNode.contains(oldNode)) {
500
+ newNode.remove();
397
501
  }
502
+ oldNode.replaceWith(newNode);
503
+ }
504
+ };
505
+ const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
506
+
507
+ if (typeof Symbol === 'undefined') {
508
+ window.Symbol = function Symbol(description) {
509
+ return `@@SYMBOL_${description || ''}_${$random().toString(36).slice(2)}`;
398
510
  };
399
- // Handle change
400
- const handleChange = () => {
401
- if (disabled) {
511
+ }
512
+
513
+ // Shared utilities and cached native methods for kt.js framework
514
+ Object.defineProperty(window, '__ktjs__', { value: '0.23.4' });
515
+
516
+ const isKT = (obj) => obj?.isKT;
517
+
518
+ class KTRef {
519
+ /**
520
+ * Indicates that this is a KTRef instance
521
+ */
522
+ isKT = true;
523
+ ktType = 1 /* KTReactiveType.REF */;
524
+ /**
525
+ * @internal
526
+ */
527
+ _value;
528
+ /**
529
+ * @internal
530
+ */
531
+ _onChanges;
532
+ constructor(_value, _onChanges) {
533
+ this._value = _value;
534
+ this._onChanges = _onChanges;
535
+ }
536
+ /**
537
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
538
+ */
539
+ get value() {
540
+ return this._value;
541
+ }
542
+ set value(newValue) {
543
+ if ($is(newValue, this._value)) {
402
544
  return;
403
545
  }
404
- checked = inputEl.checked;
405
- indeterminate = false;
406
- toggleIcon(checked, indeterminate);
407
- onChange(checked, value);
408
- };
409
- let { checked = false, value = '', label = '', size = 'medium', disabled = false, color = 'primary', indeterminate = false, } = props;
410
- const onChange = generateHandler(props, 'kt:change');
411
- const inputEl = (jsx("input", { type: "checkbox", class: "mui-checkbox-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }));
412
- // Unchecked icon
413
- const uncheckedIcon = (jsx("span", { class: "mui-checkbox-icon-unchecked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z" }) }) }));
414
- // Checked icon
415
- const checkedIcon = (jsx("span", { class: "mui-checkbox-icon-checked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }) }));
416
- // Indeterminate icon
417
- const indeterminateIcon = (jsx("span", { class: "mui-checkbox-icon-indeterminate", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z" }) }) }));
418
- // Initialize icon state
419
- toggleIcon(checked, indeterminate);
420
- const container = (jsxs("label", { class: `mui-checkbox-wrapper mui-checkbox-size-${size} ${disabled ? 'mui-checkbox-disabled' : ''} mui-checkbox-color-${color}`, children: [inputEl, jsxs("span", { class: "mui-checkbox-icon", children: [uncheckedIcon, checkedIcon, indeterminateIcon] }), jsx("span", { "k-if": label, class: "mui-checkbox-label", children: label })] }));
421
- $defines(container, {
422
- checked: {
423
- get() {
424
- return checked;
425
- },
426
- set(newChecked) {
427
- checked = newChecked;
428
- indeterminate = false;
429
- inputEl.checked = checked;
430
- toggleIcon(checked, indeterminate);
431
- },
432
- },
433
- value: {
434
- get() {
435
- return value;
436
- },
437
- set(newValue) {
438
- value = newValue;
439
- inputEl.value = value;
440
- },
441
- },
442
- disabled: {
443
- get() {
444
- return disabled;
445
- },
446
- set(newDisabled) {
447
- disabled = Boolean(newDisabled);
448
- inputEl.disabled = disabled;
449
- container.classList.toggle('mui-checkbox-disabled', disabled);
450
- },
451
- },
452
- });
453
- return container;
454
- }
455
- /**
456
- * CheckboxGroup component - groups multiple checkboxes together
457
- */
458
- function CheckboxGroup(props) {
459
- let { value = [], size = 'medium', row = false } = props;
460
- const onChange = generateHandler(props, 'kt:change');
461
- let selectedValues = new Set(value);
462
- const changeHandler = (checked, checkboxValue) => {
463
- if (checked) {
464
- selectedValues.add(checkboxValue);
546
+ const oldValue = this._value;
547
+ $replaceNode(oldValue, newValue);
548
+ this._value = newValue;
549
+ for (let i = 0; i < this._onChanges.length; i++) {
550
+ this._onChanges[i](newValue, oldValue);
465
551
  }
466
- else {
467
- selectedValues.delete(checkboxValue);
552
+ }
553
+ /**
554
+ * Register a callback when the value changes
555
+ * @param callback (newValue, oldValue) => xxx
556
+ */
557
+ addOnChange(callback) {
558
+ if (typeof callback !== 'function') {
559
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
468
560
  }
469
- onChange(Array.from(selectedValues));
470
- };
471
- const checkboxes = props.options.map((o) => {
472
- o.size = size;
473
- o.checked = selectedValues.has(o.value);
474
- const originalChange = generateHandler(o, 'kt:change');
475
- if (originalChange) {
476
- o['kt:change'] = (checked, value) => {
477
- originalChange(checked, value);
478
- changeHandler(checked, value);
479
- };
561
+ this._onChanges.push(callback);
562
+ }
563
+ removeOnChange(callback) {
564
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
565
+ if (this._onChanges[i] === callback) {
566
+ this._onChanges.splice(i, 1);
567
+ return true;
568
+ }
480
569
  }
481
- else {
482
- o['kt:change'] = changeHandler;
570
+ return false;
571
+ }
572
+ }
573
+ /**
574
+ * Reference to the created HTML element.
575
+ * - **Only** respond to `ref.value` changes, not reactive to internal changes of the element.
576
+ * - can alse be used to store normal values, but it is not reactive.
577
+ * - if the value is already a `KTRef`, it will be returned **directly**.
578
+ * @param value mostly an HTMLElement
579
+ */
580
+ function ref(value, onChange) {
581
+ return new KTRef(value, onChange ? [onChange] : []);
582
+ }
583
+ function deref(value) {
584
+ return isKT(value) ? value.value : value;
585
+ }
586
+ // # asserts
587
+ /**
588
+ * Assert k-model to be a ref object
589
+ */
590
+ const $modelOrRef = (props, defaultValue) => {
591
+ // & props is an object. Won't use it in any other place
592
+ if ('k-model' in props) {
593
+ const kmodel = props['k-model'];
594
+ if (!kmodel?.isKT) {
595
+ throw new Error(`[kt.js error] k-model data must be a KTRef object, please use 'ref(...)' to wrap it.`);
483
596
  }
484
- return Checkbox(o);
485
- });
486
- const container = (jsx("div", { class: `mui-checkbox-group ${row ? 'mui-checkbox-group-row' : ''} ${props.class ? props.class : ''}`, style: props.style ? props.style : '', role: "group", children: checkboxes }));
487
- $defines(container, {
488
- value: {
489
- get() {
490
- return Array.from(selectedValues);
491
- },
492
- set(newValues) {
493
- selectedValues = new Set(newValues);
494
- for (let i = 0; i < checkboxes.length; i++) {
495
- const checkbox = checkboxes[i];
496
- checkbox.checked = selectedValues.has(checkbox.value);
497
- }
498
- },
499
- },
500
- disabled: {
501
- get() {
502
- return checkboxes.map((cb) => cb.disabled);
503
- },
504
- set(newDisabled) {
505
- for (let i = 0; i < checkboxes.length; i++) {
506
- const checkbox = checkboxes[i];
507
- checkbox.disabled = Boolean(newDisabled);
508
- }
509
- },
510
- },
511
- disableAll: {
512
- value: () => {
513
- for (let i = 0; i < checkboxes.length; i++) {
514
- checkboxes[i].disabled = true;
597
+ return kmodel;
598
+ }
599
+ return ref(defaultValue);
600
+ };
601
+
602
+ class KTComputed {
603
+ /**
604
+ * Indicates that this is a KTRef instance
605
+ */
606
+ isKT = true;
607
+ ktType = 2 /* KTReactiveType.COMPUTED */;
608
+ /**
609
+ * @internal
610
+ */
611
+ _calculator;
612
+ /**
613
+ * @internal
614
+ */
615
+ _value;
616
+ /**
617
+ * @internal
618
+ */
619
+ _onChanges = [];
620
+ /**
621
+ * @internal
622
+ */
623
+ _subscribe(reactives) {
624
+ for (let i = 0; i < reactives.length; i++) {
625
+ const reactive = reactives[i];
626
+ reactive.addOnChange(() => {
627
+ const oldValue = this._value;
628
+ this._value = this._calculator();
629
+ if (oldValue === this._value) {
630
+ return;
515
631
  }
516
- },
517
- },
518
- enableAll: {
519
- value: () => {
520
- for (let i = 0; i < checkboxes.length; i++) {
521
- checkboxes[i].disabled = false;
632
+ $replaceNode(oldValue, this._value);
633
+ for (let i = 0; i < this._onChanges.length; i++) {
634
+ this._onChanges[i](this._value, oldValue);
522
635
  }
523
- },
524
- },
636
+ });
637
+ }
638
+ }
639
+ constructor(_calculator, reactives) {
640
+ this._calculator = _calculator;
641
+ this._value = _calculator();
642
+ this._subscribe(reactives);
643
+ }
644
+ /**
645
+ * If new value and old value are both nodes, the old one will be replaced in the DOM
646
+ */
647
+ get value() {
648
+ return this._value;
649
+ }
650
+ set value(_newValue) {
651
+ throw new Error('[kt.js error] KTComputed: cannot set value of a computed value');
652
+ }
653
+ /**
654
+ * Register a callback when the value changes
655
+ * @param callback (newValue, oldValue) => xxx
656
+ */
657
+ addOnChange(callback) {
658
+ if (typeof callback !== 'function') {
659
+ throw new Error('[kt.js error] KTRef.addOnChange: callback must be a function');
660
+ }
661
+ this._onChanges.push(callback);
662
+ }
663
+ /**
664
+ * Unregister a callback
665
+ * @param callback (newValue, oldValue) => xxx
666
+ */
667
+ removeOnChange(callback) {
668
+ for (let i = this._onChanges.length - 1; i >= 0; i--) {
669
+ if (this._onChanges[i] === callback) {
670
+ this._onChanges.splice(i, 1);
671
+ return true;
672
+ }
673
+ }
674
+ return false;
675
+ }
676
+ }
677
+ /**
678
+ * Create a reactive computed value
679
+ * @param computeFn
680
+ * @param reactives refs and computeds that this computed depends on
681
+ */
682
+ function computed(computeFn, reactives) {
683
+ if (reactives.some((v) => !isKT(v))) {
684
+ throw new Error('[kt.js error] computed: all reactives must be KTRef or KTComputed instances');
685
+ }
686
+ return new KTComputed(computeFn, reactives);
687
+ }
688
+
689
+ const toReactive = (value, onChange) => {
690
+ if (isKT(value)) {
691
+ if (onChange) {
692
+ value.addOnChange(onChange);
693
+ }
694
+ return value;
695
+ }
696
+ else {
697
+ return ref(value, onChange);
698
+ }
699
+ };
700
+
701
+ const registerPrefixedEventsForButton = (element, props) => {
702
+ for (const key in props) {
703
+ if (key.startsWith('on:') && key !== 'on:click' && key !== 'on:dblclick') {
704
+ element.addEventListener(key.slice(3), props[key]);
705
+ }
706
+ }
707
+ };
708
+
709
+ /**
710
+ * Button component - mimics MUI Button appearance and behavior
711
+ */
712
+ function Button(props) {
713
+ let { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = $emptyFn, // & must be bound because of the ripple effect
714
+ } = props;
715
+ const updateClass = () => {
716
+ container.className = [
717
+ 'mui-button',
718
+ `mui-button-${variant}`,
719
+ `mui-button-${variant}-${color}`,
720
+ `mui-button-size-${size}`,
721
+ fullWidth ? 'mui-button-fullwidth' : '',
722
+ iconOnly ? 'mui-button-icon-only' : '',
723
+ disabledRef.value ? 'mui-button-disabled' : '',
724
+ props.class ? props.class : '',
725
+ ].join(' ');
726
+ };
727
+ const disabledRef = toReactive(disabled, (v) => {
728
+ container.disabled = v;
729
+ updateClass();
730
+ });
731
+ const rippleContainer = jsx("span", { class: "mui-button-ripple" });
732
+ const createRippleEffect = (mouseX, mouseY) => {
733
+ const rect = container.getBoundingClientRect();
734
+ const size = Math.max(rect.width, rect.height);
735
+ const x = mouseX - rect.left - size / 2;
736
+ const y = mouseY - rect.top - size / 2;
737
+ const ripple = (jsx("span", { class: "mui-button-ripple-effect", style: `width:${size}px; height:${size}px; left:${x}px; top:${y}px;` }));
738
+ rippleContainer.appendChild(ripple);
739
+ setTimeout(() => ripple.remove(), 600); // Remove ripple after animation
740
+ };
741
+ const handleClick = (e) => {
742
+ if (disabledRef.value) {
743
+ e.preventDefault();
744
+ return;
745
+ }
746
+ createRippleEffect(e.clientX, e.clientY);
747
+ onClick(e);
748
+ };
749
+ const container = (jsxs("button", { style: parseStyle(props.style), type: type, disabled: disabledRef.value, "on:click": handleClick, children: [jsx("span", { "k-if": startIcon, class: "mui-button-start-icon", children: startIcon }), jsx("span", { class: "mui-button-label", children: children }), jsx("span", { "k-if": endIcon, class: "mui-button-end-icon", children: endIcon }), rippleContainer] }));
750
+ const onDblclick = props['on:dblclick'];
751
+ if (onDblclick) {
752
+ container.addEventListener('dblclick', (e) => {
753
+ if (disabledRef.value) {
754
+ e.preventDefault();
755
+ return;
756
+ }
757
+ createRippleEffect(e.clientX, e.clientY);
758
+ onDblclick(e);
759
+ });
760
+ }
761
+ registerPrefixedEventsForButton(container, props);
762
+ // # initialize
763
+ updateClass();
764
+ return container;
765
+ }
766
+
767
+ const $ArrayPushUnique = (arr, item) => {
768
+ if (!arr.includes(item)) {
769
+ arr.push(item);
770
+ }
771
+ };
772
+ const $ArrayDelete = (arr, item) => {
773
+ const index = arr.indexOf(item);
774
+ if (index > -1) {
775
+ arr.splice(index, 1);
776
+ }
777
+ };
778
+
779
+ const createUncheckedIcon = () => (jsx("span", { class: "mui-checkbox-icon-unchecked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z" }) }) }));
780
+ const createCheckedIcon = () => (jsx("span", { class: "mui-checkbox-icon-checked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }) }));
781
+ const createIndeterminateIcon = () => (jsx("span", { class: "mui-checkbox-icon-indeterminate", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-2 10H7v-2h10v2z" }) }) }));
782
+ /**
783
+ * Checkbox component - mimics MUI Checkbox appearance and behavior
784
+ */
785
+ function Checkbox(props) {
786
+ const toggleIcon = (checked, indeterminate) => {
787
+ if (indeterminate) {
788
+ uncheckedIcon.style.display = 'none';
789
+ checkedIcon.style.display = 'none';
790
+ indeterminateIcon.style.display = '';
791
+ }
792
+ else {
793
+ uncheckedIcon.style.display = checked ? 'none' : '';
794
+ checkedIcon.style.display = checked ? '' : 'none';
795
+ indeterminateIcon.style.display = 'none';
796
+ }
797
+ };
798
+ // Handle change
799
+ const handleChange = () => {
800
+ if (disabledRef.value) {
801
+ return;
802
+ }
803
+ modelRef.value = inputEl.checked;
804
+ interminateRef.value = false;
805
+ toggleIcon(modelRef.value, interminateRef.value);
806
+ onChange(modelRef.value, valueRef.value);
807
+ };
808
+ const onChange = props['on:change'] ?? $emptyFn;
809
+ const labelRef = toReactive(props.label ?? '');
810
+ const valueRef = toReactive(props.value ?? '');
811
+ const interminateRef = toReactive(props.indeterminate ?? false);
812
+ const colorRef = toReactive(props.color ?? 'primary');
813
+ const sizeRef = toReactive(props.size ?? 'medium');
814
+ const disabledRef = toReactive(props.disabled ?? false, (v) => {
815
+ inputEl.disabled = v;
816
+ container.classList.toggle('mui-checkbox-disabled', v);
525
817
  });
818
+ const modelRef = $modelOrRef(props, props.checked ?? false);
819
+ modelRef.addOnChange((newValue) => {
820
+ inputEl.checked = newValue;
821
+ toggleIcon(newValue, interminateRef.value);
822
+ });
823
+ const inputEl = (jsx("input", { type: "checkbox", class: "mui-checkbox-input", checked: modelRef.value, value: valueRef, disabled: disabledRef, "on:change": handleChange }));
824
+ const uncheckedIcon = createUncheckedIcon();
825
+ const checkedIcon = createCheckedIcon();
826
+ const indeterminateIcon = createIndeterminateIcon();
827
+ // Initialize icon state
828
+ toggleIcon(modelRef.value, interminateRef.value);
829
+ const classRef = computed(() => {
830
+ return `mui-checkbox-wrapper mui-checkbox-size-${sizeRef.value} ${disabledRef.value ? 'mui-checkbox-disabled' : ''} mui-checkbox-color-${colorRef.value}`;
831
+ }, [colorRef, disabledRef, sizeRef]);
832
+ const container = (jsxs("label", { class: classRef, children: [inputEl, jsxs("span", { class: "mui-checkbox-icon", children: [uncheckedIcon, checkedIcon, indeterminateIcon] }), jsx("span", { "k-if": labelRef, class: "mui-checkbox-label", children: labelRef })] }));
833
+ return container;
834
+ }
835
+ /**
836
+ * CheckboxGroup component - groups multiple checkboxes together
837
+ */
838
+ function CheckboxGroup(props) {
839
+ let { row = false, 'on:change': onChange = $emptyFn } = props;
840
+ const changeHandler = (checked, checkboxValue) => {
841
+ if (checked) {
842
+ $ArrayPushUnique(modelRef.value, checkboxValue);
843
+ }
844
+ else {
845
+ $ArrayDelete(modelRef.value, checkboxValue);
846
+ }
847
+ onChange(modelRef.value);
848
+ };
849
+ const rowRef = toReactive(props.row ?? true);
850
+ const sizeRef = toReactive(props.size ?? 'medium');
851
+ const modelRef = $modelOrRef(props, props.value ?? []);
852
+ modelRef.addOnChange((newValues) => {
853
+ for (let i = 0; i < checkboxes.value.length; i++) {
854
+ const c = checkboxes.value[i];
855
+ c.checked = newValues.includes(c.value);
856
+ }
857
+ });
858
+ const customClassRef = toReactive(props.class ?? '');
859
+ const classRef = computed(() => {
860
+ return `mui-checkbox-group ${rowRef.value ? 'mui-checkbox-group-row' : ''} ${customClassRef.value}`;
861
+ }, [rowRef, customClassRef]);
862
+ const styleRef = toReactive(parseStyle(props.style ?? ''));
863
+ const optionsRef = toReactive(props.options);
864
+ const checkboxes = computed(() => {
865
+ return optionsRef.value.map((o) => {
866
+ o.size = sizeRef.value;
867
+ o.checked = modelRef.value.includes(o.value);
868
+ const originalChange = o['on:change'];
869
+ if (originalChange) {
870
+ if (typeof originalChange !== 'function') {
871
+ throw new Error('[kt.js error] CheckboxGroup: handler must be a function');
872
+ }
873
+ o['on:change'] = (checked, value) => {
874
+ originalChange(checked, value);
875
+ changeHandler(checked, value);
876
+ };
877
+ }
878
+ else {
879
+ o['on:change'] = changeHandler;
880
+ }
881
+ return Checkbox(o);
882
+ });
883
+ }, [optionsRef, sizeRef]);
884
+ console.log('checkboxes', checkboxes.value);
885
+ const container = (jsx("div", { class: classRef, style: styleRef, role: "group", children: checkboxes }));
526
886
  return container;
527
887
  }
528
888
 
@@ -531,11 +891,30 @@ var __ktjs_mui__ = (function (exports) {
531
891
  * Only handles open/close state, title and content are passed as props
532
892
  */
533
893
  function Dialog(props) {
534
- let { open = false, 'kt:close': onClose = $emptyFn, title, children, actions, maxWidth = 'sm', fullWidth = false, } = props;
894
+ let { 'on:close': onClose = $emptyFn, children, actions } = props;
895
+ const titleRef = toReactive(props.title ?? '');
896
+ const openRef = toReactive(props.open ?? false, (isOpen) => {
897
+ if (isOpen) {
898
+ // Opening: set display first, then add class with double RAF for animation
899
+ container.style.display = 'flex';
900
+ setTimeout(() => container.classList.add('kt-dialog-backdrop-open'), 50);
901
+ }
902
+ else {
903
+ container.classList.remove('kt-dialog-backdrop-open');
904
+ setTimeout(() => {
905
+ if (!openRef.value) {
906
+ container.style.display = 'none';
907
+ }
908
+ }, 225);
909
+ }
910
+ });
911
+ const sizeRef = toReactive(props.size ?? 'sm');
912
+ const fullWidthRef = toReactive(props.fullWidth ?? false);
913
+ const classNameRef = computed(() => `kt-dialog-paper ${sizeRef.value ? `kt-dialog-maxWidth-${sizeRef.value}` : ''} ${fullWidthRef.value ? 'kt-dialog-fullWidth' : ''}`, [sizeRef, fullWidthRef]);
535
914
  // Handle ESC key - store handler for cleanup
536
915
  const keyDownHandler = (e) => {
537
916
  if (e.key === 'Escape') {
538
- open = false;
917
+ openRef.value = false;
539
918
  onClose();
540
919
  }
541
920
  };
@@ -545,7 +924,7 @@ var __ktjs_mui__ = (function (exports) {
545
924
  }
546
925
  };
547
926
  // Backdrop element
548
- const container = (jsx("div", { class: `kt-dialog-backdrop ${open ? 'kt-dialog-backdrop-open' : ''}`, "on:click": handleBackdropClick, style: { display: open ? 'flex' : 'none' }, children: jsxs("div", { class: `kt-dialog-paper ${maxWidth ? `kt-dialog-maxWidth-${maxWidth}` : ''} ${fullWidth ? 'kt-dialog-fullWidth' : ''}`, "on:click": (e) => e.stopPropagation(), children: [jsx("div", { "k-if": title, class: "kt-dialog-title", children: jsx("h2", { children: title }) }), jsx("div", { "k-if": children, class: "kt-dialog-content", children: children }), jsx("div", { "k-if": actions, class: "kt-dialog-actions", children: actions })] }) }));
927
+ const container = (jsx("div", { class: `kt-dialog-backdrop ${openRef.value ? 'kt-dialog-backdrop-open' : ''}`, style: { display: openRef.value ? 'flex' : 'none' }, "on:click": handleBackdropClick, children: jsxs("div", { class: classNameRef, "on:click": (e) => e.stopPropagation(), children: [jsx("div", { "k-if": titleRef, class: "kt-dialog-title", children: jsx("h2", { children: titleRef }) }), jsx("div", { "k-if": children, class: "kt-dialog-content", children: children }), jsx("div", { "k-if": actions, class: "kt-dialog-actions", children: actions })] }) }));
549
928
  document.addEventListener('keydown', keyDownHandler);
550
929
  // Store cleanup function
551
930
  const originalRemove = container.remove;
@@ -555,30 +934,6 @@ var __ktjs_mui__ = (function (exports) {
555
934
  }
556
935
  return originalRemove.call(container);
557
936
  };
558
- $defines(container, {
559
- open: {
560
- get: () => open,
561
- set: (isOpen) => {
562
- if (isOpen === open) {
563
- return;
564
- }
565
- open = isOpen;
566
- if (isOpen) {
567
- // Opening: set display first, then add class with double RAF for animation
568
- container.style.display = 'flex';
569
- setTimeout(() => container.classList.add('kt-dialog-backdrop-open'), 50);
570
- }
571
- else {
572
- container.classList.remove('kt-dialog-backdrop-open');
573
- setTimeout(() => {
574
- if (!open) {
575
- container.style.display = 'none';
576
- }
577
- }, 225);
578
- }
579
- },
580
- },
581
- });
582
937
  return container;
583
938
  }
584
939
 
@@ -606,270 +961,125 @@ var __ktjs_mui__ = (function (exports) {
606
961
  return element;
607
962
  }
608
963
 
609
- // Cached native methods for performance optimization
610
- const { get: $buttonDisabledGetter, set: $buttonDisabledSetter } = Object.getOwnPropertyDescriptor(HTMLButtonElement.prototype, 'disabled');
611
-
612
- // Shared utilities and cached native methods for kt.js framework
613
- // Re-export all utilities
614
- Object.defineProperty(window, '@ktjs/shared', { value: '0.20.0' });
615
-
616
- document.createElement('div');
617
-
618
- class KTRef {
619
- /**
620
- * Indicates that this is a KTRef instance
621
- */
622
- isKT = true;
623
- _value;
624
- _onChanges;
625
- constructor(_value, _onChanges) {
626
- this._value = _value;
627
- this._onChanges = _onChanges;
628
- }
629
- /**
630
- * If new value and old value are both nodes, the old one will be replaced in the DOM
631
- */
632
- get value() {
633
- return this._value;
634
- }
635
- set value(newValue) {
636
- if (newValue === this._value) {
637
- return;
638
- }
639
- // replace the old node with the new one in the DOM if both are nodes
640
- if (this._value instanceof Node && newValue instanceof Node) {
641
- if (newValue.contains(this._value)) {
642
- this._value.remove();
643
- }
644
- this._value.replaceWith(newValue);
645
- }
646
- const oldValue = this._value;
647
- this._value = newValue;
648
- for (let i = 0; i < this._onChanges.length; i++) {
649
- this._onChanges[i](newValue, oldValue);
650
- }
651
- }
652
- addOnChange(callback) {
653
- this._onChanges.push(callback);
654
- }
655
- removeOnChange(callback) {
656
- for (let i = this._onChanges.length - 1; i >= 0; i--) {
657
- if (this._onChanges[i] === callback) {
658
- this._onChanges.splice(i, 1);
659
- return true;
660
- }
661
- }
662
- return false;
663
- }
664
- }
665
- /**
666
- * Reference to the created HTML element.
667
- * - can alse be used to store normal values, but it is not reactive.
668
- * @param value mostly an HTMLElement
669
- */
670
- function ref(value, onChange) {
671
- return new KTRef(value, []);
672
- }
673
- /**
674
- * A helper to create redrawable elements
675
- * ```tsx
676
- * export function MyComponent() {
677
- * let aa = 10;
678
- * // ...
679
- * // aa might be changed
680
- * return createRedrawable(() => <div>{aa}</div>);
681
- * }
682
- * ```
683
- * Then the returned element has a `redraw` method to redraw itself with new values.
684
- * @param creator a simple creator function that returns an element
685
- * @returns created element's ref
686
- */
687
- function createRedrawable(creator) {
688
- const elRef = ref();
689
- const redraw = () => {
690
- elRef.value = creator(); // ref setter automatically calls replaceWith
691
- elRef.redraw = redraw;
692
- return elRef.value;
693
- };
694
- redraw();
695
- return elRef;
696
- }
697
-
698
- /**
699
- * LinearProgress component - mimics MUI LinearProgress appearance and behavior
700
- */
701
964
  function LinearProgress(props) {
702
- let { variant = 'indeterminate', progress: value = 0, color = 'primary' } = props;
703
- const classes = [
704
- 'mui-linear-progress',
705
- `mui-linear-progress-${color}`,
706
- `mui-linear-progress-${variant}`,
707
- props.class ? props.class : '',
708
- ].join(' ');
709
- const styleString = parseStyle(props.style);
710
- // Calculate progress percentage
711
- let progressValue = Math.min(Math.max(value, 0), 100);
712
- const barRef = ref();
713
- const container = (jsx("div", { class: classes, style: styleString, role: "progressbar", "aria-valuenow": progressValue, children: jsx("div", { ref: barRef, class: "mui-linear-progress-bar", style: variant === 'determinate' ? `width: ${progressValue}%` : '' }) }));
714
- $defines(container, {
715
- progress: {
716
- get() {
717
- return progressValue;
718
- },
719
- set(newValue) {
720
- progressValue = Math.min(Math.max(newValue, 0), 100);
721
- if (variant === 'determinate') {
722
- barRef.value.style.width = `${progressValue}%`;
723
- }
724
- },
725
- },
726
- });
965
+ const valueRef = ref(props.progress ?? 0);
966
+ const customClassRef = toReactive(props.class ?? '');
967
+ const colorRef = toReactive(props.color ?? 'primary');
968
+ const variantRef = toReactive(props.variant ?? 'indeterminate');
969
+ const classRef = computed(() => {
970
+ return `mui-linear-progress mui-linear-progress-${variantRef.value} mui-linear-progress-${colorRef.value} ${customClassRef.value}`;
971
+ }, [customClassRef, colorRef, variantRef]);
972
+ const styleRef = toReactive(props.style ?? '');
973
+ const barLengthRef = computed(() => {
974
+ return variantRef.value === 'determinate' ? `width: ${valueRef.value}%` : '';
975
+ }, [variantRef, valueRef]);
976
+ const container = (jsx("div", { class: classRef, style: styleRef, role: "progressbar", "aria-valuenow": valueRef, children: jsx("div", { class: "mui-linear-progress-bar", style: barLengthRef }) }));
727
977
  return container;
728
978
  }
729
979
 
730
- /**
731
- * TextField component - mimics MUI TextField appearance and behavior
732
- */
733
980
  function TextField(props) {
734
- let { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, size = 'medium', } = props;
735
- const onInput = generateHandler(props, 'kt:input');
736
- const onInputTrim = generateHandler(props, 'kt-trim:input');
737
- const onChange = generateHandler(props, 'kt:change');
738
- const onChangeTrim = generateHandler(props, 'kt-trim:change');
739
- const onBlur = generateHandler(props, 'kt:blur');
740
- const onFocus = generateHandler(props, 'kt:focus');
741
- const updateContainerClass = () => {
742
- container.className = [
743
- 'mui-textfield-root',
744
- `mui-textfield-size-${size}`,
745
- isFocused ? 'mui-textfield-focused' : '',
746
- error ? 'mui-textfield-error' : '',
747
- disabled ? 'mui-textfield-disabled' : '',
748
- fullWidth ? 'mui-textfield-fullwidth' : '',
749
- label && inputEl.value ? 'mui-textfield-has-value' : '',
750
- label ? '' : 'mui-textfield-no-label',
751
- ].join(' ');
752
- };
981
+ // # events
982
+ const onInput = props['on:input'] ?? $emptyFn;
983
+ const onInputTrim = props['on-trim:input'] ?? $emptyFn;
984
+ const onChange = props['on:change'] ?? $emptyFn;
985
+ const onChangeTrim = props['on-trim:change'] ?? $emptyFn;
986
+ const onBlur = props['on:blur'] ?? $emptyFn;
987
+ const onFocus = props['on:focus'] ?? $emptyFn;
988
+ const isFocusedRef = ref(false);
989
+ // # methods
753
990
  const handleInput = () => {
754
- updateContainerClass();
755
- if (type === 'number') {
991
+ if (inputType === 'number') {
756
992
  const v = Number(inputEl.value);
993
+ modelRef.value = v;
757
994
  onInput(v);
758
995
  onInputTrim(v);
759
996
  }
760
997
  else {
998
+ modelRef.value = inputEl.value;
761
999
  onInput(inputEl.value);
762
1000
  onInputTrim(inputEl.value.trim());
763
1001
  }
764
1002
  };
765
1003
  const handleChange = () => {
766
- updateContainerClass();
767
- if (type === 'number') {
1004
+ if (inputType === 'number') {
768
1005
  const v = Number(inputEl.value);
1006
+ modelRef.value = v;
769
1007
  onChange(v);
770
1008
  onChangeTrim(v);
771
1009
  }
772
1010
  else {
1011
+ modelRef.value = inputEl.value;
773
1012
  onChange(inputEl.value);
774
1013
  onChangeTrim(inputEl.value.trim());
775
1014
  }
776
1015
  };
777
1016
  const handleFocus = () => {
778
- isFocused = true;
779
- updateContainerClass();
1017
+ isFocused.value = true;
780
1018
  onFocus(inputEl.value);
781
1019
  };
782
1020
  const handleBlur = () => {
783
- isFocused = false;
784
- updateContainerClass();
1021
+ isFocused.value = false;
785
1022
  onBlur(inputEl.value);
786
1023
  };
787
- const getPlaceholder = () => (label && !isFocused && !value ? '' : placeholder);
788
- let isFocused = false;
1024
+ const handleWrapperMouseDown = (e) => {
1025
+ if (disabledRef.value) {
1026
+ return;
1027
+ }
1028
+ const target = e.target;
1029
+ if (!target || target === inputEl) {
1030
+ return;
1031
+ }
1032
+ setTimeout(() => inputEl.focus(), 0);
1033
+ };
1034
+ const getPlaceholder = () => (labelRef.value && !isFocused && !modelRef.value ? '' : placeholderRef.value);
1035
+ // # non-refs
1036
+ const inputType = deref(props.type ?? 'text');
1037
+ const multiline = props.multiline;
1038
+ // # refs
1039
+ // Create refs for all reactive properties
1040
+ const labelRef = toReactive(props.label ?? '');
1041
+ const placeholderRef = toReactive(props.placeholder ?? '', () => (inputEl.placeholder = getPlaceholder()));
1042
+ const disabledRef = toReactive(props.disabled ?? false);
1043
+ const readOnlyRef = toReactive(props.readOnly ?? false);
1044
+ const requiredRef = toReactive(props.required ?? false);
1045
+ const errorRef = toReactive(props.error ?? false);
1046
+ const helperTextRef = toReactive(props.helperText ?? '');
1047
+ const fullWidthRef = toReactive(props.fullWidth ?? false);
1048
+ const rowsRef = toReactive(props.rows ?? 3);
1049
+ const sizeRef = toReactive(props.size ?? 'medium');
1050
+ // k-model takes precedence over value prop for two-way binding
1051
+ const modelRef = $modelOrRef(props, props.value ?? '');
1052
+ // Add change listeners for reactive properties
1053
+ // `k-if` changing triggers redrawing, no need to do this again
1054
+ // // labelRef.addOnChange(() => {
1055
+ // // wrapperRef.redraw();
1056
+ // // updateContainerClass();
1057
+ // // });
1058
+ const isFocused = ref(false);
789
1059
  const inputEl = multiline
790
- ? (jsx("textarea", { class: "mui-textfield-input", placeholder: getPlaceholder(), value: value, disabled: disabled, readOnly: readonly, required: required, rows: rows, "on:input": handleInput, "on:change": handleChange, "on:focus": handleFocus, "on:blur": handleBlur }))
791
- : (jsx("input", { type: type, class: "mui-textfield-input", placeholder: getPlaceholder(), value: value, disabled: disabled, readOnly: readonly, required: required, "on:input": handleInput, "on:change": handleChange, "on:focus": handleFocus, "on:blur": handleBlur }));
792
- const helperTextEl = jsx("p", { class: "mui-textfield-helper-text", children: helperText });
793
- const wrapperRef = createRedrawable(() => (jsxs("div", { class: "mui-textfield-wrapper", children: [jsxs("label", { "k-if": label, class: "mui-textfield-label", children: [label, required && jsx("span", { class: "mui-textfield-required", children: "*" })] }), jsx("div", { class: "mui-textfield-input-wrapper", children: inputEl }), jsx("fieldset", { class: "mui-textfield-fieldset", children: jsx("legend", { "k-if": label, class: "mui-textfield-legend", children: jsxs("span", { children: [label, required && '*'] }) }) })] })));
794
- const container = (jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: parseStyle(props.style), children: [wrapperRef, helperTextEl] }));
795
- // Initialize classes
796
- setTimeout(() => updateContainerClass(), 0);
797
- $defines(container, {
798
- value: {
799
- get() {
800
- return inputEl.value;
801
- },
802
- set(newValue) {
803
- inputEl.value = newValue;
804
- updateContainerClass();
805
- },
806
- },
807
- label: {
808
- get() {
809
- return label;
810
- },
811
- set(newLabel) {
812
- label = newLabel;
813
- wrapperRef.redraw(); // label takes too much and should be redrawn
814
- updateContainerClass();
815
- },
816
- },
817
- placeholder: {
818
- get() {
819
- return placeholder;
820
- },
821
- set(newPlaceholder) {
822
- placeholder = newPlaceholder;
823
- inputEl.placeholder = getPlaceholder();
824
- },
825
- },
826
- type: {
827
- get() {
828
- return type;
829
- },
830
- set(newType) {
831
- type = newType || 'text';
832
- inputEl.type = type;
833
- },
834
- },
835
- disabled: {
836
- get() {
837
- return disabled;
838
- },
839
- set(val) {
840
- disabled = !!val;
841
- inputEl.disabled = disabled;
842
- container.classList.toggle('mui-textfield-disabled', disabled);
843
- },
844
- },
845
- readonly: {
846
- get() {
847
- return readonly;
848
- },
849
- set(val) {
850
- readonly = Boolean(val);
851
- inputEl.readOnly = readonly;
852
- },
853
- },
854
- error: {
855
- get() {
856
- return error;
857
- },
858
- set(val) {
859
- error = Boolean(val);
860
- container.classList.toggle('mui-textfield-error', error);
861
- },
862
- },
863
- helperText: {
864
- get() {
865
- return helperText;
866
- },
867
- set(text) {
868
- helperTextEl.textContent = text;
869
- helperTextEl.style.display = text ? 'block' : 'none';
870
- },
871
- },
872
- });
1060
+ ? (jsx("textarea", { class: "mui-textfield-input", placeholder: getPlaceholder(), value: modelRef.value, disabled: disabledRef, readOnly: readOnlyRef, required: requiredRef, rows: rowsRef, "on:input": handleInput, "on:change": handleChange, "on:focus": handleFocus, "on:blur": handleBlur }))
1061
+ : (jsx("input", { type: inputType, class: "mui-textfield-input", placeholder: getPlaceholder(), value: modelRef.value, disabled: disabledRef, readOnly: readOnlyRef, required: requiredRef, "on:input": handleInput, "on:change": handleChange, "on:focus": handleFocus, "on:blur": handleBlur }));
1062
+ modelRef.addOnChange((newValue) => (inputEl.value = newValue));
1063
+ const styleRef = toReactive(parseStyle(props.style ?? ''));
1064
+ const customClassRef = toReactive(props.class ?? '');
1065
+ const classRef = computed(() => {
1066
+ const className = [
1067
+ 'mui-textfield-root',
1068
+ `mui-textfield-size-${sizeRef.value}`,
1069
+ isFocusedRef.value ? 'mui-textfield-focused' : '',
1070
+ errorRef.value ? 'mui-textfield-error' : '',
1071
+ disabledRef.value ? 'mui-textfield-disabled' : '',
1072
+ fullWidthRef.value ? 'mui-textfield-fullwidth' : '',
1073
+ labelRef.value && inputEl.value ? 'mui-textfield-has-value' : '',
1074
+ labelRef.value ? '' : 'mui-textfield-no-label',
1075
+ customClassRef.value ? customClassRef.value : '',
1076
+ ].join(' ');
1077
+ return className;
1078
+ }, [sizeRef, errorRef, disabledRef, fullWidthRef, labelRef, isFocusedRef, customClassRef]);
1079
+ // if (multiline) {
1080
+ // rowsRef.addOnChange((newRows) => ((inputEl as HTMLTextAreaElement).rows = newRows));
1081
+ // }
1082
+ const container = (jsxs("div", { class: classRef, style: styleRef, children: [jsxs("div", { class: "mui-textfield-wrapper", "on:mousedown": handleWrapperMouseDown, children: [jsxs("label", { class: "mui-textfield-label", children: [labelRef, jsx("span", { "k-if": requiredRef, class: "mui-textfield-required", children: "*" })] }), jsx("div", { class: "mui-textfield-input-wrapper", children: inputEl }), jsx("fieldset", { class: "mui-textfield-fieldset", children: jsx("legend", { class: "mui-textfield-legend", children: jsxs("span", { children: [labelRef, jsx("span", { "k-if": requiredRef, children: "*" })] }) }) })] }), jsx("p", { class: "mui-textfield-helper-text", children: helperTextRef })] }));
873
1083
  return container;
874
1084
  }
875
1085
 
@@ -877,7 +1087,7 @@ var __ktjs_mui__ = (function (exports) {
877
1087
  * Radio component - mimics MUI Radio appearance and behavior
878
1088
  */
879
1089
  function Radio(props) {
880
- const onChange = generateHandler(props, 'kt:change');
1090
+ const onChange = props['on:change'] ?? $emptyFn;
881
1091
  const toggleIcon = (checked) => {
882
1092
  uncheckedIcon.style.display = checked ? 'none' : '';
883
1093
  checkedIcon.style.display = checked ? '' : 'none';
@@ -892,7 +1102,9 @@ var __ktjs_mui__ = (function (exports) {
892
1102
  onChange(checked, value);
893
1103
  };
894
1104
  let { checked = false, value = '', label: text = '', size = 'small', disabled = false, color = 'primary' } = props;
895
- const input = (jsx("input", { type: "radio", class: "mui-radio-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }));
1105
+ const valueRef = toReactive(props.value ?? '');
1106
+ const disabledRef = toReactive(props.disabled ?? false);
1107
+ const input = (jsx("input", { type: "radio", class: "mui-radio-input", checked: checked, value: valueRef, disabled: disabledRef, "on:change": handleChange }));
896
1108
  const uncheckedIcon = (jsx("span", { class: "mui-radio-icon-unchecked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" }) }) }));
897
1109
  const checkedIcon = (jsx("span", { class: "mui-radio-icon-checked", children: jsx("svg", { viewBox: "0 0 24 24", children: jsx("path", { d: "M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" }) }) }));
898
1110
  // initialize icon state
@@ -922,7 +1134,7 @@ var __ktjs_mui__ = (function (exports) {
922
1134
  */
923
1135
  function RadioGroup(props) {
924
1136
  let { value = '', size = 'small', row = false } = props;
925
- const onChange = generateHandler(props, 'kt:change');
1137
+ const onChange = props['on:change'] ?? $emptyFn;
926
1138
  const changeHandler = (checked, value) => {
927
1139
  if (checked) {
928
1140
  onChange(value);
@@ -932,15 +1144,15 @@ var __ktjs_mui__ = (function (exports) {
932
1144
  const radios = props.options.map((o) => {
933
1145
  o.size = size;
934
1146
  o.checked = value === o.value;
935
- const originalChange = o['kt:change'];
1147
+ const originalChange = o['on:change'];
936
1148
  if (originalChange) {
937
- o['kt:change'] = (checked, newValue) => {
1149
+ o['on:change'] = (checked, newValue) => {
938
1150
  originalChange(checked, newValue);
939
1151
  changeHandler(checked, newValue);
940
1152
  };
941
1153
  }
942
1154
  else {
943
- o['kt:change'] = changeHandler;
1155
+ o['on:change'] = changeHandler;
944
1156
  }
945
1157
  return Radio(o);
946
1158
  });
@@ -963,22 +1175,10 @@ var __ktjs_mui__ = (function (exports) {
963
1175
  * Select component - mimics MUI Select appearance and behavior
964
1176
  */
965
1177
  function Select(props) {
966
- let { value = '', options = [], label = '', placeholder = '', size = 'medium', fullWidth = false, disabled = false, } = props;
967
- const onChange = generateHandler(props, 'kt:change');
968
- let isOpen = false;
969
- let isFocused = false;
970
- const selectRef = ref();
971
- const selectLabelRef = ref();
972
- // Toggle dropdown
973
- const toggleMenu = () => {
974
- if (disabled) {
975
- return;
976
- }
977
- isOpen = !isOpen;
978
- updateMenu();
979
- };
980
- // Update menu visibility
981
- const updateMenu = () => {
1178
+ const onChange = props['on:change'] ?? $emptyFn;
1179
+ // # refs
1180
+ const isFocusedRef = ref(false);
1181
+ const open = ref(false, (isOpen) => {
982
1182
  if (isOpen) {
983
1183
  menu.value.style.display = 'block';
984
1184
  void menu.value.offsetHeight; // & Trigger reflow to enable animation
@@ -992,85 +1192,139 @@ var __ktjs_mui__ = (function (exports) {
992
1192
  }, 200);
993
1193
  }
994
1194
  menu.value.classList.toggle('mui-select-menu-open', isOpen);
995
- selectRef.value.classList.toggle('mui-select-open', isOpen);
1195
+ container.classList.toggle('mui-select-open', isOpen);
1196
+ });
1197
+ // # ref props
1198
+ const placeholderRef = toReactive(props.placeholder ?? '');
1199
+ const labelRef = toReactive(props.label ?? '');
1200
+ const optionsRef = toReactive(props.options, (newOptions) => {
1201
+ if (!newOptions.find((o) => o.value === modelRef.value)) {
1202
+ onChange((modelRef.value = ''));
1203
+ }
1204
+ });
1205
+ const disabledRef = toReactive(props.disabled ?? false, (v) => container.classList.toggle('mui-select-disabled', v));
1206
+ const modelRef = $modelOrRef(props, props.value ?? '');
1207
+ const styleRef = toReactive(parseStyle(props.style ?? ''));
1208
+ const classRef = toReactive(props.class ?? '');
1209
+ const sizeRef = toReactive(props.size ?? 'medium');
1210
+ const fullwidthRef = toReactive(props.fullWidth ?? false);
1211
+ const className = computed(() => {
1212
+ return `mui-select-wrapper mui-select-size-${sizeRef.value} ${fullwidthRef.value ? 'mui-select-fullWidth' : ''} ${classRef.value} ${disabledRef.value ? 'mui-select-disabled' : ''}`;
1213
+ }, [sizeRef, fullwidthRef, classRef, disabledRef]);
1214
+ const label = computed(() => {
1215
+ if (labelRef.value) {
1216
+ return (jsx("label", { class: `mui-select-label ${modelRef.value || isFocusedRef.value || placeholderRef.value ? 'focused' : ''}`, children: labelRef }));
1217
+ }
1218
+ return '';
1219
+ }, [labelRef, modelRef, isFocusedRef, placeholderRef]);
1220
+ // Toggle dropdown
1221
+ const toggleMenu = () => {
1222
+ if (!disabledRef.value) {
1223
+ open.value = !open.value;
1224
+ }
996
1225
  };
997
1226
  // Handle option click
998
- const handleOptionClick = (newValue) => {
999
- value = newValue;
1000
- isOpen = false;
1001
- onChange(value);
1002
- updateMenu();
1003
- updateLabelState();
1004
- valueDisplay.redraw();
1005
- setTimeout(() => menu.redraw(), 200);
1227
+ const handleOptionClick = (e) => {
1228
+ const newValue = e.currentTarget.dataset.value;
1229
+ modelRef.value = newValue;
1230
+ onChange(newValue);
1231
+ open.value = false;
1006
1232
  };
1007
1233
  // Close menu when clicking outside
1008
1234
  const handleClickOutside = (e) => {
1009
- if (!selectRef.value.contains(e.target)) {
1010
- isOpen = false;
1011
- updateMenu();
1235
+ if (!container.contains(e.target)) {
1236
+ open.value = false;
1012
1237
  }
1013
1238
  };
1014
1239
  // Handle focus
1015
- const handleFocus = () => {
1016
- isFocused = true;
1017
- updateLabelState();
1018
- };
1019
- const handleBlur = () => {
1020
- isFocused = false;
1021
- updateLabelState();
1022
- };
1023
- // Update label focused state
1024
- const updateLabelState = () => {
1025
- selectLabelRef.value.classList?.toggle('focused', isFocused || !!value);
1026
- };
1027
- const valueDisplay = createRedrawable(() => {
1028
- const o = options.find((opt) => opt.value === value);
1029
- let inner;
1030
- if (o === undefined) {
1031
- inner = jsx("span", { class: "mui-select-placeholder", children: placeholder || '\u00a0' });
1032
- }
1033
- else {
1034
- inner = o.label;
1035
- }
1036
- return jsx("div", { class: "mui-select-display", children: inner });
1037
- });
1038
- const menu = createRedrawable(() => {
1039
- return (jsx("div", { class: "mui-select-menu", style: "display: none;", children: options.map((option) => (jsx("div", { class: `mui-select-option ${option.value === value ? 'selected' : ''}`, "on:click": () => handleOptionClick(option.value), children: option.label }))) }));
1040
- });
1240
+ const handleFocus = () => (isFocusedRef.value = true);
1241
+ const handleBlur = () => (isFocusedRef.value = false);
1242
+ const defaultEmpty = jsx("span", { class: "mui-select-placeholder", children: placeholderRef.value || '\u00a0' });
1243
+ const displayedValue = computed(() => {
1244
+ const o = optionsRef.value.find((item) => item.value === modelRef.value);
1245
+ return jsx("div", { class: "mui-select-display", children: o?.label ?? defaultEmpty });
1246
+ }, [modelRef]);
1247
+ const menu = computed(() => {
1248
+ return (jsxs("div", { class: "mui-select-menu", style: "display: none;", children: [optionsRef.value.map((o) => (jsx("div", { class: `mui-select-option ${o.value === modelRef.value ? 'selected' : ''}`, "data-value": o.value, "on:click": handleOptionClick, children: o.label }))), ' '] }));
1249
+ }, [optionsRef, modelRef]);
1041
1250
  // Create container
1042
- const container = (jsxs("div", { ref: selectRef, class: `mui-select-wrapper mui-select-size-${size} ${props.class ?? ''} ${fullWidth ? 'mui-select-fullWidth' : ''} ${disabled ? 'mui-select-disabled' : ''}`, style: parseStyle(props.style), children: [jsx("label", { "k-if": label, ref: selectLabelRef, class: `mui-select-label ${value || isFocused ? 'focused' : ''}`, children: label }), jsxs("div", { class: "mui-select-control mui-select-outlined", "on:click": toggleMenu, "on:focus": handleFocus, "on:blur": handleBlur, tabIndex: disabled ? -1 : 0, children: [valueDisplay, jsx("input", { type: "hidden", value: value }), jsx("fieldset", { class: "mui-select-fieldset", children: jsx("legend", { children: jsx("span", { children: label }) }) }), jsx("svg", { class: "mui-select-icon", focusable: "false", "aria-hidden": "true", viewBox: "0 0 24 24", width: "24", height: "24", children: jsx("path", { d: "M7 10l5 5 5-5Z", fill: "currentColor" }) })] }), menu] }));
1043
- $defines(container, {
1044
- value: {
1045
- get: () => value,
1046
- set: (newValue) => {
1047
- value = newValue;
1048
- updateLabelState();
1049
- valueDisplay.redraw();
1050
- menu.redraw();
1051
- },
1052
- },
1053
- disabled: {
1054
- get: () => disabled,
1055
- set: (newDisabled) => {
1056
- disabled = newDisabled;
1057
- if (disabled) {
1058
- isOpen = false;
1059
- updateMenu();
1060
- }
1061
- container.classList.toggle('mui-select-disabled', disabled);
1062
- },
1063
- },
1064
- });
1251
+ const container = (jsxs("div", { class: className, style: styleRef, children: [label, jsxs("div", { class: "mui-select-control mui-select-outlined", "on:click": toggleMenu, "on:focus": handleFocus, "on:blur": handleBlur, tabIndex: disabledRef.value ? -1 : 0, children: [displayedValue, jsx("input", { type: "hidden", value: modelRef }), jsx("fieldset", { class: "mui-select-fieldset", children: jsx("legend", { class: "mui-select-legend", children: jsx("span", { children: labelRef }) }) }), jsx("svg", { class: "mui-select-icon", focusable: "false", "aria-hidden": "true", viewBox: "0 0 24 24", width: "24", height: "24", children: jsx("path", { d: "M7 10l5 5 5-5Z", fill: "currentColor" }) })] }), menu] }));
1065
1252
  // Add global click listener
1066
1253
  setTimeout(() => {
1067
1254
  document.removeEventListener('click', handleClickOutside);
1068
1255
  document.addEventListener('click', handleClickOutside);
1069
- updateLabelState();
1070
1256
  }, 0);
1071
1257
  return container;
1072
1258
  }
1073
1259
 
1260
+ /**
1261
+ * Card component - mimics MUI Card appearance and behavior
1262
+ */
1263
+ function Card(props) {
1264
+ // # ref props
1265
+ const variantRef = toReactive(props.variant ?? 'elevation');
1266
+ const elevationRef = toReactive(props.elevation ?? 1);
1267
+ const squareRef = toReactive(props.square ?? false);
1268
+ const raisedRef = toReactive(props.raised ?? false);
1269
+ const styleRef = toReactive(parseStyle(props.style ?? ''));
1270
+ const classRef = toReactive(props.class ?? '');
1271
+ const className = computed(() => {
1272
+ const base = 'mui-card';
1273
+ const variantClass = `mui-card-${variantRef.value}`;
1274
+ const elevationClass = variantRef.value === 'elevation' ? `mui-card-elevation-${Math.min(24, Math.max(0, elevationRef.value))}` : '';
1275
+ const squareClass = squareRef.value ? 'mui-card-square' : '';
1276
+ const raisedClass = raisedRef.value ? 'mui-card-raised' : '';
1277
+ return `${base} ${variantClass} ${elevationClass} ${squareClass} ${raisedClass} ${classRef.value}`.trim().replace(/\s+/g, ' ');
1278
+ }, [variantRef, elevationRef, squareRef, raisedRef, classRef]);
1279
+ // Handle click
1280
+ const handleClick = props['on:click'] ?? (() => { });
1281
+ const container = (jsx("div", { class: className, style: styleRef, "on:click": handleClick, children: props.children }));
1282
+ return container;
1283
+ }
1284
+
1285
+ /**
1286
+ * Switch component - mimics MUI Switch appearance and behavior
1287
+ */
1288
+ function Switch(props) {
1289
+ const onChange = props['on:change'] ?? $emptyFn;
1290
+ // # ref props
1291
+ const labelRef = toReactive(props.label ?? '');
1292
+ const valueRef = toReactive(props.value ?? '');
1293
+ const colorRef = toReactive(props.color ?? 'primary');
1294
+ const sizeRef = toReactive(props.size ?? 'medium');
1295
+ const disabledRef = toReactive(props.disabled ?? false, (v) => {
1296
+ inputEl.disabled = v;
1297
+ container.classList.toggle('mui-switch-disabled', v);
1298
+ });
1299
+ const modelRef = $modelOrRef(props, props.checked ?? false);
1300
+ modelRef.addOnChange((newValue) => {
1301
+ inputEl.checked = newValue;
1302
+ track.classList.toggle('mui-switch-track-checked', newValue);
1303
+ thumb.classList.toggle('mui-switch-thumb-checked', newValue);
1304
+ });
1305
+ const styleRef = toReactive(parseStyle(props.style ?? ''));
1306
+ const classRef = toReactive(props.class ?? '');
1307
+ const className = computed(() => {
1308
+ return `mui-switch-wrapper mui-switch-size-${sizeRef.value} ${disabledRef.value ? 'mui-switch-disabled' : ''} mui-switch-color-${colorRef.value} ${classRef.value}`;
1309
+ }, [colorRef, disabledRef, sizeRef, classRef]);
1310
+ // Handle change
1311
+ const handleChange = () => {
1312
+ if (disabledRef.value) {
1313
+ return;
1314
+ }
1315
+ modelRef.value = inputEl.checked;
1316
+ onChange(modelRef.value, valueRef.value);
1317
+ };
1318
+ const inputEl = (jsx("input", { type: "checkbox", class: "mui-switch-input", checked: modelRef.value, value: valueRef, disabled: disabledRef, "on:change": handleChange }));
1319
+ const track = jsx("span", { class: "mui-switch-track" });
1320
+ const thumb = jsx("span", { class: "mui-switch-thumb" });
1321
+ const container = (jsxs("label", { class: className, style: styleRef, children: [inputEl, jsxs("span", { class: "mui-switch-base", children: [track, thumb] }), jsx("span", { "k-if": labelRef, class: "mui-switch-label", children: labelRef })] }));
1322
+ // Initialize state
1323
+ track.classList.toggle('mui-switch-track-checked', modelRef.value);
1324
+ thumb.classList.toggle('mui-switch-thumb-checked', modelRef.value);
1325
+ return container;
1326
+ }
1327
+
1074
1328
  function DownloadIcon(props) {
1075
1329
  return (jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "1em", height: "1em", ...props, children: jsx("path", { d: "M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" }) }));
1076
1330
  }
@@ -1161,6 +1415,7 @@ var __ktjs_mui__ = (function (exports) {
1161
1415
 
1162
1416
  exports.Alert = Alert;
1163
1417
  exports.Button = Button;
1418
+ exports.Card = Card;
1164
1419
  exports.Checkbox = Checkbox;
1165
1420
  exports.CheckboxGroup = CheckboxGroup;
1166
1421
  exports.ColorLensIcon = ColorLensIcon;
@@ -1188,6 +1443,7 @@ var __ktjs_mui__ = (function (exports) {
1188
1443
  exports.SettingsIcon = SettingsIcon;
1189
1444
  exports.StopIcon = StopIcon;
1190
1445
  exports.SubtitlesIcon = SubtitlesIcon;
1446
+ exports.Switch = Switch;
1191
1447
  exports.TextField = TextField;
1192
1448
  exports.UploadFileIcon = UploadFileIcon;
1193
1449
  exports.VideoFileIcon = VideoFileIcon;