docs: ✏️ 调整文档结构增加文档内容可用宽度,支持收起演示demo (#765)

This commit is contained in:
不如摸鱼去 2024-12-07 23:51:59 +08:00 committed by GitHub
parent b06a7a751b
commit 402f73f6ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
90 changed files with 2828 additions and 1004 deletions

View File

@ -1,7 +1,7 @@
/*
* @Author: weisheng
* @Date: 2023-07-27 10:26:09
* @LastEditTime: 2024-11-09 21:52:24
* @LastEditTime: 2024-12-07 15:20:53
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/config.mts
@ -9,6 +9,7 @@
*/
import { defineConfig } from 'vitepress';
import viteCompression from 'vite-plugin-compression'
import { fileURLToPath, URL } from 'node:url'
import { MarkdownTransform } from './plugins/markdown-transform'
@ -25,7 +26,41 @@ export default defineConfig({
ext: '.gz',
})
],
ssr: { noExternal: ['element-plus'] }
ssr: { noExternal: ['element-plus'] },
resolve: {
alias: [
{
find: /^.*\/VPSidebar\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPSidebar.vue', import.meta.url)
)
},
{
find: /^.*\/VPContent\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPContent.vue', import.meta.url)
)
},
{
find: /^.*\/VPDoc\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPDoc.vue', import.meta.url)
)
},
{
find: /^.*\/VPLocalNav\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPLocalNav.vue', import.meta.url)
)
},
{
find: /^.*\/VPNavBar\.vue$/,
replacement: fileURLToPath(
new URL('./theme/components/VPNavBar.vue', import.meta.url)
)
}
]
}
},
title: `Wot Design Uni`,
description: '一个参照wot-design打造的uni-app组件库',

View File

@ -1,4 +1,5 @@
import type { Plugin } from 'vite'
import { Plugin } from 'vite';
import { camelCase } from '../../../src/uni_modules/wot-design-uni/components/common/util'
import path from 'path'
export function MarkdownTransform(): Plugin {
@ -7,7 +8,7 @@ export function MarkdownTransform(): Plugin {
enforce: 'pre',
async transform(code, id) {
if (!id.endsWith('.md')) return
if (!code.includes('<frame/>')) return
if (!id.includes('/component')) return
const GITHUB_URL = 'https://github.com/Moonofweisheng/wot-design-uni/tree/master'
const componentId = path.basename(id, '.md')
const componentName = `wd-${componentId}`

View File

@ -1,19 +1,18 @@
<!--
* @Author: weisheng
* @Date: 2024-01-07 00:46:50
* @LastEditTime: 2024-01-07 01:57:00
* @LastEditTime: 2024-12-07 14:23:56
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/theme/components/CustomFooter.vue
* 记得注释
-->
<script setup lang="ts">
import { useData } from 'vitepress/dist/client/theme-default/composables/data';
import { useSidebar } from 'vitepress/dist/client/theme-default/composables/sidebar';
import { computed, onMounted, ref } from 'vue';
import { useData} from 'vitepress'
import { useSidebar } from 'vitepress/theme'
const { theme } = useData()
const { theme }:any = useData()
const { hasSidebar } = useSidebar()
const isNetlify = ref<boolean>(false)

View File

@ -0,0 +1,107 @@
<!--
* @Author: weisheng
* @Date: 2024-12-07 18:41:41
* @LastEditTime: 2024-12-07 21:28:08
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/theme/components/VPContent.vue
* 记得注释
-->
<script setup lang="ts">
import { useData } from 'vitepress';
import NotFound from 'vitepress/dist/client/theme-default//NotFound.vue'
import VPDoc from './VPDoc.vue'
import VPHome from 'vitepress/dist/client/theme-default/components/VPHome.vue'
import VPPage from 'vitepress/dist/client/theme-default/components/VPPage.vue'
import { useSidebar } from 'vitepress/theme';
const { page, frontmatter }:any = useData()
const { hasSidebar } = useSidebar()
</script>
<template>
<div
class="VPContent"
id="VPContent"
:class="{
'has-sidebar': hasSidebar,
'is-home': frontmatter.layout === 'home'
}"
>
<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>
</VPPage>
<VPHome v-else-if="frontmatter.layout === 'home'">
<template #home-hero-before><slot name="home-hero-before" /></template>
<template #home-hero-info-before><slot name="home-hero-info-before" /></template>
<template #home-hero-info><slot name="home-hero-info" /></template>
<template #home-hero-info-after><slot name="home-hero-info-after" /></template>
<template #home-hero-actions-after><slot name="home-hero-actions-after" /></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"
/>
<VPDoc v-else>
<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 #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>
</VPDoc>
</div>
</template>
<style scoped>
.VPContent {
flex-grow: 1;
flex-shrink: 0;
margin: var(--vp-layout-top-height, 0px) auto 0;
width: 100%;
}
.VPContent.is-home {
width: 100%;
max-width: 100%;
}
.VPContent.has-sidebar {
margin: 0;
}
.VPNavBar.container{
max-width: none;
}
@media (min-width: 960px) {
.VPContent {
padding-top: var(--vp-nav-height);
}
.VPContent.has-sidebar {
margin: var(--vp-layout-top-height, 0px) 0 0;
padding-left: var(--vp-sidebar-width);
}
}
</style>

View File

@ -0,0 +1,226 @@
<script setup lang="ts">
import { useRoute } from 'vitepress'
import { computed, ref } from 'vue'
import VPDocAside from 'vitepress/dist/client/theme-default/components/VPDocAside.vue'
import VPDocFooter from 'vitepress/dist/client/theme-default/components/VPDocFooter.vue'
import { useData } from 'vitepress';
import { useSidebar } from 'vitepress/theme';
import VPIframe from './VPIframe.vue'
const { theme }: any = useData()
const route = useRoute()
const { hasSidebar, hasAside, leftAside } = useSidebar()
const pageName = computed(() =>
route.path.replace(/[./]+/g, '_').replace(/_html$/, '')
)
const isComponent = computed(() => route.path.startsWith('/component'))
const expanded = ref(true)
</script>
<template>
<div class="VPDoc"
:class="{ 'has-sidebar': hasSidebar, 'has-aside': hasAside, 'is-component': isComponent, 'is-expanded': expanded }">
<slot name="doc-top" />
<div class="container">
<div v-if="hasAside" class="aside" :class="{ 'left-aside': leftAside }">
<div class="aside-curtain" />
<div class="aside-container">
<div class="aside-content">
<VPDocAside>
<template #aside-top>
<slot name="aside-top" />
</template>
<template #aside-bottom>
<slot name="aside-bottom" />
</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>
</VPDocAside>
</div>
</div>
</div>
<div class="content">
<div class="content-container">
<slot name="doc-before" />
<main class="main">
<Content class="vp-doc" :class="[
pageName,
theme.externalLinkIcon && 'external-link-icon-enabled'
]" />
<VPIframe v-if="isComponent" v-model:expanded="expanded" />
</main>
<VPDocFooter>
<template #doc-footer-before>
<slot name="doc-footer-before" />
</template>
</VPDocFooter>
<slot name="doc-after" />
</div>
</div>
</div>
<slot name="doc-bottom" />
</div>
</template>
<style scoped>
.VPDoc {
padding: 32px 24px 96px;
width: 100%;
}
@media (min-width: 768px) {
.VPDoc {
padding: 48px 32px 128px;
}
}
@media (min-width: 960px) {
.VPDoc {
padding: 48px 32px 0;
}
.VPDoc:not(.has-sidebar) .container {
display: flex;
justify-content: center;
max-width: 992px;
}
.VPDoc:not(.has-sidebar) .content {
max-width: 752px;
}
}
@media (min-width: 1280px) {
.VPDoc .container {
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;
}
}
.container {
margin: 0 auto;
width: 100%;
}
.aside {
position: relative;
display: none;
order: 2;
flex-grow: 1;
padding-left: 32px;
width: 100%;
max-width: 256px;
}
.left-aside {
order: 1;
padding-left: unset;
padding-right: 32px;
}
.aside-container {
position: fixed;
top: 0;
padding-top: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + var(--vp-doc-top-height, 0px) + 48px);
width: 224px;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: none;
}
.aside-container::-webkit-scrollbar {
display: none;
}
.aside-curtain {
position: fixed;
bottom: 0;
z-index: 10;
width: 224px;
height: 32px;
background: linear-gradient(transparent, var(--vp-c-bg) 70%);
}
.aside-content {
display: flex;
flex-direction: column;
min-height: calc(100vh - (var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px));
padding-bottom: 32px;
}
.content {
position: relative;
margin: 0 auto;
width: 100%;
}
@media (min-width: 960px) {
.content {
padding: 0 32px 128px;
}
}
@media (min-width: 1280px) {
.content {
order: 1;
margin: 0;
min-width: 640px;
}
}
@media (min-width: 1280px) {
.VPDoc.is-component.is-expanded .container {
padding-right: 358px;
}
.VPDoc.is-component:not(.is-expanded) .container {
padding-right: 48px;
}
}
@media (min-width: 1440px) {
.VPDoc.is-component.is-expanded .container {
padding-right: 424px;
}
.VPDoc.is-component:not(.is-expanded) .container {
padding-right: 64px;
}
}
.content-container {
margin: 0 auto;
}
</style>

