@nectary/components 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/accordion/index.js +0 -4
  2. package/action-menu/index.js +5 -1
  3. package/action-menu-option/index.js +2 -4
  4. package/alert/index.js +2 -9
  5. package/alert/utils.d.ts +0 -3
  6. package/alert/utils.js +1 -6
  7. package/avatar/index.js +4 -20
  8. package/avatar/utils.d.ts +0 -4
  9. package/avatar/utils.js +0 -13
  10. package/badge/index.js +2 -8
  11. package/badge/utils.d.ts +0 -3
  12. package/badge/utils.js +1 -6
  13. package/button/index.js +25 -27
  14. package/button/utils.d.ts +0 -3
  15. package/button/utils.js +1 -9
  16. package/chat-bubble/index.d.ts +0 -1
  17. package/chat-bubble/index.js +1 -2
  18. package/checkbox/index.js +6 -9
  19. package/chip/index.js +22 -19
  20. package/chip/utils.d.ts +0 -1
  21. package/chip/utils.js +0 -5
  22. package/color-menu/index.js +6 -3
  23. package/color-swatch/index.js +1 -4
  24. package/color-swatch/utils.d.ts +0 -1
  25. package/color-swatch/utils.js +0 -5
  26. package/date-picker/index.js +1 -27
  27. package/date-picker/utils.d.ts +0 -8
  28. package/date-picker/utils.js +0 -20
  29. package/dialog/index.js +1 -2
  30. package/emoji-picker/index.js +7 -14
  31. package/file-picker/index.js +11 -9
  32. package/file-status/index.js +2 -9
  33. package/file-status/utils.d.ts +0 -3
  34. package/file-status/utils.js +1 -6
  35. package/help-tooltip/index.d.ts +0 -1
  36. package/help-tooltip/index.js +1 -2
  37. package/icon/index.js +1 -1
  38. package/icon-button/index.js +23 -27
  39. package/icon-button/utils.d.ts +0 -3
  40. package/icon-button/utils.js +1 -9
  41. package/inline-alert/index.js +2 -9
  42. package/inline-alert/utils.d.ts +0 -3
  43. package/inline-alert/utils.js +1 -6
  44. package/input/index.js +10 -16
  45. package/input/utils.d.ts +0 -3
  46. package/input/utils.js +1 -6
  47. package/package.json +2 -3
  48. package/pagination/index.js +0 -4
  49. package/pagination/types.d.ts +0 -1
  50. package/pop/index.js +23 -25
  51. package/pop/utils.d.ts +0 -3
  52. package/pop/utils.js +0 -5
  53. package/popover/index.js +1 -4
  54. package/popover/utils.d.ts +0 -3
  55. package/popover/utils.js +0 -5
  56. package/radio/index.js +0 -4
  57. package/radio/types.d.ts +0 -1
  58. package/rich-text/index.js +2 -9
  59. package/rich-text/utils.d.ts +0 -3
  60. package/rich-text/utils.js +1 -6
  61. package/segment/index.js +1 -4
  62. package/segment-collapse/index.js +0 -4
  63. package/segment-collapse/types.d.ts +0 -1
  64. package/segmented-control/index.js +0 -4
  65. package/segmented-icon-control/index.js +0 -4
  66. package/select-button/index.js +23 -20
  67. package/select-menu/index.js +6 -3
  68. package/skeleton/index.js +45 -9
  69. package/skeleton-item/index.js +1 -28
  70. package/spinner/index.js +2 -9
  71. package/tabs/index.js +0 -4
  72. package/tag/index.js +1 -4
  73. package/tag/utils.d.ts +0 -1
  74. package/tag/utils.js +0 -5
  75. package/text/index.js +2 -9
  76. package/text/utils.d.ts +0 -3
  77. package/text/utils.js +1 -6
  78. package/textarea/index.js +0 -4
  79. package/tile-control/index.js +0 -4
  80. package/time-picker/index.js +1 -9
  81. package/time-picker/utils.d.ts +1 -1
  82. package/time-picker/utils.js +17 -5
  83. package/title/index.js +2 -12
  84. package/title/utils.d.ts +0 -7
  85. package/title/utils.js +1 -29
  86. package/toast/index.js +2 -9
  87. package/toast/utils.d.ts +0 -3
  88. package/toast/utils.js +1 -6
  89. package/toggle/index.js +0 -4
  90. package/toggle/types.d.ts +0 -1
  91. package/tooltip/index.js +1 -7
  92. package/tooltip/utils.d.ts +0 -5
  93. package/tooltip/utils.js +0 -10
  94. package/utils/dom.js +0 -5
  95. package/utils/element.js +2 -2
  96. package/utils/size.d.ts +0 -5
  97. package/utils/size.js +1 -17
