diff --git a/package.json b/package.json index 1df3a47..b068fd4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "react-dom": "^18.0.0", "react-konva": "^18.0.0-0", "react-redux": "^7.2.6", + "react-resize-detector": "^7.0.0", "react-router-dom": "^6.2.2", "react-scripts": "5.0.0", "react-split": "^2.0.14", diff --git a/src/hooks/useTitle.ts b/src/hooks/useTitle.ts new file mode 100644 index 0000000..1836ed3 --- /dev/null +++ b/src/hooks/useTitle.ts @@ -0,0 +1,15 @@ +import useRouteContextEffect from './useRouteContextEffect'; +import produceState from '../utils/produceState'; + +const useTitle = (title: string) => { + useRouteContextEffect( + (setRouteContentState) => { + produceState(setRouteContentState, (context) => { + context.title = title; + }); + }, + [title] + ); +}; + +export default useTitle; diff --git a/src/pages/dataUrlViewPage/DataUrlViewPage.tsx b/src/pages/dataUrlViewPage/DataUrlViewPage.tsx index d1b6092..f38e027 100644 --- a/src/pages/dataUrlViewPage/DataUrlViewPage.tsx +++ b/src/pages/dataUrlViewPage/DataUrlViewPage.tsx @@ -3,8 +3,7 @@ import useQuery from '../../hooks/useQuery'; import styles from './DataUrlViewPage.module.scss'; import { Navigate } from 'react-router-dom'; import { routes } from '../../constants/router/routes'; -import useRouteContextEffect from '../../hooks/useRouteContextEffect'; -import produceState from '../../utils/produceState'; +import useTitle from '../../hooks/useTitle'; export interface DataUrlViewPageQueryParams { data?: string; @@ -18,14 +17,7 @@ const DataUrlViewPage: FunctionComponent = () => { return ; } - useRouteContextEffect( - (setRouteContentState) => { - produceState(setRouteContentState, (context) => { - context.title = title ?? routes.dataUrlView.title; - }); - }, - [title] - ); + useTitle(title ?? routes.dataUrlView.title); return (
diff --git a/src/pages/htmlEditorPage/HtmlEditorPage.module.scss b/src/pages/htmlEditorPage/HtmlEditorPage.module.scss index 1f60236..819b90a 100644 --- a/src/pages/htmlEditorPage/HtmlEditorPage.module.scss +++ b/src/pages/htmlEditorPage/HtmlEditorPage.module.scss @@ -1,5 +1,12 @@ @import '../../styles/main'; +.titleExtra { + flex: 1; + display: flex; + flex-direction: row; + justify-content: space-between; +} + .container { @include monacoBackground; @@ -7,8 +14,13 @@ height: 100%; display: flex; flex-direction: row; + flex: 1; + overflow: hidden; + max-width: 100%; .col { + @include monacoBackground; + display: flex; flex-direction: column; @@ -17,50 +29,23 @@ &:only-child { flex: 1; + + & > * { + max-width: 100%; + overflow: auto; + } } } .leftCol { - &:not(:only-child) { - border-right: 1px solid; - @include borderColor; - } - - .editorTabs { - flex: 1; - display: flex; - - & > *:last-child { - display: flex; - } - - .editorTab { - flex: 1; - display: flex; - - .editor { - flex: 1; - } - } - - .tabTitleWrapper { - height: 20px !important; - width: 50px !important; - - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - - .tabTitle { - font-size: 28px; - } - } - } } .rightCol { - background: white; + .tabTitleWrapper { + .tabTitle { + font-size: 24px; + } + } } .resultFrame { @@ -68,6 +53,39 @@ border: none; max-width: 100%; overflow: auto; + background: white; + } + } +} + +.editorTabs { + flex: 1; + display: flex; + + & > *:last-child { + display: flex; + } + + .editorTab { + flex: 1; + display: flex; + + .editor { + flex: 1; + } + } + + .tabTitleWrapper { + height: 20px !important; + width: 50px !important; + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + .tabTitle { + font-size: 28px; } } } diff --git a/src/pages/htmlEditorPage/HtmlEditorPage.scss b/src/pages/htmlEditorPage/HtmlEditorPage.scss index d07f6d3..c9b0e50 100644 --- a/src/pages/htmlEditorPage/HtmlEditorPage.scss +++ b/src/pages/htmlEditorPage/HtmlEditorPage.scss @@ -10,4 +10,9 @@ .gutter.gutter-horizontal { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); cursor: col-resize; + z-index: 1; + + border-right: 1px solid; + border-left: 1px solid; + @include borderColor; } diff --git a/src/pages/htmlEditorPage/HtmlEditorPage.tsx b/src/pages/htmlEditorPage/HtmlEditorPage.tsx index 5656531..1a7694b 100644 --- a/src/pages/htmlEditorPage/HtmlEditorPage.tsx +++ b/src/pages/htmlEditorPage/HtmlEditorPage.tsx @@ -1,7 +1,6 @@ import React, { CSSProperties, FunctionComponent, useCallback } from 'react'; -import PageContainer from '../../layouts/pages/pageContainer/PageContainer'; import styles from './HtmlEditorPage.module.scss'; -import { Button, Col, Row, Tabs } from 'antd'; +import { Button, Col, Row, Select, Space, Tabs, Tooltip } from 'antd'; import { useLocalstorageState } from 'rooks'; import getLocalStorageKey from '../../utils/getLocalStorageKey'; import useChangeStateHandler from '../../hooks/useChangeStateHandler'; @@ -15,10 +14,12 @@ import { OnMount } from '@monaco-editor/react'; import { emmetCSS, emmetHTML, emmetJSX } from 'emmet-monaco-es'; import { editor } from 'monaco-editor'; import ButtonGroup from 'antd/lib/button/button-group'; -import { VerticalSplit, ViewHeadline } from '@mui/icons-material'; +import { Code, VerticalSplit, ViewHeadline, Visibility } from '@mui/icons-material'; import useChangeValueStateHandler from '../../hooks/useChangeValueStateHandler'; import Split from 'react-split'; import './HtmlEditorPage.scss'; +import SingleConverterPageContainer from '../../layouts/pages/singleConverterPageContainer/SingleConverterPageContainer'; +import { EditOutlined, MinusOutlined, PlusOutlined } from '@ant-design/icons'; enum EditorTab { HTML = 'html', @@ -26,6 +27,11 @@ enum EditorTab { JS = 'js' } +enum ViewTab { + PREVIEW = 'PREVIEW', + SOURCE = 'SOURCE' +} + type EditorSources = Record; const sourcesInitial: EditorSources = { @@ -60,7 +66,13 @@ const tabBarStyle: CSSProperties = { }; const editorOptions: editor.IStandaloneEditorConstructionOptions = { - minimap: { enabled: false } + minimap: { enabled: false }, + automaticLayout: false +}; + +const resultEditorOptions: editor.IStandaloneEditorConstructionOptions = { + ...editorOptions, + readOnly: true }; enum ViewMode { @@ -91,6 +103,11 @@ const HtmlEditorPage: FunctionComponent = () => { EditorTab.HTML ); + const [viewTab, setViewTab] = useLocalstorageState( + getLocalStorageKey('html-editor', 'viewTab'), + ViewTab.PREVIEW + ); + const [viewMode, setViewMode] = useLocalstorageState( getLocalStorageKey('html-editor', 'viewMode'), ViewMode.SPLIT @@ -102,21 +119,19 @@ const HtmlEditorPage: FunctionComponent = () => { setEditorTab(tab as EditorTab); }, []); + const handleViewTabChange = useCallback((tab: string) => { + setViewTab(tab as ViewTab); + }, []); + const handleViewModeChange = useChangeValueStateHandler(setViewMode); const resultSource = useDebouncedMemo( { sources }, ({ sources }) => { - return ` - + const cssPart = sources.css.trim() ? `` : ''; + const jsPart = sources.js.trim() ? `` : ''; -${sources.html} - -`.trim(); + return [cssPart, sources.html, jsPart].filter(Boolean).join('\n\n'); }, [sources], 50 @@ -126,6 +141,8 @@ ${sources.js} emmetHTML(monaco); emmetCSS(monaco); emmetJSX(monaco); + + editor.layout(); }, []); // const leftColSpan = { @@ -148,22 +165,6 @@ ${sources.js} tabBarStyle={tabBarStyle} className={styles.editorTabs} tabBarGutter={10} - tabBarExtraContent={ - -