View File

@ -0,0 +1,242 @@
<template>
<!-- 主容器根据展开状态和过渡状态添加对应类名 -->
<div v-if="href" class="demo-model" :class="{
'collapsed': !expanded,
'transition-end': transitionEnd
}" @transitionend="onTransitionEnd">
<!-- 头部控制栏 -->
<div class="demo-header">
<ExternalLink :href="href" class="demo-link" :style="`${expanded ? '' : 'height:0;width:0;opacity:0'}`">
</ExternalLink>
<el-icon class="expand-icon" style="cursor: pointer;" @click="toggleExpand">
<component :is="expanded ? Fold : Expand" />
</el-icon>
</div>
<!-- iframe 容器 -->
<div class="iframe-container">
<iframe v-if="expanded" ref="iframe" id="demo" class="iframe" scrolling="auto" frameborder="0" :src="href" />
</div>
</div>
</template>
<script setup lang="ts">
import { Expand, Fold } from '@element-plus/icons-vue'
import { useRoute, useData } from 'vitepress'
import { computed, onMounted, ref, watch } from 'vue'
interface Props {
/** 是否展开状态 */
expanded?: boolean
}
const props = withDefaults(defineProps<Props>(), {
expanded: true
})
//
const baseUrl = ref('')
const iframe = ref<HTMLIFrameElement | null>(null)
const transitionEnd = ref(false)
const emit = defineEmits<{
'update:expanded': [boolean] //
'state-change': [boolean] //
}>()
const route = useRoute()
const vitepressData = useData()
const href = computed(() => {
const path = route.path
const paths = path ? path.split('.')[0].split('/') : []
if (!paths.length) return baseUrl.value
return baseUrl.value + `pages/${kebabToCamel(paths[paths.length - 1])}/Index`
})
// kebab-case camelCase
function kebabToCamel(input: string): string {
return input.replace(/-([a-z])/g, (match, group) => group.toUpperCase())
}
// props.expanded
watch(
() => props.expanded,
(newVal) => {
if (newVal) {
transitionEnd.value = false
}
}
)
// /
function toggleExpand() {
//
emit('update:expanded', !props.expanded)
emit('state-change', !props.expanded)
if (props.expanded) {
transitionEnd.value = false
}
}
//
function onTransitionEnd() {
if (!props.expanded) {
transitionEnd.value = true
}
}
// iframe
function sendMessage() {
if (iframe.value?.contentWindow) {
iframe.value.contentWindow.postMessage(vitepressData.isDark.value, href.value)
}
}
onMounted(() => {
baseUrl.value = process.env.NODE_ENV === 'production'
? `${location.origin}/demo/?timestamp=${new Date().getTime()}#/`
: 'http://localhost:5173/demo/#/'
// iframe
iframe.value?.addEventListener('load', sendMessage)
})
watch(
() => vitepressData.isDark.value,
sendMessage
)
</script>
<style scoped>
::-webkit-scrollbar {
width: 0;
height: 0;
}
.demo-model {
position: fixed;
z-index: 10;
right: 32px;
top: calc(var(--vp-nav-height) + 32px);
width: 330px;
font-size: 16px;
background: var(--vp-c-bg-alt);
border-radius: 12px;
box-shadow: var(--vp-shadow-4);
overflow: hidden;
transition: all 0.3s ease-in-out;
}
.iframe-container {
height: calc(100% - 56px);
overflow: hidden;
transition: all 0.3s ease-in-out;
}
.collapsed .iframe-container {
height: 0;
}
.fade-enter-active,
.fade-leave-active,
.fade-enter,
.fade-leave-to {
display: none;
}
.dark .demo-model {
background: #1b1b1b;
}
.iframe {
height: 100%;
width: 100%;
}
.demo-header {
position: relative;
height: 48px;
width: 100%;
padding: 8px 12px;
border-radius: 6px 6px 0px 0px;
box-sizing: border-box;
background: var(--vp-sidebar-bg-color);
margin-bottom: 8px;
display: flex;
align-items: center;
font-size: 28px;
cursor: pointer;
}
.demo-link {
font-size: 28px !important;
transition: all 0.3s ease-in-out;
position: absolute;
left: 0;
--color: inherit;
fill: currentColor;
color: var(--color);
}
.expand-icon {
position: absolute;
right: 8px;
cursor: pointer;
}
.collapsed {
width: 48px !important;
height: 48px;
border-radius: 12px;
}
.collapsed.transition-end .demo-header {
justify-content: center;
/* 动画结束后居中对齐 */
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to
/* .fade-leave-active in <2.1.8 */
{
opacity: 0;
}
@media screen and (min-width: 1280px) {
.demo-model {
width: 310px;
height: calc(310px * 143.6 / 70.9 + 56px);
right: 48px;
}
.collapsed {
height: 48px;
}
}
@media screen and (min-width: 1440px) {
.demo-model {
width: 360px;
height: calc(360px * 143.6 / 70.9 + 56px);
right: 64px;
}
.collapsed {
height: 48px;
}
}
@media (max-width: 1279px) {
.demo-model {
display: none;
}
}
</style>

View File

@ -0,0 +1,166 @@
<script lang="ts" setup>
import { onContentUpdated } from 'vitepress'
import { useWindowScroll } from '@vueuse/core'
import { computed, onMounted, ref } from 'vue'
import VPLocalNavOutlineDropdown from 'vitepress/dist/client/theme-default/components/VPLocalNavOutlineDropdown.vue'
import { useData } from 'vitepress';
import { useLocalNav, useSidebar } from 'vitepress/theme';
import { getHeaders } from 'vitepress/dist/client/theme-default/composables/outline.js'
defineProps<{
open: boolean
}>()
defineEmits<{
(e: 'open-menu'): void
}>()
const { theme, frontmatter }:any = useData()
const { hasSidebar } = useSidebar()
const { headers } = useLocalNav()
const { y } = useWindowScroll()
const navHeight = ref(0)
onMounted(() => {
navHeight.value = parseInt(
getComputedStyle(document.documentElement).getPropertyValue(
'--vp-nav-height'
)
)
})
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme.value.outline)
})
const empty = computed(() => {
return headers.value.length === 0
})
const emptyAndNoSidebar = computed(() => {
return empty.value && !hasSidebar.value
})
const classes = computed(() => {
return {
VPLocalNav: true,
'has-sidebar': hasSidebar.value,
empty: empty.value,
fixed: emptyAndNoSidebar.value
}
})
</script>
<template>
<div
v-if="frontmatter.layout !== 'home' && (!emptyAndNoSidebar || y >= navHeight)"
:class="classes"
>
<div class="container">
<button
v-if="hasSidebar"
class="menu"
:aria-expanded="open"
aria-controls="VPSidebarNav"
@click="$emit('open-menu')"
>
<span class="vpi-align-left menu-icon"></span>
<span class="menu-text">
{{ theme.sidebarMenuLabel || 'Menu' }}
</span>
</button>
<VPLocalNavOutlineDropdown :headers="headers" :navHeight="navHeight" />
</div>
</div>
</template>
<style scoped>
.VPLocalNav {
position: sticky;
top: 0;
/*rtl:ignore*/
left: 0;
z-index: var(--vp-z-index-local-nav);
border-bottom: 1px solid var(--vp-c-gutter);
padding-top: var(--vp-layout-top-height, 0px);
width: 100%;
background-color: var(--vp-local-nav-bg-color);
}
.VPLocalNav.fixed {
position: fixed;
}
@media (min-width: 960px) {
.VPLocalNav {
top: var(--vp-nav-height);
}
.VPLocalNav.has-sidebar {
padding-left: var(--vp-sidebar-width);
}
.VPLocalNav.empty {
display: none;
}
}
@media (min-width: 1280px) {
.VPLocalNav {
display: none;
}
}
.container {
display: flex;
justify-content: space-between;
align-items: center;
}
.menu {
display: flex;
align-items: center;
padding: 12px 24px 11px;
line-height: 24px;
font-size: 12px;
font-weight: 500;
color: var(--vp-c-text-2);
transition: color 0.5s;
}
.menu:hover {
color: var(--vp-c-text-1);
transition: color 0.25s;
}
@media (min-width: 768px) {
.menu {
padding: 0 32px;
}
}
@media (min-width: 960px) {
.menu {
display: none;
}
}
.menu-icon {
margin-right: 8px;
font-size: 14px;
}
.VPOutlineDropdown {
padding: 12px 24px 11px;
}
@media (min-width: 768px) {
.VPOutlineDropdown {
padding: 12px 32px 11px;
}
}
</style>