@@ -1,6 +1 @@
1
- export const typeValues = ['info', 'success', 'warn', 'error'];
2
- export const assertType = value => {
3
- if (value === null || !typeValues.includes(value)) {
4
- throw new Error(`sinch-inline-alert: invalid type attribute: ${value}`);
5
- }
6
- };
1
+ export const typeValues = ['info', 'success', 'warn', 'error'];
package/input/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Context, defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
2
- import { assertSize, DEFAULT_SIZE, sizeValues } from '../utils/size';
2
+ import { DEFAULT_SIZE, sizeValues } from '../utils/size';
3
3
  const templateHTML = '<style>:host{all:initial;display:inline-block;vertical-align:middle}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;box-sizing:border-box;border-radius:var(--sinch-local-shape-radius);width:100%;height:var(--sinch-local-size);background-color:var(--sinch-comp-input-color-default-background-initial);--sinch-local-size:var(--sinch-comp-input-size-container-m);--sinch-global-size-icon:var(--sinch-comp-input-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-input-shape-radius-size-m)}:host([data-size="l"])>#wrapper{--sinch-local-size:var(--sinch-comp-input-size-container-l);--sinch-global-size-icon:var(--sinch-comp-input-size-icon-l);--sinch-local-shape-radius:var(--sinch-comp-input-shape-radius-size-l)}:host([data-size="m"])>#wrapper{--sinch-local-size:var(--sinch-comp-input-size-container-m);--sinch-global-size-icon:var(--sinch-comp-input-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-input-shape-radius-size-m)}:host([data-size="s"])>#wrapper{--sinch-local-size:var(--sinch-comp-input-size-container-s);--sinch-global-size-icon:var(--sinch-comp-input-size-icon-s);--sinch-local-shape-radius:var(--sinch-comp-input-shape-radius-size-s)}#input{all:initial;flex:1;flex-basis:0;min-width:0;height:100%;padding:0 12px;font:var(--sinch-comp-input-font-input);color:var(--sinch-comp-input-color-default-text-initial)}#input::placeholder{font:var(--sinch-comp-input-font-placeholder);color:var(--sinch-comp-input-color-default-text-placeholder);opacity:1}#border{position:absolute;border:1px solid var(--sinch-comp-input-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}#input:disabled{color:var(--sinch-comp-input-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-input-color-disabled-text-initial)}#input:disabled+#border{border-color:var(--sinch-comp-input-color-disabled-border-initial)}#input:focus+#border{border-color:var(--sinch-comp-input-color-default-border-focus);border-width:2px}:host([invalid]:not(:focus-within)) #input:enabled+#border{border-color:var(--sinch-comp-input-color-invalid-border-initial)}#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:12px;top:0;bottom:0;pointer-events:none;--sinch-global-color-icon:var(--sinch-comp-input-color-default-icon-initial)}:host([disabled]) #icon{--sinch-global-color-icon:var(--sinch-comp-input-color-disabled-icon-initial)}#icon-wrapper.empty{display:none}#icon-wrapper.empty~#input{padding-left:12px}#icon-wrapper:not(.empty)~#input{padding-left:calc(var(--sinch-global-size-icon) + 20px)}#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="right"><slot name="right"></slot></div></div>';
4
- import { assertType, inputTypes } from './utils';
4
+ import { inputTypes } from './utils';
5
5
  const template = document.createElement('template');
6
6
  template.innerHTML = templateHTML;
