diff --git a/crabfit-frontend/public/fonts/molot.otf b/crabfit-frontend/public/fonts/molot.otf deleted file mode 100644 index 28c3049..0000000 Binary files a/crabfit-frontend/public/fonts/molot.otf and /dev/null differ diff --git a/crabfit-frontend/public/fonts/molot.woff b/crabfit-frontend/public/fonts/molot.woff new file mode 100644 index 0000000..831ef3f Binary files /dev/null and b/crabfit-frontend/public/fonts/molot.woff differ diff --git a/crabfit-frontend/public/fonts/molot.woff2 b/crabfit-frontend/public/fonts/molot.woff2 new file mode 100644 index 0000000..a5299b3 Binary files /dev/null and b/crabfit-frontend/public/fonts/molot.woff2 differ diff --git a/crabfit-frontend/public/fonts/samuraibob.ttf b/crabfit-frontend/public/fonts/samuraibob.ttf deleted file mode 100644 index 8ab735c..0000000 Binary files a/crabfit-frontend/public/fonts/samuraibob.ttf and /dev/null differ diff --git a/crabfit-frontend/public/fonts/samuraibob.woff b/crabfit-frontend/public/fonts/samuraibob.woff new file mode 100644 index 0000000..a0ac9b4 Binary files /dev/null and b/crabfit-frontend/public/fonts/samuraibob.woff differ diff --git a/crabfit-frontend/public/fonts/samuraibob.woff2 b/crabfit-frontend/public/fonts/samuraibob.woff2 new file mode 100644 index 0000000..69f0a93 Binary files /dev/null and b/crabfit-frontend/public/fonts/samuraibob.woff2 differ diff --git a/crabfit-frontend/public/index.css b/crabfit-frontend/public/index.css index 1101eb6..952af59 100644 --- a/crabfit-frontend/public/index.css +++ b/crabfit-frontend/public/index.css @@ -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; } diff --git a/crabfit-frontend/src/App.tsx b/crabfit-frontend/src/App.tsx index 28cb828..74cdd38 100644 --- a/crabfit-frontend/src/App.tsx +++ b/crabfit-frontend/src/App.tsx @@ -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 ( - {process.env.NODE_ENV !== 'production' && } + {process.env.NODE_ENV !== 'production' && } ({ html: { @@ -63,6 +64,8 @@ const App = () => { + + ); diff --git a/crabfit-frontend/src/components/CalendarField/CalendarField.tsx b/crabfit-frontend/src/components/CalendarField/CalendarField.tsx index 3f79424..dea287e 100644 --- a/crabfit-frontend/src/components/CalendarField/CalendarField.tsx +++ b/crabfit-frontend/src/components/CalendarField/CalendarField.tsx @@ -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 ( diff --git a/crabfit-frontend/src/components/Settings/Settings.tsx b/crabfit-frontend/src/components/Settings/Settings.tsx new file mode 100644 index 0000000..a2ac8d2 --- /dev/null +++ b/crabfit-frontend/src/components/Settings/Settings.tsx @@ -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 ( + <> + setIsOpen(!isOpen)} title="Options" + > + + + + setIsOpen(false)} /> + + Options + + store.setWeekStart(value === 'Monday' ? 1 : 0)} + /> + + store.setTimeFormat(value)} + /> + + + ); +}; + +export default Settings; diff --git a/crabfit-frontend/src/components/Settings/settingsStyle.ts b/crabfit-frontend/src/components/Settings/settingsStyle.ts new file mode 100644 index 0000000..2c4fe75 --- /dev/null +++ b/crabfit-frontend/src/components/Settings/settingsStyle.ts @@ -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; +`; diff --git a/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx b/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx index d044b4d..dfb0695 100644 --- a/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx +++ b/crabfit-frontend/src/components/TimeRangeField/TimeRangeField.tsx @@ -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 && end ? 0 : start} end={end} />} { document.addEventListener('mousemove', handleMouseMove); isStartMoving.current = true; @@ -112,7 +145,7 @@ const TimeRangeField = ({ /> { document.addEventListener('mousemove', handleMouseMove); isEndMoving.current = true; diff --git a/crabfit-frontend/src/components/ToggleField/ToggleField.tsx b/crabfit-frontend/src/components/ToggleField/ToggleField.tsx new file mode 100644 index 0000000..83aa3b7 --- /dev/null +++ b/crabfit-frontend/src/components/ToggleField/ToggleField.tsx @@ -0,0 +1,40 @@ +import { + Wrapper, + ToggleContainer, + StyledLabel, + Option, + HiddenInput, + LabelButton, +} from './toggleFieldStyle'; + +const ToggleField = ({ + label, + id, + name, + options = [], + value, + onChange, + ...props +}) => ( + + {label && {label}} + + + {options.map(option => + + )} + + +); + +export default ToggleField; diff --git a/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts new file mode 100644 index 0000000..a1e91aa --- /dev/null +++ b/crabfit-frontend/src/components/ToggleField/toggleFieldStyle.ts @@ -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; +`; diff --git a/crabfit-frontend/src/components/index.ts b/crabfit-frontend/src/components/index.ts index 3ce1ec4..2cff948 100644 --- a/crabfit-frontend/src/components/index.ts +++ b/crabfit-frontend/src/components/index.ts @@ -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'; diff --git a/crabfit-frontend/src/pages/Home/Home.tsx b/crabfit-frontend/src/pages/Home/Home.tsx index 97aea75..4e718a3 100644 --- a/crabfit-frontend/src/pages/Home/Home.tsx +++ b/crabfit-frontend/src/pages/Home/Home.tsx @@ -136,7 +136,7 @@ const Home = () => {
- Create a + CREATE A CRAB FIT About / Donate diff --git a/crabfit-frontend/src/stores/index.ts b/crabfit-frontend/src/stores/index.ts new file mode 100644 index 0000000..7eea586 --- /dev/null +++ b/crabfit-frontend/src/stores/index.ts @@ -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' }, +));