mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-11-29 17:58:08 +08:00
epic(website): refactor website (#3426)
* feat(website): init viteperss (#3162) * init vitepress * Finish homepage * relayout page * partial finish * update * update use lang * remove font weight; change font size * docs: optimize docs folder structure * reorganize project * fix ssr issue * fix build issues Co-authored-by: Kevin <sxzz@sxzz.moe> Co-authored-by: zouhang <zouhang@didiglobal.com> * feat(website) integrate with crowdin (#3218) - Introduce Crowdin CLI - Add script for fetching Crowdin token from env - Add script for local development * fix formatting * update codeblocks (#3249) * feat(docs): update website preview script - Update azure preview build script - Eliminate dead links * bootstrap docs * fix homepage layout issue * fix formating * Finish ToC and codepen integration * reorganized files and fix issue that causes demo unavailable after build * feat(docs): migration documentations (#3283) * feat(docs): migration documentations - Move documentations to docs/ * remove unused files * docs: [popconfirm] migrate document (#3285) * feat(docs): migrate documentation from space.md to upload.md (#3292) - Upload documentations acorrodingly * docs: [date-picker] migrate document (#3289) * docs: [date-picker] migrate document * fix: typo * docs: [slider] migrate document (#3287) * docs: migrate documents (#3290) * docs: migrate-datetime-picker * docs: migrate descriptions * docs: migrate dialog * docs: migrate divider * docs: migrate drawer * docs: migrate drapdown * docs: fix drapdown * docs: migrate empty * docs: migrate form * docs: add scoped for style * docs: simplify toRefs * chore: update doc (#3297) * chore: update doc * chore: update doc * chore: update doc * feat(docs): migrate documentations from infinite-scroll to page-header (#3303) - Update docs accordingly - Update CodePen icon to match style - Update component name to match others * docs: migrate documentions (#3293) migrate list: * image * popover * scrollbar * radio * rate * skeleton * select * select-v2 * reault * progress * pagination * chore: update doc (#3305) Co-authored-by: 0song <0song@gmail.com> * Fix broken pipeline * chore: update demo plugin * website perfection * fix hydration bug * docs: update guide (#3342) * WIP docs * docs: update docs * add dark mode * make dev fetch components from local instead of node_modules Co-authored-by: msidolphin <msidolphin@outlook.com> Co-authored-by: Aex <spryti@qq.com> Co-authored-by: 0song <82012629+0song@users.noreply.github.com> Co-authored-by: 0song <0song@gmail.com> Co-authored-by: zouhang <zouhang@didiglobal.com> Co-authored-by: 三咲智子 <sxzz@sxzz.moe> * fix(docs): fix codepen code example issue (#3380) * fix(docs): fix codepen code example issue - Add lang="ts" for all example files - Fix codepen import error * revert changes in resource.vue * feat(docs): complete crowdin integration (#3408) * Update mds for preparing the integration script * deprecate old website * update sponsors and even handler for resize * update build script for preview * fix preview-build error * fix preview-build error * fix preview-build error * fix preview-build error * fix preview-build error * update deploy script and some bugs * Fix existing issue * chore(project): add dev preview workflow * chore(project): rename dev to staging * update the size of toc * update staging-preview script * update preview scripts * enable website build for preview * fix build error * final commitment for update the details * remove azure pipeline * move docs ignores into docs and update date Co-authored-by: Kevin <sxzz@sxzz.moe> Co-authored-by: zouhang <zouhang@didiglobal.com> Co-authored-by: msidolphin <msidolphin@outlook.com> Co-authored-by: Aex <spryti@qq.com> Co-authored-by: 0song <82012629+0song@users.noreply.github.com> Co-authored-by: 0song <0song@gmail.com>
This commit is contained in:
parent
70ebeaaedc
commit
c6bed151a5
30
.github/workflows/master-deploy.yml
vendored
30
.github/workflows/master-deploy.yml
vendored
@ -15,25 +15,41 @@ jobs:
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '12'
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn bootstrap
|
||||
|
||||
- name: Build Website
|
||||
run: yarn website-build
|
||||
- name: Init docs
|
||||
run: cd docs && yarn
|
||||
|
||||
- name: Build Indices
|
||||
run: yarn build:indices
|
||||
- name: Fetch Crowdin token for pulling languages
|
||||
run: yarn init:crowdin
|
||||
env:
|
||||
ALGOLIA_KEY: ${{secrets.ALGOLIA_KEY}}
|
||||
CROWDIN_TOKEN: ${{secrets.CROWDIN_TOKEN}}
|
||||
|
||||
- name: Pull Crowdin translations
|
||||
run: cd docs && yarn crowdin download
|
||||
|
||||
- name: Generate common locale
|
||||
run: yarn docs:gen-locale
|
||||
|
||||
- name: Build website
|
||||
run: yarn docs:build
|
||||
env:
|
||||
DOC_ENV: production
|
||||
|
||||
# - name: Build Indices
|
||||
# run: yarn build:indices
|
||||
# env:
|
||||
# ALGOLIA_KEY: ${{secrets.ALGOLIA_KEY}}
|
||||
|
||||
- name: Deploy
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BRANCH: gh-pages
|
||||
FOLDER: website-dist
|
||||
FOLDER: docs/.vitepress/dist
|
||||
GIT_CONFIG_NAME: ElementPlusBot
|
||||
GIT_CONFIG_EMAIL: hello@element-plus.org
|
||||
COMMIT_MESSAGE: website deploy
|
||||
|
24
.github/workflows/preview-build.yml
vendored
24
.github/workflows/preview-build.yml
vendored
@ -14,25 +14,33 @@ jobs:
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: node_modules
|
||||
key: node_modules-${{ hashFiles('**/yarn.lock') }}
|
||||
node-version: '16'
|
||||
registry-url: https://registry.npmjs.com/
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn bootstrap
|
||||
|
||||
- name: Init docs
|
||||
run: cd docs && yarn
|
||||
|
||||
- name: Generate common locale
|
||||
run: yarn docs:gen-locale
|
||||
|
||||
- name: Build Element Plus
|
||||
run: sh scripts/build.sh
|
||||
|
||||
- name: Build website
|
||||
run: yarn website-build
|
||||
run: yarn docs:build
|
||||
env:
|
||||
DOC_ENV: preview
|
||||
|
||||
# share website dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: website
|
||||
path: website-dist/
|
||||
name: docs
|
||||
path: docs/.vitepress/dist/
|
||||
retention-days: 1
|
||||
|
||||
# write pr.txt for share
|
||||
|
2
.github/workflows/preview-deploy.yml
vendored
2
.github/workflows/preview-deploy.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
with:
|
||||
workflow: ${{ github.event.workflow_run.workflow_id }}
|
||||
workflow_conclusion: success
|
||||
name: website
|
||||
name: docs
|
||||
|
||||
- name: Deploy Site
|
||||
run: |
|
||||
|
55
.github/workflows/staging-preview.yml
vendored
Normal file
55
.github/workflows/staging-preview.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
name: Staging Preview
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn bootstrap
|
||||
|
||||
- name: Compile Element Plus
|
||||
run: sh scripts/build.sh
|
||||
|
||||
- name: Init docs
|
||||
run: cd docs && yarn
|
||||
|
||||
- name: Init Crowdin token
|
||||
run: yarn init:crowdin
|
||||
env:
|
||||
CROWDIN_TOKEN: ${{secrets.CROWDIN_TOKEN}}
|
||||
|
||||
- name: Upload source files
|
||||
run: cd docs && yarn crowdin upload sources
|
||||
|
||||
- name: Pull Crowdin translations
|
||||
run: cd docs && yarn crowdin download
|
||||
|
||||
- name: generate common locale
|
||||
run: yarn docs:gen-locale
|
||||
|
||||
- name: Build website
|
||||
run: yarn docs:build
|
||||
env:
|
||||
DOC_ENV: staging
|
||||
|
||||
- name: Deploy Site
|
||||
run: |
|
||||
export DEPLOY_DOMAIN=https://staging-preview-element-plus.surge.sh
|
||||
echo "Deploy to $DEPLOY_DOMAIN"
|
||||
npx surge --project ./docs/.vitepress/dist/ --domain $DEPLOY_DOMAIN --token $SURGE_TOKEN
|
||||
env:
|
||||
SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
|
17
README.md
17
README.md
@ -12,11 +12,19 @@
|
||||
<br>
|
||||
</p>
|
||||
|
||||
<p align="center">Element Plus - A Vue.js 3.0 UI library</p>
|
||||
<p align="center">Element Plus - A Vue.js 3 UI library</p>
|
||||
|
||||
- 💪 Vue 3.0 Composition API
|
||||
- 💪 Vue 3 Composition API
|
||||
- 🔥 Written in TypeScript
|
||||
|
||||
## Archived website
|
||||
|
||||
If you are looking for previous version website, here is the link.
|
||||
|
||||
[Element Plus Documentation Archived](https://github.com/element-plus/doc-archive)
|
||||
|
||||
The new website is launched at 17th Sep 2021.
|
||||
|
||||
## Status: Beta
|
||||
|
||||
This project is still under heavy development. Feel free to join us and make your first pull request.
|
||||
@ -47,6 +55,11 @@ This project is still under heavy development. Feel free to join us and make you
|
||||
|
||||
---
|
||||
|
||||
## Translations
|
||||
|
||||
Element Plus is translated to multiple languages, you can click the badge to help up update the translation or apply to become
|
||||
a proofreader [![Crowdin](https://badges.crowdin.net/element-plus/localized.svg)](https://crowdin.com/project/element-plus)
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find for more details, API, and other docs on [https://element-plus.org](https://element-plus.org/)
|
||||
|
@ -5,8 +5,6 @@ import { epRoot } from './paths'
|
||||
|
||||
const { name, version } = require(path.resolve(epRoot, './package.json'))
|
||||
|
||||
import icon from '../website/icon.json'
|
||||
const icons = icon.map((item) => 'el-icon-' + item).join('/')
|
||||
const tagVer = process.env.TAG_VERSION
|
||||
const _version = tagVer
|
||||
? tagVer.startsWith('v')
|
||||
@ -18,7 +16,7 @@ helper({
|
||||
name,
|
||||
version: _version,
|
||||
entry:
|
||||
'website/docs/en-US/!(custom-theme|datetime-picker|i18n|installation|message-box|message|migration-from-2.x|notification|quickstart|transition|typography).md',
|
||||
'docs/en-US/!(custom-theme|datetime-picker|i18n|installation|message-box|message|migration-from-2.x|notification|quickstart|transition|typography).md',
|
||||
outDir: 'dist/element-plus',
|
||||
reComponentName,
|
||||
reDocUrl,
|
||||
@ -53,9 +51,7 @@ function reAttribute(value, key, item) {
|
||||
const _value = value.match(/^\*\*(.*)\*\*$/)
|
||||
const str = _value ? _value[1] : value
|
||||
|
||||
if (key === 'Accepted Values' && /icon/i.test(item[0])) {
|
||||
return icons
|
||||
} else if (key === 'Name' && /^(-|—)$/.test(str)) {
|
||||
if (key === 'Name' && /^(-|—)$/.test(str)) {
|
||||
return 'default'
|
||||
} else if (str === '' || /^(-|—)$/.test(str)) {
|
||||
return undefined
|
||||
|
@ -28,7 +28,9 @@ const langs = {
|
||||
;['zh-CN', 'en-US', 'es', 'fr-FR', 'jp'].forEach((lang) => {
|
||||
const indexName = langs[lang]
|
||||
const index = client.initIndex(indexName)
|
||||
index.clearObjects().then(() => {
|
||||
index
|
||||
.clearObjects()
|
||||
.then(() => {
|
||||
const files = fg.sync(`website/docs/${lang}/*.md`)
|
||||
let indices: Index[] = []
|
||||
files.forEach((file) => {
|
||||
@ -49,30 +51,17 @@ const langs = {
|
||||
.split('\n')
|
||||
.filter((part) => !!part)
|
||||
)
|
||||
.map((match) => {
|
||||
const length = match.length
|
||||
if (length > 2) {
|
||||
const desc = match.slice(1, length).join('')
|
||||
return [match[0], desc]
|
||||
}
|
||||
return match
|
||||
})
|
||||
let i = 0
|
||||
indices = indices.concat(
|
||||
matches.map((match) => {
|
||||
const title = match[0].replace(/#{2,4}/, '').trim()
|
||||
const index = { component, title } as Index
|
||||
index.anchor = slugify(title)
|
||||
index.content = (match[1] || title).replace(/<[^>]+>/g, '')
|
||||
index.path = path
|
||||
index.sort = i++
|
||||
return index
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
index.saveObjects(indices, {
|
||||
index
|
||||
.saveObjects(indices, {
|
||||
autoGenerateObjectIDIfNotExist: true,
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e)
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e)
|
||||
})
|
||||
})
|
||||
|
24
build/crowdin-credentials.ts
Normal file
24
build/crowdin-credentials.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import chalk from 'chalk'
|
||||
|
||||
const credentialPlaceholder = 'API_TOKEN_PLACEHOLDER'
|
||||
|
||||
const CREDENTIAL = process.env.CROWDIN_TOKEN
|
||||
|
||||
;(async () => {
|
||||
console.info(chalk.cyan('Fetching Crowdin credential'))
|
||||
const configPath = path.resolve(__dirname, '../docs/crowdin.yml')
|
||||
try {
|
||||
const file = await fs.promises.readFile(configPath, {
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
await fs.promises.writeFile(
|
||||
configPath,
|
||||
file.replace(credentialPlaceholder, CREDENTIAL!)
|
||||
)
|
||||
console.info(chalk.green('Crowdin credential update successfully'))
|
||||
} catch (e) {
|
||||
throw e
|
||||
}
|
||||
})()
|
110
build/crowdin-generate.ts
Normal file
110
build/crowdin-generate.ts
Normal file
@ -0,0 +1,110 @@
|
||||
/* eslint-disable */
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import chalk from 'chalk'
|
||||
|
||||
import { docRoot } from './paths'
|
||||
|
||||
// NB: this file is only for generating files that enables developers to develop the website.
|
||||
const componentLocaleRoot = path.resolve(docRoot, '.vitepress/crowdin')
|
||||
|
||||
const exists = 'File already exists'
|
||||
|
||||
async function main() {
|
||||
const localeOutput = path.resolve(docRoot, '.vitepress/i18n')
|
||||
if (fs.existsSync(localeOutput)) {
|
||||
throw new Error(exists)
|
||||
}
|
||||
console.log(chalk.cyan('Starting for build doc for developing'))
|
||||
// all language should be identical since it is mirrored from crowdin.
|
||||
const dirs = await fs.promises.readdir(componentLocaleRoot, {
|
||||
withFileTypes: true,
|
||||
})
|
||||
const languages = dirs.map((dir) => dir.name)
|
||||
const langWithoutEn = languages.filter((l) => l !== 'en-US')
|
||||
|
||||
await fs.promises.mkdir(localeOutput)
|
||||
|
||||
// build lang.json for telling `header>language-select` how many languages are there
|
||||
await fs.promises.writeFile(
|
||||
path.resolve(localeOutput, 'lang.json'),
|
||||
JSON.stringify(languages),
|
||||
{
|
||||
encoding: 'utf-8',
|
||||
}
|
||||
)
|
||||
|
||||
// loop through en-US
|
||||
|
||||
const enUS = path.resolve(componentLocaleRoot, 'en-US')
|
||||
// we do not include en-US since we are currently using it as template
|
||||
const languagePaths = langWithoutEn.map((l) => {
|
||||
return {
|
||||
name: l,
|
||||
pathname: path.resolve(componentLocaleRoot, l),
|
||||
}
|
||||
})
|
||||
|
||||
console.log(languagePaths)
|
||||
await traverseDir(enUS, languagePaths, localeOutput)
|
||||
}
|
||||
|
||||
async function traverseDir(dir, paths, targetPath) {
|
||||
const contents = await fs.promises.readdir(dir, { withFileTypes: true })
|
||||
|
||||
await Promise.all(
|
||||
contents.map(async (c) => {
|
||||
if (c.isDirectory()) {
|
||||
await fs.promises.mkdir(path.resolve(targetPath, c.name), {
|
||||
recursive: true,
|
||||
})
|
||||
|
||||
return traverseDir(
|
||||
path.resolve(dir, c.name),
|
||||
paths.map((p) => {
|
||||
return {
|
||||
...p,
|
||||
pathname: path.resolve(p.pathname, c.name),
|
||||
}
|
||||
}),
|
||||
path.resolve(targetPath, c.name)
|
||||
)
|
||||
} else if (c.isFile()) {
|
||||
const content = require(path.resolve(dir, c.name))
|
||||
|
||||
const contentToWrite = {
|
||||
'en-US': content,
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
paths.map(async (p) => {
|
||||
const content = require(path.resolve(p.pathname, c.name))
|
||||
|
||||
contentToWrite[p.name] = content
|
||||
})
|
||||
)
|
||||
|
||||
return fs.promises.writeFile(
|
||||
path.resolve(targetPath, c.name),
|
||||
JSON.stringify(contentToWrite, null, 2),
|
||||
{
|
||||
encoding: 'utf-8',
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => {
|
||||
console.log(chalk.green('Locale for website development generated'))
|
||||
})
|
||||
.catch((e) => {
|
||||
if (e.message === exists) {
|
||||
// do nothing
|
||||
} else {
|
||||
console.log(chalk.red(e.message))
|
||||
throw e
|
||||
}
|
||||
})
|
@ -10,3 +10,4 @@ export const directiveRoot = path.resolve(pkgRoot, './directives')
|
||||
export const epRoot = path.resolve(pkgRoot, './element-plus')
|
||||
export const utilRoot = path.resolve(pkgRoot, './utils')
|
||||
export const buildOutput = path.resolve(projRoot, './dist')
|
||||
export const docRoot = path.resolve(projRoot, './docs')
|
||||
|
12
docs/.gitignore
vendored
Normal file
12
docs/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
.vitepress/*.js
|
||||
|
||||
.vitepress/i18n/*
|
||||
temp
|
||||
zh-CN
|
||||
fr-FR
|
||||
es-ES
|
||||
ja-JP
|
||||
|
||||
.vitepress/crowdin/*
|
||||
|
||||
!.vitepress/crowdin/en-US
|
120
docs/.vitepress/config.js
Normal file
120
docs/.vitepress/config.js
Normal file
@ -0,0 +1,120 @@
|
||||
/* eslint-disable */
|
||||
const sidebars = require('./sidebars')
|
||||
const nav = require('./nav')
|
||||
const mdPlugin = require('./plugins')
|
||||
const features = require('./features')
|
||||
|
||||
const transformer = () => {
|
||||
return {
|
||||
props: [],
|
||||
needRuntime: true,
|
||||
}
|
||||
}
|
||||
|
||||
const buildTransformers = () => {
|
||||
const transformers = {}
|
||||
const directives = [
|
||||
'infinite-scroll',
|
||||
'loading',
|
||||
'popover',
|
||||
'click-outside',
|
||||
'repeat-click',
|
||||
'trap-focus',
|
||||
'mousewheel',
|
||||
'resize',
|
||||
]
|
||||
directives.forEach((k) => {
|
||||
transformers[k] = transformer
|
||||
})
|
||||
|
||||
return transformers
|
||||
}
|
||||
console.log(process.env.DOC_ENV)
|
||||
|
||||
module.exports = {
|
||||
title: 'ElementPlus',
|
||||
head: [
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'icon',
|
||||
href: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
|
||||
[
|
||||
'link',
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: '//fonts.loli.net/css?family=Inter:300,400,500,600|Open+Sans:400,600;display=swap',
|
||||
},
|
||||
],
|
||||
features.theme
|
||||
? [
|
||||
'script',
|
||||
{},
|
||||
require('fs').readFileSync(
|
||||
require('path').resolve(__dirname, './darkmode.js'),
|
||||
'utf-8'
|
||||
),
|
||||
]
|
||||
: [],
|
||||
],
|
||||
themeConfig: {
|
||||
repo: 'element-plus/element-plus',
|
||||
docsDir: 'docs',
|
||||
|
||||
editLinks: true,
|
||||
editLinkText: 'Edit this page on GitHub',
|
||||
lastUpdated: 'Last Updated',
|
||||
|
||||
logo: '/images/element-plus-logo.svg',
|
||||
logoSmall: '/images/element-plus-logo-small.svg',
|
||||
sidebars,
|
||||
nav,
|
||||
agolia: {
|
||||
apiKey: 'e32c681af38f324039e81d81834e70b8',
|
||||
appId: '7DCTSU0WBW',
|
||||
},
|
||||
features,
|
||||
},
|
||||
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
mdPlugin(md)
|
||||
},
|
||||
},
|
||||
vue: {
|
||||
template: {
|
||||
ssr: true,
|
||||
compilerOptions: {
|
||||
directiveTransforms: buildTransformers(),
|
||||
},
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
sourcemap: true,
|
||||
...(process.env.DOC_ENV === 'production'
|
||||
? {}
|
||||
: {
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^element-plus$/,
|
||||
replacement: require('path').resolve(
|
||||
__dirname,
|
||||
'../../dist/element-plus/es/index'
|
||||
),
|
||||
},
|
||||
{
|
||||
find: /^element-plus\/lib\/utils\/(.*)/,
|
||||
replacement: require('path').resolve(
|
||||
__dirname,
|
||||
'../../dist/element-plus/es/utils/$1'
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
10
docs/.vitepress/crowdin/en-US/component/demo-block.json
Normal file
10
docs/.vitepress/crowdin/en-US/component/demo-block.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"view-source": "View source",
|
||||
"edit-on-github": "Edit on Github",
|
||||
"edit-in-codepen": "Edit in Codepen.io",
|
||||
"copy-button-text": "Copy",
|
||||
"switch-button-option-text": "Switch to Options API",
|
||||
"switch-button-setup-text": "Switch to Composition API",
|
||||
"copy-success": "Copied!",
|
||||
"copy-error": "This browser does not support automatic copy!"
|
||||
}
|
4
docs/.vitepress/crowdin/en-US/component/icons.json
Normal file
4
docs/.vitepress/crowdin/en-US/component/icons.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"copy-success": "Copied!",
|
||||
"copy-error": "Your browser does not support copy :("
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Last Updated"
|
||||
}
|
5
docs/.vitepress/crowdin/en-US/component/search.json
Normal file
5
docs/.vitepress/crowdin/en-US/component/search.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"search": "Search",
|
||||
"empty": "No results",
|
||||
"index": "en"
|
||||
}
|
4
docs/.vitepress/crowdin/en-US/component/sponsor.json
Normal file
4
docs/.vitepress/crowdin/en-US/component/sponsor.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"title": "Sponsors",
|
||||
"sponsoredBy": "Sponsored by"
|
||||
}
|
12
docs/.vitepress/crowdin/en-US/component/sponsors.json
Normal file
12
docs/.vitepress/crowdin/en-US/component/sponsors.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"name": "bit",
|
||||
"img": "/images/bit.svg",
|
||||
"url": "https://bit.dev/?from=element-ui"
|
||||
},
|
||||
{
|
||||
"name": "renren.io",
|
||||
"img": "/images/renren.png",
|
||||
"url": "https://www.renren.io/?from=element-ui"
|
||||
}
|
||||
]
|
3
docs/.vitepress/crowdin/en-US/component/translation.json
Normal file
3
docs/.vitepress/crowdin/en-US/component/translation.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"help": "Help Translate 😉"
|
||||
}
|
13
docs/.vitepress/crowdin/en-US/layouts/footer.json
Normal file
13
docs/.vitepress/crowdin/en-US/layouts/footer.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"links": "Links",
|
||||
"repo": "GitHub",
|
||||
"community": "Community",
|
||||
"changelog": "Changelog",
|
||||
"theme": "Online Theme Roller",
|
||||
"faq": "FAQ",
|
||||
"gitter": "Gitter",
|
||||
"starter": "Starter kit",
|
||||
"feedback": "Feedback",
|
||||
"contribution": "Contribution",
|
||||
"eleme": "Eleme"
|
||||
}
|
7
docs/.vitepress/crowdin/en-US/layouts/header.json
Normal file
7
docs/.vitepress/crowdin/en-US/layouts/header.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"guide": "Guide",
|
||||
"components": "Component",
|
||||
"theme": "Theme",
|
||||
"resource": "Resource",
|
||||
"backToTop": "Back to top"
|
||||
}
|
302
docs/.vitepress/crowdin/en-US/pages/component.json
Normal file
302
docs/.vitepress/crowdin/en-US/pages/component.json
Normal file
@ -0,0 +1,302 @@
|
||||
{
|
||||
"basic": {
|
||||
"text": "Basic",
|
||||
"children": [
|
||||
{
|
||||
"link": "/border",
|
||||
"text": "Border"
|
||||
},
|
||||
{
|
||||
"link": "/button",
|
||||
"text": "Button"
|
||||
},
|
||||
{
|
||||
"link": "/color",
|
||||
"text": "Color"
|
||||
},
|
||||
{
|
||||
"link": "/container",
|
||||
"text": "Layout Container"
|
||||
},
|
||||
{
|
||||
"link": "/icon",
|
||||
"text": "Icon"
|
||||
},
|
||||
{
|
||||
"link": "/layout",
|
||||
"text": "Layout"
|
||||
},
|
||||
{
|
||||
"link": "/link",
|
||||
"text": "Link"
|
||||
},
|
||||
{
|
||||
"link": "/scrollbar",
|
||||
"text": "Scrollbar"
|
||||
},
|
||||
{
|
||||
"link": "/space",
|
||||
"text": "Space"
|
||||
},
|
||||
{
|
||||
"link": "/typography",
|
||||
"text": "Typography"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"text": "Configuration",
|
||||
"children": [
|
||||
{
|
||||
"link": "/config-provider",
|
||||
"text": "Config Provider"
|
||||
}
|
||||
]
|
||||
},
|
||||
"form": {
|
||||
"text": "Form",
|
||||
"children": [
|
||||
{
|
||||
"link": "/cascader",
|
||||
"text": "Cascader"
|
||||
},
|
||||
{
|
||||
"link": "/checkbox",
|
||||
"text": "Checkbox"
|
||||
},
|
||||
{
|
||||
"link": "/color-picker",
|
||||
"text": "Color Picker"
|
||||
},
|
||||
{
|
||||
"link": "/date-picker",
|
||||
"text": "Date Picker"
|
||||
},
|
||||
{
|
||||
"link": "/datetime-picker",
|
||||
"text": "DateTime Picker"
|
||||
},
|
||||
{
|
||||
"link": "/form",
|
||||
"text": "Form"
|
||||
},
|
||||
{
|
||||
"link": "/input",
|
||||
"text": "Input"
|
||||
},
|
||||
{
|
||||
"link": "/input-number",
|
||||
"text": "Input Number"
|
||||
},
|
||||
{
|
||||
"link": "/radio",
|
||||
"text": "Radio"
|
||||
},
|
||||
{
|
||||
"link": "/rate",
|
||||
"text": "Rate"
|
||||
},
|
||||
{
|
||||
"link": "/select",
|
||||
"text": "Select"
|
||||
},
|
||||
{
|
||||
"link": "/select-v2",
|
||||
"text": "Virtualized Select"
|
||||
},
|
||||
{
|
||||
"link": "/slider",
|
||||
"text": "Slider"
|
||||
},
|
||||
{
|
||||
"link": "/switch",
|
||||
"text": "Switch"
|
||||
},
|
||||
{
|
||||
"link": "/time-picker",
|
||||
"text": "Time Picker"
|
||||
},
|
||||
{
|
||||
"link": "/time-select",
|
||||
"text": "Time Select"
|
||||
},
|
||||
{
|
||||
"link": "/transfer",
|
||||
"text": "Transfer"
|
||||
},
|
||||
{
|
||||
"link": "/upload",
|
||||
"text": "Upload"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"data": {
|
||||
"text": "Data",
|
||||
"children": [
|
||||
{
|
||||
"link": "/avatar",
|
||||
"text": "Avatar"
|
||||
},
|
||||
{
|
||||
"link": "/badge",
|
||||
"text": "Badge"
|
||||
},
|
||||
{
|
||||
"link": "/calendar",
|
||||
"text": "Calendar"
|
||||
},
|
||||
{
|
||||
"link": "/card",
|
||||
"text": "Card"
|
||||
},
|
||||
{
|
||||
"link": "/carousel",
|
||||
"text": "Carousel"
|
||||
},
|
||||
{
|
||||
"link": "/collapse",
|
||||
"text": "Collapse"
|
||||
},
|
||||
{
|
||||
"link": "/descriptions",
|
||||
"text": "Descriptions"
|
||||
},
|
||||
{
|
||||
"link": "/empty",
|
||||
"text": "Empty"
|
||||
},
|
||||
{
|
||||
"link": "/image",
|
||||
"text": "Image"
|
||||
},
|
||||
{
|
||||
"link": "/infinite-scroll",
|
||||
"text": "Infinite Scroll"
|
||||
},
|
||||
{
|
||||
"link": "/pagination",
|
||||
"text": "Pagination"
|
||||
},
|
||||
{
|
||||
"link": "/progress",
|
||||
"text": "Progress"
|
||||
},
|
||||
{
|
||||
"link": "/result",
|
||||
"text": "Result"
|
||||
},
|
||||
{
|
||||
"link": "/skeleton",
|
||||
"text": "Skeleton"
|
||||
},
|
||||
{
|
||||
"link": "/table",
|
||||
"text": "Table"
|
||||
},
|
||||
{
|
||||
"link": "/tag",
|
||||
"text": "Tag"
|
||||
},
|
||||
{
|
||||
"link": "/timeline",
|
||||
"text": "Timeline"
|
||||
},
|
||||
{
|
||||
"link": "/tree",
|
||||
"text": "Tree"
|
||||
}
|
||||
]
|
||||
},
|
||||
"navigation": {
|
||||
"text": "Navigation",
|
||||
"children": [
|
||||
{
|
||||
"link": "/affix",
|
||||
"text": "Affix"
|
||||
},
|
||||
{
|
||||
"link": "/breadcrumb",
|
||||
"text": "Breadcrumb"
|
||||
},
|
||||
{
|
||||
"link": "/dropdown",
|
||||
"text": "Dropdown"
|
||||
},
|
||||
{
|
||||
"link": "/menu",
|
||||
"text": "NavMenu"
|
||||
},
|
||||
{
|
||||
"link": "/page-header",
|
||||
"text": "Page Header"
|
||||
},
|
||||
{
|
||||
"link": "/steps",
|
||||
"text": "Steps"
|
||||
},
|
||||
{
|
||||
"link": "/tabs",
|
||||
"text": "Tabs"
|
||||
}
|
||||
]
|
||||
},
|
||||
"feedback": {
|
||||
"text": "Feedback",
|
||||
"children": [
|
||||
{
|
||||
"link": "/alert",
|
||||
"text": "Alert"
|
||||
},
|
||||
{
|
||||
"link": "/dialog",
|
||||
"text": "Dialog"
|
||||
},
|
||||
{
|
||||
"link": "/drawer",
|
||||
"text": "Drawer"
|
||||
},
|
||||
{
|
||||
"link": "/loading",
|
||||
"text": "Loading"
|
||||
},
|
||||
{
|
||||
"link": "/message",
|
||||
"text": "Message"
|
||||
},
|
||||
{
|
||||
"link": "/message-box",
|
||||
"text": "Message Box"
|
||||
},
|
||||
{
|
||||
"link": "/notification",
|
||||
"text": "Notification"
|
||||
},
|
||||
{
|
||||
"link": "/popconfirm",
|
||||
"text": "Pop Confirm"
|
||||
},
|
||||
{
|
||||
"link": "/popover",
|
||||
"text": "Popover"
|
||||
},
|
||||
{
|
||||
"link": "/tooltip",
|
||||
"text": "Tooltip"
|
||||
}
|
||||
]
|
||||
},
|
||||
"others": {
|
||||
"text": "Others",
|
||||
"children": [
|
||||
{
|
||||
"link": "/backtop",
|
||||
"text": "Backtop"
|
||||
},
|
||||
{
|
||||
"link": "/divider",
|
||||
"text": "Divider"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
26
docs/.vitepress/crowdin/en-US/pages/guide.json
Normal file
26
docs/.vitepress/crowdin/en-US/pages/guide.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"intro": {
|
||||
"text": "Basics",
|
||||
"children": [
|
||||
{ "text": "Design", "link": "/guide/design" },
|
||||
{ "text": "Navigation", "link": "/guide/nav" },
|
||||
{
|
||||
"text": "Installation",
|
||||
"link": "/guide/installation"
|
||||
},
|
||||
{
|
||||
"text": "Quick Start",
|
||||
"link": "/guide/quickstart"
|
||||
}
|
||||
]
|
||||
},
|
||||
"advanced": {
|
||||
"text": "Advanced",
|
||||
"children": [
|
||||
{ "text": "i18n", "link": "/guide/i18n" },
|
||||
{ "text": "Migration", "link": "/guide/migration" },
|
||||
{ "text": "Theming", "link": "/guide/theming" },
|
||||
{ "text": "Built-in Transitions", "link": "/guide/transitions" }
|
||||
]
|
||||
}
|
||||
}
|
19
docs/.vitepress/crowdin/en-US/pages/home.json
Normal file
19
docs/.vitepress/crowdin/en-US/pages/home.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"1": "A Desktop UI Library",
|
||||
"2": "Element Plus, a Vue 3.0 based component library for developers, designers and product managers",
|
||||
"3": "Guide",
|
||||
"4": "Understand the design guidelines, helping designers build product that's logically sound, reasonably structured and easy to use.",
|
||||
"5": "View Detail",
|
||||
"6": "Component",
|
||||
"7": "Experience interaction details by strolling through component demos. Use encapsulated code to improve developing efficiency.",
|
||||
"8": "Resource",
|
||||
"9": "Download relevant design resources for shaping page prototype or visual draft, increasing design efficiency.",
|
||||
"10": "Theme",
|
||||
"11": "Online theme roller, visualize custom and manage site themes and component styles",
|
||||
"12": "Theme customization is available!",
|
||||
"13": "Click here",
|
||||
"14": "Make your own theme",
|
||||
"lang": "en-US",
|
||||
"titleSize": "34",
|
||||
"paraSize": "18"
|
||||
}
|
5
docs/.vitepress/crowdin/en-US/pages/not-found.json
Normal file
5
docs/.vitepress/crowdin/en-US/pages/not-found.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"title": "Resource not found",
|
||||
"button-title": "Back to Home",
|
||||
"desc": "The page you requested did not exist"
|
||||
}
|
17
docs/.vitepress/crowdin/en-US/pages/sidebar.json
Normal file
17
docs/.vitepress/crowdin/en-US/pages/sidebar.json
Normal file
@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"text": "Guide",
|
||||
"link": "/guide/design",
|
||||
"activeMatch": "/guide/"
|
||||
},
|
||||
{
|
||||
"text": "Component",
|
||||
"link": "/component/border",
|
||||
"activeMatch": "/component/"
|
||||
},
|
||||
{
|
||||
"text": "Resource",
|
||||
"link": "/resource/",
|
||||
"activeMatch": "/resource/"
|
||||
}
|
||||
]
|
10
docs/.vitepress/darkmode.js
Normal file
10
docs/.vitepress/darkmode.js
Normal file
@ -0,0 +1,10 @@
|
||||
;(() => {
|
||||
const saved = localStorage.getItem('preferred_theme')
|
||||
if (
|
||||
saved === 'auto'
|
||||
? window.matchMedia(`(prefers-color-scheme: dark)`).matches
|
||||
: saved === 'dark'
|
||||
) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
})()
|
3
docs/.vitepress/features.js
Normal file
3
docs/.vitepress/features.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
theme: false,
|
||||
}
|
19
docs/.vitepress/nav.js
Normal file
19
docs/.vitepress/nav.js
Normal file
@ -0,0 +1,19 @@
|
||||
const navLocale = require('./i18n/pages/sidebar.json')
|
||||
const { ensureLang } = require('./site-utils')
|
||||
|
||||
// Mapping the first sub link to the nav link to avoid 404 error.
|
||||
|
||||
function getNav() {
|
||||
const nav = {}
|
||||
Object.entries(navLocale).forEach(([lang, val]) => {
|
||||
nav[lang] = Object.values(val).map((item) => {
|
||||
return {
|
||||
...item,
|
||||
link: `${ensureLang(lang)}${item.link}`,
|
||||
}
|
||||
})
|
||||
})
|
||||
return nav
|
||||
}
|
||||
|
||||
module.exports = getNav()
|
77
docs/.vitepress/plugins.js
Normal file
77
docs/.vitepress/plugins.js
Normal file
@ -0,0 +1,77 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const mdContainer = require('markdown-it-container')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { highlight } = require('vitepress/dist/node/markdown/plugins/highlight')
|
||||
const { parse } = require('@vue/compiler-sfc')
|
||||
|
||||
const scriptSetupRE = /<\s*script[^>]*\bsetup\b[^>]*/
|
||||
|
||||
module.exports = (md) => {
|
||||
md.use(mdContainer, 'demo', {
|
||||
validate(params) {
|
||||
return params.trim().match(/^demo\s*(.*)$/)
|
||||
},
|
||||
render(tokens, idx) {
|
||||
const data = md.__data
|
||||
const hoistedTags = data.hoistedTags || (data.hoistedTags = [])
|
||||
|
||||
const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/)
|
||||
if (tokens[idx].nesting === 1) {
|
||||
const description = m && m.length > 1 ? m[1] : ''
|
||||
const content =
|
||||
tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''
|
||||
|
||||
const sourceFileToken = tokens[idx + 2]
|
||||
let source = ''
|
||||
const sourceFile = sourceFileToken.children[0].content
|
||||
if (sourceFileToken.type === 'inline') {
|
||||
source = fs.readFileSync(
|
||||
path.resolve(__dirname, '../examples', `${sourceFile}.vue`),
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
const existingScriptIndex = hoistedTags.findIndex((tag) => {
|
||||
return scriptSetupRE.test(tag)
|
||||
})
|
||||
|
||||
if (existingScriptIndex === -1) {
|
||||
hoistedTags.push(`
|
||||
<script setup>
|
||||
const demos = import.meta.globEager('../../examples/${
|
||||
sourceFile.split('/')[0]
|
||||
}/*.vue')
|
||||
</script>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!source) throw new Error(`Incorrect source file: ${sourceFile}`)
|
||||
|
||||
const { html, js, css, cssPreProcessor, jsPreProcessor } =
|
||||
generateCodePenSnippet(source)
|
||||
|
||||
return `<Demo :demos="demos" source="${encodeURIComponent(
|
||||
highlight(source, 'vue')
|
||||
)}" path="${sourceFile}" html="${html}" js="${js}" css="${css}" css-pre-processor="${cssPreProcessor}" js-pre-processor="${jsPreProcessor}">
|
||||
${description ? `` : ''}
|
||||
<!--element-demo: ${content}:element-demo-->
|
||||
`
|
||||
}
|
||||
|
||||
return '</Demo>'
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function generateCodePenSnippet(source) {
|
||||
const { template, script, styles } = parse(source).descriptor
|
||||
const css = (styles || [{ content: '' }]).pop()
|
||||
return {
|
||||
html: encodeURIComponent(template.content),
|
||||
js: encodeURIComponent((script || { content: '' }).content),
|
||||
css: encodeURIComponent(css?.content || ''),
|
||||
cssPreProcessor: css?.lang || 'none',
|
||||
jsPreProcessor: script?.lang || 'none',
|
||||
}
|
||||
}
|
50
docs/.vitepress/sidebars.js
Normal file
50
docs/.vitepress/sidebars.js
Normal file
@ -0,0 +1,50 @@
|
||||
const guideLocale = require('./i18n/pages/guide.json')
|
||||
const componentLocale = require('./i18n/pages/component.json')
|
||||
const { ensureLang } = require('./site-utils')
|
||||
|
||||
function getGuideSidebar() {
|
||||
const guideSidebars = {}
|
||||
Object.entries(guideLocale).forEach(([lang, val]) => {
|
||||
guideSidebars[lang] = Object.values(val).map((item) => {
|
||||
return mapPrefix(item, lang)
|
||||
})
|
||||
})
|
||||
return guideSidebars
|
||||
}
|
||||
|
||||
function getComponentsSideBar() {
|
||||
const componentSidebar = {}
|
||||
Object.entries(componentLocale).forEach(([lang, val]) => {
|
||||
componentSidebar[lang] = Object.values(val).map((item) => {
|
||||
return mapPrefix(item, lang, '/component')
|
||||
})
|
||||
})
|
||||
|
||||
return componentSidebar
|
||||
}
|
||||
|
||||
// return sidebar with language configs.
|
||||
// this might create duplicated data but the overhead is ignorable
|
||||
const getSidebars = () => {
|
||||
return {
|
||||
'/guide/': getGuideSidebar(),
|
||||
'/component/': getComponentsSideBar(),
|
||||
}
|
||||
}
|
||||
|
||||
function mapPrefix(item, lang, prefix = '') {
|
||||
if (item.children && item.children.length > 0) {
|
||||
return {
|
||||
...item,
|
||||
children: item.children.map((child) => {
|
||||
return mapPrefix(child, lang, prefix)
|
||||
}),
|
||||
}
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
link: `${ensureLang(lang)}${prefix}${item.link}`,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = getSidebars()
|
5
docs/.vitepress/site-utils.js
Normal file
5
docs/.vitepress/site-utils.js
Normal file
@ -0,0 +1,5 @@
|
||||
const ensureLang = (lang) => {
|
||||
return `/${lang}`
|
||||
}
|
||||
|
||||
exports.ensureLang = ensureLang
|
16
docs/.vitepress/theme/index.js
Normal file
16
docs/.vitepress/theme/index.js
Normal file
@ -0,0 +1,16 @@
|
||||
import ElementPlus from 'element-plus'
|
||||
|
||||
import VPApp, { globals, NotFound } from '../vitepress'
|
||||
|
||||
export default {
|
||||
NotFound,
|
||||
Layout: VPApp,
|
||||
logo: '/images/element-plus-logo-small.svg',
|
||||
enhanceApp: ({ app }) => {
|
||||
app.use(ElementPlus)
|
||||
|
||||
globals.forEach(([name, Comp]) => {
|
||||
app.component(name, Comp)
|
||||
})
|
||||
},
|
||||
}
|
38
docs/.vitepress/vitepress/components/common/vp-link.vue
Normal file
38
docs/.vitepress/vitepress/components/common/vp-link.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import ExternalLink from '../icons/external-link-icon.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
href?: string
|
||||
noIcon?: boolean
|
||||
}>()
|
||||
|
||||
const isExternal = computed(() => props.href && /^[a-z]+:/i.test(props.href))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component
|
||||
:is="href ? 'a' : 'span'"
|
||||
class="link-item"
|
||||
:class="{ link: href }"
|
||||
:href="href"
|
||||
:target="isExternal ? '_blank' : undefined"
|
||||
:rel="isExternal ? 'noopener noreferrer' : undefined"
|
||||
>
|
||||
<slot />
|
||||
<ElIcon v-if="isExternal && !noIcon">
|
||||
<ExternalLink class="link-icon" />
|
||||
</ElIcon>
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.link-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
15
docs/.vitepress/vitepress/components/common/vp-switch.vue
Normal file
15
docs/.vitepress/vitepress/components/common/vp-switch.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
// for now el-switch does not support customized icon in the dot
|
||||
// we will implement a simple version of el-switch then update the switch
|
||||
// component for this feature
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="switch" role="switch">
|
||||
<div class="switch__action">
|
||||
<div class="switch__icon">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import VPSwitch from './vp-switch.vue'
|
||||
import DarkIcon from '../icons/dark.vue'
|
||||
import LightIcon from '../icons/light.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPSwitch>
|
||||
<ElIcon :size="13">
|
||||
<DarkIcon class="dark-icon" />
|
||||
<LightIcon class="light-icon" />
|
||||
</ElIcon>
|
||||
</VPSwitch>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dark-icon,
|
||||
.light-icon {
|
||||
transition: color var(--el-transition-duration),
|
||||
opacity var(--el-transition-duration);
|
||||
}
|
||||
|
||||
.light-icon {
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dark-icon {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@at-root .dark {
|
||||
.dark-icon {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.light-icon {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
102
docs/.vitepress/vitepress/components/demo/vp-codepen.vue
Normal file
102
docs/.vitepress/vitepress/components/demo/vp-codepen.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, getCurrentInstance } from 'vue'
|
||||
|
||||
const vm = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
css: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cssPreProcessor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
js: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
jsPreProcessor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
html: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const globalMapper = {
|
||||
"'vue'": 'Vue',
|
||||
"'element-plus'": 'ElementPlus',
|
||||
}
|
||||
|
||||
const js = computed(() => {
|
||||
const decodedJs = decodeURIComponent(props.js || '')
|
||||
const imports = /(import*) ([^'\n]*) from ([^\n]*)/g
|
||||
const globals = []
|
||||
let match
|
||||
while ((match = imports.exec(decodedJs))) {
|
||||
const [_, __, members, target] = match
|
||||
|
||||
globals.push(`const ${members} = ${globalMapper[target]};`)
|
||||
}
|
||||
const componentRegex = decodedJs.includes('export default defineComponent')
|
||||
? /export default defineComponent\({([\s\S]*)}\)/g
|
||||
: /export default {([\s\S]*)}/g
|
||||
|
||||
let component = componentRegex.exec(decodedJs)
|
||||
component = ((component && component[1]) || '')
|
||||
.replace(/\n {2}/g, '\n')
|
||||
.trim()
|
||||
|
||||
return `${globals.join('\n')}
|
||||
var Main = {
|
||||
${component}
|
||||
};
|
||||
|
||||
const app = Vue.createApp(Main);
|
||||
app.use(ElementPlus);
|
||||
app.mount("#app");
|
||||
`
|
||||
})
|
||||
|
||||
const data = computed(() => {
|
||||
return JSON.stringify({
|
||||
html: `<script src="//unpkg.com/vue@next"><${'/script'}>
|
||||
<script src="//unpkg.com/element-plus/dist/index.full.js"><${'/script'}>
|
||||
<div id="app">
|
||||
${decodeURIComponent(props.html).trim()}
|
||||
</div>
|
||||
`,
|
||||
css: `@import url("//unpkg.com/element-plus/dist/index.css");
|
||||
${decodeURIComponent(props.css).trim()}
|
||||
`,
|
||||
js: js.value,
|
||||
css_pre_processor: props.cssPreProcessor,
|
||||
js_pre_processor:
|
||||
props.jsPreProcessor === 'ts' ? ' typescript' : props.jsPreProcessor,
|
||||
})
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const submit = () => {
|
||||
formRef.value.submit?.()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
submit,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form
|
||||
ref="formRef"
|
||||
action="https://codepen.io/pen/define/"
|
||||
method="POST"
|
||||
target="_blank"
|
||||
>
|
||||
<input type="hidden" name="data" :value="data" />
|
||||
</form>
|
||||
</template>
|
48
docs/.vitepress/vitepress/components/demo/vp-example.vue
Normal file
48
docs/.vitepress/vitepress/components/demo/vp-example.vue
Normal file
@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { withBase } from 'vitepress'
|
||||
import { shallowRef, onMounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
file: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
demo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="example-showcase">
|
||||
<ClientOnly>
|
||||
<component v-if="demo" :is="demo" v-bind="$attrs" />
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.example-showcase {
|
||||
padding: 1rem;
|
||||
margin: 0.5px;
|
||||
background-color: var(--bg-color);
|
||||
&.transparent-enabled {
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
rgb(249, 249, 250) 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(135deg, rgb(249, 249, 250) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, rgb(249, 249, 250) 75%),
|
||||
linear-gradient(135deg, transparent 75%, rgb(249, 249, 250) 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0px 0px, 10px 0px, 10px -10px, 0px 10px;
|
||||
}
|
||||
|
||||
@at-root .dark .example-showcase {
|
||||
background-image: unset;
|
||||
background-color: var(--bg-color-soft);
|
||||
}
|
||||
}
|
||||
</style>
|
25
docs/.vitepress/vitepress/components/demo/vp-source-code.vue
Normal file
25
docs/.vitepress/vitepress/components/demo/vp-source-code.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const decoded = computed(() => {
|
||||
return decodeURIComponent(props.source)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="example-source language-vue" v-html="decoded"></div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.language-vue {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
import { useEditLink } from '../../composables/edit-link'
|
||||
import ExternalIcon from '../icons/external-link-icon.vue'
|
||||
|
||||
const { url, text } = useEditLink()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="edit-link">
|
||||
<a
|
||||
v-if="url"
|
||||
class="link"
|
||||
:href="url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{ text }}
|
||||
<ElIcon :size="16" style="vertical-align: text-top; line-height: 24px">
|
||||
<ExternalIcon />
|
||||
</ElIcon>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.link {
|
||||
display: inline-block;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-color-light);
|
||||
}
|
||||
|
||||
.link:hover {
|
||||
text-decoration: none;
|
||||
color: var(--brand-color);
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,54 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import { useLang } from '../../composables/lang'
|
||||
import localeData from '../../../i18n/component/last-update-at.json'
|
||||
|
||||
const { theme, page } = useData()
|
||||
const lang = useLang()
|
||||
|
||||
const prefix = computed(() => {
|
||||
return localeData[lang.value].title
|
||||
})
|
||||
|
||||
const datetime = ref('')
|
||||
onMounted(() => {
|
||||
datetime.value = new Date(page.value.lastUpdated).toLocaleString(lang.value)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p class="last-updated">
|
||||
<span class="prefix">{{ prefix }}:</span>
|
||||
<span class="datetime">{{ datetime }}</span>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.last-updated {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-color-light);
|
||||
|
||||
.prefix {
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.datetime {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to('lg') {
|
||||
.last-updated {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import VPEditLink from './vp-edit-link.vue'
|
||||
import VPLastUpdatedAt from './vp-last-updated-at.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="page-footer">
|
||||
<div class="edit">
|
||||
<VPEditLink />
|
||||
</div>
|
||||
<div class="updated">
|
||||
<VPLastUpdatedAt />
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../styles/mixins';
|
||||
.page-footer {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
overflow: auto;
|
||||
|
||||
.updated {
|
||||
padding-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@include respond-to('lg') {
|
||||
.page-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.updated {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
import { withBase } from 'vitepress'
|
||||
import { ArrowLeft, ArrowRight } from '@element-plus/icons'
|
||||
import { usePageNav } from '../../composables/page-nav'
|
||||
|
||||
const { hasLinks, prev, next } = usePageNav()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="hasLinks" class="next-and-prev-link">
|
||||
<div class="container">
|
||||
<div class="prev">
|
||||
<a v-if="prev" class="link" :href="withBase(prev.link)">
|
||||
<ElIcon>
|
||||
<ArrowLeft />
|
||||
</ElIcon>
|
||||
<span class="text">{{ prev.text }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="next">
|
||||
<a v-if="next" class="link" :href="withBase(next.link)">
|
||||
<span class="text">{{ next.text }}</span>
|
||||
<ElIcon>
|
||||
<ArrowRight />
|
||||
</ElIcon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.next-and-prev-link {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid var(--border-color);
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.prev,
|
||||
.next {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.prev {
|
||||
justify-content: flex-start;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.next {
|
||||
justify-content: flex-end;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
font-size: 1rem;
|
||||
color: var(--text-color);
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.icon-prev {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.icon-next {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useData } from 'vitepress'
|
||||
import { ref, watch } from 'vue'
|
||||
import { useToc } from '../../composables/use-toc'
|
||||
import { useActiveSidebarLinks } from '../../composables/active-bar'
|
||||
|
||||
const headers = useToc()
|
||||
const marker = ref()
|
||||
const container = ref()
|
||||
useActiveSidebarLinks(container, marker)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside ref="container" class="toc-wrapper">
|
||||
<nav class="toc-content">
|
||||
<h3 class="toc-content__heading">Contents</h3>
|
||||
<ul class="toc-items">
|
||||
<li v-for="{ link, text, children } in headers" class="toc-item">
|
||||
<a class="toc-link" :href="link">{{ text }}</a>
|
||||
<ul v-if="children">
|
||||
<li v-for="{ link, text } in children" class="toc-item">
|
||||
<a class="toc-link subitem" :href="link">{{ text }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div ref="marker" class="toc-marker"></div>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
@ -0,0 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import VPLink from '../common/vp-link.vue'
|
||||
|
||||
import type { Link } from '../../types'
|
||||
|
||||
defineProps<{
|
||||
item: Link
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
:class="{
|
||||
'is-menu-link': true,
|
||||
}"
|
||||
:href="item.link"
|
||||
:no-icon="true"
|
||||
>
|
||||
{{ item.text }}
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.is-menu-link {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
color: var(--text-color);
|
||||
transition: color var(--el-transition-duration);
|
||||
|
||||
&.active {
|
||||
border-bottom: 2px solid var(--brand-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--brand-color);
|
||||
}
|
||||
}
|
||||
</style>
|
23
docs/.vitepress/vitepress/components/full-screen/vp-menu.vue
Normal file
23
docs/.vitepress/vitepress/components/full-screen/vp-menu.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import { useNav } from '../../composables/nav'
|
||||
import VPMenuLink from './vp-menu-link.vue'
|
||||
|
||||
const navs = useNav()
|
||||
|
||||
defineEmits(['close'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="navs" class="full-screen-menu">
|
||||
<div v-for="item in navs" class="full-screen-menu__item">
|
||||
<VPMenuLink :item="item" @click="$emit('close')" />
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.full-screen-menu__item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import CommonThemeToggler from '../common/vp-theme-toggler.vue'
|
||||
import { useTheme } from '../../composables/theme'
|
||||
|
||||
const toggle = useTheme()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="full-screen-theme-toggler">
|
||||
<span> Theme </span>
|
||||
<CommonThemeToggler @click="toggle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.full-screen-theme-toggler {
|
||||
display: flex;
|
||||
padding: 12px 14px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 16px;
|
||||
font-size: 13px;
|
||||
background-color: var(--bg-color-soft);
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import VPLink from '../common/vp-link.vue'
|
||||
import { useTranslation } from '../../composables/translation'
|
||||
import { useToggle } from '../../composables/toggle'
|
||||
import ExpandIcon from '../icons/expand.vue'
|
||||
|
||||
const { languageMap, langs, lang, switchLang, helpTranslate } = useTranslation()
|
||||
|
||||
const [show, toggle] = useToggle()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="full-screen-translation">
|
||||
<ElButton
|
||||
type="text"
|
||||
@click="toggle"
|
||||
style="width: 100%; color: var(--text-color)"
|
||||
>
|
||||
<div class="translation-toggler">
|
||||
<span> Translations </span>
|
||||
<ElIcon :size="14">
|
||||
<ExpandIcon class="toggle-icon" :class="{ expanded: show }" />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</ElButton>
|
||||
<div v-show="show" class="translation-items">
|
||||
<p
|
||||
v-for="l in langs"
|
||||
class="translation-item"
|
||||
:class="{ active: l === lang }"
|
||||
@click="switchLang(l)"
|
||||
>
|
||||
{{ languageMap[l] }}
|
||||
</p>
|
||||
<p class="translation-item">
|
||||
<VPLink href="https://crowdin.com/project/element-plus">
|
||||
{{ helpTranslate }}
|
||||
</VPLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.full-screen-translation {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
.translation-toggler {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
line-height: 24px;
|
||||
.toggle-icon {
|
||||
transition: transform var(--el-transition-duration);
|
||||
transform: rotate(180deg);
|
||||
|
||||
&.expanded {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.translation-items {
|
||||
padding-bottom: 12px;
|
||||
.translation-item {
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
|
||||
&.active {
|
||||
font-weight: 500;
|
||||
color: var(--brand-color);
|
||||
}
|
||||
|
||||
.link-item {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
95
docs/.vitepress/vitepress/components/globals/icons.vue
Normal file
95
docs/.vitepress/vitepress/components/globals/icons.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, markRaw } from 'vue'
|
||||
import { hyphenate } from '@vue/shared'
|
||||
import clipboardCopy from 'clipboard-copy'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import * as Icons from '@element-plus/icons'
|
||||
import { useLang } from '../../composables/lang'
|
||||
import localeData from '../../../i18n/component/icons.json'
|
||||
|
||||
const lang = useLang()
|
||||
const locale = computed(() => localeData[lang.value])
|
||||
|
||||
const copySvgIcon = async (svg) => {
|
||||
try {
|
||||
await clipboardCopy(
|
||||
`<el-icon>
|
||||
<${hyphenate(svg)} />
|
||||
</el-icon>`
|
||||
)
|
||||
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: locale.value['copy-success'],
|
||||
type: 'success',
|
||||
})
|
||||
} catch (e) {
|
||||
ElMessage({
|
||||
showClose: true,
|
||||
message: locale.value['copy-error'],
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul class="demo-icon-list">
|
||||
<li
|
||||
v-for="component in Icons"
|
||||
:key="component"
|
||||
class="icon-item"
|
||||
@click="copySvgIcon(component.name)"
|
||||
>
|
||||
<span class="demo-svg-icon">
|
||||
<ElIcon :size="20">
|
||||
<component :is="component" />
|
||||
</ElIcon>
|
||||
<span class="icon-name">{{ component.name }}</span>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.demo-icon-list {
|
||||
overflow: hidden;
|
||||
list-style: none;
|
||||
padding: 0 !important;
|
||||
border-top: 1px solid var(--el-border-color-base);
|
||||
border-left: 1px solid var(--el-border-color-base);
|
||||
border-radius: 4px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
|
||||
.icon-item {
|
||||
text-align: center;
|
||||
color: var(--el-text-color-regular);
|
||||
height: 120px;
|
||||
font-size: 13px;
|
||||
border-right: 1px solid var(--el-border-color-base);
|
||||
border-bottom: 1px solid var(--el-border-color-base);
|
||||
transition: background-color var(--el-transition-duration);
|
||||
&:hover {
|
||||
background-color: var(--el-border-color-extra-light);
|
||||
.el-icon {
|
||||
color: var(--brand-color-light);
|
||||
}
|
||||
color: var(--brand-color-light);
|
||||
}
|
||||
|
||||
.demo-svg-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
|
||||
.icon-name {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
53
docs/.vitepress/vitepress/components/globals/main-color.vue
Normal file
53
docs/.vitepress/vitepress/components/globals/main-color.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="10" :xs="{ span: 12 }">
|
||||
<div class="demo-color-box" :style="{ background: primary }">
|
||||
Brand Color
|
||||
<div class="value">#409EFF</div>
|
||||
<div
|
||||
class="bg-color-sub"
|
||||
:style="{ background: tintColor(primary, 0.9) }"
|
||||
>
|
||||
<div
|
||||
v-for="item in 9"
|
||||
:key="item"
|
||||
class="bg-blue-sub-item"
|
||||
:style="{ background: tintColor(primary, (item + 1) / 10) }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const primary = ref('#409EFF')
|
||||
const tintColor = (c: string, tint: number) => {
|
||||
const color = c.replace('#', '')
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) {
|
||||
// when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
|
||||
return `#${red.toString(16)}${green.toString(16)}${blue.toString(16)}`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
tintColor,
|
||||
primary,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
137
docs/.vitepress/vitepress/components/globals/neutral-color.vue
Normal file
137
docs/.vitepress/vitepress/components/globals/neutral-color.vue
Normal file
@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="6" :xs="{ span: 12 }">
|
||||
<div class="demo-color-box-group">
|
||||
<div
|
||||
v-for="(text, i) in textColors"
|
||||
:key="i"
|
||||
class="demo-color-box demo-color-box-other"
|
||||
:style="{ background: getCssVarName('text-color', text.type) }"
|
||||
>
|
||||
{{ text.name || formatType(text.type) + ' Text' }}
|
||||
<div class="value">
|
||||
{{ getCssVarValue('text-color', text.type).toUpperCase() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="{ span: 12 }">
|
||||
<div class="demo-color-box-group">
|
||||
<div
|
||||
v-for="(border, i) in borderColors"
|
||||
:key="i"
|
||||
class="demo-color-box demo-color-box-other demo-color-box-lite"
|
||||
:style="{ background: `var(--el-border-color-${border.type})` }"
|
||||
>
|
||||
{{ formatType(border.type) + ' Border' }}
|
||||
<div class="value">
|
||||
{{ getColorValue(border.type).toUpperCase() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="6" :xs="{ span: 12 }">
|
||||
<div class="demo-color-box-group">
|
||||
<div
|
||||
class="demo-color-box demo-color-box-other"
|
||||
:style="{ background: black }"
|
||||
>
|
||||
Basic Black
|
||||
<div class="value">{{ black }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="demo-color-box demo-color-box-other"
|
||||
:style="{
|
||||
background: white,
|
||||
color: '#303133',
|
||||
border: '1px solid #eee',
|
||||
}"
|
||||
>
|
||||
Basic White
|
||||
<div class="value">{{ white }}</div>
|
||||
</div>
|
||||
<div class="demo-color-box demo-color-box-other bg-transparent">
|
||||
Transparent
|
||||
<div class="value">Transparent</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const getColorValue = (type: string) => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--el-border-color-${type}`
|
||||
)
|
||||
}
|
||||
|
||||
const formatType = (type: string) => {
|
||||
return type
|
||||
.split('-')
|
||||
.map((item) => item.charAt(0).toUpperCase() + item.slice(1))
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
const getCssVarName = (namespace: string, type: string) => {
|
||||
return `var(--el-${namespace}-${type})`
|
||||
}
|
||||
|
||||
const getCssVarValue = (namespace, type) => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--el-${namespace}-${type}`
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
borderColors: markRaw([
|
||||
{
|
||||
type: 'base',
|
||||
color: '#DCDFE6',
|
||||
},
|
||||
{
|
||||
type: 'light',
|
||||
color: '#E4E7ED',
|
||||
},
|
||||
{
|
||||
type: 'lighter',
|
||||
color: '#EBEEF5',
|
||||
},
|
||||
{
|
||||
type: 'extra-light',
|
||||
color: '#F2F6FC',
|
||||
},
|
||||
]),
|
||||
textColors: markRaw([
|
||||
{
|
||||
name: 'Primary Text',
|
||||
type: 'primary',
|
||||
},
|
||||
{
|
||||
name: 'Regular Text',
|
||||
type: 'regular',
|
||||
},
|
||||
{
|
||||
name: 'Secondary Text',
|
||||
type: 'secondary',
|
||||
},
|
||||
{
|
||||
name: 'Placeholder Text',
|
||||
type: 'placeholder',
|
||||
},
|
||||
]),
|
||||
black: '#000000',
|
||||
white: '#FFFFFF',
|
||||
|
||||
formatType,
|
||||
getColorValue,
|
||||
getCssVarName,
|
||||
getCssVarValue,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<el-row :gutter="12">
|
||||
<el-col
|
||||
v-for="(type, i) in colorsType"
|
||||
:key="type"
|
||||
:span="6"
|
||||
:xs="{ span: 12 }"
|
||||
>
|
||||
<div class="demo-color-box" :style="{ background: getColorValue(type) }">
|
||||
{{ type.charAt(0).toUpperCase() + type.slice(1) }}
|
||||
<div class="value">{{ getColorValue(type).toUpperCase() }}</div>
|
||||
<div class="bg-color-sub">
|
||||
<div
|
||||
v-for="(_, key) in Array(2)"
|
||||
:key="key"
|
||||
class="bg-secondary-sub-item"
|
||||
:style="{
|
||||
background: tintColor(getColorValue(type), (key + 8) / 10),
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const tintColor = (c: string, tint: number) => {
|
||||
const color = c.trim().slice(1)
|
||||
let red = parseInt(color.slice(0, 2), 16)
|
||||
let green = parseInt(color.slice(2, 4), 16)
|
||||
let blue = parseInt(color.slice(4, 6), 16)
|
||||
|
||||
if (tint === 0) {
|
||||
// when primary color is in its rgb space
|
||||
return [red, green, blue].join(',')
|
||||
} else {
|
||||
red += Math.round(tint * (255 - red))
|
||||
green += Math.round(tint * (255 - green))
|
||||
blue += Math.round(tint * (255 - blue))
|
||||
return `#${red.toString(16)}${green.toString(16)}${blue.toString(16)}`
|
||||
}
|
||||
}
|
||||
|
||||
const getColorValue = (type: string) => {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
`--el-color-${type}`
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
colorsType: markRaw(['success', 'warning', 'danger', 'info']),
|
||||
getColorValue,
|
||||
tintColor,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
147
docs/.vitepress/vitepress/components/globals/vp-demo.vue
Normal file
147
docs/.vitepress/vitepress/components/globals/vp-demo.vue
Normal file
@ -0,0 +1,147 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, shallowRef, watch, toRef, ref } from 'vue'
|
||||
import { useToggle } from '../composables/toggle'
|
||||
import { useLang } from '../composables/lang'
|
||||
import { useSourceCode } from '../composables/source-code'
|
||||
|
||||
import GithubIcon from './icons/github.vue'
|
||||
import SourceCodeIcon from './icons/source-code.vue'
|
||||
import CodepenIcon from './icons/codepen.vue'
|
||||
|
||||
import Example from './demo/vp-example.vue'
|
||||
import SourceCode from './demo/vp-source-code.vue'
|
||||
import Codepen from './demo/vp-codepen.vue'
|
||||
|
||||
import demoBlockLocale from '../../i18n/component/demo-block.json'
|
||||
|
||||
const props = defineProps({
|
||||
// source is encoded via encodeURIComponent
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
css: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cssPreProcessor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
js: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
html: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
demos: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const formatPathDemos = computed(() => {
|
||||
const demos = {}
|
||||
|
||||
Object.keys(props.demos).forEach((key) => {
|
||||
demos[key.replace('../../examples/', '').replace('.vue', '')] =
|
||||
props.demos[key].default
|
||||
})
|
||||
|
||||
return demos
|
||||
})
|
||||
|
||||
const loaded = shallowRef(false)
|
||||
const hasError = shallowRef(false)
|
||||
const dataOpt = shallowRef('')
|
||||
const setupScript = shallowRef('')
|
||||
const template = shallowRef('')
|
||||
const [sourceVisible, setSourceVisible] = useToggle()
|
||||
const hasSetup = computed(() => loaded.value && setupScript.value !== '')
|
||||
const lang = useLang()
|
||||
|
||||
const locale = computed(() => demoBlockLocale[lang.value])
|
||||
|
||||
const onDemoLoaded = (content) => {
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
const demoSourceUrl = useSourceCode(toRef(props, 'path'))
|
||||
const codepenRef = ref()
|
||||
|
||||
const onCodepenClicked = () => {
|
||||
codepenRef.value.submit?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="example">
|
||||
<Codepen
|
||||
:css="props.css"
|
||||
:css-pre-processor="props.cssPreProcessor"
|
||||
:html="props.html"
|
||||
:js="props.js"
|
||||
ref="codepenRef"
|
||||
/>
|
||||
<div class="op-btns">
|
||||
<ElTooltip :content="locale['edit-in-codepen']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn">
|
||||
<CodepenIcon @click="onCodepenClicked" />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
<ElTooltip :content="locale['edit-on-github']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn github">
|
||||
<a :href="demoSourceUrl" rel="noreferrer noopener" target="_blank">
|
||||
<GithubIcon />
|
||||
</a>
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
<ElTooltip :content="locale['view-source']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn" @click="setSourceVisible">
|
||||
<SourceCodeIcon />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<Example :file="path" :demo="formatPathDemos[path]" />
|
||||
<ElDivider v-if="sourceVisible" />
|
||||
<el-collapse-transition>
|
||||
<SourceCode v-show="sourceVisible" :source="source" />
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.example {
|
||||
border: var(--el-border-base);
|
||||
border-radius: var(--el-border-radius-base);
|
||||
|
||||
.op-btns {
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
|
||||
.op-btn {
|
||||
margin: 0 0.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
&.github a {
|
||||
color: var(--text-color-lighter);
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-divider {
|
||||
margin: 0;
|
||||
background-color: var(--border-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path
|
||||
d="M256 48C141.13 48 48 141.13 48 256s93.13 208 208 208s208-93.13 208-208S370.87 48 256 48zm91.36 212.65a16 16 0 0 1-22.63.09L272 208.42V342a16 16 0 0 1-32 0V208.42l-52.73 52.32A16 16 0 1 1 164.73 238l80-79.39a16 16 0 0 1 22.54 0l80 79.39a16 16 0 0 1 .09 22.65z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
14
docs/.vitepress/vitepress/components/icons/codepen.vue
Normal file
14
docs/.vitepress/vitepress/components/icons/codepen.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M18.144 13.067v-2.134L16.55 12zm1.276 1.194a.628.628 0 0 1-.006.083l-.005.028l-.011.053l-.01.031a.443.443 0 0 1-.017.047l-.014.03a.78.78 0 0 1-.021.043l-.019.03a.57.57 0 0 1-.08.1l-.026.025a.602.602 0 0 1-.036.03l-.029.022l-.01.008l-6.782 4.522a.637.637 0 0 1-.708 0L4.864 14.79l-.01-.008a.599.599 0 0 1-.065-.052l-.026-.025l-.032-.034l-.021-.028a.588.588 0 0 1-.067-.11l-.014-.031a.644.644 0 0 1-.017-.047l-.01-.03c-.004-.018-.008-.036-.01-.054l-.006-.028a.628.628 0 0 1-.006-.083V9.739a.58.58 0 0 1 .006-.083l.005-.027l.011-.054l.01-.03a.574.574 0 0 1 .12-.217l.031-.034l.026-.025a.62.62 0 0 1 .065-.052l.01-.008l6.782-4.521a.638.638 0 0 1 .708 0l6.782 4.521l.01.008l.03.022l.035.03c.01.008.017.016.026.025a.545.545 0 0 1 .08.1l.019.03a.633.633 0 0 1 .021.043l.014.03c.007.016.012.032.017.047l.01.031c.004.018.008.036.01.054l.006.027a.619.619 0 0 1 .006.083zM12 0C5.373 0 0 5.372 0 12c0 6.627 5.373 12 12 12c6.628 0 12-5.372 12-12c0-6.627-5.372-12-12-12m0 10.492L9.745 12L12 13.51L14.255 12zm.638 4.124v2.975l4.996-3.33l-2.232-1.493zm-6.272-.356l4.996 3.33v-2.974l-2.764-1.849zm11.268-4.52l-4.996-3.33v2.974l2.764 1.85zm-6.272-.356V6.41L6.366 9.74l2.232 1.493zm-5.506 1.549v2.134L7.45 12z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CodePenIcon',
|
||||
}
|
||||
</script>
|
8
docs/.vitepress/vitepress/components/icons/dark.vue
Normal file
8
docs/.vitepress/vitepress/components/icons/dark.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M11.01 3.05C6.51 3.54 3 7.36 3 12a9 9 0 0 0 9 9c4.63 0 8.45-3.5 8.95-8c.09-.79-.78-1.42-1.54-.95A5.403 5.403 0 0 1 11.1 7.5c0-1.06.31-2.06.84-2.89c.45-.67-.04-1.63-.93-1.56z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 44 44">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M37.41,32.37c0,1.57-.83,1.93-.83,1.93L21.51,43A1.69,1.69,0,0,1,20,43S5.2,34.4,4.66,34a1.29,1.29,0,0,1-.55-1V15.24c0-.78,1-1.33,1-1.33L19.86,5.36a2,2,0,0,1,1.79,0l14.46,8.41a2.06,2.06,0,0,1,1.25,2.06V32.37Zm-5.9-17L21.35,9.5a1.59,1.59,0,0,0-1.41,0L8.33,16.15s-.77.46-.76,1.08,0,13.92,0,13.92A1,1,0,0,0,8,31.9c.43.3,12,7,12,7a1.31,1.31,0,0,0,1.19,0C21.91,38.5,33,32.11,33,32.11s.65-.28.65-1.51V27.13l-13,7.9V32a3.05,3.05,0,0,1,1-2.07L33.2,23a2.44,2.44,0,0,0,.55-1.46V18.43L20.64,26.35v-3.2a2.22,2.22,0,0,1,.83-1.79ZM41.07,4.22a.39.39,0,0,0-.37-.42H38V1.06c0-.16-.26-.22-.53-.22L36,1.08c-.18,0-.31.12-.31.23V3.8H33a.4.4,0,0,0-.36.37v2h3V9c0,.16.26.27.54.23l1.51-.25c.18,0,.29-.13.29-.23V6.14h3Z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 153.71 38">
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M142,26.16s.28,0,.82,0a.72.72,0,0,1,.69.41s1.08,2,1.37,2.48,0,.42-.12.41h0s-.31,0-3.45,0a4.93,4.93,0,0,1-4.54-4.54v-7H134.3V15.28c0-.36.41-.41.41-.41h2.07V12.25a.6.6,0,0,1,.41-.55l2.3-.66c.34-.1.59,0,.59.35V15h3.58c.34,0,.41.41.41.41V18h-4v6.06c0,1.76,1.93,2.07,1.93,2.07Zm-10.6,3h-2.2c-.43,0-.41-.55-.41-.55V18.45c0-.62-.83-.83-.83-.83h-4.54c-.68,0-.69.83-.69.83V28.77a.41.41,0,0,1-.41.42h-2.2c-.48,0-.41-.55-.41-.55V15.83c0-1,1.24-1.24,1.24-1.24h9.63c1,0,1.23,1.24,1.23,1.24V28.5c0,.72-.41.69-.41.69ZM115.73,23.4H107.2v2.07c0,.74,1,1,1,1H115a1.16,1.16,0,0,1,.82.42s.61,1.25.83,1.79-.41.55-.41.55H106c-1.24,0-1.51-1.52-1.51-1.52V16c0-.67,1-1,1-1h10.32c1,0,1.24,1.23,1.24,1.23v5.93c0,1-1.24,1.23-1.24,1.23Zm-1.52-4.95s-.08-.69-.68-.69h-5.65s-.68.18-.68.69V20a.69.69,0,0,0,.68.69h5.65a.9.9,0,0,0,.68-.83V18.45ZM101.28,29.19h-2.2c-.29,0-.41-.42-.41-.42V18.45c0-.64-.83-.83-.83-.83H95.78c-.58,0-.69.83-.69.83V28.77c0,.35-.41.42-.41.42h-2.2c-.31,0-.42-.42-.42-.42V18.45c0-.66-.82-.83-.82-.83H89.17c-.63,0-.68.83-.68.83V28.77a.39.39,0,0,1-.42.42h-2.2a.41.41,0,0,1-.41-.42V15.69c0-.75,1.1-1.1,1.1-1.1h13.76c1.1,0,1.37,1.38,1.37,1.38v12.8c0,.48-.41.42-.41.42Zm-20-5.79H72.8v2.07c0,.74,1,1,1,1h6.88a1.19,1.19,0,0,1,.83.42s.6,1.25.82,1.79-.41.55-.41.55H71.56c-1.24,0-1.51-1.52-1.51-1.52V16c0-.67,1-1,1-1H81.33c1,0,1.24,1.23,1.24,1.23v5.93c0,1-1.24,1.23-1.24,1.23Zm-1.51-4.95s-.09-.69-.69-.69H73.49s-.69.18-.69.69V20a.69.69,0,0,0,.69.69h5.64a.91.91,0,0,0,.69-.83V18.45ZM68,29.19H62.76a4.35,4.35,0,0,1-4.13-4c0-3.91,0-16.1,0-16.1h2.48a.79.79,0,0,1,.82.82V24.37A2.58,2.58,0,0,0,63.86,26h2.2s.72-.23,1.24.69l1.1,1.93s.08.55-.41.55Zm-26.56-.83V10.19a1,1,0,0,1,.69-1H55.05c.73,0,.42.83.42.83s-.41,1.12-.69,1.65a1,1,0,0,1-.83.55H45.56a.77.77,0,0,0-.83.69v4.54h9.5c.55,0,.27.69.27.69s-.71,1.52-1,1.93a1.05,1.05,0,0,1-.83.41h-8v4.82a.91.91,0,0,0,.69.83h8.81a.85.85,0,0,1,.82.41l1.24,1.93c.37.56-.14.69-.14.69H42.26C41.68,29.19,41.43,28.36,41.43,28.36Zm-8.14-1.14c0,1.57-.83,1.93-.83,1.93S18.32,37.31,17.4,37.83a1.68,1.68,0,0,1-1.52,0S1.09,29.25.55,28.87a1.29,1.29,0,0,1-.55-1s0-17,0-17.78S1,8.76,1,8.76L15.75.21a2,2,0,0,1,1.79,0S30.6,7.8,32,8.62a2.08,2.08,0,0,1,1.25,2.06s0,15.07,0,16.54Zm-5.9-17c-3-1.74-10.16-5.87-10.16-5.87a1.58,1.58,0,0,0-1.41,0L4.22,11s-.77.46-.76,1.08S3.46,26,3.46,26a1,1,0,0,0,.43.75c.43.3,12,7,12,7a1.3,1.3,0,0,0,1.19,0c.72-.4,11.82-6.79,11.82-6.79s.65-.28.65-1.51c0-.36,0-1.74,0-3.47L16.53,29.88v-3a3,3,0,0,1,1-2.07l11.56-7a2.49,2.49,0,0,0,.55-1.46c0-1.27,0-2.37,0-3.07L16.53,21.2V18a2.17,2.17,0,0,1,.83-1.79Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M150.32,11.21h-2.24v-5c0-.11.12-.21.29-.24l1.44-.26c.26,0,.51.07.51.24Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M148.08,9h2.24v5.11c0,.11-.11.21-.28.25l-1.45.26c-.26.05-.51-.07-.51-.24Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M145.09,9h8.22a.4.4,0,0,1,.4.4v1.85a0,0,0,0,1,0,0h-9a0,0,0,0,1,0,0V9.36A.4.4,0,0,1,145.09,9Z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
8
docs/.vitepress/vitepress/components/icons/expand.vue
Normal file
8
docs/.vitepress/vitepress/components/icons/expand.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M11.29 8.71L6.7 13.3a.996.996 0 1 0 1.41 1.41L12 10.83l3.88 3.88a.996.996 0 1 0 1.41-1.41L12.7 8.71a.996.996 0 0 0-1.41 0z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<svg
|
||||
class="link-icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M853.333333 469.333333a42.666667 42.666667 0 0 0-42.666666 42.666667v256a42.666667 42.666667 0 0 1-42.666667 42.666667H256a42.666667 42.666667 0 0 1-42.666667-42.666667V256a42.666667 42.666667 0 0 1 42.666667-42.666667h256a42.666667 42.666667 0 0 0 0-85.333333H256a128 128 0 0 0-128 128v512a128 128 0 0 0 128 128h512a128 128 0 0 0 128-128v-256a42.666667 42.666667 0 0 0-42.666667-42.666667z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M682.666667 213.333333h67.413333l-268.373333 267.946667a42.666667 42.666667 0 0 0 0 60.586667 42.666667 42.666667 0 0 0 60.586666 0L810.666667 273.92V341.333333a42.666667 42.666667 0 0 0 42.666666 42.666667 42.666667 42.666667 0 0 0 42.666667-42.666667V170.666667a42.666667 42.666667 0 0 0-42.666667-42.666667h-170.666666a42.666667 42.666667 0 0 0 0 85.333333z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
15
docs/.vitepress/vitepress/components/icons/github.vue
Normal file
15
docs/.vitepress/vitepress/components/icons/github.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 16 16">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59c.4.07.55-.17.55-.38c0-.19-.01-.82-.01-1.49c-2.01.37-2.53-.49-2.69-.94c-.09-.23-.48-.94-.82-1.13c-.28-.15-.68-.52-.01-.53c.63-.01 1.08.58 1.23.82c.72 1.21 1.87.87 2.33.66c.07-.52.28-.87.51-1.07c-1.78-.2-3.64-.89-3.64-3.95c0-.87.31-1.59.82-2.15c-.08-.2-.36-1.02.08-2.12c0 0 .67-.21 2.2.82c.64-.18 1.32-.27 2-.27c.68 0 1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82c.44 1.1.16 1.92.08 2.12c.51.56.82 1.27.82 2.15c0 3.07-1.87 3.75-3.65 3.95c.29.25.54.73.54 1.48c0 1.07-.01 1.93-.01 2.2c0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'GithubIcon',
|
||||
}
|
||||
</script>
|
8
docs/.vitepress/vitepress/components/icons/light.vue
Normal file
8
docs/.vitepress/vitepress/components/icons/light.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M6.05 4.14l-.39-.39a.993.993 0 0 0-1.4 0l-.01.01a.984.984 0 0 0 0 1.4l.39.39c.39.39 1.01.39 1.4 0l.01-.01a.984.984 0 0 0 0-1.4zM3.01 10.5H1.99c-.55 0-.99.44-.99.99v.01c0 .55.44.99.99.99H3c.56.01 1-.43 1-.98v-.01c0-.56-.44-1-.99-1zm9-9.95H12c-.56 0-1 .44-1 .99v.96c0 .55.44.99.99.99H12c.56.01 1-.43 1-.98v-.97c0-.55-.44-.99-.99-.99zm7.74 3.21c-.39-.39-1.02-.39-1.41-.01l-.39.39a.984.984 0 0 0 0 1.4l.01.01c.39.39 1.02.39 1.4 0l.39-.39a.984.984 0 0 0 0-1.4zm-1.81 15.1l.39.39a.996.996 0 1 0 1.41-1.41l-.39-.39a.993.993 0 0 0-1.4 0c-.4.4-.4 1.02-.01 1.41zM20 11.49v.01c0 .55.44.99.99.99H22c.55 0 .99-.44.99-.99v-.01c0-.55-.44-.99-.99-.99h-1.01c-.55 0-.99.44-.99.99zM12 5.5c-3.31 0-6 2.69-6 6s2.69 6 6 6s6-2.69 6-6s-2.69-6-6-6zm-.01 16.95H12c.55 0 .99-.44.99-.99v-.96c0-.55-.44-.99-.99-.99h-.01c-.55 0-.99.44-.99.99v.96c0 .55.44.99.99.99zm-7.74-3.21c.39.39 1.02.39 1.41 0l.39-.39a.993.993 0 0 0 0-1.4l-.01-.01a.996.996 0 0 0-1.41 0l-.39.39c-.38.4-.38 1.02.01 1.41z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
14
docs/.vitepress/vitepress/components/icons/source-code.vue
Normal file
14
docs/.vitepress/vitepress/components/icons/source-code.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path
|
||||
d="M8.7 15.9L4.8 12l3.9-3.9a.984.984 0 0 0 0-1.4a.984.984 0 0 0-1.4 0l-4.59 4.59a.996.996 0 0 0 0 1.41l4.59 4.6c.39.39 1.01.39 1.4 0a.984.984 0 0 0 0-1.4zm6.6 0l3.9-3.9l-3.9-3.9a.984.984 0 0 1 0-1.4a.984.984 0 0 1 1.4 0l4.59 4.59c.39.39.39 1.02 0 1.41l-4.59 4.6a.984.984 0 0 1-1.4 0a.984.984 0 0 1 0-1.4z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SourceCodeIcon',
|
||||
}
|
||||
</script>
|
18
docs/.vitepress/vitepress/components/icons/toggle-button.vue
Normal file
18
docs/.vitepress/vitepress/components/icons/toggle-button.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<g fill="none">
|
||||
<path
|
||||
d="M4 6a1 1 0 0 1 1-1h14a1 1 0 1 1 0 2H5a1 1 0 0 1-1-1z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M4 18a1 1 0 0 1 1-1h14a1 1 0 1 1 0 2H5a1 1 0 0 1-1-1z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M11 11a1 1 0 1 0 0 2h8a1 1 0 1 0 0-2h-8z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 512 512">
|
||||
<path
|
||||
d="M478.33 433.6l-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362L368 281.65L401.17 362z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M267.84 342.92a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73c39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36c-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93c.92 1.19 1.83 2.35 2.74 3.51c-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59c22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
13
docs/.vitepress/vitepress/components/navbar/vp-hamburger.vue
Normal file
13
docs/.vitepress/vitepress/components/navbar/vp-hamburger.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
active: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{ 'menu-hamburger': true, active }" role="button">
|
||||
<span class="hamburger-1" />
|
||||
<span class="hamburger-2" />
|
||||
<span class="hamburger-3" />
|
||||
</div>
|
||||
</template>
|
50
docs/.vitepress/vitepress/components/navbar/vp-menu-link.vue
Normal file
50
docs/.vitepress/vitepress/components/navbar/vp-menu-link.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<script lang="ts" setup>
|
||||
import { useData, useRoute } from 'vitepress'
|
||||
import VPLink from '../common/vp-link.vue'
|
||||
import { isActiveLink } from '../../utils'
|
||||
|
||||
import type { Link } from '../../types'
|
||||
|
||||
defineProps<{
|
||||
item: Link
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VPLink
|
||||
:class="{
|
||||
'is-menu-link': true,
|
||||
active: isActiveLink(
|
||||
route,
|
||||
item.activeMatch || item.link,
|
||||
!!item.activeMatch
|
||||
),
|
||||
}"
|
||||
:href="item.link"
|
||||
:no-icon="true"
|
||||
>
|
||||
{{ item.text }}
|
||||
</VPLink>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.is-menu-link {
|
||||
display: block;
|
||||
padding: 0 12px;
|
||||
line-height: calc(var(--nav-height) - 3px);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color);
|
||||
transition: color var(--el-transition-duration);
|
||||
|
||||
&.active {
|
||||
border-bottom: 2px solid var(--brand-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--brand-color);
|
||||
}
|
||||
}
|
||||
</style>
|
12
docs/.vitepress/vitepress/components/navbar/vp-menu.vue
Normal file
12
docs/.vitepress/vitepress/components/navbar/vp-menu.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import VPMenuLink from './vp-menu-link.vue'
|
||||
import { useNav } from '../../composables/nav'
|
||||
|
||||
const navs = useNav()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav v-if="navs" class="navbar-menu">
|
||||
<VPMenuLink v-for="item in navs" :item="item" />
|
||||
</nav>
|
||||
</template>
|
205
docs/.vitepress/vitepress/components/navbar/vp-search.vue
Normal file
205
docs/.vitepress/vitepress/components/navbar/vp-search.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<script setup lang="ts">
|
||||
import '@docsearch/css'
|
||||
import { ref, computed, watch, onMounted, getCurrentInstance } from 'vue'
|
||||
import { useRouter, useRoute } from 'vitepress'
|
||||
import docsearch from '@docsearch/js'
|
||||
import { useLang } from '../../composables/lang'
|
||||
// import type { DefaultTheme } from '../config'
|
||||
import type { DocSearchHit } from '@docsearch/react/dist/esm/types'
|
||||
|
||||
const props = defineProps<{
|
||||
options: any
|
||||
multilang?: boolean
|
||||
}>()
|
||||
|
||||
const vm = getCurrentInstance()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
watch(
|
||||
() => props.options,
|
||||
(value) => {
|
||||
update(value)
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
initialize(props.options)
|
||||
})
|
||||
|
||||
function isSpecialClick(event: MouseEvent) {
|
||||
return (
|
||||
event.button === 1 ||
|
||||
event.altKey ||
|
||||
event.ctrlKey ||
|
||||
event.metaKey ||
|
||||
event.shiftKey
|
||||
)
|
||||
}
|
||||
|
||||
function getRelativePath(absoluteUrl: string) {
|
||||
const { pathname, hash } = new URL(absoluteUrl)
|
||||
|
||||
return pathname + hash
|
||||
}
|
||||
|
||||
function update(options: any) {
|
||||
if (vm && vm.vnode.el) {
|
||||
vm.vnode.el.innerHTML =
|
||||
'<div class="algolia-search-box" id="docsearch"></div>'
|
||||
initialize(options)
|
||||
}
|
||||
}
|
||||
|
||||
const searchIndexMap = {
|
||||
'zh-CN': 'element-zh',
|
||||
'en-US': 'element-en',
|
||||
es: 'element-es',
|
||||
'fr-FR': 'element-fr',
|
||||
jp: 'element-jp',
|
||||
}
|
||||
|
||||
const lang = useLang()
|
||||
|
||||
function initialize(userOptions: any) {
|
||||
// if the user has multiple locales, the search results should be filtered
|
||||
// based on the language
|
||||
const facetFilters = props.multilang ? ['language:' + lang.value] : []
|
||||
|
||||
docsearch(
|
||||
Object.assign({}, userOptions, {
|
||||
container: '#docsearch',
|
||||
indexName: searchIndexMap[lang.value],
|
||||
searchParameters: Object.assign({}, userOptions.searchParameters, {
|
||||
// pass a custom lang facetFilter to allow multiple language search
|
||||
// https://github.com/algolia/docsearch-configs/pull/3942
|
||||
facetFilters: facetFilters.concat(
|
||||
userOptions.searchParameters?.facetFilters || []
|
||||
),
|
||||
}),
|
||||
|
||||
navigator: {
|
||||
navigate: ({ suggestionUrl }: { suggestionUrl: string }) => {
|
||||
const { pathname: hitPathname } = new URL(
|
||||
window.location.origin + suggestionUrl
|
||||
)
|
||||
|
||||
// Router doesn't handle same-page navigation so we use the native
|
||||
// browser location API for anchor navigation
|
||||
if (route.path === hitPathname) {
|
||||
window.location.assign(window.location.origin + suggestionUrl)
|
||||
} else {
|
||||
router.go(suggestionUrl)
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformItems: (items: DocSearchHit[]) => {
|
||||
return items.map((item) => {
|
||||
return Object.assign({}, item, {
|
||||
url: getRelativePath(item.url),
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
hitComponent: ({
|
||||
hit,
|
||||
children,
|
||||
}: {
|
||||
hit: DocSearchHit
|
||||
children: any
|
||||
}) => {
|
||||
const relativeHit = hit.url.startsWith('http')
|
||||
? getRelativePath(hit.url as string)
|
||||
: hit.url
|
||||
|
||||
return {
|
||||
type: 'a',
|
||||
ref: undefined,
|
||||
constructor: undefined,
|
||||
key: undefined,
|
||||
props: {
|
||||
href: hit.url,
|
||||
onClick: (event: MouseEvent) => {
|
||||
if (isSpecialClick(event)) {
|
||||
return
|
||||
}
|
||||
|
||||
// we rely on the native link scrolling when user is already on
|
||||
// the right anchor because Router doesn't support duplicated
|
||||
// history entries
|
||||
if (route.path === relativeHit) {
|
||||
return
|
||||
}
|
||||
|
||||
// if the hits goes to another page, we prevent the native link
|
||||
// behavior to leverage the Router loading feature
|
||||
if (route.path !== relativeHit) {
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
router.go(relativeHit)
|
||||
},
|
||||
children,
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="algolia-search-box" id="docsearch" />
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../styles/mixins';
|
||||
.algolia-search-box {
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// line-height: var(--header-height);
|
||||
// padding-left: 0.5rem;
|
||||
// padding-top: 1px;
|
||||
// margin-right: 12px;
|
||||
// .search-box-placeholder,
|
||||
// .search-box-key {
|
||||
// display: flex;
|
||||
// }
|
||||
|
||||
@include respond-to('md') {
|
||||
min-width: 176.3px;
|
||||
}
|
||||
}
|
||||
|
||||
.DocSearch {
|
||||
--docsearch-primary-color: var(--brand-color);
|
||||
--docsearch-key-gradient: rgba(125, 125, 125, 0.1);
|
||||
// --docsearch-key-shadow: rgba(125, 125, 125, 0.3);
|
||||
--docsearch-footer-height: 44px;
|
||||
--docsearch-footer-background: var(--bg-color);
|
||||
--docsearch-footer-shadow: 0 -1px 0 0 #e0e3e8,
|
||||
0 -3px 6px 0 rgba(69, 98, 155, 0.12);
|
||||
--docsearch-searchbox-background: var(--bg-color-soft);
|
||||
--docsearch-searchbox-focus-background: var(--bg-color-mute);
|
||||
--docsearch-muted-color: var(--text-color-lighter);
|
||||
--docsearch-text-color: var(--text-color-light);
|
||||
--docsearch-modal-background: var(--bg-color-soft);
|
||||
|
||||
.dark & {
|
||||
--docsearch-text-color: var(--text-color-light);
|
||||
// --docsearch-searchbox-focus-background: var(--bg-color-mute);
|
||||
.DocSearch-Button {
|
||||
.DocSearch-Button-Key {
|
||||
box-shadow: unset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
@include respond-to('md') {
|
||||
background-color: var(--docsearch-searchbox-background);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue'
|
||||
|
||||
defineProps<{
|
||||
icon: Component
|
||||
link: string
|
||||
text: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a
|
||||
:href="link"
|
||||
:title="text"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
class="social-link"
|
||||
>
|
||||
<ElIcon :size="20">
|
||||
<component :is="icon" />
|
||||
</ElIcon>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.social-link {
|
||||
padding: 0 4px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
import VPSocialLink from './vp-social-link.vue'
|
||||
import { useSocialLinks } from '../../composables/social-links'
|
||||
|
||||
const { theme } = useData()
|
||||
|
||||
const links = useSocialLinks()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="social-links">
|
||||
<VPSocialLink v-for="link in links" v-bind="link" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.social-links {
|
||||
height: 20px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from '../../composables/theme'
|
||||
import CommonThemeToggler from '../common/vp-theme-toggler.vue'
|
||||
|
||||
const toggle = useTheme()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="theme-toggler-content">
|
||||
<CommonThemeToggler @click="toggle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '../../styles/mixins';
|
||||
.theme-toggler-content {
|
||||
@include with-bg;
|
||||
display: none;
|
||||
border-radius: 50%;
|
||||
height: 20px;
|
||||
padding: 0 8px;
|
||||
|
||||
@include respond-to('md') {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from 'vue'
|
||||
import VPLink from '../common/vp-link.vue'
|
||||
import { useTranslation } from '../../composables/translation'
|
||||
import { PREFERRED_LANG_KEY, defaultLang } from '../../constant'
|
||||
|
||||
import TranslationIcon from '../icons/translation-icon.vue'
|
||||
|
||||
const { switchLang, languageMap, langs, lang, helpTranslate } = useTranslation()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="translation-container">
|
||||
<ClientOnly>
|
||||
<ElPopover
|
||||
:show-arrow="false"
|
||||
trigger="hover"
|
||||
popper-class="translation-popup"
|
||||
>
|
||||
<template #reference>
|
||||
<ElIcon :size="20">
|
||||
<TranslationIcon />
|
||||
</ElIcon>
|
||||
</template>
|
||||
<div
|
||||
v-for="l in langs"
|
||||
:key="l"
|
||||
@click="switchLang(l)"
|
||||
:class="{ language: true, selected: l === lang }"
|
||||
>
|
||||
{{ languageMap[l] }}
|
||||
</div>
|
||||
<div class="language">
|
||||
<VPLink href="https://crowdin.com/project/element-plus">
|
||||
{{ helpTranslate }}
|
||||
</VPLink>
|
||||
</div>
|
||||
</ElPopover>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '../../styles/mixins';
|
||||
.translation-container {
|
||||
display: none;
|
||||
height: 20px;
|
||||
padding: 0 8px;
|
||||
|
||||
@include respond-to('md') {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@at-root .translation-popup.el-popper {
|
||||
box-shadow: var(--el-box-shadow-base);
|
||||
|
||||
.language {
|
||||
cursor: pointer;
|
||||
padding: 0 16px;
|
||||
line-height: 28px;
|
||||
&.selected {
|
||||
color: var(--brand-color);
|
||||
}
|
||||
|
||||
.link-item {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,61 @@
|
||||
<script lang="ts" setup>
|
||||
import { useRoute } from 'vitepress'
|
||||
import { isActive } from '../../utils'
|
||||
|
||||
import type { Link } from '../../types'
|
||||
|
||||
defineProps<{
|
||||
item: Link
|
||||
}>()
|
||||
|
||||
defineEmits(['close'])
|
||||
|
||||
const route = useRoute()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a
|
||||
:class="{ link: true, active: isActive(route, item.link) }"
|
||||
:href="item.link"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<p class="link-text">{{ item.text }}</p>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.link {
|
||||
display: block;
|
||||
padding: 0.625rem 2rem 0.625rem 1.5rem;
|
||||
line-height: 1.5;
|
||||
font-size: 0.9rem;
|
||||
margin: 0 8px;
|
||||
border-radius: 8px;
|
||||
|
||||
.link-text {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.link:hover .link-text {
|
||||
color: var(--brand-color);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
|
||||
.link.active {
|
||||
background-color: var(--bg-brand-color);
|
||||
.link-text {
|
||||
font-weight: 600;
|
||||
color: var(--brand-color);
|
||||
transition: color 0.25s;
|
||||
}
|
||||
}
|
||||
|
||||
.link-text {
|
||||
line-height: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--text-color-light);
|
||||
transition: color 0.5s;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,32 @@
|
||||
<script lang="ts" setup>
|
||||
import ToggleButton from '../icons/toggle-button.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sidebar-button">
|
||||
<ElIcon :size="24">
|
||||
<ToggleButton />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.sidebar-button {
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar-button .icon {
|
||||
display: block;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
/* @media screen and (max-width: 1044px) {
|
||||
.sidebar-button {
|
||||
display: block;
|
||||
}
|
||||
} */
|
||||
</style>
|
59
docs/.vitepress/vitepress/components/vp-app.vue
Normal file
59
docs/.vitepress/vitepress/components/vp-app.vue
Normal file
@ -0,0 +1,59 @@
|
||||
<script setup>
|
||||
import VPOverlay from './vp-overlay.vue'
|
||||
import VPNav from './vp-nav.vue'
|
||||
import VPSubNav from './vp-subnav.vue'
|
||||
import VPSidebar from './vp-sidebar.vue'
|
||||
import VPContent from './vp-content.vue'
|
||||
import VPSponsors from './vp-sponsors.vue'
|
||||
import { useToggle } from '../composables/toggle'
|
||||
import { useSidebar } from '../composables/sidebar'
|
||||
import { useToggleWidgets } from '../composables/toggle-widgets'
|
||||
import { breakpoints } from '../constant'
|
||||
|
||||
const [isSidebarOpen, toggleSidebar] = useToggle(false)
|
||||
const { hasSidebar } = useSidebar()
|
||||
|
||||
useToggleWidgets(isSidebarOpen, () => {
|
||||
if (window.outerWidth >= breakpoints.lg) {
|
||||
toggleSidebar(false)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="App">
|
||||
<VPOverlay
|
||||
class="overlay"
|
||||
:show="isSidebarOpen"
|
||||
@click="toggleSidebar(false)"
|
||||
/>
|
||||
<VPNav />
|
||||
<VPSubNav v-if="hasSidebar" @open-menu="toggleSidebar(true)" />
|
||||
<VPSidebar :open="isSidebarOpen" @close="toggleSidebar(false)">
|
||||
<template #top>
|
||||
<VPSponsors />
|
||||
</template>
|
||||
<template #bottom>
|
||||
<slot name="sidebar-bottom" />
|
||||
</template>
|
||||
</VPSidebar>
|
||||
<VPContent>
|
||||
<template #content-top>
|
||||
<slot name="content-top" />
|
||||
</template>
|
||||
<template #content-bottom>
|
||||
<slot name="content-bottom" />
|
||||
</template>
|
||||
<template #aside-top>
|
||||
<slot name="aside-top" />
|
||||
</template>
|
||||
<template #aside-mid>
|
||||
<slot name="aside-mid" />
|
||||
</template>
|
||||
<template #aside-bottom>
|
||||
<slot name="aside-bottom" />
|
||||
</template>
|
||||
</VPContent>
|
||||
<Debug />
|
||||
</div>
|
||||
</template>
|
25
docs/.vitepress/vitepress/components/vp-content.vue
Normal file
25
docs/.vitepress/vitepress/components/vp-content.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useData, useRoute } from 'vitepress'
|
||||
import { useSidebar } from '../composables/sidebar'
|
||||
import VPHeroContent from './vp-hero-content.vue'
|
||||
import VPDocContent from './vp-doc-content.vue'
|
||||
import VPNotFound from './vp-not-found.vue'
|
||||
|
||||
const { frontmatter } = useData()
|
||||
const route = useRoute()
|
||||
const isNotFound = computed(() => route.component === VPNotFound)
|
||||
const isHeroPost = computed(() => frontmatter.value.page === true)
|
||||
const { hasSidebar } = useSidebar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main :class="{ 'page-content': true, 'has-sidebar': hasSidebar }">
|
||||
<VPNotFound v-if="isNotFound" />
|
||||
<VPHeroContent v-else-if="isHeroPost" />
|
||||
<VPDocContent v-else>
|
||||
<template #content-top><slot name="content-top" /></template>
|
||||
<template #content-bottom><slot name="content-bottom" /></template>
|
||||
</VPDocContent>
|
||||
</main>
|
||||
</template>
|
154
docs/.vitepress/vitepress/components/vp-demo.vue
Normal file
154
docs/.vitepress/vitepress/components/vp-demo.vue
Normal file
@ -0,0 +1,154 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, shallowRef, watch, toRef, ref } from 'vue'
|
||||
import { useToggle } from '../composables/toggle'
|
||||
import { useLang } from '../composables/lang'
|
||||
import { useSourceCode } from '../composables/source-code'
|
||||
|
||||
import GithubIcon from './icons/github.vue'
|
||||
import SourceCodeIcon from './icons/source-code.vue'
|
||||
import CodepenIcon from './icons/codepen.vue'
|
||||
|
||||
import Example from './demo/vp-example.vue'
|
||||
import SourceCode from './demo/vp-source-code.vue'
|
||||
import Codepen from './demo/vp-codepen.vue'
|
||||
|
||||
import demoBlockLocale from '../../i18n/component/demo-block.json'
|
||||
|
||||
const props = defineProps({
|
||||
// source is encoded via encodeURIComponent
|
||||
source: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
css: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
cssPreProcessor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
js: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
jsPreProcessor: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
html: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
demos: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const formatPathDemos = computed(() => {
|
||||
const demos = {}
|
||||
|
||||
Object.keys(props.demos).forEach((key) => {
|
||||
demos[key.replace('../../examples/', '').replace('.vue', '')] =
|
||||
props.demos[key].default
|
||||
})
|
||||
|
||||
return demos
|
||||
})
|
||||
|
||||
const loaded = shallowRef(false)
|
||||
const hasError = shallowRef(false)
|
||||
const dataOpt = shallowRef('')
|
||||
const setupScript = shallowRef('')
|
||||
const template = shallowRef('')
|
||||
const [sourceVisible, setSourceVisible] = useToggle()
|
||||
const hasSetup = computed(() => loaded.value && setupScript.value !== '')
|
||||
const lang = useLang()
|
||||
|
||||
const locale = computed(() => demoBlockLocale[lang.value])
|
||||
|
||||
const onDemoLoaded = (content) => {
|
||||
loaded.value = true
|
||||
}
|
||||
|
||||
const demoSourceUrl = useSourceCode(toRef(props, 'path'))
|
||||
const codepenRef = ref()
|
||||
|
||||
const onCodepenClicked = () => {
|
||||
codepenRef.value.submit?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div class="example">
|
||||
<Codepen
|
||||
ref="codepenRef"
|
||||
:css="props.css"
|
||||
:css-pre-processor="props.cssPreProcessor"
|
||||
:html="props.html"
|
||||
:js="props.js"
|
||||
:js-pre-processor="props.jsPreProcessor"
|
||||
/>
|
||||
<div class="op-btns">
|
||||
<ElTooltip :content="locale['edit-in-codepen']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn">
|
||||
<CodepenIcon @click="onCodepenClicked" />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
<ElTooltip :content="locale['edit-on-github']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn github">
|
||||
<a :href="demoSourceUrl" rel="noreferrer noopener" target="_blank">
|
||||
<GithubIcon />
|
||||
</a>
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
<ElTooltip :content="locale['view-source']" :visible-arrow="false">
|
||||
<ElIcon :size="20" class="op-btn" @click="setSourceVisible">
|
||||
<SourceCodeIcon />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
<ElDivider />
|
||||
<Example :file="path" :demo="formatPathDemos[path]" />
|
||||
<ElDivider v-if="sourceVisible" />
|
||||
<el-collapse-transition>
|
||||
<SourceCode v-show="sourceVisible" :source="source" />
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.example {
|
||||
border: 1px solid var(--border-color-light);
|
||||
border-radius: var(--el-border-radius-base);
|
||||
|
||||
.op-btns {
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: 3rem;
|
||||
line-height: 3rem;
|
||||
|
||||
.op-btn {
|
||||
margin: 0 0.5rem;
|
||||
cursor: pointer;
|
||||
color: var(--text-color);
|
||||
|
||||
&.github a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-divider {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
19
docs/.vitepress/vitepress/components/vp-doc-content.vue
Normal file
19
docs/.vitepress/vitepress/components/vp-doc-content.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<script lang="ts" setup>
|
||||
import { useData } from 'vitepress'
|
||||
import VPPageFooter from './doc-content/vp-page-footer.vue'
|
||||
import VPPageNav from './doc-content/vp-page-nav.vue'
|
||||
import VPTableOfContent from './doc-content/vp-table-of-content.vue'
|
||||
|
||||
const { page } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="doc-content-wrapper">
|
||||
<div class="doc-content-container">
|
||||
<Content class="doc-content" />
|
||||
<VPPageFooter />
|
||||
<VPPageNav />
|
||||
</div>
|
||||
<VPTableOfContent v-if="page.headers" />
|
||||
</div>
|
||||
</template>
|
73
docs/.vitepress/vitepress/components/vp-header.vue
Normal file
73
docs/.vitepress/vitepress/components/vp-header.vue
Normal file
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<header class="nav-bar">
|
||||
<div class="nav-wrapper">
|
||||
<div class="header-container">
|
||||
<a :href="currentRoot" class="icon-link">
|
||||
<slot name="header-logo"></slot>
|
||||
<!-- <ElIcon class="logo">
|
||||
<ElementPlusTextLogo class="nav-logo" />
|
||||
</ElIcon> -->
|
||||
</a>
|
||||
|
||||
<div class="nav-action-items">
|
||||
<AlgoliaSearch :options="theme.agolia" />
|
||||
<nav class="nav-menu">
|
||||
<slot name="header-nav"></slot>
|
||||
<!-- <nav-link
|
||||
v-for="(navItem, key) in nav"
|
||||
:key="key"
|
||||
:item="navItem"
|
||||
class="nav-item"
|
||||
/> -->
|
||||
</nav>
|
||||
<div :class="{ 'theme-switcher': true, 'is-dark': isDark }">
|
||||
<ElIcon class="header-icon" @click="$emit('toggle-dark')">
|
||||
<Dark class="dark-icon" />
|
||||
<Light class="light-icon" />
|
||||
</ElIcon>
|
||||
</div>
|
||||
<div class="action-icons">
|
||||
<div class="action-group translations">
|
||||
<ElIcon class="header-icon translation-icon">
|
||||
<TranslationIcon />
|
||||
<div class="dropdown-content">
|
||||
<ul class="languages-list">
|
||||
<li
|
||||
v-for="l in langs"
|
||||
:key="l"
|
||||
@click="switchLang(l)"
|
||||
:class="{ language: true, selected: l === lang }"
|
||||
>
|
||||
{{ languageMap[l] }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ElIcon>
|
||||
</div>
|
||||
<div class="header-socials" v-if></div>
|
||||
<ElIcon class="header-icon social-icon">
|
||||
<a
|
||||
href="https://github.com/element-plus/element-plus"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<GithubIcon />
|
||||
</a>
|
||||
</ElIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="sub-nav">
|
||||
<ToggleSidebarBtn v-if="hasSidebar" @toggle="$emit('toggle-sidebar')" />
|
||||
<Transition name="shifting">
|
||||
<ElButton
|
||||
type="text"
|
||||
:class="{ 'go-back-top': true, show: shouldShow }"
|
||||
@click.prevent.stop="scrollToTop"
|
||||
>{{ 'Back to top' }}</ElButton
|
||||
>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
5
docs/.vitepress/vitepress/components/vp-hero-content.vue
Normal file
5
docs/.vitepress/vitepress/components/vp-hero-content.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="hero-content">
|
||||
<Content />
|
||||
</div>
|
||||
</template>
|
64
docs/.vitepress/vitepress/components/vp-nav-full.vue
Normal file
64
docs/.vitepress/vitepress/components/vp-nav-full.vue
Normal file
@ -0,0 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import VPFullScreenMenu from './full-screen/vp-menu.vue'
|
||||
import VPFullScreenTranslation from './full-screen/vp-translation.vue'
|
||||
import VPFullScreenThemeToggler from './full-screen/vp-theme-toggler.vue'
|
||||
import { useLockScreen } from '../composables/lock-screen'
|
||||
import { useFeatureFlag } from '../composables/feature-flag'
|
||||
|
||||
defineProps<{
|
||||
fullScreen: boolean
|
||||
}>()
|
||||
|
||||
const { lock, cleanup } = useLockScreen()
|
||||
const fullscreen = ref()
|
||||
const themeEnabled = useFeatureFlag('theme')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="el-fade-in" @enter="lock" @after-leave="cleanup">
|
||||
<div v-if="fullScreen" ref="fullscreen">
|
||||
<div class="full-screen-container">
|
||||
<VPFullScreenMenu @close="$emit('close')" />
|
||||
<VPFullScreenTranslation />
|
||||
<VPFullScreenThemeToggler v-if="themeEnabled" />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.full-screen {
|
||||
position: fixed;
|
||||
top: var(--nav-height);
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0 32px;
|
||||
width: 100%;
|
||||
background-color: var(--bg-color);
|
||||
transition: background-color 0.5s;
|
||||
overflow-y: auto;
|
||||
|
||||
&.el-fade-in-enter-active,
|
||||
&.el-fade-in-leave-active {
|
||||
.full-screen-container {
|
||||
transition: transform var(--el-transition-duration)
|
||||
var(--el-transition-function-ease-in-out-bezier);
|
||||
}
|
||||
}
|
||||
|
||||
&.el-fade-in-enter-from,
|
||||
&.el-fade-in-leave-to {
|
||||
.full-screen-container {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
}
|
||||
|
||||
.full-screen-container {
|
||||
margin: 0 auto;
|
||||
padding: 24px 0 96px;
|
||||
max-width: 18rem;
|
||||
}
|
||||
}
|
||||
</style>
|
26
docs/.vitepress/vitepress/components/vp-nav.vue
Normal file
26
docs/.vitepress/vitepress/components/vp-nav.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue'
|
||||
import { useSidebar } from '../composables/sidebar'
|
||||
import { useFullScreen } from '../composables/fullscreen'
|
||||
import { useToggleWidgets } from '../composables/toggle-widgets'
|
||||
import { breakpoints } from '../constant'
|
||||
import VpNavbar from './vp-navbar.vue'
|
||||
import VpNavFull from './vp-nav-full.vue'
|
||||
|
||||
const { hasSidebar } = useSidebar()
|
||||
const { toggleFullScreen, isFullScreen } = useFullScreen()
|
||||
const close = () => toggleFullScreen(false)
|
||||
|
||||
useToggleWidgets(isFullScreen, () => {
|
||||
if (window.outerWidth >= breakpoints.md) {
|
||||
close()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header :class="{ navbar: true, 'has-sidebar': hasSidebar }">
|
||||
<VpNavbar :full-screen="isFullScreen" @toggle="toggleFullScreen" />
|
||||
<VpNavFull :full-screen="isFullScreen" @close="close" class="full-screen" />
|
||||
</header>
|
||||
</template>
|
65
docs/.vitepress/vitepress/components/vp-navbar.vue
Normal file
65
docs/.vitepress/vitepress/components/vp-navbar.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<script setup lang="ts">
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
import VPNavbarSearch from './navbar/vp-search.vue'
|
||||
import VPNavbarMenu from './navbar/vp-menu.vue'
|
||||
import VPNavbarThemeToggler from './navbar/vp-theme-toggler.vue'
|
||||
import VPNavbarTranslation from './navbar/vp-translation.vue'
|
||||
import VPNavbarSocialLinks from './navbar/vp-social-links.vue'
|
||||
import VPNavbarHamburger from './navbar/vp-hamburger.vue'
|
||||
import { useFeatureFlag } from '../composables/feature-flag'
|
||||
|
||||
defineProps<{
|
||||
fullScreen: boolean
|
||||
}>()
|
||||
|
||||
defineEmits(['toggle'])
|
||||
const themeEnabled = useFeatureFlag('theme')
|
||||
|
||||
const { theme } = useData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="navbar-wrapper">
|
||||
<div class="container">
|
||||
<div class="logo-container">
|
||||
<a href="/">
|
||||
<img
|
||||
class="logo"
|
||||
src="/images/element-plus-logo.svg"
|
||||
alt="Elemenet Plus Logo"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="content">
|
||||
<VPNavbarSearch class="search" :options="theme.agolia" />
|
||||
<VPNavbarMenu class="menu" />
|
||||
<VPNavbarThemeToggler v-if="themeEnabled" class="theme-toggler" />
|
||||
<VPNavbarTranslation class="translation" />
|
||||
<VPNavbarSocialLinks class="social-links" />
|
||||
<VPNavbarHamburger
|
||||
:active="fullScreen"
|
||||
class="hamburger"
|
||||
@click="$emit('toggle')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--header-height);
|
||||
> a {
|
||||
height: 28px;
|
||||
width: 128px;
|
||||
}
|
||||
.logo {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
28
docs/.vitepress/vitepress/components/vp-not-found.vue
Normal file
28
docs/.vitepress/vitepress/components/vp-not-found.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useLang } from '../composables/lang'
|
||||
import localeData from '../../i18n/pages/not-found.json'
|
||||
|
||||
const lang = useLang()
|
||||
|
||||
const locale = computed(() => localeData[lang.value])
|
||||
|
||||
const goHome = () => {
|
||||
window.location.href = `/${lang.value}/`
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-result icon="error" :title="locale.title" :sub-title="locale.desc">
|
||||
<template #extra>
|
||||
<el-button @click="goHome">{{ locale['button-title'] }}</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.el-result {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
</style>
|
11
docs/.vitepress/vitepress/components/vp-overlay.vue
Normal file
11
docs/.vitepress/vitepress/components/vp-overlay.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
show: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="el-fade-in">
|
||||
<div v-if="show" />
|
||||
</Transition>
|
||||
</template>
|
45
docs/.vitepress/vitepress/components/vp-sidebar.vue
Normal file
45
docs/.vitepress/vitepress/components/vp-sidebar.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, markRaw, watch } from 'vue'
|
||||
import { useRoute, withBase, useData } from 'vitepress'
|
||||
import VPSidebarLink from './sidebar/vp-sidebar-link.vue'
|
||||
import { useSidebar } from '../composables/sidebar'
|
||||
import { useLang } from '../composables/lang'
|
||||
|
||||
import sponsorsData from '../../i18n/component/sponsor.json'
|
||||
|
||||
type SideNavItem = {
|
||||
beta: boolean
|
||||
text: string
|
||||
link: string
|
||||
activeMatch: string
|
||||
children: Array<SideNavItem>
|
||||
}
|
||||
|
||||
defineProps<{ open: boolean }>()
|
||||
defineEmits(['close'])
|
||||
|
||||
// const isHome = useIsHome()
|
||||
const { theme } = useData()
|
||||
const route = useRoute()
|
||||
const lang = useLang()
|
||||
const { sidebars, hasSidebar } = useSidebar()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside v-if="hasSidebar" :class="{ sidebar: true, open }">
|
||||
<slot name="top" />
|
||||
<div class="sidebar-groups">
|
||||
<section v-for="(item, key) of sidebars" class="sidebar-group">
|
||||
<p class="sidebar-group__title">
|
||||
{{ item.text }}
|
||||
</p>
|
||||
<VPSidebarLink
|
||||
v-for="item in item.children"
|
||||
:item="item"
|
||||
@close="$emit('close')"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
<slot name="bottom" />
|
||||
</aside>
|
||||
</template>
|
33
docs/.vitepress/vitepress/components/vp-sponsor.vue
Normal file
33
docs/.vitepress/vitepress/components/vp-sponsor.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
item: {
|
||||
name: string
|
||||
img: string
|
||||
url: string
|
||||
}
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sponsor-item">
|
||||
<a :href="item.url" :title="item.name">
|
||||
<img :src="item.img" :alt="item.name" />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.sponsor-item {
|
||||
margin-right: 4px;
|
||||
|
||||
a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
38
docs/.vitepress/vitepress/components/vp-sponsors.vue
Normal file
38
docs/.vitepress/vitepress/components/vp-sponsors.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import VPSponsor from './vp-sponsor.vue'
|
||||
import sponsorsLocale from '../../i18n/component/sponsors.json'
|
||||
import sponsorLocale from '../../i18n/component/sponsor.json'
|
||||
import { useLang } from '../composables/lang'
|
||||
import { defaultLang } from '../constant'
|
||||
|
||||
const lang = useLang()
|
||||
const sponsors = computed(() => sponsorsLocale[lang.value])
|
||||
|
||||
const sponsor = computed(() => sponsorLocale[lang.value])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sponsors">
|
||||
<p class="sponsors-title">{{ sponsor.sponsoredBy }}</p>
|
||||
<div class="container">
|
||||
<VPSponsor v-for="sponsor in sponsors" :item="sponsor" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sponsors {
|
||||
padding: 0.35rem 1.5rem 0.35rem 1.25rem;
|
||||
.sponsors-title {
|
||||
color: var(--text-color-lighter);
|
||||
font-weight: 300;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
23
docs/.vitepress/vitepress/components/vp-subnav.vue
Normal file
23
docs/.vitepress/vitepress/components/vp-subnav.vue
Normal file
@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import ToggleSidebarBtn from './subnav/toggle-sidebar-btn.vue'
|
||||
import { useSidebar } from '../composables/sidebar'
|
||||
import { useBackTop } from '../composables/back-top'
|
||||
defineEmits(['open-menu'])
|
||||
|
||||
const { hasSidebar } = useSidebar()
|
||||
const { shouldShow, scrollToTop } = useBackTop()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sub-nav">
|
||||
<ToggleSidebarBtn v-if="hasSidebar" @click="$emit('open-menu')" />
|
||||
<Transition name="shifting">
|
||||
<ElButton
|
||||
type="text"
|
||||
:class="{ 'go-back-top': true, show: shouldShow }"
|
||||
@click.prevent.stop="scrollToTop"
|
||||
>{{ 'Back to top' }}</ElButton
|
||||
>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
119
docs/.vitepress/vitepress/composables/active-bar.ts
Normal file
119
docs/.vitepress/vitepress/composables/active-bar.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { computed, shallowRef, onMounted, onUnmounted, onUpdated } from 'vue'
|
||||
import { throttleAndDebounce } from '../utils'
|
||||
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
const BOUNDING_OFFSET = 100 // 56 the header height + margin-top 32
|
||||
|
||||
export function useActiveSidebarLinks(
|
||||
container: Ref<HTMLElement>,
|
||||
marker: Ref<HTMLElement>
|
||||
) {
|
||||
const onScroll = throttleAndDebounce(setActiveLink, 150)
|
||||
function setActiveLink() {
|
||||
const sidebarLinks = getSidebarLinks()
|
||||
const anchors = getAnchors(sidebarLinks)
|
||||
|
||||
if (
|
||||
anchors.length &&
|
||||
window.scrollY + window.innerHeight === document.body.offsetHeight
|
||||
) {
|
||||
activateLink(anchors[anchors.length - 1].hash)
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < anchors.length; i++) {
|
||||
const anchor = anchors[i]
|
||||
const nextAnchor = anchors[i + 1]
|
||||
const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor)
|
||||
if (isActive) {
|
||||
history.replaceState(
|
||||
null,
|
||||
document.title,
|
||||
hash ? (hash as string) : ' '
|
||||
)
|
||||
activateLink(hash as string)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let prevActiveLink: HTMLAnchorElement | null = null
|
||||
|
||||
function activateLink(hash: string) {
|
||||
deactiveLink(prevActiveLink)
|
||||
|
||||
const activeLink = (prevActiveLink =
|
||||
hash == null
|
||||
? null
|
||||
: (container.value.querySelector(
|
||||
`.toc-item a[href="${decodeURIComponent(hash)}"]`
|
||||
) as HTMLAnchorElement))
|
||||
if (activeLink) {
|
||||
activeLink.classList.add('active')
|
||||
marker.value.style.opacity = '1'
|
||||
marker.value.style.top = activeLink.offsetTop + 'px'
|
||||
} else {
|
||||
marker.value.style.opacity = '0'
|
||||
marker.value.style.top = '33px'
|
||||
}
|
||||
}
|
||||
|
||||
function deactiveLink(link: HTMLElement) {
|
||||
link && link.classList.remove('active')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.requestAnimationFrame(setActiveLink)
|
||||
window.addEventListener('scroll', onScroll)
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
activateLink(location.hash)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('scroll', onScroll)
|
||||
})
|
||||
}
|
||||
function getSidebarLinks() {
|
||||
return Array.from(
|
||||
document.querySelectorAll('.toc-content .toc-link')
|
||||
) as HTMLAnchorElement[]
|
||||
}
|
||||
function getAnchors(sidebarLinks: HTMLAnchorElement[]) {
|
||||
return (
|
||||
Array.from(
|
||||
document.querySelectorAll('.doc-content .header-anchor')
|
||||
) as HTMLAnchorElement[]
|
||||
).filter((anchor) =>
|
||||
sidebarLinks.some((sidebarLink) => sidebarLink.hash === anchor.hash)
|
||||
)
|
||||
}
|
||||
function getPageOffset() {
|
||||
return (document.querySelector('.navbar') as HTMLElement).offsetHeight
|
||||
}
|
||||
function getAnchorTop(anchor: HTMLAnchorElement) {
|
||||
const pageOffset = getPageOffset()
|
||||
try {
|
||||
return anchor.parentElement.offsetTop - pageOffset - 15
|
||||
} catch (e) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
function isAnchorActive(
|
||||
index: number,
|
||||
anchor: HTMLAnchorElement,
|
||||
nextAnchor: HTMLAnchorElement
|
||||
) {
|
||||
const scrollTop = window.scrollY
|
||||
if (index === 0 && scrollTop === 0) {
|
||||
return [true, null]
|
||||
}
|
||||
if (scrollTop < getAnchorTop(anchor)) {
|
||||
return [false, null]
|
||||
}
|
||||
if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) {
|
||||
return [true, decodeURIComponent(anchor.hash)]
|
||||
}
|
||||
return [false, null]
|
||||
}
|
61
docs/.vitepress/vitepress/composables/back-top.ts
Normal file
61
docs/.vitepress/vitepress/composables/back-top.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { throttleAndDebounce } from '../utils'
|
||||
|
||||
const threshold = 960
|
||||
|
||||
const cubic = (value: number): number => Math.pow(value, 3)
|
||||
const easeInOutCubic = (value: number): number =>
|
||||
value < 0.5 ? cubic(value * 2) / 2 : 1 - cubic((1 - value) * 2) / 2
|
||||
|
||||
export const useBackTop = (offset = 200) => {
|
||||
const shouldShow = ref(false)
|
||||
const throttleResize = throttleAndDebounce(onResize, 300)
|
||||
const throttleScroll = throttleAndDebounce(onScroll, 160)
|
||||
|
||||
onMounted(() => {
|
||||
onResize()
|
||||
onScroll()
|
||||
window.addEventListener('resize', throttleResize)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', throttleResize)
|
||||
window.removeEventListener('scroll', throttleScroll)
|
||||
})
|
||||
|
||||
const scrollToTop = () => {
|
||||
const beginTime = Date.now()
|
||||
const beginValue = document.documentElement.scrollTop
|
||||
const rAF = window.requestAnimationFrame
|
||||
const frameFunc = () => {
|
||||
const progress = (Date.now() - beginTime) / 500
|
||||
if (progress < 1) {
|
||||
document.documentElement.scrollTop =
|
||||
beginValue * (1 - easeInOutCubic(progress))
|
||||
rAF(frameFunc)
|
||||
} else {
|
||||
document.documentElement.scrollTop = 0
|
||||
}
|
||||
}
|
||||
rAF(frameFunc)
|
||||
}
|
||||
|
||||
function onResize() {
|
||||
const { clientWidth } = document.body
|
||||
|
||||
if (clientWidth < threshold) {
|
||||
window.addEventListener('scroll', throttleScroll)
|
||||
} else {
|
||||
window.removeEventListener('scroll', throttleScroll)
|
||||
}
|
||||
}
|
||||
|
||||
function onScroll() {
|
||||
shouldShow.value = document.documentElement.scrollTop > offset
|
||||
}
|
||||
|
||||
return {
|
||||
shouldShow,
|
||||
scrollToTop,
|
||||
}
|
||||
}
|
32
docs/.vitepress/vitepress/composables/edit-link.ts
Normal file
32
docs/.vitepress/vitepress/composables/edit-link.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import { createGitHubUrl } from '../utils'
|
||||
|
||||
export function useEditLink() {
|
||||
const { page, theme, frontmatter } = useData()
|
||||
const url = computed(() => {
|
||||
const {
|
||||
repo,
|
||||
docsDir = '',
|
||||
docsBranch = 'dev',
|
||||
docsRepo = repo,
|
||||
editLinks,
|
||||
} = theme.value
|
||||
const showEditLink =
|
||||
frontmatter.value.editLink != null
|
||||
? frontmatter.value.editLink
|
||||
: editLinks
|
||||
const { relativePath } = page.value
|
||||
if (!showEditLink || !relativePath || !repo) {
|
||||
return null
|
||||
}
|
||||
return createGitHubUrl(docsRepo, docsDir, docsBranch, relativePath)
|
||||
})
|
||||
const text = computed(() => {
|
||||
return theme.value.editLinkText || 'Edit this page'
|
||||
})
|
||||
return {
|
||||
url,
|
||||
text,
|
||||
}
|
||||
}
|
10
docs/.vitepress/vitepress/composables/feature-flag.ts
Normal file
10
docs/.vitepress/vitepress/composables/feature-flag.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
export const useFeatureFlag = (flag: string) => {
|
||||
const { theme } = useData()
|
||||
|
||||
return computed(() => {
|
||||
return (theme.value.features || {})[flag]
|
||||
})
|
||||
}
|
8
docs/.vitepress/vitepress/composables/fullscreen.ts
Normal file
8
docs/.vitepress/vitepress/composables/fullscreen.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { useToggle } from './toggle'
|
||||
export const useFullScreen = () => {
|
||||
const [isFullScreen, toggleFullScreen] = useToggle()
|
||||
return {
|
||||
isFullScreen,
|
||||
toggleFullScreen,
|
||||
}
|
||||
}
|
19
docs/.vitepress/vitepress/composables/lang.ts
Normal file
19
docs/.vitepress/vitepress/composables/lang.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vitepress'
|
||||
import { defaultLang } from '../constant'
|
||||
|
||||
export const useLang = () => {
|
||||
const route = useRoute()
|
||||
return computed(() => {
|
||||
// the first part of the first slash
|
||||
const path = route.data?.relativePath
|
||||
let lang: string
|
||||
|
||||
if (path?.includes('/')) {
|
||||
lang = path.split('/').shift()
|
||||
} else {
|
||||
lang = defaultLang
|
||||
}
|
||||
return lang
|
||||
})
|
||||
}
|
58
docs/.vitepress/vitepress/composables/lock-screen.ts
Normal file
58
docs/.vitepress/vitepress/composables/lock-screen.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { watch, onUnmounted } from 'vue'
|
||||
import {
|
||||
addClass,
|
||||
hasClass,
|
||||
getStyle,
|
||||
removeClass,
|
||||
} from 'element-plus/lib/utils/dom'
|
||||
import getScrollBarWidth from 'element-plus/lib/utils/scrollbar-width'
|
||||
import { isServer } from '../utils'
|
||||
|
||||
export const useLockScreen = () => {
|
||||
let scrollBarWidth = 0
|
||||
let withoutHiddenClass = false
|
||||
let bodyPaddingRight = '0'
|
||||
let computedBodyPaddingRight = 0
|
||||
|
||||
onUnmounted(() => {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
const cleanup = () => {
|
||||
if (isServer) return
|
||||
removeClass(document.body, 'el-popup-parent--hidden')
|
||||
if (withoutHiddenClass) {
|
||||
document.body.style.paddingRight = bodyPaddingRight
|
||||
}
|
||||
}
|
||||
|
||||
const lock = () => {
|
||||
if (isServer) return
|
||||
withoutHiddenClass = !hasClass(document.body, 'el-popup-parent--hidden')
|
||||
if (withoutHiddenClass) {
|
||||
bodyPaddingRight = document.body.style.paddingRight
|
||||
computedBodyPaddingRight = parseInt(
|
||||
getStyle(document.body, 'paddingRight'),
|
||||
10
|
||||
)
|
||||
}
|
||||
scrollBarWidth = getScrollBarWidth()
|
||||
const bodyHasOverflow =
|
||||
document.documentElement.clientHeight < document.body.scrollHeight
|
||||
const bodyOverflowY = getStyle(document.body, 'overflowY')
|
||||
if (
|
||||
scrollBarWidth > 0 &&
|
||||
(bodyHasOverflow || bodyOverflowY === 'scroll') &&
|
||||
withoutHiddenClass
|
||||
) {
|
||||
document.body.style.paddingRight =
|
||||
computedBodyPaddingRight + scrollBarWidth + 'px'
|
||||
}
|
||||
addClass(document.body, 'el-popup-parent--hidden')
|
||||
}
|
||||
|
||||
return {
|
||||
lock,
|
||||
cleanup,
|
||||
}
|
||||
}
|
13
docs/.vitepress/vitepress/composables/nav.ts
Normal file
13
docs/.vitepress/vitepress/composables/nav.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
|
||||
import { useLang } from './lang'
|
||||
|
||||
export const useNav = () => {
|
||||
const { theme } = useData()
|
||||
const lang = useLang()
|
||||
|
||||
return computed(() => {
|
||||
return theme.value.nav[lang.value]
|
||||
})
|
||||
}
|
53
docs/.vitepress/vitepress/composables/page-nav.ts
Normal file
53
docs/.vitepress/vitepress/composables/page-nav.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { computed } from 'vue'
|
||||
import { useData } from 'vitepress'
|
||||
import {
|
||||
isArray,
|
||||
ensureStartingSlash,
|
||||
removeExtention as removeExtension,
|
||||
} from '../utils'
|
||||
import { useLang } from './lang'
|
||||
import { getSidebarConfig, getFlatSideBarLinks } from './sidebar'
|
||||
|
||||
export function usePageNav() {
|
||||
const { page, theme } = useData()
|
||||
const lang = useLang()
|
||||
|
||||
const path = computed(() => {
|
||||
return removeExtension(ensureStartingSlash(page.value.relativePath))
|
||||
})
|
||||
|
||||
const candidates = computed(() => {
|
||||
const config = getSidebarConfig(
|
||||
theme.value.sidebars,
|
||||
path.value,
|
||||
lang.value
|
||||
)
|
||||
return isArray(config) ? getFlatSideBarLinks(config) : []
|
||||
})
|
||||
|
||||
const index = computed(() => {
|
||||
return candidates.value.findIndex((item) => {
|
||||
return item.link === path.value
|
||||
})
|
||||
})
|
||||
const next = computed(() => {
|
||||
if (
|
||||
theme.value.nextLinks !== false &&
|
||||
index.value > -1 &&
|
||||
index.value < candidates.value.length - 1
|
||||
) {
|
||||
return candidates.value[index.value + 1]
|
||||
}
|
||||
})
|
||||
const prev = computed(() => {
|
||||
if (theme.value.prevLinks !== false && index.value > 0) {
|
||||
return candidates.value[index.value - 1]
|
||||
}
|
||||
})
|
||||
const hasLinks = computed(() => !!next.value || !!prev.value)
|
||||
return {
|
||||
next,
|
||||
prev,
|
||||
hasLinks,
|
||||
}
|
||||
}
|
86
docs/.vitepress/vitepress/composables/sidebar.ts
Normal file
86
docs/.vitepress/vitepress/composables/sidebar.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { computed } from 'vue'
|
||||
import { useRoute, useData } from 'vitepress'
|
||||
import { isArray, ensureStartingSlash, removeExtention } from '../utils'
|
||||
import { useLang } from './lang'
|
||||
|
||||
export const useSidebar = () => {
|
||||
const route = useRoute()
|
||||
const { site, page } = useData()
|
||||
const lang = useLang()
|
||||
if (!page.value) {
|
||||
return {
|
||||
sidebars: computed(() => []),
|
||||
hasSidebar: computed(() => false),
|
||||
}
|
||||
}
|
||||
const sidebars = computed(() => {
|
||||
if (page.value.frontmatter.sidebar === false) return []
|
||||
const sidebars = getSidebarConfig(
|
||||
site.value.themeConfig.sidebars,
|
||||
route.data.relativePath,
|
||||
lang.value
|
||||
)
|
||||
return sidebars
|
||||
})
|
||||
|
||||
return {
|
||||
sidebars,
|
||||
hasSidebar: computed(() => sidebars.value.length > 0),
|
||||
}
|
||||
}
|
||||
|
||||
export function isSideBarConfig(sidebar) {
|
||||
return sidebar === false || sidebar === 'auto' || isArray(sidebar)
|
||||
}
|
||||
export function isSideBarGroup(item) {
|
||||
return item.children !== undefined
|
||||
}
|
||||
export function isSideBarEmpty(sidebar) {
|
||||
return isArray(sidebar) ? sidebar.length === 0 : !sidebar
|
||||
}
|
||||
|
||||
type SidebarItem = {
|
||||
text: string
|
||||
link: string
|
||||
}
|
||||
|
||||
type SidebarConfig = SidebarItem[]
|
||||
|
||||
type Sidebar =
|
||||
| {
|
||||
[key: string]: SidebarConfig
|
||||
}
|
||||
| false
|
||||
| 'auto'
|
||||
|
||||
export function getSidebarConfig(sidebar: Sidebar, path: string, lang: string) {
|
||||
if (sidebar === false || Array.isArray(sidebar) || sidebar === 'auto') {
|
||||
return []
|
||||
}
|
||||
|
||||
path = ensureStartingSlash(path)
|
||||
for (const dir in sidebar) {
|
||||
// make sure the multi sidebar key starts with slash too
|
||||
if (path.startsWith(ensureStartingSlash(`${lang}${dir}`))) {
|
||||
return sidebar[dir][lang]
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
/**
|
||||
* Get flat sidebar links from the sidebar items. This method is useful for
|
||||
* creating the "next and prev link" feature. It will ignore any items that
|
||||
* don't have `link` property and removes `.md` or `.html` extension if a
|
||||
* link contains it.
|
||||
*/
|
||||
export function getFlatSideBarLinks(sidebar) {
|
||||
return sidebar.reduce((links, item) => {
|
||||
if (item.link) {
|
||||
links.push({ text: item.text, link: removeExtention(item.link) })
|
||||
}
|
||||
if (isSideBarGroup(item)) {
|
||||
links = [...links, ...getFlatSideBarLinks(item.children)]
|
||||
}
|
||||
return links
|
||||
}, [])
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user