jb-select 5.3.1 → 6.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 +67 -29
- package/dist/index.cjs.js +2 -0
- package/dist/index.cjs.js.br +0 -0
- package/dist/index.cjs.js.gz +0 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.br +0 -0
- package/dist/index.js.gz +0 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +2 -0
- package/dist/index.umd.js.br +0 -0
- package/dist/index.umd.js.gz +0 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/web-component/jb-select/lib/index.d.ts +7 -0
- package/dist/web-component/jb-select/lib/jb-option/jb-option.d.ts +21 -0
- package/dist/web-component/jb-select/lib/jb-option/render.d.ts +1 -0
- package/dist/web-component/jb-select/lib/jb-option/types.d.ts +4 -0
- package/dist/web-component/jb-select/lib/jb-option-list/jb-option-list.d.ts +10 -0
- package/dist/web-component/jb-select/lib/jb-option-list/types.d.ts +5 -0
- package/dist/web-component/jb-select/lib/jb-select.d.ts +3 -7
- package/dist/web-component/jb-select/lib/types.d.ts +6 -10
- package/index.js +2 -2
- package/lib/index.ts +8 -0
- package/lib/jb-option/jb-option.scss +23 -0
- package/lib/jb-option/jb-option.ts +136 -0
- package/lib/jb-option/render.ts +9 -0
- package/lib/jb-option/types.ts +4 -0
- package/lib/jb-option-list/jb-option-list.ts +125 -0
- package/lib/jb-option-list/types.ts +5 -0
- package/lib/jb-select.html +1 -1
- package/lib/jb-select.scss +36 -45
- package/lib/jb-select.ts +165 -238
- package/lib/types.ts +8 -9
- package/package.json +6 -4
- package/react/README.md +188 -0
- package/react/dist/common/hooks/use-event.d.ts +3 -0
- package/react/dist/common/hooks/useLazyRef.d.ts +4 -0
- package/react/dist/common/hooks/useMobx.d.ts +4 -0
- package/react/dist/common/scripts/device-detection.d.ts +1 -0
- package/react/dist/common/scripts/persian-helper.d.ts +3 -0
- package/react/dist/index.cjs.js +122 -0
- package/react/dist/index.cjs.js.map +1 -0
- package/react/dist/index.js +118 -0
- package/react/dist/index.js.map +1 -0
- package/react/dist/index.umd.js +125 -0
- package/react/dist/index.umd.js.map +1 -0
- package/react/dist/web-component/jb-select/react/lib/JBOption.d.ts +19 -0
- package/react/dist/web-component/jb-select/react/lib/JBOptionList.d.ts +22 -0
- package/react/dist/web-component/jb-select/react/lib/JBSelect.d.ts +38 -0
- package/react/dist/web-component/jb-select/react/lib/index.d.ts +3 -0
- package/react/index.js +1 -0
- package/react/lib/JBOption.tsx +45 -0
- package/react/lib/JBOptionList.tsx +59 -0
- package/react/lib/JBSelect.tsx +105 -0
- package/react/lib/index.tsx +3 -0
- package/react/package.json +33 -0
- package/react/tsconfig.json +19 -0
- package/dist/jb-select.cjs.js +0 -2
- package/dist/jb-select.cjs.js.br +0 -0
- package/dist/jb-select.cjs.js.gz +0 -0
- package/dist/jb-select.cjs.js.map +0 -1
- package/dist/jb-select.js +0 -2
- package/dist/jb-select.js.br +0 -0
- package/dist/jb-select.js.gz +0 -0
- package/dist/jb-select.js.map +0 -1
- package/dist/jb-select.umd.js +0 -2
- package/dist/jb-select.umd.js.br +0 -0
- package/dist/jb-select.umd.js.gz +0 -0
- package/dist/jb-select.umd.js.map +0 -1
package/lib/jb-select.ts
CHANGED
|
@@ -3,45 +3,38 @@ import CSS from "./jb-select.scss";
|
|
|
3
3
|
import {
|
|
4
4
|
JBSelectCallbacks,
|
|
5
5
|
JBSelectElements,
|
|
6
|
-
JBSelectOptionElement,
|
|
7
6
|
ValidationValue,
|
|
8
7
|
} from "./types";
|
|
9
|
-
import {ShowValidationErrorInput, ValidationHelper, type ValidationItem, type ValidationResult, type WithValidation} from "jb-validation";
|
|
8
|
+
import { ShowValidationErrorInput, ValidationHelper, type ValidationItem, type ValidationResult, type WithValidation } from "jb-validation";
|
|
10
9
|
import { isMobile } from "../../../common/scripts/device-detection";
|
|
11
|
-
import {JBFormInputStandards} from 'jb-form';
|
|
10
|
+
import { JBFormInputStandards } from 'jb-form';
|
|
11
|
+
// eslint-disable-next-line no-duplicate-imports
|
|
12
|
+
import { JBOptionWebComponent } from "./jb-option/jb-option";
|
|
13
|
+
// eslint-disable-next-line no-duplicate-imports
|
|
12
14
|
//TOption is the type of option, TValue is the type of value we extract from option
|
|
13
|
-
export class JBSelectWebComponent<
|
|
15
|
+
export class JBSelectWebComponent<TValue = any> extends HTMLElement implements WithValidation<ValidationValue<TValue>>, JBFormInputStandards<TValue> {
|
|
14
16
|
static get formAssociated() {
|
|
15
17
|
return true;
|
|
16
18
|
}
|
|
17
19
|
// we keep selected option here by option but we return TValue when user demand
|
|
18
|
-
#value:
|
|
20
|
+
#value: TValue;
|
|
19
21
|
#textValue = "";
|
|
20
22
|
// if user set value and current option list is not contain the option.
|
|
21
23
|
// we hold it in _notFoundedValue and select value when option value get updated
|
|
22
24
|
#notFoundedValue: TValue = null;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}else{
|
|
28
|
-
console.error("title must be string please provide a valid getOptionTitle","provided title:",option);
|
|
29
|
-
return "NOT SUPPORTED TITLE TYPE";
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
getOptionValue: null,
|
|
33
|
-
getOptionDOM: null,
|
|
34
|
-
getSelectedValueDOM: null,
|
|
35
|
-
};
|
|
25
|
+
#optionList = new Set<JBOptionWebComponent<TValue>>()
|
|
26
|
+
//keep selected option dom
|
|
27
|
+
#selectedOption: JBOptionWebComponent<TValue> | null = null;
|
|
28
|
+
callbacks: JBSelectCallbacks<TValue> = {}
|
|
36
29
|
elements!: JBSelectElements;
|
|
37
|
-
get value():TValue{
|
|
30
|
+
get value(): TValue {
|
|
38
31
|
if (this.#value) {
|
|
39
|
-
return this.#
|
|
32
|
+
return this.#value;
|
|
40
33
|
} else {
|
|
41
34
|
return null;
|
|
42
35
|
}
|
|
43
36
|
}
|
|
44
|
-
set value(value:TValue) {
|
|
37
|
+
set value(value: TValue) {
|
|
45
38
|
this.#setValueFromOutside(value);
|
|
46
39
|
}
|
|
47
40
|
get textValue() {
|
|
@@ -54,29 +47,11 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
54
47
|
}
|
|
55
48
|
get selectedOptionTitle() {
|
|
56
49
|
if (this.value) {
|
|
57
|
-
return this.#
|
|
50
|
+
return this.#selectedOption.optionContentText;
|
|
58
51
|
} else {
|
|
59
52
|
return "";
|
|
60
53
|
}
|
|
61
54
|
}
|
|
62
|
-
#optionList: TOption[] = [];
|
|
63
|
-
#displayOptionList: TOption[] = [];
|
|
64
|
-
get optionList() {
|
|
65
|
-
return this.#optionList || [];
|
|
66
|
-
}
|
|
67
|
-
set optionList(value) {
|
|
68
|
-
if (!Array.isArray(value)) {
|
|
69
|
-
console.error(
|
|
70
|
-
"your provided option list to jb-select is not a array. you must provide array value",
|
|
71
|
-
{ value }
|
|
72
|
-
);
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
this.#optionList = value;
|
|
76
|
-
//every time optionList get updated we set our value base on current option list we use _notFoundedValue in case of value provided to component before optionList
|
|
77
|
-
this.displayOptionList = this.#filterOptionList(this.textValue);
|
|
78
|
-
this.#setValueOnOptionListChanged();
|
|
79
|
-
}
|
|
80
55
|
#placeholder = "";
|
|
81
56
|
get placeholder() {
|
|
82
57
|
return this.#placeholder;
|
|
@@ -97,18 +72,6 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
97
72
|
set searchPlaceholder(value) {
|
|
98
73
|
this.#searchPlaceholder = value;
|
|
99
74
|
}
|
|
100
|
-
get displayOptionList() {
|
|
101
|
-
return this.#displayOptionList;
|
|
102
|
-
}
|
|
103
|
-
set displayOptionList(value: TOption[]) {
|
|
104
|
-
if (Array.isArray(value) && value.length == 0) {
|
|
105
|
-
this.elements.emptyListPlaceholder.classList.add("--show");
|
|
106
|
-
} else if (Array.isArray(value)) {
|
|
107
|
-
this.elements.emptyListPlaceholder.classList.remove("--show");
|
|
108
|
-
}
|
|
109
|
-
this.#displayOptionList = value;
|
|
110
|
-
this.#updateOptionListDOM();
|
|
111
|
-
}
|
|
112
75
|
get isMobileDevice() {
|
|
113
76
|
return isMobile();
|
|
114
77
|
}
|
|
@@ -116,42 +79,41 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
116
79
|
return this.elements.componentWrapper.classList.contains("--focused");
|
|
117
80
|
}
|
|
118
81
|
// this value used by validation module to send to validation callbacks
|
|
119
|
-
get #ValidationValue():ValidationValue<
|
|
82
|
+
get #ValidationValue(): ValidationValue<TValue> {
|
|
120
83
|
return {
|
|
121
|
-
inputtedText:this.#textValue,
|
|
122
|
-
selectedOption:this.#
|
|
123
|
-
value:this.value
|
|
84
|
+
inputtedText: this.#textValue,
|
|
85
|
+
selectedOption: this.#selectedOption,
|
|
86
|
+
value: this.value
|
|
124
87
|
};
|
|
125
88
|
}
|
|
126
|
-
#validation = new ValidationHelper<ValidationValue<
|
|
127
|
-
clearValidationError:this.clearValidationError.bind(this),
|
|
128
|
-
showValidationError:this.showValidationError.bind(this),
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
getValueString:()=>this.#textValue,
|
|
132
|
-
setValidationResult:this.#setValidationResult.bind(this)
|
|
89
|
+
#validation = new ValidationHelper<ValidationValue<TValue>>({
|
|
90
|
+
clearValidationError: this.clearValidationError.bind(this),
|
|
91
|
+
showValidationError: this.showValidationError.bind(this),
|
|
92
|
+
getValue: () => this.#ValidationValue,
|
|
93
|
+
getValidations: this.#getInsideValidation.bind(this),
|
|
94
|
+
getValueString: () => this.#textValue,
|
|
95
|
+
setValidationResult: this.#setValidationResult.bind(this)
|
|
133
96
|
});
|
|
134
|
-
get validation(){
|
|
97
|
+
get validation() {
|
|
135
98
|
return this.#validation;
|
|
136
99
|
}
|
|
137
100
|
#disabled = false;
|
|
138
|
-
get disabled(){
|
|
101
|
+
get disabled() {
|
|
139
102
|
return this.#disabled;
|
|
140
103
|
}
|
|
141
|
-
set disabled(value:boolean){
|
|
104
|
+
set disabled(value: boolean) {
|
|
142
105
|
this.#disabled = value;
|
|
143
106
|
this.elements.input.disabled = value;
|
|
144
|
-
if(value){
|
|
145
|
-
//TODO: remove as any when typescript support
|
|
107
|
+
if (value) {
|
|
146
108
|
(this.#internals as any).states?.add("disabled");
|
|
147
|
-
}else{
|
|
109
|
+
} else {
|
|
148
110
|
(this.#internals as any).states?.delete("disabled");
|
|
149
111
|
}
|
|
150
112
|
}
|
|
151
113
|
#required = false;
|
|
152
|
-
set required(value:boolean){
|
|
114
|
+
set required(value: boolean) {
|
|
153
115
|
this.#required = value;
|
|
154
|
-
this.#validation.checkValiditySync({showError:false});
|
|
116
|
+
this.#validation.checkValiditySync({ showError: false });
|
|
155
117
|
}
|
|
156
118
|
get required() {
|
|
157
119
|
return this.#required;
|
|
@@ -164,11 +126,11 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
164
126
|
//currently we only support disable-validation in attribute and only in initiate time but later we can add support for change of this
|
|
165
127
|
return this.getAttribute('disable-auto-validation') === '' || this.getAttribute('disable-auto-validation') === 'true' ? true : false;
|
|
166
128
|
}
|
|
167
|
-
get name(){
|
|
129
|
+
get name() {
|
|
168
130
|
return this.getAttribute('name') || '';
|
|
169
131
|
}
|
|
170
132
|
initialValue: TValue | null = null;
|
|
171
|
-
get isDirty(): boolean{
|
|
133
|
+
get isDirty(): boolean {
|
|
172
134
|
return this.value !== this.initialValue;
|
|
173
135
|
}
|
|
174
136
|
constructor() {
|
|
@@ -196,7 +158,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
196
158
|
#initWebComponent() {
|
|
197
159
|
const shadowRoot = this.attachShadow({
|
|
198
160
|
mode: "open",
|
|
199
|
-
delegatesFocus:true,
|
|
161
|
+
delegatesFocus: true,
|
|
200
162
|
});
|
|
201
163
|
const html = `<style>${CSS}</style>` + "\n" + HTML;
|
|
202
164
|
const element = document.createElement("template");
|
|
@@ -211,43 +173,37 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
211
173
|
messageBox: shadowRoot.querySelector(".message-box")!,
|
|
212
174
|
optionList: shadowRoot.querySelector(".select-list")!,
|
|
213
175
|
optionListWrapper: shadowRoot.querySelector(".select-list-wrapper")!,
|
|
176
|
+
optionListSlot: shadowRoot.querySelector(".select-list-wrapper .select-list slot")!,
|
|
214
177
|
arrowIcon: shadowRoot.querySelector(".arrow-icon")!,
|
|
215
178
|
label: {
|
|
216
179
|
wrapper: shadowRoot.querySelector("label")!,
|
|
217
180
|
text: shadowRoot.querySelector("label .label-value")!,
|
|
218
181
|
},
|
|
219
|
-
emptyListPlaceholder: shadowRoot.querySelector(
|
|
220
|
-
".empty-list-placeholder"
|
|
221
|
-
)!,
|
|
182
|
+
emptyListPlaceholder: shadowRoot.querySelector(".empty-list-placeholder")!,
|
|
222
183
|
};
|
|
223
184
|
this.#registerEventListener();
|
|
224
185
|
}
|
|
225
186
|
#registerEventListener() {
|
|
226
|
-
this.elements.input.addEventListener("change", (e:Event) => {
|
|
187
|
+
this.elements.input.addEventListener("change", (e: Event) => {
|
|
227
188
|
this.#onInputChange(e);
|
|
228
189
|
});
|
|
229
|
-
this.elements.input.addEventListener(
|
|
230
|
-
"keypress",
|
|
231
|
-
this.#onInputKeyPress.bind(this)
|
|
232
|
-
);
|
|
190
|
+
this.elements.input.addEventListener("keypress", this.#onInputKeyPress.bind(this));
|
|
233
191
|
this.elements.input.addEventListener("keyup", this.#onInputKeyup.bind(this));
|
|
234
|
-
this.elements.input.addEventListener(
|
|
235
|
-
|
|
236
|
-
this.#onInputBeforeInput.bind(this)
|
|
237
|
-
);
|
|
238
|
-
this.elements.input.addEventListener("input", (e) => {
|
|
239
|
-
this.#onInputInput(e as unknown as InputEvent);
|
|
240
|
-
});
|
|
192
|
+
this.elements.input.addEventListener("beforeinput", this.#onInputBeforeInput.bind(this));
|
|
193
|
+
this.elements.input.addEventListener("input", (e) => { this.#onInputInput(e as unknown as InputEvent); });
|
|
241
194
|
this.elements.input.addEventListener("focus", this.#onInputFocus.bind(this));
|
|
242
195
|
this.elements.input.addEventListener("blur", this.#onInputBlur.bind(this));
|
|
243
|
-
this.elements.arrowIcon.addEventListener(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
);
|
|
196
|
+
this.elements.arrowIcon.addEventListener("click", this.#onArrowKeyClick.bind(this));
|
|
197
|
+
//events to work with options
|
|
198
|
+
this.addEventListener("select", this.#onOptionSelect.bind(this));
|
|
199
|
+
this.addEventListener("jb-option-connected", this.#onOptionConnected.bind(this));
|
|
200
|
+
this.elements.optionListSlot.addEventListener("slotchange",this.#onOptionSlotChange.bind(this));
|
|
201
|
+
|
|
247
202
|
}
|
|
203
|
+
|
|
248
204
|
#initProp() {
|
|
249
205
|
this.textValue = "";
|
|
250
|
-
this.value = this.getAttribute("value") as TValue || null
|
|
206
|
+
this.value = this.getAttribute("value") as TValue || null;
|
|
251
207
|
}
|
|
252
208
|
static get observedAttributes() {
|
|
253
209
|
return [
|
|
@@ -259,7 +215,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
259
215
|
"search-placeholder",
|
|
260
216
|
];
|
|
261
217
|
}
|
|
262
|
-
attributeChangedCallback(name:string, oldValue:string, newValue:string) {
|
|
218
|
+
attributeChangedCallback(name: string, oldValue: string, newValue: string) {
|
|
263
219
|
// do something when an attribute has changed
|
|
264
220
|
this.#onAttributeChange(name, newValue);
|
|
265
221
|
}
|
|
@@ -294,6 +250,23 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
294
250
|
break;
|
|
295
251
|
}
|
|
296
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* will check option list and if select has no option it will show empty list placeholder
|
|
255
|
+
*/
|
|
256
|
+
#updateListEmptyPlaceholder(){
|
|
257
|
+
const ss = Array.from(this.#optionList);
|
|
258
|
+
console.log(ss);
|
|
259
|
+
const isAnyOptionVisible = Array.from(this.#optionList).some(x=>x.hidden==false);
|
|
260
|
+
if(isAnyOptionVisible){
|
|
261
|
+
this.elements.emptyListPlaceholder.classList.remove("--show");
|
|
262
|
+
}else{
|
|
263
|
+
this.elements.emptyListPlaceholder.classList.add("--show");
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
#onOptionSlotChange(e:Event){
|
|
267
|
+
this.#setValueOnOptionListChanged();
|
|
268
|
+
this.#updateListEmptyPlaceholder();
|
|
269
|
+
}
|
|
297
270
|
#setValueOnOptionListChanged() {
|
|
298
271
|
//when option list changed we see if current value is valid for new optionlist we set it if not we reset value to null.
|
|
299
272
|
//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
|
|
@@ -309,23 +282,29 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
309
282
|
this.#setValueFromOutside(this.value);
|
|
310
283
|
}
|
|
311
284
|
}
|
|
285
|
+
//when user set value by attribute or value prop directly we call this function
|
|
312
286
|
#setValueFromOutside(value: TValue): boolean {
|
|
313
|
-
|
|
314
|
-
|
|
287
|
+
if(value === null || value === undefined){
|
|
288
|
+
this.#setValue(null);
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
let matchedOption:JBOptionWebComponent<TValue>| null = null;
|
|
292
|
+
this.#optionList.forEach((option) => {
|
|
315
293
|
// if we have value mapper we set selected value by object that match mapper
|
|
316
|
-
if (
|
|
317
|
-
|
|
294
|
+
if (option.value == value) {
|
|
295
|
+
matchedOption = option;
|
|
318
296
|
}
|
|
319
297
|
});
|
|
320
|
-
if (matchedOption
|
|
321
|
-
this.#setValue(matchedOption);
|
|
298
|
+
if (matchedOption) {
|
|
299
|
+
this.#setValue(matchedOption.value);
|
|
300
|
+
matchedOption.selected = true;
|
|
322
301
|
return true;
|
|
323
302
|
} else {
|
|
324
303
|
this.#notFoundedValue = value;
|
|
325
304
|
return false;
|
|
326
305
|
}
|
|
327
306
|
}
|
|
328
|
-
#setValue(value:
|
|
307
|
+
#setValue(value: TValue) {
|
|
329
308
|
this.#notFoundedValue = null;
|
|
330
309
|
this.#value = value;
|
|
331
310
|
if (value === null || value === undefined) {
|
|
@@ -355,24 +334,24 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
355
334
|
this.focus();
|
|
356
335
|
}
|
|
357
336
|
}
|
|
358
|
-
#onInputKeyPress(e:KeyboardEvent) {
|
|
359
|
-
const eventOptions:KeyboardEventInit = {
|
|
360
|
-
altKey:e.altKey,
|
|
361
|
-
bubbles:e.bubbles,
|
|
362
|
-
cancelable:e.cancelable,
|
|
363
|
-
code:e.code,
|
|
364
|
-
composed:e.composed,
|
|
365
|
-
ctrlKey:e.ctrlKey,
|
|
366
|
-
detail:e.detail,
|
|
367
|
-
isComposing:e.isComposing,
|
|
368
|
-
key:e.key,
|
|
369
|
-
location:e.location,
|
|
370
|
-
metaKey:e.metaKey,
|
|
371
|
-
view:e.view,
|
|
372
|
-
repeat:e.repeat,
|
|
373
|
-
shiftKey:e.shiftKey
|
|
337
|
+
#onInputKeyPress(e: KeyboardEvent) {
|
|
338
|
+
const eventOptions: KeyboardEventInit = {
|
|
339
|
+
altKey: e.altKey,
|
|
340
|
+
bubbles: e.bubbles,
|
|
341
|
+
cancelable: e.cancelable,
|
|
342
|
+
code: e.code,
|
|
343
|
+
composed: e.composed,
|
|
344
|
+
ctrlKey: e.ctrlKey,
|
|
345
|
+
detail: e.detail,
|
|
346
|
+
isComposing: e.isComposing,
|
|
347
|
+
key: e.key,
|
|
348
|
+
location: e.location,
|
|
349
|
+
metaKey: e.metaKey,
|
|
350
|
+
view: e.view,
|
|
351
|
+
repeat: e.repeat,
|
|
352
|
+
shiftKey: e.shiftKey
|
|
374
353
|
};
|
|
375
|
-
const event = new KeyboardEvent("keypress",eventOptions);
|
|
354
|
+
const event = new KeyboardEvent("keypress", eventOptions);
|
|
376
355
|
this.dispatchEvent(event);
|
|
377
356
|
}
|
|
378
357
|
#onInputBeforeInput(e: InputEvent) {
|
|
@@ -383,8 +362,9 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
383
362
|
const inputtedText = (e.target as HTMLInputElement).value;
|
|
384
363
|
this.textValue = inputtedText;
|
|
385
364
|
this.#handleSelectedValueDisplay(inputtedText);
|
|
386
|
-
this.#validation.checkValidity({showError:false});
|
|
365
|
+
this.#validation.checkValidity({ showError: false });
|
|
387
366
|
this.#dispatchInputEvent(e);
|
|
367
|
+
this.#updateListEmptyPlaceholder();
|
|
388
368
|
}
|
|
389
369
|
#dispatchInputEvent(e: InputEvent) {
|
|
390
370
|
const event = new InputEvent("input", {
|
|
@@ -471,7 +451,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
471
451
|
this.textValue = "";
|
|
472
452
|
this.#handleSelectedValueDisplay("");
|
|
473
453
|
this.#hideOptionList();
|
|
474
|
-
this.#validation.checkValidity({showError:true});
|
|
454
|
+
this.#validation.checkValidity({ showError: true });
|
|
475
455
|
if (this.isMobileDevice) {
|
|
476
456
|
if (this.value) {
|
|
477
457
|
this.elements.input.placeholder = "";
|
|
@@ -488,85 +468,58 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
488
468
|
this.elements.optionListWrapper.classList.remove("--show");
|
|
489
469
|
}
|
|
490
470
|
#updateOptionList(filterText: string) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
#updateOptionListDOM() {
|
|
494
|
-
const optionDomList: HTMLElement[] = [];
|
|
495
|
-
this.displayOptionList.forEach((item) => {
|
|
496
|
-
const optionDOM = this.#createOptionDOM(item);
|
|
497
|
-
optionDomList.push(optionDOM);
|
|
498
|
-
});
|
|
499
|
-
this.elements.optionList.innerHTML = "";
|
|
500
|
-
optionDomList.forEach((optionElement) => {
|
|
501
|
-
this.elements.optionList.appendChild(optionElement);
|
|
502
|
-
});
|
|
471
|
+
const event = new CustomEvent("filter-change", { detail: { filterText }, bubbles: false, cancelable: false, composed: false });
|
|
472
|
+
this.dispatchEvent(event);
|
|
503
473
|
}
|
|
504
|
-
#
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
);
|
|
514
|
-
|
|
515
|
-
|
|
474
|
+
#onOptionSelect(e: CustomEvent) {
|
|
475
|
+
const prevValue = this.#value;
|
|
476
|
+
const prevOption = this.#selectedOption;
|
|
477
|
+
//because jb-option may be in another shadow dom like jb-option-list we have to get first composed element as a target
|
|
478
|
+
const target = (e.composedPath()[0] as JBOptionWebComponent<TValue>);
|
|
479
|
+
if (target instanceof JBOptionWebComponent) {
|
|
480
|
+
const value = target.value;
|
|
481
|
+
this.#selectOption(value,target);
|
|
482
|
+
this.blur();
|
|
483
|
+
const dispatchedEvent = this.#dispatchOnChangeEvent();
|
|
484
|
+
if (dispatchedEvent.defaultPrevented) {
|
|
485
|
+
e.preventDefault();
|
|
486
|
+
this.#selectOption(prevValue,prevOption);
|
|
487
|
+
}
|
|
516
488
|
}
|
|
517
|
-
optionDOM.value = item;
|
|
518
|
-
return optionDOM;
|
|
519
|
-
}
|
|
520
489
|
|
|
521
|
-
#createDefaultOptionDom(item: TOption, isSelected: boolean): JBSelectOptionElement<TOption> {
|
|
522
|
-
const optionElement = document.createElement("div");
|
|
523
|
-
optionElement.classList.add("select-option");
|
|
524
|
-
if (isSelected) {
|
|
525
|
-
optionElement.classList.add("--selected-option");
|
|
526
|
-
}
|
|
527
|
-
//it has default function who return exact same input
|
|
528
|
-
optionElement.innerHTML = this.#getOptionTitle(item);
|
|
529
|
-
optionElement.addEventListener("click", this.#onOptionClicked.bind(this));
|
|
530
|
-
return optionElement;
|
|
531
490
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
this.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
this.#
|
|
491
|
+
//called when an jb-Option connected to the dom
|
|
492
|
+
#onOptionConnected(e: CustomEvent) {
|
|
493
|
+
e.stopPropagation();
|
|
494
|
+
const target = (e.composedPath()[0] as JBOptionWebComponent<TValue>);
|
|
495
|
+
target.addEventListener("jb-option-disconnected",this.#onOptionDisconnected.bind(this),{once:true,passive:true});
|
|
496
|
+
target.setSelectElement(this);
|
|
497
|
+
this.#optionList.add(target);
|
|
498
|
+
if(this.#notFoundedValue){
|
|
499
|
+
this.#setValueOnOptionListChanged();
|
|
500
|
+
}
|
|
501
|
+
this.#updateListEmptyPlaceholder();
|
|
502
|
+
}
|
|
503
|
+
#onOptionDisconnected(e:CustomEvent){
|
|
504
|
+
e.stopPropagation();
|
|
505
|
+
const target = e.target as JBOptionWebComponent<TValue>;
|
|
506
|
+
this.#optionList.delete(target);
|
|
507
|
+
this.#updateListEmptyPlaceholder();
|
|
508
|
+
if(target.value == this.#value){
|
|
509
|
+
this.#setValueOnOptionListChanged();
|
|
541
510
|
}
|
|
542
511
|
}
|
|
543
|
-
#selectOption(value:
|
|
512
|
+
#selectOption(value: TValue, optionDom:JBOptionWebComponent<TValue>) {
|
|
513
|
+
this.#selectedOption = optionDom;
|
|
544
514
|
this.#setValue(value);
|
|
545
515
|
this.#checkValidity(true);
|
|
546
516
|
}
|
|
547
|
-
#filterOptionList(filterString: string): TOption[] {
|
|
548
|
-
const displayOptionList: TOption[] = [];
|
|
549
|
-
this.optionList.filter((option) => {
|
|
550
|
-
const optionTitle = this.#getOptionTitle(option);
|
|
551
|
-
const isString = typeof optionTitle == "string";
|
|
552
|
-
if (isString && optionTitle.includes(filterString)) {
|
|
553
|
-
displayOptionList.push(option);
|
|
554
|
-
}
|
|
555
|
-
if (!isString) {
|
|
556
|
-
console.warn(
|
|
557
|
-
"the provided values for optionsList is not of type string.",
|
|
558
|
-
{ option, title: optionTitle }
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
return displayOptionList;
|
|
563
|
-
}
|
|
564
517
|
/**
|
|
565
518
|
* @description show given string as a error in message place
|
|
566
519
|
* @public
|
|
567
520
|
*/
|
|
568
521
|
showValidationError(error: ShowValidationErrorInput | string) {
|
|
569
|
-
const message = typeof error == "string"?error:error.message;
|
|
522
|
+
const message = typeof error == "string" ? error : error.message;
|
|
570
523
|
this.elements.messageBox.innerHTML = message;
|
|
571
524
|
this.elements.messageBox.classList.add("--error");
|
|
572
525
|
}
|
|
@@ -575,11 +528,11 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
575
528
|
this.elements.messageBox.classList.remove("--error");
|
|
576
529
|
}
|
|
577
530
|
#dispatchOnChangeEvent() {
|
|
578
|
-
const event = new Event("change",{bubbles:true,cancelable:true});
|
|
531
|
+
const event = new Event("change", { bubbles: true, cancelable: true });
|
|
579
532
|
this.dispatchEvent(event);
|
|
580
533
|
return event;
|
|
581
534
|
}
|
|
582
|
-
#setSelectedOptionDom(value:
|
|
535
|
+
#setSelectedOptionDom(value: TValue) {
|
|
583
536
|
//when user select option or value changed in any condition we set selected option DOM
|
|
584
537
|
this.elements.selectedValueWrapper.innerHTML = "";
|
|
585
538
|
//if value was null or undefined it remain empty
|
|
@@ -588,62 +541,35 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
588
541
|
this.elements.selectedValueWrapper.appendChild(selectedOptionDom);
|
|
589
542
|
}
|
|
590
543
|
}
|
|
591
|
-
#createSelectedValueDom(value:
|
|
544
|
+
#createSelectedValueDom(value: TValue) {
|
|
592
545
|
if (typeof this.callbacks.getSelectedValueDOM == "function") {
|
|
593
|
-
return this.callbacks.getSelectedValueDOM(value);
|
|
546
|
+
return this.callbacks.getSelectedValueDOM(value,this.#selectedOption);
|
|
594
547
|
} else {
|
|
595
|
-
return this.#createDefaultSelectedValueDom(
|
|
548
|
+
return this.#createDefaultSelectedValueDom();
|
|
596
549
|
}
|
|
597
550
|
}
|
|
598
|
-
#createDefaultSelectedValueDom(
|
|
599
|
-
|
|
551
|
+
#createDefaultSelectedValueDom() {
|
|
552
|
+
//TODO: put some backup way for when we have value but no option provided
|
|
553
|
+
let contentNodes:Node[] = [];
|
|
554
|
+
if(this.#selectedOption){
|
|
555
|
+
contentNodes = this.#selectedOption.optionContent;
|
|
556
|
+
}
|
|
600
557
|
const selectedOptionDom = document.createElement("div");
|
|
601
558
|
selectedOptionDom.classList.add("selected-value");
|
|
602
|
-
selectedOptionDom.
|
|
559
|
+
selectedOptionDom.append(...contentNodes.map(n=>n.cloneNode()));
|
|
603
560
|
return selectedOptionDom;
|
|
604
561
|
}
|
|
605
|
-
#
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
}
|
|
609
|
-
try {
|
|
610
|
-
if(typeof this.callbacks.getOptionValue == "function"){
|
|
611
|
-
return this.callbacks.getOptionValue(option);
|
|
612
|
-
}else{
|
|
613
|
-
return option as unknown as TValue;
|
|
614
|
-
}
|
|
615
|
-
} catch (e) {
|
|
616
|
-
console.error(
|
|
617
|
-
`Invalid getOptionValue callback Result, must be a function that returns the value of an option`,
|
|
618
|
-
option
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
#getOptionTitle(option: TOption): string {
|
|
623
|
-
if (typeof this.callbacks.getOptionTitle !== "function") {
|
|
624
|
-
console.error("getOptionTitle callback is not a function");
|
|
625
|
-
}
|
|
626
|
-
try {
|
|
627
|
-
return this.callbacks.getOptionTitle(option);
|
|
628
|
-
} catch (e) {
|
|
629
|
-
console.error(
|
|
630
|
-
`Invalid getOptionTitle callback Result, must be a function that returns the value of an option`,
|
|
631
|
-
option
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
return "";
|
|
635
|
-
}
|
|
636
|
-
#getInsideValidation(){
|
|
637
|
-
const ValidationList:ValidationItem<ValidationValue<TOption,TValue>>[] = [];
|
|
638
|
-
if(this.required){
|
|
562
|
+
#getInsideValidation() {
|
|
563
|
+
const ValidationList: ValidationItem<ValidationValue<TValue>>[] = [];
|
|
564
|
+
if (this.required) {
|
|
639
565
|
const label = this.getAttribute("label") || "";
|
|
640
566
|
const message = `${label} حتما باید انتخاب شود`;
|
|
641
567
|
ValidationList.push({
|
|
642
|
-
validator:({
|
|
643
|
-
return
|
|
568
|
+
validator: ({ value }) => {
|
|
569
|
+
return value !== null && value !== undefined;
|
|
644
570
|
},
|
|
645
|
-
message:message,
|
|
646
|
-
stateType:"valueMissing"
|
|
571
|
+
message: message,
|
|
572
|
+
stateType: "valueMissing"
|
|
647
573
|
});
|
|
648
574
|
}
|
|
649
575
|
return ValidationList;
|
|
@@ -651,7 +577,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
651
577
|
//
|
|
652
578
|
#checkValidity(showError: boolean) {
|
|
653
579
|
if (!this.isAutoValidationDisabled) {
|
|
654
|
-
return this.#validation.checkValidity({showError});
|
|
580
|
+
return this.#validation.checkValidity({ showError });
|
|
655
581
|
}
|
|
656
582
|
}
|
|
657
583
|
/**
|
|
@@ -660,7 +586,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
660
586
|
* this method used by #internal of component
|
|
661
587
|
*/
|
|
662
588
|
checkValidity(): boolean {
|
|
663
|
-
const validationResult = this.#validation.checkValiditySync({showError:false});
|
|
589
|
+
const validationResult = this.#validation.checkValiditySync({ showError: false });
|
|
664
590
|
if (!validationResult.isAllValid) {
|
|
665
591
|
const event = new CustomEvent('invalid');
|
|
666
592
|
this.dispatchEvent(event);
|
|
@@ -672,7 +598,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
672
598
|
* @description this method used to check for validity and show error to user
|
|
673
599
|
*/
|
|
674
600
|
reportValidity(): boolean {
|
|
675
|
-
const validationResult = this.#validation.checkValiditySync({showError:true});
|
|
601
|
+
const validationResult = this.#validation.checkValiditySync({ showError: true });
|
|
676
602
|
if (!validationResult.isAllValid) {
|
|
677
603
|
const event = new CustomEvent('invalid');
|
|
678
604
|
this.dispatchEvent(event);
|
|
@@ -682,7 +608,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
682
608
|
/**
|
|
683
609
|
* @description this method called on every checkValidity calls and update validation result of #internal
|
|
684
610
|
*/
|
|
685
|
-
#setValidationResult(result: ValidationResult<ValidationValue<
|
|
611
|
+
#setValidationResult(result: ValidationResult<ValidationValue<TValue>>) {
|
|
686
612
|
if (result.isAllValid) {
|
|
687
613
|
this.#internals?.setValidity({}, '');
|
|
688
614
|
} else {
|
|
@@ -692,7 +618,7 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
692
618
|
if (!res.isValid) {
|
|
693
619
|
if (res.validation.stateType) {
|
|
694
620
|
states[res.validation.stateType] = true;
|
|
695
|
-
}else{
|
|
621
|
+
} else {
|
|
696
622
|
states["customError"] = true;
|
|
697
623
|
}
|
|
698
624
|
if (message == '') { message = res.message; }
|
|
@@ -702,9 +628,10 @@ export class JBSelectWebComponent<TOption = any, TValue = TOption> extends HTMLE
|
|
|
702
628
|
this.#internals?.setValidity(states, message);
|
|
703
629
|
}
|
|
704
630
|
}
|
|
705
|
-
get validationMessage(){
|
|
631
|
+
get validationMessage() {
|
|
706
632
|
return this.#internals?.validationMessage || this.#validation.resultSummary.message;
|
|
707
633
|
}
|
|
634
|
+
|
|
708
635
|
}
|
|
709
636
|
const myElementNotExists = !customElements.get("jb-select");
|
|
710
637
|
if (myElementNotExists) {
|