jb-select 6.4.2 → 7.0.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,5 @@
1
1
  import { type JBSelectWebComponent } from '../jb-select';
2
- import CSS from './jb-option.scss';
2
+ import CSS from './jb-option.css';
3
3
  import { renderHTML } from "./render";
4
4
  import { JBOptionElements } from "./types";
5
5
 
@@ -12,6 +12,7 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
12
12
  // it may be empty
13
13
  #SelectElement?: JBSelectWebComponent
14
14
  #value: TValue;
15
+ #internals?: ElementInternals;
15
16
  get value(): TValue {
16
17
  return this.#value;
17
18
  }
@@ -21,30 +22,30 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
21
22
  #selected = false;
22
23
  set selected(value: boolean) {
23
24
  this.#selected = value;
24
- if(value){
25
+ if (value) {
25
26
  this.#elements.componentWrapper.classList.add("--selected");
26
- }else{
27
+ } else {
27
28
  this.#elements.componentWrapper.classList.remove("--selected");
28
29
  }
29
30
  }
30
31
  get selected() {
31
32
  return this.#selected;
32
33
  }
33
- get optionContent():Node[]{
34
+ get optionContent(): Node[] {
34
35
  const optionNodes = this.#elements.contentWrapper.querySelector("slot").assignedNodes();
35
36
  return optionNodes;
36
37
  }
37
38
  //TODO: add search hidden property for more accurate hidden and more personalized logic
38
39
  #hidden = false;
39
- get hidden(){
40
+ get hidden() {
40
41
  return this.#hidden;
41
42
  }
42
- set hidden(value:boolean){
43
+ set hidden(value: boolean) {
43
44
  this.#hidden = value;
44
- if(value){
45
+ if (value) {
45
46
  this.#elements.componentWrapper.classList.add('--hidden');
46
- this.setAttribute("inert","");
47
- }else{
47
+ this.setAttribute("inert", "");
48
+ } else {
48
49
  this.#elements.componentWrapper.classList.remove('--hidden');
49
50
  this.removeAttribute("inert");
50
51
  }
@@ -52,15 +53,20 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
52
53
  /**
53
54
  * return text content of option (it used in search by default to filter option)
54
55
  */
55
- get optionContentText(){
56
- const optionTextContent = this.optionContent.reduce((acc,item)=>{
56
+ get optionContentText() {
57
+ const optionTextContent = this.optionContent.reduce((acc, item) => {
57
58
  acc += item.textContent;
58
59
  return acc;
59
- },"");
60
+ }, "");
60
61
  return optionTextContent;
61
62
  }
62
63
  constructor() {
63
64
  super();
65
+ if (typeof this.attachInternals == "function") {
66
+ //some browser don't support attachInternals
67
+ this.#internals = this.attachInternals();
68
+ this.#internals.role = "option";
69
+ }
64
70
  this.#initWebComponent();
65
71
  this.#initProp();
66
72
  }
@@ -75,18 +81,18 @@ export class JBOptionWebComponent<TValue> extends HTMLElement {
75
81
  this.#SelectElement.addEventListener("filter-change", this.#onFilterChange.bind(this));
76
82
  }
77
83
  }
78
- #onFilterChange(e: CustomEvent){
79
- const {filterText} = e.detail;
84
+ #onFilterChange(e: CustomEvent) {
85
+ const { filterText } = e.detail;
80
86
  const optionTextContent = this.optionContentText.toLowerCase();
81
- if(optionTextContent.includes(filterText.toLowerCase())){
87
+ if (optionTextContent.includes(filterText.toLowerCase())) {
82
88
  this.hidden = false;
83
- }else{
89
+ } else {
84
90
  this.hidden = true;
85
91
  }
86
92
  }
87
93
  disconnectedCallback() {
88
94
  this.#SelectElement?.removeEventListener("filter-change", this.#onFilterChange.bind(this));
89
- const event = new CustomEvent("jb-option-disconnected",{bubbles:true,composed:true,cancelable:false});
95
+ const event = new CustomEvent("jb-option-disconnected", { bubbles: true, composed: true, cancelable: false });
90
96
  this.dispatchEvent(event);
91
97
  }
92
98
  #initWebComponent() {
@@ -0,0 +1,279 @@
1
+ /* @use "~jb-core/styles/medias.scss" as *; */
2
+
3
+ @custom-media --tablet-until (max-width: 768px);
4
+ @custom-media --tablet-from (min-width: 769px);
5
+
6
+ .jb-select-web-component {
7
+ width: var(--jb-select-width, 100%);
8
+ margin: var(--jb-select-margin, 0 0);
9
+ box-sizing: border-box;
10
+
11
+ &.--has-value {
12
+
13
+ /*if user select a option and value is set and not null*/
14
+ .select-box {
15
+ border-color: var(--jb-select-border-color-selected, var(--border-color));
16
+ background-color: var(--jb-select-bgcolor-selected, var(--select-box-bg-color));
17
+ }
18
+
19
+ .select-box .end-section .clear-button {
20
+ display: block;
21
+ }
22
+ }
23
+
24
+ .label-wrapper {
25
+ label {
26
+ width: 100%;
27
+ margin: 4px 0px;
28
+ display: block;
29
+ font-size: var(--jb-select-label-font-size, 0.8em);
30
+ font-weight: var(--jb-select-label-font-weight, normal);
31
+ color: var(--label-color);
32
+
33
+ &.--hide {
34
+ display: none;
35
+ }
36
+ }
37
+ }
38
+
39
+ &:focus-within .select-box {
40
+
41
+ @media(--tablet-from) {
42
+ /*when there is focus on whole select means menu is open so we change select styles*/
43
+ border-color: var(--border-color);
44
+ border-bottom-color: transparent;
45
+ border-radius: var(--rounded) var(--rounded) 0 0;
46
+ }
47
+ }
48
+
49
+ .select-box {
50
+ width: 100%;
51
+ box-sizing: border-box;
52
+ height: var(--height);
53
+ border: solid var(--border-width) var(--border-color);
54
+ border-bottom: solid var(--border-bottom-width) var(--border-color);
55
+ border-radius: var(--box-border-radius);
56
+ background-color: var(--select-box-bg-color);
57
+ margin: var(--jb-select-select-box-margin, 4px 0px 0px 0px);
58
+ overflow: hidden;
59
+ display: flex;
60
+ padding-inline-end: var(--jb-select-box-padding-end, 1rem);
61
+ gap: 0.5rem;
62
+ align-items: center;
63
+
64
+ .start-section {
65
+ height: 100%;
66
+ width: auto;
67
+ display: flex;
68
+ justify-content: center;
69
+ align-items: center;
70
+ }
71
+
72
+ .middle-section {
73
+ position: relative;
74
+ width: 100%;
75
+ height: 100%;
76
+ flex: 1;
77
+
78
+ .selected-value-wrapper {
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ width: 100%;
83
+ height: 100%;
84
+ border-radius: inherit;
85
+ overflow: hidden;
86
+ z-index: 1;
87
+
88
+ &.--search-typed {
89
+ opacity: 0;
90
+ }
91
+
92
+ .selected-value {
93
+ width: 100%;
94
+ box-sizing: border-box;
95
+ height: 100%;
96
+ background-color: transparent;
97
+ padding: 2px 12px 0 12px;
98
+ display: block;
99
+ font-family: inherit;
100
+ font-size: var(--jb-select-selected-value-font-size, 1.1em);
101
+ color: var(--value-color);
102
+ margin: 0;
103
+ border-radius: 0;
104
+ display: flex;
105
+ align-items: center;
106
+ }
107
+ }
108
+
109
+ .front-box {
110
+ position: absolute;
111
+ top: 0;
112
+ left: 0;
113
+ width: 100%;
114
+ height: 100%;
115
+ border-radius: inherit;
116
+ overflow: hidden;
117
+ z-index: 2;
118
+
119
+ .search-input {
120
+ height: 100%;
121
+ background-color: transparent;
122
+ padding: 2px 12px 0 12px;
123
+ display: block;
124
+ margin: 0;
125
+ border-radius: 0;
126
+
127
+ &:focus {
128
+ outline: none;
129
+ }
130
+
131
+ &::placeholder {
132
+ color: var(--placeholder-color);
133
+ font-size: var(--jb-select-placeholder-font-size, 1.1em);
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ .end-section {
140
+ display: flex;
141
+ gap: 1.5rem;
142
+ align-items: center;
143
+
144
+ .arrow-icon {
145
+ margin: var(--jb-select-arrow-icon-margin, 0 0 0 0);
146
+ }
147
+
148
+ .clear-button {
149
+ /*show when there is value*/
150
+ display: none;
151
+ --jb-button-padding-xs: 0;
152
+ --jb-button-height-xs: 1rem;
153
+ }
154
+ }
155
+
156
+
157
+ &:focus-within .selected-value {
158
+ opacity: 0.7;
159
+ transition: all 0.3s ease;
160
+ }
161
+
162
+
163
+ }
164
+
165
+ .message-box {
166
+ font-size: var(--jb-select-message-font-size, 0.7em);
167
+ font-weight: var(--jb-select-message-font-weight, normal);
168
+ padding: 4px 8px;
169
+ color: var(--message-color);
170
+
171
+ &:empty {
172
+ padding: 0;
173
+ }
174
+
175
+ &.--error {
176
+ color: red;
177
+ }
178
+ }
179
+
180
+ .popover-wrapper {
181
+ position: relative;
182
+ .select-list-wrapper {
183
+ @media(--tablet-from) {
184
+ width: 100%;
185
+ --jb-popover-border-radius: var(--list-border-radius);
186
+ --jb-popover-bg-color: var(--select-box-bg-color);
187
+ --jb-popover-top: 0;
188
+ }
189
+
190
+ &::part(content) {
191
+
192
+ @media(--tablet-from) {
193
+ /*in desktop*/
194
+ border: solid var(--border-width) var(--border-color);
195
+ border-bottom: solid var(--border-bottom-width) var(--border-color);
196
+ border-top: none;
197
+ }
198
+ }
199
+
200
+ .mobile-search-input-wrapper {
201
+ display: none;
202
+
203
+ @media(--tablet-until) {
204
+ display: block;
205
+ margin-block-end: 1rem;
206
+ margin-inline: 0.75rem;
207
+
208
+ .search-input {
209
+ background-color: var(--jb-select-mobile-input-bgcolor, var(--select-box-bg-color));
210
+ border-radius: var(--box-border-radius);
211
+ width: 100%;
212
+ height: var(--height);
213
+ }
214
+ }
215
+
216
+ }
217
+
218
+
219
+ .select-list {
220
+ width: 100%;
221
+ max-height: var(--jb-select-list-max-height, 400px);
222
+ overflow-y: auto;
223
+
224
+ slot {
225
+ padding: var(--jb-select-list-padding, 16px 0);
226
+
227
+ &:empty {
228
+ padding: 0;
229
+ }
230
+ }
231
+
232
+ /* option style places */
233
+
234
+ &::-webkit-scrollbar {
235
+ width: 9px;
236
+ background-color: transparent;
237
+ }
238
+
239
+ &::-webkit-scrollbar-thumb {
240
+ background-color: var(--list-scroll-color);
241
+ border-radius: var(--jb-select-list-scroll-border-radius, 4px);
242
+ }
243
+ }
244
+
245
+ .empty-list-placeholder {
246
+ display: none;
247
+ text-align: center;
248
+ color: var(--empty-list-placeholder-color);
249
+ font-style: italic;
250
+ padding: 8px 0;
251
+
252
+ &.--show {
253
+ display: block;
254
+ }
255
+ }
256
+
257
+ }
258
+ }
259
+
260
+ .search-input {
261
+ border: none;
262
+ width: 100%;
263
+ box-sizing: border-box;
264
+ padding: 2px 12px 0 12px;
265
+ display: block;
266
+ font-family: inherit;
267
+ font-size: var(--jb-select-value-font-size, 1.1rem);
268
+ color: var(--value-color);
269
+
270
+ &:focus {
271
+ outline: none;
272
+ }
273
+
274
+ &::placeholder {
275
+ color: var(--placeholder-color);
276
+ font-size: var(--jb-select-placeholder-font-size, 1.1em);
277
+ }
278
+ }
279
+ }
package/lib/jb-select.ts CHANGED
@@ -1,22 +1,31 @@
1
- import CSS from "./jb-select.scss";
2
- import {
1
+ import "jb-button";
2
+ import "jb-popover";
3
+ import CSS from "./jb-select.css";
4
+ import VariablesCSS from "./variables.css";
5
+ import type {
3
6
  JBSelectCallbacks,
4
7
  JBSelectElements,
5
8
  ValidationValue,
6
9
  } from "./types";
7
- import { ShowValidationErrorParameters, ValidationHelper, type ValidationItem, type ValidationResult, type WithValidation } from "jb-validation";
10
+ import { type ShowValidationErrorParameters, ValidationHelper, type ValidationItem, type ValidationResult, type WithValidation } from "jb-validation";
8
11
  import { isMobile } from "jb-core";
9
- import { JBFormInputStandards } from 'jb-form';
12
+ import type { JBFormInputStandards } from 'jb-form';
10
13
  // eslint-disable-next-line no-duplicate-imports
11
14
  import { JBOptionWebComponent } from "./jb-option/jb-option";
12
15
  import { registerDefaultVariables } from 'jb-core/theme';
13
16
  import { renderHTML } from "./render";
17
+ import { dictionary } from "./i18n";
18
+ import { i18n } from "jb-core/i18n";
19
+ import type { JBButtonWebComponent } from "jb-button";
14
20
 
21
+ //TODO: add clean button to empty the select box after value selection
15
22
  //TODO: add IncludeInputInList or freeSolo so user can select item that he wrote without even it exist in select list
16
23
  //TODO: handleHomeEndKeys to move focus inside the popup with the Home and End keys.
17
24
  /**
18
25
  * TValue is the type of value we extract from option
19
26
  */
27
+
28
+ // biome-ignore lint/suspicious/noExplicitAny: <we support any type of value and there is no limitation on value type>
20
29
  export class JBSelectWebComponent<TValue = any> extends HTMLElement implements WithValidation<ValidationValue<TValue>>, JBFormInputStandards<TValue> {
21
30
  static get formAssociated() {
22
31
  return true;
@@ -63,6 +72,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
63
72
  }
64
73
  set placeholder(value: string) {
65
74
  this.#placeholder = value;
75
+ this.#internals.ariaPlaceholder = value;
66
76
  if (this.value !== null && this.value !== undefined) {
67
77
  this.elements.input.placeholder = "";
68
78
  } else {
@@ -111,13 +121,16 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
111
121
  this.elements.input.disabled = value;
112
122
  if (value) {
113
123
  (this.#internals as any).states?.add("disabled");
124
+ this.#internals.ariaDisabled = "true";
114
125
  } else {
115
126
  (this.#internals as any).states?.delete("disabled");
127
+ this.#internals.ariaDisabled = "false";
116
128
  }
117
129
  }
118
130
  #required = false;
119
131
  set required(value: boolean) {
120
132
  this.#required = value;
133
+ this.#internals.ariaRequired = value ? "true" : "false";
121
134
  this.#validation.checkValiditySync({ showError: false });
122
135
  }
123
136
  get required() {
@@ -143,6 +156,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
143
156
  if (typeof this.attachInternals == "function") {
144
157
  //some browser dont support attachInternals
145
158
  this.#internals = this.attachInternals();
159
+ this.#internals.role = "combobox"
146
160
  }
147
161
  this.#initWebComponent();
148
162
  this.#initProp();
@@ -166,29 +180,45 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
166
180
  delegatesFocus: true,
167
181
  });
168
182
  registerDefaultVariables();
169
- const html = `<style>${CSS}</style>` + "\n" + renderHTML();
183
+ const html = `<style>${CSS} ${VariablesCSS}</style>\n${renderHTML()}`;
170
184
  const element = document.createElement("template");
171
185
  element.innerHTML = html;
172
186
  shadowRoot.appendChild(element.content.cloneNode(true));
173
187
  this.elements = {
174
- input: shadowRoot.querySelector(".select-box input")!,
188
+ input: shadowRoot.querySelector(".search-input")!,
175
189
  componentWrapper: shadowRoot.querySelector(".jb-select-web-component")!,
176
- selectedValueWrapper: shadowRoot.querySelector(
177
- ".selected-value-wrapper"
178
- )!,
190
+ selectedValueWrapper: shadowRoot.querySelector(".selected-value-wrapper")!,
179
191
  messageBox: shadowRoot.querySelector(".message-box")!,
180
192
  optionList: shadowRoot.querySelector(".select-list")!,
181
193
  optionListWrapper: shadowRoot.querySelector(".select-list-wrapper")!,
182
194
  optionListSlot: shadowRoot.querySelector(".select-list-wrapper .select-list slot")!,
183
195
  arrowIcon: shadowRoot.querySelector(".arrow-icon")!,
196
+ clearButton: shadowRoot.querySelector(".clear-button") as JBButtonWebComponent,
184
197
  label: {
185
198
  wrapper: shadowRoot.querySelector("label")!,
186
199
  text: shadowRoot.querySelector("label .label-value")!,
187
200
  },
188
201
  emptyListPlaceholder: shadowRoot.querySelector(".empty-list-placeholder")!,
202
+ mobileSearchInputWrapper: shadowRoot.querySelector(".mobile-search-input-wrapper"),
203
+ frontBox: shadowRoot.querySelector(".front-box"),
189
204
  };
190
205
  this.#registerEventListener();
191
206
  this.#updateListEmptyPlaceholder();
207
+ this.#setupDeviceRelates();
208
+ }
209
+ /**
210
+ * place code that change on select resize between mobile & desktop
211
+ */
212
+ #setupDeviceRelates() {
213
+ const onResize = ()=> {
214
+ if (isMobile()) {
215
+ this.elements.mobileSearchInputWrapper.appendChild(this.elements.input)
216
+ } else {
217
+ this.elements.frontBox.appendChild(this.elements.input);
218
+ }
219
+ }
220
+ addEventListener("resize", onResize);
221
+ onResize();
192
222
  }
193
223
  #registerEventListener() {
194
224
  this.elements.input.addEventListener("change", (e: Event) => {
@@ -198,9 +228,10 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
198
228
  this.elements.input.addEventListener("keyup", this.#onInputKeyup.bind(this));
199
229
  this.elements.input.addEventListener("beforeinput", this.#onInputBeforeInput.bind(this));
200
230
  this.elements.input.addEventListener("input", (e) => { this.#onInputInput(e as unknown as InputEvent); });
201
- this.elements.input.addEventListener("focus", this.#onInputFocus.bind(this));
231
+ this.addEventListener("focus", this.#onSelectFocus.bind(this));
202
232
  this.elements.input.addEventListener("blur", this.#onInputBlur.bind(this));
203
233
  this.elements.arrowIcon.addEventListener("click", this.#onArrowKeyClick.bind(this));
234
+ this.elements.clearButton.addEventListener("click", this.#onClearButtonClick.bind(this));
204
235
  //events to work with options
205
236
  this.addEventListener("select", this.#onOptionSelect.bind(this));
206
237
  this.addEventListener("jb-option-connected", this.#onOptionConnected.bind(this));
@@ -223,7 +254,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
223
254
  "error",
224
255
  ];
225
256
  }
226
- attributeChangedCallback(name: string, oldValue: string, newValue: string) {
257
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
227
258
  // do something when an attribute has changed
228
259
  this.#onAttributeChange(name, newValue);
229
260
  }
@@ -231,6 +262,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
231
262
  switch (name) {
232
263
  case "label":
233
264
  this.elements.label.text.innerHTML = value;
265
+ this.#internals.ariaLabel = value;
234
266
  if (value == null || value == undefined || value == "") {
235
267
  this.elements.label.wrapper.classList.add("--hide");
236
268
  } else {
@@ -238,6 +270,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
238
270
  }
239
271
  break;
240
272
  case "message":
273
+ this.#internals.ariaDescription = value;
241
274
  this.elements.messageBox.innerHTML = value;
242
275
  break;
243
276
  case "value":
@@ -252,6 +285,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
252
285
  break;
253
286
  case "placeholder":
254
287
  this.placeholder = value;
288
+ this.#internals.ariaPlaceholder = value;
255
289
  break;
256
290
  case "search-placeholder":
257
291
  this.searchPlaceholder = value;
@@ -278,12 +312,12 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
278
312
  }
279
313
  #setValueOnOptionListChanged() {
280
314
  //when option list changed we see if current value is valid for new optionlist we set it if not we reset value to null.
281
- //in some scenario value is setted before optionList attached so we store it on this.#notFoundedValue and after option list setted we set value from this.#notFoundedValue
315
+ //in some scenario value is set before optionList attached so we store it on this.#notFoundedValue and after option list set we set value from this.#notFoundedValue
282
316
  if (this.#notFoundedValue) {
283
317
  //if select has no prev value or pending not found value we don't set it because user may input some search terms in input box and developer-user update list base on that value
284
318
  //if we set it to null the search term and this.textValue will become null and empty too and it make impossible for user to search in dynamic back-end provided searchable list so we put this condition to prevent it
285
- const isSetted = this.#setValueFromOutside(this.#notFoundedValue);
286
- if (isSetted) {
319
+ const isSet = this.#setValueFromOutside(this.#notFoundedValue);
320
+ if (isSet) {
287
321
  //after list update and when not founded value is found in new option list we clear old not founded value
288
322
  this.#notFoundedValue = null;
289
323
  }
@@ -314,7 +348,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
314
348
  }
315
349
  //null option mean deselect all
316
350
  #changeSelectedOption(option: JBOptionWebComponent<TValue> | null) {
317
- this.#optionList.forEach((x) => x.selected = false);
351
+ this.#optionList.forEach((x) => { x.selected = false });
318
352
  if (option) {
319
353
  option.selected = true;
320
354
  this.#selectedOption = option;
@@ -353,6 +387,13 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
353
387
  this.focus();
354
388
  }
355
389
  }
390
+ #onClearButtonClick(e:MouseEvent) {
391
+ e.stopPropagation();
392
+ e.preventDefault();
393
+ this.#setValue(null,null);
394
+ this.#checkValidity(true);
395
+ this.#dispatchOnChangeEvent();
396
+ }
356
397
  #onInputKeyPress(e: KeyboardEvent) {
357
398
  const eventOptions: KeyboardEventInit = {
358
399
  altKey: e.altKey,
@@ -373,7 +414,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
373
414
  const event = new KeyboardEvent("keypress", eventOptions);
374
415
  this.dispatchEvent(event);
375
416
  }
376
- #onInputBeforeInput(e: InputEvent) {
417
+ #onInputBeforeInput(_e: InputEvent) {
377
418
  // const inputtedText = e.data || "";
378
419
  //TODO: add cancelable event dispatch here
379
420
  }
@@ -443,6 +484,9 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
443
484
  //here is the rare time we update _text_value directly because we want trigger event that may read value directly from dom
444
485
  this.#textValue = inputText;
445
486
  }
487
+ #onSelectFocus() {
488
+ this.focus();
489
+ }
446
490
  #onInputFocus() {
447
491
  this.focus();
448
492
  }
@@ -460,13 +504,14 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
460
504
  focus() {
461
505
  this.elements.input.focus();
462
506
  this.#showOptionList();
463
- this.elements.componentWrapper.classList.add("--focused");
507
+ this.elements.optionListWrapper.open();
464
508
  if (this.isMobileDevice) {
465
509
  this.elements.input.placeholder = this.#searchPlaceholder;
466
510
  }
467
511
  }
468
512
  blur() {
469
- this.elements.componentWrapper.classList.remove("--focused");
513
+ // this.elements.componentWrapper.classList.remove("--focused");
514
+ this.elements.optionListWrapper.close();
470
515
  this.textValue = "";
471
516
  this.#handleSelectedValueDisplay("");
472
517
  this.#hideOptionList();
@@ -589,7 +634,7 @@ export class JBSelectWebComponent<TValue = any> extends HTMLElement implements W
589
634
  }
590
635
  if (this.required) {
591
636
  const label = this.getAttribute("label") || "";
592
- const message = `${label} حتما باید انتخاب شود`;
637
+ const message = dictionary.get(i18n, "requireMessage")(label || null);
593
638
  validationList.push({
594
639
  validator: ({ value }) => {
595
640
  return value !== null && value !== undefined;