initial commit

This commit is contained in:
Inshal
2024-10-25 01:02:11 +05:00
commit 6e65bc3a62
1710 changed files with 273609 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
<!-- Thanks: https://markus.oberlehner.net/blog/transition-to-height-auto-with-vue/ -->
<script>
import { Transition } from 'vue'
export default defineComponent({
name: 'TransitionExpand',
setup(_, { slots }) {
const onEnter = element => {
const width = getComputedStyle(element).width
element.style.width = width
element.style.position = 'absolute'
element.style.visibility = 'hidden'
element.style.height = 'auto'
const height = getComputedStyle(element).height
element.style.width = ''
element.style.position = ''
element.style.visibility = ''
element.style.height = '0px'
// Force repaint to make sure the
// animation is triggered correctly.
// eslint-disable-next-line no-unused-expressions
getComputedStyle(element).height
// Trigger the animation.
// We use `requestAnimationFrame` because we need
// to make sure the browser has finished
// painting after setting the `height`
// to `0` in the line above.
requestAnimationFrame(() => {
element.style.height = height
})
}
const onAfterEnter = element => {
element.style.height = 'auto'
}
const onLeave = element => {
const height = getComputedStyle(element).height
element.style.height = height
// Force repaint to make sure the
// animation is triggered correctly.
// eslint-disable-next-line no-unused-expressions
getComputedStyle(element).height
requestAnimationFrame(() => {
element.style.height = '0px'
})
}
return () => h(h(Transition), {
name: 'expand',
onEnter,
onAfterEnter,
onLeave,
}, () => slots.default?.())
},
})
</script>
<style>
.expand-enter-active,
.expand-leave-active {
overflow: hidden;
transition: block-size var(--expand-transition-duration, 0.25s) ease;
}
.expand-enter-from,
.expand-leave-to {
block-size: 0;
}
</style>
<style scoped>
* {
backface-visibility: hidden;
perspective: 1000px;
transform: translateZ(0);
will-change: block-size;
}
</style>

View File