View File

@ -0,0 +1,266 @@
<!--
* @Author: weisheng
* @Date: 2024-12-07 12:18:28
* @LastEditTime: 2024-12-07 15:15:28
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/theme/components/VPNavBar.vue
* 记得注释
-->
<script lang="ts" setup>
import { useWindowScroll } from '@vueuse/core'
import { ref, watchPostEffect } from 'vue'
import VPNavBarAppearance from 'vitepress/dist/client/theme-default/components/VPNavBarAppearance.vue'
import VPNavBarExtra from 'vitepress/dist/client/theme-default/components/VPNavBarExtra.vue'
import VPNavBarHamburger from 'vitepress/dist/client/theme-default/components/VPNavBarHamburger.vue'
import VPNavBarMenu from 'vitepress/dist/client/theme-default/components/VPNavBarMenu.vue'
import VPNavBarSearch from 'vitepress/dist/client/theme-default/components/VPNavBarSearch.vue'
import VPNavBarSocialLinks from 'vitepress/dist/client/theme-default/components/VPNavBarSocialLinks.vue'
import VPNavBarTitle from 'vitepress/dist/client/theme-default/components/VPNavBarTitle.vue'
import VPNavBarTranslations from 'vitepress/dist/client/theme-default/components/VPNavBarTranslations.vue'
import { useSidebar } from 'vitepress/theme'
import { useData } from 'vitepress'
const props = defineProps<{
isScreenOpen: boolean
}>()
defineEmits<{
(e: 'toggle-screen'): void
}>()
const { y } = useWindowScroll()
const { hasSidebar } = useSidebar()
const { frontmatter } = useData()
const classes = ref<Record<string, boolean>>({})
watchPostEffect(() => {
classes.value = {
'has-sidebar': hasSidebar.value,
'home': frontmatter.value.layout === 'home',
'top': y.value === 0,
'screen-open': props.isScreenOpen
}
})
</script>
<template>
<div class="VPNavBar" :class="classes">
<div class="wrapper">
<div class="container">
<div class="title">
<VPNavBarTitle>
<template #nav-bar-title-before><slot name="nav-bar-title-before" /></template>
<template #nav-bar-title-after><slot name="nav-bar-title-after" /></template>
</VPNavBarTitle>
</div>
<div class="content">
<div class="content-body">
<slot name="nav-bar-content-before" />
<VPNavBarSearch class="search" />
<VPNavBarMenu class="menu" />
<VPNavBarTranslations class="translations" />
<VPNavBarAppearance class="appearance" />
<VPNavBarSocialLinks class="social-links" />
<VPNavBarExtra class="extra" />
<slot name="nav-bar-content-after" />
<VPNavBarHamburger class="hamburger" :active="isScreenOpen" @click="$emit('toggle-screen')" />
</div>
</div>
</div>
</div>
<div class="divider">
<div class="divider-line" />
</div>
</div>
</template>
<style scoped>
.VPNavBar {
position: relative;
height: var(--vp-nav-height);
pointer-events: none;
white-space: nowrap;
transition: background-color 0.25s;
}
.VPNavBar.screen-open {
transition: none;
background-color: var(--vp-nav-bg-color);
border-bottom: 1px solid var(--vp-c-divider);
}
.VPNavBar:not(.home) {
background-color: var(--vp-nav-bg-color);
}
@media (min-width: 960px) {
.VPNavBar:not(.home) {
background-color: transparent;
}
.VPNavBar:not(.has-sidebar):not(.home.top) {
background-color: var(--vp-nav-bg-color);
}
}
.wrapper {
padding: 0 8px 0 24px;
}
@media (min-width: 768px) {
.wrapper {
padding: 0 32px;
}
}
@media (min-width: 960px) {
.VPNavBar.has-sidebar .wrapper {
padding: 0;
}
}
.container {
display: flex;
justify-content: space-between;
margin: 0 auto;
height: var(--vp-nav-height);
pointer-events: none;
}
.container > .title,
.container > .content {
pointer-events: none;
}
.container :deep(*) {
pointer-events: auto;
}
@media (min-width: 960px) {
.VPNavBar.has-sidebar .container {
max-width: 100%;
}
}
.title {
flex-shrink: 0;
height: calc(var(--vp-nav-height) - 1px);
transition: background-color 0.5s;
}
@media (min-width: 960px) {
.VPNavBar.has-sidebar .title {
position: absolute;
top: 0;
left: 0;
z-index: 2;
padding: 0 32px;
width: var(--vp-sidebar-width);
height: var(--vp-nav-height);
background-color: transparent;
}
}
.content {
flex-grow: 1;
}
@media (min-width: 960px) {
.VPNavBar.has-sidebar .content {
position: relative;
z-index: 1;
padding-right: 32px;
padding-left: var(--vp-sidebar-width);
}
}
.content-body {
display: flex;
justify-content: flex-end;
align-items: center;
height: var(--vp-nav-height);
transition: background-color 0.5s;
}
@media (min-width: 960px) {
.VPNavBar:not(.home.top) .content-body {
position: relative;
background-color: var(--vp-nav-bg-color);
}
.VPNavBar:not(.has-sidebar):not(.home.top) .content-body {
background-color: transparent;
}
}
@media (max-width: 767px) {
.content-body {
column-gap: 0.5rem;
}
}
.menu + .translations::before,
.menu + .appearance::before,
.menu + .social-links::before,
.translations + .appearance::before,
.appearance + .social-links::before {
margin-right: 8px;
margin-left: 8px;
width: 1px;
height: 24px;
background-color: var(--vp-c-divider);
content: "";
}
.menu + .appearance::before,
.translations + .appearance::before {
margin-right: 16px;
}
.appearance + .social-links::before {
margin-left: 16px;
}
.social-links {
margin-right: -8px;
}
.divider {
width: 100%;
height: 1px;
}
@media (min-width: 960px) {
.VPNavBar.has-sidebar .divider {
padding-left: var(--vp-sidebar-width);
}
}
.divider-line {
width: 100%;
height: 1px;
transition: background-color 0.5s;
}
.VPNavBar:not(.home) .divider-line {
background-color: var(--vp-c-gutter);
}
@media (min-width: 960px) {
.VPNavBar:not(.home.top) .divider-line {
background-color: var(--vp-c-gutter);
}
.VPNavBar:not(.has-sidebar):not(.home.top) .divider {
background-color: var(--vp-c-gutter);
}
}
</style>

