@nectary/components 0.40.0 → 0.41.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 (74) hide show
  1. package/accordion-item/index.js +4 -0
  2. package/action-menu/index.js +11 -13
  3. package/action-menu-option/index.js +2 -1
  4. package/button/index.js +5 -1
  5. package/button/types.d.ts +1 -1
  6. package/checkbox/index.js +4 -0
  7. package/chip/index.js +18 -15
  8. package/chip/types.d.ts +3 -4
  9. package/color-menu/index.d.ts +1 -0
  10. package/color-menu/index.js +37 -51
  11. package/color-menu/types.d.ts +5 -6
  12. package/color-menu/utils.d.ts +1 -0
  13. package/color-menu/utils.js +15 -0
  14. package/color-swatch/index.js +8 -7
  15. package/color-swatch/types.d.ts +3 -4
  16. package/colors.json +14 -10
  17. package/date-picker/index.js +1 -1
  18. package/field/index.js +22 -5
  19. package/file-drop/index.js +1 -1
  20. package/file-picker/index.js +1 -1
  21. package/help-tooltip/index.js +10 -27
  22. package/icon-button/index.d.ts +1 -0
  23. package/icon-button/index.js +26 -15
  24. package/icon-button/types.d.ts +16 -2
  25. package/input/index.js +4 -0
  26. package/link/index.js +5 -1
  27. package/package.json +1 -1
  28. package/pagination/index.js +4 -0
  29. package/pop/index.d.ts +11 -0
  30. package/pop/index.js +429 -0
  31. package/pop/types.d.ts +35 -0
  32. package/pop/utils.d.ts +7 -0
  33. package/pop/utils.js +18 -0
  34. package/popover/index.d.ts +1 -0
  35. package/popover/index.js +91 -230
  36. package/popover/types.d.ts +8 -1
  37. package/popover/utils.d.ts +5 -0
  38. package/popover/utils.js +17 -1
  39. package/radio-option/index.js +4 -0
  40. package/segment-collapse/index.js +4 -0
  41. package/segmented-control-option/index.js +5 -1
  42. package/segmented-icon-control-option/index.js +5 -1
  43. package/select-button/index.js +6 -1
  44. package/select-menu/index.js +12 -13
  45. package/tabs-option/index.js +4 -0
  46. package/tag/index.js +13 -15
  47. package/tag/types.d.ts +3 -4
  48. package/textarea/index.js +4 -0
  49. package/theme.css +76 -10
  50. package/tile-control-option/index.js +5 -1
  51. package/time-picker/index.js +1 -1
  52. package/toggle/index.js +5 -1
  53. package/tooltip/index.d.ts +2 -0
  54. package/tooltip/index.js +160 -17
  55. package/tooltip/types.d.ts +13 -0
  56. package/tooltip/utils.d.ts +5 -0
  57. package/tooltip/utils.js +25 -1
  58. package/types.d.ts +0 -7
  59. package/utils/animation.d.ts +17 -0
  60. package/utils/animation.js +142 -0
  61. package/utils/colors.d.ts +4 -9
  62. package/utils/colors.js +4 -120
  63. package/utils/context.d.ts +15 -0
  64. package/utils/context.js +57 -0
  65. package/utils/index.d.ts +5 -9
  66. package/utils/index.js +49 -50
  67. package/dropdown-checkbox-option/index.d.ts +0 -11
  68. package/dropdown-checkbox-option/index.js +0 -74
  69. package/dropdown-checkbox-option/types.d.ts +0 -15
  70. package/dropdown-radio-option/index.d.ts +0 -11
  71. package/dropdown-radio-option/index.js +0 -74
  72. package/dropdown-radio-option/types.d.ts +0 -15
  73. package/dropdown-radio-option/types.js +0 -1
  74. /package/{dropdown-checkbox-option → pop}/types.js +0 -0
package/tag/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import '../text';
2
- import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass } from '../utils';
3
- import { assertColorNameValue, colorMap, colorNameValues, NO_COLOR } from '../utils/colors';
4
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{display:flex;flex-direction:row;align-items:center;gap:4px;width:100%;height:24px;padding:0 12px;border-radius:12px;color:var(--sinch-color-text-default);box-sizing:border-box;--sinch-color-icon:var(--sinch-color-stormy-500);--sinch-size-icon:16px}#wrapper.no-color{background-color:var(--sinch-color-snow-500)}#wrapper.inverted{color:var(--sinch-color-text-inverted);--sinch-color-icon:var(--sinch-color-snow-100)}#text{flex:1}:host([small]:not([small=false])) #wrapper{height:20px;border-radius:10px;padding:0 8px}::slotted(*){margin-left:-4px}:host([small]:not([small=false])) ::slotted(*){margin-left:-2px}</style><div id="wrapper"><slot name="icon"></slot><sinch-text id="text" type="xs" ellipsis></sinch-text></div>';
2
+ import { defineCustomElement, getBooleanAttribute, getAttribute, updateBooleanAttribute, updateAttribute, NectaryElement } from '../utils';
3
+ import { NO_COLOR } from '../utils/colors';
4
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{display:flex;flex-direction:row;align-items:center;gap:4px;width:100%;height:24px;padding:0 9px;border-radius:12px;background-color:var(--sinch-color-snow-500);color:var(--sinch-color-text-default);box-sizing:border-box;--sinch-color-icon:var(--sinch-color-stormy-500);--sinch-size-icon:16px}#text{flex:1}:host([small]:not([small=false])) #wrapper{height:20px;border-radius:10px;padding:0 8px}::slotted(*){margin-left:-4px}</style><div id="wrapper"><slot name="icon"></slot><sinch-text id="text" type="xs" ellipsis></sinch-text></div>';
5
5
  const template = document.createElement('template');
6
6
  template.innerHTML = templateHTML;
