@nectary/components 2.2.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/accordion/index.js +21 -16
  2. package/accordion-item/index.js +9 -17
  3. package/action-menu/index.js +6 -9
  4. package/action-menu-option/index.js +3 -7
  5. package/alert/index.js +12 -12
  6. package/avatar/index.js +26 -23
  7. package/badge/index.js +2 -2
  8. package/button/index.js +6 -4
  9. package/card/index.js +6 -6
  10. package/checkbox/index.js +3 -3
  11. package/chip/index.js +2 -2
  12. package/code-tag/index.js +6 -9
  13. package/color-menu/index.js +10 -17
  14. package/color-menu-option/index.d.ts +0 -1
  15. package/color-menu-option/index.js +2 -6
  16. package/color-swatch/index.js +9 -9
  17. package/date-picker/index.js +0 -3
  18. package/dialog/index.js +16 -10
  19. package/dialog/types.d.ts +2 -0
  20. package/emoji/index.js +3 -0
  21. package/field/index.js +4 -4
  22. package/file-drop/index.js +21 -19
  23. package/file-picker/index.js +18 -18
  24. package/file-status/index.js +8 -5
  25. package/flag/index.js +3 -0
  26. package/help-tooltip/index.d.ts +1 -0
  27. package/help-tooltip/index.js +2 -1
  28. package/horizontal-stepper/index.js +1 -4
  29. package/horizontal-stepper-item/index.js +0 -3
  30. package/icon-button/index.js +5 -6
  31. package/input/index.js +8 -5
  32. package/link/index.js +2 -2
  33. package/package.json +1 -1
  34. package/pagination/index.js +2 -6
  35. package/pop/index.js +5 -4
  36. package/popover/index.js +2 -2
  37. package/progress/index.js +4 -4
  38. package/progress-stepper/index.js +1 -4
  39. package/progress-stepper-item/index.js +5 -5
  40. package/radio/index.js +28 -24
  41. package/radio-option/index.js +12 -34
  42. package/rich-text/index.js +12 -12
  43. package/segment/index.js +5 -2
  44. package/segmented-control/index.js +0 -3
  45. package/segmented-control-option/index.js +5 -4
  46. package/segmented-icon-control/index.js +0 -3
  47. package/segmented-icon-control-option/index.js +5 -4
  48. package/select-button/index.js +3 -4
  49. package/select-menu/index.js +0 -3
  50. package/select-menu-option/index.js +2 -2
  51. package/skeleton/index.js +0 -3
  52. package/spinner/index.js +0 -3
  53. package/table-head-cell/index.js +2 -2
  54. package/table-row/index.js +2 -2
  55. package/tabs/index.js +7 -7
  56. package/tabs-icon-option/index.js +2 -2
  57. package/tabs-option/index.js +2 -2
  58. package/tag/index.js +5 -2
  59. package/text/index.js +5 -2
  60. package/textarea/index.js +12 -6
  61. package/tile-control/index.js +2 -2
  62. package/tile-control-option/index.js +0 -3
  63. package/time-picker/index.js +3 -4
  64. package/title/index.js +4 -4
  65. package/toast/index.js +0 -3
  66. package/toggle/index.js +2 -2
  67. package/tooltip/index.js +1 -1
  68. package/utils/dom.d.ts +1 -0
  69. package/utils/dom.js +5 -2
  70. package/vertical-stepper/index.js +1 -4
  71. package/vertical-stepper-item/index.js +0 -3
