diff --git a/src/calendar/calendar.tsx b/src/calendar/calendar.tsx index 555ebca6e..b30084e95 100644 --- a/src/calendar/calendar.tsx +++ b/src/calendar/calendar.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import styled, { css } from "styled-components"; -import { V2_Color } from "../v2_color"; import { InternalCalendar } from "../shared/internal-calendar"; +import { Border, Colour, Radius } from "../theme"; import { CalendarProps } from "./types"; export const Calendar = ({ @@ -58,8 +58,8 @@ const Wrapper = styled.div` ${(props) => { if (props.$hasBorder) { return css` - border: 1px solid ${V2_Color.Neutral[5](props)}; - border-radius: 12px; + border: ${Border["width-010"]} ${Border.solid} ${Colour.border}; + border-radius: ${Radius.lg}; overflow: hidden; `; } diff --git a/src/shared/internal-calendar/calendar-dropdown.style.tsx b/src/shared/internal-calendar/calendar-dropdown.style.tsx index f3b517f8f..5131db235 100644 --- a/src/shared/internal-calendar/calendar-dropdown.style.tsx +++ b/src/shared/internal-calendar/calendar-dropdown.style.tsx @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { V2_MediaQuery } from "../../v2_media"; +import { MediaQuery } from "../../theme"; // ============================================================================= // STYLE INTERFACE @@ -16,7 +16,7 @@ export const CalendarWrapper = styled.div` max-width: 41rem; min-width: 21rem; - ${V2_MediaQuery.MaxWidth.mobileL} { + ${MediaQuery.MaxWidth.sm} { min-width: 17.5rem; } `; diff --git a/src/shared/internal-calendar/calendar-manager.style.tsx b/src/shared/internal-calendar/calendar-manager.style.tsx index 680bbd7b3..543b86cca 100644 --- a/src/shared/internal-calendar/calendar-manager.style.tsx +++ b/src/shared/internal-calendar/calendar-manager.style.tsx @@ -3,8 +3,7 @@ import { ChevronLeftIcon } from "@lifesg/react-icons/chevron-left"; import { ChevronRightIcon } from "@lifesg/react-icons/chevron-right"; import styled, { css } from "styled-components"; import { Button } from "../../button"; -import { V2_Color } from "../../v2_color"; -import { V2_TextStyleHelper } from "../../v2_text"; +import { Colour, Font } from "../../theme"; import { ClickableIcon } from "../clickable-icon"; // ============================================================================= @@ -26,7 +25,7 @@ interface OverlayStyleProps { // ICONS // ----------------------------------------------------------------------------- const iconStyle = css` - color: ${V2_Color.Neutral[3]}; + color: ${Colour.icon}; height: 1rem; width: 1rem; `; @@ -72,7 +71,7 @@ export const OptionsOverlay = styled.div` left: 0; height: 100%; width: 100%; - background: ${V2_Color.Neutral[8]}; + background: ${Colour.bg}; ${(props) => { if (!props.$visible) { @@ -118,11 +117,12 @@ export const DropdownButton = styled.button` } `; } - }} + }}; `; -export const DropdownText = styled.p` - ${V2_TextStyleHelper.getTextStyle("H5", "regular")} +export const DropdownText = styled.span` + ${Font["body-md-regular"]} + color: ${Colour["text"]}; `; export const HeaderArrows = styled.div` diff --git a/src/shared/internal-calendar/day-cell/day-cell.style.tsx b/src/shared/internal-calendar/day-cell/day-cell.style.tsx index eb22ae187..851543740 100644 --- a/src/shared/internal-calendar/day-cell/day-cell.style.tsx +++ b/src/shared/internal-calendar/day-cell/day-cell.style.tsx @@ -1,6 +1,5 @@ import styled, { css } from "styled-components"; -import { V2_Color } from "../../../v2_color"; -import { V2_Text, V2_TextStyleHelper } from "../../../v2_text"; +import { Border, Colour, Font, FontSpec, Motion, Radius } from "../../../theme"; import { CellType, LabelType } from "./types"; // ============================================================================= @@ -8,7 +7,6 @@ import { CellType, LabelType } from "./types"; // ============================================================================= interface StyleProps { $type?: CellType; - $shadow?: boolean; } interface LabelStyleProps { @@ -17,50 +15,55 @@ interface LabelStyleProps { $interactive: boolean | null; } +interface IndicatorStyleProps { + $disabled: boolean; +} + // ============================================================================= // HELPERS // ============================================================================= const getCellStyle = (props: StyleProps) => { - let color = V2_Color.Neutral[8]; - let border = "1px solid transparent"; + let color = Colour.bg; + let borderColor: typeof color | string = "transparent"; switch (props.$type) { - case "current": - color = V2_Color.Accent.Light[5]; - break; - case "hover-dash": - color = V2_Color.Accent.Light[6]; - border = `1px dashed ${V2_Color.Accent.Light[4](props)}`; + case "hover-subtle": + color = Colour["bg-hover"]; + borderColor = Colour["bg-hover"]; break; - case "hover-current": - color = V2_Color.Neutral[8]; - border = `1px solid ${V2_Color.Primary(props)}`; + case "hover": + color = Colour["bg-hover-strong"]; + borderColor = Colour["bg-hover-strong"]; break; - case "selected": - color = V2_Color.Accent.Light[5]; - border = `1px solid ${V2_Color.Accent.Light[4](props)}`; + case "hover-outline": + color = Colour["bg-hover-subtle"]; + borderColor = Colour["border-hover"]; break; case "selected-outline": - color = V2_Color.Accent.Light[5]; - border = `1px solid ${V2_Color.Primary(props)}`; + color = Colour["bg-selected"]; + borderColor = Colour["border-selected"]; break; - case "overlap": - color = V2_Color.Accent.Light[4]; - border = `1px solid ${V2_Color.Accent.Light[4](props)}`; + case "selected-outline-subtle": + color = Colour["bg-selected"]; + borderColor = Colour["border-selected-subtle"]; break; - case "overlap-outline": - color = V2_Color.Accent.Light[4]; - border = `1px solid ${V2_Color.Primary(props)}`; + case "selected-hover": + color = Colour["bg-selected-hover"]; + // no border to give it an overlay effect + break; + case "selected-hover-outline": + color = Colour["bg-selected-hover"]; + borderColor = Colour["border-selected-hover"]; break; } - return { color, border }; + return { color, borderColor }; }; // ============================================================================= // COMPONENTS // ============================================================================= -export const Cell = styled.div` +export const Cell = styled.div` display: flex; align-items: center; justify-content: center; @@ -73,17 +76,21 @@ const Half = styled.div` position: absolute; height: 2.5rem; width: 50%; + transition: ${Motion["duration-150"]} ${Motion["ease-default"]}; + border: ${Border["width-010"]} ${Border["solid"]} transparent; + border-left: none; + border-right: none; ${(props) => { if (!props.$type) { return; } - const { color, border } = getCellStyle(props); + const { color, borderColor } = getCellStyle(props); return css` background-color: ${color}; - background-clip: content-box; - border-top: ${border}; - border-bottom: ${border}; + background-clip: border-box; + border-top-color: ${borderColor}; + border-bottom-color: ${borderColor}; `; }} `; @@ -96,25 +103,6 @@ export const RightHalf = styled(Half)` right: 0; `; -const HalfShadow = styled.div` - z-index: -1; - box-shadow: 0 0 4px 1px ${V2_Color.Shadow.Accent}; - position: absolute; - height: 100%; - width: 50%; - display: none; - - ${(props) => props.$shadow && "display: block;"} -`; - -export const LeftHalfShadow = styled(HalfShadow)` - left: 0; -`; - -export const RightHalfShadow = styled(HalfShadow)` - right: 0; -`; - export const Circle = styled.div` position: absolute; z-index: 1; @@ -124,51 +112,34 @@ export const Circle = styled.div` justify-content: center; height: 2.5rem; width: 2.5rem; + transition: ${Motion["duration-150"]} ${Motion["ease-default"]}; - border: 1px solid transparent; - border-radius: 50%; + border: ${Border["width-010"]} ${Border["solid"]} transparent; + border-radius: ${Radius.md}; ${(props) => { if (props.$type) { - const { color, border } = getCellStyle(props); + const { color, borderColor } = getCellStyle(props); return css` background-color: ${color}; background-clip: content-box; - border: ${border}; + border-color: ${borderColor}; `; } }} - - ${(props) => - props.$shadow && - css` - &:before { - content: ""; - border-radius: 50%; - position: absolute; - height: 100%; - width: 100%; - } - `} `; export const LeftCircle = styled(Circle)` right: calc(50% - 1.25rem); clip-path: inset(-3px 1.25rem -3px -3px); - &:before { - box-shadow: -1px 0 4px 1px ${V2_Color.Shadow.Accent}; - } `; export const RightCircle = styled(Circle)` left: calc(50% - 1.25rem); clip-path: inset(-3px -3px -3px 1.25rem); - &:before { - box-shadow: 1px 0 4px 1px ${V2_Color.Shadow.Accent}; - } `; -export const Label = styled(V2_Text.H5)` +export const Label = styled.div` position: absolute; top: 0; bottom: 0; @@ -179,7 +150,8 @@ export const Label = styled(V2_Text.H5)` justify-content: center; height: 2.5rem; width: 2.5rem; - border-radius: 50%; + ${Font["body-md-regular"]} + transition: ${Motion["duration-150"]} ${Motion["ease-default"]}; cursor: ${(props) => { if (props.$interactive) { @@ -195,31 +167,35 @@ export const Label = styled(V2_Text.H5)` const { $disabled, $type } = props; if ($disabled) { - if ($type === "selected") { - return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")}; - color: ${V2_Color.Accent.Light[2]}; - `; - } - return css` - color: ${V2_Color.Neutral[4]}; + color: ${Colour["text-disabled-subtlest"]}; `; } switch ($type) { case "selected": return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")}; - color: ${V2_Color.Primary}; + font-weight: ${FontSpec["weight-semibold"]}; + color: ${Colour["text-selected"]}; + `; + case "selected-hover": + return css` + font-weight: ${FontSpec["weight-semibold"]}; + color: ${Colour["text-selected-hover"]}; `; case "current": return css` - color: ${V2_Color.Neutral[3]}; + font-weight: ${FontSpec["weight-semibold"]}; + color: ${Colour["text-primary"]}; + `; + case "hover": + return css` + font-weight: ${FontSpec["weight-semibold"]}; + color: ${Colour["text-hover"]}; `; case "unavailable": return css` - color: ${V2_Color.Neutral[4]}; + color: ${Colour["text-disabled-subtlest"]}; `; case "hidden": return css` @@ -228,8 +204,17 @@ export const Label = styled(V2_Text.H5)` case "available": default: return css` - color: ${V2_Color.Neutral[1]}; + color: ${Colour.text}; `; } }} `; + +export const Indicator = styled.div` + position: absolute; + width: 4px; + height: 4px; + background-color: currentColor; + border-radius: 50%; + bottom: 4px; +`; diff --git a/src/shared/internal-calendar/day-cell/day-cell.tsx b/src/shared/internal-calendar/day-cell/day-cell.tsx index 9bc1f1dad..1598202b6 100644 --- a/src/shared/internal-calendar/day-cell/day-cell.tsx +++ b/src/shared/internal-calendar/day-cell/day-cell.tsx @@ -1,12 +1,12 @@ +import dayjs from "dayjs"; import { Cell, + Indicator, Label, LeftCircle, LeftHalf, - LeftHalfShadow, RightCircle, RightHalf, - RightHalfShadow, } from "./day-cell.style"; import { DayCellProps } from "./types"; @@ -15,16 +15,20 @@ export const DayCell = ({ bgRight, circleLeft, circleRight, - shadow, - circleShadow, labelType, disabled, interactive, + currentDateIndicator, date, onSelect, onHover, onHoverEnd, }: DayCellProps) => { + // ========================================================================= + // CONST + // ========================================================================= + const today = dayjs().isSame(date, "day"); + // ========================================================================= // EVENT HANDLERS // ========================================================================= @@ -45,20 +49,11 @@ export const DayCell = ({ // ========================================================================= return ( - - - - - - + + + + ); diff --git a/src/shared/internal-calendar/day-cell/types.tsx b/src/shared/internal-calendar/day-cell/types.tsx index d18766701..fbc86ea60 100644 --- a/src/shared/internal-calendar/day-cell/types.tsx +++ b/src/shared/internal-calendar/day-cell/types.tsx @@ -1,19 +1,21 @@ import { Dayjs } from "dayjs"; export type CellType = - | "current" - | "selected" + | "hover-subtle" + | "hover" + | "hover-outline" | "selected-outline" - | "overlap" - | "overlap-outline" - | "hover-dash" - | "hover-current"; + | "selected-outline-subtle" + | "selected-hover" + | "selected-hover-outline"; export type LabelType = | "available" | "unavailable" | "current" + | "hover" | "selected" + | "selected-hover" | "hidden"; export interface CellStyleProps { @@ -21,11 +23,10 @@ export interface CellStyleProps { bgRight?: CellType | undefined; circleLeft?: CellType | undefined; circleRight?: CellType | undefined; - shadow?: boolean | undefined; - circleShadow?: boolean | undefined; labelType?: LabelType | undefined; disabled?: boolean | undefined; interactive?: boolean | null | undefined; + currentDateIndicator?: boolean | undefined; } export interface DayCellProps extends CellStyleProps { diff --git a/src/shared/internal-calendar/fixed-range/fixed-range-calendar-day-view.tsx b/src/shared/internal-calendar/fixed-range/fixed-range-calendar-day-view.tsx index dc8dd18c4..628988348 100644 --- a/src/shared/internal-calendar/fixed-range/fixed-range-calendar-day-view.tsx +++ b/src/shared/internal-calendar/fixed-range/fixed-range-calendar-day-view.tsx @@ -1,6 +1,5 @@ import dayjs, { Dayjs } from "dayjs"; import { useMemo, useState } from "react"; -import { V2_Text } from "../../../v2_text/text"; import { CalendarHelper } from "../../../util/calendar-helper"; import { HeaderCell, RowDayCell, Wrapper } from "../standard"; import { CommonCalendarProps } from "../types"; @@ -64,9 +63,7 @@ export const FixedRangeCalendarDayView = ({ const renderHeader = () => { return weeksOfTheMonth[0].map((day, index) => ( - - {dayjs(day).format("ddd")} - + {dayjs(day).format("ddd")} )); }; diff --git a/src/shared/internal-calendar/fixed-range/fixed-range-cell.tsx b/src/shared/internal-calendar/fixed-range/fixed-range-cell.tsx index bf1b74641..ef46d73f4 100644 --- a/src/shared/internal-calendar/fixed-range/fixed-range-cell.tsx +++ b/src/shared/internal-calendar/fixed-range/fixed-range-cell.tsx @@ -104,7 +104,7 @@ export const FixedRangeDayCell = ({ if (isHover) { applyRange( props, - "hover-dash", + "hover", formattedDate === hoverStart, formattedDate === hoverEnd ); @@ -112,32 +112,16 @@ export const FixedRangeDayCell = ({ if (isSelected) { applyRange( props, - "selected", + "selected-outline", formattedDate === rangeStart, formattedDate === rangeEnd ); } if (isSelected && isHover) { - applyRange(props, "overlap", isStart, isEnd); - } - - if (formattedDate === rangeStart) { - if (isHover) { - props.circleLeft = "overlap-outline"; - props.circleRight = "overlap-outline"; - } else { - props.circleRight = "selected-outline"; - props.circleLeft = "selected-outline"; - } - } + applyRange(props, "selected-hover-outline", isStart, isEnd); - if (formattedDate === hoverStart) { - props.circleLeft = "hover-current"; - props.circleRight = "hover-current"; - props.circleShadow = true; - if (hoverStart >= rangeStart && hoverStart < rangeEnd) { - props.circleLeft = "overlap-outline"; - props.circleRight = "overlap-outline"; + if (formattedDate === hoverStart && formattedDate !== rangeStart) { + props.circleLeft = "selected-hover"; } } @@ -153,8 +137,6 @@ export const FixedRangeDayCell = ({ props.labelType = "unavailable"; } else if (dayjs().isSame(date, "day") && !disabled) { props.labelType = "current"; - props.circleLeft = "current"; - props.circleRight = "current"; } return props; @@ -169,6 +151,7 @@ export const FixedRangeDayCell = ({ calendarDate, disabled, interactive, + currentDateIndicator: true, onSelect: handleSelect, onHover: handleHover, }; diff --git a/src/shared/internal-calendar/internal-calendar-month.style.tsx b/src/shared/internal-calendar/internal-calendar-month.style.tsx index bd7cda5d2..1ab7052a8 100644 --- a/src/shared/internal-calendar/internal-calendar-month.style.tsx +++ b/src/shared/internal-calendar/internal-calendar-month.style.tsx @@ -1,7 +1,5 @@ import styled, { css } from "styled-components"; -import { V2_Color } from "../../v2_color"; -import { V2_TextStyleHelper } from "../../v2_text/helper"; -import { V2_Text } from "../../v2_text/text"; +import { Border, Colour, Font, FontSpec, Motion, Radius } from "../../theme"; import { MonthVariant } from "./internal-calendar-month"; import { CalendarType } from "./types"; @@ -49,18 +47,23 @@ export const MonthCell = styled.div` display: flex; align-items: center; justify-content: center; - cursor: default; - border-radius: 5rem; + border-radius: ${Radius.md}; margin: 0 0.5rem; + transition: ${Motion["duration-150"]} ${Motion["ease-default"]}; + + // default styles + ${Font["body-md-regular"]} + border-radius: ${Radius.md}; + border: ${Border["width-010"]} ${Border.solid} transparent; + background-clip: border-box; + color: ${Colour["text"]}; + cursor: default; + // cursor style ${(props) => { if (props.$interactive) { return css` cursor: pointer; - &:hover { - box-shadow: 0px 0px 4px 1px ${V2_Color.Shadow.Accent}; - border: 1px solid ${V2_Color.Accent.Light[1]}; - } `; } if (props.$disabledDisplay) { @@ -70,43 +73,63 @@ export const MonthCell = styled.div` } }} - ${(props) => { - switch (props.$variant) { - case "current-month": - return css` - background-color: ${V2_Color.Accent.Light[6](props)}; - `; - case "selected-month": - return css` - background-color: ${V2_Color.Accent.Light[5](props)}; - border: 1px solid ${V2_Color.Primary(props)}; - `; - case "default": - break; + // background, border and text styles + ${({ $variant, $interactive, $disabledDisplay }) => { + if ($variant === "selected-month") { + return css` + background: ${Colour["bg-selected"]}; + border-color: ${Colour["border-selected"]}; + color: ${Colour["text-selected"]}; + font-weight: ${FontSpec["weight-semibold"]}; + + ${$interactive && + css` + &:hover { + background: ${Colour["bg-selected-hover"]}; + border-color: ${Colour["border-selected-hover"]}; + color: ${Colour["text-selected-hover"]}; + } + `} + `; } - }} -`; -export const CellLabel = styled(V2_Text.H5)` - ${(props) => { - if (props.$disabledDisplay) { + if ($variant === "current-month") { return css` - color: ${V2_Color.Neutral[4]}; + color: ${Colour["text-primary"]}; + font-weight: ${FontSpec["weight-semibold"]}; `; } - switch (props.$variant) { - case "current-month": - return css` - color: ${V2_Color.Neutral[3](props)}; - `; - case "selected-month": - return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")} - color: ${V2_Color.Primary(props)}; - `; - case "default": - break; + if ($disabledDisplay) { + return css` + color: ${Colour["text-disabled-subtlest"]}; + `; + } + }} + + // hover styles + ${({ $variant, $interactive }) => { + if (!$interactive) { + return; + } + + if ($variant === "selected-month") { + return css` + &:hover { + background: ${Colour["bg-selected-hover"]}; + border-color: ${Colour["border-selected-hover"]}; + color: ${Colour["text-selected-hover"]}; + font-weight: ${FontSpec["weight-semibold"]}; + } + `; } + + return css` + &:hover { + background: ${Colour["bg-hover"]}; + color: ${Colour["text-hover"]}; + font-weight: ${FontSpec["weight-semibold"]}; + } + `; }} `; diff --git a/src/shared/internal-calendar/internal-calendar-month.tsx b/src/shared/internal-calendar/internal-calendar-month.tsx index 23bbda720..d336db13e 100644 --- a/src/shared/internal-calendar/internal-calendar-month.tsx +++ b/src/shared/internal-calendar/internal-calendar-month.tsx @@ -1,7 +1,7 @@ import dayjs, { Dayjs } from "dayjs"; import { useMemo } from "react"; import { CalendarHelper } from "../../util/calendar-helper"; -import { CellLabel, MonthCell, Wrapper } from "./internal-calendar-month.style"; +import { MonthCell, Wrapper } from "./internal-calendar-month.style"; import { FocusType, InternalCalendarProps } from "./types"; export type MonthVariant = "default" | "current-month" | "selected-month"; @@ -117,13 +117,7 @@ export const InternalCalendarMonth = ({ $interactive={interactive} onClick={() => handleMonthClick(date, !interactive)} > - - {month} - + {month} ); })} diff --git a/src/shared/internal-calendar/internal-calendar-year.style.tsx b/src/shared/internal-calendar/internal-calendar-year.style.tsx index 2a0cd25ca..e468e7230 100644 --- a/src/shared/internal-calendar/internal-calendar-year.style.tsx +++ b/src/shared/internal-calendar/internal-calendar-year.style.tsx @@ -1,7 +1,5 @@ import styled, { css } from "styled-components"; -import { V2_Color } from "../../v2_color"; -import { V2_TextStyleHelper } from "../../v2_text/helper"; -import { V2_Text } from "../../v2_text/text"; +import { Border, Colour, Font, FontSpec, Motion, Radius } from "../../theme"; import { YearVariant } from "./internal-calendar-year"; import { CalendarType } from "./types"; @@ -27,6 +25,7 @@ export const Wrapper = styled.div` height: 100%; display: grid; align-content: center; + align-items: center; grid-template-columns: repeat(3, 1fr); ${(props) => { @@ -49,70 +48,96 @@ export const YearCell = styled.div` display: flex; justify-content: center; align-items: center; - cursor: default; - border-radius: 0.5rem; margin: 0 0.5rem; + transition: ${Motion["duration-150"]} ${Motion["ease-default"]}; + padding: 0.5rem; - ${(props) => { - if (props.$interactive) { + // default styles + ${Font["body-md-regular"]} + border-radius: ${Radius.md}; + border: ${Border["width-010"]} ${Border.solid} transparent; + background-clip: border-box; + color: ${Colour["text"]}; + cursor: default; + + // cursor style + ${({ $interactive, $disabledDisplay }) => { + if ($interactive) { return css` cursor: pointer; - &:hover { - box-shadow: 0px 0px 4px 1px ${V2_Color.Shadow.Accent}; - border: 1px solid ${V2_Color.Accent.Light[1]}; - } `; } - if (props.$disabledDisplay) { + if ($disabledDisplay) { return css` cursor: not-allowed; `; } }} - ${(props) => { - switch (props.$variant) { - case "current-year": - return css` - background: ${V2_Color.Accent.Light[6](props)}; - `; - case "selected-year": - return css` - background: ${V2_Color.Accent.Light[5](props)}; - border: 1px solid ${V2_Color.Primary(props)}; - `; - case "other-decade": - case "default": - break; + // background, border and text styles + ${({ $variant, $interactive, $disabledDisplay }) => { + if ($variant === "selected-year") { + return css` + background: ${Colour["bg-selected"]}; + border-color: ${Colour["border-selected"]}; + color: ${Colour["text-selected"]}; + font-weight: ${FontSpec["weight-semibold"]}; + + ${$interactive && + css` + &:hover { + background: ${Colour["bg-selected-hover"]}; + border-color: ${Colour["border-selected-hover"]}; + color: ${Colour["text-selected-hover"]}; + } + `} + `; } - }}; -`; -export const CellLabel = styled(V2_Text.H5)` - ${(props) => { - if (props.$disabledDisplay) { + if ($variant === "current-year") { return css` - color: ${V2_Color.Neutral[4]}; + color: ${Colour["text-primary"]}; + font-weight: ${FontSpec["weight-semibold"]}; `; } - switch (props.$variant) { - case "current-year": - return css` - color: ${V2_Color.Neutral[3](props)}; - `; - case "selected-year": - return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")} - color: ${V2_Color.Primary(props)}; - `; - case "other-decade": - return css` - color: ${V2_Color.Neutral[4](props)}; - `; - case "default": - break; + if ($variant === "other-decade") { + return css` + color: ${Colour["text-disabled-subtlest"]}; + `; } + + if ($disabledDisplay) { + return css` + color: ${Colour["text-disabled-subtlest"]}; + `; + } + }} + + // hover styles + ${({ $variant, $interactive }) => { + if (!$interactive) { + return; + } + + if ($variant === "selected-year") { + return css` + &:hover { + background: ${Colour["bg-selected-hover"]}; + border-color: ${Colour["border-selected-hover"]}; + color: ${Colour["text-selected-hover"]}; + font-weight: ${FontSpec["weight-semibold"]}; + } + `; + } + + return css` + &:hover { + background: ${Colour["bg-hover"]}; + color: ${Colour["text-hover"]}; + font-weight: ${FontSpec["weight-semibold"]}; + } + `; }} `; diff --git a/src/shared/internal-calendar/internal-calendar-year.tsx b/src/shared/internal-calendar/internal-calendar-year.tsx index d4daa0aae..91fe7f7bc 100644 --- a/src/shared/internal-calendar/internal-calendar-year.tsx +++ b/src/shared/internal-calendar/internal-calendar-year.tsx @@ -1,7 +1,7 @@ import dayjs, { Dayjs } from "dayjs"; import { useMemo } from "react"; import { CalendarHelper } from "../../util/calendar-helper"; -import { CellLabel, Wrapper, YearCell } from "./internal-calendar-year.style"; +import { Wrapper, YearCell } from "./internal-calendar-year.style"; import { FocusType, InternalCalendarProps } from "./types"; export type YearVariant = @@ -126,14 +126,7 @@ export const InternalCalendarYear = ({ $interactive={interactive} onClick={() => handleYearClick(date, !interactive)} > - - {year} - + {year} ); })} diff --git a/src/shared/internal-calendar/internal-calendar.style.tsx b/src/shared/internal-calendar/internal-calendar.style.tsx index 14aeb4aa3..ca357c6c9 100644 --- a/src/shared/internal-calendar/internal-calendar.style.tsx +++ b/src/shared/internal-calendar/internal-calendar.style.tsx @@ -1,5 +1,5 @@ import styled, { css } from "styled-components"; -import { V2_Color } from "../../v2_color"; +import { Border, Colour, Radius } from "../../theme"; import { CalendarType } from "./types"; // ============================================================================= @@ -15,17 +15,17 @@ interface GeneralStyleProps { export const Container = styled.div` width: 100%; padding: 1.5rem 2rem; - background: ${V2_Color.Neutral[8]}; + background: ${Colour.bg}; ${(props) => { if (props.$type === "input") { return css` - border: 1px solid ${V2_Color.Neutral[5]}; - border-radius: 8px; + border: ${Border["width-010"]} ${Border.solid} ${Colour.border}; + border-radius: ${Radius["lg"]}; overflow: hidden; padding: 1.5rem 1.25rem; - [data-id="header"] { + [data-id="calendar-header"] { margin: 0 0 0.25rem 0; } `; diff --git a/src/shared/internal-calendar/internal-calendar.tsx b/src/shared/internal-calendar/internal-calendar.tsx index e31a39439..5aba93cfc 100644 --- a/src/shared/internal-calendar/internal-calendar.tsx +++ b/src/shared/internal-calendar/internal-calendar.tsx @@ -1,8 +1,9 @@ import { Dayjs } from "dayjs"; import React, { useImperativeHandle, useRef } from "react"; import { CalendarManager } from "./calendar-manager"; -import { FixedRangeCalendarDayView } from "./fixed-range/fixed-range-calendar-day-view"; +import { FixedRangeCalendarDayView } from "./fixed-range"; import { Container } from "./internal-calendar.style"; +import { SingleCalendarDayView } from "./single"; import { StandardCalendarDayView } from "./standard"; import { CalendarManagerRef, @@ -159,6 +160,19 @@ export const Component = ( /> ); case "single": + return ( + + ); case "range": default: // standalone type return ( diff --git a/src/shared/internal-calendar/single/index.ts b/src/shared/internal-calendar/single/index.ts new file mode 100644 index 000000000..c1fde322e --- /dev/null +++ b/src/shared/internal-calendar/single/index.ts @@ -0,0 +1 @@ +export * from "./single-calendar-day-view"; diff --git a/src/shared/internal-calendar/single/single-calendar-day-view.tsx b/src/shared/internal-calendar/single/single-calendar-day-view.tsx new file mode 100644 index 000000000..52ac422af --- /dev/null +++ b/src/shared/internal-calendar/single/single-calendar-day-view.tsx @@ -0,0 +1,108 @@ +import dayjs, { Dayjs } from "dayjs"; +import isBetween from "dayjs/plugin/isBetween"; +import { useMemo, useState } from "react"; +import { CalendarHelper } from "../../../util/calendar-helper"; +import { HeaderCell, RowDayCell, Wrapper } from "../standard"; +import { CommonCalendarProps } from "../types"; +import { SingleCell } from "./single-cell"; + +dayjs.extend(isBetween); + +// TODO: to remove after all references have been cleaned up +export type DayVariant = "default" | "other-month" | "today"; + +interface CalendarDayViewProps extends CommonCalendarProps { + selectedDate: string; + calendarDate: Dayjs; + onSelect: (value: Dayjs) => void; + onHover: (value: string) => void; +} + +export const SingleCalendarDayView = ({ + calendarDate, + disabledDates, + selectedDate, + onSelect, + onHover, + minDate, + maxDate, + allowDisabledSelection, + showActiveMonthDaysOnly, +}: CalendarDayViewProps) => { + // ============================================================================= + // CONST, STATE, REF + // ============================================================================= + const weeksOfTheMonth = useMemo( + (): Dayjs[][] => CalendarHelper.generateDays(calendarDate), + [calendarDate] + ); + const [hoverValue, setHoverValue] = useState(""); + + // ============================================================================= + // EVENT HANDLERS + // ============================================================================= + const handleDayClick = (value: Dayjs, isDisabled: boolean) => { + if (isDisabled && !allowDisabledSelection) return; + + onSelect(value); + }; + + const handleHoverCell = (value: string, isDisabled: boolean) => { + if (isDisabled && !allowDisabledSelection) return; + + setHoverValue(value); + onHover(value); + }; + + const handleMouseLeaveCell = () => { + setHoverValue(""); + onHover(""); + }; + + // ============================================================================= + // RENDER FUNCTIONS + // ============================================================================= + const renderHeader = () => { + return weeksOfTheMonth[0].map((day, index) => ( + + {dayjs(day).format("ddd")} + + )); + }; + + const renderDayCells = () => { + return weeksOfTheMonth.map((week, weekIndex) => { + return ( + + {week.map((day, dayIndex) => { + return ( + + ); + })} + + ); + }); + }; + + return ( + + {renderHeader()} + {renderDayCells()} + + ); +}; diff --git a/src/shared/internal-calendar/single/single-cell.tsx b/src/shared/internal-calendar/single/single-cell.tsx new file mode 100644 index 000000000..7dfdbd1a0 --- /dev/null +++ b/src/shared/internal-calendar/single/single-cell.tsx @@ -0,0 +1,103 @@ +import dayjs, { Dayjs } from "dayjs"; +import { CalendarHelper } from "../../../util"; +import { CellStyleProps, DayCell, DayCellProps } from "../day-cell"; + +interface Props { + date: Dayjs; + calendarDate: Dayjs; + selectedDate: string; + hoverDate: string; + minDate?: string | undefined; + maxDate?: string | undefined; + disabledDates?: string[] | undefined; + allowDisabledSelection?: boolean | undefined; + showActiveMonthDaysOnly?: boolean | undefined; + onSelect: (value: Dayjs, disabled: boolean) => void; + onHover: (value: string, disabled: boolean) => void; +} + +export const SingleCell = ({ + date, + calendarDate, + selectedDate, + hoverDate, + minDate, + maxDate, + disabledDates, + allowDisabledSelection, + showActiveMonthDaysOnly, + onSelect, + onHover, +}: Props) => { + // ========================================================================= + // CONSTS + // ========================================================================= + const disabled = CalendarHelper.isDisabledDay( + date, + disabledDates, + minDate, + maxDate + ); + const interactive = !disabled || allowDisabledSelection; + + // ========================================================================= + // EVENT HANDLERS + // ========================================================================= + const handleSelect = () => { + onSelect(date, !interactive); + }; + + const handleHover = () => { + onHover(date.format("YYYY-MM-DD"), !interactive); + }; + + // ========================================================================= + // HELPERS + // ========================================================================= + const getCellStyle = () => { + const props: CellStyleProps = {}; + + if (calendarDate.month() !== date.month()) { + props.labelType = showActiveMonthDaysOnly + ? "hidden" + : "unavailable"; + } else if (dayjs().isSame(date, "day") && !disabled) { + props.labelType = "current"; + } + + const isSelected = date.isSame(selectedDate, "day"); + const isHover = date.isSame(hoverDate, "day"); + + if (isSelected && isHover) { + props.labelType = "selected-hover"; + props.circleLeft = "selected-hover-outline"; + props.circleRight = "selected-hover-outline"; + } else if (isSelected) { + props.labelType = "selected"; + props.circleLeft = "selected-outline"; + props.circleRight = "selected-outline"; + } else if (isHover) { + props.labelType = "hover"; + props.circleLeft = "hover-subtle"; + props.circleRight = "hover-subtle"; + } + + return props; + }; + + // ============================================================================= + // RENDER FUNCTION + // ============================================================================= + + const commonProps: DayCellProps = { + date, + calendarDate, + disabled, + interactive, + currentDateIndicator: true, + onSelect: handleSelect, + onHover: handleHover, + }; + + return ; +}; diff --git a/src/shared/internal-calendar/standard/standard-calendar-day-view.style.tsx b/src/shared/internal-calendar/standard/standard-calendar-day-view.style.tsx index 6138066f0..ae90a2ae7 100644 --- a/src/shared/internal-calendar/standard/standard-calendar-day-view.style.tsx +++ b/src/shared/internal-calendar/standard/standard-calendar-day-view.style.tsx @@ -1,33 +1,6 @@ -import styled, { css } from "styled-components"; -import { V2_Color } from "../../../v2_color"; -import { V2_Text, V2_TextStyleHelper } from "../../../v2_text"; -import { DayVariant } from "./standard-calendar-day-view"; +import styled from "styled-components"; +import { Colour, Font } from "../../../theme"; -// ============================================================================= -// STYLE INTERFACES, transient props are denoted with $ -// See more https://styled-components.com/docs/api#transient-props -// ============================================================================= -export interface StyleProps { - $disabledDisplay?: boolean; - $interactive?: boolean; - $overlap?: boolean; - $hovered?: boolean; - $selected?: boolean; -} - -export interface DayLabelStyleProps extends StyleProps { - $variant: DayVariant; -} - -export interface BaseOverflowDisplayProps extends StyleProps { - $position: "left" | "right"; -} - -export interface BaseInteractiveCircleProps extends DayLabelStyleProps {} - -// ============================================================================= -// COMMON STYLING for DAY CELL -// ============================================================================= export const Wrapper = styled.div` width: 100%; display: grid; @@ -42,194 +15,12 @@ export const HeaderCell = styled.div` height: 2.5rem; pointer-events: none; user-select: none; + + ${Font["body-sm-semibold"]}; + color: ${Colour["text"]}; `; export const RowDayCell = styled.div` grid-column: 1 / -1; display: flex; `; - -export const GrowDayCell = styled.div` - display: flex; - position: relative; - height: 2.5rem; - align-items: center; - justify-content: center; - flex: 1; -`; - -export const BaseOverflowDisplay = styled.div` - position: absolute; - width: 50%; - height: 100%; - - ${(props) => { - switch (props.$position) { - case "left": - return css` - left: 0; - `; - case "right": - return css` - right: 0; - `; - } - }} -`; - -export const BaseInteractiveCircle = styled.div` - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - width: 2.5rem; - height: 2.5rem; - cursor: default; - position: absolute; -`; - -export const DayLabel = styled(V2_Text.H5)` - ${(props) => { - const { $disabledDisplay, $selected, $variant } = props; - - if ($disabledDisplay && $selected) { - return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")}; - color: ${V2_Color.Accent.Light[2]}; - `; - } - - if ($disabledDisplay) { - return css` - color: ${V2_Color.Neutral[4]}; - `; - } - - if ($selected) { - return css` - ${V2_TextStyleHelper.getTextStyle("H5", "semibold")}; - color: ${V2_Color.Primary}; - `; - } - - switch ($variant) { - case "other-month": - return css` - color: ${V2_Color.Neutral[4]}; - `; - case "today": - return css` - color: ${V2_Color.Neutral[3]}; - `; - case "default": - return css` - color: ${V2_Color.Neutral[1]}; - `; - } - }} -`; - -// ============================================================================= -// STYLING for Regular -// ============================================================================= -export const OverflowDisplay = styled(BaseOverflowDisplay)` - ${(props) => { - const { $selected } = props; - - if ($selected) { - return css` - border-top: 1px solid ${V2_Color.Accent.Light[4]}; - border-bottom: 1px solid ${V2_Color.Accent.Light[4]}; - background-color: ${V2_Color.Accent.Light[5]}; - `; - } - }} - - ${(props) => { - const { $hovered, $overlap } = props; - - if ($hovered) { - return css` - border-top: 1px dashed ${V2_Color.Accent.Light[4]}; - border-bottom: 1px dashed ${V2_Color.Accent.Light[4]}; - background-color: ${V2_Color.Accent.Light[6]}; - `; - } - - if ($overlap) { - return css` - background-color: ${V2_Color.Accent.Light[4]}; - `; - } - }} -`; - -export const InteractiveCircle = styled(BaseInteractiveCircle)` - ${(props) => { - const { $hovered, $selected } = props; - - if ($selected) { - return css` - background: ${V2_Color.Accent.Light[5]}; - border: 1px solid ${V2_Color.Primary}; - `; - } - - if ($hovered) { - return css` - box-shadow: 0px 0px 4px 1px ${V2_Color.Shadow.Accent}; - border: 1px solid ${V2_Color.Accent.Light[1]}; - background-color: ${V2_Color.Neutral[8]}; - `; - } - }} - - ${(props) => { - const { $interactive, $disabledDisplay } = props; - - if ($interactive) { - return css` - cursor: pointer; - :hover { - box-shadow: 0px 0px 4px 1px ${V2_Color.Shadow.Accent}; - border: 1px solid ${V2_Color.Accent.Light[1]}; - background-color: ${V2_Color.Neutral[8]}; - } - `; - } else if ($disabledDisplay) { - return css` - cursor: not-allowed; - `; - } - }} - - ${(props) => { - const { $disabledDisplay, $overlap, $variant } = props; - - if ($overlap) { - return css` - border: 1px solid ${V2_Color.Accent.Light[1]}; - background: ${V2_Color.Accent.Light[4]}; - - :hover { - background: ${V2_Color.Accent.Light[4]}; - } - `; - } - - if ($disabledDisplay) { - return css` - color: ${V2_Color.Neutral[4]}; - `; - } - - switch ($variant) { - case "today": - return css` - background: ${V2_Color.Accent.Light[5]}; - `; - default: - break; - } - }} -`; diff --git a/src/shared/internal-calendar/standard/standard-calendar-day-view.tsx b/src/shared/internal-calendar/standard/standard-calendar-day-view.tsx index c05d80b95..9469b2ff4 100644 --- a/src/shared/internal-calendar/standard/standard-calendar-day-view.tsx +++ b/src/shared/internal-calendar/standard/standard-calendar-day-view.tsx @@ -1,7 +1,6 @@ import dayjs, { Dayjs } from "dayjs"; import isBetween from "dayjs/plugin/isBetween"; import { useMemo, useState } from "react"; -import { V2_Text } from "../../../v2_text/text"; import { CalendarHelper } from "../../../util/calendar-helper"; import { CommonCalendarProps, FocusType } from "../types"; import { @@ -76,9 +75,7 @@ export const StandardCalendarDayView = ({ const renderHeader = () => { return weeksOfTheMonth[0].map((day, index) => ( - - {dayjs(day).format("ddd")} - + {dayjs(day).format("ddd")} )); }; diff --git a/src/shared/internal-calendar/standard/standard-cell.tsx b/src/shared/internal-calendar/standard/standard-cell.tsx index 2d055a2be..ff08bebcf 100644 --- a/src/shared/internal-calendar/standard/standard-cell.tsx +++ b/src/shared/internal-calendar/standard/standard-cell.tsx @@ -71,8 +71,6 @@ export const StandardCell = ({ : "unavailable"; } else if (dayjs().isSame(date, "day") && !disabled) { props.labelType = "current"; - props.circleLeft = "current"; - props.circleRight = "current"; } else if (isNewSelection) { const beforeStart = currentFocus === "end" && startDate && date.isBefore(startDate); @@ -112,10 +110,10 @@ export const StandardCell = ({ props.labelType = "selected"; if (!isStart) { - props.bgLeft = "selected"; + props.bgLeft = "selected-outline-subtle"; } if (!isEnd) { - props.bgRight = "selected"; + props.bgRight = "selected-outline-subtle"; } } @@ -131,27 +129,35 @@ export const StandardCell = ({ const isHover = date.isSame(hoverDate, "day"); - if (isHover) { - props.circleShadow = true; - props.circleLeft = "hover-current"; - props.circleRight = "hover-current"; - } - const { hoverStart, hoverEnd, overlapStart, overlapEnd } = getHoverRange(); + if (isHover) { + const isStart = date.isSame(startDate, "day"); + const isEnd = date.isSame(endDate, "day"); + if (isStart || isEnd) { + props.labelType = "selected-hover"; + props.circleLeft = "selected-hover-outline"; + props.circleRight = "selected-hover-outline"; + } else { + props.labelType = "hover"; + props.circleLeft = "hover"; + props.circleRight = "hover"; + } + } + if (hoverStart && hoverEnd) { if (date.isBetween(hoverStart, hoverEnd, "day", "[]")) { const isStart = date.isSame(hoverStart, "day"); const isEnd = date.isSame(hoverEnd, "day"); - props.labelType = "selected"; - if (!isStart) { - props.bgLeft = "hover-dash"; + props.labelType = "hover"; + props.bgLeft = "hover-outline"; } if (!isEnd) { - props.bgRight = "hover-dash"; + props.labelType = "hover"; + props.bgRight = "hover-outline"; } } @@ -160,22 +166,10 @@ export const StandardCell = ({ if (overlapStart && overlapEnd) { if (date.isBetween(overlapStart, overlapEnd, "day", "[]")) { - const isStart = date.isSame(overlapStart, "day"); - const isEnd = date.isSame(overlapEnd, "day"); - - props.labelType = "selected"; - - if (isStart || isEnd) { - props.circleShadow = true; - props.circleLeft = "overlap-outline"; - props.circleRight = "overlap-outline"; - } - - if (!isStart) { - props.bgLeft = "overlap"; - } - if (!isEnd) { - props.bgRight = "overlap"; + if (isHover) { + props.labelType = "selected-hover"; + props.circleLeft = "selected-hover"; + props.circleRight = "selected-hover"; } } @@ -236,6 +230,7 @@ export const StandardCell = ({ calendarDate, disabled, interactive, + currentDateIndicator: true, onSelect: handleSelect, onHover: handleHover, }; diff --git a/src/shared/internal-calendar/week/week-calendar-day-view.tsx b/src/shared/internal-calendar/week/week-calendar-day-view.tsx index aa5f366ad..d7d8b3f61 100644 --- a/src/shared/internal-calendar/week/week-calendar-day-view.tsx +++ b/src/shared/internal-calendar/week/week-calendar-day-view.tsx @@ -1,6 +1,5 @@ import dayjs, { Dayjs } from "dayjs"; import { useMemo, useState } from "react"; -import { V2_Text } from "../../../v2_text/text"; import { CalendarHelper } from "../../../util/calendar-helper"; import { HeaderCell, RowDayCell, Wrapper } from "../standard"; import { CommonCalendarProps } from "../types"; @@ -64,9 +63,7 @@ export const WeekCalendarDayView = ({ const renderHeader = () => { return weeksOfTheMonth[0].map((day, index) => ( - - {dayjs(day).format("ddd")} - + {dayjs(day).format("ddd")} )); }; diff --git a/src/shared/internal-calendar/week/week-day-cell.tsx b/src/shared/internal-calendar/week/week-day-cell.tsx index f83a390b5..0d357c862 100644 --- a/src/shared/internal-calendar/week/week-day-cell.tsx +++ b/src/shared/internal-calendar/week/week-day-cell.tsx @@ -1,6 +1,12 @@ import dayjs, { Dayjs } from "dayjs"; import { CalendarHelper, DateHelper } from "../../../util"; -import { CellStyleProps, CellType, DayCell, DayCellProps } from "../day-cell"; +import { + CellStyleProps, + CellType, + DayCell, + DayCellProps, + LabelType, +} from "../day-cell"; interface Props { date: Dayjs; @@ -73,17 +79,21 @@ export const WeekDayCell = ({ const props: CellStyleProps = {}; let type: CellType = undefined; + let labelType: LabelType = undefined; if (isSelected && isHover) { - type = "hover-current"; - props.shadow = true; - props.circleShadow = isStart || isEnd; + type = "selected-hover-outline"; + labelType = "selected-hover"; } else if (isSelected) { type = "selected-outline"; + labelType = "selected"; } else if (isHover) { - type = "hover-dash"; + type = "hover"; + labelType = "hover"; } if (type) { + props.labelType = labelType; + if (isStart) { props.circleLeft = type; } else { @@ -103,14 +113,10 @@ export const WeekDayCell = ({ const getCellStyle = () => { const props: CellStyleProps = {}; - if (isSelected || isHover) { - props.labelType = "selected"; - } else if (calendarDate.month() !== date.month()) { + if (calendarDate.month() !== date.month()) { props.labelType = "unavailable"; } else if (dayjs().isSame(date, "day") && !disabled) { props.labelType = "current"; - props.circleLeft = "current"; - props.circleRight = "current"; } return props; @@ -125,6 +131,7 @@ export const WeekDayCell = ({ calendarDate, disabled, interactive, + currentDateIndicator: true, onSelect: handleSelect, onHover: handleHover, }; diff --git a/src/time-slot-bar-week/time-slot-bar-week-days.tsx b/src/time-slot-bar-week/time-slot-bar-week-days.tsx index 988cbb56b..7fcb90b95 100644 --- a/src/time-slot-bar-week/time-slot-bar-week-days.tsx +++ b/src/time-slot-bar-week/time-slot-bar-week-days.tsx @@ -168,17 +168,22 @@ export const TimeSlotBarWeekDays = ({ ? null : isHoverEnabled; - if (isHoverEnabled && hoverDay && day.isSame(hoverDay, "day")) { - dayCellStyleProps.circleLeft = "hover-current"; - dayCellStyleProps.circleRight = "hover-current"; - dayCellStyleProps.circleShadow = true; - } - - // Apply selected styles - if ([selectedDate].includes(dateStartWithYear)) { + const isHover = + isHoverEnabled && hoverDay && day.isSame(hoverDay, "day"); + const isSelected = [selectedDate].includes(dateStartWithYear); + + if (isSelected && isHover) { + dayCellStyleProps.labelType = "selected-hover"; + dayCellStyleProps.circleLeft = "selected-hover-outline"; + dayCellStyleProps.circleRight = "selected-hover-outline"; + } else if (isSelected) { dayCellStyleProps.labelType = "selected"; dayCellStyleProps.circleLeft = "selected-outline"; dayCellStyleProps.circleRight = "selected-outline"; + } else if (isHover) { + dayCellStyleProps.labelType = "hover"; + dayCellStyleProps.circleLeft = "hover-subtle"; + dayCellStyleProps.circleRight = "hover-subtle"; } return dayCellStyleProps; diff --git a/src/time-slot-week-view/time-slot-week-days.style.tsx b/src/time-slot-week-view/time-slot-week-days.style.tsx index bf7a0468e..90ac3383c 100644 --- a/src/time-slot-week-view/time-slot-week-days.style.tsx +++ b/src/time-slot-week-view/time-slot-week-days.style.tsx @@ -1,29 +1,36 @@ import styled, { css } from "styled-components"; -import { DayLabel } from "../shared/internal-calendar/standard"; import { Colour, Font } from "../theme"; -export const DayLabelWeek = styled(DayLabel)` - ${(props) => { - const { $variant } = props; - switch ($variant) { - case "default": - return css` - ${Font["body-md-semibold"]} - color: ${Colour["text-subtler"]}; - `; - } - }} -`; +// ============================================================================= +// STYLE INTERFACES +// ============================================================================= +interface LabelStyleProps { + $disabled: boolean; +} +// ============================================================================= +// STYLING +// ============================================================================= export const HeaderCellWeek = styled.div` display: flex; + flex-direction: column; align-items: center; justify-content: center; - pointer-events: none; user-select: none; margin-bottom: 0.188rem; `; +export const DayLabel = styled.div` + ${Font["body-xs-semibold"]} + color:${Colour["text"]}; + + ${(props) => + props.$disabled && + css` + color: ${Colour["text-disabled-subtlest"]}; + `}; +`; + export const Wrapper = styled.div` width: 100%; display: grid; @@ -35,6 +42,7 @@ export const ColumnWeekCell = styled.div` display: flex; min-height: 7.625rem; `; + export const TimeSlotText = styled.div` ${Font["body-xs-semibold"]} margin: 1rem 0rem; diff --git a/src/time-slot-week-view/time-slot-week-days.tsx b/src/time-slot-week-view/time-slot-week-days.tsx index 7c73190df..002573d3e 100644 --- a/src/time-slot-week-view/time-slot-week-days.tsx +++ b/src/time-slot-week-view/time-slot-week-days.tsx @@ -1,21 +1,15 @@ import dayjs, { Dayjs } from "dayjs"; import isBetween from "dayjs/plugin/isBetween"; -import { Colour } from "../theme"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { InternalCalendarProps } from "../shared/internal-calendar"; -import { - GrowDayCell, - InteractiveCircle, - RowDayCell, - StyleProps, -} from "../shared/internal-calendar/standard"; +import { CellStyleProps, DayCell } from "../shared/internal-calendar/day-cell"; +import { Colour } from "../theme"; import { TimeSlot as TimeSlotComponent } from "../time-slot-bar/time-slot-bar.styles"; import { TimeSlot } from "../time-slot-bar/types"; -import { Typography } from "../typography"; import { CalendarHelper } from "../util/calendar-helper"; import { ColumnWeekCell, - DayLabelWeek, + DayLabel, HeaderCellWeek, TimeSlotText, TimeSlotWrapper, @@ -38,6 +32,8 @@ interface TimeSlotWeekDaysProps onSlotClick?: ((date: string, timeSlot: TimeSlot) => void) | undefined; } +const dateFormat = "YYYY-MM-DD"; + const fallbackSlot = { id: "1", startTime: "", @@ -67,6 +63,7 @@ export const TimeSlotWeekDays = ({ const currentCalendarWeek = useMemo((): Dayjs[] => { return CalendarHelper.generateDaysForCurrentWeek(calendarDate); }, [calendarDate]); + const [hoverDay, setHoverDay] = useState(); // ============================================================================= // EVENT HANDLERS @@ -80,7 +77,14 @@ export const TimeSlotWeekDays = ({ const handleSlotClick = (date: string, slot: TimeSlot) => { onSlotClick(date, slot); }; - const dateFormat = "YYYY-MM-DD"; + + const handleDayHover = (value: Dayjs) => { + setHoverDay(value); + }; + + const handleDayMouseout = () => { + setHoverDay(undefined); + }; // ============================================================================= // HELPER FUNCTIONS @@ -101,78 +105,63 @@ export const TimeSlotWeekDays = ({ const generateStyleProps = (day: Dayjs) => { const dateStartWithYear = day.format(dateFormat); const disabled = isDisabled(day); - - const styleCircleProps: StyleProps = {}, - styleLabelProps: StyleProps = {}; + const isHoverEnabled = enableSelection && !disabled; + const isHover = + isHoverEnabled && hoverDay && day.isSame(hoverDay, "day"); + const isSelected = [selectedDate].includes(dateStartWithYear); + + const dayCellStyleProps: CellStyleProps = { + labelType: "available", + interactive: !enableSelection ? null : isHoverEnabled, + }; if (disabled) { - styleCircleProps.$disabledDisplay = true; - styleLabelProps.$disabledDisplay = true; + dayCellStyleProps.disabled = true; + dayCellStyleProps.labelType = "unavailable"; } - styleCircleProps.$interactive = enableSelection && !disabled; - - // apply selected styles - if ([selectedDate].includes(dateStartWithYear)) { - styleCircleProps.$selected = true; - styleLabelProps.$selected = true; + if (isSelected && isHover) { + dayCellStyleProps.labelType = "selected-hover"; + dayCellStyleProps.circleLeft = "selected-hover-outline"; + dayCellStyleProps.circleRight = "selected-hover-outline"; + } else if (isSelected) { + dayCellStyleProps.labelType = "selected"; + dayCellStyleProps.circleLeft = "selected-outline"; + dayCellStyleProps.circleRight = "selected-outline"; + } else if (isHover) { + dayCellStyleProps.labelType = "hover"; + dayCellStyleProps.circleLeft = "hover-subtle"; + dayCellStyleProps.circleRight = "hover-subtle"; } - return { - styleCircleProps, - styleLabelProps, - }; + return dayCellStyleProps; }; // ============================================================================= // RENDER FUNCTIONS // ============================================================================= - const renderWeek = () => { - return currentCalendarWeek.map((day, index) => ( - - - {dayjs(day).format("ddd")} - - - )); - }; - const renderHeader = () => { - return ( - - {currentCalendarWeek.map((day, dayIndex) => { - const variant = "default"; - const { styleCircleProps, styleLabelProps } = - generateStyleProps(day); - - return ( - - - handleDayClick( - day, - !styleCircleProps.$interactive - ) - } - {...styleCircleProps} - > - - {day.format("D")} - - - - ); - })} - - ); + return currentCalendarWeek.map((day, index) => { + const dayCellStyleProps = generateStyleProps(day); + + return ( + + { + handleDayClick(day, !dayCellStyleProps.interactive); + }} + onHover={handleDayHover} + onHoverEnd={handleDayMouseout} + {...dayCellStyleProps} + /> + + {dayjs(day).format("ddd")} + + + ); + }); }; const renderTimeSlotBarCells = () => { @@ -253,7 +242,6 @@ export const TimeSlotWeekDays = ({ return ( {renderHeader()} - {renderWeek()} {renderTimeSlotBarCells()} );