7
7
  defineCustomElement('sinch-tag', class extends NectaryElement {
@@ -21,11 +21,11 @@ defineCustomElement('sinch-tag', class extends NectaryElement {
21
21
  }
22
22
 
23
23
  get color() {
24
- return getLiteralAttribute(this, colorNameValues, 'color', null);
24
+ return getAttribute(this, 'color');
25
25
  }
26
26
 
27
27
  set color(value) {
28
- updateLiteralAttribute(this, colorNameValues, 'color', value);
28
+ updateAttribute(this, 'color', value);
29
29
  }
30
30
 
31
31
  get text() {
@@ -52,7 +52,6 @@ defineCustomElement('sinch-tag', class extends NectaryElement {
52
52
  switch (name) {
53
53
  case 'color':
54
54
  {
55
- assertColorNameValue(newVal);
56
55
  this.#updateColor();
57
56
  break;
58
57
  }
@@ -67,17 +66,16 @@ defineCustomElement('sinch-tag', class extends NectaryElement {
67
66
 
68
67
  #updateColor() {
69
68
  const colorName = this.color ?? NO_COLOR;
70
- const {
71
- value,
72
- isInverted
73
- } = colorMap[colorName];
74
69
 
75
- if (value !== NO_COLOR) {
76
- this.#$wrapper.style.backgroundColor = `var(--sinch-color-${value})`;
70
+ if (colorName !== NO_COLOR) {
71
+ this.#$wrapper.style.setProperty('background-color', `var(--sinch-color-map-${colorName}-bg)`);
72
+ this.#$wrapper.style.setProperty('color', `var(--sinch-color-map-${colorName}-fg)`);
73
+ this.#$wrapper.style.setProperty('--sinch-color-icon', `var(--sinch-color-map-${colorName}-fg)`);
74
+ } else {
75
+ this.#$wrapper.style.removeProperty('background-color');
76
+ this.#$wrapper.style.removeProperty('color');
77
+ this.#$wrapper.style.removeProperty('--sinch-color-icon');
77
78
  }
78
-
79
- setClass(this.#$wrapper, 'no-color', value === NO_COLOR);
80
- setClass(this.#$wrapper, 'inverted', isInverted);
81
79
  }
82
80
 
83
81
  });
package/tag/types.d.ts CHANGED
@@ -1,16 +1,15 @@
1
1
  import type { TSinchElementReact } from '../types';
2
- import type { TSinchColorName } from '../utils/colors';
3
2
  export declare type TSinchTagElement = HTMLElement & {
4
3
  /** Text */
5
4
  text: string;
6
5
  /** Color, gray by default */
7
- color: TSinchColorName | null;
6
+ color: string | null;
8
7
  /** Small */
9
8
  small: boolean;
10
9
  /** Text */
11
10
  setAttribute(name: 'text', value: string): void;
12
11
  /** Color, gray by default */
13
- setAttribute(name: 'color', value: TSinchColorName): void;
12
+ setAttribute(name: 'color', value: string): void;
14
13
  /** Small */
15
14
  setAttribute(name: 'small', value: ''): void;
16
15
  };
@@ -18,7 +17,7 @@ export declare type TSinchTagReact = TSinchElementReact<TSinchTagElement> & {
18
17
  /** Text */
19
18
  text: string;
20
19
  /** Color, gray by default */
21
- color?: TSinchColorName;
20
+ color?: string;
22
21
  /** Small */
23
22
  small?: boolean;
24
23
  };
package/textarea/index.js CHANGED
@@ -173,6 +173,10 @@ defineCustomElement('sinch-textarea', class extends NectaryElement {
173
173
  this.#$input.selectionDirection = value;
174
174
  }
175
175
 
176
+ get focusable() {
177
+ return true;
178
+ }
179
+
176
180
  focus() {
177
181
  this.#$input.focus();
178
182
  }
package/theme.css CHANGED
@@ -47,24 +47,28 @@
47
47
  --sinch-color-raspberry-200: #FBD5D5;
48
48
  --sinch-color-raspberry-100: #FFE8E4;
49
49
  --sinch-color-raspberry-50: #FFF6F5;
50
+ --sinch-color-night-700: #1223a1;
50
51
  --sinch-color-night-400: #3247E9;
51
52
  --sinch-color-night-200: #D1D6FA;
52
- --sinch-color-aqua-400: #3AA7EA;
53
- --sinch-color-aqua-200: #A1D5F5;
53
+ --sinch-color-grass-700: #006602;
54
54
  --sinch-color-grass-400: #39B93D;
55
55
  --sinch-color-grass-200: #B4E4B5;
56
+ --sinch-color-dirt-700: #363230;
56
57
  --sinch-color-dirt-400: #828282;
57
58
  --sinch-color-dirt-200: #E0DDDC;
58
- --sinch-color-berry-400: #EF5858;
59
- --sinch-color-berry-200: #FFB8B8;
59
+ --sinch-color-candy-700: #981b77;
60
60
  --sinch-color-candy-400: #E467C3;
61
61
  --sinch-color-candy-200: #F6CBEA;
62
+ --sinch-color-mud-700: #6d4e46;
62
63
  --sinch-color-mud-400: #8B6559;
63
64
  --sinch-color-mud-200: #D7C6C1;
65
+ --sinch-color-orange-700: #974302;
64
66
  --sinch-color-orange-400: #FF8C34;
65
67
  --sinch-color-orange-200: #FFD4B3;
68
+ --sinch-color-bolt-700: #805500;
66
69
  --sinch-color-bolt-400: #FFBE3C;
67
70
  --sinch-color-bolt-200: #FFE6B3;
71
+ --sinch-color-violet-700: #4F1B98;
68
72
  --sinch-color-violet-400: #9E67E4;
69
73
  --sinch-color-violet-200: #DECBF6;
70
74
  --sinch-color-error-700: #882024;
@@ -79,12 +83,12 @@
79
83
  --sinch-color-warning-700: #9C2E00;
80
84
  --sinch-color-warning-500: #F35B1C;
81
85
  --sinch-color-warning-200: #FFE8D6;
82
- --sinch-color-skin-tone-10: #FFCC4D;
83
- --sinch-color-skin-tone-20: #F7DECE;
84
- --sinch-color-skin-tone-30: #F3D2A2;
85
- --sinch-color-skin-tone-40: #D4AB88;
86
- --sinch-color-skin-tone-50: #AF7E57;
87
- --sinch-color-skin-tone-60: #7C533E;
86
+ --sinch-color-skin-tone-0: #FFCC4D;
87
+ --sinch-color-skin-tone-10: #F7DECE;
88
+ --sinch-color-skin-tone-20: #F3D2A2;
89
+ --sinch-color-skin-tone-30: #D4AB88;
90
+ --sinch-color-skin-tone-40: #AF7E57;
91
+ --sinch-color-skin-tone-50: #7C533E;
88
92
  --sinch-color-transparent: transparent;
89
93
 
90
94
  /* Contextual */
@@ -110,6 +114,68 @@
110
114
  --sinch-color-border-focus: var(--sinch-color-ocean-500);
111
115
  --sinch-color-border-invalid: var(--sinch-color-raspberry-600);
112
116
 
117
+ /* Color Map */
118
+ --sinch-color-map-light-blue-bg: var(--sinch-color-night-200);
119
+ --sinch-color-map-light-blue-fg: var(--sinch-color-night-700);
120
+ --sinch-color-map-light-brown-bg: var(--sinch-color-mud-200);
121
+ --sinch-color-map-light-brown-fg: var(--sinch-color-mud-700);
122
+ --sinch-color-map-light-gray-bg: var(--sinch-color-dirt-200);
123
+ --sinch-color-map-light-gray-fg: var(--sinch-color-dirt-700);
124
+ --sinch-color-map-light-green-bg: var(--sinch-color-grass-200);
125
+ --sinch-color-map-light-green-fg: var(--sinch-color-grass-700);
126
+ --sinch-color-map-light-orange-bg: var(--sinch-color-orange-200);
127
+ --sinch-color-map-light-orange-fg: var(--sinch-color-orange-700);
128
+ --sinch-color-map-light-pink-bg: var(--sinch-color-candy-200);
129
+ --sinch-color-map-light-pink-fg: var(--sinch-color-candy-700);
130
+ --sinch-color-map-light-violet-bg: var(--sinch-color-violet-200);
131
+ --sinch-color-map-light-violet-fg: var(--sinch-color-violet-700);
132
+ --sinch-color-map-light-yellow-bg: var(--sinch-color-bolt-200);
133
+ --sinch-color-map-light-yellow-fg: var(--sinch-color-bolt-700);
134
+ --sinch-color-map-dark-blue-bg: var(--sinch-color-night-700);
135
+ --sinch-color-map-dark-blue-fg: var(--sinch-color-night-200);
136
+ --sinch-color-map-dark-brown-bg: var(--sinch-color-mud-700);
137
+ --sinch-color-map-dark-brown-fg: var(--sinch-color-mud-200);
138
+ --sinch-color-map-dark-gray-bg: var(--sinch-color-dirt-700);
139
+ --sinch-color-map-dark-gray-fg: var(--sinch-color-dirt-200);
140
+ --sinch-color-map-dark-green-bg: var(--sinch-color-grass-700);
141
+ --sinch-color-map-dark-green-fg: var(--sinch-color-grass-200);
142
+ --sinch-color-map-dark-orange-bg: var(--sinch-color-orange-700);
143
+ --sinch-color-map-dark-orange-fg: var(--sinch-color-orange-200);
144
+ --sinch-color-map-dark-pink-bg: var(--sinch-color-candy-700);
145
+ --sinch-color-map-dark-pink-fg: var(--sinch-color-candy-200);
146
+ --sinch-color-map-dark-violet-bg: var(--sinch-color-violet-700);
147
+ --sinch-color-map-dark-violet-fg: var(--sinch-color-violet-200);
148
+ --sinch-color-map-dark-yellow-bg: var(--sinch-color-bolt-700);
149
+ --sinch-color-map-dark-yellow-fg: var(--sinch-color-bolt-200);
150
+ --sinch-color-map-blue-bg: var(--sinch-color-night-400);
151
+ --sinch-color-map-blue-fg: var(--sinch-color-snow-100);
152
+ --sinch-color-map-brown-bg: var(--sinch-color-mud-400);
153
+ --sinch-color-map-brown-fg: var(--sinch-color-snow-100);
154
+ --sinch-color-map-gray-bg: var(--sinch-color-dirt-400);
155
+ --sinch-color-map-gray-fg: var(--sinch-color-snow-100);
156
+ --sinch-color-map-green-bg: var(--sinch-color-grass-400);
157
+ --sinch-color-map-green-fg: var(--sinch-color-stormy-500);
158
+ --sinch-color-map-orange-bg: var(--sinch-color-orange-400);
159
+ --sinch-color-map-orange-fg: var(--sinch-color-stormy-500);
160
+ --sinch-color-map-pink-bg: var(--sinch-color-candy-400);
161
+ --sinch-color-map-pink-fg: var(--sinch-color-stormy-500);
162
+ --sinch-color-map-violet-bg: var(--sinch-color-violet-400);
163
+ --sinch-color-map-violet-fg: var(--sinch-color-stormy-500);
164
+ --sinch-color-map-yellow-bg: var(--sinch-color-bolt-400);
165
+ --sinch-color-map-yellow-fg: var(--sinch-color-stormy-500);
166
+ --sinch-color-map-skin-tone-0-bg: var(--sinch-color-skin-tone-0);
167
+ --sinch-color-map-skin-tone-0-fg: var(--sinch-color-stormy-500);
168
+ --sinch-color-map-skin-tone-10-bg: var(--sinch-color-skin-tone-10);
169
+ --sinch-color-map-skin-tone-10-fg: var(--sinch-color-stormy-500);
170
+ --sinch-color-map-skin-tone-20-bg: var(--sinch-color-skin-tone-20);
171
+ --sinch-color-map-skin-tone-20-fg: var(--sinch-color-stormy-500);
172
+ --sinch-color-map-skin-tone-30-bg: var(--sinch-color-skin-tone-30);
173
+ --sinch-color-map-skin-tone-30-fg: var(--sinch-color-stormy-500);
174
+ --sinch-color-map-skin-tone-40-bg: var(--sinch-color-skin-tone-40);
175
+ --sinch-color-map-skin-tone-40-fg: var(--sinch-color-stormy-500);
176
+ --sinch-color-map-skin-tone-50-bg: var(--sinch-color-skin-tone-50);
177
+ --sinch-color-map-skin-tone-50-fg: var(--sinch-color-snow-100);
178
+
113
179
  /* Typography */
114
180
  --sinch-font-family: "Gilroy", "Arial", "sans-serif";
115
181
  --sinch-font-weight-emphasized: 600;
@@ -1,5 +1,5 @@
1
1
  import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}button{all:initial;position:relative;display:flex;flex-direction:column;justify-content:space-between;box-sizing:border-box;width:88px;height:88px;padding:16px 8px 8px;border-radius:var(--sinch-shape-radius-l);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-leve-1);cursor:pointer}:host([data-small])>button{width:64px;height:64px;border-radius:var(--sinch-shape-radius-m);padding:12px 4px 4px}button::before{content:"";position:absolute;left:0;top:0;bottom:0;right:0;border:1px solid var(--sinch-color-snow-700);border-radius:var(--sinch-shape-radius-l);pointer-events:none}:host([data-small])>button::before{border-radius:var(--sinch-shape-radius-m)}button:focus-visible::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-aqua-400);border-radius:calc(var(--sinch-shape-radius-l) + 4px);pointer-events:none}:host([data-small])>button:focus-visible::after{border-radius:calc(var(--sinch-shape-radius-m) + 4px)}@supports not selector(:focus-visible){button:focus::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-aqua-400);border-radius:calc(var(--sinch-shape-radius-l) + 4px)}:host([data-small])>button:focus::after{border-radius:calc(var(--sinch-shape-radius-m) + 4px)}}button:enabled:hover{background-color:var(--sinch-color-tropical-100)}button:enabled:hover::before{border-color:var(--sinch-color-tropical-100)}:host([data-checked])>button:enabled,button:enabled:active{box-shadow:var(--sinch-elevation-level-0)}:host([data-checked])>button:enabled::before,button:enabled:active::before{border-width:2px;border-color:var(--sinch-color-stormy-500)}button:disabled{cursor:initial}button:disabled>#icon,button:disabled>#text{opacity:.5}#text{display:inline-block;min-height:20px;text-align:center;font:var(--sinch-font-text-xs);color:var(--sinch-color-stormy-500);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#icon{width:32px;height:32px;align-self:center;--sinch-size-icon:32px;--sinch-color-icon:var(--sinch-color-stormy-500)}:host([data-small]) #icon{width:24px;height:24px;--sinch-size-icon:24px}button>*{pointer-events:none}</style><button><div id="icon"><slot name="icon"></slot></div><span id="text"></span></button>';
2
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}button{all:initial;position:relative;display:flex;flex-direction:column;justify-content:space-between;box-sizing:border-box;width:88px;height:88px;padding:16px 8px 8px;border-radius:var(--sinch-shape-radius-l);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-leve-1);cursor:pointer}:host([data-small])>button{width:64px;height:64px;border-radius:var(--sinch-shape-radius-m);padding:12px 4px 4px}button::before{content:"";position:absolute;left:0;top:0;bottom:0;right:0;border:1px solid var(--sinch-color-snow-700);border-radius:var(--sinch-shape-radius-l);pointer-events:none}:host([data-small])>button::before{border-radius:var(--sinch-shape-radius-m)}button:focus-visible::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-shape-radius-l) + 4px);pointer-events:none}:host([data-small])>button:focus-visible::after{border-radius:calc(var(--sinch-shape-radius-m) + 4px)}@supports not selector(:focus-visible){button:focus::after{position:absolute;content:"";left:50%;top:50%;transform:translate(-50%,-50%);width:100%;height:100%;padding:2px;border:2px solid var(--sinch-color-border-focus);border-radius:calc(var(--sinch-shape-radius-l) + 4px)}:host([data-small])>button:focus::after{border-radius:calc(var(--sinch-shape-radius-m) + 4px)}}button:enabled:hover{background-color:var(--sinch-color-tropical-100)}button:enabled:hover::before{border-color:var(--sinch-color-tropical-100)}:host([data-checked])>button:enabled,button:enabled:active{box-shadow:var(--sinch-elevation-level-0)}:host([data-checked])>button:enabled::before,button:enabled:active::before{border-width:2px;border-color:var(--sinch-color-stormy-500)}button:disabled{cursor:initial}button:disabled>#icon,button:disabled>#text{opacity:.5}#text{display:inline-block;min-height:20px;text-align:center;font:var(--sinch-font-text-xs);color:var(--sinch-color-stormy-500);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#icon{width:32px;height:32px;align-self:center;--sinch-size-icon:32px;--sinch-color-icon:var(--sinch-color-stormy-500)}:host([data-small]) #icon{width:24px;height:24px;--sinch-size-icon:24px}button>*{pointer-events:none}</style><button><div id="icon"><slot name="icon"></slot></div><span id="text"></span></button>';
3
3
  const template = document.createElement('template');
