ketekny-ui-kit 1.0.126 → 1.0.128
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/README.md
CHANGED
|
@@ -205,7 +205,7 @@ Components:
|
|
|
205
205
|
- `kSelectButton`, `kTags`, `kSearch`, `kArrayList`, `kList`, `kTextArea`
|
|
206
206
|
- `kDialog`, `kDrawer`
|
|
207
207
|
- `kAppHeader`, `kAppFooter`, `kAppMain`, `kHero`
|
|
208
|
-
- `kSingleColPage`, `
|
|
208
|
+
- `kSingleColPage`, `kMultiColPage`, `kTwoColPage` (backwards-compatible alias), `twoColLayout`, `legacyTwoColLayout`
|
|
209
209
|
|
|
210
210
|
Other exports:
|
|
211
211
|
|
|
@@ -242,4 +242,3 @@ ISC
|
|
|
242
242
|
|
|
243
243
|
- Live demo/docs: https://ui.ketekny.gr
|
|
244
244
|
- npm: https://www.npmjs.com/package/ketekny-ui-kit
|
|
245
|
-
|
package/index.js
CHANGED
|
@@ -40,6 +40,7 @@ import kAppFooter from './src/layout/kAppFooter.vue'
|
|
|
40
40
|
import kAppMain from './src/layout/kAppMain.vue'
|
|
41
41
|
import kHero from './src/layout/kHero.vue'
|
|
42
42
|
import kSingleColPage from './src/layout/kSingleColPage.vue'
|
|
43
|
+
import kMultiColPage from './src/layout/kMultiColPage.vue'
|
|
43
44
|
import kTwoColPage from './src/layout/kTwoColPage.vue'
|
|
44
45
|
import twoColLayout from './src/twoColLayout/components/TwoColLayout.vue'
|
|
45
46
|
import legacyTwoColLayout from './src/layout/twoColLayout.vue'
|
|
@@ -66,7 +67,7 @@ export {
|
|
|
66
67
|
kDialog, kDrawer,
|
|
67
68
|
|
|
68
69
|
// Layout Components
|
|
69
|
-
kAppHeader, kAppFooter, kAppMain, kHero, kSingleColPage, kTwoColPage, twoColLayout, legacyTwoColLayout,
|
|
70
|
+
kAppHeader, kAppFooter, kAppMain, kHero, kSingleColPage, kMultiColPage, kTwoColPage, twoColLayout, legacyTwoColLayout,
|
|
70
71
|
|
|
71
72
|
// Plugins
|
|
72
73
|
toastPlugin, confirmPlugin, alertPlugin, tooltipPlugin, inputDialogPlugin,
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
v-if="showMobileOverlay"
|
|
11
11
|
class="fixed inset-0 z-20 bg-slate-950/40 backdrop-blur-[1px] lg:hidden"
|
|
12
12
|
:style="mobileOverlayStyle"
|
|
13
|
-
@click="
|
|
13
|
+
@click="closeMobileSidebar"
|
|
14
14
|
/>
|
|
15
15
|
|
|
16
16
|
<section
|
|
@@ -59,31 +59,73 @@
|
|
|
59
59
|
<div class="flex min-h-0 flex-1 flex-col overflow-hidden bg-slate-100 py-4">
|
|
60
60
|
<div
|
|
61
61
|
class="lg:hidden"
|
|
62
|
-
:class="mobileSidebarOpen ? 'mx-auto flex w-full max-w-none px-4 pb-3 sm:px-6' : 'fixed
|
|
62
|
+
:class="mobileSidebarOpen ? 'mx-auto flex w-full max-w-none items-center justify-between gap-3 px-4 pb-3 sm:px-6' : 'pointer-events-none fixed inset-x-0 top-1/2 z-20 flex -translate-y-1/2'"
|
|
63
63
|
>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
<button
|
|
65
|
+
v-if="hasLeftSidebar"
|
|
66
|
+
type="button"
|
|
67
|
+
:aria-label="mobileSidebarButtonAriaLabel('left')"
|
|
68
|
+
class="mobile-sidebar-trigger pointer-events-auto"
|
|
69
|
+
:class="mobileSidebarButtonClass('left')"
|
|
70
|
+
@click="toggleMobileSidebar('left')"
|
|
71
|
+
>
|
|
72
|
+
<kIcon
|
|
73
|
+
:name="mobileSidebarIconName('left')"
|
|
74
|
+
size="18"
|
|
75
|
+
/>
|
|
76
|
+
<span v-if="mobileSidebarButtonShowsLabel('left')" class="mobile-sidebar-trigger__label">
|
|
77
|
+
{{ mobileSidebarButtonLabel('left') }}
|
|
78
|
+
</span>
|
|
79
|
+
</button>
|
|
80
|
+
<button
|
|
81
|
+
v-if="hasRightSidebar"
|
|
82
|
+
type="button"
|
|
83
|
+
:aria-label="mobileSidebarButtonAriaLabel('right')"
|
|
84
|
+
class="mobile-sidebar-trigger pointer-events-auto"
|
|
85
|
+
:class="mobileSidebarButtonClass('right')"
|
|
86
|
+
@click="toggleMobileSidebar('right')"
|
|
87
|
+
>
|
|
88
|
+
<kIcon
|
|
89
|
+
:name="mobileSidebarIconName('right')"
|
|
90
|
+
size="18"
|
|
91
|
+
/>
|
|
92
|
+
<span v-if="mobileSidebarButtonShowsLabel('right')" class="mobile-sidebar-trigger__label">
|
|
93
|
+
{{ mobileSidebarButtonLabel('right') }}
|
|
94
|
+
</span>
|
|
95
|
+
</button>
|
|
74
96
|
</div>
|
|
75
97
|
<div class="mx-auto flex min-h-0 w-full max-w-none flex-1 gap-2">
|
|
76
98
|
<aside
|
|
77
|
-
v-if="
|
|
99
|
+
v-if="showLeftSidebar"
|
|
78
100
|
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"
|
|
79
|
-
:class="mobileSidebarClass"
|
|
80
|
-
:style="mobileSidebarStyle"
|
|
101
|
+
:class="mobileSidebarClass('left')"
|
|
102
|
+
:style="mobileSidebarStyle('left')"
|
|
81
103
|
>
|
|
82
|
-
<
|
|
104
|
+
<div v-if="effectiveSidebarLeftTitle" class="pb-3">
|
|
105
|
+
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">
|
|
106
|
+
{{ effectiveSidebarLeftTitle }}
|
|
107
|
+
</p>
|
|
108
|
+
</div>
|
|
109
|
+
<slot name="sidebar-left" v-bind="sidebarSlotProps('left')">
|
|
110
|
+
<slot name="sidebar" v-bind="sidebarSlotProps('left')" />
|
|
111
|
+
</slot>
|
|
83
112
|
</aside>
|
|
84
113
|
<section class="min-h-0 min-w-0 flex-1 overflow-y-auto px-2">
|
|
85
114
|
<slot name="content" />
|
|
86
115
|
</section>
|
|
116
|
+
<aside
|
|
117
|
+
v-if="showRightSidebar"
|
|
118
|
+
class="min-h-0 shrink-0 overflow-y-auto bg-slate-100 px-4 sm:px-6 lg:mr-8 lg:pl-4 lg:pr-0"
|
|
119
|
+
:class="mobileSidebarClass('right')"
|
|
120
|
+
:style="mobileSidebarStyle('right')"
|
|
121
|
+
>
|
|
122
|
+
<div v-if="sidebarRightTitle" class="pb-3">
|
|
123
|
+
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-slate-500">
|
|
124
|
+
{{ sidebarRightTitle }}
|
|
125
|
+
</p>
|
|
126
|
+
</div>
|
|
127
|
+
<slot name="sidebar-right" v-bind="sidebarSlotProps('right')" />
|
|
128
|
+
</aside>
|
|
87
129
|
</div>
|
|
88
130
|
</div>
|
|
89
131
|
</main>
|
|
@@ -92,6 +134,7 @@
|
|
|
92
134
|
<script>
|
|
93
135
|
import packageJson from '../../package.json'
|
|
94
136
|
import kButton from '../ui/kButton.vue'
|
|
137
|
+
import kIcon from '../ui/kIcon.vue'
|
|
95
138
|
|
|
96
139
|
const LG_VIEWPORT_QUERY = '(min-width: 1024px)'
|
|
97
140
|
const MAIN_MENU_TOGGLE_EVENT = 'ketekny:two-col-layout-mobile-menu'
|
|
@@ -100,8 +143,8 @@ const MOBILE_SWIPE_OPEN_DISTANCE = 52
|
|
|
100
143
|
const MOBILE_SWIPE_VERTICAL_TOLERANCE = 48
|
|
101
144
|
|
|
102
145
|
export default {
|
|
103
|
-
name: '
|
|
104
|
-
components: { kButton },
|
|
146
|
+
name: 'MultiColPage',
|
|
147
|
+
components: { kButton, kIcon },
|
|
105
148
|
props: {
|
|
106
149
|
appTitle: {
|
|
107
150
|
type: String,
|
|
@@ -123,13 +166,35 @@ export default {
|
|
|
123
166
|
type: String,
|
|
124
167
|
default: '300px',
|
|
125
168
|
},
|
|
169
|
+
sidebarLeftWidth: {
|
|
170
|
+
type: String,
|
|
171
|
+
default: '',
|
|
172
|
+
},
|
|
173
|
+
sidebarTitle: {
|
|
174
|
+
type: String,
|
|
175
|
+
default: '',
|
|
176
|
+
},
|
|
177
|
+
sidebarLeftTitle: {
|
|
178
|
+
type: String,
|
|
179
|
+
default: '',
|
|
180
|
+
},
|
|
181
|
+
sidebarRightWidth: {
|
|
182
|
+
type: String,
|
|
183
|
+
default: '',
|
|
184
|
+
},
|
|
185
|
+
sidebarRightTitle: {
|
|
186
|
+
type: String,
|
|
187
|
+
default: '',
|
|
188
|
+
},
|
|
126
189
|
},
|
|
127
190
|
data() {
|
|
128
191
|
return {
|
|
129
192
|
isDesktopViewport: true,
|
|
130
193
|
mobileSidebarOpen: false,
|
|
194
|
+
mobileSidebarSide: 'left',
|
|
131
195
|
viewportQuery: null,
|
|
132
196
|
touchGesture: {
|
|
197
|
+
side: 'left',
|
|
133
198
|
startX: 0,
|
|
134
199
|
startY: 0,
|
|
135
200
|
currentX: 0,
|
|
@@ -151,33 +216,39 @@ export default {
|
|
|
151
216
|
if (!target) return ''
|
|
152
217
|
return target.replace(/^https?:\/\//i, '').replace(/\/$/, '')
|
|
153
218
|
},
|
|
154
|
-
|
|
219
|
+
hasLeftSidebar() {
|
|
220
|
+
return Boolean(this.$slots['sidebar-left'] || this.$slots.sidebar)
|
|
221
|
+
},
|
|
222
|
+
hasRightSidebar() {
|
|
223
|
+
return Boolean(this.$slots['sidebar-right'])
|
|
224
|
+
},
|
|
225
|
+
effectiveSidebarLeftWidth() {
|
|
226
|
+
return this.sidebarLeftWidth || this.sidebarWidth || '300px'
|
|
227
|
+
},
|
|
228
|
+
effectiveSidebarLeftTitle() {
|
|
229
|
+
return this.sidebarLeftTitle || this.sidebarTitle
|
|
230
|
+
},
|
|
231
|
+
effectiveSidebarRightWidth() {
|
|
232
|
+
return this.sidebarRightWidth || this.effectiveSidebarLeftWidth
|
|
233
|
+
},
|
|
234
|
+
sidebarLeftStyle() {
|
|
155
235
|
return {
|
|
156
|
-
minWidth: this.
|
|
157
|
-
maxWidth: this.
|
|
236
|
+
minWidth: this.effectiveSidebarLeftWidth,
|
|
237
|
+
maxWidth: this.effectiveSidebarLeftWidth,
|
|
158
238
|
}
|
|
159
239
|
},
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
240
|
+
sidebarRightStyle() {
|
|
241
|
+
return {
|
|
242
|
+
minWidth: this.effectiveSidebarRightWidth,
|
|
243
|
+
maxWidth: this.effectiveSidebarRightWidth,
|
|
244
|
+
}
|
|
163
245
|
},
|
|
164
246
|
dragOffset() {
|
|
165
247
|
if (!this.touchGesture.trackingEdgeSwipe) return 0
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
mobileSidebarStyle() {
|
|
169
|
-
const baseStyle = this.sidebarStyle
|
|
170
|
-
if (this.isDesktopViewport || this.mobileSidebarOpen || !this.touchGesture.trackingEdgeSwipe) {
|
|
171
|
-
return baseStyle
|
|
172
|
-
}
|
|
173
|
-
const widthValue = Number.parseFloat(this.sidebarWidth)
|
|
174
|
-
const effectiveWidth = Number.isFinite(widthValue) ? widthValue : 300
|
|
175
|
-
const clampedOffset = Math.min(this.dragOffset, effectiveWidth)
|
|
176
|
-
return {
|
|
177
|
-
...baseStyle,
|
|
178
|
-
transform: `translateX(${clampedOffset - effectiveWidth}px)`,
|
|
179
|
-
transition: 'none',
|
|
248
|
+
if (this.touchGesture.side === 'right') {
|
|
249
|
+
return Math.max(0, this.touchGesture.startX - this.touchGesture.currentX)
|
|
180
250
|
}
|
|
251
|
+
return Math.max(0, this.touchGesture.currentX - this.touchGesture.startX)
|
|
181
252
|
},
|
|
182
253
|
showMobileOverlay() {
|
|
183
254
|
return !this.isDesktopViewport && (this.mobileSidebarOpen || this.touchGesture.trackingEdgeSwipe)
|
|
@@ -189,8 +260,13 @@ export default {
|
|
|
189
260
|
opacity: String(progress),
|
|
190
261
|
}
|
|
191
262
|
},
|
|
192
|
-
|
|
193
|
-
|
|
263
|
+
showLeftSidebar() {
|
|
264
|
+
if (!this.hasLeftSidebar) return false
|
|
265
|
+
return this.showSidebarForSide('left')
|
|
266
|
+
},
|
|
267
|
+
showRightSidebar() {
|
|
268
|
+
if (!this.hasRightSidebar) return false
|
|
269
|
+
return this.showSidebarForSide('right')
|
|
194
270
|
},
|
|
195
271
|
},
|
|
196
272
|
mounted() {
|
|
@@ -206,10 +282,101 @@ export default {
|
|
|
206
282
|
window.removeEventListener(MAIN_MENU_TOGGLE_EVENT, this.handleMainMenuToggle)
|
|
207
283
|
},
|
|
208
284
|
methods: {
|
|
285
|
+
sidebarSlotProps(side) {
|
|
286
|
+
return {
|
|
287
|
+
side,
|
|
288
|
+
title: side === 'right' ? this.sidebarRightTitle : this.effectiveSidebarLeftTitle,
|
|
289
|
+
isDesktopViewport: this.isDesktopViewport,
|
|
290
|
+
mobileSidebarOpen: this.mobileSidebarOpen,
|
|
291
|
+
mobileSidebarSide: this.mobileSidebarSide,
|
|
292
|
+
isMobileSidebarActive: this.mobileSidebarOpen && this.mobileSidebarSide === side,
|
|
293
|
+
toggleMobileSidebar: () => this.toggleMobileSidebar(side),
|
|
294
|
+
closeMobileSidebar: this.closeMobileSidebar,
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
showSidebarForSide(side) {
|
|
298
|
+
if (this.isDesktopViewport) return true
|
|
299
|
+
return (
|
|
300
|
+
(this.mobileSidebarOpen && this.mobileSidebarSide === side) ||
|
|
301
|
+
(this.touchGesture.trackingEdgeSwipe && this.touchGesture.side === side)
|
|
302
|
+
)
|
|
303
|
+
},
|
|
304
|
+
mobileSidebarClass(side) {
|
|
305
|
+
if (this.isDesktopViewport) return ''
|
|
306
|
+
const horizontalClass = side === 'right'
|
|
307
|
+
? 'fixed right-0 top-0 border-l border-slate-200'
|
|
308
|
+
: 'fixed left-0 top-0 border-r border-slate-200'
|
|
309
|
+
return `${horizontalClass} z-20 h-full max-w-[85vw] bg-slate-100 py-4 shadow-xl shadow-slate-900/20 transition-transform duration-200 ease-out`
|
|
310
|
+
},
|
|
311
|
+
mobileSidebarStyle(side) {
|
|
312
|
+
const baseStyle = side === 'right' ? this.sidebarRightStyle : this.sidebarLeftStyle
|
|
313
|
+
if (
|
|
314
|
+
this.isDesktopViewport ||
|
|
315
|
+
this.mobileSidebarOpen ||
|
|
316
|
+
!this.touchGesture.trackingEdgeSwipe ||
|
|
317
|
+
this.touchGesture.side !== side
|
|
318
|
+
) {
|
|
319
|
+
return baseStyle
|
|
320
|
+
}
|
|
321
|
+
const widthValue = Number.parseFloat(
|
|
322
|
+
side === 'right' ? this.effectiveSidebarRightWidth : this.effectiveSidebarLeftWidth
|
|
323
|
+
)
|
|
324
|
+
const effectiveWidth = Number.isFinite(widthValue) ? widthValue : 300
|
|
325
|
+
const clampedOffset = Math.min(this.dragOffset, effectiveWidth)
|
|
326
|
+
const hiddenOffset = effectiveWidth - clampedOffset
|
|
327
|
+
const direction = side === 'right' ? hiddenOffset : clampedOffset - effectiveWidth
|
|
328
|
+
return {
|
|
329
|
+
...baseStyle,
|
|
330
|
+
transform: `translateX(${direction}px)`,
|
|
331
|
+
transition: 'none',
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
mobileSidebarButtonClass(side) {
|
|
335
|
+
if (this.mobileSidebarOpen) {
|
|
336
|
+
return side === 'right' ? 'ml-auto' : ''
|
|
337
|
+
}
|
|
338
|
+
const title = this.sidebarTitleForSide(side)
|
|
339
|
+
const edgeClass = side === 'right'
|
|
340
|
+
? 'ml-auto rounded-l-xl rounded-r-none border-r-0'
|
|
341
|
+
: 'rounded-l-none rounded-r-xl border-l-0'
|
|
342
|
+
if (title) {
|
|
343
|
+
return `${edgeClass} mobile-sidebar-trigger--titled min-h-0 min-w-0 px-2 py-3`
|
|
344
|
+
}
|
|
345
|
+
return side === 'right'
|
|
346
|
+
? 'ml-auto rounded-full'
|
|
347
|
+
: 'rounded-full'
|
|
348
|
+
},
|
|
349
|
+
sidebarTitleForSide(side) {
|
|
350
|
+
return side === 'right' ? this.sidebarRightTitle : this.effectiveSidebarLeftTitle
|
|
351
|
+
},
|
|
352
|
+
mobileSidebarButtonLabel(side) {
|
|
353
|
+
if (this.mobileSidebarOpen && this.mobileSidebarSide === side) {
|
|
354
|
+
return 'Hide sidebar'
|
|
355
|
+
}
|
|
356
|
+
return this.sidebarTitleForSide(side) || 'Show sidebar'
|
|
357
|
+
},
|
|
358
|
+
mobileSidebarButtonAriaLabel(side) {
|
|
359
|
+
if (this.mobileSidebarOpen && this.mobileSidebarSide === side) return null
|
|
360
|
+
return this.sidebarTitleForSide(side) || 'Show sidebar'
|
|
361
|
+
},
|
|
362
|
+
mobileSidebarButtonShowsLabel(side) {
|
|
363
|
+
if (this.mobileSidebarOpen && this.mobileSidebarSide === side) return true
|
|
364
|
+
return Boolean(this.sidebarTitleForSide(side))
|
|
365
|
+
},
|
|
366
|
+
mobileSidebarIconName(side) {
|
|
367
|
+
if (side === 'right') {
|
|
368
|
+
return this.mobileSidebarOpen && this.mobileSidebarSide === side
|
|
369
|
+
? 'PanelRightClose'
|
|
370
|
+
: 'PanelRightOpen'
|
|
371
|
+
}
|
|
372
|
+
return this.mobileSidebarOpen && this.mobileSidebarSide === side
|
|
373
|
+
? 'PanelLeftClose'
|
|
374
|
+
: 'PanelLeftOpen'
|
|
375
|
+
},
|
|
209
376
|
handleMainMenuToggle(event) {
|
|
210
377
|
if (this.isDesktopViewport) return
|
|
211
378
|
if (event?.detail?.open) {
|
|
212
|
-
this.
|
|
379
|
+
this.closeMobileSidebar()
|
|
213
380
|
}
|
|
214
381
|
},
|
|
215
382
|
handleViewportChange(event) {
|
|
@@ -218,13 +385,22 @@ export default {
|
|
|
218
385
|
},
|
|
219
386
|
syncSidebarForViewport() {
|
|
220
387
|
if (!this.isDesktopViewport) {
|
|
221
|
-
this.
|
|
222
|
-
this.resetTouchState()
|
|
388
|
+
this.closeMobileSidebar()
|
|
223
389
|
}
|
|
224
390
|
},
|
|
225
|
-
toggleMobileSidebar() {
|
|
391
|
+
toggleMobileSidebar(side = 'left') {
|
|
226
392
|
if (this.isDesktopViewport) return
|
|
227
|
-
this.
|
|
393
|
+
if ((side === 'left' && !this.hasLeftSidebar) || (side === 'right' && !this.hasRightSidebar)) return
|
|
394
|
+
if (this.mobileSidebarOpen && this.mobileSidebarSide === side) {
|
|
395
|
+
this.mobileSidebarOpen = false
|
|
396
|
+
} else {
|
|
397
|
+
this.mobileSidebarOpen = true
|
|
398
|
+
this.mobileSidebarSide = side
|
|
399
|
+
}
|
|
400
|
+
this.resetTouchState()
|
|
401
|
+
},
|
|
402
|
+
closeMobileSidebar() {
|
|
403
|
+
this.mobileSidebarOpen = false
|
|
228
404
|
this.resetTouchState()
|
|
229
405
|
},
|
|
230
406
|
handleTouchStart(event) {
|
|
@@ -234,11 +410,19 @@ export default {
|
|
|
234
410
|
}
|
|
235
411
|
const touch = event.touches?.[0]
|
|
236
412
|
if (!touch) return
|
|
413
|
+
const viewportWidth = typeof window === 'undefined' ? 0 : window.innerWidth
|
|
414
|
+
const isLeftEdge = this.hasLeftSidebar && touch.clientX <= MOBILE_EDGE_SWIPE_THRESHOLD
|
|
415
|
+
const isRightEdge = this.hasRightSidebar && viewportWidth - touch.clientX <= MOBILE_EDGE_SWIPE_THRESHOLD
|
|
416
|
+
if (!isLeftEdge && !isRightEdge) {
|
|
417
|
+
this.resetTouchState()
|
|
418
|
+
return
|
|
419
|
+
}
|
|
237
420
|
this.touchGesture = {
|
|
421
|
+
side: isRightEdge ? 'right' : 'left',
|
|
238
422
|
startX: touch.clientX,
|
|
239
423
|
startY: touch.clientY,
|
|
240
424
|
currentX: touch.clientX,
|
|
241
|
-
trackingEdgeSwipe:
|
|
425
|
+
trackingEdgeSwipe: true,
|
|
242
426
|
}
|
|
243
427
|
},
|
|
244
428
|
handleTouchMove(event) {
|
|
@@ -255,14 +439,18 @@ export default {
|
|
|
255
439
|
this.resetTouchState()
|
|
256
440
|
return
|
|
257
441
|
}
|
|
258
|
-
const deltaX = this.touchGesture.
|
|
442
|
+
const deltaX = this.touchGesture.side === 'right'
|
|
443
|
+
? this.touchGesture.startX - this.touchGesture.currentX
|
|
444
|
+
: this.touchGesture.currentX - this.touchGesture.startX
|
|
259
445
|
if (deltaX >= MOBILE_SWIPE_OPEN_DISTANCE) {
|
|
260
446
|
this.mobileSidebarOpen = true
|
|
447
|
+
this.mobileSidebarSide = this.touchGesture.side
|
|
261
448
|
}
|
|
262
449
|
this.resetTouchState()
|
|
263
450
|
},
|
|
264
451
|
resetTouchState() {
|
|
265
452
|
this.touchGesture = {
|
|
453
|
+
side: 'left',
|
|
266
454
|
startX: 0,
|
|
267
455
|
startY: 0,
|
|
268
456
|
currentX: 0,
|
|
@@ -277,3 +465,56 @@ export default {
|
|
|
277
465
|
},
|
|
278
466
|
}
|
|
279
467
|
</script>
|
|
468
|
+
|
|
469
|
+
<style scoped>
|
|
470
|
+
.mobile-sidebar-trigger {
|
|
471
|
+
display: inline-flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
justify-content: center;
|
|
474
|
+
gap: 0.5rem;
|
|
475
|
+
min-height: 3.5rem;
|
|
476
|
+
padding: 0.875rem;
|
|
477
|
+
border: 1px solid rgb(203 213 225);
|
|
478
|
+
background: rgb(239 246 255);
|
|
479
|
+
color: rgb(15 23 42);
|
|
480
|
+
box-shadow: 0 10px 25px -15px rgba(15, 23, 42, 0.45);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.mobile-sidebar-trigger--titled {
|
|
484
|
+
flex-direction: column;
|
|
485
|
+
width: 3rem;
|
|
486
|
+
min-height: 8.5rem;
|
|
487
|
+
padding: 0.75rem 0.35rem;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.mobile-sidebar-trigger:focus-visible {
|
|
491
|
+
outline: 2px solid rgb(37 99 235);
|
|
492
|
+
outline-offset: 2px;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.mobile-sidebar-trigger__label {
|
|
496
|
+
display: inline-block;
|
|
497
|
+
font-size: 0.625rem;
|
|
498
|
+
font-weight: 700;
|
|
499
|
+
letter-spacing: 0.08em;
|
|
500
|
+
text-transform: uppercase;
|
|
501
|
+
white-space: nowrap;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.mobile-sidebar-trigger--titled .mobile-sidebar-trigger__label {
|
|
505
|
+
writing-mode: vertical-rl;
|
|
506
|
+
text-orientation: mixed;
|
|
507
|
+
line-height: 1;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.mobile-sidebar-trigger--titled :deep(svg) {
|
|
511
|
+
width: 0.9rem;
|
|
512
|
+
height: 0.9rem;
|
|
513
|
+
flex-shrink: 0;
|
|
514
|
+
display: block;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.mobile-sidebar-trigger--titled.rounded-l-none .mobile-sidebar-trigger__label {
|
|
518
|
+
transform: rotate(180deg);
|
|
519
|
+
}
|
|
520
|
+
</style>
|
|
@@ -639,6 +639,9 @@ export default {
|
|
|
639
639
|
>
|
|
640
640
|
Δεν βρέθηκαν αποτελέσματα.
|
|
641
641
|
</p>
|
|
642
|
+
<div v-if="!collapsed && $slots['sidebar-panel']" class="px-1 pt-3">
|
|
643
|
+
<slot name="sidebar-panel" />
|
|
644
|
+
</div>
|
|
642
645
|
</nav>
|
|
643
646
|
|
|
644
647
|
<div class="mt-auto border-t border-[var(--sidebar-border)] px-3 pb-3 pt-4">
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
<template v-if="$slots['sidebar-header']" #sidebar-header>
|
|
27
27
|
<slot name="sidebar-header" />
|
|
28
28
|
</template>
|
|
29
|
+
<template v-if="$slots['sidebar-panel']" #sidebar-panel>
|
|
30
|
+
<slot name="sidebar-panel" />
|
|
31
|
+
</template>
|
|
29
32
|
</MultilevelSidebar>
|
|
30
33
|
|
|
31
34
|
<main class="flex min-h-0 flex-1 flex-col overflow-hidden">
|