@nectary/components 0.42.1 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/action-menu/index.js +1 -2
  2. package/card-container/index.js +1 -1
  3. package/color-menu/index.d.ts +3 -3
  4. package/color-menu/index.js +1 -2
  5. package/color-menu/types.d.ts +2 -2
  6. package/dialog/index.js +2 -2
  7. package/emoji/index.d.ts +11 -0
  8. package/emoji/index.js +47 -0
  9. package/emoji/types.d.ts +11 -0
  10. package/emoji/types.js +1 -0
  11. package/emoji/utils.d.ts +1 -0
  12. package/emoji/utils.js +46 -0
  13. package/emoji-picker/index.d.ts +28 -0
  14. package/emoji-picker/index.js +319 -0
  15. package/emoji-picker/types.d.ts +25 -0
  16. package/emoji-picker/types.js +1 -0
  17. package/icon/index.d.ts +11 -0
  18. package/icon/index.js +32 -0
  19. package/icon/types.d.ts +11 -0
  20. package/icon/types.js +1 -0
  21. package/icon-button/index.js +1 -1
  22. package/package.json +2 -2
  23. package/pop/index.js +1 -2
  24. package/popover/index.js +1 -2
  25. package/segment/index.js +1 -1
  26. package/select-menu/index.js +1 -2
  27. package/tabs/index.js +24 -76
  28. package/tabs/types.d.ts +9 -2
  29. package/tabs-icon-option/index.d.ts +11 -0
  30. package/tabs-icon-option/index.js +75 -0
  31. package/tabs-icon-option/types.d.ts +19 -0
  32. package/tabs-icon-option/types.js +1 -0
  33. package/tabs-option/index.d.ts +1 -0
  34. package/tabs-option/index.js +8 -15
  35. package/tabs-option/types.d.ts +13 -5
  36. package/theme/emoji.css +6 -0
  37. package/theme/fonts.css +9 -0
  38. package/theme/fonts.json +9 -0
  39. package/theme/icon.css +7 -0
  40. package/theme.css +2 -0
  41. package/tooltip/index.js +78 -31
  42. package/utils/csv.d.ts +3 -0
  43. package/utils/csv.js +21 -0
  44. package/utils/dom.d.ts +30 -0
  45. package/utils/dom.js +143 -0
  46. package/utils/element.d.ts +12 -0
  47. package/utils/element.js +38 -0
  48. package/utils/get-react-event-handler.d.ts +1 -0
  49. package/utils/get-react-event-handler.js +8 -0
  50. package/utils/index.d.ts +8 -57
  51. package/utils/index.js +8 -301
  52. package/utils/rect.d.ts +4 -0
  53. package/utils/rect.js +29 -0
  54. package/utils/slot.d.ts +4 -0
  55. package/utils/slot.js +38 -0
  56. package/utils/throttle.d.ts +4 -0
  57. package/utils/throttle.js +25 -0
  58. /package/{utils/animation.d.ts → tooltip/tooltip-state.d.ts} +0 -0
  59. /package/{utils/animation.js → tooltip/tooltip-state.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  import '../tooltip';
2
2
  import { defineCustomElement, getBooleanAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute } from '../utils';
3
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}button{all:initial;position:relative;display:flex;align-items:center;justify-content:center;width:48px;height:48px;cursor:pointer;contain:size;--sinch-size-icon:24px;--sinch-color-icon:var(--sinch-icon-button-color, var(--sinch-color-stormy-500));--sinch-icon-button-shape-radius:var(--sinch-shape-radius-l)}button::before{content:"";position:absolute;left:0;top:0;width:100%;height:100%;border-radius:var(--sinch-icon-button-shape-radius);pointer-events:none;box-sizing:border-box;border:1px solid transparent;background-color:transparent;mix-blend-mode:multiply}button:focus-visible::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-icon-button-shape-radius) + 4px);pointer-events:none}@supports not selector(:focus-visible){button:focus::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-icon-button-shape-radius) + 4px);pointer-events:none}}button:enabled:hover::before{background-color:#f1f2f4}button:enabled:active::before{background-color:#e3e6e8}button:disabled{background-color:transparent;cursor:initial;--sinch-color-spinner-bg:var(--sinch-color-snow-200);--sinch-color-spinner-fg:var(--sinch-color-stormy-200);--sinch-color-icon:var(--sinch-color-stormy-100)}:host([small]:not([small=false])) #button{width:32px;height:32px;--sinch-icon-button-shape-radius:var(--sinch-shape-radius-m)}button>*{pointer-events:none}</style><sinch-tooltip id="tooltip"><button id="button"><slot name="icon"></slot></button></sinch-tooltip>';
3
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}button{all:initial;position:relative;display:flex;align-items:center;justify-content:center;width:48px;height:48px;cursor:pointer;contain:size;--sinch-size-icon:24px;--sinch-color-icon:var(--sinch-icon-button-color, var(--sinch-color-stormy-500));--sinch-icon-button-shape-radius:var(--sinch-shape-radius-l)}button::before{content:"";position:absolute;left:0;top:0;width:100%;height:100%;border-radius:var(--sinch-icon-button-shape-radius);pointer-events:none;box-sizing:border-box;border:1px solid transparent;background-color:transparent;mix-blend-mode:multiply}button:focus-visible::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-icon-button-shape-radius) + 4px);pointer-events:none}@supports not selector(:focus-visible){button:focus::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-icon-button-shape-radius) + 4px);pointer-events:none}}button:enabled:hover::before{background-color:#f1f2f4}button:enabled:active::before{background-color:#e3e6e8}button:disabled{background-color:transparent;cursor:initial;--sinch-color-spinner-bg:var(--sinch-color-snow-200);--sinch-color-spinner-fg:var(--sinch-color-stormy-200);--sinch-color-icon:var(--sinch-color-stormy-100)}:host([small]:not([small=false])) #button{width:32px;height:32px;--sinch-icon-button-shape-radius:var(--sinch-shape-radius-m)}button>*{pointer-events:none}</style><sinch-tooltip id="tooltip" inverted><button id="button"><slot name="icon"></slot></button></sinch-tooltip>';
4
4
  const template = document.createElement('template');
5
5
  template.innerHTML = templateHTML;
6
6
  defineCustomElement('sinch-icon-button', class extends NectaryElement {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "0.42.1",
3
+ "version": "0.43.0",
4
4
  "files": [
5
5
  "theme.css",
6
6
  "theme/*.css",
7
- "theme/fonts.json",
7
+ "theme/*.json",
8
8
  "**/*/*.js",
9
9
  "**/*/*.d.ts",
10
10
  "types.d.ts",
package/pop/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import dialogPolyfill from 'dialog-polyfill';
2
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement } from '../utils';
3
- import { Context, dispatchContextConnectEvent, dispatchContextDisconnectEvent } from '../utils/context';
2
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, dispatchContextConnectEvent, dispatchContextDisconnectEvent } from '../utils';
4
3
  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}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:transparent}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}#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" aria-haspopup="dialog" aria-expanded="false" name="target"></slot><div id="focus" tabindex="-1"></div><dialog id="dialog"><div id="content"><slot name="content"></slot></div><div id="target-open"><slot name="target-open"></slot></div></dialog>';
5
4
  import { assertOrientation, disableScroll, enableScroll, orientationValues } from './utils';
6
5
  const template = document.createElement('template');
package/popover/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import '../pop';
2
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateBooleanAttribute, NectaryElement, updateAttribute, getReactEventHandler, isAttrTrue, setClass, rectOverlap } from '../utils';
3
- import { dispatchContextConnectEvent, dispatchContextDisconnectEvent } from '../utils/context';
2
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateBooleanAttribute, NectaryElement, updateAttribute, getReactEventHandler, isAttrTrue, setClass, rectOverlap, dispatchContextConnectEvent, dispatchContextDisconnectEvent } from '../utils';
4
3
  const templateHTML = '<style>:host{display:contents}#content-wrapper{position:relative;padding-top:4px}#content{font:var(--sinch-font-body);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);border:1px solid var(--sinch-color-snow-500);border-radius:var(--sinch-shape-radius-m);box-shadow:var(--sinch-elevation-level-2);overflow:hidden}#tip{position:absolute;left:50%;top:13px;transform:translateX(-50%) rotate(180deg);transform-origin:top center;fill:var(--sinch-color-snow-100);stroke:var(--sinch-color-snow-500);stroke-linecap:square;pointer-events:none;display:none}:host([orientation^=top]) #tip{transform:translateX(-50%) rotate(0);top:calc(100% - 13px)}:host([tip]) #tip:not(.hidden){display:block}:host([tip]) #content{box-shadow:none}:host([tip]) #content-wrapper{padding-top:12px;filter:drop-shadow(var(--sinch-elevation-level-2))}:host([orientation^=top]) #content-wrapper{padding-top:0;padding-bottom:4px}:host([orientation^=top][tip]) #content-wrapper{padding-top:0;padding-bottom:12px}</style><sinch-pop id="pop" inset="4"><slot name="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><slot name="content"></slot></div><svg id="tip" width="16" height="9" aria-hidden="true"><path d="m0 0 8 8 8 -8"/></svg></div></sinch-pop>';
5
4
  import { assertOrientation, getPopOrientation, orientationValues } from './utils';
6
5
  const TIP_SIZE = 16;
package/segment/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import '../title';
2
2
  import { getTitleLevelFromType } from '../title/utils';
3
3
  import { defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute, updateLiteralAttribute } from '../utils';
4
- const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;gap:16px;width:100%;height:100%;border:1px solid var(--sinch-color-snow-700);border-radius:var(--sinch-shape-radius-l);box-sizing:border-box;background-color:var(--sinch-color-snow-100);color:var(--sinch-color-text-default);font:var(--sinch-font-body);box-shadow:var(--sinch-elevation-level-2);padding:8px 24px 16px}#header{display:flex;flex-direction:row;align-items:center;height:48px;gap:8px;--sinch-size-icon:32px}#caption{color:var(--sinch-color-text-default);min-width:1em}#info{display:flex;flex-direction:row;align-items:center;gap:8px;margin-left:auto;align-self:stretch}#info.empty{display:none}#preview{flex:1;flex-basis:auto;height:48px;min-width:0;overflow:hidden;margin-left:24px}#preview.empty{display:none}#info.empty+#collapse{margin-left:auto}#collapse.empty{display:none}#preview:not(.empty)+#info.empty+#collapse:not(.empty),#preview:not(.empty)+#info:not(.empty){margin-left:24px}#content-wrapper{flex:1;min-height:0;overflow-y:auto}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px}#action.empty{display:none}:host([collapsed]:not([collapsed=false])) :is(#content-wrapper,#action){display:none}:host([collapsed]:not([collapsed=false])) #wrapper{padding-bottom:8px}::slotted([slot=icon]){margin-right:8px}</style><div id="wrapper"><div id="header"><slot name="icon"></slot><sinch-title id="caption" level="3" type="m" ellipsis></sinch-title><div id="preview"><slot name="preview"></slot></div><div id="info"><slot name="info"></slot></div><div id="collapse"><slot name="collapse"></slot></div></div><div id="content-wrapper"><slot name="content"></slot></div><div id="action"><slot name="action"></slot></div></div>';
4
+ const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;gap:12px;width:100%;height:100%;border:1px solid var(--sinch-color-snow-700);border-radius:var(--sinch-shape-radius-l);box-sizing:border-box;background-color:var(--sinch-color-snow-100);color:var(--sinch-color-text-default);font:var(--sinch-font-body);box-shadow:var(--sinch-elevation-level-2);padding:8px 0 16px}#header{display:flex;flex-direction:row;align-items:center;height:48px;gap:8px;padding:0 24px;--sinch-size-icon:32px}#caption{color:var(--sinch-color-text-default);min-width:1em}#info{display:flex;flex-direction:row;align-items:center;gap:8px;margin-left:auto;align-self:stretch}#info.empty{display:none}#preview{flex:1;flex-basis:auto;height:48px;min-width:0;overflow:hidden;margin-left:24px}#preview.empty{display:none}#info.empty+#collapse{margin-left:auto}#collapse.empty{display:none}#preview:not(.empty)+#info.empty+#collapse:not(.empty),#preview:not(.empty)+#info:not(.empty){margin-left:24px}#content-wrapper{flex:1;min-height:0;overflow-y:auto;padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;padding:0 24px}#action.empty{display:none}:host([collapsed]:not([collapsed=false])) :is(#content-wrapper,#action){display:none}:host([collapsed]:not([collapsed=false])) #wrapper{padding-bottom:8px}::slotted([slot=icon]){margin-right:8px}</style><div id="wrapper"><div id="header"><slot name="icon"></slot><sinch-title id="caption" level="3" type="m" ellipsis></sinch-title><div id="preview"><slot name="preview"></slot></div><div id="info"><slot name="info"></slot></div><div id="collapse"><slot name="collapse"></slot></div></div><div id="content-wrapper"><slot name="content"></slot></div><div id="action"><slot name="action"></slot></div></div>';
5
5
  import { assertSize, sizeValues } from './utils';
6
6
  const template = document.createElement('template');
7
7
  template.innerHTML = templateHTML;
@@ -1,5 +1,4 @@
1
- import { attrValueToPixels, defineCustomElement, getAttribute, getBooleanAttribute, getCsvSet, getFirstCsvValue, getIntegerAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateCsv, updateExplicitBooleanAttribute, updateIntegerAttribute } from '../utils';
2
- import { dispatchContextConnectEvent, dispatchContextDisconnectEvent } from '../utils/context';
1
+ import { attrValueToPixels, defineCustomElement, dispatchContextConnectEvent, dispatchContextDisconnectEvent, getAttribute, getBooleanAttribute, getCsvSet, getFirstCsvValue, getIntegerAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateCsv, updateExplicitBooleanAttribute, updateIntegerAttribute } from '../utils';
3
2
  const templateHTML = '<style>:host{display:block;outline:0}#listbox{overflow-y:auto}</style><div id="listbox" role="presentation"><slot></slot></div>';
4
3
  const ITEM_HEIGHT = 40;
5
4
  const template = document.createElement('template');
package/tabs/index.js CHANGED
@@ -1,12 +1,10 @@
1
- import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, NectaryElement, updateAttribute, updateBooleanAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:row;height:44px;width:100%;box-sizing:border-box}</style><div id="wrapper"><slot></slot></div>';
3
- const findSelectedOption = elements => {
4
- return elements.find(el => el.checked) ?? null;
5
- };
1
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, getRect, NectaryElement, updateAttribute, updateBooleanAttribute } from '../utils';
2
+ const templateHTML = '<style>:host{display:block}#wrapper{display:flex;width:100%;border-bottom:1px solid var(--sinch-color-border-default)}</style><div id="wrapper"><slot></slot></div>';
6
3
  const template = document.createElement('template');
7
4
  template.innerHTML = templateHTML;
8
5
  defineCustomElement('sinch-tabs', class extends NectaryElement {
9
6
  #$slot;
7
+ #controller = null;
10
8
  constructor() {
11
9
  super();
12
10
  const shadowRoot = this.attachShadow();
@@ -14,17 +12,23 @@ defineCustomElement('sinch-tabs', class extends NectaryElement {
14
12
  this.#$slot = shadowRoot.querySelector('slot');
15
13
  }
16
14
  connectedCallback() {
15
+ this.#controller = new AbortController();
16
+ const {
17
+ signal
18
+ } = this.#controller;
17
19
  this.setAttribute('role', 'tablist');
18
- this.#$slot.addEventListener('keydown', this.#onOptionKeyDown);
19
- this.#$slot.addEventListener('option-change', this.#onOptionChange);
20
- this.#$slot.addEventListener('slotchange', this.#onSlotChange);
21
- this.addEventListener('-change', this.#onChangeReactHandler);
20
+ this.#$slot.addEventListener('option-change', this.#onOptionChange, {
21
+ signal
22
+ });
23
+ this.#$slot.addEventListener('slotchange', this.#onSlotChange, {
24
+ signal
25
+ });
26
+ this.addEventListener('-change', this.#onChangeReactHandler, {
27
+ signal
28
+ });
22
29
  }
23
30
  disconnectedCallback() {
24
- this.#$slot.removeEventListener('keydown', this.#onOptionKeyDown);
25
- this.#$slot.removeEventListener('option-change', this.#onOptionChange);
26
- this.#$slot.removeEventListener('slotchange', this.#onSlotChange);
27
- this.removeEventListener('-change', this.#onChangeReactHandler);
31
+ this.#controller.abort();
28
32
  }
29
33
  static get observedAttributes() {
30
34
  return ['value'];
@@ -47,32 +51,13 @@ defineCustomElement('sinch-tabs', class extends NectaryElement {
47
51
  }
48
52
  }
49
53
  }
50
- #onOptionKeyDown = e => {
51
- switch (e.code) {
52
- case 'ArrowUp':
53
- case 'ArrowLeft':
54
- {
55
- e.preventDefault();
56
- const $option = this.#getPrevOption();
57
- if ($option !== null) {
58
- $option.focus();
59
- this.#dispatchChangeEvent($option.value);
60
- }
61
- break;
62
- }
63
- case 'ArrowDown':
64
- case 'ArrowRight':
65
- {
66
- e.preventDefault();
67
- const $option = this.#getNextOption();
68
- if ($option !== null) {
69
- $option.focus();
70
- this.#dispatchChangeEvent($option.value);
71
- }
72
- break;
73
- }
54
+ nthOptionRect(index) {
55
+ const $el = this.#$slot.assignedElements()[index];
56
+ if ($el != null) {
57
+ return getRect($el);
74
58
  }
75
- };
59
+ return null;
60
+ }
76
61
  #onSlotChange = () => {
77
62
  this.#onValueChange(this.value);
78
63
  };
@@ -83,7 +68,7 @@ defineCustomElement('sinch-tabs', class extends NectaryElement {
83
68
  #onValueChange(value) {
84
69
  for (const $option of this.#$slot.assignedElements()) {
85
70
  const isChecked = !getBooleanAttribute($option, 'disabled') && value === getAttribute($option, 'value', '');
86
- updateBooleanAttribute($option, 'checked', isChecked);
71
+ updateBooleanAttribute($option, 'data-checked', isChecked);
87
72
  }
88
73
  }
89
74
  #dispatchChangeEvent(value) {
@@ -95,43 +80,6 @@ defineCustomElement('sinch-tabs', class extends NectaryElement {
95
80
  detail: value
96
81
  }));
97
82
  }
98
- #getFirstOption() {
99
- for (const $option of this.#$slot.assignedElements()) {
100
- if ($option.disabled !== true) {
101
- return $option;
102
- }
103
- }
104
- return null;
105
- }
106
- #getLastOption() {
107
- for (const $option of this.#$slot.assignedElements().reverse()) {
108
- if ($option.disabled !== true) {
109
- return $option;
110
- }
111
- }
112
- return null;
113
- }
114
- #getNextOption() {
115
- const $options = this.#getEnabledRadioElements();
116
- const $selectedOption = findSelectedOption($options);
117
- const currentIndex = $selectedOption !== null ? $options.indexOf($selectedOption) : -1;
118
- if (currentIndex < 0) {
119
- return this.#getFirstOption();
120
- }
121
- return $options[(currentIndex + 1) % $options.length];
122
- }
123
- #getPrevOption() {
124
- const $options = this.#getEnabledRadioElements();
125
- const $selectedOption = findSelectedOption($options);
126
- const currentIndex = $selectedOption !== null ? $options.indexOf($selectedOption) : -1;
127
- if (currentIndex < 0) {
128
- return this.#getLastOption();
129
- }
130
- return $options[(currentIndex - 1 + $options.length) % $options.length];
131
- }
132
- #getEnabledRadioElements() {
133
- return this.#$slot.assignedElements().filter(opt => opt.disabled !== true);
134
- }
135
83
  #onChangeReactHandler = e => {
136
84
  getReactEventHandler(this, 'on-change')?.(e);
137
85
  };
package/tabs/types.d.ts CHANGED
@@ -1,14 +1,21 @@
1
- import type { TSinchElementReact } from '../types';
1
+ import type { TRect, TSinchElementReact } from '../types';
2
2
  import type { SyntheticEvent } from 'react';
3
3
  export declare type TSinchTabsElement = HTMLElement & {
4
+ /** Value */
4
5
  value: string;
6
+ nthOptionRect(index: number): TRect | null;
7
+ /** Change value event */
5
8
  addEventListener(type: '-change', listener: (e: CustomEvent<string>) => void): void;
9
+ /** Value */
6
10
  setAttribute(name: 'value', value: string): void;
7
11
  };
8
12
  export declare type TSinchTabsReact = TSinchElementReact<TSinchTabsElement> & {
13
+ /** Value */
9
14
  value: string;
15
+ /** Label that is used for a11y */
10
16
  'aria-label': string;
11
- /** @deprecated */
17
+ /** @deprecated Change value handler */
12
18
  onChange?: (event: SyntheticEvent<TSinchTabsElement, CustomEvent<string>>) => void;
19
+ /** Change value event */
13
20
  'on-change'?: (e: CustomEvent<string>) => void;
14
21
  };
