carbon-components-svelte 0.106.2 → 0.107.0
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 +1 -1
- package/src/Checkbox/CheckboxGroup.svelte +17 -3
- package/src/ContentSwitcher/ContentSwitcher.svelte +6 -1
- package/src/Portal/FloatingPortal.svelte +46 -19
- package/src/Portal/FloatingPortal.svelte.d.ts +9 -0
- package/src/Portal/Portal.svelte +13 -3
- package/src/Portal/Portal.svelte.d.ts +7 -0
- package/src/RadioButtonGroup/RadioButtonGroup.svelte +9 -1
package/package.json
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
createEventDispatcher,
|
|
56
56
|
onMount,
|
|
57
57
|
setContext,
|
|
58
|
+
tick,
|
|
58
59
|
} from "svelte";
|
|
59
60
|
import { readonly, writable } from "svelte/store";
|
|
60
61
|
|
|
@@ -67,6 +68,14 @@
|
|
|
67
68
|
const groupName = writable(name);
|
|
68
69
|
const groupRequired = writable(required);
|
|
69
70
|
const groupDisabled = writable(disabled);
|
|
71
|
+
let isInitialRender = true;
|
|
72
|
+
let isSyncingSelected = false;
|
|
73
|
+
|
|
74
|
+
function syncSelectedValues() {
|
|
75
|
+
isSyncingSelected = true;
|
|
76
|
+
$selectedValues = selected;
|
|
77
|
+
isSyncingSelected = false;
|
|
78
|
+
}
|
|
70
79
|
|
|
71
80
|
/**
|
|
72
81
|
* @type {(value: string | number, checked: boolean) => void}
|
|
@@ -89,16 +98,21 @@
|
|
|
89
98
|
});
|
|
90
99
|
|
|
91
100
|
onMount(() => {
|
|
92
|
-
|
|
101
|
+
syncSelectedValues();
|
|
102
|
+
tick().then(() => {
|
|
103
|
+
isInitialRender = false;
|
|
104
|
+
});
|
|
93
105
|
});
|
|
94
106
|
|
|
95
107
|
beforeUpdate(() => {
|
|
96
|
-
|
|
108
|
+
syncSelectedValues();
|
|
97
109
|
});
|
|
98
110
|
|
|
99
111
|
selectedValues.subscribe((value) => {
|
|
100
112
|
selected = value;
|
|
101
|
-
|
|
113
|
+
if (!isInitialRender && !isSyncingSelected) {
|
|
114
|
+
dispatch("change", value);
|
|
115
|
+
}
|
|
102
116
|
});
|
|
103
117
|
|
|
104
118
|
$: $groupName = name;
|
|
@@ -31,11 +31,16 @@
|
|
|
31
31
|
/** @type {HTMLDivElement | null} */
|
|
32
32
|
let refContainer = null;
|
|
33
33
|
|
|
34
|
+
let prevIndex = -1;
|
|
35
|
+
|
|
34
36
|
$: currentIndex = -1;
|
|
35
37
|
$: focusedIndex = -1;
|
|
36
38
|
$: switches = [];
|
|
37
39
|
$: if (switches[currentIndex]) {
|
|
38
|
-
|
|
40
|
+
if (prevIndex > -1 && prevIndex !== currentIndex) {
|
|
41
|
+
dispatch("change", currentIndex);
|
|
42
|
+
}
|
|
43
|
+
prevIndex = currentIndex;
|
|
39
44
|
currentId.set(switches[currentIndex].id);
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -90,6 +90,15 @@
|
|
|
90
90
|
*/
|
|
91
91
|
export let ref = null;
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Specify the DOM element to mount the portal into.
|
|
95
|
+
* When not set, mounts into the anchor's nearest `<dialog>` ancestor if one
|
|
96
|
+
* exists (so the portal participates in the dialog's top layer), otherwise
|
|
97
|
+
* falls back to `document.body`.
|
|
98
|
+
* @type {HTMLElement | null}
|
|
99
|
+
*/
|
|
100
|
+
export let target = null;
|
|
101
|
+
|
|
93
102
|
import { onMount, tick } from "svelte";
|
|
94
103
|
import Portal from "./Portal.svelte";
|
|
95
104
|
|
|
@@ -181,11 +190,29 @@
|
|
|
181
190
|
}
|
|
182
191
|
}
|
|
183
192
|
|
|
193
|
+
// Auto-detect the nearest <dialog> ancestor of the anchor so that portalled
|
|
194
|
+
// content participates in the dialog's top layer by default. An explicit
|
|
195
|
+
// `target` prop overrides this.
|
|
196
|
+
$: effectiveTarget = target ?? anchor?.closest("dialog") ?? null;
|
|
197
|
+
|
|
198
|
+
// When the portal is mounted into a custom target (e.g. a native <dialog>
|
|
199
|
+
// opened with showModal()), `position: absolute` resolves against the target's
|
|
200
|
+
// containing block rather than the viewport. Use `position: fixed` in that
|
|
201
|
+
// case — fixed stays viewport-relative even inside a top-layer dialog — and
|
|
202
|
+
// skip the document scroll offsets, which only apply to absolute positioning
|
|
203
|
+
// relative to `document.body`.
|
|
204
|
+
$: useFixedPosition =
|
|
205
|
+
effectiveTarget != null &&
|
|
206
|
+
typeof document !== "undefined" &&
|
|
207
|
+
effectiveTarget !== document.body;
|
|
208
|
+
|
|
184
209
|
function updatePosition() {
|
|
185
210
|
if (!mounted || !anchor || !ref) return;
|
|
186
211
|
|
|
187
212
|
const rect = anchor.getBoundingClientRect();
|
|
188
213
|
const floatingRect = ref.getBoundingClientRect();
|
|
214
|
+
const scrollYOffset = useFixedPosition ? 0 : window.scrollY;
|
|
215
|
+
const scrollXOffset = useFixedPosition ? 0 : window.scrollX;
|
|
189
216
|
|
|
190
217
|
const isVertical = direction === "top" || direction === "bottom";
|
|
191
218
|
let actualDirection = direction;
|
|
@@ -234,27 +261,27 @@
|
|
|
234
261
|
let width;
|
|
235
262
|
|
|
236
263
|
if (actualDirection === "bottom") {
|
|
237
|
-
top = rect.bottom +
|
|
238
|
-
left = rect.left +
|
|
264
|
+
top = rect.bottom + scrollYOffset + gapBottom;
|
|
265
|
+
left = rect.left + scrollXOffset;
|
|
239
266
|
width = rect.width;
|
|
240
267
|
} else if (actualDirection === "top") {
|
|
241
|
-
top = rect.top +
|
|
242
|
-
left = rect.left +
|
|
268
|
+
top = rect.top + scrollYOffset - floatingRect.height - gapTop;
|
|
269
|
+
left = rect.left + scrollXOffset;
|
|
243
270
|
width = rect.width;
|
|
244
271
|
} else if (actualDirection === "right") {
|
|
245
272
|
if (intrinsicWidth) {
|
|
246
273
|
if (intrinsicAlign === "start") {
|
|
247
|
-
top = rect.top +
|
|
274
|
+
top = rect.top + scrollYOffset + verticalAlignOffsetRight;
|
|
248
275
|
} else if (intrinsicAlign === "end") {
|
|
249
276
|
top =
|
|
250
277
|
rect.bottom +
|
|
251
|
-
|
|
278
|
+
scrollYOffset -
|
|
252
279
|
floatingRect.height +
|
|
253
280
|
verticalAlignOffsetRight;
|
|
254
281
|
} else {
|
|
255
282
|
top =
|
|
256
283
|
rect.top +
|
|
257
|
-
|
|
284
|
+
scrollYOffset +
|
|
258
285
|
rect.height / 2 -
|
|
259
286
|
floatingRect.height / 2 +
|
|
260
287
|
verticalAlignOffsetRight;
|
|
@@ -262,27 +289,27 @@
|
|
|
262
289
|
} else {
|
|
263
290
|
top =
|
|
264
291
|
rect.top +
|
|
265
|
-
|
|
292
|
+
scrollYOffset +
|
|
266
293
|
rect.height / 2 -
|
|
267
294
|
floatingRect.height / 2 +
|
|
268
295
|
verticalAlignOffsetRight;
|
|
269
296
|
}
|
|
270
|
-
left = rect.right +
|
|
297
|
+
left = rect.right + scrollXOffset + horizontalGapRight;
|
|
271
298
|
} else {
|
|
272
299
|
// left
|
|
273
300
|
if (intrinsicWidth) {
|
|
274
301
|
if (intrinsicAlign === "start") {
|
|
275
|
-
top = rect.top +
|
|
302
|
+
top = rect.top + scrollYOffset + verticalAlignOffsetLeft;
|
|
276
303
|
} else if (intrinsicAlign === "end") {
|
|
277
304
|
top =
|
|
278
305
|
rect.bottom +
|
|
279
|
-
|
|
306
|
+
scrollYOffset -
|
|
280
307
|
floatingRect.height +
|
|
281
308
|
verticalAlignOffsetLeft;
|
|
282
309
|
} else {
|
|
283
310
|
top =
|
|
284
311
|
rect.top +
|
|
285
|
-
|
|
312
|
+
scrollYOffset +
|
|
286
313
|
rect.height / 2 -
|
|
287
314
|
floatingRect.height / 2 +
|
|
288
315
|
verticalAlignOffsetLeft;
|
|
@@ -290,13 +317,12 @@
|
|
|
290
317
|
} else {
|
|
291
318
|
top =
|
|
292
319
|
rect.top +
|
|
293
|
-
|
|
320
|
+
scrollYOffset +
|
|
294
321
|
rect.height / 2 -
|
|
295
322
|
floatingRect.height / 2 +
|
|
296
323
|
verticalAlignOffsetLeft;
|
|
297
324
|
}
|
|
298
|
-
left =
|
|
299
|
-
rect.left + window.scrollX - floatingRect.width - horizontalGapLeft;
|
|
325
|
+
left = rect.left + scrollXOffset - floatingRect.width - horizontalGapLeft;
|
|
300
326
|
}
|
|
301
327
|
|
|
302
328
|
let posLeft = left;
|
|
@@ -308,11 +334,11 @@
|
|
|
308
334
|
(actualDirection === "top" || actualDirection === "bottom")
|
|
309
335
|
) {
|
|
310
336
|
if (intrinsicAlign === "center") {
|
|
311
|
-
posLeft = rect.left +
|
|
337
|
+
posLeft = rect.left + scrollXOffset + rect.width / 2;
|
|
312
338
|
} else if (intrinsicAlign === "start") {
|
|
313
|
-
posLeft = rect.left +
|
|
339
|
+
posLeft = rect.left + scrollXOffset;
|
|
314
340
|
} else {
|
|
315
|
-
posLeft = rect.right +
|
|
341
|
+
posLeft = rect.right + scrollXOffset;
|
|
316
342
|
}
|
|
317
343
|
posWidth = undefined;
|
|
318
344
|
}
|
|
@@ -370,7 +396,7 @@
|
|
|
370
396
|
: null;
|
|
371
397
|
|
|
372
398
|
$: portalStyle = [
|
|
373
|
-
"position: absolute",
|
|
399
|
+
useFixedPosition ? "position: fixed" : "position: absolute",
|
|
374
400
|
`top: ${pos.top}px`,
|
|
375
401
|
`left: ${pos.left}px`,
|
|
376
402
|
intrinsicTranslateX,
|
|
@@ -423,6 +449,7 @@
|
|
|
423
449
|
{#if open}
|
|
424
450
|
<Portal
|
|
425
451
|
bind:ref
|
|
452
|
+
target={effectiveTarget}
|
|
426
453
|
{...$$restProps}
|
|
427
454
|
data-floating-portal
|
|
428
455
|
data-floating-direction={pos.actualDirection}
|
|
@@ -89,6 +89,15 @@ export type FloatingPortalProps = {
|
|
|
89
89
|
*/
|
|
90
90
|
ref?: null | HTMLElement;
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Specify the DOM element to mount the portal into.
|
|
94
|
+
* When not set, mounts into the anchor's nearest `<dialog>` ancestor if one
|
|
95
|
+
* exists (so the portal participates in the dialog's top layer), otherwise
|
|
96
|
+
* falls back to `document.body`.
|
|
97
|
+
* @default null
|
|
98
|
+
*/
|
|
99
|
+
target?: HTMLElement | null;
|
|
100
|
+
|
|
92
101
|
children?: (
|
|
93
102
|
this: void,
|
|
94
103
|
...args: [{ direction: "bottom" | "top" | "left" | "right" }]
|
package/src/Portal/Portal.svelte
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export let tag = "div";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Specify the DOM element to mount the portal into.
|
|
10
|
+
* Defaults to `document.body`.
|
|
11
|
+
* @type {HTMLElement | null}
|
|
12
|
+
*/
|
|
13
|
+
export let target = null;
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Obtain a reference to the portal element.
|
|
10
17
|
* @type {null | HTMLElement}
|
|
@@ -27,11 +34,14 @@
|
|
|
27
34
|
};
|
|
28
35
|
});
|
|
29
36
|
|
|
30
|
-
$:
|
|
31
|
-
|
|
37
|
+
$: effectiveTarget =
|
|
38
|
+
target ?? (typeof document === "undefined" ? null : document.body);
|
|
39
|
+
|
|
40
|
+
$: if (mounted && ref && effectiveTarget) {
|
|
41
|
+
if (ref.parentNode !== effectiveTarget) {
|
|
32
42
|
const activeEl = document.activeElement;
|
|
33
43
|
const hadFocus = ref.contains(activeEl);
|
|
34
|
-
|
|
44
|
+
effectiveTarget.appendChild(ref);
|
|
35
45
|
if (hadFocus && activeEl instanceof HTMLElement) {
|
|
36
46
|
activeEl.focus();
|
|
37
47
|
}
|
|
@@ -10,6 +10,13 @@ type $Props = {
|
|
|
10
10
|
*/
|
|
11
11
|
tag?: keyof HTMLElementTagNameMap;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Specify the DOM element to mount the portal into.
|
|
15
|
+
* Defaults to `document.body`.
|
|
16
|
+
* @default null
|
|
17
|
+
*/
|
|
18
|
+
target?: HTMLElement | null;
|
|
19
|
+
|
|
13
20
|
/**
|
|
14
21
|
* Obtain a reference to the portal element.
|
|
15
22
|
* @default null
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
export let id = undefined;
|
|
66
66
|
|
|
67
67
|
import {
|
|
68
|
+
afterUpdate,
|
|
68
69
|
beforeUpdate,
|
|
69
70
|
createEventDispatcher,
|
|
70
71
|
onMount,
|
|
@@ -79,6 +80,7 @@
|
|
|
79
80
|
const selectedValue = writable(selected);
|
|
80
81
|
const groupName = writable(name);
|
|
81
82
|
const groupRequired = writable(required);
|
|
83
|
+
let isInitialRender = true;
|
|
82
84
|
|
|
83
85
|
/**
|
|
84
86
|
* @type {(data: { checked: boolean; value: Value }) => void}
|
|
@@ -114,7 +116,13 @@
|
|
|
114
116
|
|
|
115
117
|
selectedValue.subscribe((value) => {
|
|
116
118
|
selected = value;
|
|
117
|
-
|
|
119
|
+
if (!isInitialRender) {
|
|
120
|
+
dispatch("change", value);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
afterUpdate(() => {
|
|
125
|
+
isInitialRender = false;
|
|
118
126
|
});
|
|
119
127
|
|
|
120
128
|
$: $groupName = name;
|