Rounder corners, thicker borders, highlight highest availability

This commit is contained in:
Ben Grant 2021-05-17 18:52:00 +10:00
parent 8b24b2e27a
commit d2e5bcc4cb
10 changed files with 81 additions and 10 deletions

View file

@ -87,7 +87,10 @@ const AvailabilityEditor = ({
{isSpecificDates && <DateLabel>{parsedDate.format('MMM D')}</DateLabel>}
<DayLabel>{parsedDate.format('ddd')}</DayLabel>
<Times>
<Times
borderRight={last}
borderLeft={x === 0 || (parsedDate).diff(isSpecificDates ? dayjs(dates[x-1], 'DDMMYYYY') : dayjs().day(dates[x-1]), 'day') > 1}
>
{timeLabels.map((timeLabel, y) => {
if (!timeLabel.time) return null;
if (!times.includes(`${timeLabel.time}-${date}`)) {

View file

@ -46,6 +46,7 @@ const AvailabilityViewer = ({
}) => {
const [tooltip, setTooltip] = useState(null);
const timeFormat = useSettingsStore(state => state.timeFormat);
const highlight = useSettingsStore(state => state.highlight);
const [filteredPeople, setFilteredPeople] = useState([]);
const [touched, setTouched] = useState(false);
const [tempFocus, setTempFocus] = useState(null);
@ -118,7 +119,10 @@ const AvailabilityViewer = ({
{isSpecificDates && <DateLabel>{parsedDate.format('MMM D')}</DateLabel>}
<DayLabel>{parsedDate.format('ddd')}</DayLabel>
<Times>
<Times
borderRight={last}
borderLeft={i === 0 || (parsedDate).diff(isSpecificDates ? dayjs(dates[i-1], 'DDMMYYYY') : dayjs().day(dates[i-1]), 'day') > 1}
>
{timeLabels.map((timeLabel, i) => {
if (!timeLabel.time) return null;
if (!times.includes(`${timeLabel.time}-${date}`)) {
@ -140,6 +144,7 @@ const AvailabilityViewer = ({
aria-label={peopleHere.join(', ')}
maxPeople={tempFocus !== null ? 1 : Math.min(max, filteredPeople.length)}
minPeople={tempFocus !== null ? 0 : Math.min(min, filteredPeople.length)}
highlight={highlight}
onMouseEnter={(e) => {
const cellBox = e.currentTarget.getBoundingClientRect();
const wrapperBox = wrapper?.current?.getBoundingClientRect() ?? { x: 0, y: 0 };

View file

@ -36,6 +36,19 @@ export const Times = styled.div`
display: flex;
flex-direction: column;
background-color: ${props => props.theme.text};
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
${props => props.borderLeft && `
border-left: 1px solid transparent;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
`}
${props => props.borderRight && `
border-right: 1px solid transparent;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
`}
`;
export const DateLabel = styled.label`
@ -56,6 +69,7 @@ export const Time = styled.div`
height: 10px;
margin: 1px;
background-color: ${props => props.theme.background};
background-origin: border-box;
${props => props.time.slice(2, 4) !== '00' && `
margin-top: -1px;
@ -66,10 +80,20 @@ export const Time = styled.div`
border-top: 2px dotted ${props.theme.text};
`}
background-image: linear-gradient(
${props => `${props.theme.primary}${Math.round((props.peopleCount/props.maxPeople)*255).toString(16)}`},
${props => `${props.theme.primary}${Math.round((props.peopleCount/props.maxPeople)*255).toString(16)}`}
${props => props.highlight && props.peopleCount === props.maxPeople ? `
background-image: repeating-linear-gradient(
45deg,
${props.theme.primary},
${props.theme.primary} 4.3px,
${props.theme.primaryDark} 4.3px,
${props.theme.primaryDark} 8.6px
);
` : `
background-image: linear-gradient(
${`${props.theme.primary}${Math.round((props.peopleCount/props.maxPeople)*255).toString(16)}`},
${`${props.theme.primary}${Math.round((props.peopleCount/props.maxPeople)*255).toString(16)}`}
);
`}
`;
export const Spacer = styled.div`
@ -174,7 +198,7 @@ export const Person = styled.button`
${props => props.filtered && `
background: ${props.theme.primary};
color: ${props.theme.background};
color: #FFFFFF;
border-color: ${props.theme.primary};
`}
`;

View file

@ -48,6 +48,9 @@ const GoogleCalendar = ({ timeZone, timeMin, timeMax, onImport }) => {
const importAvailability = () => {
setFreeBusyLoading(true);
gtag('event', 'google_cal_sync', {
'event_category': 'event',
});
window.gapi.client.calendar.freebusy.query({
timeMin,
timeMax,
@ -145,11 +148,12 @@ const GoogleCalendar = ({ timeZone, timeMin, timeMax, onImport }) => {
)}
{calendars !== undefined && (
<>
<Info>Importing will overwrite your currently inputted availability</Info>
<Info>Importing will overwrite your current availability</Info>
<Button
buttonWidth="170px"
buttonHeight="35px"
isLoading={freeBusyLoading}
disabled={freeBusyLoading}
onClick={() => importAvailability()}
>Import availability</Button>
</>

View file

@ -1,4 +1,5 @@
import { useTheme } from '@emotion/react';
import { useSettingsStore } from 'stores';
import {
Wrapper,
@ -15,16 +16,23 @@ const Legend = ({
...props
}) => {
const theme = useTheme();
const highlight = useSettingsStore(state => state.highlight);
const setHighlight = useSettingsStore(state => state.setHighlight);
return (
<Wrapper>
<Label>{min}/{total} available</Label>
<Bar onMouseOut={() => onSegmentFocus(null)}>
<Bar
onMouseOut={() => onSegmentFocus(null)}
onClick={() => setHighlight(!highlight)}
title="Click to highlight highest availability"
>
{[...Array(max+1-min).keys()].map(i => i+min).map(i =>
<Grade
key={i}
color={`${theme.primary}${Math.round((i/(max))*255).toString(16)}`}
highlight={highlight && i === max}
onMouseOver={() => onSegmentFocus(i)}
/>
)}

View file

@ -39,4 +39,14 @@ export const Bar = styled.div`
export const Grade = styled.div`
flex: 1;
background-color: ${props => props.color};
${props => props.highlight && `
background-image: repeating-linear-gradient(
45deg,
${props.theme.primary},
${props.theme.primary} 4.5px,
${props.theme.primaryDark} 4.5px,
${props.theme.primaryDark} 9px
);
`}
`;

View file

@ -58,6 +58,16 @@ const Settings = () => {
value={store.theme}
onChange={value => store.setTheme(value)}
/>
<ToggleField
label="Highlight highest availability"
name="highlight"
id="highlight"
title="Make the highest availability on the heatmap stand out"
options={['Off', 'On']}
value={store.highlight ? 'On' : 'Off'}
onChange={value => store.setHighlight(value === 'On')}
/>
</Modal>
</>
);

View file

@ -11,13 +11,14 @@ const ToggleField = ({
label,
id,
name,
title = '',
options = [],
value,
onChange,
...props
}) => (
<Wrapper>
{label && <StyledLabel>{label}</StyledLabel>}
{label && <StyledLabel title={title}>{label}</StyledLabel>}
<ToggleContainer>
{options.map(option =>

View file

@ -72,6 +72,10 @@ export const LoginForm = styled.form`
}
@media (max-width: 400px) {
grid-template-columns: 1fr;
& div:last-child {
--btn-width: 100%;
}
}
`;

View file

@ -6,10 +6,12 @@ export const useSettingsStore = create(persist(
weekStart: 0,
timeFormat: '12h',
theme: 'System',
highlight: false,
setWeekStart: weekStart => set({ weekStart }),
setTimeFormat: timeFormat => set({ timeFormat }),
setTheme: theme => set({ theme }),
setHighlight: highlight => set({ highlight }),
}),
{ name: 'crabfit-settings' },
));