@ktjs/mui 0.17.9 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;
@@ -9,7 +9,7 @@ interface AlertProps {
9
9
  severity?: 'error' | 'warning' | 'info' | 'success';
10
10
  variant?: 'standard' | 'filled' | 'outlined';
11
11
  icon?: HTMLElement | KTHTMLElement | false;
12
- 'mui:close'?: () => void;
12
+ 'kt:close'?: () => void;
13
13
  }
14
14
  /**s
15
15
  * Alert component - mimics MUI Alert appearance and behavior
@@ -41,7 +41,7 @@ interface CheckboxProps {
41
41
  label?: string | KTHTMLElement | HTMLElement;
42
42
  checked?: boolean;
43
43
  size?: 'small' | 'medium';
44
- 'mui:change'?: (checked: boolean, value: string) => void;
44
+ 'kt:change'?: (checked: boolean, value: string) => void;
45
45
  disabled?: boolean;
46
46
  color?: 'primary' | 'secondary' | 'default' | 'success' | 'error' | 'warning';
47
47
  indeterminate?: boolean;
@@ -49,9 +49,13 @@ interface CheckboxProps {
49
49
  type KTMuiCheckbox = KTHTMLElement & {
50
50
  checked: boolean;
51
51
  value: string;
52
+ disabled: boolean;
52
53
  };
53
54
  type KTMuiCheckboxGroup = KTHTMLElement & {
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
- 'mui:change'?: (values: string[]) => void;
70
+ 'kt:change'?: (values: string[]) => void;
67
71
  row?: boolean;
68
72
  }
69
73
  /**
@@ -73,7 +77,7 @@ declare function CheckboxGroup(props: CheckboxGroupProps): KTMuiCheckboxGroup;
73
77
 
74
78
  interface DialogProps {
75
79
  open?: boolean;
76
- 'mui:close'?: () => void;
80
+ 'kt:close'?: () => void;
77
81
  title?: string;
78
82
  children?: HTMLElement | HTMLElement[] | string;
79
83
  actions?: HTMLElement | HTMLElement[];
@@ -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) => void;
131
+
126
132
  interface TextFieldProps {
127
133
  class?: string;
128
134
  style?: string;
@@ -138,15 +144,24 @@ interface TextFieldProps {
138
144
  fullWidth?: boolean;
139
145
  multiline?: boolean;
140
146
  rows?: number;
141
- maxRows?: number;
142
147
  size?: 'small' | 'medium';
143
- 'mui:input'?: (value: string, event: Event) => void;
144
- 'mui:change'?: (value: string, event: Event) => void;
145
- 'mui:blur'?: (value: string, event: Event) => void;
146
- 'mui: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>;
147
154
  }
148
- type KTMuiTextField = KTHTMLElement & {
155
+ type KTMuiTextField = KTHTMLElement$1 & {
149
156
  value: string;
157
+ label: string;
158
+ placeholder: string;
159
+ type: string;
160
+ disabled: boolean;
161
+ readonly: boolean;
162
+ required: boolean;
163
+ error: boolean;
164
+ helperText: string;
150
165
  };
151
166
  /**
152
167
  * TextField component - mimics MUI TextField appearance and behavior
@@ -158,7 +173,7 @@ interface RadioProps {
158
173
  text: string | KTHTMLElement | HTMLElement;
159
174
  checked?: boolean;
160
175
  size?: 'small' | 'medium';
161
- 'mui:change'?: (checked: boolean, value: string) => void;
176
+ 'kt:change'?: (checked: boolean, value: string) => void;
162
177
  disabled?: boolean;
163
178
  color?: 'primary' | 'secondary' | 'default';
164
179
  }
@@ -176,8 +191,8 @@ interface RadioGroupProps {
176
191
  name?: string;
177
192
  size?: 'small' | 'medium';
178
193
  options: RadioProps[];
179
- 'mui:change'?: (value: string) => void;
180
- 'mui:click'?: (checked: boolean) => void;
194
+ 'kt:change'?: (value: string) => void;
195
+ 'kt:click'?: (checked: boolean) => void;
181
196
  row?: boolean;
182
197
  }
183
198
  /**
@@ -197,7 +212,7 @@ interface SelectProps {
197
212
  options: SelectOption[];
198
213
  label?: string;
199
214
  placeholder?: string;
200
- 'mui:change'?: (value: string) => void;
215
+ 'kt:change'?: (value: string) => void;
201
216
  fullWidth?: boolean;
202
217
  disabled?: boolean;
203
218
  }
@@ -251,4 +266,4 @@ declare function ContentCopyIcon(props: KTAttribute): JSX.Element;
251
266
  declare function SelectAllIcon(props: KTAttribute): JSX.Element;
252
267
 
253
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 };
254
- export type { KTMuiDialog, KTMuiLinearProgress };
269
+ export type { KTMuiDialog, KTMuiLinearProgress, KTMuiTextField };
@@ -5,7 +5,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
5
5
  * Alert component - mimics MUI Alert appearance and behavior
6
6
  */
