@mptw/skylens-ui 1.4.34 → 1.4.36
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/app.vue +2 -4
- package/components/SLProfileButton.vue +10 -1
- package/components/SLSidebarNav.vue +263 -0
- package/components/SLSidebarNavGroupItem.vue +37 -0
- package/components/SLSidebarNavGroupItemSection.vue +435 -0
- package/components/SLSidebarNavLinkItem.vue +255 -0
- package/components/SLSitePage.vue +2 -1
- package/package.json +4 -4
- package/tailwind.config.js +2 -2
- package/components/SLSiderbarNav.vue +0 -394
- package/components/SLSiderbarNavItem.vue +0 -204
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
<template lang="pug">
|
|
2
|
+
.sidebar-nav-group-item-section(:class='{ collapsed }')
|
|
3
|
+
.sidebar-nav-group-item-section-header(
|
|
4
|
+
:class='{ "router-link-active": isLinkActive, "router-link-exact-active": isLinkExactActive, active: actived }'
|
|
5
|
+
ref='headerEl'
|
|
6
|
+
@mouseenter='onIconMouseEnter',
|
|
7
|
+
@mouseleave='onIconMouseLeave'
|
|
8
|
+
)
|
|
9
|
+
NuxtLink.sidebar-nav-group-item-section-header-left(
|
|
10
|
+
:to='itemConfig.to ? itemConfig.to : "javascript:;"',
|
|
11
|
+
:external='!itemConfig.to',
|
|
12
|
+
@mousedown='onMouseDownLink',
|
|
13
|
+
@mouseup='onMouseUpLink',
|
|
14
|
+
@click='onClickedSectionItem'
|
|
15
|
+
)
|
|
16
|
+
SLIcon(:icon='itemConfig.preIcon')
|
|
17
|
+
.sidebar-nav-group-item-section-header-right
|
|
18
|
+
NuxtLink.sidebar-nav-group-section-title(
|
|
19
|
+
:to='itemConfig.to ? itemConfig.to : "javascript:;"',
|
|
20
|
+
:external='!itemConfig.to',
|
|
21
|
+
@mousedown='onMouseDownLink',
|
|
22
|
+
@mouseup='onMouseUpLink',
|
|
23
|
+
@click='onClickedSectionItem'
|
|
24
|
+
) {{ itemConfig.title }}
|
|
25
|
+
.sidebar-nav-group-item-section-status
|
|
26
|
+
a.sidebar-nav-group-item-section-toggle-btn(
|
|
27
|
+
v-if='itemConfig.status === "normal"'
|
|
28
|
+
href='javascript:;'
|
|
29
|
+
@mousedown='onMouseDownToggle',
|
|
30
|
+
@mouseup='onMouseUpLink',
|
|
31
|
+
@click='onClickedToggle'
|
|
32
|
+
)
|
|
33
|
+
SLIcon(icon='sort-down')
|
|
34
|
+
.sidebar-nav-group-item-section-body(ref='bodyEl')
|
|
35
|
+
.sidebar-nav-group-item-section-body-wrapper
|
|
36
|
+
NuxtLink.sidebar-nav-group-item-section-link(
|
|
37
|
+
v-for='(link, i) in itemConfig.links'
|
|
38
|
+
:to='link.to ? link.to : "javascript:;"',
|
|
39
|
+
:external='!link.to',
|
|
40
|
+
:key='link.code'
|
|
41
|
+
@click='onClickedLinkItem(link.emit)'
|
|
42
|
+
) {{ link.title }}
|
|
43
|
+
ClientOnly
|
|
44
|
+
Teleport(to='#nav')
|
|
45
|
+
.sidebar-nav-group-item-section-popup(
|
|
46
|
+
:class='{ single: itemConfig.links.length === 0 }'
|
|
47
|
+
ref='popupEl'
|
|
48
|
+
@mouseenter='onPopupMouseEnter'
|
|
49
|
+
@mouseleave='onIconMouseLeave'
|
|
50
|
+
)
|
|
51
|
+
NuxtLink.sidebar-nav-group-item-section-popup-link(
|
|
52
|
+
:to='itemConfig.to ? itemConfig.to : "javascript:;"',
|
|
53
|
+
:external='!itemConfig.to',
|
|
54
|
+
@click='onClickedSectionItem'
|
|
55
|
+
)
|
|
56
|
+
.sidebar-nav-group-item-section-popup-link-title {{ itemConfig.title }}
|
|
57
|
+
.sidebar-nav-group-item-section-popup-link-icon(v-if='itemConfig.status === "plus"')
|
|
58
|
+
SLIcon(icon='add')
|
|
59
|
+
.sidebar-nav-group-item-section-popup-divide(v-if='itemConfig.links.length > 0')
|
|
60
|
+
NuxtLink.sidebar-nav-group-item-section-popup-link(
|
|
61
|
+
v-for='(link, i) in itemConfig.links'
|
|
62
|
+
:to='link.to ? link.to : "javascript:;"',
|
|
63
|
+
:external='!link.to',
|
|
64
|
+
:key='link.code'
|
|
65
|
+
@click='onClickedLinkItem(link.emit)'
|
|
66
|
+
)
|
|
67
|
+
.sidebar-nav-group-item-section-popup-link-title {{ link.title }}
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<script lang="ts">
|
|
71
|
+
import { fromEvent, Subscription } from 'rxjs';
|
|
72
|
+
import gsap from 'gsap';
|
|
73
|
+
import { createPopper } from '@popperjs/core';
|
|
74
|
+
import type { Instance as PopperInstance } from '@popperjs/core';
|
|
75
|
+
import { NavItemStatus, type NavItemSection } from '~/interface/NavConfigType';
|
|
76
|
+
|
|
77
|
+
export default defineComponent({
|
|
78
|
+
name: 'SLSidebarNavGroupItemSection',
|
|
79
|
+
props: {
|
|
80
|
+
navCollapsed: {
|
|
81
|
+
type: Boolean,
|
|
82
|
+
default: false,
|
|
83
|
+
},
|
|
84
|
+
itemConfig: {
|
|
85
|
+
type: Object as PropType<NavItemSection>,
|
|
86
|
+
required: true,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
emits: ['click:item'],
|
|
90
|
+
setup(props, { emit }) {
|
|
91
|
+
const route = useRoute();
|
|
92
|
+
|
|
93
|
+
const { navCollapsed, itemConfig } = toRefs(props);
|
|
94
|
+
|
|
95
|
+
const headerEl = shallowRef<HTMLDivElement | null>(null);
|
|
96
|
+
const bodyEl = shallowRef<HTMLDivElement | null>(null);
|
|
97
|
+
const popupEl = shallowRef<HTMLDivElement | null>(null);
|
|
98
|
+
const collapsed = ref(false);
|
|
99
|
+
const actived = ref(false);
|
|
100
|
+
|
|
101
|
+
let windowDragEndSubscription: Subscription | null = null;
|
|
102
|
+
let collapseTween: GSAPTween | null = null;
|
|
103
|
+
let popperInstance: PopperInstance | null = null;
|
|
104
|
+
let popupHideTimeoutId: any = null;
|
|
105
|
+
|
|
106
|
+
const isLinkActive = computed(() => {
|
|
107
|
+
if (!itemConfig.value.to) return false;
|
|
108
|
+
|
|
109
|
+
return route.path.includes(itemConfig.value.to);
|
|
110
|
+
});
|
|
111
|
+
const isLinkExactActive = computed(() => {
|
|
112
|
+
if (!itemConfig.value.to) return false;
|
|
113
|
+
|
|
114
|
+
if (itemConfig.value.code === 'M01') {
|
|
115
|
+
return ['/application/web', '/application/web/create'].includes(route.path);
|
|
116
|
+
} else if (itemConfig.value.code === 'M02') {
|
|
117
|
+
return ['/application/poi', '/application/poi/create'].includes(route.path);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return route.path === itemConfig.value.to;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
function onClickedSectionItem() {
|
|
124
|
+
if (itemConfig.value.to || !itemConfig.value.emit) return;
|
|
125
|
+
|
|
126
|
+
emit('click:item', itemConfig.value.emit);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function onClickedToggle() {
|
|
130
|
+
if (!collapsed.value && isLinkActive.value && !isLinkExactActive.value) return;
|
|
131
|
+
collapsed.value = !collapsed.value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function onMouseDownLink() {
|
|
135
|
+
actived.value = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function onMouseUpLink() {
|
|
139
|
+
actived.value = false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function onMouseDownToggle() {
|
|
143
|
+
if (itemConfig.value.status === NavItemStatus.Plus) {
|
|
144
|
+
actived.value = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function onClickedLinkItem(emitEvent?: string) {
|
|
149
|
+
if (emitEvent) {
|
|
150
|
+
emit('click:item', emitEvent);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function destroyWindowDragEndSubscription() {
|
|
155
|
+
if (windowDragEndSubscription) {
|
|
156
|
+
windowDragEndSubscription.unsubscribe();
|
|
157
|
+
}
|
|
158
|
+
windowDragEndSubscription = null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function clearTween() {
|
|
162
|
+
if (collapseTween) {
|
|
163
|
+
collapseTween.kill();
|
|
164
|
+
}
|
|
165
|
+
collapseTween = null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function onCollapse() {
|
|
169
|
+
clearTween();
|
|
170
|
+
|
|
171
|
+
const bodyWrapper = bodyEl.value?.querySelector('.sidebar-nav-group-item-section-body-wrapper');
|
|
172
|
+
if (!bodyEl.value || !bodyWrapper) return;
|
|
173
|
+
|
|
174
|
+
const wrapperRect = bodyWrapper.getBoundingClientRect();
|
|
175
|
+
collapseTween = gsap.to(bodyEl.value, {
|
|
176
|
+
height: 0,
|
|
177
|
+
duration: 0.3,
|
|
178
|
+
ease: 'power2.out',
|
|
179
|
+
onStart: () => {
|
|
180
|
+
if (!bodyEl.value) return;
|
|
181
|
+
bodyEl.value.style.height = `${wrapperRect.height + 4}px`;
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function onExpand() {
|
|
187
|
+
clearTween();
|
|
188
|
+
|
|
189
|
+
const bodyWrapper = bodyEl.value?.querySelector('.sidebar-nav-group-item-section-body-wrapper');
|
|
190
|
+
if (!bodyEl.value || !bodyWrapper) return;
|
|
191
|
+
|
|
192
|
+
const wrapperRect = bodyWrapper.getBoundingClientRect();
|
|
193
|
+
collapseTween = gsap.to(bodyEl.value, {
|
|
194
|
+
height: wrapperRect.height + 4,
|
|
195
|
+
duration: 0.3,
|
|
196
|
+
ease: 'power2.out',
|
|
197
|
+
onComplete: () => {
|
|
198
|
+
if (!bodyEl.value) return;
|
|
199
|
+
bodyEl.value.removeAttribute('style');
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function showPopup() {
|
|
205
|
+
if (
|
|
206
|
+
!headerEl.value ||
|
|
207
|
+
!popupEl.value ||
|
|
208
|
+
popupEl.value.hasAttribute('data-show') ||
|
|
209
|
+
!popperInstance
|
|
210
|
+
)
|
|
211
|
+
return;
|
|
212
|
+
|
|
213
|
+
popupEl.value.setAttribute('data-show', '');
|
|
214
|
+
popperInstance.setOptions((options) => ({
|
|
215
|
+
...options,
|
|
216
|
+
modifiers: [
|
|
217
|
+
...(options.modifiers || []),
|
|
218
|
+
{ name: 'eventListeners', enabled: true },
|
|
219
|
+
],
|
|
220
|
+
}));
|
|
221
|
+
popperInstance.update();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function clearDisappearTimeout() {
|
|
225
|
+
if (popupHideTimeoutId) {
|
|
226
|
+
clearTimeout(popupHideTimeoutId);
|
|
227
|
+
}
|
|
228
|
+
popupHideTimeoutId = null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function onIconMouseEnter() {
|
|
232
|
+
clearDisappearTimeout();
|
|
233
|
+
|
|
234
|
+
if (!navCollapsed.value) return;
|
|
235
|
+
|
|
236
|
+
showPopup();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function hidePopup() {
|
|
240
|
+
if (!popupEl.value || !popperInstance) return;
|
|
241
|
+
|
|
242
|
+
popupEl.value.removeAttribute('data-show');
|
|
243
|
+
popperInstance.setOptions((options) => ({
|
|
244
|
+
...options,
|
|
245
|
+
modifiers: [
|
|
246
|
+
...(options.modifiers || []),
|
|
247
|
+
{ name: 'eventListeners', enabled: false },
|
|
248
|
+
],
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function onIconMouseLeave() {
|
|
253
|
+
clearDisappearTimeout();
|
|
254
|
+
|
|
255
|
+
popupHideTimeoutId = setTimeout(hidePopup, 300);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function onPopupMouseEnter() {
|
|
259
|
+
clearDisappearTimeout();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function setupPopper() {
|
|
263
|
+
nextTick(() => {
|
|
264
|
+
if (!headerEl.value || !popupEl.value) return;
|
|
265
|
+
popperInstance = createPopper(headerEl.value, popupEl.value, {
|
|
266
|
+
placement: 'right-start',
|
|
267
|
+
strategy: 'fixed',
|
|
268
|
+
modifiers: [
|
|
269
|
+
{
|
|
270
|
+
name: 'offset',
|
|
271
|
+
options: {
|
|
272
|
+
offset: [0, 12],
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
{ name: 'eventListeners', enabled: false },
|
|
276
|
+
],
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function destroyPopper() {
|
|
282
|
+
if (popperInstance) {
|
|
283
|
+
popperInstance.destroy();
|
|
284
|
+
}
|
|
285
|
+
popperInstance = null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
watch(collapsed, () => {
|
|
289
|
+
if (collapsed.value) {
|
|
290
|
+
onCollapse();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
onExpand();
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
watch(isLinkActive, () => {
|
|
298
|
+
if (navCollapsed.value) return;
|
|
299
|
+
|
|
300
|
+
collapsed.value = !isLinkActive.value;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
watch(navCollapsed, () => {
|
|
304
|
+
if (navCollapsed.value) {
|
|
305
|
+
collapsed.value = true;
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
collapsed.value = !isLinkActive.value;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
onMounted(() => {
|
|
313
|
+
collapsed.value = !isLinkActive.value;
|
|
314
|
+
|
|
315
|
+
destroyWindowDragEndSubscription();
|
|
316
|
+
windowDragEndSubscription = fromEvent(window, 'dragend').subscribe((e) => {
|
|
317
|
+
actived.value = false;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
setupPopper();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
onBeforeUnmount(() => {
|
|
324
|
+
destroyWindowDragEndSubscription();
|
|
325
|
+
clearDisappearTimeout();
|
|
326
|
+
destroyPopper();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
headerEl,
|
|
331
|
+
bodyEl,
|
|
332
|
+
popupEl,
|
|
333
|
+
collapsed,
|
|
334
|
+
actived,
|
|
335
|
+
isLinkActive,
|
|
336
|
+
isLinkExactActive,
|
|
337
|
+
onClickedSectionItem,
|
|
338
|
+
onClickedToggle,
|
|
339
|
+
onMouseDownLink,
|
|
340
|
+
onMouseUpLink,
|
|
341
|
+
onMouseDownToggle,
|
|
342
|
+
onClickedLinkItem,
|
|
343
|
+
onIconMouseEnter,
|
|
344
|
+
onIconMouseLeave,
|
|
345
|
+
onPopupMouseEnter,
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
</script>
|
|
350
|
+
|
|
351
|
+
<style lang="stylus">
|
|
352
|
+
.sidebar.closure
|
|
353
|
+
.sidebar-nav-group-item-section
|
|
354
|
+
@apply items-center
|
|
355
|
+
|
|
356
|
+
&-header
|
|
357
|
+
@apply s('lg:p-2') s('lg:rounded-full') s('lg:text-lg')
|
|
358
|
+
|
|
359
|
+
&-left
|
|
360
|
+
@apply s('lg:inline-flex')
|
|
361
|
+
|
|
362
|
+
&-right
|
|
363
|
+
@apply s('lg:hidden')
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
.sidebar-nav-group-item-section
|
|
367
|
+
@apply flex flex-col
|
|
368
|
+
|
|
369
|
+
&.collapsed
|
|
370
|
+
.sidebar-nav-group-item-section-header
|
|
371
|
+
&-right
|
|
372
|
+
.sidebar-nav-group-item-section-status
|
|
373
|
+
.sidebar-nav-group-item-section-toggle-btn
|
|
374
|
+
@apply rotate-0
|
|
375
|
+
|
|
376
|
+
&-header
|
|
377
|
+
@apply flex items-center flex-nowrap py-2 pr-1 pl-2 bg-primary-50 rounded-lg text-base text-primary
|
|
378
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold')
|
|
379
|
+
|
|
380
|
+
&.active, &.router-link-exact-active
|
|
381
|
+
@apply bg-primary-600 text-white font-bold
|
|
382
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('hover:font-bold')
|
|
383
|
+
|
|
384
|
+
&-left
|
|
385
|
+
@apply flex-grow-0 flex-shrink-0
|
|
386
|
+
|
|
387
|
+
&-right
|
|
388
|
+
@apply flex-grow flex items-center flex-nowrap space-x-1
|
|
389
|
+
|
|
390
|
+
.sidebar-nav-group-section-title
|
|
391
|
+
@apply flex-grow pl-1 whitespace-nowrap
|
|
392
|
+
|
|
393
|
+
.sidebar-nav-group-item-section-status
|
|
394
|
+
@apply flex-grow-0 flex-shrink-0
|
|
395
|
+
|
|
396
|
+
.sidebar-nav-group-item-section-toggle-btn
|
|
397
|
+
@apply flex items-center justify-center w-4 h-4 text-sm rotate-180 transition-transform origin-center
|
|
398
|
+
|
|
399
|
+
&-body
|
|
400
|
+
@apply overflow-hidden transition-height
|
|
401
|
+
|
|
402
|
+
&-wrapper
|
|
403
|
+
@apply pl-3
|
|
404
|
+
|
|
405
|
+
.sidebar-nav-group-item-section-link
|
|
406
|
+
@apply block py-1 px-2 bg-primary-50 rounded-lg text-sm text-primary whitespace-nowrap
|
|
407
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold') s('active:bg-primary-600') s('active:text-white') s('active:font-bold')
|
|
408
|
+
|
|
409
|
+
&.router-link-exact-active
|
|
410
|
+
@apply bg-primary-600 text-white font-bold
|
|
411
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('hover:font-bold')
|
|
412
|
+
|
|
413
|
+
.sidebar-nav-group-item-section-popup
|
|
414
|
+
@apply flex-col p-2 space-y-1 bg-white rounded-lg overflow-hidden shadow-md hidden
|
|
415
|
+
|
|
416
|
+
&[data-show]
|
|
417
|
+
@apply flex
|
|
418
|
+
|
|
419
|
+
&.single
|
|
420
|
+
@apply p-0
|
|
421
|
+
|
|
422
|
+
&-link
|
|
423
|
+
@apply flex items-center justify-between flex-nowrap py-1 px-2 space-x-4 bg-white rounded-lg text-sm text-primary
|
|
424
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold') s('active:bg-primary-600') s('active:text-white') s('active:font-bold')
|
|
425
|
+
|
|
426
|
+
&.router-link-exact-active
|
|
427
|
+
@apply bg-primary-600 text-white font-bold
|
|
428
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('hover:font-bold')
|
|
429
|
+
|
|
430
|
+
&-icon
|
|
431
|
+
@apply inline-flex
|
|
432
|
+
|
|
433
|
+
&-divide
|
|
434
|
+
@apply h-px bg-primary-200
|
|
435
|
+
</style>
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<template lang="pug">
|
|
2
|
+
NuxtLink.sidebar-nav-link-item(
|
|
3
|
+
:to='itemConfig.to ? itemConfig.to : "javascript:;"',
|
|
4
|
+
:external='!itemConfig.to',
|
|
5
|
+
:class='{ "sidebar-nav-link-item--home": itemConfig.to === "/application" }'
|
|
6
|
+
ref='linkComp'
|
|
7
|
+
@click='onClickedItem'
|
|
8
|
+
)
|
|
9
|
+
.sidebar-nav-link-item-left(
|
|
10
|
+
@mouseenter='onIconMouseEnter',
|
|
11
|
+
@mouseleave='onIconMouseLeave'
|
|
12
|
+
)
|
|
13
|
+
SLIcon(
|
|
14
|
+
:icon='itemConfig.preIcon'
|
|
15
|
+
)
|
|
16
|
+
.sidebar-nav-link-item-right
|
|
17
|
+
.item-title {{ itemConfig.title }}
|
|
18
|
+
.item-post-icon(v-if='itemConfig.status !== "normal"')
|
|
19
|
+
SLIcon(
|
|
20
|
+
v-if='itemConfig.status === "lock"',
|
|
21
|
+
icon='lock'
|
|
22
|
+
)
|
|
23
|
+
SLIcon(
|
|
24
|
+
v-else-if='itemConfig.status === "new"',
|
|
25
|
+
icon='new-circle'
|
|
26
|
+
)
|
|
27
|
+
.inline-flex.items-center.justify-center.w-4.h-4(v-else)
|
|
28
|
+
.bg-danger.rounded-full(class='w-1.5 h-1.5')
|
|
29
|
+
ClientOnly
|
|
30
|
+
Teleport(to='#nav')
|
|
31
|
+
.sidebar-nav-link-item-popup(
|
|
32
|
+
ref='popupEl'
|
|
33
|
+
@mouseenter='onPopupMouseEnter'
|
|
34
|
+
@mouseleave='onIconMouseLeave'
|
|
35
|
+
)
|
|
36
|
+
NuxtLink.sidebar-nav-link-item-popup-link(
|
|
37
|
+
:to='itemConfig.to ? itemConfig.to : "javascript:;"',
|
|
38
|
+
:external='!itemConfig.to',
|
|
39
|
+
:class='{ "sidebar-nav-link-item--home": itemConfig.to === "/application" }'
|
|
40
|
+
@click='onClickedItem'
|
|
41
|
+
)
|
|
42
|
+
.item-title {{ itemConfig.title }}
|
|
43
|
+
.item-post-icon(v-if='itemConfig.status !== "normal"')
|
|
44
|
+
SLIcon(
|
|
45
|
+
v-if='itemConfig.status === "lock"',
|
|
46
|
+
icon='lock'
|
|
47
|
+
)
|
|
48
|
+
SLIcon(
|
|
49
|
+
v-else-if='itemConfig.status === "new"',
|
|
50
|
+
icon='new-circle'
|
|
51
|
+
)
|
|
52
|
+
.inline-flex.items-center.justify-center.w-4.h-4(v-else)
|
|
53
|
+
.bg-danger.rounded-full(class='w-1.5 h-1.5')
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script lang="ts">
|
|
57
|
+
import { NuxtLink } from '#components';
|
|
58
|
+
import { createPopper } from '@popperjs/core';
|
|
59
|
+
import type { Instance as PopperInstance } from '@popperjs/core';
|
|
60
|
+
import { NavItemStatus, type NavItemLink } from '~/interface/NavConfigType';
|
|
61
|
+
|
|
62
|
+
type NuxtLinkType = InstanceType<typeof NuxtLink>;
|
|
63
|
+
|
|
64
|
+
export default defineComponent({
|
|
65
|
+
name: 'SLSidebarNavLinkItem',
|
|
66
|
+
props: {
|
|
67
|
+
navCollapsed: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
default: false,
|
|
70
|
+
},
|
|
71
|
+
itemConfig: {
|
|
72
|
+
type: Object as PropType<NavItemLink>,
|
|
73
|
+
required: true,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
emits: ['click:item'],
|
|
77
|
+
setup(props, { emit }) {
|
|
78
|
+
const { navCollapsed, itemConfig } = toRefs(props);
|
|
79
|
+
|
|
80
|
+
const linkComp = shallowRef<NuxtLinkType | null>(null);
|
|
81
|
+
const popupEl = shallowRef<HTMLDivElement | null>(null);
|
|
82
|
+
|
|
83
|
+
let popperInstance: PopperInstance | null = null;
|
|
84
|
+
let popupHideTimeoutId: any = null;
|
|
85
|
+
|
|
86
|
+
function showPopup() {
|
|
87
|
+
if (
|
|
88
|
+
!linkComp.value ||
|
|
89
|
+
!popupEl.value ||
|
|
90
|
+
popupEl.value.hasAttribute('data-show') ||
|
|
91
|
+
!popperInstance
|
|
92
|
+
)
|
|
93
|
+
return;
|
|
94
|
+
|
|
95
|
+
popupEl.value.setAttribute('data-show', '');
|
|
96
|
+
popperInstance.setOptions((options) => ({
|
|
97
|
+
...options,
|
|
98
|
+
modifiers: [
|
|
99
|
+
...(options.modifiers || []),
|
|
100
|
+
{ name: 'eventListeners', enabled: true },
|
|
101
|
+
],
|
|
102
|
+
}));
|
|
103
|
+
popperInstance.update();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function clearDisappearTimeout() {
|
|
107
|
+
if (popupHideTimeoutId) {
|
|
108
|
+
clearTimeout(popupHideTimeoutId);
|
|
109
|
+
}
|
|
110
|
+
popupHideTimeoutId = null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function onIconMouseEnter() {
|
|
114
|
+
clearDisappearTimeout();
|
|
115
|
+
|
|
116
|
+
if (!navCollapsed.value) return;
|
|
117
|
+
|
|
118
|
+
showPopup();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function hidePopup() {
|
|
122
|
+
if (!popupEl.value || !popperInstance) return;
|
|
123
|
+
|
|
124
|
+
popupEl.value.removeAttribute('data-show');
|
|
125
|
+
popperInstance.setOptions((options) => ({
|
|
126
|
+
...options,
|
|
127
|
+
modifiers: [
|
|
128
|
+
...(options.modifiers || []),
|
|
129
|
+
{ name: 'eventListeners', enabled: false },
|
|
130
|
+
],
|
|
131
|
+
}));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function onIconMouseLeave() {
|
|
135
|
+
clearDisappearTimeout();
|
|
136
|
+
|
|
137
|
+
popupHideTimeoutId = setTimeout(hidePopup, 300);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function onPopupMouseEnter() {
|
|
141
|
+
clearDisappearTimeout();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function onClickedItem() {
|
|
145
|
+
if (
|
|
146
|
+
itemConfig.value.to ||
|
|
147
|
+
!itemConfig.value.emit ||
|
|
148
|
+
itemConfig.value.status === NavItemStatus.Lock
|
|
149
|
+
) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
emit('click:item', itemConfig.value.emit);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function setupPopper() {
|
|
157
|
+
nextTick(() => {
|
|
158
|
+
if (!linkComp.value || !popupEl.value) return;
|
|
159
|
+
popperInstance = createPopper(linkComp.value.$el, popupEl.value, {
|
|
160
|
+
placement: 'right',
|
|
161
|
+
strategy: 'fixed',
|
|
162
|
+
modifiers: [
|
|
163
|
+
{
|
|
164
|
+
name: 'offset',
|
|
165
|
+
options: {
|
|
166
|
+
offset: [0, 12],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{ name: 'eventListeners', enabled: false },
|
|
170
|
+
],
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function destroyPopper() {
|
|
176
|
+
if (popperInstance) {
|
|
177
|
+
popperInstance.destroy();
|
|
178
|
+
}
|
|
179
|
+
popperInstance = null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
onMounted(() => {
|
|
183
|
+
setupPopper();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
onBeforeUnmount(() => {
|
|
187
|
+
clearDisappearTimeout();
|
|
188
|
+
destroyPopper();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
linkComp,
|
|
193
|
+
popupEl,
|
|
194
|
+
onIconMouseEnter,
|
|
195
|
+
onIconMouseLeave,
|
|
196
|
+
onPopupMouseEnter,
|
|
197
|
+
onClickedItem,
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
</script>
|
|
202
|
+
|
|
203
|
+
<style lang="stylus">
|
|
204
|
+
.sidebar.closure
|
|
205
|
+
.sidebar-nav-link-item
|
|
206
|
+
@apply s('lg:p-2') s('lg:text-lg')
|
|
207
|
+
|
|
208
|
+
&-left
|
|
209
|
+
@apply s('lg:inline-flex')
|
|
210
|
+
|
|
211
|
+
&-right
|
|
212
|
+
@apply s('lg:hidden')
|
|
213
|
+
|
|
214
|
+
.sidebar-nav-link-item
|
|
215
|
+
@apply flex items-center justify-start py-2 pr-3 pl-2 bg-white rounded-lg text-base text-primary
|
|
216
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold') s('active:bg-primary-600') s('active:text-white') s('active:font-bold')
|
|
217
|
+
|
|
218
|
+
&.router-link-exact-active, &.router-link-active
|
|
219
|
+
@apply bg-primary-600 text-white font-bold
|
|
220
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('active:font-bold')
|
|
221
|
+
|
|
222
|
+
&.sidebar-nav-link-item--home.router-link-active
|
|
223
|
+
@apply bg-white text-primary font-normal
|
|
224
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold') s('active:bg-primary-600') s('active:text-white') s('active:font-bold')
|
|
225
|
+
|
|
226
|
+
&.sidebar-nav-link-item--home.router-link-exact-active.router-link-active
|
|
227
|
+
@apply bg-primary-600 text-white font-bold
|
|
228
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('hover:font-bold')
|
|
229
|
+
|
|
230
|
+
&-left
|
|
231
|
+
@apply flex-grow-0 flex-shrink-0
|
|
232
|
+
|
|
233
|
+
&-right
|
|
234
|
+
@apply flex-grow flex items-center justify-start flex-nowrap s('space-x-1.5')
|
|
235
|
+
|
|
236
|
+
.item-title
|
|
237
|
+
@apply pl-1 whitespace-nowrap
|
|
238
|
+
|
|
239
|
+
.sidebar-nav-link-item + .sidebar-nav-link-item
|
|
240
|
+
@apply mt-1
|
|
241
|
+
|
|
242
|
+
.sidebar-nav-link-item-popup
|
|
243
|
+
@apply rounded-lg overflow-hidden shadow-md hidden
|
|
244
|
+
|
|
245
|
+
&[data-show]
|
|
246
|
+
@apply block
|
|
247
|
+
|
|
248
|
+
&-link
|
|
249
|
+
@apply flex items-center p-2 space-x-4 bg-white text-sm text-primary whitespace-nowrap
|
|
250
|
+
@apply s('hover:bg-primary-200') s('hover:font-bold') s('active:bg-primary-600') s('active:text-white') s('active:font-bold')
|
|
251
|
+
|
|
252
|
+
&.router-link-exact-active
|
|
253
|
+
@apply bg-primary-600 text-white font-bold
|
|
254
|
+
@apply s('hover:bg-primary-600') s('hover:text-white') s('hover:font-bold')
|
|
255
|
+
</style>
|
|
@@ -108,7 +108,8 @@ export default defineComponent({
|
|
|
108
108
|
|
|
109
109
|
<style lang="stylus">
|
|
110
110
|
.site-page
|
|
111
|
-
@apply flex flex-col w-full min-h-full pt-1
|
|
111
|
+
@apply flex flex-col w-full min-h-full s('pt-1.5')
|
|
112
|
+
@apply s('lg:pt-2')
|
|
112
113
|
|
|
113
114
|
&-header
|
|
114
115
|
@apply flex justify-between mb-4
|