VEGA-4499: адаптация UI главной страницы документации

This commit is contained in:
Захаров Дмитрий Анатольевич
2025-08-25 11:21:09 +00:00
committed by Речкина Елена Валерьевна
parent 2ce47117e1
commit baca55494c
140 changed files with 2458 additions and 325 deletions
@@ -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>
+35 -37
View File
@@ -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>
+51 -63
View File
@@ -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>
+1
View File
@@ -0,0 +1 @@
export const NAVBAR_HEIGHT = 64
+5
View File
@@ -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) {