@bagelink/vue 0.0.282 → 0.0.284
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/dist/components/ComboBox.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/index.cjs +41 -38
- package/dist/index.mjs +41 -38
- package/dist/style.css +46 -29
- package/dist/types/materialIcons.d.ts +1 -1
- package/dist/types/materialIcons.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ComboBox.vue +79 -74
- package/src/components/form/inputs/SelectInput.vue +23 -21
- package/src/styles/layout.css +26 -5
- package/src/types/materialIcons.ts +1 -0
|
@@ -1,137 +1,142 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
2
|
+
<Dropdown placement="auto-start" class="bagel-input combobox">
|
|
3
|
+
<button type="button" class="combobox-btn" @click="toggle">
|
|
4
|
+
{{ valueToLabel(selectedItem) || placeholder || 'Select' }}
|
|
5
|
+
<MaterialIcon v-bind="{ 'icon': open ? 'unfold_less' : 'unfold_more' }" />
|
|
6
|
+
</button>
|
|
7
|
+
<input style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1;" v-if="required"
|
|
8
|
+
v-model="selectedItem" required>
|
|
9
|
+
<template #popper="{ hide }">
|
|
10
|
+
<Card thin class="combobox-options">
|
|
11
|
+
<TextInput v-if="searchable" ref="searchInput" dense :placeholder="'Search'" icon="search" v-model="search" />
|
|
12
|
+
<div class="combobox-option" v-for="(option, i) in filteredOptions" :key="`${option}${i}`" @click="() => {
|
|
13
|
+
select(option)
|
|
14
|
+
hide();
|
|
15
|
+
}" :class="{ selected: option === selectedItem }">
|
|
16
|
+
<span>
|
|
17
|
+
{{ label(option) }}
|
|
18
|
+
</span>
|
|
19
|
+
<Icon v-if="isSelected(option)" icon="check" />
|
|
20
|
+
</div>
|
|
21
|
+
</Card>
|
|
22
|
+
</template>
|
|
23
|
+
</Dropdown>
|
|
24
24
|
</template>
|
|
25
25
|
|
|
26
26
|
<script lang="ts" setup>
|
|
27
|
+
import { watch } from 'vue';
|
|
27
28
|
import { Dropdown } from 'floating-vue';
|
|
28
29
|
import 'floating-vue/style.css';
|
|
29
30
|
import {
|
|
30
|
-
|
|
31
|
+
TextInput, Card, Icon, MaterialIcon,
|
|
31
32
|
} from '@bagelink/vue';
|
|
32
33
|
|
|
33
34
|
type Option = string | number | Record<string, any> | { label: string, value: string | number };
|
|
34
35
|
let open = $ref(false);
|
|
35
|
-
let selectedItem = $ref<Option>();
|
|
36
36
|
|
|
37
37
|
const searchInput = $ref<HTMLInputElement | null>(null);
|
|
38
38
|
|
|
39
39
|
const props = defineProps<{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
options: Option[];
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
modelValue?: Option;
|
|
44
|
+
searchable?: boolean;
|
|
45
|
+
required?: boolean;
|
|
46
46
|
}>();
|
|
47
|
+
let selectedItem = $ref<Option>();
|
|
47
48
|
let search = $ref('');
|
|
48
49
|
|
|
49
50
|
const toggle = () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
open = !open;
|
|
52
|
+
if (!open) search = '';
|
|
53
|
+
if (open) setTimeout(() => (searchInput as any)?.$el?.querySelector('input')?.focus(), 100);
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
const valueToLabel = (value?: Option) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
if (!value) return '';
|
|
58
|
+
const option = props.options.find((option) => {
|
|
59
|
+
if (typeof option === 'string' || typeof option === 'number') return option === value;
|
|
60
|
+
return option.value === value;
|
|
61
|
+
});
|
|
62
|
+
if (!option) return value;
|
|
63
|
+
return label(option);
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
const emit = defineEmits(['update:modelValue']);
|
|
66
67
|
|
|
67
68
|
const label = (option: Option) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (typeof option === 'string') return option;
|
|
70
|
+
if (typeof option === 'number') return `${option}`;
|
|
71
|
+
return option.label;
|
|
71
72
|
};
|
|
72
73
|
|
|
73
74
|
const isSelected = (option: Option) => {
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
if (typeof option === 'string' || typeof option === 'number') return option === selectedItem;
|
|
76
|
+
return option.value === selectedItem;
|
|
76
77
|
};
|
|
77
78
|
|
|
78
79
|
const filteredOptions = $computed(() => props.options.filter((option) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
const searchTerm = new RegExp(search, 'gi');
|
|
81
|
+
if (typeof option === 'string') return option.match(searchTerm);
|
|
82
|
+
if (typeof option === 'number') return `${option}`.match(searchTerm);
|
|
83
|
+
return option.label.match(searchTerm);
|
|
83
84
|
}));
|
|
84
85
|
|
|
85
86
|
const select = (option: Option) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
if (typeof option === 'string') selectedItem = option;
|
|
88
|
+
else if (typeof option === 'number') selectedItem = option;
|
|
89
|
+
else selectedItem = option.value;
|
|
90
|
+
emit('update:modelValue', selectedItem);
|
|
91
|
+
open = false;
|
|
91
92
|
};
|
|
93
|
+
|
|
94
|
+
watch(() => props.modelValue, (value) => {
|
|
95
|
+
if (value !== selectedItem) selectedItem = value;
|
|
96
|
+
}, { immediate: true });
|
|
92
97
|
</script>
|
|
93
98
|
|
|
94
99
|
<style scoped>
|
|
95
100
|
.combobox {
|
|
96
|
-
|
|
101
|
+
width: 100%;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
.combobox-option {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
padding: 6px 12px;
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
border-radius: 5px;
|
|
108
|
+
transition: all 0.2s;
|
|
109
|
+
display: flex;
|
|
110
|
+
justify-content: space-between;
|
|
111
|
+
width: 100%;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
.combobox-options {
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
max-height: 300px;
|
|
116
|
+
overflow-y: auto;
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
.combobox-option:hover {
|
|
115
|
-
|
|
120
|
+
background: var(--bgl-gray-20);
|
|
116
121
|
}
|
|
117
122
|
</style>
|
|
118
123
|
|
|
119
124
|
<style>
|
|
120
125
|
.combobox-btn {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: space-between;
|
|
128
|
+
align-items: center;
|
|
129
|
+
height: var(--input-height);
|
|
130
|
+
border-radius: var(--input-border-radius);
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
.v-popper__arrow-container {
|
|
129
|
-
|
|
134
|
+
display: none;
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
.v-popper--theme-dropdown .v-popper__inner {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
border: none;
|
|
139
|
+
background: transparent;
|
|
140
|
+
border-radius: var(--card-border-radius);
|
|
136
141
|
}
|
|
137
142
|
</style>
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
<div>
|
|
3
|
+
<label class="txt-start" :for="id">
|
|
4
|
+
{{ label }}
|
|
5
|
+
<Multiselect
|
|
6
|
+
ref="multiselect" :id="id" label="label" trackBy="value" :options="optionList" :required="required"
|
|
7
|
+
:placeholder="placeholder" v-model="seletValue" :close-on-select="true" v-bind="extraProps"
|
|
8
|
+
/>
|
|
9
|
+
<!-- <input v-model="dataValue" type="hidden" :name="id" :required v-bind="extraProps"> -->
|
|
10
|
+
</label>
|
|
11
|
+
</div>
|
|
10
12
|
</template>
|
|
11
13
|
|
|
12
14
|
<script lang="ts" setup>
|
|
@@ -36,24 +38,24 @@ const emit = defineEmits(['update:modelValue']);
|
|
|
36
38
|
const optionList = $ref<Option[]>([]);
|
|
37
39
|
|
|
38
40
|
const seletValue = $computed({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
get: () => optionList.find((opt) => opt.value === dataValue),
|
|
42
|
+
set: (val?: Option) => {
|
|
43
|
+
dataValue = val?.value;
|
|
44
|
+
emit('update:modelValue', dataValue);
|
|
45
|
+
},
|
|
44
46
|
});
|
|
45
47
|
|
|
46
48
|
function optnToValueLabel(option: RawOption): Option {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
if (typeof option === 'string' || typeof option === 'number') {
|
|
50
|
+
return { label: `${option}`, value: option };
|
|
51
|
+
}
|
|
52
|
+
return option;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
function updateOptionList() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
const { options } = props;
|
|
57
|
+
const optnLst = typeof options === 'string' ? options.split('\n|,') : options || [];
|
|
58
|
+
optionList.push(...optnLst.map(optnToValueLabel));
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
watch(() => props.options, updateOptionList, { immediate: true });
|
|
@@ -344,7 +346,7 @@ fieldset[disabled] .multiselect {
|
|
|
344
346
|
border-top: none;
|
|
345
347
|
border-bottom-left-radius: 5px;
|
|
346
348
|
border-bottom-right-radius: 5px;
|
|
347
|
-
z-index:
|
|
349
|
+
z-index: 150;
|
|
348
350
|
-webkit-overflow-scrolling: touch;
|
|
349
351
|
}
|
|
350
352
|
|
package/src/styles/layout.css
CHANGED
|
@@ -81,10 +81,6 @@
|
|
|
81
81
|
grid-auto-columns: max-content;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
.w-100 {
|
|
85
|
-
width: 100%;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
84
|
.col-gap-1 {
|
|
89
85
|
column-gap: 1rem;
|
|
90
86
|
}
|
|
@@ -164,7 +160,12 @@
|
|
|
164
160
|
.w550,
|
|
165
161
|
.w600,
|
|
166
162
|
.w650,
|
|
167
|
-
.w700
|
|
163
|
+
.w700,
|
|
164
|
+
.w770,
|
|
165
|
+
.w900,
|
|
166
|
+
.w970,
|
|
167
|
+
.w1030,
|
|
168
|
+
.w1170 {
|
|
168
169
|
margin-left: auto;
|
|
169
170
|
margin-right: auto;
|
|
170
171
|
width: 98%;
|
|
@@ -206,6 +207,26 @@
|
|
|
206
207
|
max-width: 700px;
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
.w770 {
|
|
211
|
+
max-width: 770px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.w900 {
|
|
215
|
+
max-width: 900px;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.w970 {
|
|
219
|
+
max-width: 970px;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.w1030 {
|
|
223
|
+
max-width: 1030px;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.w1170 {
|
|
227
|
+
max-width: 1170px;
|
|
228
|
+
}
|
|
229
|
+
|
|
209
230
|
.gap-1 {
|
|
210
231
|
gap: 0.25rem;
|
|
211
232
|
}
|