ketekny-ui-kit 1.0.76 → 1.0.77
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/package.json +1 -1
- package/src/layout/twoColLayout.vue +175 -344
- package/src/layout/twoColSidebarPanel.vue +329 -0
- package/src/style/style.css +6 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class="relative flex min-h-screen flex-col bg-[#edf4fb] lg:h-screen lg:flex-row lg:overflow-hidden"
|
|
4
4
|
>
|
|
5
5
|
<button
|
|
6
|
-
v-show="isSidebarCollapsed"
|
|
6
|
+
v-show="isDesktopViewport && isSidebarCollapsed"
|
|
7
7
|
type="button"
|
|
8
8
|
:aria-label="expandLabel"
|
|
9
9
|
v-tooltip.right="expandLabel"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
</button>
|
|
17
17
|
|
|
18
18
|
<button
|
|
19
|
-
v-show="!isSidebarCollapsed"
|
|
19
|
+
v-show="isDesktopViewport && !isSidebarCollapsed"
|
|
20
20
|
type="button"
|
|
21
21
|
:aria-label="collapseLabel"
|
|
22
22
|
v-tooltip.left="collapseLabel"
|
|
@@ -29,228 +29,91 @@
|
|
|
29
29
|
</button>
|
|
30
30
|
|
|
31
31
|
<aside
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
? 'lg:relative lg:z-30 lg:w-[76px]'
|
|
36
|
-
: 'lg:relative lg:z-30 lg:translate-x-0'
|
|
37
|
-
"
|
|
32
|
+
v-if="isDesktopViewport"
|
|
33
|
+
class="hidden lg:flex lg:shrink-0 transition-all duration-300 ease-out"
|
|
34
|
+
:class="isSidebarCollapsed ? 'lg:w-[76px]' : 'lg:w-[360px]'"
|
|
38
35
|
>
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<slot name="sidebar-header" />
|
|
63
|
-
</div>
|
|
36
|
+
<SidebarPanel
|
|
37
|
+
:title="title"
|
|
38
|
+
:collapsed-logo-src="collapsedLogoSrc"
|
|
39
|
+
:logo-src="logoSrc"
|
|
40
|
+
:logo-alt="logoAlt"
|
|
41
|
+
:show-sponsors="showSponsors"
|
|
42
|
+
:menu-items="normalizedMainMenu"
|
|
43
|
+
:show-contact-info="showContactInfo"
|
|
44
|
+
:account="account"
|
|
45
|
+
:contact-email="contactEmail"
|
|
46
|
+
:collapsed="isSidebarCollapsed"
|
|
47
|
+
@logo-click="handleLogoClick"
|
|
48
|
+
@signin="handleSignin"
|
|
49
|
+
@signup="handleSignup"
|
|
50
|
+
@signout="handleSignout"
|
|
51
|
+
@edit-profile="handleEditProfile"
|
|
52
|
+
>
|
|
53
|
+
<template #sidebar-header>
|
|
54
|
+
<slot name="sidebar-header" />
|
|
55
|
+
</template>
|
|
56
|
+
</SidebarPanel>
|
|
57
|
+
</aside>
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
:active="item.active"
|
|
75
|
-
v-bind="item.bindings"
|
|
76
|
-
v-tooltip.right="item.label"
|
|
77
|
-
/>
|
|
78
|
-
</div>
|
|
79
|
-
<div v-else-if="mainMenu.length" class="mx-2 mt-3 space-y-3">
|
|
80
|
-
<RouterLink
|
|
81
|
-
v-for="item in normalizedMainMenu"
|
|
82
|
-
:key="item.key"
|
|
83
|
-
v-bind="item.bindings"
|
|
84
|
-
class="flex w-full items-start gap-3 rounded-2xl border px-4 py-4 text-left transition-all duration-200"
|
|
85
|
-
:class="item.classes"
|
|
86
|
-
>
|
|
87
|
-
<span
|
|
88
|
-
class="mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-white/10 text-cyan-100 transition-colors duration-200"
|
|
89
|
-
:class="item.active ? 'bg-slate-950 text-white' : ''"
|
|
90
|
-
>
|
|
91
|
-
<kIcon :name="item.icon" />
|
|
92
|
-
</span>
|
|
93
|
-
<span class="min-w-0">
|
|
94
|
-
<span class="block font-semibold leading-tight">
|
|
95
|
-
{{ item.label }}
|
|
96
|
-
</span>
|
|
97
|
-
<span
|
|
98
|
-
v-if="item.description"
|
|
99
|
-
class="mt-1 block text-sm"
|
|
100
|
-
:class="item.active ? 'text-slate-700' : 'text-slate-300'"
|
|
101
|
-
>
|
|
102
|
-
{{ item.description }}
|
|
103
|
-
</span>
|
|
104
|
-
</span>
|
|
105
|
-
</RouterLink>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
59
|
+
<button
|
|
60
|
+
v-if="!isDesktopViewport && !mobileMenuOpen"
|
|
61
|
+
type="button"
|
|
62
|
+
class="fixed bottom-4 left-4 z-[80] flex h-14 w-14 items-center justify-center rounded-full bg-[#121b2e] text-white shadow-2xl shadow-black/40 transition-transform duration-200 hover:scale-105 hover:bg-[#1a2640] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70"
|
|
63
|
+
aria-label="Open menu"
|
|
64
|
+
@click="mobileMenuOpen = true"
|
|
65
|
+
>
|
|
66
|
+
<kIcon name="Menu" size="22" class="text-white" />
|
|
67
|
+
</button>
|
|
108
68
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
:title="'Επεξεργασία προφίλ'"
|
|
119
|
-
v-tooltip.right="'Επεξεργασία προφίλ'"
|
|
120
|
-
/>
|
|
121
|
-
<SidebarIconLink
|
|
122
|
-
icon="LogOut"
|
|
123
|
-
warning
|
|
124
|
-
:on-click="() => $emit('signout')"
|
|
125
|
-
:title="'Αποσύνδεση'"
|
|
126
|
-
v-tooltip.right="'Αποσύνδεση'"
|
|
127
|
-
/>
|
|
128
|
-
</div>
|
|
129
|
-
<div v-else class="flex flex-col items-center gap-2">
|
|
130
|
-
<SidebarIconLink
|
|
131
|
-
icon="LogIn"
|
|
132
|
-
:on-click="() => $emit('signin')"
|
|
133
|
-
:title="'Sign in'"
|
|
134
|
-
v-tooltip.right="'Sign in'"
|
|
135
|
-
/>
|
|
136
|
-
<SidebarIconLink
|
|
137
|
-
icon="UserPlus"
|
|
138
|
-
:on-click="() => $emit('signup')"
|
|
139
|
-
:title="'Sign up'"
|
|
140
|
-
v-tooltip.right="'Sign up'"
|
|
141
|
-
/>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
144
|
-
<div v-else class="flex flex-col gap-3 px-3">
|
|
145
|
-
<div class="min-w-0">
|
|
146
|
-
<p
|
|
147
|
-
v-if="account.loggedIn"
|
|
148
|
-
class="mt-2 truncate text-lg font-semibold text-white"
|
|
149
|
-
>
|
|
150
|
-
{{ accountDisplayName }}
|
|
151
|
-
</p>
|
|
152
|
-
<p
|
|
153
|
-
v-if="account.loggedIn && displayEmail"
|
|
154
|
-
class="truncate text-sm text-slate-300"
|
|
155
|
-
>
|
|
156
|
-
{{ displayEmail }}
|
|
157
|
-
</p>
|
|
158
|
-
</div>
|
|
159
|
-
<div class="flex flex-wrap justify-between gap-2">
|
|
160
|
-
<kButton
|
|
161
|
-
v-if="!account.loggedIn"
|
|
162
|
-
secondary
|
|
163
|
-
icon="LogIn"
|
|
164
|
-
label="Σύνδεση"
|
|
165
|
-
@click="$emit('signin')"
|
|
166
|
-
/>
|
|
167
|
-
<kButton
|
|
168
|
-
v-if="!account.loggedIn"
|
|
169
|
-
secondary
|
|
170
|
-
icon="UserPlus"
|
|
171
|
-
label="Εγγραφή"
|
|
172
|
-
@click="$emit('signup')"
|
|
173
|
-
/>
|
|
174
|
-
<kButton
|
|
175
|
-
v-if="account.loggedIn"
|
|
176
|
-
variant="soft"
|
|
177
|
-
icon="UserRoundCog"
|
|
178
|
-
label="Επεξεργασία προφίλ"
|
|
179
|
-
@click="$emit('edit-profile')"
|
|
180
|
-
/>
|
|
181
|
-
<kButton
|
|
182
|
-
v-if="account.loggedIn"
|
|
183
|
-
icon="LogOut"
|
|
184
|
-
warning
|
|
185
|
-
label="Αποσύνδεση"
|
|
186
|
-
@click="$emit('signout')"
|
|
187
|
-
/>
|
|
188
|
-
</div>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
69
|
+
<transition name="mobile-menu-backdrop">
|
|
70
|
+
<button
|
|
71
|
+
v-if="!isDesktopViewport && mobileMenuOpen"
|
|
72
|
+
type="button"
|
|
73
|
+
class="fixed inset-0 z-[85] bg-black/50 backdrop-blur-sm"
|
|
74
|
+
aria-label="Close menu"
|
|
75
|
+
@click="closeMobileMenu"
|
|
76
|
+
/>
|
|
77
|
+
</transition>
|
|
191
78
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
79
|
+
<transition name="mobile-menu-panel">
|
|
80
|
+
<aside
|
|
81
|
+
v-if="!isDesktopViewport && mobileMenuOpen"
|
|
82
|
+
class="fixed inset-y-0 left-0 z-[90] flex h-[100dvh] w-[min(90vw,420px)] max-w-full overflow-hidden shadow-2xl"
|
|
83
|
+
role="dialog"
|
|
84
|
+
aria-modal="true"
|
|
195
85
|
>
|
|
196
|
-
<div class="flex
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
>
|
|
216
|
-
{{ contactEmail }}
|
|
217
|
-
</a>
|
|
218
|
-
</span>
|
|
219
|
-
<a
|
|
220
|
-
href="https://ketekny.gr"
|
|
221
|
-
target="_blank"
|
|
222
|
-
rel="noreferrer"
|
|
223
|
-
class="whitespace-nowrap hover:underline"
|
|
86
|
+
<div class="relative flex h-full w-full flex-col bg-transparent">
|
|
87
|
+
<div class="flex-1 overflow-y-auto bg-transparent">
|
|
88
|
+
<SidebarPanel
|
|
89
|
+
:title="title"
|
|
90
|
+
:collapsed-logo-src="collapsedLogoSrc"
|
|
91
|
+
:logo-src="logoSrc"
|
|
92
|
+
:logo-alt="logoAlt"
|
|
93
|
+
:show-sponsors="showSponsors"
|
|
94
|
+
:menu-items="normalizedMainMenu"
|
|
95
|
+
:show-contact-info="showContactInfo"
|
|
96
|
+
:account="account"
|
|
97
|
+
:contact-email="contactEmail"
|
|
98
|
+
:collapsed="false"
|
|
99
|
+
@logo-click="handleLogoClick"
|
|
100
|
+
@item-click="closeMobileMenu"
|
|
101
|
+
@signin="handleSignin"
|
|
102
|
+
@signup="handleSignup"
|
|
103
|
+
@signout="handleSignout"
|
|
104
|
+
@edit-profile="handleEditProfile"
|
|
224
105
|
>
|
|
225
|
-
|
|
226
|
-
|
|
106
|
+
<template #sidebar-header>
|
|
107
|
+
<slot name="sidebar-header" />
|
|
108
|
+
</template>
|
|
109
|
+
</SidebarPanel>
|
|
227
110
|
</div>
|
|
228
111
|
</div>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
v-if="showSponsors && !isSidebarCollapsed"
|
|
232
|
-
class="flex flex-col gap-3 px-4 py-1 bg-[#0d4b87] mt-2"
|
|
233
|
-
>
|
|
234
|
-
<div>
|
|
235
|
-
<img
|
|
236
|
-
src="https://s3.ketekny.gr/public/web-apps/sponsors/Greece 2.0_NextGeneration_White_gr.png"
|
|
237
|
-
alt="Tameio Anakampsis"
|
|
238
|
-
class="mx-auto max-h-14 w-auto object-contain"
|
|
239
|
-
/>
|
|
240
|
-
</div>
|
|
241
|
-
<div>
|
|
242
|
-
<img
|
|
243
|
-
src="https://s3.ketekny.gr/public/web-apps/sponsors/yp_ygeias_dark.png"
|
|
244
|
-
alt="Ypourgeio Ygeias"
|
|
245
|
-
class="mx-auto max-h-14 w-auto object-contain"
|
|
246
|
-
/>
|
|
247
|
-
</div>
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
</aside>
|
|
112
|
+
</aside>
|
|
113
|
+
</transition>
|
|
251
114
|
|
|
252
115
|
<main
|
|
253
|
-
class="min-h-
|
|
116
|
+
class="min-h-0 min-w-0 flex-1 overflow-visible lg:h-full lg:min-h-0 lg:overflow-y-auto lg:overflow-x-hidden"
|
|
254
117
|
>
|
|
255
118
|
<div class="w-full">
|
|
256
119
|
<slot />
|
|
@@ -259,68 +122,18 @@
|
|
|
259
122
|
</div>
|
|
260
123
|
</template>
|
|
261
124
|
|
|
262
|
-
|
|
263
|
-
import { h } from "vue";
|
|
264
|
-
import kButton from "../ui/kButton.vue";
|
|
125
|
+
<script>
|
|
265
126
|
import kIcon from "../ui/kIcon.vue";
|
|
266
|
-
import
|
|
267
|
-
|
|
268
|
-
const SidebarIconLink = {
|
|
269
|
-
props: {
|
|
270
|
-
icon: { type: String, required: true },
|
|
271
|
-
active: { type: Boolean, default: false },
|
|
272
|
-
to: { type: [String, Object], default: null },
|
|
273
|
-
href: { type: String, default: null },
|
|
274
|
-
title: { type: String, default: "" },
|
|
275
|
-
isExternal: { type: Boolean, default: false },
|
|
276
|
-
target: { type: String, default: "_blank" },
|
|
277
|
-
rel: { type: String, default: "noreferrer" },
|
|
278
|
-
onClick: { type: Function, default: null },
|
|
279
|
-
warning: { type: Boolean, default: false },
|
|
280
|
-
tooltip: { type: String, default: "" },
|
|
281
|
-
},
|
|
282
|
-
methods: {
|
|
283
|
-
handleClick(event) {
|
|
284
|
-
if (this.onClick) this.onClick(event);
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
render() {
|
|
288
|
-
const isLink = Boolean(this.to);
|
|
289
|
-
const tag = isLink ? RouterLink : this.isExternal ? "a" : "button";
|
|
290
|
-
|
|
291
|
-
return h(
|
|
292
|
-
tag,
|
|
293
|
-
{
|
|
294
|
-
to: isLink ? this.to : undefined,
|
|
295
|
-
href: this.href,
|
|
296
|
-
target: this.isExternal ? this.target : null,
|
|
297
|
-
rel: this.isExternal ? this.rel : null,
|
|
298
|
-
type: isLink || this.isExternal ? null : "button",
|
|
299
|
-
title: this.title,
|
|
300
|
-
class: [
|
|
301
|
-
"group relative flex h-14 w-14 items-center justify-center rounded-xl border border-white/15 transition",
|
|
302
|
-
this.active
|
|
303
|
-
? "bg-slate-950 text-white"
|
|
304
|
-
: this.warning
|
|
305
|
-
? "bg-[#d97706] text-white hover:bg-[#b45309]"
|
|
306
|
-
: "bg-white/5 text-white hover:border-cyan-400/40 hover:bg-white/10",
|
|
307
|
-
isLink ? "cursor-pointer" : "",
|
|
308
|
-
],
|
|
309
|
-
onClick: this.handleClick,
|
|
310
|
-
},
|
|
311
|
-
[h(kIcon, { name: this.icon, size: "20", color: "#ffffff" })]
|
|
312
|
-
);
|
|
313
|
-
},
|
|
314
|
-
};
|
|
127
|
+
import SidebarPanel from "./twoColSidebarPanel.vue";
|
|
315
128
|
|
|
316
129
|
const DEFAULT_STORAGE_KEY = "two-col-layout-sidebar-collapsed";
|
|
130
|
+
const TAILWIND_MD_QUERY = "(min-width: 768px) and (max-width: 1023px)";
|
|
131
|
+
const TAILWIND_LG_QUERY = "(min-width: 1024px)";
|
|
317
132
|
|
|
318
133
|
export default {
|
|
319
134
|
components: {
|
|
320
|
-
kButton,
|
|
321
135
|
kIcon,
|
|
322
|
-
|
|
323
|
-
SidebarIconLink,
|
|
136
|
+
SidebarPanel,
|
|
324
137
|
},
|
|
325
138
|
emits: ["logo-click", "signin", "signup", "signout", "edit-profile"],
|
|
326
139
|
props: {
|
|
@@ -345,56 +158,110 @@ export default {
|
|
|
345
158
|
},
|
|
346
159
|
data() {
|
|
347
160
|
return {
|
|
348
|
-
isSidebarCollapsed:
|
|
161
|
+
isSidebarCollapsed: null,
|
|
349
162
|
mdCollapseQuery: null,
|
|
163
|
+
lgViewportQuery: null,
|
|
164
|
+
isDesktopViewport: false,
|
|
165
|
+
mobileMenuOpen: false,
|
|
350
166
|
};
|
|
351
167
|
},
|
|
352
168
|
created() {
|
|
353
|
-
this.
|
|
169
|
+
this.isSidebarCollapsed = this.getInitialSidebarState();
|
|
170
|
+
|
|
171
|
+
if (typeof window !== "undefined") {
|
|
172
|
+
this.isDesktopViewport = window.matchMedia(TAILWIND_LG_QUERY).matches;
|
|
173
|
+
}
|
|
354
174
|
},
|
|
355
175
|
mounted() {
|
|
356
|
-
this.mdCollapseQuery = window.matchMedia(
|
|
357
|
-
|
|
358
|
-
|
|
176
|
+
this.mdCollapseQuery = window.matchMedia(TAILWIND_MD_QUERY);
|
|
177
|
+
this.lgViewportQuery = window.matchMedia(TAILWIND_LG_QUERY);
|
|
178
|
+
this.isDesktopViewport = this.lgViewportQuery.matches;
|
|
179
|
+
|
|
359
180
|
this.syncSidebarForViewport();
|
|
360
|
-
this.mdCollapseQuery.addEventListener(
|
|
361
|
-
|
|
362
|
-
this.syncSidebarForViewport
|
|
363
|
-
);
|
|
181
|
+
this.mdCollapseQuery.addEventListener("change", this.syncSidebarForViewport);
|
|
182
|
+
this.lgViewportQuery.addEventListener("change", this.syncDesktopViewport);
|
|
364
183
|
},
|
|
365
184
|
beforeUnmount() {
|
|
366
185
|
this.mdCollapseQuery?.removeEventListener(
|
|
367
186
|
"change",
|
|
368
187
|
this.syncSidebarForViewport
|
|
369
188
|
);
|
|
189
|
+
this.lgViewportQuery?.removeEventListener(
|
|
190
|
+
"change",
|
|
191
|
+
this.syncDesktopViewport
|
|
192
|
+
);
|
|
193
|
+
if (typeof document !== "undefined") {
|
|
194
|
+
document.body.style.overflow = "";
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
watch: {
|
|
198
|
+
mobileMenuOpen(isOpen) {
|
|
199
|
+
if (typeof document === "undefined") return;
|
|
200
|
+
|
|
201
|
+
document.body.style.overflow = isOpen ? "hidden" : "";
|
|
202
|
+
},
|
|
370
203
|
},
|
|
371
204
|
methods: {
|
|
372
|
-
|
|
205
|
+
isMdViewport() {
|
|
206
|
+
if (typeof window === "undefined") return false;
|
|
207
|
+
|
|
208
|
+
return window.matchMedia(TAILWIND_MD_QUERY).matches;
|
|
209
|
+
},
|
|
210
|
+
getInitialSidebarState() {
|
|
373
211
|
if (typeof window === "undefined") {
|
|
374
|
-
|
|
375
|
-
|
|
212
|
+
return this.defaultCollapsed;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (this.isMdViewport()) {
|
|
216
|
+
return true;
|
|
376
217
|
}
|
|
377
218
|
|
|
378
219
|
const stored = window.localStorage.getItem(this.storageKey);
|
|
379
220
|
if (stored == null) {
|
|
380
|
-
|
|
381
|
-
return;
|
|
221
|
+
return this.defaultCollapsed;
|
|
382
222
|
}
|
|
383
223
|
|
|
384
|
-
|
|
224
|
+
return stored === "true";
|
|
385
225
|
},
|
|
386
226
|
syncSidebarForViewport() {
|
|
387
227
|
if (typeof window === "undefined") return;
|
|
388
228
|
|
|
389
|
-
|
|
390
|
-
"(min-width: 768px) and (max-width: 1023px)"
|
|
391
|
-
).matches;
|
|
392
|
-
if (isMd) {
|
|
229
|
+
if (this.isMdViewport()) {
|
|
393
230
|
this.isSidebarCollapsed = true;
|
|
394
231
|
return;
|
|
395
232
|
}
|
|
396
233
|
|
|
397
|
-
this.
|
|
234
|
+
this.isSidebarCollapsed = this.getInitialSidebarState();
|
|
235
|
+
},
|
|
236
|
+
syncDesktopViewport(event) {
|
|
237
|
+
this.isDesktopViewport = event.matches;
|
|
238
|
+
|
|
239
|
+
if (event.matches) {
|
|
240
|
+
this.mobileMenuOpen = false;
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
closeMobileMenu() {
|
|
244
|
+
this.mobileMenuOpen = false;
|
|
245
|
+
},
|
|
246
|
+
handleLogoClick() {
|
|
247
|
+
this.closeMobileMenu();
|
|
248
|
+
this.$emit("logo-click");
|
|
249
|
+
},
|
|
250
|
+
handleSignin() {
|
|
251
|
+
this.closeMobileMenu();
|
|
252
|
+
this.$emit("signin");
|
|
253
|
+
},
|
|
254
|
+
handleSignup() {
|
|
255
|
+
this.closeMobileMenu();
|
|
256
|
+
this.$emit("signup");
|
|
257
|
+
},
|
|
258
|
+
handleSignout() {
|
|
259
|
+
this.closeMobileMenu();
|
|
260
|
+
this.$emit("signout");
|
|
261
|
+
},
|
|
262
|
+
handleEditProfile() {
|
|
263
|
+
this.closeMobileMenu();
|
|
264
|
+
this.$emit("edit-profile");
|
|
398
265
|
},
|
|
399
266
|
persistSidebarState() {
|
|
400
267
|
if (typeof window === "undefined") return;
|
|
@@ -408,11 +275,6 @@ export default {
|
|
|
408
275
|
this.isSidebarCollapsed = !this.isSidebarCollapsed;
|
|
409
276
|
this.persistSidebarState();
|
|
410
277
|
},
|
|
411
|
-
menuItemTag(item) {
|
|
412
|
-
if (item?.to) return RouterLink;
|
|
413
|
-
if (item?.href) return "a";
|
|
414
|
-
return "button";
|
|
415
|
-
},
|
|
416
278
|
},
|
|
417
279
|
computed: {
|
|
418
280
|
normalizedMainMenu() {
|
|
@@ -452,56 +314,25 @@ export default {
|
|
|
452
314
|
};
|
|
453
315
|
});
|
|
454
316
|
},
|
|
455
|
-
menuItemBindings(item) {
|
|
456
|
-
if (item?.to) {
|
|
457
|
-
return {
|
|
458
|
-
to: item.to,
|
|
459
|
-
title: item.title || item.label,
|
|
460
|
-
"aria-current": item.active ? "page" : null,
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (item?.href) {
|
|
465
|
-
return {
|
|
466
|
-
href: item.href,
|
|
467
|
-
target: item.target || "_blank",
|
|
468
|
-
rel: item.rel || "noreferrer",
|
|
469
|
-
title: item.title || item.label,
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return {
|
|
474
|
-
type: "button",
|
|
475
|
-
title: item?.title || item?.label,
|
|
476
|
-
};
|
|
477
|
-
},
|
|
478
|
-
accountDisplayName() {
|
|
479
|
-
if (!this.account) return "";
|
|
480
|
-
return (
|
|
481
|
-
[this.account.firstName, this.account.lastName]
|
|
482
|
-
.filter(Boolean)
|
|
483
|
-
.join(" ")
|
|
484
|
-
.trim() || "Account"
|
|
485
|
-
);
|
|
486
|
-
},
|
|
487
|
-
displayEmail() {
|
|
488
|
-
return this.account?.email || "";
|
|
489
|
-
},
|
|
490
|
-
collapsedContactTooltip() {
|
|
491
|
-
return `
|
|
492
|
-
<div style="min-width: 220px;">
|
|
493
|
-
<div style="font-weight: 700; text-transform: uppercase; letter-spacing: 0.25em; font-size: 11px; color: rgba(255,255,255,0.65); margin-bottom: 8px;">
|
|
494
|
-
KETEKNY AE
|
|
495
|
-
</div>
|
|
496
|
-
<div style="display: flex; flex-direction: column; gap: 4px; font-size: 13px; line-height: 1.4; color: rgba(255,255,255,0.9);">
|
|
497
|
-
<div>Βερανζέρου 13, 10677, Αθήνα</div>
|
|
498
|
-
<div>210 3648 337</div>
|
|
499
|
-
<div>${this.contactEmail}</div>
|
|
500
|
-
<div>https://ketekny.gr</div>
|
|
501
|
-
</div>
|
|
502
|
-
</div>
|
|
503
|
-
`;
|
|
504
|
-
},
|
|
505
317
|
},
|
|
506
318
|
};
|
|
507
319
|
</script>
|
|
320
|
+
|
|
321
|
+
<style scoped>
|
|
322
|
+
.mobile-menu-panel-enter-active,
|
|
323
|
+
.mobile-menu-panel-leave-active,
|
|
324
|
+
.mobile-menu-backdrop-enter-active,
|
|
325
|
+
.mobile-menu-backdrop-leave-active {
|
|
326
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.mobile-menu-panel-enter-from,
|
|
330
|
+
.mobile-menu-panel-leave-to {
|
|
331
|
+
transform: translateX(-100%);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
.mobile-menu-backdrop-enter-from,
|
|
335
|
+
.mobile-menu-backdrop-leave-to {
|
|
336
|
+
opacity: 0;
|
|
337
|
+
}
|
|
338
|
+
</style>
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="flex h-full w-full flex-col overflow-x-hidden bg-[radial-gradient(circle_at_0_0,#ffffff0f,#0000_34%),linear-gradient(#101a2d_0%,#0a1120_52%,#070d19_100%)] text-white shadow-2xl transition-all duration-300 ease-out lg:relative lg:z-30 lg:will-change-transform"
|
|
4
|
+
>
|
|
5
|
+
<div class="relative">
|
|
6
|
+
<button
|
|
7
|
+
v-if="logoSrc"
|
|
8
|
+
type="button"
|
|
9
|
+
class="block w-full text-left"
|
|
10
|
+
v-tooltip.right.html="collapsed ? collapsedContactTooltip : logoAlt"
|
|
11
|
+
@click="$emit('logo-click')"
|
|
12
|
+
>
|
|
13
|
+
<img
|
|
14
|
+
:alt="logoAlt"
|
|
15
|
+
:src="collapsed ? collapsedLogoSrc : logoSrc"
|
|
16
|
+
class="px-4 py-4 transition-all duration-300 cursor-pointer"
|
|
17
|
+
/>
|
|
18
|
+
</button>
|
|
19
|
+
<div
|
|
20
|
+
v-if="title && !collapsed"
|
|
21
|
+
class="py-3 text-xl font-bold text-center"
|
|
22
|
+
>
|
|
23
|
+
{{ title }}
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<slot name="sidebar-header" />
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div class="flex-1 pt-4">
|
|
30
|
+
<div v-if="collapsed" class="flex flex-col items-center gap-2 mx-1 mt-3">
|
|
31
|
+
<component
|
|
32
|
+
:is="menuItemComponent(item)"
|
|
33
|
+
v-for="item in menuItems"
|
|
34
|
+
:key="item.key"
|
|
35
|
+
v-bind="item.bindings"
|
|
36
|
+
class="relative flex items-center justify-center transition border group h-14 w-14 rounded-xl border-white/15"
|
|
37
|
+
:class="item.classes"
|
|
38
|
+
v-tooltip.right="item.label"
|
|
39
|
+
@click="$emit('item-click', item)"
|
|
40
|
+
>
|
|
41
|
+
<kIcon
|
|
42
|
+
:name="item.icon"
|
|
43
|
+
size="20"
|
|
44
|
+
:color="item.active ? '#0f172a' : '#ffffff'"
|
|
45
|
+
/>
|
|
46
|
+
</component>
|
|
47
|
+
</div>
|
|
48
|
+
<div v-else-if="menuItems.length" class="mx-2 mt-3 space-y-3">
|
|
49
|
+
<component
|
|
50
|
+
:is="menuItemComponent(item)"
|
|
51
|
+
v-for="item in menuItems"
|
|
52
|
+
:key="item.key"
|
|
53
|
+
v-bind="item.bindings"
|
|
54
|
+
class="flex items-start w-full gap-3 px-4 py-4 text-left transition-all duration-200 border rounded-2xl"
|
|
55
|
+
:class="item.classes"
|
|
56
|
+
@click="$emit('item-click', item)"
|
|
57
|
+
>
|
|
58
|
+
<span
|
|
59
|
+
class="mt-0.5 flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-white/10 text-cyan-100 transition-colors duration-200"
|
|
60
|
+
:class="
|
|
61
|
+
item.active
|
|
62
|
+
? 'bg-slate-950 text-white'
|
|
63
|
+
: item.label === 'Settings'
|
|
64
|
+
? 'bg-sky-500/20 text-sky-50 ring-1 ring-sky-400/25'
|
|
65
|
+
: ''
|
|
66
|
+
"
|
|
67
|
+
>
|
|
68
|
+
<kIcon :name="item.icon" />
|
|
69
|
+
</span>
|
|
70
|
+
<span class="min-w-0">
|
|
71
|
+
<span class="block font-semibold leading-tight">
|
|
72
|
+
{{ item.label }}
|
|
73
|
+
</span>
|
|
74
|
+
<span
|
|
75
|
+
v-if="item.description"
|
|
76
|
+
class="block mt-1 text-sm"
|
|
77
|
+
:class="item.active ? 'text-slate-700' : 'text-slate-300'"
|
|
78
|
+
>
|
|
79
|
+
{{ item.description }}
|
|
80
|
+
</span>
|
|
81
|
+
</span>
|
|
82
|
+
</component>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div
|
|
87
|
+
v-if="account"
|
|
88
|
+
class="px-1 pb-4 border-b shrink-0 border-white/10 bg-slate-900/80 text-white/80"
|
|
89
|
+
>
|
|
90
|
+
<div v-if="collapsed" class="flex flex-col gap-3">
|
|
91
|
+
<div v-if="account.loggedIn" class="flex flex-col items-center gap-2">
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
class="relative flex items-center justify-center text-white transition border group h-14 w-14 rounded-xl border-white/15 bg-white/5 hover:border-cyan-400/40 hover:bg-white/10"
|
|
95
|
+
:title="editProfileLabel"
|
|
96
|
+
v-tooltip.right="editProfileLabel"
|
|
97
|
+
@click="$emit('edit-profile')"
|
|
98
|
+
>
|
|
99
|
+
<kIcon name="UserRoundCog" size="20" color="#ffffff" />
|
|
100
|
+
</button>
|
|
101
|
+
<button
|
|
102
|
+
type="button"
|
|
103
|
+
class="group relative flex h-14 w-14 items-center justify-center rounded-xl border border-white/15 bg-[#d97706] text-white transition hover:bg-[#b45309]"
|
|
104
|
+
:title="signOutLabel"
|
|
105
|
+
v-tooltip.right="signOutLabel"
|
|
106
|
+
@click="$emit('signout')"
|
|
107
|
+
>
|
|
108
|
+
<kIcon name="LogOut" size="20" color="#ffffff" />
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
<div v-else class="flex flex-col items-center gap-2">
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
class="relative flex items-center justify-center text-white transition border group h-14 w-14 rounded-xl border-white/15 bg-white/5 hover:border-cyan-400/40 hover:bg-white/10"
|
|
115
|
+
:title="signInLabel"
|
|
116
|
+
v-tooltip.right="signInLabel"
|
|
117
|
+
@click="$emit('signin')"
|
|
118
|
+
>
|
|
119
|
+
<kIcon name="LogIn" size="20" color="#ffffff" />
|
|
120
|
+
</button>
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
class="relative flex items-center justify-center text-white transition border group h-14 w-14 rounded-xl border-white/15 bg-white/5 hover:border-cyan-400/40 hover:bg-white/10"
|
|
124
|
+
:title="signUpLabel"
|
|
125
|
+
v-tooltip.right="signUpLabel"
|
|
126
|
+
@click="$emit('signup')"
|
|
127
|
+
>
|
|
128
|
+
<kIcon name="UserPlus" size="20" color="#ffffff" />
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div v-else class="flex flex-col gap-3 px-3">
|
|
133
|
+
<div class="min-w-0">
|
|
134
|
+
<p
|
|
135
|
+
v-if="account.loggedIn"
|
|
136
|
+
class="mt-2 text-lg font-semibold text-white truncate"
|
|
137
|
+
>
|
|
138
|
+
{{ accountDisplayName }}
|
|
139
|
+
</p>
|
|
140
|
+
<p
|
|
141
|
+
v-if="account.loggedIn && displayEmail"
|
|
142
|
+
class="text-sm truncate text-slate-300"
|
|
143
|
+
>
|
|
144
|
+
{{ displayEmail }}
|
|
145
|
+
</p>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="flex flex-wrap justify-between gap-2">
|
|
148
|
+
<kButton
|
|
149
|
+
v-if="!account.loggedIn"
|
|
150
|
+
secondary
|
|
151
|
+
icon="LogIn"
|
|
152
|
+
:label="signInLabel"
|
|
153
|
+
@click="$emit('signin')"
|
|
154
|
+
/>
|
|
155
|
+
<kButton
|
|
156
|
+
v-if="!account.loggedIn"
|
|
157
|
+
secondary
|
|
158
|
+
icon="UserPlus"
|
|
159
|
+
:label="signUpLabel"
|
|
160
|
+
@click="$emit('signup')"
|
|
161
|
+
/>
|
|
162
|
+
<kButton
|
|
163
|
+
v-if="account.loggedIn"
|
|
164
|
+
variant="soft"
|
|
165
|
+
icon="UserRoundCog"
|
|
166
|
+
:label="editProfileLabel"
|
|
167
|
+
@click="$emit('edit-profile')"
|
|
168
|
+
/>
|
|
169
|
+
<kButton
|
|
170
|
+
v-if="account.loggedIn"
|
|
171
|
+
icon="LogOut"
|
|
172
|
+
warning
|
|
173
|
+
:label="signOutLabel"
|
|
174
|
+
@click="$emit('signout')"
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<div
|
|
181
|
+
v-if="!collapsed"
|
|
182
|
+
class="border-t shrink-0 border-white/10 text-white/50"
|
|
183
|
+
>
|
|
184
|
+
<div v-if="showContactInfo" class="flex flex-col gap-2 px-4">
|
|
185
|
+
<p class="text-sm font-semibold uppercase tracking-[0.25em] text-white/60">
|
|
186
|
+
KETEKNY AE
|
|
187
|
+
</p>
|
|
188
|
+
<div class="flex flex-wrap items-center text-sm gap-x-3">
|
|
189
|
+
<span class="whitespace-nowrap">{{ contactAddressLabel }}</span>
|
|
190
|
+
<span class="whitespace-nowrap">210 3648 337</span>
|
|
191
|
+
<span class="whitespace-nowrap">
|
|
192
|
+
<a
|
|
193
|
+
:href="`mailto:${contactEmail}`"
|
|
194
|
+
class="whitespace-nowrap hover:underline"
|
|
195
|
+
>
|
|
196
|
+
{{ contactEmail }}
|
|
197
|
+
</a>
|
|
198
|
+
</span>
|
|
199
|
+
<a
|
|
200
|
+
href="https://ketekny.gr"
|
|
201
|
+
target="_blank"
|
|
202
|
+
rel="noreferrer"
|
|
203
|
+
class="whitespace-nowrap hover:underline"
|
|
204
|
+
>
|
|
205
|
+
https://ketekny.gr
|
|
206
|
+
</a>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div
|
|
211
|
+
v-if="showSponsors"
|
|
212
|
+
class="mt-2 flex flex-col gap-3 bg-[#0d4b87] px-4 py-1"
|
|
213
|
+
>
|
|
214
|
+
<div>
|
|
215
|
+
<img
|
|
216
|
+
src="https://s3.ketekny.gr/public/web-apps/sponsors/Greece 2.0_NextGeneration_White_gr.png"
|
|
217
|
+
alt="Tameio Anakampsis"
|
|
218
|
+
class="object-contain w-auto mx-auto max-h-14"
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
<div>
|
|
222
|
+
<img
|
|
223
|
+
src="https://s3.ketekny.gr/public/web-apps/sponsors/yp_ygeias_dark.png"
|
|
224
|
+
alt="Ypourgeio Ygeias"
|
|
225
|
+
class="object-contain w-auto mx-auto max-h-14"
|
|
226
|
+
/>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
</template>
|
|
232
|
+
|
|
233
|
+
<script>
|
|
234
|
+
import { RouterLink } from "vue-router";
|
|
235
|
+
import kButton from "../ui/kButton.vue";
|
|
236
|
+
import kIcon from "../ui/kIcon.vue";
|
|
237
|
+
|
|
238
|
+
const LABELS = {
|
|
239
|
+
editProfile: "Το προφίλ μου",
|
|
240
|
+
signOut: "Αποσύνδεση",
|
|
241
|
+
signIn: "Σύνδεση",
|
|
242
|
+
signUp: "Εγγραφή",
|
|
243
|
+
contactAddress: "Βερανζέου 13, 10677, Αθήνα",
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export default {
|
|
247
|
+
name: "TwoColSidebarPanel",
|
|
248
|
+
components: {
|
|
249
|
+
kButton,
|
|
250
|
+
kIcon,
|
|
251
|
+
RouterLink,
|
|
252
|
+
},
|
|
253
|
+
emits: [
|
|
254
|
+
"logo-click",
|
|
255
|
+
"item-click",
|
|
256
|
+
"signin",
|
|
257
|
+
"signup",
|
|
258
|
+
"signout",
|
|
259
|
+
"edit-profile",
|
|
260
|
+
],
|
|
261
|
+
props: {
|
|
262
|
+
title: { type: String, default: "" },
|
|
263
|
+
collapsedLogoSrc: {
|
|
264
|
+
type: String,
|
|
265
|
+
default: "https://s3.ketekny.gr/public/web-apps/logos/logo-icon-dark.png",
|
|
266
|
+
},
|
|
267
|
+
logoSrc: { type: String, default: "" },
|
|
268
|
+
logoAlt: { type: String, default: "Logo" },
|
|
269
|
+
showSponsors: { type: Boolean, default: false },
|
|
270
|
+
menuItems: { type: Array, default: () => [] },
|
|
271
|
+
showContactInfo: { type: Boolean, default: false },
|
|
272
|
+
account: { type: Object, default: null },
|
|
273
|
+
contactEmail: { type: String, default: "grammateia@ketekny.gr" },
|
|
274
|
+
collapsed: { type: Boolean, default: false },
|
|
275
|
+
},
|
|
276
|
+
computed: {
|
|
277
|
+
accountDisplayName() {
|
|
278
|
+
if (!this.account) return "";
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
[this.account.firstName, this.account.lastName]
|
|
282
|
+
.filter(Boolean)
|
|
283
|
+
.join(" ")
|
|
284
|
+
.trim() || "Account"
|
|
285
|
+
);
|
|
286
|
+
},
|
|
287
|
+
displayEmail() {
|
|
288
|
+
return this.account?.email || "";
|
|
289
|
+
},
|
|
290
|
+
editProfileLabel() {
|
|
291
|
+
return LABELS.editProfile;
|
|
292
|
+
},
|
|
293
|
+
signOutLabel() {
|
|
294
|
+
return LABELS.signOut;
|
|
295
|
+
},
|
|
296
|
+
signInLabel() {
|
|
297
|
+
return LABELS.signIn;
|
|
298
|
+
},
|
|
299
|
+
signUpLabel() {
|
|
300
|
+
return LABELS.signUp;
|
|
301
|
+
},
|
|
302
|
+
contactAddressLabel() {
|
|
303
|
+
return LABELS.contactAddress;
|
|
304
|
+
},
|
|
305
|
+
collapsedContactTooltip() {
|
|
306
|
+
return `
|
|
307
|
+
<div style="min-width: 220px;">
|
|
308
|
+
<div style="font-weight: 700; text-transform: uppercase; letter-spacing: 0.25em; font-size: 11px; color: rgba(255,255,255,0.65); margin-bottom: 8px;">
|
|
309
|
+
KETEKNY AE
|
|
310
|
+
</div>
|
|
311
|
+
<div style="display: flex; flex-direction: column; gap: 4px; font-size: 13px; line-height: 1.4; color: rgba(255,255,255,0.9);">
|
|
312
|
+
<div>${LABELS.contactAddress}</div>
|
|
313
|
+
<div>210 3648 337</div>
|
|
314
|
+
<div>${this.contactEmail}</div>
|
|
315
|
+
<div>https://ketekny.gr</div>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
`;
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
methods: {
|
|
322
|
+
menuItemComponent(item) {
|
|
323
|
+
if (item?.isLink) return RouterLink;
|
|
324
|
+
if (item?.isExternal) return "a";
|
|
325
|
+
return "button";
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
</script>
|
package/src/style/style.css
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap");
|
|
2
|
+
|
|
1
3
|
@tailwind base;
|
|
2
4
|
@tailwind components;
|
|
3
5
|
@tailwind utilities;
|
|
4
6
|
|
|
7
|
+
body {
|
|
8
|
+
font-family: "Ubuntu", Helvetica, Arial, sans-serif;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
|
|
6
12
|
h1, h2, h3, h4{
|
|
7
13
|
color: theme('colors.primary');
|