@nectary/components 1.2.2 → 1.3.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/package.json +1 -1
- package/pop/index.js +7 -11
- package/skeleton/index.d.ts +11 -0
- package/skeleton/index.js +75 -0
- package/skeleton/types.d.ts +9 -0
- package/skeleton/types.js +1 -0
- package/skeleton-item/index.d.ts +11 -0
- package/skeleton-item/index.js +38 -0
- package/skeleton-item/types.d.ts +13 -0
- package/skeleton-item/types.js +1 -0
- package/theme/chip.css +6 -0
- package/theme/tag.css +6 -0
- package/toast-manager/index.js +3 -3
- package/tooltip/index.js +22 -21
- package/tooltip/tooltip-state.d.ts +2 -1
- package/tooltip/tooltip-state.js +50 -48
- package/utils/dom.d.ts +1 -0
- package/utils/dom.js +2 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +2 -1
- package/utils/uid.d.ts +1 -0
- package/utils/uid.js +13 -0
package/package.json
CHANGED
package/pop/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, subscribeContext, isTargetEqual } from '../utils';
|
|
2
|
-
const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;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"
|
|
2
|
+
const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;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>';
|
|
3
3
|
import { assertOrientation, disableScroll, enableScroll, orientationValues } from './utils';
|
|
4
4
|
const template = document.createElement('template');
|
|
5
5
|
template.innerHTML = templateHTML;
|
|
@@ -153,13 +153,13 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
|
|
|
153
153
|
if (!this.isConnected || this.#isOpen()) {
|
|
154
154
|
return;
|
|
155
155
|
}
|
|
156
|
-
this.#$targetSlot.addEventListener('blur', this.#
|
|
157
|
-
this.#$focus.
|
|
158
|
-
this.#$focus.
|
|
156
|
+
this.#$targetSlot.addEventListener('blur', this.#captureActiveElement, true);
|
|
157
|
+
this.#$focus.setAttribute('tabindex', '-1');
|
|
158
|
+
this.#$focus.style.display = 'block';
|
|
159
159
|
this.#$focus.focus();
|
|
160
|
-
this.#$
|
|
161
|
-
this.#$focus.
|
|
162
|
-
this.#$
|
|
160
|
+
this.#$targetSlot.removeEventListener('blur', this.#captureActiveElement, true);
|
|
161
|
+
this.#$focus.removeAttribute('tabindex');
|
|
162
|
+
this.#$focus.removeAttribute('style');
|
|
163
163
|
this.#$dialog.showModal();
|
|
164
164
|
this.#$target.setAttribute('aria-expanded', 'true');
|
|
165
165
|
this.#updateOrientation();
|
|
@@ -336,10 +336,6 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
|
|
|
336
336
|
#dispatchCloseEvent() {
|
|
337
337
|
this.dispatchEvent(new CustomEvent('-close'));
|
|
338
338
|
}
|
|
339
|
-
#captureRelatedActiveElement = e => {
|
|
340
|
-
e.stopPropagation();
|
|
341
|
-
this.#targetActiveElement = e.relatedTarget;
|
|
342
|
-
};
|
|
343
339
|
#captureActiveElement = e => {
|
|
344
340
|
e.stopPropagation();
|
|
345
341
|
this.#targetActiveElement = e.target;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TSinchSkeletonElement, TSinchSkeletonReact } from './types';
|
|
2
|
+
declare global {
|
|
3
|
+
namespace JSX {
|
|
4
|
+
interface IntrinsicElements {
|
|
5
|
+
'sinch-skeleton': TSinchSkeletonReact;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'sinch-skeleton': TSinchSkeletonElement;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { defineCustomElement, getUid, isAttrTrue, NectaryElement, shouldReduceMotion } from '../utils';
|
|
2
|
+
const templateHTML = '<style>:host{display:block}#wrapper{position:relative;display:flex;flex-direction:column;gap:16px;height:100%;box-sizing:border-box;overflow:hidden}:host([card]:not([card=false]))>#wrapper{padding:16px;background-color:var(--sinch-color-snow-100);border-radius:var(--sinch-shape-radius-l);border:1px solid var(--sinch-color-snow-500)}#shimmer{position:absolute;inset:0;background-image:linear-gradient(90deg,transparent 0,var(--sinch-color-snow-400) 100px,transparent 200px);clip-path:url("#clip")}#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></div>';
|
|
3
|
+
const template = document.createElement('template');
|
|
4
|
+
template.innerHTML = templateHTML;
|
|
5
|
+
const ANIMATION_DURATION = 2000;
|
|
6
|
+
const BORDER_WIDTH = 1;
|
|
7
|
+
defineCustomElement('sinch-skeleton', class extends NectaryElement {
|
|
8
|
+
#animation = null;
|
|
9
|
+
#shimmer;
|
|
10
|
+
#controller = null;
|
|
11
|
+
#clip;
|
|
12
|
+
#borderWidth = 0;
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
const shadowRoot = this.attachShadow();
|
|
16
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
17
|
+
this.#shimmer = shadowRoot.querySelector('#shimmer');
|
|
18
|
+
this.#clip = shadowRoot.querySelector('#clip');
|
|
19
|
+
}
|
|
20
|
+
connectedCallback() {
|
|
21
|
+
const id = getUid();
|
|
22
|
+
this.#shimmer.style.setProperty('clip-path', `url(#${id})`);
|
|
23
|
+
this.#clip.setAttribute('id', id);
|
|
24
|
+
this.#controller = new AbortController();
|
|
25
|
+
if (!shouldReduceMotion()) {
|
|
26
|
+
this.addEventListener('skeleton-item-data', this.#onSkeletonItemData, {
|
|
27
|
+
signal: this.#controller.signal
|
|
28
|
+
});
|
|
29
|
+
this.#updateAnimation();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
disconnectedCallback() {
|
|
33
|
+
this.#animation.cancel();
|
|
34
|
+
this.#animation = null;
|
|
35
|
+
this.#controller.abort();
|
|
36
|
+
this.#controller = null;
|
|
37
|
+
}
|
|
38
|
+
static get observedAttributes() {
|
|
39
|
+
return ['card'];
|
|
40
|
+
}
|
|
41
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
42
|
+
if (oldVal === newVal) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
switch (name) {
|
|
46
|
+
case 'card':
|
|
47
|
+
{
|
|
48
|
+
this.#borderWidth = isAttrTrue(newVal) ? BORDER_WIDTH : 0;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
#updateAnimation() {
|
|
54
|
+
const bb = this.getBoundingClientRect();
|
|
55
|
+
const bgWidth = bb.width * 4;
|
|
56
|
+
this.#shimmer.style.setProperty('background-size', `${bgWidth}px`);
|
|
57
|
+
this.#animation = this.#shimmer.animate({
|
|
58
|
+
backgroundPosition: [`0px`, `${bgWidth}px`]
|
|
59
|
+
}, {
|
|
60
|
+
duration: ANIMATION_DURATION,
|
|
61
|
+
iterations: Infinity
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
#onSkeletonItemData = e => {
|
|
65
|
+
const bb = this.getBoundingClientRect();
|
|
66
|
+
const data = e.detail;
|
|
67
|
+
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
68
|
+
rect.setAttribute('x', (data.x - bb.x - this.#borderWidth).toString());
|
|
69
|
+
rect.setAttribute('y', (data.y - bb.y - this.#borderWidth).toString());
|
|
70
|
+
rect.setAttribute('width', data.width.toString());
|
|
71
|
+
rect.setAttribute('height', data.height.toString());
|
|
72
|
+
rect.setAttribute('rx', data.radius.toString());
|
|
73
|
+
this.#clip.appendChild(rect);
|
|
74
|
+
};
|
|
75
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TSinchElementReact } from '../types';
|
|
2
|
+
export type TSinchSkeletonElement = HTMLElement & {
|
|
3
|
+
/** Card like container */
|
|
4
|
+
setAttribute(name: 'card', value: ''): void;
|
|
5
|
+
};
|
|
6
|
+
export type TSinchSkeletonReact = TSinchElementReact<TSinchSkeletonElement> & {
|
|
7
|
+
/** Card like container */
|
|
8
|
+
card?: boolean;
|
|
9
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TSinchSkeletonItemElement, TSinchSkeletonItemReact } from './types';
|
|
2
|
+
declare global {
|
|
3
|
+
namespace JSX {
|
|
4
|
+
interface IntrinsicElements {
|
|
5
|
+
'sinch-skeleton-item': TSinchSkeletonItemReact;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
interface HTMLElementTagNameMap {
|
|
9
|
+
'sinch-skeleton-item': TSinchSkeletonItemElement;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineCustomElement, getCssVar, NectaryElement, attrValueToInteger, shouldReduceMotion } from '../utils';
|
|
2
|
+
const templateHTML = '<style>:host{display:block;height:var(--sinch-size-m);--sinch-shape-radius:var(--sinch-shape-radius-m)}:host([size=xs]){height:var(--sinch-size-xs);--sinch-shape-radius:var(--sinch-shape-radius-xs)}:host([size="s"]){height:var(--sinch-size-s);--sinch-shape-radius:var(--sinch-shape-radius-s)}:host([size="m"]){height:var(--sinch-size-m);--sinch-shape-radius:var(--sinch-shape-radius-m)}:host([size="l"]){height:var(--sinch-size-l);--sinch-shape-radius:var(--sinch-shape-radius-l)}#content{height:100%;background-color:var(--sinch-color-snow-500);border-radius:var(--sinch-shape-radius)}</style><div id="content"></div>';
|
|
3
|
+
const template = document.createElement('template');
|
|
4
|
+
template.innerHTML = templateHTML;
|
|
5
|
+
defineCustomElement('sinch-skeleton-item', class extends NectaryElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
const shadowRoot = this.attachShadow();
|
|
9
|
+
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
10
|
+
}
|
|
11
|
+
connectedCallback() {
|
|
12
|
+
if (!shouldReduceMotion()) {
|
|
13
|
+
requestAnimationFrame(() => {
|
|
14
|
+
const {
|
|
15
|
+
x,
|
|
16
|
+
y,
|
|
17
|
+
width,
|
|
18
|
+
height
|
|
19
|
+
} = this.getBoundingClientRect();
|
|
20
|
+
const radiusStr = getCssVar(this, '--sinch-shape-radius') ?? '0';
|
|
21
|
+
const radius = attrValueToInteger(radiusStr, {
|
|
22
|
+
min: 0,
|
|
23
|
+
defaultValue: 0
|
|
24
|
+
});
|
|
25
|
+
this.dispatchEvent(new CustomEvent('skeleton-item-data', {
|
|
26
|
+
bubbles: true,
|
|
27
|
+
detail: {
|
|
28
|
+
x,
|
|
29
|
+
y,
|
|
30
|
+
width,
|
|
31
|
+
height,
|
|
32
|
+
radius
|
|
33
|
+
}
|
|
34
|
+
}));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { TRect, TSinchElementReact } from '../types';
|
|
2
|
+
import type { TSinchSizeEx } from '../utils/size';
|
|
3
|
+
export type TSinchSkeletonItemBoundingBox = TRect & {
|
|
4
|
+
radius: number;
|
|
5
|
+
};
|
|
6
|
+
export type TSinchSkeletonItemElement = HTMLElement & {
|
|
7
|
+
/** Size */
|
|
8
|
+
setAttribute(name: 'size', value: TSinchSizeEx): void;
|
|
9
|
+
};
|
|
10
|
+
export type TSinchSkeletonItemReact = TSinchElementReact<TSinchSkeletonItemElement> & {
|
|
11
|
+
/** Size */
|
|
12
|
+
size?: TSinchSizeEx;
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/theme/chip.css
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
--sinch-chip-color-light-violet-fg: var(--sinch-color-violet-700);
|
|
19
19
|
--sinch-chip-color-light-yellow-bg: var(--sinch-color-bolt-200);
|
|
20
20
|
--sinch-chip-color-light-yellow-fg: var(--sinch-color-bolt-700);
|
|
21
|
+
--sinch-chip-color-light-red-bg: var(--sinch-color-jasper-200);
|
|
22
|
+
--sinch-chip-color-light-red-fg: var(--sinch-color-jasper-700);
|
|
21
23
|
--sinch-chip-color-dark-blue-bg: var(--sinch-color-night-700);
|
|
22
24
|
--sinch-chip-color-dark-blue-fg: var(--sinch-color-night-200);
|
|
23
25
|
--sinch-chip-color-dark-brown-bg: var(--sinch-color-mud-700);
|
|
@@ -34,6 +36,8 @@
|
|
|
34
36
|
--sinch-chip-color-dark-violet-fg: var(--sinch-color-violet-200);
|
|
35
37
|
--sinch-chip-color-dark-yellow-bg: var(--sinch-color-bolt-700);
|
|
36
38
|
--sinch-chip-color-dark-yellow-fg: var(--sinch-color-bolt-200);
|
|
39
|
+
--sinch-chip-color-dark-red-bg: var(--sinch-color-jasper-700);
|
|
40
|
+
--sinch-chip-color-dark-red-fg: var(--sinch-color-jasper-200);
|
|
37
41
|
--sinch-chip-color-blue-bg: var(--sinch-color-night-400);
|
|
38
42
|
--sinch-chip-color-blue-fg: var(--sinch-color-snow-100);
|
|
39
43
|
--sinch-chip-color-brown-bg: var(--sinch-color-mud-400);
|
|
@@ -50,6 +54,8 @@
|
|
|
50
54
|
--sinch-chip-color-violet-fg: var(--sinch-color-stormy-500);
|
|
51
55
|
--sinch-chip-color-yellow-bg: var(--sinch-color-bolt-400);
|
|
52
56
|
--sinch-chip-color-yellow-fg: var(--sinch-color-stormy-500);
|
|
57
|
+
--sinch-chip-color-red-bg: var(--sinch-color-jasper-400);
|
|
58
|
+
--sinch-chip-color-red-fg: var(--sinch-color-stormy-500);
|
|
53
59
|
--sinch-chip-color-celtic-bg: var(--sinch-color-feedback-info-bg);
|
|
54
60
|
--sinch-chip-color-celtic-fg: var(--sinch-color-feedback-info-contrast);
|
|
55
61
|
--sinch-chip-color-olive-bg: var(--sinch-color-feedback-success-bg);
|
package/theme/tag.css
CHANGED
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
--sinch-tag-color-light-violet-fg: var(--sinch-color-violet-700);
|
|
19
19
|
--sinch-tag-color-light-yellow-bg: var(--sinch-color-bolt-200);
|
|
20
20
|
--sinch-tag-color-light-yellow-fg: var(--sinch-color-bolt-700);
|
|
21
|
+
--sinch-tag-color-light-red-bg: var(--sinch-color-jasper-200);
|
|
22
|
+
--sinch-tag-color-light-red-fg: var(--sinch-color-jasper-700);
|
|
21
23
|
--sinch-tag-color-dark-blue-bg: var(--sinch-color-night-700);
|
|
22
24
|
--sinch-tag-color-dark-blue-fg: var(--sinch-color-night-200);
|
|
23
25
|
--sinch-tag-color-dark-brown-bg: var(--sinch-color-mud-700);
|
|
@@ -34,6 +36,8 @@
|
|
|
34
36
|
--sinch-tag-color-dark-violet-fg: var(--sinch-color-violet-200);
|
|
35
37
|
--sinch-tag-color-dark-yellow-bg: var(--sinch-color-bolt-700);
|
|
36
38
|
--sinch-tag-color-dark-yellow-fg: var(--sinch-color-bolt-200);
|
|
39
|
+
--sinch-tag-color-dark-red-bg: var(--sinch-color-jasper-700);
|
|
40
|
+
--sinch-tag-color-dark-red-fg: var(--sinch-color-jasper-200);
|
|
37
41
|
--sinch-tag-color-blue-bg: var(--sinch-color-night-400);
|
|
38
42
|
--sinch-tag-color-blue-fg: var(--sinch-color-snow-100);
|
|
39
43
|
--sinch-tag-color-brown-bg: var(--sinch-color-mud-400);
|
|
@@ -50,6 +54,8 @@
|
|
|
50
54
|
--sinch-tag-color-violet-fg: var(--sinch-color-stormy-500);
|
|
51
55
|
--sinch-tag-color-yellow-bg: var(--sinch-color-bolt-400);
|
|
52
56
|
--sinch-tag-color-yellow-fg: var(--sinch-color-stormy-500);
|
|
57
|
+
--sinch-tag-color-red-bg: var(--sinch-color-jasper-400);
|
|
58
|
+
--sinch-tag-color-red-fg: var(--sinch-color-stormy-500);
|
|
53
59
|
--sinch-tag-color-celtic-bg: var(--sinch-color-feedback-info-bg);
|
|
54
60
|
--sinch-tag-color-celtic-fg: var(--sinch-color-feedback-info-contrast);
|
|
55
61
|
--sinch-tag-color-olive-bg: var(--sinch-color-feedback-success-bg);
|
package/toast-manager/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../title';
|
|
2
2
|
import '../text';
|
|
3
|
-
import { cloneNode, defineCustomElement, getRect, NectaryElement } from '../utils';
|
|
3
|
+
import { cloneNode, defineCustomElement, getRect, NectaryElement, shouldReduceMotion } from '../utils';
|
|
4
4
|
const templateHTML = '<style>:host{display:block}#items{display:block;width:0;height:0;visibility:hidden;overflow:hidden}#list{display:flex;flex-direction:column;position:fixed;z-index:1;bottom:0;right:16px}.item-wrapper{height:0;opacity:0;position:relative}.item-wrapper[data-deleting]::after{content:"";position:absolute;top:0;left:0;bottom:0;right:0}</style><slot id="items"></slot><div id="list"></div>';
|
|
5
5
|
const DURATION_ADD = 250;
|
|
6
6
|
const DURATION_REMOVE = 250;
|
|
@@ -11,7 +11,7 @@ defineCustomElement('sinch-toast-manager', class extends NectaryElement {
|
|
|
11
11
|
#$slot;
|
|
12
12
|
#$list;
|
|
13
13
|
#map = new WeakMap();
|
|
14
|
-
#shouldReduceMotion
|
|
14
|
+
#shouldReduceMotion;
|
|
15
15
|
#animations = new Set();
|
|
16
16
|
constructor() {
|
|
17
17
|
super();
|
|
@@ -19,10 +19,10 @@ defineCustomElement('sinch-toast-manager', class extends NectaryElement {
|
|
|
19
19
|
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
20
20
|
this.#$slot = shadowRoot.querySelector('slot');
|
|
21
21
|
this.#$list = shadowRoot.querySelector('#list');
|
|
22
|
+
this.#shouldReduceMotion = shouldReduceMotion();
|
|
22
23
|
}
|
|
23
24
|
connectedCallback() {
|
|
24
25
|
this.#$slot.addEventListener('slotchange', this.#onSlotChange);
|
|
25
|
-
this.#shouldReduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
26
26
|
}
|
|
27
27
|
disconnectedCallback() {
|
|
28
28
|
this.#$slot.removeEventListener('slotchange', this.#onSlotChange);
|
package/tooltip/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import '../text';
|
|
2
2
|
import '../pop';
|
|
3
|
-
import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass, rectOverlap, getReactEventHandler } from '../utils';
|
|
3
|
+
import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass, rectOverlap, getReactEventHandler, shouldReduceMotion } from '../utils';
|
|
4
4
|
const templateHTML = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-600);border-radius:var(--sinch-shape-radius-xs);pointer-events:none;opacity:0}: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}#text{word-break:break-word;pointer-events:none}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-color-snow-600);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([inverted]:not([inverted=false])) #content{background-color:var(--sinch-color-stormy-500);color:var(--sinch-color-text-inverted)}:host([inverted]:not([inverted=false])) #tip{fill:var(--sinch-color-stormy-500)}</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>';
|
|
5
5
|
import { TooltipState } from './tooltip-state';
|
|
6
6
|
import { assertOrientation, getPopOrientation, orientationValues } from './utils';
|
|
7
7
|
const TIP_SIZE = 8;
|
|
8
8
|
const SHOW_DELAY = 1000;
|
|
9
9
|
const HIDE_DELAY = 0;
|
|
10
|
-
const ANIMATION_DURATION =
|
|
10
|
+
const ANIMATION_DURATION = 100;
|
|
11
11
|
const template = document.createElement('template');
|
|
12
12
|
template.innerHTML = templateHTML;
|
|
13
13
|
defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
@@ -32,12 +32,13 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
32
32
|
this.#$contentWrapper = shadowRoot.querySelector('#content-wrapper');
|
|
33
33
|
this.#$tip = shadowRoot.querySelector('#tip');
|
|
34
34
|
this.#$target = shadowRoot.querySelector('#target');
|
|
35
|
-
this.#shouldReduceMotion =
|
|
35
|
+
this.#shouldReduceMotion = shouldReduceMotion();
|
|
36
36
|
this.#tooltipState = new TooltipState({
|
|
37
37
|
showDelay: SHOW_DELAY,
|
|
38
38
|
hideDelay: this.#shouldReduceMotion ? HIDE_DELAY + ANIMATION_DURATION : HIDE_DELAY,
|
|
39
39
|
hideAnimationDuration: this.#shouldReduceMotion ? 0 : ANIMATION_DURATION,
|
|
40
|
-
|
|
40
|
+
onShowStart: this.#onStateShowStart,
|
|
41
|
+
onShowEnd: this.#onStateShowEnd,
|
|
41
42
|
onHideStart: this.#onStateHideStart,
|
|
42
43
|
onHideEnd: this.#onStateHideEnd
|
|
43
44
|
});
|
|
@@ -108,29 +109,27 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
108
109
|
}
|
|
109
110
|
}
|
|
110
111
|
#onMouseDown = () => {
|
|
111
|
-
this.#tooltipState.
|
|
112
|
-
this.#unsubscribeScroll();
|
|
112
|
+
this.#tooltipState.destroy();
|
|
113
113
|
};
|
|
114
114
|
#onPopClose = () => {
|
|
115
115
|
this.#tooltipState.destroy();
|
|
116
|
-
this.#unsubscribeScroll();
|
|
117
116
|
};
|
|
118
117
|
#onMouseEnter = () => {
|
|
119
118
|
this.#tooltipState.show();
|
|
120
|
-
this.#subscribeScroll();
|
|
121
|
-
this.#subscribeMouseLeaveEvents();
|
|
122
119
|
};
|
|
123
120
|
#onMouseLeave = e => {
|
|
124
121
|
if (!this.#isOpen() || e.relatedTarget !== this.#$contentWrapper && e.relatedTarget !== this.#$target) {
|
|
125
122
|
this.#tooltipState.hide();
|
|
126
|
-
this.#unsubscribeScroll();
|
|
127
123
|
}
|
|
128
124
|
};
|
|
129
125
|
#onScroll = () => {
|
|
130
126
|
this.#tooltipState.destroy();
|
|
131
|
-
this.#unsubscribeScroll();
|
|
132
127
|
};
|
|
133
|
-
#
|
|
128
|
+
#onStateShowStart = () => {
|
|
129
|
+
this.#subscribeScroll();
|
|
130
|
+
this.#subscribeMouseLeaveEvents();
|
|
131
|
+
};
|
|
132
|
+
#onStateShowEnd = () => {
|
|
134
133
|
this.#dispatchShowEvent();
|
|
135
134
|
updateBooleanAttribute(this.#$pop, 'open', true);
|
|
136
135
|
requestAnimationFrame(this.#updateTipOrientation);
|
|
@@ -152,11 +151,14 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
152
151
|
this.#animation.play();
|
|
153
152
|
};
|
|
154
153
|
#onStateHideEnd = () => {
|
|
155
|
-
this.#
|
|
154
|
+
if (this.#isOpen()) {
|
|
155
|
+
this.#animation.finish();
|
|
156
|
+
this.#dispatchHideEvent();
|
|
157
|
+
updateBooleanAttribute(this.#$pop, 'open', false);
|
|
158
|
+
}
|
|
156
159
|
this.#resetTipOrientation();
|
|
157
|
-
updateBooleanAttribute(this.#$pop, 'open', false);
|
|
158
|
-
this.#dispatchHideEvent();
|
|
159
160
|
this.#unsubscribeMouseLeaveEvents();
|
|
161
|
+
this.#unsubscribeScroll();
|
|
160
162
|
};
|
|
161
163
|
#resetTipOrientation() {
|
|
162
164
|
this.#$tip.style.top = '';
|
|
@@ -187,17 +189,16 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
187
189
|
if (!this.isConnected) {
|
|
188
190
|
return;
|
|
189
191
|
}
|
|
190
|
-
const
|
|
191
|
-
this.#$tooltipText.textContent =
|
|
192
|
-
if (
|
|
192
|
+
const text = this.text;
|
|
193
|
+
this.#$tooltipText.textContent = text;
|
|
194
|
+
if (text.length === 0) {
|
|
193
195
|
if (this.#isSubscribed) {
|
|
194
196
|
this.#tooltipState.destroy();
|
|
195
197
|
this.#unsubscribeMouseEnterEvent();
|
|
196
|
-
this.#unsubscribeMouseLeaveEvents();
|
|
197
198
|
}
|
|
198
|
-
|
|
199
|
+
} else {
|
|
200
|
+
this.#subscribeMouseEnterEvent();
|
|
199
201
|
}
|
|
200
|
-
this.#subscribeMouseEnterEvent();
|
|
201
202
|
}
|
|
202
203
|
#subscribeMouseEnterEvent() {
|
|
203
204
|
if (!this.isConnected || this.#isSubscribed) {
|
package/tooltip/tooltip-state.js
CHANGED
|
@@ -1,119 +1,121 @@
|
|
|
1
1
|
export class TooltipState {
|
|
2
2
|
#timerId = null;
|
|
3
|
-
#state = '
|
|
3
|
+
#state = 'hide';
|
|
4
4
|
#options;
|
|
5
5
|
constructor(options) {
|
|
6
6
|
this.#options = options;
|
|
7
7
|
}
|
|
8
8
|
show() {
|
|
9
|
+
console.log('-> show');
|
|
9
10
|
switch (this.#state) {
|
|
10
|
-
case '
|
|
11
|
-
{
|
|
12
|
-
this.#delayShow();
|
|
13
|
-
break;
|
|
14
|
-
}
|
|
15
|
-
case 'hide-delay':
|
|
11
|
+
case 'hide':
|
|
16
12
|
{
|
|
17
|
-
this.#
|
|
13
|
+
this.#switchToHideToShow();
|
|
18
14
|
break;
|
|
19
15
|
}
|
|
20
|
-
case 'hide':
|
|
16
|
+
case 'show-to-hide':
|
|
21
17
|
{
|
|
22
|
-
this.#
|
|
23
|
-
this.#onShow();
|
|
18
|
+
this.#switchToState('show');
|
|
24
19
|
break;
|
|
25
20
|
}
|
|
26
21
|
}
|
|
27
22
|
}
|
|
28
23
|
hide() {
|
|
24
|
+
console.log('-> hide');
|
|
29
25
|
switch (this.#state) {
|
|
30
|
-
case 'show
|
|
26
|
+
case 'hide-to-show':
|
|
31
27
|
{
|
|
32
|
-
this.#
|
|
28
|
+
this.#onHideAnimationEnd();
|
|
33
29
|
break;
|
|
34
30
|
}
|
|
35
31
|
case 'show':
|
|
36
32
|
{
|
|
37
|
-
this.#
|
|
33
|
+
this.#switchToShowToHide();
|
|
38
34
|
break;
|
|
39
35
|
}
|
|
40
36
|
}
|
|
41
37
|
}
|
|
42
38
|
interrupt() {
|
|
39
|
+
console.log('-> interrupt');
|
|
43
40
|
switch (this.#state) {
|
|
44
|
-
case 'show
|
|
41
|
+
case 'hide-to-show':
|
|
45
42
|
{
|
|
46
|
-
this.#
|
|
43
|
+
this.#onHideAnimationEnd();
|
|
47
44
|
break;
|
|
48
45
|
}
|
|
49
46
|
case 'show':
|
|
50
47
|
{
|
|
51
|
-
this.#
|
|
48
|
+
this.#switchToShowToHide(true, false);
|
|
52
49
|
break;
|
|
53
50
|
}
|
|
54
|
-
case 'hide
|
|
51
|
+
case 'show-to-hide':
|
|
55
52
|
{
|
|
56
|
-
this.#
|
|
57
|
-
this.#onHideStart();
|
|
53
|
+
this.#switchToShowToHide(true, false);
|
|
58
54
|
break;
|
|
59
55
|
}
|
|
60
56
|
}
|
|
61
57
|
}
|
|
62
58
|
destroy() {
|
|
59
|
+
console.log('-> destroy');
|
|
63
60
|
switch (this.#state) {
|
|
64
|
-
case 'show
|
|
61
|
+
case 'hide-to-show':
|
|
65
62
|
{
|
|
66
|
-
this.#
|
|
63
|
+
this.#onHideAnimationEnd();
|
|
67
64
|
break;
|
|
68
65
|
}
|
|
69
66
|
case 'show':
|
|
70
67
|
{
|
|
71
|
-
this.#
|
|
68
|
+
this.#switchToShowToHide(true, true);
|
|
72
69
|
break;
|
|
73
70
|
}
|
|
74
|
-
case 'hide
|
|
71
|
+
case 'show-to-hide':
|
|
75
72
|
{
|
|
76
|
-
this.#
|
|
77
|
-
this.#onHideStart(true);
|
|
73
|
+
this.#switchToShowToHide(true, true);
|
|
78
74
|
break;
|
|
79
75
|
}
|
|
80
76
|
case 'hide':
|
|
81
77
|
{
|
|
82
|
-
this.#
|
|
83
|
-
this.#onHideEnd();
|
|
78
|
+
this.#onHideAnimationEnd();
|
|
84
79
|
break;
|
|
85
80
|
}
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
|
-
#
|
|
89
|
-
this.#
|
|
90
|
-
this.#
|
|
83
|
+
#switchToHideToShow() {
|
|
84
|
+
this.#switchToState('hide-to-show');
|
|
85
|
+
this.#options.onShowStart();
|
|
86
|
+
if (this.#options.showDelay === 0) {
|
|
87
|
+
this.#onSwitchToShow();
|
|
88
|
+
} else {
|
|
89
|
+
this.#timerId = window.setTimeout(this.#onSwitchToShow, this.#options.showDelay);
|
|
90
|
+
}
|
|
91
91
|
}
|
|
92
|
-
#
|
|
93
|
-
this.#
|
|
94
|
-
|
|
92
|
+
#switchToShowToHide(skipDelay, skipHideAnimation) {
|
|
93
|
+
this.#switchToState('show-to-hide');
|
|
94
|
+
if (skipDelay === true || this.#options.hideDelay === 0) {
|
|
95
|
+
this.#onShowToHideEnd(skipHideAnimation);
|
|
96
|
+
} else {
|
|
97
|
+
this.#timerId = window.setTimeout(this.#onShowToHideEnd, this.#options.hideDelay);
|
|
98
|
+
}
|
|
95
99
|
}
|
|
96
|
-
#
|
|
97
|
-
this.#
|
|
98
|
-
this.#options.
|
|
100
|
+
#onSwitchToShow = () => {
|
|
101
|
+
this.#switchToState('show');
|
|
102
|
+
this.#options.onShowEnd();
|
|
99
103
|
};
|
|
100
|
-
#
|
|
101
|
-
this.#
|
|
104
|
+
#onShowToHideEnd = skipHideAnimation => {
|
|
105
|
+
this.#switchToState('hide');
|
|
102
106
|
this.#options.onHideStart();
|
|
103
|
-
if (
|
|
104
|
-
this.#
|
|
107
|
+
if (skipHideAnimation === true || this.#options.hideAnimationDuration === 0) {
|
|
108
|
+
this.#onHideAnimationEnd();
|
|
105
109
|
} else {
|
|
106
|
-
this.#timerId = window.setTimeout(this.#
|
|
110
|
+
this.#timerId = window.setTimeout(this.#onHideAnimationEnd, this.#options.hideAnimationDuration);
|
|
107
111
|
}
|
|
108
112
|
};
|
|
109
|
-
#
|
|
110
|
-
this.#
|
|
113
|
+
#onHideAnimationEnd = () => {
|
|
114
|
+
this.#switchToState('hide');
|
|
111
115
|
this.#options.onHideEnd();
|
|
112
116
|
};
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
this.#state = nextState;
|
|
116
|
-
}
|
|
117
|
+
#switchToState(nextState) {
|
|
118
|
+
this.#state = nextState;
|
|
117
119
|
if (this.#timerId !== null) {
|
|
118
120
|
clearTimeout(this.#timerId);
|
|
119
121
|
this.#timerId = null;
|
package/utils/dom.d.ts
CHANGED
|
@@ -29,4 +29,5 @@ export declare const hasClass: (elem: Element, name: string) => boolean;
|
|
|
29
29
|
export declare const getCssVar: (element: Element, variableName: string) => string | null;
|
|
30
30
|
export declare const getCssVars: (element: Element, variableNames: string[]) => (string | null)[];
|
|
31
31
|
export declare const cloneNode: (el: Element, deep: boolean) => Element;
|
|
32
|
+
export declare const shouldReduceMotion: () => boolean;
|
|
32
33
|
export {};
|
package/utils/dom.js
CHANGED
package/utils/index.d.ts
CHANGED
package/utils/index.js
CHANGED
package/utils/uid.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getUid: () => string;
|
package/utils/uid.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const getUid = () => crypto.getRandomValues(new Uint8Array(21)).reduce((id, byte) => {
|
|
2
|
+
const nextByte = byte & 63;
|
|
3
|
+
if (nextByte < 36) {
|
|
4
|
+
return id + nextByte.toString(36);
|
|
5
|
+
}
|
|
6
|
+
if (nextByte < 62) {
|
|
7
|
+
return id + (nextByte - 26).toString(36).toUpperCase();
|
|
8
|
+
}
|
|
9
|
+
if (nextByte > 62) {
|
|
10
|
+
return `${id}-`;
|
|
11
|
+
}
|
|
12
|
+
return `${id}_`;
|
|
13
|
+
}, '');
|