4
4
  template.innerHTML = templateHTML;
5
5
  defineCustomElement('sinch-tile-control-option', class extends NectaryElement {
@@ -85,6 +85,10 @@ defineCustomElement('sinch-tile-control-option', class extends NectaryElement {
85
85
  return getAttribute(this, 'text', '');
86
86
  }
87
87
 
88
+ get focusable() {
89
+ return true;
90
+ }
91
+
88
92
  focus() {
89
93
  this.#$button.focus();
90
94
  }
@@ -5,7 +5,7 @@ import '../icons/arrow-drop-down';
5
5
  import '../segmented-control';
6
6
  import '../segmented-control-option';
7
7
  import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, getRect, isAttrTrue, NectaryElement, setClass, updateAttribute, updateBooleanAttribute } from '../utils';
8
- const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-font-title-xl);line-height:48px}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-color-stormy-500)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;font:var(--sinch-font-text-s);line-height:28px;text-align:center;color:var(--sinch-color-text-default);top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-24{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-minute{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-hour-12:hover,.digit-hour-24:hover,.digit-minute:hover{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected,.digit-hour-24.selected,.digit-minute.selected{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected{font-size:16px}.digit-hour-24.selected{font-size:16px}.digit-minute.selected{font-size:16px}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-color-stormy-500)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-color-tropical-500)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0;--sinch-size-icon:24px}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-hours::before,#header-minutes::before{content:"";display:none;position:absolute;border-radius:12px;left:50%;top:50%;width:100%;height:100%;transform:translate(-50%,-50%);padding:1px;border:1px solid var(--sinch-color-aqua-400)}#header-hours-down,#header-hours-up,#header-minutes-down,#header-minutes-up{display:none;position:absolute;left:50%;transform:translateX(-50%)}#header-hours-up,#header-minutes-up{top:-18px}#header-hours-down,#header-minutes-down{bottom:-18px}#header-hours:focus #header-hours-down,#header-hours:focus #header-hours-up,#header-hours:focus::before{display:block}#header-minutes:focus #header-minutes-down,#header-minutes:focus #header-minutes-up,#header-minutes:focus::before{display:block}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" tabindex="0" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span><sinch-icon-arrow-drop-up id="header-hours-up"></sinch-icon-arrow-drop-up><sinch-icon-arrow-drop-down id="header-hours-down"></sinch-icon-arrow-drop-down></div><div id="header-colon">&colon;</div><div id="header-minutes" tabindex="0" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span><sinch-icon-arrow-drop-up id="header-minutes-up"></sinch-icon-arrow-drop-up><sinch-icon-arrow-drop-down id="header-minutes-down"></sinch-icon-arrow-drop-down></div><sinch-icon-button id="submit" small aria-label="Submit"><sinch-icon-done slot="icon"></sinch-icon-done></sinch-icon-button></div><div id="picker" aria-hidden="true"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour"></div><div id="needle-minute"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
8
+ const templateHTML = '<style>:host{display:block;outline:0}#wrapper{display:flex;flex-direction:column;width:248px;padding:16px;box-sizing:border-box;gap:16px}#header{position:relative;width:100%;height:48px;font:var(--sinch-font-title-xl);line-height:48px}#footer{display:flex;justify-content:center;width:100%;height:32px}#picker{position:relative;width:216px;height:216px;border-radius:50%;box-sizing:border-box;border:1px solid var(--sinch-color-stormy-500)}#picker-hours,#picker-minutes{position:absolute;left:0;top:0;width:100%;height:100%;border-radius:50%;pointer-events:none;user-select:none}.digit-hour-12,.digit-hour-24,.digit-minute{position:absolute;width:28px;height:28px;font:var(--sinch-font-text-s);line-height:28px;text-align:center;color:var(--sinch-color-text-default);top:calc(50% - 14px);left:calc(50% - 14px);z-index:1;cursor:pointer}.digit-hour-24{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-minute{font:var(--sinch-font-text-xs);line-height:28px;color:var(--sinch-color-text-muted)}.digit-hour-12:hover,.digit-hour-24:hover,.digit-minute:hover{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected,.digit-hour-24.selected,.digit-minute.selected{color:var(--sinch-color-tropical-500)}.digit-hour-12.selected{font-size:16px}.digit-hour-24.selected{font-size:16px}.digit-minute.selected{font-size:16px}#picker-touch{position:absolute;left:0;top:0;width:100%;height:100%;cursor:pointer;border-radius:50%}#needle-hour,#needle-minute,#picker-touch::after{background-color:var(--sinch-color-stormy-500)}#needle-hour,#needle-minute{position:absolute;transform-origin:bottom center;transform:rotate(0);bottom:50%;height:50px;transition-duration:.25s;transition-timing-function:ease-in-out;transition-property:transform height;z-index:2}@media (prefers-reduced-motion){#needle-hour,#needle-minute{transition:none}}#needle-hour{width:4px;left:calc(50% - 2px);border-radius:2px}#needle-minute{width:2px;left:calc(50% - 1px);border-radius:1px}#needle-minute:not(.selected)::after{content:"";position:absolute;transform:translateX(-50%);left:0;top:-16px;width:4px;height:4px;border-radius:50%;background-color:var(--sinch-color-tropical-500)}#picker-touch::after{content:"";position:absolute;top:50%;left:50%;width:12px;height:12px;border-radius:50%;transform:translate(-50%,-50%)}#header-hours,#header-minutes{position:absolute;padding:0 4px;width:50px;outline:0;--sinch-size-icon:24px}#header-hours{right:calc(50% + 8px);text-align:right}#header-minutes{left:calc(50% + 8px)}#header-hours::before,#header-minutes::before{content:"";display:none;position:absolute;border-radius:12px;left:50%;top:50%;width:100%;height:100%;transform:translate(-50%,-50%);padding:1px;border:1px solid var(--sinch-color-border-focus)}#header-hours-down,#header-hours-up,#header-minutes-down,#header-minutes-up{display:none;position:absolute;left:50%;transform:translateX(-50%)}#header-hours-up,#header-minutes-up{top:-18px}#header-hours-down,#header-minutes-down{bottom:-18px}#header-hours:focus #header-hours-down,#header-hours:focus #header-hours-up,#header-hours:focus::before{display:block}#header-minutes:focus #header-minutes-down,#header-minutes:focus #header-minutes-up,#header-minutes:focus::before{display:block}#header-colon{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%)}#submit{position:absolute;right:0;top:50%;transform:translateY(-50%)}:host([ampm]) .digit-hour-24{display:none}:host(:not([ampm])) #footer{display:none}</style><div id="wrapper"><div id="header"><div id="header-hours" tabindex="0" role="meter" aria-valuemin="0" aria-valuemax="23" aria-valuenow="0" aria-valuetext="0"><span>00</span><sinch-icon-arrow-drop-up id="header-hours-up"></sinch-icon-arrow-drop-up><sinch-icon-arrow-drop-down id="header-hours-down"></sinch-icon-arrow-drop-down></div><div id="header-colon">&colon;</div><div id="header-minutes" tabindex="0" role="meter" aria-valuemin="0" aria-valuemax="59" aria-valuenow="0" aria-valuetext="0"><span>00</span><sinch-icon-arrow-drop-up id="header-minutes-up"></sinch-icon-arrow-drop-up><sinch-icon-arrow-drop-down id="header-minutes-down"></sinch-icon-arrow-drop-down></div><sinch-icon-button id="submit" small aria-label="Submit"><sinch-icon-done slot="icon"></sinch-icon-done></sinch-icon-button></div><div id="picker" aria-hidden="true"><div id="picker-hours"></div><div id="picker-minutes"></div><div id="picker-touch"><div id="needle-hour"></div><div id="needle-minute"></div></div></div><div id="footer"><sinch-segmented-control id="ampm"><sinch-segmented-control-option value="am" text="AM" aria-label="AM"></sinch-segmented-control-option><sinch-segmented-control-option value="pm" text="PM" aria-label="PM"></sinch-segmented-control-option></sinch-segmented-control></div></div>';
9
9
  import { getNeedleRotationDeg, getShortestCssDeg, hourToIndex, parseTime, stringifyHour, stringifyHourFace, stringifyMinute, stringifyTime } from './utils';
10
10
  const template = document.createElement('template');
11
11
  template.innerHTML = templateHTML;
package/toggle/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineCustomElement, getAttribute, getBooleanAttribute, getReactEventHandler, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
2
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;box-sizing:border-box;width:100%;height:32px}:host([small]:not([small=false])) #wrapper{font:var(--sinch-font-small-text)}#input{all:initial;display:block;position:absolute;left:0;top:6px;width:40px;height:20px;cursor:pointer;pointer-events:initial}#input:disabled{cursor:initial}:host([small]:not([small=false])) #input{width:32px;height:16px;top:8px}#input:focus-visible::after{position:absolute;content:"";left:-4px;top:-4px;right:-4px;bottom:-4px;border:2px solid var(--sinch-color-aqua-400);border-radius:18px;pointer-events:none}:host([small]:not([small=false])) #input:focus-visible::after{border-radius:14px}@supports not selector(:focus-visible){#input:focus::after{position:absolute;content:"";left:-4px;top:-4px;right:-4px;bottom:-4px;border:2px solid var(--sinch-color-aqua-400);border-radius:18px;pointer-events:none}:host([small]:not([small=false])) #input:focus::after{border-radius:14px}}#knob-container{position:relative;box-sizing:border-box;width:40px;height:20px;border-radius:10px;pointer-events:none;padding:2px;background-color:var(--sinch-color-stormy-100);overflow:hidden}:host([small]:not([small=false])) #knob-container{width:32px;height:16px;border-radius:8px}#input:checked~#knob-container{background-color:var(--sinch-color-tropical-500)}#knob{position:relative;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-1);transform:translateX(0);transition:transform .1s ease-in-out}:host([small]:not([small=false])) #knob{width:12px;height:12px}#input:checked~#knob-container>#knob{transform:translateX(20px)}:host([small]:not([small=false])) #input:checked~#knob-container>#knob{transform:translateX(16px)}#knob::after,#knob::before{font:var(--sinch-font-body);color:var(--sinch-color-snow-100);text-transform:uppercase;font-size:8px;line-height:16px;display:none;position:absolute;top:0;padding:0 3px}#knob::before{content:"on";right:100%}#knob::after{content:"off";left:100%}:host([labeled]:not([labeled=false]):is(:not([small]),[small=false])) #knob::after,:host([labeled]:not([labeled=false]):is(:not([small]),[small=false])) #knob::before{display:block}@media (prefers-reduced-motion){#knob{transition:none}}#label{flex:1;align-self:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding-left:8px;font:var(--sinch-font-body);color:var(--sinch-color-text-default)}#label:empty{display:none}#input:disabled~#label{color:var(--sinch-color-stormy-200)}#input:disabled~#knob-container{background-color:var(--sinch-color-snow-700)}#input:checked:disabled~#knob-container{background-color:var(--sinch-color-tropical-100)}</style><div id="wrapper"><input id="input" type="checkbox"><div id="knob-container"><div id="knob"></div></div><label id="label" for="input"></label></div>';
2
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#wrapper{position:relative;display:flex;flex-direction:row;align-items:center;box-sizing:border-box;width:100%;height:32px}:host([small]:not([small=false])) #wrapper{font:var(--sinch-font-small-text)}#input{all:initial;display:block;position:absolute;left:0;top:6px;width:40px;height:20px;cursor:pointer;pointer-events:initial}#input:disabled{cursor:initial}:host([small]:not([small=false])) #input{width:32px;height:16px;top:8px}#input:focus-visible::after{position:absolute;content:"";left:-4px;top:-4px;right:-4px;bottom:-4px;border:2px solid var(--sinch-color-border-focus);border-radius:18px;pointer-events:none}:host([small]:not([small=false])) #input:focus-visible::after{border-radius:14px}@supports not selector(:focus-visible){#input:focus::after{position:absolute;content:"";left:-4px;top:-4px;right:-4px;bottom:-4px;border:2px solid var(--sinch-color-border-focus);border-radius:18px;pointer-events:none}:host([small]:not([small=false])) #input:focus::after{border-radius:14px}}#knob-container{position:relative;box-sizing:border-box;width:40px;height:20px;border-radius:10px;pointer-events:none;padding:2px;background-color:var(--sinch-color-stormy-100);overflow:hidden}:host([small]:not([small=false])) #knob-container{width:32px;height:16px;border-radius:8px}#input:checked~#knob-container{background-color:var(--sinch-color-tropical-500)}#knob{position:relative;box-sizing:border-box;width:16px;height:16px;border-radius:50%;background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-1);transform:translateX(0);transition:transform .1s ease-in-out}:host([small]:not([small=false])) #knob{width:12px;height:12px}#input:checked~#knob-container>#knob{transform:translateX(20px)}:host([small]:not([small=false])) #input:checked~#knob-container>#knob{transform:translateX(16px)}#knob::after,#knob::before{font:var(--sinch-font-body);color:var(--sinch-color-snow-100);text-transform:uppercase;font-size:8px;line-height:16px;display:none;position:absolute;top:0;padding:0 3px}#knob::before{content:"on";right:100%}#knob::after{content:"off";left:100%}:host([labeled]:not([labeled=false]):is(:not([small]),[small=false])) #knob::after,:host([labeled]:not([labeled=false]):is(:not([small]),[small=false])) #knob::before{display:block}@media (prefers-reduced-motion){#knob{transition:none}}#label{flex:1;align-self:center;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding-left:8px;font:var(--sinch-font-body);color:var(--sinch-color-text-default)}#label:empty{display:none}#input:disabled~#label{color:var(--sinch-color-stormy-200)}#input:disabled~#knob-container{background-color:var(--sinch-color-snow-700)}#input:checked:disabled~#knob-container{background-color:var(--sinch-color-tropical-100)}</style><div id="wrapper"><input id="input" type="checkbox"><div id="knob-container"><div id="knob"></div></div><label id="label" for="input"></label></div>';
3
3
  const template = document.createElement('template');