View File

@ -0,0 +1,130 @@
<script lang="ts" setup>
import { useScrollLock } from '@vueuse/core'
import { inBrowser } from 'vitepress'
import { ref, watch } from 'vue'
import VPSidebarGroup from 'vitepress/dist/client/theme-default/components/VPSidebarGroup.vue'
import { useSidebar } from 'vitepress/theme';
const { sidebarGroups, hasSidebar } = useSidebar()
const props = defineProps<{
open: boolean
}>()
// a11y: focus Nav element when menu has opened
const navEl = ref<HTMLElement | null>(null)
const isLocked = useScrollLock(inBrowser ? document.body : null)
watch(
[props, navEl],
() => {
if (props.open) {
isLocked.value = true
navEl.value?.focus()
} else isLocked.value = false
},
{ immediate: true, flush: 'post' }
)
const key = ref(0)
watch(
sidebarGroups,
() => {
key.value += 1
},
{ deep: true }
)
</script>
<template>
<aside
v-if="hasSidebar"
class="VPSidebar"
:class="{ open }"
ref="navEl"
@click.stop
>
<div class="curtain" />
<nav
class="nav"
id="VPSidebarNav"
aria-labelledby="sidebar-aria-label"
tabindex="-1"
>
<span class="visually-hidden" id="sidebar-aria-label">
Sidebar Navigation
</span>
<slot name="sidebar-nav-before" />
<VPSidebarGroup :items="sidebarGroups" :key="key" />
<slot name="sidebar-nav-after" />
</nav>
</aside>
</template>
<style scoped>
.VPSidebar {
position: fixed;
top: var(--vp-layout-top-height, 0px);
bottom: 0;
left: 0;
z-index: var(--vp-z-index-sidebar);
padding: 32px 32px 96px;
width: var(--vp-sidebar-width);
background-color: var(--vp-sidebar-bg-color);
opacity: 0;
box-shadow: var(--vp-c-shadow-3);
overflow-x: hidden;
overflow-y: auto;
transform: translateX(-100%);
transition: opacity 0.5s, transform 0.25s ease;
overscroll-behavior: contain;
}
.VPSidebar.open {
opacity: 1;
visibility: visible;
transform: translateX(0);
transition: opacity 0.25s,
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
.dark .VPSidebar {
box-shadow: var(--vp-shadow-1);
}
@media (min-width: 960px) {
.VPSidebar {
padding-top: var(--vp-nav-height);
width: var(--vp-sidebar-width);
max-width: 100%;
background-color: var(--vp-sidebar-bg-color);
opacity: 1;
visibility: visible;
box-shadow: none;
transform: translateX(0);
}
}
@media (min-width: 960px) {
.curtain {
position: sticky;
top: -64px;
left: 0;
z-index: 1;
margin-top: calc(var(--vp-nav-height) * -1);
margin-right: -32px;
margin-left: -32px;
height: var(--vp-nav-height);
background-color: var(--vp-sidebar-bg-color);
}
}
.nav {
outline: 0;
}
</style>

View File

@ -1,145 +0,0 @@
<template>
<iframe v-if="href" ref="iframe" id="demo" class="iframe demo-model" scrolling="auto" frameborder="0" :src="href"></iframe>
</template>
<script setup lang="ts">
import { useRoute, useData } from 'vitepress'
import { computed, onMounted, ref, watch } from 'vue'
const baseUrl =
process.env.NODE_ENV === 'production' ? `${location.origin}/demo/?timestamp=${new Date().getTime()}#/` : 'http://localhost:5173/demo/#/'
const route = useRoute()
const href = computed(() => {
const path = route.path
const paths = path ? path.split('.')[0].split('/') : []
let href = ''
if (paths.length) {
href = baseUrl + `pages/${kebabToCamel(paths[paths.length - 1])}/Index`
} else {
href = baseUrl
}
return href
})
const iframe = ref<HTMLIFrameElement | null>(null)
const vitepressData = useData()
onMounted(() => {
iframe.value &&
iframe.value.addEventListener('load', () => {
// iframe
ssendMessage()
})
})
watch(
() => vitepressData.isDark.value,
() => {
ssendMessage()
}
)
function ssendMessage() {
if (iframe.value && iframe.value.contentWindow) {
iframe.value.contentWindow.postMessage(vitepressData.isDark.value, href.value)
}
}
function kebabToCamel(input: string): string {
return input.replace(/-([a-z])/g, (match, group) => group.toUpperCase())
}
</script>
<style scoped>
::-webkit-scrollbar {
width: 0;
height: 0;
}
@media screen and (min-width: 1301px) and (max-width: 1449px) {
.page-demo {
padding-right: 340px;
}
}
@media screen and (min-width: 1450px) and (max-width: 1679px) {
.page-demo {
padding-right: 350px;
}
}
@media screen and (min-width: 1680px) {
.page-demo {
padding-right: 330px;
}
}
@media screen and (min-width: 1920px) {
.page-demo {
padding-right: 370px;
}
}
.demo-model {
font-size: 16px;
background-color: #fff;
width: 330px;
position: fixed;
right: 50%;
z-index: 10;
margin: 0;
right: 12px;
top: 0;
box-sizing: border-box;
background-repeat: no-repeat;
background-size: 100%;
box-shadow: 0 4px 25px 0 rgba(4, 40, 60, 0.18);
overflow: hidden;
background: #eff2f5;
border-radius: 20px;
}
.iframe {
height: 100%;
width: 100%;
border-radius: 20px;
}
@media screen and (min-width: 1200px) {
.demo-model {
width: 310px;
height: calc(310px * 143.6 / 70.9);
top: calc((100vh - 310px * 143.6 / 70.9 - 3.6rem) / 2 + 3.6rem);
}
}
@media (max-width: 1300px) {
.demo-model {
display: none;
}
}
@media screen and (min-width: 1366px) {
.demo-model {
width: 270px;
height: calc(270px * 143.6 / 70.9);
top: calc((100vh - 270px * 143.6 / 70.9 - 3.6rem) / 2 + 3.6rem);
}
}
@media screen and (min-width: 1500px) {
.demo-model {
width: 310px;
height: calc(310px * 143.6 / 70.9);
top: calc((100vh - 310px * 143.6 / 70.9 - 3.6rem) / 2 + 3.6rem);
}
}
@media screen and (min-width: 1920px) {
.demo-model {
width: 360px;
height: calc(360px * 143.6 / 70.9);
top: calc((100vh - 350px * 143.6 / 70.9 - 3.6rem) / 2 + 3.6rem);
}
}
</style>

View File

@ -1,7 +1,7 @@
/*
* @Author: weisheng
* @Date: 2024-10-12 22:09:33
* @LastEditTime: 2024-11-09 23:03:07
* @LastEditTime: 2024-12-07 20:34:33
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/docs/.vitepress/theme/index.ts
@ -18,7 +18,6 @@ import NavBarTitleAfter from './components/NavBarTitleAfter.vue'
import CustomFooter from './components/CustomFooter.vue'
import SvgImage from './components/SvgImage.vue'
import HomeStar from './components/HomeStar.vue'
import frame from './components/frame.vue'
import ExternalLink from './components/ExternalLink.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
@ -36,7 +35,6 @@ export default {
},
enhanceApp({ app }) {
app.component('SvgImage', SvgImage)
app.component('frame', frame)
app.component('ExternalLink',ExternalLink)
app.use(ElementPlus)
},

View File

@ -1,4 +1,3 @@
/**
* Colors
* -------------------------------------------------------------------------- */
@ -40,17 +39,13 @@
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
--vp-home-hero-name-background: -webkit-linear-gradient(120deg,
#4d80f0 30%,
#bd34fe
);
#bd34fe);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
--vp-home-hero-image-background-image: linear-gradient(-45deg,
#bd34fe 50%,
#4d80f0 50%
);
#4d80f0 50%);
--vp-home-hero-image-filter: blur(40px);
}
@ -126,3 +121,15 @@
}
/* side bar */
:root {
--vp-sidebar-width: 338px;
--vp-sidebar-bg-color: var(--vp-c-bg);
}
.dark {
--vp-sidebar-bg-color:var(--vp-c-bg-alt)
}

