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.
- package/README.md +6 -7
- package/dist/i18n.d.ts +17 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.br +0 -0
- package/dist/index.cjs.js.gz +0 -0
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.br +0 -0
- package/dist/index.js.gz +0 -0
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.br +0 -0
- package/dist/index.umd.js.gz +0 -0
- package/dist/index.umd.js.map +1 -1
- package/dist/jb-option/jb-option.d.ts.map +1 -1
- package/dist/jb-select.d.ts +6 -4
- package/dist/jb-select.d.ts.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/types.d.ts +6 -1
- package/dist/types.d.ts.map +1 -1
- package/lib/global.d.ts +1 -1
- package/lib/i18n.ts +24 -0
- package/lib/jb-option/jb-option.ts +23 -17
- package/lib/jb-select.css +279 -0
- package/lib/jb-select.ts +64 -19
- package/lib/render.ts +20 -23
- package/lib/types.ts +6 -1
- package/lib/variables.css +6 -7
- package/package.json +5 -3
- package/react/README.md +3 -1
- package/lib/jb-select.scss +0 -354
- /package/lib/jb-option/{jb-option.scss → jb-option.css} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type JBSelectWebComponent } from '../jb-select';
|
|
2
|
-
import CSS from './jb-option.
|
|
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
|
|
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
|
|
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(".
|
|
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.
|
|
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,
|
|
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
|
|
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
|
|
286
|
-
if (
|
|
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(
|
|
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.
|
|
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 =
|
|
637
|
+
const message = dictionary.get(i18n, "requireMessage")(label || null);
|
|
593
638
|
validationList.push({
|
|
594
639
|
validator: ({ value }) => {
|
|
595
640
|
return value !== null && value !== undefined;
|