Skip to content

Commit cbd1191

Browse files
committed
feat(vue-playground): supports change layout, fullscreen, show or hide code
1 parent 78a4475 commit cbd1191

File tree

10 files changed

+397
-26
lines changed

10 files changed

+397
-26
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ module.exports = /** @type { import('eslint').Linter.Config } */ ({
103103
'vue/no-unused-vars': 'warn',
104104
'vue/multi-word-component-names': 'off',
105105
'vue/one-component-per-file': 'off',
106+
'vue/no-v-model-argument': 'off',
106107
'vue/comment-directive': [
107108
'warn',
108109
{
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<script lang="ts">
2+
export default defineComponent({
3+
name: 'ControlButton'
4+
})
5+
</script>
6+
<script setup lang="ts">
7+
import {defineComponent} from 'vue'
8+
9+
defineProps({
10+
title: {
11+
type: String
12+
},
13+
active: {
14+
type: Boolean,
15+
default: false
16+
}
17+
})
18+
</script>
19+
<template>
20+
<div
21+
role="button"
22+
:title="title"
23+
class="vue-playground-control-btn"
24+
:class="{'vue-playground-toolbar-direction-active': active}"
25+
>
26+
<slot></slot>
27+
</div>
28+
</template>
29+
<style scoped>
30+
.vue-playground-control-btn {
31+
display: flex;
32+
align-items: center;
33+
justify-content: center;
34+
width: 24px;
35+
height: 24px;
36+
font-size: 12px;
37+
color: #656666;
38+
cursor: pointer;
39+
background-color: #fff;
40+
border: 1px solid #c5c7c8;
41+
border-radius: 4px;
42+
box-shadow: 0 4px 6px -1px rgb(0, 0, 0, 0.1), 0 2px 4px -2px rgb(0, 0, 0, 0.1);
43+
}
44+
45+
.vue-playground-control-btn.vue-playground-toolbar-direction-active {
46+
color: #584984;
47+
border: 1px solid #584984;
48+
}
49+
</style>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import {defineComponent, VNode} from 'vue'
3+
4+
const createComponent = (name: string, render: (props: Record<string, any>) => VNode) =>
5+
defineComponent({
6+
name,
7+
render() {
8+
const props = {...this.$props, ...this.$attrs}
9+
return render(props)
10+
}
11+
})
12+
13+
export const EditorLeftPreviewRightIcon = createComponent('EditorLeftPreviewRightIcon', props => (
14+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
15+
<path
16+
d="M12 5v14h7V5h-7zM4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z"
17+
fill="currentColor"
18+
></path>
19+
</svg>
20+
))
21+
22+
export const EditorTopPreviewBottomIcon = createComponent('EditorTopPreviewBottomIcon', props => (
23+
<EditorLeftPreviewRightIcon {...props} style={{transform: 'rotate(90deg)'}} />
24+
))
25+
26+
export const EditorRightPreviewLeftIcon = createComponent('EditorRightPreviewLeftIcon', props => (
27+
<EditorLeftPreviewRightIcon {...props} style={{transform: 'rotate(180deg)'}} />
28+
))
29+
30+
export const EditorBottomPreviewTopIcon = createComponent('EditorBottomPreviewTopIcon', props => (
31+
<EditorLeftPreviewRightIcon {...props} style={{transform: 'rotate(270deg)'}} />
32+
))
33+
34+
export const ErrorIcon = createComponent('ErrorIcon', props => (
35+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
36+
<path
37+
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"
38+
fill="currentColor"
39+
></path>
40+
</svg>
41+
))
42+
43+
export const FullscreenIcon = createComponent('FullscreenIcon', props => (
44+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
45+
<path d="M5 5h5v2H7v3H5V5m9 0h5v5h-2V7h-3V5m3 9h2v5h-5v-2h3v-3m-7 3v2H5v-5h2v3h3z" fill="currentColor"></path>
46+
</svg>
47+
))
48+
49+
export const ExitFullscreenIcon = createComponent('ExitFullscreenIcon', props => (
50+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
51+
<path d="M14 14h5v2h-3v3h-2v-5m-9 0h5v5H8v-3H5v-2m3-9h2v5H5V8h3V5m11 3v2h-5V5h2v3h3z" fill="currentColor"></path>
52+
</svg>
53+
))
54+
55+
export const ShowCodeIcon = createComponent('ShowCodeIcon', props => (
56+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
57+
<path
58+
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"
59+
fill="currentColor"
60+
></path>
61+
</svg>
62+
))
63+
64+
export const HideCodeIcon = createComponent('HideCodeIcon', props => (
65+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
66+
<path
67+
d="M19.17 12l-3.88-3.88a.996.996 0 1 1 1.41-1.41l4.59 4.59c.39.39.39 1.02 0 1.41l-2.88 2.88L17 14.17L19.17 12zM2.1 4.93l3.49 3.49l-2.88 2.88a.996.996 0 0 0 0 1.41L7.3 17.3a.996.996 0 1 0 1.41-1.41L4.83 12L7 9.83L19.07 21.9a.996.996 0 1 0 1.41-1.41L3.51 3.51a.996.996 0 0 0-1.41 0c-.39.4-.39 1.03 0 1.42z"
68+
fill="currentColor"
69+
></path>
70+
</svg>
71+
))
72+
73+
export const DisconnectedIcon = createComponent('DisconnectedIcon', props => (
74+
<svg width="1em" height="1em" viewBox="0 0 24 24" {...props}>
75+
<path
76+
fill="currentColor"
77+
d="M21.707 3.707a1 1 0 0 0-1.414-1.414L18.496 4.09a4.252 4.252 0 0 0-5.251.604l-1.068 1.069a1.75 1.75 0 0 0 0 2.474l3.585 3.586a1.75 1.75 0 0 0 2.475 0l1.068-1.068a4.252 4.252 0 0 0 .605-5.25l1.797-1.798Zm-11 8a1 1 0 0 0-1.414-1.414l-1.47 1.47l-.293-.293a.75.75 0 0 0-1.06 0l-1.775 1.775a4.252 4.252 0 0 0-.605 5.25l-1.797 1.798a1 1 0 1 0 1.414 1.414l1.798-1.797a4.252 4.252 0 0 0 5.25-.605l1.775-1.775a.75.75 0 0 0 0-1.06l-.293-.293l1.47-1.47a1 1 0 0 0-1.414-1.414l-1.47 1.47l-1.586-1.586l1.47-1.47Z"
78+
/>
79+
</svg>
80+
))
81+
82+
export const ConnectedIcon = createComponent('ConnectedIcon', props => (
83+
<svg width="1em" height="1em" viewBox="0 0 20 20" {...props}>
84+
<path
85+
fill="currentColor"
86+
d="M17.78 3.28a.75.75 0 0 0-1.06-1.06l-2.446 2.445a4.037 4.037 0 0 0-5.128.481l-.3.3a1.49 1.49 0 0 0 0 2.108l3.6 3.6a1.49 1.49 0 0 0 2.107 0l.3-.3a4.037 4.037 0 0 0 .482-5.128L17.78 3.28ZM7.554 8.846a1.49 1.49 0 0 0-2.107 0l-.3.3a4.037 4.037 0 0 0-.481 5.128L2.22 16.72a.75.75 0 1 0 1.06 1.06l2.446-2.446a4.037 4.037 0 0 0 5.128-.48l.3-.3a1.49 1.49 0 0 0 0-2.108l-3.6-3.6Z"
87+
/>
88+
</svg>
89+
))

packages/vue-playground/src/playground/components/message.vue

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,19 @@ function formatMessage(err: string | Error): string {
3737

3838
<template>
3939
<Transition name="fade">
40-
<div v-if="!dismissed && (err || warn)" class="msg" :class="err ? 'err' : 'warn'">
40+
<div
41+
v-if="!dismissed && (err || warn)"
42+
class="vue-playground-msg"
43+
:class="err ? 'vue-playground-msg-err' : 'vue-playground-msg-warn'"
44+
>
4145
<pre>{{ formatMessage(err || warn) }}</pre>
42-
<button class="dismiss" @click="dismissed = true">✕</button>
46+
<button class="vue-playground-dismiss" @click="dismissed = true">✕</button>
4347
</div>
4448
</Transition>
4549
</template>
4650

4751
<style scoped>
48-
.msg {
52+
.vue-playground-msg {
4953
position: absolute;
5054
right: 8px;
5155
bottom: 0;
@@ -61,12 +65,14 @@ function formatMessage(err: string | Error): string {
6165
border: 2px solid transparent;
6266
border-radius: 6px;
6367
}
64-
pre {
68+
69+
.vue-playground-msg pre {
6570
padding: 12px 20px;
6671
margin: 0;
6772
overflow: scroll;
6873
}
69-
.dismiss {
74+
75+
.vue-playground-dismiss {
7076
position: absolute;
7177
top: 2px;
7278
right: 2px;
@@ -81,27 +87,31 @@ pre {
8187
background-color: red;
8288
border-radius: 9px;
8389
}
90+
8491
@media (max-width: 720px) {
85-
.dismiss {
92+
.vue-playground-dismiss {
8693
top: -9px;
8794
right: -9px;
8895
}
89-
.msg {
96+
.vue-playground-msg {
9097
bottom: 50px;
9198
}
9299
}
93-
.msg.err {
100+
101+
.vue-playground-msg-err {
94102
color: red;
95103
background-color: #ffd7d7;
96104
border-color: red;
97105
}
98-
.msg.warn {
106+
107+
.vue-playground-msg-warn {
99108
--color: rgb(105, 95, 27);
100109
color: var(--color);
101110
background-color: rgb(247, 240, 205);
102111
border-color: var(--color);
103112
}
104-
.msg.warn .dismiss {
113+
114+
.vue-playground-msg-warn .vue-playground-dismiss {
105115
background-color: var(--color);
106116
}
107117
.fade-enter-active,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import {defineComponent, h, Teleport} from 'vue'
3+
4+
const vm = defineComponent({
5+
name: 'Portal',
6+
props: {
7+
to: {
8+
type: String
9+
}
10+
},
11+
render() {
12+
const {to} = this
13+
if (!to) return this.$slots.default?.()
14+
return h(
15+
Teleport as any,
16+
{
17+
to
18+
},
19+
this.$slots.default?.()
20+
)
21+
}
22+
})
23+
24+
export default vm

packages/vue-playground/src/playground/components/preview.vue

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
computed,
1818
unref
1919
} from 'vue'
20-
import {Preview, PreviewProxy} from '../../core/'
20+
import {Preview} from '../../core/'
2121
import {CLEAR_CONSOLE_INJECT_KEY, STORE_INJECT_KEY} from '../constants'
2222
import {PreviewExpose} from '../utils/types-helper'
2323
@@ -28,8 +28,6 @@ const container = ref<HTMLElement>()
2828
const runtimeError = ref()
2929
const runtimeWarning = ref()
3030
31-
let proxy: PreviewProxy
32-
3331
let stopUpdateWatcher: WatchStopHandle | undefined
3432
3533
const preview = new Preview({
@@ -85,7 +83,7 @@ watch(
8583
)
8684
8785
onUnmounted(() => {
88-
proxy.destroy()
86+
preview.destroy()
8987
})
9088
9189
defineExpose({
@@ -95,16 +93,22 @@ defineExpose({
9593
</script>
9694

9795
<template>
98-
<div class="preview">
99-
<div ref="container" class="iframe-container"></div>
96+
<div class="vue-playground-preview">
97+
<div ref="container" class="vue-playground-preview-iframe-container"></div>
10098
<Message :err="runtimeError" />
10199
<Message v-if="!runtimeError" :warn="runtimeWarning" />
102100
</div>
103101
</template>
104102

105103
<style scoped>
106-
.iframe-container,
107-
.iframe-container :deep(iframe) {
104+
.vue-playground-preview {
105+
box-sizing: border-box;
106+
flex: 1;
107+
width: 100%;
108+
overflow: hidden;
109+
}
110+
.vue-playground-preview-iframe-container,
111+
.vue-playground-preview-iframe-container :deep(iframe) {
108112
width: 100%;
109113
height: 100%;
110114
background-color: #fff;

0 commit comments

Comments
 (0)