View File

@ -1,5 +1,3 @@
<frame/>
# ActionSheet 动作面板
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Backtop 回到顶部 `1.2.21`
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Badge 徽标

View File

@ -1,5 +1,3 @@
<frame/>
# Button 按钮
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# CalendarView 日历面板组件
提供日历单选、多选、范围选择、周维度、月维度等功能。可以根据实际业务场景基于该组件进行封装高度定制化组件。

View File

@ -1,5 +1,3 @@
<frame/>
# Calendar 日历选择器
提供日历单选、多选、范围选择、周维度、月维度等功能。

View File

@ -1,5 +1,3 @@
<frame/>
# Card 卡片
## 基本使用

View File

@ -1,5 +1,3 @@
<frame/>
# Cell 单格
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Checkbox 复选框
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Circle 环形进度条 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.19</el-tag>
圆环形的进度条组件,支持进度渐变动画。

View File

@ -1,5 +1,3 @@
<frame/>
# ColPicker 多列选择器
使用多列选择器来做级联,交互效果较好,多列选择器支持无限级选择。

View File

@ -1,5 +1,3 @@
<frame/>
# Collapse 折叠面板
## 基本使用

View File

@ -1,5 +1,3 @@
<frame/>
# ConfigProvider 全局配置
## 介绍

