Calendar field and time field

This commit is contained in:
Ben Grant 2021-03-02 20:31:32 +11:00
parent edcd4dcaa0
commit 0dde47109f
32 changed files with 901 additions and 65 deletions

View file

@ -1,6 +0,0 @@
.gcloudignore
.git
.gitignore
node_modules/

View file

@ -1,6 +0,0 @@
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}

View file

@ -8,10 +8,17 @@
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.20",
"@types/node": "^14.14.31",
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"dayjs": "^1.10.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-hook-form": "^6.15.4",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"typescript": "^4.2.2",
"web-vitals": "^1.0.1",
"zustand": "^3.3.2"
},

Binary file not shown.

View file

@ -0,0 +1,5 @@
@font-face {
font-family: Karla;
src: url('fonts/karla-variable.ttf') format('truetype');
font-weight: 1 999;
}

View file

@ -12,6 +12,8 @@
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="%PUBLIC_URL%/index.css" />
<title>Crab Fit</title>
</head>
<body>

View file

@ -1,25 +0,0 @@
import {
BrowserRouter,
Switch,
Route,
Redirect,
useLocation,
} from 'react-router-dom';
import {
Home,
Event,
} from 'pages';
const App = () => {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/:id" component={Event} exact />
</Switch>
</BrowserRouter>
);
}
export default App;

View file

@ -0,0 +1,49 @@
import { useState } from 'react';
import {
BrowserRouter,
Switch,
Route,
} from 'react-router-dom';
import { ThemeProvider, Global } from '@emotion/react';
import {
Home,
Event,
} from 'pages';
import theme from 'theme';
const App = () => {
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
const [isDark, setIsDark] = useState(darkQuery.matches);
darkQuery.addListener(e => setIsDark(e.matches));
return (
<BrowserRouter>
<ThemeProvider theme={theme[isDark ? 'dark' : 'light']}>
<button onClick={() => setIsDark(!isDark)} style={{ position: 'absolute', top: 0, left: 0 }}>{isDark ? 'dark' : 'light'}</button>
<Global
styles={theme => ({
body: {
backgroundColor: theme.background,
color: theme.text,
fontFamily: `'Karla', sans-serif`,
fontWeight: 600,
margin: 0,
},
a: {
color: theme.primary,
},
})}
/>
<Switch>
<Route path="/" component={Home} exact />
<Route path="/:id" component={Event} exact />
</Switch>
</ThemeProvider>
</BrowserRouter>
);
}
export default App;

View file

@ -0,0 +1,14 @@
import { Wrapper, Top, Bottom } from './buttonStyle';
const Button = ({
buttonHeight,
buttonWidth,
...props
}) => (
<Wrapper buttonHeight={buttonHeight} buttonWidth={buttonWidth}>
<Top {...props} />
<Bottom />
</Wrapper>
);
export default Button;

View file

@ -0,0 +1,47 @@
import styled from '@emotion/styled';
export const Wrapper = styled.div`
display: inline-block;
position: relative;
--btn-height: ${props => props.buttonHeight || '40px'};
--btn-width: ${props => props.buttonWidth || '100px'};
height: var(--btn-height);
width: var(--btn-width);
`;
export const Top = styled.button`
border: 0;
cursor: pointer;
font: inherit;
box-sizing: border-box;
background: ${props => props.theme.primary};
color: #FFF;
font-weight: 600;
text-shadow: 0 -1.5px .5px ${props => props.theme.primaryDark};
padding: ${props => props.padding || '10px 14px'};
border-radius: 3px;
height: var(--btn-height);
width: var(--btn-width);
position: absolute;
top: -4px;
user-select: none;
transition: top .15s;
outline: none;
&:active {
top: 0;
}
&:focus-visible {
filter: brightness(1.2);
}
`;
export const Bottom = styled.div`
box-sizing: border-box;
background: ${props => props.theme.primaryDark};
border-radius: 3px;
height: var(--btn-height);
width: var(--btn-width);
`;

View file

