@rettangoli/ui 0.1.2-rc27 → 0.1.2-rc29
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/rettangoli-iife-layout.min.js +8 -8
- package/dist/rettangoli-iife-ui.min.js +41 -36
- package/package.json +1 -1
- package/src/components/form/form.handlers.js +39 -0
- package/src/components/form/form.store.js +23 -0
- package/src/components/form/form.view.yaml +77 -2
- package/src/components/select/select.handlers.js +26 -0
- package/src/components/select/select.store.js +9 -1
- package/src/components/select/select.view.yaml +17 -0
- package/src/components/tooltip/tooltip.handlers.js +0 -0
- package/src/components/tooltip/tooltip.store.js +12 -0
- package/src/components/tooltip/tooltip.view.yaml +27 -0
- package/src/primitives/button.js +10 -0
- package/src/primitives/popover.js +7 -2
package/package.json
CHANGED
|
@@ -186,3 +186,42 @@ export const handleWaveformClick = (e, deps) => {
|
|
|
186
186
|
}),
|
|
187
187
|
);
|
|
188
188
|
};
|
|
189
|
+
|
|
190
|
+
export const handleSelectAddOption = (e, deps) => {
|
|
191
|
+
const { store, dispatchEvent } = deps;
|
|
192
|
+
const name = e.currentTarget.id.replace("select-", "");
|
|
193
|
+
dispatchEvent(
|
|
194
|
+
new CustomEvent("action-click", {
|
|
195
|
+
detail: {
|
|
196
|
+
actionId: 'select-options-add',
|
|
197
|
+
name: name,
|
|
198
|
+
formValues: store.selectFormValues(),
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const handleTooltipMouseEnter = (e, deps) => {
|
|
205
|
+
const { store, render, props } = deps;
|
|
206
|
+
const fieldName = e.currentTarget.id.replace('tooltip-icon-', '');
|
|
207
|
+
|
|
208
|
+
// Find the field with matching name to get tooltip content
|
|
209
|
+
const form = props.form;
|
|
210
|
+
const field = form.fields.find(f => f.name === fieldName);
|
|
211
|
+
|
|
212
|
+
if (field && field.tooltip) {
|
|
213
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
214
|
+
store.showTooltip({
|
|
215
|
+
x: rect.left + rect.width / 2,
|
|
216
|
+
y: rect.top - 8,
|
|
217
|
+
content: field.tooltip.content
|
|
218
|
+
});
|
|
219
|
+
render();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const handleTooltipMouseLeave = (e, deps) => {
|
|
224
|
+
const { store, render } = deps;
|
|
225
|
+
store.hideTooltip();
|
|
226
|
+
render();
|
|
227
|
+
};
|
|
@@ -26,6 +26,12 @@ function pick(obj, keys) {
|
|
|
26
26
|
|
|
27
27
|
export const INITIAL_STATE = Object.freeze({
|
|
28
28
|
formValues: {},
|
|
29
|
+
tooltipState: {
|
|
30
|
+
open: false,
|
|
31
|
+
x: 0,
|
|
32
|
+
y: 0,
|
|
33
|
+
content: ''
|
|
34
|
+
},
|
|
29
35
|
});
|
|
30
36
|
|
|
31
37
|
// Lodash-like utility functions for nested property access
|
|
@@ -140,6 +146,7 @@ export const toViewData = ({ state, props, attrs }) => {
|
|
|
140
146
|
buttons: [],
|
|
141
147
|
},
|
|
142
148
|
formValues: state.formValues,
|
|
149
|
+
tooltipState: state.tooltipState,
|
|
143
150
|
};
|
|
144
151
|
};
|
|
145
152
|
|
|
@@ -174,3 +181,19 @@ export const setFormFieldValue = (state, { name, value, props }) => {
|
|
|
174
181
|
);
|
|
175
182
|
state.formValues = formValues;
|
|
176
183
|
};
|
|
184
|
+
|
|
185
|
+
export const showTooltip = (state, { x, y, content }) => {
|
|
186
|
+
state.tooltipState = {
|
|
187
|
+
open: true,
|
|
188
|
+
x: x,
|
|
189
|
+
y: y,
|
|
190
|
+
content: content
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const hideTooltip = (state) => {
|
|
195
|
+
state.tooltipState = {
|
|
196
|
+
...state.tooltipState,
|
|
197
|
+
open: false
|
|
198
|
+
};
|
|
199
|
+
};
|
|
@@ -33,6 +33,11 @@ propsSchema:
|
|
|
33
33
|
const: inputText
|
|
34
34
|
placeholder:
|
|
35
35
|
type: string
|
|
36
|
+
tooltip:
|
|
37
|
+
type: object
|
|
38
|
+
properties:
|
|
39
|
+
content:
|
|
40
|
+
type: string
|
|
36
41
|
required:
|
|
37
42
|
- name
|
|
38
43
|
- label
|
|
@@ -52,6 +57,11 @@ propsSchema:
|
|
|
52
57
|
type: string
|
|
53
58
|
noClear:
|
|
54
59
|
type: boolean
|
|
60
|
+
addOption:
|
|
61
|
+
type: object
|
|
62
|
+
properties:
|
|
63
|
+
label:
|
|
64
|
+
type: string
|
|
55
65
|
options:
|
|
56
66
|
type: array
|
|
57
67
|
items:
|
|
@@ -64,6 +74,11 @@ propsSchema:
|
|
|
64
74
|
required:
|
|
65
75
|
- label
|
|
66
76
|
- value
|
|
77
|
+
tooltip:
|
|
78
|
+
type: object
|
|
79
|
+
properties:
|
|
80
|
+
content:
|
|
81
|
+
type: string
|
|
67
82
|
required:
|
|
68
83
|
- name
|
|
69
84
|
- label
|
|
@@ -82,6 +97,11 @@ propsSchema:
|
|
|
82
97
|
const: colorPicker
|
|
83
98
|
value:
|
|
84
99
|
type: string
|
|
100
|
+
tooltip:
|
|
101
|
+
type: object
|
|
102
|
+
properties:
|
|
103
|
+
content:
|
|
104
|
+
type: string
|
|
85
105
|
required:
|
|
86
106
|
- name
|
|
87
107
|
- label
|
|
@@ -105,6 +125,11 @@ propsSchema:
|
|
|
105
125
|
type: number
|
|
106
126
|
value:
|
|
107
127
|
type: number
|
|
128
|
+
tooltip:
|
|
129
|
+
type: object
|
|
130
|
+
properties:
|
|
131
|
+
content:
|
|
132
|
+
type: string
|
|
108
133
|
required:
|
|
109
134
|
- name
|
|
110
135
|
- label
|
|
@@ -128,6 +153,11 @@ propsSchema:
|
|
|
128
153
|
type: number
|
|
129
154
|
value:
|
|
130
155
|
type: number
|
|
156
|
+
tooltip:
|
|
157
|
+
type: object
|
|
158
|
+
properties:
|
|
159
|
+
content:
|
|
160
|
+
type: string
|
|
131
161
|
required:
|
|
132
162
|
- name
|
|
133
163
|
- label
|
|
@@ -149,6 +179,11 @@ propsSchema:
|
|
|
149
179
|
type: number
|
|
150
180
|
placeholder:
|
|
151
181
|
type: string
|
|
182
|
+
tooltip:
|
|
183
|
+
type: object
|
|
184
|
+
properties:
|
|
185
|
+
content:
|
|
186
|
+
type: string
|
|
152
187
|
required:
|
|
153
188
|
- name
|
|
154
189
|
- label
|
|
@@ -174,6 +209,33 @@ propsSchema:
|
|
|
174
209
|
type: object
|
|
175
210
|
waveformData:
|
|
176
211
|
type: object
|
|
212
|
+
tooltip:
|
|
213
|
+
type: object
|
|
214
|
+
properties:
|
|
215
|
+
content:
|
|
216
|
+
type: string
|
|
217
|
+
required:
|
|
218
|
+
- name
|
|
219
|
+
- label
|
|
220
|
+
- inputType
|
|
221
|
+
additionalProperties: false
|
|
222
|
+
- type: object
|
|
223
|
+
properties:
|
|
224
|
+
name:
|
|
225
|
+
type: string
|
|
226
|
+
label:
|
|
227
|
+
type: string
|
|
228
|
+
description:
|
|
229
|
+
type: string
|
|
230
|
+
inputType:
|
|
231
|
+
const: popover-input
|
|
232
|
+
placeholder:
|
|
233
|
+
type: string
|
|
234
|
+
tooltip:
|
|
235
|
+
type: object
|
|
236
|
+
properties:
|
|
237
|
+
content:
|
|
238
|
+
type: string
|
|
177
239
|
required:
|
|
178
240
|
- name
|
|
179
241
|
- label
|
|
@@ -200,6 +262,12 @@ refs:
|
|
|
200
262
|
eventListeners:
|
|
201
263
|
click:
|
|
202
264
|
handler: handleActionClick
|
|
265
|
+
tooltip-icon-*:
|
|
266
|
+
eventListeners:
|
|
267
|
+
mouseenter:
|
|
268
|
+
handler: handleTooltipMouseEnter
|
|
269
|
+
mouseleave:
|
|
270
|
+
handler: handleTooltipMouseLeave
|
|
203
271
|
input-*:
|
|
204
272
|
eventListeners:
|
|
205
273
|
input-change:
|
|
@@ -208,6 +276,8 @@ refs:
|
|
|
208
276
|
eventListeners:
|
|
209
277
|
select-change:
|
|
210
278
|
handler: handleSelectChange
|
|
279
|
+
add-option-selected:
|
|
280
|
+
handler: handleSelectAddOption
|
|
211
281
|
colorpicker-*:
|
|
212
282
|
eventListeners:
|
|
213
283
|
colorpicker-change:
|
|
@@ -240,6 +310,7 @@ refs:
|
|
|
240
310
|
events:
|
|
241
311
|
form-change: {}
|
|
242
312
|
extra-event: {}
|
|
313
|
+
action-click: {}
|
|
243
314
|
|
|
244
315
|
template:
|
|
245
316
|
- rtgl-view w=f p=md g=lg ${containerAttrString}:
|
|
@@ -250,7 +321,10 @@ template:
|
|
|
250
321
|
- $for field, i in fields:
|
|
251
322
|
- rtgl-view g=md w=f:
|
|
252
323
|
- rtgl-view g=sm:
|
|
253
|
-
- rtgl-
|
|
324
|
+
- rtgl-view d=h g=md av=c:
|
|
325
|
+
- rtgl-text: ${field.label}
|
|
326
|
+
- $if field.tooltip:
|
|
327
|
+
- rtgl-svg#tooltip-icon-${field.name} svg="info" wh=16 c=mu-fg cur=help ml=xs:
|
|
254
328
|
- rtgl-text s=sm c=mu-fg: ${field.description}
|
|
255
329
|
- $if field.inputType == "read-only-text":
|
|
256
330
|
- rtgl-text s=sm: ${field.defaultValue}
|
|
@@ -259,7 +333,7 @@ template:
|
|
|
259
333
|
- $if field.inputType == "popover-input":
|
|
260
334
|
- rtgl-popover-input#popover-input-${field.name} label="${field.label}" .defaultValue=fields[${i}].defaultValue:
|
|
261
335
|
- $if field.inputType == "select":
|
|
262
|
-
- 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:
|
|
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:
|
|
263
337
|
- $if field.inputType == "colorPicker":
|
|
264
338
|
- rtgl-color-picker#colorpicker-${field.name} key=${key} value=${field.defaultValue}:
|
|
265
339
|
- $if field.inputType == "slider":
|
|
@@ -282,3 +356,4 @@ template:
|
|
|
282
356
|
- rtgl-view d=h ah=e g=sm w=f:
|
|
283
357
|
- $for button, i in actions.buttons:
|
|
284
358
|
- rtgl-button#action-${button.id}: ${button.content}
|
|
359
|
+
- rtgl-tooltip ?open=${tooltipState.open} x=${tooltipState.x} y=${tooltipState.y} placement="top" content="${tooltipState.content}":
|
|
@@ -138,3 +138,29 @@ export const handleClearClick = (e, deps) => {
|
|
|
138
138
|
|
|
139
139
|
render();
|
|
140
140
|
}
|
|
141
|
+
|
|
142
|
+
export const handleAddOptionClick = (e, deps) => {
|
|
143
|
+
const { store, render, dispatchEvent } = deps;
|
|
144
|
+
|
|
145
|
+
// Close the popover
|
|
146
|
+
store.closeOptionsPopover();
|
|
147
|
+
|
|
148
|
+
// Dispatch custom event for add option (no detail)
|
|
149
|
+
dispatchEvent(new CustomEvent('add-option-selected', {
|
|
150
|
+
bubbles: true
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
render();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const handleAddOptionMouseEnter = (e, deps) => {
|
|
157
|
+
const { store, render } = deps;
|
|
158
|
+
store.setHoveredAddOption(true);
|
|
159
|
+
render();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const handleAddOptionMouseLeave = (e, deps) => {
|
|
163
|
+
const { store, render } = deps;
|
|
164
|
+
store.setHoveredAddOption(false);
|
|
165
|
+
render();
|
|
166
|
+
}
|
|
@@ -31,6 +31,7 @@ export const INITIAL_STATE = Object.freeze({
|
|
|
31
31
|
},
|
|
32
32
|
selectedValue: null,
|
|
33
33
|
hoveredOptionId: null,
|
|
34
|
+
hoveredAddOption: false,
|
|
34
35
|
});
|
|
35
36
|
|
|
36
37
|
export const toViewData = ({ state, props, attrs }) => {
|
|
@@ -72,7 +73,10 @@ export const toViewData = ({ state, props, attrs }) => {
|
|
|
72
73
|
selectedLabelColor: isPlaceholderLabel ? "mu-fg" : "fg",
|
|
73
74
|
placeholder: props.placeholder || 'Select an option',
|
|
74
75
|
hasValue: currentValue !== null && currentValue !== undefined,
|
|
75
|
-
showClear: !attrs['no-clear'] && !props['no-clear'] && (currentValue !== null && currentValue !== undefined)
|
|
76
|
+
showClear: !attrs['no-clear'] && !props['no-clear'] && (currentValue !== null && currentValue !== undefined),
|
|
77
|
+
showAddOption: !!props.addOption,
|
|
78
|
+
addOptionLabel: props.addOption?.label ? `+ ${props.addOption.label}` : '+ Add',
|
|
79
|
+
addOptionBgc: state.hoveredAddOption ? 'ac' : ''
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -119,5 +123,9 @@ export const clearSelectedValue = (state) => {
|
|
|
119
123
|
state.selectedValue = undefined;
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
export const setHoveredAddOption = (state, isHovered) => {
|
|
127
|
+
state.hoveredAddOption = isHovered;
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
|
|
123
131
|
|
|
@@ -23,6 +23,11 @@ propsSchema:
|
|
|
23
23
|
type: function
|
|
24
24
|
no-clear:
|
|
25
25
|
type: boolean
|
|
26
|
+
addOption:
|
|
27
|
+
type: object
|
|
28
|
+
properties:
|
|
29
|
+
label:
|
|
30
|
+
type: string
|
|
26
31
|
|
|
27
32
|
refs:
|
|
28
33
|
select-button:
|
|
@@ -45,6 +50,14 @@ refs:
|
|
|
45
50
|
handler: handleOptionMouseEnter
|
|
46
51
|
mouseleave:
|
|
47
52
|
handler: handleOptionMouseLeave
|
|
53
|
+
option-add:
|
|
54
|
+
eventListeners:
|
|
55
|
+
click:
|
|
56
|
+
handler: handleAddOptionClick
|
|
57
|
+
mouseenter:
|
|
58
|
+
handler: handleAddOptionMouseEnter
|
|
59
|
+
mouseleave:
|
|
60
|
+
handler: handleAddOptionMouseLeave
|
|
48
61
|
|
|
49
62
|
events: {}
|
|
50
63
|
|
|
@@ -61,3 +74,7 @@ template:
|
|
|
61
74
|
- $for option, i in options:
|
|
62
75
|
- rtgl-view#option-${i} w=f ph=lg pv=md cur=p br=md bgc=${option.bgc}:
|
|
63
76
|
- rtgl-text: ${option.label}
|
|
77
|
+
- $if showAddOption:
|
|
78
|
+
- rtgl-view w=f bw=xs bc=mu-bg bt=sm:
|
|
79
|
+
- rtgl-view#option-add w=f ph=lg pv=md cur=p br=md bgc=${addOptionBgc}:
|
|
80
|
+
- rtgl-text c=ac: ${addOptionLabel}
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
elementName: rtgl-tooltip
|
|
2
|
+
|
|
3
|
+
viewDataSchema:
|
|
4
|
+
type: object
|
|
5
|
+
|
|
6
|
+
attrsSchema:
|
|
7
|
+
type: object
|
|
8
|
+
properties:
|
|
9
|
+
open:
|
|
10
|
+
type: string
|
|
11
|
+
x:
|
|
12
|
+
type: string
|
|
13
|
+
y:
|
|
14
|
+
type: string
|
|
15
|
+
placement:
|
|
16
|
+
type: string
|
|
17
|
+
content:
|
|
18
|
+
type: string
|
|
19
|
+
|
|
20
|
+
refs:
|
|
21
|
+
popover:
|
|
22
|
+
|
|
23
|
+
template:
|
|
24
|
+
- rtgl-popover#popover ?open=${open} x=${x} y=${y} placement=${placement} no-overlay:
|
|
25
|
+
- rtgl-view slot=content bgc=background bc=border br=md p=sm ah=c av=c:
|
|
26
|
+
- rtgl-text ta=c s=sm c=foreground: ${content}
|
|
27
|
+
|
package/src/primitives/button.js
CHANGED
|
@@ -296,6 +296,16 @@ class RettangoliButtonElement extends HTMLElement {
|
|
|
296
296
|
this._buttonElement.style.maxWidth = "";
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
+
|
|
300
|
+
// Public method to get the actual button's bounding rect
|
|
301
|
+
// This is needed because the host element has display: contents
|
|
302
|
+
getBoundingClientRect() {
|
|
303
|
+
if (this._buttonElement) {
|
|
304
|
+
return this._buttonElement.getBoundingClientRect();
|
|
305
|
+
}
|
|
306
|
+
// Fallback to host element
|
|
307
|
+
return super.getBoundingClientRect();
|
|
308
|
+
}
|
|
299
309
|
}
|
|
300
310
|
|
|
301
311
|
// Export factory function to maintain API compatibility
|
|
@@ -28,7 +28,7 @@ class RettangoliPopoverElement extends HTMLElement {
|
|
|
28
28
|
outline: none;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
:host([open]) .popover-overlay {
|
|
31
|
+
:host([open]:not([no-overlay])) .popover-overlay {
|
|
32
32
|
display: block;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -36,6 +36,11 @@ class RettangoliPopoverElement extends HTMLElement {
|
|
|
36
36
|
display: block;
|
|
37
37
|
visibility: hidden;
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/* For no-overlay mode, make the container non-interactive */
|
|
41
|
+
:host([no-overlay]) .popover-container {
|
|
42
|
+
pointer-events: none;
|
|
43
|
+
}
|
|
39
44
|
|
|
40
45
|
:host([open][positioned]) .popover-container {
|
|
41
46
|
visibility: visible;
|
|
@@ -96,7 +101,7 @@ class RettangoliPopoverElement extends HTMLElement {
|
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
static get observedAttributes() {
|
|
99
|
-
return ["open", "x", "y", "placement"];
|
|
104
|
+
return ["open", "x", "y", "placement", "no-overlay"];
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
connectedCallback() {
|