@@ -0,0 +1,188 @@
<script setup>
import axios from '@axios';
import { PerfectScrollbar } from 'vue3-perfect-scrollbar';
import { useDisplay } from 'vuetify';
const currentUser = ref(localStorage.getItem('user_role'));
const seetingPlanLogo = ref();
const props = defineProps({
tag: {
type: [
String,
null,
],
required: false,
default: 'aside',
},
isOverlayNavActive: {
type: Boolean,
required: true,
},
toggleIsOverlayNavActive: {
type: Function,
required: true,
},
})
const { mdAndDown } = useDisplay()
const refNav = ref()
const route = useRoute()
watch(() => route.path, () => {
props.toggleIsOverlayNavActive(false)
})
onMounted(async () => {
let setting = await axios.post('/api/settings', {})
// console.log(setting.data)
seetingPlanLogo.value = '/assets/logo/' + setting.data.logo
})
const isVerticalNavScrolled = ref(false)
const updateIsVerticalNavScrolled = val => isVerticalNavScrolled.value = val
const handleNavScroll = evt => {
isVerticalNavScrolled.value = evt.target.scrollTop > 0
}
</script>
<template>
<Component :is="props.tag" ref="refNav" class="layout-vertical-nav" :class="[
{
'visible': isOverlayNavActive,
'scrolled': isVerticalNavScrolled,
'overlay-nav': mdAndDown,
},
]">
<!-- 👉 Header -->
<div class="nav-header px-0 py-3">
<slot name="nav-header">
<RouterLink to="/provider/dashboard" v-if="currentUser == 'agent'"
class="app-logo d-flex align-center gap-x-3 app-title-wrapper">
<!-- <div class="d-flex " /> -->
<h1 class="leading-normal text-primary">
<VImg :src="seetingPlanLogo" width="150" height="50" class="logo-img" />
</h1>
</RouterLink>
<RouterLink to="/" v-if="currentUser == 'patient'"
class="app-logo d-flex align-center gap-x-3 app-title-wrapper">
<!-- <div class="d-flex " /> -->
<h1 class="leading-normal text-primary" style="margin-right: 27px;">
<VImg :src="seetingPlanLogo" width="150" height="50" class="logo-img" />
</h1>
</RouterLink>
</slot>
</div>
<slot name="before-nav-items">
<div class="vertical-nav-items-shadow" />
</slot>
<slot name="nav-items" :update-is-vertical-nav-scrolled="updateIsVerticalNavScrolled">
<PerfectScrollbar tag="ul" class="nav-items" :options="{ wheelPropagation: false }"
@ps-scroll-y="handleNavScroll">
<slot />
</PerfectScrollbar>
</slot>
<slot name="after-nav-items" />
</Component>
</template>
<style lang="scss">
@use "@configured-variables" as variables;
@use "@layouts/styles/mixins";
.logo-img {
display: block;
position: relative;
margin: 0 auto
}
.nav-header.px-0.py-3 {
margin: 1px 45px !important;
}
.layout-nav-type-vertical .layout-vertical-nav .nav-link .router-link-exact-active:after,
.layout-nav-type-vertical .layout-vertical-nav .nav-group.active:not(.nav-group .nav-group)>:first-child:after {
position: absolute;
background-color: rgb(var(--v-theme-yellow-theme-button)) !important;
block-size: 2.625rem;
border-end-start-radius: .375rem;
border-start-start-radius: .375rem;
content: "";
inline-size: .25rem;
inset-inline-end: -1rem;
}
.nav-header {
display: block !important;
position: relative !important;
margin: 0 auto !important;
}
// 👉 Vertical Nav
.layout-vertical-nav {
position: fixed;
z-index: variables.$layout-vertical-nav-z-index;
display: flex;
flex-direction: column;
block-size: 100%;
inline-size: variables.$layout-vertical-nav-width;
inset-block-start: 0;
inset-inline-start: 0;
transition: transform 0.25s ease-in-out, inline-size 0.25s ease-in-out, box-shadow 0.25s ease-in-out;
will-change: transform, inline-size;
background-color: rgb(var(--v-theme-yellow)) !important;
.nav-header {
display: flex;
align-items: center;
.header-action {
cursor: pointer;
}
}
.app-title-wrapper {
margin-inline-end: auto;
}
.nav-items {
block-size: 100%;
padding-left: 0px;
// We no loner needs this overflow styles as perfect scrollbar applies it
// overflow-x: hidden;
// // We used `overflow-y` instead of `overflow` to mitigate overflow x. Revert back if any issue found.
// overflow-y: auto;
}
.nav-item-title {
overflow: hidden;
margin-inline-end: auto;
text-overflow: ellipsis;
white-space: nowrap;
}
// 👉 Collapsed
.layout-vertical-nav-collapsed & {
&:not(.hovered) {
inline-size: variables.$layout-vertical-nav-collapsed-width;
}
}
// 👉 Overlay nav
&.overlay-nav {
&:not(.visible) {
transform: translateX(-#{variables.$layout-vertical-nav-width});
@include mixins.rtl {
transform: translateX(variables.$layout-vertical-nav-width);
}
}
}
}
</style>

View File

@@ -0,0 +1,184 @@
<script>
import VerticalNav from '@layouts/components/VerticalNav.vue'
import { useDisplay } from 'vuetify'
export default defineComponent({
setup(props, { slots }) {
const isOverlayNavActive = ref(false)
const isLayoutOverlayVisible = ref(false)
const toggleIsOverlayNavActive = useToggle(isOverlayNavActive)
const route = useRoute()
const { mdAndDown } = useDisplay()
// This is alternative to below two commented watcher
// We want to show overlay if overlay nav is visible and want to hide overlay if overlay is hidden and vice versa.
syncRef(isOverlayNavActive, isLayoutOverlayVisible)
return () => {
// 👉 Vertical nav
const verticalNav = h(VerticalNav, { isOverlayNavActive: isOverlayNavActive.value, toggleIsOverlayNavActive }, {
'nav-header': () => slots['vertical-nav-header']?.(),
'before-nav-items': () => slots['before-vertical-nav-items']?.(),
'default': () => slots['vertical-nav-content']?.(),
'after-nav-items': () => slots['after-vertical-nav-items']?.(),
})
// 👉 Navbar
const navbar = h('header', { class: ['layout-navbar navbar-blur'] }, [
h('div', { class: 'navbar-content-container' }, slots.navbar?.({
toggleVerticalOverlayNavActive: toggleIsOverlayNavActive,
})),
])
const main = h('main', { class: 'layout-page-content' }, h('div', { class: 'page-content-container' }, slots.default?.()))
// 👉 Footer
const footer = h('footer', { class: 'layout-footer' }, [
h('div', { class: 'footer-content-container' }, slots.footer?.()),
])
// 👉 Overlay
const layoutOverlay = h('div', {
class: ['layout-overlay', { visible: isLayoutOverlayVisible.value }],
onClick: () => { isLayoutOverlayVisible.value = !isLayoutOverlayVisible.value },
})
return h('div', {
class: [
'layout-wrapper layout-nav-type-vertical layout-navbar-static layout-footer-static layout-content-width-fluid',
mdAndDown.value && 'layout-overlay-nav',
route.meta.layoutWrapperClasses,
],
}, [
verticalNav,
h('div', { class: 'layout-content-wrapper' }, [
navbar,
main,
footer,
]),
layoutOverlay,
])
}
},
})
</script>
<style lang="scss">
@use "@configured-variables" as variables;
@use "@layouts/styles/placeholders";
@use "@layouts/styles/mixins";
.bg-primary {
background-color: rgb(var(--v-theme-yellow-theme-button)) !important;
}
.layout-nav-type-vertical .layout-vertical-nav .nav-link>.router-link-exact-active {
--v-activated-opacity: 0.16;
background-color: rgb(var(--v-theme-yellow-theme-button));
box-shadow: none;
color: #fff;
}
.layout-wrapper.layout-nav-type-vertical {
// TODO(v2): Check why we need height in vertical nav & min-height in horizontal nav
block-size: 100%;
.layout-content-wrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
min-block-size: calc(var(--vh, 1vh) * 100);
transition: padding-inline-start 0.2s ease-in-out;
will-change: padding-inline-start;
}
.layout-navbar {
z-index: variables.$layout-vertical-nav-layout-navbar-z-index;
.navbar-content-container {
block-size: variables.$layout-vertical-nav-navbar-height;
}
@at-root {
.layout-wrapper.layout-nav-type-vertical {
.layout-navbar {
@if variables.$layout-vertical-nav-navbar-is-contained {
@include mixins.boxed-content;
}
@else {
.navbar-content-container {
@include mixins.boxed-content;
}
}
}
}
}
}
&.layout-navbar-sticky .layout-navbar {
@extend %layout-navbar-sticky;
}
&.layout-navbar-hidden .layout-navbar {
@extend %layout-navbar-hidden;
}
// 👉 Footer
.layout-footer {
@include mixins.boxed-content;
}
// 👉 Layout overlay
.layout-overlay {
position: fixed;
z-index: variables.$layout-overlay-z-index;
background-color: rgb(0 0 0 / 60%);
cursor: pointer;
inset: 0;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease-in-out;
will-change: transform;
&.visible {
opacity: 1;
pointer-events: auto;
}
}
&:not(.layout-overlay-nav) .layout-content-wrapper {
padding-inline-start: variables.$layout-vertical-nav-width;
}
// Adjust right column pl when vertical nav is collapsed
&.layout-vertical-nav-collapsed .layout-content-wrapper {
padding-inline-start: variables.$layout-vertical-nav-collapsed-width;
}
// 👉 Content height fixed
&.layout-content-height-fixed {
.layout-content-wrapper {
max-block-size: calc(var(--vh) * 100);
}
.layout-page-content {
display: flex;
overflow: hidden;
.page-content-container {
inline-size: 100%;
> :first-child {
max-block-size: 100%;
overflow-y: auto;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,82 @@
<script setup>
import { ref } from "vue";
const props = defineProps({
item: {
type: null,
required: true,
},
});
const isDropdownOpen = ref(false);
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value;
};
</script>
<template>
<li class="nav-link" :class="{ disabled: item.disabled, open: isDropdownOpen }">
<Component :is="item.to ? 'RouterLink' : 'a'" :to="item.to" :href="item.href"
@click.prevent="item.children && toggleDropdown()">
<VIcon :icon="item.icon" class="nav-item-icon" />
<span class="nav-item-title">{{ item.title }}</span>
<VIcon v-if="item.children" :icon="isDropdownOpen ? 'mdi-chevron-up' : 'mdi-chevron-down'"
class="dropdown-icon" />
</Component>
<transition name="dropdown-transition">
<ul v-if="item.children && isDropdownOpen" class="dropdown-menu1">
<li v-for="(child, index) in item.children" :key="index">
<VerticalNavLink :item="child">
<template #default="{ item }">
<VIcon :icon="item.icon" class="nav-item-icon" />
<span class="nav-item-title">{{ item.title }}</span>
</template>
</VerticalNavLink>
</li>
</ul>
</transition>
</li>
</template>
<style lang="scss">
.layout-vertical-nav {
.nav-link a {
display: flex;
align-items: center;
cursor: pointer;
}
.nav-link {
color: rgb(var(--v-theme-yellow-theme-button)) !important;
}
.dropdown-menu1 {
padding-left: 1rem;
overflow: hidden;
}
.dropdown-icon {
margin-left: auto;
transition: transform 0.3s ease-in-out;
}
.nav-link.open .dropdown-icon {
transform: rotate(180deg);
}
}
.dropdown-transition-enter-active,
.dropdown-transition-leave-active {
transition: max-height 0.3s ease-in-out;
max-height: 200px;
/* Adjust this value based on the maximum height of your dropdown menu */
overflow: hidden;
}
.dropdown-transition-enter-from,
.dropdown-transition-leave-to {
max-height: 0;
}
</style>

View File

@@ -0,0 +1,21 @@
<script setup>
const props = defineProps({
item: {
type: null,
required: true,
},
})
</script>
<template>
<li class="nav-section-title">
<div class="title-wrapper">
<!-- eslint-disable vue/no-v-text-v-html-on-component -->
<span
class="title-text"
v-text="item.heading"
/>
<!-- eslint-enable vue/no-v-text-v-html-on-component -->
</div>
</li>
</template>

View File

@@ -0,0 +1,3 @@
.cursor-pointer {
cursor: pointer;
}

View File

@@ -0,0 +1,35 @@
// These are styles which are both common in layout w/ vertical nav & horizontal nav
@use "@layouts/styles/rtl";
@use "@layouts/styles/placeholders";
@use "@layouts/styles/mixins";
@use "@configured-variables" as variables;
html,
body {
min-block-size: 100%;
}
.layout-page-content {
@include mixins.boxed-content(true);
flex-grow: 1;
// TODO: Use grid gutter variable here
padding-block: 1.5rem;
}
.layout-footer {
.footer-content-container {
block-size: variables.$layout-vertical-nav-footer-height;
}
.layout-footer-sticky & {
position: sticky;
inset-block-end: 0;
will-change: transform;
}
.layout-footer-hidden & {
display: none;
}
}

View File

@@ -0,0 +1,10 @@
*,
::before,
::after {
box-sizing: inherit;
background-repeat: no-repeat;
}
html {
box-sizing: border-box;
}

View File

@@ -0,0 +1,28 @@
@use "placeholders";
@use "@configured-variables" as variables;
@mixin rtl {
@if variables.$enable-rtl-styles {
[dir="rtl"] & {
@content;
}
}
}
@mixin boxed-content($nest-selector: false) {
& {
@extend %boxed-content-spacing;
@at-root {
@if $nest-selector == false {
.layout-content-width-boxed#{&} {
@extend %boxed-content;
}
} @else {
.layout-content-width-boxed & {
@extend %boxed-content;
}
}
}
}
}

View File

@@ -0,0 +1,53 @@
// placeholders
@use "@configured-variables" as variables;
%boxed-content {
@at-root #{&}-spacing {
// TODO: Use grid gutter variable here
padding-inline: 1.5rem;
}
inline-size: 100%;
margin-inline: auto;
max-inline-size: variables.$layout-boxed-content-width;
}
%layout-navbar-hidden {
display: none;
}
// We created this placeholder even it is being used in just layout w/ vertical nav because in future we might apply style to both navbar & horizontal nav separately
%layout-navbar-sticky {
position: sticky;
inset-block-start: 0;
// will-change: transform;
// inline-size: 100%;
}
%style-scroll-bar {
/* width */
&::-webkit-scrollbar {
background: rgb(var(--v-theme-surface));
block-size: 8px;
border-end-end-radius: 14px;
border-start-end-radius: 14px;
inline-size: 4px;
}
/* Track */
&::-webkit-scrollbar-track {
background: transparent;
}
/* Handle */
&::-webkit-scrollbar-thumb {
border-radius: 0.5rem;
background: rgb(var(--v-theme-perfect-scrollbar-thumb));
}
&::-webkit-scrollbar-corner {
display: none;
}
}

View File

@@ -0,0 +1,7 @@
@use "./mixins";
.layout-vertical-nav .nav-group-arrow {
@include mixins.rtl {
transform: rotate(180deg);
}
}

View File

@@ -0,0 +1,28 @@
// @use "@styles/style.scss";
// 👉 Vertical nav
$layout-vertical-nav-z-index: 12 !default;
$layout-vertical-nav-width: 260px !default;
$layout-vertical-nav-collapsed-width: 80px !default;
// 👉 Horizontal nav
$layout-horizontal-nav-z-index: 11 !default;
$layout-horizontal-nav-navbar-height: 64px !default;
// 👉 Navbar
$layout-vertical-nav-navbar-height: 64px !default;
$layout-vertical-nav-navbar-is-contained: true !default;
$layout-vertical-nav-layout-navbar-z-index: 11 !default;
$layout-horizontal-nav-layout-navbar-z-index: 11 !default;
// 👉 Main content
$layout-boxed-content-width: 1440px !default;
// 👉Footer
$layout-vertical-nav-footer-height: 56px !default;
// 👉 Layout overlay
$layout-overlay-z-index: 11 !default;
// 👉 RTL
$enable-rtl-styles: true !default;

View File

@@ -0,0 +1,3 @@
@use "_global";
@use "vue3-perfect-scrollbar/dist/vue3-perfect-scrollbar.min.css";
@use "_classes";

View File

@@ -0,0 +1 @@
export const injectionKeyIsVerticalNavHovered = Symbol('isVerticalNavHovered')

View File

@@ -0,0 +1,12 @@
export const hexToRgb = hex => {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
return r + r + g + g + b + b
})
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result ? `${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)}` : null
}