Skip to content

Commit fd2b3f6

Browse files
authored
Merge pull request #75 from modderme123/master
download as zip, use vite in codesandbox
2 parents f7dd8ea + fe3e651 commit fd2b3f6

File tree

7 files changed

+224
-159
lines changed

7 files changed

+224
-159
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@tailwindcss/forms": "^0.4.0",
4343
"babel-preset-solid": "1.2.6",
4444
"dedent": "^0.7.0",
45+
"jszip": "^3.7.1",
4546
"mitt": "^3.0.0",
4647
"monaco-editor": "^0.31.1",
4748
"monaco-editor-textmate": "^3.0.0",

playground/components/header.tsx

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { Component, onCleanup, createSignal, JSX, Show, createMemo, For } from 'solid-js';
1+
import { Component, onCleanup, createSignal, Show, createMemo, For } from 'solid-js';
22
import { Icon } from 'solid-heroicons';
3-
import { share, link, upload, xCircle, menu, moon, sun } from 'solid-heroicons/outline';
3+
import { share, link, download, xCircle, menu, moon, sun } from 'solid-heroicons/outline';
44
import Dismiss from 'solid-dismiss';
55

66
import logo from '../assets/logo.svg?url';
7-
import { processImport, Tab } from '../../src';
8-
import { exportToCsb } from '../utils/exportToCsb';
9-
import { exportToJSON } from '../utils/exportToJson';
7+
import type { Tab } from '../../src';
8+
import { exportToCsb, exportToZip } from '../utils/exportFiles';
109
import { ZoomDropdown } from './zoomDropdown';
1110
import pkg from '../../package.json';
1211

@@ -53,14 +52,6 @@ export const Header: Component<{
5352
.catch(console.error);
5453
}
5554

