jb-select 7.2.1 → 7.3.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.
@@ -1,5 +1,6 @@
1
1
  import type { JBSelectWebComponent } from '../jb-select';
2
2
  import CSS from './jb-option.css';
3
+ import CSSVariable from './variables.css';
3
4
  import { renderHTML } from "./render";
4
5
  import type { JBOptionElements } from "./types";
5
6
  import { removeCheckboxNodes } from './utils';
@@ -104,7 +105,7 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
104
105
  mode: "open",
105
106
  serializable: true
106
107
  });
107
- const html = `<style>${CSS}</style>\n${renderHTML()}`;
108
+ const html = `<style>${CSSVariable} \n ${CSS}</style>\n${renderHTML()}`;
108
109
  const element = document.createElement("template");
109
110
  element.innerHTML = html;
110
111
  shadowRoot.appendChild(element.content.cloneNode(true));
@@ -139,6 +140,7 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
139
140
  this.#value = value as TValue;
140
141
  }
141
142
  }
143
+
142
144
  #onOptionClick() {
143
145
  if (this.#isChangeCalled) {
144
146
  this.#isChangeCalled = false;
@@ -158,6 +160,13 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
158
160
  }
159
161
  }
160
162
  }
163
+ /**
164
+ * @public
165
+ * this function used by jb-select to toggle option when it active and user hit enter to select or deselect option
166
+ */
167
+ toggleOption(){
168
+ this.#onOptionClick();
169
+ }
161
170
  #dispatchSelectEvent() {
162
171
  const event = new Event("select", { bubbles: true, cancelable: false, composed: true });
163
172
  this.dispatchEvent(event);
@@ -183,6 +192,18 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
183
192
  }
184
193
  }
185
194
  }
195
+ #active = false;
196
+ get active(){
197
+ return this.#active;
198
+ }
199
+ set active(value:boolean){
200
+ this.#active = value;
201
+ if(value){
202
+ this.#internals?.states.add("active");
203
+ }else{
204
+ this.#internals?.states.delete("active");
205
+ }
206
+ }
186
207
 
187
208
  }
188
209
  const myElementNotExists = !customElements.get("jb-option");
