@rettangoli/ui 1.0.0-rc13 → 1.0.0-rc14

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.
@@ -16,23 +16,15 @@ refs:
16
16
  field*:
17
17
  eventListeners:
18
18
  value-input:
19
- handler: handleInputChange
19
+ handler: handleValueInput
20
20
  value-change:
21
- handler: handleInputChange
22
- add-option-click:
23
- handler: handleSelectAddOption
21
+ handler: handleValueChange
24
22
  image*:
25
23
  eventListeners:
26
24
  click:
27
25
  handler: handleImageClick
28
26
  contextmenu:
29
27
  handler: handleImageClick
30
- waveform*:
31
- eventListeners:
32
- click:
33
- handler: handleWaveformClick
34
- contextmenu:
35
- handler: handleWaveformClick
36
28
  template:
37
29
  - rtgl-view#formContainer w=f p=md g=lg ${containerAttrString}:
38
30
  - rtgl-view g=sm w=f:
@@ -41,49 +33,72 @@ template:
41
33
  - $if description:
42
34
  - rtgl-text c=mu-fg: ${description}
43
35
  - rtgl-view g=lg w=f:
44
- - $for field, i in fields:
45
- - rtgl-view g=md w=f:
46
- - $if field.label || field.description:
36
+ - $for field, i in flatFields:
37
+ - $if field._isSection:
38
+ - rtgl-view g=md w=f:
47
39
  - rtgl-view g=sm:
48
- - rtgl-view d=h g=md av=c:
49
- - $if field.label:
50
- - rtgl-text: ${field.label}
51
- - $if field.label && field.tooltip:
52
- - rtgl-svg#tooltipIcon${i} data-field-name=${field.name} svg="info" wh=16 c=mu-fg cur=help ml=xs: null
40
+ - $if field.label:
41
+ - rtgl-text s=md fw=bd: ${field.label}
53
42
  - $if field.description:
54
43
  - rtgl-text s=sm c=mu-fg: ${field.description}
