workflow(sfc-playground): share and download buttons

This commit is contained in:
Evan You
2021-03-28 23:36:36 -04:00
parent aa8bf1b7a3
commit 9613969ffc
15 changed files with 232 additions and 25 deletions

View File

@@ -27,5 +27,10 @@ declare module '*.vue' {
}
declare module '*?raw' {
const content: string
export default content
}
declare module 'file-saver' {
export function saveAs(blob: any, name: any): void
}

View File

@@ -19,5 +19,9 @@
"@vitejs/plugin-vue": "^1.2.0",
"codemirror": "^5.60.0",
"vite": "^2.1.3"
},
"dependencies": {
"file-saver": "^2.0.5",
"jszip": "^3.6.0"
}
}

View File

@@ -22,8 +22,8 @@ import Output from './output/Output.vue'
<style>
body {
font-size: 13px;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
color: #444;
margin: 0;
background-color: #f8f8f8;
@@ -36,4 +36,12 @@ body {
.wrapper {
height: calc(100vh - var(--nav-height));
}
</style>
button {
border: none;
outline: none;
cursor: pointer;
margin: 0;
background-color: transparent;
}
</style>

View File

@@ -1,9 +1,59 @@
<template>
<nav>
<h1>Vue SFC Playground</h1>
<button class="share" @click="copyLink">
<svg width="1.4em" height="1.4em" viewBox="0 0 24 24">
<g fill="none" stroke="#626262" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="18" cy="5" r="3"/>
<circle cx="6" cy="12" r="3"/>
<circle cx="18" cy="19" r="3"/>
<path d="M8.59 13.51l6.83 3.98"/>
<path d="M15.41 6.51l-6.82 3.98"/>
</g>
</svg>
</button>
<button class="download" @click="downloadProject">
<svg width="1.7em" height="1.7em" viewBox="0 0 24 24">
<g fill="#626262">
<rect x="4" y="18" width="16" height="2" rx="1" ry="1"/>
<rect x="3" y="17" width="4" height="2" rx="1" ry="1" transform="rotate(-90 5 18)"/>
<rect x="17" y="17" width="4" height="2" rx="1" ry="1" transform="rotate(-90 19 18)"/>
<path d="M12 15a1 1 0 0 1-.58-.18l-4-2.82a1 1 0 0 1-.24-1.39a1 1 0 0 1 1.4-.24L12 12.76l3.4-2.56a1 1 0 0 1 1.2 1.6l-4 3a1 1 0 0 1-.6.2z"/><path d="M12 13a1 1 0 0 1-1-1V4a1 1 0 0 1 2 0v8a1 1 0 0 1-1 1z"/>
</g>
</svg>
</button>
</nav>
</template>
<script setup lang="ts">
import { exportFiles } from './store'
import { saveAs } from 'file-saver'
function copyLink() {
navigator.clipboard.writeText(location.href)
alert('Sharable URL has been copied to clipboard.')
}
async function downloadProject() {
const { default: JSZip } = await import('jszip')
const zip = new JSZip()
// basic structure
// project src
const src = zip.folder('src')!
const files = exportFiles()
for (const file in files) {
src.file(file, files[file])
}
const blob = await zip.generateAsync({ type: 'blob' })
saveAs(blob, 'vue-project.zip')
}
</script>
<style>
nav {
height: var(--nav-height);
@@ -20,4 +70,16 @@ h1 {
line-height: var(--nav-height);
font-weight: 500;
}
.share {
position: absolute;
top: 14px;
right: 56px;
}
.download {
position: absolute;
top: 13px;
right: 16px;
}
</style>

View File

@@ -0,0 +1,31 @@
import { exportFiles } from '../store'
import { saveAs } from 'file-saver'
import index from './template/index.html?raw'
import main from './template/main.js?raw'
import pkg from './template/package.json?raw'
import config from './template/vite.config.js?raw'
import readme from './template/README.md?raw'
export async function downloadProject() {
const { default: JSZip } = await import('jszip')
const zip = new JSZip()
// basic structure
zip.file('index.html', index)
zip.file('package.json', pkg)
zip.file('vite.config.js', config)
zip.file('README.md', readme)
// project src
const src = zip.folder('src')!
src.file('main.js', main)
const files = exportFiles()
for (const file in files) {
src.file(file, files[file])
}
const blob = await zip.generateAsync({ type: 'blob' })
saveAs(blob, 'vue-project.zip')
}

View File

@@ -0,0 +1,14 @@
# Vite Vue Starter
This is a project template using [Vite](https://vitejs.dev/). It requires [Node.js](https://nodejs.org) v12+.
To start:
```sh
npm install
npm run dev
# if using yarn:
yarn
yarn dev
```

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,4 @@
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')

View File

@@ -0,0 +1,17 @@
{
"name": "vite-vue-starter",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "^3.0.9"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.1.5",
"@vue/compiler-sfc": "^3.0.9",
"vite": "^2.1.3"
}
}

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})

View File

@@ -101,14 +101,9 @@ function doneAddFile() {
padding-left: 0;
}
.add {
margin: 0;
font-size: 20px;
font-family: var(--font-code);
color: #999;
border: none;
outline: none;
background-color: transparent;
cursor: pointer;
vertical-align: middle;
margin-left: 6px;
}

View File

@@ -38,15 +38,10 @@ const mode = ref<Modes>('preview')
background-color: white;
}
.tab-buttons button {
margin: 0;
font-size: 13px;
font-family: var(--font-code);
border: none;
outline: none;
background-color: transparent;
padding: 8px 16px 6px;
text-transform: uppercase;
cursor: pointer;
color: #999;
box-sizing: border-box;
}

View File

@@ -97,7 +97,8 @@
['clear', 'log', 'info', 'dir', 'warn', 'error', 'table'].forEach((level) => {
const original = console[level];
console[level] = (...args) => {
if (String(args[0]).includes('You are running a development build of Vue')) {
const msg = String(args[0])
if (msg.includes('You are running a development build of Vue')) {
return
}
const stringifiedArgs = stringify(args);

View File

@@ -7,8 +7,6 @@ import {
rewriteDefault
} from '@vue/compiler-sfc'
const STORAGE_KEY = 'vue-sfc-playground'
const welcomeCode = `
<template>
<h1>{{ msg }}</h1>
@@ -48,12 +46,19 @@ interface Store {
errors: (string | Error)[]
}
const savedFiles = localStorage.getItem(STORAGE_KEY)
const files = savedFiles
? JSON.parse(savedFiles)
: {
'App.vue': new File(MAIN_FILE, welcomeCode)
}
let files: Store['files'] = {}
const savedFiles = location.hash.slice(1)
if (savedFiles) {
const saved = JSON.parse(decodeURIComponent(savedFiles))
for (const filename in saved) {
files[filename] = new File(filename, saved[filename])
}
} else {
files = {
'App.vue': new File(MAIN_FILE, welcomeCode)
}
}
export const store: Store = reactive({
files,
@@ -64,17 +69,26 @@ export const store: Store = reactive({
errors: []
})
watchEffect(() => compileFile(store.activeFile))
for (const file in store.files) {
if (file !== MAIN_FILE) {
compileFile(store.files[file])
}
}
watchEffect(() => compileFile(store.activeFile))
watchEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(store.files))
location.hash = encodeURIComponent(JSON.stringify(exportFiles()))
})
export function exportFiles() {
const exported: Record<string, string> = {}
for (const filename in store.files) {
exported[filename] = store.files[filename].code
}
return exported
}
export function setActive(filename: string) {
store.activeFilename = filename
}