Skip to content

Commit 28c544e

Browse files
committed
feat: wip preview, need to wait for features on vitepress
1 parent 0384cb0 commit 28c544e

File tree

13 files changed

+624
-0
lines changed

13 files changed

+624
-0
lines changed

docs/.vitepress/components/Bit.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<template>
2+
<p class="bit-sponsor">
3+
<a href="https://www.bitsrc.io/?utm_source=vue&utm_medium=vue&utm_campaign=vue&utm_term=vue&utm_content=vue" target="_blank">
4+
<span>This project is sponsored by</span>
5+
<img alt="bit" src="https://raw.githubusercontent.com/vuejs/vuejs.org/master/themes/vue/source/images/bit-wide.png">
6+
</a>
7+
</p>
8+
</template>
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
<template>
2+
<div class="demo" :class="containerClasses">
3+
<Promised :promise="examplePromise">
4+
<span></span>
5+
<span slot="catch" slot-scope="error"></span>
6+
<ExamplePreviewBar slot-scope="then" :router="router" :view-code.sync="viewCode" :files="files" :current-file.sync="currentFile" :codesandbox-params="codesandboxParams" />
7+
</Promised>
8+
<Promised :promise="examplePromise">
9+
<div class="example">Loading example...</div>
10+
<div slot-scope="then">
11+
<template v-if="viewCode">
12+
<!-- <ExamplePreviewFilesTabs :files="files" :current-file.sync="currentFile" :view-code.sync="viewCode" /> -->
13+
<ExamplePreviewExplorer v-if="currentFile" :file="currentFile" />
14+
</template>
15+
<template v-else>
16+
<div class="example">
17+
<component :is="page" />
18+
</div>
19+
</template>
20+
</div>
21+
<div class="example error" slot="catch" slot-scope="error">
22+
<p>There was an error loading the example</p>
23+
24+
<button class="action-button" @click="loadPage">Retry</button>
25+
</div>
26+
</Promised>
27+
</div>
28+
</template>
29+
30+
<script>
31+
import Router from 'vue-router'
32+
import Promised from 'vue-promised'
33+
import ExamplePreviewBar from '../example-preview/ExamplePreviewBar'
34+
import 'focus-visible'
35+
import ExamplePreviewExplorer from '../example-preview/ExamplePreviewExplorer'
36+
import { getCodesandboxParameters, removeScriptSection } from '../example-preview/utils'
37+
38+
export default {
39+
props: {
40+
name: String,
41+
initialView: {
42+
type: String,
43+
default: 'demo'
44+
}
45+
},
46+
47+
data() {
48+
return {
49+
viewCode: this.initialView === 'code',
50+
router: null,
51+
page: null,
52+
files: [],
53+
currentFile: null,
54+
codesandboxParams: null,
55+
56+
examplePromise: null
57+
}
58+
},
59+
60+
methods: {
61+
async loadPage () {
62+
if (!this.name) return
63+
this.examplePromise = (this.pagePath ? import(`@docs/${this.pagePath}/examples/${this.name}/index.js`) : import(`@docs/examples/${this.name}/index.js`))
64+
const Page = await this.examplePromise
65+
if (!Page || !Page.App) return
66+
let { App, files, codesandbox } = Page
67+
68+
// Do not create a new Router if the example has been loaded before
69+
// otherwise, visiting a new page and coming back will break the navigation bar
70+
if (App._exampleLoaded) {
71+
this.router = App.router
72+
} else {
73+
// create the router again but force the mode to abstract
74+
this.router = App.router = new Router({
75+
...App.router.options,
76+
mode: 'abstract'
77+
})
78+
App._exampleLoaded = true
79+
}
80+
81+
this.router.push('/')
82+
this.page = App
83+
// reset file
84+
this.currentFile = null
85+
this.codesandboxParams = null
86+
87+
this.files = []
88+
// files is usually an object, transform if it's not
89+
if (Array.isArray(files)) {
90+
files = files.reduce((filesDict, name) => ({...filesDict, [name]: name}), {})
91+
}
92+
93+
// load the content of all the files
94+
Object.keys(files).forEach(name => {
95+
const handleImport = ({ default: content }) => {
96+
// remove the script part that contains the router
97+
if (name === 'App.vue') {
98+
content = removeScriptSection(content)
99+
}
100+
this.files.push({ name, content })
101+
if (!this.currentFile) this.currentFile = this.files[this.files.length - 1]
102+
}
103+
if (this.pagePath) {
104+
import(`!raw-loader!@docs/${this.pagePath}/examples/${this.name}/${files[name]}`).then(handleImport)
105+
} else {
106+
import(`!raw-loader!@docs/examples/${this.name}/${files[name]}`).then(handleImport)
107+
}
108+
})
109+
110+
if (!codesandbox || !codesandbox.length) return
111+
const allFiles = codesandbox.map(filename => {
112+
const transformFile = ({ default: content }) => {
113+
// remove the script part from App.vue because it has the router
114+
if (filename === 'App.vue') {
115+
content = removeScriptSection(content)
116+
}
117+
return {
118+
[filename]: { content }
119+
}
120+
}
121+
if (this.pagePath) {
122+
return import(`!raw-loader!@docs/${this.pagePath}/examples/${this.name}/${filename}`).then(transformFile)
123+
} else {
124+
return import(`!raw-loader!@docs/examples/${this.name}/${filename}`).then(transformFile)
125+
}
126+
})
127+
128+
// add the entry point (common for all examples) main.js
129+
if (codesandbox.indexOf('main.js') < 0) {
130+
allFiles.push(import(`!raw-loader!@docs/examples/common/main.js`).then(({ default: content })=> ({
131+
'main.js': { content }
132+
})))
133+
}
134+
135+
this.codesandboxParams = await Promise.all(allFiles).then(getCodesandboxParameters)
136+
}
137+
},
138+
139+
computed: {
140+
containerClasses() {
141+
return { explorer: this.viewCode }
142+
},
143+
// this seems to be necessary to correctly code split
144+
// it allows import path to have the slashes in them
145+
pagePath () {
146+
return this.$localePath.replace(/^\//, '').replace(/\/$/, '')
147+
}
148+
},
149+
150+
watch: {
151+
name: {
152+
handler: 'loadPage',
153+
immediate: true
154+
}
155+
},
156+
157+
components: { ExamplePreviewBar, ExamplePreviewExplorer, Promised }
158+
}
159+
</script>
160+
161+
<style scoped lang="stylus">
162+
@import '~@default-theme/styles/config.styl';
163+
164+
.demo {
165+
border: 1px solid #ddd;
166+
border-radius: 4px;
167+
168+
& .example {
169+
padding: 1rem 1.5rem;
170+
overflow: hidden;
171+
172+
&.error {
173+
color: #ff2828;
174+
}
175+
}
176+
}
177+
178+
@media (max-width: $MQMobileNarrow) {
179+
.demo {
180+
margin-left: -1.5rem;
181+
margin-right: -1.5rem;
182+
border-radius: 0;
183+
}
184+
}
185+
186+
.action-button {
187+
display: inline-block;
188+
font-size: 1.2rem;
189+
color: #fff;
190+
background-color: #3eaf7c;
191+
padding: 0.8rem 1.6rem;
192+
border-radius: 4px;
193+
transition: background-color 0.1s ease;
194+
box-sizing: border-box;
195+
border-bottom: 1px solid #389d70;
196+
197+
&:hover {
198+
background-color: #4abf8a;
199+
cursor: pointer;
200+
}
201+
}
202+
</style>
203+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<template>
2+
<button class="button">
3+
<component :is="svgComponent" />
4+
</button>
5+
</template>
6+
7+
<script>
8+
import { defineComponent, shallowRef, watch } from 'vue'
9+
10+
export default defineComponent({
11+
props: {
12+
icon: String,
13+
},
14+
15+
setup(props) {
16+
const svgComponent = shallowRef()
17+
18+
watch(
19+
() => props.icon,
20+
async icon => {
21+
svgComponent.value = (
22+
await import(`../example-preview/icons/${icon}.vue`)
23+
).default
24+
},
25+
{ immediate: true }
26+
)
27+
28+
return { svgComponent }
29+
},
30+
})
31+
</script>
32+
33+
<style scoped>
34+
.button {
35+
/* reset button style */
36+
border: none;
37+
padding: 0;
38+
width: auto;
39+
overflow: visible;
40+
background: transparent;
41+
font: inherit;
42+
/* acutal styles */
43+
color: rgb(135, 135, 135);
44+
font-size: 1.5rem;
45+
line-height: 0.5;
46+
vertical-align: middle;
47+
text-align: center;
48+
margin: 0px 0.1rem;
49+
}
50+
51+
.button:not([disabled]):hover {
52+
background-color: rgb(226, 226, 226);
53+
cursor: pointer;
54+
}
55+
56+
.button[disabled] {
57+
color: rgb(192, 192, 192);
58+
}
59+
</style>

0 commit comments

Comments
 (0)