diff --git a/package.json b/package.json index 5faed221f..53a2e39af 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,11 @@ "puppeteer-core": "9.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-feather": "^2.0.9", "styled-components": "^5.3.0", - "twemoji": "13.0.2" + "twemoji": "13.0.2", + "use-local-storage-state": "^9.0.2", + "zustand": "^3.5.1" }, "devDependencies": { "@types/marked": "2.0.2", diff --git a/src/components/Field.tsx b/src/components/Field.tsx new file mode 100644 index 000000000..16cea0c3f --- /dev/null +++ b/src/components/Field.tsx @@ -0,0 +1,9 @@ +import tw from "twin.macro"; + +export const Field = tw.div` + flex items-center space-x-4 +`; + +export const Label = tw.label` + w-24 +`; diff --git a/src/components/Input.tsx b/src/components/Input.tsx new file mode 100644 index 000000000..2781ee7a0 --- /dev/null +++ b/src/components/Input.tsx @@ -0,0 +1,8 @@ +import React from "react"; +import tw from "twin.macro"; + +export const Input = tw.input` + appearance-none w-full border rounded + px-3 py-1 h-9 + focus:outline-none focus:ring-2 focus:ring-accent +`; diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx new file mode 100644 index 000000000..b9fac4a1b --- /dev/null +++ b/src/components/Layout.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import { ILayout } from "../types"; +import { Field, Label } from "./Field"; +import { Input } from "./Input"; +import { Select } from "./Select"; +import "twin.macro"; + +export interface Props { + layout: ILayout; +} + +export const Layout: React.FC = ({ layout, ...props }) => { + return ( +
+ {layout.properties.map(p => ( + + + + {p.type === "text" ? ( + + ) : p.type === "select" ? ( + { + if (onChange != null) { + onChange(e.target.value); + } + }} + > + {options.map(opt => ( + + ))} + + + +
+ ); + }, +); diff --git a/src/hooks/useIsMounted.ts b/src/hooks/useIsMounted.ts new file mode 100644 index 000000000..3ee7ad163 --- /dev/null +++ b/src/hooks/useIsMounted.ts @@ -0,0 +1,7 @@ +import { useEffect, useState } from "react"; + +export const useIsMounted = (): boolean => { + const [isMounted, setIsMounted] = useState(false); + useEffect(() => setIsMounted(true), []); + return isMounted; +}; diff --git a/src/layouts.ts b/src/layouts.ts new file mode 100644 index 000000000..79eaf7373 --- /dev/null +++ b/src/layouts.ts @@ -0,0 +1,25 @@ +import { ILayout } from "./types"; + +export const simpleLayout: ILayout = { + name: "Simple", + properties: [ + { name: "Test", type: "text" }, + { + name: "Boop", + description: "This is a test", + type: "select", + options: ["one", "two", "three"], + }, + ], +}; + +export const starterLayout: ILayout = { + name: "Starter", + properties: [ + { name: "Name", type: "text" }, + { name: "Icon", type: "text" }, + { name: "URL", type: "text" }, + ], +}; + +export const layouts: ILayout[] = [simpleLayout, starterLayout]; diff --git a/src/pages/api/_lib/template.ts b/src/pages/api/_lib/template.ts index edf004126..bf7e4a180 100644 --- a/src/pages/api/_lib/template.ts +++ b/src/pages/api/_lib/template.ts @@ -1,4 +1,3 @@ -import { readFileSync } from "fs"; import marked from "marked"; import { sanitizeHtml } from "./sanitizer"; import { ParsedRequest } from "./types"; @@ -7,19 +6,6 @@ const twOptions = { folder: "svg", ext: ".svg" }; const emojify = (text: string) => twemoji.parse(text, twOptions); function getCss(theme: string, fontSize: string) { - console.log("DIRNAME", __dirname); - - // const rglr = readFileSync(`../_fonts/Inter-Regular.woff2`).toString("base64"); - - // console.log("REGULAR", rglr); - - // const bold = readFileSync(`${__dirname}/../_fonts/Inter-Bold.woff2`).toString( - // "base64", - // ); - // const mono = readFileSync(`${__dirname}/../_fonts/Vera-Mono.woff2`).toString( - // "base64", - // ); - let background = "white"; let foreground = "black"; let radial = "lightgray"; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 4182daad9..8e67ce316 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,18 +1,31 @@ import { NextPage } from "next"; -import React from "react"; +import React, { useMemo, useState } from "react"; import "twin.macro"; +import { Field, Label } from "../components/Field"; +import { Input } from "../components/Input"; +import { Layout } from "../components/Layout"; +import { Select } from "../components/Select"; +import { useIsMounted } from "../hooks/useIsMounted"; +import { layouts } from "../layouts"; +import { useStore } from "../store"; +import { FileType, Theme } from "../types"; const Home: NextPage = () => { + const isMounted = useIsMounted(); + return (

Railway OG Image Generator

-
- - -
+ {/* We pull the state from local storage so need the app to be loaded in the browser */} + {isMounted && ( +
+ + +
+ )}
); }; @@ -20,17 +33,59 @@ const Home: NextPage = () => { export default Home; export const Config: React.FC = () => { - return
CONFIG
; + const [{ theme, fileType, layout: layoutName }, setStore] = useStore(); + + const layout = useMemo( + () => layouts.find(l => l.name === layoutName), + [layoutName], + ); + + return ( +
+ + + + setStore(s => ({ ...s, fileType: fileType as FileType })) + } + /> + + + + +