@rettangoli/ui 1.7.0 → 1.7.2
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 +1 -1
- package/dist/rettangoli-iife-ui.min.js +50 -50
- package/package.json +1 -1
- package/src/components/tagSelect/tagSelect.handlers.js +5 -1
- package/src/components/tagSelect/tagSelect.methods.js +97 -4
- package/src/components/tagSelect/tagSelect.schema.yaml +18 -1
- package/src/components/tagSelect/tagSelect.store.js +21 -58
- package/src/components/tagSelect/tagSelect.view.yaml +4 -7
- package/src/primitives/popover.js +2 -1
package/package.json
CHANGED
|
@@ -75,7 +75,11 @@ export const handleOnUpdate = (deps, payload) => {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (oldProps.options !== newProps.options) {
|
|
78
|
-
|
|
78
|
+
const hasCurrentValues = resolveCurrentValues({ store, props: newProps }).length > 0;
|
|
79
|
+
|
|
80
|
+
if (store.getState().isOpen || hasCurrentValues) {
|
|
81
|
+
shouldRender = true;
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
if (shouldRender) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { deepEqual } from "../../common.js";
|
|
2
|
+
|
|
1
3
|
const resolvePopoverPosition = (trigger) => {
|
|
2
4
|
if (!trigger || typeof trigger.getBoundingClientRect !== "function") {
|
|
3
5
|
return null;
|
|
@@ -11,12 +13,66 @@ const resolvePopoverPosition = (trigger) => {
|
|
|
11
13
|
};
|
|
12
14
|
};
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
const normalizeSelectedValues = (selectedValues) => {
|
|
17
|
+
if (!Array.isArray(selectedValues)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return [...selectedValues];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const renderInstance = (instance) => {
|
|
25
|
+
if (typeof instance.render === "function") {
|
|
26
|
+
instance.render();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const resolveCurrentValues = (instance) => {
|
|
31
|
+
if (instance.store?.selectHasSelectedValues?.()) {
|
|
32
|
+
return instance.store?.selectSelectedValues?.() || [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return normalizeSelectedValues(instance.props?.selectedValues);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const openPopoverWithDraft = (instance, values = []) => {
|
|
39
|
+
const position = resolvePopoverPosition(instance.refs?.trigger);
|
|
40
|
+
|
|
41
|
+
if (!position || !instance.store?.openOptionsPopover) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
instance.store.openOptionsPopover({
|
|
46
|
+
position,
|
|
47
|
+
values,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const setDraftValues = (instance, values = [], keepOpen = false) => {
|
|
54
|
+
const state = instance.store?.getState?.();
|
|
55
|
+
|
|
56
|
+
if (state?.isOpen && instance.store?.updateDraftSelectedValues) {
|
|
57
|
+
instance.store.updateDraftSelectedValues({ values });
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (keepOpen) {
|
|
62
|
+
return openPopoverWithDraft(instance, values);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return false;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const refreshPopover = function (payload = {}) {
|
|
15
69
|
const state = this.store?.getState?.();
|
|
70
|
+
const draftValues = Array.isArray(payload.values)
|
|
71
|
+
? normalizeSelectedValues(payload.values)
|
|
72
|
+
: (this.store?.selectDraftSelectedValues?.() || []);
|
|
16
73
|
|
|
17
74
|
if (state?.isOpen) {
|
|
18
75
|
const position = resolvePopoverPosition(this.refs?.trigger);
|
|
19
|
-
const draftValues = this.store?.selectDraftSelectedValues?.() || [];
|
|
20
76
|
|
|
21
77
|
if (position) {
|
|
22
78
|
this.store.openOptionsPopover({
|
|
@@ -24,9 +80,46 @@ export const refreshPopover = function () {
|
|
|
24
80
|
values: draftValues,
|
|
25
81
|
});
|
|
26
82
|
}
|
|
83
|
+
} else if (payload.keepOpen) {
|
|
84
|
+
openPopoverWithDraft(this, draftValues.length > 0 ? draftValues : resolveCurrentValues(this));
|
|
27
85
|
}
|
|
28
86
|
|
|
29
|
-
|
|
30
|
-
|
|
87
|
+
renderInstance(this);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const setDraftSelectedValues = function (payload = {}) {
|
|
91
|
+
const values = normalizeSelectedValues(payload.values);
|
|
92
|
+
|
|
93
|
+
if (!setDraftValues(this, values, !!payload.keepOpen)) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
renderInstance(this);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const appendDraftSelectedValue = function (payload = {}) {
|
|
101
|
+
if (!Object.prototype.hasOwnProperty.call(payload || {}, "value")) {
|
|
102
|
+
return;
|
|
31
103
|
}
|
|
104
|
+
|
|
105
|
+
const state = this.store?.getState?.();
|
|
106
|
+
const currentValues = state?.isOpen
|
|
107
|
+
? (this.store?.selectDraftSelectedValues?.() || [])
|
|
108
|
+
: resolveCurrentValues(this);
|
|
109
|
+
|
|
110
|
+
if (currentValues.some((currentValue) => deepEqual(currentValue, payload.value))) {
|
|
111
|
+
if (!!payload.keepOpen && !state?.isOpen && openPopoverWithDraft(this, currentValues)) {
|
|
112
|
+
renderInstance(this);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const nextValues = [...normalizeSelectedValues(currentValues), payload.value];
|
|
119
|
+
|
|
120
|
+
if (!setDraftValues(this, nextValues, !!payload.keepOpen)) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
renderInstance(this);
|
|
32
125
|
};
|
|
@@ -48,4 +48,21 @@ events:
|
|
|
48
48
|
methods:
|
|
49
49
|
type: object
|
|
50
50
|
properties:
|
|
51
|
-
refreshPopover:
|
|
51
|
+
refreshPopover:
|
|
52
|
+
description: Recomputes the open popover position and rerenders the current options and draft values.
|
|
53
|
+
params:
|
|
54
|
+
- values
|
|
55
|
+
- keepOpen
|
|
56
|
+
returns: void
|
|
57
|
+
setDraftSelectedValues:
|
|
58
|
+
description: Replaces the draft-only selection without committing `selectedValues`; optionally reopens the popover around the trigger when `keepOpen` is true.
|
|
59
|
+
params:
|
|
60
|
+
- values
|
|
61
|
+
- keepOpen
|
|
62
|
+
returns: void
|
|
63
|
+
appendDraftSelectedValue:
|
|
64
|
+
description: Adds a value to the draft-only selection without committing `selectedValues`; optionally reopens the popover around the trigger when `keepOpen` is true.
|
|
65
|
+
params:
|
|
66
|
+
- value
|
|
67
|
+
- keepOpen
|
|
68
|
+
returns: void
|
|
@@ -68,26 +68,6 @@ const sameValueArray = (left = [], right = []) => {
|
|
|
68
68
|
return left.every((value, index) => deepEqual(value, right[index]));
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
const stringifyKeyPart = (value) => {
|
|
72
|
-
if (value === undefined) {
|
|
73
|
-
return "undefined";
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (value === null) {
|
|
77
|
-
return "null";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (typeof value === "string") {
|
|
81
|
-
return value;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
return JSON.stringify(value);
|
|
86
|
-
} catch {
|
|
87
|
-
return String(value);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
|
|
91
71
|
const isSelectedValue = (selectedValues = [], candidate) => {
|
|
92
72
|
return selectedValues.some((value) => deepEqual(value, candidate));
|
|
93
73
|
};
|
|
@@ -133,37 +113,6 @@ const buildTagStyle = ({ isSelected = true, isAddChip = false } = {}) => {
|
|
|
133
113
|
return `${baseStyle.join("; ")};`;
|
|
134
114
|
};
|
|
135
115
|
|
|
136
|
-
const buildPopoverSignature = (options = []) => {
|
|
137
|
-
if (!Array.isArray(options) || options.length === 0) {
|
|
138
|
-
return "empty";
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return options.map((option, index) => {
|
|
142
|
-
const type = getOptionType(option);
|
|
143
|
-
|
|
144
|
-
if (type === "section") {
|
|
145
|
-
return `section:${index}:${option.label || ""}`;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (type === "separator") {
|
|
149
|
-
return `separator:${index}`;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return `item:${index}:${option.label || ""}:${stringifyKeyPart(option.value)}`;
|
|
153
|
-
}).join("|");
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const buildPopoverKey = (options = []) => {
|
|
157
|
-
const signature = buildPopoverSignature(options);
|
|
158
|
-
let hash = 0;
|
|
159
|
-
|
|
160
|
-
for (let index = 0; index < signature.length; index += 1) {
|
|
161
|
-
hash = ((hash << 5) - hash + signature.charCodeAt(index)) >>> 0;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return `tagSelectPopover${hash}`;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
116
|
const normalizeOption = ({
|
|
168
117
|
option = {},
|
|
169
118
|
index,
|
|
@@ -227,7 +176,8 @@ export const selectViewData = ({ state, props }) => {
|
|
|
227
176
|
const isDisabled = !!props.disabled;
|
|
228
177
|
const currentValues = getCurrentValues({ state, props });
|
|
229
178
|
const draftValues = getDraftValues({ state, props });
|
|
230
|
-
const
|
|
179
|
+
const shouldResolveOptions = state.isOpen || currentValues.length > 0;
|
|
180
|
+
const options = shouldResolveOptions && Array.isArray(props.options) ? props.options : [];
|
|
231
181
|
const hasIconColumn = options.some((option) => isSelectableOption(option) && hasOwnProp(option, "icon"));
|
|
232
182
|
const normalizedOptions = options.map((option, index) =>
|
|
233
183
|
normalizeOption({
|
|
@@ -254,6 +204,19 @@ export const selectViewData = ({ state, props }) => {
|
|
|
254
204
|
|
|
255
205
|
const hasSelectableOptions = normalizedOptions.some((option) => option.isItem);
|
|
256
206
|
const hasDraftChanges = !sameValueArray(currentValues, draftValues);
|
|
207
|
+
const triggerTags = selectedTags.length > 0
|
|
208
|
+
? selectedTags.map((tag) => ({
|
|
209
|
+
...tag,
|
|
210
|
+
tagStyle: buildTagStyle({ isSelected: true }),
|
|
211
|
+
}))
|
|
212
|
+
: [{
|
|
213
|
+
value: undefined,
|
|
214
|
+
selectionIndex: "",
|
|
215
|
+
label: props.placeholder || "Add tag",
|
|
216
|
+
icon: "",
|
|
217
|
+
testId: "",
|
|
218
|
+
tagStyle: buildTagStyle({ isAddChip: true }),
|
|
219
|
+
}];
|
|
257
220
|
|
|
258
221
|
return {
|
|
259
222
|
containerAttrString,
|
|
@@ -262,18 +225,14 @@ export const selectViewData = ({ state, props }) => {
|
|
|
262
225
|
position: state.position,
|
|
263
226
|
options: normalizedOptions,
|
|
264
227
|
hasSelectableOptions,
|
|
265
|
-
popoverKey: buildPopoverKey(options),
|
|
266
228
|
placeholder: props.placeholder || "Add tag",
|
|
267
|
-
|
|
268
|
-
hasSelectedTags: selectedTags.length > 0,
|
|
269
|
-
triggerTagStyle: buildTagStyle({ isSelected: true }),
|
|
270
|
-
placeholderTagStyle: buildTagStyle({ isAddChip: true }),
|
|
229
|
+
triggerTags,
|
|
271
230
|
triggerCursor: isDisabled ? "not-allowed" : "pointer",
|
|
272
231
|
triggerTabIndex: isDisabled ? -1 : 0,
|
|
273
232
|
showAddOption: true,
|
|
274
233
|
addOptionLabel: props.addOption?.label || "Add tag",
|
|
275
234
|
hasDraftChanges,
|
|
276
|
-
submitDisabled: isDisabled
|
|
235
|
+
submitDisabled: isDisabled,
|
|
277
236
|
submitLabel: "Save",
|
|
278
237
|
};
|
|
279
238
|
};
|
|
@@ -305,6 +264,10 @@ export const closeOptionsPopover = ({ state }) => {
|
|
|
305
264
|
state.draftSelectedValues = [];
|
|
306
265
|
};
|
|
307
266
|
|
|
267
|
+
export const updateDraftSelectedValues = ({ state }, payload = {}) => {
|
|
268
|
+
state.draftSelectedValues = normalizeSelectedValues(payload.values);
|
|
269
|
+
};
|
|
270
|
+
|
|
308
271
|
export const updateSelectedValues = ({ state }, payload = {}) => {
|
|
309
272
|
const values = normalizeSelectedValues(payload.values);
|
|
310
273
|
state.selectedValues = values;
|
|
@@ -31,14 +31,11 @@ styles:
|
|
|
31
31
|
template:
|
|
32
32
|
- 'rtgl-view#trigger d=h av=c g=sm cur=${triggerCursor} ${containerAttrString} data-testid="tag-select-trigger" role="button" tabindex=${triggerTabIndex} aria-disabled=${isDisabled} style="min-height: 24px;"':
|
|
33
33
|
- rtgl-view d=h av=c wrap g=sm w=1fg:
|
|
34
|
-
- $
|
|
35
|
-
- 'rtgl-tag v=mu style="${
|
|
36
|
-
${placeholder}
|
|
37
|
-
- $for tag, i in selectedTags:
|
|
38
|
-
- 'rtgl-tag#tag${tag.selectionIndex} data-selection-index=${tag.selectionIndex} pre=${tag.icon} data-testid=${tag.testId} v=mu style="${triggerTagStyle}"':
|
|
34
|
+
- $for tag, i in triggerTags:
|
|
35
|
+
- 'rtgl-tag data-selection-index=${tag.selectionIndex} pre=${tag.icon} data-testid=${tag.testId} v=mu style="${tag.tagStyle}"':
|
|
39
36
|
${tag.label}
|
|
40
|
-
-
|
|
41
|
-
- rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y} place=bs content-w=${position.w} content-g=sm content-sv=true content-pv=
|
|
37
|
+
- $if isOpen:
|
|
38
|
+
- rtgl-popover#popover ?open=${isOpen} x=${position.x} y=${position.y} place=bs content-w=${position.w} content-g=sm content-sv=true content-ph=md content-pv=md:
|
|
42
39
|
- $if !hasSelectableOptions:
|
|
43
40
|
- rtgl-text s=sm c=mu-fg: No tags available
|
|
44
41
|
- rtgl-view d=h wrap g=sm w=f:
|
|
@@ -136,6 +136,7 @@ class RettangoliPopoverElement extends HTMLElement {
|
|
|
136
136
|
"content-wh",
|
|
137
137
|
"content-g",
|
|
138
138
|
"content-sv",
|
|
139
|
+
"content-ph",
|
|
139
140
|
"content-pv",
|
|
140
141
|
"content-bgc",
|
|
141
142
|
"content-style",
|
|
@@ -232,7 +233,7 @@ class RettangoliPopoverElement extends HTMLElement {
|
|
|
232
233
|
}
|
|
233
234
|
|
|
234
235
|
wrapper.setAttribute("bgc", this.getAttribute("content-bgc") || "su");
|
|
235
|
-
wrapper.setAttribute("ph", "sm");
|
|
236
|
+
wrapper.setAttribute("ph", this.getAttribute("content-ph") || "sm");
|
|
236
237
|
wrapper.setAttribute("pv", this.getAttribute("content-pv") || "sm");
|
|
237
238
|
|
|
238
239
|
const contentStyle = this.getAttribute("content-style");
|