diff --git a/src/app/App.tsx b/src/app/App.tsx index 1b5eb36..241df27 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -2,7 +2,7 @@ import { GroupContextProvider } from "../common/context/groupContext"; import { LecturerContextProvider } from "../common/context/lecturerContext"; import { PreloadedListsContextProvider } from "../common/context/preloadedListsContext"; import { WeekContextProvider } from "../common/context/weekContext"; -import { useCurrentDateParams } from "../common/utils/useCurrentDateParams"; +import { useCurrentDateParams } from "../common/hooks/useCurrentDateParams"; import ThemeContextProvider from "../common/context/themeContext"; import Navbar from "../containers/navbar"; import ScheduleRouter from "../containers/router"; diff --git a/src/common/constants/dayOptions.ts b/src/common/constants/dayOptions.ts index 630189b..4a80356 100644 --- a/src/common/constants/dayOptions.ts +++ b/src/common/constants/dayOptions.ts @@ -1,22 +1,24 @@ import { ListOption } from '../../types/ListOption'; import { ScreenSize } from '../../types/ScreenSize'; -export const DAY_OPTIONS: Partial[]>> = { +export type DaysRange = `${number}-${number}`; + +export const DAY_OPTIONS: Partial[]>> = { [ScreenSize.ExtraSmall]: [ - {label: 'ПН', value: '0-1'}, - {label: 'ВТ', value: '1-2'}, - {label: 'СР', value: '2-3'}, - {label: 'ЧТ', value: '3-4'}, - {label: 'ПТ', value: '4-5'}, - {label: 'СБ', value: '5-6'}, + {label: 'ПН', value: '1-1'}, + {label: 'ВТ', value: '2-2'}, + {label: 'СР', value: '3-3'}, + {label: 'ЧТ', value: '4-4'}, + {label: 'ПТ', value: '5-5'}, + {label: 'СБ', value: '6-6'}, ], [ScreenSize.Small]: [ - {label: 'ПН-ВТ', value: '0-2'}, - {label: 'СР-ЧТ', value: '2-4'}, - {label: 'ПТ-СБ', value: '4-6'}, + {label: 'ПН-ВТ', value: '1-2'}, + {label: 'СР-ЧТ', value: '3-4'}, + {label: 'ПТ-СБ', value: '5-6'}, ], [ScreenSize.Medium]: [ - {label: 'ПН-СР', value: '0-3'}, - {label: 'ЧТ-СБ', value: '3-6'}, + {label: 'ПН-СР', value: '1-3'}, + {label: 'ЧТ-СБ', value: '4-6'}, ], }; diff --git a/src/common/utils/getScreenSize.ts b/src/common/constants/screenSize.ts similarity index 57% rename from src/common/utils/getScreenSize.ts rename to src/common/constants/screenSize.ts index bcc762d..a07d980 100644 --- a/src/common/utils/getScreenSize.ts +++ b/src/common/constants/screenSize.ts @@ -1,12 +1,8 @@ import { ScreenSize } from '../../types/ScreenSize'; -const SCRREN_SIZES: Record = { +export const SCREEN_SIZES: Record = { [ScreenSize.Big]: '1441px', [ScreenSize.Medium]: '1440px', [ScreenSize.Small]: '988px', [ScreenSize.ExtraSmall]: '639px', -}; - -export const getScreenSize = (mode: ScreenSize) => { - return SCRREN_SIZES[mode]; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/common/context/sliceOptionsContext.tsx b/src/common/context/sliceOptionsContext.tsx index 26caaa0..db6b176 100644 --- a/src/common/context/sliceOptionsContext.tsx +++ b/src/common/context/sliceOptionsContext.tsx @@ -1,12 +1,75 @@ -import { createContext, useContext } from 'react'; +import { createContext, useContext, useEffect, useState } from 'react'; +import { ScreenSize } from '../../types/ScreenSize'; +import { useScreenSize } from '../hooks/useScreenSize'; +import { clamp, inRange, range } from 'lodash-es'; -export interface Slice { - start?: number; - end?: number; +export type Slice = [number, number]; + +export interface SliceContext { + slice: Slice; + setSlice: (slice: Slice) => void; + setCurrentDay: (currentDay: number) => void; } -const SliceOptionsContext = createContext({}); +const defaultValue: Slice = [0, 0]; + +const SliceOptionsContext = createContext({ + slice: defaultValue, + setSlice: () => ({}), + setCurrentDay: () => ({}), +}); export const useSliceOptionsContext = () => useContext(SliceOptionsContext); +interface SliceContextProviderProps { + children: React.ReactNode | React.ReactNode[]; + initialDay: number; +} + +const DAYS_COUNT = 6; + +const ScreenSizeSlicesCount: Record = { + [ScreenSize.Big]: 1, + [ScreenSize.Medium]: 2, + [ScreenSize.Small]: 3, + [ScreenSize.ExtraSmall]: 6 +}; + +const generateSlices = (screenSize: ScreenSize): Slice[] => { + const numberOfSlices = ScreenSizeSlicesCount[screenSize]; + const sliceRange = DAYS_COUNT / numberOfSlices; + + return range(0, numberOfSlices).map( + (index) => ([sliceRange * index + 1, sliceRange * index + sliceRange]) + ); +}; + +const getCurrentSlice = (screenSize: ScreenSize, currendDay: number): Slice => { + const slices = generateSlices(screenSize); + + return slices.find(([start, end]) => inRange(clamp(currendDay, 1, DAYS_COUNT) , start, end + 1)) || defaultValue; +}; + +export const SliceContextProvider = ({ children, initialDay }: SliceContextProviderProps) => { + const { screenSize } = useScreenSize(); + const [slice, setSlice] = useState(defaultValue); + const [currentDay, setCurrentDay] = useState(initialDay); + + useEffect(() => { + setSlice(getCurrentSlice(screenSize, currentDay)); + }, [screenSize, currentDay]); + + const value: SliceContext = { + slice, + setSlice, + setCurrentDay, + }; + + return ( + + {children} + + ); +}; + export default SliceOptionsContext.Provider; \ No newline at end of file diff --git a/src/common/utils/useCurrentDateParams.ts b/src/common/hooks/useCurrentDateParams.ts similarity index 87% rename from src/common/utils/useCurrentDateParams.ts rename to src/common/hooks/useCurrentDateParams.ts index 63d012f..b91198c 100644 --- a/src/common/utils/useCurrentDateParams.ts +++ b/src/common/hooks/useCurrentDateParams.ts @@ -4,9 +4,9 @@ import { CurrentTime } from '../../models/CurrentTime'; export const useCurrentDateParams = () => { const [dateParams, setDateParams] = useState({ - currentDay: -1, - currentLesson: -1, - currentWeek: -1, + currentDay: 0, + currentLesson: 0, + currentWeek: 0, }); useEffect(() => { diff --git a/src/common/hooks/useScreenSize.ts b/src/common/hooks/useScreenSize.ts new file mode 100644 index 0000000..b4fb8df --- /dev/null +++ b/src/common/hooks/useScreenSize.ts @@ -0,0 +1,28 @@ +import { ScreenSize } from '../../types/ScreenSize'; +import { useCallback, useEffect, useState } from "react"; +import { SCREEN_SIZES } from '../constants/screenSize'; + +export const useScreenSize = () => { + const detectScreenSize = () => { + for (let key in ScreenSize) { + if (window.innerWidth <= parseInt(SCREEN_SIZES[ScreenSize[key]])) { + return ScreenSize[key]; + } + } + + return ScreenSize.Big; + }; + + const [screenSize, setScreenSize] = useState(detectScreenSize()); + + const updateScreenSize = useCallback(() => setScreenSize(detectScreenSize()), []); + + useEffect(() => { + window.addEventListener("resize", updateScreenSize); + + return () => window.removeEventListener("resize", updateScreenSize); + }, [updateScreenSize]); + + + return { screenSize }; +}; diff --git a/src/common/styles/styles.ts b/src/common/styles/styles.ts index 8c7c190..bbabd38 100644 --- a/src/common/styles/styles.ts +++ b/src/common/styles/styles.ts @@ -1,6 +1,6 @@ import styled, { css } from 'styled-components'; import { flexbox, space } from 'styled-system'; -import { getScreenSize } from '../utils/getScreenSize'; +import { SCREEN_SIZES } from '../constants/screenSize'; import { getValueFromTheme } from '../../common/utils/getValueFromTheme'; import { Link } from 'react-router-dom'; import { ScreenSize } from '../../types/ScreenSize'; @@ -19,10 +19,10 @@ export const Pictogram = styled.img` `; export const media = { - [ScreenSize.ExtraSmall]: `@media(max-width: ${getScreenSize(ScreenSize.ExtraSmall)})`, - [ScreenSize.Small]: `@media(max-width: ${getScreenSize(ScreenSize.Small)})`, - [ScreenSize.Medium]: `@media(max-width: ${getScreenSize(ScreenSize.Medium)})`, - [ScreenSize.Big]: `@media(min-width: ${getScreenSize(ScreenSize.Big)})`, + [ScreenSize.ExtraSmall]: `@media(max-width: ${SCREEN_SIZES[ScreenSize.ExtraSmall]})`, + [ScreenSize.Small]: `@media(max-width: ${SCREEN_SIZES[ScreenSize.Small]})`, + [ScreenSize.Medium]: `@media(max-width: ${SCREEN_SIZES[ScreenSize.Medium]})`, + [ScreenSize.Big]: `@media(min-width: ${SCREEN_SIZES[ScreenSize.Big]})`, }; export const Logo = styled(Pictogram)` diff --git a/src/common/utils/useCurrentMode.ts b/src/common/utils/useCurrentMode.ts deleted file mode 100644 index 83adc12..0000000 --- a/src/common/utils/useCurrentMode.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ScreenSize } from '../../types/ScreenSize'; -import { useEffect, useState } from "react"; -import { getScreenSize } from './getScreenSize'; - -export const useCurrentMode = () => { - const [currentMode, setCurrentMode] = useState(null); - - useEffect(() => { - detectCurrentMode(); - window.addEventListener("resize", detectCurrentMode); - - return () => window.removeEventListener("resize", detectCurrentMode); - }, []); - - const detectCurrentMode = () => { - for (let key in ScreenSize) { - if (window.innerWidth <= parseInt(getScreenSize(ScreenSize[key]))) { - setCurrentMode(ScreenSize[key]); - return; - } - } - - setCurrentMode(ScreenSize.Big); - }; - - return currentMode; -}; diff --git a/src/containers/ScheduleTable/ScheduleTable.tsx b/src/containers/ScheduleTable/ScheduleTable.tsx new file mode 100644 index 0000000..d265492 --- /dev/null +++ b/src/containers/ScheduleTable/ScheduleTable.tsx @@ -0,0 +1,38 @@ +import ScheduleRow from "../scheduleRow"; +import { ScheduleMatrix, ScheduleMatrixRow, generateScheduleMatrix } from "../../common/utils/generateScheduleMatrix"; +import { useWeekContext } from "../../common/context/weekContext"; +import { useSliceOptionsContext } from '../../common/context/sliceOptionsContext'; + +interface ScheduleWrapperProps { + data: T[]; +} + +const weekValue: Record = { + firstWeek: "scheduleFirstWeek", + secondWeek: "scheduleSecondWeek", +}; + +const ScheduleTable = ({ data }: ScheduleWrapperProps) => { + const { slice } = useSliceOptionsContext(); + const { currentWeek } = useWeekContext(); + + const generateScheduleRows = (scheduleMatrix: ScheduleMatrix) => { + return scheduleMatrix.map((item: ScheduleMatrixRow, i: number) => { + const [start, end] = slice; + const slicedDataset = item.slice(start - 1, end); + return ; + }); + }; + + if (!data) { + return null; + } + + return ( + <> + {generateScheduleRows(generateScheduleMatrix(data[weekValue[currentWeek]]))} + + ); +}; + +export default ScheduleTable; diff --git a/src/containers/schedule/schedule.tsx b/src/containers/schedule/schedule.tsx index 38df80e..ac2dc1c 100644 --- a/src/containers/schedule/schedule.tsx +++ b/src/containers/schedule/schedule.tsx @@ -1,17 +1,18 @@ import React from 'react'; +import { range } from 'lodash-es'; import TimeDivider from '../../components/timeDivider'; import { CurrentDayContainer, GridContainer } from './schedule.style'; -import ScheduleHeader from '../scheduleHeader'; +import { ScheduleHeader } from '../ScheduleHeader'; import { TIME_POINTS } from '../../common/constants/scheduleParams'; import { useSliceOptionsContext } from '../../common/context/sliceOptionsContext'; -import { useCurrentDateParams } from '../../common/utils/useCurrentDateParams'; +import { useCurrentDateParams } from '../../common/hooks/useCurrentDateParams'; interface ScheduleProps { children: React.ReactNode; } const Schedule = ({ children }: ScheduleProps) => { - const sliceOptions = useSliceOptionsContext(); + const { slice } = useSliceOptionsContext(); const { currentDay } = useCurrentDateParams(); const dynamicGeneratedTable = React.Children.map(children, (child, index) => ( @@ -21,19 +22,15 @@ const Schedule = ({ children }: ScheduleProps) => { )); - const isDayInSlice = () => { - if (sliceOptions.start && sliceOptions.end && currentDay) { - return currentDay >= sliceOptions.start && currentDay < sliceOptions.end; - } + const [start, end] = slice; - return true; - }; - - const gridDayStart = currentDay || 0 + 1; + const currentDayColumn = + range(start, end + 1) + .indexOf(currentDay) + 1; return ( - {gridDayStart > 0 && isDayInSlice() ? : null} + {currentDayColumn ? : null} {dynamicGeneratedTable} diff --git a/src/containers/scheduleDayToggler/scheduleDayToggler.tsx b/src/containers/scheduleDayToggler/scheduleDayToggler.tsx index a32e4b0..7008819 100644 --- a/src/containers/scheduleDayToggler/scheduleDayToggler.tsx +++ b/src/containers/scheduleDayToggler/scheduleDayToggler.tsx @@ -1,47 +1,33 @@ import OptionMultipleToggler from "../../components/optionMultipleToggler"; -import { useCurrentMode } from "../../common/utils/useCurrentMode"; -import { DAY_OPTIONS } from "../../common/constants/dayOptions"; +import { useScreenSize } from "../../common/hooks/useScreenSize"; +import { DAY_OPTIONS, DaysRange } from "../../common/constants/dayOptions"; import { ScheduleDayTogglerContainer } from "./scheduleDayToggler.style"; -import { useCurrentDateParams } from "../../common/utils/useCurrentDateParams"; -import { ScreenSize } from '../../types/ScreenSize'; - -interface ScheduleDayTogglerProps { - onChange: (value: string) => void; -} - -const ScheduleDayToggler = ({ onChange }: ScheduleDayTogglerProps) => { - const mode = useCurrentMode(); - const { currentDay } = useCurrentDateParams(); - - const getDayOption = () => { - if (!currentDay || currentDay === 6 || currentDay < 0) { - return 0; - } - - switch (mode) { - case ScreenSize.ExtraSmall: - return currentDay; - case ScreenSize.Small: - return Math.floor(currentDay / 2); - case ScreenSize.Medium: - return Math.floor(currentDay / 3) - default: - return 0; - } - }; +import { Slice, useSliceOptionsContext } from '../../common/context/sliceOptionsContext'; + +const ScheduleDayToggler = () => { + const { screenSize } = useScreenSize(); + const { slice, setSlice } = useSliceOptionsContext(); - if (!mode || mode === ScreenSize.Big) { + const options = DAY_OPTIONS[screenSize]; + + if (!options) { return null; } + const handleChange = (value: DaysRange) => { + const [start, end] = value.split('-'); + + setSlice([+start, +end]); + }; + + const convertSlice = ([start, end]: Slice): DaysRange => `${start}-${end}`; + return ( ); diff --git a/src/containers/scheduleHeader/scheduleHeader.style.ts b/src/containers/scheduleHeader/WeekDay.tsx similarity index 100% rename from src/containers/scheduleHeader/scheduleHeader.style.ts rename to src/containers/scheduleHeader/WeekDay.tsx diff --git a/src/containers/scheduleHeader/index.ts b/src/containers/scheduleHeader/index.ts index f755a48..495b207 100644 --- a/src/containers/scheduleHeader/index.ts +++ b/src/containers/scheduleHeader/index.ts @@ -1,3 +1 @@ -import ScheduleHeader from './scheduleHeader'; - -export default ScheduleHeader; \ No newline at end of file +export { ScheduleHeader } from './ScheduleHeader'; diff --git a/src/containers/scheduleHeader/scheduleHeader.tsx b/src/containers/scheduleHeader/scheduleHeader.tsx index fd93352..434e840 100644 --- a/src/containers/scheduleHeader/scheduleHeader.tsx +++ b/src/containers/scheduleHeader/scheduleHeader.tsx @@ -1,27 +1,23 @@ -import { WeekDay } from './scheduleHeader.style'; +import { WeekDay } from './WeekDay'; import { useSliceOptionsContext } from '../../common/context/sliceOptionsContext'; const DAYS = [ - {label: 'Понеділок', value: 'monday'}, - {label: 'Вівторок', value: 'tuesday'}, - {label: 'Середа', value: 'wednesday'}, - {label: 'Четвер', value: 'thursday'}, - {label: 'П\'ятниця', value: 'friday'}, - {label: 'Субота', value: 'saturday'}, + 'Понеділок', + 'Вівторок', + 'Середа', + 'Четвер', + 'П\'ятниця', + 'Субота', ]; -const ScheduleHeader = () => { - const sliceOptions = useSliceOptionsContext(); - - const slicedDays = sliceOptions ? DAYS.slice(sliceOptions.start, sliceOptions.end) : DAYS; +export const ScheduleHeader = () => { + const { slice } = useSliceOptionsContext(); + const [start, end] = slice; + const slicedDays = DAYS.slice(start - 1, end); return ( <> - {slicedDays.map(item => { - return {item.label}; - })} + {slicedDays.map(item => ({item}))} ); }; - -export default ScheduleHeader; \ No newline at end of file diff --git a/src/containers/scheduleWrapper/scheduleWrapper.tsx b/src/containers/scheduleWrapper/scheduleWrapper.tsx index 1c01a38..807bfc2 100644 --- a/src/containers/scheduleWrapper/scheduleWrapper.tsx +++ b/src/containers/scheduleWrapper/scheduleWrapper.tsx @@ -1,14 +1,13 @@ import { useEffect, useState } from "react"; -import ScheduleRow from "../scheduleRow"; import { GridWrapper } from "./scheduleWrapper.style"; -import SliceOptionsContext, { Slice } from "../../common/context/sliceOptionsContext"; +import { SliceContextProvider } from "../../common/context/sliceOptionsContext"; import ScheduleDayToggler from "../scheduleDayToggler"; import Schedule from "../schedule"; -import { ScheduleMatrix, ScheduleMatrixRow, generateScheduleMatrix } from "../../common/utils/generateScheduleMatrix"; -import { useWeekContext } from "../../common/context/weekContext"; import { useLecturerContext } from "../../common/context/lecturerContext"; import { useGroupContext } from "../../common/context/groupContext"; import { PagedResponse } from '../../models/PagedResponse'; +import { useCurrentDateParams } from '../../common/hooks/useCurrentDateParams'; +import ScheduleTable from '../ScheduleTable/ScheduleTable'; interface ScheduleWrapperProps { getData: (id: string) => Promise>; @@ -19,18 +18,11 @@ const ScheduleWrapper = ({ getData, contextType, }: ScheduleWrapperProps) => { - const [sliceParams, setSliceParams] = useState({}); const [data, setData] = useState(); - - const { currentWeek } = useWeekContext(); + const { currentDay } = useCurrentDateParams() const { lecturer } = useLecturerContext(); const { group } = useGroupContext(); - const weekValue: Record= { - firstWeek: "scheduleFirstWeek", - secondWeek: "scheduleSecondWeek", - }; - useEffect(() => { const contextValue = contextType === "lecturer" ? lecturer?.id : group?.id; @@ -41,25 +33,6 @@ const ScheduleWrapper = ({ } }, [lecturer, group, contextType, getData]); - const generateScheduleRows = (scheduleMatrix: ScheduleMatrix) => { - return scheduleMatrix.map((item: ScheduleMatrixRow, i: number) => { - const slicedDataset = sliceParams - ? item.slice(sliceParams.start, sliceParams.end) - : item; - return ; - }); - }; - - const setSlice = (value: string) => { - if (!value) { - setSliceParams({}); - return; - } - - const ranges = value.split("-"); - setSliceParams({ start: +ranges[0], end: +ranges[1] }); - }; - if (!data) { return null; } @@ -67,14 +40,12 @@ const ScheduleWrapper = ({ return (
- - + + - {generateScheduleRows( - generateScheduleMatrix(data[weekValue[currentWeek]]) - )} + - +
);