7
7
  defineCustomElement('sinch-input', class extends NectaryElement {
@@ -19,9 +19,7 @@ defineCustomElement('sinch-input', class extends NectaryElement {
19
19
  #sizeContext;
20
20
  constructor() {
21
21
  super();
22
- const shadowRoot = this.attachShadow({
23
- delegatesFocus: true
24
- });
22
+ const shadowRoot = this.attachShadow();
25
23
  shadowRoot.appendChild(template.content.cloneNode(true));
26
24
  this.#$input = shadowRoot.querySelector('#input');
27
25
  this.#$iconSlot = shadowRoot.querySelector('slot[name="icon"]');
@@ -64,7 +62,7 @@ defineCustomElement('sinch-input', class extends NectaryElement {
64
62
  this.#controller.abort();
65
63
  }
66
64
  static get observedAttributes() {
67
- return ['type', 'value', 'placeholder', 'invalid', 'disabled', 'size', 'autocomplete', 'data-size'];
65
+ return ['type', 'value', 'placeholder', 'invalid', 'disabled', 'size', 'autocomplete', 'data-size', 'aria-label'];
68
66
  }
69
67
  attributeChangedCallback(name, oldVal, newVal) {
70
68
  if (oldVal === newVal) {
@@ -73,9 +71,6 @@ defineCustomElement('sinch-input', class extends NectaryElement {
73
71
  switch (name) {
74
72
  case 'type':
75
73
  {
76
- if ('production' !== 'production') {
77
- assertType(newVal);
78
- }
79
74
  updateLiteralAttribute(this.#$input, inputTypes, 'type', newVal);
80
75
  updateAttribute(this.#$input, 'spellcheck', newVal === 'password' ? 'false' : null);
81
76
  break;
@@ -122,15 +117,18 @@ defineCustomElement('sinch-input', class extends NectaryElement {
122
117
  }
123
118
  case 'data-size':
124
119
  {
125
- if ('production' !== 'production') {
126
- assertSize(newVal, 'sinch-input');
127
- }
128
120
  this.#onSizeUpdate();
129
121
  break;
130
122
  }
131
123
  case 'autocomplete':
132
124
  {
133
125
  updateAttribute(this.#$input, name, newVal);
126
+ break;
127
+ }
128
+ case 'aria-label':
129
+ {
130
+ this.#$input.ariaLabel = newVal;
131
+ break;
134
132
  }
135
133
  }
136
134
  }
@@ -244,10 +242,6 @@ defineCustomElement('sinch-input', class extends NectaryElement {
244
242
  }
245
243
  this.#isPendingDk = false;
246
244
  this.#cursorPos = nextCursorPos;
247
- this.dispatchEvent(new CustomEvent('change', {
248
- detail: nextValue,
249
- bubbles: true
250
- }));
251
245
  this.dispatchEvent(new CustomEvent('-change', {
252
246
  detail: nextValue
253
247
  }));
package/input/utils.d.ts CHANGED
@@ -1,5 +1,2 @@
1
1
  import type { TSinchInputType } from './types';
2
2
  export declare const inputTypes: readonly TSinchInputType[];
3
- type TAssertType = (value: string | null) => asserts value is TSinchInputType | null;
4
- export declare const assertType: TAssertType;
5
- export {};
package/input/utils.js CHANGED
@@ -1,6 +1 @@
1
- export const inputTypes = ['text', 'password'];
2
- export const assertType = value => {
3
- if (value !== null && !inputTypes.includes(value)) {
4
- throw new Error(`sinch-input: invalid type attribute: ${value}`);
5
- }
6
- };
1
+ export const inputTypes = ['text', 'password'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
@@ -24,8 +24,7 @@
24
24
  "@babel/preset-typescript": "^7.21.4",
25
25
  "@types/node": "^18.16.0",
26
26
  "@types/react": "^18.0.38",
27
- "babel-plugin-html-inline-minifier": "workspace:^0.0.0",
28
- "babel-plugin-transform-inline-environment-variables": "^0.4.4",
27
+ "babel-plugin-html-inline-minifier": "workspace:*",
29
28
  "typescript": "^5.0.4"
30
29
  }
31
30
  }
@@ -120,10 +120,6 @@ defineCustomElement('sinch-pagination', class extends NectaryElement {
120
120
  }
121
121
  #dispatchChangeEvent(value) {
122
122
  const detail = this.#clamp(value);
123
- this.dispatchEvent(new CustomEvent('change', {
124
- detail,
125
- bubbles: true
126
- }));
127
123
  this.dispatchEvent(new CustomEvent('-change', {
128
124
  detail
129
125
  }));
@@ -5,7 +5,6 @@ export type TSinchPaginationElement = HTMLElement & {
5
5
  readonly prevButtonRect: TRect;
6
6
  readonly nextButtonRect: TRect;
7
7
  nthButtonRect(index: number): TRect | null;
8
- addEventListener(type: 'change', listener: (e: CustomEvent<number>) => void): void;
9
8
  addEventListener(type: '-change', listener: (e: CustomEvent<number>) => void): void;
10
9
  setAttribute(name: 'value', value: string): void;
11
10
  setAttribute(name: 'max', value: string): void;
package/pop/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, subscribeContext, isTargetEqual } from '../utils';
2
2
  const templateHTML = '<style>:host{display:contents;position:relative}dialog{position:fixed;left:0;top:0;margin:0;outline:0;padding:0;border:none;box-sizing:border-box;max-width:unset;max-height:unset;z-index:1;background:0 0;overflow:visible}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}#content{position:relative;z-index:1}#target-open{display:flex;flex-direction:column;position:absolute;left:0;top:0}#focus{display:none;position:absolute;width:0;height:0}</style><slot id="target" name="target" aria-haspopup="dialog" aria-expanded="false"></slot><div id="focus"></div><dialog id="dialog"><div id="target-open"><slot name="target-open"></slot></div><div id="content"><slot name="content"></slot></div></dialog>';
3
- import { assertOrientation, disableScroll, enableScroll, orientationValues } from './utils';
3
+ import { disableScroll, enableScroll, orientationValues } from './utils';
4
4
  const template = document.createElement('template');
5
5
  template.innerHTML = templateHTML;
6
6
  defineCustomElement('sinch-pop', class extends NectaryElement {
7
- #$target;
7
+ #$targetWrapper;
8
8
  #$focus;
9
9
  #$dialog;
10
10
  #resizeThrottle;
@@ -21,7 +21,7 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
21
21
  super();
22
22
  const shadowRoot = this.attachShadow();
23
23
  shadowRoot.appendChild(template.content.cloneNode(true));
24
- this.#$target = shadowRoot.querySelector('#target');
24
+ this.#$targetWrapper = shadowRoot.querySelector('#target');
25
25
  this.#$focus = shadowRoot.querySelector('#focus');
26
26
  this.#$dialog = shadowRoot.querySelector('#dialog');
27
27
  this.#$targetSlot = shadowRoot.querySelector('slot[name="target"]');
@@ -119,9 +119,6 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
119
119
  }
120
120
  case 'orientation':
121
121
  {
122
- if ('production' !== 'production') {
123
- assertOrientation(newVal);
124
- }
125
122
  if (this.#$dialog.open) {
126
123
  this.#updateOrientation();
127
124
  }
@@ -135,7 +132,7 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
135
132
  item = getFirstSlotElement(this.#$targetOpenSlot, true);
136
133
  }
137
134
  if (item === null) {
138
- return getRect(this.#$target);
135
+ return getRect(this.#$targetWrapper);
139
136
  }
140
137
  if (Reflect.has(item, 'footprintRect')) {
141
138
  return item.footprintRect;
@@ -145,7 +142,7 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
145
142
  #getFirstTargetElement(slot) {
146
143
  const item = getFirstSlotElement(slot, true);
147
144
  if (item === null) {
148
- return this.#$target;
145
+ return this.#$targetWrapper;
149
146
  }
150
147
  return item;
151
148
  }
@@ -163,7 +160,7 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
163
160
  this.#$focus.removeAttribute('tabindex');
164
161
  this.#$focus.removeAttribute('style');
165
162
  this.#$dialog.showModal();
166
- this.#$target.setAttribute('aria-expanded', 'true');
163
+ this.#$targetWrapper.setAttribute('aria-expanded', 'true');
167
164
  this.#updateOrientation();
168
165
  if (this.modal) {
169
166
  getFirstFocusableElement(this.#$contentSlot)?.focus();
@@ -175,9 +172,9 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
175
172
  const marginTop = parseInt(targetElComputedStyle.marginTop);
176
173
  const marginBottom = parseInt(targetElComputedStyle.marginBottom);
177
174
  const targetRect = this.#getTargetRect();
178
- this.#$target.style.setProperty('display', 'block');
179
- this.#$target.style.setProperty('width', `${targetRect.width + marginLeft + marginRight}px`);
180
- this.#$target.style.setProperty('height', `${targetRect.height + marginTop + marginBottom}px`);
175
+ this.#$targetWrapper.style.setProperty('display', 'block');
176
+ this.#$targetWrapper.style.setProperty('width', `${targetRect.width + marginLeft + marginRight}px`);
177
+ this.#$targetWrapper.style.setProperty('height', `${targetRect.height + marginTop + marginBottom}px`);
181
178
  this.#$targetOpenWrapper.style.setProperty('width', `${targetRect.width}px`);
182
179
  this.#$targetOpenWrapper.style.setProperty('height', `${targetRect.height}px`);
183
180
  this.#targetStyleValue = $targetEl.getAttribute('style');
@@ -221,26 +218,27 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
221
218
  this.#dispatchContentVisibility(false);
222
219
  this.#$targetOpenSlot.removeEventListener('keydown', this.#onTargetKeydown);
223
220
  if (isNonModal) {
224
- this.#targetActiveElement = null;
225
221
  this.#$targetOpenSlot.addEventListener('blur', this.#captureActiveElement, true);
226
222
  }
227
223
  this.#$dialog.close();
228
- this.#$target.setAttribute('aria-expanded', 'false');
224
+ this.#$targetWrapper.setAttribute('aria-expanded', 'false');
229
225
  if (isNonModal) {
230
226
  this.#$targetOpenSlot.removeEventListener('blur', this.#captureActiveElement, true);
231
227
  }
232
- const targetEl = this.#getFirstTargetElement(this.#$targetOpenSlot);
233
- targetEl.style.removeProperty('margin');
234
- targetEl.style.removeProperty('position');
235
- targetEl.style.removeProperty('transform');
236
- if (this.#targetStyleValue !== null) {
237
- targetEl.setAttribute('style', this.#targetStyleValue);
238
- this.#targetStyleValue = null;
228
+ if (isNonModal) {
229
+ const targetEl = this.#getFirstTargetElement(this.#$targetOpenSlot);
230
+ targetEl.style.removeProperty('margin');
231
+ targetEl.style.removeProperty('position');
232
+ targetEl.style.removeProperty('transform');
233
+ if (this.#targetStyleValue !== null) {
234
+ targetEl.setAttribute('style', this.#targetStyleValue);
235
+ this.#targetStyleValue = null;
236
+ }
237
+ getFirstSlotElement(this.#$targetOpenSlot)?.setAttribute('slot', 'target');
238
+ this.#$targetWrapper.style.removeProperty('display');
239
+ this.#$targetWrapper.style.removeProperty('width');
240
+ this.#$targetWrapper.style.removeProperty('height');
239
241
  }
240
- getFirstSlotElement(this.#$targetOpenSlot)?.setAttribute('slot', 'target');
241
- this.#$target.style.removeProperty('display');
242
- this.#$target.style.removeProperty('width');
243
- this.#$target.style.removeProperty('height');
244
242
  if (this.#targetActiveElement !== null) {
245
243
  if (!isElementFocused(this.#targetActiveElement)) {
246
244
  this.#$targetSlot.addEventListener('focus', this.#stopEventPropagation, true);
package/pop/utils.d.ts CHANGED
@@ -1,7 +1,4 @@
1
1
  import type { TSinchPopOrientation } from './types';
2
2
  export declare const orientationValues: readonly TSinchPopOrientation[];
3
- type TAssertOrientation = (value: string | null) => asserts value is TSinchPopOrientation;
4
- export declare const assertOrientation: TAssertOrientation;
5
3
  export declare const disableScroll: () => void;
6
4
  export declare const enableScroll: () => void;
7
- export {};
package/pop/utils.js CHANGED
@@ -1,9 +1,4 @@
1
1
  export const orientationValues = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'bottom-stretch', 'bottom-center', 'top-stretch', 'top-center', 'center-left', 'center-right'];
2
- export const assertOrientation = value => {
3
- if (value === null || !orientationValues.includes(value)) {
4
- throw new Error(`sinch-pop: invalid orientation attribute: ${value}`);
5
- }
6
- };
7
2
  const bodyEl = document.body;
8
3
  export const disableScroll = () => {
9
4
  bodyEl.style.overflow = 'hidden';
package/popover/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import '../pop';
2
2
  import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateBooleanAttribute, NectaryElement, updateAttribute, getReactEventHandler, isAttrTrue, setClass, rectOverlap, subscribeContext } from '../utils';
3
3
  const templateHTML = '<style>:host{display:contents}#content-wrapper{position:relative;padding-top:4px}:host([tip]) #content-wrapper{padding-top:12px;filter:drop-shadow(var(--sinch-comp-popover-shadow))}:host([orientation^=top]) #content-wrapper{padding-top:0;padding-bottom:4px}:host([orientation^=top][tip]) #content-wrapper{padding-top:0;padding-bottom:12px}#content{background-color:var(--sinch-comp-popover-color-default-background-initial);border:1px solid var(--sinch-comp-popover-color-default-border-initial);border-radius:var(--sinch-comp-popover-shape-radius);box-shadow:var(--sinch-comp-popover-shadow);overflow:hidden}:host([tip]) #content{box-shadow:none}#tip{position:absolute;left:50%;top:13px;transform:translateX(-50%) rotate(180deg);transform-origin:top center;fill:var(--sinch-comp-popover-color-default-background-initial);stroke:var(--sinch-comp-popover-color-default-border-initial);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}</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>';
4
- import { assertOrientation, getPopOrientation, orientationValues } from './utils';
4
+ import { getPopOrientation, orientationValues } from './utils';
5
5
  const TIP_SIZE = 16;
6
6
  const template = document.createElement('template');
7
7
  template.innerHTML = templateHTML;
@@ -45,9 +45,6 @@ defineCustomElement('sinch-popover', class extends NectaryElement {
45
45
  switch (name) {
46
46
  case 'orientation':
47
47
  {
48
- if ('production' !== 'production') {
49
- assertOrientation(newVal);
50
- }
51
48
  updateAttribute(this.#$pop, 'orientation', getPopOrientation(this.orientation));
52
49
  if (this.#$pop.open) {
53
50
  this.#updateTipOrientation();
@@ -2,6 +2,3 @@ import type { TSinchPopoverOrientation } from './types';
2
2
  import type { TSinchPopOrientation } from '../pop/types';
3
3
  export declare const orientationValues: readonly TSinchPopoverOrientation[];
4
4
  export declare const getPopOrientation: (orientation: TSinchPopoverOrientation) => TSinchPopOrientation;
5
- type TAssertOrientation = (value: string | null) => asserts value is TSinchPopoverOrientation;
6
- export declare const assertOrientation: TAssertOrientation;
7
- export {};
package/popover/utils.js CHANGED
@@ -7,9 +7,4 @@ export const getPopOrientation = orientation => {
7
7
  return 'bottom-stretch';
8
8
  }
9
9
  return orientation;
10
- };
11
- export const assertOrientation = value => {
12
- if (value === null || !orientationValues.includes(value)) {
13
- throw new Error(`sinch-popover: invalid orientation attribute: ${value}`);
14
- }
15
10
  };
package/radio/index.js CHANGED
@@ -95,10 +95,6 @@ defineCustomElement('sinch-radio', class extends NectaryElement {
95
95
  }
96
96
  }
97
97
  #dispatchChangeEvent(value) {
98
- this.dispatchEvent(new CustomEvent('change', {
99
- detail: value,
100
- bubbles: true
101
- }));
102
98
  this.dispatchEvent(new CustomEvent('-change', {
103
99
  detail: value
104
100
  }));
package/radio/types.d.ts CHANGED
@@ -2,7 +2,6 @@ import type { TSinchElementReact } from '../types';
2
2
  export type TSinchRadioElement = HTMLElement & {
3
3
  value: string;
4
4
  invalid: boolean;
5
- addEventListener(type: 'change', listener: (e: CustomEvent<boolean>) => void): void;
6
5
  addEventListener(type: '-change', listener: (e: CustomEvent<boolean>) => void): void;
7
6
  setAttribute(name: 'value', value: string): void;
8
7
  setAttribute(name: 'invalid', value: ''): void;
@@ -1,6 +1,6 @@
1
1
  import { defineCustomElement, NectaryElement, getLiteralAttribute, updateLiteralAttribute, getAttribute, updateAttribute, parseMarkdown } from '../utils';
2
2
  const templateHTML = '<style>:host{display:block;--sinch-comp-rich-text-font:var(--sinch-sys-font-body-m)}:host([size="s"]){--sinch-comp-rich-text-font:var(--sinch-sys-font-body-s)}:host([size=xs]){--sinch-comp-rich-text-font:var(--sinch-sys-font-body-xs)}:host([size=xxs]){--sinch-comp-rich-text-font:var(--sinch-sys-font-body-xxs)}#wrapper{font:var(--sinch-comp-rich-text-font);color:var(--sinch-global-color-text,var(--sinch-sys-color-text-default))}.em1{font-style:italic}.em2{font-weight:var(--sinch-ref-typography-font-weight-700)}.em3{font-style:italic;font-weight:var(--sinch-ref-typography-font-weight-700)}.strikethrough{text-decoration:line-through}.link{font:var(--sinch-comp-link-default-font-initial);color:var(--sinch-comp-link-color-default-text-initial);text-decoration:underline}.link:hover{color:var(--sinch-comp-link-color-default-text-hover);text-decoration:none}.code{font:var(--sinch-comp-code-tag-font-text);line-height:inherit;font-size:inherit;border:1px solid var(--sinch-comp-code-tag-color-default-border-initial);background-color:var(--sinch-comp-code-tag-color-default-background-initial);padding:0 .25em;border-radius:var(--sinch-comp-code-tag-shape-radius)}.paragraph{margin:0}.paragraph+.paragraph{margin-top:12px}</style><div id="wrapper"></div>';
3
- import { assertSize, sizeValues } from './utils';
3
+ import { sizeValues } from './utils';
4
4
  const template = document.createElement('template');
5
5
  template.innerHTML = templateHTML;
6
6
  defineCustomElement('sinch-rich-text', class extends NectaryElement {
@@ -27,17 +27,10 @@ defineCustomElement('sinch-rich-text', class extends NectaryElement {
27
27
  updateAttribute(this, 'text', value);
28
28
  }
29
29
  static get observedAttributes() {
30
- return ['size', 'text'];
30
+ return ['text'];
31
31
  }
32
32
  attributeChangedCallback(name, _, newVal) {
33
33
  switch (name) {
34
- case 'size':
35
- {
36
- if ('production' !== 'production') {
37
- assertSize(newVal);
38
- }
39
- break;
40
- }
41
34
  case 'text':
42
35
  {
43
36
  this.#updateText(newVal);
@@ -1,5 +1,2 @@
1
1
  import type { TSinchTextType } from '../text/types';
2
2
  export declare const sizeValues: readonly TSinchTextType[];
3
- type TAssertSize = (value: string | null) => asserts value is TSinchTextType;
4
- export declare const assertSize: TAssertSize;
5
- export {};
@@ -1,6 +1 @@
1
- export const sizeValues = ['m', 's', 'xs', 'xxs'];
2
- export const assertSize = value => {
3
- if (value === null || !sizeValues.includes(value)) {
4
- throw new Error(`sinch-rich-text: invalid "size" attribute: "${value}"`);
5
- }
6
- };
1
+ export const sizeValues = ['m', 's', 'xs', 'xxs'];
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
- import { assertSize, DEFAULT_SIZE, sizeValues } from '../utils/size';
4
+ import { DEFAULT_SIZE, sizeValues } from '../utils/size';
5
5
  const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;gap:12px;width:100%;height:100%;border:1px solid var(--sinch-comp-segment-color-default-border-initial);border-radius:var(--sinch-comp-segment-shape-radius);box-sizing:border-box;background-color:var(--sinch-comp-segment-color-default-background-initial);padding:8px 0 16px}#header{display:flex;flex-direction:row;align-items:center;height:48px;gap:8px;padding:0 24px}#icon{--sinch-global-size-icon:32px}#caption{min-width:1em;--sinch-comp-title-font:var(--sinch-comp-segment-font-size-m-title)}:host([size="l"]) #caption{--sinch-comp-title-font:var(--sinch-comp-segment-font-size-l-title)}:host([size="s"]) #caption{--sinch-comp-title-font:var(--sinch-comp-segment-font-size-s-title)}#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]) :is(#content-wrapper,#action){display:none}:host([collapsed]) #wrapper{padding-bottom:8px}::slotted([slot=icon]){margin-right:8px}</style><div id="wrapper"><div id="header"><slot id="icon" 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>';
6
6
  import { getTitleTypeFromSize } from './utils';
7
7
  const template = document.createElement('template');
@@ -63,9 +63,6 @@ defineCustomElement('sinch-segment', class extends NectaryElement {
63
63
  }
64
64
  case 'size':
65
65
  {
66
- if ('production' !== 'production') {
67
- assertSize(newVal, 'sinch-segment');
68
- }
69
66
  const titleType = getTitleTypeFromSize(this.size);
70
67
  updateAttribute(this.#$caption, 'level', getTitleLevelFromType(titleType));
71
68
  break;
@@ -50,10 +50,6 @@ defineCustomElement('sinch-segment-collapse', class extends NectaryElement {
50
50
  }
51
51
  #onClick = () => {
52
52
  const detail = !this.value;
53
- this.dispatchEvent(new CustomEvent('change', {
54
- detail,
55
- bubbles: true
56
- }));
57
53
  this.dispatchEvent(new CustomEvent('-change', {
58
54
  detail
59
55
  }));
@@ -1,7 +1,6 @@
1
1
  import type { TSinchElementReact } from '../types';
2
2
  export type TSinchSegmentExpandElement = HTMLElement & {
3
3
  value: boolean;
4
- addEventListener(type: 'change', listener: (e: CustomEvent<boolean>) => void): void;
5
4
  addEventListener(type: '-change', listener: (e: CustomEvent<boolean>) => void): void;
6
5
  setAttribute(name: 'value', value: string): void;
7
6
  };
@@ -48,10 +48,6 @@ defineCustomElement('sinch-segmented-control', class extends NectaryElement {
48
48
  #onOptionChange = e => {
49
49
  e.stopPropagation();
50
50
  const detail = e.detail;
51
- this.dispatchEvent(new CustomEvent('change', {
52
- detail,
53
- bubbles: true
54
- }));
55
51
  this.dispatchEvent(new CustomEvent('-change', {
56
52
  detail
57
53
  }));
@@ -56,10 +56,6 @@ defineCustomElement('sinch-segmented-icon-control', class extends NectaryElement
56
56
  const $elem = e.target;
57
57
  const value = e.detail;
58
58
  const detail = this.multiple ? updateCsv(this.value, value, !getBooleanAttribute($elem, 'data-checked')) : value;
59
- this.dispatchEvent(new CustomEvent('change', {
60
- detail,
61
- bubbles: true
62
- }));
63
59
  this.dispatchEvent(new CustomEvent('-change', {
64
60
  detail
65
61
  }));
@@ -1,12 +1,11 @@
1
1
  import '../text';
2
2
  import '../icon';
3
3
  import { defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute, Context } from '../utils';
4
- import { assertSize, DEFAULT_SIZE, sizeValues } from '../utils/size';
5
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;gap:8px;box-sizing:border-box;width:100%;height:var(--sinch-local-size);padding:0 8px 0 12px;background-color:var(--sinch-comp-select-button-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);--sinch-local-size:var(--sinch-comp-select-button-size-container-m);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-m);--sinch-global-color-icon:var(--sinch-comp-select-button-color-default-icon-initial)}:host([data-size="l"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-l);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-l);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-l)}:host([data-size="m"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-m);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-m)}:host([data-size="s"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-s);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-s);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-s)}:host([data-size="l"]) #wrapper{padding:0 12px}:host([data-size="m"]) #wrapper{padding:0 8px 0 12px}:host([data-size="s"]) #wrapper{padding:0 4px 0 12px}:host([disabled]) #wrapper{--sinch-global-color-icon:var(--sinch-comp-select-button-color-disabled-icon-initial)}#text{flex:1;min-width:0;--sinch-comp-text-font:var(--sinch-comp-select-button-font-input);--sinch-global-color-text:var(--sinch-comp-select-button-color-default-text-initial)}#text:empty{display:none}#placeholder{display:none;flex:1;min-width:0;--sinch-comp-text-font:var(--sinch-comp-select-button-font-placeholder);--sinch-global-color-text:var(--sinch-comp-select-button-color-default-placeholder-initial)}#text:empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-select-button-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}#button{all:initial;cursor:pointer;position:absolute;left:0;top:0;width:100%;height:100%;z-index:1}#button:disabled{cursor:initial}#button:disabled+#border{border-color:var(--sinch-comp-select-button-color-disabled-border-initial)}#button:disabled~#text{--sinch-global-color-text:var(--sinch-comp-select-button-color-disabled-text-initial)}#button:disabled~#placeholder{--sinch-global-color-text:var(--sinch-comp-select-button-color-disabled-placeholder-initial)}:host([invalid]) #button:enabled+#border{border-color:var(--sinch-comp-select-button-color-invalid-border-initial)}#wrapper>#button:focus-visible+#border{border-color:var(--sinch-comp-select-button-color-default-border-focus);border-width:2px}#left{display:flex;flex-direction:row;align-self:stretch;align-items:center;gap:4px;margin-left:-4px}#left.empty{display:none}#dropdown-icon{margin-left:-4px}</style><div id="wrapper"><button id="button"></button><div id="border"></div><div id="left"><slot name="left"></slot></div><slot name="icon"></slot><sinch-text id="text" type="m" ellipsis></sinch-text><sinch-text id="placeholder" type="m" ellipsis></sinch-text><sinch-icon id="dropdown-icon" name="keyboard_arrow_down"></sinch-icon></div>';
4
+ import { DEFAULT_SIZE, sizeValues } from '../utils/size';
5
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;cursor:pointer}:host([disabled]){cursor:initial}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;gap:8px;box-sizing:border-box;width:100%;height:var(--sinch-local-size);padding:0 8px 0 12px;background-color:var(--sinch-comp-select-button-color-default-background-initial);border-radius:var(--sinch-local-shape-radius);--sinch-local-size:var(--sinch-comp-select-button-size-container-m);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-m);--sinch-global-color-icon:var(--sinch-comp-select-button-color-default-icon-initial)}:host([data-size="l"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-l);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-l);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-l)}:host([data-size="m"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-m);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-m)}:host([data-size="s"])>#wrapper{--sinch-local-size:var(--sinch-comp-select-button-size-container-s);--sinch-global-size-icon:var(--sinch-comp-select-button-size-icon-s);--sinch-local-shape-radius:var(--sinch-comp-select-button-shape-radius-size-s)}:host([data-size="l"]) #wrapper{padding:0 12px}:host([data-size="m"]) #wrapper{padding:0 8px 0 12px}:host([data-size="s"]) #wrapper{padding:0 4px 0 12px}:host([disabled]) #wrapper{--sinch-global-color-icon:var(--sinch-comp-select-button-color-disabled-icon-initial)}#text{flex:1;min-width:0;--sinch-comp-text-font:var(--sinch-comp-select-button-font-input);--sinch-global-color-text:var(--sinch-comp-select-button-color-default-text-initial)}#text:empty{display:none}#placeholder{display:none;flex:1;min-width:0;--sinch-comp-text-font:var(--sinch-comp-select-button-font-placeholder);--sinch-global-color-text:var(--sinch-comp-select-button-color-default-placeholder-initial)}#text:empty+#placeholder{display:block}#border{position:absolute;border:1px solid var(--sinch-comp-select-button-color-default-border-initial);border-radius:var(--sinch-local-shape-radius);inset:0;pointer-events:none}:host(:focus-visible) #border{border-color:var(--sinch-comp-select-button-color-default-border-focus);border-width:2px}:host([invalid]) #border{border-color:var(--sinch-comp-select-button-color-invalid-border-initial)}:host([disabled]) #border{border-color:var(--sinch-comp-select-button-color-disabled-border-initial)}:host([disabled]) #text{--sinch-global-color-text:var(--sinch-comp-select-button-color-disabled-text-initial)}:host([disabled]) #placeholder{--sinch-global-color-text:var(--sinch-comp-select-button-color-disabled-placeholder-initial)}#left{display:flex;flex-direction:row;align-self:stretch;align-items:center;gap:4px;margin-left:-4px}#left.empty{display:none}#dropdown-icon{margin-left:-4px}</style><div id="wrapper" inert><div id="border"></div><div id="left"><slot name="left"></slot></div><slot name="icon"></slot><sinch-text id="text" type="m" ellipsis></sinch-text><sinch-text id="placeholder" type="m" ellipsis></sinch-text><sinch-icon id="dropdown-icon" name="keyboard_arrow_down"></sinch-icon></div>';
6
6
  const template = document.createElement('template');
7
7
  template.innerHTML = templateHTML;
8
8
  defineCustomElement('sinch-select-button', class extends NectaryElement {
9
- #$button;
10
9
  #$text;
11
10
  #$placeholder;
12
11
  #$leftSlot;
@@ -17,10 +16,9 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
17
16
  constructor() {
18
17
  super();
19
18
  const shadowRoot = this.attachShadow({
20
- delegatesFocus: true
19
+ delegatesFocus: false
21
20
  });
22
21
  shadowRoot.appendChild(template.content.cloneNode(true));
23
- this.#$button = shadowRoot.querySelector('#button');
24
22
  this.#$text = shadowRoot.querySelector('#text');
25
23
  this.#$placeholder = shadowRoot.querySelector('#placeholder');
26
24
  this.#$leftSlot = shadowRoot.querySelector('slot[name="left"]');
@@ -34,14 +32,18 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
34
32
  const {
35
33
  signal
36
34
  } = this.#controller;
37
- this.setAttribute('role', 'button');
38
- this.#$button.addEventListener('click', this.#onButtonClick, {
35
+ this.role = 'button';
36
+ this.tabIndex = 0;
37
+ this.addEventListener('click', this.#onButtonClick, {
39
38
  signal
40
39
  });
41
- this.#$button.addEventListener('focus', this.#onButtonFocus, {
40
+ this.addEventListener('focus', this.#onButtonFocus, {
42
41
  signal
43
42
  });
44
- this.#$button.addEventListener('blur', this.#onButtonBlur, {
43
+ this.addEventListener('blur', this.#onButtonBlur, {
44
+ signal
45
+ });
46
+ this.addEventListener('keydown', this.#onButtonKeydown, {
45
47
  signal
46
48
  });
47
49
  this.addEventListener('-click', this.#onClickReactHandler, {
@@ -93,7 +95,6 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
93
95
  case 'disabled':
94
96
  {
95
97
  const isDisabled = isAttrTrue(newVal);
96
- this.#$button.disabled = isDisabled;
97
98
  updateBooleanAttribute(this, 'disabled', isDisabled);
98
99
  break;
99
100
  }
@@ -104,9 +105,6 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
104
105
  }
105
106
  case 'data-size':
106
107
  {
107
- if ('production' !== 'production') {
108
- assertSize(newVal, 'sinch-select-button');
109
- }
110
108
  this.#onSizeUpdate();
111
109
  break;
112
110
  }
@@ -145,12 +143,6 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
145
143
  get focusable() {
146
144
  return true;
147
145
  }
148
- focus() {
149
- this.#$button.focus();
150
- }
151
- blur() {
152
- this.#$button.blur();
153
- }
154
146
  #onContextSize = e => {
155
147
  if (this.hasAttribute('size')) {
156
148
  return;
@@ -183,8 +175,19 @@ defineCustomElement('sinch-select-button', class extends NectaryElement {
183
175
  #onButtonBlur = () => {
184
176
  this.dispatchEvent(new CustomEvent('-blur'));
185
177
  };
178
+ #onButtonKeydown = e => {
179
+ switch (e.code) {
180
+ case 'Space':
181
+ case 'Enter':
182
+ {
183
+ this.click();
184
+ }
185
+ }
186
+ };
186
187
  #onButtonClick = () => {
187
- this.dispatchEvent(new CustomEvent('-click'));
188
+ if (!this.disabled) {
189
+ this.dispatchEvent(new CustomEvent('-click'));
190
+ }
188
191
  };
189
192
  #onClickReactHandler = e => {
190
193
  getReactEventHandler(this, 'on-click')?.(e);
@@ -18,7 +18,9 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
18
18
  #searchDebounce;
19
19
  constructor() {
20
20
  super();
21
- const shadowRoot = this.attachShadow();
21
+ const shadowRoot = this.attachShadow({
22
+ delegatesFocus: false
23
+ });
22
24
  shadowRoot.appendChild(template.content.cloneNode(true));
23
25
  this.#$optionSlot = shadowRoot.querySelector('slot');
24
26
  this.#$listbox = shadowRoot.querySelector('#listbox');
@@ -32,8 +34,8 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
32
34
  const options = {
33
35
  signal: this.#controller.signal
34
36
  };
35
- this.setAttribute('role', 'listbox');
36
- this.setAttribute('tabindex', '0');
37
+ this.role = 'listbox';
38
+ this.tabIndex = 0;
37
39
  this.addEventListener('keydown', this.#onListboxKeyDown, options);
38
40
  this.addEventListener('focus', this.#onFocus, options);
39
41
  this.addEventListener('blur', this.#onListboxBlur, options);
@@ -118,6 +120,7 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
118
120
  };
119
121
  #onListboxClick = e => {
120
122
  const $elem = e.target;
123
+ this.focus();
121
124
  if (!getBooleanAttribute($elem, 'disabled')) {
122
125
  this.#selectOption($elem);
123
126
  }