From cde03c9ed85ff471b0b4a4438d7053212ff399b3 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 08:52:07 +0800 Subject: [PATCH 1/8] Create calendar react-play --- package.json | 7 +- src/meta/play-meta.js | 14 + src/plays/calendar/Calendar.scss | 324 ++++++++++++++++++++++ src/plays/calendar/Calendar.tsx | 34 +++ src/plays/calendar/CalendarDayTile.tsx | 40 +++ src/plays/calendar/CalendarEvent.tsx | 37 +++ src/plays/calendar/CalendarEventForm.tsx | 143 ++++++++++ src/plays/calendar/CalendarEventInfo.tsx | 23 ++ src/plays/calendar/CalendarEvents.tsx | 40 +++ src/plays/calendar/CalendarEventsMore.tsx | 29 ++ src/plays/calendar/CalendarGrid.tsx | 55 ++++ src/plays/calendar/CalendarNavigation.tsx | 49 ++++ src/plays/calendar/Context.tsx | 140 ++++++++++ src/plays/calendar/ModalContainer.tsx | 28 ++ src/plays/calendar/Readme.md | 1 + src/plays/calendar/cover.png | Bin 0 -> 87299 bytes src/plays/index.js | 1 + 17 files changed, 963 insertions(+), 2 deletions(-) create mode 100644 src/plays/calendar/Calendar.scss create mode 100644 src/plays/calendar/Calendar.tsx create mode 100644 src/plays/calendar/CalendarDayTile.tsx create mode 100644 src/plays/calendar/CalendarEvent.tsx create mode 100644 src/plays/calendar/CalendarEventForm.tsx create mode 100644 src/plays/calendar/CalendarEventInfo.tsx create mode 100644 src/plays/calendar/CalendarEvents.tsx create mode 100644 src/plays/calendar/CalendarEventsMore.tsx create mode 100644 src/plays/calendar/CalendarGrid.tsx create mode 100644 src/plays/calendar/CalendarNavigation.tsx create mode 100644 src/plays/calendar/Context.tsx create mode 100644 src/plays/calendar/ModalContainer.tsx create mode 100644 src/plays/calendar/Readme.md create mode 100644 src/plays/calendar/cover.png diff --git a/package.json b/package.json index 9d0d6d3ce2..50646899a1 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,11 @@ "private": true, "dependencies": { "@giscus/react": "^2.0.3", + "@types/lodash": "^4.14.182", "@types/react": "^18.0.6", "@types/react-dom": "^18.0.2", + "date-fns": "^2.28.0", + "lodash": "^4.17.21", "node-sass": "^7.0.1", "plop": "^3.0.5", "react": "^18.0.0", @@ -51,8 +54,8 @@ ] }, "devDependencies": { - "typescript": "^4.6.4", + "puppeteer": "^13.7.0", "react-snap": "^1.23.0", - "puppeteer": "^13.7.0" + "typescript": "^4.6.4" } } diff --git a/src/meta/play-meta.js b/src/meta/play-meta.js index 5edc878dec..2ad49d559d 100644 --- a/src/meta/play-meta.js +++ b/src/meta/play-meta.js @@ -16,6 +16,7 @@ QuoteGenerator, PasswordGenerator, WhyTypescript, NetlifyCardGame, +Calendar, //import play here } from "plays"; @@ -240,5 +241,18 @@ export const plays = [ video: '', language: 'js', featured: true, + }, { + id: 'pl-calendar', + name: 'Calendar', + description: 'Simple calendar app to manage events', + component: () => {return }, + path: '/plays/calendar', + level: 'Intermediate', + tags: 'JSX,Hooks,Typescript', + github: 'vincentBCP', + cover: '', + blog: '', + video: '', + language: 'ts' }, //replace new play item here ]; diff --git a/src/plays/calendar/Calendar.scss b/src/plays/calendar/Calendar.scss new file mode 100644 index 0000000000..b84e19dbb3 --- /dev/null +++ b/src/plays/calendar/Calendar.scss @@ -0,0 +1,324 @@ +$calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); + +.calendar { + background-color: white; + padding: 30px; +} + +.calendar-navigation { + display: flex; + align-items: center; + margin-bottom: 30px; + + button { + background-color: transparent; + border: 1px solid lightgray; + border-radius: 5px; + font-size: 14px; + padding: 10px 15px; + font-weight: 500; + margin-right: 20px; + } + + .calendar-navigation-arrow { + width: 35px; + height: 35px; + display: inline-flex; + justify-content: center; + align-items: center; + border-radius: 50%; + font-size: 28px; + cursor: pointer; + padding-top: 3px; + } + + .calendar-navigation-current-date { + font-size: 24px; + margin-left: 20px; + } + + button, + .calendar-navigation-arrow { + &:hover { + background-color: rgba(0,0,0,0.05); + } + + &:active { + background-color: darkgray; + } + } +} + +.calendar-body { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + border-left: 1px solid lightgray; + border-bottom: 1px solid lightgray; +} + +.calendar-day-tile { + height: 150px; + border-right: 1px solid lightgray; + border-top: 1px solid lightgray; + + .calendar-week, + .calendar-day { + display: inline-block; + text-align: center; + width: 100%; + font-size: 12px; + } + + .calendar-week { + text-transform: uppercase; + font-weight: 500; + color: gray; + } +} + +.calendar-modal { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding-top: 120px; + z-index: 999; + + .calendar-modal-content { + width: 500px; + margin: auto; + background-color: white; + padding: 30px 30px 30px 30px; + border-radius: 5px; + box-shadow: $calendar-box-shadow; + + & > div:first-of-type { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 20px; + + span:last-of-type { + cursor: pointer; + margin-left: auto; + } + } + } +} + +.calendar-event-form { + input[name='title'] { + width: 100%; + border: none; + border-bottom: 1px solid lightgray; + font-size: 18px; + outline: none; + padding-bottom: 5px; + margin-bottom: 20px; + } + + p { + font-size: 14px; + color: gray; + } + + & > div:first-of-type { + display: flex; + gap: 30px; + + div { + width: 50%; + + label { + display: block; + color: gray; + font-size: 14px; + } + + input { + width: 100%; + outline: none; + border: none; + border-bottom: 1px solid lightgray; + } + } + } + + & > div:last-of-type { + display: flex; + justify-content: flex-end; + gap: 15px; + margin-top: 40px; + + button { + min-width: 80px; + padding: 10px; + border: none; + outline: none; + border-radius: 5px; + font-weight: 500; + transition-duration: 0.3s; + + &.delete { + background-color: transparent; + margin-right: auto; + color: red; + + &:hover { + background-color: #ffe3e3; + } + + &:active { + background-color: #ffcaca; + } + } + + &.close { + background-color: transparent; + + &:hover { + background-color: rgba(0,0,0,0.05); + } + + &:active { + background-color: rgba(0,0,0,0.2); + } + } + + &.save { + color: white; + background-color: #1a73e8; + + &:hover { + background-color: #1a84e8; + } + + &:active { + background-color: #1aa7e8; + } + } + } + } +} + +.calendar-event-info { + p:first-of-type { + font-size: 18px; + } + + div { + text-align: right; + + button { + min-width: 80px; + padding: 10px; + border: none; + outline: none; + border-radius: 5px; + font-weight: 500; + transition-duration: 0.3s; + + &:hover { + background-color: rgba(0,0,0,0.1); + } + + &:active { + background-color: rgba(0,0,0,0.2); + } + } + } +} + +.calendar-events { + margin-top: 5px; +} + +.calendar-events-more { + padding: 3px 10px; + display: block; + cursor: pointer; + position: relative; + + &:hover { + background-color: rgba(0,0,0,0.05); + } + + & > span { + font-weight: 600; + font-size: 12px; + } + + .calendar-events-more-popup { + display: none; + position: absolute; + left: -200px; + top: -200%; + width: 200px; + background-color: white; + padding: 20px 10px; + border-radius: 5px; + box-shadow: $calendar-box-shadow; + overflow: auto; + max-height: 200px; + + & > div:first-of-type { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 10px; + + span { + &:first-of-type { + font-size: 14px; + color: gray; + text-transform: uppercase; + } + + &:last-of-type { + font-size: 20px; + } + } + } + } + + &:hover { + .calendar-events-more-popup { + display: block; + } + } +} + +.calendar-event { + display: flex; + align-items: center; + gap: 7px; + overflow: hidden; + cursor: pointer; + padding: 3px 10px; + transition-duration: 0.3s; + + &:hover { + background-color: rgba(0,0,0,0.05); + } + + div { + width: 10px; + height: 10px; + border-radius: 50%; + background-color: #1a73e8; + flex-shrink: 0; + } + + span { + font-size: 12px; + white-space: nowrap; + + &:last-of-type { + font-weight: 500; + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; + } + } +} \ No newline at end of file diff --git a/src/plays/calendar/Calendar.tsx b/src/plays/calendar/Calendar.tsx new file mode 100644 index 0000000000..556f09550c --- /dev/null +++ b/src/plays/calendar/Calendar.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { getPlayById } from 'meta/play-meta-util'; + +import PlayHeader from 'common/playlists/PlayHeader'; +import CalendarGrid from './CalendarGrid' +import { ContextProvider } from './Context'; + +import './Calendar.scss' +import ModalContainer from './ModalContainer'; + +function Calendar(props:any) { + // Do not remove the below lines. + // The following code is to fetch the current play from the URL + const { id } = props; + const play = getPlayById(id); + + // Your Code Start below. + + return ( + <> +
+ +
+ + + + +
+
+ + ); +} + +export default Calendar; \ No newline at end of file diff --git a/src/plays/calendar/CalendarDayTile.tsx b/src/plays/calendar/CalendarDayTile.tsx new file mode 100644 index 0000000000..db3de00f91 --- /dev/null +++ b/src/plays/calendar/CalendarDayTile.tsx @@ -0,0 +1,40 @@ +import React, { useContext } from 'react' +import { format } from 'date-fns' +import CalendarEvents from './CalendarEvents' +import CalendarEventForm from './CalendarEventForm' +import { Context } from './Context' + +interface Props { + date: Date, + showWeek?: boolean +} + +const CalendarDayTile = ({ date, showWeek }: Props) => { + const context = useContext(Context) + const { showModal, hideModal } = context + + const handleClick = () => { + showModal( + , + format(date, 'ccc, MMMM dd') + ) + } + + return ( +
+ {showWeek && {format(date, "EEE")}} + + {format(date, date.getDate() === 1 ? "MMM d" : "d")} + + +
+ ) +} + +export default CalendarDayTile \ No newline at end of file diff --git a/src/plays/calendar/CalendarEvent.tsx b/src/plays/calendar/CalendarEvent.tsx new file mode 100644 index 0000000000..6a969bdc2b --- /dev/null +++ b/src/plays/calendar/CalendarEvent.tsx @@ -0,0 +1,37 @@ +import React, { useContext } from 'react' +import { format } from 'date-fns' +import CalendarEventForm from './CalendarEventForm' +import { Context } from './Context' + +interface Props { + event: any +} + +const CalendarEvent = ({ event }: Props) => { + const context = useContext(Context) + const { showModal, hideModal } = context + + const handleClick = () => { + showModal( + , + format(new Date(event.date), 'ccc, MMMM dd') + ) + } + + return ( +
+
+ {format(new Date(`1990-01-01 ${event.startTime}`), 'h:mm aaa')} + {event.title} +
+ ) +} + +export default CalendarEvent \ No newline at end of file diff --git a/src/plays/calendar/CalendarEventForm.tsx b/src/plays/calendar/CalendarEventForm.tsx new file mode 100644 index 0000000000..d36dc5ddfb --- /dev/null +++ b/src/plays/calendar/CalendarEventForm.tsx @@ -0,0 +1,143 @@ +import { format } from 'date-fns/esm' +import React, { useState, useContext, useEffect } from 'react' +import CalendarEventInfo from './CalendarEventInfo' +import { Context } from './Context' + +interface Props { + date: Date, + event?: any, + onCancel: VoidFunction +} + +const CalendarEventForm = ({ date, event, onCancel }: Props) => { + const [calendarEvent, setCalendarEvent] = useState() + const [data, setData] = useState() + const [editable, setEditable] = useState(false) + const context = useContext(Context) + const { addEvent, updateEvent, deleteEvent } = context + + useEffect(() => { + if (event) { + setData({...event}) + setCalendarEvent({...event}) + setEditable(false) + return + } + + setData({ + date: format(date, 'yyyy-MM-dd'), + title: '', + startTime: '', + endTime: '' + }) + setEditable(true) + }, [date, event]) + + const handleSave = () => { + if (!data.title) { + alert('Please provide title') + return + } + + if (!data.startTime) { + alert('Please provide start time') + return + } + + if (!data.endTime) { + alert('Please provide end time') + return + } + + if (event) { + updateEvent(data) + setCalendarEvent({...data}) + setEditable(false) + return + } + + addEvent(data) + onCancel() + } + + const handleDelete = () => { + deleteEvent(event) + onCancel() + } + + const handleCancel = () => { + if (event) { + setEditable(false) + return + } + + onCancel() + } + + const handleEdit = () => { + setEditable(true) + } + + if (!data) return null + + if (calendarEvent && !editable) { + return ( + + ) + } + + return ( +
ev.stopPropagation()} + > + setData({ ...data, title: ev.target.value })} + /> +
+
+ + + setData({ ...data, startTime: ev.target.value }) + } + /> +
+
+ + setData({ ...data, endTime: ev.target.value })} + /> +
+
+
+ {Boolean(event) && ( + + )} + + +
+
+ ) +} + +export default CalendarEventForm \ No newline at end of file diff --git a/src/plays/calendar/CalendarEventInfo.tsx b/src/plays/calendar/CalendarEventInfo.tsx new file mode 100644 index 0000000000..d525bb0e7f --- /dev/null +++ b/src/plays/calendar/CalendarEventInfo.tsx @@ -0,0 +1,23 @@ +import { format } from 'date-fns' +import React from 'react' + +interface Props { + event: any, + onEdit: VoidFunction +} + +const CalendarEventInfo = ({ event, onEdit }: Props) => { + return ( +
+

{event.title}

+

{format(new Date(`1990-01-01 ${event.startTime}`), 'h:mm a')} - {format(new Date(`1990-01-01 ${event.endTime}`), 'h:mm a')}

+
+ +
+
+ ) +} + +export default CalendarEventInfo \ No newline at end of file diff --git a/src/plays/calendar/CalendarEvents.tsx b/src/plays/calendar/CalendarEvents.tsx new file mode 100644 index 0000000000..089ce3fb35 --- /dev/null +++ b/src/plays/calendar/CalendarEvents.tsx @@ -0,0 +1,40 @@ +import React, { useState, useContext, useEffect } from 'react' +import CalendarEvent from './CalendarEvent' +import CalendarEventsMore from './CalendarEventsMore' +import { Context } from './Context' + +interface Props { + date: Date +} + +const MAX_VISIBLE_ITEMS = 3 + +const CalendarEvents = ({ date }: Props) => { + const [events, setEvents] = useState([]) + const context = useContext(Context) + const { getEvents } = context + + useEffect(() => { + setEvents(getEvents(date)) + }, [date, getEvents]) + + return ( +
ev.stopPropagation()}> + {events + .filter((e: any, index: number) => index < MAX_VISIBLE_ITEMS) + .map((event: any) => ( + + ))} + {events.length > MAX_VISIBLE_ITEMS && ( + index >= MAX_VISIBLE_ITEMS + )} + /> + )} +
+ ); +} + +export default CalendarEvents \ No newline at end of file diff --git a/src/plays/calendar/CalendarEventsMore.tsx b/src/plays/calendar/CalendarEventsMore.tsx new file mode 100644 index 0000000000..dc2735acbe --- /dev/null +++ b/src/plays/calendar/CalendarEventsMore.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import { format } from 'date-fns' +import CalendarEvent from './CalendarEvent' + +interface Props { + date: Date, + events: any +} + +const CalendarEventsMore = ({ date, events }: Props) => { + return ( +
+ {events.length} more +
+
+ {format(date, "ccc")} + {format(date, "dd")} +
+
+ {events.map((event: any) => ( + + ))} +
+
+
+ ); +} + +export default CalendarEventsMore \ No newline at end of file diff --git a/src/plays/calendar/CalendarGrid.tsx b/src/plays/calendar/CalendarGrid.tsx new file mode 100644 index 0000000000..d1cd63fa0c --- /dev/null +++ b/src/plays/calendar/CalendarGrid.tsx @@ -0,0 +1,55 @@ +import React, { useState } from 'react' +import CalendarNavigation from './CalendarNavigation' +import CalendarDayTile from './CalendarDayTile' +import { endOfMonth, format, startOfMonth, startOfWeek } from 'date-fns' +import { addDays, endOfWeek } from 'date-fns/esm' + +const WEEK_STARTS_ON = 0 // Sunday + +const CalendarGrid = () => { + const [currentDate, setCurrentDate] = useState(new Date()) + + const generateTiles = () => { + const startDate = startOfWeek(startOfMonth(currentDate), { weekStartsOn: WEEK_STARTS_ON }) + const endDate = endOfWeek(endOfMonth(currentDate), { weekStartsOn: WEEK_STARTS_ON }) + + let curDate = startDate + + const tiles: React.ReactNode[] = [] + + do { + tiles.push( + + ) + + curDate = addDays(curDate, 1) + } while(format(curDate, 'yyyy-MM-dd') !== format(endDate, 'yyyy-MM-dd')) + + tiles.push( + + ) + + return tiles + } + + return ( +
+ setCurrentDate(date)} + /> +
+ { generateTiles() } +
+
+ ) +} + +export default CalendarGrid \ No newline at end of file diff --git a/src/plays/calendar/CalendarNavigation.tsx b/src/plays/calendar/CalendarNavigation.tsx new file mode 100644 index 0000000000..cbf123eb55 --- /dev/null +++ b/src/plays/calendar/CalendarNavigation.tsx @@ -0,0 +1,49 @@ +import { addMonths, format } from 'date-fns' +import React from 'react' + +interface Props { + currentDate: Date, + onDateChange: (date: Date) => void +} + +const CalendarNavigation = ({ currentDate, onDateChange }: Props) => { + const navigateTo = (direction: number) => { + /* + -1 = prev + 0 = today + 1 = next + */ + + if (direction === 0) { + onDateChange(new Date()) + return + } + + onDateChange(addMonths(currentDate, direction)) + } + + return ( +
+ + navigateTo(-1)} + > + < + + navigateTo(1)} + > + > + + + {format(currentDate, 'MMMM yyyy')} + +
+ ) +} + +export default CalendarNavigation \ No newline at end of file diff --git a/src/plays/calendar/Context.tsx b/src/plays/calendar/Context.tsx new file mode 100644 index 0000000000..ef59332f83 --- /dev/null +++ b/src/plays/calendar/Context.tsx @@ -0,0 +1,140 @@ +import { format } from "date-fns"; +import React, { useState, useCallback } from "react"; +import { orderBy } from 'lodash' + +export const Context = React.createContext({ + modalTitle: '', + modalContent: undefined, + getEvents: (date: Date) => {}, + addEvent: (event: any) => {}, + updateEvent: (event: any) => {}, + deleteEvent: (event: any) => {}, + showModal: (content: React.ReactNode, title?: '') => {}, + hideModal: () => {} +}) + +export const ContextProvider = ({ children }: any) => { + const [events, setEvents] = useState([ + { + id: '1', + date: '2022-05-11', + title: 'Lorem ipsum dolor sit amet consectutar adispiscing', + startTime: '08:30', + endTime: '09:00' + }, + { + id: '2', + date: '2022-05-19', + title: 'Lorem ipsum dolor', + startTime: '08:30', + endTime: '09:00' + }, + { + id: '3', + date: '2022-05-23', + title: 'Lorem ipsum dolor', + startTime: '08:30', + endTime: '09:00' + }, + { + id: '4', + date: '2022-05-27', + title: 'Lorem ipsum dolor', + startTime: '08:30', + endTime: '09:00' + }, + { + id: '5', + date: '2022-05-27', + title: 'Lorem ipsum dolor', + startTime: '10:30', + endTime: '11:30' + }, + { + id: '6', + date: '2022-05-27', + title: 'Lorem ipsum dolor', + startTime: '12:00', + endTime: '13:30' + }, + { + id: '7', + date: '2022-05-27', + title: 'Lorem ipsum dolor', + startTime: '15:00', + endTime: '15:30' + }, + { + id: '8', + date: '2022-05-27', + title: 'Lorem ipsum dolor', + startTime: '16:30', + endTime: '17:00' + } + ]) + const [modalTitle, setModalTitle] = useState('') + const [modalContent, setModalContent] = useState(undefined) + + const getEvents = useCallback((date: Date) => { + return orderBy(events.filter(e => e.date === format(date, 'yyyy-MM-dd')), ['startTime']) + }, [events]) + + const addEvent = useCallback((event: any) => { + event.id = (new Date()).getTime().toString() + setEvents(oldValue => ([...oldValue, event])) + }, []) + + const updateEvent = useCallback((event: any) => { + setEvents(oldValue => { + const newEvents = [...oldValue] + const index = newEvents.findIndex(e => e.id === event.id) + + if (index === -1) return newEvents + + newEvents[index] = event + + return newEvents + }) + }, []) + + const deleteEvent = useCallback((event: any) => { + setEvents(oldValue => { + const newEvents = [...oldValue] + const index = newEvents.findIndex(e => e.id === event.id) + + if (index === -1) return newEvents + + newEvents.splice(index, 1) + + return newEvents + }) + }, []) + + const showModal = (content: React.ReactNode, title?: '') => { + setModalTitle(title || '') + setModalContent(content) + } + + const hideModal = () => { + setModalTitle('') + setModalContent(undefined) + } + + return ( + + {children} + + ) +} + diff --git a/src/plays/calendar/ModalContainer.tsx b/src/plays/calendar/ModalContainer.tsx new file mode 100644 index 0000000000..e70fe5c311 --- /dev/null +++ b/src/plays/calendar/ModalContainer.tsx @@ -0,0 +1,28 @@ +import React, { useContext } from 'react' +import { Context } from './Context' + +const ModalContainer = () => { + const context = useContext(Context) + const { modalContent, modalTitle, hideModal } = context + + if (!modalContent) return null + + return ( +
+
ev.stopPropagation()} + > +
+ {Boolean(modalTitle) && ( + {modalTitle} + )} + +
+ {modalContent} +
+
+ ); +} + +export default ModalContainer \ No newline at end of file diff --git a/src/plays/calendar/Readme.md b/src/plays/calendar/Readme.md new file mode 100644 index 0000000000..39f2c21786 --- /dev/null +++ b/src/plays/calendar/Readme.md @@ -0,0 +1 @@ +# Calendar \ No newline at end of file diff --git a/src/plays/calendar/cover.png b/src/plays/calendar/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..ff9f8462726502c3439322faf1011411bcf2c5ab GIT binary patch literal 87299 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 Date: Thu, 19 May 2022 09:10:33 +0800 Subject: [PATCH 2/8] Update Readme.md --- src/plays/calendar/Readme.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plays/calendar/Readme.md b/src/plays/calendar/Readme.md index 39f2c21786..d88e0dd8a9 100644 --- a/src/plays/calendar/Readme.md +++ b/src/plays/calendar/Readme.md @@ -1 +1,3 @@ -# Calendar \ No newline at end of file +# Calendar + +A simple calendar to add, update, and delete events. \ No newline at end of file From dee87b24ad19461978bbade2a681fd60e6281f6b Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 12:18:56 +0800 Subject: [PATCH 3/8] [plays/calendar] Remove unnecessary use of useCallback --- src/plays/calendar/Context.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plays/calendar/Context.tsx b/src/plays/calendar/Context.tsx index ef59332f83..9b6b64878b 100644 --- a/src/plays/calendar/Context.tsx +++ b/src/plays/calendar/Context.tsx @@ -1,5 +1,5 @@ import { format } from "date-fns"; -import React, { useState, useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { orderBy } from 'lodash' export const Context = React.createContext({ @@ -79,12 +79,12 @@ export const ContextProvider = ({ children }: any) => { return orderBy(events.filter(e => e.date === format(date, 'yyyy-MM-dd')), ['startTime']) }, [events]) - const addEvent = useCallback((event: any) => { + const addEvent = (event: any) => { event.id = (new Date()).getTime().toString() setEvents(oldValue => ([...oldValue, event])) - }, []) + } - const updateEvent = useCallback((event: any) => { + const updateEvent = (event: any) => { setEvents(oldValue => { const newEvents = [...oldValue] const index = newEvents.findIndex(e => e.id === event.id) @@ -95,9 +95,9 @@ export const ContextProvider = ({ children }: any) => { return newEvents }) - }, []) + } - const deleteEvent = useCallback((event: any) => { + const deleteEvent = (event: any) => { setEvents(oldValue => { const newEvents = [...oldValue] const index = newEvents.findIndex(e => e.id === event.id) @@ -108,7 +108,7 @@ export const ContextProvider = ({ children }: any) => { return newEvents }) - }, []) + } const showModal = (content: React.ReactNode, title?: '') => { setModalTitle(title || '') From 898b62981c154aa4544823922cf8c46fbe18317c Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 13:34:20 +0800 Subject: [PATCH 4/8] [plays/calendar] Add more dummy data --- src/plays/calendar/Context.tsx | 137 ++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 60 deletions(-) diff --git a/src/plays/calendar/Context.tsx b/src/plays/calendar/Context.tsx index 9b6b64878b..85c62278ef 100644 --- a/src/plays/calendar/Context.tsx +++ b/src/plays/calendar/Context.tsx @@ -1,5 +1,5 @@ -import { format } from "date-fns"; -import React, { useCallback, useState } from "react"; +import { addDays, endOfMonth, endOfWeek, format, getWeekOfMonth, isWeekend, startOfMonth, startOfWeek } from "date-fns"; +import React, { useCallback, useEffect, useState } from "react"; import { orderBy } from 'lodash' export const Context = React.createContext({ @@ -14,67 +14,84 @@ export const Context = React.createContext({ }) export const ContextProvider = ({ children }: any) => { - const [events, setEvents] = useState([ - { - id: '1', - date: '2022-05-11', - title: 'Lorem ipsum dolor sit amet consectutar adispiscing', - startTime: '08:30', - endTime: '09:00' - }, - { - id: '2', - date: '2022-05-19', - title: 'Lorem ipsum dolor', - startTime: '08:30', - endTime: '09:00' - }, - { - id: '3', - date: '2022-05-23', - title: 'Lorem ipsum dolor', - startTime: '08:30', - endTime: '09:00' - }, - { - id: '4', - date: '2022-05-27', - title: 'Lorem ipsum dolor', - startTime: '08:30', - endTime: '09:00' - }, - { - id: '5', - date: '2022-05-27', - title: 'Lorem ipsum dolor', - startTime: '10:30', - endTime: '11:30' - }, - { - id: '6', - date: '2022-05-27', - title: 'Lorem ipsum dolor', - startTime: '12:00', - endTime: '13:30' - }, - { - id: '7', - date: '2022-05-27', - title: 'Lorem ipsum dolor', - startTime: '15:00', - endTime: '15:30' - }, - { - id: '8', - date: '2022-05-27', - title: 'Lorem ipsum dolor', - startTime: '16:30', - endTime: '17:00' - } - ]) + const [events, setEvents] = useState([]) const [modalTitle, setModalTitle] = useState('') const [modalContent, setModalContent] = useState(undefined) + useEffect(() => { + // create fake events + + const startDate = startOfWeek(startOfMonth(new Date()), { weekStartsOn: 0 }) + const endDate = endOfWeek(endOfMonth(new Date()), { weekStartsOn: 0 }) + + let curDate = startDate + + let events: any[] = [] + + const addEvent = (date: Date) => { + // add event only if date is Mon - Fri + if (isWeekend(date)) return + + events.push({ + id: format(date, 'yyyy-MM-dd'), + date: format(date, 'yyyy-MM-dd'), + title: 'Daily stand up', + startTime: '09:30', + endTime: '10:00' + }) + + if (getWeekOfMonth(date) % 2 === 0 && date.getDay() === 2) { + events.push({ + id: format(date, 'yyyy-MM-dd') + '-key', + date: format(date, 'yyyy-MM-dd'), + title: 'Sprint Planning', + startTime: '15:00', + endTime: '17:00' + }) + } + } + + do { + addEvent(curDate) + curDate = addDays(curDate, 1) + } while(format(curDate, 'yyyy-MM-dd') !== format(endDate, 'yyyy-MM-dd')) + + addEvent(curDate) + + events = events.concat([ + { + id: '1', + date: format(new Date(), 'yyyy-MM') + '-13', + title: 'Lorem ipsum dolor sit amet', + startTime: '07:30', + endTime: '08:30' + }, + { + id: '2', + date: format(new Date(), 'yyyy-MM') + '-13', + title: 'Lorem ipsum dolor sit amet', + startTime: '10:00', + endTime: '11:00' + }, + { + id: '3', + date: format(new Date(), 'yyyy-MM') + '-13', + title: 'Lorem ipsum dolor sit amet', + startTime: '12:30', + endTime: '13:30' + }, + { + id: '4', + date: format(new Date(), 'yyyy-MM') + '-13', + title: 'Lorem ipsum dolor sit amet', + startTime: '14:00', + endTime: '14:30' + } + ]) + + setEvents(events) + }, []) + const getEvents = useCallback((date: Date) => { return orderBy(events.filter(e => e.date === format(date, 'yyyy-MM-dd')), ['startTime']) }, [events]) From 698bfc13735fa39637e3789f3d0c8f2cb7c8d874 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 14:09:10 +0800 Subject: [PATCH 5/8] [plays/calendar] Use unique classname and fix UI mobile issue --- src/plays/calendar/Calendar.scss | 70 ++++++++++++++--------- src/plays/calendar/Calendar.tsx | 2 +- src/plays/calendar/CalendarDayTile.tsx | 6 +- src/plays/calendar/CalendarEvent.tsx | 2 +- src/plays/calendar/CalendarEventForm.tsx | 2 +- src/plays/calendar/CalendarEventInfo.tsx | 2 +- src/plays/calendar/CalendarEvents.tsx | 2 +- src/plays/calendar/CalendarEventsMore.tsx | 4 +- src/plays/calendar/CalendarGrid.tsx | 2 +- src/plays/calendar/CalendarNavigation.tsx | 8 +-- src/plays/calendar/ModalContainer.tsx | 6 +- 11 files changed, 62 insertions(+), 44 deletions(-) diff --git a/src/plays/calendar/Calendar.scss b/src/plays/calendar/Calendar.scss index b84e19dbb3..088ee0e144 100644 --- a/src/plays/calendar/Calendar.scss +++ b/src/plays/calendar/Calendar.scss @@ -1,14 +1,15 @@ -$calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); +$vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); -.calendar { +.vincentBCP-calendar { background-color: white; padding: 30px; } -.calendar-navigation { +.vincentBCP-calendar-navigation { display: flex; align-items: center; margin-bottom: 30px; + user-select: none; button { background-color: transparent; @@ -20,7 +21,7 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); margin-right: 20px; } - .calendar-navigation-arrow { + .vincentBCP-calendar-navigation-arrow { width: 35px; height: 35px; display: inline-flex; @@ -32,13 +33,13 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); padding-top: 3px; } - .calendar-navigation-current-date { + .vincentBCP-calendar-navigation-current-date { font-size: 24px; margin-left: 20px; } button, - .calendar-navigation-arrow { + .vincentBCP-calendar-navigation-arrow { &:hover { background-color: rgba(0,0,0,0.05); } @@ -49,34 +50,34 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.calendar-body { +.vincentBCP-calendar-body { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-left: 1px solid lightgray; border-bottom: 1px solid lightgray; } -.calendar-day-tile { +.vincentBCP-calendar-day-tile { height: 150px; border-right: 1px solid lightgray; border-top: 1px solid lightgray; - .calendar-week, - .calendar-day { + .vincentBCP-calendar-week, + .vincentBCP-calendar-day { display: inline-block; text-align: center; width: 100%; font-size: 12px; } - .calendar-week { + .vincentBCP-calendar-week { text-transform: uppercase; font-weight: 500; color: gray; } } -.calendar-modal { +.vincentBCP-calendar-modal { position: absolute; top: 0; left: 0; @@ -85,13 +86,13 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); padding-top: 120px; z-index: 999; - .calendar-modal-content { + .vincentBCP-calendar-modal-content { width: 500px; margin: auto; background-color: white; padding: 30px 30px 30px 30px; border-radius: 5px; - box-shadow: $calendar-box-shadow; + box-shadow: $vincentBCP-calendar-box-shadow; & > div:first-of-type { display: flex; @@ -104,10 +105,14 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); margin-left: auto; } } + + @media (max-width: 768px) { + max-width: 90%; + } } } -.calendar-event-form { +.vincentBCP-calendar-event-form { input[name='title'] { width: 100%; border: none; @@ -202,7 +207,7 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.calendar-event-info { +.vincentBCP-calendar-event-info { p:first-of-type { font-size: 18px; } @@ -230,12 +235,12 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.calendar-events { +.vincentBCP-calendar-events { margin-top: 5px; } -.calendar-events-more { - padding: 3px 10px; +.vincentBCP-calendar-events-more { + padding: 0 10px; display: block; cursor: pointer; position: relative; @@ -247,9 +252,10 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); & > span { font-weight: 600; font-size: 12px; + white-space: nowrap; } - .calendar-events-more-popup { + .vincentBCP-calendar-events-more-popup { display: none; position: absolute; left: -200px; @@ -258,7 +264,7 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); background-color: white; padding: 20px 10px; border-radius: 5px; - box-shadow: $calendar-box-shadow; + box-shadow: $vincentBCP-calendar-box-shadow; overflow: auto; max-height: 200px; @@ -283,16 +289,22 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } &:hover { - .calendar-events-more-popup { + .vincentBCP-calendar-events-more-popup { display: block; } } + + @media (max-width: 425px) { + span { + font-size: 10px; + } + } } -.calendar-event { +.vincentBCP-calendar-event { display: flex; align-items: center; - gap: 7px; + gap: 5px; overflow: hidden; cursor: pointer; padding: 3px 10px; @@ -303,8 +315,8 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } div { - width: 10px; - height: 10px; + width: 8px; + height: 8px; border-radius: 50%; background-color: #1a73e8; flex-shrink: 0; @@ -321,4 +333,10 @@ $calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); text-overflow: ellipsis; } } + + @media (max-width: 425px) { + span { + font-size: 10px; + } + } } \ No newline at end of file diff --git a/src/plays/calendar/Calendar.tsx b/src/plays/calendar/Calendar.tsx index 556f09550c..fb3437315e 100644 --- a/src/plays/calendar/Calendar.tsx +++ b/src/plays/calendar/Calendar.tsx @@ -20,7 +20,7 @@ function Calendar(props:any) { <>

