- Introduced typography styles in _typography.scss for headings, paragraphs, blockquotes, and horizontal rules. - Added utility classes in _utils.scss for card styling and clearfix. - Updated layout.scss to include new typography and utility styles. - Defined common CSS variables in _common.scss for consistent theming. - Created dark and light theme variables in _dark.scss and _light.scss respectively. - Integrated Tailwind CSS with custom configurations in tailwind.config.js and postcss.config.js. - Implemented database migrations for contact and contact_persons tables. - Added data fixtures for generating sample contact data. - Developed Contact and ContactPerson entities with appropriate validation and serialization. - Enhanced ContactRepository with search and type filtering methods.
72 lines
2.3 KiB
Vue
72 lines
2.3 KiB
Vue
<script setup>
|
|
import { useLayout } from './composables/layout';
|
|
import { computed, ref, watch } from 'vue';
|
|
import AppFooter from './AppFooter.vue';
|
|
import AppSidebar from './AppSidebar.vue';
|
|
import AppTopbar from './AppTopbar.vue';
|
|
|
|
const { layoutConfig, layoutState, isSidebarActive } = useLayout();
|
|
|
|
const outsideClickListener = ref(null);
|
|
|
|
watch(isSidebarActive, (newVal) => {
|
|
if (newVal) {
|
|
bindOutsideClickListener();
|
|
} else {
|
|
unbindOutsideClickListener();
|
|
}
|
|
});
|
|
|
|
const containerClass = computed(() => {
|
|
return {
|
|
'layout-overlay': layoutConfig.menuMode === 'overlay',
|
|
'layout-static': layoutConfig.menuMode === 'static',
|
|
'layout-static-inactive': layoutState.staticMenuDesktopInactive && layoutConfig.menuMode === 'static',
|
|
'layout-overlay-active': layoutState.overlayMenuActive,
|
|
'layout-mobile-active': layoutState.staticMenuMobileActive
|
|
};
|
|
});
|
|
|
|
function bindOutsideClickListener() {
|
|
if (!outsideClickListener.value) {
|
|
outsideClickListener.value = (event) => {
|
|
if (isOutsideClicked(event)) {
|
|
layoutState.overlayMenuActive = false;
|
|
layoutState.staticMenuMobileActive = false;
|
|
layoutState.menuHoverActive = false;
|
|
}
|
|
};
|
|
document.addEventListener('click', outsideClickListener.value);
|
|
}
|
|
}
|
|
|
|
function unbindOutsideClickListener() {
|
|
if (outsideClickListener.value) {
|
|
document.removeEventListener('click', outsideClickListener);
|
|
outsideClickListener.value = null;
|
|
}
|
|
}
|
|
|
|
function isOutsideClicked(event) {
|
|
const sidebarEl = document.querySelector('.layout-sidebar');
|
|
const topbarEl = document.querySelector('.layout-menu-button');
|
|
|
|
return !(sidebarEl.isSameNode(event.target) || sidebarEl.contains(event.target) || topbarEl.isSameNode(event.target) || topbarEl.contains(event.target));
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="layout-wrapper" :class="containerClass">
|
|
<app-topbar></app-topbar>
|
|
<app-sidebar></app-sidebar>
|
|
<div class="layout-main-container">
|
|
<div class="layout-main">
|
|
<router-view></router-view>
|
|
</div>
|
|
<app-footer></app-footer>
|
|
</div>
|
|
<div class="layout-mask animate-fadein"></div>
|
|
</div>
|
|
<Toast />
|
|
</template>
|