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