@nectary/components 5.31.3 → 5.32.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/bundle.js CHANGED
@@ -12975,7 +12975,6 @@ defineCustomElement("sinch-select-menu-option", SelectMenuOption);
12975
12975
  const isSelectMenuOption = (el) => el.localName === "sinch-select-menu-option";
12976
12976
  const templateHTML$h = '<style>:host{display:block;outline:0}#listbox{overflow-y:auto;max-height:var(--sinch-comp-select-menu-font-max-height)}#search{display:none;margin:10px}#search.active{display:block}#search-clear:not(.active){display:none}#not-found{display:flex;align-items:center;justify-content:center;width:100%;height:30px;margin-bottom:10px;pointer-events:none;user-select:none;--sinch-comp-text-font:var(--sinch-comp-select-menu-font-not-found-text);--sinch-global-color-text:var(--sinch-comp-select-menu-color-default-not-found-text-initial)}#not-found:not(.active){display:none}::slotted(.hidden){display:none}::slotted(sinch-title){padding:8px 16px;--sinch-global-color-text:var(--sinch-comp-select-menu-color-default-title-initial)}</style><sinch-input id="search" size="s" placeholder="Search"><sinch-icon icons-version="2" name="magnifying-glass" id="icon-search" slot="icon"></sinch-icon><sinch-button id="search-clear" slot="right"><sinch-icon icons-version="2" name="fa-xmark" slot="icon"></sinch-icon></sinch-button></sinch-input><div id="not-found"><sinch-text type="m">No results</sinch-text></div><div id="listbox" role="presentation"><slot></slot></div>';
12977
12977
  const ITEM_HEIGHT = 40;
12978
- const NUM_ITEMS_SEARCH = 7;
12979
12978
  const template$h = document.createElement("template");
12980
12979
  template$h.innerHTML = templateHTML$h;
