Font optimise and settings on home page

This commit is contained in:
Ben Grant 2021-03-11 01:27:01 +11:00
parent 291034ca4e
commit 26c4b6629d
17 changed files with 329 additions and 43 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -5,13 +5,17 @@
}
@font-face {
font-family: 'Samurai Bob';
src: url('fonts/samuraibob.ttf') format('truetype');
font-weight: 400;
font-family: 'Samurai Bob';
src: url('fonts/samuraibob.woff2') format('woff2'),
url('fonts/samuraibob.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Molot';
src: url('fonts/molot.otf') format('opentype');
font-weight: 400;
font-family: 'Molot';
src: url('fonts/molot.woff2') format('woff2'),
url('fonts/molot.woff') format('woff');
font-weight: 400;
font-style: normal;
}

View file

@ -6,6 +6,7 @@ import {
} from 'react-router-dom';
import { ThemeProvider, Global } from '@emotion/react';
import { Settings } from 'components';
import {
Home,
Event,
@ -22,7 +23,7 @@ const App = () => {
return (
<BrowserRouter>
<ThemeProvider theme={theme[isDark ? 'dark' : 'light']}>
{process.env.NODE_ENV !== 'production' && <button onClick={() => setIsDark(!isDark)} style={{ position: 'absolute', top: 0, left: 0 }}>{isDark ? 'dark' : 'light'}</button>}
{process.env.NODE_ENV !== 'production' && <button onClick={() => setIsDark(!isDark)} style={{ position: 'absolute', top: 0, left: 0, zIndex: 1000 }}>{isDark ? 'dark' : 'light'}</button>}
<Global
styles={theme => ({
html: {
@ -63,6 +64,8 @@ const App = () => {
<Route path="/" component={Home} exact />
<Route path="/:id" component={Event} exact />
</Switch>
<Settings />
</ThemeProvider>
</BrowserRouter>
);

View file

@ -2,8 +2,11 @@ import { useState, useEffect, useRef } from 'react';
import dayjs from 'dayjs';
import isToday from 'dayjs/plugin/isToday';
import localeData from 'dayjs/plugin/localeData';
import updateLocale from 'dayjs/plugin/updateLocale';
import { Button } from 'components';
import { useSettingsStore } from 'stores';
import {
Wrapper,
StyledLabel,
@ -17,12 +20,13 @@ import {
dayjs.extend(isToday);
dayjs.extend(localeData);
dayjs.extend(updateLocale);
const calculateMonth = (month, year) => {
const calculateMonth = (month, year, weekStart) => {
const date = dayjs().month(month).year(year);
const daysInMonth = date.daysInMonth();
const daysBefore = date.date(1).day();
const daysAfter = 6 - date.date(daysInMonth).day();
const daysBefore = date.date(1).day() - weekStart;
const daysAfter = 6 - date.date(daysInMonth).day() + weekStart;
let dates = [];
let curDate = date.date(1).subtract(daysBefore, 'day');
@ -49,7 +53,9 @@ const CalendarField = ({
register,
...props
}) => {
const [dates, setDates] = useState(calculateMonth(dayjs().month(), dayjs().year()));
const weekStart = useSettingsStore(state => state.weekStart);
const [dates, setDates] = useState(calculateMonth(dayjs().month(), dayjs().year(), weekStart));
const [month, setMonth] = useState(dayjs().month());
const [year, setYear] = useState(dayjs().year());
@ -70,8 +76,14 @@ const CalendarField = ({
};
useEffect(() => {
setDates(calculateMonth(month, year));
}, [month, year]);
if (weekStart !== dayjs.Ls.en.weekStart) {
dayjs.updateLocale('en', {
weekStart: weekStart,
weekdaysShort: weekStart ? 'Mon_Tue_Wed_Thu_Fri_Sat_Sun'.split('_') : 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'),
});
}
setDates(calculateMonth(month, year, weekStart));
}, [weekStart, month, year]);
return (
<Wrapper>

View file

@ -0,0 +1,57 @@
import { useState } from 'react';
import { useTheme } from '@emotion/react';
import { ToggleField } from 'components';
import { useSettingsStore } from 'stores';
import {
OpenButton,
Modal,
Heading,
Cover,
} from './settingsStyle';
const Settings = () => {
const theme = useTheme();
const store = useSettingsStore();
const [isOpen, setIsOpen] = useState(false);
return (
<>
<OpenButton
isOpen={isOpen}
tabIndex="1"
type="button"
onClick={() => setIsOpen(!isOpen)} title="Options"
>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke={theme.text} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
</OpenButton>
<Cover isOpen={isOpen} onClick={() => setIsOpen(false)} />
<Modal isOpen={isOpen}>
<Heading>Options</Heading>
<ToggleField
label="Week starts on"
name="weekStart"
id="weekStart"
options={['Sunday', 'Monday']}
value={store.weekStart === 1 ? 'Monday' : 'Sunday'}
onChange={value => store.setWeekStart(value === 'Monday' ? 1 : 0)}
/>
<ToggleField
label="Time format"
name="timeFormat"
id="timeFormat"
options={['12h', '24h']}
value={store.timeFormat}
onChange={value => store.setTimeFormat(value)}
/>
</Modal>
</>
);
};
export default Settings;

View file

@ -0,0 +1,79 @@
import styled from '@emotion/styled';
export const OpenButton = styled.button`
border: 0;
background: none;
height: 50px;
width: 50px;
cursor: pointer;
color: inherit;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 12px;
right: 12px;
z-index: 200;
border-radius: 100%;
transition: background-color .15s;
transition: transform .15s;
padding: 0;
&:focus {
outline: 0;
}
&:focus-visible {
background-color: ${props => props.theme.text}22;
}
${props => props.isOpen && `
transform: rotate(-45deg);
`}
`;
export const Cover = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
display: none;
${props => props.isOpen && `
display: block;
`}
`;
export const Modal = styled.div`
position: absolute;
top: 70px;
right: 12px;
background-color: ${props => props.theme.background};
border: 1px solid ${props => props.theme.primaryBackground};
z-index: 150;
padding: 10px 18px;
border-radius: 3px;
width: 250px;
box-sizing: border-box;
max-width: calc(100% - 20px);
box-shadow: 0 3px 6px 0 rgba(0,0,0,.3);
pointer-events: none;
opacity: 0;
transform: translateY(-10px);
transition: opacity .15s, transform .15s;
${props => props.isOpen && `
pointer-events: all;
opacity: 1;
transform: translateY(0);
`}
`;
export const Heading = styled.span`
font-size: 1.5rem;
display: block;
margin: 6px 0;
line-height: 1em;
`;

View file

@ -1,5 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { useSettingsStore } from 'stores';
import {
Wrapper,
StyledLabel,
@ -9,33 +11,62 @@ import {
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 times = {
'12h': [
'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',
],
'24h': [
'00',
'01',
'02',
'03',
'04',
'05',
'06',
'07',
'08',
'09',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'0',
],
};
const TimeRangeField = ({
label,
@ -44,6 +75,8 @@ const TimeRangeField = ({
register,
...props
}) => {
const timeFormat = useSettingsStore(state => state.timeFormat);
const [start, setStart] = useState(9);
const [end, setEnd] = useState(17);
@ -90,7 +123,7 @@ const TimeRangeField = ({
{start > end && <Selected start={start > end ? 0 : start} end={end} />}
<Handle
value={start}
label={times[start]}
label={times[timeFormat][start]}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isStartMoving.current = true;
@ -112,7 +145,7 @@ const TimeRangeField = ({
/>
<Handle
value={end}
label={times[end]}
label={times[timeFormat][end]}
onMouseDown={() => {
document.addEventListener('mousemove', handleMouseMove);
isEndMoving.current = true;

View file

@ -0,0 +1,40 @@
import {
Wrapper,
ToggleContainer,
StyledLabel,
Option,
HiddenInput,
LabelButton,
} from './toggleFieldStyle';
const ToggleField = ({
label,
id,
name,
options = [],
value,
onChange,
...props
}) => (
<Wrapper>
{label && <StyledLabel>{label}</StyledLabel>}
<ToggleContainer>
{options.map(option =>
<Option key={option}>
<HiddenInput
type="radio"
name={name}
value={option}
id={`${name}-${option}`}
checked={value === option}
onChange={() => onChange(option)}
/>
<LabelButton htmlFor={`${name}-${option}`}>{option}</LabelButton>
</Option>
)}
</ToggleContainer>
</Wrapper>
);
export default ToggleField;

View file

@ -0,0 +1,43 @@
import styled from '@emotion/styled';
export const Wrapper = styled.div`
margin: 10px 0;
`;
export const ToggleContainer = styled.div`
display: flex;
border: 1px solid ${props => props.theme.primary};
border-radius: 3px;
overflow: hidden;
`;
export const StyledLabel = styled.label`
display: block;
padding-bottom: 4px;
font-size: .9rem;
`;
export const Option = styled.div`
flex: 1;
`;
export const HiddenInput = styled.input`
height: 0;
width: 0;
position: absolute;
right: -1000px;
top: 0;
&:checked + label {
color: ${props => props.theme.background};
background-color: ${props => props.theme.primary};
}
`;
export const LabelButton = styled.label`
padding: 6px;
display: block;
text-align: center;
cursor: pointer;
user-select: none;
`;

View file

@ -2,6 +2,7 @@ export { default as TextField } from './TextField/TextField';
export { default as SelectField } from './SelectField/SelectField';
export { default as CalendarField } from './CalendarField/CalendarField';
export { default as TimeRangeField } from './TimeRangeField/TimeRangeField';
export { default as ToggleField } from './ToggleField/ToggleField';
export { default as Button } from './Button/Button';
export { default as Legend } from './Legend/Legend';
@ -11,3 +12,4 @@ export { default as Error } from './Error/Error';
export { default as Center } from './Center/Center';
export { default as Donate } from './Donate/Donate';
export { default as Settings } from './Settings/Settings';

View file

@ -136,7 +136,7 @@ const Home = () => {
<Center>
<Logo src={logo} alt="" />
</Center>
<TitleSmall>Create a</TitleSmall>
<TitleSmall>CREATE A</TitleSmall>
<TitleLarge>CRAB FIT</TitleLarge>
<Links>
<a href="#about">About</a> / <a href="#donate">Donate</a>

View file

@ -0,0 +1,13 @@
import create from 'zustand';
import { persist } from 'zustand/middleware';
export const useSettingsStore = create(persist(
set => ({
weekStart: 0,
timeFormat: '12h',
setWeekStart: weekStart => set({ weekStart }),
setTimeFormat: timeFormat => set({ timeFormat }),
}),
{ name: 'crabfit-settings' },
));