Calendar day next (#3513)
This commit is contained in:
parent
5ffc99cb08
commit
e963fa625b
@ -16,8 +16,11 @@ import { useTranslation } from './translate';
|
||||
interface Props {
|
||||
className?: string;
|
||||
date: Date;
|
||||
hasNextDay: boolean;
|
||||
now: Date;
|
||||
scheduled: EntryInfo[];
|
||||
setNextDay: () => void;
|
||||
setPrevDay: () => void;
|
||||
}
|
||||
|
||||
const HOURS = ((): number[] => {
|
||||
@ -30,16 +33,14 @@ const HOURS = ((): number[] => {
|
||||
return hours;
|
||||
})();
|
||||
|
||||
const NOOP = () => undefined;
|
||||
|
||||
function Day ({ className, date, now, scheduled }: Props): React.ReactElement<Props> {
|
||||
function Day ({ className, date, hasNextDay, now, scheduled, setNextDay, setPrevDay }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const monthRef = useRef(MONTHS.map((m) => t(m)));
|
||||
|
||||
const [isToday, nowHours, nowMinutes] = useMemo(
|
||||
() => [
|
||||
date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(),
|
||||
date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear() && date.getDate() === now.getDate(),
|
||||
now.getHours(),
|
||||
now.getMinutes()
|
||||
],
|
||||
@ -51,14 +52,14 @@ function Day ({ className, date, now, scheduled }: Props): React.ReactElement<Pr
|
||||
<div className='dayHeader'>
|
||||
<Button
|
||||
icon='chevron-left'
|
||||
isDisabled
|
||||
onClick={NOOP}
|
||||
isDisabled={isToday}
|
||||
onClick={setPrevDay}
|
||||
/>
|
||||
<div>{date.getDate()} {monthRef.current[date.getMonth()]} {date.getFullYear()} {isToday && <DayTime />}</div>
|
||||
<Button
|
||||
icon='chevron-right'
|
||||
isDisabled
|
||||
onClick={NOOP}
|
||||
isDisabled={!hasNextDay}
|
||||
onClick={setNextDay}
|
||||
/>
|
||||
</div>
|
||||
<div className='hoursContainer'>
|
||||
@ -87,10 +88,6 @@ export default React.memo(styled(Day)`
|
||||
font-size: 1.25rem;
|
||||
justify-content: space-between;
|
||||
padding: 1rem 1.5rem 0;
|
||||
|
||||
.ui--Button {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hoursContainer {
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { EntryInfo } from './types';
|
||||
import { DateState, EntryInfo } from './types';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Button } from '@polkadot/react-components';
|
||||
|
||||
@ -14,110 +14,22 @@ import MonthDay from './MonthDay';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
hasNextMonth: boolean;
|
||||
lastDay: number;
|
||||
now: Date;
|
||||
onChange: (date: Date) => void;
|
||||
scheduled: EntryInfo[];
|
||||
setDay: (day: number) => void;
|
||||
setNextMonth: () => void;
|
||||
setPrevMonth: () => void;
|
||||
state: DateState;
|
||||
}
|
||||
|
||||
interface DateState {
|
||||
dateMonth: Date;
|
||||
dateMonthNext: Date;
|
||||
dateSelected: Date;
|
||||
days: number[];
|
||||
startClass: string;
|
||||
}
|
||||
|
||||
function newZeroDate (input: Date): Date {
|
||||
const date = new Date(input);
|
||||
|
||||
date.setHours(0);
|
||||
date.setMinutes(0);
|
||||
date.setSeconds(0);
|
||||
date.setMilliseconds(0);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
function nextMonth (date: Date, firstDay = 1): Date {
|
||||
const currMonth = date.getMonth();
|
||||
|
||||
return currMonth === 11
|
||||
? new Date(date.getFullYear() + 1, 0, firstDay)
|
||||
: new Date(date.getFullYear(), currMonth + 1, firstDay);
|
||||
}
|
||||
|
||||
function prevMonth (date: Date): Date {
|
||||
const currMonth = date.getMonth();
|
||||
|
||||
return currMonth === 0
|
||||
? new Date(date.getFullYear() - 1, 11, 1)
|
||||
: new Date(date.getFullYear(), currMonth - 1, 1);
|
||||
}
|
||||
|
||||
function getDateState (_dateMonth: Date, _dateSelected: Date): DateState {
|
||||
const dateMonth = newZeroDate(_dateMonth);
|
||||
|
||||
dateMonth.setDate(1);
|
||||
|
||||
const dateMonthNext = nextMonth(dateMonth);
|
||||
const dateSelected = newZeroDate(_dateSelected);
|
||||
const numDays = nextMonth(dateMonth, 0).getDate();
|
||||
const days: number[] = [];
|
||||
|
||||
for (let i = 1; i <= numDays; i++) {
|
||||
days.push(i);
|
||||
}
|
||||
|
||||
return {
|
||||
dateMonth,
|
||||
dateMonthNext,
|
||||
dateSelected,
|
||||
days,
|
||||
startClass: `start${DAYS[dateMonth.getDay()]}`
|
||||
};
|
||||
}
|
||||
|
||||
function Month ({ className, now, onChange, scheduled }: Props): React.ReactElement<Props> {
|
||||
function Month ({ className, hasNextMonth, lastDay, now, scheduled, setDay, setNextMonth, setPrevMonth, state: { dateMonth, dateSelected, days, startClass } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ dateMonth, dateMonthNext, dateSelected, days, startClass }, setDate] = useState(getDateState(now, now));
|
||||
|
||||
const dayOfWeekRef = useRef(DAYS.map((d) => t(d)));
|
||||
const monthRef = useRef(MONTHS.map((m) => t(m)));
|
||||
|
||||
const hasNextMonth = useMemo(
|
||||
(): boolean => {
|
||||
const nextTime = dateMonthNext.getTime();
|
||||
|
||||
return scheduled.some(({ dateTime }) => dateTime >= nextTime);
|
||||
},
|
||||
[dateMonthNext, scheduled]
|
||||
);
|
||||
|
||||
const _nextMonth = useCallback(
|
||||
() => setDate(({ dateMonth, dateSelected }) => getDateState(nextMonth(dateMonth), dateSelected)),
|
||||
[]
|
||||
);
|
||||
|
||||
const _prevMonth = useCallback(
|
||||
() => setDate(({ dateMonth, dateSelected }) => getDateState(prevMonth(dateMonth), dateSelected)),
|
||||
[]
|
||||
);
|
||||
|
||||
const _setDay = useCallback(
|
||||
(day: number) => setDate(({ dateMonth }): DateState => {
|
||||
const date = new Date(dateMonth);
|
||||
|
||||
date.setDate(day);
|
||||
|
||||
return getDateState(date, date);
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
onChange(dateSelected);
|
||||
}, [dateSelected, onChange]);
|
||||
|
||||
const [isCurrYear, isCurrMonth, isNowYear, isNowMonth, isOlderMonth] = useMemo(
|
||||
() => [
|
||||
dateMonth.getFullYear() === dateSelected.getFullYear(),
|
||||
@ -136,13 +48,13 @@ function Month ({ className, now, onChange, scheduled }: Props): React.ReactElem
|
||||
<Button
|
||||
icon='chevron-left'
|
||||
isDisabled={isNowYear && (isOlderMonth || isNowMonth)}
|
||||
onClick={_prevMonth}
|
||||
onClick={setPrevMonth}
|
||||
/>
|
||||
<div>{monthRef.current[dateMonth.getMonth()]} {dateMonth.getFullYear()}</div>
|
||||
<Button
|
||||
icon='chevron-right'
|
||||
isDisabled={!hasNextMonth}
|
||||
onClick={_nextMonth}
|
||||
onClick={setNextMonth}
|
||||
/>
|
||||
</div>
|
||||
<div className='dayOfWeek'>
|
||||
@ -156,10 +68,10 @@ function Month ({ className, now, onChange, scheduled }: Props): React.ReactElem
|
||||
dateMonth={dateMonth}
|
||||
day={day}
|
||||
isCurrent={isCurrYear && isCurrMonth && day === dateSelected.getDate()}
|
||||
isDisabled={isNowYear && (isOlderMonth || (isNowMonth && now.getDate() > day))}
|
||||
isDisabled={(isNowYear && (isOlderMonth || (isNowMonth && now.getDate() > day))) || (!hasNextMonth && day > lastDay)}
|
||||
key={day}
|
||||
scheduled={scheduled}
|
||||
setDay={_setDay}
|
||||
setDay={setDay}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { DateState } from './types';
|
||||
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Tabs } from '@polkadot/react-components';
|
||||
|
||||
@ -10,6 +12,7 @@ import Day from './Day';
|
||||
import Month from './Month';
|
||||
import { useTranslation } from './translate';
|
||||
import useScheduled from './useScheduled';
|
||||
import { getDateState, nextMonth, prevMonth } from './util';
|
||||
|
||||
interface Props {
|
||||
basePath: string;
|
||||
@ -22,7 +25,7 @@ function CalendarApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
const { t } = useTranslation();
|
||||
const scheduled = useScheduled();
|
||||
const [now, setNow] = useState(new Date());
|
||||
const [selected, setSelected] = useState(new Date());
|
||||
const [dateState, setDateState] = useState(getDateState(now, now));
|
||||
|
||||
const itemsRef = useRef([{
|
||||
isRoot: true,
|
||||
@ -38,6 +41,72 @@ function CalendarApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
};
|
||||
}, []);
|
||||
|
||||
const [hasNextMonth, hasNextDay, lastDay] = useMemo(
|
||||
() => {
|
||||
const nextDay = new Date(dateState.dateSelected);
|
||||
|
||||
nextDay.setDate(nextDay.getDate() + 1);
|
||||
|
||||
const nextDayTime = nextDay.getTime();
|
||||
const nextMonthTime = dateState.dateMonthNext.getTime();
|
||||
const hasNextMonth = scheduled.some(({ dateTime }) => dateTime >= nextMonthTime);
|
||||
const hasNextDay = hasNextMonth || scheduled.some(({ dateTime }) => dateTime >= nextDayTime);
|
||||
const lastDay = hasNextMonth
|
||||
? 42 // more than days in month
|
||||
: scheduled.reduce((lastDay: number, { date }): number => {
|
||||
return date.getFullYear() === dateState.dateMonth.getFullYear() && date.getMonth() === dateState.dateMonth.getMonth()
|
||||
? Math.max(lastDay, date.getDate())
|
||||
: lastDay;
|
||||
}, 0);
|
||||
|
||||
return [hasNextMonth, hasNextDay, lastDay];
|
||||
},
|
||||
[dateState, scheduled]
|
||||
);
|
||||
|
||||
const _nextMonth = useCallback(
|
||||
() => setDateState(({ dateMonth, dateSelected }) => getDateState(nextMonth(dateMonth), dateSelected)),
|
||||
[]
|
||||
);
|
||||
|
||||
const _prevMonth = useCallback(
|
||||
() => setDateState(({ dateMonth, dateSelected }) => getDateState(prevMonth(dateMonth), dateSelected)),
|
||||
[]
|
||||
);
|
||||
|
||||
const _nextDay = useCallback(
|
||||
() => setDateState(({ dateSelected }): DateState => {
|
||||
const date = new Date(dateSelected);
|
||||
|
||||
date.setDate(date.getDate() + 1);
|
||||
|
||||
return getDateState(date, date);
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const _prevDay = useCallback(
|
||||
() => setDateState(({ dateSelected }): DateState => {
|
||||
const date = new Date(dateSelected);
|
||||
|
||||
date.setDate(date.getDate() - 1);
|
||||
|
||||
return getDateState(date, date);
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const _setDay = useCallback(
|
||||
(day: number) => setDateState(({ dateMonth }): DateState => {
|
||||
const date = new Date(dateMonth);
|
||||
|
||||
date.setDate(day);
|
||||
|
||||
return getDateState(date, date);
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<main className={className}>
|
||||
<header>
|
||||
@ -48,14 +117,22 @@ function CalendarApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
</header>
|
||||
<div className='calendarFlex'>
|
||||
<Day
|
||||
date={selected}
|
||||
date={dateState.dateSelected}
|
||||
hasNextDay={hasNextDay}
|
||||
now={now}
|
||||
scheduled={scheduled}
|
||||
setNextDay={_nextDay}
|
||||
setPrevDay={_prevDay}
|
||||
/>
|
||||
<Month
|
||||
hasNextMonth={hasNextMonth}
|
||||
lastDay={lastDay}
|
||||
now={now}
|
||||
onChange={setSelected}
|
||||
scheduled={scheduled}
|
||||
setDay={_setDay}
|
||||
setNextMonth={_nextMonth}
|
||||
setPrevMonth={_prevMonth}
|
||||
state={dateState}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -15,3 +15,11 @@ export interface EntryInfo {
|
||||
isPending?: boolean;
|
||||
type: EntryType;
|
||||
}
|
||||
|
||||
export interface DateState {
|
||||
dateMonth: Date;
|
||||
dateMonthNext: Date;
|
||||
dateSelected: Date;
|
||||
days: number[];
|
||||
startClass: string;
|
||||
}
|
||||
|
||||
57
packages/page-calendar/src/util.ts
Normal file
57
packages/page-calendar/src/util.ts
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2017-2020 @polkadot/app-calendar authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DateState } from './types';
|
||||
|
||||
import { DAYS } from './constants';
|
||||
|
||||
export function newZeroDate (input: Date): Date {
|
||||
const date = new Date(input);
|
||||
|
||||
date.setHours(0);
|
||||
date.setMinutes(0);
|
||||
date.setSeconds(0);
|
||||
date.setMilliseconds(0);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
export function nextMonth (date: Date, firstDay = 1): Date {
|
||||
const currMonth = date.getMonth();
|
||||
|
||||
return currMonth === 11
|
||||
? new Date(date.getFullYear() + 1, 0, firstDay)
|
||||
: new Date(date.getFullYear(), currMonth + 1, firstDay);
|
||||
}
|
||||
|
||||
export function prevMonth (date: Date): Date {
|
||||
const currMonth = date.getMonth();
|
||||
|
||||
return currMonth === 0
|
||||
? new Date(date.getFullYear() - 1, 11, 1)
|
||||
: new Date(date.getFullYear(), currMonth - 1, 1);
|
||||
}
|
||||
|
||||
export function getDateState (_dateMonth: Date, _dateSelected: Date): DateState {
|
||||
const dateMonth = newZeroDate(_dateMonth);
|
||||
|
||||
dateMonth.setDate(1);
|
||||
|
||||
const dateMonthNext = nextMonth(dateMonth);
|
||||
const dateSelected = newZeroDate(_dateSelected);
|
||||
const numDays = nextMonth(dateMonth, 0).getDate();
|
||||
const days: number[] = [];
|
||||
|
||||
for (let i = 1; i <= numDays; i++) {
|
||||
days.push(i);
|
||||
}
|
||||
|
||||
return {
|
||||
dateMonth,
|
||||
dateMonthNext,
|
||||
dateSelected,
|
||||
days,
|
||||
startClass: `start${DAYS[dateMonth.getDay()]}`
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user