12981
12980
  class SelectMenu extends NectaryElement {
@@ -13076,6 +13075,7 @@ class SelectMenu extends NectaryElement {
13076
13075
  "value",
13077
13076
  "rows",
13078
13077
  "multiple",
13078
+ "searchable",
13079
13079
  "search-value",
13080
13080
  "search-placeholder",
13081
13081
  "search-autocomplete"
@@ -13093,6 +13093,10 @@ class SelectMenu extends NectaryElement {
13093
13093
  this.#internals.ariaMultiSelectable = isAttrTrue(newVal).toString();
13094
13094
  break;
13095
13095
  }
13096
+ case "searchable": {
13097
+ this.#onOptionSlotChange();
13098
+ break;
13099
+ }
13096
13100
  case "search-autocomplete": {
13097
13101
  updateAttribute(this.#$search, "autocomplete", newVal);
13098
13102
  break;
@@ -13102,13 +13106,7 @@ class SelectMenu extends NectaryElement {
13102
13106
  break;
13103
13107
  }
13104
13108
  case "rows": {
13105
- const numberOfItems = this.#$optionSlot.assignedElements().length;
13106
- const maxNumberOfRows = parseInt(newVal ?? "0", 10);
13107
- this.#$listbox.style.maxHeight = attrValueToPixels(newVal, {
13108
- min: 2,
13109
- itemSizeMultiplier: ITEM_HEIGHT,
13110
- addExtraSpace: numberOfItems > maxNumberOfRows
13111
- });
13109
+ this.#updateListboxMaxHeight();
13112
13110
  break;
13113
13111
  }
13114
13112
  case "search-placeholder": {
@@ -13148,7 +13146,13 @@ class SelectMenu extends NectaryElement {
13148
13146
  return getBooleanAttribute(this, "multiple");
13149
13147
  }
13150
13148
  set searchable(isSearchable) {
13151
- updateBooleanAttribute(this, "searchable", isSearchable);
13149
+ if (isSearchable === false) {
13150
+ this.setAttribute("searchable", "false");
13151
+ } else if (isSearchable === true) {
13152
+ updateBooleanAttribute(this, "searchable", true);
13153
+ } else {
13154
+ this.removeAttribute("searchable");
13155
+ }
13152
13156
  }
13153
13157
  get searchable() {
13154
13158
  const searchableAttribute = this.getAttribute("searchable");
@@ -13230,11 +13234,14 @@ class SelectMenu extends NectaryElement {
13230
13234
  const $options = this.#getOptionElements();
13231
13235
  let someFound = false;
13232
13236
  for (const $opt of $options) {
13233
- const isHidden = searchValue.length > 0 && !$opt.matchesSearch(searchValue);
13234
- someFound ||= !isHidden;
13237
+ const isAction = getBooleanAttribute($opt, "action");
13238
+ const isHidden = searchValue.length > 0 && !isAction && !$opt.matchesSearch(searchValue);
13239
+ if (!isAction) {
13240
+ someFound ||= !isHidden;
13241
+ }
13235
13242
  setClass($opt, "hidden", isHidden);
13236
13243
  }
13237
- setClass(this.#$notFound, "active", !someFound);
13244
+ setClass(this.#$notFound, "active", searchValue.length > 0 && !someFound);
13238
13245
  this.#selectOption(null);
13239
13246
  };
13240
13247
  #onContextKeyDown = (e) => {
@@ -13282,16 +13289,27 @@ class SelectMenu extends NectaryElement {
13282
13289
  }
13283
13290
  };
13284
13291
  #onOptionSlotChange = () => {
13292
+ if (this.hasAttribute("rows")) {
13293
+ this.#updateListboxMaxHeight();
13294
+ }
13285
13295
  const searchable = this.searchable;
13286
- const options = this.#getOptionElements();
13287
- const isEnoughOptions = options.length >= NUM_ITEMS_SEARCH;
13288
- const isSearchActive = isEnoughOptions && searchable !== false || Boolean(searchable);
13296
+ const isSearchActive = searchable !== false;
13289
13297
  if (!isSearchActive) {
13290
13298
  updateAttribute(this.#$search, "value", null);
13291
13299
  }
13292
13300
  setClass(this.#$search, "active", isSearchActive);
13293
13301
  this.#onValueChange(this.value);
13294
13302
  };
13303
+ #updateListboxMaxHeight = () => {
13304
+ const rowsAttr = this.getAttribute("rows");
13305
+ const numberOfItems = this.#$optionSlot.assignedElements().length;
13306
+ const maxNumberOfRows = parseInt(rowsAttr ?? "0", 10);
13307
+ this.#$listbox.style.maxHeight = attrValueToPixels(rowsAttr, {
13308
+ min: 2,
13309
+ itemSizeMultiplier: ITEM_HEIGHT,
13310
+ addExtraSpace: numberOfItems > maxNumberOfRows
13311
+ });
13312
+ };
13295
13313
  #onValueChange(csv) {
13296
13314
  if (this.multiple) {
13297
13315
  const values = unpackCsv(csv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nectary/components",
3
- "version": "5.31.3",
3
+ "version": "5.32.0",
4
4
  "files": [
5
5
  "**/*/*.css",
6
6
  "**/*/*.json",
@@ -22,8 +22,8 @@ export declare class SelectMenu extends NectaryElement {
22
22
  get rows(): number | null;
23
23
  set multiple(isMultiple: boolean);
24
24
  get multiple(): boolean;
25
- set searchable(isSearchable: boolean | null);
26
- get searchable(): boolean | null;
25
+ set searchable(isSearchable: boolean | null | undefined);
26
+ get searchable(): boolean | null | undefined;
27
27
  set 'search-autocomplete'(autocomplete: string);
28
28
  get 'search-autocomplete'(): string;
29
29
  set 'search-placeholder'(placeholder: string);
@@ -4,7 +4,7 @@ import "../text/index.js";
4
4
  import { isSelectMenuOption } from "../select-menu-option/utils.js";
5
5
  import { subscribeContext } from "../utils/context.js";
6
6
  import { unpackCsv, getFirstCsvValue, updateCsv } from "../utils/csv.js";
7
- import { getBooleanAttribute, updateAttribute, attrValueToPixels, updateExplicitBooleanAttribute, isAttrTrue, getAttribute, updateIntegerAttribute, getIntegerAttribute, updateBooleanAttribute, hasClass, setClass } from "../utils/dom.js";
7
+ import { getBooleanAttribute, updateAttribute, updateExplicitBooleanAttribute, isAttrTrue, getAttribute, updateIntegerAttribute, getIntegerAttribute, updateBooleanAttribute, hasClass, setClass, attrValueToPixels } from "../utils/dom.js";
8
8
  import { defineCustomElement, NectaryElement } from "../utils/element.js";
9
9
  import { debounceTimeout } from "../utils/debounce.js";
10
10
  import { getReactEventHandler } from "../utils/get-react-event-handler.js";
@@ -12,7 +12,6 @@ import { isTargetEqual } from "../utils/event-target.js";
12
12
  import { setFormValue, CSVToFormData } from "../utils/form.js";
13
13
  const templateHTML = '<style>:host{display:block;outline:0}#listbox{overflow-y:auto;max-height:var(--sinch-comp-select-menu-font-max-height)}#search{display:none;margin:10px}#search.active{display:block}#search-clear:not(.active){display:none}#not-found{display:flex;align-items:center;justify-content:center;width:100%;height:30px;margin-bottom:10px;pointer-events:none;user-select:none;--sinch-comp-text-font:var(--sinch-comp-select-menu-font-not-found-text);--sinch-global-color-text:var(--sinch-comp-select-menu-color-default-not-found-text-initial)}#not-found:not(.active){display:none}::slotted(.hidden){display:none}::slotted(sinch-title){padding:8px 16px;--sinch-global-color-text:var(--sinch-comp-select-menu-color-default-title-initial)}</style><sinch-input id="search" size="s" placeholder="Search"><sinch-icon icons-version="2" name="magnifying-glass" id="icon-search" slot="icon"></sinch-icon><sinch-button id="search-clear" slot="right"><sinch-icon icons-version="2" name="fa-xmark" slot="icon"></sinch-icon></sinch-button></sinch-input><div id="not-found"><sinch-text type="m">No results</sinch-text></div><div id="listbox" role="presentation"><slot></slot></div>';
14
14
  const ITEM_HEIGHT = 40;
15
- const NUM_ITEMS_SEARCH = 7;
16
15
  const template = document.createElement("template");
17
16
  template.innerHTML = templateHTML;
18
17
  class SelectMenu extends NectaryElement {
@@ -113,6 +112,7 @@ class SelectMenu extends NectaryElement {
113
112
  "value",
114
113
  "rows",
115
114
  "multiple",
115
+ "searchable",
116
116
  "search-value",
117
117
  "search-placeholder",
118
118
  "search-autocomplete"
@@ -130,6 +130,10 @@ class SelectMenu extends NectaryElement {
130
130
  this.#internals.ariaMultiSelectable = isAttrTrue(newVal).toString();
131
131
  break;
132
132
  }
133
+ case "searchable": {
134
+ this.#onOptionSlotChange();
135
+ break;
136
+ }
133
137
  case "search-autocomplete": {
134
138
  updateAttribute(this.#$search, "autocomplete", newVal);
135
139
  break;
@@ -139,13 +143,7 @@ class SelectMenu extends NectaryElement {
139
143
  break;
140
144
  }
141
145
  case "rows": {
142
- const numberOfItems = this.#$optionSlot.assignedElements().length;
143
- const maxNumberOfRows = parseInt(newVal ?? "0", 10);
144
- this.#$listbox.style.maxHeight = attrValueToPixels(newVal, {
145
- min: 2,
146
- itemSizeMultiplier: ITEM_HEIGHT,
147
- addExtraSpace: numberOfItems > maxNumberOfRows
148
- });
146
+ this.#updateListboxMaxHeight();
149
147
  break;
150
148
  }
151
149
  case "search-placeholder": {
@@ -185,7 +183,13 @@ class SelectMenu extends NectaryElement {
185
183
  return getBooleanAttribute(this, "multiple");
186
184
  }
187
185
  set searchable(isSearchable) {
188
- updateBooleanAttribute(this, "searchable", isSearchable);
186
+ if (isSearchable === false) {
187
+ this.setAttribute("searchable", "false");
188
+ } else if (isSearchable === true) {
189
+ updateBooleanAttribute(this, "searchable", true);
190
+ } else {
191
+ this.removeAttribute("searchable");
192
+ }
189
193
  }
190
194
  get searchable() {
191
195
  const searchableAttribute = this.getAttribute("searchable");
@@ -267,11 +271,14 @@ class SelectMenu extends NectaryElement {
267
271
  const $options = this.#getOptionElements();
268
272
  let someFound = false;
269
273
  for (const $opt of $options) {
270
- const isHidden = searchValue.length > 0 && !$opt.matchesSearch(searchValue);
271
- someFound ||= !isHidden;
274
+ const isAction = getBooleanAttribute($opt, "action");
275
+ const isHidden = searchValue.length > 0 && !isAction && !$opt.matchesSearch(searchValue);
276
+ if (!isAction) {
277
+ someFound ||= !isHidden;
278
+ }
272
279
  setClass($opt, "hidden", isHidden);
273
280
  }
274
- setClass(this.#$notFound, "active", !someFound);
281
+ setClass(this.#$notFound, "active", searchValue.length > 0 && !someFound);
275
282
  this.#selectOption(null);
276
283
  };
277
284
  #onContextKeyDown = (e) => {
@@ -319,16 +326,27 @@ class SelectMenu extends NectaryElement {
319
326
  }
320
327
  };
321
328
  #onOptionSlotChange = () => {
329
+ if (this.hasAttribute("rows")) {
330
+ this.#updateListboxMaxHeight();
331
+ }
322
332
  const searchable = this.searchable;
323
- const options = this.#getOptionElements();
324
- const isEnoughOptions = options.length >= NUM_ITEMS_SEARCH;
325
- const isSearchActive = isEnoughOptions && searchable !== false || Boolean(searchable);
333
+ const isSearchActive = searchable !== false;
326
334
  if (!isSearchActive) {
327
335
  updateAttribute(this.#$search, "value", null);
328
336
  }
329
337
  setClass(this.#$search, "active", isSearchActive);
330
338
  this.#onValueChange(this.value);
331
339
  };
340
+ #updateListboxMaxHeight = () => {
341
+ const rowsAttr = this.getAttribute("rows");
342
+ const numberOfItems = this.#$optionSlot.assignedElements().length;
343
+ const maxNumberOfRows = parseInt(rowsAttr ?? "0", 10);
344
+ this.#$listbox.style.maxHeight = attrValueToPixels(rowsAttr, {
345
+ min: 2,
346
+ itemSizeMultiplier: ITEM_HEIGHT,
347
+ addExtraSpace: numberOfItems > maxNumberOfRows
348
+ });
349
+ };
332
350
  #onValueChange(csv) {
333
351
  if (this.multiple) {
334
352
  const values = unpackCsv(csv);
@@ -8,7 +8,7 @@ export type TSinchSelectMenuProps = {
8
8
  rows?: number;
9
9
  /** Allows multiple selection */
10
10
  multiple?: boolean;
11
- /** Enforce the search bar appearing, by default it appears above a certain number of options */
11
+ /** Show the search bar, shown by default unless explicitly set to false */
12
12
  searchable?: boolean | null;
13
13
  /** Value for the search input */
14
14
  'search-value'?: string;