4
4
  template.innerHTML = templateHTML;
5
5
  defineCustomElement('sinch-toggle', class extends NectaryElement {
@@ -111,6 +111,10 @@ defineCustomElement('sinch-toggle', class extends NectaryElement {
111
111
  }
112
112
  }
113
113
 
114
+ get focusable() {
115
+ return true;
116
+ }
117
+
114
118
  focus() {
115
119
  this.#$input.focus();
116
120
  }
@@ -1,3 +1,5 @@
1
+ import '../text';
2
+ import '../pop';
1
3
  import type { TSinchTooltipElement, TSinchTooltipReact } from './types';
2
4
  declare global {
3
5
  namespace JSX {
package/tooltip/index.js CHANGED
@@ -1,18 +1,81 @@
1
- import { attrValueToPixels, defineCustomElement, getBooleanAttribute, getIntegerAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, getRect, NectaryElement } from '../utils';
2
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle}#wrapper{position:relative;display:block}#tooltip{position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;opacity:0;transition:opacity .2s linear .2s}@media (prefers-reduced-motion){#tooltip{transition:none}}:host(:hover) #tooltip{opacity:1}#text{position:absolute;padding:2px 6px;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-600);width:max-content;border-radius:var(--sinch-shape-radius-xs);top:-8px;left:50%;transform:translateX(-50%) translateY(-100%);word-break:break-word}#arrow{position:absolute;top:-8px;left:50%;transform:translateX(-50%);fill:var(--sinch-color-snow-600)}:host([orientation=top-left]) #text{transform:translateX(-80%) translateY(-100%)}:host([orientation=top-right]) #text{transform:translateX(-20%) translateY(-100%)}:host([orientation=bottom-right]) #text{top:calc(100% + 8px);transform:translateX(-20%)}:host([orientation=bottom-left]) #text{top:calc(100% + 8px);transform:translateX(-80%)}:host([orientation=bottom]) #text{top:calc(100% + 8px);transform:translateX(-50%)}:host([orientation^=bottom]) #arrow{top:calc(100% + 4px);transform:translateX(-50%) rotate(180deg)}:host([orientation=left]) #text{left:unset;right:calc(100% + 8px);top:50%;transform:translateY(-50%)}:host([orientation=left]) #arrow{left:-11px;top:50%;transform:translateY(-50%) rotate(270deg)}:host([orientation=right]) #text{left:calc(100% + 8px);top:50%;transform:translateY(-50%)}:host([orientation=right]) #arrow{top:50%;left:calc(100% + 2px);transform:translateY(-50%) rotate(90deg)}:host([inverted]:not([inverted=false])) #text{color:var(--sinch-color-text-inverted);background-color:var(--sinch-color-stormy-500)}:host([inverted]:not([inverted=false])) #arrow{fill:var(--sinch-color-stormy-500)}::slotted(*){display:block}</style><div id="wrapper"><slot></slot><div id="tooltip"><div id="text"></div><svg id="arrow" width="9" height="4" aria-hidden="true"><path d="m4.5 4 4-4h-8l4 4Z"/></svg></div></div>';
3
- import { orientationValues } from './utils';
1
+ import '../text';
2
+ import '../pop';
3
+ import { defineCustomElement, getBooleanAttribute, getAttribute, getLiteralAttribute, updateBooleanAttribute, updateAttribute, updateLiteralAttribute, NectaryElement, setClass, rectOverlap } from '../utils';
4
+ import { TooltipState } from '../utils/animation';
5
+ const templateHTML = '<style>:host{display:contents}#content-wrapper{padding-bottom:8px}#content{position:relative;display:block;max-width:300px;padding:2px 6px;box-sizing:border-box;color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-600);border-radius:var(--sinch-shape-radius-xs);pointer-events:none;opacity:0}:host([orientation=left]) #content-wrapper{padding-bottom:0;padding-right:8px}:host([orientation=right]) #content-wrapper{padding-bottom:0;padding-left:8px}:host([orientation^=bottom]) #content-wrapper{padding-bottom:0;padding-top:8px}#text{word-break:break-word;pointer-events:none}#tip{position:absolute;left:50%;top:100%;transform:translateX(-50%) rotate(0);transform-origin:top center;fill:var(--sinch-color-snow-600);pointer-events:none}#tip.hidden{display:none}:host([orientation=left]) #tip{transform:translateX(-50%) rotate(270deg);top:50%;left:100%}:host([orientation=right]) #tip{transform:translateX(-50%) rotate(90deg);top:50%;left:0}:host([orientation^=bottom]) #tip{transform:translateX(-50%) rotate(180deg);top:0}:host([inverted]:not([inverted=false])) #content{background-color:var(--sinch-color-stormy-500);color:var(--sinch-color-text-inverted)}:host([inverted]:not([inverted=false])) #tip{fill:var(--sinch-color-stormy-500)}</style><sinch-pop id="pop"><slot id="target" slot="target"></slot><div id="content-wrapper" slot="content"><div id="content"><sinch-text id="text" type="s"></sinch-text><svg id="tip" width="8" height="4" aria-hidden="true"><path d="m4 4 4-4h-8l4 4Z"/></svg></div></div></sinch-pop>';
6
+ import { assertOrientation, getPopOrientation, orientationValues } from './utils';
7
+ const TIP_SIZE = 8;
8
+ const SHOW_DELAY = 1000;
9
+ const HIDE_DELAY = 100;
10
+ const ANIMATION_DURATION = 100;
4
11
  const template = document.createElement('template');
5
12
  template.innerHTML = templateHTML;
6
13
  defineCustomElement('sinch-tooltip', class extends NectaryElement {
14
+ #$pop;
15
+ #$tooltipText;
16
+ #$content;
17
+ #$contentWrapper;
18
+ #$tip;
19
+ #$target;
20
+ #controller = null;
21
+ #tooltipState;
22
+ #animation = null;
23
+ #shouldReduceMotion = false;
24
+
7
25
  constructor() {
8
26
  super();
9
27
  const shadowRoot = this.attachShadow();
10
28
  shadowRoot.appendChild(template.content.cloneNode(true));
11
- this.$tooltipText = shadowRoot.querySelector('#text');
29
+ this.#$pop = shadowRoot.querySelector('#pop');
30
+ this.#$tooltipText = shadowRoot.querySelector('#text');
31
+ this.#$content = shadowRoot.querySelector('#content');
32
+ this.#$contentWrapper = shadowRoot.querySelector('#content-wrapper');
33
+ this.#$tip = shadowRoot.querySelector('#tip');
34
+ this.#$target = shadowRoot.querySelector('#target');
35
+ this.#shouldReduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
36
+ this.#tooltipState = new TooltipState({
37
+ showDelay: SHOW_DELAY,
38
+ hideDelay: this.#shouldReduceMotion ? HIDE_DELAY + ANIMATION_DURATION : HIDE_DELAY,
39
+ hideAnimationDuration: this.#shouldReduceMotion ? 0 : ANIMATION_DURATION,
40
+ onShow: this.#onShow,
41
+ onHideStart: this.#onHideStart,
42
+ onHideEnd: this.#onHideEnd
43
+ });
44
+ }
45
+
46
+ connectedCallback() {
47
+ this.#controller = new AbortController();
48
+ const {
49
+ signal
50
+ } = this.#controller;
51
+ this.#$pop.addEventListener('-close', this.#onPopClose, {
52
+ signal
53
+ });
54
+ this.#$target.addEventListener('mousedown', this.#onMouseDown, {
55
+ signal
56
+ });
57
+ this.#$target.addEventListener('mouseenter', this.#onMouseEnter, {
58
+ signal
59
+ });
60
+ this.#$target.addEventListener('mouseleave', this.#onMouseLeave, {
61
+ signal
62
+ });
63
+ this.#$contentWrapper.addEventListener('mouseenter', this.#onMouseEnter, {
64
+ signal
65
+ });
66
+ this.#$contentWrapper.addEventListener('mouseleave', this.#onMouseLeave, {
67
+ signal
68
+ });
69
+ updateAttribute(this.#$pop, 'orientation', getPopOrientation(this.orientation));
70
+ }
71
+
72
+ disconnectedCallback() {
73
+ this.#controller.abort();
74
+ this.#tooltipState.destroy();
12
75
  }
