VEGA-4499: адаптация UI главной страницы документации
This commit is contained in:
committed by
Речкина Елена Валерьевна
parent
2ce47117e1
commit
baca55494c
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div :class="[$style.CustomFeatureImage, classes]">
|
||||
<CustomIcon :icon="icon" size="large" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Icons } from '@beeline/design-tokens/js/iconfont/icons';
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import CustomIcon from './CustomIcon.vue';
|
||||
|
||||
const { size = 'medium' } = defineProps<{
|
||||
icon: Icons,
|
||||
size?: 'medium' | 'small'
|
||||
}>()
|
||||
|
||||
const style = useCssModule()
|
||||
const classes = computed(() => ({
|
||||
[style.CustomAvatar]: true,
|
||||
[style.CustomAvatarSmall]: size === 'small'
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use '@beeline/design-tokens/scss/tokens/components/avatar';
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
|
||||
.CustomAvatar {
|
||||
border-radius: avatar.$avatar-squircle-border-radius;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
height: avatar.$avatar-medium-height;
|
||||
width: avatar.$avatar-medium-width;
|
||||
letter-spacing: avatar.$avatar-medium-text-font-letter-spacing;
|
||||
line-height: avatar.$avatar-medium-text-font-line-height;
|
||||
font-size: avatar.$avatar-medium-text-font-size;
|
||||
background-color: themes.$color-status-neutral-background;
|
||||
|
||||
&Small {
|
||||
height: avatar.$avatar-small-height;
|
||||
width: avatar.$avatar-small-width;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,49 +11,82 @@ const { hasSidebar } = useSidebar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="VPContent CustomContent"
|
||||
id="VPContent"
|
||||
:class="{
|
||||
<div class="VPContent CustomContent" id="VPContent" :class="{
|
||||
'has-sidebar': hasSidebar,
|
||||
'is-home': frontmatter.layout === 'home'
|
||||
}"
|
||||
>
|
||||
<slot name="not-found" v-if="page.isNotFound"><NotFound /></slot>
|
||||
}">
|
||||
<slot name="not-found" v-if="page.isNotFound">
|
||||
<NotFound />
|
||||
</slot>
|
||||
|
||||
<VPPage v-else-if="frontmatter.layout === 'page'">
|
||||
<template #page-top><slot name="page-top" /></template>
|
||||
<template #page-bottom><slot name="page-bottom" /></template>
|
||||
<template #page-top>
|
||||
<slot name="page-top" />
|
||||
</template>
|
||||
<template #page-bottom>
|
||||
<slot name="page-bottom" />
|
||||
</template>
|
||||
</VPPage>
|
||||
|
||||
<VPHome v-else-if="frontmatter.layout === 'home'">
|
||||
<template #home-hero-before><slot name="home-hero-before" /></template>
|
||||
<template #home-hero-info><slot name="home-hero-info" /></template>
|
||||
<template #home-hero-image><slot name="home-hero-image" /></template>
|
||||
<template #home-hero-after><slot name="home-hero-after" /></template>
|
||||
<template #home-features-before><slot name="home-features-before" /></template>
|
||||
<template #home-features-after><slot name="home-features-after" /></template>
|
||||
<template #home-hero-before>
|
||||
<slot name="home-hero-before" />
|
||||
</template>
|
||||
<template #home-hero-info>
|
||||
<slot name="home-hero-info" />
|
||||
</template>
|
||||
<template #home-hero-image>
|
||||
<slot name="home-hero-image" />
|
||||
</template>
|
||||
<template #home-hero-after>
|
||||
<slot name="home-hero-after" />
|
||||
</template>
|
||||
<template #home-features-before>
|
||||
<slot name="home-features-before" />
|
||||
</template>
|
||||
<template #home-features-after>
|
||||
<slot name="home-features-after" />
|
||||
</template>
|
||||
</VPHome>
|
||||
|
||||
<component
|
||||
v-else-if="frontmatter.layout && frontmatter.layout !== 'doc'"
|
||||
:is="frontmatter.layout"
|
||||
/>
|
||||
<component v-else-if="frontmatter.layout && frontmatter.layout !== 'doc'" :is="frontmatter.layout" />
|
||||
|
||||
<CustomDoc v-else>
|
||||
<template #doc-top><slot name="doc-top" /></template>
|
||||
<template #doc-bottom><slot name="doc-bottom" /></template>
|
||||
<template #doc-top>
|
||||
<slot name="doc-top" />
|
||||
</template>
|
||||
<template #doc-bottom>
|
||||
<slot name="doc-bottom" />
|
||||
</template>
|
||||
|
||||
<template #doc-footer-before><slot name="doc-footer-before" /></template>
|
||||
<template #doc-before><slot name="doc-before" /></template>
|
||||
<template #doc-after><slot name="doc-after" /></template>
|
||||
<template #doc-footer-before>
|
||||
<slot name="doc-footer-before" />
|
||||
</template>
|
||||
<template #doc-before>
|
||||
<slot name="doc-before" />
|
||||
</template>
|
||||
<template #doc-after>
|
||||
<slot name="doc-after" />
|
||||
</template>
|
||||
|
||||
<template #aside-top><slot name="aside-top" /></template>
|
||||
<template #aside-outline-before><slot name="aside-outline-before" /></template>
|
||||
<template #aside-outline-after><slot name="aside-outline-after" /></template>
|
||||
<template #aside-ads-before><slot name="aside-ads-before" /></template>
|
||||
<template #aside-ads-after><slot name="aside-ads-after" /></template>
|
||||
<template #aside-bottom><slot name="aside-bottom" /></template>
|
||||
<template #aside-top>
|
||||
<slot name="aside-top" />
|
||||
</template>
|
||||
<template #aside-outline-before>
|
||||
<slot name="aside-outline-before" />
|
||||
</template>
|
||||
<template #aside-outline-after>
|
||||
<slot name="aside-outline-after" />
|
||||
</template>
|
||||
<template #aside-ads-before>
|
||||
<slot name="aside-ads-before" />
|
||||
</template>
|
||||
<template #aside-ads-after>
|
||||
<slot name="aside-ads-after" />
|
||||
</template>
|
||||
<template #aside-bottom>
|
||||
<slot name="aside-bottom" />
|
||||
</template>
|
||||
</CustomDoc>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -73,58 +73,70 @@ const pageName = computed(() =>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="scss">
|
||||
@use 'src/assets/scss/app/helpers/media';
|
||||
|
||||
.VPDoc {
|
||||
padding: 32px 24px 96px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.VPDoc {
|
||||
@include media.media_min(960px) {
|
||||
padding: 48px 32px 0;
|
||||
}
|
||||
|
||||
@include media.media_max(768px) {
|
||||
padding: 48px 32px 128px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPDoc {
|
||||
padding: 48px 32px 0;
|
||||
.VPDoc:not(.has-sidebar) .container {
|
||||
@include media.media_min(1440px) {
|
||||
max-width: 1104px;
|
||||
}
|
||||
|
||||
.VPDoc:not(.has-sidebar) .container {
|
||||
display: flex;
|
||||
@include media.media_max(960px) {
|
||||
padding: 0 32px 128px;display: flex;
|
||||
justify-content: center;
|
||||
max-width: 992px;
|
||||
}
|
||||
}
|
||||
|
||||
.VPDoc:not(.has-sidebar) .content {
|
||||
.VPDoc:not(.has-sidebar) .content {
|
||||
@include media.media_min(1440px) {
|
||||
max-width: 784px;
|
||||
}
|
||||
|
||||
@include media.media_max(960px) {
|
||||
max-width: 752px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.VPDoc .container {
|
||||
.VPDoc .container {
|
||||
@include media.media_min(1280px) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.VPDoc .aside {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.VPDoc:not(.has-sidebar) .content {
|
||||
max-width: 784px;
|
||||
}
|
||||
|
||||
.VPDoc:not(.has-sidebar) .container {
|
||||
max-width: 1104px;
|
||||
.VPDoc .aside {
|
||||
@include media.media_min(1280px) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
|
||||
@include media.media_min(1280px) {
|
||||
order: 1;
|
||||
margin: 0;
|
||||
min-width: 640px;
|
||||
}
|
||||
|
||||
@include media.media_max(960px) {
|
||||
padding: 0 32px 128px;
|
||||
}
|
||||
}
|
||||
|
||||
.aside {
|
||||
@@ -180,20 +192,6 @@ const pageName = computed(() =>
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.content {
|
||||
padding: 0 32px 128px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.content {
|
||||
order: 1;
|
||||
margin: 0;
|
||||
min-width: 640px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-container {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import VPImage from 'vitepress/dist/client/theme-default/components/VPImage.vue'
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import VPIconArrowRight from 'vitepress/dist/client/theme-default/components/icons/VPIconArrowRight.vue'
|
||||
|
||||
defineProps<{
|
||||
icon?: DefaultTheme.FeatureIcon
|
||||
title: string
|
||||
details?: string
|
||||
link?: string
|
||||
linkText?: string
|
||||
rel?: string
|
||||
target?: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
class="VPFeature CustomFeature"
|
||||
:href="link"
|
||||
:rel="rel"
|
||||
:target="target"
|
||||
:no-icon="true"
|
||||
:tag="link ? 'a' : 'div'"
|
||||
>
|
||||
<article class="box">
|
||||
<div v-if="typeof icon === 'object' && icon.wrap" class="icon">
|
||||
<VPImage
|
||||
:image="icon"
|
||||
:alt="icon.alt"
|
||||
:height="icon.height || 48"
|
||||
:width="icon.width || 48"
|
||||
/>
|
||||
</div>
|
||||
<VPImage
|
||||
v-else-if="typeof icon === 'object'"
|
||||
:image="icon"
|
||||
:alt="icon.alt"
|
||||
:height="icon.height || 48"
|
||||
:width="icon.width || 48"
|
||||
/>
|
||||
<div v-else-if="icon" class="icon" v-html="icon"></div>
|
||||
<h2 class="title" v-html="title"></h2>
|
||||
<p v-if="details" class="details" v-html="details"></p>
|
||||
|
||||
<div v-if="linkText" class="link-text">
|
||||
<p class="link-text-value">
|
||||
{{ linkText }} <VPIconArrowRight class="link-text-icon" />
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.VPFeature {
|
||||
display: block;
|
||||
border: 1px solid var(--vp-c-bg-soft);
|
||||
border-radius: 12px;
|
||||
height: 100%;
|
||||
background-color: var(--vp-c-bg-soft);
|
||||
transition: border-color 0.25s, background-color 0.25s;
|
||||
}
|
||||
|
||||
.VPFeature.link:hover {
|
||||
border-color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 24px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.box > :deep(.VPImage) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 6px;
|
||||
background-color: var(--vp-c-default-soft);
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: 24px;
|
||||
transition: background-color 0.25s;
|
||||
}
|
||||
|
||||
.title {
|
||||
line-height: 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-active);
|
||||
}
|
||||
|
||||
.details {
|
||||
flex-grow: 1;
|
||||
padding-top: 8px;
|
||||
line-height: 24px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-inactive);
|
||||
}
|
||||
|
||||
.link-text {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.link-text-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vp-c-brand-1);
|
||||
}
|
||||
|
||||
.link-text-icon {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
fill: currentColor;
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { type Ref, inject } from 'vue'
|
||||
import { type Ref, inject, ref } from 'vue'
|
||||
import type { DefaultTheme } from 'vitepress/theme'
|
||||
import CustomButton from './CustomButton.vue'
|
||||
import VPImage from 'vitepress/dist/client/theme-default/components/VPImage.vue'
|
||||
import HomeHeroSearchButton from './HomeHeroSearchButton.vue'
|
||||
import VPLocalSearchBox from 'vitepress/dist/client/theme-default/components/VPLocalSearchBox.vue'
|
||||
|
||||
export interface HeroAction {
|
||||
theme?: 'brand' | 'alt'
|
||||
@@ -14,11 +16,14 @@ defineProps<{
|
||||
name?: string
|
||||
text?: string
|
||||
tagline?: string
|
||||
search?: boolean
|
||||
image?: DefaultTheme.ThemeableImage
|
||||
actions?: HeroAction[]
|
||||
}>()
|
||||
|
||||
const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
|
||||
const showSearch = ref(false)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -27,21 +32,20 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
<div class="main">
|
||||
<slot name="home-hero-info">
|
||||
<h1 v-if="name" class="name">
|
||||
<span v-html="name" class="clip"></span>
|
||||
{{ name }}
|
||||
</h1>
|
||||
<p v-if="text" v-html="text" class="text"></p>
|
||||
<p v-if="tagline" v-html="tagline" class="tagline"></p>
|
||||
</slot>
|
||||
|
||||
<div v-if="search" class="VPHeroSearchWrapper">
|
||||
<VPLocalSearchBox v-if="showSearch" @close="showSearch = false" />
|
||||
<HomeHeroSearchButton @click="showSearch = true" />
|
||||
</div>
|
||||
|
||||
<div v-if="actions" class="actions">
|
||||
<div v-for="action in actions" :key="action.link" class="action">
|
||||
<CustomButton
|
||||
tag="a"
|
||||
size="medium"
|
||||
:theme="action.theme"
|
||||
:text="action.text"
|
||||
:href="action.link"
|
||||
/>
|
||||
<CustomButton tag="a" size="medium" :theme="action.theme" :text="action.text" :href="action.link" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,20 +63,26 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
@use "@beeline/design-tokens/scss/mixin";
|
||||
|
||||
$topSpacing: 40px;
|
||||
|
||||
.VPHero {
|
||||
margin-top: calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px;
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + $topSpacing) 24px 48px;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.VPHero {
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px;
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + $topSpacing) 48px 64px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.VPHero {
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px;
|
||||
padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + $topSpacing) 64px 64px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +90,7 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 auto;
|
||||
max-width: 1152px;
|
||||
max-width: 1080px;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
@@ -95,46 +105,41 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
order: 2;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.VPHero.has-image .container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
background-color: themes.$color-background-secondary;
|
||||
padding: 98px 92px;
|
||||
border-radius: sizes.$size-border-radius-x6;
|
||||
|
||||
.VPHero.has-image .container {
|
||||
text-align: left;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.main {
|
||||
order: 1;
|
||||
width: calc((100% / 3) * 2);
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.main {
|
||||
order: 1;
|
||||
width: calc((100% / 3) * 2);
|
||||
.VPHero.has-image .main {
|
||||
max-width: 592px;
|
||||
}
|
||||
}
|
||||
|
||||
.VPHero.has-image .main {
|
||||
max-width: 592px;
|
||||
.name,
|
||||
.text {
|
||||
@include mixin.h1;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.name,
|
||||
.text {
|
||||
max-width: 392px;
|
||||
letter-spacing: -0.4px;
|
||||
line-height: 40px;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.VPHero.has-image .name,
|
||||
.VPHero.has-image .text {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: var(--vp-home-hero-name-color);
|
||||
.VPHero.has-image .name,
|
||||
.VPHero.has-image .text {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: themes.$color-text-active;
|
||||
padding-bottom: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.clip {
|
||||
@@ -144,22 +149,7 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
-webkit-text-fill-color: var(--vp-home-hero-name-color);
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.name,
|
||||
.text {
|
||||
max-width: 576px;
|
||||
line-height: 56px;
|
||||
font-size: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 960px) {
|
||||
.name,
|
||||
.text {
|
||||
line-height: 64px;
|
||||
font-size: 56px;
|
||||
}
|
||||
|
||||
.VPHero.has-image .name,
|
||||
.VPHero.has-image .text {
|
||||
margin: 0;
|
||||
@@ -168,7 +158,6 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
|
||||
.tagline {
|
||||
padding-top: 8px;
|
||||
max-width: 392px;
|
||||
line-height: 28px;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
@@ -183,7 +172,6 @@ const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
|
||||
@media (min-width: 640px) {
|
||||
.tagline {
|
||||
padding-top: 12px;
|
||||
max-width: 576px;
|
||||
line-height: 32px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import VPHomeHero from 'vitepress/dist/client/theme-default/components/VPHomeHero.vue'
|
||||
import CustomHomeFeatures from './CustomHomeFeatures.vue';
|
||||
import CustomHomeServices from './CustomHomeServices.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="VPHome CustomHome">
|
||||
<slot name="home-hero-before" />
|
||||
<VPHomeHero>
|
||||
<template #home-hero-info>
|
||||
<slot name="home-hero-info" />
|
||||
</template>
|
||||
<template #home-hero-image>
|
||||
<slot name="home-hero-image" />
|
||||
</template>
|
||||
</VPHomeHero>
|
||||
<slot name="home-hero-after" />
|
||||
|
||||
<slot name="home-features-before" />
|
||||
<CustomHomeFeatures />
|
||||
<slot name="home-features-after" />
|
||||
|
||||
<CustomHomeServices />
|
||||
|
||||
<Content />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.VPHome {
|
||||
padding-bottom: 96px;
|
||||
}
|
||||
|
||||
.VPHome :deep(.VPHomeSponsors) {
|
||||
margin-top: 112px;
|
||||
margin-bottom: -128px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import { computed, useCssModule } from 'vue';
|
||||
import { NAVBAR_HEIGHT } from '../constants'
|
||||
import { Icons } from '@beeline/design-tokens/js/iconfont/icons'
|
||||
import CustomAvatar from './CustomAvatar.vue';
|
||||
|
||||
const { disabled, scrollTo, link, items } = defineProps<{
|
||||
icon: string
|
||||
title: string
|
||||
link?: string
|
||||
scrollTo?: string
|
||||
disabled?: boolean
|
||||
items?: ItemFeature[]
|
||||
}>()
|
||||
|
||||
type ItemFeature = {
|
||||
title: string
|
||||
link?: string
|
||||
}
|
||||
|
||||
const style = useCssModule()
|
||||
const classes = computed(() => ({
|
||||
[style.CustomFeature]: true,
|
||||
[style.CustomFeatureDisabled]: disabled || !link,
|
||||
[style.CustomFeatureWithScroll]: scrollTo && !disabled
|
||||
}))
|
||||
|
||||
const handleClick = (event: Event) => {
|
||||
if (scrollTo) {
|
||||
const targetElTop = document.querySelector(scrollTo)?.getBoundingClientRect().top
|
||||
|
||||
if (targetElTop) {
|
||||
event.stopPropagation()
|
||||
window.scrollTo({ behavior: 'smooth', top: targetElTop + window.scrollY - NAVBAR_HEIGHT - 32 })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Component :is="!disabled && link ? VPLink : 'article'"
|
||||
:class="classes" @click="handleClick">
|
||||
<CustomAvatar :class="$style.CustomFeatureIcon" :icon="icon as Icons" />
|
||||
|
||||
<div :class="$style.CustomFeatureContent">
|
||||
<Component :is="!disabled && link ? VPLink : 'article'" v-bind="{ ...(!disabled && link && { href: link }) }">
|
||||
<p :class="$style.CustomFeatureTitle" :href="`/docs${link}`">
|
||||
{{ title }}
|
||||
</p>
|
||||
</Component>
|
||||
<div :class="$style.CustomFeatureContainer">
|
||||
<div v-for="(item, i) in items" :key="i">
|
||||
<Component :is="!disabled && item.link ? VPLink : 'article'" v-bind="{ ...(!disabled && item.link && { href: item.link }) }">
|
||||
<p :class="$style.CustomFeatureSubtitle">{{ item.title }}</p>
|
||||
</Component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Component>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
@use "@beeline/design-tokens/scss/mixin";
|
||||
|
||||
.CustomFeature {
|
||||
$el: &;
|
||||
border-radius: sizes.$size-border-radius-x6;
|
||||
border: 1px solid themes.$color-border;
|
||||
max-width: 344px;
|
||||
padding: sizes.$size-spacing-x8 sizes.$size-spacing-x6;
|
||||
|
||||
&WithScroll {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&Icon {
|
||||
#{$el}Disabled & {
|
||||
color: themes.$color-text-disabled;
|
||||
}
|
||||
}
|
||||
|
||||
&Content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: sizes.$size-spacing-x4;
|
||||
gap: sizes.$size-spacing-x6;
|
||||
}
|
||||
|
||||
&Title {
|
||||
@include mixin.h4;
|
||||
color: themes.$color-text-active;
|
||||
|
||||
&:hover {
|
||||
color: themes.$color-text-link !important;
|
||||
}
|
||||
|
||||
#{$el}Disabled & {
|
||||
color: themes.$color-text-disabled;
|
||||
}
|
||||
|
||||
#{$el}WithScroll & {
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
}
|
||||
|
||||
&Subtitle {
|
||||
@include mixin.caption;
|
||||
|
||||
&:hover {
|
||||
color: themes.$color-text-link;
|
||||
}
|
||||
|
||||
#{$el}Disabled & {
|
||||
color: themes.$color-text-disabled;
|
||||
}
|
||||
|
||||
#{$el}WithScroll & {
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
}
|
||||
|
||||
&Container {
|
||||
display: flex;
|
||||
gap: sizes.$size-spacing-x4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import CustomHomeFeature from './CustomHomeFeature.vue'
|
||||
|
||||
const { frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="Array.isArray(frontmatter.features)" :class="$style.HomeFeatures">
|
||||
<CustomHomeFeature v-for="feature in frontmatter.features" :key="feature.title" :title="feature.title"
|
||||
:icon="feature.icon" :link="feature.link" :scrollTo="feature.scroll_to" :disabled="feature.disabled" :items="feature.items" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
|
||||
.HomeFeatures {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: sizes.$size-spacing-x8 sizes.$size-spacing-x6;
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 80px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress/dist/client/theme-default/composables/data'
|
||||
import VPHero from 'vitepress/dist/client/theme-default/components/VPHero.vue'
|
||||
|
||||
const { frontmatter: fm } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPHero v-if="fm.hero" class="CustomHomeHero" :name="fm.hero.name" :text="fm.hero.text" :tagline="fm.hero.tagline"
|
||||
:search="fm.hero.search" :image="fm.hero.image" :actions="fm.hero.actions">
|
||||
<template #home-hero-info>
|
||||
<slot name="home-hero-info" />
|
||||
</template>
|
||||
<template #home-hero-image>
|
||||
<slot name="home-hero-image" />
|
||||
</template>
|
||||
</VPHero>
|
||||
</template>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div :class="$style.CustomHomeServiceCard">
|
||||
<h3 :class="$style.CustomHomeServiceCardTitle">{{ title }}</h3>
|
||||
|
||||
<div :class="$style.CustomHomeServiceCardLinks">
|
||||
<Component v-for="article in articles" :is="!article.disabled && article.link ? VPLink : 'div'"
|
||||
v-bind="{ ...(!article.disabled && article.link && { href: article.link }) }"
|
||||
:class="[$style.CustomHomeServiceCardLink, { [$style.CustomHomeServiceCardLinkDisabled]: article.disabled || !article.link }]">
|
||||
<div :class="$style.CustomHomeServiceCardLinkTitle">
|
||||
<div :class="[$style.CustomHomeServiceCardLinkTitleIcon, { [$style.CustomHomeServiceCardLinkTitleIconActive] : article.link && article.icon === 'magic' }]">
|
||||
<CustomIcon :icon="(article.icon || 'cloud') as Icons" />
|
||||
</div>
|
||||
<div :class="[$style.CustomHomeServiceCardLinkTitleText, { [$style.CustomHomeServiceCardLinkTitleTextActive]: article.link }]">
|
||||
{{ article.title }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.CustomHomeServiceCardLinkDescription">
|
||||
{{ article.description }}
|
||||
</div>
|
||||
</Component>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import VPLink from 'vitepress/dist/client/theme-default/components/VPLink.vue'
|
||||
import CustomIcon from './CustomIcon.vue'
|
||||
import { Icons } from '@beeline/design-tokens/js/iconfont/icons'
|
||||
|
||||
export type ServiceArticle = {
|
||||
title: string
|
||||
description: string
|
||||
icon: string
|
||||
link?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
defineProps<{
|
||||
title: string
|
||||
articles: ServiceArticle[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
@use "@beeline/design-tokens/scss/mixin";
|
||||
|
||||
.CustomHomeServiceCard {
|
||||
background-color: themes.$color-background-secondary;
|
||||
border-radius: sizes.$size-border-radius-x6;
|
||||
padding: sizes.$size-spacing-x8 sizes.$size-spacing-x6;
|
||||
|
||||
&Title {
|
||||
@include mixin.h4();
|
||||
color: themes.$color-text-active;
|
||||
margin-bottom: sizes.$size-spacing-x6;
|
||||
}
|
||||
|
||||
&Links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: sizes.$size-spacing-x6;
|
||||
}
|
||||
|
||||
&Link {
|
||||
$iconRightSpacing: 8px;
|
||||
$iconSize: 20px;
|
||||
$el: &;
|
||||
|
||||
color: themes.$color-text-active !important;
|
||||
|
||||
&Disabled {
|
||||
color: themes.$color-text-disabled !important;
|
||||
}
|
||||
|
||||
&Title {
|
||||
margin-bottom: sizes.$size-spacing-x1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&Icon {
|
||||
width: $iconSize;
|
||||
height: $iconSize;
|
||||
margin-right: $iconRightSpacing;
|
||||
|
||||
&Active {
|
||||
color: themes.$color-text-link;
|
||||
}
|
||||
}
|
||||
|
||||
&Text {
|
||||
@include mixin.subtitle2;
|
||||
|
||||
&Active:hover {
|
||||
color: themes.$color-text-link;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&Description {
|
||||
padding-left: calc($iconSize + $iconRightSpacing);
|
||||
color: themes.$color-text-inactive;
|
||||
@include mixin.caption;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div :class="$style.CustomHomeServices">
|
||||
<h2 id="home-services-section-title" :class="$style.CustomHomeServicesTitle">Документация по сервисам</h2>
|
||||
|
||||
<div v-if="Array.isArray(frontmatter.services)" :class="$style.CustomHomeServicesList">
|
||||
<CustomHomeServiceCard v-for="service in frontmatter.services" :key="service.title" :title="service.title"
|
||||
:articles="service.articles" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress';
|
||||
import CustomHomeServiceCard from './CustomHomeServiceCard.vue';
|
||||
|
||||
const { frontmatter } = useData()
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
@use "@beeline/design-tokens/scss/mixin";
|
||||
|
||||
.CustomHomeServices {
|
||||
max-width: 1080px;
|
||||
margin: 0 auto;
|
||||
|
||||
&Title {
|
||||
@include mixin.h2;
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: themes.$color-text-active;
|
||||
}
|
||||
|
||||
&List {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: sizes.$size-spacing-x6;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<i :class="['beeline-icons', `beeline-icons-${icon}`, classes]"></i>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Icons } from '@beeline/design-tokens/js/iconfont/icons';
|
||||
import { computed, useCssModule } from 'vue';
|
||||
|
||||
const { size = 'medium' } = defineProps<{
|
||||
icon: Icons,
|
||||
size?: 'large' | 'medium' | 'small'
|
||||
}>()
|
||||
|
||||
const style = useCssModule()
|
||||
const classes = computed(() => ({
|
||||
[style.CustomIcon]: true,
|
||||
[style.CustomIconLarge]: size === 'large',
|
||||
[style.CustomIconSmall]: size === 'small'
|
||||
}))
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/components/icon";
|
||||
|
||||
.CustomIcon {
|
||||
font-size: icon.$icon-medium-size !important;
|
||||
|
||||
&Large {
|
||||
font-size: icon.$icon-large-size !important;
|
||||
}
|
||||
|
||||
&Small {
|
||||
font-size: icon.$icon-small-size !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
useSessionStorage
|
||||
} from '@vueuse/core'
|
||||
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
||||
import Mark from 'mark.js/src/vanilla.js'
|
||||
import Mark from 'mark.js/dist/mark'
|
||||
import MiniSearch, { type SearchResult } from 'minisearch'
|
||||
import { dataSymbol, inBrowser, useRouter } from 'vitepress'
|
||||
import {
|
||||
|
||||
@@ -45,8 +45,12 @@ watchPostEffect(() => {
|
||||
<div class="container">
|
||||
<div class="title">
|
||||
<CustomNavBarTitle>
|
||||
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
|
||||
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
|
||||
<template #nav-bar-title-before>
|
||||
<slot name="nav-bar-title-before" />
|
||||
</template>
|
||||
<template #nav-bar-title-after>
|
||||
<slot name="nav-bar-title-after" />
|
||||
</template>
|
||||
</CustomNavBarTitle>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ButtonTranslations } from 'vitepress/types/local-search'
|
||||
import { createSearchTranslate } from 'vitepress/dist/client/theme-default/support/translation'
|
||||
|
||||
const defaultTranslations: { button: ButtonTranslations } = {
|
||||
button: {
|
||||
buttonText: 'Поиск',
|
||||
buttonAriaLabel: 'Поиск'
|
||||
}
|
||||
}
|
||||
|
||||
const $t = createSearchTranslate(defaultTranslations)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button type="button" :class="$style.HomeHeroSearchButton" :aria-label="$t('button.buttonAriaLabel')">
|
||||
<span :class="$style.HomeHeroSearchButtonContainer">
|
||||
<img :class="$style.HomeHeroSearchButtonIcon" src="/icons/search.svg" alt="Поиск">
|
||||
<span :class="$style.HomeHeroSearchButtonPlaceholder">{{ $t('button.buttonText') }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@use "@beeline/design-tokens/scss/tokens/themes";
|
||||
@use "@beeline/design-tokens/scss/tokens/globals/sizes";
|
||||
@use "@beeline/design-tokens/scss/mixin";
|
||||
@use "@beeline/design-tokens/scss/tokens/components/search";
|
||||
|
||||
.HomeHeroSearchButton {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
border-radius: search.$search-border-radius;
|
||||
height: search.$search-medium-height;
|
||||
background: search.$search-background-color;
|
||||
|
||||
&Container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
&Icon {
|
||||
margin: 0 search.$search-category-icon-spacing;
|
||||
color: themes.$color-text-inactive;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&Placeholder {
|
||||
@include mixin.body2;
|
||||
color: themes.$color-text-disabled;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
export const NAVBAR_HEIGHT = 64
|
||||
@@ -1,3 +1,8 @@
|
||||
@use "@beeline/design-tokens/scss/iconfont/iconfont" with (
|
||||
$font-path-iconfont: '/fonts/iconfont'
|
||||
);
|
||||
@use "@beeline/design-tokens/scss/iconfont/icons";
|
||||
|
||||
$font-path-beeline-sans: '/fonts/beeline-sans' !default;
|
||||
|
||||
@mixin beeline-sans-font($type, $weight, $style: normal) {
|
||||
|
||||
Reference in New Issue
Block a user