7
7
  function Alert(props) {
8
- const { children, severity = 'info', variant = 'standard', icon, 'mui:close': onClose, sx } = props;
8
+ const { children, severity = 'info', variant = 'standard', icon, 'kt:close': onClose, sx } = props;
9
9
  const classes = `mui-alert mui-alert-${severity} mui-alert-${variant} ${props.class ? props.class : ''}`;
10
10
  // Convert sx object to style string
11
11
  let styleString = props.style || '';
@@ -44,12 +44,23 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
44
44
  return alert;
45
45
  }
46
46
 
47
- const emptyFn$4 = () => { };
47
+ const emptyFn$3 = () => { };
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$3;
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$4, } = props;
63
+ const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$3, } = props;
53
64
  const classes = [
54
65
  'mui-button',
55
66
  `mui-button-${variant}`,
@@ -91,7 +102,7 @@ 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$3 = () => { };
105
+ const emptyFn$2 = () => { };
95
106
  /**
96
107
  * Checkbox component - mimics MUI Checkbox appearance and behavior
97
108
  */
@@ -113,13 +124,13 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
113
124
  if (disabled) {
114
125
  return;
115
126
  }
116
- checked = inputRef.value.checked;
127
+ checked = inputEl.checked;
117
128
  indeterminate = false;
118
129
  toggleIcon(checked, indeterminate);
119
130
  onChange(checked, value);
120
131
  };
