mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-12 15:52:56 +10:00
implement page controller
This commit is contained in:
16
package-lock.json
generated
16
package-lock.json
generated
@ -13943,6 +13943,14 @@
|
||||
"scheduler": "^0.19.1"
|
||||
}
|
||||
},
|
||||
"react-easy-panzoom": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-panzoom/-/react-easy-panzoom-0.4.4.tgz",
|
||||
"integrity": "sha512-1zgT6boDVPcrR3Egcz8KEVpM3fs50o22iIWPRlAqvev0/4nw5RnUNFsvmOJ/b5M2nd8MDGknLmyfBdhjoLB6+g==",
|
||||
"requires": {
|
||||
"warning": "4.0.3"
|
||||
}
|
||||
},
|
||||
"react-error-overlay": {
|
||||
"version": "6.0.7",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.7.tgz",
|
||||
@ -17121,6 +17129,14 @@
|
||||
"makeerror": "1.0.x"
|
||||
}
|
||||
},
|
||||
"warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"postcss-cli": "^7.1.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-easy-panzoom": "^0.4.4",
|
||||
"react-i18next": "^11.3.4",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-scripts": "3.4.1",
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import React, { useRef, useEffect, useContext, Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PanZoom } from 'react-easy-panzoom';
|
||||
|
||||
import AppContext from '../../context/AppContext';
|
||||
import PageContext from '../../context/PageContext';
|
||||
@ -8,9 +9,11 @@ import LeftSidebar from '../LeftSidebar/LeftSidebar';
|
||||
import RightSidebar from '../RightSidebar/RightSidebar';
|
||||
|
||||
import templates from '../../templates';
|
||||
import PageController from '../../shared/PageController';
|
||||
|
||||
const App = () => {
|
||||
const pageRef = useRef(null);
|
||||
const panZoomRef = useRef(null);
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const context = useContext(AppContext);
|
||||
@ -18,24 +21,36 @@ const App = () => {
|
||||
const { theme, settings } = state;
|
||||
|
||||
const pageContext = useContext(PageContext);
|
||||
const { setPageElement } = pageContext;
|
||||
const { setPageRef, setPanZoomRef } = pageContext;
|
||||
|
||||
useEffect(() => {
|
||||
setPageElement(pageRef);
|
||||
setPageRef(pageRef);
|
||||
setPanZoomRef(panZoomRef);
|
||||
i18n.changeLanguage(settings.language);
|
||||
const storedState = JSON.parse(localStorage.getItem('state'));
|
||||
dispatch({ type: 'import_data', payload: storedState });
|
||||
}, [dispatch, setPageElement, i18n, settings.language]);
|
||||
}, [dispatch, setPageRef, setPanZoomRef, i18n, settings.language]);
|
||||
|
||||
return (
|
||||
<Suspense fallback="Loading...">
|
||||
<div className="h-screen overflow-hidden grid grid-cols-5 items-center">
|
||||
<div className="h-screen grid grid-cols-5 items-center">
|
||||
<LeftSidebar />
|
||||
|
||||
<div className="z-0 h-screen col-span-3 flex overflow-scroll justify-center items-center">
|
||||
<div className="relative z-0 h-screen overflow-hidden col-span-3 flex justify-center items-center">
|
||||
<PanZoom
|
||||
ref={panZoomRef}
|
||||
enableBoundingBox
|
||||
minZoom="0.4"
|
||||
style={{
|
||||
outline: 'none',
|
||||
}}
|
||||
>
|
||||
<div id="page" ref={pageRef} className="shadow-2xl">
|
||||
{templates.find(x => theme.layout.toLowerCase() === x.key).component()}
|
||||
</div>
|
||||
</PanZoom>
|
||||
|
||||
<PageController />
|
||||
</div>
|
||||
|
||||
<RightSidebar />
|
||||
|
||||
@ -7,28 +7,29 @@ import html2canvas from 'html2canvas';
|
||||
import * as jsPDF from 'jspdf';
|
||||
|
||||
import PageContext from '../../../context/PageContext';
|
||||
import { importJson } from '../../../utils';
|
||||
|
||||
const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
const pageContext = useContext(PageContext);
|
||||
const { pageElement } = pageContext;
|
||||
const { pageRef, panZoomRef } = pageContext;
|
||||
const { t } = useTranslation('rightSidebar');
|
||||
const fileInputRef = useRef(null);
|
||||
|
||||
const importJson = event => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('load', () => {
|
||||
const importedObject = JSON.parse(fr.result);
|
||||
dispatch({ type: 'import_data', payload: importedObject });
|
||||
dispatch({ type: 'save_data' });
|
||||
});
|
||||
fr.readAsText(event.target.files[0]);
|
||||
const exportToJson = () => {
|
||||
const backupObj = { data, theme };
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(backupObj))}`;
|
||||
const dlAnchor = document.getElementById('downloadAnchor');
|
||||
dlAnchor.setAttribute('href', dataStr);
|
||||
dlAnchor.setAttribute('download', `RxResumeBackup_${Date.now()}.json`);
|
||||
dlAnchor.click();
|
||||
};
|
||||
|
||||
const printAsPdf = () => {
|
||||
pageElement.current.style.display = 'table';
|
||||
pageElement.current.style.overflow = 'visible';
|
||||
panZoomRef.current.autoCenter(1);
|
||||
panZoomRef.current.reset();
|
||||
|
||||
html2canvas(pageElement.current, {
|
||||
setTimeout(() => {
|
||||
html2canvas(pageRef.current, {
|
||||
scale: 5,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
@ -48,21 +49,12 @@ const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
const marginX = (pageWidth - canvasWidth) / 2;
|
||||
const marginY = (pageHeight - canvasHeight) / 2;
|
||||
|
||||
pageElement.current.style.display = 'block';
|
||||
pageElement.current.style.overflow = 'scroll';
|
||||
panZoomRef.current.autoCenter(0.7);
|
||||
|
||||
doc.addImage(image, 'JPEG', marginX, marginY, canvasWidth, canvasHeight, null, 'SLOW');
|
||||
doc.save(`RxResume_${Date.now()}.pdf`);
|
||||
});
|
||||
};
|
||||
|
||||
const exportToJson = () => {
|
||||
const backupObj = { data, theme };
|
||||
const dataStr = `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(backupObj))}`;
|
||||
const dlAnchor = document.getElementById('downloadAnchor');
|
||||
dlAnchor.setAttribute('href', dataStr);
|
||||
dlAnchor.setAttribute('download', `RxResumeBackup_${Date.now()}.json`);
|
||||
dlAnchor.click();
|
||||
}, 250);
|
||||
};
|
||||
|
||||
const loadDemoData = () => {
|
||||
@ -86,7 +78,12 @@ const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
|
||||
<p className="text-sm">{t('actions.importExport.body')}</p>
|
||||
|
||||
<input ref={fileInputRef} type="file" className="hidden" onChange={importJson} />
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
className="hidden"
|
||||
onChange={e => importJson(e, dispatch)}
|
||||
/>
|
||||
<a id="downloadAnchor" className="hidden" />
|
||||
|
||||
<div className="mt-4 grid grid-cols-2 col-gap-6">
|
||||
|
||||
@ -4,12 +4,16 @@ const PageContext = React.createContext(null);
|
||||
const { Provider } = PageContext;
|
||||
|
||||
const StateProvider = ({ children }) => {
|
||||
const [pageElement, setPageElement] = useState(null);
|
||||
const [panZoomRef, setPanZoomRef] = useState(null);
|
||||
const [pageRef, setPageRef] = useState(null);
|
||||
|
||||
return (
|
||||
<Provider
|
||||
value={{
|
||||
pageElement,
|
||||
setPageElement,
|
||||
pageRef,
|
||||
setPageRef,
|
||||
panZoomRef,
|
||||
setPanZoomRef,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -67,12 +67,16 @@ ul li {
|
||||
#page {
|
||||
width: 21cm;
|
||||
min-height: 29.7cm;
|
||||
max-height: 29.7cm;
|
||||
transform: scale(0.8);
|
||||
transform-origin: center;
|
||||
overflow: scroll;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#pageController {
|
||||
bottom: 25px;
|
||||
}
|
||||
|
||||
#pageController > div {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@page {
|
||||
|
||||
50
src/shared/PageController.js
Normal file
50
src/shared/PageController.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React, { useContext } from 'react';
|
||||
import PageContext from '../context/PageContext';
|
||||
|
||||
const PageController = () => {
|
||||
const pageContext = useContext(PageContext);
|
||||
const { panZoomRef } = pageContext;
|
||||
|
||||
const zoomIn = () => panZoomRef.current.zoomIn(2);
|
||||
const zoomOut = () => panZoomRef.current.zoomOut(2);
|
||||
const centerReset = () => {
|
||||
panZoomRef.current.autoCenter(1);
|
||||
panZoomRef.current.reset(1);
|
||||
};
|
||||
|
||||
return (
|
||||
<div id="pageController" className="absolute z-20">
|
||||
<div className="px-8 border border-gray-200 rounded-full bg-white flex justify-center items-center select-none">
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={zoomIn}>
|
||||
<i className="material-icons">zoom_in</i>
|
||||
</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={zoomOut}>
|
||||
<i className="material-icons">zoom_out</i>
|
||||
</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={centerReset}>
|
||||
<i className="material-icons">center_focus_strong</i>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-400 p-3">|</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex">
|
||||
<i className="material-icons">save</i>
|
||||
</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex">
|
||||
<i className="material-icons">get_app</i>
|
||||
</div>
|
||||
|
||||
<div className="text-gray-400 p-3">|</div>
|
||||
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex">
|
||||
<i className="material-icons">help_outline</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageController;
|
||||
28
src/templates/glalie/Glalie.js
Normal file
28
src/templates/glalie/Glalie.js
Normal file
@ -0,0 +1,28 @@
|
||||
import React, { useContext } from 'react';
|
||||
import AppContext from '../../context/AppContext';
|
||||
import { hexToRgb } from '../../utils';
|
||||
|
||||
const Glalie = () => {
|
||||
const context = useContext(AppContext);
|
||||
const { state } = context;
|
||||
const { data, theme } = state;
|
||||
|
||||
const { r, g, b } = hexToRgb(theme.colors.accent) || {};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
fontFamily: theme.font.family,
|
||||
backgroundColor: theme.colors.background,
|
||||
color: theme.colors.primary,
|
||||
}}
|
||||
>
|
||||
<div className="grid grid-cols-12">
|
||||
<div className="col-span-4 bg-gray-200 py-10 px-5">Hello</div>
|
||||
<div className="col-span-8 py-10 px-5">World</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Glalie;
|
||||
5
src/templates/glalie/index.js
Normal file
5
src/templates/glalie/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import Glalie from './Glalie';
|
||||
import image from './preview.png';
|
||||
|
||||
export const Image = image;
|
||||
export default Glalie;
|
||||
BIN
src/templates/glalie/preview.png
Normal file
BIN
src/templates/glalie/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 136 KiB |
@ -2,6 +2,7 @@ import Onyx, { Image as OnyxPreview } from './onyx';
|
||||
import Pikachu, { Image as PikachuPreview } from './pikachu';
|
||||
import Gengar, { Image as GengarPreview } from './gengar';
|
||||
import Castform, { Image as CastformPreview } from './castform';
|
||||
import Glalie, { Image as GlaliePreview } from './glalie';
|
||||
|
||||
export default [
|
||||
{
|
||||
@ -28,4 +29,10 @@ export default [
|
||||
component: Castform,
|
||||
preview: CastformPreview,
|
||||
},
|
||||
{
|
||||
key: 'glalie',
|
||||
name: 'Glalie',
|
||||
component: Glalie,
|
||||
preview: GlaliePreview,
|
||||
},
|
||||
];
|
||||
|
||||
@ -90,4 +90,25 @@ const moveItemDown = (dispatch, key, value) => {
|
||||
saveData(dispatch);
|
||||
};
|
||||
|
||||
export { move, hexToRgb, copyToClipboard, saveData, addItem, deleteItem, moveItemUp, moveItemDown };
|
||||
const importJson = (event, dispatch) => {
|
||||
const fr = new FileReader();
|
||||
fr.addEventListener('load', () => {
|
||||
const importedObject = JSON.parse(fr.result);
|
||||
console.log(importedObject);
|
||||
dispatch({ type: 'import_data', payload: importedObject });
|
||||
dispatch({ type: 'save_data' });
|
||||
});
|
||||
fr.readAsText(event.target.files[0]);
|
||||
};
|
||||
|
||||
export {
|
||||
move,
|
||||
hexToRgb,
|
||||
copyToClipboard,
|
||||
saveData,
|
||||
addItem,
|
||||
deleteItem,
|
||||
moveItemUp,
|
||||
moveItemDown,
|
||||
importJson,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user