View File

@ -1,5 +1,3 @@
<frame/>
# CountDown 倒计时<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.58</el-tag>
用于实时展示倒计时数值,支持毫秒精度。

View File

@ -1,5 +1,3 @@
<frame/>
# CountTo 数字滚动<el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.8</el-tag>
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Curtain 幕帘
一般用于公告类的图片弹窗。

View File

@ -1,5 +1,3 @@
<frame/>
# DatetimePickerView 日期时间选择器视图

View File

@ -1,5 +1,3 @@
<frame/>
# DatetimePicker 日期时间选择器
为 Picker 组件的封装,在其内部构建好日期时间选项。

View File

@ -1,5 +1,3 @@
<frame/>
# Divider 分割线
:::danger 请注意

View File

@ -1,5 +1,3 @@
<frame/>
# DropMenu 下拉菜单
## 代码演示

View File

@ -1,5 +1,3 @@
<frame/>
# Fab 悬浮按钮
悬浮动作按钮组件,按下可显示一组动作按钮。

View File

@ -1,5 +1,3 @@
<frame/>
# Form 表单 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
用于数据录入、校验,支持输入框、单选框、复选框、文件上传等类型,常见的 form 表单为`单元格`形式的展示,即左侧为表单的标题描述,右侧为表单的输入。