121
- let { checked = false, value = '', label = '', size = 'medium', 'mui:change': onChange = emptyFn$3, disabled = false, color = 'primary', indeterminate = false, } = props;
122
- const inputRef = kt_js.ref();
132
+ let { checked = false, value = '', label = '', size = 'medium', 'kt:change': onChange = emptyFn$2, disabled = false, color = 'primary', indeterminate = false, } = props;
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', 'mui:change': onChange = emptyFn$3, 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,29 +193,56 @@ 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['mui:change'];
196
+ const originalChange = o['kt:change'];
165
197
  if (originalChange) {
166
- o['mui:change'] = (checked, value) => {
198
+ o['kt:change'] = (checked, value) => {
167
199
  originalChange(checked, value);
168
200
  changeHandler(checked, value);
169
201
  };
170
202
  }
171
203
  else {
172
- o['mui:change'] = changeHandler;
204
+ o['kt:change'] = changeHandler;
173
205
  }
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;
@@ -195,7 +254,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
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, 'mui:close': onClose = noop, 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') {
@@ -280,8 +339,67 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
280
339
  * - can alse be used to store normal values, but it is not reactive.
281
340
  * @param value mostly an HTMLElement
282
341
  */
283
- function ref(value) {
284
- return { value: value, isKT: true };
342
+ function ref(value, onChange) {
343
+ let _value = value;
344
+ let _onChanges = [];
345
+ return {
346
+ isKT: true,
347
+ get value() {
348
+ return _value;
349
+ },
350
+ set value(newValue) {
351
+ if (newValue === _value) {
352
+ return;
353
+ }
354
+ // replace the old node with the new one in the DOM if both are nodes
355
+ if (_value instanceof Node && newValue instanceof Node) {
356
+ if (newValue.contains(_value)) {
357
+ _value.remove();
358
+ }
359
+ _value.replaceWith(newValue);
360
+ }
361
+ const oldValue = _value;
362
+ _value = newValue;
363
+ for (let i = 0; i < _onChanges.length; i++) {
364
+ _onChanges[i](newValue, oldValue);
365
+ }
366
+ },
367
+ addOnChange: (callback) => _onChanges.push(callback),
368
+ removeOnChange: (callback) => {
369
+ for (let i = _onChanges.length - 1; i >= 0; i--) {
370
+ if (_onChanges[i] === callback) {
371
+ _onChanges.splice(i, 1);
372
+ return true;
373
+ }
374
+ }
375
+ return false;
376
+ },
377
+ };
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;
285
403
  }
286
404
 
287
405
  /**
@@ -325,64 +443,132 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
325
443
  return container;
326
444
  }
327
445
 
328
- const emptyFn$2 = () => { };
329
446
  /**
330
447
  * TextField component - mimics MUI TextField appearance and behavior
331
448
  */
332
449
  function TextField(props) {
333
- const { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, maxRows = 10, size = 'medium', 'mui:input': onInput = emptyFn$2, 'mui:change': onChange = emptyFn$2, 'mui:blur': onBlur = emptyFn$2, 'mui:focus': onFocus = emptyFn$2, } = props;
334
- let isFocused = false;
335
- const inputRef = kt_js.ref();
336
- // Update container classes
337
- const updateClasses = () => {
338
- const hasValue = inputRef.value?.value || '';
339
- const 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');
457
+ const updateContainerClass = () => {
458
+ container.className = [
340
459
  'mui-textfield-root',
341
460
  `mui-textfield-size-${size}`,
342
461
  isFocused ? 'mui-textfield-focused' : '',
343
462
  error ? 'mui-textfield-error' : '',
344
463
  disabled ? 'mui-textfield-disabled' : '',
345
464
  fullWidth ? 'mui-textfield-fullwidth' : '',
346
- label && (isFocused || hasValue) ? 'mui-textfield-has-value' : '',
465
+ label && isFocused && inputEl.value ? 'mui-textfield-has-value' : '',
347
466
  label ? '' : 'mui-textfield-no-label',
348
- ];
349
- container.className = classes.join(' ');
467
+ ].join(' ');
350
468
  };
351
- const handleInput = (e) => {
352
- const target = e.target;
353
- updateClasses();
354
- onInput(target.value, e);
469
+ const handleInput = () => {
470
+ updateContainerClass();
471
+ onInput(inputEl.value);
472
+ onInputTrim(inputEl.value.trim());
355
473
  };
356
- const handleChange = (e) => {
357
- const target = e.target;
358
- onChange(target.value, e);
474
+ const handleChange = () => {
475
+ onChange(inputEl.value);
476
+ onChangeTrim(inputEl.value.trim());
359
477
  };
360
- const handleFocus = (e) => {
478
+ const handleFocus = () => {
361
479
  isFocused = true;
362
- updateClasses();
363
- const target = e.target;
364
- onFocus(target.value, e);
480
+ updateContainerClass();
481
+ onFocus(inputEl.value);
365
482
  };
366
- const handleBlur = (e) => {
483
+ const handleBlur = () => {
367
484
  isFocused = false;
368
- updateClasses();
369
- const target = e.target;
370
- onBlur(target.value, e);
485
+ updateContainerClass();
486
+ onBlur(inputEl.value);
371
487
  };
372
- // Create input or textarea element
373
- // Only show placeholder when label is floating (focused or has value)
374
488
  const getPlaceholder = () => (label && !isFocused && !value ? '' : placeholder);
375
- const inputElement = multiline ? (jsxRuntime.jsx("textarea", { ref: inputRef, 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 })) : (jsxRuntime.jsx("input", { ref: inputRef, 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 }));
376
- const container = (jsxRuntime.jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [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: inputElement }), 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 && '*'] }) }) })] }), helperText && jsxRuntime.jsx("p", { class: "mui-textfield-helper-text", children: helperText })] }));
489
+ let isFocused = false;
490
+ const inputEl = multiline
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 }))
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 }));
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 && '*'] }) }) })] })));
495
+ const container = (jsxRuntime.jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [wrapperRef, helperTextEl] }));
377
496
  // Initialize classes
