@rettangoli/ui 0.1.13 → 0.1.14

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.14",
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,72 @@
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) {
14
+ ref.setAttribute('value', defaultValue)
15
+ }
16
+ }
17
+ if (field.inputType === 'inputText' && field.placeholder) {
18
+ const currentPlaceholder = ref.getAttribute('placeholder')
19
+ if (currentPlaceholder !== field.placeholder) {
20
+ ref.setAttribute('placeholder', field.placeholder)
21
+ }
22
+ }
23
+ if (field.inputType === 'select' && field.placeholder) {
24
+ if (ref.placeholder !== field.placeholder) {
25
+ ref.placeholder = field.placeholder;
26
+ }
27
+ }
28
+ if (field.inputType === 'select') {
29
+ if (field.placeholder !== ref.getAttribute('placeholder')) {
30
+ if (field.placeholder !== undefined) {
31
+ ref.setAttribute('placeholder', field.placeholder)
32
+ } else {
33
+ ref.removeAttribute('placeholder');
34
+ }
35
+ ref.render();
36
+ }
37
+
38
+ const defaultValue = defaultValues[field.name]
39
+ if (defaultValue !== ref.selectedValue) {
40
+ ref.selectedValue = defaultValue;
41
+ ref.render();
42
+ }
43
+ }
44
+ })
45
+ }
46
+
1
47
  export const handleBeforeMount = (deps) => {
2
48
  const { store, props } = deps;
3
49
  store.setFormValues(props.defaultValues);
4
50
  };
5
51
 
52
+ export const handleAfterMount = (deps) => {
53
+ const { props, getRefIds, render } = deps;
54
+ const { form = {}, defaultValues } = props;
55
+ const refs = getRefIds();
56
+ updateAttributes({ form, defaultValues, refs });
57
+ render();
58
+ };
59
+
6
60
  export const handleOnUpdate = (deps, payload) => {
7
61
  const { oldAttrs, newAttrs, newProps } = payload;
8
- const { store, render } = deps;
9
-
10
- if (oldAttrs?.key === newAttrs?.key) {
62
+ const { store, render, getRefIds } = deps;
63
+ const { form = {}, defaultValues } = newProps;
64
+ if (oldAttrs?.key !== newAttrs?.key) {
65
+ const refs = getRefIds();
66
+ updateAttributes({ form, defaultValues, refs });
11
67
  return;
12
68
  }
13
-
14
- store.setFormValues(newProps.defaultValues);
69
+ store.setFormValues(defaultValues);
15
70
  render();
16
71
  };
17
72
 
@@ -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-*:
@@ -329,29 +321,29 @@ template:
329
321
  - $if field.inputType == "read-only-text":
330
322
  - rtgl-text s=sm: ${field.defaultValue}
331
323
  - $if field.inputType == "inputText":
332
- - rtgl-input#input-${field.name} key=${key} w=f placeholder=${field.placeholder} value=${field.defaultValue}:
324
+ - rtgl-input#field-${field.name} w=f:
333
325
  - $if field.inputType == "popover-input":
334
- - rtgl-popover-input#popover-input-${field.name} label="${field.label}" .defaultValue=fields[${i}].defaultValue:
326
+ - rtgl-popover-input#field-${field.name} label="${field.label}":
335
327
  - $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:
328
+ - rtgl-select#field-${field.name} key=${key} w=f .options=fields[${i}].options ?no-clear=fields[${i}].noClear .addOption=fields[${i}].addOption:
337
329
  - $if field.inputType == "colorPicker":
338
- - rtgl-color-picker#colorpicker-${field.name} key=${key} value=${field.defaultValue}:
330
+ - rtgl-color-picker#field-${field.name} key=${key}:
339
331
  - $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}:
332
+ - rtgl-slider#field-${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step}:
341
333
  - $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}:
334
+ - rtgl-slider-input#field-${field.name} w=f min=${field.min} max=${field.max} step=${field.step}:
343
335
  - $if field.inputType == "image" && field.imageSrc:
344
336
  - rtgl-image#image-${field.name} src=${field.imageSrc} w=${field.width} h=${field.height} cur=p:
345
337
  - $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:
338
+ - rtgl-view#field-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
347
339
  - rtgl-text c=mu-fg ta=c: ${field.placeholderText}
348
340
  - $if field.inputType == "waveform" && field.waveformData:
349
- - rtgl-waveform#waveform-${field.name} .waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=p:
341
+ - rtgl-waveform#field-${field.name} .waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=p:
350
342
  - $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:
343
+ - rtgl-view#field-${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=p p=md:
352
344
  - rtgl-text c=mu-fg ta=c: ${field.placeholder}
353
345
  - $if field.inputType == "slot":
354
- - 'slot#slot-${field.slotName} name=${field.slot} style="display: contents;"':
346
+ - 'slot#field-${field.slotName} name=${field.slot} style="display: contents;"':
355
347
  - rtgl-view g=sm w=f:
356
348
  - rtgl-view d=h ah=e g=sm w=f:
357
349
  - $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();
@@ -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));
@@ -63,6 +63,8 @@ export const selectViewData = ({ state, props, attrs }) => {
63
63
  };
64
64
  });
65
65
 
66
+ console.log('attrs.placeholder', attrs.placeholder)
67
+
66
68
  return {
67
69
  containerAttrString,
68
70
  isOpen: state.isOpen,
@@ -71,7 +73,6 @@ export const selectViewData = ({ state, props, attrs }) => {
71
73
  selectedValue: currentValue,
72
74
  selectedLabel: displayLabel,
73
75
  selectedLabelColor: isPlaceholderLabel ? "mu-fg" : "fg",
74
- placeholder: props.placeholder || 'Select an option',
75
76
  hasValue: currentValue !== null && currentValue !== undefined,
76
77
  showClear: !attrs['no-clear'] && !props['no-clear'] && (currentValue !== null && currentValue !== undefined),
77
78
  showAddOption: !!props.addOption,
@@ -102,8 +103,8 @@ export const closeOptionsPopover = (state) => {
102
103
  state.isOpen = false;
103
104
  }
104
105
 
105
- export const updateSelectOption = (state, option) => {
106
- state.selectedValue = option.value;
106
+ export const updateSelectedValue = (state, payload) => {
107
+ state.selectedValue = payload.value;
107
108
  state.isOpen = false;
108
109
  }
109
110
 
@@ -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 {