@ktjs/mui 0.18.0 → 0.18.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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { KTHTMLElement, KTAttribute } from 'kt.js';
2
- import { KTHTMLElement as KTHTMLElement$1 } from '@ktjs/core';
2
+ import { KTHTMLElement as KTHTMLElement$1, KTRef } from '@ktjs/core';
3
3
 
4
4
  interface AlertProps {
5
5
  class?: string;
@@ -38,20 +38,24 @@ declare function Button(props: ButtonProps): KTHTMLElement;
38
38
 
39
39
  interface CheckboxProps {
40
40
  value: string;
41
- label?: string | KTHTMLElement | HTMLElement;
41
+ label?: string | KTHTMLElement$1 | HTMLElement;
42
42
  checked?: boolean;
43
43
  size?: 'small' | 'medium';
44
- 'kt:change'?: (checked: boolean, value: string) => void;
44
+ 'kt:change'?: ((checked: boolean, value: string) => void) | KTRef<boolean>;
45
45
  disabled?: boolean;
46
46
  color?: 'primary' | 'secondary' | 'default' | 'success' | 'error' | 'warning';
47
47
  indeterminate?: boolean;
48
48
  }
49
- type KTMuiCheckbox = KTHTMLElement & {
49
+ type KTMuiCheckbox = KTHTMLElement$1 & {
50
50
  checked: boolean;
51
51
  value: string;
52
+ disabled: boolean;
52
53
  };
53
- type KTMuiCheckboxGroup = KTHTMLElement & {
54
+ type KTMuiCheckboxGroup = KTHTMLElement$1 & {
54
55
  value: string[];
56
+ disabled: boolean[];
57
+ disableAll: () => void;
58
+ enableAll: () => void;
55
59
  };
56
60
  /**
57
61
  * Checkbox component - mimics MUI Checkbox appearance and behavior
@@ -63,7 +67,7 @@ interface CheckboxGroupProps {
63
67
  value?: string[];
64
68
  size?: 'small' | 'medium';
65
69
  options: CheckboxProps[];
66
- 'kt:change'?: (values: string[]) => void;
70
+ 'kt:change'?: ((values: string[]) => void) | KTRef<string[]>;
67
71
  row?: boolean;
68
72
  }
69
73
  /**
@@ -123,6 +127,8 @@ type KTMuiLinearProgress = KTHTMLElement$1 & {
123
127
  */
124
128
  declare function LinearProgress(props: LinearProgressProps): KTMuiLinearProgress;
125
129
 
130
+ type ChangeHandler<T = string> = (value: T, ...args: any[]) => void;
131
+
126
132
  interface TextFieldProps {
127
133
  class?: string;
128
134
  style?: string;
@@ -139,14 +145,14 @@ interface TextFieldProps {
139
145
  multiline?: boolean;
140
146
  rows?: number;
141
147
  size?: 'small' | 'medium';
142
- 'kt:input'?: (value: string, event: Event) => void;
143
- 'kt-trim:input'?: (value: string, event: Event) => void;
144
- 'kt:change'?: (value: string, event: Event) => void;
145
- 'kt-trim:change'?: (value: string, event: Event) => void;
146
- 'kt:blur'?: (value: string, event: Event) => void;
147
- 'kt:focus'?: (value: string, event: Event) => void;
148
+ 'kt:input'?: ChangeHandler | KTRef<string>;
149
+ 'kt-trim:input'?: ChangeHandler | KTRef<string>;
150
+ 'kt:change'?: ChangeHandler | KTRef<string>;
151
+ 'kt-trim:change'?: ChangeHandler | KTRef<string>;
152
+ 'kt:blur'?: ChangeHandler | KTRef<string>;
153
+ 'kt:focus'?: ChangeHandler | KTRef<string>;
148
154
  }
149
- type KTMuiTextField = KTHTMLElement & {
155
+ type KTMuiTextField = KTHTMLElement$1 & {
150
156
  value: string;
151
157
  label: string;
152
158
  placeholder: string;
@@ -260,4 +266,4 @@ declare function ContentCopyIcon(props: KTAttribute): JSX.Element;
260
266
  declare function SelectAllIcon(props: KTAttribute): JSX.Element;
261
267
 
262
268
  export { Alert, Button, Checkbox, CheckboxGroup, ColorLensIcon, CompressIcon, ContentCopyIcon, ContentPasteIcon, DeleteIcon, Dialog, DownloadIcon, ExpandMoreIcon, FileOpenIcon as FileOpen, FileOpenIcon, FolderOpenIcon, FormLabel, HomeIcon, LinearProgress, MenuIcon, NewReleasesIcon, PlayArrowIcon, QueuePlayNextIcon, Radio, RadioGroup, SaveIcon, Select, SelectAllIcon, SettingsIcon, StopIcon, SubtitlesIcon, TextField, UploadFileIcon, VideoFileIcon, WallpaperIcon };
263
- export type { KTMuiDialog, KTMuiLinearProgress };
269
+ export type { KTMuiDialog, KTMuiLinearProgress, KTMuiTextField };
@@ -44,12 +44,23 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
44
44
  return alert;
45
45
  }
46
46
 
47
- const emptyFn$3 = () => { };
47
+ const emptyFn$2 = () => { };
48
+ const generateHandler = (props, key) => {
49
+ const handler = props[key];
50
+ if (typeof handler === 'function') {
51
+ return handler;
52
+ }
53
+ else if (handler && typeof handler === 'object' && handler.isKT) {
54
+ return (value) => (handler.value = value);
55
+ }
56
+ return emptyFn$2;
57
+ };
58
+
48
59
  /**
49
60
  * Button component - mimics MUI Button appearance and behavior
50
61
  */
51
62
  function Button(props) {
52
- const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$3, } = props;
63
+ const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$2, } = props;
53
64
  const classes = [
54
65
  'mui-button',
55
66
  `mui-button-${variant}`,
@@ -91,7 +102,6 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
91
102
  return (jsxRuntime.jsxs("button", { class: classes, style: props.style ? props.style : '', type: type, disabled: disabled, "on:click": handleClick, children: [startIcon && jsxRuntime.jsx("span", { class: "mui-button-start-icon", children: startIcon }), jsxRuntime.jsx("span", { class: "mui-button-label", children: children }), endIcon && jsxRuntime.jsx("span", { class: "mui-button-end-icon", children: endIcon }), jsxRuntime.jsx("span", { class: "mui-button-ripple" })] }));
92
103
  }
93
104
 
94
- const emptyFn$2 = () => { };
95
105
  /**
96
106
  * Checkbox component - mimics MUI Checkbox appearance and behavior
97
107
  */
@@ -113,13 +123,14 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
113
123
  if (disabled) {
114
124
  return;
115
125
  }
116
- checked = inputRef.value.checked;
126
+ checked = inputEl.checked;
117
127
  indeterminate = false;
118
128
  toggleIcon(checked, indeterminate);
119
129
  onChange(checked, value);
120
130
  };
121
- let { checked = false, value = '', label = '', size = 'medium', 'kt:change': onChange = emptyFn$2, disabled = false, color = 'primary', indeterminate = false, } = props;
122
- const inputRef = kt_js.ref();
131
+ let { checked = false, value = '', label = '', size = 'medium', disabled = false, color = 'primary', indeterminate = false, } = props;
132
+ const onChange = generateHandler(props, 'kt:change');
133
+ const inputEl = (jsxRuntime.jsx("input", { type: "checkbox", class: "mui-checkbox-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }));
123
134
  // Unchecked icon
124
135
  const uncheckedIcon = (jsxRuntime.jsx("span", { class: "mui-checkbox-icon-unchecked", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", children: jsxRuntime.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" }) }) }));
125
136
  // Checked icon
@@ -128,26 +139,47 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
128
139
  const indeterminateIcon = (jsxRuntime.jsx("span", { class: "mui-checkbox-icon-indeterminate", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", children: jsxRuntime.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" }) }) }));
