@rettangoli/ui 1.7.2 → 1.7.3

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": "1.7.2",
3
+ "version": "1.7.3",
4
4
  "description": "A UI component library for building web interfaces.",
5
5
  "main": "dist/rettangoli-esm.min.js",
6
6
  "type": "module",
@@ -18,6 +18,19 @@ const normalizeSelectedValues = (selectedValues) => {
18
18
  return [...selectedValues];
19
19
  };
20
20
 
21
+ const resolvePopoverPosition = (trigger) => {
22
+ if (!trigger || typeof trigger.getBoundingClientRect !== "function") {
23
+ return undefined;
24
+ }
25
+
26
+ const rect = trigger.getBoundingClientRect();
27
+ return {
28
+ x: Math.round(rect.left),
29
+ y: Math.round(rect.bottom + 12),
30
+ w: Math.max(Math.round(rect.width), 240),
31
+ };
32
+ };
33
+
21
34
  const resolveCurrentValues = ({ store, props }) => {
22
35
  if (store.selectHasSelectedValues()) {
23
36
  return store.selectSelectedValues();
@@ -26,6 +39,18 @@ const resolveCurrentValues = ({ store, props }) => {
26
39
  return normalizeSelectedValues(props.selectedValues);
27
40
  };
28
41
 
42
+ const resolveDraftValues = ({ store, props }) => {
43
+ if (Array.isArray(props?.draftSelectedValues)) {
44
+ return normalizeSelectedValues(props.draftSelectedValues);
45
+ }
46
+
47
+ if (store.getState().isOpen) {
48
+ return store.selectDraftSelectedValues();
49
+ }
50
+
51
+ return resolveCurrentValues({ store, props });
52
+ };
53
+
29
54
  const emitValueChange = ({
30
55
  dispatchEvent,
31
56
  value,
@@ -50,30 +75,148 @@ const emitValueChange = ({
50
75
  );
51
76
  };
52
77
 
78
+ const emitDraftValueChange = ({ dispatchEvent, value }) => {
79
+ dispatchEvent(
80
+ new CustomEvent("draft-value-change", {
81
+ detail: {
82
+ value,
83
+ },
84
+ bubbles: true,
85
+ }),
86
+ );
87
+ };
88
+
89
+ const emitOpenChange = ({ dispatchEvent, open }) => {
90
+ dispatchEvent(
91
+ new CustomEvent("open-change", {
92
+ detail: {
93
+ open,
94
+ },
95
+ bubbles: true,
96
+ }),
97
+ );
98
+ };
99
+
100
+ const openControlledPopover = ({ store, props, refs } = {}) => {
101
+ const position = resolvePopoverPosition(refs?.trigger);
102
+ if (!position) {
103
+ return false;
104
+ }
105
+
106
+ store.openOptionsPopover({
107
+ position,
108
+ values: resolveDraftValues({ store, props }),
109
+ });
110
+
111
+ return true;
112
+ };
113
+
53
114
  export const handleBeforeMount = (deps) => {
54
- const { store, props, render } = deps;
115
+ const { store, props, render, refs } = deps;
116
+ let shouldRender = false;
55
117
 
56
118
  if (props.selectedValues !== undefined) {
57
- store.updateSelectedValues({ values: props.selectedValues });
119
+ store.updateSelectedValues({
120
+ values: props.selectedValues,
121
+ syncDraft: props.draftSelectedValues === undefined,
122
+ preserveDraft: Array.isArray(props.draftSelectedValues),
123
+ });
124
+ shouldRender = true;
125
+ }
126
+
127
+ if (Array.isArray(props.draftSelectedValues)) {
128
+ store.updateDraftSelectedValues({
129
+ values: props.draftSelectedValues,
130
+ });
131
+ shouldRender = true;
132
+ }
133
+
134
+ if (props.open === true && !props.disabled && openControlledPopover(deps)) {
135
+ shouldRender = true;
136
+ }
137
+
138
+ if (shouldRender) {
58
139
  render();
59
140
  }
60
141
  };
61
142
 
143
+ export const handleAfterMount = (deps) => {
144
+ const { props, render, store, dispatchEvent } = deps;
145
+
146
+ if (props.disabled) {
147
+ if (props.open === true) {
148
+ emitOpenChange({
149
+ dispatchEvent,
150
+ open: false,
151
+ });
152
+ }
153
+ return;
154
+ }
155
+
156
+ if (props.open === true && !store.getState().isOpen) {
157
+ if (openControlledPopover(deps)) {
158
+ render();
159
+ }
160
+ }
161
+ };
162
+
62
163
  export const handleOnUpdate = (deps, payload) => {
63
164
  const { oldProps, newProps } = payload;
64
- const { store, render } = deps;
165
+ const { store, render, refs } = deps;
65
166
  let shouldRender = false;
66
167
 
67
168
  if (!!newProps?.disabled && !oldProps?.disabled) {
169
+ const wasOpen = store.getState().isOpen;
68
170
  store.closeOptionsPopover({});
171
+ if (wasOpen) {
172
+ emitOpenChange({
173
+ dispatchEvent: deps.dispatchEvent,
174
+ open: false,
175
+ });
176
+ }
69
177
  shouldRender = true;
70
178
  }
71
179
 
72
180
  if (oldProps.selectedValues !== newProps.selectedValues) {
73
- store.updateSelectedValues({ values: newProps.selectedValues, syncDraft: true });
181
+ store.updateSelectedValues({
182
+ values: newProps.selectedValues,
183
+ syncDraft: newProps.draftSelectedValues === undefined,
184
+ preserveDraft: Array.isArray(newProps.draftSelectedValues),
185
+ });
74
186
  shouldRender = true;
75
187
  }
76
188
 
189
+ if (oldProps.draftSelectedValues !== newProps.draftSelectedValues) {
190
+ store.updateDraftSelectedValues({
191
+ values: Array.isArray(newProps?.draftSelectedValues)
192
+ ? newProps.draftSelectedValues
193
+ : resolveCurrentValues({ store, props: newProps }),
194
+ });
195
+ shouldRender = true;
196
+ }
197
+
198
+ if (oldProps.open !== newProps.open && newProps.open !== undefined) {
199
+ if (newProps.open) {
200
+ if (newProps.disabled) {
201
+ emitOpenChange({
202
+ dispatchEvent: deps.dispatchEvent,
203
+ open: false,
204
+ });
205
+ } else if (
206
+ openControlledPopover({
207
+ store,
208
+ props: newProps,
209
+ refs,
210
+ })
211
+ ) {
212
+ shouldRender = true;
213
+ }
214
+ } else {
215
+ store.closeOptionsPopover({});
216
+ shouldRender = true;
217
+ }
218
+ }
219
+
77
220
  if (oldProps.options !== newProps.options) {
78
221
  const hasCurrentValues = resolveCurrentValues({ store, props: newProps }).length > 0;
79
222
 
@@ -88,23 +231,24 @@ export const handleOnUpdate = (deps, payload) => {
88
231
  };
89
232
 
90
233
  export const handleTriggerClick = (deps, payload) => {
91
- const { store, render, refs, props } = deps;
234
+ const { store, render, refs, props, dispatchEvent } = deps;
92
235
  if (props.disabled) return;
93
236
 
94
237
  const event = payload._event;
95
238
  event.stopPropagation();
96
239
 
97
- const trigger = refs.trigger;
98
- const rect = trigger.getBoundingClientRect();
99
- const currentValues = resolveCurrentValues({ store, props });
240
+ const position = resolvePopoverPosition(refs.trigger);
241
+ if (!position) {
242
+ return;
243
+ }
100
244
 
101
245
  store.openOptionsPopover({
102
- position: {
103
- x: Math.round(rect.left),
104
- y: Math.round(rect.bottom + 12),
105
- w: Math.max(Math.round(rect.width), 240),
106
- },
107
- values: currentValues,
246
+ position,
247
+ values: resolveDraftValues({ store, props }),
248
+ });
249
+ emitOpenChange({
250
+ dispatchEvent,
251
+ open: true,
108
252
  });
109
253
  render();
110
254
  };
@@ -120,13 +264,17 @@ export const handleTriggerKeyDown = (deps, payload) => {
120
264
  };
121
265
 
122
266
  export const handlePopoverClose = (deps) => {
123
- const { store, render } = deps;
267
+ const { store, render, dispatchEvent } = deps;
124
268
  store.closeOptionsPopover({});
269
+ emitOpenChange({
270
+ dispatchEvent,
271
+ open: false,
272
+ });
125
273
  render();
126
274
  };
127
275
 
128
276
  export const handleOptionClick = (deps, payload) => {
129
- const { render, props, store } = deps;
277
+ const { render, props, store, dispatchEvent } = deps;
130
278
  if (props.disabled) return;
131
279
 
132
280
  const event = payload._event;
@@ -141,6 +289,10 @@ export const handleOptionClick = (deps, payload) => {
141
289
  }
142
290
 
143
291
  store.toggleDraftSelectedValue({ value: option.value });
292
+ emitDraftValueChange({
293
+ dispatchEvent,
294
+ value: store.selectDraftSelectedValues(),
295
+ });
144
296
  render();
145
297
  };
146
298
 
@@ -179,6 +331,11 @@ export const handleSubmitClick = (deps, payload) => {
179
331
  item: undefined,
180
332
  });
181
333
 
334
+ emitOpenChange({
335
+ dispatchEvent,
336
+ open: false,
337
+ });
338
+
182
339
  render();
183
340
  };
184
341
 
@@ -31,6 +31,12 @@ propsSchema:
31
31
  type: array
32
32
  items:
33
33
  type: any
34
+ draftSelectedValues:
35
+ type: array
36
+ items:
37
+ type: any
38
+ open:
39
+ type: boolean
34
40
  onChange:
35
41
  type: function
36
42
  addOption:
@@ -44,6 +50,8 @@ propsSchema:
44
50
  type: string
45
51
  events:
46
52
  value-change: {}
53
+ draft-value-change: {}
54
+ open-change: {}
47
55
  add-option-click: {}
48
56
  methods:
49
57
  type: object
@@ -7,7 +7,9 @@ const blacklistedProps = [
7
7
  "slot",
8
8
  "placeholder",
9
9
  "options",
10
+ "open",
10
11
  "selectedValues",
12
+ "draftSelectedValues",
11
13
  "onChange",
12
14
  "addOption",
13
15
  "disabled",
@@ -272,7 +274,7 @@ export const updateSelectedValues = ({ state }, payload = {}) => {
272
274
  const values = normalizeSelectedValues(payload.values);
273
275
  state.selectedValues = values;
274
276
  state.hasSelectedValues = true;
275
- if (payload.syncDraft || !state.isOpen) {
277
+ if ((payload.syncDraft || !state.isOpen) && !payload.preserveDraft) {
276
278
  state.draftSelectedValues = [...values];
277
279
  }
278
280
  };
@@ -48,7 +48,7 @@ template:
48
48
  - 'rtgl-tag v=mu pre=${option.icon} style="${option.tagStyle}"':
49
49
  ${option.label}
50
50
  $elif option.isSeparator:
51
- - rtgl-view w=f h=1 bgc=mu my=xs: null
51
+ - rtgl-view w=f h=1 bgc=mu mv=xs: null
52
52
  - $if showAddOption:
53
53
  - rtgl-button#addOptionButton v=gh s=sm pre=plus data-testid="tag-select-add-option":
54
54
  ${addOptionLabel}