@nectary/components 5.29.0 → 5.29.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/bundle.js +463 -105
- package/package.json +3 -3
- package/pop/index.d.ts +6 -0
- package/pop/index.js +140 -93
- package/pop/utils.d.ts +20 -0
- package/pop/utils.js +46 -0
- package/skeleton/index.js +1 -1
- package/skeleton-item/index.js +1 -1
- package/tooltip/index.js +176 -12
- package/utils/dom.d.ts +2 -0
- package/utils/dom.js +34 -0
- package/utils/index.js +3 -1
- package/utils/placement.d.ts +13 -0
- package/utils/placement.js +78 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nectary/components",
|
|
3
|
-
"version": "5.29.
|
|
3
|
+
"version": "5.29.2",
|
|
4
4
|
"files": [
|
|
5
5
|
"**/*/*.css",
|
|
6
6
|
"**/*/*.json",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime": "^7.22.15",
|
|
27
|
-
"@nectary/assets": "3.6.
|
|
27
|
+
"@nectary/assets": "3.6.9"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@babel/cli": "^7.22.15",
|
|
@@ -40,6 +40,6 @@
|
|
|
40
40
|
"vite": "^7.0.6"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@nectary/theme-base": "1.15.
|
|
43
|
+
"@nectary/theme-base": "1.15.1"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/pop/index.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import { NectaryElement } from '../utils';
|
|
|
2
2
|
import type { TSinchPopOrientation } from './types';
|
|
3
3
|
import type { TRect } from '../types';
|
|
4
4
|
export * from './types';
|
|
5
|
+
/**
|
|
6
|
+
* `<sinch-pop>` is the overlay primitive shared by `<sinch-tooltip>`,
|
|
7
|
+
* `<sinch-dropdown>` and similar components. It anchors a popover to a target
|
|
8
|
+
* element, manages its open lifecycle, and reconciles position across viewport
|
|
9
|
+
* resizes, scrolling, and transformed ancestors.
|
|
10
|
+
*/
|
|
5
11
|
export declare class Pop extends NectaryElement {
|
|
6
12
|
#private;
|
|
7
13
|
constructor();
|
package/pop/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Context, subscribeContext } from "../utils/context.js";
|
|
2
|
-
import { getBooleanAttribute, updateBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateIntegerAttribute, getIntegerAttribute, isAttrEqual, getScrollableParents, isAttrTrue } from "../utils/dom.js";
|
|
2
|
+
import { getBooleanAttribute, updateBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateIntegerAttribute, getIntegerAttribute, isAttrEqual, getScrollableParents, getTransformedAncestor, isAttrTrue } from "../utils/dom.js";
|
|
3
3
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
4
4
|
import { getRect } from "../utils/rect.js";
|
|
5
5
|
import { getFirstSlotElement, getFirstFocusableElement, isElementFocused } from "../utils/slot.js";
|
|
6
6
|
import { throttleAnimationFrame } from "../utils/throttle.js";
|
|
7
7
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
8
8
|
import { isTargetEqual } from "../utils/event-target.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import { getPlacementContext, toLocalRect } from "../utils/placement.js";
|
|
10
|
+
import { orientationValues, disableOverscroll, enableOverscroll, getAnchorPosition, clampPosition } from "./utils.js";
|
|
11
|
+
const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;transform:translate(var(--sinch-pop-offset-x),var(--sinch-pop-offset-y));margin:0;outline:0;padding:0;border:none;box-sizing:border-box;max-width:unset;max-height:unset;z-index:1;background:0 0;overflow:visible}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}#content{position:relative;z-index:1}#target-open{display:flex;flex-direction:column;position:absolute;left:0;top:0}#focus{display:none;position:absolute;width:0;height:0}</style><slot id="target" name="target" aria-haspopup="dialog" aria-expanded="false"></slot><div id="focus"></div><dialog id="dialog"><div id="target-open"><slot name="target-open"></slot></div><div id="content"><slot name="content"></slot></div></dialog>';
|
|
11
12
|
const template = document.createElement("template");
|
|
12
13
|
template.innerHTML = templateHTML;
|
|
13
14
|
class Pop extends NectaryElement {
|
|
@@ -15,6 +16,7 @@ class Pop extends NectaryElement {
|
|
|
15
16
|
#$focus;
|
|
16
17
|
#$dialog;
|
|
17
18
|
#resizeThrottle;
|
|
19
|
+
#scrollPositionThrottle;
|
|
18
20
|
#resizeObserver;
|
|
19
21
|
#$targetSlot;
|
|
20
22
|
#$targetOpenSlot;
|
|
@@ -24,10 +26,15 @@ class Pop extends NectaryElement {
|
|
|
24
26
|
#controller;
|
|
25
27
|
#keydownContext;
|
|
26
28
|
#visibilityContext;
|
|
27
|
-
#targetStyleValue = null;
|
|
28
29
|
#modalWidth = 0;
|
|
29
30
|
#modalHeight = 0;
|
|
30
31
|
#scrollableParents = [];
|
|
32
|
+
#openSession = {
|
|
33
|
+
effectiveAllowScroll: false,
|
|
34
|
+
modalSemantics: false,
|
|
35
|
+
targetStyleValue: null,
|
|
36
|
+
transformedAncestor: null
|
|
37
|
+
};
|
|
31
38
|
constructor() {
|
|
32
39
|
super();
|
|
33
40
|
const shadowRoot = this.attachShadow();
|
|
@@ -40,6 +47,9 @@ class Pop extends NectaryElement {
|
|
|
40
47
|
this.#$contentSlot = shadowRoot.querySelector('slot[name="content"]');
|
|
41
48
|
this.#$targetOpenWrapper = shadowRoot.querySelector("#target-open");
|
|
42
49
|
this.#resizeThrottle = throttleAnimationFrame(this.#updateOrientation);
|
|
50
|
+
this.#scrollPositionThrottle = throttleAnimationFrame(() => {
|
|
51
|
+
this.#updatePosition(false);
|
|
52
|
+
});
|
|
43
53
|
this.#resizeObserver = new ResizeObserver(() => {
|
|
44
54
|
this.#resizeThrottle.fn();
|
|
45
55
|
});
|
|
@@ -69,6 +79,7 @@ class Pop extends NectaryElement {
|
|
|
69
79
|
this.#controller.abort();
|
|
70
80
|
this.#controller = null;
|
|
71
81
|
this.#resizeThrottle.cancel();
|
|
82
|
+
this.#scrollPositionThrottle.cancel();
|
|
72
83
|
this.#resizeObserver.disconnect();
|
|
73
84
|
this.#onCollapse();
|
|
74
85
|
}
|
|
@@ -164,10 +175,68 @@ class Pop extends NectaryElement {
|
|
|
164
175
|
}
|
|
165
176
|
return item;
|
|
166
177
|
}
|
|
178
|
+
#prepareTransferredTarget() {
|
|
179
|
+
const targetEl = this.#getFirstTargetElement(this.#$targetSlot);
|
|
180
|
+
const targetElComputedStyle = getComputedStyle(targetEl);
|
|
181
|
+
const marginLeft = parseInt(targetElComputedStyle.marginLeft, 10);
|
|
182
|
+
const marginRight = parseInt(targetElComputedStyle.marginRight, 10);
|
|
183
|
+
const marginTop = parseInt(targetElComputedStyle.marginTop, 10);
|
|
184
|
+
const marginBottom = parseInt(targetElComputedStyle.marginBottom, 10);
|
|
185
|
+
const targetRect = this.#getTargetRect();
|
|
186
|
+
this.#$targetWrapper.style.setProperty("display", "block");
|
|
187
|
+
this.#$targetWrapper.style.setProperty("width", `${targetRect.width + marginLeft + marginRight}px`);
|
|
188
|
+
this.#$targetWrapper.style.setProperty("height", `${targetRect.height + marginTop + marginBottom}px`);
|
|
189
|
+
this.#$targetOpenWrapper.style.setProperty("width", `${targetRect.width}px`);
|
|
190
|
+
this.#$targetOpenWrapper.style.setProperty("height", `${targetRect.height}px`);
|
|
191
|
+
this.#openSession.targetStyleValue = targetEl.getAttribute("style");
|
|
192
|
+
targetEl.style.setProperty("margin", "0");
|
|
193
|
+
targetEl.style.setProperty("position", "static");
|
|
194
|
+
if (targetElComputedStyle.transform !== "none") {
|
|
195
|
+
const matrix = new DOMMatrixReadOnly(targetElComputedStyle.transform);
|
|
196
|
+
targetEl.style.setProperty("transform", matrix.translate(-matrix.e, -matrix.f).toString());
|
|
197
|
+
}
|
|
198
|
+
getFirstSlotElement(this.#$targetSlot)?.setAttribute("slot", "target-open");
|
|
199
|
+
}
|
|
200
|
+
#restoreTransferredTarget() {
|
|
201
|
+
const targetEl = this.#getFirstTargetElement(this.#$targetOpenSlot);
|
|
202
|
+
const { targetStyleValue } = this.#openSession;
|
|
203
|
+
if (targetStyleValue === null) {
|
|
204
|
+
targetEl.style.removeProperty("margin");
|
|
205
|
+
targetEl.style.removeProperty("position");
|
|
206
|
+
targetEl.style.removeProperty("transform");
|
|
207
|
+
} else {
|
|
208
|
+
targetEl.setAttribute("style", targetStyleValue);
|
|
209
|
+
}
|
|
210
|
+
getFirstSlotElement(this.#$targetOpenSlot)?.setAttribute("slot", "target");
|
|
211
|
+
this.#$targetWrapper.style.removeProperty("display");
|
|
212
|
+
this.#$targetWrapper.style.removeProperty("width");
|
|
213
|
+
this.#$targetWrapper.style.removeProperty("height");
|
|
214
|
+
}
|
|
215
|
+
#subscribeScrollTracking() {
|
|
216
|
+
this.#scrollableParents = getScrollableParents(this.#getFirstTargetElement(this.#$targetSlot));
|
|
217
|
+
this.#scrollableParents.forEach((el) => {
|
|
218
|
+
el.addEventListener("scroll", this.#onScrollableParentScroll, { passive: true, capture: true });
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
#unsubscribeScrollTracking() {
|
|
222
|
+
this.#scrollableParents.forEach((el) => {
|
|
223
|
+
el.removeEventListener("scroll", this.#onScrollableParentScroll, { capture: true });
|
|
224
|
+
});
|
|
225
|
+
}
|
|
167
226
|
#onExpand() {
|
|
168
227
|
if (!this.isDomConnected || this.#$dialog.open) {
|
|
169
228
|
return;
|
|
170
229
|
}
|
|
230
|
+
const transformedAncestor = getTransformedAncestor(this);
|
|
231
|
+
const effectiveAllowScroll = this.allowScroll || transformedAncestor != null;
|
|
232
|
+
const shouldUseModal = this.modal && transformedAncestor == null;
|
|
233
|
+
const openAsModal = shouldUseModal || !effectiveAllowScroll;
|
|
234
|
+
this.#openSession = {
|
|
235
|
+
effectiveAllowScroll,
|
|
236
|
+
modalSemantics: shouldUseModal,
|
|
237
|
+
targetStyleValue: null,
|
|
238
|
+
transformedAncestor
|
|
239
|
+
};
|
|
171
240
|
this.#$targetSlot.addEventListener("blur", this.#stopEventPropagation, true);
|
|
172
241
|
this.#$focus.setAttribute("tabindex", "-1");
|
|
173
242
|
this.#$focus.style.display = "block";
|
|
@@ -177,7 +246,7 @@ class Pop extends NectaryElement {
|
|
|
177
246
|
this.#$targetSlot.removeEventListener("blur", this.#stopEventPropagation, true);
|
|
178
247
|
this.#$focus.removeAttribute("tabindex");
|
|
179
248
|
this.#$focus.removeAttribute("style");
|
|
180
|
-
if (
|
|
249
|
+
if (openAsModal) {
|
|
181
250
|
this.#$dialog.showModal();
|
|
182
251
|
} else {
|
|
183
252
|
this.#$dialog.show();
|
|
@@ -185,32 +254,13 @@ class Pop extends NectaryElement {
|
|
|
185
254
|
this.#$targetWrapper.setAttribute("aria-expanded", "true");
|
|
186
255
|
this.#updateOrientation();
|
|
187
256
|
this.#resizeObserver.observe(this.#$dialog);
|
|
188
|
-
if (
|
|
257
|
+
if (shouldUseModal) {
|
|
189
258
|
getFirstFocusableElement(this.#$contentSlot)?.focus();
|
|
190
259
|
} else {
|
|
191
|
-
if (!
|
|
192
|
-
|
|
193
|
-
const targetElComputedStyle = getComputedStyle($targetEl);
|
|
194
|
-
const marginLeft = parseInt(targetElComputedStyle.marginLeft);
|
|
195
|
-
const marginRight = parseInt(targetElComputedStyle.marginRight);
|
|
196
|
-
const marginTop = parseInt(targetElComputedStyle.marginTop);
|
|
197
|
-
const marginBottom = parseInt(targetElComputedStyle.marginBottom);
|
|
198
|
-
const targetRect = this.#getTargetRect();
|
|
199
|
-
this.#$targetWrapper.style.setProperty("display", "block");
|
|
200
|
-
this.#$targetWrapper.style.setProperty("width", `${targetRect.width + marginLeft + marginRight}px`);
|
|
201
|
-
this.#$targetWrapper.style.setProperty("height", `${targetRect.height + marginTop + marginBottom}px`);
|
|
202
|
-
this.#$targetOpenWrapper.style.setProperty("width", `${targetRect.width}px`);
|
|
203
|
-
this.#$targetOpenWrapper.style.setProperty("height", `${targetRect.height}px`);
|
|
204
|
-
this.#targetStyleValue = $targetEl.getAttribute("style");
|
|
205
|
-
$targetEl.style.setProperty("margin", "0");
|
|
206
|
-
$targetEl.style.setProperty("position", "static");
|
|
207
|
-
if (targetElComputedStyle.transform !== "none") {
|
|
208
|
-
const matrix = new DOMMatrixReadOnly(targetElComputedStyle.transform);
|
|
209
|
-
$targetEl.style.setProperty("transform", matrix.translate(-matrix.e, -matrix.f).toString());
|
|
210
|
-
}
|
|
211
|
-
getFirstSlotElement(this.#$targetSlot)?.setAttribute("slot", "target-open");
|
|
260
|
+
if (!effectiveAllowScroll) {
|
|
261
|
+
this.#prepareTransferredTarget();
|
|
212
262
|
}
|
|
213
|
-
const activeSlot =
|
|
263
|
+
const activeSlot = effectiveAllowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
214
264
|
activeSlot.addEventListener("keydown", this.#onTargetKeydown);
|
|
215
265
|
if (this.#targetActiveElement !== null) {
|
|
216
266
|
activeSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
@@ -227,13 +277,10 @@ class Pop extends NectaryElement {
|
|
|
227
277
|
}
|
|
228
278
|
}
|
|
229
279
|
}
|
|
230
|
-
if (!
|
|
280
|
+
if (!effectiveAllowScroll) {
|
|
231
281
|
disableOverscroll();
|
|
232
282
|
} else {
|
|
233
|
-
this.#
|
|
234
|
-
this.#scrollableParents.forEach((el) => {
|
|
235
|
-
el.addEventListener("scroll", () => this.#updatePosition(false), { passive: true, capture: true });
|
|
236
|
-
});
|
|
283
|
+
this.#subscribeScrollTracking();
|
|
237
284
|
}
|
|
238
285
|
window.addEventListener("resize", this.#onResize);
|
|
239
286
|
requestAnimationFrame(() => {
|
|
@@ -248,9 +295,11 @@ class Pop extends NectaryElement {
|
|
|
248
295
|
if (!this.#$dialog.open) {
|
|
249
296
|
return;
|
|
250
297
|
}
|
|
298
|
+
const openSession = this.#openSession;
|
|
299
|
+
const effectiveAllowScroll = openSession.effectiveAllowScroll;
|
|
251
300
|
this.#resizeObserver.disconnect();
|
|
252
|
-
const isNonModal = !
|
|
253
|
-
const activeSlot =
|
|
301
|
+
const isNonModal = !openSession.modalSemantics;
|
|
302
|
+
const activeSlot = effectiveAllowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
254
303
|
this.#dispatchContentVisibility(false);
|
|
255
304
|
activeSlot.removeEventListener("keydown", this.#onTargetKeydown);
|
|
256
305
|
if (isNonModal) {
|
|
@@ -261,19 +310,8 @@ class Pop extends NectaryElement {
|
|
|
261
310
|
if (isNonModal) {
|
|
262
311
|
activeSlot.removeEventListener("blur", this.#captureActiveElement, true);
|
|
263
312
|
}
|
|
264
|
-
if (isNonModal && !
|
|
265
|
-
|
|
266
|
-
targetEl.style.removeProperty("margin");
|
|
267
|
-
targetEl.style.removeProperty("position");
|
|
268
|
-
targetEl.style.removeProperty("transform");
|
|
269
|
-
if (this.#targetStyleValue !== null) {
|
|
270
|
-
targetEl.setAttribute("style", this.#targetStyleValue);
|
|
271
|
-
this.#targetStyleValue = null;
|
|
272
|
-
}
|
|
273
|
-
getFirstSlotElement(this.#$targetOpenSlot)?.setAttribute("slot", "target");
|
|
274
|
-
this.#$targetWrapper.style.removeProperty("display");
|
|
275
|
-
this.#$targetWrapper.style.removeProperty("width");
|
|
276
|
-
this.#$targetWrapper.style.removeProperty("height");
|
|
313
|
+
if (isNonModal && !effectiveAllowScroll) {
|
|
314
|
+
this.#restoreTransferredTarget();
|
|
277
315
|
}
|
|
278
316
|
if (this.#targetActiveElement !== null) {
|
|
279
317
|
if (!isElementFocused(this.#targetActiveElement)) {
|
|
@@ -293,57 +331,69 @@ class Pop extends NectaryElement {
|
|
|
293
331
|
this.#targetActiveElement = null;
|
|
294
332
|
}
|
|
295
333
|
}
|
|
296
|
-
if (!
|
|
334
|
+
if (!effectiveAllowScroll) {
|
|
297
335
|
enableOverscroll();
|
|
298
336
|
} else {
|
|
299
|
-
this.#
|
|
300
|
-
el.removeEventListener("scroll", () => this.#updatePosition(false), { capture: true });
|
|
301
|
-
});
|
|
337
|
+
this.#unsubscribeScrollTracking();
|
|
302
338
|
}
|
|
303
339
|
this.#resizeThrottle.cancel();
|
|
340
|
+
this.#scrollPositionThrottle.cancel();
|
|
304
341
|
window.removeEventListener("resize", this.#onResize);
|
|
305
342
|
this.#scrollableParents = [];
|
|
306
343
|
this.#$contentSlot.removeEventListener("slotchange", this.#onContentSlotChange);
|
|
344
|
+
this.#openSession = {
|
|
345
|
+
effectiveAllowScroll: false,
|
|
346
|
+
modalSemantics: false,
|
|
347
|
+
targetStyleValue: null,
|
|
348
|
+
transformedAncestor: null
|
|
349
|
+
};
|
|
307
350
|
}
|
|
308
351
|
#onResize = () => {
|
|
309
352
|
this.#resizeThrottle.fn();
|
|
310
353
|
};
|
|
354
|
+
#onScrollableParentScroll = () => {
|
|
355
|
+
this.#scrollPositionThrottle.fn();
|
|
356
|
+
};
|
|
311
357
|
#updatePosition = (updateWidth) => {
|
|
312
|
-
const
|
|
358
|
+
const placementContext = getPlacementContext(this, this.#openSession.transformedAncestor);
|
|
359
|
+
const { scaleX, scaleY, boundsWidth, boundsHeight } = placementContext;
|
|
360
|
+
const { modalSemantics, effectiveAllowScroll } = this.#openSession;
|
|
361
|
+
const shouldClamp = !effectiveAllowScroll;
|
|
362
|
+
const targetRectViewport = modalSemantics || effectiveAllowScroll ? this.#getTargetRect() : this.#$targetWrapper.getBoundingClientRect();
|
|
313
363
|
const orient = this.orientation;
|
|
314
|
-
const modalWidth = this.#modalWidth;
|
|
315
|
-
const modalHeight = this.#modalHeight;
|
|
316
364
|
const inset = this.inset;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
365
|
+
const insetX = inset / scaleX;
|
|
366
|
+
const insetY = inset / scaleY;
|
|
367
|
+
const targetRect = toLocalRect(targetRectViewport, placementContext);
|
|
368
|
+
const liveRect = this.#$dialog.open ? this.#$dialog.getBoundingClientRect() : null;
|
|
369
|
+
const isStretch = orient === "top-stretch" || orient === "bottom-stretch";
|
|
370
|
+
const modalWidthViewport = isStretch ? this.#modalWidth : liveRect?.width ?? this.#modalWidth;
|
|
371
|
+
const modalHeightViewport = liveRect?.height ?? this.#modalHeight;
|
|
372
|
+
const modalWidth = modalWidthViewport / scaleX;
|
|
373
|
+
const modalHeight = modalHeightViewport / scaleY;
|
|
374
|
+
const localPos = getAnchorPosition(targetRect, modalWidth, modalHeight, orient);
|
|
375
|
+
const xPos = localPos.x;
|
|
376
|
+
const yPos = localPos.y;
|
|
377
|
+
const localViewportInfos = { boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight };
|
|
378
|
+
const clampedPosition = shouldClamp ? clampPosition({ x: xPos, y: yPos, ...localViewportInfos }) : { x: xPos, y: yPos };
|
|
379
|
+
const clampedXPos = clampedPosition.x;
|
|
380
|
+
const clampedYPos = clampedPosition.y;
|
|
381
|
+
if (this.hideOutsideViewport) {
|
|
382
|
+
const viewportPosition = getAnchorPosition(targetRectViewport, modalWidthViewport, modalHeightViewport, orient);
|
|
383
|
+
const visibilityViewportInfos = {
|
|
384
|
+
boundsWidth: window.innerWidth,
|
|
385
|
+
boundsHeight: window.innerHeight,
|
|
386
|
+
insetX: inset,
|
|
387
|
+
insetY: inset,
|
|
388
|
+
modalWidth: modalWidthViewport,
|
|
389
|
+
modalHeight: modalHeightViewport
|
|
390
|
+
};
|
|
391
|
+
const isOutOfViewport = this.#isOutsideViewport(viewportPosition.x, viewportPosition.y, visibilityViewportInfos);
|
|
392
|
+
if (isOutOfViewport) {
|
|
393
|
+
this.#$dialog.style.setProperty("visibility", "hidden");
|
|
394
|
+
} else {
|
|
395
|
+
this.#$dialog.style.removeProperty("visibility");
|
|
396
|
+
}
|
|
347
397
|
} else {
|
|
348
398
|
this.#$dialog.style.removeProperty("visibility");
|
|
349
399
|
}
|
|
@@ -352,7 +402,7 @@ class Pop extends NectaryElement {
|
|
|
352
402
|
if (updateWidth === true) {
|
|
353
403
|
this.#$dialog.style.setProperty("width", `${modalWidth}px`);
|
|
354
404
|
}
|
|
355
|
-
if (!
|
|
405
|
+
if (!modalSemantics && !effectiveAllowScroll) {
|
|
356
406
|
const targetLeftPos = targetRect.x - clampedXPos;
|
|
357
407
|
const targetTopPos = targetRect.y - clampedYPos;
|
|
358
408
|
this.#$targetOpenWrapper.style.setProperty("left", `${targetLeftPos}px`);
|
|
@@ -428,13 +478,10 @@ class Pop extends NectaryElement {
|
|
|
428
478
|
this.#updateOrientation();
|
|
429
479
|
}
|
|
430
480
|
};
|
|
431
|
-
#
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
const clampedX = Math.max(inset, Math.min(x, window.innerWidth - modalWidth - inset));
|
|
436
|
-
const clampedY = Math.max(inset, Math.min(y, window.innerHeight - modalHeight - inset));
|
|
437
|
-
return Math.abs(clampedX - x) > 2 || Math.abs(clampedY - y) > 2;
|
|
481
|
+
#isOutsideViewport(x, y, viewportInfos) {
|
|
482
|
+
const { boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight } = viewportInfos;
|
|
483
|
+
const clampedPosition = clampPosition({ x, y, boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight });
|
|
484
|
+
return Math.abs(clampedPosition.x - x) > 2 || Math.abs(clampedPosition.y - y) > 2;
|
|
438
485
|
}
|
|
439
486
|
}
|
|
440
487
|
defineCustomElement("sinch-pop", Pop);
|
package/pop/utils.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import type { TSinchPopOrientation } from './types';
|
|
2
|
+
import type { TRect } from '../types';
|
|
2
3
|
export declare const orientationValues: readonly TSinchPopOrientation[];
|
|
4
|
+
type TClampPositionArgs = {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
boundsWidth: number;
|
|
8
|
+
boundsHeight: number;
|
|
9
|
+
insetX: number;
|
|
10
|
+
insetY: number;
|
|
11
|
+
modalWidth: number;
|
|
12
|
+
modalHeight: number;
|
|
13
|
+
};
|
|
14
|
+
export declare const getAnchorPosition: (rect: TRect, width: number, height: number, orient: TSinchPopOrientation) => {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
export declare const clampPosition: ({ x, y, boundsWidth, boundsHeight, insetX, insetY, modalWidth, modalHeight, }: TClampPositionArgs) => {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
};
|
|
3
22
|
export declare const disableOverscroll: () => void;
|
|
4
23
|
export declare const enableOverscroll: () => void;
|
|
24
|
+
export {};
|
package/pop/utils.js
CHANGED
|
@@ -10,6 +10,50 @@ const orientationValues = [
|
|
|
10
10
|
"center-left",
|
|
11
11
|
"center-right"
|
|
12
12
|
];
|
|
13
|
+
const getAnchorPosition = (rect, width, height, orient) => {
|
|
14
|
+
let x = 0;
|
|
15
|
+
let y = 0;
|
|
16
|
+
if (orient === "bottom-right" || orient === "top-right" || orient === "top-stretch" || orient === "bottom-stretch") {
|
|
17
|
+
x = rect.x;
|
|
18
|
+
}
|
|
19
|
+
if (orient === "bottom-left" || orient === "top-left") {
|
|
20
|
+
x = rect.x + rect.width - width;
|
|
21
|
+
}
|
|
22
|
+
if (orient === "bottom-center" || orient === "top-center") {
|
|
23
|
+
x = rect.x + rect.width / 2 - width / 2;
|
|
24
|
+
}
|
|
25
|
+
if (orient === "center-right") {
|
|
26
|
+
x = rect.x + rect.width;
|
|
27
|
+
}
|
|
28
|
+
if (orient === "center-left") {
|
|
29
|
+
x = rect.x - width;
|
|
30
|
+
}
|
|
31
|
+
if (orient === "bottom-left" || orient === "bottom-right" || orient === "bottom-stretch" || orient === "bottom-center") {
|
|
32
|
+
y = rect.y + rect.height;
|
|
33
|
+
}
|
|
34
|
+
if (orient === "top-left" || orient === "top-right" || orient === "top-stretch" || orient === "top-center") {
|
|
35
|
+
y = rect.y - height;
|
|
36
|
+
}
|
|
37
|
+
if (orient === "center-left" || orient === "center-right") {
|
|
38
|
+
y = rect.y + rect.height / 2 - height / 2;
|
|
39
|
+
}
|
|
40
|
+
return { x, y };
|
|
41
|
+
};
|
|
42
|
+
const clampPosition = ({
|
|
43
|
+
x,
|
|
44
|
+
y,
|
|
45
|
+
boundsWidth,
|
|
46
|
+
boundsHeight,
|
|
47
|
+
insetX,
|
|
48
|
+
insetY,
|
|
49
|
+
modalWidth,
|
|
50
|
+
modalHeight
|
|
51
|
+
}) => {
|
|
52
|
+
return {
|
|
53
|
+
x: Math.max(insetX, Math.min(x, boundsWidth - modalWidth - insetX)),
|
|
54
|
+
y: Math.max(insetY, Math.min(y, boundsHeight - modalHeight - insetY))
|
|
55
|
+
};
|
|
56
|
+
};
|
|
13
57
|
const bodyEl = document.body;
|
|
14
58
|
const disableOverscroll = () => {
|
|
15
59
|
bodyEl.__pop_counter__ = (bodyEl.__pop_counter__ ?? 0) + 1;
|
|
@@ -26,7 +70,9 @@ const enableOverscroll = () => {
|
|
|
26
70
|
}
|
|
27
71
|
};
|
|
28
72
|
export {
|
|
73
|
+
clampPosition,
|
|
29
74
|
disableOverscroll,
|
|
30
75
|
enableOverscroll,
|
|
76
|
+
getAnchorPosition,
|
|
31
77
|
orientationValues
|
|
32
78
|
};
|
package/skeleton/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAttrTrue, shouldReduceMotion, getCssVar, attrValueToInteger } from "../utils/dom.js";
|
|
2
2
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
3
3
|
import { getUid } from "../utils/uid.js";
|
|
4
|
-
const templateHTML = '<style>:host{display:block;position:relative}#wrapper{position:relative;display:flex;flex-direction:column;gap:16px;overflow:hidden;box-sizing:border-box}:host([card]:not([card=false])) #wrapper{background-color:var(--sinch-sys-color-surface-primary-default);border:1px solid var(--sinch-sys-color-border-subtle);border-radius:var(--sinch-sys-shape-radius-l);padding:16px}#shimmer{position:absolute;inset:0;overflow:hidden;pointer-events:none}#shimmer-inner{position:absolute;left:0;top:0;width:400%;height:100%;background-image:linear-gradient(90deg,transparent 0,transparent
|
|
4
|
+
const templateHTML = '<style>:host{display:block;position:relative}#wrapper{position:relative;display:flex;flex-direction:column;gap:16px;overflow:hidden;box-sizing:border-box}:host([card]:not([card=false])) #wrapper{background-color:var(--sinch-sys-color-surface-primary-default);border:1px solid var(--sinch-sys-color-border-subtle);border-radius:var(--sinch-sys-shape-radius-l);padding:16px}#shimmer{position:absolute;inset:0;overflow:hidden;pointer-events:none}#shimmer-inner{position:absolute;left:0;top:0;width:400%;height:100%;background-image:linear-gradient(90deg,transparent 0,transparent 30%,var(--sinch-comp-skeleton-color-shimmer) 50%,transparent 53%,transparent 100%);background-size:100% 100%}#shimmer.animated #shimmer-inner{animation:nectary-skeleton-shimmer 2s linear infinite}@media (prefers-reduced-motion:reduce){#shimmer.animated #shimmer-inner{animation:none}}@keyframes nectary-skeleton-shimmer{0%{transform:translateX(-75%)}100%{transform:translateX(0)}}#svg{display:block;width:0;height:0}</style><svg id="svg"><defs><clipPath id="clip"></clipPath></defs></svg><div id="wrapper"><slot></slot><div id="shimmer"><div id="shimmer-inner"></div></div></div>';
|
|
5
5
|
const template = document.createElement("template");
|
|
6
6
|
template.innerHTML = templateHTML;
|
|
7
7
|
const BORDER_WIDTH = 1;
|
package/skeleton-item/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
2
|
-
const templateHTML = '<style>:host{display:block;box-sizing:border-box;--sinch-local-shape-radius:var(--sinch-sys-shape-radius-m, 4px)}#content{width:100%;height:100%;min-height:100%;box-sizing:border-box;background-color:var(--sinch-
|
|
2
|
+
const templateHTML = '<style>:host{display:block;box-sizing:border-box;--sinch-local-shape-radius:var(--sinch-sys-shape-radius-m, 4px)}#content{width:100%;height:100%;min-height:100%;box-sizing:border-box;background-color:var(--sinch-comp-skeleton-color-background);overflow:hidden;position:relative;border-radius:var(--sinch-local-shape-radius)}:host([size=xs]){height:var(--sinch-sys-size-xs,24px);--sinch-local-shape-radius:var(--sinch-sys-shape-radius-xs, 4px)}:host([size="s"]){height:var(--sinch-sys-size-s,32px);--sinch-local-shape-radius:var(--sinch-sys-shape-radius-s, 4px)}:host(:not([size])),:host([size="m"]){height:var(--sinch-sys-size-m,40px);--sinch-local-shape-radius:var(--sinch-sys-shape-radius-m, 4px)}:host([size="l"]){height:var(--sinch-sys-size-l,48px);--sinch-local-shape-radius:var(--sinch-sys-shape-radius-l, 4px)}</style><div id="content"></div>';
|
|
3
3
|
const template = document.createElement("template");
|
|
4
4
|
template.innerHTML = templateHTML;
|
|
5
5
|
class SkeletonItem extends NectaryElement {
|