@ekzo-dev/bootstrap-addons 5.2.23 → 5.2.25
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekzo-dev/bootstrap-addons",
|
|
3
3
|
"description": "Aurelia Bootstrap additional component",
|
|
4
|
-
"version": "5.2.
|
|
4
|
+
"version": "5.2.25",
|
|
5
5
|
"homepage": "https://github.com/ekzo-dev/aurelia-components/tree/main/packages/bootstrap-addons",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@ekzo-dev/bootstrap": "^5.2.
|
|
12
|
+
"@ekzo-dev/bootstrap": "^5.2.19",
|
|
13
13
|
"@ekzo-dev/vanilla-jsoneditor": "^0.23.7",
|
|
14
14
|
"@ekzo-dev/toolkit": "^1.2.4",
|
|
15
15
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
|
@@ -5,11 +5,13 @@ import { valueConverter } from 'aurelia';
|
|
|
5
5
|
export class Filter {
|
|
6
6
|
toView(
|
|
7
7
|
list: Map<string, ISelectOption[]> | ISelectOption[],
|
|
8
|
-
search: string
|
|
8
|
+
search: string,
|
|
9
|
+
emptyOption?: ISelectOption
|
|
9
10
|
): Map<string, ISelectOption[]> | ISelectOption[] {
|
|
10
|
-
if (search === '') return list;
|
|
11
|
+
if (search === '' && emptyOption === undefined) return list;
|
|
11
12
|
|
|
12
|
-
const cb = (option:
|
|
13
|
+
const cb = (option: ISelectOption) =>
|
|
14
|
+
option.value !== emptyOption?.value && option.text.toLowerCase().includes(search.toLowerCase());
|
|
13
15
|
|
|
14
16
|
if (list instanceof Map) {
|
|
15
17
|
const result = new Map<string, ISelectOption[]>();
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
id.bind="id"
|
|
29
29
|
class="form-select ${bsSize ? `form-select-${bsSize}` : ''} ${valid ? 'is-valid' : valid === false ? 'is-invalid' : ''}"
|
|
30
30
|
bs-dropdown-toggle="arrow.bind: false"
|
|
31
|
-
value="${
|
|
31
|
+
value="${valueText}"
|
|
32
|
+
placeholder="${emptyOption?.text ?? ''}"
|
|
32
33
|
disabled.bind="disabled"
|
|
33
34
|
required.bind="required"
|
|
34
35
|
form.bind="form & attr"
|
|
@@ -36,7 +37,14 @@
|
|
|
36
37
|
title.bind="title & attr"
|
|
37
38
|
autocomplete="off"
|
|
38
39
|
keydown.trigger="$event.preventDefault()"
|
|
40
|
+
ref="control"
|
|
39
41
|
/>
|
|
42
|
+
<button
|
|
43
|
+
if.bind="emptyOption && selectedOption?.value !== emptyOption?.value"
|
|
44
|
+
bs-close-button
|
|
45
|
+
type="button"
|
|
46
|
+
click.trigger="selectOption(emptyOption)"
|
|
47
|
+
></button>
|
|
40
48
|
<bs-dropdown-menu>
|
|
41
49
|
<div bs-dropdown-item="text" if.bind="optionsCount > 10">
|
|
42
50
|
<input class="form-control" placeholder="Filter options" type="search" value.bind="filter & debounce:250" />
|
|
@@ -44,19 +52,19 @@
|
|
|
44
52
|
<hr bs-dropdown-item="divider" if.bind="optionsCount > 10" />
|
|
45
53
|
<button
|
|
46
54
|
type="button"
|
|
47
|
-
repeat.for="option of ungroupedOptions | filter:filter"
|
|
48
|
-
bs-dropdown-item="active.bind: option.value === selectedOption
|
|
55
|
+
repeat.for="option of ungroupedOptions | filter:filter:emptyOption"
|
|
56
|
+
bs-dropdown-item="active.bind: option.value === selectedOption?.value; disabled.bind: option.disabled"
|
|
49
57
|
click.trigger="selectOption(option)"
|
|
50
58
|
>
|
|
51
59
|
${option.text || ' '}
|
|
52
60
|
</button>
|
|
53
|
-
<template repeat.for="[k, v] of groupedOptions | filter:filter">
|
|
61
|
+
<template repeat.for="[k, v] of groupedOptions | filter:filter:emptyOption">
|
|
54
62
|
<h6 bs-dropdown-item="header">${k}</h6>
|
|
55
63
|
<button
|
|
56
64
|
class="ps-4"
|
|
57
65
|
type="button"
|
|
58
66
|
repeat.for="option of v"
|
|
59
|
-
bs-dropdown-item="active.bind: option.value === selectedOption
|
|
67
|
+
bs-dropdown-item="active.bind: option.value === selectedOption?.value; disabled.bind: option.disabled"
|
|
60
68
|
click.trigger="selectOption(option)"
|
|
61
69
|
>
|
|
62
70
|
${option.text || ' '}
|
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
@import '
|
|
2
|
-
@import '
|
|
3
|
-
@import '
|
|
4
|
-
@import '
|
|
5
|
-
@import '
|
|
6
|
-
@import '~bootstrap/scss/forms/form-check';
|
|
1
|
+
@import 'bootstrap/scss/functions';
|
|
2
|
+
@import 'bootstrap/scss/variables';
|
|
3
|
+
@import 'bootstrap/scss/maps';
|
|
4
|
+
@import 'bootstrap/scss/mixins';
|
|
5
|
+
@import 'bootstrap/scss/forms/form-check';
|
|
7
6
|
|
|
8
7
|
bs-select {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
> .form-select {
|
|
8
|
+
.form-select {
|
|
12
9
|
cursor: default;
|
|
13
10
|
}
|
|
14
11
|
|
|
12
|
+
&:has(.btn-close) .form-select {
|
|
13
|
+
padding-right: 4rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.btn-close {
|
|
17
|
+
display: block;
|
|
18
|
+
position: relative;
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
width: 0.8rem;
|
|
21
|
+
height: 0.8rem;
|
|
22
|
+
top: calc(-1px - 1.5rem);
|
|
23
|
+
left: calc(100% - 3.25rem);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.form-select-lg + .btn-close {
|
|
27
|
+
top: calc(-1px - 1.8rem);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.form-select-sm + .btn-close {
|
|
31
|
+
top: calc(-1px - 1.3rem);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&.form-floating .btn-close {
|
|
35
|
+
top: calc(-1px - 2.15rem) !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
.dropdown-menu {
|
|
16
39
|
width: max-content;
|
|
17
40
|
min-width: 100%;
|
|
@@ -76,3 +99,16 @@ bs-select {
|
|
|
76
99
|
}
|
|
77
100
|
}
|
|
78
101
|
}
|
|
102
|
+
|
|
103
|
+
/* stylelint-disable */
|
|
104
|
+
.was-validated bs-select:has(.btn-close) .form-select:invalid,
|
|
105
|
+
.was-validated bs-select:has(.btn-close) .form-select:valid,
|
|
106
|
+
bs-select:has(.btn-close) .form-select.is-invalid,
|
|
107
|
+
bs-select:has(.btn-close) .form-select.is-valid {
|
|
108
|
+
padding-right: 5.5rem !important;
|
|
109
|
+
|
|
110
|
+
+ .btn-close {
|
|
111
|
+
left: calc(100% - 4.75rem);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/* stylelint-enable */
|
|
@@ -15,12 +15,14 @@ export default {
|
|
|
15
15
|
args: {
|
|
16
16
|
label: 'Label',
|
|
17
17
|
options: [
|
|
18
|
-
{ value: undefined, text: '' },
|
|
18
|
+
{ value: undefined, text: 'Select option' },
|
|
19
19
|
{ value: '1', text: 'One', disabled: true },
|
|
20
20
|
{ value: '2', text: 'Two' },
|
|
21
21
|
{ value: '3', text: 'Three', group: 'Group' },
|
|
22
22
|
],
|
|
23
23
|
value: '2',
|
|
24
|
+
floatingLabel: false,
|
|
25
|
+
valid: null,
|
|
24
26
|
},
|
|
25
27
|
argTypes: {
|
|
26
28
|
bsSize: {
|
|
@@ -3,6 +3,7 @@ import template from './select.html';
|
|
|
3
3
|
import './select.scss';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
BsCloseButton,
|
|
6
7
|
BsDropdown,
|
|
7
8
|
BsDropdownItem,
|
|
8
9
|
BsDropdownMenu,
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
BsSelect as BaseBsSelect,
|
|
11
12
|
ISelectOption,
|
|
12
13
|
} from '@ekzo-dev/bootstrap';
|
|
13
|
-
import { customElement, ICustomElementViewModel } from 'aurelia';
|
|
14
|
+
import { bindable, customElement, ICustomElementViewModel } from 'aurelia';
|
|
14
15
|
|
|
15
16
|
import { Filter } from './filter';
|
|
16
17
|
|
|
@@ -22,9 +23,12 @@ const BS_SIZE_MULTIPLIER = {
|
|
|
22
23
|
@customElement({
|
|
23
24
|
name: 'bs-select',
|
|
24
25
|
template,
|
|
25
|
-
dependencies: [BsDropdown, BsDropdownMenu, BsDropdownToggle, BsDropdownItem, Filter],
|
|
26
|
+
dependencies: [BsDropdown, BsDropdownMenu, BsDropdownToggle, BsDropdownItem, Filter, BsCloseButton],
|
|
26
27
|
})
|
|
27
28
|
export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
29
|
+
@bindable()
|
|
30
|
+
emptyValue?: unknown = null;
|
|
31
|
+
|
|
28
32
|
control!: HTMLFieldSetElement;
|
|
29
33
|
|
|
30
34
|
filter: string = '';
|
|
@@ -33,6 +37,8 @@ export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
|
33
37
|
|
|
34
38
|
deactivating: boolean = false;
|
|
35
39
|
|
|
40
|
+
emptyOption?: ISelectOption;
|
|
41
|
+
|
|
36
42
|
binding() {
|
|
37
43
|
super.binding();
|
|
38
44
|
this.deactivating = false;
|
|
@@ -64,6 +70,23 @@ export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
|
64
70
|
|
|
65
71
|
selectOption(option: ISelectOption) {
|
|
66
72
|
this.value = option.value;
|
|
73
|
+
this.#dispatchEvents();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
get valueText(): string {
|
|
77
|
+
const { selectedOption, emptyOption } = this;
|
|
78
|
+
|
|
79
|
+
return emptyOption && emptyOption.value === selectedOption?.value
|
|
80
|
+
? ''
|
|
81
|
+
: `${selectedOption?.group ? selectedOption.group + ' / ' : ''}${selectedOption?.text ?? ''}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#dispatchEvents() {
|
|
85
|
+
const change = new Event('change', { bubbles: true });
|
|
86
|
+
const input = new Event('input', { bubbles: true });
|
|
87
|
+
|
|
88
|
+
this.control.dispatchEvent(input);
|
|
89
|
+
this.control.dispatchEvent(change);
|
|
67
90
|
}
|
|
68
91
|
|
|
69
92
|
#setHeight(): void {
|
|
@@ -93,8 +116,9 @@ export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
|
93
116
|
get selectedOption(): ISelectOption | undefined {
|
|
94
117
|
if (this['__raw__'].deactivating) return;
|
|
95
118
|
|
|
96
|
-
const { matcher, value } = this;
|
|
119
|
+
const { matcher, value, emptyValue } = this;
|
|
97
120
|
let { options } = this;
|
|
121
|
+
let emptyOption: ISelectOption;
|
|
98
122
|
|
|
99
123
|
if (options instanceof Object && options.constructor === Object) {
|
|
100
124
|
options = Object.entries(options);
|
|
@@ -106,12 +130,19 @@ export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
|
106
130
|
let option = (options as Array<ISelectOption | readonly [unknown, string]>).find((option) => {
|
|
107
131
|
const currentValue: unknown = isEntries ? option[0] : (option as ISelectOption).value;
|
|
108
132
|
|
|
133
|
+
if (currentValue == emptyValue) {
|
|
134
|
+
emptyOption = {
|
|
135
|
+
value: currentValue,
|
|
136
|
+
text: isEntries ? option[1] : (option as ISelectOption).text,
|
|
137
|
+
} as ISelectOption;
|
|
138
|
+
}
|
|
139
|
+
|
|
109
140
|
return matcher ? matcher(value, currentValue) : value === currentValue;
|
|
110
141
|
});
|
|
111
142
|
|
|
112
143
|
option = isEntries && option !== undefined ? { value: option[0], text: option[1] } : (option as ISelectOption);
|
|
113
144
|
|
|
114
|
-
// update value next tick
|
|
145
|
+
// update value next tick
|
|
115
146
|
const foundValue = option?.value;
|
|
116
147
|
|
|
117
148
|
if (foundValue !== value) {
|
|
@@ -119,6 +150,9 @@ export class BsSelect extends BaseBsSelect implements ICustomElementViewModel {
|
|
|
119
150
|
void Promise.resolve().then(() => (this.value = foundValue));
|
|
120
151
|
}
|
|
121
152
|
|
|
153
|
+
// update empty option next tick
|
|
154
|
+
void Promise.resolve().then(() => (this.emptyOption = emptyOption));
|
|
155
|
+
|
|
122
156
|
return option;
|
|
123
157
|
}
|
|
124
158
|
}
|