@ -0,0 +1,192 @@
import { useState, useEffect, useRef } from 'react';
import dayjs from 'dayjs';
import isToday from 'dayjs/plugin/isToday';
import { Button } from 'components';
import {
Wrapper,
StyledLabel,
StyledSubLabel,
CalendarHeader,
CalendarBody,
Date,
Day,
} from './calendarFieldStyle';
dayjs.extend(isToday);
const days = [
'Sun',
'Mon',
'Tue',
'Wed',
'Thu',
'Fri',
'Sat',
];
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
const calculateMonth = (month, year) => {
const date = dayjs().month(month).year(year);
const daysInMonth = date.daysInMonth();
const daysBefore = date.date(1).day();
const daysAfter = 6 - date.date(daysInMonth).day();
let dates = [];
let curDate = date.date(1).subtract(daysBefore, 'day');
let y = 0;
let x = 0;
for (let i = 0; i < daysBefore + daysInMonth + daysAfter; i++) {
if (x === 0) dates[y] = [];
dates[y][x] = curDate.clone();
curDate = curDate.add(1, 'day');
x++;
if (x > 6) {
x = 0;
y++;
}
}
return dates;
};
const CalendarField = ({
label,
subLabel,
id,
register,
...props
}) => {
const [dates, setDates] = useState(calculateMonth(dayjs().month(), dayjs().year()));
const [month, setMonth] = useState(dayjs().month());
const [year, setYear] = useState(dayjs().year());
const [selectedDates, setSelectedDates] = useState([]);
const [selectingDates, _setSelectingDates] = useState([]);
const staticSelectingDates = useRef([]);
const setSelectingDates = newDates => {
staticSelectingDates.current = newDates;
_setSelectingDates(newDates);
};
const startPos = useRef({});
const staticMode = useRef(null);
const [mode, _setMode] = useState(staticMode.current);
const setMode = newMode => {
staticMode.current = newMode;
_setMode(newMode);
};
useEffect(() => {
setDates(calculateMonth(month, year));
}, [month, year]);
return (
<Wrapper>
{label && <StyledLabel htmlFor={id}>{label}</StyledLabel>}
{subLabel && <StyledSubLabel htmlFor={id}>{subLabel}</StyledSubLabel>}
<input
id={id}
type="hidden"
ref={register}
value={JSON.stringify(selectedDates)}
{...props}
/>
<CalendarHeader>
<Button
buttonHeight="30px"
buttonWidth="30px"
padding="0"
title="Previous month"
type="button"
onClick={() => {
if (month-1 < 0) {
setYear(year-1);
setMonth(11);
} else {
setMonth(month-1);
}
}}
>&lt;</Button>
<span>{months[month]} {year}</span>
<Button
buttonHeight="30px"
buttonWidth="30px"
padding="0"
title="Next month"
type="button"
onClick={() => {
if (month+1 > 11) {
setYear(year+1);
setMonth(0);
} else {
setMonth(month+1);
}
}}
>&gt;</Button>
</CalendarHeader>
<CalendarBody>
{days.map((name, i) =>
<Day key={i}>{name}</Day>
)}
{dates.length > 0 && dates.map((dateRow, y) =>
dateRow.map((date, x) =>
<Date
key={y+x}
otherMonth={date.month() !== month}
isToday={date.isToday()}
title={`${date.date()} ${months[date.month()]}${date.isToday() ? ' (today)' : ''}`}
selected={selectedDates.includes(date.format('DDMMYYYY'))}
selecting={selectingDates.includes(date)}
mode={mode}
onMouseDown={() => {
startPos.current = {x, y};
setMode(selectedDates.includes(date.format('DDMMYYYY')) ? 'remove' : 'add');
setSelectingDates([date]);
document.addEventListener('mouseup', () => {
if (staticMode.current === 'add') {
setSelectedDates([...selectedDates, ...staticSelectingDates.current.map(d => d.format('DDMMYYYY'))]);
} else if (staticMode.current === 'remove') {
const toRemove = staticSelectingDates.current.map(d => d.format('DDMMYYYY'));
setSelectedDates(selectedDates.filter(d => !toRemove.includes(d)));
}
setMode(null);
}, { once: true });
}}
onMouseEnter={() => {
if (staticMode.current) {
let found = [];
for (let cy = Math.min(startPos.current.y, y); cy < Math.max(startPos.current.y, y)+1; cy++) {
for (let cx = Math.min(startPos.current.x, x); cx < Math.max(startPos.current.x, x)+1; cx++) {
found.push({y: cy, x: cx});
}
}
setSelectingDates(found.map(d => dates[d.y][d.x]));
}
}}
>{date.date()}</Date>
)
)}
</CalendarBody>
</Wrapper>
);
};
export default CalendarField;

