Skip to content

Commit 783950a

Browse files
authored
feat: nutui-playground (#2385)
* feat: nutui-playground * chore: bump vitest to v0.32 * chore: update to @vue/repl v2.4.0
1 parent 87dc90b commit 783950a

File tree

29 files changed

+1338
-624
lines changed

29 files changed

+1338
-624
lines changed

package.json

+6-9
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@
9393
"@typescript-eslint/parser": "^4.20.0",
9494
"@vitejs/plugin-vue": "^4.2.0",
9595
"@vitejs/plugin-vue-jsx": "^3.0.1",
96-
"@vitest/coverage-c8": "^0.30.1",
97-
"@vitest/ui": "^0.30.1",
96+
"@vitest/coverage-v8": "^0.32.2",
97+
"@vitest/ui": "^0.32.2",
9898
"@vue/eslint-config-prettier": "^6.0.0",
9999
"@vue/eslint-config-typescript": "^7.0.0",
100-
"@vue/test-utils": "^2.3.0",
100+
"@vue/test-utils": "^2.4.0",
101101
"autoprefixer": "^10.3.4",
102102
"codesandbox": "^2.2.3",
103103
"eslint": "^7.23.2",
@@ -113,18 +113,15 @@
113113
"markdown-it": "^13.0.1",
114114
"markdown-it-container": "^3.0.0",
115115
"prettier": "^2.0.0",
116-
"rimraf": "^3.0.2",
116+
"rimraf": "^5.0.1",
117117
"typescript": "^4.9.3",
118118
"unplugin-vue-components": "^0.25.1",
119-
"vite": "^4.3.2",
119+
"vite": "^4.3.9",
120120
"vite-plugin-md": "^0.21.5",
121-
"vitest": "^0.30.1",
121+
"vitest": "^0.32.2",
122122
"vue": "^3.2.47",
123123
"vue-tsc": "^1.4.4"
124124
},
125-
"resolutions": {
126-
"@swc/core": "1.3.42"
127-
},
128125
"eslintConfig": {
129126
"root": true,
130127
"env": {

packages/nutui-playground/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# NutUI Vue SFC Playground
2+
3+
This is deployed at [nutui-playground.vercel.app](https://nutui-playground.vercel.app) or [nutui.eiinu.cn](https://nutui.eiinu.cn).
4+
5+
## Run and build
6+
7+
```sh
8+
pnpm i
9+
pnpm dev
10+
pnpm build
11+
```
12+
13+
> Some codes refer to [Vue SFC Playground](https://github.com/vuejs/core/tree/main/packages/sfc-playground)

packages/nutui-playground/index.html

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<link rel="icon" type="image/svg" href="https://img14.360buyimg.com/imagetools/jfs/t1/167902/2/8762/791358/603742d7E9b4275e3/e09d8f9a8bf4c0ef.png" />
8+
<title>NutUI Playground</title>
9+
<script>
10+
// process shim for old versions of @vue/compiler-sfc dependency
11+
window.process = { env: {} }
12+
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
13+
if (
14+
savedPreferDark === 'true' ||
15+
(!savedPreferDark && window.matchMedia('(prefers-color-scheme: dark)').matches)
16+
) {
17+
document.documentElement.classList.add('dark')
18+
}
19+
</script>
20+
<script type="module" src="/src/main.ts"></script>
21+
</head>
22+
<body>
23+
<div id="app"></div>
24+
</body>
25+
</html>
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@nutui/playground",
3+
"version": "1.0.0",
4+
"private": true,
5+
"description": "NutUI Vue SFC Playground",
6+
"keywords": [
7+
"nutui",
8+
"vue3"
9+
],
10+
"scripts": {
11+
"clean": "rimraf ./dist",
12+
"dev": "vite",
13+
"build": "vite build",
14+
"serve": "vite preview"
15+
},
16+
"devDependencies": {
17+
"rimraf": "^5.0.1",
18+
"@types/node": "^18.15.13",
19+
"@vitejs/plugin-vue": "^4.2.3",
20+
"vite": "^4.3.9",
21+
"vue": "^3.3.4"
22+
},
23+
"dependencies": {
24+
"@vue/repl": "^2.4.0",
25+
"file-saver": "^2.0.5",
26+
"jszip": "^3.10.0"
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// It is empty to be compatible with Functional Component style import

packages/nutui-playground/src/App.vue

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<script setup lang="ts">
2+
import Header from './Header.vue';
3+
import { Repl } from '@vue/repl';
4+
import CodeMirror from '@vue/repl/codemirror-editor';
5+
import { watchEffect } from 'vue';
6+
import { NutUIStore } from './store';
7+
8+
const setVH = () => {
9+
document.documentElement.style.setProperty('--vh', window.innerHeight + `px`);
10+
};
11+
window.addEventListener('resize', setVH);
12+
setVH();
13+
14+
const hash = location.hash.slice(1);
15+
16+
const store = new NutUIStore(
17+
{
18+
defaultVueRuntimeURL: 'https://cdn.jsdelivr.net/npm/@vue/runtime-dom/dist/runtime-dom.esm-browser.js'
19+
},
20+
hash
21+
);
22+
23+
store.setImportMap({
24+
imports: {
25+
'@nutui/nutui': 'https://cdn.jsdelivr.net/npm/@nutui/nutui/dist/nutui.js',
26+
'@nutui/icons-vue': 'https://cdn.jsdelivr.net/npm/@nutui/icons-vue/dist/lib/index.mjs',
27+
'@nutui/touch-emulator': 'https://cdn.jsdelivr.net/npm/@nutui/touch-emulator',
28+
// compatible with Functional Component style import
29+
'@nutui/nutui/dist/packages/toast/style': './style.js',
30+
'@nutui/nutui/dist/packages/dialog/style': './style.js',
31+
'@nutui/nutui/dist/packages/imagepreview/style': './style.js',
32+
'@nutui/nutui/dist/packages/notify/style': './style.js'
33+
}
34+
});
35+
36+
// persist state
37+
watchEffect(() => {
38+
const newHash = store.serialize();
39+
history.replaceState({}, '', newHash);
40+
});
41+
</script>
42+
43+
<template>
44+
<Header :store="store" />
45+
<Repl
46+
@keydown.ctrl.s.prevent
47+
@keydown.meta.s.prevent
48+
:editor="CodeMirror"
49+
:store="store"
50+
:showImportMap="false"
51+
:showTsConfig="false"
52+
/>
53+
</template>
54+
55+
<style>
56+
.dark {
57+
color-scheme: dark;
58+
}
59+
60+
body {
61+
font-size: 13px;
62+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
63+
'Helvetica Neue', sans-serif;
64+
margin: 0;
65+
--base: #444;
66+
--nav-height: 50px;
67+
}
68+
69+
.vue-repl {
70+
--color-branding: #ff2f2b !important;
71+
--color-branding-dark: #ff2f2b !important;
72+
height: calc(var(--vh) - var(--nav-height));
73+
}
74+
75+
button {
76+
border: none;
77+
outline: none;
78+
cursor: pointer;
79+
margin: 0;
80+
background-color: transparent;
81+
}
82+
</style>
+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<script setup lang="ts">
2+
import { downloadProject } from './download/download';
3+
import Sun from './icons/Sun.vue';
4+
import Moon from './icons/Moon.vue';
5+
import Share from './icons/Share.vue';
6+
import Download from './icons/Download.vue';
7+
import GitHub from './icons/GitHub.vue';
8+
import type { ReplStore } from '@vue/repl';
9+
10+
const props = defineProps<{
11+
store: ReplStore;
12+
}>();
13+
14+
const { store } = props;
15+
16+
async function copyLink() {
17+
await navigator.clipboard.writeText(location.href);
18+
alert('Sharable URL has been copied to clipboard.');
19+
}
20+
21+
function toggleDark() {
22+
const cls = document.documentElement.classList;
23+
cls.toggle('dark');
24+
localStorage.setItem('vue-sfc-playground-prefer-dark', String(cls.contains('dark')));
25+
}
26+
</script>
27+
28+
<template>
29+
<nav>
30+
<h1>
31+
<img
32+
alt="logo"
33+
src="https://img14.360buyimg.com/imagetools/jfs/t1/167902/2/8762/791358/603742d7E9b4275e3/e09d8f9a8bf4c0ef.png"
34+
/>
35+
<span>NutUI Playground</span>
36+
</h1>
37+
<div class="links">
38+
<button title="Toggle dark mode" class="toggle-dark" @click="toggleDark">
39+
<Sun class="light" />
40+
<Moon class="dark" />
41+
</button>
42+
<button title="Copy sharable URL" class="share" @click="copyLink">
43+
<Share />
44+
</button>
45+
<button title="Download project files" class="download" @click="downloadProject(store)">
46+
<Download />
47+
</button>
48+
<button title="View on GitHub" class="github">
49+
<a href="https://github.com/jdf2e/nutui" target="_blank">
50+
<GitHub />
51+
</a>
52+
</button>
53+
</div>
54+
</nav>
55+
</template>
56+
57+
<style>
58+
nav {
59+
--bg: #fff;
60+
--bg-light: #fff;
61+
--border: #ddd;
62+
--btn: #666;
63+
--highlight: #333;
64+
--green: #3ca877;
65+
--purple: #904cbc;
66+
--btn-bg: #eee;
67+
68+
color: var(--base);
69+
height: var(--nav-height);
70+
box-sizing: border-box;
71+
padding: 0 1em;
72+
background-color: var(--bg);
73+
box-shadow: 0 0 4px rgba(0, 0, 0, 0.33);
74+
position: relative;
75+
z-index: 999;
76+
display: flex;
77+
justify-content: space-between;
78+
}
79+
80+
.dark nav {
81+
--base: #ddd;
82+
--bg: #1a1a1a;
83+
--bg-light: #242424;
84+
--border: #383838;
85+
--highlight: #fff;
86+
--btn-bg: #333;
87+
88+
box-shadow: none;
89+
border-bottom: 1px solid var(--border);
90+
}
91+
92+
h1 {
93+
font-weight: 500;
94+
display: inline-flex;
95+
place-items: center;
96+
}
97+
98+
h1 img {
99+
height: 24px;
100+
margin-right: 10px;
101+
}
102+
103+
@media (max-width: 560px) {
104+
h1 span {
105+
font-size: 0.9em;
106+
}
107+
}
108+
109+
@media (max-width: 520px) {
110+
h1 span {
111+
display: none;
112+
}
113+
}
114+
115+
.links {
116+
display: flex;
117+
}
118+
119+
.toggle-dark svg {
120+
width: 18px;
121+
height: 18px;
122+
}
123+
124+
.toggle-dark .dark,
125+
.dark .toggle-dark .light {
126+
display: none;
127+
}
128+
129+
.dark .toggle-dark .dark {
130+
display: inline-block;
131+
}
132+
133+
.links button,
134+
.links button a {
135+
color: var(--btn);
136+
}
137+
138+
.links button:hover,
139+
.links button:hover a {
140+
color: var(--highlight);
141+
}
142+
143+
.links > * {
144+
display: flex;
145+
align-items: center;
146+
}
147+
148+
.links > * + * {
149+
margin-left: 4px;
150+
}
151+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { saveAs } from 'file-saver';
2+
3+
import index from './template/index.html?raw';
4+
import main from './template/main.js?raw';
5+
import pkg from './template/package.json?raw';
6+
import config from './template/vite.config.js?raw';
7+
import readme from './template/README.md?raw';
8+
import { ReplStore } from '@vue/repl';
9+
10+
export async function downloadProject(store: ReplStore) {
11+
if (!confirm('Download project files?')) {
12+
return;
13+
}
14+
15+
const { default: JSZip } = await import('jszip');
16+
const zip = new JSZip();
17+
18+
// basic structure
19+
zip.file('index.html', index);
20+
zip.file('package.json', pkg);
21+
zip.file('vite.config.js', config);
22+
zip.file('README.md', readme);
23+
24+
// project src
25+
const src = zip.folder('src')!;
26+
src.file('main.js', main);
27+
28+
const files = store.getFiles();
29+
src.file('App.vue', files['App.vue']);
30+
31+
const blob = await zip.generateAsync({ type: 'blob' });
32+
saveAs(blob, 'nutui-project.zip');
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# NutUI Template
2+
3+
This is a project template using [Vite](https://vitejs.dev/). It requires [Node.js](https://nodejs.org) v12+.
4+
5+
To start:
6+
7+
```sh
8+
pnpm i
9+
pnpm dev
10+
11+
# if using yarn:
12+
yarn
13+
yarn dev
14+
15+
# if using npm:
16+
npm install
17+
npm run dev
18+
```

0 commit comments

Comments
 (0)