378
- setTimeout(() => updateClasses(), 0);
379
- Object.defineProperty(container, 'value', {
380
- get() {
381
- return inputRef.value.value;
497
+ setTimeout(() => updateContainerClass(), 0);
498
+ Object.defineProperties(container, {
499
+ value: {
500
+ get() {
501
+ return inputEl.value;
502
+ },
503
+ set(newValue) {
504
+ inputEl.value = newValue;
505
+ updateContainerClass();
506
+ },
382
507
  },
383
- set(newValue) {
384
- inputRef.value.value = newValue;
385
- updateClasses();
508
+ label: {
509
+ get() {
510
+ return label;
511
+ },
512
+ set(newLabel) {
513
+ label = newLabel;
514
+ wrapperRef.value.redraw(); // label takes too much and should be redrawn
515
+ updateContainerClass();
516
+ },
517
+ },
518
+ placeholder: {
519
+ get() {
520
+ return placeholder;
521
+ },
522
+ set(newPlaceholder) {
523
+ placeholder = newPlaceholder;
524
+ inputEl.placeholder = getPlaceholder();
525
+ },
526
+ },
527
+ type: {
528
+ get() {
529
+ return type;
530
+ },
531
+ set(newType) {
532
+ type = newType || 'text';
533
+ inputEl.type = type;
534
+ },
535
+ },
536
+ disabled: {
537
+ get() {
538
+ return disabled;
539
+ },
540
+ set(val) {
541
+ disabled = !!val;
542
+ inputEl.disabled = disabled;
543
+ container.classList.toggle('mui-textfield-disabled', disabled);
544
+ },
545
+ },
546
+ readonly: {
547
+ get() {
548
+ return readonly;
549
+ },
550
+ set(val) {
551
+ readonly = Boolean(val);
552
+ inputEl.readOnly = readonly;
553
+ },
554
+ },
555
+ error: {
556
+ get() {
557
+ return error;
558
+ },
559
+ set(val) {
560
+ error = Boolean(val);
561
+ container.classList.toggle('mui-textfield-error', error);
562
+ },
563
+ },
564
+ helperText: {
565
+ get() {
566
+ return helperText;
567
+ },
568
+ set(text) {
569
+ helperTextEl.textContent = text;
570
+ helperTextEl.style.display = text ? 'block' : 'none';
571
+ },
386
572
  },
387
573
  });
388
574
  return container;
@@ -406,7 +592,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
406
592
  toggleIcon(checked);
407
593
  onChange(checked, value);
408
594
  };
409
- const { checked: initChecked = false, value = '', text = '', size = 'small', 'mui:change': onChange = emptyFn$1, disabled: initDisabled = false, color = 'primary', } = props;
595
+ const { checked: initChecked = false, value = '', text = '', size = 'small', 'kt:change': onChange = emptyFn$1, disabled: initDisabled = false, color = 'primary', } = props;
410
596
  const inputRef = kt_js.ref();
411
597
  let checked = initChecked;
412
598
  let disabled = initDisabled;
@@ -426,7 +612,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
426
612
  * RadioGroup component - groups multiple radios together
427
613
  */
428
614
  function RadioGroup(props) {
429
- const { value = '', size = 'small', 'mui:change': onChange = emptyFn$1, row = false } = props;
615
+ const { value = '', size = 'small', 'kt:change': onChange = emptyFn$1, row = false } = props;
430
616
  const changeHandler = (checked, value) => {
431
617
  if (checked) {
432
618
  onChange(value);
@@ -436,15 +622,15 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
436
622
  const radios = props.options.map((o) => {
437
623
  o.size = size;
438
624
  o.checked = value === o.value;
439
- const originalChange = o['mui:change'];
625
+ const originalChange = o['kt:change'];
440
626
  if (originalChange) {
441
- o['mui:change'] = (checked, newValue) => {
627
+ o['kt:change'] = (checked, newValue) => {
442
628
  originalChange(checked, newValue);
443
629
  changeHandler(checked, newValue);
444
630
  };
445
631
  }
446
632
  else {
447
- o['mui:change'] = changeHandler;
633
+ o['kt:change'] = changeHandler;
448
634
  }
449
635
  return Radio(o);
450
636
  });
@@ -456,7 +642,7 @@ var __ktjs_mui__ = (function (exports, jsxRuntime, kt_js) {
456
642
  * Select component - mimics MUI Select appearance and behavior
457
643
  */
458
644
  function Select(props) {
459
- let { value = '', options = [], label = '', placeholder = '', size = 'medium', 'mui:change': onChange = emptyFn, fullWidth = false, disabled = false, } = props;
645
+ let { value = '', options = [], label = '', placeholder = '', size = 'medium', 'kt:change': onChange = emptyFn, fullWidth = false, disabled = false, } = props;
460
646
  let isOpen = false;
461
647
  let isFocused = false;
462
648
  const selectRef = kt_js.ref();
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
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
6
6
  */
7
7
  function Alert(props) {
8
- const { children, severity = 'info', variant = 'standard', icon, 'mui:close': onClose, sx } = props;
8
+ const { children, severity = 'info', variant = 'standard', icon, 'kt:close': onClose, sx } = props;
9
9
  const classes = `mui-alert mui-alert-${severity} mui-alert-${variant} ${props.class ? props.class : ''}`;
10
10
  // Convert sx object to style string
11
11
  let styleString = props.style || '';
@@ -44,12 +44,23 @@ function Alert(props) {
44
44
  return alert;
45
45
  }
46
46
 
47
- const emptyFn$4 = () => { };
47
+ const emptyFn$3 = () => { };
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$3;
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$4, } = props;
63
+ const { children, variant = 'text', color = 'primary', size = 'medium', disabled = false, fullWidth = false, iconOnly = false, startIcon, endIcon, type = 'button', 'on:click': onClick = emptyFn$3, } = props;
53
64
  const classes = [
54
65
  'mui-button',
55
66
  `mui-button-${variant}`,
@@ -91,7 +102,7 @@ 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$3 = () => { };
105
+ const emptyFn$2 = () => { };
95
106
  /**
96
107
  * Checkbox component - mimics MUI Checkbox appearance and behavior
97
108
  */
@@ -113,13 +124,13 @@ function Checkbox(props) {
113
124
  if (disabled) {
114
125
  return;
115
126
  }
116
- checked = inputRef.value.checked;
127
+ checked = inputEl.checked;
117
128
  indeterminate = false;
118
129
  toggleIcon(checked, indeterminate);
119
130
  onChange(checked, value);
120
131
  };
121
- let { checked = false, value = '', label = '', size = 'medium', 'mui:change': onChange = emptyFn$3, disabled = false, color = 'primary', indeterminate = false, } = props;
122
- const inputRef = ref$1();
132
+ let { checked = false, value = '', label = '', size = 'medium', 'kt:change': onChange = emptyFn$2, disabled = false, color = 'primary', indeterminate = false, } = props;
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', 'mui:change': onChange = emptyFn$3, 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,29 +193,56 @@ 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['mui:change'];
196
+ const originalChange = o['kt:change'];
165
197
  if (originalChange) {
166
- o['mui:change'] = (checked, value) => {
198
+ o['kt:change'] = (checked, value) => {
167
199
  originalChange(checked, value);
168
200
  changeHandler(checked, value);
169
201
  };
170
202
  }
171
203
  else {
172
- o['mui:change'] = changeHandler;
204
+ o['kt:change'] = changeHandler;
173
205
  }
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;
@@ -195,7 +254,7 @@ const noop = () => { };
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, 'mui:close': onClose = noop, 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') {
@@ -280,8 +339,67 @@ document.createElement('div');
280
339
  * - can alse be used to store normal values, but it is not reactive.
281
340
  * @param value mostly an HTMLElement
282
341
  */
283
- function ref(value) {
284
- return { value: value, isKT: true };
342
+ function ref(value, onChange) {
343
+ let _value = value;
344
+ let _onChanges = [];
345
+ return {
346
+ isKT: true,
347
+ get value() {
348
+ return _value;
349
+ },
350
+ set value(newValue) {
351
+ if (newValue === _value) {
352
+ return;
353
+ }
354
+ // replace the old node with the new one in the DOM if both are nodes
355
+ if (_value instanceof Node && newValue instanceof Node) {
356
+ if (newValue.contains(_value)) {
357
+ _value.remove();
358
+ }
359
+ _value.replaceWith(newValue);
360
+ }
361
+ const oldValue = _value;
362
+ _value = newValue;
363
+ for (let i = 0; i < _onChanges.length; i++) {
364
+ _onChanges[i](newValue, oldValue);
365
+ }
366
+ },
367
+ addOnChange: (callback) => _onChanges.push(callback),
368
+ removeOnChange: (callback) => {
369
+ for (let i = _onChanges.length - 1; i >= 0; i--) {
370
+ if (_onChanges[i] === callback) {
371
+ _onChanges.splice(i, 1);
372
+ return true;
373
+ }
374
+ }
375
+ return false;
376
+ },
377
+ };
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;
285
403
  }
286
404
 
287
405
  /**
@@ -325,64 +443,132 @@ function LinearProgress(props) {
325
443
  return container;
326
444
  }
327
445
 
328
- const emptyFn$2 = () => { };
329
446
  /**
330
447
  * TextField component - mimics MUI TextField appearance and behavior
331
448
  */
332
449
  function TextField(props) {
333
- const { label = '', placeholder = '', value = '', type = 'text', disabled = false, readonly = false, required = false, error = false, helperText = '', fullWidth = false, multiline = false, rows = 3, maxRows = 10, size = 'medium', 'mui:input': onInput = emptyFn$2, 'mui:change': onChange = emptyFn$2, 'mui:blur': onBlur = emptyFn$2, 'mui:focus': onFocus = emptyFn$2, } = props;
334
- let isFocused = false;
335
- const inputRef = ref$1();
336
- // Update container classes
337
- const updateClasses = () => {
338
- const hasValue = inputRef.value?.value || '';
339
- const 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');
457
+ const updateContainerClass = () => {
458
+ container.className = [
340
459
  'mui-textfield-root',
341
460
  `mui-textfield-size-${size}`,
342
461
  isFocused ? 'mui-textfield-focused' : '',
343
462
  error ? 'mui-textfield-error' : '',
344
463
  disabled ? 'mui-textfield-disabled' : '',
345
464
  fullWidth ? 'mui-textfield-fullwidth' : '',
346
- label && (isFocused || hasValue) ? 'mui-textfield-has-value' : '',
465
+ label && isFocused && inputEl.value ? 'mui-textfield-has-value' : '',
347
466
  label ? '' : 'mui-textfield-no-label',
348
- ];
349
- container.className = classes.join(' ');
467
+ ].join(' ');
350
468
  };
351
- const handleInput = (e) => {
352
- const target = e.target;
353
- updateClasses();
354
- onInput(target.value, e);
469
+ const handleInput = () => {
470
+ updateContainerClass();
471
+ onInput(inputEl.value);
472
+ onInputTrim(inputEl.value.trim());
355
473
  };
356
- const handleChange = (e) => {
357
- const target = e.target;
358
- onChange(target.value, e);
474
+ const handleChange = () => {
475
+ onChange(inputEl.value);
476
+ onChangeTrim(inputEl.value.trim());
359
477
  };
360
- const handleFocus = (e) => {
478
+ const handleFocus = () => {
361
479
  isFocused = true;
362
- updateClasses();
363
- const target = e.target;
364
- onFocus(target.value, e);
480
+ updateContainerClass();
481
+ onFocus(inputEl.value);
365
482
  };
366
- const handleBlur = (e) => {
483
+ const handleBlur = () => {
367
484
  isFocused = false;
368
- updateClasses();
369
- const target = e.target;
370
- onBlur(target.value, e);
485
+ updateContainerClass();
486
+ onBlur(inputEl.value);
371
487
  };
372
- // Create input or textarea element
373
- // Only show placeholder when label is floating (focused or has value)
374
488
  const getPlaceholder = () => (label && !isFocused && !value ? '' : placeholder);
375
- const inputElement = multiline ? (jsx("textarea", { ref: inputRef, 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 })) : (jsx("input", { ref: inputRef, 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 }));
376
- const container = (jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [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: inputElement }), jsx("fieldset", { class: "mui-textfield-fieldset", children: jsx("legend", { "k-if": label, class: "mui-textfield-legend", children: jsxs("span", { children: [label, required && '*'] }) }) })] }), helperText && jsx("p", { class: "mui-textfield-helper-text", children: helperText })] }));
489
+ let isFocused = false;
490
+ const inputEl = multiline
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 }))
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 });
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 && '*'] }) }) })] })));
495
+ const container = (jsxs("div", { class: 'mui-textfield-root ' + (props.class ? props.class : ''), style: props.style ? props.style : '', children: [wrapperRef, helperTextEl] }));
377
496
  // Initialize classes