55
- - $if field.inputType == "read-only-text":
56
- - rtgl-text s=sm: ${field.defaultValue}
57
- - $if field.inputType == "input-text":
58
- - rtgl-input#field${i} data-field-name=${field.name} w=f min=${field.min} max=${field.max} step=${field.step} data-testid=${field.testId}: null
59
- - $if field.inputType == "input-number":
60
- - rtgl-input-number#field${i} data-field-name=${field.name} w=f data-testid=${field.testId}: null
61
- - $if field.inputType == "input-textarea":
62
- - rtgl-textarea#field${i} data-field-name=${field.name} w=f rows=${field.rows} data-testid=${field.testId}: null
63
- - $if field.inputType == "popover-input":
64
- - rtgl-popover-input#field${i} data-field-name=${field.name} label="${field.label}" data-testid=${field.testId}: null
65
- - $if field.inputType == "select":
66
- - rtgl-select#field${i} data-field-name=${field.name} key=${key} w=f :options=fields[${i}].options ?no-clear=fields[${i}].noClear :addOption=fields[${i}].addOption :selectedValue=#{field.selectedValue} :placeholder=#{field.placeholder} data-testid=${field.testId}: null
67
- - $if field.inputType == "color-picker":
68
- - rtgl-color-picker#field${i} data-field-name=${field.name} key=${key} data-testid=${field.testId}: null
69
- - $if field.inputType == "slider":
70
- - rtgl-slider#field${i} data-field-name=${field.name} key=${key} w=f min=${field.min} max=${field.max} step=${field.step} data-testid=${field.testId}: null
71
- - $if field.inputType == "slider-input":
72
- - rtgl-slider-input#field${i} data-field-name=${field.name} w=f min=${field.min} max=${field.max} step=${field.step} data-testid=${field.testId}: null
73
- - $if field.inputType == "image" && field.imageSrc:
74
- - rtgl-image#image${i} data-field-name=${field.name} src=${field.imageSrc} w=${field.width} h=${field.height} cur=pointer data-testid=${field.testId}: null
75
- - $if field.inputType == "image" && !field.imageSrc:
76
- - rtgl-view#image${i} data-field-name=${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=pointer p=md data-testid=${field.testId}:
77
- - rtgl-text c=mu-fg ta=c: ${field.placeholderText}
78
- - $if field.inputType == "waveform" && field.waveformData:
79
- - rtgl-waveform#waveform${i} data-field-name=${field.name} :waveformData=fields[${i}].waveformData w=${field.width} h=${field.height} cur=pointer data-testid=${field.testId}: null
80
- - $if field.inputType == "waveform" && !field.waveformData:
81
- - rtgl-view#waveform${i} data-field-name=${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=pointer p=md data-testid=${field.testId}:
82
- - rtgl-text c=mu-fg ta=c: ${field.placeholder}
83
- - $if field.inputType == "slot":
84
- - 'slot#fieldSlot${i} data-field-name=${field.name} name=${field.slot} style="display: contents;"': null
85
- - rtgl-view g=sm w=f:
86
- - rtgl-view d=h ah=e g=sm w=f:
87
- - $for button, i in actions.buttons:
88
- - rtgl-button#action${i} data-action-id=${button.id} data-testid=${button.testId}: ${button.content}
44
+ - $if !field._isSection:
45
+ - rtgl-view g=md w=f:
46
+ - $if field.label || field.description:
47
+ - rtgl-view g=sm:
48
+ - rtgl-view d=h g=md av=c:
49
+ - $if field.label:
50
+ - rtgl-text: ${field.label}
51
+ - $if field.required:
52
+ - rtgl-text c=fg s=sm: "*"
53
+ - $if field.tooltip:
54
+ - rtgl-svg#tooltipIcon${field._idx} data-field-name=${field.name} svg="info" wh=16 c=mu-fg cur=help ml=xs: null
55
+ - $if field.description:
56
+ - rtgl-text s=sm c=mu-fg: ${field.description}
57
+ - $if field.type == "input-text":
58
+ - rtgl-input#field${field._idx} data-field-name=${field.name} w=f type=${field._inputType} placeholder=${field.placeholder} ?disabled=${field._disabled}: null
59
+ - $if field.type == "input-number":
60
+ - rtgl-input-number#field${field._idx} data-field-name=${field.name} w=f min=${field.min} max=${field.max} step=${field.step} placeholder=${field.placeholder} ?disabled=${field._disabled}: null
61
+ - $if field.type == "input-textarea":
62
+ - rtgl-textarea#field${field._idx} data-field-name=${field.name} w=f rows=${field.rows} placeholder=${field.placeholder} ?disabled=${field._disabled}: null
63
+ - $if field.type == "select":
64
+ - rtgl-select#field${field._idx} data-field-name=${field.name} w=f :options=flatFields[${field._arrIdx}].options ?no-clear=flatFields[${field._arrIdx}].noClear :selectedValue=#{field._selectedValue} :placeholder=#{field.placeholder} ?disabled=${field._disabled}: null
65
+ - $if field.type == "color-picker":
66
+ - rtgl-color-picker#field${field._idx} data-field-name=${field.name} ?disabled=${field._disabled}: null
67
+ - $if field.type == "slider":
68
+ - rtgl-slider#field${field._idx} data-field-name=${field.name} w=f min=${field.min} max=${field.max} step=${field.step} ?disabled=${field._disabled}: null
69
+ - $if field.type == "slider-with-input":
70
+ - rtgl-slider-input#field${field._idx} data-field-name=${field.name} w=f min=${field.min} max=${field.max} step=${field.step} ?disabled=${field._disabled}: null
71
+ - $if field.type == "popover-input":
72
+ - rtgl-popover-input#field${field._idx} data-field-name=${field.name} label="${field.label}" placeholder=${field.placeholder} ?disabled=${field._disabled}: null
73
+ - $if field.type == "checkbox":
74
+ - rtgl-checkbox#field${field._idx} data-field-name=${field.name} label="${field._checkboxText}" ?disabled=${field._disabled}: null
75
+ - $if field.type == "image" && field._imageSrc:
76
+ - rtgl-image#image${field._idx} data-field-name=${field.name} src=${field._imageSrc} w=${field.width} h=${field.height} cur=pointer: null
77
+ - $if field.type == "image" && !field._imageSrc:
78
+ - rtgl-view#image${field._idx} data-field-name=${field.name} w=${field.width} h=${field.height} bc=ac bw=sm ah=c av=c cur=pointer p=md:
79
+ - rtgl-text c=mu-fg ta=c: ${field.placeholderText}
80
+ - $if field.type == "read-only-text":
81
+ - rtgl-text s=sm: ${field.content}
82
+ - $if field.type == "slot":
83
+ - 'slot#fieldSlot${field._idx} name=${field.slot} style="display: contents;"': null
84
+ - $if field._error:
85
+ - rtgl-text s=sm c=de: ${field._error}
86
+ - $if actions.buttons.length > 0:
87
+ - $if actions._layout == "split":
88
+ - rtgl-view d=h w=f g=sm:
89
+ - rtgl-view d=h g=sm:
90
+ - $for button, i in actions._leftButtons:
91
+ - rtgl-button#action${button._globalIdx} data-action-id=${button.id} v=${button.variant} ?disabled=${button._disabled} pre=${button.pre} suf=${button.suf}: ${button.label}
92
+ - rtgl-view d=h g=sm ah=e w=1fg:
93
+ - $for button, i in actions._rightButtons:
94
+ - rtgl-button#action${button._globalIdx} data-action-id=${button.id} v=${button.variant} ?disabled=${button._disabled} pre=${button.pre} suf=${button.suf}: ${button.label}
95
+ - $if actions._layout == "vertical":
96
+ - rtgl-view g=sm w=f:
97
+ - $for button, i in actions._allButtons:
98
+ - rtgl-button#action${button._globalIdx} data-action-id=${button.id} v=${button.variant} w=f ?disabled=${button._disabled} pre=${button.pre} suf=${button.suf}: ${button.label}
99
+ - $if actions._layout == "stretch":
100
+ - rtgl-view d=h g=sm w=f:
101
+ - $for button, i in actions._allButtons:
102
+ - rtgl-view w=1fg:
103
+ - rtgl-button#action${button._globalIdx} data-action-id=${button.id} v=${button.variant} w=f ?disabled=${button._disabled} pre=${button.pre} suf=${button.suf}: ${button.label}
89
104
  - rtgl-tooltip ?open=${tooltipState.open} x=${tooltipState.x} y=${tooltipState.y} place="t" content="${tooltipState.content}": null