13
76
 
14
77
  static get observedAttributes() {
15
- return ['text', 'width'];
78
+ return ['text', 'orientation'];
16
79
  }
17
80
 
18
81
  get text() {
@@ -23,14 +86,6 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
23
86
  updateAttribute(this, 'text', value);
24
87
  }
25
88
 
26
- get width() {
27
- return getIntegerAttribute(this, 'width', null);
28
- }
29
-
30
- set width(value) {
31
- updateAttribute(this, 'width', value);
32
- }
33
-
34
89
  get inverted() {
35
90
  return getBooleanAttribute(this, 'inverted');
36
91
  }
@@ -47,24 +102,112 @@ defineCustomElement('sinch-tooltip', class extends NectaryElement {
47
102
  updateLiteralAttribute(this, orientationValues, 'orientation', value);
48
103
  }
49
104
 
105
+ get footprintRect() {
106
+ return this.#$pop.footprintRect;
107
+ }
108
+
50
109
  get tooltipRect() {
51
- return getRect(this.$tooltipText);
110
+ return this.#$pop.popoverRect;
52
111
  }
53
112
 
54
113
  attributeChangedCallback(name, _, newVal) {
55
114
  switch (name) {
56
115
  case 'text':
57
116
  {
58
- this.$tooltipText.textContent = newVal;
117
+ this.#$tooltipText.textContent = newVal;
59
118
  break;
60
119
  }
61
120
 
62
- case 'width':
121
+ case 'orientation':
63
122
  {
64
- this.$tooltipText.style.maxWidth = attrValueToPixels(newVal);
123
+ assertOrientation(newVal);
124
+ updateAttribute(this.#$pop, 'orientation', getPopOrientation(newVal));
125
+
126
+ if (this.#isOpen()) {
127
+ this.#resetTipOrientation();
128
+ this.#updateTipOrientation();
129
+ }
130
+
65
131
  break;
66
132
  }
67
133
  }
68
134
  }
69
135
 
136
+ #onMouseDown = () => {
137
+ this.#tooltipState.interrupt();
138
+ };
139
+ #onPopClose = () => {
140
+ this.#tooltipState.destroy();
141
+ };
142
+ #onMouseEnter = () => {
143
+ this.#tooltipState.show();
144
+ };
145
+ #onMouseLeave = e => {
146
+ if (!this.#isOpen() || e.relatedTarget !== this.#$contentWrapper && e.relatedTarget !== this.#$target) {
147
+ this.#tooltipState.hide();
148
+ }
149
+ };
150
+ #onShow = () => {
151
+ updateBooleanAttribute(this.#$pop, 'open', true);
152
+ requestAnimationFrame(this.#updateTipOrientation);
153
+
154
+ if (this.#animation !== null) {
155
+ this.#animation.updatePlaybackRate(1);
156
+ this.#animation.play();
157
+ } else {
158
+ this.#animation = this.#$content.animate({
159
+ opacity: [0, 1]
160
+ }, {
161
+ duration: this.#shouldReduceMotion ? 0 : ANIMATION_DURATION,
162
+ iterations: 1,
163
+ fill: 'forwards'
164
+ });
165
+ }
166
+ };
167
+ #onHideStart = () => {
168
+ this.#animation.updatePlaybackRate(-1);
169
+ this.#animation.play();
170
+ };
171
+ #onHideEnd = () => {
172
+ this.#animation.finish();
173
+ this.#resetTipOrientation();
174
+ updateBooleanAttribute(this.#$pop, 'open', false);
175
+ };
176
+
177
+ #resetTipOrientation() {
178
+ this.#$tip.style.top = '';
179
+ this.#$tip.style.left = '';
180
+ }
181
+
182
+ #updateTipOrientation = () => {
183
+ const orient = this.orientation;
184
+ const targetRect = this.#$pop.footprintRect;
185
+ const contentRect = this.#$content.getBoundingClientRect();
186
+ const diffX = targetRect.x - contentRect.x;
187
+ const diffY = targetRect.y - contentRect.y;
188
+
189
+ if (orient === 'left' || orient === 'right') {
190
+ const yPos = Math.max(TIP_SIZE, Math.min(diffY + targetRect.height / 2, contentRect.height - TIP_SIZE));
191
+ this.#$tip.style.top = `${yPos}px`;
192
+ } else {
193
+ let xPos = Math.max(TIP_SIZE, Math.min(diffX + targetRect.width / 2, contentRect.width - TIP_SIZE));
194
+
195
+ if (orient === 'bottom-left' || orient === 'top-left') {
196
+ xPos = Math.max(xPos, contentRect.width * 0.75);
197
+ }
198
+
199
+ if (orient === 'bottom-right' || orient === 'top-right') {
200
+ xPos = Math.min(xPos, contentRect.width * 0.25);
201
+ }
202
+
203
+ this.#$tip.style.left = `${xPos}px`;
204
+ }
205
+
206
+ setClass(this.#$tip, 'hidden', rectOverlap(targetRect, contentRect));
207
+ };
208
+
209
+ #isOpen() {
210
+ return this.#$pop.hasAttribute('open');
211
+ }
212
+
70
213
  });
