diff --git a/package-lock.json b/package-lock.json index 8f50a732..19b4d175 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 71ca1f9c..8869fd60 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/App/App.js b/src/components/App/App.js index 3ebb99a7..aaec6ea9 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -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 ( -
+
-
-
- {templates.find(x => theme.layout.toLowerCase() === x.key).component()} -
+
+ +
+ {templates.find(x => theme.layout.toLowerCase() === x.key).component()} +
+
+ +
diff --git a/src/components/RightSidebar/tabs/Actions.js b/src/components/RightSidebar/tabs/Actions.js index d1e8a767..83b70960 100644 --- a/src/components/RightSidebar/tabs/Actions.js +++ b/src/components/RightSidebar/tabs/Actions.js @@ -7,55 +7,14 @@ 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 printAsPdf = () => { - pageElement.current.style.display = 'table'; - pageElement.current.style.overflow = 'visible'; - - html2canvas(pageElement.current, { - scale: 5, - useCORS: true, - allowTaint: true, - }).then(canvas => { - const image = canvas.toDataURL('image/jpeg', 1.0); - const doc = new jsPDF('p', 'mm', 'a4'); - const pageWidth = doc.internal.pageSize.getWidth(); - const pageHeight = doc.internal.pageSize.getHeight(); - - const widthRatio = pageWidth / canvas.width; - const heightRatio = pageHeight / canvas.height; - const ratio = widthRatio > heightRatio ? heightRatio : widthRatio; - - const canvasWidth = canvas.width * ratio; - const canvasHeight = canvas.height * ratio; - - const marginX = (pageWidth - canvasWidth) / 2; - const marginY = (pageHeight - canvasHeight) / 2; - - pageElement.current.style.display = 'block'; - pageElement.current.style.overflow = 'scroll'; - - 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))}`; @@ -65,6 +24,39 @@ const ActionsTab = ({ data, theme, dispatch }) => { dlAnchor.click(); }; + const printAsPdf = () => { + panZoomRef.current.autoCenter(1); + panZoomRef.current.reset(); + + setTimeout(() => { + html2canvas(pageRef.current, { + scale: 5, + useCORS: true, + allowTaint: true, + }).then(canvas => { + const image = canvas.toDataURL('image/jpeg', 1.0); + const doc = new jsPDF('p', 'mm', 'a4'); + const pageWidth = doc.internal.pageSize.getWidth(); + const pageHeight = doc.internal.pageSize.getHeight(); + + const widthRatio = pageWidth / canvas.width; + const heightRatio = pageHeight / canvas.height; + const ratio = widthRatio > heightRatio ? heightRatio : widthRatio; + + const canvasWidth = canvas.width * ratio; + const canvasHeight = canvas.height * ratio; + + const marginX = (pageWidth - canvasWidth) / 2; + const marginY = (pageHeight - canvasHeight) / 2; + + panZoomRef.current.autoCenter(0.7); + + doc.addImage(image, 'JPEG', marginX, marginY, canvasWidth, canvasHeight, null, 'SLOW'); + doc.save(`RxResume_${Date.now()}.pdf`); + }); + }, 250); + }; + const loadDemoData = () => { dispatch({ type: 'load_demo_data' }); dispatch({ type: 'save_data' }); @@ -86,7 +78,12 @@ const ActionsTab = ({ data, theme, dispatch }) => {

{t('actions.importExport.body')}

- + importJson(e, dispatch)} + />
diff --git a/src/context/PageContext.js b/src/context/PageContext.js index 8085b6e3..7cd29667 100644 --- a/src/context/PageContext.js +++ b/src/context/PageContext.js @@ -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 ( {children} diff --git a/src/index.css b/src/index.css index a9361314..9fcbc3fa 100644 --- a/src/index.css +++ b/src/index.css @@ -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 { diff --git a/src/shared/PageController.js b/src/shared/PageController.js new file mode 100644 index 00000000..1a1b401b --- /dev/null +++ b/src/shared/PageController.js @@ -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 ( +
+
+
+ zoom_in +
+ +
+ zoom_out +
+ +
+ center_focus_strong +
+ +
|
+ +
+ save +
+ +
+ get_app +
+ +
|
+ +
+ help_outline +
+
+
+ ); +}; + +export default PageController; diff --git a/src/templates/glalie/Glalie.js b/src/templates/glalie/Glalie.js new file mode 100644 index 00000000..9b0afbfb --- /dev/null +++ b/src/templates/glalie/Glalie.js @@ -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 ( +
+
+
Hello
+
World
+
+
+ ); +}; + +export default Glalie; diff --git a/src/templates/glalie/index.js b/src/templates/glalie/index.js new file mode 100644 index 00000000..10e38686 --- /dev/null +++ b/src/templates/glalie/index.js @@ -0,0 +1,5 @@ +import Glalie from './Glalie'; +import image from './preview.png'; + +export const Image = image; +export default Glalie; diff --git a/src/templates/glalie/preview.png b/src/templates/glalie/preview.png new file mode 100644 index 00000000..62bb43e3 Binary files /dev/null and b/src/templates/glalie/preview.png differ diff --git a/src/templates/index.js b/src/templates/index.js index 5b42cad5..825dc875 100644 --- a/src/templates/index.js +++ b/src/templates/index.js @@ -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, + }, ]; diff --git a/src/utils/index.js b/src/utils/index.js index 52b08076..bbdb2e16 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -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, +};