mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-18 10:41:56 +10:00
90% completed, only final touches left
This commit is contained in:
124
src/components/RightSidebar/tabs/Actions.js
Normal file
124
src/components/RightSidebar/tabs/Actions.js
Normal file
@ -0,0 +1,124 @@
|
||||
/* eslint-disable jsx-a11y/anchor-has-content */
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
const ActionsTab = ({ data, theme, dispatch }) => {
|
||||
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 });
|
||||
});
|
||||
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 resetEverything = () => {
|
||||
dispatch({ type: 'reset' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="shadow text-center text-sm p-5">
|
||||
Changes you make to your resume are saved automatically to your browser's local
|
||||
storage. No data gets out, hence your information is completely secure.
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">Import/Export</h6>
|
||||
|
||||
<p className="text-sm">
|
||||
You can import or export your data in JSON format. With this, you can edit and print your
|
||||
resume from any device. Save this file for later use.
|
||||
</p>
|
||||
|
||||
<input ref={fileInputRef} type="file" className="hidden" onChange={importJson} />
|
||||
<a id="downloadAnchor" className="hidden" />
|
||||
|
||||
<div className="mt-4 grid grid-cols-2 col-gap-6">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => fileInputRef.current.click()}
|
||||
className="bg-gray-600 hover:bg-gray-700 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">publish</i>
|
||||
<span className="text-sm">Import</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={exportToJson}
|
||||
className="bg-gray-600 hover:bg-gray-700 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">get_app</i>
|
||||
<span className="text-sm">Export</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">Print Your Resume</h6>
|
||||
|
||||
<div className="text-sm">
|
||||
You can simply press <pre className="inline font-bold">Cmd/Ctrl + P</pre> at any time
|
||||
while you're in the app to print your resume, but here's a fancy button to do
|
||||
the same thing, just 'cause.
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => window.print()}
|
||||
className="mt-4 w-1/2 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">
|
||||
<i className="material-icons mr-2 font-bold text-base">print</i>
|
||||
<span className="text-sm">Print</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="shadow text-center p-5">
|
||||
<h6 className="font-bold text-sm mb-2">Reset Everything!</h6>
|
||||
|
||||
<div className="text-sm">
|
||||
This action will reset all your data and remove backups made to your browser's local
|
||||
storage as well, so please make sure you have exported your information before you reset
|
||||
everything.
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={resetEverything}
|
||||
className="mt-4 w-1/2 bg-red-600 hover:bg-red-700 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">refresh</i>
|
||||
<span className="text-sm">Reset</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionsTab;
|
||||
108
src/components/RightSidebar/tabs/Colors.js
Normal file
108
src/components/RightSidebar/tabs/Colors.js
Normal file
@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { copyToClipboard } from '../../../utils';
|
||||
|
||||
const colorOptions = [
|
||||
'#f44336',
|
||||
'#E91E63',
|
||||
'#9C27B0',
|
||||
'#673AB7',
|
||||
'#3F51B5',
|
||||
'#2196F3',
|
||||
'#03A9F4',
|
||||
'#00BCD4',
|
||||
'#009688',
|
||||
'#4CAF50',
|
||||
'#8BC34A',
|
||||
'#CDDC39',
|
||||
'#FFEB3B',
|
||||
'#FFC107',
|
||||
'#FF9800',
|
||||
'#FF5722',
|
||||
'#795548',
|
||||
'#9E9E9E',
|
||||
'#607D8B',
|
||||
'#FAFAFA',
|
||||
'#212121',
|
||||
'#263238',
|
||||
];
|
||||
|
||||
const ColorsTab = ({ theme, onChange }) => {
|
||||
const copyColorToClipboard = color => {
|
||||
copyToClipboard(color);
|
||||
toast(`Color ${color} copied to clipboard.`, {
|
||||
bodyClassName: 'text-center text-gray-800 py-2',
|
||||
});
|
||||
onChange('theme.colors.accent', color);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="uppercase tracking-wide text-gray-600 text-xs font-semibold mb-4">
|
||||
Color Options
|
||||
</div>
|
||||
<div className="mb-6 grid grid-cols-8 col-gap-2 row-gap-3">
|
||||
{colorOptions.map(color => (
|
||||
<div
|
||||
key={color}
|
||||
className="cursor-pointer rounded-full border border-gray-200 h-6 w-6 hover:opacity-75"
|
||||
style={{ backgroundColor: color }}
|
||||
onClick={() => copyColorToClipboard(color)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
<div className="my-6 grid grid-cols-6 items-end">
|
||||
<div
|
||||
className="rounded-full w-8 h-8 mb-2 border-2"
|
||||
style={{ backgroundColor: theme.colors.background }}
|
||||
/>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
disabled
|
||||
label="Background Color"
|
||||
placeholder="#FFFFFF"
|
||||
value={theme.colors.background}
|
||||
onChange={v => onChange('theme.colors.background', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-6 grid grid-cols-6 items-end">
|
||||
<div
|
||||
className="rounded-full w-8 h-8 mb-2 border-2"
|
||||
style={{ backgroundColor: theme.colors.primary }}
|
||||
/>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
label="Primary Color"
|
||||
placeholder="#FFFFFF"
|
||||
value={theme.colors.primary}
|
||||
onChange={v => onChange('theme.colors.primary', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-6 grid grid-cols-6 items-end">
|
||||
<div
|
||||
className="rounded-full w-8 h-8 mb-2 border-2"
|
||||
style={{ backgroundColor: theme.colors.accent }}
|
||||
/>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
label="Accent Color"
|
||||
placeholder="#FFFFFF"
|
||||
value={theme.colors.accent}
|
||||
onChange={v => onChange('theme.colors.accent', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorsTab;
|
||||
35
src/components/RightSidebar/tabs/Fonts.js
Normal file
35
src/components/RightSidebar/tabs/Fonts.js
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
const fontOptions = [
|
||||
'Lato',
|
||||
'Merriweather',
|
||||
'Montserrat',
|
||||
'Open Sans',
|
||||
'Raleway',
|
||||
'Roboto',
|
||||
'Rubik',
|
||||
'Source Sans Pro',
|
||||
'Titillium Web',
|
||||
'Ubuntu',
|
||||
];
|
||||
|
||||
const FontsTab = ({ theme, onChange }) => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
{fontOptions.map(x => (
|
||||
<div
|
||||
key={x}
|
||||
style={{ fontFamily: x }}
|
||||
onClick={() => onChange('theme.font.family', x)}
|
||||
className={`w-full rounded border py-4 shadow text-xl text-center ${
|
||||
theme.font.family === x ? 'border-gray-500' : 'border-transparent'
|
||||
} hover:border-gray-400 cursor-pointer`}
|
||||
>
|
||||
{x}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FontsTab;
|
||||
31
src/components/RightSidebar/tabs/Layout.js
Normal file
31
src/components/RightSidebar/tabs/Layout.js
Normal file
@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
import Onyx, { Image as OnyxPreview } from '../../../templates/onyx';
|
||||
|
||||
const layouts = [
|
||||
{
|
||||
name: 'Onyx',
|
||||
component: Onyx,
|
||||
preview: OnyxPreview,
|
||||
},
|
||||
];
|
||||
|
||||
const LayoutTab = ({ theme }) => {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
{layouts.map(x => (
|
||||
<div key={x.name} className="text-center">
|
||||
<img
|
||||
className={`rounded cursor-pointer object-cover border shadow hover:shadow-md ${
|
||||
theme.layout === x.name ? 'border-gray-500' : 'border-transparent '
|
||||
} hover:border-gray-400 cursor-pointer`}
|
||||
src={x.preview}
|
||||
alt={x.name}
|
||||
/>
|
||||
<p className="mt-1 text-sm font-medium">{x.name}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LayoutTab;
|
||||
Reference in New Issue
Block a user