vue3-core/packages-private/sfc-playground/src/App.vue

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

262 lines
6.1 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import Header from './Header.vue'
2024-01-21 16:51:28 +08:00
import {
Repl,
type SFCOptions,
useStore,
useVueImportMap,
mergeImportMap,
File,
StoreState,
ImportMap,
2024-01-21 16:51:28 +08:00
} from '@vue/repl'
import Monaco from '@vue/repl/monaco-editor'
import { ref, watchEffect, onMounted, computed, watch } from 'vue'
import welcomeSFC from './welcome.vue?raw'
const replRef = ref<InstanceType<typeof Repl>>()
2021-09-17 21:48:17 +08:00
const setVH = () => {
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`)
}
window.addEventListener('resize', setVH)
setVH()
const useSSRMode = ref(false)
const useVaporMode = ref(true)
2024-01-21 16:51:28 +08:00
const AUTO_SAVE_STORAGE_KEY = 'vue-sfc-playground-auto-save'
const initAutoSave: boolean = JSON.parse(
localStorage.getItem(AUTO_SAVE_STORAGE_KEY) ?? 'true',
)
const autoSave = ref(initAutoSave)
2024-01-21 16:51:28 +08:00
const {
vueVersion,
productionMode,
importMap: vueImportMap,
} = useVueImportMap({
runtimeDev: import.meta.env.PROD
? `${location.origin}/vue.runtime.esm-browser.js`
: `${location.origin}/src/vue-dev-proxy`,
runtimeProd: import.meta.env.PROD
? `${location.origin}/vue.runtime.esm-browser.prod.js`
: `${location.origin}/src/vue-dev-proxy-prod`,
serverRenderer: import.meta.env.PROD
? `${location.origin}/server-renderer.esm-browser.js`
: `${location.origin}/src/vue-server-renderer-dev-proxy`,
})
const importMap = computed(() => {
const vapor = import.meta.env.PROD
? `${location.origin}/vue-vapor.esm-browser.js`
: `${location.origin}/src/vue-vapor-dev-proxy`
const vaporImportMap: ImportMap = {
2024-01-21 16:51:28 +08:00
imports: {
'vue/vapor': vapor,
2024-01-21 16:51:28 +08:00
},
}
return mergeImportMap(vueImportMap.value, vaporImportMap)
})
let hash = location.hash.slice(1)
if (hash.startsWith('__DEV__')) {
hash = hash.slice(7)
2024-01-21 16:51:28 +08:00
productionMode.value = false
}
if (hash.startsWith('__PROD__')) {
hash = hash.slice(8)
2024-01-21 16:51:28 +08:00
productionMode.value = true
}
if (hash.startsWith('__SSR__')) {
hash = hash.slice(7)
useSSRMode.value = true
}
2024-01-21 16:51:28 +08:00
if (hash.startsWith('__VAPOR__')) {
hash = hash.slice(9)
useVaporMode.value = true
}
2024-01-21 16:51:28 +08:00
const files: StoreState['files'] = ref(Object.create(null))
// enable experimental features
2024-01-21 16:51:28 +08:00
const sfcOptions = computed(
(): SFCOptions => ({
script: {
inlineTemplate: productionMode.value,
isProd: productionMode.value,
propsDestructure: true,
vapor: useVaporMode.value,
2024-01-21 16:51:28 +08:00
},
style: {
isProd: productionMode.value,
},
2024-01-21 16:51:28 +08:00
template: {
vapor: useVaporMode.value,
isProd: productionMode.value,
compilerOptions: {
isCustomElement: (tag: string) =>
tag === 'mjx-container' || tag.startsWith('custom-'),
2024-01-21 16:51:28 +08:00
},
},
}),
)
const store = useStore(
{
files,
vueVersion,
builtinImportMap: importMap,
sfcOptions,
template: ref({
welcomeSFC: welcomeSFC,
}),
},
2024-01-21 16:51:28 +08:00
hash,
)
// @ts-expect-error
globalThis.store = store
watch(
useVaporMode,
() => {
if (useVaporMode.value) {
files.value['src/index.html'] = new File(
'src/index.html',
`<script type="module">
import { createVaporApp } from 'vue/vapor'
2024-01-21 16:51:28 +08:00
import App from './App.vue'
createVaporApp(App).mount('#app')` +
2024-01-21 16:51:28 +08:00
'<' +
'/script>' +
`<div id="app"></div>`,
true,
)
store.mainFile = 'src/index.html'
2024-01-21 16:51:28 +08:00
} else if (files.value['src/index.html']?.hidden) {
delete files.value['src/index.html']
store.mainFile = 'src/App.vue'
if (store.activeFile.filename === 'src/index.html') {
store.activeFile = files.value['src/App.vue']
}
2024-01-21 16:51:28 +08:00
}
},
{ immediate: true },
)
// persist state
watchEffect(() => {
const newHash = store
.serialize()
2024-01-21 16:51:28 +08:00
.replace(/^#/, useVaporMode.value ? `#__VAPOR__` : `#`)
.replace(/^#/, useSSRMode.value ? `#__SSR__` : `#`)
2024-01-21 16:51:28 +08:00
.replace(/^#/, productionMode.value ? `#__PROD__` : `#`)
history.replaceState({}, '', newHash)
})
function toggleProdMode() {
2024-01-21 16:51:28 +08:00
productionMode.value = !productionMode.value
}
function toggleSSR() {
useSSRMode.value = !useSSRMode.value
2024-01-21 16:51:28 +08:00
}
function toggleVapor() {
useVaporMode.value = !useVaporMode.value
}
function toggleAutoSave() {
autoSave.value = !autoSave.value
localStorage.setItem(AUTO_SAVE_STORAGE_KEY, String(autoSave.value))
}
function reloadPage() {
replRef.value?.reload()
}
const theme = ref<'dark' | 'light'>('dark')
function toggleTheme(isDark: boolean) {
theme.value = isDark ? 'dark' : 'light'
}
onMounted(() => {
const cls = document.documentElement.classList
toggleTheme(cls.contains('dark'))
// @ts-expect-error process shim for old versions of @vue/compiler-sfc dependency
window.process = { env: {} }
})
</script>
2021-03-28 13:35:45 +08:00
<template>
<Header
:store="store"
2024-01-21 16:51:28 +08:00
:prod="productionMode"
:ssr="useSSRMode"
2024-01-21 16:51:28 +08:00
:vapor="useVaporMode"
:autoSave="autoSave"
:theme="theme"
@toggle-theme="toggleTheme"
@toggle-prod="toggleProdMode"
@toggle-ssr="toggleSSR"
@toggle-autosave="toggleAutoSave"
2024-01-21 16:51:28 +08:00
@toggle-vapor="toggleVapor"
@reload-page="reloadPage"
/>
<Repl
ref="replRef"
:theme="theme"
:editor="Monaco"
@keydown.ctrl.s.prevent
@keydown.meta.s.prevent
:ssr="useSSRMode"
:model-value="autoSave"
:editorOptions="{ autoSaveText: false }"
:store="store"
:showCompileOutput="true"
:autoResize="true"
:clearConsole="false"
:preview-options="{
customCode: {
importCode: `import { initCustomFormatter } from 'vue'`,
useCode: `if (window.devtoolsFormatters) {
const index = window.devtoolsFormatters.findIndex((v) => v.__vue_custom_formatter)
window.devtoolsFormatters.splice(index, 1)
initCustomFormatter()
} else {
initCustomFormatter()
}`,
},
}"
/>
2021-03-28 13:35:45 +08:00
</template>
<style>
.dark {
color-scheme: dark;
}
2021-03-28 13:35:45 +08:00
body {
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
2021-03-28 13:35:45 +08:00
margin: 0;
--base: #444;
2021-03-28 13:35:45 +08:00
--nav-height: 50px;
}
.vue-repl {
height: calc(var(--vh) - var(--nav-height)) !important;
2021-03-28 13:35:45 +08:00
}
button {
border: none;
outline: none;
cursor: pointer;
margin: 0;
background-color: transparent;
}
</style>