first commit

This commit is contained in:
Inshal
2024-05-29 22:34:28 +05:00
commit e63fc41a20
1470 changed files with 174828 additions and 0 deletions

View File

@@ -0,0 +1,218 @@
<script setup>
import { TransitionGroup } from 'vue'
import { layoutConfig } from '@layouts'
import {
TransitionExpand,
VerticalNavLink,
} from '@layouts/components'
import { canViewNavMenuGroup } from '@layouts/plugins/casl'
import { useLayoutConfigStore } from '@layouts/stores/config'
import { injectionKeyIsVerticalNavHovered } from '@layouts/symbols'
import {
getDynamicI18nProps,
isNavGroupActive,
openGroups,
} from '@layouts/utils'
const props = defineProps({
item: {
type: null,
required: true,
},
})
defineOptions({
name: 'VerticalNavGroup',
})
const route = useRoute()
const router = useRouter()
const configStore = useLayoutConfigStore()
const hideTitleAndBadge = configStore.isVerticalNavMini()
/* We provided default value `ref(false)` because inject will return `T | undefined`
Docs: https://vuejs.org/api/composition-api-dependency-injection.html#inject
*/
const isVerticalNavHovered = inject(injectionKeyIsVerticalNavHovered, ref(false))
// isGroupOpen.value = value ? false : isGroupActive.value
// })
const isGroupActive = ref(false)
const isGroupOpen = ref(false)
const isAnyChildOpen = children => {
return children.some(child => {
let result = openGroups.value.includes(child.title)
if ('children' in child)
result = isAnyChildOpen(child.children) || result
return result
})
}
const collapseChildren = children => {
children.forEach(child => {
if ('children' in child)
collapseChildren(child.children)
openGroups.value = openGroups.value.filter(group => group !== child.title)
})
}
/*Watch for route changes, more specifically route path. Do note that this won't trigger if route's query is updated.
updates isActive & isOpen based on active state of group.
*/
watch(() => route.path, () => {
const isActive = isNavGroupActive(props.item.children, router)
// Don't open group if vertical nav is collapsed and window size is more than overlay nav breakpoint
isGroupOpen.value = isActive && !configStore.isVerticalNavMini(isVerticalNavHovered).value
isGroupActive.value = isActive
}, { immediate: true })
watch(isGroupOpen, val => {
// Find group index for adding/removing group from openGroups array
const grpIndex = openGroups.value.indexOf(props.item.title)
// update openGroups array for addition/removal of current group
// If group is opened => Add it to `openGroups` array
if (val && grpIndex === -1) {
openGroups.value.push(props.item.title)
} else if (!val && grpIndex !== -1) {
openGroups.value.splice(grpIndex, 1)
collapseChildren(props.item.children)
}
}, { immediate: true })
/*Watch for openGroups
It will help in making vertical nav adapting the behavior of accordion.
If we open multiple groups without navigating to any route we must close the inactive or temporarily opened groups.
😵‍💫 Gotchas:
* If we open inactive group then it will auto close that group because we close groups based on active state.
Goal of this watcher is auto close groups which are not active when openGroups array is updated.
So, we have to find a way to do not close recently opened inactive group.
For this we will fetch recently added group in openGroups array and won't perform closing operation if recently added group is current group
*/
watch(openGroups, val => {
// Prevent closing recently opened inactive group.
const lastOpenedGroup = val.at(-1)
if (lastOpenedGroup === props.item.title)
return
const isActive = isNavGroupActive(props.item.children, router)
// Goal of this watcher is to close inactive groups. So don't do anything for active groups.
if (isActive)
return
// We won't close group if any of child group is open in current group
if (isAnyChildOpen(props.item.children))
return
isGroupOpen.value = isActive
isGroupActive.value = isActive
}, { deep: true })
// Previously instead of below watcher we were using two individual watcher for `isVerticalNavHovered`, `isVerticalNavCollapsed` & `isLessThanOverlayNavBreakpoint`
watch(configStore.isVerticalNavMini(isVerticalNavHovered), val => {
isGroupOpen.value = val ? false : isGroupActive.value
})
// isGroupOpen.value = value ? false : isGroupActive.value
// })
const isMounted = useMounted()
</script>
<template>
<li
v-if="canViewNavMenuGroup(item)"
class="nav-group"
:class="[
{
active: isGroupActive,
open: isGroupOpen,
disabled: item.disable,
},
]"
>
<div
class="nav-group-label"
@click="isGroupOpen = !isGroupOpen"
>
<Component
:is="layoutConfig.app.iconRenderer || 'div'"
v-bind="item.icon || layoutConfig.verticalNav.defaultNavItemIconProps"
class="nav-item-icon"
/>
<!--
isMounted is workaround of nuxt's hydration issue:
https://github.com/vuejs/core/issues/6715
-->
<Component
:is="isMounted ? TransitionGroup : 'div'"
name="transition-slide-x"
v-bind="!isMounted ? { class: 'd-flex align-center flex-grow-1' } : undefined"
>
<!-- 👉 Title -->
<Component
:is=" layoutConfig.app.i18n.enable ? 'i18n-t' : 'span'"
v-bind="getDynamicI18nProps(item.title, 'span')"
v-show="!hideTitleAndBadge"
key="title"
class="nav-item-title"
>
{{ item.title }}
</Component>
<!-- 👉 Badge -->
<Component
:is="layoutConfig.app.i18n.enable ? 'i18n-t' : 'span'"
v-bind="getDynamicI18nProps(item.badgeContent, 'span')"
v-show="!hideTitleAndBadge"
v-if="item.badgeContent"
key="badge"
class="nav-item-badge"
:class="item.badgeClass"
>
{{ item.badgeContent }}
</Component>
<Component
:is="layoutConfig.app.iconRenderer || 'div'"
v-show="!hideTitleAndBadge"
v-bind="layoutConfig.icons.chevronRight"
key="arrow"
class="nav-group-arrow"
/>
</Component>
</div>
<TransitionExpand>
<ul
v-show="isGroupOpen"
class="nav-group-children"
>
<Component
:is="'children' in child ? 'VerticalNavGroup' : VerticalNavLink"
v-for="child in item.children"
:key="child.title"
:item="child"
/>
</ul>
</TransitionExpand>
</li>
</template>
<style lang="scss">
.layout-vertical-nav {
.nav-group {
&-label {
display: flex;
align-items: center;
cursor: pointer;
}
}
}
</style>