View File

@ -1,5 +1,3 @@
<frame/>
# Gap 间隔槽
一般用于页面布局时代替margin或者padding;或者充当(底部)占位元素。

View File

@ -1,5 +1,3 @@
<frame/>
# Grid 宫格
## 代码演示

View File

@ -1,5 +1,3 @@
<frame/>
# Icon 图标 <el-tag text style="vertical-align: middle;margin-left:8px;" type="warning">0.1.27 更新</el-tag>

View File

@ -1,5 +1,3 @@
<frame/>
# ImgCropper 图片裁剪

View File

@ -1,5 +1,3 @@
<frame/>
# Img 图片
增强版的 img 标签,提供多种图片填充模式,支持图片懒加载、加载完成、加载失败

View File

@ -1,6 +1,4 @@
<frame/>
# IndexBar 索引栏 `1.2.21`
# IndexBar 索引栏 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.2.21</el-tag>
用于列表的索引分类显示和快速定位。

View File

@ -1,5 +1,3 @@
<frame/>
# InputNumber 计数器

View File

@ -1,5 +1,3 @@
<frame/>
# Input 输入框
::: tip 提示

View File

@ -1,5 +1,3 @@
<frame/>
# Keyboard 虚拟键盘 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">1.3.10</el-tag>
虚拟数字键盘,用于输入数字、密码、身份证或车牌号等场景。

View File

@ -1,5 +1,3 @@
<frame/>
# Layout 布局
`Layout` 组件由 `wd-col` 组件和 `wd-row` 组成。

View File

@ -1,5 +1,3 @@
<frame/>
# Loading 加载指示器

View File

@ -1,5 +1,3 @@
<frame/>
# loadmore 加载更多
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# MessageBox 弹框
弹框有三种alert、confirm 和 prompt。

View File

@ -1,5 +1,3 @@
<frame/>
# Navbar 导航栏 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.33</el-tag>
为页面提供导航功能,常用于页面顶部。

View File

@ -1,5 +1,3 @@
<frame/>
# NoticeBar 通知栏
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Notify 消息通知
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# NumberKeyboard 数字键盘
虚拟数字键盘,用于输入数字、密码或身份证等场景。

View File

@ -1,5 +1,3 @@
<frame/>
# Overlay 遮罩层 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.30</el-tag>
创建一个遮罩层,用于强调特定的页面元素,并阻止用户进行其他操作。

View File

@ -1,5 +1,3 @@
<frame/>
# Pagination 分页

View File

@ -1,5 +1,3 @@
<frame/>
# PasswordInput 密码输入框 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
带网格的输入框组件,可以用于输入密码、短信验证码等场景,通常与[数字键盘](./number-keyboard.md)组件配合使用。

View File

@ -1,5 +1,3 @@
<frame/>
# PickerView 选择器视图
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Picker 选择器
Picker 组件为 popup 和 pickerView 的组合。

View File

@ -1,5 +1,3 @@
<frame/>
# Popover 气泡
常用于展示提示信息。

View File

@ -1,5 +1,3 @@
<frame/>
# Popup 弹出层

View File

@ -1,5 +1,3 @@
<frame/>
# Progress 进度条
用于展示操作的当前进度。

View File

@ -1,5 +1,3 @@
<frame/>
# Radio 单选框

View File

@ -1,5 +1,3 @@
<frame/>
# Rate 评分

View File

@ -1,5 +1,3 @@
<frame/>
# Resize 监听元素尺寸变化
当组件包裹的文档流尺寸发生变化时,触发 `size` 事件。一般用于监听 dom 内容更新时导致的 dom 尺寸位置的变化,重新获取 dom 尺寸和位置,进行内容展示的计算操作。

View File

@ -1,5 +1,3 @@
<frame/>
# Search 搜索框
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Segmented 分段器 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.23</el-tag>
## 何时使用

View File

@ -1,5 +1,3 @@
<frame/>
# SelectPicker 单复选选择器 <el-tag text style="vertical-align: middle;margin-left:8px;" type="warning">0.1.34 更新</el-tag>
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Sidebar 侧边导航 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.49</el-tag>
垂直展示的导航栏,用于在不同的内容区域之间进行切换。