-
+
diff --git a/src/plays/calendar/CalendarDayTile.tsx b/src/plays/calendar/CalendarDayTile.tsx index db3de00f91..26190c86f4 100644 --- a/src/plays/calendar/CalendarDayTile.tsx +++ b/src/plays/calendar/CalendarDayTile.tsx @@ -25,11 +25,11 @@ const CalendarDayTile = ({ date, showWeek }: Props) => { return (
- {showWeek && {format(date, "EEE")}} - + {showWeek && {format(date, "EEE")}} + {format(date, date.getDate() === 1 ? "MMM d" : "d")} diff --git a/src/plays/calendar/CalendarEvent.tsx b/src/plays/calendar/CalendarEvent.tsx index 6a969bdc2b..15c628e743 100644 --- a/src/plays/calendar/CalendarEvent.tsx +++ b/src/plays/calendar/CalendarEvent.tsx @@ -24,7 +24,7 @@ const CalendarEvent = ({ event }: Props) => { return (
diff --git a/src/plays/calendar/CalendarEventForm.tsx b/src/plays/calendar/CalendarEventForm.tsx index d36dc5ddfb..8fa2760a28 100644 --- a/src/plays/calendar/CalendarEventForm.tsx +++ b/src/plays/calendar/CalendarEventForm.tsx @@ -91,7 +91,7 @@ const CalendarEventForm = ({ date, event, onCancel }: Props) => { return (
ev.stopPropagation()} > { return ( -
+

{event.title}

{format(new Date(`1990-01-01 ${event.startTime}`), 'h:mm a')} - {format(new Date(`1990-01-01 ${event.endTime}`), 'h:mm a')}

diff --git a/src/plays/calendar/CalendarEvents.tsx b/src/plays/calendar/CalendarEvents.tsx index 089ce3fb35..128bc78cae 100644 --- a/src/plays/calendar/CalendarEvents.tsx +++ b/src/plays/calendar/CalendarEvents.tsx @@ -19,7 +19,7 @@ const CalendarEvents = ({ date }: Props) => { }, [date, getEvents]) return ( -
ev.stopPropagation()}> +
ev.stopPropagation()}> {events .filter((e: any, index: number) => index < MAX_VISIBLE_ITEMS) .map((event: any) => ( diff --git a/src/plays/calendar/CalendarEventsMore.tsx b/src/plays/calendar/CalendarEventsMore.tsx index dc2735acbe..3e78425617 100644 --- a/src/plays/calendar/CalendarEventsMore.tsx +++ b/src/plays/calendar/CalendarEventsMore.tsx @@ -9,9 +9,9 @@ interface Props { const CalendarEventsMore = ({ date, events }: Props) => { return ( -
+
{events.length} more -
+
{format(date, "ccc")} {format(date, "dd")} diff --git a/src/plays/calendar/CalendarGrid.tsx b/src/plays/calendar/CalendarGrid.tsx index d1cd63fa0c..d471e512d1 100644 --- a/src/plays/calendar/CalendarGrid.tsx +++ b/src/plays/calendar/CalendarGrid.tsx @@ -45,7 +45,7 @@ const CalendarGrid = () => { currentDate={currentDate} onDateChange={date => setCurrentDate(date)} /> -
+
{ generateTiles() }
diff --git a/src/plays/calendar/CalendarNavigation.tsx b/src/plays/calendar/CalendarNavigation.tsx index cbf123eb55..23705989b2 100644 --- a/src/plays/calendar/CalendarNavigation.tsx +++ b/src/plays/calendar/CalendarNavigation.tsx @@ -23,23 +23,23 @@ const CalendarNavigation = ({ currentDate, onDateChange }: Props) => { } return ( -
+
navigateTo(-1)} > < navigateTo(1)} > > - + {format(currentDate, 'MMMM yyyy')}
diff --git a/src/plays/calendar/ModalContainer.tsx b/src/plays/calendar/ModalContainer.tsx index e70fe5c311..f4f7375d5d 100644 --- a/src/plays/calendar/ModalContainer.tsx +++ b/src/plays/calendar/ModalContainer.tsx @@ -8,14 +8,14 @@ const ModalContainer = () => { if (!modalContent) return null return ( -
+
ev.stopPropagation()} >
{Boolean(modalTitle) && ( - {modalTitle} + {modalTitle} )}
From e1b4fab293595cdef3c2f727c9a2f3643a1c72a4 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 14:29:51 +0800 Subject: [PATCH 6/8] [plays/calendar] Update Readme.md --- src/plays/calendar/Readme.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/plays/calendar/Readme.md b/src/plays/calendar/Readme.md index d88e0dd8a9..97561fba67 100644 --- a/src/plays/calendar/Readme.md +++ b/src/plays/calendar/Readme.md @@ -1,3 +1,12 @@ # Calendar -A simple calendar to add, update, and delete events. \ No newline at end of file +A simple calendar to add, update, and delete events. + +## What will you learn + +- How to use `useState`, and `useContext` to manage component state. +- How to use `useEffect`, and `useCallback` to implement component behaviour. +- How to use `Context` to manage application state. +- How to use `date-fns` to manipulate date using its cool functions. +- How to pass `props` from parent component to child component. +- How to slice your app into smaller components for re-usability and easier management. \ No newline at end of file From 7d276dad0a9de2c7dab3655c98633740dde7d8da Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 19 May 2022 15:36:35 +0800 Subject: [PATCH 7/8] [plays/calendar] Add time validation --- src/plays/calendar/CalendarEventForm.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plays/calendar/CalendarEventForm.tsx b/src/plays/calendar/CalendarEventForm.tsx index 8fa2760a28..5779b25d1b 100644 --- a/src/plays/calendar/CalendarEventForm.tsx +++ b/src/plays/calendar/CalendarEventForm.tsx @@ -1,4 +1,6 @@ +import { isBefore } from 'date-fns' import { format } from 'date-fns/esm' +import { isEqual } from 'lodash' import React, { useState, useContext, useEffect } from 'react' import CalendarEventInfo from './CalendarEventInfo' import { Context } from './Context' @@ -49,6 +51,13 @@ const CalendarEventForm = ({ date, event, onCancel }: Props) => { return } + const start = new Date(`1990-01-01 ${data.startTime}`) + const end = new Date(`1990-01-01 ${data.endTime}`) + if (isEqual(start, end) || isBefore(end, start)) { + alert('Invalid time values') + return + } + if (event) { updateEvent(data) setCalendarEvent({...data}) From 001c4d3119feab91ba04d21930f430d7d622c769 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 20 May 2022 13:43:38 +0800 Subject: [PATCH 8/8] [plays/calendar] Tweak stylings and refactor code --- src/plays/calendar/Calendar.scss | 93 ++++++++++++++-------- src/plays/calendar/Calendar.tsx | 2 +- src/plays/calendar/CalendarDayTile.tsx | 17 ++-- src/plays/calendar/CalendarEvent.tsx | 5 +- src/plays/calendar/CalendarEventForm.tsx | 8 +- src/plays/calendar/CalendarEventInfo.tsx | 5 +- src/plays/calendar/CalendarEvents.tsx | 11 +-- src/plays/calendar/CalendarEventsMore.tsx | 9 ++- src/plays/calendar/CalendarGrid.tsx | 3 +- src/plays/calendar/CalendarNavigation.tsx | 8 +- src/plays/calendar/Context.tsx | 94 +++-------------------- src/plays/calendar/EventType.ts | 9 +++ src/plays/calendar/ModalContainer.tsx | 6 +- src/plays/calendar/utils.tsx | 85 ++++++++++++++++++++ 14 files changed, 209 insertions(+), 146 deletions(-) create mode 100644 src/plays/calendar/EventType.ts create mode 100644 src/plays/calendar/utils.tsx diff --git a/src/plays/calendar/Calendar.scss b/src/plays/calendar/Calendar.scss index 088ee0e144..f5c574c686 100644 --- a/src/plays/calendar/Calendar.scss +++ b/src/plays/calendar/Calendar.scss @@ -1,11 +1,20 @@ -$vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); +$calendar-play-box-shadow-dark:rgba(0,0,0,0.1); +$calendar-play-box-shadow: 0px 0px 10px 5px $calendar-play-box-shadow-dark; -.vincentBCP-calendar { +$calendar-play-blue-500: #3182CE; +$calendar-play-blue-400: #4299E1; +$calendar-play-blue-300: #63B3ED; + +$calendar-play-red-500: #E53E3E; +$calendar-play-red-200: #FEB2B2; +$calendar-play-red-100: #FED7D7; + +.calendar-play { background-color: white; padding: 30px; } -.vincentBCP-calendar-navigation { +.calendar-play-navigation { display: flex; align-items: center; margin-bottom: 30px; @@ -21,7 +30,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); margin-right: 20px; } - .vincentBCP-calendar-navigation-arrow { + .calendar-play-navigation-arrow { width: 35px; height: 35px; display: inline-flex; @@ -33,13 +42,13 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); padding-top: 3px; } - .vincentBCP-calendar-navigation-current-date { + .calendar-play-navigation-current-date { font-size: 24px; margin-left: 20px; } button, - .vincentBCP-calendar-navigation-arrow { + .calendar-play-navigation-arrow { &:hover { background-color: rgba(0,0,0,0.05); } @@ -50,34 +59,53 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.vincentBCP-calendar-body { +.calendar-play-body { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border-left: 1px solid lightgray; border-bottom: 1px solid lightgray; } -.vincentBCP-calendar-day-tile { +.calendar-play-day-tile { height: 150px; border-right: 1px solid lightgray; border-top: 1px solid lightgray; + display: flex; + flex-direction: column; + align-items: center; + padding-top: 5px; + + .calendar-play-week { + font-size: 12px; + } - .vincentBCP-calendar-week, - .vincentBCP-calendar-day { - display: inline-block; - text-align: center; + .calendar-play-day { + display: inline-flex; + align-items: center; + justify-content: center; + height: 25px; width: 100%; font-size: 12px; } - .vincentBCP-calendar-week { + .calendar-play-week { text-transform: uppercase; font-weight: 500; color: gray; } + + &.today { + .calendar-play-day { + width: 25px; + font-weight: 700; + color: white; + background-color: $calendar-play-blue-500; + border-radius: 50%; + } + } } -.vincentBCP-calendar-modal { +.calendar-play-modal { position: absolute; top: 0; left: 0; @@ -85,14 +113,15 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); height: 100%; padding-top: 120px; z-index: 999; + background-color: rgba(0,0,0,0.5); - .vincentBCP-calendar-modal-content { + .calendar-play-modal-content { width: 500px; margin: auto; background-color: white; padding: 30px 30px 30px 30px; border-radius: 5px; - box-shadow: $vincentBCP-calendar-box-shadow; + box-shadow: $calendar-play-box-shadow; & > div:first-of-type { display: flex; @@ -112,7 +141,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.vincentBCP-calendar-event-form { +.calendar-play-event-form { input[name='title'] { width: 100%; border: none; @@ -164,18 +193,19 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); border-radius: 5px; font-weight: 500; transition-duration: 0.3s; + border: 1px solid transparent; &.delete { background-color: transparent; margin-right: auto; - color: red; + color: $calendar-play-red-500; &:hover { - background-color: #ffe3e3; + border: 1px solid $calendar-play-red-500; } &:active { - background-color: #ffcaca; + background-color: $calendar-play-red-100; } } @@ -193,21 +223,21 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); &.save { color: white; - background-color: #1a73e8; + background-color: $calendar-play-blue-500; &:hover { - background-color: #1a84e8; + background-color: $calendar-play-blue-400; } &:active { - background-color: #1aa7e8; + background-color: $calendar-play-blue-300; } } } } } -.vincentBCP-calendar-event-info { +.calendar-play-event-info { p:first-of-type { font-size: 18px; } @@ -235,11 +265,12 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.vincentBCP-calendar-events { +.calendar-play-events { margin-top: 5px; + width: 100%; } -.vincentBCP-calendar-events-more { +.calendar-play-events-more { padding: 0 10px; display: block; cursor: pointer; @@ -255,7 +286,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); white-space: nowrap; } - .vincentBCP-calendar-events-more-popup { + .calendar-play-events-more-popup { display: none; position: absolute; left: -200px; @@ -264,7 +295,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); background-color: white; padding: 20px 10px; border-radius: 5px; - box-shadow: $vincentBCP-calendar-box-shadow; + box-shadow: $calendar-play-box-shadow; overflow: auto; max-height: 200px; @@ -289,7 +320,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } &:hover { - .vincentBCP-calendar-events-more-popup { + .calendar-play-events-more-popup { display: block; } } @@ -301,7 +332,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); } } -.vincentBCP-calendar-event { +.calendar-play-event { display: flex; align-items: center; gap: 5px; @@ -318,7 +349,7 @@ $vincentBCP-calendar-box-shadow: 0px 0px 10px 5px rgba(0,0,0,0.1); width: 8px; height: 8px; border-radius: 50%; - background-color: #1a73e8; + background-color: $calendar-play-blue-500; flex-shrink: 0; } diff --git a/src/plays/calendar/Calendar.tsx b/src/plays/calendar/Calendar.tsx index fb3437315e..d2e2039467 100644 --- a/src/plays/calendar/Calendar.tsx +++ b/src/plays/calendar/Calendar.tsx @@ -20,7 +20,7 @@ function Calendar(props:any) { <>
-
+
diff --git a/src/plays/calendar/CalendarDayTile.tsx b/src/plays/calendar/CalendarDayTile.tsx index 26190c86f4..d836f86229 100644 --- a/src/plays/calendar/CalendarDayTile.tsx +++ b/src/plays/calendar/CalendarDayTile.tsx @@ -6,10 +6,11 @@ import { Context } from './Context' interface Props { date: Date, - showWeek?: boolean + showWeek?: boolean, + isToday?: boolean } -const CalendarDayTile = ({ date, showWeek }: Props) => { +const CalendarDayTile = ({ date, showWeek, isToday }: Props) => { const context = useContext(Context) const { showModal, hideModal } = context @@ -25,16 +26,18 @@ const CalendarDayTile = ({ date, showWeek }: Props) => { return (
- {showWeek && {format(date, "EEE")}} - - {format(date, date.getDate() === 1 ? "MMM d" : "d")} + {showWeek && ( + {format(date, "EEE")} + )} + + {format(date, date.getDate() === 1 && !isToday ? "MMM d" : "d")}
- ) + ); } export default CalendarDayTile \ No newline at end of file diff --git a/src/plays/calendar/CalendarEvent.tsx b/src/plays/calendar/CalendarEvent.tsx index 15c628e743..2a5ed36b4f 100644 --- a/src/plays/calendar/CalendarEvent.tsx +++ b/src/plays/calendar/CalendarEvent.tsx @@ -2,9 +2,10 @@ import React, { useContext } from 'react' import { format } from 'date-fns' import CalendarEventForm from './CalendarEventForm' import { Context } from './Context' +import EventType from './EventType' interface Props { - event: any + event: EventType } const CalendarEvent = ({ event }: Props) => { @@ -24,7 +25,7 @@ const CalendarEvent = ({ event }: Props) => { return (
diff --git a/src/plays/calendar/CalendarEventForm.tsx b/src/plays/calendar/CalendarEventForm.tsx index 5779b25d1b..233ee7fc6e 100644 --- a/src/plays/calendar/CalendarEventForm.tsx +++ b/src/plays/calendar/CalendarEventForm.tsx @@ -4,15 +4,16 @@ import { isEqual } from 'lodash' import React, { useState, useContext, useEffect } from 'react' import CalendarEventInfo from './CalendarEventInfo' import { Context } from './Context' +import EventType from './EventType' interface Props { date: Date, - event?: any, + event?: EventType, onCancel: VoidFunction } const CalendarEventForm = ({ date, event, onCancel }: Props) => { - const [calendarEvent, setCalendarEvent] = useState() + const [calendarEvent, setCalendarEvent] = useState() const [data, setData] = useState() const [editable, setEditable] = useState(false) const context = useContext(Context) @@ -84,6 +85,7 @@ const CalendarEventForm = ({ date, event, onCancel }: Props) => { } const handleEdit = () => { + setData({...calendarEvent}) setEditable(true) } @@ -100,7 +102,7 @@ const CalendarEventForm = ({ date, event, onCancel }: Props) => { return (
ev.stopPropagation()} > { return ( -
+

{event.title}

{format(new Date(`1990-01-01 ${event.startTime}`), 'h:mm a')} - {format(new Date(`1990-01-01 ${event.endTime}`), 'h:mm a')}

diff --git a/src/plays/calendar/CalendarEvents.tsx b/src/plays/calendar/CalendarEvents.tsx index 128bc78cae..826fa67d1c 100644 --- a/src/plays/calendar/CalendarEvents.tsx +++ b/src/plays/calendar/CalendarEvents.tsx @@ -2,6 +2,7 @@ import React, { useState, useContext, useEffect } from 'react' import CalendarEvent from './CalendarEvent' import CalendarEventsMore from './CalendarEventsMore' import { Context } from './Context' +import EventType from './EventType' interface Props { date: Date @@ -10,7 +11,7 @@ interface Props { const MAX_VISIBLE_ITEMS = 3 const CalendarEvents = ({ date }: Props) => { - const [events, setEvents] = useState([]) + const [events, setEvents] = useState([]) const context = useContext(Context) const { getEvents } = context @@ -19,17 +20,17 @@ const CalendarEvents = ({ date }: Props) => { }, [date, getEvents]) return ( -
ev.stopPropagation()}> +
ev.stopPropagation()}> {events - .filter((e: any, index: number) => index < MAX_VISIBLE_ITEMS) - .map((event: any) => ( + .filter((e, index) => index < MAX_VISIBLE_ITEMS) + .map(event => ( ))} {events.length > MAX_VISIBLE_ITEMS && ( index >= MAX_VISIBLE_ITEMS + (e, index) => index >= MAX_VISIBLE_ITEMS )} /> )} diff --git a/src/plays/calendar/CalendarEventsMore.tsx b/src/plays/calendar/CalendarEventsMore.tsx index 3e78425617..f92dbb78ab 100644 --- a/src/plays/calendar/CalendarEventsMore.tsx +++ b/src/plays/calendar/CalendarEventsMore.tsx @@ -1,23 +1,24 @@ import React from 'react' import { format } from 'date-fns' import CalendarEvent from './CalendarEvent' +import EventType from './EventType' interface Props { date: Date, - events: any + events: EventType[] } const CalendarEventsMore = ({ date, events }: Props) => { return ( -
+
{events.length} more -
+
{format(date, "ccc")} {format(date, "dd")}
- {events.map((event: any) => ( + {events.map(event => ( ))}
diff --git a/src/plays/calendar/CalendarGrid.tsx b/src/plays/calendar/CalendarGrid.tsx index d471e512d1..5acfab9195 100644 --- a/src/plays/calendar/CalendarGrid.tsx +++ b/src/plays/calendar/CalendarGrid.tsx @@ -23,6 +23,7 @@ const CalendarGrid = () => { key={format(curDate, 'yyyy-MM-dd')} date={curDate} showWeek={tiles.length < 7} + isToday={format(new Date(), "yyyy-MM-dd") === format(curDate, "yyyy-MM-dd")} /> ) @@ -45,7 +46,7 @@ const CalendarGrid = () => { currentDate={currentDate} onDateChange={date => setCurrentDate(date)} /> -
+
{ generateTiles() }
diff --git a/src/plays/calendar/CalendarNavigation.tsx b/src/plays/calendar/CalendarNavigation.tsx index 23705989b2..06a60dea44 100644 --- a/src/plays/calendar/CalendarNavigation.tsx +++ b/src/plays/calendar/CalendarNavigation.tsx @@ -23,23 +23,23 @@ const CalendarNavigation = ({ currentDate, onDateChange }: Props) => { } return ( -
+
navigateTo(-1)} > < navigateTo(1)} > > - + {format(currentDate, 'MMMM yyyy')}
diff --git a/src/plays/calendar/Context.tsx b/src/plays/calendar/Context.tsx index 85c62278ef..d765771600 100644 --- a/src/plays/calendar/Context.tsx +++ b/src/plays/calendar/Context.tsx @@ -1,107 +1,35 @@ -import { addDays, endOfMonth, endOfWeek, format, getWeekOfMonth, isWeekend, startOfMonth, startOfWeek } from "date-fns"; -import React, { useCallback, useEffect, useState } from "react"; +import { format } from "date-fns"; +import React, { useCallback, useState } from "react"; import { orderBy } from 'lodash' +import { getDummyEvents } from "./utils"; +import EventType from "./EventType"; export const Context = React.createContext({ modalTitle: '', modalContent: undefined, getEvents: (date: Date) => {}, - addEvent: (event: any) => {}, - updateEvent: (event: any) => {}, - deleteEvent: (event: any) => {}, + addEvent: (event: EventType) => {}, + updateEvent: (event: EventType) => {}, + deleteEvent: (event: EventType) => {}, showModal: (content: React.ReactNode, title?: '') => {}, hideModal: () => {} }) export const ContextProvider = ({ children }: any) => { - const [events, setEvents] = useState([]) + const [events, setEvents] = useState(getDummyEvents()) const [modalTitle, setModalTitle] = useState('') const [modalContent, setModalContent] = useState(undefined) - useEffect(() => { - // create fake events - - const startDate = startOfWeek(startOfMonth(new Date()), { weekStartsOn: 0 }) - const endDate = endOfWeek(endOfMonth(new Date()), { weekStartsOn: 0 }) - - let curDate = startDate - - let events: any[] = [] - - const addEvent = (date: Date) => { - // add event only if date is Mon - Fri - if (isWeekend(date)) return - - events.push({ - id: format(date, 'yyyy-MM-dd'), - date: format(date, 'yyyy-MM-dd'), - title: 'Daily stand up', - startTime: '09:30', - endTime: '10:00' - }) - - if (getWeekOfMonth(date) % 2 === 0 && date.getDay() === 2) { - events.push({ - id: format(date, 'yyyy-MM-dd') + '-key', - date: format(date, 'yyyy-MM-dd'), - title: 'Sprint Planning', - startTime: '15:00', - endTime: '17:00' - }) - } - } - - do { - addEvent(curDate) - curDate = addDays(curDate, 1) - } while(format(curDate, 'yyyy-MM-dd') !== format(endDate, 'yyyy-MM-dd')) - - addEvent(curDate) - - events = events.concat([ - { - id: '1', - date: format(new Date(), 'yyyy-MM') + '-13', - title: 'Lorem ipsum dolor sit amet', - startTime: '07:30', - endTime: '08:30' - }, - { - id: '2', - date: format(new Date(), 'yyyy-MM') + '-13', - title: 'Lorem ipsum dolor sit amet', - startTime: '10:00', - endTime: '11:00' - }, - { - id: '3', - date: format(new Date(), 'yyyy-MM') + '-13', - title: 'Lorem ipsum dolor sit amet', - startTime: '12:30', - endTime: '13:30' - }, - { - id: '4', - date: format(new Date(), 'yyyy-MM') + '-13', - title: 'Lorem ipsum dolor sit amet', - startTime: '14:00', - endTime: '14:30' - } - ]) - - setEvents(events) - }, []) - const getEvents = useCallback((date: Date) => { return orderBy(events.filter(e => e.date === format(date, 'yyyy-MM-dd')), ['startTime']) }, [events]) - const addEvent = (event: any) => { + const addEvent = (event: EventType) => { event.id = (new Date()).getTime().toString() setEvents(oldValue => ([...oldValue, event])) } - const updateEvent = (event: any) => { + const updateEvent = (event: EventType) => { setEvents(oldValue => { const newEvents = [...oldValue] const index = newEvents.findIndex(e => e.id === event.id) @@ -114,7 +42,7 @@ export const ContextProvider = ({ children }: any) => { }) } - const deleteEvent = (event: any) => { + const deleteEvent = (event: EventType) => { setEvents(oldValue => { const newEvents = [...oldValue] const index = newEvents.findIndex(e => e.id === event.id) diff --git a/src/plays/calendar/EventType.ts b/src/plays/calendar/EventType.ts new file mode 100644 index 0000000000..9c81d2e4ba --- /dev/null +++ b/src/plays/calendar/EventType.ts @@ -0,0 +1,9 @@ +type EventType = { + id: string, + date: string, + title: string, + startTime: string, + endTime: string +} + +export default EventType \ No newline at end of file diff --git a/src/plays/calendar/ModalContainer.tsx b/src/plays/calendar/ModalContainer.tsx index f4f7375d5d..e8e3b033ea 100644 --- a/src/plays/calendar/ModalContainer.tsx +++ b/src/plays/calendar/ModalContainer.tsx @@ -8,14 +8,14 @@ const ModalContainer = () => { if (!modalContent) return null return ( -
+
ev.stopPropagation()} >
{Boolean(modalTitle) && ( - {modalTitle} + {modalTitle} )}
diff --git a/src/plays/calendar/utils.tsx b/src/plays/calendar/utils.tsx new file mode 100644 index 0000000000..26c12f8bae --- /dev/null +++ b/src/plays/calendar/utils.tsx @@ -0,0 +1,85 @@ +import { + addDays, + endOfMonth, + endOfWeek, + format, + getWeekOfMonth, + isWeekend, + startOfMonth, + startOfWeek, +} from "date-fns"; +import EventType from "./EventType"; + +export const getDummyEvents = (): EventType[] => { + // create fake events + + const startDate = startOfWeek(startOfMonth(new Date()), { weekStartsOn: 0 }); + const endDate = endOfWeek(endOfMonth(new Date()), { weekStartsOn: 0 }); + + let curDate = startDate; + + let events: EventType[] = []; + + const addEvent = (date: Date) => { + // add event only if date is Mon - Fri + if (isWeekend(date)) return; + + events.push({ + id: format(date, "yyyy-MM-dd"), + date: format(date, "yyyy-MM-dd"), + title: "Daily stand up", + startTime: "09:30", + endTime: "10:00", + }); + + if (getWeekOfMonth(date) % 2 === 0 && date.getDay() === 2) { + events.push({ + id: `${format(date, "yyyy-MM-dd")}-key`, + date: format(date, "yyyy-MM-dd"), + title: "Sprint Planning", + startTime: "15:00", + endTime: "17:00", + }); + } + }; + + do { + addEvent(curDate); + curDate = addDays(curDate, 1); + } while (format(curDate, "yyyy-MM-dd") !== format(endDate, "yyyy-MM-dd")); + + addEvent(curDate); + + events = events.concat([ + { + id: "1", + date: format(new Date(), "yyyy-MM") + "-13", + title: "Lorem ipsum dolor sit amet", + startTime: "07:30", + endTime: "08:30", + }, + { + id: "2", + date: format(new Date(), "yyyy-MM") + "-13", + title: "Lorem ipsum dolor sit amet", + startTime: "10:00", + endTime: "11:00", + }, + { + id: "3", + date: format(new Date(), "yyyy-MM") + "-13", + title: "Lorem ipsum dolor sit amet", + startTime: "12:30", + endTime: "13:30", + }, + { + id: "4", + date: format(new Date(), "yyyy-MM") + "-13", + title: "Lorem ipsum dolor sit amet", + startTime: "14:00", + endTime: "14:30", + }, + ]); + + return events; +};