@rettangoli/ui 0.1.13 → 0.1.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rettangoli/ui",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -1,17 +1,79 @@
1
+
2
+ const updateAttributes = ({ form, defaultValues = {}, refs }) => {
3
+ const { fields = [] } = form;
4
+ fields.forEach((field) => {
5
+ const ref = refs[`field-${field.name}`]?.elm;
6
+
7
+ if (!ref) {
8
+ return;
9
+ }
10
+
11
+ if (['inputText', 'colorPicker', 'slider', 'slider-input', 'popover-input'].includes(field.inputType)) {
12
+ const defaultValue = defaultValues[field.name];
13
+ if (defaultValue === undefined || defaultValue === null) {
14
+ ref.removeAttribute('value')
15
+ } else {
16
+ ref.setAttribute('value', defaultValue)
17
+ }
18
+ }
19
+ if (field.inputType === 'inputText' && field.placeholder) {
20
+ const currentPlaceholder = ref.getAttribute('placeholder')
21
+ if (currentPlaceholder !== field.placeholder) {
22
+ if (field.placeholder === undefined || field.placeholder === null) {
23
+ ref.removeAttribute('placeholder');
24
+ } else {
25
+ ref.setAttribute('placeholder', field.placeholder);
26
+ }
27
+ }
28
+ }
29
+ if (field.inputType === 'select' && field.placeholder) {
30
+ if (ref.placeholder !== field.placeholder) {
31
+ ref.placeholder = field.placeholder;
32
+ }
33
+ }
34
+ if (field.inputType === 'select') {
35
+ if (field.placeholder !== ref.getAttribute('placeholder')) {
36
+ if (field.placeholder !== undefined) {
37
+ ref.setAttribute('placeholder', field.placeholder)
38
+ } else {
39
+ ref.removeAttribute('placeholder');
40
+ }
41
+ ref.render();
42
+ }
43
+
44
+ const defaultValue = defaultValues[field.name]
45
+ if (defaultValue !== ref.selectedValue) {
46
+ ref.selectedValue = defaultValue;
47
+ ref.render();
48
+ }
49
+ }
50
+ })
51
+ }
52
+
1
53
  export const handleBeforeMount = (deps) => {
2
54
  const { store, props } = deps;
3
55
  store.setFormValues(props.defaultValues);
4
56
  };
5
57
 
