Skip to content

Commit 7456e49

Browse files
committed
feat(playground): playground
1 parent 668c4fa commit 7456e49

File tree

16 files changed

+468
-93
lines changed

16 files changed

+468
-93
lines changed

playground/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@ coverage
2727
*.sln
2828
*.sw?
2929

30-
auto-components.d.ts
30+
components.d.ts
3131
auto-imports.d.ts

playground/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"scripts": {
66
"dev": "vite",
7-
"build": "run-p type-check build-only",
7+
"build": "run-p build-only",
88
"preview": "vite preview",
99
"build-only": "vite build",
1010
"type-check": "vue-tsc --noEmit",
@@ -16,6 +16,9 @@
1616
"@unocss/reset": "^0.50.3",
1717
"@varlet/touch-emulator": "^2.8.5",
1818
"@varlet/ui": "^2.8.5",
19+
"@vueuse/core": "^9.13.0",
20+
"monaco-editor": "^0.36.1",
21+
"theme-vitesse": "^0.6.3",
1922
"vue": "^3.2.47"
2023
},
2124
"devDependencies": {

playground/src/App.vue

Lines changed: 14 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,23 @@
11
<script setup lang="ts">
2-
import { transformScript, FileType } from '../../src/index'
3-
import initSwc, { parseSync } from '@swc/wasm-web'
2+
import * as monaco from 'monaco-editor';
3+
import { useDark } from '@vueuse/core';
44
5-
const count = ref(0)
6-
7-
const text = ref('')
8-
9-
async function init() {
10-
await initSwc()
11-
12-
const code = transformScript({
13-
fileType: FileType.ts,
14-
script: `
15-
import { defineComponent, PropType, ref } from "vue"
16-
import Header from "../components/Header.vue"
17-
import Tab from "../components/Tab.vue"
18-
import touchdir from "vtouchdir"
19-
export default defineComponent({
20-
name: 'App',
21-
components: {
22-
Header,
23-
Tab,
24-
},
25-
directives: {
26-
force: {},
27-
touchdir,
28-
},
29-
props: {
30-
items: Array as PropType<number[]>
31-
},
32-
emit: ["click"],
33-
setup(props, { emit, attrs, slots: mySlots, expose }) {
34-
const bar = ref(0)
35-
expose({ bar })
36-
emit("change");
37-
return {
38-
bar
39-
}
40-
}
41-
})`.trim(),
42-
offset: 0,
43-
parseSync: parseSync as any,
44-
output: {
45-
warn() {
46-
47-
},
48-
log() {
49-
50-
},
51-
success() {
52-
53-
},
54-
error() {
55-
}
56-
}
57-
})
58-
59-
if (code) {
60-
text.value = code
61-
}
62-
}
63-
64-
init()
5+
const isDark = useDark()
6+
watchEffect(() => {
7+
monaco.editor.setTheme(isDark.value ? 'vitesse-dark' : 'vitesse-light');
8+
})
659
</script>
6610

6711
<template>
68-
<div>
69-
<div>
70-
{{ count }}
71-
<var-button @click="count++" type="primary">click</var-button>
12+
<div class="flex flex-col h-full">
13+
<Header class="flex-shrink-0" />
14+
<div class="flex-1 box-border pt-3px flex">
15+
<Edit />
16+
17+
<div class="flex-shrink-0 w-2px bg-[rgba(0,0,0,0.2)] dark:bg-#ddd" />
18+
19+
<View />
7220
</div>
73-
<var-paper :elevation="2">
74-
<pre>
75-
{{ text }}
76-
</pre>
77-
</var-paper>
7821
</div>
7922
</template>
8023

playground/src/components/Edit.vue

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script setup lang="ts">
2+
const { code, codeType } = useUrlKeepParams()
3+
4+
const monaco = ref<{ setValue: (value: string) => void } | null>(null)
5+
6+
const editCode = computed({
7+
get() {
8+
return atou(code.value)
9+
},
10+
set(value) {
11+
code.value = utoa(value)
12+
}
13+
})
14+
15+
watch(() => codeType.value, () => {
16+
code.value = ''
17+
monaco.value?.setValue('')
18+
})
19+
20+
const language = computed(() => {
21+
return codeType.value === 'sfc' ? 'html' : 'javascript'
22+
})
23+
</script>
24+
25+
<template>
26+
<div class="h-full flex-1 flex flex-col">
27+
<var-paper class="flex-shrink-0 px-30px flex items-center" :elevation="1" :height="36">
28+
<var-radio-group v-model="codeType" class="text-14px">
29+
<var-radio :checked-value="CodeType.SFC" icon-size="18px">
30+
<span :class="TEXT_COLOR">SFC</span>
31+
</var-radio>
32+
<var-radio :checked-value="CodeType.SCRIPT" icon-size="18px">
33+
<span :class="TEXT_COLOR">Script</span>
34+
</var-radio>
35+
</var-radio-group>
36+
</var-paper>
37+
38+
<div class="flex-1 box-border pt-2px">
39+
<Monaco v-model="editCode" :language="language" ref="monaco" />
40+
</div>
41+
</div>
42+
</template>

playground/src/components/Header.vue

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<script setup lang="tsx">
2+
import { StyleProvider, Themes } from '@varlet/ui';
3+
import { useDark, useToggle } from '@vueuse/core'
4+
import Share from './Share.vue'
5+
6+
const isDark = useDark()
7+
const toggleDark = useToggle(isDark)
8+
9+
watchEffect(() => {
10+
StyleProvider(isDark.value ? Themes.dark : null)
11+
})
12+
13+
async function shareLink() {
14+
await navigator.clipboard.writeText(location.href)
15+
Snackbar.success('Sharable URL has been copied to clipboard.')
16+
}
17+
18+
const handleIcon = computed(() => (
19+
<>
20+
<var-button round>
21+
<var-icon name={isDark.value ? 'white-balance-sunny' : 'weather-night'} size={ICON_SIZE}
22+
transition="300" onClick={() => toggleDark()} />
23+
</var-button>
24+
25+
<var-button type="info" round>
26+
<Share size={ICON_SIZE} onClick={() => shareLink()} title="share link" />
27+
</var-button>
28+
29+
<var-link target="_blank" href="https://github.com/a145789/vue3-script-to-setup" underline="none">
30+
<var-button type="success" round>
31+
<var-icon name="github" size={ICON_SIZE} title="github" />
32+
</var-button>
33+
</var-link>
34+
</>
35+
))
36+
37+
const iconClass = 'text-#555 dark:text-#fff'
38+
</script>
39+
40+
<template>
41+
<var-paper :elevation="2" :height="50" class="px-25px">
42+
<var-row align="center" justify="space-between" class="h-full">
43+
<var-col :span="12" :xs="20">
44+
<div class="font-500 text-20px" :class="TEXT_COLOR">Vue3 script to script-setup</div>
45+
</var-col>
46+
<var-col :span="12" :xs="2">
47+
<var-space justify="flex-end" class="w-full hidden sm:flex" :class="iconClass">
48+
<component :is="handleIcon" />
49+
</var-space>
50+
51+
<var-menu>
52+
<var-button round class="block sm:hidden">
53+
<var-icon name="menu" :size="ICON_SIZE" :class="iconClass" />
54+
</var-button>
55+
56+
<template #menu>
57+
<div class="flex flex-col px-12px py-8px h-120px justify-between" :class="iconClass">
58+
<component :is="handleIcon" />
59+
</div>
60+
</template>
61+
</var-menu>
62+
</var-col>
63+
</var-row>
64+
</var-paper>
65+
</template>

playground/src/components/Monaco.vue

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<template>
2+
<div ref="editorContainer" class="h-full w-full" />
3+
</template>
4+
5+
<script lang="ts" setup>
6+
import * as monaco from 'monaco-editor';
7+
8+
const props = defineProps<{ modelValue: string, language: 'typescript' | 'html', readOnly?: boolean }>()
9+
10+
const emit = defineEmits<{ (e: 'update:modelValue', code: string): void }>()
11+
12+
const editorContainer = ref<HTMLElement | null>(null);
13+
const editor = shallowRef<monaco.editor.IStandaloneCodeEditor | null>(null);
14+
15+
onMounted(() => {
16+
editor.value = monaco.editor.create(editorContainer.value!, {
17+
value: props.modelValue,
18+
language: props.language,
19+
readOnly: props.readOnly,
20+
});
21+
22+
editor.value.onDidChangeModelContent(() => {
23+
emit("update:modelValue", editor.value!.getValue());
24+
});
25+
});
26+
27+
function setValue(value: string) {
28+
editor.value?.setValue(value)
29+
}
30+
31+
onUnmounted(() => {
32+
editor.value?.dispose();
33+
});
34+
35+
defineExpose({ setValue })
36+
</script>

playground/src/components/Share.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<template>
2+
<svg :width="`${size}px`" :height="`${size}px`" viewBox="0 0 24 24">
3+
<g fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
4+
<circle cx="18" cy="5" r="3" />
5+
<circle cx="6" cy="12" r="3" />
6+
<circle cx="18" cy="19" r="3" />
7+
<path d="M8.59 13.51l6.83 3.98" />
8+
<path d="M15.41 6.51l-6.82 3.98" />
9+
</g>
10+
</svg>
11+
</template>
12+
13+
<script lang="ts" setup>
14+
defineProps<{ size: number }>()
15+
</script>

playground/src/components/View.vue

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script setup lang="ts">
2+
import { useClipboard } from '@vueuse/core';
3+
const { code: originCode, codeType, propsNotOnlyTs } = useUrlKeepParams()
4+
5+
const output = {
6+
warn(message: string) {
7+
Snackbar.warning(message)
8+
},
9+
error(message: string) {
10+
Snackbar.error(message)
11+
},
12+
log(message: string) {
13+
Snackbar.info(message)
14+
},
15+
success(message: string) {
16+
Snackbar.success(message)
17+
},
18+
}
19+
20+
const code = useTransform(codeType, computed(() => atou(originCode.value)), computed(() => Boolean(Number(propsNotOnlyTs.value))), output)
21+
22+
const language = computed(() => {
23+
return codeType.value === 'sfc' ? 'html' : 'javascript'
24+
})
25+
26+
const { copy } = useClipboard()
27+
28+
function copyCode() {
29+
copy(code.value)
30+
31+
output.success(`Copied code to clipboard!`)
32+
}
33+
34+
const monaco = ref<{ setValue: (value: string) => void } | null>(null)
35+
watchEffect(() => {
36+
monaco.value?.setValue(code.value)
37+
})
38+
</script>
39+
40+
<template>
41+
<div class="h-full flex-1 flex flex-col">
42+
<var-paper class="flex-shrink-0 px-30px flex items-center justify-end" :elevation="1" :height="36">
43+
<var-checkbox v-model="propsNotOnlyTs" checked-value="1" unchecked-value="0">
44+
<span :class="TEXT_COLOR">PropsNotOnlyTs</span>
45+
</var-checkbox>
46+
<var-button type="warning" round class="ml-6px">
47+
<var-icon name="content-copy" :size="16" title="copy code" @click="copyCode" />
48+
</var-button>
49+
</var-paper>
50+
51+
<div class="flex-1 box-border pt-2px">
52+
<Monaco v-model="code" :language="language" read-only ref="monaco" />
53+
</div>
54+
</div>
55+
</template>

playground/src/composables/encode.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function utoa(data: string): string {
2+
if (!data) {
3+
return "";
4+
}
5+
return btoa(unescape(encodeURIComponent(data)));
6+
}
7+
8+
export function atou(base64: string): string {
9+
if (!base64) {
10+
return "";
11+
}
12+
return decodeURIComponent(escape(atob(base64)));
13+
}

playground/src/composables/store.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { CodeType } from "@/constants";
2+
import { useUrlSearchParams } from "@vueuse/core";
3+
4+
export function createStore<T extends object>(cb: () => T) {
5+
const value = cb();
6+
7+
return () => toRefs(value);
8+
}
9+
10+
const initialValue: {
11+
code: string;
12+
codeType: CodeType;
13+
propsNotOnlyTs: "0" | "1";
14+
} = {
15+
code: "",
16+
codeType: CodeType.SFC,
17+
propsNotOnlyTs: "0",
18+
};
19+
export const useUrlKeepParams = createStore(() => {
20+
const params = useUrlSearchParams("hash-params", {
21+
initialValue,
22+
});
23+
24+
const keys = Object.keys(initialValue) as (keyof typeof initialValue)[];
25+
for (const key of keys) {
26+
if (!(key in params)) {
27+
(params as any)[key] = initialValue[key];
28+
}
29+
}
30+
31+
return params;
32+
});

0 commit comments

Comments
 (0)