introduce print dialog to add more options in exporting resume

This commit is contained in:
Amruth Pillai
2020-04-04 23:19:46 +05:30
parent be84d2a5e4
commit a6efc3c328
14 changed files with 327 additions and 119 deletions

View File

@ -10,6 +10,7 @@ import RightSidebar from '../RightSidebar/RightSidebar';
import templates from '../../templates';
import PageController from '../../shared/PageController';
import PrintDialog from '../../shared/PrintDialog';
const App = () => {
const pageRef = useRef(null);
@ -36,7 +37,7 @@ const App = () => {
<div className="h-screen grid grid-cols-5 items-center">
<LeftSidebar />
<div className="relative z-0 h-screen overflow-hidden col-span-3 flex justify-center items-center">
<div className="relative z-10 h-screen overflow-hidden col-span-3 flex justify-center items-center">
<PanZoom
ref={panZoomRef}
minZoom="0.4"
@ -48,7 +49,7 @@ const App = () => {
style={{ outline: 'none' }}
>
<div id="page" ref={pageRef} className="shadow-2xl break-words">
{templates.find(x => theme.layout.toLowerCase() === x.key).component()}
{templates.find((x) => theme.layout.toLowerCase() === x.key).component()}
</div>
</PanZoom>
@ -56,6 +57,8 @@ const App = () => {
</div>
<RightSidebar />
<PrintDialog />
</div>
</Suspense>
);

View File

@ -1,15 +1,16 @@
/* eslint-disable new-cap */
/* eslint-disable jsx-a11y/anchor-has-content */
/* eslint-disable jsx-a11y/anchor-is-valid */
import React, { useRef, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import PageContext from '../../../context/PageContext';
import { importJson, saveAsPdf } from '../../../utils';
import { importJson } from '../../../utils';
const ActionsTab = ({ data, theme, dispatch }) => {
const pageContext = useContext(PageContext);
const { pageRef, panZoomRef } = pageContext;
const { setPrintDialogOpen } = pageContext;
const { t } = useTranslation('rightSidebar');
const fileInputRef = useRef(null);
@ -47,7 +48,7 @@ const ActionsTab = ({ data, theme, dispatch }) => {
ref={fileInputRef}
type="file"
className="hidden"
onChange={e => importJson(e, dispatch)}
onChange={(e) => importJson(e, dispatch)}
/>
<a id="downloadAnchor" className="hidden" />
@ -84,7 +85,7 @@ const ActionsTab = ({ data, theme, dispatch }) => {
<button
type="button"
onClick={() => saveAsPdf(pageRef, panZoomRef)}
onClick={() => setPrintDialogOpen(true)}
className="mt-4 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium py-2 px-5 rounded"
>
<div className="flex justify-center items-center">

View File

@ -97,7 +97,7 @@ const reducer = (state, { type, payload }) => {
return set({ ...state }, `data.${payload.key}.items`, items);
case 'delete_item':
items = get({ ...state }, `data.${payload.key}.items`, []);
remove(items, x => x === payload.value);
remove(items, (x) => x === payload.value);
return set({ ...state }, `data.${payload.key}.items`, items);
case 'move_item_up':
items = get({ ...state }, `data.${payload.key}.items`, []);

View File

@ -4,8 +4,9 @@ const PageContext = React.createContext(null);
const { Provider } = PageContext;
const StateProvider = ({ children }) => {
const [panZoomRef, setPanZoomRef] = useState(null);
const [pageRef, setPageRef] = useState(null);
const [panZoomRef, setPanZoomRef] = useState(null);
const [isPrintDialogOpen, setPrintDialogOpen] = useState(false);
return (
<Provider
@ -14,6 +15,8 @@ const StateProvider = ({ children }) => {
setPageRef,
panZoomRef,
setPanZoomRef,
isPrintDialogOpen,
setPrintDialogOpen,
}}
>
{children}

View File

@ -16,5 +16,23 @@
"label": "Add"
}
},
"printDialog": {
"heading": "Download your Resume",
"quality": {
"label": "Quality"
},
"printType": {
"label": "Type",
"types": {
"unconstrained": "Unconstrained",
"fitInA4": "Fit in A4",
"multiPageA4": "Multi-Page A4"
}
},
"buttons": {
"cancel": "Cancel",
"saveAsPdf": "Save as PDF"
}
},
"markdownHelpText": "You can use <1>GitHub Flavored Markdown</1> to style this section of the text."
}

View File

@ -32,6 +32,35 @@ ul li {
padding: 0;
}
input[type='range']::-moz-range-thumb {
width: 20px;
height: 20px;
appearance: none;
cursor: ew-resize;
background: #fff;
border: none;
box-shadow: -405px 0 0 400px #605e5c;
border-radius: 50%;
}
input[type='range']::-webkit-slider-thumb {
width: 20px;
height: 20px;
appearance: none;
cursor: ew-resize;
background: #fff;
border: none;
box-shadow: -405px 0 0 400px #605e5c;
border-radius: 50%;
}
.centered {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
@media screen {
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {

View File

@ -1,10 +1,10 @@
import React, { useContext } from 'react';
import PageContext from '../context/PageContext';
import { saveAsPdf } from '../utils';
const PageController = () => {
const pageContext = useContext(PageContext);
const { pageRef, panZoomRef } = pageContext;
const { panZoomRef, setPrintDialogOpen } = pageContext;
const zoomIn = () => panZoomRef.current.zoomIn(2);
const zoomOut = () => panZoomRef.current.zoomOut(2);
@ -35,7 +35,7 @@ const PageController = () => {
<div
className="p-3 hover:bg-gray-200 cursor-pointer flex"
onClick={() => saveAsPdf(pageRef, panZoomRef)}
onClick={() => setPrintDialogOpen(true)}
>
<i className="material-icons">save</i>
</div>

103
src/shared/PrintDialog.js Normal file
View File

@ -0,0 +1,103 @@
import React, { useState, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import PageContext from '../context/PageContext';
import Dropdown from './Dropdown';
import { saveAsPdf, saveAsMultiPagePdf } from '../utils';
const PrintDialog = () => {
const { t } = useTranslation();
const pageContext = useContext(PageContext);
const { pageRef, panZoomRef, isPrintDialogOpen, setPrintDialogOpen } = pageContext;
const printTypes = [
{ key: 'unconstrained', value: `${t('printDialog.printType.types.unconstrained')}` },
{ key: 'fitInA4', value: `${t('printDialog.printType.types.fitInA4')}` },
{ key: 'multiPageA4', value: `${t('printDialog.printType.types.multiPageA4')}` },
];
const [quality, setQuality] = useState(80);
const [type, setType] = useState(printTypes[0].key);
return (
<div
className={`absolute inset-0 transition-all duration-200 ease-in-out ${
isPrintDialogOpen ? 'opacity-100 z-20' : 'opacity-0 z-0'
}`}
style={{ backgroundColor: 'rgba(0, 0, 0, 0.25)' }}
onClick={() => {
setPrintDialogOpen(false);
}}
>
<div
className="centered py-8 px-12 bg-white shadow-xl rounded w-full md:w-1/3"
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
}}
>
<h5 className="mb-6 text-lg font-bold">{t('printDialog.heading')}</h5>
<h6 className="mb-1 text-sm font-medium">{t('printDialog.quality.label')}</h6>
<div className="flex items-center">
<input
type="range"
className="w-full h-4 my-2 rounded-full overflow-hidden appearance-none focus:outline-none bg-gray-400"
value={quality}
onChange={(e) => setQuality(e.target.value)}
min="40"
max="100"
step="5"
/>
<h6 className="font-medium pl-5">{quality}%</h6>
</div>
<h6 className="mt-4 mb-2 text-sm font-medium">{t('printDialog.printType.label')}</h6>
<Dropdown
value={type}
options={printTypes}
onChange={setType}
optionItem={(x) => (
<option key={x.key} value={x.key}>
{x.value}
</option>
)}
/>
<div className="flex justify-between">
<button
type="button"
onClick={() => {
setPrintDialogOpen(false);
}}
className="mt-6 border border-red-600 text-red-600 hover:bg-red-600 hover:text-white text-sm font-medium py-2 px-5 rounded"
>
<div className="flex justify-center items-center">
<i className="material-icons mr-2 font-bold text-base">close</i>
<span className="text-sm">{t('printDialog.buttons.cancel')}</span>
</div>
</button>
<button
type="button"
onClick={async () => {
await (type === 'multiPageA4'
? saveAsMultiPagePdf(pageRef, panZoomRef, quality)
: saveAsPdf(pageRef, panZoomRef, quality, type));
setPrintDialogOpen(false);
}}
className="mt-6 border border-gray-700 text-gray-700 hover:bg-gray-700 hover:text-white text-sm font-medium py-2 px-5 rounded"
>
<div className="flex justify-center items-center">
<i className="material-icons mr-2 font-bold text-base">save</i>
<span className="text-sm">{t('printDialog.buttons.saveAsPdf')}</span>
</div>
</button>
</div>
</div>
</div>
);
};
export default PrintDialog;

View File

@ -78,7 +78,7 @@ const Castform = () => {
</div>
);
const SkillItem = x => (
const SkillItem = (x) => (
<li key={x} className="text-sm my-2">
{x}
</li>
@ -96,7 +96,7 @@ const Castform = () => {
const Objective = () =>
data.objective && data.objective.enable && <p className="m-5 text-sm">{data.objective.body}</p>;
const WorkItem = x => (
const WorkItem = (x) => (
<div key={x.title} className="my-3 px-5">
<div className="flex justify-between">
<div>
@ -116,11 +116,11 @@ const Castform = () => {
data.work.enable && (
<div>
<Heading light title={data.work.heading} />
{data.work.items.filter(x => x.enable).map(WorkItem)}
{data.work.items.filter((x) => x.enable).map(WorkItem)}
</div>
);
const ReferenceItem = x => (
const ReferenceItem = (x) => (
<div key={x.id} className="flex flex-col">
<h6 className="text-sm font-medium">{x.name}</h6>
<span className="text-xs">{x.position}</span>
@ -136,12 +136,12 @@ const Castform = () => {
<div>
<Heading light title={data.references.heading} />
<div className="grid grid-cols-2 gap-6 px-5">
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
</div>
</div>
);
const LanguageItem = x => (
const LanguageItem = (x) => (
<div key={x.id} className="flex flex-col my-2">
<h6 className="text-sm font-medium mb-1">{x.key}</h6>
<div className="relative h-5">
@ -168,12 +168,12 @@ const Castform = () => {
<div>
<Heading title={data.languages.heading} />
<div className="px-5 mb-6">
{data.languages.items.filter(x => x.enable).map(LanguageItem)}
{data.languages.items.filter((x) => x.enable).map(LanguageItem)}
</div>
</div>
);
const EducationItem = x => (
const EducationItem = (x) => (
<div key={x.name} className="my-3 px-5">
<div className="flex justify-between">
<div>
@ -196,11 +196,11 @@ const Castform = () => {
data.education.enable && (
<div>
<Heading light title={data.education.heading} />
{data.education.items.filter(x => x.enable).map(EducationItem)}
{data.education.items.filter((x) => x.enable).map(EducationItem)}
</div>
);
const AwardItem = x => (
const AwardItem = (x) => (
<div key={x.title} className="my-3 px-5">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -213,11 +213,11 @@ const Castform = () => {
data.awards.enable && (
<div>
<Heading light title={data.awards.heading} />
{data.awards.items.filter(x => x.enable).map(AwardItem)}
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
</div>
);
const CertificationItem = x => (
const CertificationItem = (x) => (
<div key={x.title} className="my-3 px-5">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -230,11 +230,11 @@ const Castform = () => {
data.certifications.enable && (
<div>
<Heading title={data.certifications.heading} />
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
{data.certifications.items.filter((x) => x.enable).map(CertificationItem)}
</div>
);
const ExtraItem = x => (
const ExtraItem = (x) => (
<div key={x.id} className="px-5 my-2">
<h6 className="text-xs font-bold">{x.key}</h6>
<div className="text-sm">{x.value}</div>
@ -246,7 +246,7 @@ const Castform = () => {
data.extras.enable && (
<div>
<Heading title={data.extras.heading} />
{data.extras.items.filter(x => x.enable).map(ExtraItem)}
{data.extras.items.filter((x) => x.enable).map(ExtraItem)}
</div>
);
@ -260,7 +260,7 @@ const Castform = () => {
>
<div className="grid grid-cols-12">
<div
className="col-span-4 rounded"
className="col-span-4"
style={{
color: theme.colors.background,
backgroundColor: theme.colors.accent,

View File

@ -63,7 +63,7 @@ const Gengar = () => {
</div>
);
const SkillItem = x => (
const SkillItem = (x) => (
<li key={x} className="text-sm py-1">
{x}
</li>
@ -78,7 +78,7 @@ const Gengar = () => {
</div>
);
const EducationItem = x => (
const EducationItem = (x) => (
<div key={x.name} className="mb-3">
<div className="flex justify-between items-center">
<div>
@ -109,11 +109,11 @@ const Gengar = () => {
data.education.enable && (
<div className="mb-6">
<Heading title={data.education.heading} />
{data.education.items.filter(x => x.enable).map(EducationItem)}
{data.education.items.filter((x) => x.enable).map(EducationItem)}
</div>
);
const CertificationItem = x => (
const CertificationItem = (x) => (
<div key={x.title} className="mb-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -126,11 +126,11 @@ const Gengar = () => {
data.certifications.enable && (
<div className="mb-6">
<Heading title={data.certifications.heading} />
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
{data.certifications.items.filter((x) => x.enable).map(CertificationItem)}
</div>
);
const AwardItem = x => (
const AwardItem = (x) => (
<div key={x.title} className="mb-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -143,11 +143,11 @@ const Gengar = () => {
data.awards.enable && (
<div className="mb-6">
<Heading title={data.awards.heading} />
{data.awards.items.filter(x => x.enable).map(AwardItem)}
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
</div>
);
const ReferenceItem = x => (
const ReferenceItem = (x) => (
<div key={x.id} className="flex flex-col">
<h6 className="text-sm font-medium">{x.name}</h6>
<span className="text-xs">{x.position}</span>
@ -163,12 +163,12 @@ const Gengar = () => {
<div>
<Heading title={data.references.heading} />
<div className="grid grid-cols-2 gap-6">
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
</div>
</div>
);
const WorkItem = x => (
const WorkItem = (x) => (
<div key={x.title} className="mb-3">
<div className="flex justify-between items-center">
<div>
@ -188,11 +188,11 @@ const Gengar = () => {
data.work.enable && (
<div className="mb-6">
<Heading title={data.work.heading} />
{data.work.items.filter(x => x.enable).map(WorkItem)}
{data.work.items.filter((x) => x.enable).map(WorkItem)}
</div>
);
const LanguageItem = x => (
const LanguageItem = (x) => (
<div key={x.id} className="grid grid-cols-2 items-center py-2">
<h6 className="text-sm font-medium">{x.key}</h6>
<div className="flex">
@ -210,11 +210,11 @@ const Gengar = () => {
data.languages.enable && (
<div>
<Heading title={data.languages.heading} />
<div className="mb-6">{data.languages.items.filter(x => x.enable).map(LanguageItem)}</div>
<div className="mb-6">{data.languages.items.filter((x) => x.enable).map(LanguageItem)}</div>
</div>
);
const ExtraItem = x => (
const ExtraItem = (x) => (
<div key={x.id} className="text-sm my-1">
<h6 className="text-xs font-bold">{x.key}</h6>
<h6>{x.value}</h6>
@ -227,7 +227,7 @@ const Gengar = () => {
<div>
<Heading title={data.extras.heading} />
<div className="grid grid-cols-2">
{data.extras.items.filter(x => x.enable).map(ExtraItem)}
{data.extras.items.filter((x) => x.enable).map(ExtraItem)}
</div>
</div>
);

View File

@ -95,7 +95,7 @@ const Glalie = () => {
</div>
);
const WorkItem = x => (
const WorkItem = (x) => (
<div key={x.title} className="mt-3">
<div className="flex justify-between">
<div>
@ -114,11 +114,11 @@ const Glalie = () => {
data.work.enable && (
<div>
<Heading title={data.work.heading} />
{data.work.items.filter(x => x.enable).map(WorkItem)}
{data.work.items.filter((x) => x.enable).map(WorkItem)}
</div>
);
const EducationItem = x => (
const EducationItem = (x) => (
<div key={x.name} className="mt-3">
<div>
<h6 className="font-semibold text-xs">{x.name}</h6>
@ -137,12 +137,12 @@ const Glalie = () => {
<div>
<Heading title={data.education.heading} />
<div className="grid grid-cols-2 gap-4">
{data.education.items.filter(x => x.enable).map(EducationItem)}
{data.education.items.filter((x) => x.enable).map(EducationItem)}
</div>
</div>
);
const AwardItem = x => (
const AwardItem = (x) => (
<div key={x.title} className="mt-3 text-left">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -155,11 +155,11 @@ const Glalie = () => {
data.awards.enable && (
<div>
<Heading title={data.awards.heading} />
{data.awards.items.filter(x => x.enable).map(AwardItem)}
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
</div>
);
const CertificationItem = x => (
const CertificationItem = (x) => (
<div key={x.title} className="mt-3 text-left">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -172,11 +172,11 @@ const Glalie = () => {
data.certifications.enable && (
<div>
<Heading title={data.certifications.heading} />
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
{data.certifications.items.filter((x) => x.enable).map(CertificationItem)}
</div>
);
const SkillItem = x => (
const SkillItem = (x) => (
<li key={x} className="text-xs font-medium">
{x}
</li>
@ -191,7 +191,7 @@ const Glalie = () => {
</div>
);
const LanguageItem = x => (
const LanguageItem = (x) => (
<div key={x.id} className="grid grid-cols-2 items-center py-2">
<h6 className="text-xs font-medium text-left">{x.key}</h6>
<div className="flex">
@ -209,11 +209,13 @@ const Glalie = () => {
data.languages.enable && (
<div>
<Heading title={data.languages.heading} />
<div className="w-3/4">{data.languages.items.filter(x => x.enable).map(LanguageItem)}</div>
<div className="w-3/4">
{data.languages.items.filter((x) => x.enable).map(LanguageItem)}
</div>
</div>
);
const ReferenceItem = x => (
const ReferenceItem = (x) => (
<div key={x.id} className="flex flex-col">
<h6 className="text-sm font-medium">{x.name}</h6>
<span className="text-xs">{x.position}</span>
@ -229,12 +231,12 @@ const Glalie = () => {
<div>
<Heading title={data.references.heading} />
<div className="grid grid-cols-3 gap-8">
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
</div>
</div>
);
const ExtraItem = x => (
const ExtraItem = (x) => (
<tr key={x.id}>
<td className="border font-medium px-4 py-2 text-xs">{x.key}</td>
<td className="border px-4 py-2 text-xs">{x.value}</td>
@ -247,7 +249,7 @@ const Glalie = () => {
<div>
<Heading title={data.extras.heading} />
<table className="mt-4 w-2/3 table-auto">
<tbody>{data.extras.items.filter(x => x.enable).map(ExtraItem)}</tbody>
<tbody>{data.extras.items.filter((x) => x.enable).map(ExtraItem)}</tbody>
</table>
</div>
);

View File

@ -60,7 +60,7 @@ const Onyx = () => {
</div>
);
const WorkItem = x => (
const WorkItem = (x) => (
<div key={x.title} className="mt-3">
<div className="flex justify-between">
<div>
@ -80,11 +80,11 @@ const Onyx = () => {
data.work.enable && (
<div>
<Heading title={data.work.heading} />
{data.work.items.filter(x => x.enable).map(WorkItem)}
{data.work.items.filter((x) => x.enable).map(WorkItem)}
</div>
);
const EducationItem = x => (
const EducationItem = (x) => (
<div key={x.name} className="mt-3">
<div className="flex justify-between">
<div>
@ -107,11 +107,11 @@ const Onyx = () => {
data.education.enable && (
<div>
<Heading title={data.education.heading} />
{data.education.items.filter(x => x.enable).map(EducationItem)}
{data.education.items.filter((x) => x.enable).map(EducationItem)}
</div>
);
const AwardItem = x => (
const AwardItem = (x) => (
<div key={x.title} className="mt-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -124,11 +124,11 @@ const Onyx = () => {
data.awards.enable && (
<div>
<Heading title={data.awards.heading} />
{data.awards.items.filter(x => x.enable).map(AwardItem)}
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
</div>
);
const CertificationItem = x => (
const CertificationItem = (x) => (
<div key={x.title} className="mt-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -141,11 +141,11 @@ const Onyx = () => {
data.certifications.enable && (
<div>
<Heading title={data.certifications.heading} />
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
{data.certifications.items.filter((x) => x.enable).map(CertificationItem)}
</div>
);
const SkillItem = x => (
const SkillItem = (x) => (
<span
key={x}
className="text-xs rounded-full px-3 py-1 font-medium my-2 mr-2"
@ -167,7 +167,7 @@ const Onyx = () => {
</div>
);
const LanguageItem = x => (
const LanguageItem = (x) => (
<div key={x.id} className="grid grid-cols-2 items-center py-2">
<h6 className="text-sm font-medium">{x.key}</h6>
<div className="flex">
@ -185,11 +185,13 @@ const Onyx = () => {
data.languages.enable && (
<div>
<Heading title={data.languages.heading} />
<div className="w-3/4">{data.languages.items.filter(x => x.enable).map(LanguageItem)}</div>
<div className="w-3/4">
{data.languages.items.filter((x) => x.enable).map(LanguageItem)}
</div>
</div>
);
const ReferenceItem = x => (
const ReferenceItem = (x) => (
<div key={x.id} className="flex flex-col">
<h6 className="text-sm font-medium">{x.name}</h6>
<span className="text-xs">{x.position}</span>
@ -205,12 +207,12 @@ const Onyx = () => {
<div>
<Heading title={data.references.heading} />
<div className="grid grid-cols-3 gap-6">
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
</div>
</div>
);
const ExtraItem = x => (
const ExtraItem = (x) => (
<tr key={x.id}>
<td className="border font-medium px-4 py-2 text-sm">{x.key}</td>
<td className="border px-4 py-2 text-sm">{x.value}</td>
@ -223,7 +225,7 @@ const Onyx = () => {
<div>
<Heading title={data.extras.heading} />
<table className="w-2/3 table-auto">
<tbody>{data.extras.items.filter(x => x.enable).map(ExtraItem)}</tbody>
<tbody>{data.extras.items.filter((x) => x.enable).map(ExtraItem)}</tbody>
</table>
</div>
);

View File

@ -58,7 +58,7 @@ const Pikachu = () => {
</div>
);
const SkillItem = x => (
const SkillItem = (x) => (
<span
key={x}
className="leading-none rounded-lg text-sm font-medium bg-gray-300 py-3 my-1 px-4"
@ -76,7 +76,7 @@ const Pikachu = () => {
</div>
);
const ReferenceItem = x => (
const ReferenceItem = (x) => (
<div key={x.id} className="flex flex-col">
<h6 className="text-sm font-medium">{x.name}</h6>
<span className="text-xs">{x.position}</span>
@ -92,12 +92,12 @@ const Pikachu = () => {
<div>
<Heading title={data.references.heading} />
<div className="grid grid-cols-2 gap-6">
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
{data.references.items.filter((x) => x.enable).map(ReferenceItem)}
</div>
</div>
);
const LanguageItem = x => (
const LanguageItem = (x) => (
<div key={x.id} className="grid grid-cols-2 items-center py-2">
<h6 className="text-sm font-medium">{x.key}</h6>
<div className="flex">
@ -115,11 +115,11 @@ const Pikachu = () => {
data.languages.enable && (
<div>
<Heading title={data.languages.heading} />
<div className="mb-6">{data.languages.items.filter(x => x.enable).map(LanguageItem)}</div>
<div className="mb-6">{data.languages.items.filter((x) => x.enable).map(LanguageItem)}</div>
</div>
);
const ExtraItem = x => (
const ExtraItem = (x) => (
<div key={x.id} className="text-sm my-1">
<h6 className="text-xs font-bold">{x.key}</h6>
<h6 className="">{x.value}</h6>
@ -132,12 +132,12 @@ const Pikachu = () => {
<div>
<Heading title={data.extras.heading} />
<div className="flex flex-col mb-6">
{data.extras.items.filter(x => x.enable).map(ExtraItem)}
{data.extras.items.filter((x) => x.enable).map(ExtraItem)}
</div>
</div>
);
const WorkItem = x => (
const WorkItem = (x) => (
<div key={x.title} className="mb-3">
<div className="flex justify-between items-center">
<div>
@ -158,12 +158,12 @@ const Pikachu = () => {
<div>
<Heading title={data.work.heading} />
<div className="flex flex-col mb-4">
{data.work.items.filter(x => x.enable).map(WorkItem)}
{data.work.items.filter((x) => x.enable).map(WorkItem)}
</div>
</div>
);
const EducationItem = x => (
const EducationItem = (x) => (
<div key={x.name} className="mb-3">
<div className="flex justify-between items-center">
<div>
@ -189,12 +189,12 @@ const Pikachu = () => {
<div>
<Heading title={data.education.heading} />
<div className="flex flex-col mb-4">
{data.education.items.filter(x => x.enable).map(EducationItem)}
{data.education.items.filter((x) => x.enable).map(EducationItem)}
</div>
</div>
);
const AwardItem = x => (
const AwardItem = (x) => (
<div key={x.title} className="mb-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -208,12 +208,12 @@ const Pikachu = () => {
<div>
<Heading title={data.awards.heading} />
<div className="flex flex-col mb-2">
{data.awards.items.filter(x => x.enable).map(AwardItem)}
{data.awards.items.filter((x) => x.enable).map(AwardItem)}
</div>
</div>
);
const CertificationItem = x => (
const CertificationItem = (x) => (
<div key={x.title} className="mb-3">
<h6 className="font-semibold">{x.title}</h6>
<p className="text-xs">{x.subtitle}</p>
@ -227,7 +227,7 @@ const Pikachu = () => {
<div>
<Heading title={data.certifications.heading} />
<div className="flex flex-col mb-2">
{data.certifications.items.filter(x => x.enable).map(CertificationItem)}
{data.certifications.items.filter((x) => x.enable).map(CertificationItem)}
</div>
</div>
);

View File

@ -10,7 +10,7 @@ const move = (array, element, delta) => {
array.splice(indexes[0], 2, array[indexes[1]], array[indexes[0]]);
};
const hexToRgb = hex => {
const hexToRgb = (hex) => {
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
@ -23,7 +23,7 @@ const hexToRgb = hex => {
: null;
};
const copyToClipboard = text => {
const copyToClipboard = (text) => {
const textArea = document.createElement('textarea');
textArea.style.position = 'fixed';
textArea.style.top = 0;
@ -44,7 +44,7 @@ const copyToClipboard = text => {
return successful;
};
const saveData = dispatch => dispatch({ type: 'save_data' });
const saveData = (dispatch) => dispatch({ type: 'save_data' });
const addItem = (dispatch, key, value) => {
dispatch({
@ -104,42 +104,88 @@ const importJson = (event, dispatch) => {
fr.readAsText(event.target.files[0]);
};
const saveAsPdf = (pageRef, panZoomRef) => {
panZoomRef.current.autoCenter(1);
panZoomRef.current.reset();
const saveAsPdf = (pageRef, panZoomRef, quality, type) =>
new Promise((resolve) => {
panZoomRef.current.autoCenter(1);
panZoomRef.current.reset();
setTimeout(() => {
html2canvas(pageRef.current, {
scale: 6,
useCORS: true,
allowTaint: true,
}).then(canvas => {
const image = canvas.toDataURL('image/jpeg', 1.0);
const doc = new jsPDF({
orientation: 'portrait',
unit: 'px',
format: [canvas.width, canvas.height],
setTimeout(() => {
html2canvas(pageRef.current, {
scale: 5,
useCORS: true,
allowTaint: true,
}).then((canvas) => {
const image = canvas.toDataURL('image/jpeg', quality / 100);
const doc = new jsPDF({
orientation: 'portrait',
unit: 'px',
format: type === 'unconstrained' ? [canvas.width, canvas.height] : '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;
let marginX = 0;
let marginY = 0;
if (type !== 'unconstrained') {
marginX = (pageWidth - canvasWidth) / 2;
marginY = (pageHeight - canvasHeight) / 2;
}
doc.addImage(image, 'JPEG', marginX, marginY, canvasWidth, canvasHeight, null, 'SLOW');
doc.save(`RxResume_${Date.now()}.pdf`);
resolve();
});
}, 250);
});
const pageWidth = doc.internal.pageSize.getWidth();
const pageHeight = doc.internal.pageSize.getHeight();
const saveAsMultiPagePdf = (pageRef, panZoomRef, quality) =>
new Promise((resolve) => {
panZoomRef.current.autoCenter(1);
panZoomRef.current.reset();
const widthRatio = pageWidth / canvas.width;
const heightRatio = pageHeight / canvas.height;
const ratio = widthRatio > heightRatio ? heightRatio : widthRatio;
setTimeout(() => {
html2canvas(pageRef.current, {
scale: 5,
useCORS: true,
allowTaint: true,
}).then((canvas) => {
const image = canvas.toDataURL('image/jpeg', quality / 100);
const doc = new jsPDF({
orientation: 'portrait',
unit: 'px',
format: 'a4',
});
const canvasWidth = canvas.width * ratio;
const canvasHeight = canvas.height * ratio;
// const marginX = (pageWidth - canvasWidth) / 2;
// const marginY = (pageHeight - canvasHeight) / 2;
const pageHeight = doc.internal.pageSize.getHeight();
const canvasWidth = doc.internal.pageSize.getWidth();
const canvasHeight = (canvas.height * canvasWidth) / canvas.width;
let marginTop = 0;
let heightLeft = canvasHeight;
panZoomRef.current.autoCenter(0.7);
doc.addImage(image, 'JPEG', 0, marginTop, canvasWidth, canvasHeight);
heightLeft -= pageHeight;
doc.addImage(image, 'JPEG', 0, 0, canvasWidth, canvasHeight, null, 'SLOW');
doc.save(`RxResume_${Date.now()}.pdf`);
});
}, 200);
};
while (heightLeft >= 0) {
marginTop = heightLeft - canvasHeight;
doc.addPage();
doc.addImage(image, 'JPEG', 0, marginTop, canvasWidth, canvasHeight);
heightLeft -= pageHeight;
}
doc.save(`RxResume_${Date.now()}.pdf`);
resolve();
});
}, 250);
});
export {
move,
@ -152,4 +198,5 @@ export {
moveItemDown,
importJson,
saveAsPdf,
saveAsMultiPagePdf,
};