@@ -1,19 +1,32 @@
1
1
  import type { TRect, TSinchElementReact } from '../types';
2
2
  export declare type TSinchTooltipOrientation = 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
3
3
  export declare type TSinchTooltipElement = HTMLElement & {
4
+ /** Text */
4
5
  text: string;
6
+ /** @deprecated */
5
7
  width: number | null;
8
+ /** Inverted */
6
9
  inverted: boolean;
10
+ /** Orientation, where it *points to* from origin */
7
11
  orientation: TSinchTooltipOrientation;
12
+ readonly footprintRect: TRect;
8
13
  readonly tooltipRect: TRect;
14
+ /** Text */
9
15
  setAttribute(name: 'text', value: string): void;
16
+ /** @deprecated */
10
17
  setAttribute(name: 'width', value: string): void;
18
+ /** Inverted */
11
19
  setAttribute(name: 'inverted', value: ''): void;
20
+ /** Orientation, where it *points to* from origin */
12
21
  setAttribute(name: 'orientation', value: TSinchTooltipOrientation): void;
13
22
  };
14
23
  export declare type TSinchTooltipReact = TSinchElementReact<TSinchTooltipElement> & {
24
+ /** Text */
15
25
  text: string;
26
+ /** @deprecated */
16
27
  width?: number;
28
+ /** Inverted */
17
29
  inverted?: boolean;
30
+ /** Orientation, where it *points to* from origin */
18
31
  orientation?: TSinchTooltipOrientation;
19
32
  };
