replace vanilla calendar with shadcn/ui one
This commit is contained in:
parent
1c4560fd2f
commit
e93cc51a68
@ -1,12 +1,13 @@
|
||||
import { PageHeader } from '@/components/navigation';
|
||||
import { SlotsCalendar, TimeSlots } from '@/components/schedule';
|
||||
import { ScheduleSlotsProvider } from '@/context/schedule-slots';
|
||||
|
||||
export default function SchedulePage() {
|
||||
return (
|
||||
<>
|
||||
<ScheduleSlotsProvider>
|
||||
<PageHeader title="График работы" />
|
||||
<SlotsCalendar />
|
||||
<TimeSlots />
|
||||
</>
|
||||
</ScheduleSlotsProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
'use client';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import 'vanilla-calendar-pro/styles/index.css';
|
||||
import { Calendar, type Options } from 'vanilla-calendar-pro';
|
||||
|
||||
type CalendarProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
readonly config?: Options;
|
||||
};
|
||||
|
||||
function VanillaCalendar({ config, ...attributes }: CalendarProps) {
|
||||
const ref = useRef(null);
|
||||
const [calendar, setCalendar] = useState<Calendar | null>(null);
|
||||
const locale = useLocale();
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
setCalendar(new Calendar(ref.current, { ...config, locale }));
|
||||
}, [config, locale, ref]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!calendar) return;
|
||||
calendar.init();
|
||||
}, [calendar]);
|
||||
|
||||
return <div {...attributes} ref={ref} />;
|
||||
}
|
||||
|
||||
export default VanillaCalendar;
|
||||
@ -1,13 +1,24 @@
|
||||
import VanillaCalendar from '@/components/common/vanilla-calendar';
|
||||
'use client';
|
||||
import { ScheduleSlotsContext } from '@/context/schedule-slots';
|
||||
import { Calendar } from '@repo/ui/components/ui/calendar';
|
||||
import dayjs from 'dayjs';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export function SlotsCalendar() {
|
||||
const { selectedDate, setSelectedDate } = useContext(ScheduleSlotsContext);
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<VanillaCalendar
|
||||
config={{
|
||||
dateMin: new Date(),
|
||||
selectionYearsMode: false,
|
||||
<div>
|
||||
<Calendar
|
||||
className="bg-background"
|
||||
disabled={(date) => {
|
||||
return dayjs().isAfter(dayjs(date), 'day');
|
||||
}}
|
||||
mode="single"
|
||||
onSelect={(date) => {
|
||||
if (date) setSelectedDate(date);
|
||||
}}
|
||||
selected={selectedDate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
17
apps/web/context/schedule-slots.tsx
Normal file
17
apps/web/context/schedule-slots.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
'use client';
|
||||
import { createContext, useMemo, useState } from 'react';
|
||||
|
||||
type ContextType = {
|
||||
selectedDate: Date;
|
||||
setSelectedDate: (date: Date) => void;
|
||||
};
|
||||
|
||||
export const ScheduleSlotsContext = createContext<ContextType>({} as ContextType);
|
||||
|
||||
export function ScheduleSlotsProvider({ children }: { readonly children: React.ReactNode }) {
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
|
||||
const value = useMemo(() => ({ selectedDate, setSelectedDate }), [selectedDate, setSelectedDate]);
|
||||
|
||||
return <ScheduleSlotsContext value={value}>{children}</ScheduleSlotsContext>;
|
||||
}
|
||||
@ -17,6 +17,7 @@
|
||||
"@repo/ui": "workspace:*",
|
||||
"@tanstack/react-query": "^5.64.1",
|
||||
"@telegram-apps/sdk-react": "^2.0.19",
|
||||
"dayjs": "^1.11.13",
|
||||
"graphql": "catalog:",
|
||||
"lucide-react": "catalog:",
|
||||
"next": "^15.1.5",
|
||||
@ -27,7 +28,6 @@
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"use-debounce": "^10.0.4",
|
||||
"vanilla-calendar-pro": "^3.0.3",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"type-check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@repo/eslint-config": "workspace:*",
|
||||
"@repo/lint-staged-config": "workspace:*",
|
||||
"@repo/typescript-config": "workspace:*",
|
||||
@ -50,7 +50,9 @@
|
||||
"@radix-ui/react-label": "^2.1.1",
|
||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||
"@radix-ui/react-select": "^2.1.4",
|
||||
"date-fns": "^4.1.0",
|
||||
"react": "catalog:",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-dom": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
75
packages/ui/src/components/ui/calendar.tsx
Normal file
75
packages/ui/src/components/ui/calendar.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
/* eslint-disable react/no-unstable-nested-components */
|
||||
'use client';
|
||||
import { buttonVariants } from '@repo/ui/components/ui/button';
|
||||
import { cn } from '@repo/ui/lib/utils';
|
||||
import { ru } from 'date-fns/locale';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
import { DayPicker } from 'react-day-picker';
|
||||
|
||||
export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
||||
readonly localeName?: 'ru';
|
||||
};
|
||||
|
||||
function Calendar({
|
||||
className,
|
||||
classNames,
|
||||
localeName = 'ru',
|
||||
showOutsideDays = true,
|
||||
...props
|
||||
}: CalendarProps) {
|
||||
return (
|
||||
<DayPicker
|
||||
className={cn('p-3', className)}
|
||||
classNames={{
|
||||
caption: 'flex justify-center pt-1 relative items-center',
|
||||
caption_label: 'text-sm font-medium',
|
||||
cell: 'h-9 w-full text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
|
||||
day: cn(
|
||||
buttonVariants({ variant: 'ghost' }),
|
||||
'h-9 w-full p-0 font-normal aria-selected:opacity-100',
|
||||
),
|
||||
day_disabled: 'text-muted-foreground opacity-50',
|
||||
day_hidden: 'invisible',
|
||||
day_outside:
|
||||
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
|
||||
day_range_end: 'day-range-end',
|
||||
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
|
||||
day_selected:
|
||||
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
|
||||
day_today: 'bg-accent text-accent-foreground',
|
||||
head_cell: 'text-muted-foreground rounded-md w-full font-normal text-[0.8rem]',
|
||||
head_row: 'flex',
|
||||
month: 'space-y-4',
|
||||
months: 'flex flex-col flex-row space-y-4 space-x-4 space-y-0',
|
||||
nav: 'space-x-1 flex items-center',
|
||||
nav_button: cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||
),
|
||||
nav_button_next: 'absolute right-1',
|
||||
nav_button_previous: 'absolute left-1',
|
||||
row: 'flex w-full mt-2',
|
||||
table: 'w-full border-collapse space-y-1',
|
||||
...classNames,
|
||||
}}
|
||||
components={{
|
||||
IconLeft: ({ className, ...props }) => (
|
||||
<ChevronLeft className={cn('h-4 w-4', className)} {...props} />
|
||||
),
|
||||
IconRight: ({ className, ...props }) => (
|
||||
<ChevronRight className={cn('h-4 w-4', className)} {...props} />
|
||||
),
|
||||
}}
|
||||
showOutsideDays={showOutsideDays}
|
||||
{...props}
|
||||
locale={localeName === 'ru' ? ru : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
Calendar.displayName = 'Calendar';
|
||||
|
||||
export { Calendar };
|
||||
40
pnpm-lock.yaml
generated
40
pnpm-lock.yaml
generated
@ -147,6 +147,9 @@ importers:
|
||||
'@telegram-apps/sdk-react':
|
||||
specifier: ^2.0.19
|
||||
version: 2.0.19(@types/react@19.0.1)(react@19.0.0)
|
||||
dayjs:
|
||||
specifier: ^1.11.13
|
||||
version: 1.11.13
|
||||
graphql:
|
||||
specifier: 'catalog:'
|
||||
version: 16.9.0
|
||||
@ -177,9 +180,6 @@ importers:
|
||||
use-debounce:
|
||||
specifier: ^10.0.4
|
||||
version: 10.0.4(react@19.0.0)
|
||||
vanilla-calendar-pro:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
zod:
|
||||
specifier: 'catalog:'
|
||||
version: 3.24.1
|
||||
@ -327,15 +327,21 @@ importers:
|
||||
'@radix-ui/react-select':
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
date-fns:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.0.0
|
||||
react-day-picker:
|
||||
specifier: 8.10.1
|
||||
version: 8.10.1(date-fns@4.1.0)(react@19.0.0)
|
||||
react-dom:
|
||||
specifier: 'catalog:'
|
||||
version: 19.0.0(react@19.0.0)
|
||||
devDependencies:
|
||||
'@radix-ui/react-slot':
|
||||
specifier: ^1.1.0
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/react@19.0.1)(react@19.0.0)
|
||||
'@repo/eslint-config':
|
||||
specifier: workspace:*
|
||||
@ -3281,6 +3287,12 @@ packages:
|
||||
dataloader@2.2.3:
|
||||
resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==}
|
||||
|
||||
date-fns@4.1.0:
|
||||
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||
|
||||
dayjs@1.11.13:
|
||||
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||
|
||||
debounce@1.2.1:
|
||||
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||
|
||||
@ -5277,6 +5289,12 @@ packages:
|
||||
ramda@0.30.1:
|
||||
resolution: {integrity: sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==}
|
||||
|
||||
react-day-picker@8.10.1:
|
||||
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
|
||||
peerDependencies:
|
||||
date-fns: ^2.28.0 || ^3.0.0
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
|
||||
react-dom@19.0.0:
|
||||
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
|
||||
peerDependencies:
|
||||
@ -6124,9 +6142,6 @@ packages:
|
||||
resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
vanilla-calendar-pro@3.0.3:
|
||||
resolution: {integrity: sha512-bfmeGFaDeakk/OrwgM+22qDiRvIx1Zu+GG3+E/sCPjaKwbJR7AsDyRBfJQzVHRw3eeavU8jQQTRSFt8EPIPOGw==}
|
||||
|
||||
vite-node@2.1.8:
|
||||
resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@ -9785,6 +9800,10 @@ snapshots:
|
||||
|
||||
dataloader@2.2.3: {}
|
||||
|
||||
date-fns@4.1.0: {}
|
||||
|
||||
dayjs@1.11.13: {}
|
||||
|
||||
debounce@1.2.1: {}
|
||||
|
||||
debug@3.2.7:
|
||||
@ -12047,6 +12066,11 @@ snapshots:
|
||||
|
||||
ramda@0.30.1: {}
|
||||
|
||||
react-day-picker@8.10.1(date-fns@4.1.0)(react@19.0.0):
|
||||
dependencies:
|
||||
date-fns: 4.1.0
|
||||
react: 19.0.0
|
||||
|
||||
react-dom@19.0.0(react@19.0.0):
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
@ -12951,8 +12975,6 @@ snapshots:
|
||||
|
||||
value-or-promise@1.0.12: {}
|
||||
|
||||
vanilla-calendar-pro@3.0.3: {}
|
||||
|
||||
vite-node@2.1.8(@types/node@20.17.8):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user