first commit
This commit is contained in:
185
src/layouts/MainLayout.vue
Normal file
185
src/layouts/MainLayout.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<q-layout view="lHr lpr lFr" class="bg-transparent">
|
||||
<q-header class="main-content text-grey glass">
|
||||
<div ref="headerContainer" class="flex q-ma-md justify-between no-wrap items-center">
|
||||
<base-logo ref="logo"/>
|
||||
<div
|
||||
ref="menuContainer"
|
||||
class="row items-center q-ml-md no-wrap"
|
||||
style="min-width: 42px"
|
||||
>
|
||||
<div
|
||||
ref="buttonsContainer"
|
||||
:class="{ 'invisible absolute': !showFullMenu }"
|
||||
class="flex row no-wrap"
|
||||
>
|
||||
<q-btn
|
||||
v-for="item in menuItems"
|
||||
:key="item.id"
|
||||
flat
|
||||
no-caps
|
||||
@click="scrollToElement(item.ref)"
|
||||
ref="menuButtons"
|
||||
>
|
||||
<span class="text-no-wrap">{{ $t(item.title) }}</span>
|
||||
</q-btn>
|
||||
</div>
|
||||
<q-btn
|
||||
v-if="!showFullMenu"
|
||||
flat
|
||||
round
|
||||
icon="menu"
|
||||
@click="showDrawer = !showDrawer"
|
||||
/>
|
||||
<q-btn outline color="primary" class="q-ml-sm">
|
||||
<div class="flex items-center no-wrap">
|
||||
<span class="text-bold">{{ locale.split('-')[0] }}</span>
|
||||
</div>
|
||||
|
||||
<q-menu>
|
||||
<q-list style="min-width: 100px">
|
||||
<q-item
|
||||
v-for="lang in langNames"
|
||||
:key="lang"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="setLocale(lang)"
|
||||
>
|
||||
<q-item-section>
|
||||
<q-item-label :class="isCurrentLang(lang.locale) ? 'text-primary' : ''">{{ lang.label }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-menu>
|
||||
</q-btn>
|
||||
</div>
|
||||
<q-resize-observer @resize="checkSpace"/>
|
||||
</div>
|
||||
</q-header>
|
||||
|
||||
<q-drawer v-model="showDrawer" side="right" overlay>
|
||||
<div class="flex column items-end q-gutter-y-md q-pa-md">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
icon="mdi-close"
|
||||
@click="showDrawer = !showDrawer"
|
||||
/>
|
||||
<q-btn
|
||||
v-for="item in menuItems"
|
||||
:key="item.id"
|
||||
flat
|
||||
no-caps
|
||||
@click="scrollToElement(item.ref)"
|
||||
>
|
||||
<span class="text-no-wrap text-h6">{{ $t(item.title) }}</span>
|
||||
</q-btn>
|
||||
</div>
|
||||
</q-drawer>
|
||||
|
||||
<q-page-container
|
||||
class="main-content q-pa-none q-ma-none bg-transparent"
|
||||
>
|
||||
<q-page class="column">
|
||||
<hero-banner/>
|
||||
<problem-section/>
|
||||
<how-works-section id='how_works'/>
|
||||
<price-section id='price'/>
|
||||
<faq-section id='FAQ'/>
|
||||
<footer-section id='contacts' class="bg-grey-14 text-white"/>
|
||||
</q-page>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import baseLogo from 'components/BaseLogo.vue'
|
||||
import heroBanner from 'components/HeroBanner.vue'
|
||||
import problemSection from 'components/ProblemSection.vue'
|
||||
import HowWorksSection from 'components/HowWorksSection.vue'
|
||||
import PriceSection from 'components/PriceSection.vue'
|
||||
import FaqSection from 'components/FAQSection.vue'
|
||||
import FooterSection from 'components/FooterSection.vue'
|
||||
|
||||
const showDrawer = ref(false)
|
||||
|
||||
const menuItems = [
|
||||
{ id: 0, title: 'main__how_it_works', ref: 'how_works' },
|
||||
{ id: 1, title: 'main__price', ref: 'price' },
|
||||
{ id: 2, title: 'main__faq', ref: 'FAQ' },
|
||||
{ id: 3, title: 'main__contacts', ref: 'contacts' }
|
||||
]
|
||||
|
||||
const showFullMenu = ref(true)
|
||||
const menuButtons = ref([])
|
||||
const headerContainer = ref(null)
|
||||
const logo = ref(null)
|
||||
const menuContainer = ref(null)
|
||||
|
||||
const calculateButtonsWidth = () => {
|
||||
return menuButtons.value.reduce(
|
||||
(total, btn) => total + (btn?.$el.offsetWidth || 0), 0
|
||||
)
|
||||
}
|
||||
|
||||
const checkSpace = () => {
|
||||
if (!headerContainer.value || !logo.value || !menuContainer.value) return
|
||||
|
||||
const headerWidth = headerContainer.value.offsetWidth
|
||||
const logoWidth = logo.value.$el.offsetWidth
|
||||
const menuMargin = parseFloat(getComputedStyle(menuContainer.value).marginLeft) || 0
|
||||
const availableWidth = headerWidth - logoWidth - menuMargin - 40 // 40px - запас
|
||||
|
||||
const buttonsWidth = calculateButtonsWidth()
|
||||
|
||||
showFullMenu.value = buttonsWidth <= availableWidth
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick()
|
||||
checkSpace()
|
||||
})
|
||||
|
||||
|
||||
import { scroll } from 'quasar'
|
||||
const { getScrollTarget, setVerticalScrollPosition } = scroll
|
||||
|
||||
const scrollToElement = (id) => {
|
||||
const el = document.querySelector('#' + id)
|
||||
const target = getScrollTarget(el)
|
||||
const offset = el.offsetTop - 12
|
||||
const duration = 300
|
||||
setVerticalScrollPosition(target, offset, duration)
|
||||
showDrawer.value = false
|
||||
}
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { setGlobalLocale } from 'src/boot/i18n'
|
||||
|
||||
const { locale } = useI18n({ useScope: 'global' })
|
||||
const langNames = [
|
||||
{ locale: 'en-US', label: 'English'},
|
||||
{ locale: 'ru-RU', label: 'Русский'}
|
||||
]
|
||||
|
||||
const isCurrentLang = (lang) => locale.value === lang
|
||||
|
||||
const setLocale = (newLocale) => {
|
||||
setGlobalLocale(newLocale.locale)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.invisible {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.glass {
|
||||
background-color: rgba(255, 255, 255, 0.8) !important;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user