update css, js links depending on the output code

This commit is contained in:
dialmedu 2023-11-28 05:53:48 -05:00
parent ed3dbc9c59
commit b6a804e234
6 changed files with 119 additions and 133 deletions

View File

@ -18,7 +18,7 @@ import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs";
import SettingsDialog from "./components/SettingsDialog"; import SettingsDialog from "./components/SettingsDialog";
import { Settings, EditorTheme, AppState } from "./types"; import { Settings, EditorTheme, AppState, ComponentLibrary } from "./types";
import { IS_RUNNING_ON_CLOUD } from "./config"; import { IS_RUNNING_ON_CLOUD } from "./config";
import { PicoBadge } from "./components/PicoBadge"; import { PicoBadge } from "./components/PicoBadge";
import { OnboardingNote } from "./components/OnboardingNote"; import { OnboardingNote } from "./components/OnboardingNote";
@ -43,6 +43,7 @@ function App() {
isImageGenerationEnabled: true, isImageGenerationEnabled: true,
editorTheme: EditorTheme.COBALT, editorTheme: EditorTheme.COBALT,
isTermOfServiceAccepted: false, isTermOfServiceAccepted: false,
componentLibrary: ComponentLibrary.HTML,
}, },
"setting" "setting"
); );

View File

@ -1,10 +1,10 @@
import { FaCopy } from "react-icons/fa"; import { FaCopy } from "react-icons/fa";
import CodeMirror from "./CodeMirror"; import CodeMirror from "./CodeMirror";
import { Button } from "./ui/button"; import { ComponentLibrary, Settings } from "../types";
import { Settings } from "../types";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { useCallback } from "react"; import { useCallback } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import OpenInCodepenio from "./OpenInCodepenio";
interface Props { interface Props {
code: string; code: string;
@ -18,41 +18,6 @@ function CodeTab({ code, setCode, settings }: Props) {
toast.success("Copied to clipboard"); toast.success("Copied to clipboard");
}, [code]); }, [code]);
const doOpenInCodepenio = useCallback(async () => {
// TODO: Update CSS and JS external links depending on the framework being used
const data = {
html: code,
editors: "100", // 1: Open HTML, 0: Close CSS, 0: Close JS
layout: "left",
css_external:
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" +
(code.includes("<ion-")
? ",https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css"
: ""),
js_external:
"https://cdn.tailwindcss.com " +
(code.includes("<ion-")
? ",https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js,https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js"
: ""),
};
// Create a hidden form and submit it to open the code in CodePen
// Can't use fetch API directly because we want to open the URL in a new tab
const input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", "data");
input.setAttribute("value", JSON.stringify(data));
const form = document.createElement("form");
form.setAttribute("method", "POST");
form.setAttribute("action", "https://codepen.io/pen/define");
form.setAttribute("target", "_blank");
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}, [code]);
return ( return (
<div className="relative"> <div className="relative">
<div className="flex justify-start items-center px-4 mb-2"> <div className="flex justify-start items-center px-4 mb-2">
@ -63,17 +28,7 @@ function CodeTab({ code, setCode, settings }: Props) {
> >
Copy Code <FaCopy className="ml-2" /> Copy Code <FaCopy className="ml-2" />
</span> </span>
<Button <OpenInCodepenio code={code} support={settings.componentLibrary || ComponentLibrary.HTML}></OpenInCodepenio>
onClick={doOpenInCodepenio}
className="bg-gray-100 text-black ml-2 py-2 px-4 border border-black rounded-md hover:bg-gray-400 focus:outline-none"
>
Open in{" "}
<img
src="https://assets.codepen.io/t-1/codepen-logo.svg"
alt="codepen.io"
className="h-4 ml-1"
/>
</Button>
</div> </div>
<CodeMirror <CodeMirror
code={code} code={code}

View File

@ -1,83 +1,27 @@
import { Component } from 'react'; import { useCallback } from 'react';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { libraries, LibraryType } from '@/lib/support';
import { ComponentLibrary } from '@/types';
import toast from 'react-hot-toast';
enum SupportType { export interface OpenInProps {
HTML = 'html',
IONIC = 'ionic'
}
enum LibraryType {
css = 'css',
js = 'js',
}
interface OpenInCodepenioProps {
code: string; code: string;
support?: SupportType; support?: ComponentLibrary;
onDoOpen?: () => void; onDoOpen?: () => void;
} }
interface Library {
[LibraryType.css]: string[],
[LibraryType.js]: string[],
validate: (support: SupportType, code: String) => Boolean
}
/** /**
* Component show button do open in new windows to CodePen Editor, this component add support * Component show button do open in new windows to CodePen Editor, this component add support
* of diferrent tecnologies do use `libraries` property. * of diferrent tecnologies do use `libraries` property.
* @component * @component
* Redirect to codepen.io * Redirect to codepen.io
* @param { OpenInCodepenioProps } props * @param { OpenInProps } props
* @property {string} code - this generated code. * @property {string} code - this generated code.
* @property {Function} onDoOpen - handle after open and redirect to Codepen Editor * @property {Function} onDoOpen - handle after open and redirect to Codepen Editor
* @property {Function} libraries[SupportType].css: [array style sheets libraries]
* @property {Function} libraries[SupportType].js: [array javascript libraries]
* @property {Function} libraries[SupportType].validate: return Boolean the result of custom validating
* @property {Function} libraries[SupportType].css: [array style sheets libraries]
* @property {Function} libraries[SupportType].js: [array javascript libraries]
* @property {Function} libraries[SupportType].validate: return Boolean the result of custom validating
*/ */
class OpenInCodepenio extends Component<OpenInCodepenioProps> { function OpenInCodepenio({ code, support, onDoOpen }: OpenInProps) {
libraries: Record<string, Library> = { const doOpenInCodepenio = useCallback(async () => {
[SupportType.HTML] : {
css: ['https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css'],
js: ['https://cdn.tailwindcss.com'],
validate: (support) => {
return support == SupportType.HTML
}
},
[SupportType.IONIC] : {
css: [
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css',
'https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css'],
js: [
'https://cdn.tailwindcss.com',
'https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js',
'https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js'
],
validate: (support, code) => {
return support == SupportType.IONIC || code.includes('<ion-')
}
},
}
getExternalLibraries = (type: LibraryType): string => {
let library: string[] = []
const { code, support = SupportType.HTML } = this.props;
Object.values(this.libraries).forEach((value: Library) => {
if( value.validate(support, code)){
library = value[type]
}
});
return library.join(',');
}
doOpenInCodepenio = () => {
const { code, onDoOpen } = this.props;
var form = document.createElement('form'); var form = document.createElement('form');
form.setAttribute('method', 'POST'); form.setAttribute('method', 'POST');
form.setAttribute('action', 'https://codepen.io/pen/define'); form.setAttribute('action', 'https://codepen.io/pen/define');
@ -87,15 +31,10 @@ class OpenInCodepenio extends Component<OpenInCodepenioProps> {
html: code, html: code,
editors: "100", // 1:Open html, 0:close CSS, 0:Close Js editors: "100", // 1:Open html, 0:close CSS, 0:Close Js
layout: "left", layout: "left",
css_external: this.getExternalLibraries(LibraryType.css), css_external: libraries.getLibraries(LibraryType.css, { code , support }),
js_external: this.getExternalLibraries(LibraryType.js), js_external: libraries.getLibraries(LibraryType.js, {code , support }),
}
// test have html to json
try {
JSON.stringify({ html: data.html })
} catch (error) {
data.html = "<!-- Copy your code here -->"
} }
var input = document.createElement('input'); var input = document.createElement('input');
input.setAttribute('type', 'hidden'); input.setAttribute('type', 'hidden');
input.setAttribute('name', 'data'); input.setAttribute('name', 'data');
@ -106,18 +45,25 @@ class OpenInCodepenio extends Component<OpenInCodepenioProps> {
form.submit(); form.submit();
if (onDoOpen) { if (onDoOpen) {
onDoOpen(); onDoOpen();
} else {
toast.success("Will Opening codepen.io, need enable popup windows");
} }
} }, [code, support])
render() {
return ( return (
<div> <Button
<Button onClick={this.doOpenInCodepenio} className="bg-gray-100 text-black ml-2 py-2 px-4 border border-black rounded-md hover:bg-gray-400 focus:outline-none"> onClick={doOpenInCodepenio}
Open in <img src="https://assets.codepen.io/t-1/codepen-logo.svg" alt="codepen.io" className="h-4 ml-1" /> className="bg-gray-100 text-black ml-2 py-2 px-4 border border-black rounded-md hover:bg-gray-400 focus:outline-none"
>
Open in{" "}
<img
src="https://assets.codepen.io/t-1/codepen-logo.svg"
alt="codepen.io"
className="h-4 ml-1"
/>
</Button> </Button>
</div>
); );
} }
}
export default OpenInCodepenio; export default OpenInCodepenio;

View File

@ -9,7 +9,7 @@ import {
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { FaCog } from "react-icons/fa"; import { FaCog } from "react-icons/fa";
import { EditorTheme, Settings } from "../types"; import { EditorTheme, Settings, ComponentLibrary } from "../types";
import { Switch } from "./ui/switch"; import { Switch } from "./ui/switch";
import { Label } from "./ui/label"; import { Label } from "./ui/label";
import { Input } from "./ui/input"; import { Input } from "./ui/input";
@ -120,6 +120,25 @@ function SettingsDialog({ settings, setSettings }: Props) {
<option value="espresso">Espresso</option> <option value="espresso">Espresso</option>
</Select> </Select>
</div> </div>
<Label htmlFor="output-code">
<div>Output Code</div>
</Label>
<div>
<Select
id="output-code"
value={settings.componentLibrary}
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
setSettings((s) => ({
...s,
componentLibrary: e.target.value as ComponentLibrary,
}))
}
>
<option disabled>Default</option>
<option value={ComponentLibrary.HTML}>{ComponentLibrary.HTML.toUpperCase()}</option>
</Select>
</div>
</div> </div>
<DialogFooter> <DialogFooter>

View File

@ -0,0 +1,56 @@
import { ComponentLibrary } from "@/types"
export enum LibraryType {
css = 'css',
js = 'js',
}
export interface Library {
[LibraryType.css]: string[],
[LibraryType.js]: string[],
validate: (support: ComponentLibrary, code: String) => Boolean
}
class SupportLibraries {
libraries: Record<string, Library> = {
[ComponentLibrary.HTML] : {
css: ['https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css'],
js: ['https://cdn.tailwindcss.com'],
validate: (support) => {
return support == ComponentLibrary.HTML
}
},
[ComponentLibrary.IONIC] : {
css: [
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css',
'https://cdn.jsdelivr.net/npm/@ionic/core/css/ionic.bundle.css'],
js: [
'https://cdn.tailwindcss.com',
'https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js',
'https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.js'
],
validate: (support, code) => {
return support == ComponentLibrary.IONIC || code.includes('<ion-')
}
},
}
getLibraries = (type: LibraryType, { code = '', support = ComponentLibrary.HTML} ): string => {
let library: string[] = []
debugger
Object.values(this.libraries).forEach((value: Library) => {
if( value.validate(support, code)){
library = value[type]
}
});
return library.join(',');
}
}
const libraries = new SupportLibraries()
export {
libraries
}

View File

@ -1,3 +1,11 @@
export enum ComponentLibrary {
HTML = 'html',
IONIC = 'ionic',
// TODO: add support to FLUTTER = 'flutter',
// TODO: add support to REACT = 'react',
// TODO: add support to VUE = 'vue',
}
export enum EditorTheme { export enum EditorTheme {
ESPRESSO = "espresso", ESPRESSO = "espresso",
COBALT = "cobalt", COBALT = "cobalt",
@ -9,6 +17,7 @@ export interface Settings {
isImageGenerationEnabled: boolean; isImageGenerationEnabled: boolean;
editorTheme: EditorTheme; editorTheme: EditorTheme;
isTermOfServiceAccepted: boolean; // Only relevant for hosted version isTermOfServiceAccepted: boolean; // Only relevant for hosted version
componentLibrary: ComponentLibrary;
} }
export enum AppState { export enum AppState {