@@ -1,5 +1,12 @@
1
1
  :host{
2
- --background-color:var(--jb-select-option-background-color, transparent);
3
- --background-color-hover:var(--jb-select-option-background-color-hover, #1073db);
4
- --option-color-hover:var(--jb-select-option-color-hover, #fff);
2
+ /*Sizes*/
3
+ --border-radius:var(--jb-option-border-radius, 0);
4
+ --padding: var(--jb-option-padding, 0.5rem 1rem);
5
+ --font-size: var(--jb-option-font-size, 0.9rem);
6
+ --min-height: var(--jb-option-min-height, 2.25rem);
7
+ /*Colors*/
8
+ --color: var(--jb-option-color, inherit);
9
+ --color-active: var(--jb-option-color-active, var(--jb-option-color, inherit));
10
+ --background-color:var(--jb-option-background-color, transparent);
11
+ --background-color-active:var(--jb-option-background-color-active, var(--jb-primary-subtle));
5
12
  }
@@ -23,6 +23,9 @@ export class JBOptionListWebComponent<TOption, TValue> extends HTMLElement {
23
23
  this.#optionList = value;
24
24
  this.#initOptionList(value);
25
25
  }
26
+ get optionListDom(){
27
+ return Array.from(this.#optionPairMap.values());
28
+ }
26
29
  constructor() {
27
30
  super();
28
31
  this.#initWebComponent();
package/lib/jb-select.css CHANGED
@@ -213,6 +213,7 @@
213
213
 
214
214
  .popover-wrapper {
215
215
  position: relative;
216
+ --jb-popover-padding: 0;
216
217
 
217
218
  .select-list-wrapper {
218
219
  @media(--tablet-from) {
package/lib/jb-select.ts CHANGED
@@ -19,6 +19,7 @@ import { dictionary } from "./i18n";
19
19
  import { i18n } from "jb-core/i18n";
20
20
  import type { JBButtonWebComponent } from "jb-button";
21
21
  import { JBPopoverWebComponent } from "jb-popover";
22
+ import { JBOptionListWebComponent } from "./jb-option-list/jb-option-list";
22
23
 
23
24
  //TODO: add IncludeInputInList or freeSolo so user can select item that he wrote without even it exist in select list
24
25
  //TODO: handleHomeEndKeys to move focus inside the popup with the Home and End keys.
@@ -57,6 +58,21 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
57
58
  if (value === undefined) return;
58
59
  this.#popoverPosition = value;
59
60
  }
61
+ /**
62
+ * this is a expensive option list please use it when you really need optionList with order
63
+ */
64
+ get optionListWithOrder(): JBOptionWebComponent<TValue>[] {
65
+ const elements = this.elements.optionListSlot.assignedElements();
66
+ const optionList = elements.flatMap(x => {
67
+ // extract option list from JBOptionList to make option list flat by index
68
+ if (x instanceof JBOptionListWebComponent) {
69
+ return x.optionListDom;
70
+ } else {
71
+ return x
72
+ }
73
+ }).filter(x => (x instanceof JBOptionWebComponent && !x.hidden));
74
+ return optionList as JBOptionWebComponent<TValue>[];
75
+ }
60
76
  get multiple() {
61
77
  return this.hasAttribute('multiple')
62
78
  }
@@ -379,15 +395,15 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
379
395
  } else if (this.value) {
380
396
  this.#setValueFromOutside(this.value);
381
397
  }
382
- if (this.multiple && Array.isArray(this.#value) && this.#selectedOptions.values.length< this.#value.length) {
398
+ if (this.multiple && Array.isArray(this.#value) && this.#selectedOptions.values.length < this.#value.length) {
383
399
  //in this particular edge case our value is already set but some option maybe missing in first place and added later
384
- const missing:JBOptionWebComponent<TValue>[] = [];
385
- this.#optionList.forEach((op)=>{
386
- if(op.selected == false && (this.#value as unknown[]).includes(op.value)){
400
+ const missing: JBOptionWebComponent<TValue>[] = [];
401
+ this.#optionList.forEach((op) => {
402
+ if (op.selected == false && (this.#value as unknown[]).includes(op.value)) {
387
403
  missing.push(op);
388
404
  }
389
405
  });
390
- if(missing.length>0){
406
+ if (missing.length > 0) {
391
407
  this.#setSelectedOption(missing);
392
408
  }
393
409
  }
@@ -503,6 +519,8 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
503
519
  const event = createKeyboardEvent("keypress", e, {})
504
520
  this.dispatchEvent(event);
505
521
  }
522
+
523
+
506
524
  #onInputBeforeInput(_e: InputEvent) {
507
525
  // const inputtedText = e.data || "";
508
526
  //TODO: add cancelable event dispatch here
@@ -526,9 +544,60 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
526
544
  //because on keypress dont receive backspace key press
527
545
  this.#handleSelectedValueDisplay(inputText);
528
546
  }
529
-
547
+ switch (e.key) {
548
+ case "ArrowUp":
549
+ this.#activePrevOption();
550
+ break;
551
+ case "ArrowDown":
552
+ this.#activeNextOption();
553
+ break;
554
+ case "Enter":
555
+ this.#optionList.forEach(x => {
556
+ if (x.active) {x.toggleOption();}
557
+ })
558
+ break;
559
+ }
530
560
  this.#triggerOnInputKeyup(e);
531
561
  }
562
+ /**
563
+ * used when change activeItem with arrow keys
564
+ */
565
+ #activePrevOption() {
566
+ const optionList = this.optionListWithOrder;
567
+ const activeOption = optionList.find((option, index) => {
568
+ if (option.active) {
569
+ if (optionList[index - 1]) {
570
+ option.active = false;
571
+ optionList[index - 1].active = true;
572
+ optionList[index - 1].scrollIntoView({ block: "nearest" });
573
+ }
574
+ return true;
575
+ }
576
+ return false
577
+ });
578
+ if (!activeOption && optionList[optionList.length - 1]) {
579
+ optionList[optionList.length - 1].active = true;
580
+ optionList[optionList.length - 1].scrollIntoView({ block: "nearest" });
581
+ }
582
+ }
583
+ /**
584
+ * used when change activeItem with arrow keys
585
+ */
586
+ #activeNextOption() {
587
+ const optionList = this.optionListWithOrder;
588
+ const activeOption = optionList.find((option, index) => {
589
+ if (option.active) {
590
+ if (optionList[index + 1]) {
591
+ option.active = false;
592
+ optionList[index + 1].active = true;
593
+ optionList[index + 1].scrollIntoView({ block: "nearest" });
594
+ }
595
+ return true;
596
+ }
597
+ return false
598
+ });
599
+ if (!activeOption && optionList[0]) { optionList[0].active = true; optionList[0].scrollIntoView({ block: "nearest" }) }
600
+ }
532
601
  #handleSelectedValueDisplay(inputValue: string) {
533
602
  if (inputValue !== "") {
534
603
  this.elements.selectedValueWrapper.classList.add("--search-typed");
@@ -599,6 +668,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
599
668
  }
600
669
  }
601
670
  this.elements.input.blur();
671
+ this.#optionList.forEach(x => { x.active = false })
602
672
  }
603
673
  #showOptionList() {
604
674
  this.#internals.states.add("open")
@@ -637,13 +707,13 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
637
707
  target.selected = false;
638
708
  this.#selectedOptions.delete(e.target as JBOptionWebComponent<TValue>)
639
709
  this.#updateSelectedOptionDom();
640
- if(Array.isArray(this.#value)){
710
+ if (Array.isArray(this.#value)) {
641
711
  const index = this.#value.indexOf(target.value);
642
- if(index !== -1) this.#value.splice(index,1);
643
- }else if(this.value === target.value){
712
+ if (index !== -1) this.#value.splice(index, 1);
713
+ } else if (this.value === target.value) {
644
714
  this.#value = null;
645
715
  }
646
- this.#value=this.#value
716
+ this.#value = this.#value
647
717
  this.#checkValidity(true);
648
718
  }
649
719
  //called when an jb-Option connected to the dom
@@ -657,6 +727,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
657
727
  this.#setValueOnOptionListChanged();
658
728
  }
659
729
  this.#updateListEmptyPlaceholder();
730
+ target.addEventListener("mouseenter",this.#onOptionHover)
660
731
  }
661
732
  #onOptionDisconnected(e: CustomEvent) {
662
733
  e.stopPropagation();
@@ -666,14 +737,21 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
666
737
  if (target.value == this.#value) {
667
738
  this.#setValueOnOptionListChanged();
668
739
  }
740
+ target.removeEventListener("mouseenter",this.#onOptionHover)
741
+ }
742
+ #onOptionHover = (e:MouseEvent)=>{
743
+ const target = e.target as JBOptionWebComponent<TValue>;
744
+ if(!target.active){
745
+ this.#optionList.forEach(x=>{x.active = false});
746
+ }
747
+ target.active = true;
669
748
  }
670
-
671
749
  #selectOption(value: TValue, optionDom: JBOptionWebComponent<TValue>) {
672
- if(this.multiple){
673
- if(Array.isArray(this.#value)){
674
- value = [...this.#value,value] as TValue
675
- }else{
676
- value = [this.#value,value] as TValue
750
+ if (this.multiple) {
751
+ if (Array.isArray(this.#value)) {
752
+ value = [...this.#value, value] as TValue
753
+ } else {
754
+ value = [this.#value, value] as TValue
677
755
  }
678
756
  }
679
757
  this.#setValue(value, optionDom);
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "web component",
17
17
  "react component"
18
18
  ],
19
- "version": "7.2.1",
19
+ "version": "7.3.0",
20
20
  "bugs": "https://github.com/javadbat/jb-select/issues",
21
21
  "homepage": "https://javadbat.github.io/design-system/?path=/story/components-form-elements-jbselect",
22
22
  "license": "MIT",
@@ -37,7 +37,7 @@
37
37
  "dependencies": {
38
38
  "jb-validation": ">=0.4.0",
39
39
  "jb-button": ">=3.9.1",
40
- "jb-popover": ">=1.7.1",
40
+ "jb-popover": ">=1.12.0",
41
41
  "jb-core":">=0.27.2"
42
42
  },
43
43
  "devDependencies": {