@nectary/components 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/popover/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import _classPrivateFieldGet from '@babel/runtime/helpers/classPrivateFieldGet';
2
2
  import _classPrivateFieldSet from '@babel/runtime/helpers/classPrivateFieldSet';
3
3
 
4
- var _$target, _$dialog, _isConnected, _resizeThrottle, _prevOverflowValue, _onExpand, _onCollapse, _isOpen, _onResize, _updateOrientation, _onBackdropClick, _onCancel, _onCloseReactHandler, _dispatchCloseEvent;
4
+ var _$target, _$dialog, _isConnected, _resizeThrottle, _prevOverflowValue, _onExpand, _onCollapse, _isOpen, _onResize, _updateOrientation, _updateOrientationModal, _onBackdropMouseDown, _onCancel, _onCloseReactHandler, _onTargetKeydown, _dispatchCloseEvent;
5
5
 
6
6
  function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
7
7
 
@@ -12,13 +12,13 @@ function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollect
12
12
  function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
13
13
 
14
14
  import dialogPolyfill from 'dialog-polyfill';
15
- import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, getCssVar, throttleAnimationFrame } from '../utils';
16
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{position:relative}dialog{position:fixed;top:0;left:0;margin:0;outline:0;font:var(--sinch-font-body);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-2);border:1px solid var(--sinch-color-snow-500);border-radius:var(--sinch-shape-radius-s);contain:content;padding:0;box-sizing:border-box;max-width:unset;max-height:unset}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:transparent}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}#target{display:flex;flex-direction:column}</style><div id="wrapper"><div id="target" aria-haspopup="dialog" aria-expanded="false"><slot name="target"></slot></div><dialog id="dialog" tabindex="-1"><slot name="content"></slot></dialog></div>';
15
+ import { defineCustomElement, getBooleanAttribute, getLiteralAttribute, getRect, isAttrTrue, updateLiteralAttribute, getReactEventHandler, updateBooleanAttribute, NectaryElement, throttleAnimationFrame } from '../utils';
16
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0}#wrapper{position:relative}dialog{position:fixed;top:0;left:0;margin:0;outline:0;font:var(--sinch-font-body);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);box-shadow:var(--sinch-elevation-level-2);border:1px solid var(--sinch-color-snow-500);border-radius:var(--sinch-popover-shape-radius,var(--sinch-shape-radius-l));contain:content;padding:0;box-sizing:border-box;max-width:unset;max-height:unset;z-index:1}dialog:not([open]){display:none}dialog::backdrop{background-color:transparent}dialog+.backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background-color:transparent}dialog.fixed{position:fixed;top:50%;transform:translate(0,-50%)}._dialog_overlay{position:fixed;top:0;right:0;bottom:0;left:0}#target{display:flex;flex-direction:column}</style><div id="wrapper"><div id="target" aria-haspopup="dialog" aria-expanded="false"><slot name="target"></slot></div><dialog id="dialog" tabindex="-1"><slot name="content"></slot></dialog></div>';
17
17
  import { orientationValues } from './utils';
18
18
  const template = document.createElement('template');
19
19
  template.innerHTML = templateHTML;
20
20
  const POPOVER_OFFSET = 4;