@@ -16,19 +16,13 @@ defineCustomElement('sinch-color-swatch', class extends NectaryElement {
16
16
  super.connectedCallback();
17
17
  this.#updateColor();
18
18
  }
19
- get name() {
20
- return getAttribute(this, 'name');
21
- }
22
- set name(value) {
23
- updateAttribute(this, 'name', value);
19
+ disconnectedCallback() {
20
+ super.disconnectedCallback();
24
21
  }
25
22
  static get observedAttributes() {
26
23
  return ['name'];
27
24
  }
28
- attributeChangedCallback(name, oldValue, newVal) {
29
- if (oldValue === newVal) {
30
- return;
31
- }
25
+ attributeChangedCallback(name) {
32
26
  switch (name) {
33
27
  case 'name':
34
28
  {
@@ -37,6 +31,12 @@ defineCustomElement('sinch-color-swatch', class extends NectaryElement {
37
31
  }
38
32
  }
39
33
  }
34
+ get name() {
35
+ return getAttribute(this, 'name');
36
+ }
37
+ set name(value) {
38
+ updateAttribute(this, 'name', value);
39
+ }
40
40
  #updateColor() {
41
41
  if (!this.isDomConnected) {
42
42
  return;
@@ -67,9 +67,6 @@ defineCustomElement('sinch-date-picker', class extends NectaryElement {
67
67
  return ['value', 'min', 'max', 'locale', 'range', 'prev-year-aria-label', 'next-year-aria-label', 'prev-month-aria-label', 'next-month-aria-label'];
68
68
  }
69
69
  attributeChangedCallback(name, prevValue, newVal) {
70
- if (newVal === prevValue) {
71
- return;
72
- }
73
70
  switch (name) {
74
71
  case 'value':
75
72
  {
package/dialog/index.js CHANGED
@@ -2,7 +2,7 @@ import '../icon-button';
2
2
  import '../icon';
3
3
  import '../stop-events';
4
4
  import '../title';
5
- import { defineCustomElement, getAttribute, getBooleanAttribute, getRect, isAttrTrue, updateAttribute, getReactEventHandler, NectaryElement, updateBooleanAttribute, setClass, isTargetEqual } from '../utils';
5
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getRect, isAttrTrue, updateAttribute, getReactEventHandler, NectaryElement, updateBooleanAttribute, setClass, isTargetEqual, isAttrEqual } from '../utils';
6
6
  const templateHTML = '<style>:host{display:contents;--sinch-comp-dialog-max-width:512px;--sinch-comp-dialog-max-height:50vh}#dialog{position:fixed;left:0;right:0;margin:auto;display:flex;flex-direction:column;padding:24px 0;max-width:var(--sinch-comp-dialog-max-width);max-height:unset;border-radius:var(--sinch-comp-dialog-shape-radius);box-sizing:border-box;contain:content;background-color:var(--sinch-comp-dialog-color-default-background-initial);border:none;box-shadow:var(--sinch-comp-dialog-shadow);outline:0}#dialog:not([open]){display:none}dialog::backdrop{background-color:#000;opacity:.55}#header{display:flex;flex-direction:row;justify-content:space-between;align-items:flex-start;margin-bottom:12px;padding:0 24px}#caption{--sinch-global-color-text:var(--sinch-comp-dialog-color-default-title-initial);--sinch-comp-title-font:var(--sinch-comp-dialog-font-title)}#content{min-height:0;overflow:auto;max-height:var(--sinch-comp-dialog-max-height);padding:4px 24px}#action{display:flex;flex-direction:row;justify-content:flex-end;gap:16px;margin-top:20px;padding:0 24px}#action.empty{display:none}#close{position:relative;left:4px;top:-4px;--sinch-global-color-icon:var(--sinch-comp-dialog-color-default-icon-initial)}</style><dialog id="dialog"><div id="header"><sinch-title id="caption" type="m" level="3" ellipsis></sinch-title><sinch-icon-button id="close" size="s"><sinch-icon id="icon-close" slot="icon" name="close"></sinch-icon></sinch-icon-button></div><div id="content"><sinch-stop-events events="close"><slot name="content"></slot></sinch-stop-events></div><div id="action"><sinch-stop-events events="close"><slot name="buttons"></slot></sinch-stop-events></div></dialog>';
7
7
  import { disableScroll, enableScroll } from './utils';
8
8
  const template = document.createElement('template');
@@ -26,31 +26,34 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
26
26
  }
27
27
  connectedCallback() {
28
28
  super.connectedCallback();
29
- this.setAttribute('role', 'dialog');
30
29
  this.#controller = new AbortController();
31
30
  const options = {
32
31
  signal: this.#controller.signal
33
32
  };
33
+ this.role = 'dialog';
34
34
  this.#$closeButton.addEventListener('click', this.#onCloseClick, options);
35
35
  this.#$dialog.addEventListener('mousedown', this.#onBackdropMouseDown, options);
36
36
  this.#$dialog.addEventListener('cancel', this.#onCancel, options);
37
37
  this.#$actionSlot.addEventListener('slotchange', this.#onActionSlotChange, options);
38
38
  this.addEventListener('-close', this.#onCloseReactHandler, options);
39
39
  this.#onActionSlotChange();
40
- if (getBooleanAttribute(this, 'open')) {
40
+ if (this.open) {
41
41
  this.#onExpand();
42
42
  }
43
43
  }
44
44
  disconnectedCallback() {
45
- this.#onCollapse();
46
45
  super.disconnectedCallback();
46
+ this.#onCollapse();
47
47
  this.#controller.abort();
48
48
  this.#controller = null;
49
49
  }
50
50
  static get observedAttributes() {
51
51
  return ['caption', 'open', 'close-aria-label'];
52
52
  }
53
- attributeChangedCallback(name, _, newVal) {
53
+ attributeChangedCallback(name, oldVal, newVal) {
54
+ if (isAttrEqual(oldVal, newVal)) {
55
+ return;
56
+ }
54
57
  switch (name) {
55
58
  case 'caption':
56
59
  {
@@ -83,6 +86,12 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
83
86
  get caption() {
84
87
  return getAttribute(this, 'caption', '');
85
88
  }
89
+ set open(isOpen) {
90
+ updateBooleanAttribute(this, 'open', isOpen);
91
+ }
92
+ get open() {
93
+ return getBooleanAttribute(this, 'open');
94
+ }
86
95
  get dialogRect() {
87
96
  return getRect(this.#$dialog);
88
97
  }
@@ -116,22 +125,19 @@ defineCustomElement('sinch-dialog', class extends NectaryElement {
116
125
  }));
117
126
  }
118
127
  #onExpand() {
119
- if (!this.isDomConnected || this.#isOpen()) {
128
+ if (!this.isDomConnected || this.#$dialog.open || !this.open) {
120
129
  return;
121
130
  }
122
131
  this.#$dialog.showModal();
123
132
  disableScroll();
124
133
  }
125
134
  #onCollapse() {
126
- if (!this.#isOpen()) {
135
+ if (!this.#$dialog.open) {
127
136
  return;
128
137
  }
129
138
  this.#$dialog.close?.();
130
139
  enableScroll();
131
140
  }
132
- #isOpen() {
133
- return getBooleanAttribute(this.#$dialog, 'open');
134
- }
135
141
  #onActionSlotChange = () => {
136
142
  setClass(this.#$actionWrapper, 'empty', this.#$actionSlot.assignedElements().length === 0);
137
143
  };
package/dialog/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { TRect, TSinchElementReact } from '../types';
2
2
  export type TSinchDialogCloseDetail = 'close' | 'escape' | 'backdrop';
3
3
  export type TSinchDialogElement = HTMLElement & {
4
+ /** Open/close state */
5
+ open: boolean;
4
6
  /** Dialog caption */
5
7
  caption: string;
6
8
  readonly dialogRect: TRect;
package/emoji/index.js CHANGED
@@ -15,6 +15,9 @@ defineCustomElement('sinch-emoji', class extends NectaryElement {
15
15
  super.connectedCallback();
16
16
  this.#updateChar();
17
17
  }
18
+ disconnectedCallback() {
19
+ super.disconnectedCallback();
20
+ }
18
21
  static get observedAttributes() {
19
22
  return ['char'];
20
23
  }
package/field/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineCustomElement, getAttribute, getBooleanAttribute, getFirstSlotElement, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
1
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getFirstSlotElement, isAttrEqual, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
2
2
  const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;width:100%}#bottom,#top{display:flex;align-items:baseline}#top{height:24px;margin-bottom:2px}#additional,#invalid,#label,#optional{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#label{font:var(--sinch-comp-field-font-label);color:var(--sinch-comp-field-color-default-label-initial)}#optional{flex:1;font:var(--sinch-comp-field-font-optional);color:var(--sinch-comp-field-color-default-optional-initial);text-align:right}#additional{flex:1;text-align:right;font:var(--sinch-comp-field-font-additional);color:var(--sinch-comp-field-color-default-additional-initial);line-height:20px;margin-top:2px}#additional:empty{display:none}#invalid{font:var(--sinch-comp-field-font-invalid);color:var(--sinch-comp-field-color-invalid-text-initial);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]) #label{color:var(--sinch-comp-field-color-disabled-label-initial)}:host([disabled]) #additional{color:var(--sinch-comp-field-color-disabled-additional-initial)}:host([disabled]) #optional{color:var(--sinch-comp-field-color-disabled-optional-initial)}</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;
@@ -42,9 +42,6 @@ defineCustomElement('sinch-field', class extends NectaryElement {
42
42
  return ['label', 'optionaltext', 'additionaltext', 'invalidtext', 'disabled'];
43
43
  }
44
44
  attributeChangedCallback(name, oldVal, newVal) {
45
- if (oldVal === newVal) {
46
- return;
47
- }
48
45
  switch (name) {
49
46
  case 'label':
50
47
  {
@@ -68,6 +65,9 @@ defineCustomElement('sinch-field', class extends NectaryElement {
68
65
  }
69
66
  case 'disabled':
70
67
  {
68
+ if (isAttrEqual(oldVal, newVal)) {
69
+ break;
70
+ }
71
71
  updateBooleanAttribute(this, name, isAttrTrue(newVal));
72
72
  break;
73
73
  }
@@ -1,6 +1,6 @@
1
1
  import '../text';
2
2
  import '../file-picker';
3
- import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, getReactEventHandler, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
3
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, getReactEventHandler, isAttrEqual, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
4
4
  const templateHTML = '<style>:host{display:block}#wrapper{position:relative;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;align-content:center;gap:8px;height:148px;min-width:148px;box-sizing:border-box;padding:16px;border-radius:var(--sinch-comp-file-drop-shape-radius);background-color:var(--sinch-comp-file-drop-color-default-background-initial)}#wrapper::after{content:"";position:absolute;top:0;left:0;right:0;bottom:0;border:1px dashed var(--sinch-comp-file-drop-color-default-border-initial);border-radius:var(--sinch-comp-file-drop-shape-radius);pointer-events:none}#placeholder{align-self:center;text-align:center;--sinch-global-color-text:var(--sinch-comp-file-drop-color-default-placeholder-initial);--sinch-comp-text-font:var(--sinch-comp-file-drop-font-placeholder)}:host([invalid]) #wrapper{background-color:var(--sinch-comp-file-drop-color-invalid-background-initial)}:host([invalid]) #wrapper::after{border-color:var(--sinch-comp-file-drop-color-invalid-border-initial);border-width:1px}#wrapper.drop::after{pointer-events:all}#wrapper.drop.valid{background-color:var(--sinch-comp-file-drop-color-default-background-active)}#wrapper.drop.valid::after{border-color:var(--sinch-comp-file-drop-color-default-border-active);border-width:2px}#wrapper.drop.valid>#placeholder{--sinch-global-color-text:var(--sinch-comp-file-drop-color-default-placeholder-active)}:host([disabled])>#wrapper>#placeholder{--sinch-global-color-text:var(--sinch-comp-file-drop-color-disabled-placeholder-initial)}:host([disabled])>#wrapper{background-color:var(--sinch-comp-file-drop-color-disabled-background-initial)}:host([disabled])>#wrapper::after{border-color:var(--sinch-comp-file-drop-color-disabled-border-initial);border-width:1px}</style><div id="wrapper"><sinch-text id="placeholder" type="m" aria-hidden="true"></sinch-text><sinch-file-picker id="file-picker"><slot></slot></sinch-file-picker></div>';
5
5
  import { areFilesAccepted, doFilesSatisfySize } from './utils';
6
6
  const template = document.createElement('template');
@@ -9,6 +9,7 @@ defineCustomElement('sinch-file-drop', class extends NectaryElement {
9
9
  #$filePicker;
10
10
  #$dropArea;
11
11
  #$placeholder;
12
+ #controller = null;
12
13
  constructor() {
13
14
  super();
14
15
  const shadowRoot = this.attachShadow();
@@ -18,30 +19,31 @@ defineCustomElement('sinch-file-drop', class extends NectaryElement {
18
19
  this.#$filePicker = shadowRoot.querySelector('#file-picker');
19
20
  }
20
21
  connectedCallback() {
21
- this.addEventListener('-change', this.#onChangeReactHandler);
22
- this.addEventListener('-invalid', this.#onInvalidReactHandler);
23
- this.addEventListener('dragenter', this.#onDragEnter);
24
- this.addEventListener('dragleave', this.#onDragLeave);
25
- this.addEventListener('dragover', this.#onDragOver);
26
- this.addEventListener('drop', this.#onDrop);
27
- this.#$filePicker.addEventListener('-change', this.#onFilePickerChange);
28
- this.#$filePicker.addEventListener('-invalid', this.#onFilePickerInvalid);
22
+ this.#controller = new AbortController();
23
+ const {
24
+ signal
25
+ } = this.#controller;
26
+ const options = {
27
+ signal
28
+ };
29
+ this.addEventListener('-change', this.#onChangeReactHandler, options);
30
+ this.addEventListener('-invalid', this.#onInvalidReactHandler, options);
31
+ this.addEventListener('dragenter', this.#onDragEnter, options);
32
+ this.addEventListener('dragleave', this.#onDragLeave, options);
33
+ this.addEventListener('dragover', this.#onDragOver, options);
34
+ this.addEventListener('drop', this.#onDrop, options);
35
+ this.#$filePicker.addEventListener('-change', this.#onFilePickerChange, options);
36
+ this.#$filePicker.addEventListener('-invalid', this.#onFilePickerInvalid, options);
29
37
  }
30
38
  disconnectedCallback() {
31
- this.removeEventListener('-change', this.#onChangeReactHandler);
32
- this.removeEventListener('-invalid', this.#onInvalidReactHandler);
33
- this.removeEventListener('dragenter', this.#onDragEnter);
34
- this.removeEventListener('dragleave', this.#onDragLeave);
35
- this.removeEventListener('dragover', this.#onDragOver);
36
- this.removeEventListener('drop', this.#onDrop);
37
- this.#$filePicker.removeEventListener('-change', this.#onFilePickerChange);
38
- this.#$filePicker.removeEventListener('-invalid', this.#onFilePickerInvalid);
39
+ this.#controller.abort();
40
+ this.#controller = null;
39
41
  }
40
42
  static get observedAttributes() {
41
43
  return ['accept', 'multiple', 'placeholder', 'disabled', 'invalid', 'size'];
42
44
  }
43
45
  attributeChangedCallback(name, oldVal, newVal) {
44
- if (newVal === oldVal) {
46
+ if (isAttrEqual(oldVal, newVal)) {
45
47
  return;
46
48
  }
47
49
  switch (name) {
@@ -62,8 +64,8 @@ defineCustomElement('sinch-file-drop', class extends NectaryElement {
62
64
  }
63
65
  case 'disabled':
64
66
  {
65
- updateBooleanAttribute(this, 'disabled', isAttrTrue(newVal));
66
67
  this.#setDragEffect(false);
68
+ updateBooleanAttribute(this, 'disabled', isAttrTrue(newVal));
67
69
  break;
68
70
  }
69
71
  case 'invalid':
@@ -1,4 +1,4 @@
1
- import { defineCustomElement, getAttribute, getBooleanAttribute, getFirstSlotElement, getIntegerAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute } from '../utils';
1
+ import { defineCustomElement, getAttribute, getBooleanAttribute, getFirstSlotElement, getIntegerAttribute, getReactEventHandler, isAttrEqual, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute } from '../utils';
2
2
  const templateHTML = '<style>:host{display:inline-block}::slotted(*){display:block}</style><slot></slot>';
3
3
  import { doFilesSatisfySize } from './utils';
4
4
  const template = document.createElement('template');
@@ -35,6 +35,23 @@ defineCustomElement('sinch-file-picker', class extends NectaryElement {
35
35
  static get observedAttributes() {
36
36
  return ['accept', 'multiple'];
37
37
  }
38
+ attributeChangedCallback(name, oldVal, newVal) {
39
+ switch (name) {
40
+ case 'multiple':
41
+ {
42
+ if (isAttrEqual(oldVal, newVal)) {
43
+ return;
44
+ }
45
+ updateBooleanAttribute(this.#$input, 'multiple', isAttrTrue(newVal));
46
+ break;
47
+ }
48
+ case 'accept':
49
+ {
50
+ updateAttribute(this.#$input, 'accept', newVal);
51
+ break;
52
+ }
53
+ }
54
+ }
38
55
  set multiple(isMultiple) {
39
56
  updateBooleanAttribute(this, 'multiple', isMultiple);
40
57
  }
@@ -53,23 +70,6 @@ defineCustomElement('sinch-file-picker', class extends NectaryElement {
53
70
  set size(value) {
54
71
  updateAttribute(this, 'size', value);
55
72
  }
56
- attributeChangedCallback(name, oldVal, newVal) {
57
- if (newVal === oldVal) {
58
- return;
59
- }
60
- switch (name) {
61
- case 'multiple':
62
- {
63
- updateBooleanAttribute(this.#$input, 'multiple', isAttrTrue(newVal));
64
- break;
65
- }
66
- case 'accept':
67
- {
68
- updateAttribute(this.#$input, 'accept', newVal);
69
- break;
70
- }
71
- }
72
- }
73
73
  #onTargetSlotChange = () => {
74
74
  this.#$target?.removeEventListener('-click', this.#onTargetClick);
75
75
  this.#$target = getFirstSlotElement(this.#$targetSlot, true);
@@ -9,6 +9,7 @@ template.innerHTML = templateHTML;
9
9
  defineCustomElement('sinch-file-status', class extends NectaryElement {
10
10
  #$filename;
11
11
  #$contentSlot;
12
+ #controller = null;
12
13
  constructor() {
13
14
  super();
14
15
  const shadowRoot = this.attachShadow();
@@ -18,12 +19,17 @@ defineCustomElement('sinch-file-status', class extends NectaryElement {
18
19
  }
19
20
  connectedCallback() {
20
21
  super.connectedCallback();
21
- this.#$contentSlot.addEventListener('slotchange', this.#onContentSlotChange);
22
+ this.#controller = new AbortController();
23
+ const options = {
24
+ signal: this.#controller.signal
25
+ };
26
+ this.#$contentSlot.addEventListener('slotchange', this.#onContentSlotChange, options);
22
27
  this.#onContentSlotChange();
23
28
  }
24
29
  disconnectedCallback() {
25
30
  super.disconnectedCallback();
26
- this.#$contentSlot.removeEventListener('slotchange', this.#onContentSlotChange);
31
+ this.#controller.abort();
32
+ this.#controller = null;
27
33
  }
28
34
  get type() {
29
35
  return getLiteralAttribute(this, typeValues, 'type');
@@ -41,9 +47,6 @@ defineCustomElement('sinch-file-status', class extends NectaryElement {
41
47
  return ['filename'];
42
48
  }
43
49
  attributeChangedCallback(name, oldVal, newVal) {
44
- if (oldVal === newVal) {
45
- return;
46
- }
47
50
  switch (name) {
48
51
  case 'filename':
49
52
  {
package/flag/index.js CHANGED
@@ -15,6 +15,9 @@ defineCustomElement('sinch-flag', class extends NectaryElement {
15
15
  super.connectedCallback();
16
16
  this.#updateCode();
17
17
  }
18
+ disconnectedCallback() {
19
+ super.disconnectedCallback();
20
+ }
18
21
  static get observedAttributes() {
19
22
  return ['code'];
20
23
  }
@@ -1,4 +1,5 @@
1
1
  import '../tooltip';
2
+ import '../icon';
2
3
  import type { TSinchHelpTooltipElement, TSinchHelpTooltipReact } from './types';
3
4
  declare global {
4
5
  namespace JSX {
@@ -1,6 +1,7 @@
1
1
  import '../tooltip';
2
+ import '../icon';
2
3
  import { defineCustomElement, getAttribute, getIntegerAttribute, getReactEventHandler, NectaryElement, updateAttribute, updateIntegerAttribute } from '../utils';
3
- const templateHTML = '<style>:host{display:contents;--sinch-comp-help-tooltip-color-icon:var(--sinch-sys-color-text-default)}#icon{height:18px;fill:var(--sinch-comp-help-tooltip-color-icon)}</style><sinch-tooltip type="fast"><svg id="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8Zm-1-4h2v2h-2v-2Zm1.61-9.96c-2.06-.3-3.88.97-4.43 2.79-.18.58.26 1.17.87 1.17h.2c.41 0 .74-.29.88-.67.32-.89 1.27-1.5 2.3-1.28.95.2 1.65 1.13 1.57 2.1-.1 1.34-1.62 1.63-2.45 2.88 0 .01-.01.01-.01.02-.01.02-.02.03-.03.05-.09.15-.18.32-.25.5-.01.03-.03.05-.04.08-.01.02-.01.04-.02.07-.12.34-.2.75-.2 1.25h2c0-.42.11-.77.28-1.07.02-.03.03-.06.05-.09.08-.14.18-.27.28-.39.01-.01.02-.03.03-.04.1-.12.21-.23.33-.34.96-.91 2.26-1.65 1.99-3.56-.24-1.74-1.61-3.21-3.35-3.47Z"/></svg></sinch-tooltip>';
4
+ const templateHTML = '<style>:host{display:contents}#icon{--sinch-global-size-icon:18px}</style><sinch-tooltip type="fast"><sinch-icon id="icon" name="help"></sinch-icon></sinch-tooltip>';
4
5
  const template = document.createElement('template');
5
6
  template.innerHTML = templateHTML;
6
7
  defineCustomElement('sinch-help-tooltip', class extends NectaryElement {
@@ -23,10 +23,7 @@ defineCustomElement('sinch-horizontal-stepper', class extends NectaryElement {
23
23
  static get observedAttributes() {
24
24
  return ['index'];
25
25
  }
26
- attributeChangedCallback(name, oldVal, newVal) {
27
- if (oldVal === newVal) {
28
- return;
29
- }
26
+ attributeChangedCallback(name) {
30
27
  switch (name) {
31
28
  case 'index':
32
29
  {
@@ -22,9 +22,6 @@ defineCustomElement('sinch-horizontal-stepper-item', class extends NectaryElemen
22
22
  return ['label', 'description', 'data-index'];
23
23
  }
24
24
  attributeChangedCallback(name, oldVal, newVal) {
25
- if (oldVal === newVal) {
26
- return;
27
- }
28
25
  switch (name) {
29
26
  case 'label':
30
27
  {
@@ -1,5 +1,5 @@
1
1
  import '../tooltip';
2
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, subscribeContext, updateAttribute, updateBooleanAttribute, updateLiteralAttribute, Context, getAttribute } from '../utils';
2
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, NectaryElement, subscribeContext, updateAttribute, updateBooleanAttribute, updateLiteralAttribute, Context, getAttribute, isAttrEqual } from '../utils';
3
3
  import { DEFAULT_SIZE, sizeExValues } from '../utils/size';
4
4
  const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#button{display:block;position:relative;width:var(--sinch-local-size);height:var(--sinch-local-size);cursor:pointer;user-select:none;contain:size;--sinch-global-color-icon:var(--sinch-local-color-icon)}:host([disabled]) #button{cursor:initial}:host([data-size="l"]) #button{--sinch-local-size:var(--sinch-comp-icon-button-size-container-l);--sinch-global-size-icon:var(--sinch-comp-icon-button-size-icon-l);--sinch-local-shape-radius:var(--sinch-comp-icon-button-shape-radius-size-l)}#button,:host([data-size="m"]) #button{--sinch-local-size:var(--sinch-comp-icon-button-size-container-m);--sinch-global-size-icon:var(--sinch-comp-icon-button-size-icon-m);--sinch-local-shape-radius:var(--sinch-comp-icon-button-shape-radius-size-m)}:host([data-size="s"]) #button{--sinch-local-size:var(--sinch-comp-icon-button-size-container-s);--sinch-global-size-icon:var(--sinch-comp-icon-button-size-icon-s);--sinch-local-shape-radius:var(--sinch-comp-icon-button-shape-radius-size-s)}:host([data-size=xs]) #button{--sinch-local-size:var(--sinch-comp-icon-button-size-container-xs);--sinch-global-size-icon:var(--sinch-comp-icon-button-size-icon-xs);--sinch-local-shape-radius:var(--sinch-comp-icon-button-shape-radius-size-xs)}:host([type=primary]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-primary-default-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-primary-default-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-primary-default-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-primary-initial);--sinch-local-color-outline:var(--sinch-comp-icon-button-color-primary-default-outline-focus)}:host([type=secondary]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-secondary-default-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-secondary-default-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-secondary-default-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-secondary-initial);--sinch-local-color-outline:var(--sinch-comp-icon-button-color-secondary-default-outline-focus)}#button,:host([type=tertiary]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-tertiary-default-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-tertiary-default-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-tertiary-default-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-tertiary-initial);--sinch-local-color-outline:var(--sinch-comp-icon-button-color-tertiary-default-outline-focus)}:host([type=primary][disabled]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-primary-disabled-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-primary-disabled-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-primary-disabled-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-primary-disabled)}:host([type=secondary][disabled]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-secondary-disabled-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-secondary-disabled-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-secondary-disabled-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-secondary-disabled)}:host([disabled]) #button,:host([type=tertiary][disabled]) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-tertiary-disabled-background-initial);--sinch-local-color-border:var(--sinch-comp-icon-button-color-tertiary-disabled-border-initial);--sinch-local-color-icon:var(--sinch-comp-icon-button-color-tertiary-disabled-icon-initial);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-tertiary-disabled)}:host([type=primary]:focus) #button{--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-primary-focus)}:host([type=secondary]:focus) #button{--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-secondary-focus)}:host(:focus),:host([type=tertiary]:focus) #button{--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-tertiary-focus)}:host([type=primary]:not([disabled]):hover) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-primary-default-background-hover);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-primary-hover)}:host([type=secondary]:not([disabled]):hover) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-secondary-default-background-hover);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-secondary-hover)}:host(:not([disabled]):hover) #button,:host([type=tertiary]:not([disabled]):active) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-tertiary-default-background-hover);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-tertiary-hover)}:host([type=primary]:not([disabled]):active) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-primary-default-background-active);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-primary-active)}:host([type=secondary]:not([disabled]):active) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-secondary-default-background-active);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-secondary-active)}:host(:not([disabled]):active) #button,:host([type=tertiary]:not([disabled]):active) #button{--sinch-local-color-background:var(--sinch-comp-icon-button-color-tertiary-default-background-active);--sinch-local-shadow:var(--sinch-comp-icon-button-shadow-tertiary-active)}#button::before{content:"";position:absolute;inset:0;background-color:var(--sinch-local-color-background);border:1px solid var(--sinch-local-color-border);border-radius:var(--sinch-local-shape-radius);box-shadow:var(--sinch-local-shadow);pointer-events:none}:host(:focus-visible) #button::after{content:"";position:absolute;inset:-3px;border:2px solid var(--sinch-local-color-outline);border-radius:calc(var(--sinch-local-shape-radius) + 3px);pointer-events:none}#content{position:relative;display:flex;align-items:center;justify-content:center;width:100%;height:100%}</style><sinch-tooltip id="tooltip"><div id="button"><div id="content" inert><slot name="icon"></slot></div></div></sinch-tooltip>';
5
5
  import { typeValues } from './utils';
@@ -50,14 +50,13 @@ defineCustomElement('sinch-icon-button', class extends NectaryElement {
50
50
  return ['size', 'disabled', 'aria-label', 'data-size'];
51
51
  }
52
52
  attributeChangedCallback(name, oldVal, newVal) {
53
- if (oldVal === newVal) {
54
- return;
55
- }
56
53
  switch (name) {
57
54
  case 'disabled':
58
55
  {
59
- const isDisabled = isAttrTrue(newVal);
60
- updateBooleanAttribute(this, 'disabled', isDisabled);
56
+ if (isAttrEqual(oldVal, newVal)) {
57
+ return;
58
+ }
59
+ updateBooleanAttribute(this, 'disabled', isAttrTrue(newVal));
61
60
  break;
62
61
  }
63
62
  case 'aria-label':
package/input/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Context, defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrTrue, isElementFocused, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
1
+ import { Context, defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, getReactEventHandler, isAttrEqual, isAttrTrue, isElementFocused, NectaryElement, setClass, subscribeContext, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
2
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-wrapper{position:relative;flex:1;flex-basis:0;min-width:0;align-self:stretch}#input{all:initial;width:100%;height:100%;padding:0 12px;box-sizing:border-box;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}#input:disabled{color:var(--sinch-comp-input-color-disabled-text-initial);-webkit-text-fill-color:var(--sinch-comp-input-color-disabled-text-initial)}#input-mask{display:none;position:absolute;inset:0;padding:0 12px;pointer-events:none;color:var(--sinch-comp-input-color-default-text-placeholder);white-space:pre;height:fit-content;margin:auto 0;overflow:hidden}#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}:host([disabled]) #border{border-color:var(--sinch-comp-input-color-disabled-border-initial)}#input-wrapper:focus-within+#border{border-color:var(--sinch-comp-input-color-default-border-focus);border-width:2px}#input-mask,:host([mask]) #input{font:var(--sinch-sys-font-body-monospace-m)}:host([mask]) #input-mask{display:block}:host([invalid]:not([disabled])) #input-wrapper:not(:focus-within)+#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-wrapper>#input,#icon-wrapper.empty~#input-wrapper>#input-mask{padding-left:12px}#icon-wrapper:not(.empty)~#input-wrapper>#input,#icon-wrapper:not(.empty)~#input-wrapper>#input-mask{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><div id="input-wrapper"><div id="input-mask"></div><input id="input" type="text"/></div><div id="border"></div><div id="right"><slot name="right"></slot></div></div>';
4
4
  import { deleteContentBackward, deleteContentForward, getMaskSymbols, inputTypes, insertText, beginMaskedComposition, endMaskedComposition, splitValueAndMask, getMergedValueSliced, insertFromPaste } from './utils';
@@ -42,7 +42,7 @@ defineCustomElement('sinch-input', class extends NectaryElement {
42
42
  }
43
43
  connectedCallback() {
44
44
  super.connectedCallback();
45
- this.setAttribute('role', 'textbox');
45
+ this.role = 'textbox';
46
46
  if (this.#controller === null) {
47
47
  this.#controller = new AbortController();
48
48
  }
@@ -82,9 +82,6 @@ defineCustomElement('sinch-input', class extends NectaryElement {
82
82
  return ['type', 'value', 'placeholder', 'mask', 'invalid', 'disabled', 'size', 'autocomplete', 'data-size', 'aria-label'];
83
83
  }
84
84
  attributeChangedCallback(name, oldVal, newVal) {
85
- if (oldVal === newVal) {
86
- return;
87
- }
88
85
  switch (name) {
89
86
  case 'type':
90
87
  {
@@ -134,6 +131,9 @@ defineCustomElement('sinch-input', class extends NectaryElement {
134
131
  }
135
132
  case 'invalid':
136
133
  {
134
+ if (isAttrEqual(oldVal, newVal)) {
135
+ return;
136
+ }
137
137
  const isInvalid = isAttrTrue(newVal);
138
138
  updateExplicitBooleanAttribute(this, 'aria-invalid', isInvalid);
139
139
  updateBooleanAttribute(this, name, isInvalid);
@@ -141,6 +141,9 @@ defineCustomElement('sinch-input', class extends NectaryElement {
141
141
  }
142
142
  case 'disabled':
143
143
  {
144
+ if (isAttrEqual(oldVal, newVal)) {
145
+ return;
146
+ }
144
147
  const isDisabled = isAttrTrue(newVal);
145
148
  this.#$input.disabled = isDisabled;
146
149
  updateBooleanAttribute(this, name, isDisabled);
package/link/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import '../icon';
2
- import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, NectaryElement, isAttrTrue, getReactEventHandler } from '../utils';
2
+ import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, NectaryElement, isAttrTrue, getReactEventHandler, isAttrEqual } from '../utils';
3
3
  const templateHTML = '<style>:host{display:inline}a{font:var(--sinch-comp-link-default-font-initial);font-size:inherit;line-height:inherit;text-decoration:var(--sinch-comp-link-default-text-decoration-initial);color:var(--sinch-comp-link-color-default-text-initial);border-radius:.5em;white-space:nowrap;--sinch-global-color-icon:var(--sinch-comp-link-color-default-icon-initial)}a:hover{text-decoration:var(--sinch-comp-link-default-text-decoration-hover);color:var(--sinch-comp-link-color-default-text-hover);--sinch-global-color-icon:var(--sinch-comp-link-color-default-icon-hover)}a:focus-visible{outline:2px solid var(--sinch-comp-link-color-default-outline-focus);outline-offset:2px}:host([standalone]){display:block}:host([standalone]) a{display:block;font:var(--sinch-comp-link-standalone-font-initial);font-size:inherit;line-height:inherit;text-decoration:none;width:fit-content}#external-icon,#standalone-icon{display:none;height:1em}#standalone-icon-prefix{display:none}:host([external]:not([standalone])) #external-icon{display:inline-block;margin-right:.2em;vertical-align:-.2em;--sinch-global-size-icon:1em}:host([standalone][external]) #external-icon{display:inline-block;vertical-align:-.4em;margin-right:-.4em;--sinch-global-size-icon:1.5em}:host([standalone]) #standalone-icon-prefix{display:inline}:host([standalone]:not([external])) #standalone-icon{display:inline-block;vertical-align:-.4em;--sinch-global-size-icon:1.5em}:host([disabled]) a{color:var(--sinch-comp-link-color-disabled-text-initial);pointer-events:none;cursor:initial;text-decoration:var(--sinch-comp-link-default-text-decoration-disabled);--sinch-global-color-icon:var(--sinch-comp-link-color-disabled-icon-initial)}#content{white-space:var(--sinch-global-text-white-space,normal)}</style><a referrerpolicy="no-referer" aria-hidden="true"><sinch-icon id="external-icon" name="open_in_new"></sinch-icon><span id="content"></span><span id="standalone-icon-prefix">&nbsp;</span><sinch-icon id="standalone-icon" name="arrow_forward"></sinch-icon></a>';
4
4
  const template = document.createElement('template');
5
5
  template.innerHTML = templateHTML;
@@ -34,7 +34,7 @@ defineCustomElement('sinch-link', class extends NectaryElement {
34
34
  return ['text', 'href', 'external', 'standalone', 'disabled'];
35
35
  }
36
36
  attributeChangedCallback(name, oldVal, newVal) {
37
- if (oldVal === newVal) {
37
+ if (isAttrEqual(oldVal, newVal)) {
38
38
  return;
39
39
  }
40
40
  switch (name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
@@ -1,6 +1,6 @@
1
1
  import '../icon';
2
2
  import { defineCustomElement, updateAttribute, getIntegerAttribute, setClass, NectaryElement, getRect, getReactEventHandler, isTargetEqual, getTargetIndexInParent } from '../utils';
3
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{display:flex;justify-content:center;gap:8px;width:100%;height:var(--sinch-local-size);--sinch-local-size:24px}button{all:initial;position:relative;display:flex;justify-content:center;align-items:center;width:var(--sinch-local-size);height:var(--sinch-local-size);cursor:pointer;border-radius:var(--sinch-comp-pagination-shape-radius);--sinch-global-color-icon:var(--sinch-comp-pagination-color-default-icon-default)}button:focus-visible::before{content:"";position:absolute;inset:-4px;border:2px solid var(--sinch-comp-pagination-color-default-outline-focus);border-radius:calc(var(--sinch-comp-pagination-shape-radius) + 4px);pointer-events:none}button:disabled{--sinch-global-color-icon:var(--sinch-comp-pagination-color-disabled-icon-initial);cursor:initial}button:enabled:hover{background-color:var(--sinch-comp-pagination-color-default-background-hover)}button>*{display:block;overflow:hidden;pointer-events:none}.page{font:var(--sinch-comp-pagination-font-default-page-number);color:var(--sinch-comp-pagination-color-default-text-initial);background-color:var(--sinch-comp-pagination-color-default-background-initial)}.page.dots>span{display:none}.page.dots::after{content:"..."}.page.active{font:var(--sinch-comp-pagination-font-checked-page-number);background-color:var(--sinch-comp-pagination-color-checked-background-initial);pointer-events:none;cursor:initial}.page.active:hover{background-color:var(--sinch-comp-pagination-color-checked-background-hover)}.page.hidden{display:none}#left,#right{--sinch-icon-size:24px}</style><div id="wrapper"><button id="left"><sinch-icon id="icon-left" name="keyboard_arrow_left"></sinch-icon></button><button class="page"><span>1</span></button><button class="page active"><span>2</span></button><button class="page"><span>3</span></button><button class="page"><span>4</span></button><button class="page"><span>5</span></button><button class="page dots"><span>6</span></button><button class="page"><span>20</span></button><button id="right"><sinch-icon id="icon-right" name="keyboard_arrow_right"></sinch-icon></button></div>';
3
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{display:flex;justify-content:center;gap:8px;width:100%;height:var(--sinch-local-size);--sinch-local-size:24px}button{all:initial;position:relative;display:flex;justify-content:center;align-items:center;min-width:var(--sinch-local-size);height:var(--sinch-local-size);cursor:pointer;border-radius:var(--sinch-comp-pagination-shape-radius);user-select:none;--sinch-global-color-icon:var(--sinch-comp-pagination-color-default-icon-default)}button:focus-visible::before{content:"";position:absolute;inset:-3px;border:2px solid var(--sinch-comp-pagination-color-default-outline-focus);border-radius:calc(var(--sinch-comp-pagination-shape-radius) + 3px);pointer-events:none}button:disabled{--sinch-global-color-icon:var(--sinch-comp-pagination-color-disabled-icon-initial);cursor:initial}button:enabled:hover{background-color:var(--sinch-comp-pagination-color-default-background-hover)}button>*{display:block;overflow:hidden;pointer-events:none}.page{font:var(--sinch-comp-pagination-font-default-page-number);color:var(--sinch-comp-pagination-color-default-text-initial);background-color:var(--sinch-comp-pagination-color-default-background-initial);padding:0 4px;box-sizing:border-box}.page.dots>span{display:none}.page.dots::after{content:"..."}.page.active{font:var(--sinch-comp-pagination-font-checked-page-number);background-color:var(--sinch-comp-pagination-color-checked-background-initial);pointer-events:none;cursor:initial}.page.active:hover{background-color:var(--sinch-comp-pagination-color-checked-background-hover)}.page.hidden{display:none}#left,#right{--sinch-icon-size:24px}</style><div id="wrapper"><button id="left"><sinch-icon id="icon-left" name="keyboard_arrow_left"></sinch-icon></button><button class="page"><span>1</span></button><button class="page active"><span>2</span></button><button class="page"><span>3</span></button><button class="page"><span>4</span></button><button class="page"><span>5</span></button><button class="page dots"><span>6</span></button><button class="page"><span>20</span></button><button id="right"><sinch-icon id="icon-right" name="keyboard_arrow_right"></sinch-icon></button></div>';
4
4
  const NUM_BUTTONS = 7;
5
5
  const MIDDLE_BTN_INDEX = Math.floor(NUM_BUTTONS / 2);
6
6
  const FIRST_BTN_INDEX = 0;
@@ -35,13 +35,9 @@ defineCustomElement('sinch-pagination', class extends NectaryElement {
35
35
  static get observedAttributes() {
36
36
  return ['max', 'value'];
37
37
  }
38
- attributeChangedCallback(name, _) {
38
+ attributeChangedCallback(name) {
39
39
  switch (name) {
40
40
  case 'value':
41
- {
42
- this.#onValueChange();
43
- break;
44
- }
45
41
  case 'max':
46
42
  {
47
43
  this.#onValueChange();
package/pop/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, subscribeContext, isTargetEqual } from '../utils';
1
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame, isElementFocused, updateIntegerAttribute, getIntegerAttribute, getFirstFocusableElement, getFirstSlotElement, Context, subscribeContext, isTargetEqual, isAttrEqual } 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
3
  import { disableOverscroll, enableOverscroll, orientationValues } from './utils';
4
4
  const template = document.createElement('template');
@@ -101,13 +101,14 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
101
101
  return getRect(this.#$dialog);
102
102
  }
103
103
  attributeChangedCallback(name, oldVal, newVal) {
104
- if (oldVal === newVal) {
104
+ if (isAttrEqual(oldVal, newVal)) {
105
105
  return;
106
106
  }
107
107
  switch (name) {
108
108
  case 'open':
109
109
  {
110
- if (isAttrTrue(newVal)) {
110
+ const shouldOpen = isAttrTrue(newVal);
111
+ if (shouldOpen) {
111
112
  requestAnimationFrame(() => {
112
113
  if (this.isDomConnected && getBooleanAttribute(this, 'open')) {
113
114
  this.#onExpand();
@@ -116,7 +117,7 @@ defineCustomElement('sinch-pop', class extends NectaryElement {
116
117
  } else {
117
118
  this.#onCollapse();
118
119
  }
119
- updateBooleanAttribute(this, 'open', isAttrTrue(newVal));
120
+ updateBooleanAttribute(this, 'open', shouldOpen);
120
121
  break;
121
122
  }
122
123
  case 'orientation':
package/popover/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import '../pop';
2
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateBooleanAttribute, NectaryElement, updateAttribute, getReactEventHandler, isAttrTrue, setClass, rectOverlap, subscribeContext } from '../utils';
2
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, updateLiteralAttribute, updateBooleanAttribute, NectaryElement, updateAttribute, getReactEventHandler, isAttrTrue, setClass, rectOverlap, subscribeContext, isAttrEqual } 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
4
  import { getPopOrientation, orientationValues } from './utils';
5
5
  const TIP_SIZE = 16;
@@ -40,7 +40,7 @@ defineCustomElement('sinch-popover', class extends NectaryElement {
40
40
  return ['orientation', 'open', 'modal', 'tip'];
41
41
  }
42
42
  attributeChangedCallback(name, oldVal, newVal) {
43
- if (oldVal === newVal) {
43
+ if (isAttrEqual(oldVal, newVal)) {
44
44
  return;
45
45
  }
46
46
  switch (name) {