58
+ export const handleAfterMount = (deps) => {
59
+ const { props, getRefIds, render } = deps;
60
+ const { form = {}, defaultValues } = props;
61
+ const refs = getRefIds();
62
+ updateAttributes({ form, defaultValues, refs });
63
+ render();
64
+ };
65
+
6
66
  export const handleOnUpdate = (deps, payload) => {
7
67
  const { oldAttrs, newAttrs, newProps } = payload;
8
- const { store, render } = deps;
9
-
10
- if (oldAttrs?.key === newAttrs?.key) {
68
+ const { store, render, getRefIds } = deps;
69
+ const { form = {}, defaultValues } = newProps;
70
+ if (oldAttrs?.key !== newAttrs?.key) {
71
+ const refs = getRefIds();
72
+ updateAttributes({ form, defaultValues, refs });
73
+ store.setFormValues(defaultValues);
74
+ render();
11
75
  return;
12
76
  }
13
-
14
- store.setFormValues(newProps.defaultValues);
15
77
  render();
16
78
  };
17
79
 
@@ -44,28 +106,7 @@ export const handleActionClick = (deps, payload) => {
44
106
  export const handleInputChange = (deps, payload) => {
45
107
  const { store, dispatchEvent, props } = deps;
46
108
  const event = payload._event;
47
- const name = event.currentTarget.id.replace("input-", "");
48
- // TODO fix double event
49
- if (name && event.detail.value !== undefined) {
50
- store.setFormFieldValue({
51
- name: name,
52
- value: event.detail.value,
53
- props,
54
- });
55
- dispatchFormChange(
56
- name,
57
- event.detail.value,
58
- store.selectFormValues(),
59
- dispatchEvent,
60
- );
61
- }
62
- };
63
-
64
- export const handlePopoverInputChange = (deps, payload) => {
65
- const { store, dispatchEvent, props } = deps;
66
- const event = payload._event;
67
- const name = event.currentTarget.id.replace("popover-input-", "");
68
- // TODO fix double event
109
+ let name = event.currentTarget.id.replace("field-", "");
69
110
  if (name && event.detail.value !== undefined) {
70
111
  store.setFormFieldValue({
71
112
  name: name,
@@ -84,7 +125,7 @@ export const handlePopoverInputChange = (deps, payload) => {
84
125
  export const handleSelectChange = (deps, payload) => {
85
126
  const { store, dispatchEvent, render, props } = deps;
86
127
  const event = payload._event;
87
- const name = event.currentTarget.id.replace("select-", "");
128
+ const name = event.currentTarget.id.replace("field-", "");
88
129
  if (name) {
89
130
  store.setFormFieldValue({
90
131
  name: name,
@@ -104,7 +145,7 @@ export const handleSelectChange = (deps, payload) => {
104
145
  export const handleColorPickerChange = (deps, payload) => {
105
146
  const { store, dispatchEvent, props } = deps;
106
147
  const event = payload._event;
107
- const name = event.currentTarget.id.replace("colorpicker-", "");
148
+ const name = event.currentTarget.id.replace("field-", "");
108
149
  if (name && event.detail.value !== undefined) {
109
150
  store.setFormFieldValue({
110
151
  name: name,
@@ -123,7 +164,7 @@ export const handleColorPickerChange = (deps, payload) => {
123
164
  export const handleSliderChange = (deps, payload) => {
124
165
  const { store, dispatchEvent, props } = deps;
125
166
  const event = payload._event;
126
- const name = event.currentTarget.id.replace("slider-", "");
167
+ const name = event.currentTarget.id.replace("field-", "");
127
168
  if (name && event.detail.value !== undefined) {
128
169
  store.setFormFieldValue({
129
170
  name: name,
@@ -142,7 +183,7 @@ export const handleSliderChange = (deps, payload) => {
142
183
  export const handleSliderInputChange = (deps, payload) => {
143
184
  const { store, dispatchEvent, props } = deps;
144
185
  const event = payload._event;
145
- const name = event.currentTarget.id.replace("slider-input-", "");
186
+ const name = event.currentTarget.id.replace("field-", "");
146
187
  if (name && event.detail.value !== undefined) {
147
188
  store.setFormFieldValue({
148
189
  name: name,
@@ -199,7 +240,7 @@ export const handleWaveformClick = (deps, payload) => {
199
240
  export const handleSelectAddOption = (deps, payload) => {
200
241
  const { store, dispatchEvent } = deps;
201
242
  const event = payload._event;
202
- const name = event.currentTarget.id.replace("select-", "");
243
+ const name = event.currentTarget.id.replace("field-", "");
203
244
  dispatchEvent(
204
245
  new CustomEvent("action-click", {
205
246
  detail: {
@@ -231,7 +272,7 @@ export const handleTooltipMouseEnter = (deps, payload) => {
231
272
  }
232
273
  };
233
274
 
234
- export const handleTooltipMouseLeave = (deps, payload) => {
275
+ export const handleTooltipMouseLeave = (deps) => {
235
276
  const { store, render } = deps;
236
277
  store.hideTooltip();
237
278
  render();
@@ -11,7 +11,7 @@ const encode = (input) => {
11
11
  };
12
12
  return text.replace(/[&<>"']/g, char => map[char]);
13
13
  }
14
- if (input === undefined || input === null) {
14
+ if (input === undefined || input === null || input === "") {
15
15
  return ""
16
16
  }
17
17
  return `"${escapeHtml(String(input))}"`;
@@ -89,12 +89,13 @@ const stringifyAttrs = (attrs) => {
89
89
  .join(" ");
90
90
  };
91
91
 
92
- export const selectForm = ({ state, props }) => {
93
- const { form } = props;
92
+ export const selectForm = ({ props }) => {
93
+ const { form = {} } = props;
94
94
  const { context } = props;
95
95
 
96
96
  if (context) {
97
- return parseAndRender(form, context);
97
+ const result = parseAndRender(form, context);
98
+ return result
98
99
  }
99
100
 
100
101
  return form;
@@ -103,23 +104,15 @@ export const selectForm = ({ state, props }) => {
103
104
 
104
105
  export const selectViewData = ({ state, props, attrs }) => {
105
106
  const containerAttrString = stringifyAttrs(attrs);
106
- const defaultValues = state.formValues;
107
107
 
108
108
  const form = selectForm({ state, props });
109
109
  const fields = structuredClone(form.fields || []);
110
110
  fields.forEach((field) => {
111
111
  // Use formValues from state if available, otherwise fall back to defaultValues from props
112
- const defaultValue = get(state.formValues, field.name) ?? get(defaultValues, field.name)
113
- if (["popover-input", "select", "read-only-text"].includes(field.inputType)) {
112
+ const defaultValue = get(state.formValues, field.name)
113
+ if (["read-only-text"].includes(field.inputType)) {
114
114
  field.defaultValue = defaultValue
115
- } else {
116
- field.defaultValue = encode(defaultValue);
117
115
  }
118
-
119
- if (["inputText"].includes(field.inputType)) {
120
- field.placeholder = encode(field.placeholder)
121
- }
122
-
123
116
  if (field.inputType === "image") {
124
117
  const src = field.src;
125
118
  // Only set imageSrc if src exists and is not empty
@@ -268,26 +268,18 @@ refs:
268
268
  handler: handleTooltipMouseEnter
269
269
  mouseleave:
270
270
  handler: handleTooltipMouseLeave
271
- input-*:
271
+ field-*:
272
272
  eventListeners:
273
273
  input-change:
274
274
  handler: handleInputChange
275
- select-*:
276
- eventListeners:
277
275
  select-change:
278
276
  handler: handleSelectChange
279
277
  add-option-selected:
280
278
  handler: handleSelectAddOption
281
- colorpicker-*:
282
- eventListeners:
283
279
  colorpicker-change:
284
280
  handler: handleColorPickerChange
285
- slider-*:
286
- eventListeners:
287
281
  slider-change:
288
282
  handler: handleSliderChange
289
- slider-input-*:
290
- eventListeners:
291
283
  slider-input-value-change:
292
284
  handler: handleSliderInputChange
293
285
  image-*:
@@ -302,10 +294,6 @@ refs:
302
294
  handler: handleWaveformClick
303
295
  contextmenu:
304
296
  handler: handleWaveformClick
305
- popover-input-*:
306
- eventListeners:
307
- input-change:
308
- handler: handlePopoverInputChange
309
297
 
310
298
  events:
311
299
  form-change: {}
@@ -329,29 +317,29 @@ template:
329
317
  - $if field.inputType == "read-only-text":
330
318
  - rtgl-text s=sm: ${field.defaultValue}
331
319
  - $if field.inputType == "inputText":
332
- - rtgl-input#input-${field.name} key=${key} w=f placeholder=${field.placeholder} value=${field.defaultValue}:
320
+ - rtgl-input#field-${field.name} w=f:
333
321
  - $if field.inputType == "popover-input":
334
- - rtgl-popover-input#popover-input-${field.name} label="${field.label}" .defaultValue=fields[${i}].defaultValue:
322
+ - rtgl-popover-input#field-${field.name} label="${field.label}":
335
323
  - $if field.inputType == "select":
336
- - rtgl-select#select-${field.name} key=${key} w=f .options=fields[${i}].options .placeholder=fields[${i}].placeholder .selectedValue=fields[${i}].defaultValue ?no-clear=fields[${i}].noClear .addOption=fields[${i}].addOption:
324
+ - rtgl-select#field-${field.name} key=${key} w=f .options=fields[${i}].options ?no-clear=fields[${i}].noClear .addOption=fields[${i}].addOption:
337
325
  - $if field.inputType == "colorPicker":
338
- - rtgl-color-picker#colorpicker-${field.name} key=${key} value=${field.defaultValue}:
326
+ - rtgl-color-picker#field-${field.name} key=${key}:
339
327
  - $if field.inputType == "slider":
340
- - rtgl-slider#slider-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step} value=${field.defaultValue}:
328
+ - rtgl-slider#field-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step}:
341
329
  - $if field.inputType == "slider-input":
342
- - rtgl-slider-input#slider-input-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step} defaultValue=${field.defaultValue}:
330
+ - rtgl-slider-input#field-${field.name} w=f min=${field.min} max=${field.max} step=${field.step}:
343
331
  - $if field.inputType == "image" && field.imageSrc:
344
332
  - rtgl-image#image-${field.name} src=${field.imageSrc} w=${field.width} h=${field.height} cur=p:
345
333
  - $if field.inputType == "image" && !field.imageSrc:
346
- - rtgl-view#image-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
334
+ - rtgl-view#field-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
347
335
  - rtgl-text c=mu-fg ta=c: ${field.placeholderText}
348
336
  - $if field.inputType == "waveform" && field.waveformData:
349
- - rtgl-waveform#waveform-${field.name} .waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=p:
337
+ - rtgl-waveform#field-${field.name} .waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=p:
350
338
  - $if field.inputType == "waveform" && !field.waveformData:
351
- - rtgl-view#waveform-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
339
+ - rtgl-view#field-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
352
340
  - rtgl-text c=mu-fg ta=c: ${field.placeholder}
353
341
  - $if field.inputType == "slot":
354
- - 'slot#slot-${field.slotName} name=${field.slot} style="display: contents;"':
342
+ - 'slot#field-${field.slotName} name=${field.slot} style="display: contents;"':
355
343
  - rtgl-view g=sm w=f:
356
344
  - rtgl-view d=h ah=e g=sm w=f:
357
345
  - $for button, i in actions.buttons:
@@ -1,17 +1,18 @@
1
1
  export const handleBeforeMount = (deps) => {
2
- const { store, props } = deps;
2
+ const { store, attrs } = deps;
3
3
 
4
- if (props.value !== undefined || props.defaultValue !== undefined) {
5
- store.setValue(props.value || props.defaultValue || '');
4
+ if (attrs.value !== undefined) {
5
+ store.setValue(attrs.value || '');
6
6
  }
7
7
  }
8
8
 
9
9
  export const handleOnUpdate = (deps, payload) => {
10
- const { oldProps, newProps} = payload
11
- const { store, props, render } = deps;
10
+ const { oldAttrs, newAttrs } = payload;
11
+ const { store, render } = deps;
12
12
 
13
- if (oldProps.defaultValue !== newProps.defaultValue) {
14
- store.setValue(props.defaultValue || '');
13
+ if (oldAttrs?.value !== newAttrs?.value) {
14
+ const value = newAttrs?.value ?? '';
15
+ store.setValue(value);
15
16
  }
16
17
 
17
18
  render();
@@ -63,9 +64,8 @@ export const handleInputChange = (deps, payload) => {
63
64
  render();
64
65
  }
65
66
 
66
- export const handleSubmitClick = (deps, payload) => {
67
+ export const handleSubmitClick = (deps) => {
67
68
  const { store, render, dispatchEvent, getRefIds } = deps;
68
- const event = payload._event;
69
69
  const { input } = getRefIds()
70
70
  const value = input.elm.value;
71
71
 
@@ -8,16 +8,15 @@ export const createInitialState = () => Object.freeze({
8
8
  tempValue: '',
9
9
  });
10
10
 
11
- export const selectViewData = ({ attrs, state, props }) => {
12
- // Use state's current value if it has been modified, otherwise use props
11
+ export const selectViewData = ({ attrs, state }) => {
13
12
  const value = state.value || '-';
14
13
 
15
14
  return {
16
15
  isOpen: state.isOpen,
17
16
  position: state.position,
18
- value: value ?? '-',
17
+ value: value,
19
18
  tempValue: state.tempValue,
20
- placeholder: props.placeholder ?? '',
19
+ placeholder: attrs.placeholder ?? '',
21
20
  label: attrs.label,
22
21
  };
23
22
  }
@@ -30,7 +29,6 @@ export const openPopover = (state, payload) => {
30
29
  const { position } = payload;
31
30
  state.position = position;
32
31
  state.isOpen = true;
33
- // Reset the current value to match the display value when opening
34
32
  state.hasUnsavedChanges = false;
35
33
  }
36
34
 
@@ -4,22 +4,12 @@ viewDataSchema:
4
4
  type: object
5
5
 
6
6
  attrsSchema:
7
- type: object
8
- properties:
9
- auto-focus:
10
- type: boolean
11
-
12
- propsSchema:
13
7
  type: object
14
8
  properties:
15
9
  value:
16
10
  type: string
17
- defaultValue:
18
- type: string
19
11
  placeholder:
20
12
  type: string
21
- onChange:
22
- type: function
23
13
 
24
14
  refs:
25
15
  text-display:
@@ -46,10 +36,10 @@ events:
46
36
 
47
37
  template:
48
38
  - rtgl-view#text-display w=f cur=p:
49
- - rtgl-text: ${value}
39
+ - rtgl-text: ${value}
50
40
  - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y}:
51
- - rtgl-view g=md w=240 slot=content bgc=background br=md:
52
- - rtgl-text: ${label}
53
- - rtgl-input#input w=f placeholder=${placeholder}:
54
- - rtgl-view w=f ah=e:
55
- - rtgl-button#submit: Submit
41
+ - rtgl-view g=md w=240 slot=content bgc=background br=md:
42
+ - rtgl-text: ${label}
43
+ - rtgl-input#input w=f placeholder=${placeholder}:
44
+ - rtgl-view w=f ah=e:
45
+ - rtgl-button#submit: Submit
@@ -6,7 +6,9 @@ export const handleBeforeMount = (deps) => {
6
6
  if (props.selectedValue !== null && props.selectedValue !== undefined && props.options) {
7
7
  const selectedOption = props.options.find(opt => deepEqual(opt.value, props.selectedValue));
8
8
  if (selectedOption) {
9
- store.updateSelectOption(selectedOption);
9
+ store.updateSelectedValue({
10
+ value: selectedOption?.value
11
+ });
10
12
  render();
11
13
  }
12
14
  }
@@ -28,10 +30,15 @@ export const handleOnUpdate = (deps, payload) => {
28
30
  if (selectedValue !== null && selectedValue !== undefined && options) {
29
31
  const selectedOption = options.find(opt => deepEqual(opt.value, selectedValue));
30
32
  if (selectedOption) {
31
- store.updateSelectOption(selectedOption);
33
+ store.updateSelectedValue({
34
+ value: selectedOption.value
35
+ });
32
36
  }
33
37
  }
34
38
  render();
39
+ } else if (oldProps.selectedValue !== newProps.selectedValue) {
40
+ store.updateSelectedValue(newProps.selectedValue);
41
+ render();
35
42
  }
36
43
  }
37
44
 
@@ -65,7 +72,7 @@ export const handleButtonClick = (deps, payload) => {
65
72
  render();
66
73
  }
67
74
 
68
- export const handleClickOptionsPopoverOverlay = (deps, payload) => {
75
+ export const handleClickOptionsPopoverOverlay = (deps) => {
69
76
  const { store, render } = deps;
70
77
  store.closeOptionsPopover();
71
78
  render();
@@ -80,7 +87,7 @@ export const handleOptionClick = (deps, payload) => {
80
87
  const option = props.options[id];
81
88
 
82
89
  // Update internal state
83
- store.updateSelectOption(option);
90
+ store.updateSelectedValue({ value: option?.value });
84
91
 
85
92
  // Call onChange if provided
86
93
  if (props.onChange && typeof props.onChange === 'function') {
@@ -3,13 +3,13 @@ import { deepEqual } from '../../common.js';
3
3
  // Attributes that should not be passed through to the container
4
4
  // These are either handled internally or have special meaning
5
5
  const blacklistedAttrs = [
6
- "id",
7
- "class",
8
- "style",
6
+ "id",
7
+ "class",
8
+ "style",
9
9
  "slot",
10
10
  // Select-specific props that are handled separately
11
11
  "placeholder",
12
- "selectedValue",
12
+ "selectedValue",
13
13
  "selected-value",
14
14
  "onChange",
15
15
  "on-change",
@@ -42,7 +42,7 @@ export const selectViewData = ({ state, props, attrs }) => {
42
42
  const currentValue = state.selectedValue !== null ? state.selectedValue : props.selectedValue;
43
43
 
44
44
  // Calculate display label from value
45
- let displayLabel = props.placeholder || 'Select an option';
45
+ let displayLabel = attrs.placeholder || 'Select an option';
46
46
  let isPlaceholderLabel = true;
47
47
  if (currentValue !== null && currentValue !== undefined && props.options) {
48
48
  const selectedOption = props.options.find(opt => deepEqual(opt.value, currentValue));
@@ -71,7 +71,6 @@ export const selectViewData = ({ state, props, attrs }) => {
71
71
  selectedValue: currentValue,
72
72
  selectedLabel: displayLabel,
73
73
  selectedLabelColor: isPlaceholderLabel ? "mu-fg" : "fg",
74
- placeholder: props.placeholder || 'Select an option',
75
74
  hasValue: currentValue !== null && currentValue !== undefined,
76
75
  showClear: !attrs['no-clear'] && !props['no-clear'] && (currentValue !== null && currentValue !== undefined),
77
76
  showAddOption: !!props.addOption,
@@ -102,8 +101,8 @@ export const closeOptionsPopover = (state) => {
102
101
  state.isOpen = false;
103
102
  }
104
103
 
105
- export const updateSelectOption = (state, option) => {
106
- state.selectedValue = option.value;
104
+ export const updateSelectedValue = (state, payload) => {
105
+ state.selectedValue = payload.value;
107
106
  state.isOpen = false;
108
107
  }
109
108
 
@@ -3,6 +3,12 @@ elementName: rtgl-select
3
3
  viewDataSchema:
4
4
  type: object
5
5
 
6
+ attrsSchema:
7
+ type: object
8
+ properties:
9
+ placeholder:
10
+ type: string
11
+
6
12
  propsSchema:
7
13
  type: object
8
14
  properties:
@@ -17,8 +23,6 @@ propsSchema:
17
23
  type: any
18
24
  selectedValue:
19
25
  type: any
20
- placeholder:
21
- type: string
22
26
  onChange:
23
27
  type: function
24
28
  no-clear:
@@ -1,21 +1,15 @@
1
1
  export const handleBeforeMount = (deps) => {
2
2
  const { store, attrs } = deps;
3
- store.setValue(attrs.defaultValue || 0);
3
+ store.setValue(attrs.value ?? 0);
4
4
  }
5
5
 
6
6
  export const handleOnUpdate = (deps, payload) => {
7
7
  const { oldAttrs, newAttrs } = payload;
8
- const { store, render, attrs } = deps;
8
+ const { store, render } = deps;
9
9
 
10
- // Reset when key changes
11
- if (oldAttrs?.key !== newAttrs?.key && newAttrs?.key) {
12
- const defaultValue = newAttrs?.defaultValue || attrs?.defaultValue || 0;
13
- store.setValue(defaultValue);
14
- render();
15
- } else if (oldAttrs?.defaultValue !== newAttrs?.defaultValue) {
16
- // Also reset when defaultValue changes
17
- const defaultValue = newAttrs?.defaultValue || 0;
18
- store.setValue(defaultValue);
10
+ if (oldAttrs?.value !== newAttrs?.value) {
11
+ const value = newAttrs?.value ?? 0;
12
+ store.setValue(value);
19
13
  render();
20
14
  }
21
15
  }
@@ -6,21 +6,21 @@ viewDataSchema:
6
6
  attrsSchema:
7
7
  type: object
8
8
  properties:
9
- defaultValue:
9
+ value:
10
10
  type: string
11
- default: '0'
11
+ default: "0"
12
12
  w:
13
13
  type: string
14
- default: ''
14
+ default: ""
15
15
  min:
16
16
  type: string
17
- default: '0'
17
+ default: "0"
18
18
  max:
19
19
  type: string
20
- default: '100'
20
+ default: "100"
21
21
  step:
22
22
  type: string
23
- default: '1'
23
+ default: "1"
24
24
 
25
25
  refs:
26
26
  input:
@@ -37,6 +37,7 @@ events:
37
37
 
38
38
  template:
39
39
  - rtgl-view d=h av=c g=md w=${w}:
40
- - rtgl-slider#slider key=${key} w=f type=range min=${min} max=${max} step=${step} value=${value}:
41
- - rtgl-view w=84:
42
- - rtgl-input#input key=${key} w=f type=number min=${min} max=${max} step=${step} value=${value}:
40
+ - rtgl-slider#slider key=${key} w=f type=range min=${min} max=${max} step=${step} value=${value}:
41
+ - rtgl-view w=84:
42
+ - rtgl-input#input key=${key} w=f type=number min=${min} max=${max} step=${step} value=${value}:
43
+
@@ -123,17 +123,26 @@ class RettangoliInputElement extends HTMLElement {
123
123
  };
124
124
 
125
125
  attributeChangedCallback(name, oldValue, newValue) {
126
- // Handle key attribute change - reset value
127
- if (name === "key" && oldValue !== newValue) {
126
+ if (name === 'value') {
128
127
  requestAnimationFrame((() => {
129
128
  const value = this.getAttribute("value");
130
129
  this._inputElement.value = value ?? "";
131
130
  }))
132
- return;
131
+ }
132
+
133
+ if (name === 'placeholder') {
134
+ requestAnimationFrame((() => {
135
+ const placeholder = this.getAttribute("placeholder");
136
+ if (placeholder === undefined || placeholder === 'null') {
137
+ this._inputElement.removeAttribute('placeholder');
138
+ } else {
139
+ this._inputElement.setAttribute('placeholder', placeholder ?? "");
140
+ }
141
+ }))
133
142
  }
134
143
 
135
144
  // Handle input-specific attributes first
136
- if (["type", "placeholder", "disabled", "value", "step", "s"].includes(name)) {
145
+ if (["type", "disabled", "step", "s"].includes(name)) {
137
146
  this._updateInputAttributes();
138
147
  return;
139
148
  }
@@ -205,23 +214,13 @@ class RettangoliInputElement extends HTMLElement {
205
214
 
206
215
  _updateInputAttributes() {
207
216
  const type = this.getAttribute("type") || "text";
208
- const placeholder = this.getAttribute("placeholder");
209
- const value = this.getAttribute("value");
217
+ // const placeholder = this.getAttribute("placeholder");
218
+ // const value = this.getAttribute("value");
210
219
  const step = this.getAttribute("step");
211
220
  const isDisabled = this.hasAttribute('disabled');
212
221
 
213
222
  this._inputElement.setAttribute("type", type);
214
223
 
215
- if (placeholder !== null) {
216
- this._inputElement.setAttribute("placeholder", placeholder);
217
- } else {
218
- this._inputElement.removeAttribute("placeholder");
219
- }
220
-
221
- if (value !== null) {
222
- this._inputElement.value = value;
223
- }
224
-
225
224
  if (step !== null) {
226
225
  this._inputElement.setAttribute("step", step);
227
226
  } else {