오윤석
1 +{
2 + "presets": [
3 + "next/babel"
4 + ],
5 + "plugins": [
6 + [
7 + "babel-plugin-styled-components",
8 + {
9 + "fileName": true,
10 + "displayName": true,
11 + "pure": true
12 + }
13 + ]
14 + ]
15 +}
...\ No newline at end of file ...\ No newline at end of file
1 +export const browserslist = ["defaults"];
1 +# Config
2 +!config/default.json
3 +!config/development.json.sample
4 +config/*.json
5 +
6 +# Next
7 +.next
8 +out
9 +
10 +# Logs
11 +npm-debug.log*
12 +
13 +# NPM
14 +node_modules/
15 +
16 +# Transpiled code
17 +dist
18 +out
19 +.out
20 +
21 +# Dev tools
22 +.DS_Store
23 +.vscode
24 +.idea
25 +*.swp
26 +*.bak
27 +
28 +node_modules.nosync/
29 +*.env.*
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "editor.formatOnSave": true,
3 + "prettier.semi": false,
4 + "prettier.trailingComma": "all",
5 + "prettier.singleQuote": true,
6 + "prettier.tslintIntegration": true,
7 + "prettier.tabWidth": 2,
8 + "prettier.printWidth": 120
9 +}
1 +node_modules
2 +dist/*
...\ No newline at end of file ...\ No newline at end of file
1 +var blackTheme = {
2 + 'common.bi.image': 'https://uicdn.toast.com/toastui/img/tui-image-editor-bi.png',
3 + 'common.bisize.width': '251px',
4 + 'common.bisize.height': '21px',
5 + 'common.backgroundImage': 'none',
6 + 'common.backgroundColor': '#1e1e1e',
7 + 'common.border': '0px',
8 +
9 + // header
10 + 'header.backgroundImage': 'none',
11 + 'header.backgroundColor': 'transparent',
12 + 'header.border': '0px',
13 +
14 + // load button
15 + 'loadButton.backgroundColor': '#fff',
16 + 'loadButton.border': '1px solid #ddd',
17 + 'loadButton.color': '#222',
18 + 'loadButton.fontFamily': "'Noto Sans', sans-serif",
19 + 'loadButton.fontSize': '12px',
20 +
21 + // download button
22 + 'downloadButton.backgroundColor': '#fdba3b',
23 + 'downloadButton.border': '1px solid #fdba3b',
24 + 'downloadButton.color': '#fff',
25 + 'downloadButton.fontFamily': "'Noto Sans', sans-serif",
26 + 'downloadButton.fontSize': '12px',
27 +
28 + // main icons
29 + 'menu.normalIcon.color': '#8a8a8a',
30 + 'menu.activeIcon.color': '#555555',
31 + 'menu.disabledIcon.color': '#434343',
32 + 'menu.hoverIcon.color': '#e9e9e9',
33 + 'menu.iconSize.width': '24px',
34 + 'menu.iconSize.height': '24px',
35 +
36 + // submenu icons
37 + 'submenu.normalIcon.color': '#8a8a8a',
38 + 'submenu.activeIcon.color': '#e9e9e9',
39 + 'submenu.iconSize.width': '32px',
40 + 'submenu.iconSize.height': '32px',
41 +
42 + // submenu primary color
43 + 'submenu.backgroundColor': '#1e1e1e',
44 + 'submenu.partition.color': '#3c3c3c',
45 +
46 + // submenu labels
47 + 'submenu.normalLabel.color': '#8a8a8a',
48 + 'submenu.normalLabel.fontWeight': 'lighter',
49 + 'submenu.activeLabel.color': '#fff',
50 + 'submenu.activeLabel.fontWeight': 'lighter',
51 +
52 + // checkbox style
53 + 'checkbox.border': '0px',
54 + 'checkbox.backgroundColor': '#fff',
55 +
56 + // range style
57 + 'range.pointer.color': '#fff',
58 + 'range.bar.color': '#666',
59 + 'range.subbar.color': '#d1d1d1',
60 +
61 + 'range.disabledPointer.color': '#414141',
62 + 'range.disabledBar.color': '#282828',
63 + 'range.disabledSubbar.color': '#414141',
64 +
65 + 'range.value.color': '#fff',
66 + 'range.value.fontWeight': 'lighter',
67 + 'range.value.fontSize': '11px',
68 + 'range.value.border': '1px solid #353535',
69 + 'range.value.backgroundColor': '#151515',
70 + 'range.title.color': '#fff',
71 + 'range.title.fontWeight': 'lighter',
72 +
73 + // colorpicker style
74 + 'colorpicker.button.border': '1px solid #1e1e1e',
75 + 'colorpicker.title.color': '#fff',
76 +};
1 +<!DOCTYPE html>
2 +<html>
3 + <head>
4 + <meta charset="UTF-8" />
5 + <title>TUI Example</title>
6 + <link
7 + type="text/css"
8 + href="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.css"
9 + rel="stylesheet"
10 + />
11 + <link type="text/css" href="./tui-image-editor.css" rel="stylesheet" />
12 + <style>
13 + @import url(http://fonts.googleapis.com/css?family=Noto+Sans);
14 + html,
15 + body {
16 + height: 100%;
17 + margin: 0;
18 + }
19 + </style>
20 + </head>
21 + <body>
22 + <div id="tui-image-editor-container"></div>
23 + <script
24 + type="text/javascript"
25 + src="https://api-storage.cloud.toast.com/v1/AUTH_e18353c4ea5746c097143946d0644e61/toast-ui-cdn/tui-image-editor/v3.11.0/example/fabric-v4.2.0.js"
26 + ></script>
27 + <script
28 + type="text/javascript"
29 + src="https://uicdn.toast.com/tui.code-snippet/v1.5.0/tui-code-snippet.min.js"
30 + ></script>
31 + <script
32 + type="text/javascript"
33 + src="https://uicdn.toast.com/tui-color-picker/v2.2.6/tui-color-picker.js"
34 + ></script>
35 + <script
36 + type="text/javascript"
37 + src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.3/FileSaver.min.js"
38 + ></script>
39 + <script type="text/javascript" src="./tui-image-editor.js"></script>
40 + <script type="text/javascript" src="./black-theme.js"></script>
41 + <script type="text/javascript" src="../../dist/gif-generator.js"></script>
42 + <script>
43 + // Image editor
44 + var imageEditor = new tui.ImageEditor("#tui-image-editor-container", {
45 + includeUI: {
46 + loadImage: {
47 + path: "./sampleImage2.png",
48 + name: "SampleImage",
49 + },
50 + theme: blackTheme, // or whiteTheme
51 + initMenu: "filter",
52 + menuBarPosition: "bottom",
53 + },
54 + cssMaxWidth: 700,
55 + cssMaxHeight: 500,
56 + usageStatistics: false,
57 + });
58 + window.onresize = function () {
59 + imageEditor.ui.resizeEditor();
60 + };
61 +
62 + let gifGenerator;
63 + setTimeout(function () {
64 + gifGenerator = new GifGenerator(imageEditor._graphics.getCanvas());
65 + }, 1000);
66 + </script>
67 + </body>
68 +</html>
This diff is collapsed. Click to expand it.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
1 +{
2 + "name": "gif-generator",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "start": "webpack -w",
8 + "build": "webpack"
9 + },
10 + "author": "",
11 + "license": "ISC",
12 + "devDependencies": {
13 + "@babel/cli": "^7.13.14",
14 + "@babel/core": "^7.13.15",
15 + "@babel/preset-env": "^7.13.15",
16 + "babel-loader": "^8.2.2",
17 + "fabric": "^4.4.0",
18 + "webpack": "^5.31.0",
19 + "webpack-cli": "^4.6.0"
20 + },
21 + "dependencies": {
22 + "@babel/plugin-proposal-class-properties": "^7.13.0",
23 + "gifencoder": "^2.0.1",
24 + "stream": "0.0.2"
25 + }
26 +}
1 +import GIF from "gifencoder";
2 +
3 +class GifGenerator {
4 + constructor(canvas) {
5 + this.canvas = canvas;
6 + this.width = canvas.getWidth();
7 + this.height = canvas.getHeight();
8 + this.gif = new GIF(this.width, this.height);
9 +
10 + this.gif.start();
11 + this.gif.setTransparent(null);
12 + this.gif.setRepeat(0);
13 + this.gif.setQuality(10);
14 + }
15 +
16 + addFrame(delay = 0) {
17 + this.gif.setDelay(delay);
18 + this.gif.addFrame(this.canvas.getContext());
19 + }
20 +
21 + render() {
22 + this.gif.finish();
23 + const byte = new Uint8Array(this.gif.out.data);
24 +
25 + return new Blob([byte], { type: "image/gif" });
26 + }
27 +}
28 +
29 +window.GifGenerator = GifGenerator;
1 +const path = require('path');
2 +
3 +module.exports = {
4 + entry: './src/index.js',
5 + output: {
6 + path: __dirname + '/dist',
7 + filename: 'gif-generator.js',
8 + sourceMapFilename: 'gif-generator.map',
9 + },
10 + module: {
11 + rules: [
12 + {
13 + test: /\.js$/,
14 + include: [
15 + path.resolve(__dirname, 'src/js')
16 + ],
17 + exclude: /node_modules/,
18 + use: {
19 + loader: 'babel-loader',
20 + options: {
21 + presets: ['@babel/preset-env'],
22 + plugins: ['@babel/plugin-proposal-class-properties']
23 + }
24 + }
25 + }
26 + ]
27 + },
28 + devtool: 'source-map',
29 + mode: 'development'
30 +};
...\ No newline at end of file ...\ No newline at end of file
1 +/// <reference types="next" />
2 +/// <reference types="next/types/global" />
1 +/* eslint-disable @typescript-eslint/no-var-requires */
2 +const withBundleAnalyzer = require("@next/bundle-analyzer")({
3 + enabled: process.env.ANALYZE === "true",
4 +});
5 +
6 +module.exports = withBundleAnalyzer({
7 + target: "serverless",
8 + env: {
9 + BASE_URL: process.env.BASE_URL,
10 + },
11 +
12 + webpack(conf) {
13 + conf.module.rules.push({
14 + test: /\.svg$/,
15 + use: [
16 + {
17 + loader: "@svgr/webpack",
18 + options: {
19 + svgoConfig: {
20 + plugins: [
21 + {
22 + // Enable figma's wrong mask-type attribute work
23 + removeRasterImages: false,
24 + removeStyleElement: false,
25 + removeUnknownsAndDefaults: false,
26 + // Enable svgr's svg to fill the size
27 + removeViewBox: false,
28 + },
29 + ],
30 + },
31 + },
32 + },
33 + ],
34 + });
35 + // 절대경로
36 + conf.resolve.modules.push(__dirname);
37 + return conf;
38 + },
39 +});
1 +{
2 + "name": "website",
3 + "version": "0.1.0",
4 + "private": true,
5 + "dependencies": {
6 + "@next/bundle-analyzer": "^10.0.7",
7 + "@testing-library/jest-dom": "^5.11.4",
8 + "@testing-library/react": "^11.1.0",
9 + "@testing-library/user-event": "^12.1.10",
10 + "next": "^10.0.5",
11 + "react": "^17.0.2",
12 + "react-dom": "^17.0.2",
13 + "styled-components": "^5.2.3",
14 + "styled-reset": "^4.3.4",
15 + "tui-image-editor": "3.14.2",
16 + "@toast-ui/react-image-editor": "3.14.2",
17 + "web-vitals": "^1.0.1"
18 + },
19 + "devDependencies": {
20 + "@babel/core": "^7.13.10",
21 + "@babel/plugin-syntax-dynamic-import": "^7.8.3",
22 + "@svgr/webpack": "^5.5.0",
23 + "@types/node": "^14.14.22",
24 + "@types/react": "^17.0.0",
25 + "@types/react-dom": "^17.0.0",
26 + "@types/react-window": "^1.8.2",
27 + "@types/styled-components": "^5.1.7",
28 + "@typescript-eslint/eslint-plugin": "^4.14.1",
29 + "@typescript-eslint/eslint-plugin-tslint": "^4.14.1",
30 + "@typescript-eslint/parser": "^4.14.1",
31 + "babel-loader": "^8.2.2",
32 + "eslint": "^7.18.0",
33 + "eslint-config-airbnb-typescript": "^12.3.1",
34 + "eslint-config-prettier": "^8.1.0",
35 + "eslint-plugin-import": "^2.22.1",
36 + "eslint-plugin-jsx-a11y": "^6.4.1",
37 + "eslint-plugin-react": "^7.22.0",
38 + "eslint-plugin-react-hooks": "^4.2.0",
39 + "prettier": "^2.2.1",
40 + "typescript": "^4.1.3"
41 + },
42 + "scripts": {
43 + "dev": "next",
44 + "debug": "NODE_OPTIONS='--inspect' next dev",
45 + "build": "next build",
46 + "start": "next start",
47 + "export": "next export",
48 + "type-check": "tsc",
49 + "eslint": "eslint .",
50 + "analyze": "ANALYZE=true next build"
51 + },
52 + "eslintConfig": {
53 + "extends": [
54 + "react-app",
55 + "react-app/jest"
56 + ]
57 + },
58 + "browserslist": {
59 + "production": [
60 + ">0.2%",
61 + "not dead",
62 + "not op_mini all"
63 + ],
64 + "development": [
65 + "last 1 chrome version",
66 + "last 1 firefox version",
67 + "last 1 safari version"
68 + ]
69 + }
70 +}
No preview for this file type
1 +import styled from "styled-components";
2 +
3 +const Header = () => {
4 + return <Container>Gif Generator</Container>;
5 +};
6 +
7 +const Container = styled.div`
8 + position: fixed;
9 + top: 0;
10 + left: 0;
11 + width: 100%;
12 + padding: 1.5rem;
13 + background-color: white;
14 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
15 + text-align: center;
16 + font-size: 1.6rem;
17 + font-weight: 500;
18 + font-style: italic;
19 +`;
20 +
21 +export default Header;
1 +import dynamic from "next/dynamic";
2 +import { useState } from "react";
3 +import styled from "styled-components";
4 +
5 +const ToastEditor = dynamic(() => import("components/ToastEditor"), {
6 + ssr: false,
7 +});
8 +const Image = ({ previewURL, setPreviewURL }) => {
9 + const [file, setFile] = useState(undefined);
10 + console.log("previewURL", previewURL);
11 +
12 + // const uploadImage = (file) => {
13 + // if (!file) {
14 + // return;
15 + // }
16 + // };
17 +
18 + // const selectImg = (e) => {
19 + // const reader = new FileReader();
20 + // const targetFile = e.target.files[0];
21 + // setFile(targetFile);
22 + // // uploadImage(targetFile);
23 +
24 + // reader.onloadend = () => {
25 + // setPreviewURL(reader.result);
26 + // };
27 +
28 + // reader.readAsDataURL(targetFile);
29 + // };
30 +
31 + // const [isEditorOpened, setIsEditorOpened] = useState(false);
32 + // const handleEditor = () => {
33 + // setIsEditorOpened(true);
34 + // };
35 +
36 + return (
37 + <>
38 + <Container>
39 + <ImgBox>
40 + {/* <div onClick={handleEditor}>asdf</div> */}
41 + {/* {file === undefined ? ( */}
42 + <>
43 + {/* <div className="sub-flex">
44 + <BlankBox />
45 + <div>Click to add a photo</div>
46 + <input
47 + type="file"
48 + style={{
49 + position: "absolute",
50 + top: 0,
51 + paddingLeft: 0,
52 + zIndex: 0,
53 + width: "90%",
54 + height: "100%",
55 + border: "none",
56 + cursor: "pointer",
57 + outline: "none",
58 + }}
59 + onChange={selectImg}
60 + />
61 + </div>
62 + <div className="sub-flex">Open Image Editor</div> */}
63 + </>
64 + {/* ) : ( */}
65 + <img
66 + id="image"
67 + alt={""}
68 + style={{
69 + objectFit: "cover",
70 + display: "flex",
71 + maxHeight: "90%",
72 + maxWidth: "90%",
73 + }}
74 + src={previewURL as string}
75 + />
76 + {/* )} */}
77 + </ImgBox>
78 + {/* <Menu /> */}
79 + </Container>
80 + {/* {isEditorOpened && <ToastEditor {...{ setPreviewURL, setIsImgAdded }} />} */}
81 + </>
82 + );
83 +};
84 +
85 +const Menu = () => {
86 + return (
87 + <div style={{ width: "15rem", marginLeft: "2rem" }}>
88 + <Box />
89 + <Box />
90 + <Box />
91 + <Box />
92 + </div>
93 + );
94 +};
95 +
96 +const Container = styled.div`
97 + width: 100%;
98 + display: flex;
99 + justify-content: center;
100 + margin-top: 10rem;
101 +`;
102 +const ImgBox = styled.div`
103 + position: relative;
104 + width: 90%;
105 + /* height: 30rem; */
106 + background-color: white;
107 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
108 + border-radius: 2rem;
109 + margin-top: 2rem;
110 + display: flex;
111 + align-items: center;
112 + justify-content: center;
113 + font-size: 1rem;
114 + display: flex;
115 + /* flex: 0.6; */
116 + padding: 1rem 0;
117 + /* .sub-flex {
118 + position: relative;
119 + width: 100%;
120 + height: 100%;
121 + display: flex;
122 + align-items: center;
123 + justify-content: center;
124 + :first-child {
125 + border-right: 1px solid ${({ theme }) => theme.color.gray};
126 + }
127 + } */
128 +`;
129 +
130 +const Box = styled.div`
131 + width: 100%;
132 + height: 10rem;
133 + margin-top: 2rem;
134 + border-radius: 1rem;
135 + background-color: white;
136 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
137 +`;
138 +const BlankBox = styled.div`
139 + z-index: 1;
140 + position: absolute;
141 + top: 0;
142 + width: 90%;
143 + height: 50px;
144 + background-color: white;
145 +`;
146 +
147 +export default Image;
1 +/// <reference path="react-image-editor.d.ts" />
2 +import ImageEditor from "@toast-ui/react-image-editor";
3 +import { useEffect, useState } from "react";
4 +import styled from "styled-components";
5 +import "tui-image-editor/dist/tui-image-editor.css";
6 +
7 +const ToastEditor = ({ setPreviewURL, setIsImgAdded, setIsEditorOpened }) => {
8 + // const [lowerCanvas, setLowerCanvas] = useState<HTMLCanvasElement>();
9 + // const [upperCanvas, setUpperCanvas] = useState<HTMLCanvasElement>();
10 + // // console.log(
11 + // // document.getElementsByClassName("lower-canvas")[0]?.toDataURL("image/png")
12 + // // );
13 + // console.log("s");
14 +
15 + // // const [upperCanvas, setUpperCanvas] = useState(
16 + // // document.getElementsByClassName("upper-canvas ")[0]
17 + // // );
18 +
19 + // useEffect(() => {
20 + // window?.addEventListener("click", () => {
21 + // setLowerCanvas(
22 + // document.getElementsByClassName("lower-canvas")[0] as HTMLCanvasElement
23 + // );
24 + // setUpperCanvas(
25 + // document.getElementsByClassName("upper-canvas")[0] as HTMLCanvasElement
26 + // );
27 + // });
28 + // }, []);
29 +
30 + // useEffect(() => {
31 + // const img = lowerCanvas?.toDataURL("image/png");
32 + // const uploaded = document.getElementById("image");
33 + // console.log(uploaded);
34 + // // let w = window.open();
35 + // // if (w?.window) w.document.body.innerHTML = "<img src='" + img + "'>";
36 + // const image = new Image();
37 + // // image.onload = function () {
38 + // // lowerCanvas.width = uploaded.clientWidth;
39 + // // lowerCanvas.height = uploaded.clientHeight;
40 + // // lowerCanvas?.getContext("2d").drawImage(image, 0, 0);
41 + // // };
42 + // image.src = previewURL;
43 + // console.log("b");
44 + // if (lowerCanvas?.getContext&&upperCanvas?.getContext) {
45 + // image.onload = function () {
46 +
47 + // lowerCanvas.width = 1000;
48 + // lowerCanvas.height = 572;
49 + // upperCanvas.width = 1000;
50 + // upperCanvas.height = 572;
51 + // lowerCanvas?.getContext("2d").drawImage(image, 0, 0);
52 + // };
53 + // console.log(lowerCanvas.getContext("2d"));
54 + // }
55 + // }, [lowerCanvas?.toDataURL("image/png")]);
56 +
57 + const handleEnd = () => {
58 + const lowerCanvas = document.getElementsByClassName(
59 + "lower-canvas"
60 + )[0] as HTMLCanvasElement;
61 + setPreviewURL(lowerCanvas.toDataURL("image/png"));
62 + console.log("asdf");
63 + setIsImgAdded(true);
64 + setIsEditorOpened(false);
65 + };
66 +
67 + return (
68 + <Container>
69 + <div onClick={handleEnd} className="upload">
70 + Upload
71 + </div>
72 + <ImageEditor
73 + includeUI={{
74 + loadImage: {
75 + // path: 'img/sampleImage.jpg',
76 + name: "SampleImage",
77 + },
78 + // theme: myTheme,
79 + menu: ["shape", "filter"],
80 + initMenu: "filter",
81 + uiSize: {
82 + width: "100%",
83 + height: "700px",
84 + },
85 + menuBarPosition: "bottom",
86 + }}
87 + cssMaxHeight={500}
88 + cssMaxWidth={700}
89 + selectionStyle={{
90 + cornerSize: 20,
91 + rotatingPointOffset: 70,
92 + }}
93 + usageStatistics={true}
94 + />
95 + </Container>
96 + );
97 +};
98 +
99 +const Container = styled.div`
100 + position: fixed;
101 + width: 90%;
102 + top: 10rem;
103 + border-radius: 1.5rem;
104 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
105 + display: flex;
106 + flex-direction: column;
107 + align-items: center;
108 + .upload {
109 + font: 800 11.5px Arial;
110 + position: absolute;
111 + right: 0;
112 + top: 0;
113 + width: 120px;
114 + height: 40px;
115 + background: red;
116 + z-index: 10;
117 + border-radius: 20px;
118 + margin: 8px;
119 + background-color: #fdba3b;
120 + display: flex;
121 + align-items: center;
122 + justify-content: center;
123 + cursor: pointer;
124 + }
125 + .tui-image-editor-container {
126 + border-radius: 1.5rem;
127 + }
128 + .tui-image-editor-container .tui-image-editor-help-menu.top {
129 + top: 2rem;
130 + }
131 +`;
132 +
133 +export default ToastEditor;
1 +declare module "@toast-ui/react-image-editor";
1 +import type { AppProps } from "next/app";
2 +import Head from "next/head";
3 +import { ThemeProvider } from "styled-components";
4 +import { GlobalStyle } from "styles/global-style";
5 +import { theme } from "styles/theme";
6 +
7 +function MyApp({ Component, pageProps }: AppProps) {
8 + return (
9 + <>
10 + <Head>
11 + <meta name="viewport" content="width=device-width, initial-scale=1" />
12 + <title>Gif Generator</title>
13 + </Head>
14 + <GlobalStyle />
15 + <ThemeProvider theme={theme}>
16 + <Component {...pageProps} />
17 + <div id="modal-root" />
18 + </ThemeProvider>
19 + </>
20 + );
21 +}
22 +
23 +export default MyApp;
1 +import Document, {
2 + Html,
3 + Head,
4 + Main,
5 + NextScript,
6 + DocumentContext,
7 +} from "next/document";
8 +import { ServerStyleSheet } from "styled-components";
9 +
10 +class MyDocument extends Document {
11 + static async getInitialProps(ctx: DocumentContext) {
12 + const sheet = new ServerStyleSheet();
13 + const originalRenderPage = ctx.renderPage;
14 + try {
15 + ctx.renderPage = () =>
16 + originalRenderPage({
17 + enhanceApp: (App) => (props) =>
18 + sheet.collectStyles(<App {...props} />),
19 + });
20 +
21 + const initialProps = await Document.getInitialProps(ctx);
22 + return {
23 + ...initialProps,
24 + styles: (
25 + <>
26 + {initialProps.styles}
27 + {sheet.getStyleElement()}
28 + </>
29 + ),
30 + };
31 + } finally {
32 + sheet.seal();
33 + }
34 + }
35 +
36 + render() {
37 + return (
38 + <Html>
39 + <Head>
40 + <meta charSet="utf-8" />
41 + <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
42 + <meta property="og:title" content="네이버 예약" />
43 + <link rel="preconnect" href="https://fonts.gstatic.com" />
44 + <link
45 + href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"
46 + rel="preload"
47 + as="style"
48 + />
49 + <link
50 + href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"
51 + rel="stylesheet"
52 + />
53 + <script
54 + type="text/javascript"
55 + src="https://openapi.map.naver.com/openapi/v3/maps.js?ncpClientId=3ioixdkru3"
56 + />
57 + </Head>
58 + <body>
59 + <Main />
60 + <NextScript />
61 + <script src="libs/code-snippet.min.js"></script>
62 + <script src="libs/jquery.min.js"></script>
63 + <script src="libs/fabric.min.js"></script>
64 + <script src="js/image-editor.js"></script>
65 + <div id="my-image-editor">
66 + <canvas></canvas>
67 + </div>
68 + </body>
69 + </Html>
70 + );
71 + }
72 +}
73 +
74 +export default MyDocument;
1 +import Header from "components/Header";
2 +import Image from "components/Image";
3 +import styled from "styled-components";
4 +import dynamic from "next/dynamic";
5 +import { useState } from "react";
6 +
7 +const ToastEditor = dynamic(() => import("components/ToastEditor"), {
8 + ssr: false,
9 +});
10 +
11 +const Index = () => {
12 + const [isEditorOpened, setIsEditorOpened] = useState(false);
13 + const [previewURL, setPreviewURL] = useState<string | ArrayBuffer>("");
14 + const [isImgAdded, setIsImgAdded] = useState(false);
15 +
16 + return (
17 + <Container>
18 + <Header />
19 +
20 + {!isImgAdded ? (
21 + <div
22 + style={{
23 + height: "80vh",
24 + width: "100%",
25 + display: "flex",
26 + alignItems: "center",
27 + justifyContent: "center",
28 + }}
29 + >
30 + <button
31 + className="open-button"
32 + onClick={() => setIsEditorOpened(true)}
33 + >
34 + Open Image Editor
35 + </button>
36 + </div>
37 + ) : (
38 + !isEditorOpened && (
39 + <>
40 + <div style={{ position: "fixed", top: "5rem" }}>
41 + <button
42 + className="open-button"
43 + onClick={() => setIsEditorOpened(true)}
44 + >
45 + Change Image
46 + </button>
47 + </div>
48 + <Image {...{ previewURL, setPreviewURL }} />
49 + </>
50 + )
51 + )}
52 + {isEditorOpened && (
53 + <ToastEditor {...{ setPreviewURL, setIsImgAdded, setIsEditorOpened }} />
54 + )}
55 + </Container>
56 + );
57 +};
58 +
59 +const Container = styled.div`
60 + display: flex;
61 + flex-direction: column;
62 + align-items: center;
63 + .open-button {
64 + margin-top: 3rem;
65 + padding: 0.5rem 2rem;
66 + display: flex;
67 + align-items: center;
68 + transition: 0.3s;
69 + :hover {
70 + font-size: 1.1rem;
71 + transition: 0.3s;
72 + }
73 + ::before {
74 + width: 2.315rem;
75 + content: "+";
76 + font-size: 2rem;
77 + margin-right: 1rem;
78 + box-shadow: ${({ theme }) => theme.boxShadow.normal};
79 + border-radius: 50%;
80 + }
81 + }
82 +`;
83 +
84 +export default Index;
1 +import { createGlobalStyle } from "styled-components";
2 +import { reset } from "styled-reset";
3 +import { media } from "./theme";
4 +
5 +export const GlobalStyle = createGlobalStyle`
6 + ${reset}
7 + :focus {
8 + outline: none;
9 + border: none;
10 + }
11 + div[role="button"] {
12 + cursor: pointer;
13 + }
14 + ::-webkit-scrollbar {
15 + display: none;
16 + }
17 + html{
18 + -webkit-text-size-adjust: none;
19 + font-family: -apple-system,BlinkMacSystemFont,helvetica,Apple SD Gothic Neo,sans-serif;
20 + font-display: fallback;
21 + ${media.tablet}{
22 + font-size: 10px;
23 + }
24 + -ms-overflow-style: none;
25 + scrollbar-width: none;
26 + }
27 + button {
28 + background: none;
29 + padding: 0;
30 + border: none;
31 + cursor: pointer;
32 + &:disabled {
33 + cursor: default;
34 + fill: #f2f3f4;
35 + }
36 + }
37 +
38 + .pc-tablet-only {
39 + display: block;
40 + ${media.mobile} {
41 + display: none;
42 + }
43 + }
44 + .tablet-mobile-only{
45 + display: none;
46 + ${media.tablet}{
47 + display:block;
48 + }
49 + }
50 + .mobile-only {
51 + display: none;
52 + ${media.mobile} {
53 + display: block;
54 + }
55 + }
56 +`;
1 +import "styled-components";
2 +
3 +declare module "styled-components" {
4 + export interface DefaultTheme {
5 + color: {
6 + purple: "#8661de";
7 + blue: "#00bac7";
8 + gray: "#f6f6f6";
9 + green: "#07b495";
10 + lightGreen: "#99ecdd";
11 + darkGray: "#54595d";
12 + };
13 + boxShadow: {
14 + normal: "0 3px 8px 0 rgb(0 0 0 / 10%)";
15 + purple: "0 3px 8px 0 #d6c9ff";
16 + blue: "0 3px 8px 0 #b3e2e6";
17 + };
18 + }
19 +}
1 +import { DefaultTheme } from "styled-components";
2 +
3 +export const theme: DefaultTheme = {
4 + color: {
5 + purple: "#8661de",
6 + blue: "#00bac7",
7 + gray: "#f6f6f6",
8 + green: "#07b495",
9 + lightGreen: "#99ecdd",
10 + darkGray: "#54595d",
11 + },
12 + boxShadow: {
13 + normal: "0 3px 8px 0 rgb(0 0 0 / 10%)",
14 + purple: "0 3px 8px 0 #d6c9ff",
15 + blue: "0 3px 8px 0 #b3e2e6",
16 + },
17 +};
18 +
19 +const customMediaQuery = (maxWidth: number): string =>
20 + `@media (max-width: ${maxWidth}px)`;
21 +
22 +export const media = {
23 + custom: customMediaQuery,
24 + pc: customMediaQuery(1440),
25 + tablet: customMediaQuery(768),
26 + mobile: customMediaQuery(576),
27 +};
1 +{
2 + "compilerOptions": {
3 + "target": "es5",
4 + "lib": [
5 + "dom",
6 + "dom.iterable",
7 + "esnext"
8 + ],
9 + "allowJs": true,
10 + "skipLibCheck": true,
11 + "strict": false,
12 + "forceConsistentCasingInFileNames": true,
13 + "noEmit": true,
14 + "esModuleInterop": true,
15 + "module": "esnext",
16 + "moduleResolution": "node",
17 + "resolveJsonModule": true,
18 + "isolatedModules": true,
19 + "jsx": "preserve",
20 + "baseUrl": "src",
21 + "rootDir": "src",
22 + },
23 + "include": [
24 + "next-env.d.ts",
25 + "**/*.ts",
26 + "**/*.tsx",
27 + "next.config.js",
28 + "custom.d.ts",
29 + "styled.d.ts",
30 + "src/styles",
31 + "src/pages",
32 + "public",
33 + "**/*.scss",
34 + "src/components",
35 + "resources",
36 + ],
37 + "exclude": [
38 + "node_modules",
39 + ]
40 +}
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.