@bagelink/vue 1.4.153 → 1.4.159
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 +17 -2
- package/dist/components/Alert.vue.d.ts.map +1 -1
- package/dist/components/ListItem.vue.d.ts +2 -2
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/Loading.vue.d.ts +3 -2
- package/dist/components/Loading.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ColorInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/NumberInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +21 -21
- package/dist/index.mjs +21 -21
- package/dist/style.css +1 -1
- package/dist/types/BagelForm.d.ts +1 -0
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/types/BtnOptions.d.ts +3 -0
- package/dist/types/BtnOptions.d.ts.map +1 -1
- package/dist/utils/sizeParsing.d.ts +3 -0
- package/dist/utils/sizeParsing.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/components/Alert.vue +12 -9
- package/src/components/ListItem.vue +42 -35
- package/src/components/Loading.vue +73 -25
- package/src/components/Modal.vue +40 -24
- package/src/components/form/inputs/ColorInput.vue +5 -5
- package/src/components/form/inputs/DateInput.vue +0 -1
- package/src/components/form/inputs/NumberInput.vue +2 -2
- package/src/components/form/inputs/RichText/index.vue +147 -271
- package/src/components/form/inputs/Upload/UploadInput.vue +95 -212
- package/src/components/form/inputs/Upload/upload.css +36 -2
- package/src/components/layout/AppSidebar.vue +1 -1
- package/src/components/layout/TabsNav.vue +24 -22
- package/src/components/lightbox/Lightbox.vue +1 -0
- package/src/styles/colors.css +502 -3
- package/src/styles/inputs.css +12 -4
- package/src/styles/layout.css +779 -1
- package/src/styles/mobilLayout.css +779 -1
- package/src/styles/mobileColors.css +4167 -3739
- package/src/types/BagelForm.ts +1 -0
- package/src/types/BtnOptions.ts +4 -0
- package/src/utils/sizeParsing.ts +26 -0
|
@@ -66,225 +66,108 @@ function fileName(pathKey: string) {
|
|
|
66
66
|
</script>
|
|
67
67
|
|
|
68
68
|
<template>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<Btn
|
|
80
|
-
v-if="!pathKeys.length && !fileQueue.length"
|
|
81
|
-
class="px-1-5"
|
|
82
|
-
icon="upload"
|
|
83
|
-
outline
|
|
84
|
-
:value="btnPlaceholder || 'Upload'"
|
|
85
|
-
@click="browse()"
|
|
86
|
-
/>
|
|
87
|
-
|
|
88
|
-
<template v-for="path_key in pathKeys" :key="path_key">
|
|
89
|
-
<div class="txt-gray txt-12 flex">
|
|
90
|
-
<div class="m-05 flex opacity-7 z-99">
|
|
91
|
-
<Btn
|
|
92
|
-
v-tooltip="'Delete'"
|
|
93
|
-
color="gray"
|
|
94
|
-
thin
|
|
95
|
-
icon="delete"
|
|
96
|
-
@click="removeFile(path_key)"
|
|
97
|
-
/>
|
|
98
|
-
<Btn
|
|
99
|
-
v-tooltip="'Replace'"
|
|
100
|
-
color="gray"
|
|
101
|
-
thin
|
|
102
|
-
icon="autorenew"
|
|
103
|
-
@click="browse()"
|
|
104
|
-
/>
|
|
105
|
-
<Btn
|
|
106
|
-
icon="download"
|
|
107
|
-
flat
|
|
108
|
-
thin
|
|
109
|
-
:href="pathKeyToURL(path_key)"
|
|
110
|
-
:download="fileName(path_key)"
|
|
111
|
-
/>
|
|
112
|
-
<div class="flex gap-025 rounded pe-1 ps-05 py-025 bg-gray-80 -my-1 ">
|
|
113
|
-
<Icon icon="draft" :size="1.5" />
|
|
114
|
-
<p
|
|
115
|
-
v-lightbox="{ src: pathKeyToURL(path_key), download: true }"
|
|
116
|
-
class="ellipsis-1 word-break-all h-20 m-0 color-black txt16"
|
|
117
|
-
>
|
|
118
|
-
{{ fileName(path_key) }}
|
|
119
|
-
</p>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</template>
|
|
124
|
-
|
|
125
|
-
<span
|
|
126
|
-
v-if="!pathKeys.length && !fileQueue.length"
|
|
127
|
-
class="txt-gray txt-12"
|
|
128
|
-
>
|
|
129
|
-
{{ noFilePlaceholder || 'No file selected' }}
|
|
69
|
+
<div class="bagel-input">
|
|
70
|
+
<label v-if="label">{{ label }}</label>
|
|
71
|
+
<input v-if="required && !pathKeys.length" placeholder="required" type="text" required class="pixel">
|
|
72
|
+
<Card v-if="theme === 'basic'" outline class="flex p-05 gap-1" @dragover="(e) => handleDrag(e, true)" @drop="handleDrop">
|
|
73
|
+
<Btn v-if="!pathKeys.length && !fileQueue.length" class="px-1-5" icon="upload" outline :value="btnPlaceholder || 'Upload'" @click="browse()" />
|
|
74
|
+
<!-- Loading state during upload -->
|
|
75
|
+
<div v-if="fileQueue.length > 0" class="flex align-items-center gap-1 p-05">
|
|
76
|
+
<Loading size="20" />
|
|
77
|
+
<span class="txt-gray txt-12">
|
|
78
|
+
Uploading {{ fileQueue.length }} file{{ fileQueue.length > 1 ? 's' : '' }}...
|
|
130
79
|
</span>
|
|
131
|
-
</
|
|
80
|
+
</div>
|
|
132
81
|
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
@click="browse()"
|
|
143
|
-
@dragover="(e) => handleDrag(e, true)"
|
|
144
|
-
@drop="handleDrop"
|
|
145
|
-
@dragleave="(e) => handleDrag(e)"
|
|
146
|
-
>
|
|
147
|
-
<slot name="files" :files="pathKeys" :fileQueue>
|
|
148
|
-
<div v-if="multiple" class="bgl-multi-preview">
|
|
149
|
-
<div
|
|
150
|
-
v-for="path_key in pathKeys"
|
|
151
|
-
:key="path_key"
|
|
152
|
-
v-lightbox="{ src: pathKeyToURL(path_key), download: true }"
|
|
153
|
-
class="multi-image-item-preview"
|
|
154
|
-
:class="{ 'bgl_fill-image': fill }"
|
|
155
|
-
>
|
|
156
|
-
<Image
|
|
157
|
-
v-if="isImage(path_key)"
|
|
158
|
-
:pathKey="path_key"
|
|
159
|
-
class="multi-preview"
|
|
160
|
-
/>
|
|
161
|
-
<Icon v-else icon="description" class="multi-preview" />
|
|
162
|
-
<p class="m-0">
|
|
82
|
+
<template v-for="path_key in pathKeys" :key="path_key">
|
|
83
|
+
<div class="txt-gray txt-12 flex">
|
|
84
|
+
<div class="m-05 flex opacity-8 z-99 gap-025">
|
|
85
|
+
<Btn v-tooltip="'Delete'" color="gray" thin icon="delete" @click="removeFile(path_key)" />
|
|
86
|
+
<Btn v-tooltip="'Replace'" color="gray" thin icon="autorenew" @click="browse()" />
|
|
87
|
+
<Btn icon="download" color="gray" thin :href="pathKeyToURL(path_key)" :download="fileName(path_key)" />
|
|
88
|
+
<div class="flex gap-025 rounded pe-1 ps-05 py-025 -my-1 ">
|
|
89
|
+
<Icon icon="draft" :size="1.5" />
|
|
90
|
+
<p v-lightbox="{ src: pathKeyToURL(path_key), download: true }" class="ellipsis-1 word-break-all h-20 m-0 color-black txt16">
|
|
163
91
|
{{ fileName(path_key) }}
|
|
164
92
|
</p>
|
|
165
|
-
<Btn
|
|
166
|
-
thin
|
|
167
|
-
flat
|
|
168
|
-
icon="delete"
|
|
169
|
-
color="red"
|
|
170
|
-
@click="removeFile(path_key)"
|
|
171
|
-
/>
|
|
172
|
-
</div>
|
|
173
|
-
<div
|
|
174
|
-
v-for="file in fileQueue"
|
|
175
|
-
:key="file.name"
|
|
176
|
-
class="multi-image-item-preview"
|
|
177
|
-
:class="{ 'bgl_fill-image': fill }"
|
|
178
|
-
>
|
|
179
|
-
<img
|
|
180
|
-
v-if="isImage(file.file.type)"
|
|
181
|
-
class="multi-preview"
|
|
182
|
-
:src="file.url"
|
|
183
|
-
alt=""
|
|
184
|
-
>
|
|
185
|
-
<Icon v-else icon="description" class="multi-preview" />
|
|
186
|
-
<p class="no-margin multi-preview-txt">
|
|
187
|
-
{{ file.name }}
|
|
188
|
-
</p>
|
|
189
|
-
<div
|
|
190
|
-
class="pie"
|
|
191
|
-
:style="{ '--p': file.progress }"
|
|
192
|
-
style="--b: 2px"
|
|
193
|
-
:class="{ complete: file.progress === 100 }"
|
|
194
|
-
>
|
|
195
|
-
<span v-if="file.progress < 100" class="progress">
|
|
196
|
-
{{ file.progress.toFixed(0) }}
|
|
197
|
-
</span>
|
|
198
|
-
<Icon class="success" icon="check_circle" />
|
|
199
93
|
</div>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
|
|
203
|
-
<div
|
|
204
|
-
v-else-if="pathKeys.length > 0 || fileQueue.length > 0"
|
|
205
|
-
class="bgl-single-preview"
|
|
206
|
-
>
|
|
207
|
-
<div
|
|
208
|
-
v-for="path_key in pathKeys"
|
|
209
|
-
:key="path_key"
|
|
210
|
-
class="single-image-item-preview"
|
|
211
|
-
:class="{ 'bgl_fill-image': fill }"
|
|
212
|
-
>
|
|
213
|
-
<div class="position-start m-05 flex opacity-7 z-99 gap-025">
|
|
214
|
-
<Btn
|
|
215
|
-
v-tooltip="'Delete'"
|
|
216
|
-
color="gray"
|
|
217
|
-
thin
|
|
218
|
-
icon="delete"
|
|
219
|
-
@click="removeFile(path_key)"
|
|
220
|
-
/>
|
|
221
|
-
<Btn
|
|
222
|
-
v-tooltip="'Replace'"
|
|
223
|
-
color="gray"
|
|
224
|
-
thin
|
|
225
|
-
icon="autorenew"
|
|
226
|
-
@click="browse()"
|
|
227
|
-
/>
|
|
228
|
-
<Btn
|
|
229
|
-
v-tooltip="'Download'"
|
|
230
|
-
color="gray"
|
|
231
|
-
thin
|
|
232
|
-
icon="download"
|
|
233
|
-
:href="pathKeyToURL(path_key)"
|
|
234
|
-
:download="fileName(path_key)"
|
|
235
|
-
/>
|
|
236
94
|
</div>
|
|
237
|
-
<div v-if="isImage(path_key)" class="h-100">
|
|
238
|
-
<Image
|
|
239
|
-
v-lightbox="{ src: pathKeyToURL(path_key), download: true }"
|
|
240
|
-
|
|
241
|
-
class="single-preview"
|
|
242
|
-
:pathKey="path_key"
|
|
243
|
-
alt=""
|
|
244
|
-
/>
|
|
245
95
|
</div>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<span v-if="!pathKeys.length && !fileQueue.length" class="txt-gray txt-12">
|
|
99
|
+
{{ noFilePlaceholder || 'No file selected' }}
|
|
100
|
+
</span>
|
|
101
|
+
</Card>
|
|
102
|
+
|
|
103
|
+
<div v-else class="fileUploadWrap" :class="{
|
|
104
|
+
'fileDropZone': !pathKeys.length && !fileQueue.length,
|
|
105
|
+
'dragover': isDragOver,
|
|
106
|
+
'bgl_oval-upload': oval,
|
|
107
|
+
}" :style="{ width, height }" @click="browse()" @dragover="(e) => handleDrag(e, true)" @drop="handleDrop" @dragleave="(e) => handleDrag(e)">
|
|
108
|
+
<slot name="files" :files="pathKeys" :fileQueue>
|
|
109
|
+
<div v-if="multiple" class="bgl-multi-preview">
|
|
110
|
+
<!-- Show uploaded files only if showFileList is true (default) -->
|
|
111
|
+
<div v-if="showFileList !== false" v-for="path_key in pathKeys" :key="path_key" v-lightbox="{ src: pathKeyToURL(path_key), download: true }" class="multi-image-item-preview"
|
|
112
|
+
:class="{ 'bgl_fill-image': fill }">
|
|
113
|
+
<Image v-if="isImage(path_key)" :pathKey="path_key" class="multi-preview" />
|
|
114
|
+
<Icon v-else icon="description" class="multi-preview" />
|
|
115
|
+
<p class="m-0">
|
|
116
|
+
{{ fileName(path_key) }}
|
|
117
|
+
</p>
|
|
118
|
+
<Btn thin flat icon="delete" color="red" @click="removeFile(path_key)" />
|
|
119
|
+
</div>
|
|
120
|
+
<!-- File count indicator when showFileList is false and multiple files -->
|
|
121
|
+
<div v-if="showFileList === false && pathKeys.length > 0" class="flex column gap-05 pt-1">
|
|
122
|
+
<p class="color-primary">
|
|
123
|
+
{{ pathKeys.length }} file{{ pathKeys.length > 1 ? 's' : '' }} uploaded
|
|
124
|
+
</p>
|
|
125
|
+
<Btn color="primary" thin icon="autorenew" value="Replace files" @click="browse()" />
|
|
126
|
+
</div>
|
|
127
|
+
<div v-for="file in fileQueue" :key="file.name" class="multi-image-item-preview" :class="{ 'bgl_fill-image': fill }">
|
|
128
|
+
<img v-if="isImage(file.file.type)" class="multi-preview" :src="file.url" alt="">
|
|
129
|
+
<Icon v-else icon="description" class="multi-preview" />
|
|
130
|
+
<p class="no-margin multi-preview-txt">
|
|
131
|
+
{{ file.name }}
|
|
132
|
+
</p>
|
|
133
|
+
<div class="pie" :style="{ '--p': file.progress }" style="--b: 2px" :class="{ complete: file.progress === 100 }">
|
|
134
|
+
<span v-if="file.progress < 100" class="progress">
|
|
135
|
+
{{ file.progress.toFixed(0) }}
|
|
136
|
+
</span>
|
|
137
|
+
<Icon class="success" icon="check_circle" />
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<div v-else-if="pathKeys.length > 0 || fileQueue.length > 0" class="bgl-single-preview">
|
|
143
|
+
<div v-for="path_key in pathKeys" :key="path_key" class="single-image-item-preview" :class="{ 'bgl_fill-image': fill }">
|
|
144
|
+
<div class="flex opacity-8 z-99 gap-025 absolute top-0 start-0" :class="{ 'mx-auto end-0 mt-1 justify-content-center': oval, 'm-05': !oval }">
|
|
145
|
+
<Btn v-tooltip="'Delete'" color="white" frame thin icon="delete" @click="removeFile(path_key)" />
|
|
146
|
+
<Btn v-tooltip="'Replace'" color="white" frame thin icon="autorenew" @click="browse()" />
|
|
147
|
+
<Btn v-tooltip="'Download'" color="white" frame thin icon="download" :href="pathKeyToURL(path_key)" :download="fileName(path_key)" />
|
|
148
|
+
</div>
|
|
149
|
+
<div v-if="isImage(path_key)" class="h-100">
|
|
150
|
+
<Image v-lightbox="{ src: pathKeyToURL(path_key), download: true }" class="single-preview" :pathKey="path_key" alt="" />
|
|
151
|
+
</div>
|
|
152
|
+
<Icon v-else v-lightbox="{ src: pathKeyToURL(path_key), download: true }" :size="4" weight="2" icon="description" class="color-primary w-100" />
|
|
153
|
+
</div>
|
|
154
|
+
<div v-for="file in fileQueue" :key="file.name" class="single-image-item-preview" :class="{ 'bgl_fill-image': fill }">
|
|
155
|
+
<Loading type="bar" :progress="file.progress" class="absolute bottom-05 end start w-100px mx-auto rounded overflow-hidden" />
|
|
156
|
+
<img v-if="isImage(file.file.type)" class="single-preview opacity-5" :src="file.url" alt="">
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
</slot>
|
|
160
|
+
|
|
161
|
+
<slot v-if="(!pathKeys.length && !fileQueue.length) || multiple" name="placeholder" :files="pathKeys" :fileQueue :browse>
|
|
162
|
+
<p class="p-1 flex column hover fileUploadPlaceHolder justify-content-center mb-05 ">
|
|
163
|
+
<Icon icon="upload_2" class="user-select-none" />
|
|
164
|
+
<span class=" pretty balance user-select-none ">
|
|
165
|
+
{{ dropPlaceholder || 'Drag and Drop files here or click to upload' }}
|
|
166
|
+
</span>
|
|
167
|
+
</p>
|
|
168
|
+
</slot>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
288
171
|
</template>
|
|
289
172
|
|
|
290
173
|
<style src="./upload.css" scoped></style>
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
object-fit: contain;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
.single-image-item-preview
|
|
109
|
+
.single-image-item-preview::after {
|
|
110
110
|
content: 'zoom_in';
|
|
111
111
|
font-size: 32px;
|
|
112
112
|
font-family: 'Material Symbols Outlined', serif;
|
|
@@ -115,6 +115,16 @@
|
|
|
115
115
|
color: var(--bgl-white);
|
|
116
116
|
z-index: 9;
|
|
117
117
|
pointer-events: none;
|
|
118
|
+
opacity: 0;
|
|
119
|
+
transition: all 0.4s ease;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.single-image-item-preview:hover::after {
|
|
123
|
+
opacity: 1;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.single-image-item-preview img {
|
|
127
|
+
transition: all 0.2s ease;
|
|
118
128
|
}
|
|
119
129
|
|
|
120
130
|
.single-image-item-preview:hover img {
|
|
@@ -125,7 +135,13 @@
|
|
|
125
135
|
height: 100%;
|
|
126
136
|
}
|
|
127
137
|
|
|
128
|
-
.bgl_fill-image.single-image-item-preview
|
|
138
|
+
.bgl_fill-image.single-image-item-preview img {
|
|
139
|
+
height: 100%;
|
|
140
|
+
width: 100%;
|
|
141
|
+
object-fit: cover;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.bgl_fill-image.single-image-item-preview div:has(img) {
|
|
129
145
|
border-radius: unset;
|
|
130
146
|
object-fit: cover;
|
|
131
147
|
box-shadow: unset;
|
|
@@ -133,6 +149,15 @@
|
|
|
133
149
|
height: auto;
|
|
134
150
|
}
|
|
135
151
|
|
|
152
|
+
.fileUploadWrap:not(.bgl_oval-upload) .bgl_fill-image .single-preview {
|
|
153
|
+
margin: 0rem !important;
|
|
154
|
+
height: 100% !important;
|
|
155
|
+
max-height: 100% !important;
|
|
156
|
+
max-width: 100% !important;
|
|
157
|
+
width: 100% !important;
|
|
158
|
+
object-fit: cover !important;
|
|
159
|
+
}
|
|
160
|
+
|
|
136
161
|
.single-image-item-preview .pie {
|
|
137
162
|
transform-origin: top;
|
|
138
163
|
transform: scale(1.4);
|
|
@@ -151,6 +176,8 @@
|
|
|
151
176
|
.bgl_oval-upload {
|
|
152
177
|
border-radius: 100% !important;
|
|
153
178
|
overflow: hidden;
|
|
179
|
+
aspect-ratio: 1 !important;
|
|
180
|
+
margin-inline: auto !important;
|
|
154
181
|
}
|
|
155
182
|
|
|
156
183
|
.bgl_oval-upload p {
|
|
@@ -177,8 +204,15 @@
|
|
|
177
204
|
.bgl_oval-upload .single-preview {
|
|
178
205
|
margin: 0;
|
|
179
206
|
height: 100% !important;
|
|
207
|
+
max-width: unset;
|
|
208
|
+
max-height: unset;
|
|
180
209
|
}
|
|
181
210
|
|
|
211
|
+
.bgl_oval-upload .bgl_fill-image .single-preview {
|
|
212
|
+
height: auto !important;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
182
216
|
.pie {
|
|
183
217
|
width: 30px;
|
|
184
218
|
height: 30px;
|
|
@@ -35,9 +35,15 @@ function updateIndicator() {
|
|
|
35
35
|
if (!tabsWrap.value) return
|
|
36
36
|
const activeTab = tabs.value.find(tab => tab.classList.contains('active'))
|
|
37
37
|
if (activeTab) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
// Wait a bit more to ensure CSS is fully loaded and elements are properly sized
|
|
39
|
+
setTimeout(() => {
|
|
40
|
+
const { offsetLeft, offsetWidth } = activeTab
|
|
41
|
+
if (offsetLeft >= 0 && offsetWidth > 0) { // Ensure valid measurements
|
|
42
|
+
tabsWrap.value?.style.setProperty('--indicator-left', `${offsetLeft}px`)
|
|
43
|
+
tabsWrap.value?.style.setProperty('--indicator-width', `${offsetWidth}px`)
|
|
44
|
+
tabsWrap.value?.style.setProperty('--indicator-opacity', '1')
|
|
45
|
+
}
|
|
46
|
+
}, 10)
|
|
41
47
|
}
|
|
42
48
|
})
|
|
43
49
|
}
|
|
@@ -69,7 +75,12 @@ watch(
|
|
|
69
75
|
|
|
70
76
|
onMounted(() => {
|
|
71
77
|
tabs.value = Array.from(tabsWrap.value?.querySelectorAll('.bgl_tab') || [])
|
|
72
|
-
|
|
78
|
+
|
|
79
|
+
// Wait for the next frame to ensure all CSS and layout calculations are done
|
|
80
|
+
requestAnimationFrame(() => {
|
|
81
|
+
updateIndicator()
|
|
82
|
+
})
|
|
83
|
+
|
|
73
84
|
window.addEventListener('resize', updateIndicator)
|
|
74
85
|
})
|
|
75
86
|
|
|
@@ -86,24 +97,14 @@ onBeforeUnmount(() => {
|
|
|
86
97
|
</script>
|
|
87
98
|
|
|
88
99
|
<template>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
v-for="(tab, i) in props.tabs"
|
|
96
|
-
:key="i"
|
|
97
|
-
type="button"
|
|
98
|
-
:class="{ active: isActive(tab) }"
|
|
99
|
-
class="bgl_tab relative z-1"
|
|
100
|
-
@click="selectTab(tab)"
|
|
101
|
-
>
|
|
102
|
-
<Icon v-if="typeof tab !== 'string' && tab.icon" :icon="tab.icon" />
|
|
103
|
-
{{ tabLabel(tab) }}
|
|
104
|
-
</button>
|
|
100
|
+
<div ref="tabsWrap" class="grid auto-flow-columns relative fit-content bgl_tabs_wrap overflow-hidden" :class="{ 'bgl_flat-tabs': flat, 'bgl_vertical-tabs': vertical }">
|
|
101
|
+
<slot name="tabs" v-bind="{ selectTab, isActive, tabLabel, tabs }">
|
|
102
|
+
<button v-for="(tab, i) in props.tabs" :key="i" type="button" :class="{ active: isActive(tab) }" class="bgl_tab relative z-1" @click="selectTab(tab)">
|
|
103
|
+
<Icon v-if="typeof tab !== 'string' && tab.icon" :icon="tab.icon" />
|
|
104
|
+
{{ tabLabel(tab) }}
|
|
105
|
+
</button>
|
|
105
106
|
</slot>
|
|
106
|
-
|
|
107
|
+
</div>
|
|
107
108
|
</template>
|
|
108
109
|
|
|
109
110
|
<style scoped>
|
|
@@ -127,7 +128,6 @@ onBeforeUnmount(() => {
|
|
|
127
128
|
border-radius: var(--input-border-radius);
|
|
128
129
|
transition: var(--bgl-transition);
|
|
129
130
|
color: inherit
|
|
130
|
-
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
.bgl_tab:hover {
|
|
@@ -145,6 +145,8 @@ onBeforeUnmount(() => {
|
|
|
145
145
|
border-radius: var(--input-border-radius);
|
|
146
146
|
transition: var(--bgl-transition);
|
|
147
147
|
z-index: 0;
|
|
148
|
+
/* Hide indicator initially until position is calculated */
|
|
149
|
+
opacity: var(--indicator-opacity, 0);
|
|
148
150
|
}
|
|
149
151
|
.bgl_flat-tabs.bgl_tabs_wrap {
|
|
150
152
|
background: transparent;
|