56-
const uploadFile: JSX.EventHandler<HTMLInputElement, Event> = async (event) => {
57-
const [file] = event.currentTarget.files!;
58-
59-
const tabs = processImport(JSON.parse(await file.text()));
60-
props.setTabs(tabs);
61-
props.setCurrent('main.tsx');
62-
};
63-
6455
const versions = createMemo(() => {
6556
const hardCoded = ['0.26.5', '1.0.0', SOLID_VERSION];
6657

@@ -109,33 +100,18 @@ export const Header: Component<{
109100
<span class="text-xs md:sr-only">{props.dark ? 'Light' : 'Dark'} mode</span>
110101
</button>
111102

112-
<label
113-
class="md:text-white flex flex-row space-x-2 items-center md:px-3 px-2 py-2 focus:outline-none focus:ring-1 rounded opacity-80 hover:opacity-100 cursor-pointer"
114-
classList={{
115-
'rounded-none active:bg-gray-300 hover:bg-gray-300 dark:hover:text-black focus:outline-none focus:highlight-none active:highlight-none focus:ring-0 active:outline-none':
116-
showMenu(),
117-
}}
118-
title="Import from JSON"
119-
>
120-
<input type="file" class="sr-only" onChange={uploadFile} accept=".json" />
121-
<Icon path={upload} class="h-6" style={{ margin: '0' }} />
122-
<span class="text-xs md:sr-only">Import from JSON</span>
123-
</label>
124-
125103
<button
126104
type="button"
127-
onClick={() => exportToJSON(props.tabs)}
105+
onClick={() => exportToZip(props.tabs)}
128106
class="md:text-white flex flex-row space-x-2 items-center md:px-3 px-2 py-2 focus:outline-none focus:ring-1 rounded opacity-80 hover:opacity-100"
129107
classList={{
130108
'rounded-none active:bg-gray-300 hover:bg-gray-300 dark:hover:text-black focus:outline-none focus:highlight-none active:highlight-none focus:ring-0 active:outline-none':
131109
showMenu(),
132110
}}
133-
title="Export to JSON"
111+
title="Export to Zip"
134112
>
135-
<svg class="fill-current h-6" viewBox="0 0 24 24">
136-
<path d="M12.043 23.968c.479-.004.953-.029 1.426-.094a11.805 11.805 0 003.146-.863 12.404 12.404 0 003.793-2.542 11.977 11.977 0 002.44-3.427 11.794 11.794 0 001.02-3.476c.149-1.16.135-2.346-.045-3.499a11.96 11.96 0 00-.793-2.788 11.197 11.197 0 00-.854-1.617c-1.168-1.837-2.861-3.314-4.81-4.3a12.835 12.835 0 00-2.172-.87h-.005c.119.063.24.132.345.201.12.074.239.146.351.225a8.93 8.93 0 011.559 1.33c1.063 1.145 1.797 2.548 2.218 4.041.284.982.434 1.998.495 3.017.044.743.044 1.491-.047 2.229-.149 1.27-.554 2.51-1.228 3.596a7.475 7.475 0 01-1.903 2.084c-1.244.928-2.877 1.482-4.436 1.114a3.916 3.916 0 01-.748-.258 4.692 4.692 0 01-.779-.45 6.08 6.08 0 01-1.244-1.105 6.507 6.507 0 01-1.049-1.747 7.366 7.366 0 01-.494-2.54c-.03-1.273.225-2.553.854-3.67a6.43 6.43 0 011.663-1.918c.225-.178.464-.333.704-.479l.016-.007a5.121 5.121 0 00-1.441-.12 4.963 4.963 0 00-1.228.24c-.359.12-.704.27-1.019.45a6.146 6.146 0 00-.733.494c-.211.18-.42.36-.615.555-1.123 1.153-1.768 2.682-2.022 4.256-.15.973-.15 1.96-.091 2.95.105 1.395.391 2.787.945 4.062a8.518 8.518 0 001.348 2.173 8.14 8.14 0 003.132 2.23 7.934 7.934 0 002.113.54c.074.015.149.015.209.015zm-2.934-.398a4.102 4.102 0 01-.45-.228 8.5 8.5 0 01-2.038-1.534c-1.094-1.137-1.827-2.566-2.247-4.08a15.184 15.184 0 01-.495-3.172 12.14 12.14 0 01.046-2.082c.135-1.257.495-2.501 1.124-3.58a6.889 6.889 0 011.783-2.053 6.23 6.23 0 011.633-.9 5.363 5.363 0 013.522-.045c.029 0 .029 0 .045.03.015.015.045.015.06.03.045.016.104.045.165.074.239.12.479.271.704.42a6.294 6.294 0 012.097 2.502c.42.914.615 1.934.631 2.938.014 1.079-.18 2.157-.645 3.146a6.42 6.42 0 01-2.638 2.832c.09.03.18.045.271.075.225.044.449.074.688.074 1.468.045 2.892-.66 3.94-1.647.195-.18.375-.375.54-.585.225-.27.435-.54.614-.823.239-.375.435-.75.614-1.154a8.112 8.112 0 00.509-1.664c.196-1.004.211-2.022.149-3.026-.135-2.022-.673-4.045-1.842-5.724a9.054 9.054 0 00-.555-.719 9.868 9.868 0 00-1.063-1.034 8.477 8.477 0 00-1.363-.915 9.927 9.927 0 00-1.692-.598l-.3-.06c-.209-.03-.42-.044-.634-.06a8.453 8.453 0 00-1.015.016c-.704.045-1.412.16-2.112.337C5.799 1.227 2.863 3.566 1.3 6.67A11.834 11.834 0 00.238 9.801a11.81 11.81 0 00-.104 3.775c.12 1.02.374 2.023.778 2.977.227.57.511 1.124.825 1.648 1.094 1.783 2.683 3.236 4.51 4.24.688.39 1.408.69 2.157.944.226.074.45.15.689.21z" />
137-
</svg>
138-
<span class="text-xs md:sr-only">Export to JSON</span>
113+
<Icon path={download} class="h-6" style={{ margin: '0' }} />
114+
<span class="text-xs md:sr-only">Export to Zip</span>
139115
</button>
140116

141117
<button

playground/components/zoomDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const ZoomDropdown: Component<{ showMenu: boolean }> = (props) => {
5252

5353
window.clearTimeout(timeoutId!);
5454

55-
timeoutId = setTimeout(() => {
55+
timeoutId = window.setTimeout(() => {
5656
setOpen(false);
5757

5858
stealFocus = true;

playground/utils/exportFiles.tsx

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import pkg from '../../package.json';
2+
import type { Tab } from '../../src';
3+
import { compressToBase64 } from '@amoutonbrady/lz-string';
4+
import dedent from 'dedent';
5+
6+
type IFiles = Record<string, { content: string | Record<string, any>; isBinary: boolean }>;
7+
const viteConfigFile = dedent`
8+
import { defineConfig } from "vite";
9+
import solidPlugin from "vite-plugin-solid";
10+
11+
export default defineConfig({
12+
plugins: [solidPlugin()],
13+
build: {
14+
target: "esnext",
15+
polyfillDynamicImport: false,
16+
},
17+
});
18+
`;
19+
const tsConfig = JSON.stringify(
20+
{
21+
compilerOptions: {
22+
strict: false,
23+
module: 'ESNext',
24+
target: 'ESNext',
25+
jsx: 'preserve',
26+
esModuleInterop: true,
27+
sourceMap: true,
28+
allowJs: true,
29+
lib: ['es6', 'dom'],
30+
rootDir: 'src',
31+
moduleResolution: 'node',
32+
jsxImportSource: 'solid-js',
33+
types: ['solid-js', 'solid-js/dom'],
34+
},
35+
},
36+
null,
37+
2,
38+
);
39+
40+
const { dependencies: d, devDependencies: dd } = pkg;
41+
42+
const packageJSON = JSON.stringify(
43+
{
44+
scripts: {
45+
start: 'vite',
46+
build: 'vite build',
47+
},
48+
dependencies: {
49+
'solid-js': d['solid-js'],
50+
},
51+
devDependencies: {
52+
vite: dd['vite'],
53+
'vite-plugin-solid': dd['vite-plugin-solid'],
54+
},
55+
},
56+
null,
57+
2,
58+
);
59+
60+
const indexHTML = (tabs: Tab[]) => dedent`
61+
<html>
62+
<head>
63+
<title>Vite Sandbox</title>
64+
<meta charset="UTF-8" />
65+
</head>
66+
67+
<body>
68+
<div id="app"></div>
69+
70+
<script type="module" src="./src/${tabs[0].name}.tsx"></script>
71+
</body>
72+
</html>
73+
`;
74+
75+
function getParameters(parameters: { files: IFiles }) {
76+
return compressToBase64(JSON.stringify(parameters))
77+
.replace(/\+/g, '-') // Convert '+' to '-'
78+
.replace(/\//g, '_') // Convert '/' to '_'
79+
.replace(/=+$/, ''); // Remove ending '='
80+
}
81+
82+
export function exportToCsb(tabs: Tab[]): void {
83+
const params = tabs.reduce<IFiles>((p, tab) => {
84+
p[`src/${tab.name}.${tab.type}`] = { content: tab.source, isBinary: false };
85+
return p;
86+
}, {});
87+
88+
const parameters = getParameters({
89+
files: {
90+
...params,
91+
'vite.config.ts': {
92+
content: viteConfigFile,
93+
isBinary: false,
94+
},
95+
'tsconfig.json': {
96+
content: tsConfig,
97+
isBinary: false,
98+
},
99+
'index.html': {
100+
content: indexHTML(tabs),
101+
isBinary: false,
102+
},
103+
'package.json': {
104+
content: packageJSON,
105+
isBinary: false,
106+
},
107+
},
108+
});
109+
110+
const url = `https://codesandbox.io/api/v1/sandboxes/define?parameters=${parameters}`;
111+
112+
const a = document.createElement('a');
113+
a.setAttribute('href', url);
114+
a.setAttribute('target', '_blank');
115+
a.setAttribute('rel', 'noopener');
116+
117+
document.body.appendChild(a);
118+
a.click();
119+
a.remove();
120+
}
121+
122+
/**
123+
* This function will convert the tabs of the playground
124+
* into a JSON formatted playground that can then be reimported later on
125+
* via the url `https://playground.solidjs.com/?data=my-file.json` or
126+
* vua the import button
127+
*
128+
* @param tabs {Tab[]} - The tabs to export
129+
*/
130+
export async function exportToZip(tabs: Tab[]): Promise<void> {
131+
const { default: JSZip } = await import('jszip');
132+
const zip = new JSZip();
133+
134+
// basic structure
135+
zip.file('index.html', indexHTML(tabs));
136+
zip.file('package.json', packageJSON);
137+
zip.file('vite.config.ts', viteConfigFile);
138+
zip.file('tsconfig.json', tsConfig);
139+
140+
// project src
141+
const src = zip.folder('src')!;
142+
143+
for (const tab of tabs) {
144+
src.file(`${tab.name}.${tab.type}`, tab.source);
145+
}
146+
147+
const blob = await zip.generateAsync({ type: 'blob' });
148+
const url = URL.createObjectURL(blob);
149+
150+
const anchor = (<a href={url} target="_blank" rel="noopener" download />) as HTMLElement;
151+
document.body.prepend(anchor);
152+
anchor.click();
153+
anchor.remove();
154+
}

playground/utils/exportToCsb.ts

Lines changed: 0 additions & 99 deletions
This file was deleted.

playground/utils/exportToJson.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)