@bagelink/vue 0.0.322 → 0.0.335
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/MaterialIcon.vue.d.ts +4 -2
- package/dist/components/MaterialIcon.vue.d.ts.map +1 -1
- package/dist/components/Modal.vue.d.ts +0 -1
- package/dist/components/ModalBglForm.vue.d.ts.map +1 -1
- package/dist/components/Popover.vue.d.ts +10 -0
- package/dist/components/Popover.vue.d.ts.map +1 -0
- package/dist/components/form/BglForm.vue.d.ts +1 -9
- package/dist/components/form/BglForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DateInput.vue.d.ts +0 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +22 -16
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TextArea.vue.d.ts +3 -1
- package/dist/components/form/inputs/TextArea.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TextInput.vue.d.ts.map +1 -1
- package/dist/components/formkit/index.d.ts +1 -14
- package/dist/components/formkit/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +0 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layout/Layout.vue.d.ts +8 -0
- package/dist/components/layout/Layout.vue.d.ts.map +1 -1
- package/dist/components/layout/SidebarMenu.vue.d.ts.map +1 -1
- package/dist/components/layout/TabbedLayout.vue.d.ts +38 -0
- package/dist/components/layout/TabbedLayout.vue.d.ts.map +1 -0
- package/dist/components/layout/Tabs.vue.d.ts +24 -0
- package/dist/components/layout/Tabs.vue.d.ts.map +1 -0
- package/dist/components/layout/TabsBody.vue.d.ts +21 -0
- package/dist/components/layout/TabsBody.vue.d.ts.map +1 -0
- package/dist/components/layout/TabsNav.vue.d.ts +25 -0
- package/dist/components/layout/TabsNav.vue.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +4 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/tabsManager.d.ts +4 -0
- package/dist/components/layout/tabsManager.d.ts.map +1 -0
- package/dist/index.cjs +783 -2035
- package/dist/index.d.ts +0 -1
- package/dist/index.mjs +785 -2037
- package/dist/style.css +2129 -695
- package/dist/types/index.d.ts +7 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/materialIcon.d.ts +2 -0
- package/dist/types/materialIcon.d.ts.map +1 -0
- package/dist/types/materialIcons.d.ts +1 -1
- package/dist/types/materialIcons.d.ts.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/MaterialIcon.vue +3 -2
- package/src/components/PageTitle.vue +10 -20
- package/src/components/form/BglField.vue +2 -2
- package/src/components/form/BglForm.vue +49 -53
- package/src/components/form/inputs/SelectInput.vue +128 -484
- package/src/components/form/inputs/TextInput.vue +158 -112
- package/src/components/index.ts +0 -2
- package/src/components/layout/Layout.vue +34 -13
- package/src/components/layout/SidebarMenu.vue +22 -22
- package/src/components/layout/TabbedLayout.vue +79 -0
- package/src/components/layout/Tabs.vue +19 -0
- package/src/components/layout/TabsBody.vue +15 -0
- package/src/components/layout/TabsNav.vue +48 -0
- package/src/components/layout/index.ts +4 -0
- package/src/components/layout/tabsManager.ts +18 -0
- package/src/styles/appearance.css +81 -0
- package/src/styles/bagel.css +2 -0
- package/src/styles/layout.css +162 -17
- package/src/styles/mobilLayout.css +1476 -0
- package/src/styles/text.css +153 -1
- package/src/types/index.ts +9 -1
- package/src/types/materialIcons.ts +1180 -1178
- package/src/utils/BagelFormUtils.ts +1 -1
- package/src/utils/index.ts +0 -1
- package/src/components/ComboBox.vue +0 -165
- package/src/components/TabbedLayout.vue +0 -87
|
@@ -1,65 +1,110 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
<div
|
|
3
|
+
class="bagel-input text-input"
|
|
4
|
+
:class="{
|
|
5
|
+
dense, small, shrink, toggleEdit, editMode, code,
|
|
6
|
+
textInputIconWrap: icon,
|
|
7
|
+
txtInputIconStart: iconStart,
|
|
8
|
+
}"
|
|
9
|
+
:title="title"
|
|
10
|
+
>
|
|
11
|
+
<label :for="id">
|
|
12
|
+
{{ label }}
|
|
13
|
+
<input
|
|
14
|
+
v-if="!multiline && !autoheight && !code"
|
|
15
|
+
:title="title"
|
|
16
|
+
:autocomplete="autocomplete"
|
|
17
|
+
:id="id"
|
|
18
|
+
v-model="inputVal"
|
|
19
|
+
:type="type"
|
|
20
|
+
:rows="1"
|
|
21
|
+
ref="input"
|
|
22
|
+
:placeholder="placeholder || label"
|
|
23
|
+
:disabled="!editMode"
|
|
24
|
+
:required="required"
|
|
25
|
+
:pattern="pattern"
|
|
26
|
+
v-bind="nativeInputAttrs"
|
|
27
|
+
@dblclick="toggleEditAction"
|
|
28
|
+
>
|
|
29
|
+
<textarea
|
|
30
|
+
v-else
|
|
31
|
+
:title="title"
|
|
32
|
+
:id="id"
|
|
33
|
+
v-model="inputVal"
|
|
34
|
+
:type="type"
|
|
35
|
+
:rows="rows"
|
|
36
|
+
ref="input"
|
|
37
|
+
:placeholder="placeholder || label"
|
|
38
|
+
:disabled="!editMode"
|
|
39
|
+
:required="required"
|
|
40
|
+
:pattern="pattern"
|
|
41
|
+
v-bind="nativeInputAttrs"
|
|
42
|
+
@dblclick="toggleEditAction"
|
|
43
|
+
/>
|
|
44
|
+
<p v-if="helptext">{{ helptext }}</p>
|
|
45
|
+
</label>
|
|
46
|
+
|
|
47
|
+
<MaterialIcon
|
|
48
|
+
class="iconStart"
|
|
49
|
+
v-if="iconStart"
|
|
50
|
+
:icon="iconStart"
|
|
51
|
+
/>
|
|
52
|
+
<MaterialIcon
|
|
53
|
+
v-if="icon"
|
|
54
|
+
:icon="icon"
|
|
55
|
+
/>
|
|
56
|
+
<Btn
|
|
57
|
+
class="toggleEditBtn"
|
|
58
|
+
v-if="toggleEdit"
|
|
59
|
+
thin
|
|
60
|
+
@click="toggleEditAction"
|
|
61
|
+
:icon="editMode ? 'check' : 'edit'"
|
|
62
|
+
flat
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
23
65
|
</template>
|
|
24
66
|
|
|
25
|
-
<script
|
|
67
|
+
<script
|
|
68
|
+
setup
|
|
69
|
+
lang="ts"
|
|
70
|
+
>
|
|
26
71
|
import { onMounted, watch } from 'vue';
|
|
27
72
|
import {
|
|
28
|
-
|
|
73
|
+
debounce, Btn, type MaterialIcons, MaterialIcon,
|
|
29
74
|
} from '@bagelink/vue';
|
|
30
75
|
|
|
31
76
|
const emit = defineEmits(['update:modelValue', 'debounce']);
|
|
32
77
|
const props = withDefaults(
|
|
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
|
-
|
|
78
|
+
defineProps<{
|
|
79
|
+
id?: string;
|
|
80
|
+
title?: string;
|
|
81
|
+
helptext?: string;
|
|
82
|
+
placeholder?: string;
|
|
83
|
+
modelValue?: string | number;
|
|
84
|
+
label?: string;
|
|
85
|
+
small?: boolean;
|
|
86
|
+
dense?: boolean;
|
|
87
|
+
required?: boolean;
|
|
88
|
+
pattern?: string;
|
|
89
|
+
shrink?: boolean;
|
|
90
|
+
toggleEdit?: boolean;
|
|
91
|
+
type?: string;
|
|
92
|
+
nativeInputAttrs?: Record<string, any>;
|
|
93
|
+
icon?: MaterialIcons;
|
|
94
|
+
iconStart?: MaterialIcons;
|
|
95
|
+
multiline?: boolean;
|
|
96
|
+
autoheight?: boolean;
|
|
97
|
+
code?: boolean;
|
|
98
|
+
lines?: number;
|
|
99
|
+
autocomplete?: string;
|
|
100
|
+
autofocus?: boolean;
|
|
101
|
+
}>(),
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
toggleEdit: false,
|
|
105
|
+
modelValue: '',
|
|
106
|
+
autocomplete: 'off',
|
|
107
|
+
},
|
|
63
108
|
);
|
|
64
109
|
let inputVal = $ref<string | number>();
|
|
65
110
|
let editMode = $ref<boolean>(!props.toggleEdit);
|
|
@@ -67,53 +112,54 @@ let editMode = $ref<boolean>(!props.toggleEdit);
|
|
|
67
112
|
const input = $ref<HTMLInputElement>();
|
|
68
113
|
|
|
69
114
|
const toggleEditAction = () => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
115
|
+
if (!props.toggleEdit) return;
|
|
116
|
+
if (editMode) {
|
|
117
|
+
editMode = false;
|
|
118
|
+
emit('debounce', inputVal);
|
|
119
|
+
emit('update:modelValue', inputVal as string);
|
|
120
|
+
} else {
|
|
121
|
+
editMode = true;
|
|
122
|
+
setTimeout(() => input?.focus(), 10);
|
|
123
|
+
}
|
|
79
124
|
};
|
|
80
125
|
|
|
81
126
|
const rows = $computed(() => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
127
|
+
if (props.lines) return props.lines;
|
|
128
|
+
if (props.autoheight) return `${inputVal}`?.split('\n').length || 1;
|
|
129
|
+
if (props.multiline || props.code) return 4;
|
|
130
|
+
return 1;
|
|
86
131
|
});
|
|
87
132
|
|
|
88
133
|
watch(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
134
|
+
() => inputVal,
|
|
135
|
+
(newVal) => {
|
|
136
|
+
if (props.toggleEdit) return;
|
|
137
|
+
if (newVal === props.modelValue) return;
|
|
138
|
+
emit('update:modelValue', newVal as string);
|
|
139
|
+
debounce(() => emit('debounce', newVal));
|
|
140
|
+
},
|
|
96
141
|
);
|
|
97
142
|
|
|
98
143
|
watch(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
144
|
+
() => props.modelValue,
|
|
145
|
+
(newVal) => {
|
|
146
|
+
if (newVal !== inputVal) inputVal = newVal;
|
|
147
|
+
},
|
|
148
|
+
{ immediate: true },
|
|
104
149
|
);
|
|
105
150
|
|
|
106
151
|
onMounted(() => {
|
|
107
|
-
|
|
152
|
+
if (props.autofocus) setTimeout(() => input?.focus(), 10);
|
|
108
153
|
});
|
|
109
154
|
</script>
|
|
110
155
|
|
|
111
156
|
<style>
|
|
112
157
|
.bagel-input.shrink,
|
|
113
158
|
.bagel-input.shrink input {
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
min-width: unset !important;
|
|
160
|
+
/* width: auto; */
|
|
116
161
|
}
|
|
162
|
+
|
|
117
163
|
.bagel-input label {
|
|
118
164
|
font-size: var(--label-font-size);
|
|
119
165
|
}
|
|
@@ -121,76 +167,76 @@ onMounted(() => {
|
|
|
121
167
|
|
|
122
168
|
<style scoped>
|
|
123
169
|
.bagel-input textarea {
|
|
124
|
-
|
|
125
|
-
|
|
170
|
+
min-height: unset;
|
|
171
|
+
font-size: var(--input-font-size);
|
|
126
172
|
}
|
|
127
173
|
|
|
128
174
|
.bagel-input.text-input textarea {
|
|
129
|
-
|
|
175
|
+
resize: none;
|
|
130
176
|
}
|
|
131
177
|
|
|
132
178
|
.code textarea {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
179
|
+
font-family: 'Inconsolata', monospace;
|
|
180
|
+
background: var(--bgl-black) !important;
|
|
181
|
+
color: var(--bgl-white) !important;
|
|
136
182
|
}
|
|
137
183
|
|
|
138
184
|
.bagel-input.toggleEdit:hover {
|
|
139
|
-
|
|
185
|
+
background-color: var(--input-bg);
|
|
140
186
|
}
|
|
141
187
|
|
|
142
188
|
.bagel-input.small {
|
|
143
|
-
|
|
144
|
-
|
|
189
|
+
margin-bottom: 0;
|
|
190
|
+
height: 30px;
|
|
145
191
|
}
|
|
146
192
|
|
|
147
193
|
.bagel-input.dense label {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 0.5rem;
|
|
151
197
|
}
|
|
152
198
|
|
|
153
199
|
.bagel-input label {
|
|
154
|
-
|
|
200
|
+
font-size: var(--label-font-size);
|
|
155
201
|
}
|
|
156
202
|
|
|
157
203
|
.toggleEditBtn {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
204
|
+
position: absolute;
|
|
205
|
+
right: -24px;
|
|
206
|
+
top: 4;
|
|
207
|
+
opacity: 0;
|
|
162
208
|
}
|
|
163
209
|
|
|
164
210
|
.textInputIconWrap {
|
|
165
|
-
|
|
211
|
+
position: relative;
|
|
166
212
|
}
|
|
167
213
|
|
|
168
214
|
.textInputIconWrap .bgl_icon-font {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
215
|
+
position: absolute;
|
|
216
|
+
inset-inline-end: 0.7rem;
|
|
217
|
+
bottom: 50%;
|
|
218
|
+
line-height: 0;
|
|
219
|
+
color: var(--bgl-gray);
|
|
174
220
|
}
|
|
175
221
|
|
|
176
222
|
.txtInputIconStart .iconStart {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
223
|
+
position: absolute;
|
|
224
|
+
inset-inline-start: 0.7rem;
|
|
225
|
+
top: 50%;
|
|
226
|
+
line-height: 0;
|
|
227
|
+
color: var(--bgl-gray);
|
|
182
228
|
}
|
|
183
229
|
|
|
184
230
|
.txtInputIconStart textarea {
|
|
185
|
-
|
|
231
|
+
padding-inline-start: 2rem;
|
|
186
232
|
}
|
|
187
233
|
|
|
188
234
|
.bagel-input:hover .toggleEditBtn,
|
|
189
235
|
.bagel-input.editMode .toggleEditBtn {
|
|
190
|
-
|
|
236
|
+
opacity: 1;
|
|
191
237
|
}
|
|
192
238
|
|
|
193
239
|
.bagel-input.small textarea {
|
|
194
|
-
|
|
240
|
+
height: 30px;
|
|
195
241
|
}
|
|
196
242
|
</style>
|
package/src/components/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ export { default as ModalForm } from './ModalForm.vue';
|
|
|
8
8
|
export { default as AccordionItem } from './AccordionItem.vue';
|
|
9
9
|
export { default as ListView } from './ListView.vue';
|
|
10
10
|
export { default as ListItem } from './ListItem.vue';
|
|
11
|
-
export { default as TabbedLayout } from './TabbedLayout.vue';
|
|
12
11
|
export { default as Comments } from './Comments.vue';
|
|
13
12
|
export { default as PageTitle } from './PageTitle.vue';
|
|
14
13
|
export { default as TableSchema } from './TableSchema.vue';
|
|
@@ -19,7 +18,6 @@ export { default as Card } from './Card.vue';
|
|
|
19
18
|
export { default as Avatar } from './Avatar.vue';
|
|
20
19
|
export { default as Title } from './Title.vue';
|
|
21
20
|
export { default as Accordion } from './Accordion.vue';
|
|
22
|
-
export { default as ComboBox } from './ComboBox.vue';
|
|
23
21
|
export { default as Alert } from './Alert.vue';
|
|
24
22
|
export { default as Badge } from './Badge.vue';
|
|
25
23
|
export { default as BglVideo } from './BglVideo.vue';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<div class="layout" :class="{ 'layout-h-100': h100 }">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script lang="ts" setup>
|
|
@@ -9,27 +9,48 @@ type LayoutProrps = {
|
|
|
9
9
|
gap?: number,
|
|
10
10
|
h100?: boolean,
|
|
11
11
|
columns?: string[]
|
|
12
|
+
mColumns?: string[],
|
|
13
|
+
mRows?: string[]
|
|
12
14
|
rows?: string[],
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
const props = withDefaults(defineProps<LayoutProrps>(), {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
gap: 1,
|
|
19
|
+
h100: false,
|
|
20
|
+
columns: () => ([]),
|
|
21
|
+
rows: () => ([]),
|
|
22
|
+
mColumns: () => ([]),
|
|
23
|
+
mRows: () => ([]),
|
|
20
24
|
});
|
|
21
25
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const gridTemplateRows = $computed(() => (props.rows.length ? props.rows.join(' ') : 'auto'));
|
|
27
|
+
const gapSize = $computed(() => `${props.gap}rem`);
|
|
28
|
+
const mGridTemplateRows = $computed(() => {
|
|
29
|
+
if (props.mRows?.length) return props.mRows.join(' ');
|
|
30
|
+
return gridTemplateRows;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const gridTemplateColumns = $computed(() => (props.columns.length ? props.columns.join(' ') : 'auto'));
|
|
34
|
+
|
|
35
|
+
const mGridTemplateColumns = $computed(() => {
|
|
36
|
+
if (props.mColumns?.length) return props.mColumns.join(' ');
|
|
37
|
+
return gridTemplateColumns;
|
|
27
38
|
});
|
|
28
39
|
</script>
|
|
29
40
|
|
|
30
|
-
<style>
|
|
41
|
+
<style scoped>
|
|
31
42
|
.layout {
|
|
32
43
|
min-height: 100px;
|
|
33
44
|
display: grid;
|
|
45
|
+
gap: v-bind(gapSize);
|
|
46
|
+
grid-template-rows: v-bind(gridTemplateRows);
|
|
47
|
+
grid-template-columns: v-bind(gridTemplateColumns);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@media screen and (max-width: 910px) {
|
|
51
|
+
.layout {
|
|
52
|
+
grid-template-rows: v-bind(mGridTemplateRows);
|
|
53
|
+
grid-template-columns: v-bind(mGridTemplateColumns);
|
|
54
|
+
}
|
|
34
55
|
}
|
|
35
56
|
</style>
|
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</Card>
|
|
2
|
+
<Card class="flex column gap-05 pt-1 bgl_sidebar" :class="{ 'wideNav': isOpen }">
|
|
3
|
+
<div class="w-100 px-075">
|
|
4
|
+
<Btn thin color="light" class="toggleNav mb-05" icon="keyboard_arrow_right" @click="toggleMenu" />
|
|
5
|
+
</div>
|
|
6
|
+
<slot v-if="!isOpen || !slots['brand-open']" name="brand" />
|
|
7
|
+
<slot v-if="isOpen" name="brand-open" />
|
|
8
|
+
<slot v-if="!navLinks" />
|
|
9
|
+
<Btn v-for="(nav, i) in navLinks" :key="i" :to="nav.to" class="nav-button px-075"
|
|
10
|
+
v-tooltip.right="{ content: nav.label, disabled: open, class: ['nav-tooltip'] }">
|
|
11
|
+
<template #default>
|
|
12
|
+
<Icon :icon="nav.icon" :size="1.4" />
|
|
13
|
+
<p v-if="open">
|
|
14
|
+
{{ nav.label }}
|
|
15
|
+
</p>
|
|
16
|
+
</template>
|
|
17
|
+
</Btn>
|
|
18
|
+
<slot name="footer" />
|
|
19
|
+
</Card>
|
|
21
20
|
</template>
|
|
22
21
|
|
|
23
22
|
<script lang="ts" setup>
|
|
24
23
|
import { useSlots } from 'vue';
|
|
25
|
-
import { Btn, Icon } from '@bagelink/vue';
|
|
24
|
+
import { Btn, Icon, Card } from '@bagelink/vue';
|
|
26
25
|
import type { NavLink } from '@bagelink/vue';
|
|
27
26
|
|
|
28
27
|
const slots = useSlots();
|
|
@@ -33,8 +32,8 @@ let isOpen = $ref(props.open);
|
|
|
33
32
|
|
|
34
33
|
const emit = defineEmits(['update:open']);
|
|
35
34
|
function toggleMenu() {
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
isOpen = !isOpen;
|
|
36
|
+
emit('update:open', isOpen);
|
|
38
37
|
}
|
|
39
38
|
</script>
|
|
40
39
|
|
|
@@ -50,6 +49,7 @@ function toggleMenu() {
|
|
|
50
49
|
.wideNav .nav-button .bgl_btn-flex {
|
|
51
50
|
justify-content: flex-start !important;
|
|
52
51
|
}
|
|
52
|
+
|
|
53
53
|
.nav-button.router-link-active {
|
|
54
54
|
background-color: var(--bgl-primary-tint) !important;
|
|
55
55
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="h-100 grid list-view gap-1" :class="{ 'side-tabs': sideTabs }">
|
|
3
|
+
<div class="bgl_card tabs-top">
|
|
4
|
+
<slot name="top-section" />
|
|
5
|
+
<div class="tabs grid auto-flow-columns fit-content">
|
|
6
|
+
<div v-for="tab in tabs" :class="{
|
|
7
|
+
active:
|
|
8
|
+
tab === activeTab ||
|
|
9
|
+
tab === router?.currentRoute.value.path.split('/').slice(-1)[0],
|
|
10
|
+
}" class="tab" :key="tab" @click="changeTab(tab)">
|
|
11
|
+
{{ tab }}
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
<div class="list-content" v-for="tab in tabs.filter((item) => item === activeTab)" :key="tab">
|
|
16
|
+
<slot :name="tab" :key="tab" />
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script setup lang="ts">
|
|
22
|
+
import { onMounted } from 'vue';
|
|
23
|
+
import type { Router } from 'vue-router';
|
|
24
|
+
|
|
25
|
+
const emit = defineEmits(['update:modelValue']);
|
|
26
|
+
|
|
27
|
+
let activeTab = $ref<string>();
|
|
28
|
+
const props = defineProps<{
|
|
29
|
+
title?: string;
|
|
30
|
+
tabs: string[];
|
|
31
|
+
modelValue?: string;
|
|
32
|
+
router?: Router;
|
|
33
|
+
sideTabs?: boolean;
|
|
34
|
+
}>();
|
|
35
|
+
|
|
36
|
+
function changeTab(tab: string) {
|
|
37
|
+
activeTab = tab;
|
|
38
|
+
emit('update:modelValue', activeTab);
|
|
39
|
+
if (!props.router) return;
|
|
40
|
+
void props.router?.push({
|
|
41
|
+
path: props.router.currentRoute.value.path,
|
|
42
|
+
query: { ...props.router.currentRoute.value.query, t: tab },
|
|
43
|
+
hash: props.router.currentRoute.value.hash,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
onMounted(() => {
|
|
48
|
+
const firstTab =
|
|
49
|
+
props.modelValue ||
|
|
50
|
+
props.router?.currentRoute.value.query.t ||
|
|
51
|
+
props.tabs[0];
|
|
52
|
+
activeTab = firstTab as string;
|
|
53
|
+
});
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<style scoped>
|
|
57
|
+
.tab {
|
|
58
|
+
text-transform: capitalize;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.side-tabs {
|
|
62
|
+
display: flex;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.side-tabs .tabs-top {
|
|
66
|
+
margin-inline-end: 1rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.side-tabs .tabs {
|
|
70
|
+
display: block;
|
|
71
|
+
padding: 0;
|
|
72
|
+
margin: 0;
|
|
73
|
+
border: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.side-tabs .tab {
|
|
77
|
+
border: none;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TabsNav :tabs="tabs" :group="group" />
|
|
3
|
+
<div v-if="currentTab">
|
|
4
|
+
<slot :name="currentTab" />
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { TabsNav, type Tab } from '@bagelink/vue';
|
|
10
|
+
import { useTabs } from './tabsManager';
|
|
11
|
+
|
|
12
|
+
defineProps<{
|
|
13
|
+
tabs: Tab[];
|
|
14
|
+
modelValue?: string;
|
|
15
|
+
}>();
|
|
16
|
+
|
|
17
|
+
const group = Math.random().toString(36).substring(7);
|
|
18
|
+
const { currentTab } = useTabs(group);
|
|
19
|
+
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="currentTab">
|
|
3
|
+
<slot :name="currentTab"></slot>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { useTabs } from './tabsManager';
|
|
9
|
+
|
|
10
|
+
const props = defineProps<{
|
|
11
|
+
group: string;
|
|
12
|
+
}>();
|
|
13
|
+
|
|
14
|
+
const { currentTab } = useTabs(props.group);
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tabs grid auto-flow-columns fit-content">
|
|
3
|
+
<button :class="{ active: isActive(tab) }" @click="selectTab(tab)" class="tab" v-for="(tab, i) in tabs" :key="i">
|
|
4
|
+
<Icon v-if="typeof tab !== 'string' && tab.icon" :icon="tab.icon" />
|
|
5
|
+
{{ tabLabel(tab) }}
|
|
6
|
+
</button>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { type Tab, Icon } from '@bagelink/vue';
|
|
12
|
+
import { useTabs } from './tabsManager';
|
|
13
|
+
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
title?: string;
|
|
16
|
+
tabs: Tab[];
|
|
17
|
+
modelValue?: string;
|
|
18
|
+
sideTabs?: boolean;
|
|
19
|
+
group: string;
|
|
20
|
+
}>();
|
|
21
|
+
|
|
22
|
+
const { currentTab } = useTabs(props.group);
|
|
23
|
+
currentTab.value = props.modelValue || typeof props.tabs[0] === 'string' ? props.tabs[0] : props.tabs[0].id;
|
|
24
|
+
const selectTab = (tab: Tab) => {
|
|
25
|
+
currentTab.value = typeof tab === 'string' ? tab : tab.id;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const isActive = (tab: Tab) => {
|
|
29
|
+
if (typeof tab === 'string') return currentTab.value === tab;
|
|
30
|
+
return currentTab.value === tab.id;
|
|
31
|
+
};
|
|
32
|
+
const tabLabel = (tab: Tab) => {
|
|
33
|
+
if (typeof tab === 'string') return tab;
|
|
34
|
+
return tab.label;
|
|
35
|
+
};
|
|
36
|
+
</script>
|
|
37
|
+
|
|
38
|
+
<style scoped>
|
|
39
|
+
.tab {
|
|
40
|
+
border: none;
|
|
41
|
+
border-bottom: 1px solid var(--border-color);
|
|
42
|
+
background: transparent;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.tab.currentTab {
|
|
46
|
+
border-bottom: 2px solid var(--primary-color);
|
|
47
|
+
}
|
|
48
|
+
</style>
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export { default as Layout } from './Layout.vue';
|
|
2
2
|
export { default as SidebarMenu } from './SidebarMenu.vue';
|
|
3
|
+
export { default as Tabs } from './Tabs.vue';
|
|
4
|
+
export { default as TabsNav } from './TabsNav.vue';
|
|
5
|
+
export { default as TabsBody } from './TabsBody.vue';
|
|
6
|
+
export { default as TabbedLayout } from './TabbedLayout.vue';
|