129
140
  // Initialize icon state
130
141
  toggleIcon(checked, indeterminate);
131
- const container = (jsxRuntime.jsxs("label", { class: `mui-checkbox-wrapper mui-checkbox-size-${size} ${disabled ? 'mui-checkbox-disabled' : ''} mui-checkbox-color-${color}`, children: [jsxRuntime.jsx("input", { ref: inputRef, type: "checkbox", class: "mui-checkbox-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }), jsxRuntime.jsxs("span", { class: "mui-checkbox-icon", children: [uncheckedIcon, checkedIcon, indeterminateIcon] }), jsxRuntime.jsx("span", { "k-if": label, class: "mui-checkbox-label", children: label })] }));
132
- Object.defineProperty(container, 'checked', {
133
- get() {
134
- return checked;
142
+ const container = (jsxRuntime.jsxs("label", { class: `mui-checkbox-wrapper mui-checkbox-size-${size} ${disabled ? 'mui-checkbox-disabled' : ''} mui-checkbox-color-${color}`, children: [inputEl, jsxRuntime.jsxs("span", { class: "mui-checkbox-icon", children: [uncheckedIcon, checkedIcon, indeterminateIcon] }), jsxRuntime.jsx("span", { "k-if": label, class: "mui-checkbox-label", children: label })] }));
143
+ Object.defineProperties(container, {
144
+ checked: {
145
+ get() {
146
+ return checked;
147
+ },
148
+ set(newChecked) {
149
+ checked = newChecked;
150
+ indeterminate = false;
151
+ inputEl.checked = checked;
152
+ toggleIcon(checked, indeterminate);
153
+ },
135
154
  },
136
- set(newChecked) {
137
- checked = newChecked;
138
- indeterminate = false;
139
- inputRef.value.checked = checked;
140
- toggleIcon(checked, indeterminate);
155
+ value: {
156
+ get() {
157
+ return value;
158
+ },
159
+ set(newValue) {
160
+ value = newValue;
161
+ inputEl.value = value;
162
+ },
163
+ },
164
+ disabled: {
165
+ get() {
166
+ return disabled;
167
+ },
168
+ set(newDisabled) {
169
+ disabled = Boolean(newDisabled);
170
+ inputEl.disabled = disabled;
171
+ container.classList.toggle('mui-checkbox-disabled', disabled);
172
+ },
141
173
  },
142
174
  });
143
- container.value = value;
144
175
  return container;
145
176
  }
146
177
  /**
147
178
  * CheckboxGroup component - groups multiple checkboxes together
148
179
  */
149
180
  function CheckboxGroup(props) {
150
- let { value = [], size = 'medium', 'kt:change': onChange = emptyFn$2, row = false } = props;
181
+ let { value = [], size = 'medium', row = false } = props;
182
+ const onChange = generateHandler(props, 'kt:change');
151
183
  let selectedValues = new Set(value);
152
184
  const changeHandler = (checked, checkboxValue) => {
153
185
  if (checked) {
@@ -161,7 +193,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
161
193
  const checkboxes = props.options.map((o) => {
162
194
  o.size = size;
163
195
  o.checked = selectedValues.has(o.value);
164
- const originalChange = o['kt:change'];
196
+ const originalChange = generateHandler(o, 'kt:change');
165
197
  if (originalChange) {
166
198
  o['kt:change'] = (checked, value) => {
167
199
  originalChange(checked, value);
@@ -174,28 +206,55 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
174
206
  return Checkbox(o);
175
207
  });
176
208
  const container = (jsxRuntime.jsx("div", { class: `mui-checkbox-group ${row ? 'mui-checkbox-group-row' : ''} ${props.class ? props.class : ''}`, style: props.style ? props.style : '', role: "group", children: checkboxes }));
177
- Object.defineProperty(container, 'value', {
178
- get() {
179
- return Array.from(selectedValues);
209
+ Object.defineProperties(container, {
210
+ value: {
211
+ get() {
212
+ return Array.from(selectedValues);
213
+ },
214
+ set(newValues) {
215
+ selectedValues = new Set(newValues);
216
+ for (let i = 0; i < checkboxes.length; i++) {
217
+ const checkbox = checkboxes[i];
218
+ checkbox.checked = selectedValues.has(checkbox.value);
219
+ }
220
+ },
180
221
  },
181
- set(newValues) {
182
- selectedValues = new Set(newValues);
183
- for (let i = 0; i < checkboxes.length; i++) {
184
- const checkbox = checkboxes[i];
185
- checkbox.checked = selectedValues.has(checkbox.value);
186
- }
222
+ disabled: {
223
+ get() {
224
+ return checkboxes.map((cb) => cb.disabled);
225
+ },
226
+ set(newDisabled) {
227
+ for (let i = 0; i < checkboxes.length; i++) {
228
+ const checkbox = checkboxes[i];
229
+ checkbox.disabled = Boolean(newDisabled);
230
+ }
231
+ },
232
+ },
233
+ disableAll: {
234
+ value: () => {
235
+ for (let i = 0; i < checkboxes.length; i++) {
236
+ checkboxes[i].disabled = true;
237
+ }
238
+ },
239
+ },
240
+ enableAll: {
241
+ value: () => {
242
+ for (let i = 0; i < checkboxes.length; i++) {
243
+ checkboxes[i].disabled = false;
244
+ }
245
+ },
187
246
  },
188
247
  });
189
248
  return container;
190
249
  }
191
250
 
192
- const noop$1 = () => { };
251
+ const noop = () => { };
193
252
  /**
194
253
  * Dialog component - mimics MUI Dialog appearance and behavior
195
254
  * Only handles open/close state, title and content are passed as props
196
255
  */
197
256
  function Dialog(props) {
198
- let { open = false, 'kt:close': onClose = noop$1, title, children, actions, maxWidth = 'sm', fullWidth = false, } = props;
257
+ let { open = false, 'kt:close': onClose = noop, title, children, actions, maxWidth = 'sm', fullWidth = false, } = props;
199
258
  // Handle ESC key - store handler for cleanup
200
259
  const keyDownHandler = (e) => {
201
260
  if (e.key === 'Escape') {
@@ -317,6 +376,31 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
317
376
  },
318
377
  };
319
378
  }
379
+ /**
380
+ * A helper to create redrawable elements
381
+ * ```tsx
382
+ * export function MyComponent() {
383
+ * let aa = 10;
384
+ * // ...
385
+ * // aa might be changed
386
+ * return createRedrawable(() => <div>{aa}</div>);
387
+ * }
388
+ * ```
389
+ * Then the returned element has a `redraw` method to redraw itself with new values.
390
+ * @param creator a simple creator function that returns an element
391
+ * @returns created element's ref
392
+ */
393
+ function createRedrawable(creator) {
394
+ const elRef = ref();
395
+ elRef.value = creator();
396
+ const redraw = () => {
397
+ elRef.value = creator(); // ref setter automatically calls replaceWith
398
+ elRef.value.redraw = redraw;
399
+ return elRef.value;
400
+ };
401
+ elRef.value.redraw = redraw;
402
+ return elRef;
403
+ }
320
404
 
321
405
  /**
322
406
  * LinearProgress component - mimics MUI LinearProgress appearance and behavior
@@ -359,15 +443,17 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
359
443
  return container;
360
444
  }
361
445
 
362
- const noop = () => { };
363
446
  /**
364
447
  * TextField component - mimics MUI TextField appearance and behavior
365
448
  */
366
449
  function TextField(props) {
367
- let { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, size = 'medium', 'kt:input': onInput = noop, 'kt-trim:input': onInputTrim = noop, 'kt:change': onChange = noop, 'kt-trim:change': onChangeTrim = noop, 'kt:blur': onBlur = noop, 'kt:focus': onFocus = noop, } = props;
368
- let isFocused = false;
369
- const helperTextEl = jsxRuntime.jsx("p", { class: "mui-textfield-helper-text", children: helperText });
370
- // Update container classes
450
+ let { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, size = 'medium', } = props;
451
+ const onInput = generateHandler(props, 'kt:input');
452
+ const onInputTrim = generateHandler(props, 'kt-trim:input');
453
+ const onChange = generateHandler(props, 'kt:change');
454
+ const onChangeTrim = generateHandler(props, 'kt-trim:change');
455
+ const onBlur = generateHandler(props, 'kt:blur');
456
+ const onFocus = generateHandler(props, 'kt:focus');
371
457
  const updateContainerClass = () => {
372
458
  container.className = [
373
459
  'mui-textfield-root',
@@ -380,36 +466,32 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
380
466
  label ? '' : 'mui-textfield-no-label',
381
467
  ].join(' ');
382
468
  };
383
- const handleInput = (e) => {
384
- const target = e.target;
469
+ const handleInput = () => {
385
470
  updateContainerClass();
386
- onInput(target.value, e);
387
- onInputTrim(target.value.trim(), e);
471
+ onInput(inputEl.value);
472
+ onInputTrim(inputEl.value.trim());
388
473
  };
389
- const handleChange = (e) => {
390
- const target = e.target;
391
- onChange(target.value, e);
392
- onChangeTrim(target.value.trim(), e);
474
+ const handleChange = () => {
475
+ onChange(inputEl.value);
476
+ onChangeTrim(inputEl.value.trim());
393
477
  };
394
- const handleFocus = (e) => {
478
+ const handleFocus = () => {
395
479
  isFocused = true;
396
480
  updateContainerClass();
397
- const target = e.target;
398
- onFocus(target.value, e);
481
+ onFocus(inputEl.value);
399
482
  };
400
- const handleBlur = (e) => {
483
+ const handleBlur = () => {
401
484
  isFocused = false;
402
485
  updateContainerClass();
403
- const target = e.target;
404
- onBlur(target.value, e);
486
+ onBlur(inputEl.value);
405
487
  };
406
- // Create input or textarea element
407
- // Only show placeholder when label is floating (focused or has value)
408
488
  const getPlaceholder = () => (label && !isFocused && !value ? '' : placeholder);
489
+ let isFocused = false;
409
490
  const inputEl = multiline
410
491
  ? (jsxRuntime.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 }))
411
492
  : (jsxRuntime.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 }));
412
- const wrapperRef = kt_js.createRedrawable(() => (jsxRuntime.jsxs("div", { class: "mui-textfield-wrapper", children: [jsxRuntime.jsxs("label", { "k-if": label, class: "mui-textfield-label", children: [label, required && jsxRuntime.jsx("span", { class: "mui-textfield-required", children: "*" })] }), jsxRuntime.jsx("div", { class: "mui-textfield-input-wrapper", children: inputEl }), jsxRuntime.jsx("fieldset", { class: "mui-textfield-fieldset", children: jsxRuntime.jsx("legend", { "k-if": label, class: "mui-textfield-legend", children: jsxRuntime.jsxs("span", { children: [label, required && '*'] }) }) })] })));
493
+ const helperTextEl = jsxRuntime.jsx("p", { class: "mui-textfield-helper-text", children: helperText });
494
+ const wrapperRef = createRedrawable(() => (jsxRuntime.jsxs("div", { class: "mui-textfield-wrapper", children: [jsxRuntime.jsxs("label", { "k-if": label, class: "mui-textfield-label", children: [label, required && jsxRuntime.jsx("span", { class: "mui-textfield-required", children: "*" })] }), jsxRuntime.jsx("div", { class: "mui-textfield-input-wrapper", children: inputEl }), jsxRuntime.jsx("fieldset", { class: "mui-textfield-fieldset", children: jsxRuntime.jsx("legend", { "k-if": label, class: "mui-textfield-legend", children: jsxRuntime.jsxs("span", { children: [label, required && '*'] }) }) })] })));
413
495
  const container = (jsxRuntime.jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [wrapperRef, helperTextEl] }));
414
496
  // Initialize classes
415
497
  setTimeout(() => updateContainerClass(), 0);
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from 'kt.js/jsx-runtime';
2
- import { ref as ref$1, createRedrawable } from 'kt.js';
2
+ import { ref as ref$1, createRedrawable as createRedrawable$1 } from 'kt.js';
3
3
 
4
4
  /**s
5
5
  * Alert component - mimics MUI Alert appearance and behavior
@@ -44,12 +44,23 @@ function Alert(props) {
44
44
  return alert;
45
45
  }
46
46
 
47
- const emptyFn$3 = () => { };
47
+ const emptyFn$2 = () => { };
48
+ const generateHandler = (props, key) => {
49
+ const handler = props[key];
50
+ if (typeof handler === 'function') {
51
+ return handler;
52
+ }
53
+ else if (handler && typeof handler === 'object' && handler.isKT) {
54
+ return (value) => (handler.value = value);
55
+ }
56
+ return emptyFn$2;
57
+ };
58
+
48
59
  /**
49
60
  * Button component - mimics MUI Button appearance and behavior
50
61
  */
51
62
  function Button(props) {
52
- const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$3, } = props;
63
+ const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$2, } = props;
53
64
  const classes = [
54
65
  'mui-button',
55
66
  `mui-button-${variant}`,
@@ -91,7 +102,6 @@ function Button(props) {
91
102
  return (jsxs("button", { class: classes, style: props.style ? 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 }), jsx("span", { class: "mui-button-ripple" })] }));
92
103
  }
93
104
 
94
- const emptyFn$2 = () => { };
95
105
  /**
96
106
  * Checkbox component - mimics MUI Checkbox appearance and behavior
97
107
  */
@@ -113,13 +123,14 @@ function Checkbox(props) {
113
123
  if (disabled) {
114
124
  return;
115
125
  }
116
- checked = inputRef.value.checked;
126
+ checked = inputEl.checked;
117
127
  indeterminate = false;
118
128
  toggleIcon(checked, indeterminate);
119
129
  onChange(checked, value);
120
130
  };
121
- let { checked = false, value = '', label = '', size = 'medium', 'kt:change': onChange = emptyFn$2, disabled = false, color = 'primary', indeterminate = false, } = props;
122
- const inputRef = ref$1();
131
+ let { checked = false, value = '', label = '', size = 'medium', disabled = false, color = 'primary', indeterminate = false, } = props;
132
+ const onChange = generateHandler(props, 'kt:change');
133
+ const inputEl = (jsx("input", { type: "checkbox", class: "mui-checkbox-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }));
123
134
  // Unchecked icon
124
135
  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" }) }) }));
125
136
  // Checked icon
@@ -128,26 +139,47 @@ function Checkbox(props) {
128
139
  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" }) }) }));
129
140
  // Initialize icon state
130
141
  toggleIcon(checked, indeterminate);
131
- const container = (jsxs("label", { class: `mui-checkbox-wrapper mui-checkbox-size-${size} ${disabled ? 'mui-checkbox-disabled' : ''} mui-checkbox-color-${color}`, children: [jsx("input", { ref: inputRef, type: "checkbox", class: "mui-checkbox-input", checked: checked, value: value, disabled: disabled, "on:change": handleChange }), jsxs("span", { class: "mui-checkbox-icon", children: [uncheckedIcon, checkedIcon, indeterminateIcon] }), jsx("span", { "k-if": label, class: "mui-checkbox-label", children: label })] }));
132
- Object.defineProperty(container, 'checked', {
133
- get() {
134
- return checked;
142
+ 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 })] }));
143
+ Object.defineProperties(container, {
144
+ checked: {
145
+ get() {
146
+ return checked;
147
+ },
148
+ set(newChecked) {
149
+ checked = newChecked;
150
+ indeterminate = false;
151
+ inputEl.checked = checked;
152
+ toggleIcon(checked, indeterminate);
153
+ },
135
154
  },
136
- set(newChecked) {
137
- checked = newChecked;
138
- indeterminate = false;
139
- inputRef.value.checked = checked;
140
- toggleIcon(checked, indeterminate);
155
+ value: {
156
+ get() {
157
+ return value;
158
+ },
159
+ set(newValue) {
160
+ value = newValue;
161
+ inputEl.value = value;
162
+ },
163
+ },
164
+ disabled: {
165
+ get() {
166
+ return disabled;
167
+ },
168
+ set(newDisabled) {
169
+ disabled = Boolean(newDisabled);
170
+ inputEl.disabled = disabled;
171
+ container.classList.toggle('mui-checkbox-disabled', disabled);
172
+ },
141
173
  },
142
174
  });
143
- container.value = value;
144
175
  return container;
145
176
  }
146
177
  /**
147
178
  * CheckboxGroup component - groups multiple checkboxes together
148
179
  */
149
180
  function CheckboxGroup(props) {
150
- let { value = [], size = 'medium', 'kt:change': onChange = emptyFn$2, row = false } = props;
181
+ let { value = [], size = 'medium', row = false } = props;
182
+ const onChange = generateHandler(props, 'kt:change');
151
183
  let selectedValues = new Set(value);
152
184
  const changeHandler = (checked, checkboxValue) => {
153
185
  if (checked) {
@@ -161,7 +193,7 @@ function CheckboxGroup(props) {
161
193
  const checkboxes = props.options.map((o) => {
162
194
  o.size = size;
163
195
  o.checked = selectedValues.has(o.value);
164
- const originalChange = o['kt:change'];
196
+ const originalChange = generateHandler(o, 'kt:change');
165
197
  if (originalChange) {
166
198
  o['kt:change'] = (checked, value) => {
167
199
  originalChange(checked, value);
@@ -174,28 +206,55 @@ function CheckboxGroup(props) {
174
206
  return Checkbox(o);
175
207
  });
176
208
  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 }));
177
- Object.defineProperty(container, 'value', {
178
- get() {
179
- return Array.from(selectedValues);
209
+ Object.defineProperties(container, {
210
+ value: {
211
+ get() {
212
+ return Array.from(selectedValues);
213
+ },
214
+ set(newValues) {
215
+ selectedValues = new Set(newValues);
216
+ for (let i = 0; i < checkboxes.length; i++) {
217
+ const checkbox = checkboxes[i];
218
+ checkbox.checked = selectedValues.has(checkbox.value);
219
+ }
220
+ },
180
221
  },
181
- set(newValues) {
182
- selectedValues = new Set(newValues);
183
- for (let i = 0; i < checkboxes.length; i++) {
184
- const checkbox = checkboxes[i];
185
- checkbox.checked = selectedValues.has(checkbox.value);
186
- }
222
+ disabled: {
223
+ get() {
224
+ return checkboxes.map((cb) => cb.disabled);
225
+ },
226
+ set(newDisabled) {
227
+ for (let i = 0; i < checkboxes.length; i++) {
228
+ const checkbox = checkboxes[i];
229
+ checkbox.disabled = Boolean(newDisabled);
230
+ }
231
+ },
232
+ },
233
+ disableAll: {
234
+ value: () => {
235
+ for (let i = 0; i < checkboxes.length; i++) {
236
+ checkboxes[i].disabled = true;
237
+ }
238
+ },
239
+ },
240
+ enableAll: {
241
+ value: () => {
242
+ for (let i = 0; i < checkboxes.length; i++) {
243
+ checkboxes[i].disabled = false;
244
+ }
245
+ },
187
246
  },
188
247
  });
189
248
  return container;
190
249
  }
191
250
 
192
- const noop$1 = () => { };
251
+ const noop = () => { };
193
252
  /**
194
253
  * Dialog component - mimics MUI Dialog appearance and behavior
195
254
  * Only handles open/close state, title and content are passed as props
196
255
  */
197
256
  function Dialog(props) {
198
- let { open = false, 'kt:close': onClose = noop$1, title, children, actions, maxWidth = 'sm', fullWidth = false, } = props;
257
+ let { open = false, 'kt:close': onClose = noop, title, children, actions, maxWidth = 'sm', fullWidth = false, } = props;
199
258
  // Handle ESC key - store handler for cleanup
200
259
  const keyDownHandler = (e) => {
201
260
  if (e.key === 'Escape') {
@@ -317,6 +376,31 @@ function ref(value, onChange) {
317
376
  },
318
377
  };
319
378
  }
379
+ /**
380
+ * A helper to create redrawable elements
381
+ * ```tsx
382
+ * export function MyComponent() {
383
+ * let aa = 10;
384
+ * // ...
385
+ * // aa might be changed
386
+ * return createRedrawable(() => <div>{aa}</div>);
387
+ * }
388
+ * ```
389
+ * Then the returned element has a `redraw` method to redraw itself with new values.
390
+ * @param creator a simple creator function that returns an element
391
+ * @returns created element's ref
392
+ */
393
+ function createRedrawable(creator) {
394
+ const elRef = ref();
395
+ elRef.value = creator();
396
+ const redraw = () => {
397
+ elRef.value = creator(); // ref setter automatically calls replaceWith
398
+ elRef.value.redraw = redraw;
399
+ return elRef.value;
400
+ };
401
+ elRef.value.redraw = redraw;
402
+ return elRef;
403
+ }
320
404
 
321
405
  /**
322
406
  * LinearProgress component - mimics MUI LinearProgress appearance and behavior
@@ -359,15 +443,17 @@ function LinearProgress(props) {
359
443
  return container;
360
444
  }
361
445
 
362
- const noop = () => { };
363
446
  /**
364
447
  * TextField component - mimics MUI TextField appearance and behavior
365
448
  */
366
449
  function TextField(props) {
367
- let { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, size = 'medium', 'kt:input': onInput = noop, 'kt-trim:input': onInputTrim = noop, 'kt:change': onChange = noop, 'kt-trim:change': onChangeTrim = noop, 'kt:blur': onBlur = noop, 'kt:focus': onFocus = noop, } = props;
368
- let isFocused = false;
369
- const helperTextEl = jsx("p", { class: "mui-textfield-helper-text", children: helperText });
370
- // Update container classes
450
+ let { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, size = 'medium', } = props;
451
+ const onInput = generateHandler(props, 'kt:input');
452
+ const onInputTrim = generateHandler(props, 'kt-trim:input');
453
+ const onChange = generateHandler(props, 'kt:change');
454
+ const onChangeTrim = generateHandler(props, 'kt-trim:change');
455
+ const onBlur = generateHandler(props, 'kt:blur');
456
+ const onFocus = generateHandler(props, 'kt:focus');
371
457
  const updateContainerClass = () => {
372
458
  container.className = [
373
459
  'mui-textfield-root',
@@ -380,35 +466,31 @@ function TextField(props) {
380
466
  label ? '' : 'mui-textfield-no-label',
381
467
  ].join(' ');
382
468
  };
383
- const handleInput = (e) => {
384
- const target = e.target;
469
+ const handleInput = () => {
385
470
  updateContainerClass();
386
- onInput(target.value, e);
387
- onInputTrim(target.value.trim(), e);
471
+ onInput(inputEl.value);
472
+ onInputTrim(inputEl.value.trim());
388
473
  };
389
- const handleChange = (e) => {
390
- const target = e.target;
391
- onChange(target.value, e);
392
- onChangeTrim(target.value.trim(), e);
474
+ const handleChange = () => {
475
+ onChange(inputEl.value);
476
+ onChangeTrim(inputEl.value.trim());
393
477
  };
394
- const handleFocus = (e) => {
478
+ const handleFocus = () => {
395
479
  isFocused = true;
396
480
  updateContainerClass();
397
- const target = e.target;
398
- onFocus(target.value, e);
481
+ onFocus(inputEl.value);
399
482
  };
400
- const handleBlur = (e) => {
483
+ const handleBlur = () => {
401
484
  isFocused = false;
402
485
  updateContainerClass();
403
- const target = e.target;
404
- onBlur(target.value, e);
486
+ onBlur(inputEl.value);
405
487
  };
406
- // Create input or textarea element
407
- // Only show placeholder when label is floating (focused or has value)
408
488
  const getPlaceholder = () => (label && !isFocused && !value ? '' : placeholder);
489
+ let isFocused = false;
409
490
  const inputEl = multiline
410
491
  ? (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 }))
411
492
  : (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 }));
493
+ const helperTextEl = jsx("p", { class: "mui-textfield-helper-text", children: helperText });
412
494
  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 && '*'] }) }) })] })));
413
495
  const container = (jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [wrapperRef, helperTextEl] }));
414
496
  // Initialize classes
@@ -632,7 +714,7 @@ function Select(props) {
632
714
  }
633
715
  }
634
716
  };
635
- const valueDisplay = createRedrawable(() => {
717
+ const valueDisplay = createRedrawable$1(() => {
636
718
  const o = options.find((opt) => opt.value === value);
637
719
  let inner;
638
720
  if (o === undefined) {
@@ -643,7 +725,7 @@ function Select(props) {
643
725
  }
644
726
  return jsx("div", { class: "mui-select-display", children: inner });
645
727
  });
646
- const menu = createRedrawable(() => {
728
+ const menu = createRedrawable$1(() => {
647
729
  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 }))) }));
648
730
  });
649
731
  // Create container
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/mui",
3
- "version": "0.18.0",
3
+ "version": "0.18.2",
4
4
  "description": "Material-UI inspired components for kt.js - pre-styled UI components",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",
@@ -35,7 +35,7 @@
35
35
  "directory": "packages/mui"
36
36
  },
37
37
  "dependencies": {
38
- "@ktjs/core": "0.18.4"
38
+ "@ktjs/core": "0.18.6"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rollup -c rollup.config.mjs",