378
- setTimeout(() => updateClasses(), 0);
379
- Object.defineProperty(container, 'value', {
380
- get() {
381
- return inputRef.value.value;
497
+ setTimeout(() => updateContainerClass(), 0);
498
+ Object.defineProperties(container, {
499
+ value: {
500
+ get() {
501
+ return inputEl.value;
502
+ },
503
+ set(newValue) {
504
+ inputEl.value = newValue;
505
+ updateContainerClass();
506
+ },
382
507
  },
383
- set(newValue) {
384
- inputRef.value.value = newValue;
385
- updateClasses();
508
+ label: {
509
+ get() {
510
+ return label;
511
+ },
512
+ set(newLabel) {
513
+ label = newLabel;
514
+ wrapperRef.value.redraw(); // label takes too much and should be redrawn
515
+ updateContainerClass();
516
+ },
517
+ },
518
+ placeholder: {
519
+ get() {
520
+ return placeholder;
521
+ },
522
+ set(newPlaceholder) {
523
+ placeholder = newPlaceholder;
524
+ inputEl.placeholder = getPlaceholder();
525
+ },
526
+ },
527
+ type: {
528
+ get() {
529
+ return type;
530
+ },
531
+ set(newType) {
532
+ type = newType || 'text';
533
+ inputEl.type = type;
534
+ },
535
+ },
536
+ disabled: {
537
+ get() {
538
+ return disabled;
539
+ },
540
+ set(val) {
541
+ disabled = !!val;
542
+ inputEl.disabled = disabled;
543
+ container.classList.toggle('mui-textfield-disabled', disabled);
544
+ },
545
+ },
546
+ readonly: {
547
+ get() {
548
+ return readonly;
549
+ },
550
+ set(val) {
551
+ readonly = Boolean(val);
552
+ inputEl.readOnly = readonly;
553
+ },
554
+ },
555
+ error: {
556
+ get() {
557
+ return error;
558
+ },
559
+ set(val) {
560
+ error = Boolean(val);
561
+ container.classList.toggle('mui-textfield-error', error);
562
+ },
563
+ },
564
+ helperText: {
565
+ get() {
566
+ return helperText;
567
+ },
568
+ set(text) {
569
+ helperTextEl.textContent = text;
570
+ helperTextEl.style.display = text ? 'block' : 'none';
571
+ },
386
572
  },
387
573
  });
388
574
  return container;
@@ -406,7 +592,7 @@ function Radio(props) {
406
592
  toggleIcon(checked);
407
593
  onChange(checked, value);
408
594
  };
409
- const { checked: initChecked = false, value = '', text = '', size = 'small', 'mui:change': onChange = emptyFn$1, disabled: initDisabled = false, color = 'primary', } = props;
595
+ const { checked: initChecked = false, value = '', text = '', size = 'small', 'kt:change': onChange = emptyFn$1, disabled: initDisabled = false, color = 'primary', } = props;
410
596
  const inputRef = ref$1();
411
597
  let checked = initChecked;
412
598
  let disabled = initDisabled;
@@ -426,7 +612,7 @@ function Radio(props) {
426
612
  * RadioGroup component - groups multiple radios together
427
613
  */
428
614
  function RadioGroup(props) {
429
- const { value = '', size = 'small', 'mui:change': onChange = emptyFn$1, row = false } = props;
615
+ const { value = '', size = 'small', 'kt:change': onChange = emptyFn$1, row = false } = props;
430
616
  const changeHandler = (checked, value) => {
431
617
  if (checked) {
432
618
  onChange(value);
@@ -436,15 +622,15 @@ function RadioGroup(props) {
436
622
  const radios = props.options.map((o) => {
437
623
  o.size = size;
438
624
  o.checked = value === o.value;
439
- const originalChange = o['mui:change'];
625
+ const originalChange = o['kt:change'];
440
626
  if (originalChange) {
441
- o['mui:change'] = (checked, newValue) => {
627
+ o['kt:change'] = (checked, newValue) => {
442
628
  originalChange(checked, newValue);
443
629
  changeHandler(checked, newValue);
444
630
  };
445
631
  }
446
632
  else {
447
- o['mui:change'] = changeHandler;
633
+ o['kt:change'] = changeHandler;
448
634
  }
449
635
  return Radio(o);
450
636
  });
@@ -456,7 +642,7 @@ const emptyFn = () => { };
456
642
  * Select component - mimics MUI Select appearance and behavior
457
643
  */
458
644
  function Select(props) {
459
- let { value = '', options = [], label = '', placeholder = '', size = 'medium', 'mui:change': onChange = emptyFn, fullWidth = false, disabled = false, } = props;
645
+ let { value = '', options = [], label = '', placeholder = '', size = 'medium', 'kt:change': onChange = emptyFn, fullWidth = false, disabled = false, } = props;
460
646
  let isOpen = false;
461
647
  let isFocused = false;
462
648
  const selectRef = ref$1();
@@ -528,7 +714,7 @@ function Select(props) {
528
714
  }
529
715
  }
530
716
  };
531
- const valueDisplay = createRedrawable(() => {
717
+ const valueDisplay = createRedrawable$1(() => {
532
718
  const o = options.find((opt) => opt.value === value);
533
719
  let inner;
534
720
  if (o === undefined) {
@@ -539,7 +725,7 @@ function Select(props) {
539
725
  }
540
726
  return jsx("div", { class: "mui-select-display", children: inner });
541
727
  });
542
- const menu = createRedrawable(() => {
728
+ const menu = createRedrawable$1(() => {
543
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 }))) }));
544
730
  });
545
731
  // Create container
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/mui",
3
- "version": "0.17.9",
3
+ "version": "0.18.1",
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.0"
38
+ "@ktjs/core": "0.18.6"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rollup -c rollup.config.mjs",