diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..f3e08235 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + // IntelliSense for taiwind variants + "tailwindCSS.experimental.classRegex": [ + ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ] +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index a2a77ba1..d1e6e78b 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -5,6 +5,7 @@ "dependencies": { "@fontsource/inter": "^5.0.16", "@material-tailwind/react": "^2.1.7", + "@radix-ui/react-checkbox": "^1.0.4", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -13,13 +14,14 @@ "@types/react": "^18.2.42", "@types/react-dom": "^18.2.17", "assert": "^2.1.0", - "date-fns": "^3.0.1", + "date-fns": "^3.3.1", "downshift": "^8.2.3", "eslint-config-react-app": "^7.0.1", "gql-client": "^1.0.0", "luxon": "^3.4.4", "octokit": "^3.1.2", "react": "^18.2.0", + "react-calendar": "^4.8.0", "react-code-blocks": "^0.1.6", "react-day-picker": "^8.9.1", "react-dom": "^18.2.0", diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.css b/packages/frontend/src/components/shared/Calendar/Calendar.css new file mode 100644 index 00000000..85ce4182 --- /dev/null +++ b/packages/frontend/src/components/shared/Calendar/Calendar.css @@ -0,0 +1,128 @@ +/* React Calendar */ +.react-calendar { + @apply border-none font-sans; +} + +/* Weekdays -- START */ +.react-calendar__month-view__weekdays { + @apply p-0 flex items-center justify-center; +} + +.react-calendar__month-view__weekdays__weekday { + @apply h-8 w-12 flex items-center justify-center p-0 font-medium text-xs text-elements-disabled mb-2; +} + +abbr[title] { + text-decoration: none; +} +/* Weekdays -- END */ + +/* Days -- START */ +.react-calendar__month-view__days { + @apply p-0 gap-0; +} + +.react-calendar__month-view__days__day--neighboringMonth { + @apply !text-elements-disabled; +} + +.react-calendar__month-view__days__day--neighboringMonth:hover { + @apply !text-elements-disabled !bg-transparent; +} + +.react-calendar__month-view__days__day--neighboringMonth { + @apply !text-elements-disabled !bg-transparent; +} + +/* For weekend days */ +.react-calendar__month-view__days__day--weekend { + /* color: ${colors.grey[950]} !important; */ +} + +.react-calendar__tile { + @apply h-12 w-12 text-elements-high-em; +} + +.react-calendar__tile:hover { + @apply bg-base-bg-emphasized rounded-lg; +} + +.react-calendar__tile:focus-visible { + @apply bg-base-bg-emphasized rounded-lg focus-ring z-10; +} + +.react-calendar__tile--now { + @apply bg-base-bg-emphasized text-elements-high-em rounded-lg; +} + +.react-calendar__tile--now:hover { + @apply bg-base-bg-emphasized text-elements-high-em rounded-lg; +} + +.react-calendar__tile--now:focus-visible { + @apply bg-base-bg-emphasized text-elements-high-em rounded-lg focus-ring; +} + +.react-calendar__tile--active { + @apply bg-controls-primary text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--active:hover { + @apply bg-controls-primary-hovered; +} + +.react-calendar__tile--active:focus-visible { + @apply bg-controls-primary-hovered focus-ring; +} + +/* Range -- START */ +.react-calendar__tile--range { + @apply bg-controls-secondary text-elements-on-secondary rounded-none; +} + +.react-calendar__tile--range:hover { + @apply bg-controls-secondary-hovered text-elements-on-secondary rounded-none; +} + +.react-calendar__tile--range:focus-visible { + @apply bg-controls-secondary-hovered text-elements-on-secondary rounded-lg; +} + +.react-calendar__tile--rangeStart { + @apply bg-controls-primary text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--rangeStart:hover { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--rangeStart:focus-visible { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring; +} + +.react-calendar__tile--rangeEnd { + @apply bg-controls-primary text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--rangeEnd:hover { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--rangeEnd:focus-visible { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring; +} +/* Range -- END */ +/* Days -- END */ + +/* Months -- START */ +.react-calendar__tile--hasActive { + @apply bg-controls-primary text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--hasActive:hover { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg; +} + +.react-calendar__tile--hasActive:focus-visible { + @apply bg-controls-primary-hovered text-elements-on-primary rounded-lg focus-ring; +} diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts new file mode 100644 index 00000000..ce6e93d0 --- /dev/null +++ b/packages/frontend/src/components/shared/Calendar/Calendar.theme.ts @@ -0,0 +1,49 @@ +import { VariantProps, tv } from 'tailwind-variants'; + +export const calendarTheme = tv({ + slots: { + wrapper: [ + 'max-w-[352px]', + 'bg-surface-floating', + 'shadow-calendar', + 'rounded-xl', + ], + calendar: ['flex', 'flex-col', 'py-2', 'px-2', 'gap-2'], + navigation: [ + 'flex', + 'items-center', + 'justify-between', + 'gap-3', + 'py-2.5', + 'px-1', + ], + actions: ['flex', 'items-center', 'justify-center', 'gap-1.5', 'flex-1'], + button: [ + 'flex', + 'items-center', + 'gap-2', + 'px-2', + 'py-2', + 'rounded-lg', + 'border', + 'border-border-interactive', + 'text-elements-high-em', + 'shadow-field', + 'bg-white', + 'hover:bg-base-bg-alternate', + 'focus-visible:bg-base-bg-alternate', + ], + footer: [ + 'flex', + 'items-center', + 'justify-end', + 'py-3', + 'px-2', + 'gap-3', + 'border-t', + 'border-border-separator', + ], + }, +}); + +export type CalendarTheme = VariantProps; diff --git a/packages/frontend/src/components/shared/Calendar/Calendar.tsx b/packages/frontend/src/components/shared/Calendar/Calendar.tsx new file mode 100644 index 00000000..dfc15e36 --- /dev/null +++ b/packages/frontend/src/components/shared/Calendar/Calendar.tsx @@ -0,0 +1,298 @@ +import React, { + ComponentPropsWithRef, + MouseEvent, + ReactNode, + useCallback, + useState, +} from 'react'; +import { + Calendar as ReactCalendar, + CalendarProps as ReactCalendarProps, +} from 'react-calendar'; +import { Value } from 'react-calendar/dist/cjs/shared/types'; +import { CalendarTheme, calendarTheme } from './Calendar.theme'; +import { Button } from 'components/shared/Button'; +import { + ChevronGrabberHorizontal, + ChevronLeft, + ChevronRight, +} from 'components/shared/CustomIcon'; + +import './Calendar.css'; +import { format } from 'date-fns'; + +const CALENDAR_VIEW = ['month', 'year', 'decade'] as const; +export type CalendarView = (typeof CALENDAR_VIEW)[number]; + +/** + * Defines a custom set of props for a React calendar component by excluding specific props + * from the original ReactCalendarProps type. + * @type {CustomReactCalendarProps} + */ +type CustomReactCalendarProps = Omit< + ReactCalendarProps, + 'view' | 'showNavigation' | 'onClickMonth' | 'onClickYear' +>; + +export interface CalendarProps extends CustomReactCalendarProps, CalendarTheme { + /** + * Optional props for wrapping a component with a div element. + */ + wrapperProps?: ComponentPropsWithRef<'div'>; + /** + * Props for the calendar wrapper component. + */ + calendarWrapperProps?: ComponentPropsWithRef<'div'>; + /** + * Optional props for the footer component. + */ + footerProps?: ComponentPropsWithRef<'div'>; + /** + * Optional custom actions to be rendered. + */ + actions?: ReactNode; + /** + * Optional callback function that is called when a value is selected. + * @param {Value} value - The selected value + * @returns None + */ + onSelect?: (value: Value) => void; + /** + * Optional callback function that is called when a cancel action is triggered. + * @returns None + */ + onCancel?: () => void; +} + +/** + * Calendar component that allows users to select dates and navigate through months and years. + * @param {Object} CalendarProps - Props for the Calendar component. + * @returns {JSX.Element} A calendar component with navigation, date selection, and actions. + */ +export const Calendar = ({ + selectRange, + activeStartDate: activeStartDateProp, + value: valueProp, + wrapperProps, + calendarWrapperProps, + footerProps, + actions, + onSelect, + onCancel, + onChange: onChangeProp, + ...props +}: CalendarProps): JSX.Element => { + const { + wrapper, + calendar, + navigation, + actions: actionsClass, + button, + footer, + } = calendarTheme(); + + const today = new Date(); + const currentMonth = format(today, 'MMM'); + const currentYear = format(today, 'yyyy'); + + const [view, setView] = useState('month'); + const [activeDate, setActiveDate] = useState( + activeStartDateProp ?? today, + ); + const [value, setValue] = useState(valueProp as Value); + const [month, setMonth] = useState(currentMonth); + const [year, setYear] = useState(currentYear); + + /** + * Update the navigation label based on the active date + */ + const changeNavigationLabel = useCallback( + (date: Date) => { + setMonth(format(date, 'MMM')); + setYear(format(date, 'yyyy')); + }, + [setMonth, setYear], + ); + + /** + * Change the active date base on the action and range + */ + const handleNavigate = useCallback( + (action: 'previous' | 'next', view: CalendarView) => { + setActiveDate((date) => { + const newDate = new Date(date); + switch (view) { + case 'month': + newDate.setMonth( + action === 'previous' ? date.getMonth() - 1 : date.getMonth() + 1, + ); + break; + case 'year': + newDate.setFullYear( + action === 'previous' + ? date.getFullYear() - 1 + : date.getFullYear() + 1, + ); + break; + case 'decade': + newDate.setFullYear( + action === 'previous' + ? date.getFullYear() - 10 + : date.getFullYear() + 10, + ); + break; + } + changeNavigationLabel(newDate); + return newDate; + }); + }, + [setActiveDate, changeNavigationLabel], + ); + + /** + * Change the view of the calendar + */ + const handleChangeView = useCallback( + (view: CalendarView) => { + setView(view); + }, + [setView], + ); + + /** + * Change the active date and set the view to the selected type + * and also update the navigation label + */ + const handleChangeNavigation = useCallback( + (view: 'month' | 'year', date: Date) => { + setActiveDate(date); + changeNavigationLabel(date); + setView(view); + }, + [setActiveDate, changeNavigationLabel, setView], + ); + + const handlePrevious = useCallback(() => { + switch (view) { + case 'month': + return handleNavigate('previous', 'month'); + case 'year': + return handleNavigate('previous', 'year'); + case 'decade': + return handleNavigate('previous', 'decade'); + } + }, [view]); + + const handleNext = useCallback(() => { + switch (view) { + case 'month': + return handleNavigate('next', 'month'); + case 'year': + return handleNavigate('next', 'year'); + case 'decade': + return handleNavigate('next', 'decade'); + } + }, [view]); + + const handleChange = useCallback( + (newValue: Value, event: MouseEvent) => { + setValue(newValue); + + // Call the onChange prop if it exists + onChangeProp?.(newValue, event); + + /** + * Update the active date and navigation label + * + * NOTE: + * For range selection, the active date is not updated + * The user only can select multiple dates within the same month + */ + if (!selectRange) { + setActiveDate(newValue as Date); + changeNavigationLabel(newValue as Date); + } + }, + [setValue, setActiveDate, changeNavigationLabel, selectRange], + ); + + return ( +
+ {/* Calendar wrapper */} +
+ {/* Navigation */} +
+ +
+ + +
+ +
+ + {/* Calendar */} + handleChangeNavigation('month', date)} + onClickYear={(date) => handleChangeNavigation('year', date)} + /> +
+ + {/* Footer or CTA */} +
+ {actions ? ( + actions + ) : ( + <> + + + + )} +
+
+ ); +}; diff --git a/packages/frontend/src/components/shared/Calendar/index.ts b/packages/frontend/src/components/shared/Calendar/index.ts new file mode 100644 index 00000000..a7233805 --- /dev/null +++ b/packages/frontend/src/components/shared/Calendar/index.ts @@ -0,0 +1 @@ +export * from './Calendar'; diff --git a/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts new file mode 100644 index 00000000..e39b66f3 --- /dev/null +++ b/packages/frontend/src/components/shared/Checkbox/Checkbox.theme.ts @@ -0,0 +1,68 @@ +import { tv, type VariantProps } from 'tailwind-variants'; + +export const getCheckboxVariant = tv({ + slots: { + wrapper: ['group', 'flex', 'gap-3'], + indicator: [ + 'grid', + 'place-content-center', + 'text-transparent', + 'group-hover:text-controls-disabled', + 'focus-visible:text-controls-disabled', + 'group-focus-visible:text-controls-disabled', + 'data-[state=checked]:text-elements-on-primary', + 'data-[state=checked]:group-focus-visible:text-elements-on-primary', + 'data-[state=indeterminate]:text-elements-on-primary', + 'data-[state=checked]:data-[disabled]:text-elements-on-disabled-active', + ], + icon: ['w-3', 'h-3', 'stroke-current', 'text-current'], + input: [ + 'h-5', + 'w-5', + 'group', + 'border', + 'border-border-interactive/10', + 'bg-controls-tertiary', + 'rounded-md', + 'transition-all', + 'duration-150', + 'focus-ring', + 'shadow-button', + 'group-hover:border-border-interactive/[0.14]', + 'group-hover:bg-controls-tertiary', + 'data-[state=checked]:bg-controls-primary', + 'data-[state=checked]:hover:bg-controls-primary-hovered', + 'data-[state=checked]:focus-visible:bg-controls-primary-hovered', + 'data-[disabled]:bg-controls-disabled', + 'data-[disabled]:shadow-none', + 'data-[disabled]:hover:border-border-interactive/10', + 'data-[disabled]:cursor-not-allowed', + 'data-[state=checked]:data-[disabled]:bg-controls-disabled-active', + ], + label: [ + 'text-sm', + 'tracking-[-0.006em]', + 'text-elements-high-em', + 'flex', + 'flex-col', + 'gap-1', + 'px-1', + ], + description: ['text-xs', 'text-elements-low-em'], + }, + variants: { + disabled: { + true: { + wrapper: ['cursor-not-allowed'], + indicator: ['group-hover:text-transparent'], + input: [ + 'group-hover:border-border-interactive/[0.14]', + 'group-hover:bg-controls-disabled', + ], + label: ['cursor-not-allowed'], + }, + }, + }, +}); + +export type CheckboxVariants = VariantProps; diff --git a/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx b/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx new file mode 100644 index 00000000..0ba9d825 --- /dev/null +++ b/packages/frontend/src/components/shared/Checkbox/Checkbox.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import * as CheckboxRadix from '@radix-ui/react-checkbox'; +import { type CheckboxProps as CheckboxRadixProps } from '@radix-ui/react-checkbox'; + +import { getCheckboxVariant, type CheckboxVariants } from './Checkbox.theme'; +import { CheckIcon } from 'components/shared/CustomIcon'; + +interface CheckBoxProps extends CheckboxRadixProps, CheckboxVariants { + /** + * The label of the checkbox. + */ + label?: string; + /** + * The description of the checkbox. + */ + description?: string; +} + +/** + * Checkbox component is used to allow users to select one or more items from a set. + * It is a wrapper around the `@radix-ui/react-checkbox` component. + * + * It accepts all the props from `@radix-ui/react-checkbox` component and the variants from the theme. + * + * It also accepts `label` and `description` props to display the label and description of the checkbox. + * + * @example + * ```tsx + * + * ``` + */ +export const Checkbox = ({ + id, + className, + label, + description, + onCheckedChange, + ...props +}: CheckBoxProps) => { + const { + wrapper: wrapperStyle, + indicator: indicatorStyle, + icon: iconStyle, + input: inputStyle, + label: labelStyle, + description: descriptionStyle, + } = getCheckboxVariant({ + disabled: props?.disabled, + }); + return ( +
+ + + + + + {label && ( + + )} +
+ ); +}; diff --git a/packages/frontend/src/components/shared/Checkbox/index.ts b/packages/frontend/src/components/shared/Checkbox/index.ts new file mode 100644 index 00000000..f5c939fa --- /dev/null +++ b/packages/frontend/src/components/shared/Checkbox/index.ts @@ -0,0 +1 @@ +export * from './Checkbox'; diff --git a/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx new file mode 100644 index 00000000..e1096cb1 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/CheckIcon.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const CheckIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx new file mode 100644 index 00000000..b62f79d0 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/ChevronGrabberHorizontal.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const ChevronGrabberHorizontal = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx new file mode 100644 index 00000000..f9c4885c --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/ChevronLeft.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const ChevronLeft = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx b/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx new file mode 100644 index 00000000..75da0052 --- /dev/null +++ b/packages/frontend/src/components/shared/CustomIcon/ChevronRight.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { CustomIcon, CustomIconProps } from './CustomIcon'; + +export const ChevronRight = (props: CustomIconProps) => { + return ( + + + + ); +}; diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts index d50d942c..34b7f485 100644 --- a/packages/frontend/src/components/shared/CustomIcon/index.ts +++ b/packages/frontend/src/components/shared/CustomIcon/index.ts @@ -1,2 +1,6 @@ export * from './PlusIcon'; export * from './CustomIcon'; +export * from './CheckIcon'; +export * from './ChevronGrabberHorizontal'; +export * from './ChevronLeft'; +export * from './ChevronRight'; diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index d751a7c5..74086848 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -1,9 +1,15 @@ import { Badge, BadgeProps } from 'components/shared/Badge'; import { Button, ButtonOrLinkProps } from 'components/shared/Button'; +import { Calendar } from 'components/shared/Calendar'; +import { Checkbox } from 'components/shared/Checkbox'; import { PlusIcon } from 'components/shared/CustomIcon'; -import React from 'react'; +import React, { useState } from 'react'; +import { Value } from 'react-calendar/dist/cjs/shared/types'; const Page = () => { + const [singleDate, setSingleDate] = useState(); + const [dateRange, setDateRange] = useState(); + return (
@@ -14,6 +20,7 @@ const Page = () => { packages/frontend/src/pages/components/index.tsx

+
{/* Insert Components here */} @@ -93,6 +100,61 @@ const Page = () => { )}
+ +
+ +
+

Checkbox

+
+ {Array.from({ length: 5 }).map((_, index) => ( + + ))} +
+
+ {Array.from({ length: 2 }).map((_, index) => ( + + ))} +
+
+ +
+ +
+

Calendar

+
+
+

Selected date: {singleDate?.toString()}

+ +
+
+

+ Start date:{' '} + {dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '} +
+ End date:{' '} + {dateRange instanceof Array ? dateRange[1]?.toString() : ''} +

+ +
+
+
); diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js index 6f6fc6ff..4bcb0b01 100644 --- a/packages/frontend/tailwind.config.js +++ b/packages/frontend/tailwind.config.js @@ -148,6 +148,9 @@ export default withMT({ boxShadow: { button: 'inset 0px 0px 4px rgba(255, 255, 255, 0.25), inset 0px -2px 0px rgba(0, 0, 0, 0.04)', + calendar: + '0px 3px 20px rgba(8, 47, 86, 0.1), 0px 0px 4px rgba(8, 47, 86, 0.14)', + field: '0px 1px 2px rgba(0, 0, 0, 0.04)', }, spacing: { 2.5: '0.625rem', diff --git a/yarn.lock b/yarn.lock index e4713492..42d230b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1280,7 +1280,7 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.10.4", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1": +"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== @@ -3277,6 +3277,104 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@radix-ui/primitive@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd" + integrity sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-checkbox@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b" + integrity sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive" "1.0.1" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-presence" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-controllable-state" "1.0.1" + "@radix-ui/react-use-previous" "1.0.1" + "@radix-ui/react-use-size" "1.0.1" + +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-context@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" + integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-presence@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.1.tgz#491990ba913b8e2a5db1b06b203cb24b5cdef9ba" + integrity sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-primitive@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" + integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.2" + +"@radix-ui/react-slot@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + +"@radix-ui/react-use-callback-ref@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" + integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-controllable-state@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" + integrity sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-callback-ref" "1.0.1" + +"@radix-ui/react-use-layout-effect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" + integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-previous@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" + integrity sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-size@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2" + integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-use-layout-effect" "1.0.1" + "@remix-run/router@1.13.1": version "1.13.1" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.13.1.tgz#07e2a8006f23a3bc898b3f317e0a58cc8076b86e" @@ -3910,6 +4008,18 @@ dependencies: "@types/node" "*" +"@types/lodash.memoize@^4.1.7": + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/lodash.memoize/-/lodash.memoize-4.1.9.tgz#9f8912d39b6e450c0d342a2b74c99d331bf2016b" + integrity sha512-glY1nQuoqX4Ft8Uk+KfJudOD7DQbbEDF6k9XpGncaohW3RW4eSWBlx6AA0fZCrh40tZcQNH4jS/Oc59J6Eq+aw== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.202" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8" + integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ== + "@types/long@^4.0.0", "@types/long@^4.0.1": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" @@ -4687,6 +4797,11 @@ "@webassemblyjs/ast" "1.11.6" "@xtuc/long" "4.2.2" +"@wojtekmaj/date-utils@^1.1.3": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@wojtekmaj/date-utils/-/date-utils-1.5.1.tgz#c3cd67177ac781cfa5736219d702a55a2aea5f2b" + integrity sha512-+i7+JmNiE/3c9FKxzWFi2IjRJ+KzZl1QPu6QNrsgaa2MuBgXvUy4gA1TVzf/JMdIIloB76xSKikTWuyYAIVLww== + "@wry/caches@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" @@ -6144,6 +6259,11 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== +clsx@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" + integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== + cmd-shim@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-6.0.1.tgz#a65878080548e1dca760b3aea1e21ed05194da9d" @@ -6821,10 +6941,10 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -date-fns@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.0.1.tgz#a95b3e8296e72cf57c99819f37679aa27ca65ec4" - integrity sha512-cr9igCUa0QSqgAMj7JOrYTY6Nh1rmyGrFDko7ADqfmaQqP/I2N4rlfrLl7AWuzDaoIpz6MNjoEcTPzgZYIrhnA== +date-fns@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-3.3.1.tgz#7581daca0892d139736697717a168afbb908cfed" + integrity sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw== dateformat@^3.0.3: version "3.0.3" @@ -8720,6 +8840,14 @@ get-tsconfig@^4.7.0: dependencies: resolve-pkg-maps "^1.0.0" +get-user-locale@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/get-user-locale/-/get-user-locale-2.3.1.tgz#fc7319429c8a70fac01b3b2a0b08b0c71c1d3fe2" + integrity sha512-VEvcsqKYx7zhZYC1CjecrDC5ziPSpl1gSm0qFFJhHSGDrSC+x4+p1KojWC/83QX//j476gFhkVXP/kNUc9q+bQ== + dependencies: + "@types/lodash.memoize" "^4.1.7" + lodash.memoize "^4.1.1" + git-raw-commits@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-3.0.0.tgz#5432f053a9744f67e8db03dbc48add81252cfdeb" @@ -11012,7 +11140,7 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== -lodash.memoize@^4.1.2: +lodash.memoize@^4.1.1, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -11065,7 +11193,7 @@ long@^5.2.0: resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== -loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -13281,7 +13409,7 @@ promzard@^1.0.0: dependencies: read "^2.0.0" -prop-types@15.8.1, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@15.8.1, prop-types@^15.6.0, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -13440,6 +13568,17 @@ react-app-polyfill@^3.0.0: regenerator-runtime "^0.13.9" whatwg-fetch "^3.6.2" +react-calendar@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/react-calendar/-/react-calendar-4.8.0.tgz#61edbba6d17e7ef8a8012de9143b5e5ff41104c8" + integrity sha512-qFgwo+p58sgv1QYMI1oGNaop90eJVKuHTZ3ZgBfrrpUb+9cAexxsKat0sAszgsizPMVo7vOXedV7Lqa0GQGMvA== + dependencies: + "@wojtekmaj/date-utils" "^1.1.3" + clsx "^2.0.0" + get-user-locale "^2.2.1" + prop-types "^15.6.0" + warning "^4.0.0" + react-code-blocks@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/react-code-blocks/-/react-code-blocks-0.1.6.tgz#ec64e7899223d3e910eb916465a66d95ce1ae1b2" @@ -15880,6 +16019,13 @@ walker@^1.0.7: dependencies: makeerror "1.0.12" +warning@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz"