@nectary/components 0.44.0 → 0.45.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 (111) hide show
  1. package/accordion-item/index.js +1 -1
  2. package/action-menu/index.js +3 -11
  3. package/alert/index.js +3 -1
  4. package/avatar/index.js +12 -13
  5. package/avatar/types.d.ts +4 -4
  6. package/avatar/utils.d.ts +1 -4
  7. package/avatar/utils.js +4 -7
  8. package/badge/index.js +9 -7
  9. package/badge/types.d.ts +4 -4
  10. package/badge/utils.d.ts +1 -4
  11. package/badge/utils.js +0 -6
  12. package/button/index.js +88 -23
  13. package/button/types.d.ts +12 -11
  14. package/button/utils.d.ts +4 -1
  15. package/button/utils.js +9 -1
  16. package/card-container/index.js +1 -1
  17. package/chat-block/index.js +1 -1
  18. package/chat-bubble/index.js +1 -1
  19. package/checkbox/index.js +4 -2
  20. package/chip/index.js +7 -6
  21. package/color-menu/index.js +9 -18
  22. package/color-swatch/index.js +5 -7
  23. package/date-picker/index.js +1 -1
  24. package/dialog/index.js +4 -7
  25. package/emoji/index.js +2 -6
  26. package/emoji-picker/data.json +1 -0
  27. package/emoji-picker/index.js +6 -14
  28. package/field/index.js +1 -1
  29. package/file-status/index.js +4 -2
  30. package/flag/index.d.ts +11 -0
  31. package/flag/index.js +43 -0
  32. package/flag/types.d.ts +11 -0
  33. package/flag/types.js +1 -0
  34. package/flag/utils.d.ts +1 -0
  35. package/flag/utils.js +19 -0
  36. package/icon/index.js +1 -1
  37. package/icon-button/index.js +77 -9
  38. package/icon-button/types.d.ts +14 -6
  39. package/icon-button/utils.d.ts +5 -0
  40. package/icon-button/utils.js +9 -0
  41. package/icons/create-icon-class.js +1 -1
  42. package/inline-alert/index.js +3 -1
  43. package/input/index.d.ts +3 -0
  44. package/input/index.js +148 -71
  45. package/input/types.d.ts +7 -0
  46. package/link/index.js +1 -1
  47. package/package.json +4 -6
  48. package/pagination/index.js +1 -1
  49. package/pop/index.js +15 -37
  50. package/popover/index.js +7 -9
  51. package/radio-option/index.js +1 -1
  52. package/segment/index.js +10 -6
  53. package/segment/types.d.ts +4 -4
  54. package/segment/utils.d.ts +3 -5
  55. package/segment/utils.js +14 -4
  56. package/segment-collapse/index.js +1 -1
  57. package/segmented-control-option/index.js +1 -1
  58. package/segmented-icon-control-option/index.js +1 -1
  59. package/select-button/index.js +93 -28
  60. package/select-button/types.d.ts +8 -1
  61. package/select-menu/index.d.ts +5 -0
  62. package/select-menu/index.js +46 -17
  63. package/select-menu-option/index.d.ts +1 -0
  64. package/select-menu-option/index.js +3 -0
  65. package/select-menu-option/types.d.ts +1 -0
  66. package/spinner/index.js +52 -7
  67. package/spinner/types.d.ts +4 -5
  68. package/stop-events/index.js +9 -5
  69. package/table/index.js +1 -1
  70. package/tag/index.js +2 -6
  71. package/text/index.js +3 -1
  72. package/textarea/index.js +1 -1
  73. package/theme/button.css +146 -0
  74. package/{theme.css → theme/contextual.css} +3 -25
  75. package/theme/elevation.css +1 -1
  76. package/theme/flag.css +4 -0
  77. package/theme/fonts.css +0 -33
  78. package/theme/fonts.json +0 -33
  79. package/theme/icon-button.css +68 -0
  80. package/theme/index.css +4 -0
  81. package/theme/index.d.ts +21 -0
  82. package/theme/index.js +21 -0
  83. package/theme/input.css +7 -0
  84. package/theme/select-button.css +7 -0
  85. package/theme/shapes.css +4 -3
  86. package/theme/size.css +9 -0
  87. package/theme/spinner.css +7 -0
  88. package/theme/typography.css +7 -7
  89. package/tile-control-option/index.js +1 -1
  90. package/time-picker/index.js +1 -1
  91. package/title/index.js +7 -3
  92. package/toast/index.js +3 -1
  93. package/toggle/index.js +1 -1
  94. package/tooltip/index.js +8 -6
  95. package/utils/context.d.ts +14 -9
  96. package/utils/context.js +60 -26
  97. package/utils/countries.d.ts +5 -0
  98. package/utils/countries.js +2 -0
  99. package/utils/countries.json +998 -0
  100. package/utils/debounce.d.ts +4 -0
  101. package/utils/debounce.js +21 -0
  102. package/utils/element.d.ts +4 -0
  103. package/utils/element.js +10 -0
  104. package/utils/index.d.ts +1 -0
  105. package/utils/index.js +1 -0
  106. package/utils/size.d.ts +10 -0
  107. package/utils/size.js +19 -0
  108. package/utils/throttle.d.ts +2 -2
  109. package/utils/throttle.js +4 -9
  110. package/spinner/utils.d.ts +0 -2
  111. package/spinner/utils.js +0 -1
@@ -15,9 +15,9 @@ import '../icons/emoji-objects';
15
15
  import '../icons/emoji-transportation';
16
16
  import '../icons/emoji-events';
17
17
  import '../icons/emoji-symbols';
18
- import { defineCustomElement, dispatchContextConnectEvent, dispatchContextDisconnectEvent, getAttribute, getBooleanAttribute, NectaryElement, updateAttribute, updateBooleanAttribute, getReactEventHandler, getRect } from '../utils';
18
+ import { defineCustomElement, getAttribute, getBooleanAttribute, NectaryElement, updateAttribute, updateBooleanAttribute, getReactEventHandler, getRect, subscribeContext } from '../utils';
19
19
  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" 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" 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>';
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
21
  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
22
  const groupLabels = ['Emotions', 'People', 'Animals and nature', 'Food and drinks', 'Travel and places', 'Sports and activities', 'Objects', 'Symbols and flags'];
23
23
  const data = dataJson;
@@ -61,9 +61,6 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
61
61
  this.addEventListener('keydown', this.#onListboxKeyDown, {
62
62
  signal
63
63
  });
64
- this.addEventListener('-keydown', this.#onContexKeydown, {
65
- signal
66
- });
67
64
  this.#$skinButton.addEventListener('-click', this.#onSkinButtonClick, {
68
65
  signal
69
66
  });
@@ -79,17 +76,12 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
79
76
  this.addEventListener('-change', this.#onChangeReactHandler, {
80
77
  signal
81
78
  });
82
- this.addEventListener('-visibility', this.#onContextVisibility, {
83
- signal
84
- });
85
- dispatchContextConnectEvent(this, 'keydown');
86
- dispatchContextConnectEvent(this, 'visibility');
79
+ subscribeContext(this, 'keydown', this.#onContextKeyDown, signal);
80
+ subscribeContext(this, 'visibility', this.#onContextVisibility, signal);
87
81
  this.#updateTabs();
88
82
  this.#updateEmojis();
89
83
  }
90
84
  disconnectedCallback() {
91
- dispatchContextDisconnectEvent(this, 'keydown');
92
- dispatchContextDisconnectEvent(this, 'visibility');
93
85
  this.#controller.abort();
94
86
  }
95
87
  get skinToneButtonRect() {
@@ -117,7 +109,7 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
117
109
  detail: value
118
110
  }));
119
111
  };
120
- #onContexKeydown = e => {
112
+ #onContextKeyDown = e => {
121
113
  this.#handleKeydown(e.detail);
122
114
  };
123
115
  #onContextVisibility = e => {