@@ -0,0 +1,11 @@
1
+ import type { TSinchTabsIconOptionElement, TSinchTabsIconOptionReact } from './types';
2
+ declare global {
3
+ namespace JSX {
4
+ interface IntrinsicElements {
5
+ 'sinch-tabs-icon-option': TSinchTabsIconOptionReact;
6
+ }
7
+ }
8
+ interface HTMLElementTagNameMap {
9
+ 'sinch-tabs-icon-option': TSinchTabsIconOptionElement;
10
+ }
11
+ }
@@ -0,0 +1,75 @@
1
+ import { defineCustomElement, getAttribute, getBooleanAttribute, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
2
+ const templateHTML = '<style>:host{display:block;outline:0}#button{all:initial;position:relative;display:flex;flex-direction:column;padding:12px 16px 0;box-sizing:border-box;cursor:pointer;border-top-left-radius:var(--sinch-shape-radius-l);border-top-right-radius:var(--sinch-shape-radius-l);height:40px;--sinch-color-icon:var(--sinch-color-stormy-300);--sinch-size-icon:16px}#button:hover{background-color:var(--sinch-color-tropical-100)}#button:focus-visible::before{content:"";position:absolute;inset:0;bottom:-3px;border:2px solid var(--sinch-color-border-focus);border-top-left-radius:var(--sinch-shape-radius-l);border-top-right-radius:var(--sinch-shape-radius-l);pointer-events:none}:host([data-checked]) #button{--sinch-color-icon:var(--sinch-color-tropical-500)}:host([data-checked]) #button::after{content:"";position:absolute;left:4px;right:4px;bottom:-1px;pointer-events:none;border-top:2px solid var(--sinch-color-tropical-500)}#button:disabled{cursor:unset;pointer-events:none;--sinch-color-icon:var(--sinch-color-border-default)}::slotted(*){display:block}</style><sinch-tooltip id="tooltip" inverted><button id="button"><slot name="icon"></slot></button></sinch-tooltip>';
3
+ const template = document.createElement('template');
4
+ template.innerHTML = templateHTML;
5
+ defineCustomElement('sinch-tabs-icon-option', class extends NectaryElement {
6
+ #$button;
7
+ #$tooltip;
8
+ constructor() {
9
+ super();
10
+ const shadowRoot = this.attachShadow();
11
+ shadowRoot.appendChild(template.content.cloneNode(true));
12
+ this.#$button = shadowRoot.querySelector('#button');
13
+ this.#$tooltip = shadowRoot.querySelector('#tooltip');
14
+ }
15
+ connectedCallback() {
16
+ this.setAttribute('role', 'tab');
17
+ this.#$button.addEventListener('click', this.#onClick);
18
+ }
19
+ disconnectedCallback() {
20
+ this.#$button.removeEventListener('click', this.#onClick);
21
+ }
22
+ static get observedAttributes() {
23
+ return ['data-checked', 'disabled', 'aria-label'];
24
+ }
25
+ set value(value) {
26
+ updateAttribute(this, 'value', value);
27
+ }
28
+ get value() {
29
+ return getAttribute(this, 'value', '');
30
+ }
31
+ set disabled(isDisabled) {
32
+ updateBooleanAttribute(this, 'disabled', isDisabled);
33
+ }
34
+ get disabled() {
35
+ return getBooleanAttribute(this, 'disabled');
36
+ }
37
+ attributeChangedCallback(name, oldVal, newVal) {
38
+ if (oldVal === newVal) {
39
+ return;
40
+ }
41
+ switch (name) {
42
+ case 'data-checked':
43
+ {
44
+ updateExplicitBooleanAttribute(this, 'aria-selected', isAttrTrue(newVal));
45
+ break;
46
+ }
47
+ case 'disabled':
48
+ {
49
+ this.#$button.disabled = isAttrTrue(newVal);
50
+ break;
51
+ }
52
+ case 'aria-label':
53
+ {
54
+ updateAttribute(this.#$tooltip, 'text', newVal);
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ get focusable() {
60
+ return true;
61
+ }
62
+ focus() {
63
+ this.#$button.focus();
64
+ }
65
+ blur() {
66
+ this.#$button.blur();
67
+ }
68
+ #onClick = e => {
69
+ e.stopPropagation();
70
+ this.dispatchEvent(new CustomEvent('option-change', {
71
+ bubbles: true,
72
+ detail: this.value
73
+ }));
74
+ };
75
+ });
@@ -0,0 +1,19 @@
1
+ import type { TSinchElementReact } from '../types';
2
+ export declare type TSinchTabsIconOptionElement = HTMLElement & {
3
+ /** Value */
4
+ value: string;
5
+ /** Disabled */
6
+ disabled: boolean;
7
+ /** Value */
8
+ setAttribute(name: 'value', value: string): void;
9
+ /** Disabled */
10
+ setAttribute(name: 'disabled', value: ''): void;
11
+ };
12
+ export declare type TSinchTabsIconOptionReact = TSinchElementReact<TSinchTabsIconOptionElement> & {
13
+ /** Value */
14
+ value: string;
15
+ /** Label that is used for a11y */
16
+ 'aria-label': string;
17
+ /** Disabled */
18
+ disabled?: boolean;
19
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
+ import '../text';
1
2
  import type { TSinchTabsOptionElement, TSinchTabsOptionReact } from './types';
2
3
  declare global {
3
4
  namespace JSX {
@@ -1,16 +1,17 @@
1
+ import '../text';
1
2
  import { defineCustomElement, getAttribute, getBooleanAttribute, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:block;outline:0}:host()>*{pointer-events:none}#wrapper{display:flex;flex-direction:row;align-items:center;gap:12px;position:relative;width:100%;height:42px;padding:8px 20px;box-sizing:border-box;box-shadow:0 1px 0 0 var(--sinch-color-stormy-100);color:var(--sinch-color-stormy-500);background-color:var(--sinch-color-snow-100);--sinch-color-icon:var(--sinch-color-stormy-500);--sinch-size-icon:16px}#content{font:var(--sinch-font-title-s);flex-shrink:1;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}:host([checked]:not([checked=false])) #wrapper{box-shadow:0 2px 0 0 var(--sinch-color-stormy-500)}#button{all:initial;position:absolute;left:0;top:0;box-sizing:border-box;width:100%;height:100%;cursor:pointer}#button:disabled{cursor:unset}:host([disabled]:not([disabled=false])) #wrapper{color:var(--sinch-color-stormy-100);--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><slot name="icon"></slot><label for="input" id="content"></label><button id="button" type="radio"/></div>';
3
+ const templateHTML = '<style>:host{display:block}#button{all:initial;position:relative;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;padding:12px 16px;box-sizing:border-box;cursor:pointer;border-top-left-radius:var(--sinch-shape-radius-l);border-top-right-radius:var(--sinch-shape-radius-l);height:40px;color:var(--sinch-color-stormy-300);--sinch-color-icon:var(--sinch-color-stormy-300);--sinch-size-icon:16px}#button:hover{background-color:var(--sinch-color-tropical-100)}#button:focus-visible::before{content:"";position:absolute;inset:0;bottom:-3px;border:2px solid var(--sinch-color-border-focus);border-top-left-radius:var(--sinch-shape-radius-l);border-top-right-radius:var(--sinch-shape-radius-l);pointer-events:none}:host([data-checked]) #button{color:var(--sinch-color-tropical-500);--sinch-color-icon:var(--sinch-color-tropical-500)}:host([data-checked]) #button::after{content:"";position:absolute;left:4px;right:4px;bottom:-1px;pointer-events:none;border-top:2px solid var(--sinch-color-tropical-500)}#button:disabled{cursor:unset;pointer-events:none;color:var(--sinch-color-border-default);--sinch-color-icon:var(--sinch-color-border-default)}#text{flex-shrink:1;flex-basis:auto;min-width:0}::slotted(*){display:block}</style><button id="button"><slot name="icon"></slot><sinch-text id="text" type="m" ellipsis></sinch-text></button>';
3
4
  const template = document.createElement('template');
4
5
  template.innerHTML = templateHTML;
5
6
  defineCustomElement('sinch-tabs-option', class extends NectaryElement {
6
7
  #$button;
7
- #$label;
8
+ #$text;
8
9
  constructor() {
9
10
  super();
10
11
  const shadowRoot = this.attachShadow();
11
12
  shadowRoot.appendChild(template.content.cloneNode(true));
12
13
  this.#$button = shadowRoot.querySelector('#button');
13
- this.#$label = shadowRoot.querySelector('#content');
14
+ this.#$text = shadowRoot.querySelector('#text');
14
15
  }
15
16
  connectedCallback() {
16
17
  this.setAttribute('role', 'tab');
@@ -20,13 +21,7 @@ defineCustomElement('sinch-tabs-option', class extends NectaryElement {
20
21
  this.#$button.removeEventListener('click', this.#onClick);
21
22
  }
22
23
  static get observedAttributes() {
23
- return ['checked', 'disabled', 'text', 'value'];
24
- }
25
- set checked(isChecked) {
26
- updateBooleanAttribute(this, 'checked', isChecked);
27
- }
28
- get checked() {
29
- return getBooleanAttribute(this, 'checked');
24
+ return ['data-checked', 'disabled', 'text'];
30
25
  }
31
26
  set value(value) {
32
27
  updateAttribute(this, 'value', value);
@@ -50,19 +45,17 @@ defineCustomElement('sinch-tabs-option', class extends NectaryElement {
50
45
  switch (name) {
51
46
  case 'text':
52
47
  {
53
- this.#$label.textContent = newVal;
48
+ this.#$text.textContent = newVal;
54
49
  break;
55
50
  }
56
- case 'checked':
51
+ case 'data-checked':
57
52
  {
58
53
  updateExplicitBooleanAttribute(this, 'aria-selected', isAttrTrue(newVal));
59
54
  break;
60
55
  }
61
56
  case 'disabled':
62
57
  {
63
- const isDisabled = isAttrTrue(newVal);
64
- this.#$button.disabled = isDisabled;
65
- updateBooleanAttribute(this, 'disabled', isDisabled);
58
+ this.#$button.disabled = isAttrTrue(newVal);
66
59
  break;
67
60
  }
68
61
  }
@@ -1,17 +1,25 @@
1
1
  import type { TSinchElementReact } from '../types';
2
2
  export declare type TSinchTabsOptionElement = HTMLElement & {
3
+ /** Value */
3
4
  value: string;
4
- disabled: boolean;
5
- checked: boolean;
5
+ /** Text */
6
6
  text: string;
7
+ /** Disabled */
8
+ disabled: boolean;
9
+ /** Value */
7
10
  setAttribute(name: 'value', value: string): void;
8
- setAttribute(name: 'disabled', value: ''): void;
9
- setAttribute(name: 'checked', value: ''): void;
11
+ /** Text */
10
12
  setAttribute(name: 'text', value: string): void;
13
+ /** Disabled */
14
+ setAttribute(name: 'disabled', value: ''): void;
11
15
  };
12
16
  export declare type TSinchTabsOptionReact = TSinchElementReact<TSinchTabsOptionElement> & {
17
+ /** Value */
13
18
  value: string;
14
- disabled?: boolean;
19
+ /** Text */
15
20
  text: string;
21
+ /** Label that is used for a11y */
16
22
  'aria-label': string;
23
+ /** Disabled */
24
+ disabled?: boolean;
17
25
  };
@@ -0,0 +1,6 @@
1
+ :root,
2
+ :host {
3
+ --sinch-emoji-src-url: "https://twemoji.maxcdn.com/v/latest/svg/%s.svg";
4
+
5
+ /* --sinch-emoji-src-url: "https://cdnjs.cloudflare.com/ajax/libs/twemoji/14.0.2/svg/%s.svg"; */
6
+ }
package/theme/fonts.css CHANGED
@@ -74,3 +74,12 @@
74
74
  url("https://d2vu40klajma73.cloudfront.net/DM-Mono-400.woff2") format("woff2"),
75
75
  url("https://d2vu40klajma73.cloudfront.net/DM-Mono-400.woff") format("woff");
76
76
  }
77
+
78
+ @font-face {
79
+ font-family: "Material Icons Outlined";
80
+ font-style: normal;
81
+ font-weight: 400;
82
+ src:
83
+ url("https://fonts.gstatic.com/s/materialsymbolsoutlined/v63/kJF1BvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oDMzByHX9rA6RzazHD_dY43zj-jCxv3fzvRNU22ZXGJpEpjC_1v-p_4MrImHCIJIZrDCvHOejbd5zrDAt.woff2") format("woff2"),
84
+ url("https://fonts.gstatic.com/s/materialsymbolsoutlined/v63/kJF1BvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oDMzByHX9rA6RzazHD_dY43zj-jCxv3fzvRNU22ZXGJpEpjC_1v-p_4MrImHCIJIZrDCvHOelbd5zrDAt.woff") format("woff");
85
+ }
package/theme/fonts.json CHANGED
@@ -75,5 +75,14 @@
75
75
  "woff2": "https://d2vu40klajma73.cloudfront.net/DM-Mono-400.woff2",
76
76
  "woff": "https://d2vu40klajma73.cloudfront.net/DM-Mono-400.woff"
77
77
  }
78
+ },
79
+ {
80
+ "family": "Material Icons Outlined",
81
+ "weight": 400,
82
+ "style": "normal",
83
+ "src": {
84
+ "woff2": "https://fonts.gstatic.com/s/materialsymbolsoutlined/v63/kJF1BvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oDMzByHX9rA6RzazHD_dY43zj-jCxv3fzvRNU22ZXGJpEpjC_1v-p_4MrImHCIJIZrDCvHOejbd5zrDAt.woff2",
85
+ "woff": "https://fonts.gstatic.com/s/materialsymbolsoutlined/v63/kJF1BvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oDMzByHX9rA6RzazHD_dY43zj-jCxv3fzvRNU22ZXGJpEpjC_1v-p_4MrImHCIJIZrDCvHOelbd5zrDAt.woff"
86
+ }
78
87
  }
79
88
  ]
package/theme/icon.css ADDED
@@ -0,0 +1,7 @@
1
+ :root,
2
+ :host {
3
+ --sinch-icon-font-family: "Material Icons Outlined";
4
+ --sinch-icon-font-variation-settings: "FILL" 1;
5
+ --sinch-icon-font-feature-settings: "liga";
6
+ --sinch-icon-font-weight: 400;
7
+ }
package/theme.css CHANGED
@@ -10,6 +10,8 @@
10
10
  @import "theme/chip.css";
11
11
  @import "theme/color-swatch.css";
12
12
  @import "theme/tag.css";
13
+ @import "theme/emoji.css";
14
+ @import "theme/icon.css";
13
15
 
14
16
  :root,
15
17
  :host {