View File

@ -1,5 +1,3 @@
<frame/>
# Skeleton 骨架屏
用于等待加载内容所展示的占位图形组合,有动态效果加载效果,减少用户等待焦虑。

View File

@ -1,5 +1,3 @@
<frame/>
# Slider 滑块
支持单向滑块和双向滑块。

View File

@ -1,5 +1,3 @@
<frame/>
# SortButton 排序按钮

View File

@ -1,5 +1,3 @@
<frame/>
# StatusTip 缺省提示
一般用于兜底占位展示。

View File

@ -1,5 +1,3 @@
<frame/>
# Steps 步骤条
:::tip 破坏性更新提醒

View File

@ -1,5 +1,3 @@
<frame/>
# Sticky 粘性布局

View File

@ -1,5 +1,3 @@
<frame/>
# SwipeAction 滑动操作
:::warning

View File

@ -1,5 +1,3 @@
<frame/>
# Swiper 轮播
用于创建轮播,它支持水平和垂直方向的滑动,可以自定义样式和指示器位置,支持视频和图片资源的轮播,支持设置轮播标题和自定义标题样式。

View File

@ -1,5 +1,3 @@
<frame/>
# Switch 开关

View File

@ -1,5 +1,3 @@
<frame/>
# Tabbar 标签栏
底部导航栏,用于在不同页面之间进行切换。

View File

@ -1,5 +1,3 @@
<frame/>
# Table 表格 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.39</el-tag>
用于展示多条结构类似的数据, 可对数据进行排序等操作。

View File

@ -1,5 +1,3 @@
<frame/>
# Tab 标签页
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Tag 标签
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Text 文本
> 1.3.4 版本提供

View File

@ -1,5 +1,3 @@
<frame/>
# Textarea 文本域 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.2.0</el-tag>
用于输入多行文本信息。

View File

@ -1,5 +1,3 @@
<frame/>
# Toast 轻提示
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Tooltip 文字提示
常用于展示提示信息。

View File

@ -1,5 +1,3 @@
<frame/>
# Transition 动画
## 基本用法

View File

@ -1,5 +1,3 @@
<frame/>
# Upload 上传
图片、视频和文件上传组件

View File

@ -1,5 +1,3 @@
<frame/>
# Watermark 水印 <el-tag text style="vertical-align: middle;margin-left:8px;" effect="plain">0.1.16</el-tag>

View File

@ -75,7 +75,6 @@
"@dcloudio/uni-quickapp-webview": "3.0.0-4020420240722002",
"element-plus": "^2.3.9",
"vite-plugin-compression": "^0.5.1",
"vitepress": "^1.0.1",
"vue": "^3.4.38",
"vue-i18n": "^9.2.2"
},
@ -87,6 +86,7 @@
"@dcloudio/uni-cli-shared": "3.0.0-4020420240722002",
"@dcloudio/uni-stacktracey": "3.0.0-4020420240722002",
"@dcloudio/vite-plugin-uni": "3.0.0-4020420240722002",
"@element-plus/icons-vue": "^2.3.1",
"@types/node": "^18.14.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
@ -96,6 +96,7 @@
"@vant/touch-emulator": "^1.4.0",
"@vue/runtime-core": "^3.4.38",
"@vue/tsconfig": "^0.1.3",
"@vueuse/core": "^12.0.0",
"eslint": "^8.36.0",
"eslint-config-prettier": "^8.7.0",
"eslint-plugin-prettier": "^4.2.1",
@ -117,7 +118,8 @@
"uni-read-pages-vite": "^0.0.6",
"unplugin-auto-import": "^0.17.5",
"unplugin-vue-components": "^0.26.0",
"vite": "5.2.8",
"vite": "5.4.11",
"vitepress": "^1.5.0",
"vitest": "^0.30.1",
"vue-eslint-parser": "^9.1.0",
"vue-tsc": "^2.0.29"

2284
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -53,11 +53,16 @@
<wd-radio :value="1">单选框1</wd-radio>
<wd-radio :value="2">单选框2</wd-radio>
</wd-radio-group>
<view class="divider"></view>
<wd-divider dashed></wd-divider>
<wd-radio-group v-model="value6" inline shape="dot">
<wd-radio :value="1">单选框1</wd-radio>
<wd-radio :value="2">单选框2</wd-radio>
</wd-radio-group>
<wd-divider dashed></wd-divider>
<wd-radio-group v-model="value13" inline shape="dot" icon-placement="right">
<wd-radio :value="1">单选框1</wd-radio>
<wd-radio :value="2">单选框2</wd-radio>
</wd-radio-group>
</demo-block>
<demo-block title="修改选中颜色">
@ -132,6 +137,7 @@ const value9 = ref<number>(1)
const value10 = ref<number>(1)
const value11 = ref<number>(1)
const value12 = ref<number>(1)
const value13 = ref<number>(1)
function change(e: any) {
console.log(e)

View File

@ -1,10 +1,10 @@
/*
* @Author: weisheng
* @Date: 2024-03-15 20:40:34
* @LastEditTime: 2024-03-18 16:01:19
* @LastEditTime: 2024-12-07 18:52:34
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-radio\types.ts
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-radio/types.ts
*
*/
import type { PropType } from 'vue'
@ -41,9 +41,11 @@ export const radioProps = {
},
/** 最大宽度 */
maxWidth: String,
/** 图标位置,默认为 left */
/**
*
* : 'left' | 'right' | 'auto'
*/
iconPlacement: {
type: [String, null] as PropType<RadioIconPlacement>,
default: null
type: String as PropType<RadioIconPlacement>
}
}