@@ -311,7 +303,7 @@ defineCustomElement('sinch-emoji-picker', class extends NectaryElement {
311
303
  el.setAttribute('char', emoji.emoji);
312
304
  el.setAttribute('label', emoji.label);
313
305
  btn.setAttribute('aria-label', emoji.label);
314
- btn.setAttribute('small', '');
306
+ btn.setAttribute('size', 's');
315
307
  btn.setAttribute('data-value', emoji.emoji);
316
308
  btn.appendChild(el);
317
309
  return btn;
package/field/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineCustomElement, getAttribute, getBooleanAttribute, getFirstSlotElement, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#wrapper{display:flex;flex-direction:column;width:100%}#bottom,#top{display:flex;align-items:baseline}#top{height:24px;margin-bottom:2px;--sinch-color-icon:var(--sinch-color-stormy-500)}#additional,#invalid,#label,#optional{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#label{font:var(--sinch-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-small-text);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-muted);line-height:20px;margin-top:2px}#additional:empty{display:none}#invalid{font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-invalid);line-height:20px;margin-top:2px}#invalid:empty{display:none}#tooltip{align-self:center;margin:0 8px;display:flex}#tooltip.empty{display:none}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])) #top{--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="input"></label><div id="tooltip"><slot name="tooltip"></slot></div><span id="optional"></span></div><slot name="input"></slot><div id="bottom"><div id="invalid"></div><div id="additional"></div></div></div>';
2
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#wrapper{display:flex;flex-direction:column;width:100%}#bottom,#top{display:flex;align-items:baseline}#top{height:24px;margin-bottom:2px;--sinch-color-icon:var(--sinch-color-stormy-500)}#additional,#invalid,#label,#optional{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#label{font:var(--sinch-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-text-s);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-text-xs);color:var(--sinch-color-text-muted);line-height:20px;margin-top:2px}#additional:empty{display:none}#invalid{font:var(--sinch-font-text-xs);color:var(--sinch-color-text-invalid);line-height:20px;margin-top:2px}#invalid:empty{display:none}#tooltip{align-self:center;margin:0 8px;display:flex}#tooltip.empty{display:none}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])) #top{--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="input"></label><div id="tooltip"><slot name="tooltip"></slot></div><span id="optional"></span></div><slot name="input"></slot><div id="bottom"><div id="invalid"></div><div id="additional"></div></div></div>';
3
3
  const template = document.createElement('template');
4
4
  template.innerHTML = templateHTML;
5
5
  defineCustomElement('sinch-field', class extends NectaryElement {
@@ -5,7 +5,7 @@ import '../icons/pending-actions';
5
5
  import '../icons/text-snippet';
6
6
  import '../text';
7
7
  import { defineCustomElement, getAttribute, getLiteralAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, updateBooleanAttribute } from '../utils';
8
- const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:row;align-items:flex-start;padding:12px 16px;box-sizing:border-box;min-height:48px;min-width:148px;background-color:var(--sinch-color-bg-primary-contrast);border-radius:var(--sinch-shape-radius-m)}:host([type=error]) #wrapper{background-color:var(--sinch-color-error-200)}#content-wrapper{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0;margin-left:16px;color:var(--sinch-color-stormy-500)}#icon-error,#icon-loading,#icon-pending,#icon-progress,#icon-success{display:none}#action{height:24px}#action ::slotted(*){position:relative;top:50%;transform:translateY(-50%)}:host([type=error]) #icon-error,:host([type=loading]) #icon-loading,:host([type=pending]) #icon-pending,:host([type=progress]) #icon-progress,:host([type=success]) #icon-success{display:block;--sinch-color-icon:var(--sinch-color-stormy-500)}:host([type=success]) #icon-success{--sinch-color-icon:var(--sinch-color-success-500)}:host([type=error]) #icon-error{--sinch-color-icon:var(--sinch-color-error-500)}</style><div id="wrapper"><sinch-spinner id="icon-loading" type="medium"></sinch-spinner><sinch-icon-check-circle id="icon-success"></sinch-icon-check-circle><sinch-icon-report id="icon-error"></sinch-icon-report><sinch-icon-pending-actions id="icon-pending"></sinch-icon-pending-actions><sinch-icon-text-snippet id="icon-progress"></sinch-icon-text-snippet><div id="content-wrapper"><sinch-text id="filename" type="m" ellipsis></sinch-text><slot name="content"></slot></div><div id="action"><slot name="action"></slot></div></div>';
8
+ const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:row;align-items:flex-start;padding:12px 16px;box-sizing:border-box;min-height:48px;min-width:148px;background-color:var(--sinch-color-bg-primary-contrast);border-radius:var(--sinch-shape-radius-m)}:host([type=error]) #wrapper{background-color:var(--sinch-color-error-200)}#content-wrapper{display:flex;flex-direction:column;gap:8px;flex:1;min-width:0;min-height:24px;margin-left:16px;color:var(--sinch-color-stormy-500)}#icon-error,#icon-loading,#icon-pending,#icon-progress,#icon-success{display:none;--sinch-color-icon:var(--sinch-color-stormy-500)}#action{display:flex;gap:4px;height:32px;margin-top:-4px;margin-bottom:-4px}:host([type=error]) #icon-error,:host([type=loading]) #icon-loading,:host([type=pending]) #icon-pending,:host([type=progress]) #icon-progress,:host([type=success]) #icon-success{display:block}:host([type=success]) #icon-success{--sinch-color-icon:var(--sinch-color-success-500)}:host([type=error]) #icon-error{--sinch-color-icon:var(--sinch-color-error-500)}</style><div id="wrapper"><sinch-spinner id="icon-loading" size="m"></sinch-spinner><sinch-icon-check-circle id="icon-success"></sinch-icon-check-circle><sinch-icon-report id="icon-error"></sinch-icon-report><sinch-icon-pending-actions id="icon-pending"></sinch-icon-pending-actions><sinch-icon-text-snippet id="icon-progress"></sinch-icon-text-snippet><div id="content-wrapper"><sinch-text id="filename" type="m" ellipsis></sinch-text><slot name="content"></slot></div><div id="action"><slot name="action"></slot></div></div>';
9
9
  import { assertType, typeValues } from './utils';
10
10
  const template = document.createElement('template');
11
11
  template.innerHTML = templateHTML;
@@ -48,7 +48,9 @@ defineCustomElement('sinch-file-status', class extends NectaryElement {
48
48
  switch (name) {
49
49
  case 'type':
50
50
  {
51
- assertType(newVal);
51
+ if ('production' !== 'production') {
52
+ assertType(newVal);
53
+ }
52
54
  break;
53
55
  }
54
56
  case 'filename':
@@ -0,0 +1,11 @@
1
+ import type { TSinchFlagElement, TSinchFlagReact } from './types';
2
+ declare global {
3
+ namespace JSX {
4
+ interface IntrinsicElements {
5
+ 'sinch-flag': TSinchFlagReact;
6
+ }
7
+ }
8
+ interface HTMLElementTagNameMap {
9
+ 'sinch-flag': TSinchFlagElement;
10
+ }
11
+ }
package/flag/index.js ADDED
@@ -0,0 +1,43 @@
1
+ import { defineCustomElement, getAttribute, NectaryElement, updateAttribute } from '../utils';
2
+ const templateHTML = '<style>:host{display:contents}#image{pointer-events:none;width:var(--sinch-size-icon,48px);height:var(--sinch-size-icon,48px);object-fit:contain}</style><img id="image" src="" alt="" loading="lazy"/>';
3
+ import { getFlagUrl } from './utils';
4
+ const template = document.createElement('template');
5
+ template.innerHTML = templateHTML;
6
+ defineCustomElement('sinch-flag', class extends NectaryElement {
7
+ #$img;
8
+ constructor() {
9
+ super();
10
+ const shadowRoot = this.attachShadow();
11
+ shadowRoot.appendChild(template.content.cloneNode(true));
12
+ this.#$img = shadowRoot.querySelector('#image');
13
+ }
14
+ connectedCallback() {
15
+ super.connectedCallback();
16
+ this.#updateCode();
17
+ }
18
+ static get observedAttributes() {
19
+ return ['code'];
20
+ }
21
+ attributeChangedCallback(name, _, newVal) {
22
+ switch (name) {
23
+ case 'code':
24
+ {
25
+ this.#$img.alt = newVal ?? '';
26
+ this.#updateCode();
27
+ break;
28
+ }
29
+ }
30
+ }
31
+ set code(value) {
32
+ updateAttribute(this, 'code', value);
33
+ }
34
+ get code() {
35
+ return getAttribute(this, 'code', '');
36
+ }
37
+ #updateCode() {
38
+ if (!this.isConnected) {
39
+ return;
40
+ }
41
+ this.#$img.src = getFlagUrl(this, this.code);
42
+ }
43
+ });
@@ -0,0 +1,11 @@
1
+ import type { TSinchElementReact } from '../types';
2
+ export declare type TSinchFlagElement = HTMLElement & {
3
+ /** Flag country code */
4
+ code: string;
5
+ /** Flag country code */
6
+ setAttribute(code: 'name', value: string): void;
7
+ };
8
+ export declare type TSinchFlagReact = TSinchElementReact<TSinchFlagElement> & {
9
+ /** Flag country code */
10
+ code: string;
11
+ };
package/flag/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const getFlagUrl: (root: Element, code: string | null) => string;
package/flag/utils.js ADDED
@@ -0,0 +1,19 @@
1
+ import { getCssVar } from '../utils';
2
+
3
+ let flagUrl = null;
4
+ export const getFlagUrl = (root, code) => {
5
+ if (code === null || code.length === 0) {
6
+ return '';
7
+ }
8
+
9
+ if (flagUrl === null) {
10
+ flagUrl = getCssVar(root, '--sinch-flag-src-url');
11
+ if (flagUrl !== null) {
12
+ flagUrl = flagUrl.replaceAll('"', '').trim();
13
+ }
14
+ }
15
+ if (flagUrl === null) {
16
+ return '';
17
+ }
18
+ return flagUrl.replace('%s', code);
19
+ };
package/icon/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineCustomElement, getAttribute, NectaryElement, updateAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:contents}#icon{display:inline-block;font-family:var(--sinch-icon-font-family);font-weight:var(--sinch-icon-font-weight);font-size:var(--sinch-size-icon, 24px);font-feature-settings:var(--sinch-icon-font-feature-settings);font-variation-settings:"FILL" 1;-webkit-font-smoothing:antialiased;line-height:1;white-space:nowrap;width:var(--sinch-size-icon,24px);height:var(--sinch-size-icon,24px);color:var(--sinch-color-icon,var(--sinch-color-text-default));user-select:none}</style><span id="icon" role="img"></span>';
2
+ const templateHTML = '<style>:host{display:contents}#icon{display:inline-block;font-family:var(--sinch-icon-font-family);font-weight:var(--sinch-icon-font-weight);font-size:var(--sinch-size-icon, 24px);font-feature-settings:var(--sinch-icon-font-feature-settings);font-variation-settings:"FILL" 1;-webkit-font-smoothing:antialiased;line-height:1;white-space:nowrap;width:var(--sinch-size-icon,24px);height:var(--sinch-size-icon,24px);color:var(--sinch-color-icon,var(--sinch-color-stormy-500));user-select:none}</style><span id="icon" role="img"></span>';
3
3
  const template = document.createElement('template');
4
4
  template.innerHTML = templateHTML;
5
5
  defineCustomElement('sinch-icon', class extends NectaryElement {
@@ -1,20 +1,27 @@
1
1
  import '../tooltip';
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" inverted><button id="button"><slot name="icon"></slot></button></sinch-tooltip>';
2
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, subscribeContext, updateAttribute, updateBooleanAttribute, updateLiteralAttribute, Context, getAttribute } from '../utils';
3
+ import { assertSizeEx, DEFAULT_SIZE, sizeExValues } from '../utils/size';
4
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;--sinch-size:var(--sinch-size-m);--sinch-size-icon:var(--sinch-icon-button-icon-size-m);--sinch-shape-radius:var(--sinch-shape-radius-m)}#button{all:initial;position:relative;display:flex;align-items:center;justify-content:center;width:var(--sinch-size);height:var(--sinch-size);cursor:pointer;contain:size;--sinch-color-icon:var(--sinch-icon-button-color-icon)}:host([data-size="l"]){--sinch-size:var(--sinch-size-l);--sinch-size-icon:var(--sinch-icon-button-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-icon-button-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-icon-button-icon-size-s);--sinch-shape-radius:var(--sinch-shape-radius-s)}:host([data-size=xs]){--sinch-size:var(--sinch-size-xs);--sinch-size-icon:var(--sinch-icon-button-icon-size-xs);--sinch-shape-radius:var(--sinch-shape-radius-xs)}:host([type=primary]){--sinch-icon-button-color-background:var(--sinch-icon-button-primary-color-background);--sinch-icon-button-color-background-hover:var(--sinch-icon-button-primary-color-background-hover);--sinch-icon-button-color-background-active:var(--sinch-icon-button-primary-color-background-active);--sinch-icon-button-color-background-disabled:var(--sinch-icon-button-primary-color-background-disabled);--sinch-icon-button-color-border:var(--sinch-icon-button-primary-color-border);--sinch-icon-button-color-border-hover:var(--sinch-icon-button-primary-color-border-hover);--sinch-icon-button-color-border-active:var(--sinch-icon-button-primary-color-border-active);--sinch-icon-button-color-border-disabled:var(--sinch-icon-button-primary-color-border-disabled);--sinch-icon-button-color-icon:var(--sinch-icon-button-primary-color-icon);--sinch-icon-button-color-icon-hover:var(--sinch-icon-button-primary-color-icon-hover);--sinch-icon-button-color-icon-active:var(--sinch-icon-button-primary-color-icon-active);--sinch-icon-button-color-icon-disabled:var(--sinch-icon-button-primary-color-icon-disabled);--sinch-icon-button-mix-blend-mode:var(--sinch-icon-button-primary-mix-blend-mode);--sinch-elevation-level:var(--sinch-elevation-level-1)}:host([type=secondary]){--sinch-icon-button-color-background:var(--sinch-icon-button-secondary-color-background);--sinch-icon-button-color-background-hover:var(--sinch-icon-button-secondary-color-background-hover);--sinch-icon-button-color-background-active:var(--sinch-icon-button-secondary-color-background-active);--sinch-icon-button-color-background-disabled:var(--sinch-icon-button-secondary-color-background-disabled);--sinch-icon-button-color-border:var(--sinch-icon-button-secondary-color-border);--sinch-icon-button-color-border-hover:var(--sinch-icon-button-secondary-color-border-hover);--sinch-icon-button-color-border-active:var(--sinch-icon-button-secondary-color-border-active);--sinch-icon-button-color-border-disabled:var(--sinch-icon-button-secondary-color-border-disabled);--sinch-icon-button-color-icon:var(--sinch-icon-button-secondary-color-icon);--sinch-icon-button-color-icon-hover:var(--sinch-icon-button-secondary-color-icon-hover);--sinch-icon-button-color-icon-active:var(--sinch-icon-button-secondary-color-icon-active);--sinch-icon-button-color-icon-disabled:var(--sinch-icon-button-secondary-color-icon-disabled);--sinch-icon-button-mix-blend-mode:var(--sinch-icon-button-secondary-mix-blend-mode);--sinch-elevation-level:var(--sinch-elevation-level-1)}:host([type=tertiary]){--sinch-icon-button-color-background:var(--sinch-icon-button-tertiary-color-background);--sinch-icon-button-color-background-hover:var(--sinch-icon-button-tertiary-color-background-hover);--sinch-icon-button-color-background-active:var(--sinch-icon-button-tertiary-color-background-active);--sinch-icon-button-color-background-disabled:var(--sinch-icon-button-tertiary-color-background-disabled);--sinch-icon-button-color-border:var(--sinch-icon-button-tertiary-color-border);--sinch-icon-button-color-border-hover:var(--sinch-icon-button-tertiary-color-border-hover);--sinch-icon-button-color-border-active:var(--sinch-icon-button-tertiary-color-border-active);--sinch-icon-button-color-border-disabled:var(--sinch-icon-button-tertiary-color-border-disabled);--sinch-icon-button-color-icon:var(--sinch-icon-button-tertiary-color-icon);--sinch-icon-button-color-icon-hover:var(--sinch-icon-button-tertiary-color-icon-hover);--sinch-icon-button-color-icon-active:var(--sinch-icon-button-tertiary-color-icon-active);--sinch-icon-button-color-icon-disabled:var(--sinch-icon-button-tertiary-color-icon-disabled);--sinch-icon-button-mix-blend-mode:var(--sinch-icon-button-tertiary-mix-blend-mode);--sinch-elevation-level:var(--sinch-elevation-level-0)}#button::before{content:"";position:absolute;inset:0;border-radius:var(--sinch-shape-radius);border:1px solid var(--sinch-icon-button-color-border);background-color:var(--sinch-icon-button-color-background);box-shadow:var(--sinch-elevation-level);pointer-events:none;mix-blend-mode:var(--sinch-icon-button-mix-blend-mode)}#button:focus-visible::after{content:"";position:absolute;inset:-3px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-shape-radius) + 3px);pointer-events:none}#button:disabled{cursor:initial;--sinch-color-icon:var(--sinch-icon-button-color-icon-disabled)}#button:disabled::before{background-color:var(--sinch-icon-button-color-background-disabled);border-color:var(--sinch-icon-button-color-border-disabled);box-shadow:var(--sinch-elevation-level-0)}#button:enabled:hover{--sinch-color-icon:var(--sinch-icon-button-color-icon-hover)}#button:enabled:hover::before{background-color:var(--sinch-icon-button-color-background-hover);border-color:var(--sinch-icon-button-color-border-hover)}#button:enabled:active{--sinch-color-icon:var(--sinch-icon-button-color-icon-active)}#button:enabled:active::before{background-color:var(--sinch-icon-button-color-background-active);border-color:var(--sinch-icon-button-color-border-active);box-shadow:var(--sinch-elevation-level-0)}#content{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:100%}#button *{pointer-events:none}</style><sinch-tooltip id="tooltip" inverted><button id="button"><div id="content"><slot name="icon"></slot></div></button></sinch-tooltip>';
5
+ import { assertType, typeValues } from './utils';
4
6
  const template = document.createElement('template');
5
7
  template.innerHTML = templateHTML;
6
8
  defineCustomElement('sinch-icon-button', class extends NectaryElement {
7
9
  #$button;
8
10
  #$tooltip;
9
11
  #controller = null;
12
+ #sizeContext;
10
13
  constructor() {
11
14
  super();
12
- const shadowRoot = this.attachShadow();
15
+ const shadowRoot = this.attachShadow({
16
+ delegatesFocus: true
17
+ });
13
18
  shadowRoot.appendChild(template.content.cloneNode(true));
14
19
  this.#$button = shadowRoot.querySelector('#button');
15
20
  this.#$tooltip = shadowRoot.querySelector('#tooltip');
21
+ this.#sizeContext = new Context(this.#$button, 'size');
16
22
  }
17
23
  connectedCallback() {
24
+ super.connectedCallback();
18
25
  this.#controller = new AbortController();
19
26
  const options = {
20
27
  signal: this.#controller.signal
@@ -26,14 +33,21 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
26
33
  this.addEventListener('-click', this.#onClickReactHandler, options);
27
34
  this.addEventListener('-focus', this.#onFocusReactHandler, options);
28
35
  this.addEventListener('-blur', this.#onBlurReactHandler, options);
36
+ subscribeContext(this, 'size', this.#onContextSize, this.#controller.signal);
37
+ this.#sizeContext.listen(this.#controller.signal);
38
+ this.#onSizeUpdate();
29
39
  }
30
40
  disconnectedCallback() {
41
+ super.disconnectedCallback();
31
42
  this.#controller.abort();
32
43
  }
33
44
  static get observedAttributes() {
34
- return ['disabled', 'aria-label'];
45
+ return ['type', 'size', 'disabled', 'aria-label', 'data-size'];
35
46
  }
36
- attributeChangedCallback(name, _, newVal) {
47
+ attributeChangedCallback(name, oldVal, newVal) {
48
+ if (oldVal === newVal) {
49
+ return;
50
+ }
37
51
  switch (name) {
38
52
  case 'disabled':
39
53
  {
@@ -47,19 +61,45 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
47
61
  updateAttribute(this.#$tooltip, 'text', newVal);
48
62
  break;
49
63
  }
64
+ case 'type':
65
+ {
66
+ if ('production' !== 'production') {
67
+ assertType(newVal);
68
+ }
69
+ break;
70
+ }
71
+ case 'size':
72
+ {
73
+ updateAttribute(this, 'data-size', newVal);
74
+ break;
75
+ }
76
+ case 'data-size':
77
+ {
78
+ if ('production' !== 'production') {
79
+ assertSizeEx(newVal, 'sinch-icon-button');
80
+ }
81
+ this.#onSizeUpdate();
82
+ break;
83
+ }
50
84
  }
51
85
  }
86
+ set type(value) {
87
+ updateLiteralAttribute(this, typeValues, 'type', value);
88
+ }
89
+ get type() {
90
+ return getLiteralAttribute(this, typeValues, 'type', 'tertiary');
91
+ }
52
92
  set disabled(isDisabled) {
53
93
  updateBooleanAttribute(this, 'disabled', isDisabled);
54
94
  }
55
95
  get disabled() {
56
96
  return getBooleanAttribute(this, 'disabled');
57
97
  }
58
- set small(isSmall) {
59
- updateBooleanAttribute(this, 'small', isSmall);
98
+ set size(size) {
99
+ updateLiteralAttribute(this, sizeExValues, 'size', size);
60
100
  }
61
- get small() {
62
- return getBooleanAttribute(this, 'small');
101
+ get size() {
102
+ return getLiteralAttribute(this, sizeExValues, 'size', DEFAULT_SIZE);
63
103
  }
64
104
  get tooltipRect() {
65
105
  return this.#$tooltip.tooltipRect;
@@ -73,6 +113,34 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
73
113
  blur() {
74
114
  this.#$button.blur();
75
115
  }
116
+ #onSizeUpdate() {
117
+ if (!this.isConnected) {
118
+ return;
119
+ }
120
+ const size = getAttribute(this, 'data-size', DEFAULT_SIZE);
121
+ this.#sizeContext.dispatch(size);
122
+ }
123
+ #onContextSize = e => {
124
+ if (this.hasAttribute('size')) {
125
+ return;
126
+ }
127
+ switch (e.detail) {
128
+ case 'l':
129
+ {
130
+ this.setAttribute('data-size', 'm');
131
+ break;
132
+ }
133
+ case 'm':
134
+ {
135
+ this.setAttribute('data-size', 's');
136
+ break;
137
+ }
138
+ default:
139
+ {
140
+ this.setAttribute('data-size', 'xs');
141
+ }
142
+ }
143
+ };
76
144
  #onButtonClick = () => {
77
145
  this.dispatchEvent(new CustomEvent('-click'));
78
146
  };
@@ -1,9 +1,13 @@
1
1
  import type { TRect, TSinchElementReact } from '../types';
2
+ import type { TSinchSizeEx } from '../utils/size';
3
+ export declare type TSinchIconButtonType = 'primary' | 'secondary' | 'tertiary';
2
4
  export declare type TSinchIconButtonElement = HTMLElement & {
5
+ /** Type, `tertiary` by default */
6
+ type: TSinchIconButtonType;
7
+ /** Size, `m` by default */
8
+ size: TSinchSizeEx;
3
9
  /** Disabled */
4
10
  disabled: boolean;
5
- /** Small */
6
- small: boolean;
7
11
  readonly tooltipRect: TRect;
8
12
  /** Click event */
9
13
  addEventListener(type: '-click', listener: (e: CustomEvent<void>) => void): void;
@@ -11,16 +15,20 @@ export declare type TSinchIconButtonElement = HTMLElement & {
11
15
  addEventListener(type: '-focus', listener: (e: CustomEvent<void>) => void): void;
12
16
  /** Blur event */
13
17
  addEventListener(type: '-blur', listener: (e: CustomEvent<void>) => void): void;
18
+ /** Type, `tertiary` by default */
19
+ setAttribute(name: 'type', value: string): void;
20
+ /** Size, `m` by default */
21
+ setAttribute(name: 'size', value: string): void;
14
22
  /** Disabled */
15
23
  setAttribute(name: 'disabled', value: ''): void;
16
- /** Small */
17
- setAttribute(name: 'small', value: ''): void;
18
24
  };
19
25
  export declare type TSinchIconButtonReact = TSinchElementReact<TSinchIconButtonElement> & {
26
+ /** Type, `tertiary` by default */
27
+ type?: TSinchIconButtonType;
28
+ /** Size, `m` by default */
29
+ size?: TSinchSizeEx;
20
30
  /** Disabled */
21
31
  disabled?: boolean;
22
- /** Small */
23
- small?: boolean;
24
32
  /** Label that is used for a11y */
25
33
  'aria-label': string;
26
34
  /** Click event handler */
@@ -0,0 +1,5 @@
1
+ import type { TSinchIconButtonType } from './types';
2
+ export declare const typeValues: readonly TSinchIconButtonType[];
3
+ declare type TAssertType = (value: string | null) => asserts value is TSinchIconButtonType;
4
+ export declare const assertType: TAssertType;
5
+ export {};
@@ -0,0 +1,9 @@
1
+ export const typeValues = ['primary', 'secondary', 'tertiary'];
2
+ export const assertType = value => {
3
+ if (value === null || value.length === 0) {
4
+ return;
5
+ }
6
+ if (!typeValues.includes(value)) {
7
+ throw new Error(`sinch-icon-button: invalid type attribute: ${value}`);
8
+ }
9
+ };
@@ -1,5 +1,5 @@
1
1
  import { NectaryElement, updateAttribute } from '../utils';
2
- const iconStylesHtml = '<style>:host{display:inline-block;vertical-align:middle}svg{display:block;pointer-events:none;fill:var(--sinch-color-icon,--sinch-color-text-default);height:var(--sinch-size-icon,24px)}</style>';
2
+ const iconStylesHtml = '<style>:host{display:inline-block;vertical-align:middle}svg{display:block;pointer-events:none;fill:var(--sinch-color-icon,var(--sinch-color-stormy-500));height:var(--sinch-size-icon,24px)}</style>';
3
3
  export const createIconClass = templateHTML => {
4
4
  const template = document.createElement('template');
5
5
  template.innerHTML = iconStylesHtml + templateHTML;
@@ -64,7 +64,9 @@ defineCustomElement('sinch-inline-alert', class extends NectaryElement {
64
64
  switch (name) {
65
65
  case 'type':
66
66
  {
67
- assertType(newVal);
67
+ if ('production' !== 'production') {
68
+ assertType(newVal);
69
+ }
68
70
  break;
69
71
  }
70
72
  case 'text':
package/input/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import '../icon-button';
2
+ import '../icons/close';
3
+ import '../stop-events';
1
4
  import type { TSinchInputElement, TSinchInputReact } from './types';
2
5
  declare global {
3
6
  namespace JSX {