@bagelink/vue 1.4.109 → 1.4.111
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/ListItem.vue.d.ts +6 -1
- package/dist/components/ListItem.vue.d.ts.map +1 -1
- package/dist/components/analytics/BarChart.vue.d.ts +39 -0
- package/dist/components/analytics/BarChart.vue.d.ts.map +1 -0
- package/dist/components/analytics/KpiCard.vue.d.ts +24 -0
- package/dist/components/analytics/KpiCard.vue.d.ts.map +1 -0
- package/dist/components/analytics/LineChart.vue.d.ts +26 -0
- package/dist/components/analytics/LineChart.vue.d.ts.map +1 -0
- package/dist/components/analytics/PieChart.vue.d.ts +24 -0
- package/dist/components/analytics/PieChart.vue.d.ts.map +1 -0
- package/dist/components/analytics/index.d.ts +5 -0
- package/dist/components/analytics/index.d.ts.map +1 -0
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/DatePicker.vue.d.ts +1 -0
- package/dist/components/form/inputs/DatePicker.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RadioGroup.vue.d.ts +6 -10
- package/dist/components/form/inputs/RadioGroup.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts +2 -2
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/layout/AppContent.vue.d.ts +34 -0
- package/dist/components/layout/AppContent.vue.d.ts.map +1 -0
- package/dist/components/layout/AppLayout.vue.d.ts +27 -0
- package/dist/components/layout/AppLayout.vue.d.ts.map +1 -0
- package/dist/components/layout/AppSidebar.vue.d.ts +44 -0
- package/dist/components/layout/AppSidebar.vue.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/composables/useFormField.d.ts.map +1 -1
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/index.cjs +19 -19
- package/dist/index.mjs +10 -10
- package/dist/style.css +1 -1
- package/dist/types/BagelForm.d.ts +2 -2
- package/dist/types/BagelForm.d.ts.map +1 -1
- package/dist/utils/BagelFormUtils.d.ts +1 -2
- package/dist/utils/BagelFormUtils.d.ts.map +1 -1
- package/dist/utils/calendar/dateUtils.d.ts +21 -0
- package/dist/utils/calendar/dateUtils.d.ts.map +1 -1
- package/dist/utils/elementUtils.d.ts +5 -0
- package/dist/utils/elementUtils.d.ts.map +1 -1
- package/dist/utils/useSearch.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ListItem.vue +32 -24
- package/src/components/analytics/BarChart.vue +153 -0
- package/src/components/analytics/KpiCard.vue +84 -0
- package/src/components/analytics/LineChart.vue +267 -0
- package/src/components/analytics/PieChart.vue +196 -0
- package/src/components/analytics/index.ts +4 -0
- package/src/components/form/BagelForm.vue +24 -0
- package/src/components/form/inputs/DatePicker.vue +3 -2
- package/src/components/form/inputs/RadioGroup.vue +60 -35
- package/src/components/form/inputs/SelectInput.vue +94 -101
- package/src/components/form/inputs/Upload/upload.css +135 -138
- package/src/components/layout/AppContent.vue +105 -0
- package/src/components/layout/AppLayout.vue +124 -0
- package/src/components/layout/AppSidebar.vue +271 -0
- package/src/components/layout/index.ts +5 -0
- package/src/composables/useFormField.ts +6 -0
- package/src/composables/useSchemaField.ts +31 -3
- package/src/styles/inputs.css +9 -0
- package/src/styles/theme.css +2 -2
- package/src/types/BagelForm.ts +3 -2
- package/src/utils/BagelFormUtils.ts +1 -3
- package/src/utils/calendar/dateUtils.ts +71 -17
- package/src/utils/elementUtils.ts +22 -0
- package/src/utils/useSearch.ts +14 -7
|
@@ -1,235 +1,232 @@
|
|
|
1
1
|
.fileUploadWrap {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
outline: 1px solid var(--border-color);
|
|
3
|
+
border-radius: var(--input-border-radius);
|
|
4
|
+
text-align: center;
|
|
5
|
+
cursor: pointer;
|
|
6
|
+
transition: var(--bgl-transition);
|
|
7
|
+
position: relative;
|
|
8
|
+
font-size: var(--input-font-size);
|
|
9
|
+
overflow-y: auto;
|
|
10
|
+
background: var(--input-bg);
|
|
11
|
+
height: 215px;
|
|
12
|
+
outline-offset: -1px;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
.bagel-input .fileUploadWrap.fileDropZone {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
background: var(--input-bg);
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
color: var(--bgl-gray);
|
|
21
|
+
flex-direction: column;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
.fileUploadWrap.dragover,
|
|
24
25
|
.fileUploadWrap:hover {
|
|
25
|
-
|
|
26
|
+
box-shadow: inset 0 0 10px #00000012;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
.fileUploadWrap[style*='height: auto;'] {
|
|
29
|
-
|
|
30
|
+
min-height: 215px;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
.multi-image-item-preview {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
border: 1px solid var(--border-color) !important;
|
|
35
|
+
border-radius: var(--input-border-radius);
|
|
36
|
+
margin: 0.5rem !important;
|
|
37
|
+
background: var(--bgl-popup-bg);
|
|
38
|
+
padding: 0;
|
|
39
|
+
padding-inline-end: 1rem;
|
|
40
|
+
padding-inline-start: 0.25rem;
|
|
41
|
+
text-align: start;
|
|
42
|
+
color: var(--input-color);
|
|
43
|
+
display: grid;
|
|
44
|
+
grid-template-columns: auto 1fr 22px;
|
|
45
|
+
gap: 0.5rem;
|
|
46
|
+
align-items: center;
|
|
47
|
+
height: var(--btn-height);
|
|
48
|
+
font-size: var(--label-font-size);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
.multi-image-item-preview p {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
text-overflow: ellipsis;
|
|
54
|
+
white-space: nowrap;
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
.multi-preview {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
/* width: 40px; */
|
|
59
|
+
height: calc(var(--btn-height) - 0.5rem);
|
|
60
|
+
max-width: calc(var(--btn-height) - 0.5rem);
|
|
61
|
+
border-radius: calc(var(--input-border-radius) / 2);
|
|
62
|
+
object-fit: cover;
|
|
63
|
+
background: var(--bgl-gray-light);
|
|
64
|
+
text-align: center !important;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
align-items: center;
|
|
67
|
+
display: flex;
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
.bgl-single-preview {
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
height: 100%;
|
|
72
|
+
position: relative;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
.bgl-single-preview
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
.bgl-single-preview+.fileUploadPlaceHolder {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
margin: auto;
|
|
79
|
+
top: calc(50% - 2rem);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
.single-image-item-preview {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
height: 100%;
|
|
84
|
+
min-height: 100%;
|
|
85
|
+
position: relative;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
.fileUploadWrap[style*='height: auto'] .single-image-item-preview {
|
|
91
|
-
|
|
92
|
+
min-height: 215px;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
.fileUploadWrap[style*='height: auto'] .single-preview {
|
|
95
|
-
|
|
96
|
+
margin: 0rem !important;
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
.single-preview {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
margin: 1rem;
|
|
101
|
+
padding: 0px;
|
|
102
|
+
height: calc(100% - 2rem);
|
|
103
|
+
max-height: calc(100% - 2rem);
|
|
104
|
+
background: var(--bgl-gray-light);
|
|
105
|
+
max-width: calc(100% - 2rem);
|
|
106
|
+
object-fit: contain;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
.single-image-item-preview:hover::after {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
content: 'zoom_in';
|
|
111
|
+
font-size: 32px;
|
|
112
|
+
font-family: 'Material Symbols Outlined', serif;
|
|
113
|
+
position: absolute;
|
|
114
|
+
border-radius: 100%;
|
|
115
|
+
color: var(--bgl-white);
|
|
116
|
+
z-index: 9;
|
|
117
|
+
pointer-events: none;
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
.single-image-item-preview:hover img {
|
|
120
|
-
|
|
121
|
+
filter: brightness(70%);
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
.bgl_fill-image.single-image-item-preview {
|
|
124
|
-
|
|
125
|
+
height: 100%;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
.bgl_fill-image.single-image-item-preview .single-preview {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
129
|
+
border-radius: unset;
|
|
130
|
+
object-fit: cover;
|
|
131
|
+
box-shadow: unset;
|
|
132
|
+
width: 100%;
|
|
133
|
+
height: auto;
|
|
133
134
|
}
|
|
134
135
|
|
|
135
136
|
.single-image-item-preview .pie {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
transform-origin: top;
|
|
138
|
+
transform: scale(1.4);
|
|
139
|
+
position: absolute;
|
|
140
|
+
text-align: center;
|
|
141
|
+
inset: 0;
|
|
142
|
+
margin: auto;
|
|
143
|
+
display: flex;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
align-items: center;
|
|
146
|
+
background: transparent;
|
|
147
|
+
border: none !important;
|
|
148
|
+
padding: 0 !important;
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
.bgl_oval-upload {
|
|
151
|
-
|
|
152
|
-
|
|
152
|
+
border-radius: 100% !important;
|
|
153
|
+
overflow: hidden;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
.bgl_oval-upload p {
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
padding: 0.75rem !important;
|
|
158
|
+
font-size: 12px;
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
.bgl_oval-upload .fileUploadPlaceHolder {
|
|
161
|
-
|
|
162
|
+
top: 0;
|
|
162
163
|
}
|
|
163
164
|
|
|
164
165
|
.bgl_oval-upload .pie {
|
|
165
|
-
|
|
166
|
+
transform: scale(1);
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
.bgl_oval-upload span.bgl_icon-font.color-primary {
|
|
169
|
-
|
|
170
|
+
transform: scale(0.4) !important;
|
|
170
171
|
}
|
|
171
172
|
|
|
172
173
|
.bgl_oval-upload .single-image-item-preview {
|
|
173
|
-
|
|
174
|
+
height: 100%;
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
.bgl_oval-upload .single-preview {
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
margin: 0;
|
|
179
|
+
height: 100% !important;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
.pie {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
width: 30px;
|
|
184
|
+
height: 30px;
|
|
185
|
+
position: relative;
|
|
186
|
+
display: flex;
|
|
187
|
+
align-items: center;
|
|
188
|
+
justify-content: center;
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
.pie:before {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
farthest-side,
|
|
204
|
-
#0000 calc(99% - var(--b)),
|
|
205
|
-
#000 calc(100% - var(--b))
|
|
206
|
-
);
|
|
192
|
+
content: '';
|
|
193
|
+
position: absolute;
|
|
194
|
+
border-radius: 50%;
|
|
195
|
+
inset: 0;
|
|
196
|
+
transition: all 0.2s ease-in-out;
|
|
197
|
+
background: conic-gradient(var(--bgl-primary) calc(var(--p) * 1%), #0000 0);
|
|
198
|
+
-webkit-mask: radial-gradient(farthest-side,
|
|
199
|
+
#0000 calc(99% - var(--b)),
|
|
200
|
+
#000 calc(100% - var(--b)));
|
|
201
|
+
mask: radial-gradient(farthest-side,
|
|
202
|
+
#0000 calc(99% - var(--b)),
|
|
203
|
+
#000 calc(100% - var(--b)));
|
|
207
204
|
}
|
|
208
205
|
|
|
209
206
|
.pie .success {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
207
|
+
transform: scale(0);
|
|
208
|
+
opacity: 0;
|
|
209
|
+
transition: all 0.3s ease-in-out;
|
|
213
210
|
}
|
|
214
211
|
|
|
215
212
|
.pie .progress {
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
position: absolute;
|
|
214
|
+
font-size: 10px;
|
|
218
215
|
}
|
|
219
216
|
|
|
220
217
|
.pie.complete .progress {
|
|
221
|
-
|
|
218
|
+
display: none;
|
|
222
219
|
}
|
|
223
220
|
|
|
224
221
|
.pie.complete .success {
|
|
225
|
-
|
|
226
|
-
|
|
222
|
+
transform: scale(1.3);
|
|
223
|
+
opacity: 1;
|
|
227
224
|
}
|
|
228
225
|
|
|
229
226
|
.pie.complete:before {
|
|
230
|
-
|
|
227
|
+
background: conic-gradient(var(--bgl-green) calc(var(--p) * 1%), #0000 0);
|
|
231
228
|
}
|
|
232
229
|
|
|
233
230
|
.pie.complete {
|
|
234
|
-
|
|
235
|
-
}
|
|
231
|
+
color: var(--bgl-green);
|
|
232
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
import { Btn, PageTitle } from '@bagelink/vue'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
title?: string
|
|
7
|
+
showMenuButton?: boolean
|
|
8
|
+
showBackButton?: boolean
|
|
9
|
+
backTo?: string
|
|
10
|
+
border?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
withDefaults(defineProps<Props>(), {
|
|
14
|
+
showMenuButton: true,
|
|
15
|
+
showBackButton: false,
|
|
16
|
+
backTo: '/',
|
|
17
|
+
border: true,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
// Inject menu state from parent
|
|
21
|
+
const menuState = inject('menuState') as {
|
|
22
|
+
isOpen: { value: boolean }
|
|
23
|
+
isMobile: { value: boolean }
|
|
24
|
+
toggleMenu: () => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Inject sidebar card style state
|
|
28
|
+
const sidebarCardStyle = inject('sidebarCardStyle', { value: true })
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<template>
|
|
32
|
+
<div class="app-content h-100p flex column" :class="{
|
|
33
|
+
'paddingAppContent': sidebarCardStyle?.value,
|
|
34
|
+
}">
|
|
35
|
+
<!-- Header -->
|
|
36
|
+
<header class="app-header flex align-items-center space-between py-1 min-h60px w-100p m_flex-wrap" :class="{
|
|
37
|
+
'border-bottom': border,
|
|
38
|
+
'px-1': !sidebarCardStyle?.value,
|
|
39
|
+
}">
|
|
40
|
+
<!-- Left Side -->
|
|
41
|
+
<div class="flex align-items-center gap-col-075 m_flex-wrap">
|
|
42
|
+
<!-- Menu Toggle Button -->
|
|
43
|
+
<Btn v-if="showMenuButton" flat icon="dock_to_right" @click="menuState.toggleMenu" />
|
|
44
|
+
|
|
45
|
+
<!-- Back Button -->
|
|
46
|
+
<Btn v-if="showBackButton" flat icon="arrow_back" :to="backTo" class="back-btn" />
|
|
47
|
+
|
|
48
|
+
<!-- Page Title -->
|
|
49
|
+
<PageTitle v-if="title">
|
|
50
|
+
{{ title }}
|
|
51
|
+
</PageTitle>
|
|
52
|
+
|
|
53
|
+
<!-- Custom Left Content -->
|
|
54
|
+
<slot name="header-left" />
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<!-- Center Content -->
|
|
58
|
+
<div class="flex align-items-center">
|
|
59
|
+
<slot name="header-center" />
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<!-- Right Side -->
|
|
63
|
+
<div class="flex align-items-center gap-row-05 m_flex-grow-1">
|
|
64
|
+
<slot name="header-right" />
|
|
65
|
+
</div>
|
|
66
|
+
</header>
|
|
67
|
+
|
|
68
|
+
<!-- Page Content -->
|
|
69
|
+
<main class="flex-grow overflow py-1 w-100p m_p-05 m_scrollbar-gutter-stable-both m_vw100" :class="{
|
|
70
|
+
'px-1': !sidebarCardStyle?.value,
|
|
71
|
+
}">
|
|
72
|
+
<slot name="content">
|
|
73
|
+
<!-- Default slot for content without explicit template -->
|
|
74
|
+
<slot />
|
|
75
|
+
</slot>
|
|
76
|
+
</main>
|
|
77
|
+
</div>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<style scoped>
|
|
81
|
+
.paddingAppContent {
|
|
82
|
+
padding-inline-start: 0.5rem;
|
|
83
|
+
padding-inline-end: 1rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
body:has(.sidebar-collapsed) .paddingAppContent {
|
|
87
|
+
padding-inline-start: 1.5rem;
|
|
88
|
+
}
|
|
89
|
+
.app-content {
|
|
90
|
+
height: 100vh;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.app-header {
|
|
94
|
+
flex-shrink: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
main {
|
|
98
|
+
min-height: 0; /* חשוב לאפשר overflow נכון */
|
|
99
|
+
}
|
|
100
|
+
@media screen and (max-width: 910px) {
|
|
101
|
+
.paddingAppContent {
|
|
102
|
+
padding-inline: 0.5rem;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ref, provide, onMounted, onUnmounted, computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
sidebarWidth?: string
|
|
6
|
+
sidebarCardStyle?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
10
|
+
sidebarWidth: '260px',
|
|
11
|
+
sidebarCardStyle: false
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
// Menu state
|
|
15
|
+
const isOpen = ref(true)
|
|
16
|
+
const isMobile = ref(false)
|
|
17
|
+
|
|
18
|
+
// Check if mobile
|
|
19
|
+
function checkMobile() {
|
|
20
|
+
isMobile.value = window.innerWidth < 768
|
|
21
|
+
if (isMobile.value) {
|
|
22
|
+
isOpen.value = false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Toggle menu
|
|
27
|
+
function toggleMenu() {
|
|
28
|
+
isOpen.value = !isOpen.value
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Close menu on mobile when clicking outside
|
|
32
|
+
function closeOnMobile() {
|
|
33
|
+
if (isMobile.value) {
|
|
34
|
+
isOpen.value = false
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Computed styles for main content margin
|
|
39
|
+
const mainContentStyles = computed(() => {
|
|
40
|
+
if (isMobile.value) {
|
|
41
|
+
return { marginLeft: '0' }
|
|
42
|
+
}
|
|
43
|
+
const collapsedWidth = props.sidebarCardStyle ? '82px' : '66px'
|
|
44
|
+
return {
|
|
45
|
+
marginLeft: isOpen.value ? props.sidebarWidth : collapsedWidth
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
// Provide state to child components
|
|
50
|
+
provide('menuState', {
|
|
51
|
+
isOpen,
|
|
52
|
+
isMobile,
|
|
53
|
+
toggleMenu,
|
|
54
|
+
closeOnMobile,
|
|
55
|
+
sidebarWidth: props.sidebarWidth,
|
|
56
|
+
sidebarCollapsedWidth: props.sidebarCardStyle ? '82px' : '66px'
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Initialize
|
|
60
|
+
onMounted(() => {
|
|
61
|
+
checkMobile()
|
|
62
|
+
window.addEventListener('resize', checkMobile)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
onUnmounted(() => {
|
|
66
|
+
window.removeEventListener('resize', checkMobile)
|
|
67
|
+
})
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<template>
|
|
71
|
+
<div class="app-layout vh-100 relative">
|
|
72
|
+
<!-- Mobile Overlay -->
|
|
73
|
+
<div
|
|
74
|
+
v-if="isMobile && isOpen"
|
|
75
|
+
class="overlay"
|
|
76
|
+
@click="closeOnMobile"
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<!-- Sidebar Slot -->
|
|
80
|
+
<slot name="sidebar" />
|
|
81
|
+
|
|
82
|
+
<!-- Main Content Area -->
|
|
83
|
+
<main
|
|
84
|
+
class="main-content flex column ease-300"
|
|
85
|
+
:style="mainContentStyles"
|
|
86
|
+
>
|
|
87
|
+
<!-- Header Slot -->
|
|
88
|
+
<slot name="header" />
|
|
89
|
+
|
|
90
|
+
<!-- Page Content -->
|
|
91
|
+
<div class="page-content overflow w-100p h-100p">
|
|
92
|
+
<slot />
|
|
93
|
+
</div>
|
|
94
|
+
</main>
|
|
95
|
+
</div>
|
|
96
|
+
</template>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.app-layout {
|
|
100
|
+
display: flex;
|
|
101
|
+
height: 100vh;
|
|
102
|
+
overflow: hidden;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Overlay for mobile */
|
|
106
|
+
.overlay {
|
|
107
|
+
position: fixed;
|
|
108
|
+
inset: 0;
|
|
109
|
+
background: rgba(0, 0, 0, 0.5);
|
|
110
|
+
z-index: 40;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Main Content */
|
|
114
|
+
.main-content {
|
|
115
|
+
flex: 1;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
transition: margin-left 400ms ease;
|
|
118
|
+
min-height: 100vh
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.page-content {
|
|
122
|
+
overflow: auto;
|
|
123
|
+
}
|
|
124
|
+
</style>
|