@nectary/components 2.8.6 → 2.8.8

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.
@@ -1,4 +1,4 @@
1
- import { defineCustomElement, getAttribute, getBooleanAttribute, unpackCsv, getFirstCsvValue, getReactEventHandler, NectaryElement, updateAttribute, updateBooleanAttribute, updateCsv, getTargetByAttribute } from '../utils';
1
+ import { NectaryElement, defineCustomElement, getAttribute, getBooleanAttribute, getFirstCsvValue, getReactEventHandler, getTargetByAttribute, unpackCsv, updateAttribute, updateBooleanAttribute, updateCsv } from '../utils';
2
2
  const templateHTML = '<style>:host{display:block}#wrapper{display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%}::slotted(sinch-accordion-item){flex-shrink:1}</style><div id="wrapper"><slot></slot></div>';
3
3
  const template = document.createElement('template');
4
4
  template.innerHTML = templateHTML;
@@ -1,6 +1,6 @@
1
1
  import '../icon';
2
- import '../title';
3
2
  import '../text';
3
+ import '../title';
4
4
  import type { TSinchAccordionItemElement, TSinchAccordionItemReact } from './types';
5
5
  declare global {
6
6
  namespace JSX {
@@ -1,6 +1,6 @@
1
1
  import '../icon';
2
- import '../title';
3
2
  import '../text';
3
+ import '../title';
4
4
  import { defineCustomElement, getAttribute, getBooleanAttribute, getLiteralAttribute, isAttrEqual, isAttrTrue, NectaryElement, updateAttribute, updateBooleanAttribute, updateExplicitBooleanAttribute, updateLiteralAttribute } from '../utils';
5
5
  const templateHTML = '<style>:host{display:block;outline:0;min-height:48px}#wrapper{display:flex;flex-direction:column;position:relative;width:100%;height:100%;box-sizing:border-box;overflow:hidden;border-bottom:1px solid var(--sinch-comp-accordion-color-default-border-initial)}:host(:last-child)>#wrapper{border-bottom:none}#button{all:initial;display:flex;position:relative;align-items:center;gap:8px;box-sizing:border-box;width:100%;height:48px;padding:0 4px 0 8px;cursor:pointer;--sinch-global-color-icon:var(--sinch-comp-accordion-color-default-icon-initial);--sinch-global-size-icon:var(--sinch-comp-accordion-size-icon)}#button>*{pointer-events:none}#button:disabled{cursor:initial;--sinch-global-color-icon:var(--sinch-comp-accordion-color-disabled-icon-initial)}#button:focus-visible::after{content:"";position:absolute;inset:0;border:2px solid var(--sinch-comp-accordion-color-default-outline-focus);pointer-events:none}#status-wrapper{display:none;width:18px;height:24px;padding:8px 8px 8px 2px;box-sizing:border-box}#status{width:8px;height:8px;border-radius:50%}:host([status]:not([status=""])) #status-wrapper{display:block}:host([status=success]) #status{background-color:var(--sinch-comp-accordion-color-default-status-success)}:host([status=warn]) #status{background-color:var(--sinch-comp-accordion-color-default-status-warning)}:host([status=error]) #status{background-color:var(--sinch-comp-accordion-color-default-status-error)}:host([status=info]) #status{background-color:var(--sinch-comp-accordion-color-default-status-info)}#title{flex:1;min-width:0;--sinch-comp-title-font:var(--sinch-comp-accordion-font-title);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-title-initial)}#button:disabled>#title{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-title-initial)}#content{display:none;overflow-y:auto;flex-shrink:1;min-height:0;padding:0 8px 12px}#dropdown-icon{transform:rotate(0);will-change:transform;transition:transform .25s ease-in-out}#button[aria-expanded=true]>#dropdown-icon{transform:rotate(180deg)}#button[aria-expanded=true]+#content{display:block}#optional{--sinch-comp-text-font:var(--sinch-comp-accordion-font-optional-text);--sinch-global-color-text:var(--sinch-comp-accordion-color-default-optional-text-initial)}#button:disabled>#optional{--sinch-global-color-text:var(--sinch-comp-accordion-color-disabled-optional-text-initial)}</style><div id="wrapper"><button id="button" aria-controls="content" aria-expanded="false"><div id="status-wrapper"><div id="status"></div></div><slot name="icon"></slot><sinch-title id="title" level="3" type="m" ellipsis></sinch-title><sinch-text id="optional" type="m"></sinch-text><sinch-icon id="dropdown-icon" name="keyboard_arrow_down"></sinch-icon></button><div id="content" role="region" aria-labelledby="button"><slot name="content"></slot></div></div>';
6
6
  import { statusValues } from './utils';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "2.8.6",
3
+ "version": "2.8.8",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
@@ -41,6 +41,7 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
41
41
  this.#$search.addEventListener('-change', this.#onSearchChange, options);
42
42
  this.#$searchClear.addEventListener('-click', this.#onSearchClearClick, options);
43
43
  this.#$optionSlot.addEventListener('slotchange', this.#onOptionSlotChange, options);
44
+ this.addEventListener('-search-change', this.#onSearchChangeReactHandler, options);
44
45
  this.addEventListener('-change', this.#onChangeReactHandler, options);
45
46
  subscribeContext(this, 'keydown', this.#onContextKeyDown, this.#controller.signal);
46
47
  subscribeContext(this, 'visibility', this.#onContextVisibility, this.#controller.signal);
@@ -52,7 +53,7 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
52
53
  this.#controller = null;
53
54
  }
54
55
  static get observedAttributes() {
55
- return ['value', 'rows', 'multiple'];
56
+ return ['value', 'rows', 'multiple', 'search-placeholder'];
56
57
  }
57
58
  attributeChangedCallback(name, oldVal, newVal) {
58
59
  switch (name) {
@@ -75,6 +76,11 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
75
76
  });
76
77
  break;
77
78
  }
79
+ case 'search-placeholder':
80
+ {
81
+ updateAttribute(this.#$search, 'placeholder', newVal);
82
+ break;
83
+ }
78
84
  }
79
85
  }
80
86
  set value(value) {
@@ -95,6 +101,19 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
95
101
  get multiple() {
96
102
  return getBooleanAttribute(this, 'multiple');
97
103
  }
104
+ set searchable(isSearchable) {
105
+ updateBooleanAttribute(this, 'searchable', isSearchable);
106
+ }
107
+ get searchable() {
108
+ const searchableAttribute = this.getAttribute('searchable');
109
+ return searchableAttribute === null ? searchableAttribute : isAttrTrue(searchableAttribute);
110
+ }
111
+ set 'search-placeholder'(placeholder) {
112
+ updateAttribute(this.#$search, 'placeholder', placeholder);
113
+ }
114
+ get 'search-placeholder'() {
115
+ return getAttribute(this.#$search, 'placeholder', '');
116
+ }
98
117
  get focusable() {
99
118
  return true;
100
119
  }
@@ -128,15 +147,22 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
128
147
  };
129
148
  #updateSearch = () => {
130
149
  const searchValue = this.#$search.value.toLowerCase();
131
- const $options = this.#getOptionElements();
132
- let someFound = false;
133
- for (const $opt of $options) {
134
- const isHidden = searchValue.length > 0 && !$opt.matchesSearch(searchValue);
135
- someFound ||= !isHidden;
136
- setClass($opt, 'hidden', isHidden);
150
+ const searchChangedEvent = new CustomEvent('-search-change', {
151
+ detail: searchValue,
152
+ cancelable: true
153
+ });
154
+ this.dispatchEvent(searchChangedEvent);
155
+ if (!searchChangedEvent.defaultPrevented) {
156
+ const $options = this.#getOptionElements();
157
+ let someFound = false;
158
+ for (const $opt of $options) {
159
+ const isHidden = searchValue.length > 0 && !$opt.matchesSearch(searchValue);
160
+ someFound ||= !isHidden;
161
+ setClass($opt, 'hidden', isHidden);
162
+ }
163
+ setClass(this.#$notFound, 'active', !someFound);
164
+ this.#selectOption(null);
137
165
  }
138
- setClass(this.#$notFound, 'active', !someFound);
139
- this.#selectOption(null);
140
166
  };
141
167
  #onContextKeyDown = e => {
142
168
  this.#handleKeydown(e.detail);
@@ -179,7 +205,10 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
179
205
  }
180
206
  }
181
207
  #onOptionSlotChange = () => {
182
- const isSearchActive = this.#$optionSlot.assignedElements().length >= NUM_ITEMS_SEARCH;
208
+ const searchable = this.searchable;
209
+ const options = this.#$optionSlot.assignedElements();
210
+ const isEnoughOptions = options.length >= NUM_ITEMS_SEARCH;
211
+ const isSearchActive = isEnoughOptions && searchable !== false || Boolean(searchable);
183
212
  if (!isSearchActive) {
184
213
  updateAttribute(this.#$search, 'value', null);
185
214
  }
@@ -304,4 +333,7 @@ defineCustomElement('sinch-select-menu', class extends NectaryElement {
304
333
  #onChangeReactHandler = e => {
305
334
  getReactEventHandler(this, 'on-change')?.(e);
306
335
  };
336
+ #onSearchChangeReactHandler = e => {
337
+ getReactEventHandler(this, 'on-search-change')?.(e);
338
+ };
307
339
  });
@@ -6,8 +6,14 @@ export type TSinchSelectMenuElement = HTMLElement & {
6
6
  rows: number | null;
7
7
  /** Allows multiple selection */
8
8
  multiple: boolean;
9
+ /** Enforce the search bar appearing, by default it appears above a certain number of options */
10
+ searchable: boolean | null;
11
+ /** Text for search bar's placeholder */
12
+ 'search-placeholder': string;
9
13
  /** Change value event */
10
14
  addEventListener(type: '-change', listener: (e: CustomEvent<string>) => void): void;
15
+ /** Change value event */
16
+ addEventListener(type: '-search-change', listener: (e: CustomEvent<string>) => void): void;
11
17
  /** Selected value, CSV when multiple */
12
18
  setAttribute(name: 'value', value: string): void;
13
19
  /** How many rows to show and scroll the rest */
@@ -22,8 +28,14 @@ export type TSinchSelectMenuReact = TSinchElementReact<TSinchSelectMenuElement>
22
28
  rows?: number;
23
29
  /** Allows multiple selection */
24
30
  multiple?: boolean;
31
+ /** Enforce the search bar appearing, by default it appears above a certain number of options */
32
+ searchable?: boolean | null;
33
+ /** Text for search bar's placeholder */
34
+ 'search-placeholder'?: string;
25
35
  /** Label that is used for a11y */
26
36
  'aria-label': string;
27
37
  /** Change value handler */
38
+ 'on-search-change'?: (e: CustomEvent<string>) => void;
39
+ /** Change value handler */
28
40
  'on-change'?: (e: CustomEvent<string>) => void;
29
41
  };
package/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ClassAttributes, DOMAttributes, HTMLAttributes } from 'react'
1
+ import type { ClassAttributes, DOMAttributes, HTMLAttributes } from 'react';
2
2
 
3
3
  export type TSinchElementReact<TElement> =
4
4
  Pick<HTMLAttributes<HTMLElement>, 'id' | 'className' | 'style' | 'slot' | 'children'> &