21
- defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new WeakMap(), _isConnected = new WeakMap(), _resizeThrottle = new WeakMap(), _prevOverflowValue = new WeakMap(), _onExpand = new WeakSet(), _onCollapse = new WeakSet(), _isOpen = new WeakSet(), _onResize = new WeakMap(), _updateOrientation = new WeakMap(), _onBackdropClick = new WeakMap(), _onCancel = new WeakMap(), _onCloseReactHandler = new WeakMap(), _dispatchCloseEvent = new WeakSet(), class extends NectaryElement {
21
+ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new WeakMap(), _isConnected = new WeakMap(), _resizeThrottle = new WeakMap(), _prevOverflowValue = new WeakMap(), _onExpand = new WeakSet(), _onCollapse = new WeakSet(), _isOpen = new WeakSet(), _onResize = new WeakMap(), _updateOrientation = new WeakMap(), _updateOrientationModal = new WeakMap(), _onBackdropMouseDown = new WeakMap(), _onCancel = new WeakMap(), _onCloseReactHandler = new WeakMap(), _onTargetKeydown = new WeakMap(), _dispatchCloseEvent = new WeakSet(), class extends NectaryElement {
22
22
  constructor() {
23
23
  super();
24
24
 
@@ -65,7 +65,7 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
65
65
  _classPrivateFieldInitSpec(this, _updateOrientation, {
66
66
  writable: true,
67
67
  value: () => {
68
- _classPrivateFieldGet(this, _$dialog).style.width = 'fit-content';
68
+ _classPrivateFieldGet(this, _$dialog).style.width = 'max-content';
69
69
 
70
70
  const targetRect = _classPrivateFieldGet(this, _$target).getBoundingClientRect();
71
71
 
@@ -74,11 +74,43 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
74
74
  let leftPos = 0;
75
75
  let topPos = 0;
76
76
  const orient = this.orientation;
77
- const shouldExpandWidthToTarget = getCssVar(this, '--sinch-popover-expand-width') !== null;
78
- const largestWidth = Math.max(modalRect.width, targetRect.width);
79
- const resultWidth = shouldExpandWidthToTarget ? largestWidth : modalRect.width;
77
+ const shouldExpandWidthToTarget = orient === 'top' || orient === 'bottom';
78
+ const resultWidth = shouldExpandWidthToTarget ? Math.max(modalRect.width, targetRect.width) : modalRect.width;
80
79
 
81
- if (orient === 'bottom-right' || orient === 'top-right') {
80
+ if (orient === 'bottom-left' || orient === 'top-left') {
81
+ leftPos = targetRect.width - resultWidth;
82
+ }
83
+
84
+ if (orient === 'bottom-left' || orient === 'bottom-right' || orient === 'bottom') {
85
+ topPos = targetRect.height + POPOVER_OFFSET;
86
+ }
87
+
88
+ if (orient === 'top-left' || orient === 'top-right' || orient === 'top') {
89
+ topPos = -(modalRect.height + POPOVER_OFFSET);
90
+ }
91
+
92
+ _classPrivateFieldGet(this, _$dialog).style.left = `${leftPos}px`;
93
+ _classPrivateFieldGet(this, _$dialog).style.top = `${topPos}px`;
94
+ _classPrivateFieldGet(this, _$dialog).style.width = `${resultWidth}px`;
95
+ }
96
+ });
97
+
98
+ _classPrivateFieldInitSpec(this, _updateOrientationModal, {
99
+ writable: true,
100
+ value: () => {
101
+ _classPrivateFieldGet(this, _$dialog).style.width = 'max-content';
102
+
103
+ const targetRect = _classPrivateFieldGet(this, _$target).getBoundingClientRect();
104
+
105
+ const modalRect = _classPrivateFieldGet(this, _$dialog).getBoundingClientRect();
106
+
107
+ let leftPos = 0;
108
+ let topPos = 0;
109
+ const orient = this.orientation;
110
+ const shouldExpandWidthToTarget = orient === 'top' || orient === 'bottom';
111
+ const resultWidth = shouldExpandWidthToTarget ? Math.max(modalRect.width, targetRect.width) : modalRect.width;
112
+
113
+ if (orient === 'bottom-right' || orient === 'top-right' || orient === 'top' || orient === 'bottom') {
82
114
  leftPos = Math.max(POPOVER_OFFSET, Math.min(targetRect.x, window.innerWidth - resultWidth - POPOVER_OFFSET));
83
115
  }
84
116
 
@@ -86,11 +118,11 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
86
118
  leftPos = Math.max(POPOVER_OFFSET, targetRect.right - resultWidth);
87
119
  }
88
120
 
89
- if (orient === 'bottom-left' || orient === 'bottom-right') {
121
+ if (orient === 'bottom-left' || orient === 'bottom-right' || orient === 'bottom') {
90
122
  topPos = Math.max(POPOVER_OFFSET, Math.min(targetRect.bottom + POPOVER_OFFSET, window.innerHeight - modalRect.height - POPOVER_OFFSET));
91
123
  }
92
124
 
93
- if (orient === 'top-left' || orient === 'top-right') {
125
+ if (orient === 'top-left' || orient === 'top-right' || orient === 'top') {
94
126
  topPos = Math.max(POPOVER_OFFSET, targetRect.top - POPOVER_OFFSET - modalRect.height);
95
127
  }
96
128
 
@@ -100,7 +132,7 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
100
132
  }
101
133
  });
102
134
 
103
- _classPrivateFieldInitSpec(this, _onBackdropClick, {
135
+ _classPrivateFieldInitSpec(this, _onBackdropMouseDown, {
104
136
  writable: true,
105
137
  value: e => {
106
138
  if (e.target !== _classPrivateFieldGet(this, _$dialog)) {
@@ -111,8 +143,6 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
111
143
  const isInside = e.x >= rect.x && e.x < rect.x + rect.width && e.y >= rect.y && e.y < rect.y + rect.height;
112
144
 
113
145
  if (!isInside) {
114
- e.stopPropagation();
115
-
116
146
  _classPrivateMethodGet(this, _dispatchCloseEvent, _dispatchCloseEvent2).call(this);
117
147
  }
118
148
  }
@@ -134,6 +164,22 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
134
164
  }
135
165
  });
136
166
 
167
+ _classPrivateFieldInitSpec(this, _onTargetKeydown, {
168
+ writable: true,
169
+ value: e => {
170
+ switch (e.key) {
171
+ case 'Escape':
172
+ {
173
+ e.preventDefault();
174
+
175
+ _classPrivateMethodGet(this, _dispatchCloseEvent, _dispatchCloseEvent2).call(this);
176
+
177
+ break;
178
+ }
179
+ }
180
+ }
181
+ });
182
+
137
183
  const shadowRoot = this.attachShadow();
138
184
  shadowRoot.appendChild(template.content.cloneNode(true));
139
185
 
@@ -143,7 +189,7 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
143
189
 
144
190
  _classPrivateFieldSet(this, _isConnected, false);
145
191
 
146
- _classPrivateFieldSet(this, _resizeThrottle, throttleAnimationFrame(_classPrivateFieldGet(this, _updateOrientation)));
192
+ _classPrivateFieldSet(this, _resizeThrottle, throttleAnimationFrame(_classPrivateFieldGet(this, _updateOrientationModal)));
147
193
 
148
194
  dialogPolyfill.registerDialog(_classPrivateFieldGet(this, _$dialog));
149
195
  }
@@ -153,7 +199,7 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
153
199
 
154
200
  _classPrivateFieldGet(this, _$dialog).addEventListener('cancel', _classPrivateFieldGet(this, _onCancel));
155
201
 
156
- _classPrivateFieldGet(this, _$dialog).addEventListener('mousedown', _classPrivateFieldGet(this, _onBackdropClick));
202
+ _classPrivateFieldGet(this, _$dialog).addEventListener('mousedown', _classPrivateFieldGet(this, _onBackdropMouseDown));
157
203
 
158
204
  this.addEventListener('close', _classPrivateFieldGet(this, _onCloseReactHandler));
159
205
 
@@ -169,15 +215,25 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
169
215
  disconnectedCallback() {
170
216
  _classPrivateFieldGet(this, _$dialog).removeEventListener('cancel', _classPrivateFieldGet(this, _onCancel));
171
217
 
172
- _classPrivateFieldGet(this, _$dialog).removeEventListener('mousedown', _classPrivateFieldGet(this, _onBackdropClick));
218
+ _classPrivateFieldGet(this, _$dialog).removeEventListener('mousedown', _classPrivateFieldGet(this, _onBackdropMouseDown));
173
219
 
174
220
  this.removeEventListener('close', _classPrivateFieldGet(this, _onCloseReactHandler));
175
221
 
222
+ _classPrivateMethodGet(this, _onCollapse, _onCollapse2).call(this);
223
+
176
224
  _classPrivateFieldSet(this, _isConnected, false);
177
225
  }
178
226
 
179
227
  static get observedAttributes() {
180
- return ['open', 'orientation'];
228
+ return ['modal', 'orientation', 'open'];
229
+ }
230
+
231
+ set modal(isModal) {
232
+ updateBooleanAttribute(this, 'modal', isModal);
233
+ }
234
+
235
+ get modal() {
236
+ return getBooleanAttribute(this, 'modal');
181
237
  }
182
238
 
183
239
  set open(isOpen) {
@@ -189,7 +245,7 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
189
245
  }
190
246
 
191
247
  get orientation() {
192
- return getLiteralAttribute(this, orientationValues, 'orientation', 'bottom-right');
248
+ return getLiteralAttribute(this, orientationValues, 'orientation', 'bottom');
193
249
  }
194
250
 
195
251
  set orientation(value) {
@@ -201,48 +257,77 @@ defineCustomElement('sinch-popover', (_$target = new WeakMap(), _$dialog = new W
201
257
  }
202
258
 
203
259
  attributeChangedCallback(name, oldVal, newVal) {
260
+ if (oldVal === newVal) {
261
+ return;
262
+ }
263
+
204
264
  switch (name) {
205
265
  case 'open':
206
266
  {
207
- if (_classPrivateFieldGet(this, _isConnected)) {
208
- if (isAttrTrue(newVal)) {
209
- _classPrivateMethodGet(this, _onExpand, _onExpand2).call(this);
210
- } else {
211
- _classPrivateMethodGet(this, _onCollapse, _onCollapse2).call(this);
212
- }
267
+ if (isAttrTrue(newVal)) {
268
+ _classPrivateMethodGet(this, _onExpand, _onExpand2).call(this);
269
+ } else {
270
+ _classPrivateMethodGet(this, _onCollapse, _onCollapse2).call(this);
213
271
  }
214
272
 
273
+ updateBooleanAttribute(this, 'open', isAttrTrue(newVal));
215
274
  break;
216
275
  }
217
276
 
218
277
  case 'orientation':
219
278
  {
220
279
  if (_classPrivateMethodGet(this, _isOpen, _isOpen2).call(this)) {
221
- _classPrivateFieldGet(this, _updateOrientation).call(this);
280
+ if (this.modal) {
281
+ _classPrivateFieldGet(this, _updateOrientationModal).call(this);
282
+ } else {
283
+ _classPrivateFieldGet(this, _updateOrientation).call(this);
284
+ }
222
285
  }
223
286
 
224
287
  break;
225
288
  }
289
+
290
+ case 'modal':
291
+ {
292
+ if (_classPrivateMethodGet(this, _isOpen, _isOpen2).call(this)) {
293
+ _classPrivateMethodGet(this, _onCollapse, _onCollapse2).call(this);
294
+
295
+ _classPrivateMethodGet(this, _onExpand, _onExpand2).call(this);
296
+ }
297
+ }
226
298
  }
227
299
  }
228
300
 
229
301
  }));
230
302
 
231
303
  function _onExpand2() {
232
- if (_classPrivateMethodGet(this, _isOpen, _isOpen2).call(this)) {
304
+ if (!_classPrivateFieldGet(this, _isConnected) || _classPrivateMethodGet(this, _isOpen, _isOpen2).call(this)) {
233
305
  return;
234
306
  }
235
307
 
236
- _classPrivateFieldGet(this, _$target).setAttribute('aria-expanded', 'true');
308
+ if (this.modal) {
309
+ _classPrivateFieldGet(this, _$dialog).style.position = 'fixed';
310
+
311
+ _classPrivateFieldGet(this, _$dialog).showModal();
312
+
313
+ _classPrivateFieldGet(this, _updateOrientationModal).call(this);
314
+
315
+ _classPrivateFieldGet(this, _$target).setAttribute('aria-expanded', 'true');
237
316
 
238
- _classPrivateFieldGet(this, _$dialog).showModal();
317
+ _classPrivateFieldSet(this, _prevOverflowValue, document.body.style.overflow);
239
318
 
240
- _classPrivateFieldGet(this, _updateOrientation).call(this);
319
+ document.body.style.overflow = 'hidden';
320
+ window.addEventListener('resize', _classPrivateFieldGet(this, _onResize));
321
+ } else {
322
+ this.addEventListener('keydown', _classPrivateFieldGet(this, _onTargetKeydown));
323
+ _classPrivateFieldGet(this, _$dialog).style.position = 'absolute';
241
324
 
242
- _classPrivateFieldSet(this, _prevOverflowValue, document.body.style.overflow);
325
+ _classPrivateFieldGet(this, _$dialog).setAttribute('open', '');
243
326
 
244
- document.body.style.overflow = 'hidden';
245
- window.addEventListener('resize', _classPrivateFieldGet(this, _onResize));
327
+ _classPrivateFieldGet(this, _updateOrientation).call(this);
328
+
329
+ _classPrivateFieldGet(this, _$target).setAttribute('aria-expanded', 'true');
330
+ }
246
331
  }
247
332
 
248
333
  function _onCollapse2() {
@@ -250,17 +335,24 @@ function _onCollapse2() {
250
335
  return;
251
336
  }
252
337
 
338
+ if (this.modal) {
339
+ _classPrivateFieldGet(this, _$dialog).close?.();
340
+ } else {
341
+ _classPrivateFieldGet(this, _$dialog).removeAttribute('open');
342
+ }
343
+
253
344
  _classPrivateFieldGet(this, _$target).setAttribute('aria-expanded', 'false');
254
345
 
255
- _classPrivateFieldGet(this, _$dialog).close?.();
256
346
  document.body.style.overflow = _classPrivateFieldGet(this, _prevOverflowValue);
257
- window.removeEventListener('resize', _classPrivateFieldGet(this, _onResize));
258
347
 
259
348
  _classPrivateFieldGet(this, _resizeThrottle).cancel();
349
+
350
+ window.removeEventListener('resize', _classPrivateFieldGet(this, _onResize));
351
+ this.removeEventListener('keydown', _classPrivateFieldGet(this, _onTargetKeydown));
260
352
  }
261
353
 
262
354
  function _isOpen2() {
263
- return _classPrivateFieldGet(this, _isConnected) && getBooleanAttribute(_classPrivateFieldGet(this, _$dialog), 'open');
355
+ return getBooleanAttribute(_classPrivateFieldGet(this, _$dialog), 'open');
264
356
  }
265
357
 
266
358
  function _dispatchCloseEvent2() {
@@ -1,17 +1,32 @@
1
1
  import type { TRect, TSinchElementReact } from '../types';
2
2
  import type { SyntheticEvent } from 'react';
3
- export declare type TSinchPopoverOrientation = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
3
+ export declare type TSinchPopoverOrientation = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'bottom' | 'top';
4
4
  export declare type TSinchPopoverElement = HTMLElement & {
5
+ /** Open/close state */
5
6
  open: boolean;
7
+ /** Orientation, where it *points to* from origin */
6
8
  orientation: TSinchPopoverOrientation;
9
+ /** Modal/non-modal mode */
10
+ modal: boolean;
7
11
  readonly popoverRect: TRect;
12
+ /** Close event */
8
13
  addEventListener(type: 'close', listener: (e: CustomEvent<void>) => void): void;
14
+ /** Open/close state */
9
15
  setAttribute(name: 'open', value: ''): void;
16
+ /** Orientation, where it *points to* from origin */
10
17
  setAttribute(name: 'orientation', value: TSinchPopoverOrientation): void;
18
+ /** Modal/non-modal mode */
19
+ setAttribute(name: 'modal', value: boolean): void;
11
20
  };
12
21
  export declare type TSinchPopoverReact = TSinchElementReact<TSinchPopoverElement> & {
22
+ /** Open/close state */
13
23
  open: boolean;
24
+ /** Orientation, where it *points to* from origin */
14
25
  orientation?: TSinchPopoverOrientation;
26
+ /** Modal/non-modal mode */
27
+ modal?: boolean;
28
+ /** Label that is used for a11y */
15
29
  'aria-label': string;
16
- onClose: (event: SyntheticEvent<TSinchPopoverElement, CustomEvent<void>>) => void;
30
+ /** Close event handler */
31
+ onClose?: (event: SyntheticEvent<TSinchPopoverElement, CustomEvent<void>>) => void;
17
32
  };
package/popover/utils.js CHANGED
@@ -1 +1 @@
1
- export const orientationValues = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
1
+ export const orientationValues = ['top-left', 'top-right', 'bottom-left', 'bottom-right', 'bottom', 'top'];
package/select/index.js CHANGED
@@ -13,7 +13,7 @@ function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(
13
13
 
14
14
  import '../dropdown';
15
15
  import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateIntegerAttribute } from '../utils';
16
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;--sinch-color-icon:var(--sinch-color-stormy-500)}#wrapper{position:relative;--sinch-popover-expand-width:1}sinch-dropdown{display:block}#button{all:initial;display:flex;align-items:center;gap:8px;border:1px solid var(--sinch-color-stormy-200);border-radius:var(--sinch-shape-radius-s);box-sizing:border-box;width:100%;height:48px;margin:2px 0;padding:8px 12px;font:var(--sinch-font-body);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);cursor:pointer}#button:focus{border-color:var(--sinch-color-stormy-600)}#dropdown-icon{fill:var(--sinch-color-stormy-500)}#button>*{pointer-events:none}#button[data-unselected]{color:var(--sinch-color-text-muted)}#button:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100);cursor:initial}#button:disabled #dropdown-icon{fill:var(--sinch-color-stormy-100)}:host([invalidtext]:not([invalidtext=""])) #button:not(:disabled){border-color:var(--sinch-color-text-invalid)}#content{flex:1;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}#bottom,#top{display:flex;align-items:baseline}#top{height:24px}#bottom{height:20px}#additional,#invalid,#label,#optional{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#label{font:var(--sinch-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-small-text);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-muted)}#invalid{font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-invalid)}::slotted(sinch-help-tooltip){align-self:center;margin:0 8px}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])){--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="button"></label><slot name="tooltip"></slot><span id="optional"></span></div><sinch-dropdown id="dropdown"><button slot="target" id="button"><span id="content"></span> <svg id="dropdown-icon" width="12" height="8" aria-hidden="true"><path d="M1.41.59 6 5.17 10.59.59 12 2 6 8 0 2 1.41.59Z"/></svg></button><slot name="option" slot="option"></slot></sinch-dropdown><div id="bottom"><span id="invalid"></span> <span id="additional"></span></div></div>';
16
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;--sinch-color-icon:var(--sinch-color-stormy-500)}#wrapper{position:relative}sinch-dropdown{display:block}#button{all:initial;display:flex;align-items:center;gap:8px;border:1px solid var(--sinch-color-stormy-200);border-radius:var(--sinch-shape-radius-s);box-sizing:border-box;width:100%;height:48px;padding:8px 12px;font:var(--sinch-font-body);color:var(--sinch-color-text-default);background-color:var(--sinch-color-snow-100);cursor:pointer}#button:focus{border-color:var(--sinch-color-stormy-600)}#dropdown-icon{fill:var(--sinch-color-stormy-500)}#button>*{pointer-events:none}#button[data-unselected]{color:var(--sinch-color-text-muted)}#button:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100);cursor:initial}#button:disabled #dropdown-icon{fill:var(--sinch-color-stormy-100)}:host([invalidtext]:not([invalidtext=""])) #button:not(:disabled){border-color:var(--sinch-color-text-invalid)}#content{flex:1;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}#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-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-small-text);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-muted);line-height:20px;margin-top:2px}#additional:empty{display:none}#invalid{font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-invalid);line-height:20px;margin-top:2px}#invalid:empty{display:none}::slotted(sinch-help-tooltip){align-self:center;margin:0 8px}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])){--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="button"></label><slot name="tooltip"></slot><span id="optional"></span></div><sinch-dropdown id="dropdown" modal orientation="bottom"><button slot="target" id="button"><span id="content"></span> <svg id="dropdown-icon" width="12" height="8" aria-hidden="true"><path d="M1.41.59 6 5.17 10.59.59 12 2 6 8 0 2 1.41.59Z"/></svg></button><slot name="option" slot="option"></slot></sinch-dropdown><div id="bottom"><span id="invalid"></span> <span id="additional"></span></div></div>';
17
17
  const template = document.createElement('template');
18
18
  template.innerHTML = templateHTML;
19
19
  defineCustomElement('sinch-select', (_$button = new WeakMap(), _$buttonContent = new WeakMap(), _$label = new WeakMap(), _$optionalText = new WeakMap(), _$additionalText = new WeakMap(), _$invalidText = new WeakMap(), _$dropdown = new WeakMap(), _$optionSlot = new WeakMap(), _$sh = new WeakMap(), _createElement = new WeakSet(), _updateButtonContent = new WeakSet(), _onValueChange = new WeakMap(), _getOptionWithValue = new WeakSet(), _onLabelClick = new WeakMap(), _onDropdownClick = new WeakMap(), _onDropdownClose = new WeakMap(), class extends NectaryElement {
package/select/types.d.ts CHANGED
@@ -1,33 +1,61 @@
1
1
  import type { TRect, TSinchElementReact } from '../types';
2
2
  import type { SyntheticEvent } from 'react';
3
3
  export declare type TSinchSelectElement = HTMLElement & {
4
+ /** Value that matches one of the options `value` */
4
5
  value: string;
6
+ /** Label */
5
7
  label: string;
8
+ /** Text that appears when it has no value set */
6
9
  placeholder: string | null;
10
+ /** Optional text */
7
11
  optionalText: string | null;
8
- invalidText: string | null;
12
+ /** Additional text */
9
13
  additionalText: string | null;
10
- maxVisibleItems: number | null;
14
+ /** Invalid text, controls the overall invalid state */
15
+ invalidText: string | null;
16
+ /** Disabled */
11
17
  disabled: boolean;
18
+ /** Number of visible at the same time options in the list */
19
+ maxVisibleItems: number | null;
12
20
  readonly dropdownRect: TRect;
21
+ /** Change value event */
13
22
  addEventListener(type: 'change', listener: (e: CustomEvent<string>) => void): void;
23
+ /** Value that matches one of the options `value` */
14
24
  setAttribute(name: 'value', value: string): void;
25
+ /** Label */
15
26
  setAttribute(name: 'label', value: string): void;
27
+ /** Text that appears when it has no value set */
16
28
  setAttribute(name: 'placeholder', value: string): void;
29
+ /** Optional text */
17
30
  setAttribute(name: 'optionaltext', value: string): void;
31
+ /** Additional text */
18
32
  setAttribute(name: 'additionaltext', value: string): void;
19
- setAttribute(name: 'maxvisibleitems', value: string): void;
33
+ /** Invalid text, controls the overall invalid state */
34
+ setAttribute(name: 'invalidtext', value: string): void;
35
+ /** Disabled */
20
36
  setAttribute(name: 'disabled', value: ''): void;
37
+ /** Number of visible at the same time options in the list */
38
+ setAttribute(name: 'maxvisibleitems', value: string): void;
21
39
  };
22
40
  export declare type TSinchSelectReact = TSinchElementReact<TSinchSelectElement> & {
41
+ /** Value that matches one of the options `value` */
23
42
  value: string;
43
+ /** Label */
24
44
  label: string;
45
+ /** Label that is used for a11y – might be different from `label` */
46
+ 'aria-label': string;
47
+ /** Text that appears when it has no value set */
25
48
  placeholder?: string;
49
+ /** Optional text */
26
50
  optionalText?: string;
27
- invalidText?: string;
51
+ /** Additional text */
28
52
  additionalText?: string;
53
+ /** Invalid text, controls the overall invalid state */
54
+ invalidText?: string;
55
+ /** Disabled */
29
56
  disabled?: boolean;
57
+ /** Number of visible at the same time options in the list */
30
58
  maxVisibleItems?: number;
31
- 'aria-label': string;
59
+ /** Change value handler */
32
60
  onChange: (e: SyntheticEvent<TSinchSelectElement, CustomEvent<string>>) => void;
33
61
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import _classPrivateFieldGet from '@babel/runtime/helpers/classPrivateFieldGet';
2
+
3
+ var _stopEvent;
4
+
5
+ function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
6
+
7
+ function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
8
+
9
+ import { defineCustomElement, getCsvSet } from '../utils';
10
+ defineCustomElement('sinch-stop-events', (_stopEvent = new WeakMap(), class extends HTMLElement {
11
+ constructor() {
12
+ super();
13
+
14
+ _classPrivateFieldInitSpec(this, _stopEvent, {
15
+ writable: true,
16
+ value: e => {
17
+ e.stopPropagation();
18
+ }
19
+ });
20
+
21
+ this.style.display = 'contents';
22
+ }
23
+
24
+ connectedCallback() {
25
+ const events = getCsvSet(this.getAttribute('events'));
26
+
27
+ for (const event of events) {
28
+ this.addEventListener(event, _classPrivateFieldGet(this, _stopEvent));
29
+ }
30
+ }
31
+
32
+ disconnectedCallback() {
33
+ const events = getCsvSet(this.getAttribute('events'));
34
+
35
+ for (const event of events) {
36
+ this.removeEventListener(event, _classPrivateFieldGet(this, _stopEvent));
37
+ }
38
+ }
39
+
40
+ }));
package/textarea/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import _classPrivateFieldGet from '@babel/runtime/helpers/classPrivateFieldGet';
2
2
  import _classPrivateFieldSet from '@babel/runtime/helpers/classPrivateFieldSet';
3
3
 
4
- var _$input, _$label, _$optionalText, _$additionalText, _$invalidText, _selectionStart, _selectionEnd, _isPendingDk, _onCompositionStart, _onInput;
4
+ var _$input, _$label, _$optionalText, _$additionalText, _$invalidText, _cursorPos, _isPendingDk, _onCompositionStart, _onSelectionChange, _onInput;
5
5
 
6
6
  function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
7
7
 
8
8
  function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
9
9
 
10
10
  import { defineCustomElement, getAttribute, getBooleanAttribute, getIntegerAttribute, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute } from '../utils';
11
- const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;--sinch-color-icon:var(--sinch-color-stormy-500)}#wrapper{width:100%;box-sizing:border-box}#input{all:initial;border:1px solid var(--sinch-color-stormy-200);box-sizing:border-box;border-radius:var(--sinch-shape-radius-s);width:100%;margin:2px 0;padding:8px 12px;font:var(--sinch-font-body);color:var(--sinch-color-text-default);caret-color:var(--sinch-caret-color,auto);background-color:var(--sinch-color-snow-100);resize:none}#input::placeholder{font:var(--sinch-font-body);color:var(--sinch-color-text-muted)}#input:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100)}#input:disabled::placeholder{color:var(--sinch-color-snow-500)}#input:focus{border-color:var(--sinch-color-stormy-600)}:host([resizable]:not([resizable=false])) #input{resize:vertical}:host([invalidtext]:not([invalidtext=""])) #input:not(:disabled){border-color:var(--sinch-color-text-invalid)}#bottom,#top{display:flex;align-items:baseline}#top{height:24px}#bottom{height:20px}#additional,#invalid,#label,#optional{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#label{font:var(--sinch-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-small-text);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-muted)}#invalid{font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-invalid)}::slotted(sinch-help-tooltip){align-self:center;margin:0 8px}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])){--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="input"></label><slot name="tooltip"></slot><span id="optional"></span></div><textarea id="input"></textarea><div id="bottom"><span id="invalid"></span> <span id="additional"></span></div></div>';
11
+ const templateHTML = '<style>:host{display:inline-block;vertical-align:middle;outline:0;--sinch-color-icon:var(--sinch-color-stormy-500)}#wrapper{width:100%;box-sizing:border-box}#input{all:initial;border:1px solid var(--sinch-color-stormy-200);box-sizing:border-box;border-radius:var(--sinch-shape-radius-s);width:100%;padding:8px 12px;font:var(--sinch-font-body);color:var(--sinch-color-text-default);caret-color:var(--sinch-caret-color,auto);background-color:var(--sinch-color-snow-100);resize:none}#input::placeholder{font:var(--sinch-font-body);color:var(--sinch-color-text-muted)}#input:disabled{border-color:var(--sinch-color-snow-500);color:var(--sinch-color-stormy-100)}#input:disabled::placeholder{color:var(--sinch-color-snow-500)}#input:focus{border-color:var(--sinch-color-stormy-600)}:host([resizable]:not([resizable=false])) #input{resize:vertical}:host([invalidtext]:not([invalidtext=""])) #input:not(:disabled){border-color:var(--sinch-color-text-invalid)}#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-font-title-s);color:var(--sinch-color-text-default)}#optional{flex:1;text-align:right;font:var(--sinch-font-small-text);color:var(--sinch-color-text-muted)}#additional{flex:1;text-align:right;font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-muted);line-height:20px;margin-top:2px}#additional:empty{display:none}#invalid{font:var(--sinch-font-extra-small-text);color:var(--sinch-color-text-invalid);line-height:20px;margin-top:2px}#invalid:empty{display:none}::slotted(sinch-help-tooltip){align-self:center;margin:0 8px}:host([disabled]:not([disabled=false])) :is(#label,#additional,#optional,#invalid){color:var(--sinch-color-stormy-100)}:host([disabled]:not([disabled=false])){--sinch-color-icon:var(--sinch-color-stormy-100)}</style><div id="wrapper"><div id="top"><label id="label" for="input"></label><slot name="tooltip"></slot><span id="optional"></span></div><textarea id="input"></textarea><div id="bottom"><span id="invalid"></span> <span id="additional"></span></div></div>';
12
12
  const template = document.createElement('template');
13
13
  template.innerHTML = templateHTML;
14
- defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new WeakMap(), _$optionalText = new WeakMap(), _$additionalText = new WeakMap(), _$invalidText = new WeakMap(), _selectionStart = new WeakMap(), _selectionEnd = new WeakMap(), _isPendingDk = new WeakMap(), _onCompositionStart = new WeakMap(), _onInput = new WeakMap(), class extends NectaryElement {
14
+ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new WeakMap(), _$optionalText = new WeakMap(), _$additionalText = new WeakMap(), _$invalidText = new WeakMap(), _cursorPos = new WeakMap(), _isPendingDk = new WeakMap(), _onCompositionStart = new WeakMap(), _onSelectionChange = new WeakMap(), _onInput = new WeakMap(), class extends NectaryElement {
15
15
  constructor() {
16
16
  super();
17
17
 
@@ -40,12 +40,7 @@ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new We
40
40
  value: void 0
41
41
  });
42
42
 
43
- _classPrivateFieldInitSpec(this, _selectionStart, {
44
- writable: true,
45
- value: null
46
- });
47
-
48
- _classPrivateFieldInitSpec(this, _selectionEnd, {
43
+ _classPrivateFieldInitSpec(this, _cursorPos, {
49
44
  writable: true,
50
45
  value: null
51
46
  });
@@ -62,6 +57,13 @@ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new We
62
57
  }
63
58
  });
64
59
 
60
+ _classPrivateFieldInitSpec(this, _onSelectionChange, {
61
+ writable: true,
62
+ value: () => {
63
+ _classPrivateFieldSet(this, _cursorPos, _classPrivateFieldGet(this, _$input).selectionEnd);
64
+ }
65
+ });
66
+
65
67
  _classPrivateFieldInitSpec(this, _onInput, {
66
68
  writable: true,
67
69
  value: e => {
@@ -72,29 +74,23 @@ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new We
72
74
  const prevValue = this.value;
73
75
 
74
76
  if (prevValue !== nextValue) {
75
- const nextSelectionStart = _classPrivateFieldGet(this, _$input).selectionStart;
76
-
77
- const nextSelectionEnd = _classPrivateFieldGet(this, _$input).selectionEnd;
78
-
79
- const prevSelectionStart = _classPrivateFieldGet(this, _selectionStart);
80
-
81
- const prevSelectionEnd = _classPrivateFieldGet(this, _selectionEnd);
82
-
83
- const isPrevCursorEnd = prevSelectionStart === prevSelectionEnd && prevSelectionStart === prevValue.length;
77
+ const nextCursorPos = _classPrivateFieldGet(this, _$input).selectionEnd;
84
78
 
85
79
  if (!_classPrivateFieldGet(this, _isPendingDk)) {
86
80
  _classPrivateFieldGet(this, _$input).value = prevValue;
87
- }
88
81
 
89
- _classPrivateFieldSet(this, _isPendingDk, false);
82
+ const prevCursorPos = _classPrivateFieldGet(this, _cursorPos);
90
83
 
91
- if (!isPrevCursorEnd) {
92
- _classPrivateFieldGet(this, _$input).setSelectionRange(prevSelectionStart, prevSelectionEnd);
84
+ const isPrevCursorEnd = prevCursorPos === null || prevCursorPos === prevValue.length;
85
+
86
+ if (!isPrevCursorEnd) {
87
+ _classPrivateFieldGet(this, _$input).setSelectionRange(prevCursorPos, prevCursorPos);
88
+ }
93
89
  }
94
90
 
95
- _classPrivateFieldSet(this, _selectionStart, nextSelectionStart);
91
+ _classPrivateFieldSet(this, _isPendingDk, false);
96
92
 
97
- _classPrivateFieldSet(this, _selectionEnd, nextSelectionEnd);
93
+ _classPrivateFieldSet(this, _cursorPos, nextCursorPos);
98
94
 
99
95
  this.dispatchEvent(new CustomEvent('change', {
100
96
  detail: nextValue,
@@ -125,12 +121,20 @@ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new We
125
121
  _classPrivateFieldGet(this, _$input).addEventListener('input', _classPrivateFieldGet(this, _onInput));
126
122
 
127
123
  _classPrivateFieldGet(this, _$input).addEventListener('compositionstart', _classPrivateFieldGet(this, _onCompositionStart));
124
+
125
+ _classPrivateFieldGet(this, _$input).addEventListener('mousedown', _classPrivateFieldGet(this, _onSelectionChange));
126
+
127
+ _classPrivateFieldGet(this, _$input).addEventListener('keydown', _classPrivateFieldGet(this, _onSelectionChange));
128
128
  }
129
129
 
130
130
  disconnectedCallback() {
131
131
  _classPrivateFieldGet(this, _$input).removeEventListener('input', _classPrivateFieldGet(this, _onInput));
132
132
 
133
133
  _classPrivateFieldGet(this, _$input).removeEventListener('compositionstart', _classPrivateFieldGet(this, _onCompositionStart));
134
+
135
+ _classPrivateFieldGet(this, _$input).removeEventListener('mousedown', _classPrivateFieldGet(this, _onSelectionChange));
136
+
137
+ _classPrivateFieldGet(this, _$input).removeEventListener('keydown', _classPrivateFieldGet(this, _onSelectionChange));
134
138
  }
135
139
 
136
140
  static get observedAttributes() {
@@ -143,12 +147,16 @@ defineCustomElement('sinch-textarea', (_$input = new WeakMap(), _$label = new We
143
147
  {
144
148
  const nextVal = newVal ?? '';
145
149
 
146
- if (nextVal !== _classPrivateFieldGet(this, _$input).value) {
150
+ const prevVal = _classPrivateFieldGet(this, _$input).value;
151
+
152
+ if (nextVal !== prevVal) {
153
+ const prevCursorPos = _classPrivateFieldGet(this, _$input).selectionEnd;
154
+
155
+ const isPrevCursorEnd = prevCursorPos === prevVal.length;
147
156
  _classPrivateFieldGet(this, _$input).value = nextVal;
148
- const isNextCursorEnd = _classPrivateFieldGet(this, _selectionStart) === _classPrivateFieldGet(this, _selectionEnd) && (_classPrivateFieldGet(this, _selectionStart) === null || _classPrivateFieldGet(this, _selectionStart) === nextVal.length);
149
157
 
150
- if (!isNextCursorEnd) {
151
- _classPrivateFieldGet(this, _$input).setSelectionRange(_classPrivateFieldGet(this, _selectionStart), _classPrivateFieldGet(this, _selectionEnd));
158
+ if (!isPrevCursorEnd) {
159
+ _classPrivateFieldGet(this, _$input).setSelectionRange(_classPrivateFieldGet(this, _cursorPos), _classPrivateFieldGet(this, _cursorPos));
152
160
  }
153
161
  }
154
162
 
package/utils.d.ts CHANGED
@@ -23,6 +23,7 @@ export declare const updateLiteralAttribute: <T extends readonly string[]>($elem
23
23
  export declare function getLiteralAttribute<T extends readonly string[]>($element: Element, literals: T, attrName: string): T[number];
24
24
  export declare function getLiteralAttribute<T extends readonly string[]>($element: Element, literals: T, attrName: string, defaultValue: null): T[number] | null;
25
25
  export declare function getLiteralAttribute<T extends readonly string[]>($element: Element, literals: T, attrName: string, defaultValue: T[number]): T[number];
26
+ export declare const clampNumber: (value: number, min: number, max: number) => number;
26
27
  declare type TRange = {
27
28
  min?: number;
28
29
  max?: number;
package/utils.js CHANGED
@@ -99,6 +99,9 @@ export function getLiteralAttribute($element, literals, attrName, defaultValue)
99
99
 
100
100
  return defaultValue;
101
101
  }
102
+ export const clampNumber = (value, min, max) => {
103
+ return Math.min(max, Math.max(min, value));
104
+ };
102
105
 
103
106
  const applyRange = (value, range) => {
104
107
  let result = value;