mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-13 06:24:54 +10:00
Merge branch 'develop' into l10n_develop
This commit is contained in:
Vendored
+79
@@ -0,0 +1,79 @@
|
||||
@-webkit-keyframes slideInLeft {
|
||||
from {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInLeft {
|
||||
from {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.slideInLeft {
|
||||
-webkit-animation-name: slideInLeft;
|
||||
animation-name: slideInLeft;
|
||||
}
|
||||
|
||||
@-webkit-keyframes slideInRight {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.slideInRight {
|
||||
-webkit-animation-name: slideInRight;
|
||||
animation-name: slideInRight;
|
||||
}
|
||||
|
||||
.animated {
|
||||
-webkit-animation-duration: 1s;
|
||||
animation-duration: 1s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
@media (print), (prefers-reduced-motion: reduce) {
|
||||
.animated {
|
||||
-webkit-animation-duration: 1ms !important;
|
||||
animation-duration: 1ms !important;
|
||||
-webkit-transition-duration: 1ms !important;
|
||||
transition-duration: 1ms !important;
|
||||
-webkit-animation-iteration-count: 1 !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/* Material Icons */
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Material Icons'), local('MaterialIcons-Regular'),
|
||||
url('../fonts/MaterialIcons/MaterialIcons-400.woff2') format('woff2'),
|
||||
url('../fonts/MaterialIcons/MaterialIcons-400.woff') format('woff');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
/* Montserrat 400 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Montserrat Regular'), local('Montserrat-Regular'),
|
||||
url('../fonts/Montserrat/Montserrat-400.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 500 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Montserrat Medium'), local('Montserrat-Medium'),
|
||||
url('../fonts/Montserrat/Montserrat-500.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 600 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Montserrat SemiBold'), local('Montserrat-SemiBold'),
|
||||
url('../fonts/Montserrat/Montserrat-600.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Montserrat 700 */
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Montserrat Bold'), local('Montserrat-Bold'),
|
||||
url('../fonts/Montserrat/Montserrat-700.woff2') format('woff2'),
|
||||
url('../fonts/Montserrat/Montserrat-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Lato 400 */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Lato Regular'), local('Lato-Regular'),
|
||||
url('../fonts/Lato/Lato-400.woff2') format('woff2'),
|
||||
url('../fonts/Lato/Lato-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Lato 700 */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/Lato/Lato-700.woff2') format('woff2'),
|
||||
url('../fonts/Lato/Lato-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 400 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Nunito Regular'), local('Nunito-Regular'),
|
||||
url('../fonts/Nunito/Nunito-400.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-400.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 600 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Nunito SemiBold'), local('Nunito-SemiBold'),
|
||||
url('../fonts/Nunito/Nunito-600.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-600.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Nunito 700 */
|
||||
@font-face {
|
||||
font-family: 'Nunito';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Nunito Bold'), local('Nunito-Bold'),
|
||||
url('../fonts/Nunito/Nunito-700.woff') format('woff2'),
|
||||
url('../fonts/Nunito/Nunito-700.woff2') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 400 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'),
|
||||
url('../fonts/OpenSans/OpenSans-400.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 600 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),
|
||||
url('../fonts/OpenSans/OpenSans-600.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Open Sans 700 */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'),
|
||||
url('../fonts/OpenSans/OpenSans-700.woff2') format('woff2'),
|
||||
url('../fonts/OpenSans/OpenSans-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 400 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Raleway'), local('Raleway-Regular'),
|
||||
url('../fonts/Raleway/Raleway-400.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 500 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Raleway Medium'), local('Raleway-Medium'),
|
||||
url('../fonts/Raleway/Raleway-500.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 600 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Raleway SemiBold'), local('Raleway-SemiBold'),
|
||||
url('../fonts/Raleway/Raleway-600.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Raleway 700 */
|
||||
@font-face {
|
||||
font-family: 'Raleway';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Raleway Bold'), local('Raleway-Bold'),
|
||||
url('../fonts/Raleway/Raleway-700.woff2') format('woff2'),
|
||||
url('../fonts/Raleway/Raleway-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 400 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Rubik'), local('Rubik-Regular'), url('../fonts/Rubik/Rubik-400.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 500 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Rubik Medium'), local('Rubik-Medium'),
|
||||
url('../fonts/Rubik/Rubik-500.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Rubik 700 */
|
||||
@font-face {
|
||||
font-family: 'Rubik';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Rubik Bold'), local('Rubik-Bold'),
|
||||
url('../fonts/Rubik/Rubik-700.woff2') format('woff2'),
|
||||
url('../fonts/Rubik/Rubik-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 400 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-400.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 600 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-600.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Source Sans Pro 700 */
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-700.woff2') format('woff2'),
|
||||
url('../fonts/SourceSansPro/SourceSansPro-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 400 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Titillium Web Regular'), local('TitilliumWeb-Regular'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-400.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 600 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Titillium Web SemiBold'), local('TitilliumWeb-SemiBold'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-600.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-600.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Titillium Web 700 */
|
||||
@font-face {
|
||||
font-family: 'Titillium Web';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Titillium Web Bold'), local('TitilliumWeb-Bold'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-700.woff2') format('woff2'),
|
||||
url('../fonts/TitilliumWeb/TitilliumWeb-700.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 400 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Ubuntu Regular'), local('Ubuntu-Regular'),
|
||||
url('../fonts/Ubuntu/Ubuntu-400.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-400.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 500 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Ubuntu Medium'), local('Ubuntu-Medium'),
|
||||
url('../fonts/Ubuntu/Ubuntu-500.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-500.woff') format('woff');
|
||||
}
|
||||
|
||||
/* Ubuntu 700 */
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Ubuntu Bold'), local('Ubuntu-Bold'),
|
||||
url('../fonts/Ubuntu/Ubuntu-700.woff2') format('woff2'),
|
||||
url('../fonts/Ubuntu/Ubuntu-700.woff') format('woff');
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 11 KiB |
@@ -12,6 +12,7 @@ import SkillsTab from './tabs/Skills';
|
||||
import ExtrasTab from './tabs/Extras';
|
||||
import LanguagesTab from './tabs/Languages';
|
||||
import ReferencesTab from './tabs/References';
|
||||
import HobbiesTab from './tabs/Hobbies';
|
||||
|
||||
const LeftSidebar = () => {
|
||||
const context = useContext(AppContext);
|
||||
@@ -26,6 +27,7 @@ const LeftSidebar = () => {
|
||||
{ key: 'awards', name: data.awards.heading },
|
||||
{ key: 'certifications', name: data.certifications.heading },
|
||||
{ key: 'skills', name: data.skills.heading },
|
||||
{ key: 'hobbies', name: data.hobbies.heading },
|
||||
{ key: 'languages', name: data.languages.heading },
|
||||
{ key: 'references', name: data.references.heading },
|
||||
{ key: 'extras', name: data.extras.heading },
|
||||
@@ -45,25 +47,27 @@ const LeftSidebar = () => {
|
||||
|
||||
const renderTabs = () => {
|
||||
switch (currentTab) {
|
||||
case tabs[0].key:
|
||||
case 'profile':
|
||||
return <ProfileTab data={data} onChange={onChange} />;
|
||||
case tabs[1].key:
|
||||
case 'objective':
|
||||
return <ObjectiveTab data={data} onChange={onChange} />;
|
||||
case tabs[2].key:
|
||||
case 'work':
|
||||
return <WorkTab data={data} onChange={onChange} />;
|
||||
case tabs[3].key:
|
||||
case 'education':
|
||||
return <EducationTab data={data} onChange={onChange} />;
|
||||
case tabs[4].key:
|
||||
case 'awards':
|
||||
return <AwardsTab data={data} onChange={onChange} />;
|
||||
case tabs[5].key:
|
||||
case 'certifications':
|
||||
return <CertificationsTab data={data} onChange={onChange} />;
|
||||
case tabs[6].key:
|
||||
case 'skills':
|
||||
return <SkillsTab data={data} onChange={onChange} />;
|
||||
case tabs[7].key:
|
||||
case 'hobbies':
|
||||
return <HobbiesTab data={data} onChange={onChange} />;
|
||||
case 'languages':
|
||||
return <LanguagesTab data={data} onChange={onChange} />;
|
||||
case tabs[8].key:
|
||||
case 'references':
|
||||
return <ReferencesTab data={data} onChange={onChange} />;
|
||||
case tabs[9].key:
|
||||
case 'extras':
|
||||
return <ExtrasTab data={data} onChange={onChange} />;
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import React, { useState, useContext } from 'react';
|
||||
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { addItem, deleteItem, moveItemUp, moveItemDown } from '../../../utils';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const HobbiesTab = ({ data, onChange }) => {
|
||||
const context = useContext(AppContext);
|
||||
const { dispatch } = context;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="my-6 grid grid-cols-6 items-center">
|
||||
<div className="col-span-1">
|
||||
<Checkbox
|
||||
checked={data.hobbies.enable}
|
||||
onChange={v => onChange('data.hobbies.enable', v)}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-5">
|
||||
<TextField
|
||||
placeholder="Heading"
|
||||
value={data.hobbies.heading}
|
||||
onChange={v => onChange('data.hobbies.heading', v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr className="my-6" />
|
||||
|
||||
{data.hobbies.items.map((x, index) => (
|
||||
<Item item={x} key={index} index={index} onChange={onChange} dispatch={dispatch} />
|
||||
))}
|
||||
|
||||
<AddItem heading={data.hobbies.heading} dispatch={dispatch} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Form = ({ item, onChange }) => {
|
||||
return (
|
||||
<input
|
||||
className="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||
placeholder="Beatboxing"
|
||||
value={item}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
type="text"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const AddItem = ({ heading, dispatch }) => {
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
const [item, setItem] = useState('');
|
||||
|
||||
const add = () => {
|
||||
if (item === '') return;
|
||||
|
||||
addItem(dispatch, 'hobbies', item);
|
||||
|
||||
setItem('');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-4 border border-gray-200 rounded p-5">
|
||||
<ItemHeading heading={heading} setOpen={setOpen} isOpen={isOpen} />
|
||||
|
||||
<div className={`mt-6 ${isOpen ? 'block' : 'hidden'}`}>
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
<div className="col-span-3">
|
||||
<Form item={item} onChange={setItem} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={add}
|
||||
className="col-span-1 bg-gray-600 hover:bg-gray-700 text-sm font-medium rounded"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<i className="material-icons font-bold text-white text-lg">add</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Item = ({ item, index, onChange, dispatch }) => {
|
||||
const identifier = `data.hobbies.items[${index}]`;
|
||||
|
||||
return (
|
||||
<div className="my-4 grid grid-cols-12">
|
||||
<div className="col-span-9">
|
||||
<Form item={item} onChange={v => onChange(identifier, v)} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemUp(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_upward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemDown(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_downward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => deleteItem(dispatch, 'hobbies', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">close</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HobbiesTab;
|
||||
@@ -3,7 +3,7 @@ import React, { useState, useContext } from 'react';
|
||||
import AppContext from '../../../context/AppContext';
|
||||
import Checkbox from '../../../shared/Checkbox';
|
||||
import TextField from '../../../shared/TextField';
|
||||
import { addItem, deleteItem } from '../../../utils';
|
||||
import { addItem, deleteItem, moveItemUp, moveItemDown } from '../../../utils';
|
||||
import ItemHeading from '../../../shared/ItemHeading';
|
||||
|
||||
const SkillsTab = ({ data, onChange }) => {
|
||||
@@ -92,18 +92,38 @@ const Item = ({ item, index, onChange, dispatch }) => {
|
||||
const identifier = `data.skills.items[${index}]`;
|
||||
|
||||
return (
|
||||
<div className="my-4 grid grid-cols-6 gap-4">
|
||||
<div className="col-span-5">
|
||||
<div className="my-4 grid grid-cols-12">
|
||||
<div className="col-span-9">
|
||||
<Form item={item} onChange={v => onChange(identifier, v)} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemUp(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_upward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => moveItemDown(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg">arrow_downward</i>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => deleteItem(dispatch, 'skills', item)}
|
||||
className="col-span-1 text-gray-600 hover:text-red-600 text-sm font-medium"
|
||||
>
|
||||
<div className="flex justify-end items-center">
|
||||
<i className="material-icons font-bold text-lg pr-4">close</i>
|
||||
<i className="material-icons font-bold text-lg">close</i>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+16
-11
@@ -24,47 +24,52 @@ const initialState = {
|
||||
email: '',
|
||||
},
|
||||
objective: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Objective',
|
||||
body: '',
|
||||
},
|
||||
work: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Work Experience',
|
||||
items: [],
|
||||
},
|
||||
education: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Education',
|
||||
items: [],
|
||||
},
|
||||
awards: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Honors & Awards',
|
||||
items: [],
|
||||
},
|
||||
certifications: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Certifications',
|
||||
items: [],
|
||||
},
|
||||
skills: {
|
||||
enable: false,
|
||||
heading: 'Skills & Hobbies',
|
||||
enable: true,
|
||||
heading: 'Skills',
|
||||
items: [],
|
||||
},
|
||||
hobbies: {
|
||||
enable: true,
|
||||
heading: 'Hobbies',
|
||||
items: [],
|
||||
},
|
||||
languages: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Languages',
|
||||
items: [],
|
||||
},
|
||||
references: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'References',
|
||||
items: [],
|
||||
},
|
||||
extras: {
|
||||
enable: false,
|
||||
enable: true,
|
||||
heading: 'Personal Information',
|
||||
items: [],
|
||||
},
|
||||
@@ -97,7 +102,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`, []);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"license": {
|
||||
"heading": "Thông tin Giấy phép",
|
||||
"body": "Dự án này được điều chỉnh bởi Giấy phép MIT, bạn có thể đọc thêm về nó dưới đây. Về cơ bản, bạn được sử dụng dự án này ở bất kì đâu, chỉ cần bạn công nhận tác giả gốc.",
|
||||
"body": "Dự án này được điều chỉnh bởi Giấy phép MIT, bạn có thể đọc thêm về nó dưới đây. Về cơ bản, bạn được xử dụng dự án này ở bất kì đâu, chỉ cần bạn công nhận tác giả gốc.",
|
||||
"buttons": {
|
||||
"mitLicense": "Giấy phép MIT"
|
||||
}
|
||||
|
||||
+2
-9
@@ -1,12 +1,5 @@
|
||||
/* Google Fonts */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Rubik:wght@400;500&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Titillium+Web:wght@400;600;700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;500;700&display=swap');
|
||||
@import './assets/css/animate.css';
|
||||
@import './assets/css/fonts.css';
|
||||
|
||||
* {
|
||||
-ms-overflow-style: none;
|
||||
|
||||
@@ -3,17 +3,21 @@ import React from 'react';
|
||||
const Checkbox = ({ checked, onChange, icon = 'check', size = '2rem' }) => {
|
||||
return (
|
||||
<div
|
||||
className="bg-white border-2 rounded border-gray-400 hover:border-gray-500 flex flex-shrink-0 justify-center items-center mr-2 focus-within:border-blue-500 cursor-pointer"
|
||||
className="relative bg-white border-2 rounded border-gray-400 hover:border-gray-500 flex flex-shrink-0 justify-center items-center mr-2 focus-within:border-blue-500 cursor-pointer"
|
||||
style={{ width: size, height: size }}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{ width: size, height: size }}
|
||||
className="opacity-0 absolute cursor-pointer"
|
||||
className="opacity-0 absolute cursor-pointer z-20"
|
||||
checked={checked}
|
||||
onChange={e => onChange(e.target.checked)}
|
||||
/>
|
||||
<i className="material-icons fill-current hidden text-sm font-bold text-gray-800 cursor-pointer">
|
||||
<i
|
||||
className={`absolute material-icons ${
|
||||
checked ? 'opacity-100' : 'opacity-0'
|
||||
} text-sm text-gray-800`}
|
||||
>
|
||||
{icon}
|
||||
</i>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ const PageController = () => {
|
||||
id="pageController"
|
||||
className="absolute z-20 opacity-75 hover:opacity-100 transition-all duration-150"
|
||||
>
|
||||
<div className="px-8 border border-gray-200 rounded-full bg-white flex justify-center items-center select-none">
|
||||
<div className="text-2xl px-8 border border-gray-200 rounded-full bg-white flex justify-center items-center leading-none select-none">
|
||||
<div className="p-3 hover:bg-gray-200 cursor-pointer flex" onClick={zoomIn}>
|
||||
<i className="material-icons">zoom_in</i>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ const PanZoomAnimation = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => setAnimationVisible(true), 500);
|
||||
setTimeout(() => setAnimationVisible(false), 4500);
|
||||
setTimeout(() => setAnimationVisible(false), 3000);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -93,6 +93,21 @@ const Castform = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const HobbyItem = x => (
|
||||
<li key={x} className="text-sm my-2">
|
||||
{x}
|
||||
</li>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies &&
|
||||
data.hobbies.enable && (
|
||||
<div>
|
||||
<Heading title={data.hobbies.heading} />
|
||||
<ul className="list-none px-5">{data.hobbies.items.map(HobbyItem)}</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Objective = () =>
|
||||
data.objective && data.objective.enable && <p className="m-5 text-sm">{data.objective.body}</p>;
|
||||
|
||||
@@ -251,7 +266,7 @@ const Castform = () => {
|
||||
data.extras &&
|
||||
data.extras.enable && (
|
||||
<div>
|
||||
<Heading title={data.extras.heading} />
|
||||
<Heading light title={data.extras.heading} />
|
||||
{data.extras.items.filter(x => x.enable).map(ExtraItem)}
|
||||
</div>
|
||||
);
|
||||
@@ -276,9 +291,9 @@ const Castform = () => {
|
||||
<PersonalInformation />
|
||||
<ContactInformation />
|
||||
<Skills />
|
||||
<Hobbies />
|
||||
<Languages />
|
||||
<Certifications />
|
||||
<Extras />
|
||||
</div>
|
||||
<div className="col-span-8">
|
||||
<Objective />
|
||||
@@ -286,6 +301,7 @@ const Castform = () => {
|
||||
<Education />
|
||||
<Awards />
|
||||
<References />
|
||||
<Extras />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -144,7 +144,21 @@ const Celebi = () => {
|
||||
<Heading title="Skills" className="w-3/4 mx-auto" />
|
||||
<ul className="list-none text-sm">
|
||||
{data.skills.items.map(x => (
|
||||
<li key="x" className="my-2">
|
||||
<li key={x} className="my-2">
|
||||
{x}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies.enable && (
|
||||
<div className="mb-6">
|
||||
<Heading title="Hobbies" className="w-3/4 mx-auto" />
|
||||
<ul className="list-none text-sm">
|
||||
{data.hobbies.items.map(x => (
|
||||
<li key={x} className="my-2">
|
||||
{x}
|
||||
</li>
|
||||
))}
|
||||
@@ -266,6 +280,7 @@ const Celebi = () => {
|
||||
<Photo />
|
||||
<Contact />
|
||||
<Skills />
|
||||
<Hobbies />
|
||||
<Languages />
|
||||
<Certifications />
|
||||
<Extras />
|
||||
|
||||
@@ -79,6 +79,21 @@ const Gengar = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const HobbyItem = x => (
|
||||
<li key={x} className="text-sm py-1">
|
||||
{x}
|
||||
</li>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies &&
|
||||
data.hobbies.enable && (
|
||||
<div className="mb-6">
|
||||
<Heading title={data.hobbies.heading} />
|
||||
<ul>{data.hobbies.items.map(HobbyItem)}</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
const EducationItem = x => (
|
||||
<div key={x.id} className="mb-3">
|
||||
<div className="flex justify-between items-center">
|
||||
@@ -260,7 +275,7 @@ const Gengar = () => {
|
||||
|
||||
<ContactItem icon="phone" value={data.profile.phone} link={`tel:${data.profile.phone}`} />
|
||||
<ContactItem
|
||||
icon="alternate_email"
|
||||
icon="email"
|
||||
value={data.profile.email}
|
||||
link={`mailto:${data.profile.email}`}
|
||||
/>
|
||||
@@ -285,6 +300,7 @@ const Gengar = () => {
|
||||
style={{ backgroundColor: `rgba(${r}, ${g}, ${b}, 0.1)` }}
|
||||
>
|
||||
<Skills />
|
||||
<Hobbies />
|
||||
<Languages />
|
||||
<Education />
|
||||
<Certifications />
|
||||
|
||||
@@ -31,10 +31,6 @@ const Glalie = () => {
|
||||
<div className="tracking-wide text-xs uppercase font-medium">{data.profile.subtitle}</div>
|
||||
);
|
||||
|
||||
const Divider = () => (
|
||||
<div className="w-1/2 mx-auto my-2 border-b-2" style={{ borderColor: theme.colors.accent }} />
|
||||
);
|
||||
|
||||
const ContactItem = ({ title, value }) =>
|
||||
value && (
|
||||
<div className="flex flex-col">
|
||||
@@ -47,14 +43,14 @@ const Glalie = () => {
|
||||
|
||||
const ContactInformation = () => (
|
||||
<div
|
||||
className="w-full border-2 pl-4 pr-4 pb-6"
|
||||
className="w-full border-2 pl-4 pr-4 mb-6"
|
||||
style={{
|
||||
borderColor: theme.colors.accent,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="inline-block relative px-4"
|
||||
style={{ top: '-.9em', color: theme.colors.accent }}
|
||||
style={{ top: '-.75em', color: theme.colors.accent }}
|
||||
>
|
||||
<h2 className="flex">
|
||||
<i className="material-icons">flare</i>
|
||||
@@ -191,6 +187,23 @@ const Glalie = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const HobbyItem = x => (
|
||||
<li key={x} className="text-xs font-medium">
|
||||
{x}
|
||||
</li>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies &&
|
||||
data.hobbies.enable && (
|
||||
<div>
|
||||
<Heading title={data.hobbies.heading} />
|
||||
<ul className="pt-2 grid grid-cols-2 row-gap-3 text-left">
|
||||
{data.hobbies.items.map(HobbyItem)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
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>
|
||||
@@ -267,33 +280,28 @@ const Glalie = () => {
|
||||
>
|
||||
<div className="grid grid-cols-12">
|
||||
<div
|
||||
className="h-full col-span-4 p-8 grid grid-cols-1 row-gap-8 text-center"
|
||||
className="h-full col-span-4 p-8 grid grid-cols-1 row-gap-4 text-center"
|
||||
style={{ backgroundColor: `rgba(${r}, ${g}, ${b}, 0.1)` }}
|
||||
>
|
||||
<div className="grid grid-cols-1 row-gap-4">
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
<Photo />
|
||||
<FullName />
|
||||
<Subtitle />
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<ContactInformation />
|
||||
<Divider />
|
||||
|
||||
<Objective />
|
||||
<Hobbies />
|
||||
<Languages />
|
||||
<Certifications />
|
||||
</div>
|
||||
|
||||
<div className="col-span-8 p-8">
|
||||
<div className="grid grid-cols-1 row-gap-6">
|
||||
<Work />
|
||||
<Education />
|
||||
<Skills />
|
||||
<Awards />
|
||||
<References />
|
||||
<Extras />
|
||||
</div>
|
||||
<div className="col-span-8 p-8 grid grid-cols-1 row-gap-4">
|
||||
<Work />
|
||||
<Education />
|
||||
<Skills />
|
||||
<Awards />
|
||||
<References />
|
||||
<Extras />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -145,6 +145,28 @@ const Onyx = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const HobbyItem = x => (
|
||||
<span
|
||||
key={x}
|
||||
className="text-xs rounded-full px-3 py-1 font-medium my-2 mr-2"
|
||||
style={{
|
||||
backgroundColor: theme.colors.primary,
|
||||
color: theme.colors.background,
|
||||
}}
|
||||
>
|
||||
{x}
|
||||
</span>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies &&
|
||||
data.hobbies.enable && (
|
||||
<div>
|
||||
<Heading title={data.hobbies.heading} />
|
||||
<div className="mt-1 flex flex-wrap">{data.hobbies.items.map(HobbyItem)}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const SkillItem = x => (
|
||||
<span
|
||||
key={x}
|
||||
@@ -227,7 +249,7 @@ const Onyx = () => {
|
||||
data.extras.enable && (
|
||||
<div>
|
||||
<Heading title={data.extras.heading} />
|
||||
<table className="w-2/3 table-auto">
|
||||
<table className="table-auto">
|
||||
<tbody>{data.extras.items.filter(x => x.enable).map(ExtraItem)}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -256,7 +278,7 @@ const Onyx = () => {
|
||||
link={`http://${data.profile.website}`}
|
||||
/>
|
||||
<ContactItem
|
||||
icon="alternate_email"
|
||||
icon="email"
|
||||
value={data.profile.email}
|
||||
link={`mailto:${data.profile.email}`}
|
||||
/>
|
||||
@@ -277,11 +299,15 @@ const Onyx = () => {
|
||||
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<Skills />
|
||||
<Languages />
|
||||
<Hobbies />
|
||||
</div>
|
||||
|
||||
<References />
|
||||
<Extras />
|
||||
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<Extras />
|
||||
<Languages />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,6 +76,24 @@ const Pikachu = () => {
|
||||
</div>
|
||||
);
|
||||
|
||||
const HobbyItem = x => (
|
||||
<span
|
||||
key={x}
|
||||
className="leading-none rounded-lg text-sm font-medium bg-gray-300 py-3 my-1 px-4"
|
||||
>
|
||||
{x}
|
||||
</span>
|
||||
);
|
||||
|
||||
const Hobbies = () =>
|
||||
data.hobbies &&
|
||||
data.hobbies.enable && (
|
||||
<div>
|
||||
<Heading title={data.hobbies.heading} />
|
||||
<div className="flex flex-col mb-6">{data.hobbies.items.map(HobbyItem)}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ReferenceItem = x => (
|
||||
<div key={x.id} className="flex flex-col">
|
||||
<h6 className="text-sm font-medium">{x.name}</h6>
|
||||
@@ -91,7 +109,7 @@ const Pikachu = () => {
|
||||
data.references.enable && (
|
||||
<div>
|
||||
<Heading title={data.references.heading} />
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-2 gap-2 mb-6">
|
||||
{data.references.items.filter(x => x.enable).map(ReferenceItem)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,7 +154,7 @@ const Pikachu = () => {
|
||||
data.extras.enable && (
|
||||
<div>
|
||||
<Heading title={data.extras.heading} />
|
||||
<div className="flex flex-col mb-6">
|
||||
<div className="grid grid-cols-2">
|
||||
{data.extras.items.filter(x => x.enable).map(ExtraItem)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,7 +187,7 @@ const Pikachu = () => {
|
||||
);
|
||||
|
||||
const EducationItem = x => (
|
||||
<div key={x.id} className="mb-3">
|
||||
<div key={x.id} className="mb-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<h6 className="font-semibold">{x.name}</h6>
|
||||
@@ -200,7 +218,7 @@ const Pikachu = () => {
|
||||
);
|
||||
|
||||
const AwardItem = x => (
|
||||
<div key={x.id} className="mb-3">
|
||||
<div key={x.id} className="mb-2">
|
||||
<h6 className="font-semibold">{x.title}</h6>
|
||||
<p className="text-xs">{x.subtitle}</p>
|
||||
<ReactMarkdown className="mt-2 text-sm" source={x.description} />
|
||||
@@ -266,7 +284,7 @@ const Pikachu = () => {
|
||||
link={`http://${data.profile.website}`}
|
||||
/>
|
||||
<ContactItem
|
||||
icon="alternate_email"
|
||||
icon="email"
|
||||
value={data.profile.email}
|
||||
link={`mailto:${data.profile.email}`}
|
||||
/>
|
||||
@@ -274,9 +292,9 @@ const Pikachu = () => {
|
||||
</div>
|
||||
|
||||
<Skills />
|
||||
<Hobbies />
|
||||
<Languages />
|
||||
<Certifications />
|
||||
<Extras />
|
||||
</div>
|
||||
|
||||
<div className="col-span-8">
|
||||
@@ -284,6 +302,7 @@ const Pikachu = () => {
|
||||
<Education />
|
||||
<Awards />
|
||||
<References />
|
||||
<Extras />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user