@bagelink/vue 0.0.431 → 0.0.437
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/form/BglField.vue.d.ts.map +1 -1
- package/dist/components/form/BglForm.vue.d.ts +4 -1
- package/dist/components/form/BglForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioPillsInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/index.cjs +48 -26
- package/dist/index.mjs +48 -26
- package/dist/style.css +169 -128
- package/dist/utils/BagelFormUtils.d.ts +1 -0
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/form/BglField.vue +5 -3
- package/src/components/form/BglForm.vue +13 -9
- package/src/components/form/inputs/RadioPillsInput.vue +4 -5
- package/src/components/form/inputs/SelectInput.vue +214 -217
- package/src/styles/bagel.css +60 -8
- package/src/styles/modal.css +74 -75
- package/src/styles/scrollbar.css +1 -2
- package/src/utils/BagelFormUtils.ts +44 -10
|
@@ -1,302 +1,299 @@
|
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
2
|
+
<Dropdown
|
|
3
|
+
@click.stop
|
|
4
|
+
@hide="updateOpen(false)"
|
|
5
|
+
ref="dropdown"
|
|
6
|
+
placement="bottom-start"
|
|
7
|
+
class="bagel-input selectinput"
|
|
8
|
+
>
|
|
9
|
+
<label>
|
|
10
|
+
{{ label }}
|
|
11
|
+
<button
|
|
12
|
+
@keydown="openOptions"
|
|
13
|
+
@click="updateOpen(true)"
|
|
14
|
+
:disabled="disabled"
|
|
15
|
+
type="button"
|
|
16
|
+
class="selectinput-btn"
|
|
17
|
+
:class="{ isEmpty: selectedItems.length === 0 }"
|
|
18
|
+
>
|
|
19
|
+
<p>{{ selectedLabel }}</p>
|
|
20
|
+
<Icon
|
|
21
|
+
v-if="!disabled"
|
|
22
|
+
v-bind="{ icon: open ? 'unfold_less' : 'unfold_more' }"
|
|
23
|
+
/>
|
|
24
|
+
</button>
|
|
25
|
+
<input
|
|
26
|
+
tabindex="-1"
|
|
27
|
+
style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1"
|
|
28
|
+
v-if="required"
|
|
29
|
+
@input="updateOpen(true)"
|
|
30
|
+
:value="selectedItems"
|
|
31
|
+
required
|
|
32
|
+
>
|
|
33
|
+
</label>
|
|
34
|
+
<template #popper>
|
|
35
|
+
<Card
|
|
36
|
+
class="selectinput-options p-05"
|
|
37
|
+
:style="{ width: fullWidth ? '100%' : 'auto' }"
|
|
38
|
+
>
|
|
39
|
+
<TextInput
|
|
40
|
+
v-if="searchable"
|
|
41
|
+
ref="searchInput"
|
|
42
|
+
dense
|
|
43
|
+
:placeholder="'Search'"
|
|
44
|
+
icon="search"
|
|
45
|
+
v-model="search"
|
|
46
|
+
/>
|
|
47
|
+
<div
|
|
48
|
+
class="selectinput-option hover gap-1"
|
|
49
|
+
v-for="(option, i) in filteredOptions"
|
|
50
|
+
:key="`${option}${i}`"
|
|
51
|
+
@click="select(option)"
|
|
52
|
+
:class="{ selected: isSelected(option) }"
|
|
53
|
+
>
|
|
54
|
+
<Icon v-if="isSelected(option)" icon="check" />
|
|
55
|
+
<Icon
|
|
56
|
+
class="opacity-3"
|
|
57
|
+
v-if="!isSelected(option)"
|
|
58
|
+
icon="fiber_manual_record"
|
|
59
|
+
/>
|
|
60
|
+
<span>
|
|
61
|
+
{{ getLabel(option) }}
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
<slot name="last" />
|
|
65
|
+
</Card>
|
|
66
|
+
</template>
|
|
67
|
+
</Dropdown>
|
|
68
68
|
</template>
|
|
69
69
|
|
|
70
70
|
<script lang="ts" setup>
|
|
71
|
-
import { watch } from
|
|
72
|
-
import { Dropdown } from
|
|
73
|
-
import
|
|
74
|
-
import { TextInput, Card, Icon } from
|
|
71
|
+
import { watch } from 'vue';
|
|
72
|
+
import { Dropdown } from 'floating-vue';
|
|
73
|
+
import 'floating-vue/style.css';
|
|
74
|
+
import { TextInput, Card, Icon } from '@bagelink/vue';
|
|
75
75
|
|
|
76
76
|
type Option =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
| string
|
|
78
|
+
| number
|
|
79
|
+
| Record<string, any>
|
|
80
|
+
| { label: string; value: string | number };
|
|
81
81
|
|
|
82
82
|
const searchInput = $ref<HTMLInputElement | null>(null);
|
|
83
83
|
|
|
84
84
|
const props = defineProps<{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
options: Option[];
|
|
86
|
+
placeholder?: string;
|
|
87
|
+
disabled?: boolean;
|
|
88
|
+
modelValue?: Option;
|
|
89
|
+
searchable?: boolean;
|
|
90
|
+
required?: boolean;
|
|
91
|
+
label?: string;
|
|
92
|
+
fullWidth?: boolean;
|
|
93
|
+
multiselect?: boolean;
|
|
94
94
|
}>();
|
|
95
95
|
|
|
96
96
|
let selectedItems = $ref<Option[]>([]);
|
|
97
|
-
let search = $ref(
|
|
97
|
+
let search = $ref('');
|
|
98
98
|
|
|
99
99
|
const dropdown = $ref<InstanceType<typeof Dropdown> | null>(null);
|
|
100
100
|
|
|
101
101
|
let open = $ref(false);
|
|
102
102
|
|
|
103
103
|
function openOptions() {
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
dropdown?.show();
|
|
105
|
+
// updateOpen(true);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function updateOpen(visible: boolean) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
open = visible;
|
|
110
|
+
if (!open) search = '';
|
|
111
|
+
else {
|
|
112
|
+
setTimeout(
|
|
113
|
+
() => (searchInput as any)?.$el?.querySelector('input')?.focus(),
|
|
114
|
+
100,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
const selectedLabel = $computed(() => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
120
|
+
if (selectedItems.length === 0) return props.placeholder || 'Select';
|
|
121
|
+
if (selectedItems.length > 4) {
|
|
122
|
+
const str = selectedItems
|
|
123
|
+
.slice(0, 4)
|
|
124
|
+
.map((item) => getLabel(item))
|
|
125
|
+
.join(', ');
|
|
126
|
+
return `${str}... +${selectedItems.length - 4}`;
|
|
127
|
+
}
|
|
128
|
+
return selectedItems.map((item) => getLabel(item)).join(', ');
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
const emit = defineEmits([
|
|
131
|
+
const emit = defineEmits(['update:modelValue']);
|
|
132
132
|
|
|
133
133
|
const getLabel = (option: Option) => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
if (!option) return '';
|
|
135
|
+
if (typeof option === 'string') return option;
|
|
136
|
+
if (typeof option === 'number') return `${option}`;
|
|
137
|
+
return option.label;
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
const getValue = (option
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
const getValue = (option?: Option) => {
|
|
141
|
+
if (!option) return undefined;
|
|
142
|
+
if (typeof option === 'string') return option;
|
|
143
|
+
if (typeof option === 'number') return option;
|
|
144
|
+
return option.value;
|
|
145
145
|
};
|
|
146
146
|
|
|
147
|
-
const isSelected = (option: Option) =>
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
);
|
|
161
|
-
})
|
|
162
|
-
);
|
|
147
|
+
const isSelected = (option: Option) => !!selectedItems.find((item) => getValue(option) === getValue(item));
|
|
148
|
+
|
|
149
|
+
const filteredOptions = $computed(() => props.options.filter((option) => {
|
|
150
|
+
const searchTerm = search
|
|
151
|
+
.split(/\s+/)
|
|
152
|
+
.filter(Boolean)
|
|
153
|
+
.map((t) => new RegExp(t, 'gi'));
|
|
154
|
+
return (
|
|
155
|
+
Boolean(option) &&
|
|
156
|
+
(searchTerm.every((s) => getLabel(option).match(s)) ||
|
|
157
|
+
searchTerm.length === 0)
|
|
158
|
+
);
|
|
159
|
+
}));
|
|
163
160
|
|
|
164
161
|
const select = (option: Option) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
162
|
+
const existingIndex = selectedItems.findIndex(
|
|
163
|
+
(item) => getValue(item) === getValue(option),
|
|
164
|
+
);
|
|
165
|
+
if (existingIndex > -1) selectedItems.splice(existingIndex, 1);
|
|
166
|
+
else if (props.multiselect) {
|
|
167
|
+
const current = [...selectedItems];
|
|
168
|
+
current.push(option);
|
|
169
|
+
|
|
170
|
+
selectedItems = current;
|
|
171
|
+
} else selectedItems.splice(0, selectedItems.length, option);
|
|
172
|
+
|
|
173
|
+
if (!props.multiselect) dropdown?.hide();
|
|
174
|
+
emitUpdate();
|
|
178
175
|
};
|
|
179
176
|
|
|
180
177
|
function emitUpdate() {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
178
|
+
if (props.multiselect) {
|
|
179
|
+
emit('update:modelValue', selectedItems.map(getValue).filter(Boolean));
|
|
180
|
+
} else {
|
|
181
|
+
// selectedItems.splice(1, selectedItems.length - 1);
|
|
182
|
+
const [item] = selectedItems;
|
|
183
|
+
emit('update:modelValue', getValue(item));
|
|
184
|
+
}
|
|
188
185
|
}
|
|
189
186
|
|
|
190
187
|
function compareArrays(arr1: Option[], arr2: Option[]) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
188
|
+
const arr1Values = [...arr1].map((a) => getValue(a)).filter(Boolean);
|
|
189
|
+
const arr2Values = [...arr2].map((a) => getValue(a)).filter(Boolean);
|
|
190
|
+
const isSame = arr1Values.every((item: Option) => arr2Values.includes(item));
|
|
191
|
+
return isSame;
|
|
195
192
|
}
|
|
196
193
|
|
|
197
194
|
watch(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
195
|
+
() => props.modelValue,
|
|
196
|
+
(newVal: Option | Option[]) => {
|
|
197
|
+
if (!props.multiselect) {
|
|
198
|
+
const newOption =
|
|
199
|
+
props.options.find((o) => getValue(o) === newVal) || newVal;
|
|
200
|
+
if (newOption && !isSelected(newOption)) selectedItems = [newOption];
|
|
201
|
+
} else {
|
|
202
|
+
const isSame = compareArrays([newVal].flat(), selectedItems);
|
|
203
|
+
if (!isSame) {
|
|
204
|
+
selectedItems.splice(0, selectedItems.length, ...[newVal].flat());
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{ immediate: true },
|
|
212
209
|
);
|
|
213
210
|
|
|
214
211
|
watch(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
212
|
+
() => props.options,
|
|
213
|
+
() => {
|
|
214
|
+
selectedItems.forEach((option, i) => {
|
|
215
|
+
const exists = props.options.find(
|
|
216
|
+
(o) => getValue(o) === getValue(option),
|
|
217
|
+
);
|
|
218
|
+
if (!exists) selectedItems.splice(i, 1);
|
|
219
|
+
else selectedItems.splice(i, 1, exists);
|
|
220
|
+
});
|
|
221
|
+
// const original = JSON.stringify(props.options.map(getValue));
|
|
222
|
+
// const newSelection = JSON.stringify(selectedItems.map(getValue));
|
|
223
|
+
// if (original !== newSelection) emitUpdate();
|
|
224
|
+
},
|
|
225
|
+
{ deep: true, immediate: true },
|
|
229
226
|
);
|
|
230
227
|
</script>
|
|
231
228
|
|
|
232
229
|
<style scoped>
|
|
233
230
|
.selectinput {
|
|
234
|
-
|
|
231
|
+
width: 100%;
|
|
235
232
|
}
|
|
236
233
|
|
|
237
234
|
.selectinput-option {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
235
|
+
padding: 6px 12px;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
border-radius: 5px;
|
|
238
|
+
transition: all 0.2s;
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: 10px 1fr;
|
|
241
|
+
justify-content: space-between;
|
|
242
|
+
width: 100%;
|
|
243
|
+
font-size: var(--input-font-size);
|
|
247
244
|
}
|
|
248
245
|
|
|
249
246
|
.selectinput-options {
|
|
250
|
-
|
|
251
|
-
|
|
247
|
+
max-height: 300px;
|
|
248
|
+
overflow-y: auto;
|
|
252
249
|
}
|
|
253
250
|
|
|
254
251
|
.selectinput-option:hover {
|
|
255
|
-
|
|
252
|
+
background: var(--bgl-gray-20);
|
|
256
253
|
}
|
|
257
254
|
.isEmpty p {
|
|
258
|
-
|
|
255
|
+
opacity: 0.3;
|
|
259
256
|
}
|
|
260
257
|
</style>
|
|
261
258
|
|
|
262
259
|
<style>
|
|
263
260
|
.bagel-input label {
|
|
264
|
-
|
|
261
|
+
font-size: var(--label-font-size);
|
|
265
262
|
}
|
|
266
263
|
|
|
267
264
|
.selectinput-btn {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
265
|
+
display: flex;
|
|
266
|
+
justify-content: space-between;
|
|
267
|
+
align-items: center;
|
|
268
|
+
height: var(--input-height);
|
|
269
|
+
border-radius: var(--input-border-radius);
|
|
270
|
+
border: none;
|
|
271
|
+
background: var(--input-bg);
|
|
272
|
+
padding: 0.7rem;
|
|
273
|
+
color: var(--input-color);
|
|
274
|
+
width: 100%;
|
|
275
|
+
font-family: inherit;
|
|
276
|
+
font-size: var(--input-font-size);
|
|
280
277
|
}
|
|
281
278
|
|
|
282
279
|
.selectinput-btn:disabled {
|
|
283
|
-
|
|
284
|
-
|
|
280
|
+
color: var(--input-disabled-color);
|
|
281
|
+
background-color: transparent;
|
|
285
282
|
}
|
|
286
283
|
|
|
287
284
|
.selectinput-btn:focus {
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
outline: none;
|
|
286
|
+
box-shadow: inset 0 0 10px #00000012;
|
|
290
287
|
}
|
|
291
288
|
|
|
292
289
|
.v-popper__arrow-container {
|
|
293
|
-
|
|
290
|
+
display: none;
|
|
294
291
|
}
|
|
295
292
|
|
|
296
293
|
.v-popper--theme-dropdown .v-popper__inner {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
border: none;
|
|
295
|
+
/* background: transparent; if anyone is changing this please talk to me first*/
|
|
296
|
+
border-radius: var(--card-border-radius);
|
|
297
|
+
color: var(--input-color);
|
|
301
298
|
}
|
|
302
299
|
</style>
|
package/src/styles/bagel.css
CHANGED
|
@@ -16,13 +16,6 @@
|
|
|
16
16
|
--transition-ease: cubic-bezier(0.1, 0.5, 0.33, 1);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
body {
|
|
20
|
-
max-width: 100vw;
|
|
21
|
-
height: 100vh;
|
|
22
|
-
padding: 0;
|
|
23
|
-
margin: 0;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
19
|
.grid>* {
|
|
27
20
|
min-height: 0;
|
|
28
21
|
}
|
|
@@ -31,7 +24,66 @@ body {
|
|
|
31
24
|
box-sizing: border-box;
|
|
32
25
|
}
|
|
33
26
|
|
|
34
|
-
|
|
27
|
+
|
|
28
|
+
html {
|
|
29
|
+
-ms-text-size-adjust: 100%;
|
|
30
|
+
-webkit-text-size-adjust: 100%;
|
|
31
|
+
height: 100%;
|
|
32
|
+
background-attachment: scroll;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body {
|
|
36
|
+
margin: 0;
|
|
37
|
+
min-height: 100%;
|
|
38
|
+
background-color: var(--bgl-bg);
|
|
39
|
+
font-family: var(--bgl-font);
|
|
40
|
+
font-size: 18px;
|
|
41
|
+
font-weight: 400;
|
|
42
|
+
line-height: 1.65;
|
|
43
|
+
width: auto;
|
|
44
|
+
height: auto;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
a {
|
|
48
|
+
background-color: transparent;
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
display: inline-block;
|
|
51
|
+
color: inherit;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
a:active,
|
|
55
|
+
a:hover {
|
|
56
|
+
outline: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
b,
|
|
60
|
+
strong {
|
|
61
|
+
font-weight: bold;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
hr {
|
|
65
|
+
box-sizing: content-box;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
img,
|
|
69
|
+
video,
|
|
70
|
+
canvas,
|
|
71
|
+
audio,
|
|
72
|
+
iframe,
|
|
73
|
+
embed,
|
|
74
|
+
object {
|
|
75
|
+
max-width: 100%;
|
|
76
|
+
vertical-align: middle;
|
|
77
|
+
border: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
input,
|
|
81
|
+
button,
|
|
82
|
+
textarea,
|
|
83
|
+
select {
|
|
84
|
+
font: inherit;
|
|
85
|
+
}
|
|
86
|
+
|
|
35
87
|
|
|
36
88
|
@media screen and (max-width: 910px) {
|
|
37
89
|
|