View file

@ -0,0 +1,73 @@
import styled from '@emotion/styled';
export const Wrapper = styled.div`
margin: 30px 0;
`;
export const StyledLabel = styled.label`
display: block;
padding-bottom: 4px;
font-size: 18px;
`;
export const StyledSubLabel = styled.label`
display: block;
padding-bottom: 6px;
font-size: 13px;
opacity: .6;
`;
export const CalendarHeader = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
user-select: none;
padding: 6px 0;
font-size: 1.2em;
font-weight: bold;
`;
export const CalendarBody = styled.div`
display: grid;
grid-template-columns: repeat(7, 1fr);
grid-gap: 2px;
`;
export const Date = styled.div`
background-color: ${props => props.theme.primary}22;
border: 1px solid ${props => props.theme.primaryLight};
display: flex;
align-items: center;
justify-content: center;
padding: 10px 0;
border-radius: 3px;
user-select: none;
${props => props.otherMonth && `
color: ${props.theme.primaryLight};
`}
${props => props.isToday && `
font-weight: 900;
color: ${props.theme.primaryDark};
`}
${props => (props.selected || (props.mode === 'add' && props.selecting)) && `
color: ${props.otherMonth ? 'rgba(255,255,255,.5)' : '#FFF'};
background-color: ${props.theme.primary};
border-color: ${props.theme.primary};
`}
${props => props.mode === 'remove' && props.selecting && `
background-color: ${props.theme.primary}22;
border: 1px solid ${props.theme.primaryLight};
color: ${props.isToday ? props.theme.primaryDark : (props.otherMonth ? props.theme.primaryLight : 'inherit')};
`}
`;
export const Day = styled.div`
display: flex;
align-items: center;
justify-content: center;
padding: 3px 10px;
font-weight: bold;
user-select: none;
opacity: .7;
`;

View file

@ -0,0 +1,22 @@
import {
Wrapper,
StyledLabel,
StyledSubLabel,
StyledInput,
} from './textFieldStyle';
const TextField = ({
label,
subLabel,
id,
register,
...props
}) => (
<Wrapper>
{label && <StyledLabel htmlFor={id}>{label}</StyledLabel>}
{subLabel && <StyledSubLabel htmlFor={id}>{subLabel}</StyledSubLabel>}
<StyledInput id={id} ref={register} {...props} />
</Wrapper>
);
export default TextField;

View file

@ -0,0 +1,38 @@
import styled from '@emotion/styled';
export const Wrapper = styled.div`
margin: 30px 0;
`;
export const StyledLabel = styled.label`
display: block;
padding-bottom: 4px;
font-size: 18px;
`;
export const StyledSubLabel = styled.label`
display: block;
padding-bottom: 6px;
font-size: 13px;
opacity: .6;
`;
export const StyledInput = styled.input`
width: 100%;
box-sizing: border-box;
font: inherit;
background: ${props => props.theme.primary}22;
color: inherit;
padding: 10px 14px;
border: 1px solid ${props => props.theme.primaryLight};
box-shadow: inset 0 0 0 0 ${props => props.theme.primaryLight};
border-radius: 3px;
font-size: 18px;
outline: none;
transition: border-color .15s, box-shadow .15s;
&:focus {
border: 1px solid ${props => props.theme.primary};
box-shadow: inset 0 -3px 0 0 ${props => props.theme.primary};
}
`;

View file

@ -0,0 +1,139 @@
import { useState, useEffect, useRef } from 'react';
import {
Wrapper,
StyledLabel,
StyledSubLabel,
Range,
Handle,
Selected,
} from './timeRangeFieldStyle';
const times = [
'12am',
'1am',
'2am',
'3am',
'4am',
'5am',
'6am',
'7am',
'8am',
'9am',
'10am',
'11am',
'12pm',
'1pm',
'2pm',
'3pm',
'4pm',
'5pm',
'6pm',
'7pm',
'8pm',
'9pm',
'10pm',
'11pm',
'12am',
];
const TimeRangeField = ({
label,
subLabel,
id,
register,
...props
}) => {
const [start, setStart] = useState(9);
const [end, setEnd] = useState(17);
const isStartMoving = useRef(false);
const isEndMoving = useRef(false);
const rangeRef = useRef();
const rangeRect = useRef();
useEffect(() => {
if (rangeRef.current) {
rangeRect.current = rangeRef.current.getBoundingClientRect();
}
}, [rangeRef]);
const handleMouseMove = e => {
if (isStartMoving.current || isEndMoving.current) {
let step = Math.round(((e.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
if (isStartMoving.current) {
setStart(step);
} else if (isEndMoving.current) {
setEnd(step);
}
}
};
return (
<Wrapper>
{label && <StyledLabel htmlFor={id}>{label}</StyledLabel>}
{subLabel && <StyledSubLabel htmlFor={id}>{subLabel}</StyledSubLabel>}
<input
id={id}
type="hidden"
ref={register}
value={JSON.stringify(start > end ? {start: end, end: start} : {start, end})}
{...props}
/>
<Range ref={rangeRef}>
<Selected start={start > end ? end : start} end={start > end ? start : end} />
<Handle
value={start}
label={times[start]}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isStartMoving.current = true;
document.addEventListener('mouseup', () => {
isStartMoving.current = false;
document.removeEventListener('mousemove', handleMouseMove);
}, { once: true });
}}
onTouchMove={(e) => {
const touch = e.targetTouches[0];
let step = Math.round(((touch.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
setStart(step);
}}
/>
<Handle
value={end}
label={times[end]}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isEndMoving.current = true;
document.addEventListener('mouseup', () => {
isEndMoving.current = false;
document.removeEventListener('mousemove', handleMouseMove);
}, { once: true });
}}
onTouchMove={(e) => {
const touch = e.targetTouches[0];
let step = Math.round(((touch.pageX - rangeRect.current.left) / rangeRect.current.width) * 24);
if (step < 0) step = 0;
if (step > 24) step = 24;
step = Math.abs(step);
setEnd(step);
}}
/>
</Range>
</Wrapper>
);
};
export default TimeRangeField;

View file

@ -0,0 +1,72 @@
import styled from '@emotion/styled';
export const Wrapper = styled.div`
margin: 30px 0;
`;
export const StyledLabel = styled.label`
display: block;
padding-bottom: 4px;
font-size: 18px;
`;
export const StyledSubLabel = styled.label`
display: block;
padding-bottom: 6px;
font-size: 13px;
opacity: .6;
`;
export const Range = styled.div`
user-select: none;
background-color: ${props => props.theme.primary}22;
border: 1px solid ${props => props.theme.primaryLight};
border-radius: 3px;
height: 50px;
position: relative;
margin: 38px 6px 18px;
`;
export const Handle = styled.div`
height: calc(100% + 20px);
width: 20px;
border: 1px solid ${props => props.theme.primary};
background-color: ${props => props.theme.primaryLight};
border-radius: 3px;
position: absolute;
top: -10px;
left: calc(${props => props.value * 4.1666666666666666}% - 11px);
cursor: ew-resize;
&:after {
content: '|||';
font-size: 8px;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: ${props => props.theme.primaryDark};
}
&:before {
content: '${props => props.label}';
position: absolute;
bottom: calc(100% + 8px);
text-align: center;
left: 50%;
transform: translateX(-50%);
}
`;
export const Selected = styled.div`
position: absolute;
height: 100%;
left: ${props => props.start * 4.1666666666666666}%;
right: calc(100% - ${props => props.end * 4.1666666666666666}%);
top: 0;
background-color: ${props => props.theme.primary};
`;

View file

@ -0,0 +1,4 @@
export { default as TextField } from './TextField/TextField';
export { default as CalendarField } from './CalendarField/CalendarField';
export { default as TimeRangeField } from './TimeRangeField/TimeRangeField';
export { default as Button } from './Button/Button';

View file

@ -1,13 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

View file

@ -1,6 +1,5 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

View file

@ -1,12 +0,0 @@
import { Link } from 'react-router-dom';
const Home = () => {
return (
<div>
<div>Home</div>
<Link to="/test">Test</Link>
</div>
);
};
export default Home;

View file

@ -0,0 +1,59 @@
import { useForm } from 'react-hook-form';
import {
TextField,
CalendarField,
TimeRangeField,
Button,
} from 'components';
import {
StyledMain,
CreateForm,
TitleSmall,
TitleLarge,
} from './homeStyle';
const Home = () => {
const { register, handleSubmit } = useForm();
const onSubmit = data => console.log('submit', data);
return (
<StyledMain>
<TitleSmall>Create a</TitleSmall>
<TitleLarge>CRAB FIT</TitleLarge>
<CreateForm onSubmit={handleSubmit(onSubmit)}>
<TextField
label="Give your event a name!"
subLabel="Or leave blank to generate one"
type="text"
name="name"
id="name"
register={register}
/>
<CalendarField
label="What dates might work?"
subLabel="Click and drag to select"
name="dates"
id="dates"
register={register}
/>
<TimeRangeField
label="What times might work?"
subLabel="Click and drag to select a time range"
name="times"
id="times"
register={register}
/>
<Button type="submit">Create</Button>
</CreateForm>
</StyledMain>
);
};
export default Home;

View file

@ -0,0 +1,32 @@
import styled from '@emotion/styled';
export const StyledMain = styled.main`
width: 600px;
margin: 30px auto;
max-width: calc(100% - 30px);
`;
export const CreateForm = styled.form`
`;
export const TitleSmall = styled.span`
display: block;
margin: 20px 0 0;
font-size: 3rem;
text-align: center;
font-family: 'CF Samurai Bob';
font-weight: 400;
color: ${props => props.theme.primaryDark};
line-height: 1em;
`;
export const TitleLarge = styled.h1`
margin: 0 0 40px;
font-size: 4rem;
text-align: center;
color: ${props => props.theme.primary};
font-family: 'Molot';
font-weight: 400;
text-shadow: 0 4px 0 ${props => props.theme.primaryDark};
line-height: 1em;
`;

View file

@ -0,0 +1 @@
/// <reference types="react-scripts" />

View file

@ -0,0 +1,61 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="1024" height="512" viewBox="0 0 1024 512">
<defs>
<style>
.cls-1 {
fill: #f79e00;
}
.cls-2 {
fill: #f4bb60;
}
.cls-3 {
fill: #f47f00;
opacity: 0.5;
}
</style>
</defs>
<g>
<path class="cls-1" d="M197.1,316.71a63.14,63.14,0,0,0-21-4.09c-29.16,0-44.69,26.71-44.69,47.15,0,6,1.36,11.44,4.08,15.53,4.64,7.08,10.63,12.81,21.81,12.81,8.44,0,20.16-3.27,36.51-12l10.08,33.24c-20.71,10.36-38.42,14.72-53.13,14.72-37.34,0-55.87-28.07-55.87-60.23,0-35.42,28.07-86.65,76-86.65A77.44,77.44,0,0,1,199,282.65Z"/>
<path class="cls-1" d="M220,289.73h.55c27.79-9.26,50.41-13.62,67.85-13.62,28.61,0,43.33,11.71,43.87,32.7.82,22.61-13.08,46.05-41.42,63.49,13.36,6.54,28.07,12,39,14.44-4.9,10.09-11.17,22.89-16.35,33-16.35-5.45-44.14-18-62.4-29.71.27,9.54.54,19.08,1.36,28.89-12.26.54-23.71,3-36,3.54-.27-11.44-.55-22.62-.55-34.06,0-33,1.37-66.22,4.09-98.38Zm75.49,32.43c4.36-7.9-1.91-10.63-10.9-10.63-10.09,0-24,3.27-31.89,5.18-.82,12.26-1.36,24.8-1.91,37.33C268.22,350.23,290.3,332,295.48,322.16Z"/>
<path class="cls-1" d="M402.57,277.2h.54c15.27,44.69,31.34,89.65,51.78,131.07-11.17,3.54-23.16,8.18-34.61,12-1.9-3.54-3.54-7.36-5.18-11.17l-39.78,9c-.82,2.73-1.64,5.45-2.45,7.9-12.54,1.37-26.71,2.46-39.24,3.82C338,397.64,380.22,305.54,402.57,277.2Zm1.63,104.37c-2.72-8.45-5.45-17.17-7.63-26.16-4.63,9-8.44,19.89-12.26,31.33Z"/>
<path class="cls-1" d="M455.43,359.49c0-27.25,2.46-51.5,4.36-75.75C479.14,281,502.85,277.47,520,278c36.52.28,48.78,11.72,48.78,24.26,0,12.8-12.81,26.43-26.44,30.52,25.89,2.18,36.25,15.26,35.7,28.07-1.63,42.78-65.94,59.95-117.72,65.4A464.72,464.72,0,0,1,455.43,359.49Zm36.25,26.71c31.34-4.63,45-13.63,45-20.17,0-4.63-6.54-8.17-18.53-8.17-7.36,0-16.62,1.36-27.25,4.36C490.86,370.12,491.13,378,491.68,386.2Zm.82-59.14c25.07-4.9,36.51-12,36.51-16.62,0-2.72-3.81-4.63-11.44-4.63-5.73,0-13.9,1.09-23.71,3.81C493.31,314.26,492.5,322.16,492.5,327.06Z"/>
<path class="cls-1" d="M748.11,276.11l.81,34.06c-13.62,1.36-25.34,2.72-39,7.36-1.1,5.72-1.37,13.62-1.64,22.89,10.9-2.18,22.35-3.82,32.7-4.91.27,10.09.55,22.89.55,33A243.26,243.26,0,0,0,709.14,375c.82,16.07,2.18,31.61,3.27,42l-34.61,9.54c-3.27-18.53-4.36-39-4.36-60.5,0-25.88,1.63-53.14,3.81-78.75C699.33,280.19,727.12,276.11,748.11,276.11Z"/>
<path class="cls-1" d="M763.36,315.89c0-11.17.28-22.34.55-33.52,12.53-2.18,24-2.18,36.24-4.08-1.36,16.89-1.91,33.79-1.91,50.41,0,29.43,1.91,58.86,4.64,87.47-11.72,2.18-23.71,3.55-35.16,5.73C765,387.56,763.36,351.86,763.36,315.89Z"/>
<path class="cls-1" d="M814.59,325.43c-.27-11.72-1.63-24-1.63-36.24a478.75,478.75,0,0,1,83.66-13.08v34.6c-7.9.55-16.08,1.09-24,2.18-1.36,19.08-2.18,37.88-2.18,57,0,16.35.54,32.7,1.91,48.78-12.27.81-23.71,3-36,3.81-.27-11.72-.54-23.43-.54-35.42,0-22.62.54-45.51,1.91-68.13C830.13,320.8,822.22,323,814.59,325.43Z"/>
</g>
<g>
<path class="cls-2" d="M164.83,164.11a40.8,40.8,0,0,0-13.58-2.65c-18.88,0-28.94,17.29-28.94,30.53A18.09,18.09,0,0,0,125,202c3,4.59,6.88,8.3,14.11,8.3,5.47,0,13.06-2.12,23.65-7.77l6.52,21.53c-13.41,6.7-24.87,9.53-34.4,9.53-24.17,0-36.17-18.18-36.17-39,0-22.93,18.17-56.1,49.23-56.1a50.18,50.18,0,0,1,18.17,3.52Z"/>
<path class="cls-2" d="M224.29,138.17c14.82,0,27.53,4.94,28.06,21.35.53,15-9,29.11-28.59,39.35,8.83,6.17,19.06,11.64,26.82,14.46-4.06,6.18-9.17,13.94-13.23,19.94-10.06-4.94-27-15.35-37.76-24.35q.27,10.86,1.06,21.71c-7.94.52-15.35,1.94-23.29,2.47-.18-7.41-.35-14.65-.35-22.06,0-21.52.88-43.23,2.64-63.87C187.06,144.7,207.18,138.17,224.29,138.17Zm4.24,30c2.82-5.3-1.06-6.88-6.88-6.88-6.53,0-15.71,1.94-20.82,3.17q-.79,10.32-1.06,20.64C211.06,183.16,225.18,174.52,228.53,168.17Z"/>
<path class="cls-2" d="M309.69,140.82l.53,21.52c-9.88.36-15.17.89-25.05,3.53-.53,3.71-1.06,7.24-1.24,10.77a159.38,159.38,0,0,1,26.82-3.71c.35,6.53,1.23,15.17,1.59,21.7a139.35,139.35,0,0,0-27.7,5.3c.17,2.29.53,4.76.88,7.41,10.58-2.65,17.47-2.83,28.58-3.89l1.24,22.41c-18.18,1.06-33.88,5.29-51.88,10.59-2.29-11.82-3.17-25.23-3.17-38.82,0-16.94,1.41-34.58,2.82-51.34A173,173,0,0,1,309.69,140.82Z"/>
<path class="cls-2" d="M358,138.7h.36c9.88,28.94,21.87,60.17,35.28,86.81-7.41,2.47-15.17,5.47-22.4,7.94-1.94-3.88-3.89-7.76-5.65-11.82l-24.53,5.47q-.78,2.91-1.58,5.82c-8.12.71-17.47,1.59-25.41,2.47C316.92,214.39,343.57,157.05,358,138.7Zm.53,65.11c-1.76-4.77-3.17-9.53-4.41-14.29a109.48,109.48,0,0,0-6.88,17.29Z"/>
<path class="cls-2" d="M384.5,170.28c-.18-7.76-1.06-15.7-1.06-23.46a365.8,365.8,0,0,1,58.93-8.65v22.59c-6.35.17-12.53.7-18.7,1.41-.88,12.35-1.41,24.52-1.41,36.87,0,10.59.35,21.18,1.23,31.59-7.94.35-15.52,1.94-23.46,2.47-.18-7.06-.36-14.12-.36-21.18,0-15.35.53-30.7,1.24-45.87Q392.71,167.9,384.5,170.28Z"/>
<path class="cls-2" d="M498.83,140.82l.53,21.52c-9.88.36-15.17.89-25.05,3.53-.53,3.71-1.06,7.24-1.24,10.77a159.38,159.38,0,0,1,26.82-3.71c.35,6.53,1.24,15.17,1.59,21.7a139.35,139.35,0,0,0-27.7,5.3c.17,2.29.53,4.76.88,7.41,10.59-2.65,17.47-2.83,28.58-3.89l1.24,22.41c-18.18,1.06-33.88,5.29-51.87,10.59-2.3-11.82-3.18-25.23-3.18-38.82,0-16.94,1.41-34.58,2.82-51.34A173,173,0,0,1,498.83,140.82Z"/>
<path class="cls-2" d="M599.93,138.7h.35c9.88,28.94,21.88,60.17,35.29,86.81-7.41,2.47-15.17,5.47-22.41,7.94-1.94-3.88-3.88-7.76-5.64-11.82L583,227.1l-1.59,5.82c-8.11.71-17.46,1.59-25.4,2.47C558.82,214.39,585.46,157.05,599.93,138.7Zm.53,65.11a148.15,148.15,0,0,1-4.41-14.29,109.48,109.48,0,0,0-6.88,17.29Z"/>
</g>
<g>
<path class="cls-2" d="M768,169.48a75.57,75.57,0,0,1-46.19-96.37h0a75.57,75.57,0,0,1,96.38-46.19L855.4,40,829,52.72l18.81,6.62c-16.21,46-69.41-3.29-89.4,1.57C734.12,66.81,768,169.48,768,169.48Z"/>
<g>
<rect class="cls-1" x="832.9" y="123.63" width="12.83" height="35.28" rx="6.42" transform="translate(94.54 -270.7) rotate(19.39)"/>
<rect class="cls-1" x="808.7" y="115.11" width="12.83" height="35.28" rx="6.42" transform="translate(90.34 -263.14) rotate(19.39)"/>
</g>
<g>
<rect class="cls-2" x="666.34" y="155.3" width="65.76" height="14.43" rx="7.22" transform="translate(93.64 -222.97) rotate(19.39)"/>
<rect class="cls-2" x="678.33" y="135.71" width="65.76" height="14.43" rx="7.22" transform="translate(222.82 -393.47) rotate(36.32)"/>
<rect class="cls-2" x="661.9" y="177.54" width="65.76" height="14.43" rx="7.22" transform="translate(5.39 -19.2) rotate(1.59)"/>
<rect class="cls-2" x="666.86" y="199.69" width="65.76" height="14.43" rx="7.22" transform="translate(-29.89 197.88) rotate(-15.76)"/>
</g>
<g>
<rect class="cls-2" x="876.62" y="229.33" width="65.76" height="14.43" rx="7.22" transform="translate(1688.84 761.68) rotate(-160.61)"/>
<rect class="cls-2" x="879.54" y="206.55" width="65.76" height="14.43" rx="7.22" transform="translate(1814.77 466.65) rotate(-177.53)"/>
<rect class="cls-2" x="866.14" y="249.44" width="65.76" height="14.43" rx="7.22" transform="translate(1459.94 1004.63) rotate(-142.8)"/>
<rect class="cls-2" x="848.4" y="263.6" width="65.76" height="14.43" rx="7.22" transform="translate(1171.77 1145.81) rotate(-125.45)"/>
</g>
<path class="cls-1" d="M829,52.72l6.89-19.57-17.7-6.23a75.57,75.57,0,0,0-96.38,46.19h0C744.48,33.92,770.6,32.18,829,52.72Z"/>
<path class="cls-2" d="M853.45,199.58a75.56,75.56,0,0,0,96.37-46.19h0A75.57,75.57,0,0,0,903.64,57L866.37,43.9l12.68,26.46-18.81-6.63c-16.21,46,56.16,40.92,68.7,57.23C944.14,140.75,853.45,199.58,853.45,199.58Z"/>
<path class="cls-1" d="M879.05,70.36l6.89-19.57L903.64,57a75.57,75.57,0,0,1,46.18,96.37h0C956.66,108.62,937.39,90.9,879.05,70.36Z"/>
<rect class="cls-1" x="698.46" y="133.23" width="213.31" height="133.12" rx="66.56" transform="translate(112.03 -256.02) rotate(19.39)"/>
<circle class="cls-3" cx="783.18" cy="192.07" r="42.5"/>
<circle class="cls-3" cx="827.05" cy="207.52" r="42.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View file

@ -0,0 +1,20 @@
const theme = {
light: {
mode: 'light',
background: '#FFFFFF',
text: '#000000',
primary: '#F79E00',
primaryDark: '#F48600',
primaryLight: '#F4BB60',
},
dark: {
mode: 'dark',
background: '#111',
text: '#DDDDDD',
primary: '#F79E00',
primaryDark: '#F4BB60',
primaryLight: '#F48600',
},
};
export default theme;

View file

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src"
},
"include": [
"src"
]
}

View file

@ -1834,7 +1834,7 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@*":
"@types/jest@*", "@types/jest@^26.0.20":
version "26.0.20"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.20.tgz#cd2f2702ecf69e86b586e1f5223a60e454056307"
integrity sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==
@ -1857,7 +1857,7 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
"@types/node@*", "@types/node@^14.14.31":
version "14.14.31"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055"
integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==
@ -1877,11 +1877,31 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd"
integrity sha512-DxZZbyMAM9GWEzXL+BMZROWz9oo6A9EilwwOMET2UVu2uZTqMWS5S69KVtuVKaRjCUpcrOXRalet86/OpG4kqw==
"@types/prop-types@*":
version "15.7.3"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
"@types/q@^1.5.1":
version "1.5.4"
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
"@types/react-dom@^17.0.1":
version "17.0.1"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.1.tgz#d92d77d020bfb083e07cc8e0ac9f933599a4d56a"
integrity sha512-yIVyopxQb8IDZ7SOHeTovurFq+fXiPICa+GV3gp0Xedsl+MwQlMLKmvrnEjFbQxjliH5YVAEWFh975eVNmKj7Q==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.2":
version "17.0.2"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.2.tgz#3de24c4efef902dd9795a49c75f760cbe4f7a5a8"
integrity sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA==
dependencies:
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/resolve@0.0.8":
version "0.0.8"
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194"
@ -3951,6 +3971,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
dayjs@^1.10.4:
version "1.10.4"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -9106,6 +9131,11 @@ react-error-overlay@^6.0.9:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
react-hook-form@^6.15.4:
version "6.15.4"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-6.15.4.tgz#328003e1ccc096cd158899ffe7e3b33735a9b024"
integrity sha512-K+Sw33DtTMengs8OdqFJI3glzNl1wBzSefD/ksQw/hJf9CnOHQAU6qy82eOrh0IRNt2G53sjr7qnnw1JDjvx1w==
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@ -10828,6 +10858,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.2.tgz#1450f020618f872db0ea17317d16d8da8ddb8c4c"
integrity sha512-tbb+NVrLfnsJy3M59lsDgrzWIflR4d4TIUjz+heUnHZwdF7YsrMTKoRERiIvI2lvBG95dfpLxB21WZhys1bgaQ==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"