@nectary/components 0.45.0 → 0.45.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/emoji-picker/index.d.ts +1 -0
- package/emoji-picker/index.js +39 -11
- package/emoji-picker/types.d.ts +1 -0
- package/help-tooltip/index.js +32 -4
- package/icon-button/index.js +16 -0
- package/icon-button/types.d.ts +4 -0
- package/input/index.js +32 -11
- package/input/types.d.ts +2 -1
- package/link/index.js +1 -1
- package/package.json +1 -1
- package/popover/index.js +1 -1
- package/text/index.js +1 -1
- package/textarea/index.js +1 -1
- package/time-picker/index.d.ts +0 -2
- package/time-picker/index.js +13 -15
- package/tooltip/index.js +17 -1
- package/tooltip/types.d.ts +8 -0
package/emoji-picker/index.d.ts
CHANGED
package/emoji-picker/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import '../popover';
|
|
|
6
6
|
import '../tabs';
|
|
7
7
|
import '../tabs-icon-option';
|
|
8
8
|
import '../emoji';
|
|
9
|
+
import '../text';
|
|
9
10
|
import '../icons/search';
|
|
10
11
|
import '../icons/sentiment-satisfied';
|
|
11
12
|
import '../icons/emoji-people';
|
|
@@ -15,13 +16,15 @@ import '../icons/emoji-objects';
|
|
|
15
16
|
import '../icons/emoji-transportation';
|
|
16
17
|
import '../icons/emoji-events';
|
|
17
18
|
import '../icons/emoji-symbols';
|
|
18
|
-
import { defineCustomElement, getAttribute, getBooleanAttribute, NectaryElement, updateAttribute, updateBooleanAttribute, getReactEventHandler, getRect, subscribeContext } from '../utils';
|
|
19
|
+
import { defineCustomElement, getAttribute, getBooleanAttribute, NectaryElement, updateAttribute, updateBooleanAttribute, getReactEventHandler, getRect, subscribeContext, debounceTimeout, setClass } from '../utils';
|
|
19
20
|
import dataJson from './data.json';
|
|
20
|
-
const templateHTML = '<style>:host{display:block}#wrapper{width:384px;max-height:504px;display:flex;flex-direction:column;gap:8px;padding:12px 0}#toolbar{display:flex;gap:8px;padding:0 12px}#input{flex:1}#list-wrapper{overflow-y:auto;overflow-x:hidden;width:384px;box-sizing:border-box;scrollbar-gutter:stable}#list{display:flex;flex-wrap:wrap;gap:8px;padding:4px 12px 0;width:384px;box-sizing:border-box}</style><div id="wrapper"><div id="toolbar"><sinch-input id="input" size="l" aria-label="Search emojis"><sinch-icon-search slot="icon"></sinch-icon-search></sinch-input><sinch-popover id="skin-popover" orientation="bottom-left" aria-label="Emoji skin tone select"><sinch-icon-button id="skin-button" slot="target" size="l" aria-label="Select emoji skin tones"><sinch-color-swatch id="skin-swatch" slot="icon" name="skin-tone-0"></sinch-color-swatch></sinch-icon-button><sinch-color-menu id="skin-menu" slot="content" cols="1" value="skin-tone-0" colors="skin-tone-0,skin-tone-10,skin-tone-20,skin-tone-30,skin-tone-40,skin-tone-50" aria-label="Emoji skin tone menu"></sinch-color-menu></sinch-popover></div><sinch-tabs id="tabs" aria-label="Emoji groups"></sinch-tabs><div id="list-wrapper"><div id="list"></div></div></div>';
|
|
21
|
+
const templateHTML = '<style>:host{display:block}#wrapper{width:384px;max-height:504px;display:flex;flex-direction:column;gap:8px;padding:12px 0}#toolbar{display:flex;gap:8px;padding:0 12px}#input{flex:1}#list-wrapper{overflow-y:auto;overflow-x:hidden;width:384px;box-sizing:border-box;scrollbar-gutter:stable}#list{display:flex;flex-wrap:wrap;gap:8px;padding:4px 12px 0;width:384px;box-sizing:border-box}#not-found{display:none;width:100%;height:48px;align-items:center;justify-content:center;color:var(--sinch-color-text-muted);pointer-events:none;user-select:none}#not-found.active{display:flex}</style><div id="wrapper"><div id="toolbar"><sinch-input id="input" size="l" aria-label="Search emojis"><sinch-icon-search slot="icon"></sinch-icon-search></sinch-input><sinch-popover id="skin-popover" orientation="bottom-left" aria-label="Emoji skin tone select"><sinch-icon-button id="skin-button" slot="target" size="l" aria-label="Select emoji skin tones"><sinch-color-swatch id="skin-swatch" slot="icon" name="skin-tone-0"></sinch-color-swatch></sinch-icon-button><sinch-color-menu id="skin-menu" slot="content" cols="1" value="skin-tone-0" colors="skin-tone-0,skin-tone-10,skin-tone-20,skin-tone-30,skin-tone-40,skin-tone-50" aria-label="Emoji skin tone menu"></sinch-color-menu></sinch-popover></div><sinch-tabs id="tabs" aria-label="Emoji groups"></sinch-tabs><div id="list-wrapper"><div id="list"></div><div id="not-found"><sinch-text type="m">No results</sinch-text></div></div></div>';
|
|
21
22
|
const groupIconTagNames = ['sinch-icon-sentiment-satisfied', 'sinch-icon-emoji-people', 'sinch-icon-emoji-nature', 'sinch-icon-emoji-food-beverage', 'sinch-icon-emoji-transportation', 'sinch-icon-emoji-events', 'sinch-icon-emoji-objects', 'sinch-icon-emoji-symbols'];
|
|
22
23
|
const groupLabels = ['Emotions', 'People', 'Animals and nature', 'Food and drinks', 'Travel and places', 'Sports and activities', 'Objects', 'Symbols and flags'];
|
|
23
24
|
const data = dataJson;
|
|
24
25
|
const template = document.createElement('template');
|
|
26
|
+
const MIN_SEARCH_LENGTH = 2;
|
|
27
|
+
const SEARCH_DEBOUNCE_TIMEOUT = 300;
|
|
25
28
|
template.innerHTML = templateHTML;
|
|
26
29
|
defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
27
30
|
#$tabs;
|
|
@@ -31,9 +34,12 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
31
34
|
#$skinSwatch;
|
|
32
35
|
#$skinButton;
|
|
33
36
|
#$list;
|
|
37
|
+
#$notFound;
|
|
34
38
|
#controller = null;
|
|
35
39
|
#$sh;
|
|
40
|
+
#searchDebounce;
|
|
36
41
|
#currentSkinTone = 0;
|
|
42
|
+
#prevTabsValue = null;
|
|
37
43
|
constructor() {
|
|
38
44
|
super();
|
|
39
45
|
const shadowRoot = this.attachShadow();
|
|
@@ -46,6 +52,8 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
46
52
|
this.#$skinSwatch = shadowRoot.querySelector('#skin-swatch');
|
|
47
53
|
this.#$skinButton = shadowRoot.querySelector('#skin-button');
|
|
48
54
|
this.#$list = shadowRoot.querySelector('#list');
|
|
55
|
+
this.#$notFound = shadowRoot.querySelector('#not-found');
|
|
56
|
+
this.#searchDebounce = debounceTimeout(SEARCH_DEBOUNCE_TIMEOUT)(this.#updateSearch);
|
|
49
57
|
}
|
|
50
58
|
connectedCallback() {
|
|
51
59
|
this.#controller = new AbortController();
|
|
@@ -83,6 +91,7 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
83
91
|
}
|
|
84
92
|
disconnectedCallback() {
|
|
85
93
|
this.#controller.abort();
|
|
94
|
+
this.#searchDebounce.cancel();
|
|
86
95
|
}
|
|
87
96
|
get skinToneButtonRect() {
|
|
88
97
|
return getRect(this.#$skinButton);
|
|
@@ -90,6 +99,9 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
90
99
|
get searchInputRect() {
|
|
91
100
|
return getRect(this.#$input);
|
|
92
101
|
}
|
|
102
|
+
get searchClearButtonRect() {
|
|
103
|
+
return this.#$input.clearButtonRect;
|
|
104
|
+
}
|
|
93
105
|
nthSkinToneRect(index) {
|
|
94
106
|
return this.#$skinMenu.nthItemRect(index);
|
|
95
107
|
}
|
|
@@ -146,13 +158,8 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
146
158
|
this.#updateEmojis();
|
|
147
159
|
};
|
|
148
160
|
#onSearchChange = e => {
|
|
149
|
-
|
|
150
|
-
this
|
|
151
|
-
if (value.length < 3) {
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
updateAttribute(this.#$tabs, 'value', '');
|
|
155
|
-
this.#updateSearchEmojis();
|
|
161
|
+
this.#$input.value = e.detail;
|
|
162
|
+
this.#searchDebounce.fn();
|
|
156
163
|
};
|
|
157
164
|
#onChangeReactHandler = e => {
|
|
158
165
|
getReactEventHandler(this, 'on-change')?.(e);
|
|
@@ -163,6 +170,24 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
163
170
|
#getDocumentRoot() {
|
|
164
171
|
return Reflect.has(this.#$sh, 'createElement') ? this.#$sh : document;
|
|
165
172
|
}
|
|
173
|
+
#updateSearch = () => {
|
|
174
|
+
const value = this.#$input.value;
|
|
175
|
+
if (value.length < MIN_SEARCH_LENGTH) {
|
|
176
|
+
if (this.#isSearchMode()) {
|
|
177
|
+
if (this.#prevTabsValue !== null) {
|
|
178
|
+
this.#$tabs.setAttribute('value', this.#prevTabsValue);
|
|
179
|
+
}
|
|
180
|
+
this.#updateEmojis();
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const currentActiveTab = this.#$tabs.getAttribute('value');
|
|
185
|
+
if (currentActiveTab !== null) {
|
|
186
|
+
this.#prevTabsValue = currentActiveTab;
|
|
187
|
+
}
|
|
188
|
+
this.#$tabs.removeAttribute('value');
|
|
189
|
+
this.#updateSearchEmojis();
|
|
190
|
+
};
|
|
166
191
|
#updateTabs() {
|
|
167
192
|
const doc = this.#getDocumentRoot();
|
|
168
193
|
const tabsFragment = document.createDocumentFragment();
|
|
@@ -183,7 +208,7 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
183
208
|
*#iterateSearchEmojis(searchValue, skinTone) {
|
|
184
209
|
for (const group of data) {
|
|
185
210
|
for (const entry of group.emojis) {
|
|
186
|
-
if (entry.label.includes(searchValue)) {
|
|
211
|
+
if (entry.label.toLowerCase().includes(searchValue)) {
|
|
187
212
|
const hasSkins = entry.skins != null;
|
|
188
213
|
if (skinTone === 0 || !hasSkins) {
|
|
189
214
|
yield entry;
|
|
@@ -214,15 +239,18 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
|
|
|
214
239
|
}
|
|
215
240
|
#updateSearchEmojis() {
|
|
216
241
|
const searchValue = this.#$input.value;
|
|
217
|
-
if (searchValue.length <
|
|
242
|
+
if (searchValue.length < MIN_SEARCH_LENGTH) {
|
|
218
243
|
return;
|
|
219
244
|
}
|
|
220
245
|
const doc = this.#getDocumentRoot();
|
|
221
246
|
const fragment = document.createDocumentFragment();
|
|
247
|
+
let someFound = false;
|
|
222
248
|
for (const entry of this.#iterateSearchEmojis(searchValue, this.#currentSkinTone)) {
|
|
223
249
|
const el = this.#createEmojiElement(doc, entry);
|
|
250
|
+
someFound = true;
|
|
224
251
|
fragment.appendChild(el);
|
|
225
252
|
}
|
|
253
|
+
setClass(this.#$notFound, 'active', !someFound);
|
|
226
254
|
this.#$list.replaceChildren(fragment);
|
|
227
255
|
this.#$list.scrollTo(0, 0);
|
|
228
256
|
}
|
package/emoji-picker/types.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare type TEmojiGroup = {
|
|
|
13
13
|
export declare type TSinchEmojiPickerElement = HTMLElement & {
|
|
14
14
|
readonly skinToneButtonRect: TRect;
|
|
15
15
|
readonly searchInputRect: TRect;
|
|
16
|
+
readonly searchClearButtonRect: TRect;
|
|
16
17
|
nthSkinToneRect(index: number): TRect | null;
|
|
17
18
|
nthTabRect(index: number): TRect | null;
|
|
18
19
|
nthEmojiRect(index: number): TRect | null;
|
package/help-tooltip/index.js
CHANGED
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import '../tooltip';
|
|
2
2
|
import '../icons/help-outline';
|
|
3
|
-
import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, NectaryElement, updateAttribute, updateBooleanAttribute, updateIntegerAttribute } from '../utils';
|
|
3
|
+
import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, getReactEventHandler, NectaryElement, updateAttribute, updateBooleanAttribute, updateIntegerAttribute } from '../utils';
|
|
4
4
|
const templateHTML = '<style>:host{display:contents}#icon{--sinch-size-icon:18px}</style><sinch-tooltip><sinch-icon-help-outline id="icon"></sinch-icon-help-outline></sinch-tooltip>';
|
|
5
5
|
const template = document.createElement('template');
|
|
6
6
|
template.innerHTML = templateHTML;
|
|
7
7
|
defineCustomElement('sinch-help-tooltip', class extends NectaryElement {
|
|
8
8
|
#$tooltip;
|
|
9
|
+
#controller = null;
|
|
9
10
|
constructor() {
|
|
10
11
|
super();
|
|
11
12
|
const shadowRoot = this.attachShadow();
|
|
12
13
|
shadowRoot.appendChild(template.content.cloneNode(true));
|
|
13
14
|
this.#$tooltip = shadowRoot.querySelector('sinch-tooltip');
|
|
14
15
|
}
|
|
16
|
+
connectedCallback() {
|
|
17
|
+
super.connectedCallback();
|
|
18
|
+
this.#controller = new AbortController();
|
|
19
|
+
const options = {
|
|
20
|
+
signal: this.#controller.signal
|
|
21
|
+
};
|
|
22
|
+
this.#$tooltip.addEventListener('-show', this.#onTooltipShow, options);
|
|
23
|
+
this.#$tooltip.addEventListener('-hide', this.#onTooltipHide, options);
|
|
24
|
+
this.addEventListener('-show', this.#onTooltipShowReactHandler, options);
|
|
25
|
+
this.addEventListener('-hide', this.#onTooltipHideReactHandler, options);
|
|
26
|
+
}
|
|
27
|
+
disconnectedCallback() {
|
|
28
|
+
super.disconnectedCallback();
|
|
29
|
+
this.#controller.abort();
|
|
30
|
+
}
|
|
15
31
|
static get observedAttributes() {
|
|
16
32
|
return ['text', 'width', 'orientation', 'inverted'];
|
|
17
33
|
}
|
|
34
|
+
attributeChangedCallback(name, _, newVal) {
|
|
35
|
+
updateAttribute(this.#$tooltip, name, newVal);
|
|
36
|
+
}
|
|
18
37
|
get text() {
|
|
19
38
|
return getAttribute(this, 'text', '');
|
|
20
39
|
}
|
|
@@ -45,7 +64,16 @@ defineCustomElement('sinch-help-tooltip', class extends NectaryElement {
|
|
|
45
64
|
get tooltipRect() {
|
|
46
65
|
return this.#$tooltip.tooltipRect;
|
|
47
66
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
67
|
+
#onTooltipShow = () => {
|
|
68
|
+
this.dispatchEvent(new CustomEvent('-show'));
|
|
69
|
+
};
|
|
70
|
+
#onTooltipHide = () => {
|
|
71
|
+
this.dispatchEvent(new CustomEvent('-hide'));
|
|
72
|
+
};
|
|
73
|
+
#onTooltipShowReactHandler = () => {
|
|
74
|
+
getReactEventHandler(this, 'on-show')?.();
|
|
75
|
+
};
|
|
76
|
+
#onTooltipHideReactHandler = () => {
|
|
77
|
+
getReactEventHandler(this, 'on-hide')?.();
|
|
78
|
+
};
|
|
51
79
|
});
|
package/icon-button/index.js
CHANGED
|
@@ -33,6 +33,10 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
|
|
|
33
33
|
this.addEventListener('-click', this.#onClickReactHandler, options);
|
|
34
34
|
this.addEventListener('-focus', this.#onFocusReactHandler, options);
|
|
35
35
|
this.addEventListener('-blur', this.#onBlurReactHandler, options);
|
|
36
|
+
this.#$tooltip.addEventListener('-show', this.#onTooltipShow, options);
|
|
37
|
+
this.#$tooltip.addEventListener('-hide', this.#onTooltipHide, options);
|
|
38
|
+
this.addEventListener('-tooltip-show', this.#onTooltipShowReactHandler, options);
|
|
39
|
+
this.addEventListener('-tooltip-hide', this.#onTooltipHideReactHandler, options);
|
|
36
40
|
subscribeContext(this, 'size', this.#onContextSize, this.#controller.signal);
|
|
37
41
|
this.#sizeContext.listen(this.#controller.signal);
|
|
38
42
|
this.#onSizeUpdate();
|
|
@@ -150,6 +154,12 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
|
|
|
150
154
|
#onButtonBlur = () => {
|
|
151
155
|
this.dispatchEvent(new CustomEvent('-blur'));
|
|
152
156
|
};
|
|
157
|
+
#onTooltipShow = () => {
|
|
158
|
+
this.dispatchEvent(new CustomEvent('-tooltip-show'));
|
|
159
|
+
};
|
|
160
|
+
#onTooltipHide = () => {
|
|
161
|
+
this.dispatchEvent(new CustomEvent('-tooltip-hide'));
|
|
162
|
+
};
|
|
153
163
|
#onFocusReactHandler = () => {
|
|
154
164
|
getReactEventHandler(this, 'on-focus')?.();
|
|
155
165
|
};
|
|
@@ -159,4 +169,10 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
|
|
|
159
169
|
#onClickReactHandler = e => {
|
|
160
170
|
getReactEventHandler(this, 'on-click')?.(e);
|
|
161
171
|
};
|
|
172
|
+
#onTooltipShowReactHandler = () => {
|
|
173
|
+
getReactEventHandler(this, 'on-tooltip-show')?.();
|
|
174
|
+
};
|
|
175
|
+
#onTooltipHideReactHandler = () => {
|
|
176
|
+
getReactEventHandler(this, 'on-tooltip-hide')?.();
|
|
177
|
+
};
|
|
162
178
|
});
|
package/icon-button/types.d.ts
CHANGED
|
@@ -37,4 +37,8 @@ export declare type TSinchIconButtonReact = TSinchElementReact<TSinchIconButtonE
|
|
|
37
37
|
'on-focus'?: (e: CustomEvent<void>) => void;
|
|
38
38
|
/** Blur event handler */
|
|
39
39
|
'on-blur'?: (e: CustomEvent<void>) => void;
|
|
40
|
+
/** Tooltip Show Event */
|
|
41
|
+
'on-tooltip-show'?: (e: CustomEvent<void>) => void;
|
|
42
|
+
/** Tooltip Hide Event */
|
|
43
|
+
'on-tooltip-hide'?: (e: CustomEvent<void>) => void;
|
|
40
44
|
};
|
package/input/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../icon-button';
|
|
2
2
|
import '../icons/close';
|
|
3
3
|
import '../stop-events';
|
|
4
|
-
import { Context, defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
|
|
4
|
+
import { Context, defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, getRect, isAttrTrue, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
|
|
5
5
|
import { assertSize, DEFAULT_SIZE, sizeValues } from '../utils/size';
|
|
6
|
-
const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}:host([data-size="l"]){--sinch-size:var(--sinch-size-l);--sinch-size-icon:var(--sinch-input-icon-size-l);--sinch-shape-radius:var(--sinch-shape-radius-l)}:host([data-size="m"]){--sinch-size:var(--sinch-size-m);--sinch-size-icon:var(--sinch-input-icon-size-m);--sinch-shape-radius:var(--sinch-shape-radius-m)}:host([data-size="s"]){--sinch-size:var(--sinch-size-s);--sinch-size-icon:var(--sinch-input-icon-size-s);--sinch-shape-radius:var(--sinch-shape-radius-s)}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;box-sizing:border-box;border-radius:var(--sinch-shape-radius);width:100%;height:var(--sinch-size);background-color:var(--sinch-color-bg-primary-light)}#input{all:initial;flex:1;min-width:0;height:100%;padding:0 12px 0 40px;font:var(--sinch-font-text-m);color:var(--sinch-color-text-default)}:host([data-size="s"]) #input{padding-left:32px}#input::placeholder{font:var(--sinch-font-text-m);color:var(--sinch-color-text-muted);opacity:1}#border{position:absolute;border:1px solid var(--sinch-color-border-dark);border-radius:var(--sinch-shape-radius);inset:0;pointer-events:none}:host([invalid]:not([invalid=false]):not([disabled])) #border{border-color:var(--sinch-color-text-invalid)}#input:disabled{color:var(--sinch-color-text-disabled)}#input:disabled::placeholder{color:var(--sinch-color-text-disabled)}#input:disabled+#border{border-color:var(--sinch-color-border-disabled)}#input:focus+#border{border-color:var(--sinch-color-border-focus);border-width:2px}#input[type=password]{font-size:1.5em;letter-spacing:.1em}#icon-wrapper{position:relative;height:100%}#icon{position:absolute;display:flex;align-items:center;left:10px;top:0;bottom:0;pointer-events:none;--sinch-color-icon:var(--sinch-color-stormy-500)}:host([disabled]:not([disabled=false])) #icon{--sinch-color-icon:var(--sinch-color-border-disabled)}#icon-wrapper.empty{display:none}#icon-wrapper.empty~#input{padding-left:12px}#clear{display:none;--sinch-icon-button-color-icon:var(--sinch-color-
|
|
6
|
+
const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}:host([data-size="l"]){--sinch-size:var(--sinch-size-l);--sinch-size-icon:var(--sinch-input-icon-size-l);--sinch-shape-radius:var(--sinch-shape-radius-l)}:host([data-size="m"]){--sinch-size:var(--sinch-size-m);--sinch-size-icon:var(--sinch-input-icon-size-m);--sinch-shape-radius:var(--sinch-shape-radius-m)}:host([data-size="s"]){--sinch-size:var(--sinch-size-s);--sinch-size-icon:var(--sinch-input-icon-size-s);--sinch-shape-radius:var(--sinch-shape-radius-s)}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;box-sizing:border-box;border-radius:var(--sinch-shape-radius);width:100%;height:var(--sinch-size);background-color:var(--sinch-color-bg-primary-light)}#input{all:initial;flex:1;min-width:0;height:100%;padding:0 12px 0 40px;font:var(--sinch-font-text-m);color:var(--sinch-color-text-default)}:host([data-size="s"]) #input{padding-left:32px}#input::placeholder{font:var(--sinch-font-text-m);color:var(--sinch-color-text-muted);opacity:1}#border{position:absolute;border:1px solid var(--sinch-color-border-dark);border-radius:var(--sinch-shape-radius);inset:0;pointer-events:none}:host([invalid]:not([invalid=false]):not([disabled])) #border{border-color:var(--sinch-color-text-invalid)}#input:disabled{color:var(--sinch-color-text-disabled)}#input:disabled::placeholder{color:var(--sinch-color-text-disabled)}#input:disabled+#border{border-color:var(--sinch-color-border-disabled)}#input:focus+#border{border-color:var(--sinch-color-border-focus);border-width:2px}#input[type=password]{font-size:1.5em;letter-spacing:.1em}#icon-wrapper{position:relative;height:100%}#icon{position:absolute;display:flex;align-items:center;left:10px;top:0;bottom:0;pointer-events:none;--sinch-color-icon:var(--sinch-color-stormy-500)}:host([disabled]:not([disabled=false])) #icon{--sinch-color-icon:var(--sinch-color-border-disabled)}#icon-wrapper.empty{display:none}#icon-wrapper.empty~#input{padding-left:12px}#clear-wrapper{margin-left:-10px;margin-right:4px}#clear{display:none;--sinch-icon-button-color-icon:var(--sinch-color-stormy-500)}:host([value]:not([value=""])) #clear[data-focus],:host([value]:not([value=""])) #clear[data-tooltip]{display:block}#right{display:flex;flex-direction:row;align-self:stretch;align-items:center;gap:4px;padding-right:4px}#right.empty{display:none}#left{display:flex;flex-direction:row;align-self:stretch;align-items:center;gap:4px;padding-left:4px}#left.empty{display:none}</style><div id="wrapper"><div id="left"><slot name="left"></slot></div><div id="icon-wrapper"><div id="icon"><slot name="icon"></slot></div></div><input id="input" type="text"/><div id="border"></div><div id="clear-wrapper"><sinch-stop-events events="keydown"><sinch-icon-button id="clear" aria-label="Clear input value"><sinch-icon-close slot="icon"></sinch-icon-close></sinch-icon-button></sinch-stop-events></div><div id="right"><slot name="right"></slot></div></div>';
|
|
7
7
|
import { inputTypes } from './utils';
|
|
8
8
|
const template = document.createElement('template');
|
|
9
9
|
template.innerHTML = templateHTML;
|
|
@@ -51,7 +51,10 @@ defineCustomElement('sinch-input', class extends NectaryElement {
|
|
|
51
51
|
this.#$input.addEventListener('keydown', this.#onSelectionChange, options);
|
|
52
52
|
this.#$input.addEventListener('focus', this.#onInputFocus, options);
|
|
53
53
|
this.#$input.addEventListener('blur', this.#onInputBlur, options);
|
|
54
|
-
this.#$clear.addEventListener('click', this.#
|
|
54
|
+
this.#$clear.addEventListener('click', this.#onClearButtonClick, options);
|
|
55
|
+
this.#$clear.addEventListener('blur', this.#onClearButtonBlur, options);
|
|
56
|
+
this.#$clear.addEventListener('-tooltip-show', this.#onClearButtonTooltipShow, options);
|
|
57
|
+
this.#$clear.addEventListener('-tooltip-hide', this.#onClearButtonTooltipHide, options);
|
|
55
58
|
this.#$iconSlot.addEventListener('slotchange', this.#onIconSlotChange, options);
|
|
56
59
|
this.#$leftSlot.addEventListener('slotchange', this.#onLeftSlotChange, options);
|
|
57
60
|
this.#$rightSlot.addEventListener('slotchange', this.#onRightSlotChange, options);
|
|
@@ -91,7 +94,6 @@ defineCustomElement('sinch-input', class extends NectaryElement {
|
|
|
91
94
|
this.#$input.setSelectionRange(this.#cursorPos, this.#cursorPos);
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
|
-
setClass(this.#$clear, 'active', nextVal.length > 0);
|
|
95
97
|
this.#onRightSlotChange();
|
|
96
98
|
break;
|
|
97
99
|
}
|
|
@@ -185,6 +187,9 @@ defineCustomElement('sinch-input', class extends NectaryElement {
|
|
|
185
187
|
set selectionDirection(value) {
|
|
186
188
|
this.#$input.selectionDirection = value;
|
|
187
189
|
}
|
|
190
|
+
get clearButtonRect() {
|
|
191
|
+
return getRect(this.#$clear);
|
|
192
|
+
}
|
|
188
193
|
get focusable() {
|
|
189
194
|
return true;
|
|
190
195
|
}
|
|
@@ -253,19 +258,18 @@ defineCustomElement('sinch-input', class extends NectaryElement {
|
|
|
253
258
|
setClass(this.#$leftWrapper, 'empty', isEmpty);
|
|
254
259
|
};
|
|
255
260
|
#onRightSlotChange = () => {
|
|
256
|
-
const isEmpty = this.#$rightSlot.assignedElements().length === 0
|
|
261
|
+
const isEmpty = this.#$rightSlot.assignedElements().length === 0;
|
|
257
262
|
setClass(this.#$rightWrapper, 'empty', isEmpty);
|
|
258
263
|
};
|
|
259
264
|
#onInputFocus = () => {
|
|
265
|
+
this.#$clear.setAttribute('data-focus', '');
|
|
260
266
|
this.dispatchEvent(new CustomEvent('-focus'));
|
|
261
267
|
};
|
|
262
|
-
#onInputBlur =
|
|
268
|
+
#onInputBlur = e => {
|
|
263
269
|
this.dispatchEvent(new CustomEvent('-blur'));
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.#$input.focus();
|
|
268
|
-
this.#handleInput();
|
|
270
|
+
if (e.relatedTarget !== this.#$clear) {
|
|
271
|
+
this.#$clear.removeAttribute('data-focus');
|
|
272
|
+
}
|
|
269
273
|
};
|
|
270
274
|
#onSizeUpdate() {
|
|
271
275
|
if (!this.isConnected) {
|
|
@@ -274,6 +278,23 @@ defineCustomElement('sinch-input', class extends NectaryElement {
|
|
|
274
278
|
const size = this.getAttribute('data-size') ?? DEFAULT_SIZE;
|
|
275
279
|
this.#sizeContext.dispatch(size);
|
|
276
280
|
}
|
|
281
|
+
#onClearButtonClick = () => {
|
|
282
|
+
this.#$input.value = '';
|
|
283
|
+
this.#$input.focus();
|
|
284
|
+
this.#handleInput();
|
|
285
|
+
};
|
|
286
|
+
#onClearButtonBlur = e => {
|
|
287
|
+
if (e.relatedTarget !== this.#$input) {
|
|
288
|
+
this.#$clear.removeAttribute('data-focus');
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
#onClearButtonTooltipShow = () => {
|
|
292
|
+
this.#$clear.setAttribute('data-tooltip', '');
|
|
293
|
+
};
|
|
294
|
+
#onClearButtonTooltipHide = () => {
|
|
295
|
+
this.#$input.focus();
|
|
296
|
+
this.#$clear.removeAttribute('data-tooltip');
|
|
297
|
+
};
|
|
277
298
|
#onChangeReactHandler = e => {
|
|
278
299
|
getReactEventHandler(this, 'on-change')?.(e);
|
|
279
300
|
};
|
package/input/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TSinchElementReact } from '../types';
|
|
1
|
+
import type { TRect, TSinchElementReact } from '../types';
|
|
2
2
|
import type { TSinchSize } from '../utils/size';
|
|
3
3
|
import type { SyntheticEvent } from 'react';
|
|
4
4
|
export declare type TSinchInputType = 'text' | 'password';
|
|
@@ -18,6 +18,7 @@ export declare type TSinchInputElement = HTMLElement & {
|
|
|
18
18
|
selectionStart: number | null;
|
|
19
19
|
selectionEnd: number | null;
|
|
20
20
|
selectionDirection: 'forward' | 'backward' | 'none' | null;
|
|
21
|
+
readonly clearButtonRect: TRect;
|
|
21
22
|
/** Change value event */
|
|
22
23
|
addEventListener(type: '-change', listener: (e: CustomEvent<string>) => void): void;
|
|
23
24
|
/** Focus event */
|
package/link/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../icons/open-in-new';
|
|
2
2
|
import '../icons/arrow-forward';
|
|
3
3
|
import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, NectaryElement, isAttrTrue, getReactEventHandler } from '../utils';
|
|
4
|
-
const templateHTML = '<style>:host{display:inline}a{font:var(--sinch-font-text-m);font-size:inherit;line-height:inherit;color:var(--sinch-color-tropical-500);border-radius:.5em;--sinch-size-icon:1em;--sinch-color-icon:var(--sinch-color-tropical-500)}a:hover{color:var(--sinch-color-tropical-600);--sinch-color-icon:var(--sinch-color-tropical-600)}a:focus-visible{outline:2px solid var(--sinch-color-border-focus);outline-offset:2px}#external-icon{display:none;margin-right:.2em;vertical-align:-.2em}#standalone-icon{display:none}:host([external]:not([external=false])) #external-icon{display:inline-block}:host([standalone]:not([standalone=false])){display:block}:host([standalone]:not([standalone=false])) a{display:block;font:var(--sinch-font-text-m);font-weight:var(--sinch-font-weight-emphasized);text-decoration:none;border-radius:var(--sinch-shape-radius-m);width:fit-content;--sinch-size-icon:24px}:host([standalone]:not([standalone=false])) #external-icon{margin-right:8px;vertical-align:-7px}:host([standalone]:not([standalone=false]):is([external=false],:not([external]))) #standalone-icon{display:inline-block;vertical-align:-7px
|
|
4
|
+
const templateHTML = '<style>:host{display:inline}a{font:var(--sinch-font-text-m);font-size:inherit;line-height:inherit;color:var(--sinch-color-tropical-500);border-radius:.5em;white-space:nowrap;--sinch-size-icon:1em;--sinch-color-icon:var(--sinch-color-tropical-500)}a:hover{color:var(--sinch-color-tropical-600);--sinch-color-icon:var(--sinch-color-tropical-600)}a:focus-visible{outline:2px solid var(--sinch-color-border-focus);outline-offset:2px}#external-icon{display:none;margin-right:.2em;vertical-align:-.2em}#standalone-icon,#standalone-icon-prefix{display:none}:host([external]:not([external=false])) #external-icon{display:inline-block}:host([standalone]:not([standalone=false])){display:block}:host([standalone]:not([standalone=false])) a{display:block;font:var(--sinch-font-text-m);font-weight:var(--sinch-font-weight-emphasized);text-decoration:none;border-radius:var(--sinch-shape-radius-m);width:fit-content;--sinch-size-icon:24px}:host([standalone]:not([standalone=false])) #external-icon{margin-right:8px;vertical-align:-7px}:host([standalone]:not([standalone=false])) #standalone-icon-prefix{display:inline}:host([standalone]:not([standalone=false]):is([external=false],:not([external]))) #standalone-icon{display:inline-block;vertical-align:-7px}:host([disabled]:not([disabled=false])) a{color:var(--sinch-color-tropical-200);pointer-events:none;cursor:initial;--sinch-color-icon:var(--sinch-color-tropical-200)}#content{white-space:var(--sinch-text-white-space,normal)}</style><a referrerpolicy="no-referer" aria-hidden="true"><sinch-icon-open-in-new id="external-icon"></sinch-icon-open-in-new><span id="content"></span><span id="standalone-icon-prefix"> </span><sinch-icon-arrow-forward id="standalone-icon"></sinch-icon-arrow-forward></a>';
|
|
5
5
|
const template = document.createElement('template');
|
|
6
6
|
template.innerHTML = templateHTML;
|
|
7
7
|
defineCustomElement('sinch-link', class extends NectaryElement {
|
package/package.json
CHANGED
package/popover/index.js
CHANGED
|
@@ -29,7 +29,7 @@ defineCustomElement('sinch-popover', class extends NectaryElement {
|
|
|
29
29
|
this.#$pop.addEventListener('-close', this.#onPopClose, {
|
|
30
30
|
signal
|
|
31
31
|
});
|
|
32
|
-
subscribeContext(this, 'visibility', this.#onContextVisibility, signal);
|
|
32
|
+
subscribeContext(this.#$content, 'visibility', this.#onContextVisibility, signal);
|
|
33
33
|
updateAttribute(this.#$pop, 'orientation', getPopOrientation(this.orientation));
|
|
34
34
|
}
|
|
35
35
|
disconnectedCallback() {
|
package/text/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../icons/cancel';
|
|
2
2
|
import { defineCustomElement, getBooleanAttribute, updateBooleanAttribute, NectaryElement, getLiteralAttribute, updateLiteralAttribute, isAttrTrue } from '../utils';
|
|
3
|
-
const templateHTML = '<style>:host{display:block;font:var(--sinch-font-text-m)}:host([inline]:not([inline=false])){display:inline}:host([type="s"]){font:var(--sinch-font-text-s)}:host([type=xs]){font:var(--sinch-font-text-xs)}:host([type=xxs]){font:var(--sinch-font-text-xxs)}:host([emphasized]:not([emphasized=false])){font-weight:var(--sinch-font-weight-emphasized)}:host([ellipsis]:not([ellipsis=false])){overflow:hidden;text-overflow:ellipsis;white-space:nowrap}</style><slot></slot>';
|
|
3
|
+
const templateHTML = '<style>:host{display:block;font:var(--sinch-font-text-m)}:host([inline]:not([inline=false])){display:inline}:host([type="s"]){font:var(--sinch-font-text-s)}:host([type=xs]){font:var(--sinch-font-text-xs)}:host([type=xxs]){font:var(--sinch-font-text-xxs)}:host([emphasized]:not([emphasized=false])){font-weight:var(--sinch-font-weight-emphasized)}:host([ellipsis]:not([ellipsis=false])){overflow:hidden;text-overflow:ellipsis;white-space:nowrap;--sinch-text-white-space:nowrap}</style><slot></slot>';
|
|
4
4
|
import { assertType, typeValues } from './utils';
|
|
5
5
|
const template = document.createElement('template');
|
|
6
6
|
template.innerHTML = templateHTML;
|
package/textarea/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
|
|
2
|
-
const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#input{all:initial;display:block;border:1px solid var(--sinch-color-stormy-200);box-sizing:border-box;border-radius:var(--sinch-shape-radius-s);width:100%;padding:8px 12px;font:var(--sinch-font-text-m);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);resize:none}#input::placeholder{font:var(--sinch-font-text-m);color:var(--sinch-color-text-muted);opacity:1}#input:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100)}#input:disabled::placeholder{color:var(--sinch-color-snow-500)}#input:focus{border-color:var(--sinch-color-stormy-600)}:host([resizable]:not([resizable=false])) #input{resize:vertical}:host([invalid]:not([invalid=false])) #input:enabled{border-color:var(--sinch-color-text-invalid)}</style><textarea id="input"></textarea>';
|
|
2
|
+
const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#input{all:initial;display:block;border:1px solid var(--sinch-color-stormy-200);box-sizing:border-box;border-radius:var(--sinch-shape-radius-s);width:100%;padding:8px 12px;font:var(--sinch-font-text-m);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);resize:none;white-space:pre-wrap}#input::placeholder{font:var(--sinch-font-text-m);color:var(--sinch-color-text-muted);opacity:1}#input:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100)}#input:disabled::placeholder{color:var(--sinch-color-snow-500)}#input:focus{border-color:var(--sinch-color-stormy-600)}:host([resizable]:not([resizable=false])) #input{resize:vertical}:host([invalid]:not([invalid=false])) #input:enabled{border-color:var(--sinch-color-text-invalid)}</style><textarea id="input"></textarea>';
|
|
3
3
|
const template = document.createElement('template');
|
|
4
4
|
template.innerHTML = templateHTML;
|
|
5
5
|
defineCustomElement('sinch-textarea', class extends NectaryElement {
|
package/time-picker/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import '../icon-button';
|
|
2
2
|
import '../icons/done';
|
|
3
|
-
import '../icons/arrow-drop-up';
|
|
4
|
-
import '../icons/arrow-drop-down';
|
|
5
3
|
import '../segmented-control';
|
|
6
4
|
import '../segmented-control-option';
|
|
7
5
|
import type { TSinchTimePickerElement, TSinchTimePickerReact } from './types';
|
package/time-picker/index.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import '../icon-button';
|
|
2
2
|
import '../icons/done';
|
|
3
|
-
import '../icons/arrow-drop-up';
|
|
4
|
-
import '../icons/arrow-drop-down';
|
|
5
3
|
import '../segmented-control';
|
|
6
4
|
import '../segmented-control-option';
|
|
7
5
|
import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, getRect, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
|
|
8
|
-
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-font-title-xl);line-height:48px}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-color-stormy-500)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;font:var(--sinch-font-text-s);line-height:28px;text-align:center;color:var(--sinch-color-text-default);top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-24{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-minute{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-hour-12:hover,.digit-hour-24:hover,.digit-minute:hover{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected,.digit-hour-24.selected,.digit-minute.selected{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected{font-size:16px}.digit-hour-24.selected{font-size:16px}.digit-minute.selected{font-size:16px}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-color-stormy-500)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-color-tropical-500)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0;--sinch-size-icon:24px}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-
|
|
6
|
+
const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-font-title-xl);line-height:48px;user-select:none}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-color-stormy-500)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;font:var(--sinch-font-text-s);line-height:28px;text-align:center;color:var(--sinch-color-text-default);top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-24{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-minute{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-hour-12:hover,.digit-hour-24:hover,.digit-minute:hover{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected,.digit-hour-24.selected,.digit-minute.selected{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected{font-size:16px}.digit-hour-24.selected{font-size:16px}.digit-minute.selected{font-size:16px}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-color-stormy-500)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2;outline:0}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-hour:focus-visible,#needle-minute:focus-visible{background-color:var(--sinch-color-tropical-500)}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-color-tropical-500)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0;--sinch-size-icon:24px}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><div id="header-colon">:</div><div id="header-minutes" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span></div><sinch-icon-button id="submit" size="s" aria-label="Submit"><sinch-icon-done slot="icon"></sinch-icon-done></sinch-icon-button></div><div id="picker" aria-hidden="true"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour" tabindex="0"></div><div id="needle-minute" tabindex="0"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
|
|
9
7
|
import { getNeedleRotationDeg, getShortestCssDeg, hourToIndex, parseTime, stringifyHour, stringifyHourFace, stringifyMinute, stringifyTime } from './utils';
|
|
10
8
|
const template = document.createElement('template');
|
|
11
9
|
template.innerHTML = templateHTML;
|
|
@@ -33,6 +31,7 @@ defineCustomElement('sinch-time-picker', class extends NectaryElement {
|
|
|
33
31
|
#$submitButton;
|
|
34
32
|
#hour = 0;
|
|
35
33
|
#minute = 0;
|
|
34
|
+
#controller = null;
|
|
36
35
|
constructor() {
|
|
37
36
|
super();
|
|
38
37
|
const shadowRoot = this.attachShadow();
|
|
@@ -96,20 +95,19 @@ defineCustomElement('sinch-time-picker', class extends NectaryElement {
|
|
|
96
95
|
this.#$pickerMinutes.appendChild(minutesFrag);
|
|
97
96
|
}
|
|
98
97
|
connectedCallback() {
|
|
99
|
-
this
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.#$
|
|
104
|
-
this.addEventListener('
|
|
98
|
+
this.#controller = new AbortController();
|
|
99
|
+
const options = {
|
|
100
|
+
signal: this.#controller.signal
|
|
101
|
+
};
|
|
102
|
+
this.#$pickerTouch.addEventListener('click', this.#onPickerClick, options);
|
|
103
|
+
this.#$ampm.addEventListener('change', this.#onAmPmChange, options);
|
|
104
|
+
this.#$submitButton.addEventListener('click', this.#onSubmitButtonClick, options);
|
|
105
|
+
this.#$needleHour.addEventListener('keydown', this.#onHoursKeydown, options);
|
|
106
|
+
this.#$needleMinute.addEventListener('keydown', this.#onMinutesKeydown, options);
|
|
107
|
+
this.addEventListener('-change', this.#onChangeReactHandler, options);
|
|
105
108
|
}
|
|
106
109
|
disconnectedCallback() {
|
|
107
|
-
this
|
|
108
|
-
this.#$ampm.removeEventListener('change', this.#onAmPmChange);
|
|
109
|
-
this.#$submitButton.removeEventListener('click', this.#onSubmitButtonClick);
|
|
110
|
-
this.#$headerHours.removeEventListener('keydown', this.#onHoursKeydown);
|
|
111
|
-
this.#$headerMinutes.removeEventListener('keydown', this.#onMinutesKeydown);
|
|
112
|
-
this.removeEventListener('-change', this.#onChangeReactHandler);
|
|
110
|
+
this.#controller.abort();
|
|
113
111
|
}
|
|
114
112
|
static get observedAttributes() {
|
|
115
113
|
return ['value', 'ampm', 'submit-aria-label'];
|
package/tooltip/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../text';
|
|
2
2
|
import '../pop';
|
|
3
|
-
import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass, rectOverlap } from '../utils';
|
|
3
|
+
import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass, rectOverlap, getReactEventHandler } 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';
|
|
@@ -49,6 +49,8 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
49
49
|
signal: this.#controller.signal
|
|
50
50
|
};
|
|
51
51
|
this.#$pop.addEventListener('-close', this.#onPopClose, options);
|
|
52
|
+
this.addEventListener('-show', this.#onShowReactHandler, options);
|
|
53
|
+
this.addEventListener('-hide', this.#onHideReactHandler, options);
|
|
52
54
|
updateAttribute(this.#$pop, 'orientation', getPopOrientation(this.orientation));
|
|
53
55
|
this.#updateText();
|
|
54
56
|
}
|
|
@@ -129,6 +131,7 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
129
131
|
this.#unsubscribeScroll();
|
|
130
132
|
};
|
|
131
133
|
#onStateShow = () => {
|
|
134
|
+
this.#dispatchShowEvent();
|
|
132
135
|
updateBooleanAttribute(this.#$pop, 'open', true);
|
|
133
136
|
requestAnimationFrame(this.#updateTipOrientation);
|
|
134
137
|
if (this.#animation !== null) {
|
|
@@ -152,6 +155,7 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
152
155
|
this.#animation.finish();
|
|
153
156
|
this.#resetTipOrientation();
|
|
154
157
|
updateBooleanAttribute(this.#$pop, 'open', false);
|
|
158
|
+
this.#dispatchHideEvent();
|
|
155
159
|
this.#unsubscribeMouseLeaveEvents();
|
|
156
160
|
};
|
|
157
161
|
#resetTipOrientation() {
|
|
@@ -232,4 +236,16 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
|
|
|
232
236
|
#isOpen() {
|
|
233
237
|
return this.#$pop.hasAttribute('open');
|
|
234
238
|
}
|
|
239
|
+
#dispatchShowEvent() {
|
|
240
|
+
this.dispatchEvent(new CustomEvent('-show'));
|
|
241
|
+
}
|
|
242
|
+
#dispatchHideEvent() {
|
|
243
|
+
this.dispatchEvent(new CustomEvent('-hide'));
|
|
244
|
+
}
|
|
245
|
+
#onShowReactHandler = () => {
|
|
246
|
+
getReactEventHandler(this, 'on-show')?.();
|
|
247
|
+
};
|
|
248
|
+
#onHideReactHandler = () => {
|
|
249
|
+
getReactEventHandler(this, 'on-hide')?.();
|
|
250
|
+
};
|
|
235
251
|
});
|
package/tooltip/types.d.ts
CHANGED
|
@@ -11,6 +11,10 @@ export declare type TSinchTooltipElement = HTMLElement & {
|
|
|
11
11
|
orientation: TSinchTooltipOrientation;
|
|
12
12
|
readonly footprintRect: TRect;
|
|
13
13
|
readonly tooltipRect: TRect;
|
|
14
|
+
/** Show event */
|
|
15
|
+
addEventListener(type: '-show', listener: (e: CustomEvent<void>) => void): void;
|
|
16
|
+
/** Hide event */
|
|
17
|
+
addEventListener(type: '-hide', listener: (e: CustomEvent<void>) => void): void;
|
|
14
18
|
/** Text */
|
|
15
19
|
setAttribute(name: 'text', value: string): void;
|
|
16
20
|
/** @deprecated */
|
|
@@ -29,4 +33,8 @@ export declare type TSinchTooltipReact = TSinchElementReact<TSinchTooltipElement
|
|
|
29
33
|
inverted?: boolean;
|
|
30
34
|
/** Orientation, where it *points to* from origin */
|
|
31
35
|
orientation?: TSinchTooltipOrientation;
|
|
36
|
+
/** Show event handler */
|
|
37
|
+
'on-show'?: (e: CustomEvent<void>) => void;
|
|
38
|
+
/** Hide event handler */
|
|
39
|
+
'on-hide'?: (e: CustomEvent<void>) => void;
|
|
32
40
|
};
|