@@ -1,2 +1,7 @@
1
+ import type { TSinchPopOrientation } from '../pop/types';
1
2
  import type { TSinchTooltipOrientation } from './types';
2
3
  export declare const orientationValues: readonly TSinchTooltipOrientation[];
4
+ export declare const getPopOrientation: (orientation: TSinchTooltipOrientation) => TSinchPopOrientation;
5
+ declare type TAssertOrientation = (value: string | null) => asserts value is TSinchTooltipOrientation;
6
+ export declare const assertOrientation: TAssertOrientation;
7
+ export {};
package/tooltip/utils.js CHANGED
@@ -1 +1,25 @@
1
- export const orientationValues = ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
1
+ export const orientationValues = ['top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'];
2
+ export const getPopOrientation = orientation => {
3
+ if (orientation === 'top') {
4
+ return 'top-center';
5
+ }
6
+
7
+ if (orientation === 'bottom') {
8
+ return 'bottom-center';
9
+ }
10
+
11
+ if (orientation === 'left') {
12
+ return 'center-left';
13
+ }
14
+
15
+ if (orientation === 'right') {
16
+ return 'center-right';
17
+ }
18
+
19
+ return orientation;
20
+ };
21
+ export const assertOrientation = value => {
22
+ if (value === null || !orientationValues.includes(value)) {
23
+ throw new Error(`sinch-tooltip: invalid orientation attribute: ${value}`);
24
+ }
25
+ };
package/types.d.ts CHANGED
@@ -12,10 +12,3 @@ export type TRect = {
12
12
  width: number,
13
13
  height: number,
14
14
  }
15
-
16
- export type TContextKeyboard = {
17
- code: string,
18
- preventDefault: () => void,
19
- }
20
-
21
- export type TContextVisibility = boolean
@@ -0,0 +1,17 @@
1
+ declare type TTooltipStateOptions = {
2
+ showDelay: number;
3
+ hideDelay: number;
4
+ hideAnimationDuration: number;
5
+ onShow(): void;
6
+ onHideStart(): void;
7
+ onHideEnd(): void;
8
+ };
9
+ export declare class TooltipState {
10
+ #private;
11
+ constructor(options: TTooltipStateOptions);
12
+ show(): void;
13
+ hide(): void;
14
+ interrupt(): void;
15
+ destroy(): void;
16
+ }
17
+ export {};