@nectary/components 5.3.0 → 5.4.1
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 +134 -81
- package/package.json +2 -2
- package/pop/index.d.ts +3 -0
- package/pop/index.js +86 -76
- package/pop/types.d.ts +3 -0
- package/popover/index.js +1 -1
- package/select-menu/types.d.ts +2 -0
- package/tooltip/index.d.ts +1 -0
- package/tooltip/index.js +23 -4
- package/tooltip/tooltip-state.d.ts +1 -0
- package/tooltip/tooltip-state.js +9 -1
- package/tooltip/types.d.ts +1 -0
- package/utils/dom.d.ts +1 -0
- package/utils/dom.js +17 -0
- package/utils/index.js +2 -1
package/bundle.js
CHANGED
|
@@ -127,6 +127,22 @@ const shouldReduceMotion = () => window.matchMedia("(prefers-reduced-motion: red
|
|
|
127
127
|
const isAttrEqual = (oldVal, newVal) => {
|
|
128
128
|
return oldVal === newVal || newVal === null && oldVal === "false" || newVal === "" && oldVal === "true";
|
|
129
129
|
};
|
|
130
|
+
const getScrollableParents = (node) => {
|
|
131
|
+
const scrollableParents = [];
|
|
132
|
+
if (node == null) {
|
|
133
|
+
return scrollableParents;
|
|
134
|
+
}
|
|
135
|
+
let parent = node.parentElement;
|
|
136
|
+
while (parent != null) {
|
|
137
|
+
const computedStyle = getComputedStyle(parent);
|
|
138
|
+
if ((parent.scrollHeight > parent.clientHeight || parent.scrollWidth > parent.clientWidth) && (computedStyle.overflow === "auto" || computedStyle.overflow === "scroll" || computedStyle.overflowY === "auto" || computedStyle.overflowY === "scroll" || computedStyle.overflowX === "auto" || computedStyle.overflowX === "scroll")) {
|
|
139
|
+
scrollableParents.push(parent);
|
|
140
|
+
}
|
|
141
|
+
parent = parent.parentElement;
|
|
142
|
+
}
|
|
143
|
+
scrollableParents.push(document);
|
|
144
|
+
return scrollableParents;
|
|
145
|
+
};
|
|
130
146
|
class NectaryElementBase extends HTMLElement {
|
|
131
147
|
static _isGlobal = false;
|
|
132
148
|
static get elementName() {
|
|
@@ -2846,6 +2862,7 @@ class Pop extends NectaryElement {
|
|
|
2846
2862
|
#targetStyleValue = null;
|
|
2847
2863
|
#modalWidth = 0;
|
|
2848
2864
|
#modalHeight = 0;
|
|
2865
|
+
#scrollableParents = [];
|
|
2849
2866
|
constructor() {
|
|
2850
2867
|
super();
|
|
2851
2868
|
const shadowRoot = this.attachShadow();
|
|
@@ -2896,6 +2913,12 @@ class Pop extends NectaryElement {
|
|
|
2896
2913
|
"open"
|
|
2897
2914
|
];
|
|
2898
2915
|
}
|
|
2916
|
+
get allowScroll() {
|
|
2917
|
+
return getBooleanAttribute(this, "allow-scroll");
|
|
2918
|
+
}
|
|
2919
|
+
get hideOutsideViewport() {
|
|
2920
|
+
return getBooleanAttribute(this, "hide-outside-viewport");
|
|
2921
|
+
}
|
|
2899
2922
|
set modal(isModal) {
|
|
2900
2923
|
updateBooleanAttribute(this, "modal", isModal);
|
|
2901
2924
|
}
|
|
@@ -2926,6 +2949,9 @@ class Pop extends NectaryElement {
|
|
|
2926
2949
|
get popoverRect() {
|
|
2927
2950
|
return getRect(this.#$dialog);
|
|
2928
2951
|
}
|
|
2952
|
+
get shouldCloseOnBackdropClick() {
|
|
2953
|
+
return !getBooleanAttribute(this, "disable-backdrop-close");
|
|
2954
|
+
}
|
|
2929
2955
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
2930
2956
|
if (isAttrEqual(oldVal, newVal)) {
|
|
2931
2957
|
return;
|
|
@@ -2986,57 +3012,71 @@ class Pop extends NectaryElement {
|
|
|
2986
3012
|
this.#$targetSlot.removeEventListener("blur", this.#stopEventPropagation, true);
|
|
2987
3013
|
this.#$focus.removeAttribute("tabindex");
|
|
2988
3014
|
this.#$focus.removeAttribute("style");
|
|
2989
|
-
this
|
|
3015
|
+
if (this.modal || !this.allowScroll) {
|
|
3016
|
+
this.#$dialog.showModal();
|
|
3017
|
+
} else {
|
|
3018
|
+
this.#$dialog.show();
|
|
3019
|
+
}
|
|
2990
3020
|
this.#$targetWrapper.setAttribute("aria-expanded", "true");
|
|
2991
3021
|
this.#updateOrientation();
|
|
2992
3022
|
this.#resizeObserver.observe(this.#$dialog);
|
|
2993
3023
|
if (this.modal) {
|
|
2994
3024
|
getFirstFocusableElement(this.#$contentSlot)?.focus();
|
|
2995
3025
|
} else {
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3026
|
+
if (!this.allowScroll) {
|
|
3027
|
+
const $targetEl = this.#getFirstTargetElement(this.#$targetSlot);
|
|
3028
|
+
const targetElComputedStyle = getComputedStyle($targetEl);
|
|
3029
|
+
const marginLeft = parseInt(targetElComputedStyle.marginLeft);
|
|
3030
|
+
const marginRight = parseInt(targetElComputedStyle.marginRight);
|
|
3031
|
+
const marginTop = parseInt(targetElComputedStyle.marginTop);
|
|
3032
|
+
const marginBottom = parseInt(targetElComputedStyle.marginBottom);
|
|
3033
|
+
const targetRect = this.#getTargetRect();
|
|
3034
|
+
this.#$targetWrapper.style.setProperty("display", "block");
|
|
3035
|
+
this.#$targetWrapper.style.setProperty("width", `${targetRect.width + marginLeft + marginRight}px`);
|
|
3036
|
+
this.#$targetWrapper.style.setProperty("height", `${targetRect.height + marginTop + marginBottom}px`);
|
|
3037
|
+
this.#$targetOpenWrapper.style.setProperty("width", `${targetRect.width}px`);
|
|
3038
|
+
this.#$targetOpenWrapper.style.setProperty("height", `${targetRect.height}px`);
|
|
3039
|
+
this.#targetStyleValue = $targetEl.getAttribute("style");
|
|
3040
|
+
$targetEl.style.setProperty("margin", "0");
|
|
3041
|
+
$targetEl.style.setProperty("position", "static");
|
|
3042
|
+
if (targetElComputedStyle.transform !== "none") {
|
|
3043
|
+
const matrix = new DOMMatrixReadOnly(targetElComputedStyle.transform);
|
|
3044
|
+
$targetEl.style.setProperty("transform", matrix.translate(-matrix.e, -matrix.f).toString());
|
|
3045
|
+
}
|
|
3046
|
+
getFirstSlotElement(this.#$targetSlot)?.setAttribute("slot", "target-open");
|
|
3047
|
+
}
|
|
3048
|
+
const activeSlot = this.allowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
3049
|
+
activeSlot.addEventListener("keydown", this.#onTargetKeydown);
|
|
3017
3050
|
if (this.#targetActiveElement !== null) {
|
|
3018
|
-
|
|
3051
|
+
activeSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
3019
3052
|
this.#targetActiveElement.focus();
|
|
3020
|
-
|
|
3053
|
+
activeSlot.removeEventListener("focus", this.#stopEventPropagation, true);
|
|
3021
3054
|
if (!isElementFocused(this.#targetActiveElement)) {
|
|
3022
3055
|
requestAnimationFrame(() => {
|
|
3023
3056
|
if (this.isDomConnected && this.#$dialog.open) {
|
|
3024
|
-
|
|
3057
|
+
activeSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
3025
3058
|
this.#targetActiveElement.focus();
|
|
3026
|
-
|
|
3059
|
+
activeSlot.removeEventListener("focus", this.#stopEventPropagation, true);
|
|
3027
3060
|
}
|
|
3028
3061
|
});
|
|
3029
3062
|
}
|
|
3030
3063
|
}
|
|
3031
3064
|
}
|
|
3032
|
-
|
|
3033
|
-
|
|
3065
|
+
if (!this.allowScroll) {
|
|
3066
|
+
disableOverscroll();
|
|
3067
|
+
} else {
|
|
3068
|
+
this.#scrollableParents = getScrollableParents(this.#getFirstTargetElement(this.#$targetSlot));
|
|
3069
|
+
this.#scrollableParents.forEach((el) => {
|
|
3070
|
+
el.addEventListener("scroll", () => this.#updatePosition(false), { passive: true, capture: true });
|
|
3071
|
+
});
|
|
3072
|
+
}
|
|
3034
3073
|
window.addEventListener("resize", this.#onResize);
|
|
3035
3074
|
requestAnimationFrame(() => {
|
|
3036
3075
|
if (this.isDomConnected && this.#$dialog.open) {
|
|
3037
3076
|
this.#$contentSlot.addEventListener("slotchange", this.#onContentSlotChange);
|
|
3038
3077
|
}
|
|
3039
3078
|
});
|
|
3079
|
+
requestAnimationFrame(() => this.#updatePosition());
|
|
3040
3080
|
this.#dispatchContentVisibility(true);
|
|
3041
3081
|
}
|
|
3042
3082
|
#onCollapse() {
|
|
@@ -3045,17 +3085,18 @@ class Pop extends NectaryElement {
|
|
|
3045
3085
|
}
|
|
3046
3086
|
this.#resizeObserver.disconnect();
|
|
3047
3087
|
const isNonModal = !this.modal;
|
|
3088
|
+
const activeSlot = this.allowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
3048
3089
|
this.#dispatchContentVisibility(false);
|
|
3049
|
-
|
|
3090
|
+
activeSlot.removeEventListener("keydown", this.#onTargetKeydown);
|
|
3050
3091
|
if (isNonModal) {
|
|
3051
|
-
|
|
3092
|
+
activeSlot.addEventListener("blur", this.#captureActiveElement, true);
|
|
3052
3093
|
}
|
|
3053
3094
|
this.#$dialog.close();
|
|
3054
3095
|
this.#$targetWrapper.setAttribute("aria-expanded", "false");
|
|
3055
3096
|
if (isNonModal) {
|
|
3056
|
-
|
|
3097
|
+
activeSlot.removeEventListener("blur", this.#captureActiveElement, true);
|
|
3057
3098
|
}
|
|
3058
|
-
if (isNonModal) {
|
|
3099
|
+
if (isNonModal && !this.allowScroll) {
|
|
3059
3100
|
const targetEl = this.#getFirstTargetElement(this.#$targetOpenSlot);
|
|
3060
3101
|
targetEl.style.removeProperty("margin");
|
|
3061
3102
|
targetEl.style.removeProperty("position");
|
|
@@ -3087,17 +3128,23 @@ class Pop extends NectaryElement {
|
|
|
3087
3128
|
this.#targetActiveElement = null;
|
|
3088
3129
|
}
|
|
3089
3130
|
}
|
|
3090
|
-
|
|
3131
|
+
if (!this.allowScroll) {
|
|
3132
|
+
enableOverscroll();
|
|
3133
|
+
} else {
|
|
3134
|
+
this.#scrollableParents.forEach((el) => {
|
|
3135
|
+
el.removeEventListener("scroll", () => this.#updatePosition(false), { capture: true });
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3091
3138
|
this.#resizeThrottle.cancel();
|
|
3092
3139
|
window.removeEventListener("resize", this.#onResize);
|
|
3093
|
-
|
|
3140
|
+
this.#scrollableParents = [];
|
|
3094
3141
|
this.#$contentSlot.removeEventListener("slotchange", this.#onContentSlotChange);
|
|
3095
3142
|
}
|
|
3096
3143
|
#onResize = () => {
|
|
3097
3144
|
this.#resizeThrottle.fn();
|
|
3098
3145
|
};
|
|
3099
|
-
#updatePosition = () => {
|
|
3100
|
-
const targetRect = this.modal ? this.#getTargetRect() : this.#$targetWrapper.getBoundingClientRect();
|
|
3146
|
+
#updatePosition = (updateWidth) => {
|
|
3147
|
+
const targetRect = this.modal || this.allowScroll ? this.#getTargetRect() : this.#$targetWrapper.getBoundingClientRect();
|
|
3101
3148
|
const orient = this.orientation;
|
|
3102
3149
|
const modalWidth = this.#modalWidth;
|
|
3103
3150
|
const modalHeight = this.#modalHeight;
|
|
@@ -3130,9 +3177,17 @@ class Pop extends NectaryElement {
|
|
|
3130
3177
|
}
|
|
3131
3178
|
const clampedXPos = Math.max(inset, Math.min(xPos, window.innerWidth - modalWidth - inset));
|
|
3132
3179
|
const clampedYPos = Math.max(inset, Math.min(yPos, window.innerHeight - modalHeight - inset));
|
|
3180
|
+
if (this.hideOutsideViewport && this.#isPopPointInViewport(xPos, yPos)) {
|
|
3181
|
+
this.#$dialog.style.setProperty("visibility", "hidden");
|
|
3182
|
+
} else {
|
|
3183
|
+
this.#$dialog.style.removeProperty("visibility");
|
|
3184
|
+
}
|
|
3133
3185
|
this.#$dialog.style.setProperty("left", `${clampedXPos}px`);
|
|
3134
3186
|
this.#$dialog.style.setProperty("top", `${clampedYPos}px`);
|
|
3135
|
-
if (
|
|
3187
|
+
if (updateWidth === true) {
|
|
3188
|
+
this.#$dialog.style.setProperty("width", `${modalWidth}px`);
|
|
3189
|
+
}
|
|
3190
|
+
if (!this.modal && !this.allowScroll) {
|
|
3136
3191
|
const targetLeftPos = targetRect.x - clampedXPos;
|
|
3137
3192
|
const targetTopPos = targetRect.y - clampedYPos;
|
|
3138
3193
|
this.#$targetOpenWrapper.style.setProperty("left", `${targetLeftPos}px`);
|
|
@@ -3147,49 +3202,12 @@ class Pop extends NectaryElement {
|
|
|
3147
3202
|
const shouldSetWidthToTarget = orient === "top-stretch" || orient === "bottom-stretch";
|
|
3148
3203
|
const modalHeight = modalRect.height;
|
|
3149
3204
|
const modalWidth = shouldSetWidthToTarget ? targetRect.width : modalRect.width;
|
|
3150
|
-
const inset = this.inset;
|
|
3151
|
-
let xPos = 0;
|
|
3152
|
-
let yPos = 0;
|
|
3153
3205
|
this.#modalHeight = modalHeight;
|
|
3154
3206
|
this.#modalWidth = modalWidth;
|
|
3155
|
-
|
|
3156
|
-
xPos = targetRect.x;
|
|
3157
|
-
}
|
|
3158
|
-
if (orient === "bottom-left" || orient === "top-left") {
|
|
3159
|
-
xPos = targetRect.x + targetRect.width - modalWidth;
|
|
3160
|
-
}
|
|
3161
|
-
if (orient === "bottom-center" || orient === "top-center") {
|
|
3162
|
-
xPos = targetRect.x + targetRect.width / 2 - modalWidth / 2;
|
|
3163
|
-
}
|
|
3164
|
-
if (orient === "center-right") {
|
|
3165
|
-
xPos = targetRect.x + targetRect.width;
|
|
3166
|
-
}
|
|
3167
|
-
if (orient === "center-left") {
|
|
3168
|
-
xPos = targetRect.x - modalWidth;
|
|
3169
|
-
}
|
|
3170
|
-
if (orient === "bottom-left" || orient === "bottom-right" || orient === "bottom-stretch" || orient === "bottom-center") {
|
|
3171
|
-
yPos = targetRect.y + targetRect.height;
|
|
3172
|
-
}
|
|
3173
|
-
if (orient === "top-left" || orient === "top-right" || orient === "top-stretch" || orient === "top-center") {
|
|
3174
|
-
yPos = targetRect.y - modalHeight;
|
|
3175
|
-
}
|
|
3176
|
-
if (orient === "center-left" || orient === "center-right") {
|
|
3177
|
-
yPos = targetRect.y + targetRect.height / 2 - modalHeight / 2;
|
|
3178
|
-
}
|
|
3179
|
-
xPos = Math.round(Math.max(inset, Math.min(xPos, window.innerWidth - modalWidth - inset)));
|
|
3180
|
-
yPos = Math.round(Math.max(inset, Math.min(yPos, window.innerHeight - modalHeight - inset)));
|
|
3181
|
-
this.#$dialog.style.setProperty("left", `${xPos}px`);
|
|
3182
|
-
this.#$dialog.style.setProperty("top", `${yPos}px`);
|
|
3183
|
-
this.#$dialog.style.setProperty("width", `${modalWidth}px`);
|
|
3184
|
-
if (!this.modal) {
|
|
3185
|
-
const targetLeftPos = targetRect.x - xPos;
|
|
3186
|
-
const targetTopPos = targetRect.y - yPos;
|
|
3187
|
-
this.#$targetOpenWrapper.style.setProperty("left", `${targetLeftPos}px`);
|
|
3188
|
-
this.#$targetOpenWrapper.style.setProperty("top", `${targetTopPos}px`);
|
|
3189
|
-
}
|
|
3207
|
+
this.#updatePosition(true);
|
|
3190
3208
|
};
|
|
3191
3209
|
#onBackdropMouseDown = (e) => {
|
|
3192
|
-
if (isTargetEqual(e, this.#$dialog)) {
|
|
3210
|
+
if (this.shouldCloseOnBackdropClick && isTargetEqual(e, this.#$dialog)) {
|
|
3193
3211
|
const rect = this.popoverRect;
|
|
3194
3212
|
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
3195
3213
|
if (!isInside) {
|
|
@@ -3245,6 +3263,14 @@ class Pop extends NectaryElement {
|
|
|
3245
3263
|
this.#updateOrientation();
|
|
3246
3264
|
}
|
|
3247
3265
|
};
|
|
3266
|
+
#isPopPointInViewport(x, y) {
|
|
3267
|
+
const inset = this.inset;
|
|
3268
|
+
const modalWidth = this.#modalWidth;
|
|
3269
|
+
const modalHeight = this.#modalHeight;
|
|
3270
|
+
const clampedX = Math.max(inset, Math.min(x, window.innerWidth - modalWidth - inset));
|
|
3271
|
+
const clampedY = Math.max(inset, Math.min(y, window.innerHeight - modalHeight - inset));
|
|
3272
|
+
return Math.abs(clampedX - x) > 2 || Math.abs(clampedY - y) > 2;
|
|
3273
|
+
}
|
|
3248
3274
|
}
|
|
3249
3275
|
defineCustomElement("sinch-pop", Pop);
|
|
3250
3276
|
class TooltipState {
|
|
@@ -3261,6 +3287,9 @@ class TooltipState {
|
|
|
3261
3287
|
};
|
|
3262
3288
|
}
|
|
3263
3289
|
show() {
|
|
3290
|
+
if (this.#options.isOpened === false) {
|
|
3291
|
+
return;
|
|
3292
|
+
}
|
|
3264
3293
|
switch (this.#state) {
|
|
3265
3294
|
case "hide": {
|
|
3266
3295
|
this.#switchToHideToShow();
|
|
@@ -3273,6 +3302,9 @@ class TooltipState {
|
|
|
3273
3302
|
}
|
|
3274
3303
|
}
|
|
3275
3304
|
hide() {
|
|
3305
|
+
if (this.#options.isOpened === true) {
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3276
3308
|
switch (this.#state) {
|
|
3277
3309
|
case "hide-to-show": {
|
|
3278
3310
|
this.#onHideAnimationEnd();
|
|
@@ -3325,13 +3357,15 @@ class TooltipState {
|
|
|
3325
3357
|
this.#options.onShowStart();
|
|
3326
3358
|
if (this.#options.showDelay === 0) {
|
|
3327
3359
|
this.#onSwitchToShow();
|
|
3360
|
+
} else if (this.#options.isOpened !== void 0) {
|
|
3361
|
+
this.#timerId = window.setTimeout(this.#onSwitchToShow, 100);
|
|
3328
3362
|
} else {
|
|
3329
3363
|
this.#timerId = window.setTimeout(this.#onSwitchToShow, this.#options.showDelay);
|
|
3330
3364
|
}
|
|
3331
3365
|
}
|
|
3332
3366
|
#switchToShowToHide(skipDelay, skipHideAnimation) {
|
|
3333
3367
|
this.#switchToState("show-to-hide");
|
|
3334
|
-
if (skipDelay === true || this.#options.hideDelay === 0) {
|
|
3368
|
+
if (skipDelay === true || this.#options.hideDelay === 0 || this.#options.isOpened !== void 0) {
|
|
3335
3369
|
this.#onShowToHideEnd(skipHideAnimation);
|
|
3336
3370
|
} else {
|
|
3337
3371
|
this.#timerId = window.setTimeout(this.#onShowToHideEnd, this.#options.hideDelay);
|
|
@@ -3393,7 +3427,7 @@ const getPopOrientation$1 = (orientation) => {
|
|
|
3393
3427
|
}
|
|
3394
3428
|
return orientation;
|
|
3395
3429
|
};
|
|
3396
|
-
const templateHTML$Q = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px;filter:drop-shadow(var(--sinch-comp-tooltip-shadow))}:host([orientation=left]) #content-wrapper{padding-bottom:0;padding-right:8px}:host([orientation=right]) #content-wrapper{padding-bottom:0;padding-left:8px}:host([orientation^=bottom]) #content-wrapper{padding-bottom:0;padding-top:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;background-color:var(--sinch-local-color-background);border-radius:var(--sinch-comp-tooltip-shape-radius);pointer-events:none;opacity:0;--sinch-local-color-background:var(--sinch-comp-tooltip-color-background);--sinch-global-color-text:var(--sinch-comp-tooltip-color-text)}#text{word-break:break-word;pointer-events:none;--sinch-comp-text-font:var(--sinch-comp-tooltip-font-body)}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-local-color-background);pointer-events:none}#tip.hidden{display:none}:host([orientation=left]) #tip{transform:translateX(-50%) rotate(270deg);top:50%;left:100%}:host([orientation=right]) #tip{transform:translateX(-50%) rotate(90deg);top:50%;left:0}:host([orientation^=bottom]) #tip{transform:translateX(-50%) rotate(180deg);top:0}:host([text-align=right]) #text{--sinch-comp-text-align:right}:host([text-align=center]) #text{--sinch-comp-text-align:center}:host([text-align=left]) #text{--sinch-comp-text-align:left}</style><sinch-pop id="pop"><slot id="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><sinch-text id="text" type="s"></sinch-text><svg id="tip" width="8" height="4" aria-hidden="true"><path d="m4 4 4-4h-8l4 4Z"/></svg></div></div></sinch-pop>';
|
|
3430
|
+
const templateHTML$Q = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px;filter:drop-shadow(var(--sinch-comp-tooltip-shadow))}:host([orientation=left]) #content-wrapper{padding-bottom:0;padding-right:8px}:host([orientation=right]) #content-wrapper{padding-bottom:0;padding-left:8px}:host([orientation^=bottom]) #content-wrapper{padding-bottom:0;padding-top:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;background-color:var(--sinch-local-color-background);border-radius:var(--sinch-comp-tooltip-shape-radius);pointer-events:none;opacity:0;--sinch-local-color-background:var(--sinch-comp-tooltip-color-background);--sinch-global-color-text:var(--sinch-comp-tooltip-color-text)}#text{word-break:break-word;pointer-events:none;--sinch-comp-text-font:var(--sinch-comp-tooltip-font-body)}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-local-color-background);pointer-events:none}#tip.hidden{display:none}:host([orientation=left]) #tip{transform:translateX(-50%) rotate(270deg);top:50%;left:100%}:host([orientation=right]) #tip{transform:translateX(-50%) rotate(90deg);top:50%;left:0}:host([orientation^=bottom]) #tip{transform:translateX(-50%) rotate(180deg);top:0}:host([text-align=right]) #text{--sinch-comp-text-align:right}:host([text-align=center]) #text{--sinch-comp-text-align:center}:host([text-align=left]) #text{--sinch-comp-text-align:left}</style><sinch-pop id="pop" allow-scroll hide-outside-viewport><slot id="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><sinch-text id="text" type="s"></sinch-text><svg id="tip" width="8" height="4" aria-hidden="true"><path d="m4 4 4-4h-8l4 4Z"/></svg></div></div></sinch-pop>';
|
|
3397
3431
|
const TIP_SIZE$1 = 8;
|
|
3398
3432
|
const SHOW_DELAY_SLOW = 1e3;
|
|
3399
3433
|
const SHOW_DELAY_FAST = 250;
|
|
@@ -3455,6 +3489,7 @@ class Tooltip extends NectaryElement {
|
|
|
3455
3489
|
}
|
|
3456
3490
|
static get observedAttributes() {
|
|
3457
3491
|
return [
|
|
3492
|
+
"is-opened",
|
|
3458
3493
|
"text",
|
|
3459
3494
|
"orientation",
|
|
3460
3495
|
"text-align",
|
|
@@ -3492,8 +3527,24 @@ class Tooltip extends NectaryElement {
|
|
|
3492
3527
|
updateAttribute(this.#$pop, name, newVal);
|
|
3493
3528
|
break;
|
|
3494
3529
|
}
|
|
3530
|
+
case "is-opened": {
|
|
3531
|
+
this.#tooltipState.updateOptions({
|
|
3532
|
+
isOpened: this.isOpenedControlled
|
|
3533
|
+
});
|
|
3534
|
+
if (this.isOpenedControlled === true) {
|
|
3535
|
+
updateBooleanAttribute(this.#$pop, "disable-backdrop-close", true);
|
|
3536
|
+
this.#tooltipState.show();
|
|
3537
|
+
} else if (this.isOpenedControlled === false) {
|
|
3538
|
+
updateBooleanAttribute(this.#$pop, "disable-backdrop-close", false);
|
|
3539
|
+
this.#tooltipState.hide();
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3495
3542
|
}
|
|
3496
3543
|
}
|
|
3544
|
+
get isOpenedControlled() {
|
|
3545
|
+
const isOpenedAttr = getAttribute(this, "is-opened");
|
|
3546
|
+
return isOpenedAttr === null ? void 0 : isOpenedAttr !== "false";
|
|
3547
|
+
}
|
|
3497
3548
|
get text() {
|
|
3498
3549
|
return getAttribute(this, "text", "");
|
|
3499
3550
|
}
|
|
@@ -3545,8 +3596,10 @@ class Tooltip extends NectaryElement {
|
|
|
3545
3596
|
};
|
|
3546
3597
|
// Tooltip begins to wait for SHOW_DELAY on mouseenter
|
|
3547
3598
|
#onStateShowStart = () => {
|
|
3548
|
-
this
|
|
3549
|
-
|
|
3599
|
+
if (this.isOpenedControlled === void 0) {
|
|
3600
|
+
this.#subscribeScroll();
|
|
3601
|
+
this.#subscribeMouseLeaveEvents();
|
|
3602
|
+
}
|
|
3550
3603
|
};
|
|
3551
3604
|
// SHOW_DELAY ended, tooltip can be shown with animation
|
|
3552
3605
|
#onStateShowEnd = () => {
|
|
@@ -5790,7 +5843,7 @@ class Popover extends NectaryElement {
|
|
|
5790
5843
|
const slottedContent = this.#getFirstAssignedElementInSlot(this.#$contentSlot);
|
|
5791
5844
|
const popoverIdReference = `popover-${(/* @__PURE__ */ new Date()).getMilliseconds()}-${Math.round(100 * Math.random())}`;
|
|
5792
5845
|
if (slottedContent != null && slottedTarget != null) {
|
|
5793
|
-
updateAttribute(slottedContent, "aria-
|
|
5846
|
+
updateAttribute(slottedContent, "aria-labelledby", popoverIdReference);
|
|
5794
5847
|
}
|
|
5795
5848
|
if (slottedTarget != null) {
|
|
5796
5849
|
updateAttribute(slottedTarget, "aria-controls", popoverIdReference);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nectary/components",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.1",
|
|
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.1.
|
|
27
|
+
"@nectary/assets": "3.1.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@babel/cli": "^7.22.15",
|
package/pop/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export declare class Pop extends NectaryElement {
|
|
|
8
8
|
connectedCallback(): void;
|
|
9
9
|
disconnectedCallback(): void;
|
|
10
10
|
static get observedAttributes(): string[];
|
|
11
|
+
get allowScroll(): boolean;
|
|
12
|
+
get hideOutsideViewport(): boolean;
|
|
11
13
|
set modal(isModal: boolean);
|
|
12
14
|
get modal(): boolean;
|
|
13
15
|
set open(isOpen: boolean);
|
|
@@ -18,5 +20,6 @@ export declare class Pop extends NectaryElement {
|
|
|
18
20
|
get inset(): number;
|
|
19
21
|
get footprintRect(): TRect;
|
|
20
22
|
get popoverRect(): TRect;
|
|
23
|
+
get shouldCloseOnBackdropClick(): boolean;
|
|
21
24
|
attributeChangedCallback(name: string, oldVal: string | null, newVal: string | null): void;
|
|
22
25
|
}
|
package/pop/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, subscribeContext } from "../utils/context.js";
|
|
2
|
-
import { getBooleanAttribute, updateBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateIntegerAttribute, getIntegerAttribute, isAttrEqual, isAttrTrue } from "../utils/dom.js";
|
|
2
|
+
import { getBooleanAttribute, updateBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateIntegerAttribute, getIntegerAttribute, isAttrEqual, getScrollableParents, 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";
|
|
@@ -27,6 +27,7 @@ class Pop extends NectaryElement {
|
|
|
27
27
|
#targetStyleValue = null;
|
|
28
28
|
#modalWidth = 0;
|
|
29
29
|
#modalHeight = 0;
|
|
30
|
+
#scrollableParents = [];
|
|
30
31
|
constructor() {
|
|
31
32
|
super();
|
|
32
33
|
const shadowRoot = this.attachShadow();
|
|
@@ -77,6 +78,12 @@ class Pop extends NectaryElement {
|
|
|
77
78
|
"open"
|
|
78
79
|
];
|
|
79
80
|
}
|
|
81
|
+
get allowScroll() {
|
|
82
|
+
return getBooleanAttribute(this, "allow-scroll");
|
|
83
|
+
}
|
|
84
|
+
get hideOutsideViewport() {
|
|
85
|
+
return getBooleanAttribute(this, "hide-outside-viewport");
|
|
86
|
+
}
|
|
80
87
|
set modal(isModal) {
|
|
81
88
|
updateBooleanAttribute(this, "modal", isModal);
|
|
82
89
|
}
|
|
@@ -107,6 +114,9 @@ class Pop extends NectaryElement {
|
|
|
107
114
|
get popoverRect() {
|
|
108
115
|
return getRect(this.#$dialog);
|
|
109
116
|
}
|
|
117
|
+
get shouldCloseOnBackdropClick() {
|
|
118
|
+
return !getBooleanAttribute(this, "disable-backdrop-close");
|
|
119
|
+
}
|
|
110
120
|
attributeChangedCallback(name, oldVal, newVal) {
|
|
111
121
|
if (isAttrEqual(oldVal, newVal)) {
|
|
112
122
|
return;
|
|
@@ -167,57 +177,71 @@ class Pop extends NectaryElement {
|
|
|
167
177
|
this.#$targetSlot.removeEventListener("blur", this.#stopEventPropagation, true);
|
|
168
178
|
this.#$focus.removeAttribute("tabindex");
|
|
169
179
|
this.#$focus.removeAttribute("style");
|
|
170
|
-
this
|
|
180
|
+
if (this.modal || !this.allowScroll) {
|
|
181
|
+
this.#$dialog.showModal();
|
|
182
|
+
} else {
|
|
183
|
+
this.#$dialog.show();
|
|
184
|
+
}
|
|
171
185
|
this.#$targetWrapper.setAttribute("aria-expanded", "true");
|
|
172
186
|
this.#updateOrientation();
|
|
173
187
|
this.#resizeObserver.observe(this.#$dialog);
|
|
174
188
|
if (this.modal) {
|
|
175
189
|
getFirstFocusableElement(this.#$contentSlot)?.focus();
|
|
176
190
|
} else {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
if (!this.allowScroll) {
|
|
192
|
+
const $targetEl = this.#getFirstTargetElement(this.#$targetSlot);
|
|
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");
|
|
195
212
|
}
|
|
196
|
-
|
|
197
|
-
|
|
213
|
+
const activeSlot = this.allowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
214
|
+
activeSlot.addEventListener("keydown", this.#onTargetKeydown);
|
|
198
215
|
if (this.#targetActiveElement !== null) {
|
|
199
|
-
|
|
216
|
+
activeSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
200
217
|
this.#targetActiveElement.focus();
|
|
201
|
-
|
|
218
|
+
activeSlot.removeEventListener("focus", this.#stopEventPropagation, true);
|
|
202
219
|
if (!isElementFocused(this.#targetActiveElement)) {
|
|
203
220
|
requestAnimationFrame(() => {
|
|
204
221
|
if (this.isDomConnected && this.#$dialog.open) {
|
|
205
|
-
|
|
222
|
+
activeSlot.addEventListener("focus", this.#stopEventPropagation, true);
|
|
206
223
|
this.#targetActiveElement.focus();
|
|
207
|
-
|
|
224
|
+
activeSlot.removeEventListener("focus", this.#stopEventPropagation, true);
|
|
208
225
|
}
|
|
209
226
|
});
|
|
210
227
|
}
|
|
211
228
|
}
|
|
212
229
|
}
|
|
213
|
-
|
|
214
|
-
|
|
230
|
+
if (!this.allowScroll) {
|
|
231
|
+
disableOverscroll();
|
|
232
|
+
} else {
|
|
233
|
+
this.#scrollableParents = getScrollableParents(this.#getFirstTargetElement(this.#$targetSlot));
|
|
234
|
+
this.#scrollableParents.forEach((el) => {
|
|
235
|
+
el.addEventListener("scroll", () => this.#updatePosition(false), { passive: true, capture: true });
|
|
236
|
+
});
|
|
237
|
+
}
|
|
215
238
|
window.addEventListener("resize", this.#onResize);
|
|
216
239
|
requestAnimationFrame(() => {
|
|
217
240
|
if (this.isDomConnected && this.#$dialog.open) {
|
|
218
241
|
this.#$contentSlot.addEventListener("slotchange", this.#onContentSlotChange);
|
|
219
242
|
}
|
|
220
243
|
});
|
|
244
|
+
requestAnimationFrame(() => this.#updatePosition());
|
|
221
245
|
this.#dispatchContentVisibility(true);
|
|
222
246
|
}
|
|
223
247
|
#onCollapse() {
|
|
@@ -226,17 +250,18 @@ class Pop extends NectaryElement {
|
|
|
226
250
|
}
|
|
227
251
|
this.#resizeObserver.disconnect();
|
|
228
252
|
const isNonModal = !this.modal;
|
|
253
|
+
const activeSlot = this.allowScroll ? this.#$targetSlot : this.#$targetOpenSlot;
|
|
229
254
|
this.#dispatchContentVisibility(false);
|
|
230
|
-
|
|
255
|
+
activeSlot.removeEventListener("keydown", this.#onTargetKeydown);
|
|
231
256
|
if (isNonModal) {
|
|
232
|
-
|
|
257
|
+
activeSlot.addEventListener("blur", this.#captureActiveElement, true);
|
|
233
258
|
}
|
|
234
259
|
this.#$dialog.close();
|
|
235
260
|
this.#$targetWrapper.setAttribute("aria-expanded", "false");
|
|
236
261
|
if (isNonModal) {
|
|
237
|
-
|
|
262
|
+
activeSlot.removeEventListener("blur", this.#captureActiveElement, true);
|
|
238
263
|
}
|
|
239
|
-
if (isNonModal) {
|
|
264
|
+
if (isNonModal && !this.allowScroll) {
|
|
240
265
|
const targetEl = this.#getFirstTargetElement(this.#$targetOpenSlot);
|
|
241
266
|
targetEl.style.removeProperty("margin");
|
|
242
267
|
targetEl.style.removeProperty("position");
|
|
@@ -268,17 +293,23 @@ class Pop extends NectaryElement {
|
|
|
268
293
|
this.#targetActiveElement = null;
|
|
269
294
|
}
|
|
270
295
|
}
|
|
271
|
-
|
|
296
|
+
if (!this.allowScroll) {
|
|
297
|
+
enableOverscroll();
|
|
298
|
+
} else {
|
|
299
|
+
this.#scrollableParents.forEach((el) => {
|
|
300
|
+
el.removeEventListener("scroll", () => this.#updatePosition(false), { capture: true });
|
|
301
|
+
});
|
|
302
|
+
}
|
|
272
303
|
this.#resizeThrottle.cancel();
|
|
273
304
|
window.removeEventListener("resize", this.#onResize);
|
|
274
|
-
|
|
305
|
+
this.#scrollableParents = [];
|
|
275
306
|
this.#$contentSlot.removeEventListener("slotchange", this.#onContentSlotChange);
|
|
276
307
|
}
|
|
277
308
|
#onResize = () => {
|
|
278
309
|
this.#resizeThrottle.fn();
|
|
279
310
|
};
|
|
280
|
-
#updatePosition = () => {
|
|
281
|
-
const targetRect = this.modal ? this.#getTargetRect() : this.#$targetWrapper.getBoundingClientRect();
|
|
311
|
+
#updatePosition = (updateWidth) => {
|
|
312
|
+
const targetRect = this.modal || this.allowScroll ? this.#getTargetRect() : this.#$targetWrapper.getBoundingClientRect();
|
|
282
313
|
const orient = this.orientation;
|
|
283
314
|
const modalWidth = this.#modalWidth;
|
|
284
315
|
const modalHeight = this.#modalHeight;
|
|
@@ -311,9 +342,17 @@ class Pop extends NectaryElement {
|
|
|
311
342
|
}
|
|
312
343
|
const clampedXPos = Math.max(inset, Math.min(xPos, window.innerWidth - modalWidth - inset));
|
|
313
344
|
const clampedYPos = Math.max(inset, Math.min(yPos, window.innerHeight - modalHeight - inset));
|
|
345
|
+
if (this.hideOutsideViewport && this.#isPopPointInViewport(xPos, yPos)) {
|
|
346
|
+
this.#$dialog.style.setProperty("visibility", "hidden");
|
|
347
|
+
} else {
|
|
348
|
+
this.#$dialog.style.removeProperty("visibility");
|
|
349
|
+
}
|
|
314
350
|
this.#$dialog.style.setProperty("left", `${clampedXPos}px`);
|
|
315
351
|
this.#$dialog.style.setProperty("top", `${clampedYPos}px`);
|
|
316
|
-
if (
|
|
352
|
+
if (updateWidth === true) {
|
|
353
|
+
this.#$dialog.style.setProperty("width", `${modalWidth}px`);
|
|
354
|
+
}
|
|
355
|
+
if (!this.modal && !this.allowScroll) {
|
|
317
356
|
const targetLeftPos = targetRect.x - clampedXPos;
|
|
318
357
|
const targetTopPos = targetRect.y - clampedYPos;
|
|
319
358
|
this.#$targetOpenWrapper.style.setProperty("left", `${targetLeftPos}px`);
|
|
@@ -328,49 +367,12 @@ class Pop extends NectaryElement {
|
|
|
328
367
|
const shouldSetWidthToTarget = orient === "top-stretch" || orient === "bottom-stretch";
|
|
329
368
|
const modalHeight = modalRect.height;
|
|
330
369
|
const modalWidth = shouldSetWidthToTarget ? targetRect.width : modalRect.width;
|
|
331
|
-
const inset = this.inset;
|
|
332
|
-
let xPos = 0;
|
|
333
|
-
let yPos = 0;
|
|
334
370
|
this.#modalHeight = modalHeight;
|
|
335
371
|
this.#modalWidth = modalWidth;
|
|
336
|
-
|
|
337
|
-
xPos = targetRect.x;
|
|
338
|
-
}
|
|
339
|
-
if (orient === "bottom-left" || orient === "top-left") {
|
|
340
|
-
xPos = targetRect.x + targetRect.width - modalWidth;
|
|
341
|
-
}
|
|
342
|
-
if (orient === "bottom-center" || orient === "top-center") {
|
|
343
|
-
xPos = targetRect.x + targetRect.width / 2 - modalWidth / 2;
|
|
344
|
-
}
|
|
345
|
-
if (orient === "center-right") {
|
|
346
|
-
xPos = targetRect.x + targetRect.width;
|
|
347
|
-
}
|
|
348
|
-
if (orient === "center-left") {
|
|
349
|
-
xPos = targetRect.x - modalWidth;
|
|
350
|
-
}
|
|
351
|
-
if (orient === "bottom-left" || orient === "bottom-right" || orient === "bottom-stretch" || orient === "bottom-center") {
|
|
352
|
-
yPos = targetRect.y + targetRect.height;
|
|
353
|
-
}
|
|
354
|
-
if (orient === "top-left" || orient === "top-right" || orient === "top-stretch" || orient === "top-center") {
|
|
355
|
-
yPos = targetRect.y - modalHeight;
|
|
356
|
-
}
|
|
357
|
-
if (orient === "center-left" || orient === "center-right") {
|
|
358
|
-
yPos = targetRect.y + targetRect.height / 2 - modalHeight / 2;
|
|
359
|
-
}
|
|
360
|
-
xPos = Math.round(Math.max(inset, Math.min(xPos, window.innerWidth - modalWidth - inset)));
|
|
361
|
-
yPos = Math.round(Math.max(inset, Math.min(yPos, window.innerHeight - modalHeight - inset)));
|
|
362
|
-
this.#$dialog.style.setProperty("left", `${xPos}px`);
|
|
363
|
-
this.#$dialog.style.setProperty("top", `${yPos}px`);
|
|
364
|
-
this.#$dialog.style.setProperty("width", `${modalWidth}px`);
|
|
365
|
-
if (!this.modal) {
|
|
366
|
-
const targetLeftPos = targetRect.x - xPos;
|
|
367
|
-
const targetTopPos = targetRect.y - yPos;
|
|
368
|
-
this.#$targetOpenWrapper.style.setProperty("left", `${targetLeftPos}px`);
|
|
369
|
-
this.#$targetOpenWrapper.style.setProperty("top", `${targetTopPos}px`);
|
|
370
|
-
}
|
|
372
|
+
this.#updatePosition(true);
|
|
371
373
|
};
|
|
372
374
|
#onBackdropMouseDown = (e) => {
|
|
373
|
-
if (isTargetEqual(e, this.#$dialog)) {
|
|
375
|
+
if (this.shouldCloseOnBackdropClick && isTargetEqual(e, this.#$dialog)) {
|
|
374
376
|
const rect = this.popoverRect;
|
|
375
377
|
const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
|
|
376
378
|
if (!isInside) {
|
|
@@ -426,6 +428,14 @@ class Pop extends NectaryElement {
|
|
|
426
428
|
this.#updateOrientation();
|
|
427
429
|
}
|
|
428
430
|
};
|
|
431
|
+
#isPopPointInViewport(x, y) {
|
|
432
|
+
const inset = this.inset;
|
|
433
|
+
const modalWidth = this.#modalWidth;
|
|
434
|
+
const modalHeight = this.#modalHeight;
|
|
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;
|
|
438
|
+
}
|
|
429
439
|
}
|
|
430
440
|
defineCustomElement("sinch-pop", Pop);
|
|
431
441
|
export {
|
package/pop/types.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { NectaryComponentReactByType, NectaryComponentVanillaByType, TRect, NectaryComponentReact, NectaryComponentVanilla } from '../types';
|
|
2
2
|
export type TSinchPopOrientation = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom-center' | 'bottom-stretch' | 'top-center' | 'top-stretch' | 'center-right' | 'center-left';
|
|
3
3
|
export type TSinchPopProps = {
|
|
4
|
+
/** Allow scrolling of the page when pop is open */
|
|
5
|
+
'allow-scroll'?: boolean;
|
|
4
6
|
/** Open/close state */
|
|
5
7
|
open: boolean;
|
|
6
8
|
/** Orientation, where it *points to* from origin */
|
|
7
9
|
orientation: TSinchPopOrientation;
|
|
10
|
+
'hide-outside-viewport'?: boolean;
|
|
8
11
|
/** Modal/non-modal mode */
|
|
9
12
|
modal?: boolean;
|
|
10
13
|
inset?: number;
|
package/popover/index.js
CHANGED
|
@@ -38,7 +38,7 @@ class Popover extends NectaryElement {
|
|
|
38
38
|
const slottedContent = this.#getFirstAssignedElementInSlot(this.#$contentSlot);
|
|
39
39
|
const popoverIdReference = `popover-${(/* @__PURE__ */ new Date()).getMilliseconds()}-${Math.round(100 * Math.random())}`;
|
|
40
40
|
if (slottedContent != null && slottedTarget != null) {
|
|
41
|
-
updateAttribute(slottedContent, "aria-
|
|
41
|
+
updateAttribute(slottedContent, "aria-labelledby", popoverIdReference);
|
|
42
42
|
}
|
|
43
43
|
if (slottedTarget != null) {
|
|
44
44
|
updateAttribute(slottedTarget, "aria-controls", popoverIdReference);
|
package/select-menu/types.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export type TSinchSelectMenuProps = {
|
|
|
10
10
|
multiple?: boolean;
|
|
11
11
|
/** Enforce the search bar appearing, by default it appears above a certain number of options */
|
|
12
12
|
searchable?: boolean | null;
|
|
13
|
+
/** Value for the search input */
|
|
14
|
+
'search-value'?: string;
|
|
13
15
|
/** Controls the autocomplete of the search input */
|
|
14
16
|
'search-autocomplete'?: HTMLInputElement['autocomplete'];
|
|
15
17
|
/** Text for search bar's placeholder */
|
package/tooltip/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class Tooltip extends NectaryElement {
|
|
|
11
11
|
disconnectedCallback(): void;
|
|
12
12
|
static get observedAttributes(): string[];
|
|
13
13
|
attributeChangedCallback(name: string, _: string | null, newVal: string | null): void;
|
|
14
|
+
get isOpenedControlled(): boolean | undefined;
|
|
14
15
|
get text(): string;
|
|
15
16
|
set text(value: string);
|
|
16
17
|
get orientation(): TSinchTooltipOrientation;
|
package/tooltip/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import "../text/index.js";
|
|
2
2
|
import "../pop/index.js";
|
|
3
|
-
import { shouldReduceMotion, updateAttribute, getAttribute, getLiteralAttribute, updateLiteralAttribute,
|
|
3
|
+
import { shouldReduceMotion, updateAttribute, updateBooleanAttribute, getAttribute, getLiteralAttribute, updateLiteralAttribute, setClass } from "../utils/dom.js";
|
|
4
4
|
import { defineCustomElement, NectaryElement } from "../utils/element.js";
|
|
5
5
|
import { rectOverlap } from "../utils/rect.js";
|
|
6
6
|
import { getReactEventHandler } from "../utils/get-react-event-handler.js";
|
|
7
7
|
import { TooltipState } from "./tooltip-state.js";
|
|
8
8
|
import { getPopOrientation, orientationValues, textAlignValues, typeValues } from "./utils.js";
|
|
9
|
-
const templateHTML = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px;filter:drop-shadow(var(--sinch-comp-tooltip-shadow))}:host([orientation=left]) #content-wrapper{padding-bottom:0;padding-right:8px}:host([orientation=right]) #content-wrapper{padding-bottom:0;padding-left:8px}:host([orientation^=bottom]) #content-wrapper{padding-bottom:0;padding-top:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;background-color:var(--sinch-local-color-background);border-radius:var(--sinch-comp-tooltip-shape-radius);pointer-events:none;opacity:0;--sinch-local-color-background:var(--sinch-comp-tooltip-color-background);--sinch-global-color-text:var(--sinch-comp-tooltip-color-text)}#text{word-break:break-word;pointer-events:none;--sinch-comp-text-font:var(--sinch-comp-tooltip-font-body)}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-local-color-background);pointer-events:none}#tip.hidden{display:none}:host([orientation=left]) #tip{transform:translateX(-50%) rotate(270deg);top:50%;left:100%}:host([orientation=right]) #tip{transform:translateX(-50%) rotate(90deg);top:50%;left:0}:host([orientation^=bottom]) #tip{transform:translateX(-50%) rotate(180deg);top:0}:host([text-align=right]) #text{--sinch-comp-text-align:right}:host([text-align=center]) #text{--sinch-comp-text-align:center}:host([text-align=left]) #text{--sinch-comp-text-align:left}</style><sinch-pop id="pop"><slot id="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><sinch-text id="text" type="s"></sinch-text><svg id="tip" width="8" height="4" aria-hidden="true"><path d="m4 4 4-4h-8l4 4Z"/></svg></div></div></sinch-pop>';
|
|
9
|
+
const templateHTML = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px;filter:drop-shadow(var(--sinch-comp-tooltip-shadow))}:host([orientation=left]) #content-wrapper{padding-bottom:0;padding-right:8px}:host([orientation=right]) #content-wrapper{padding-bottom:0;padding-left:8px}:host([orientation^=bottom]) #content-wrapper{padding-bottom:0;padding-top:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;background-color:var(--sinch-local-color-background);border-radius:var(--sinch-comp-tooltip-shape-radius);pointer-events:none;opacity:0;--sinch-local-color-background:var(--sinch-comp-tooltip-color-background);--sinch-global-color-text:var(--sinch-comp-tooltip-color-text)}#text{word-break:break-word;pointer-events:none;--sinch-comp-text-font:var(--sinch-comp-tooltip-font-body)}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-local-color-background);pointer-events:none}#tip.hidden{display:none}:host([orientation=left]) #tip{transform:translateX(-50%) rotate(270deg);top:50%;left:100%}:host([orientation=right]) #tip{transform:translateX(-50%) rotate(90deg);top:50%;left:0}:host([orientation^=bottom]) #tip{transform:translateX(-50%) rotate(180deg);top:0}:host([text-align=right]) #text{--sinch-comp-text-align:right}:host([text-align=center]) #text{--sinch-comp-text-align:center}:host([text-align=left]) #text{--sinch-comp-text-align:left}</style><sinch-pop id="pop" allow-scroll hide-outside-viewport><slot id="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><sinch-text id="text" type="s"></sinch-text><svg id="tip" width="8" height="4" aria-hidden="true"><path d="m4 4 4-4h-8l4 4Z"/></svg></div></div></sinch-pop>';
|
|
10
10
|
const TIP_SIZE = 8;
|
|
11
11
|
const SHOW_DELAY_SLOW = 1e3;
|
|
12
12
|
const SHOW_DELAY_FAST = 250;
|
|
@@ -68,6 +68,7 @@ class Tooltip extends NectaryElement {
|
|
|
68
68
|
}
|
|
69
69
|
static get observedAttributes() {
|
|
70
70
|
return [
|
|
71
|
+
"is-opened",
|
|
71
72
|
"text",
|
|
72
73
|
"orientation",
|
|
73
74
|
"text-align",
|
|
@@ -105,8 +106,24 @@ class Tooltip extends NectaryElement {
|
|
|
105
106
|
updateAttribute(this.#$pop, name, newVal);
|
|
106
107
|
break;
|
|
107
108
|
}
|
|
109
|
+
case "is-opened": {
|
|
110
|
+
this.#tooltipState.updateOptions({
|
|
111
|
+
isOpened: this.isOpenedControlled
|
|
112
|
+
});
|
|
113
|
+
if (this.isOpenedControlled === true) {
|
|
114
|
+
updateBooleanAttribute(this.#$pop, "disable-backdrop-close", true);
|
|
115
|
+
this.#tooltipState.show();
|
|
116
|
+
} else if (this.isOpenedControlled === false) {
|
|
117
|
+
updateBooleanAttribute(this.#$pop, "disable-backdrop-close", false);
|
|
118
|
+
this.#tooltipState.hide();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
108
121
|
}
|
|
109
122
|
}
|
|
123
|
+
get isOpenedControlled() {
|
|
124
|
+
const isOpenedAttr = getAttribute(this, "is-opened");
|
|
125
|
+
return isOpenedAttr === null ? void 0 : isOpenedAttr !== "false";
|
|
126
|
+
}
|
|
110
127
|
get text() {
|
|
111
128
|
return getAttribute(this, "text", "");
|
|
112
129
|
}
|
|
@@ -158,8 +175,10 @@ class Tooltip extends NectaryElement {
|
|
|
158
175
|
};
|
|
159
176
|
// Tooltip begins to wait for SHOW_DELAY on mouseenter
|
|
160
177
|
#onStateShowStart = () => {
|
|
161
|
-
this
|
|
162
|
-
|
|
178
|
+
if (this.isOpenedControlled === void 0) {
|
|
179
|
+
this.#subscribeScroll();
|
|
180
|
+
this.#subscribeMouseLeaveEvents();
|
|
181
|
+
}
|
|
163
182
|
};
|
|
164
183
|
// SHOW_DELAY ended, tooltip can be shown with animation
|
|
165
184
|
#onStateShowEnd = () => {
|
package/tooltip/tooltip-state.js
CHANGED
|
@@ -12,6 +12,9 @@ class TooltipState {
|
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
14
|
show() {
|
|
15
|
+
if (this.#options.isOpened === false) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
15
18
|
switch (this.#state) {
|
|
16
19
|
case "hide": {
|
|
17
20
|
this.#switchToHideToShow();
|
|
@@ -24,6 +27,9 @@ class TooltipState {
|
|
|
24
27
|
}
|
|
25
28
|
}
|
|
26
29
|
hide() {
|
|
30
|
+
if (this.#options.isOpened === true) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
27
33
|
switch (this.#state) {
|
|
28
34
|
case "hide-to-show": {
|
|
29
35
|
this.#onHideAnimationEnd();
|
|
@@ -76,13 +82,15 @@ class TooltipState {
|
|
|
76
82
|
this.#options.onShowStart();
|
|
77
83
|
if (this.#options.showDelay === 0) {
|
|
78
84
|
this.#onSwitchToShow();
|
|
85
|
+
} else if (this.#options.isOpened !== void 0) {
|
|
86
|
+
this.#timerId = window.setTimeout(this.#onSwitchToShow, 100);
|
|
79
87
|
} else {
|
|
80
88
|
this.#timerId = window.setTimeout(this.#onSwitchToShow, this.#options.showDelay);
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
#switchToShowToHide(skipDelay, skipHideAnimation) {
|
|
84
92
|
this.#switchToState("show-to-hide");
|
|
85
|
-
if (skipDelay === true || this.#options.hideDelay === 0) {
|
|
93
|
+
if (skipDelay === true || this.#options.hideDelay === 0 || this.#options.isOpened !== void 0) {
|
|
86
94
|
this.#onShowToHideEnd(skipHideAnimation);
|
|
87
95
|
} else {
|
|
88
96
|
this.#timerId = window.setTimeout(this.#onShowToHideEnd, this.#options.hideDelay);
|
package/tooltip/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export type TSinchTooltipOrientation = 'top' | 'bottom' | 'left' | 'right' | 'to
|
|
|
3
3
|
export type TSinchTooltipTextAlign = 'center' | 'right' | 'left';
|
|
4
4
|
export type TSinchTooltipType = 'slow' | 'fast';
|
|
5
5
|
export type TSinchTooltipProps = {
|
|
6
|
+
'is-opened'?: string;
|
|
6
7
|
/** Text */
|
|
7
8
|
text: string;
|
|
8
9
|
/** Orientation, where it *points to* from origin */
|
package/utils/dom.d.ts
CHANGED
|
@@ -32,4 +32,5 @@ export declare const getCssVars: (element: Element, variableNames: string[]) =>
|
|
|
32
32
|
export declare const cloneNode: (el: Element, deep: boolean) => Element;
|
|
33
33
|
export declare const shouldReduceMotion: () => boolean;
|
|
34
34
|
export declare const isAttrEqual: (oldVal: string | null, newVal: string | null) => boolean;
|
|
35
|
+
export declare const getScrollableParents: (node: HTMLElement | null) => (HTMLElement | Document)[];
|
|
35
36
|
export {};
|
package/utils/dom.js
CHANGED
|
@@ -137,6 +137,22 @@ const shouldReduceMotion = () => window.matchMedia("(prefers-reduced-motion: red
|
|
|
137
137
|
const isAttrEqual = (oldVal, newVal) => {
|
|
138
138
|
return oldVal === newVal || newVal === null && oldVal === "false" || newVal === "" && oldVal === "true";
|
|
139
139
|
};
|
|
140
|
+
const getScrollableParents = (node) => {
|
|
141
|
+
const scrollableParents = [];
|
|
142
|
+
if (node == null) {
|
|
143
|
+
return scrollableParents;
|
|
144
|
+
}
|
|
145
|
+
let parent = node.parentElement;
|
|
146
|
+
while (parent != null) {
|
|
147
|
+
const computedStyle = getComputedStyle(parent);
|
|
148
|
+
if ((parent.scrollHeight > parent.clientHeight || parent.scrollWidth > parent.clientWidth) && (computedStyle.overflow === "auto" || computedStyle.overflow === "scroll" || computedStyle.overflowY === "auto" || computedStyle.overflowY === "scroll" || computedStyle.overflowX === "auto" || computedStyle.overflowX === "scroll")) {
|
|
149
|
+
scrollableParents.push(parent);
|
|
150
|
+
}
|
|
151
|
+
parent = parent.parentElement;
|
|
152
|
+
}
|
|
153
|
+
scrollableParents.push(document);
|
|
154
|
+
return scrollableParents;
|
|
155
|
+
};
|
|
140
156
|
export {
|
|
141
157
|
attrValueToInteger,
|
|
142
158
|
attrValueToPixels,
|
|
@@ -148,6 +164,7 @@ export {
|
|
|
148
164
|
getCssVars,
|
|
149
165
|
getIntegerAttribute,
|
|
150
166
|
getLiteralAttribute,
|
|
167
|
+
getScrollableParents,
|
|
151
168
|
hasClass,
|
|
152
169
|
isAttrEqual,
|
|
153
170
|
isAttrTrue,
|
package/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, subscribeContext } from "./context.js";
|
|
2
2
|
import { CSV_DELIMITER, getFirstCsvValue, packCsv, unpackCsv, updateCsv } from "./csv.js";
|
|
3
|
-
import { attrValueToInteger, attrValueToPixels, clampNumber, cloneNode, getAttribute, getBooleanAttribute, getCssVar, getCssVars, getIntegerAttribute, getLiteralAttribute, hasClass, isAttrEqual, isAttrTrue, isLiteralValue, setClass, shouldReduceMotion, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateIntegerAttribute, updateLiteralAttribute } from "./dom.js";
|
|
3
|
+
import { attrValueToInteger, attrValueToPixels, clampNumber, cloneNode, getAttribute, getBooleanAttribute, getCssVar, getCssVars, getIntegerAttribute, getLiteralAttribute, getScrollableParents, hasClass, isAttrEqual, isAttrTrue, isLiteralValue, setClass, shouldReduceMotion, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateIntegerAttribute, updateLiteralAttribute } from "./dom.js";
|
|
4
4
|
import { NectaryElement, defineCustomElement, pascalToKebabCase, registerComponent, resetNectaryRegistry, setNectaryRegistry } from "./element.js";
|
|
5
5
|
import { getRect, getTargetRect, rectOverlap } from "./rect.js";
|
|
6
6
|
import { getFirstFocusableElement, getFirstSlotElement, isElementFocused } from "./slot.js";
|
|
@@ -32,6 +32,7 @@ export {
|
|
|
32
32
|
getLiteralAttribute,
|
|
33
33
|
getReactEventHandler,
|
|
34
34
|
getRect,
|
|
35
|
+
getScrollableParents,
|
|
35
36
|
getTargetAttribute,
|
|
36
37
|
getTargetByAttribute,
|
|
37
38
|
getTargetIndexInParent,
|