@@ -1,18 +1,46 @@
1
+ const normalizePopoverValue = (value) => {
2
+ if (value === undefined || value === null || value === true) {
3
+ return "";
4
+ }
5
+ return String(value);
6
+ };
7
+
8
+ const commitValue = (deps, value) => {
9
+ const { store, render, dispatchEvent } = deps;
10
+ const nextValue = normalizePopoverValue(value);
11
+
12
+ store.setValue({ value: nextValue });
13
+ store.closePopover({});
14
+
15
+ dispatchEvent(new CustomEvent("value-change", {
16
+ detail: { value: nextValue },
17
+ bubbles: true,
18
+ }));
19
+
20
+ render();
21
+ };
22
+
1
23
  export const handleBeforeMount = (deps) => {
2
24
  const { store, props } = deps;
3
25
 
4
26
  if (props.value !== undefined) {
5
- store.setValue({ value: props.value || '' });
27
+ const value = normalizePopoverValue(props.value);
28
+ store.setValue({ value });
29
+ store.setTempValue({ value });
6
30
  }
7
31
  }
8
32
 
9
33
  export const handleOnUpdate = (deps, payload) => {
10
34
  const { oldProps, newProps } = payload;
11
35
  const { store, render } = deps;
36
+ const valueChanged = oldProps?.value !== newProps?.value;
12
37
 
13
- if (oldProps?.value !== newProps?.value) {
14
- const value = newProps?.value ?? '';
38
+ if (valueChanged) {
39
+ const value = normalizePopoverValue(newProps?.value);
15
40
  store.setValue({ value });
41
+ if (!store.getState().isOpen) {
42
+ store.setTempValue({ value });
43
+ }
16
44
  }
17
45
 
18
46
  render();
@@ -22,8 +50,9 @@ export const handleTextClick = (deps, payload) => {
22
50
  const { store, render, refs, props } = deps;
23
51
  const event = payload._event;
24
52
 
25
- const value = store.selectValue();
26
- store.setTempValue({ value })
53
+ const value = normalizePopoverValue(props.value);
54
+ store.setValue({ value });
55
+ store.setTempValue({ value });
27
56
 
28
57
  store.openPopover({
29
58
  position: {
@@ -31,16 +60,18 @@ export const handleTextClick = (deps, payload) => {
31
60
  y: event.currentTarget.getBoundingClientRect().bottom,
32
61
  }
33
62
  });
34
-
35
- const { input } = refs;
36
- input.value = value;
37
63
  render();
38
64
 
39
- if (props.autoFocus) {
40
- setTimeout(() => {
41
- input.focus();
42
- }, 50)
43
- }
65
+ setTimeout(() => {
66
+ const { input } = refs;
67
+ if (!input) return;
68
+ input.value = value;
69
+ input.focus();
70
+ const innerInput = input.shadowRoot?.querySelector("input, textarea");
71
+ if (innerInput && typeof innerInput.focus === "function") {
72
+ innerInput.focus();
73
+ }
74
+ }, 50);
44
75
  }
45
76
 
46
77
  export const handlePopoverClose = (deps, payload) => {
@@ -50,54 +81,34 @@ export const handlePopoverClose = (deps, payload) => {
50
81
  }
51
82
 
52
83
  export const handleInputChange = (deps, payload) => {
53
- const { store, render, dispatchEvent } = deps;
84
+ const { store } = deps;
54
85
  const event = payload._event;
55
- const value = event.detail.value;
86
+ const value = normalizePopoverValue(event.detail.value);
56
87
 
57
88
  store.setTempValue({ value });
58
-
59
- dispatchEvent(new CustomEvent('value-input', {
60
- detail: { value },
61
- bubbles: true,
62
- }));
63
-
64
- render();
65
89
  }
66
90
 
67
91
  export const handleSubmitClick = (deps) => {
68
- const { store, render, dispatchEvent, refs } = deps;
69
- const { input } = refs
70
- const value = input.value;
71
-
72
- store.setValue({ value });
73
- store.closePopover({});
74
-
75
- dispatchEvent(new CustomEvent('value-change', {
76
- detail: { value },
77
- bubbles: true,
78
- }));
79
-
80
- render();
92
+ const { store, refs } = deps;
93
+ const { input } = refs;
94
+ const value = input ? input.value : store.getState().tempValue;
95
+ commitValue(deps, value);
81
96
  }
82
97
 
83
98
  export const handleInputKeydown = (deps, payload) => {
84
- const { store, render, dispatchEvent, refs } = deps;
99
+ const { store, refs } = deps;
85
100
  const event = payload._event;
86
101
 
87
- if (event.key === 'Enter') {
88
- const { input } = refs
89
- const value = input.value;
90
-
91
- store.closePopover({});
92
- // Dispatch custom event
93
- dispatchEvent(new CustomEvent('value-change', {
94
- detail: { value },
95
- bubbles: true,
96
- }));
97
-
98
- render();
99
- } else if (event.key === 'Escape') {
102
+ if (event.key === "Enter") {
103
+ event.preventDefault();
104
+ event.stopPropagation();
105
+ const { input } = refs;
106
+ const value = input ? input.value : store.getState().tempValue;
107
+ commitValue(deps, value);
108
+ } else if (event.key === "Escape") {
109
+ event.preventDefault();
110
+ event.stopPropagation();
100
111
  store.closePopover({});
101
- render();
112
+ deps.render();
102
113
  }
103
114
  }
@@ -11,7 +11,6 @@ propsSchema:
11
11
  autoFocus:
12
12
  type: boolean
13
13
  events:
14
- value-input: {}
15
14
  value-change: {}
16
15
  methods:
17
16
  type: object
@@ -9,15 +9,19 @@ export const createInitialState = () => Object.freeze({
9
9
  });
10
10
 
11
11
  export const selectViewData = ({ props, state }) => {
12
- const value = state.value || '-';
12
+ const hasValue = typeof state.value === "string" && state.value.length > 0;
13
+ const value = hasValue ? state.value : "-";
14
+ const placeholder = typeof props.placeholder === "string" ? props.placeholder : "";
15
+ const label = typeof props.label === "string" ? props.label : "";
13
16
 
14
17
  return {
15
18
  isOpen: state.isOpen,
16
19
  position: state.position,
17
20
  value: value,
21
+ valueColor: hasValue ? "fg" : "mu-fg",
18
22
  tempValue: state.tempValue,
19
- placeholder: props.placeholder ?? '',
20
- label: props.label,
23
+ placeholder,
24
+ label,
21
25
  };
22
26
  }
23
27
 
@@ -19,9 +19,9 @@ refs:
19
19
  handler: handleSubmitClick
20
20
  template:
21
21
  - rtgl-view#textDisplay w=f cur=pointer:
22
- - rtgl-text: ${value}
22
+ - rtgl-text c=${valueColor}: ${value}
23
23
  - rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y}:
24
- - rtgl-view g=md w=240 slot=content bgc=bg br=md:
24
+ - rtgl-view g=md w=240 slot=content bgc=mu br=md:
25
25
  - rtgl-text: ${label}
26
26
  - rtgl-input#input w=f placeholder=${placeholder}: null
27
27
  - rtgl-view w=f ah=e:
@@ -17,33 +17,26 @@ export const handleBeforeMount = (deps) => {
17
17
  export const handleOnUpdate = (deps, payload) => {
18
18
  const { oldProps, newProps } = payload;
19
19
  const { store, render } = deps;
20
+ let shouldRender = false;
20
21
 
21
- // Check if key changed
22
- if (oldProps?.key !== newProps?.key && newProps?.key) {
23
- // Clear current state using store action
24
- store.resetSelection({});
25
-
26
- // Re-apply the prop value if available
27
- const selectedValue = newProps?.selectedValue;
28
- const options = newProps?.options || [];
29
-
30
- if (selectedValue !== null && selectedValue !== undefined && options) {
31
- const selectedOption = options.find(opt => deepEqual(opt.value, selectedValue));
32
- if (selectedOption) {
33
- store.updateSelectedValue({
34
- value: selectedOption.value
35
- });
36
- }
37
- }
38
- render();
39
- } else if (oldProps.selectedValue !== newProps.selectedValue) {
22
+ if (!!newProps?.disabled && !oldProps?.disabled) {
23
+ store.closeOptionsPopover({});
24
+ shouldRender = true;
25
+ }
26
+
27
+ if (oldProps.selectedValue !== newProps.selectedValue) {
40
28
  store.updateSelectedValue({ value: newProps.selectedValue });
29
+ shouldRender = true;
30
+ }
31
+
32
+ if (shouldRender) {
41
33
  render();
42
34
  }
43
35
  }
44
36
 
45
37
  export const handleButtonClick = (deps, payload) => {
46
38
  const { store, render, refs, props } = deps;
39
+ if (props.disabled) return;
47
40
  const event = payload._event;
48
41
  event.stopPropagation();
49
42
 
@@ -80,6 +73,7 @@ export const handleClickOptionsPopoverOverlay = (deps) => {
80
73
 
81
74
  export const handleOptionClick = (deps, payload) => {
82
75
  const { render, dispatchEvent, props, store } = deps;
76
+ if (props.disabled) return;
83
77
  const event = payload._event;
84
78
  event.stopPropagation();
85
79
  const id = event.currentTarget.id.slice('option'.length);
@@ -124,6 +118,7 @@ export const handleOptionMouseLeave = (deps, payload) => {
124
118
 
125
119
  export const handleClearClick = (deps, payload) => {
126
120
  const { store, render, dispatchEvent, props } = deps;
121
+ if (props.disabled) return;
127
122
  const event = payload._event;
128
123
 
129
124
  event.stopPropagation();
@@ -150,6 +145,7 @@ export const handleClearClick = (deps, payload) => {
150
145
  }
151
146
 
152
147
  export const handleAddOptionClick = (deps, payload) => {
148
+ if (deps.props.disabled) return;
153
149
  const { store, render, dispatchEvent } = deps;
154
150
  const { _event: event } = payload;
155
151
  event.stopPropagation();
@@ -26,6 +26,8 @@ propsSchema:
26
26
  properties:
27
27
  label:
28
28
  type: string
29
+ disabled:
30
+ type: boolean
29
31
  w:
30
32
  type: string
31
33
  events:
@@ -14,6 +14,7 @@ const blacklistedProps = [
14
14
  "options",
15
15
  "noClear",
16
16
  "addOption",
17
+ "disabled",
17
18
  ];
18
19
 
19
20
  const stringifyProps = (props = {}) => {
@@ -37,9 +38,11 @@ export const createInitialState = () => Object.freeze({
37
38
  export const selectViewData = ({ state, props }) => {
38
39
  // Generate container attribute string
39
40
  const containerAttrString = stringifyProps(props);
41
+ const isDisabled = !!props.disabled;
40
42
 
41
- // Use state's selected value if available, otherwise use props.selectedValue
42
- const currentValue = state.selectedValue !== null ? state.selectedValue : props.selectedValue;
43
+ // Treat selectedValue as a controlled prop when provided by parent.
44
+ const hasControlledValue = Object.prototype.hasOwnProperty.call(props || {}, "selectedValue");
45
+ const currentValue = hasControlledValue ? props.selectedValue : state.selectedValue;
43
46
 
44
47
  // Calculate display label from value
45
48
  let displayLabel = props.placeholder || "Select an option";
@@ -65,6 +68,7 @@ export const selectViewData = ({ state, props }) => {
65
68
 
66
69
  return {
67
70
  containerAttrString,
71
+ isDisabled,
68
72
  isOpen: state.isOpen,
69
73
  position: state.position,
70
74
  options: optionsWithSelection,
@@ -72,8 +76,8 @@ export const selectViewData = ({ state, props }) => {
72
76
  selectedLabel: displayLabel,
73
77
  selectedLabelColor: isPlaceholderLabel ? "mu-fg" : "fg",
74
78
  hasValue: currentValue !== null && currentValue !== undefined,
75
- showClear: !props.noClear && (currentValue !== null && currentValue !== undefined),
76
- showAddOption: !!props.addOption,
79
+ showClear: !isDisabled && !props.noClear && (currentValue !== null && currentValue !== undefined),
80
+ showAddOption: !isDisabled && !!props.addOption,
77
81
  addOptionLabel: props.addOption?.label ? `+ ${props.addOption.label}` : "+ Add",
78
82
  addOptionBgc: state.hoveredAddOption ? "ac" : "",
79
83
  };
@@ -125,5 +129,3 @@ export const clearSelectedValue = ({ state }) => {
125
129
  export const setHoveredAddOption = ({ state }, payload = {}) => {
126
130
  state.hoveredAddOption = !!payload.isHovered;
127
131
  };
128
-
129
-
@@ -28,9 +28,9 @@ refs:
28
28
  mouseleave:
29
29
  handler: handleAddOptionMouseLeave
30
30
  template:
31
- - rtgl-button#selectButton v=ol ${containerAttrString} data-testid="select-button":
32
- - rtgl-view d=h av=c w=f:
33
- - rtgl-text w=1fg c=${selectedLabelColor} ellipsis: ${selectedLabel}
31
+ - rtgl-button#selectButton v=ol ${containerAttrString} ?disabled=${isDisabled} data-testid="select-button":
32
+ - rtgl-view d=h av=c ah=s w=f:
33
+ - rtgl-text w=1fg ta=s c=${selectedLabelColor} ellipsis: ${selectedLabel}
34
34
  - $if showClear:
35
35
  - rtgl-svg#clearButton ml=md svg=x wh=16 c=mu-fg cur=pointer data-testid="select-clear-button": null
36
36
  - rtgl-svg ml=md svg=chevronDown wh=16 c=mu-fg: null
@@ -38,7 +38,7 @@ template:
38
38
  - rtgl-view wh=300 g=xs slot=content bgc=mu br=md sv=true:
39
39
  - $for option, i in options:
40
40
  - rtgl-view#option${i} w=f ph=lg pv=md cur=pointer br=md bgc=${option.bgc} data-testid=${option.testId}:
41
- - rtgl-text: ${option.label}
41
+ - rtgl-text ta=s: ${option.label}
42
42
  - $if showAddOption:
43
43
  - rtgl-view w=f bw=xs bc=mu bwt=sm: null
44
44
  - rtgl-view#optionAdd w=f ph=lg pv=md cur=pointer br=md bgc=${addOptionBgc} data-testid="select-add-option":
@@ -6,8 +6,10 @@ export const handleBeforeMount = (deps) => {
6
6
  export const handleOnUpdate = (deps, payload) => {
7
7
  const { oldProps, newProps } = payload;
8
8
  const { store, render } = deps;
9
+ const keyChanged = oldProps?.key !== newProps?.key;
10
+ const valueChanged = oldProps?.value !== newProps?.value;
9
11
 
10
- if (oldProps?.value !== newProps?.value) {
12
+ if (keyChanged || valueChanged) {
11
13
  const value = newProps?.value ?? 0;
12
14
  store.setValue({ value });
13
15
  render();
@@ -18,8 +20,14 @@ export const handleValueChange = (deps, payload) => {
18
20
  const { store, render, dispatchEvent } = deps;
19
21
  const event = payload._event;
20
22
  const newValue = Number(event.detail.value);
23
+ const path = typeof event.composedPath === "function" ? event.composedPath() : [];
24
+ const host = path.find((node) => node?.tagName === "RTGL-SLIDER-INPUT")
25
+ || event.currentTarget?.getRootNode?.()?.host;
21
26
 
22
27
  store.setValue({ value: newValue });
28
+ if (host && typeof host.setAttribute === "function") {
29
+ host.setAttribute("value", String(newValue));
30
+ }
23
31
 
24
32
  // Re-render to sync slider and input
25
33
  render();
@@ -39,8 +47,14 @@ export const handleValueInput = (deps, payload) => {
39
47
  const { store, render, dispatchEvent } = deps;
40
48
  const event = payload._event;
41
49
  const newValue = Number(event.detail.value);
50
+ const path = typeof event.composedPath === "function" ? event.composedPath() : [];
51
+ const host = path.find((node) => node?.tagName === "RTGL-SLIDER-INPUT")
52
+ || event.currentTarget?.getRootNode?.()?.host;
42
53
 
43
54
  store.setValue({ value: newValue });
55
+ if (host && typeof host.setAttribute === "function") {
56
+ host.setAttribute("value", String(newValue));
57
+ }
44
58
 
45
59
  // Re-render to sync slider and input
46
60
  render();
@@ -8,6 +8,7 @@ import RettangoliInputNumber from './primitives/input-number.js';
8
8
  import RettangoliTextArea from './primitives/textarea.js';
9
9
  import RettangoliColorPicker from './primitives/colorPicker.js';
10
10
  import RettangoliSlider from './primitives/slider.js';
11
+ import RettangoliCheckbox from './primitives/checkbox.js';
11
12
  import RettangoliDialog from './primitives/dialog.js';
12
13
  import RettangoliPopover from './primitives/popover.js';
13
14
 
@@ -21,6 +22,7 @@ customElements.define("rtgl-input-number", RettangoliInputNumber({}));
21
22
  customElements.define("rtgl-textarea", RettangoliTextArea({}));
22
23
  customElements.define("rtgl-color-picker", RettangoliColorPicker({}));
23
24
  customElements.define("rtgl-slider", RettangoliSlider({}));
25
+ customElements.define("rtgl-checkbox", RettangoliCheckbox({}));
24
26
  customElements.define("rtgl-dialog", RettangoliDialog({}));
25
27
  customElements.define("rtgl-popover", RettangoliPopover({}));
26
28
 
package/src/index.js CHANGED
@@ -10,6 +10,7 @@ import RettangoliDialog from './primitives/dialog.js';
10
10
  import RettangoliPopover from './primitives/popover.js';
11
11
  import RettangoliColorPicker from './primitives/colorPicker.js';
12
12
  import RettangoliSlider from './primitives/slider.js';
13
+ import RettangoliCheckbox from './primitives/checkbox.js';
13
14
  import createGlobalUI from './deps/createGlobalUI.js';
14
15
 
15
16
  export {
@@ -25,5 +26,6 @@ export {
25
26
  RettangoliPopover,
26
27
  RettangoliColorPicker,
27
28
  RettangoliSlider,
29
+ RettangoliCheckbox,
28
30
  createGlobalUI,
29
31
  }