ketekny-ui-kit 1.0.114 → 1.0.115
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
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<main class="flex min-h-0 w-full max-w-none flex-1 flex-col overflow-hidden bg-slate-100">
|
|
3
|
+
<div
|
|
4
|
+
v-if="mobileSidebarOpen && !isDesktopViewport"
|
|
5
|
+
class="fixed inset-0 z-20 bg-slate-950/40 backdrop-blur-[1px] lg:hidden"
|
|
6
|
+
@click="toggleMobileSidebar"
|
|
7
|
+
/>
|
|
8
|
+
|
|
3
9
|
<section
|
|
4
10
|
v-if="hasTopSection"
|
|
5
11
|
class="shrink-0 bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8"
|
|
@@ -44,8 +50,27 @@
|
|
|
44
50
|
</div>
|
|
45
51
|
|
|
46
52
|
<div class="flex min-h-0 flex-1 flex-col overflow-hidden bg-slate-100 py-4">
|
|
53
|
+
<div
|
|
54
|
+
class="lg:hidden"
|
|
55
|
+
:class="mobileSidebarOpen ? 'mx-auto flex w-full max-w-none px-4 pb-3 sm:px-6' : 'fixed left-0 top-1/2 z-20 -translate-y-1/2'"
|
|
56
|
+
>
|
|
57
|
+
<kButton
|
|
58
|
+
:label="mobileSidebarOpen ? 'Hide sidebar' : 'Show sidebar'"
|
|
59
|
+
:icon="mobileSidebarOpen ? 'PanelLeftClose' : 'PanelLeftOpen'"
|
|
60
|
+
variant="soft"
|
|
61
|
+
:icon-only="!mobileSidebarOpen"
|
|
62
|
+
:aria-label="mobileSidebarOpen ? null : 'Show sidebar'"
|
|
63
|
+
:class="mobileSidebarOpen ? '' : 'rounded-full shadow-lg shadow-slate-900/15'"
|
|
64
|
+
@click="toggleMobileSidebar"
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
47
67
|
<div class="mx-auto flex min-h-0 w-full max-w-none flex-1 gap-2">
|
|
48
|
-
<aside
|
|
68
|
+
<aside
|
|
69
|
+
v-if="showSidebar"
|
|
70
|
+
class="min-h-0 shrink-0 overflow-y-auto bg-slate-100 px-4 sm:px-6 lg:ml-8 lg:pr-4 lg:pl-0"
|
|
71
|
+
:class="mobileSidebarClass"
|
|
72
|
+
:style="sidebarStyle"
|
|
73
|
+
>
|
|
49
74
|
<slot name="sidebar" />
|
|
50
75
|
</aside>
|
|
51
76
|
<section class="min-h-0 min-w-0 flex-1 overflow-y-auto px-2">
|
|
@@ -60,6 +85,9 @@
|
|
|
60
85
|
import packageJson from '../../package.json'
|
|
61
86
|
import kButton from '../ui/kButton.vue'
|
|
62
87
|
|
|
88
|
+
const LG_VIEWPORT_QUERY = '(min-width: 1024px)'
|
|
89
|
+
const MAIN_MENU_TOGGLE_EVENT = 'ketekny:two-col-layout-mobile-menu'
|
|
90
|
+
|
|
63
91
|
export default {
|
|
64
92
|
name: 'PageTwoColShell',
|
|
65
93
|
components: { kButton },
|
|
@@ -85,6 +113,13 @@ export default {
|
|
|
85
113
|
default: '300px',
|
|
86
114
|
},
|
|
87
115
|
},
|
|
116
|
+
data() {
|
|
117
|
+
return {
|
|
118
|
+
isDesktopViewport: true,
|
|
119
|
+
mobileSidebarOpen: false,
|
|
120
|
+
viewportQuery: null,
|
|
121
|
+
}
|
|
122
|
+
},
|
|
88
123
|
computed: {
|
|
89
124
|
hasTopSection() {
|
|
90
125
|
return Boolean(
|
|
@@ -105,8 +140,46 @@ export default {
|
|
|
105
140
|
maxWidth: this.sidebarWidth,
|
|
106
141
|
}
|
|
107
142
|
},
|
|
143
|
+
mobileSidebarClass() {
|
|
144
|
+
if (this.isDesktopViewport) return ''
|
|
145
|
+
return 'fixed left-0 top-0 z-20 h-full max-w-[85vw] border-r border-slate-200 bg-slate-100 py-4 shadow-xl shadow-slate-900/20'
|
|
146
|
+
},
|
|
147
|
+
showSidebar() {
|
|
148
|
+
return this.isDesktopViewport || this.mobileSidebarOpen
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
mounted() {
|
|
152
|
+
if (typeof window === 'undefined') return
|
|
153
|
+
this.viewportQuery = window.matchMedia(LG_VIEWPORT_QUERY)
|
|
154
|
+
this.isDesktopViewport = this.viewportQuery.matches
|
|
155
|
+
this.syncSidebarForViewport()
|
|
156
|
+
this.viewportQuery.addEventListener('change', this.handleViewportChange)
|
|
157
|
+
window.addEventListener(MAIN_MENU_TOGGLE_EVENT, this.handleMainMenuToggle)
|
|
158
|
+
},
|
|
159
|
+
beforeUnmount() {
|
|
160
|
+
this.viewportQuery?.removeEventListener?.('change', this.handleViewportChange)
|
|
161
|
+
window.removeEventListener(MAIN_MENU_TOGGLE_EVENT, this.handleMainMenuToggle)
|
|
108
162
|
},
|
|
109
163
|
methods: {
|
|
164
|
+
handleMainMenuToggle(event) {
|
|
165
|
+
if (this.isDesktopViewport) return
|
|
166
|
+
if (event?.detail?.open) {
|
|
167
|
+
this.mobileSidebarOpen = false
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
handleViewportChange(event) {
|
|
171
|
+
this.isDesktopViewport = event.matches
|
|
172
|
+
this.syncSidebarForViewport()
|
|
173
|
+
},
|
|
174
|
+
syncSidebarForViewport() {
|
|
175
|
+
if (!this.isDesktopViewport) {
|
|
176
|
+
this.mobileSidebarOpen = false
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
toggleMobileSidebar() {
|
|
180
|
+
if (this.isDesktopViewport) return
|
|
181
|
+
this.mobileSidebarOpen = !this.mobileSidebarOpen
|
|
182
|
+
},
|
|
110
183
|
openExternalUrl() {
|
|
111
184
|
const target = String(this.externalUrl || '').trim()
|
|
112
185
|
if (!target) return
|
|
@@ -18,6 +18,7 @@ import demoAvatarUrl from '../assets/placeholder-user.jpg'
|
|
|
18
18
|
const FAVORITES_STORAGE_KEY = 'ketekny-ui-sidebar-favorites'
|
|
19
19
|
const MENU_TAB_STORAGE_KEY = 'ketekny-ui-sidebar-menu-tab'
|
|
20
20
|
const LOGO_BASE_URL = 'https://s3.ketekny.gr/public/web-apps/logos'
|
|
21
|
+
const LG_VIEWPORT_QUERY = '(min-width: 1024px)'
|
|
21
22
|
|
|
22
23
|
export default {
|
|
23
24
|
name: 'MultilevelSidebar',
|
|
@@ -86,6 +87,8 @@ export default {
|
|
|
86
87
|
activeMenuTab: 'all',
|
|
87
88
|
query: '',
|
|
88
89
|
collapsed: false,
|
|
90
|
+
isDesktopViewport: true,
|
|
91
|
+
viewportQuery: null,
|
|
89
92
|
openIds: new Set(['projects']),
|
|
90
93
|
favoriteIds: new Set(),
|
|
91
94
|
footerLinks: [
|
|
@@ -232,8 +235,30 @@ export default {
|
|
|
232
235
|
activeMenuTab(value) {
|
|
233
236
|
this.persistActiveMenuTab(value)
|
|
234
237
|
},
|
|
238
|
+
mobileOpen(isOpen) {
|
|
239
|
+
if (isOpen) this.syncCollapsedForViewport()
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
mounted() {
|
|
243
|
+
if (typeof window === 'undefined') return
|
|
244
|
+
this.viewportQuery = window.matchMedia(LG_VIEWPORT_QUERY)
|
|
245
|
+
this.isDesktopViewport = this.viewportQuery.matches
|
|
246
|
+
this.syncCollapsedForViewport()
|
|
247
|
+
this.viewportQuery.addEventListener('change', this.handleViewportChange)
|
|
248
|
+
},
|
|
249
|
+
beforeUnmount() {
|
|
250
|
+
this.viewportQuery?.removeEventListener?.('change', this.handleViewportChange)
|
|
235
251
|
},
|
|
236
252
|
methods: {
|
|
253
|
+
handleViewportChange(event) {
|
|
254
|
+
this.isDesktopViewport = event.matches
|
|
255
|
+
this.syncCollapsedForViewport()
|
|
256
|
+
},
|
|
257
|
+
syncCollapsedForViewport() {
|
|
258
|
+
if (!this.isDesktopViewport) {
|
|
259
|
+
this.collapsed = false
|
|
260
|
+
}
|
|
261
|
+
},
|
|
237
262
|
findMenuItemById(items, id) {
|
|
238
263
|
for (const item of items) {
|
|
239
264
|
if (item.id === id) return item
|
|
@@ -307,6 +332,10 @@ export default {
|
|
|
307
332
|
}
|
|
308
333
|
},
|
|
309
334
|
toggleCollapsed() {
|
|
335
|
+
if (!this.isDesktopViewport) {
|
|
336
|
+
this.collapsed = false
|
|
337
|
+
return
|
|
338
|
+
}
|
|
310
339
|
this.collapsed = !this.collapsed
|
|
311
340
|
},
|
|
312
341
|
toggle(id) {
|
|
@@ -39,9 +39,9 @@
|
|
|
39
39
|
</main>
|
|
40
40
|
|
|
41
41
|
<button
|
|
42
|
-
v-if="!mobileMenuOpen"
|
|
42
|
+
v-if="!mobileMenuOpen && showMobileMenuButton"
|
|
43
43
|
type="button"
|
|
44
|
-
class="fixed bottom-
|
|
44
|
+
class="fixed bottom-0 left-0 z-50 inline-flex size-14 items-center justify-center rounded-tr-2xl transition-colors lg:hidden"
|
|
45
45
|
:class="theme === 'light'
|
|
46
46
|
? 'border border-slate-200 bg-white text-slate-700 shadow-lg shadow-slate-900/10 hover:bg-slate-100'
|
|
47
47
|
: 'bg-sidebar text-white shadow-lg shadow-black/25 hover:bg-sidebar-accent'"
|
|
@@ -56,6 +56,9 @@
|
|
|
56
56
|
import { Menu } from "@lucide/vue";
|
|
57
57
|
import MultilevelSidebar from "./MultilevelSidebar.vue";
|
|
58
58
|
|
|
59
|
+
const LG_VIEWPORT_QUERY = "(min-width: 1024px)";
|
|
60
|
+
const MAIN_MENU_TOGGLE_EVENT = "ketekny:two-col-layout-mobile-menu";
|
|
61
|
+
|
|
59
62
|
export default {
|
|
60
63
|
name: "TwoColLayout",
|
|
61
64
|
components: {
|
|
@@ -105,14 +108,66 @@ export default {
|
|
|
105
108
|
data() {
|
|
106
109
|
return {
|
|
107
110
|
mobileMenuOpen: false,
|
|
111
|
+
desktopViewportQuery: null,
|
|
112
|
+
showMobileMenuButton: true,
|
|
113
|
+
mobileScrollHideTimer: null,
|
|
108
114
|
};
|
|
109
115
|
},
|
|
116
|
+
mounted() {
|
|
117
|
+
if (typeof window === "undefined") return;
|
|
118
|
+
this.desktopViewportQuery = window.matchMedia(LG_VIEWPORT_QUERY);
|
|
119
|
+
this.syncMenuForViewport(this.desktopViewportQuery.matches);
|
|
120
|
+
this.desktopViewportQuery.addEventListener("change", this.handleViewportChange);
|
|
121
|
+
window.addEventListener("scroll", this.handleWindowScroll, { passive: true });
|
|
122
|
+
},
|
|
123
|
+
beforeUnmount() {
|
|
124
|
+
this.desktopViewportQuery?.removeEventListener?.("change", this.handleViewportChange);
|
|
125
|
+
window.removeEventListener("scroll", this.handleWindowScroll);
|
|
126
|
+
if (this.mobileScrollHideTimer) {
|
|
127
|
+
window.clearTimeout(this.mobileScrollHideTimer);
|
|
128
|
+
this.mobileScrollHideTimer = null;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
110
131
|
methods: {
|
|
132
|
+
handleWindowScroll() {
|
|
133
|
+
if (typeof window === "undefined") return;
|
|
134
|
+
if (this.mobileMenuOpen || this.desktopViewportQuery?.matches) return;
|
|
135
|
+
this.showMobileMenuButton = false;
|
|
136
|
+
if (this.mobileScrollHideTimer) {
|
|
137
|
+
window.clearTimeout(this.mobileScrollHideTimer);
|
|
138
|
+
}
|
|
139
|
+
this.mobileScrollHideTimer = window.setTimeout(() => {
|
|
140
|
+
this.showMobileMenuButton = true;
|
|
141
|
+
this.mobileScrollHideTimer = null;
|
|
142
|
+
}, 180);
|
|
143
|
+
},
|
|
144
|
+
emitMobileMenuState() {
|
|
145
|
+
if (typeof window === "undefined") return;
|
|
146
|
+
window.dispatchEvent(
|
|
147
|
+
new CustomEvent(MAIN_MENU_TOGGLE_EVENT, {
|
|
148
|
+
detail: { open: this.mobileMenuOpen },
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
handleViewportChange(event) {
|
|
153
|
+
this.syncMenuForViewport(event.matches);
|
|
154
|
+
},
|
|
155
|
+
syncMenuForViewport(isDesktopViewport) {
|
|
156
|
+
if (!isDesktopViewport) {
|
|
157
|
+
this.mobileMenuOpen = false;
|
|
158
|
+
this.emitMobileMenuState();
|
|
159
|
+
this.showMobileMenuButton = true;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
111
162
|
toggleMenu() {
|
|
112
163
|
this.mobileMenuOpen = !this.mobileMenuOpen;
|
|
164
|
+
this.showMobileMenuButton = !this.mobileMenuOpen;
|
|
165
|
+
this.emitMobileMenuState();
|
|
113
166
|
},
|
|
114
167
|
closeMenu() {
|
|
115
168
|
this.mobileMenuOpen = false;
|
|
169
|
+
this.showMobileMenuButton = true;
|
|
170
|
+
this.emitMobileMenuState();
|
|
116
171
|
},
|
|
117
172
|
handleSelect(id) {
|
|
118
173
|
this.closeMenu();
|