Skip to content

Commit

Permalink
Feat/190 calendar (#207)
Browse files Browse the repository at this point in the history
* feat: working calendar

* feat: working calendar

* feat: docs + calendar

* fix: move border logic to button

* fix: update docs

* fix: dark mode

* fix: arrow left

* fix: both arrows

* fix: alignment of arrows with date

* fix: docs
  • Loading branch information
Ademsk1 authored Dec 6, 2023
1 parent 9b71994 commit 5867f19
Show file tree
Hide file tree
Showing 10 changed files with 500 additions and 0 deletions.
30 changes: 30 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@
"@radix-ui/react-tooltip": "^1.0.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
"react-day-picker": "^8.9.1",
"tailwind-merge": "^2.0.0"
}
}
18 changes: 18 additions & 0 deletions src/assets/build/arrow-left.icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const ArrowLeftIcon = (props: any) => (
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 10"
{...props}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M13 5H1m0 0 4 4M1 5l4-4"
/>
</svg>
)
export default ArrowLeftIcon
19 changes: 19 additions & 0 deletions src/assets/build/arrow-right.icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react'
const ArrowRightIcon = (props: any) => (
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 14 10"
{...props}
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M1 5h12m0 0L9 1m4 4L9 9"
/>
</svg>
)
export default ArrowRightIcon
2 changes: 2 additions & 0 deletions src/assets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export * from './build/folder.icon'
export * from './build/circle-outline.icon'
export * from './build/angle-down.icon'
export * from './build/calendar.icon'
export * from './build/arrow-left.icon'
export * from './build/arrow-right.icon'
131 changes: 131 additions & 0 deletions src/components/Calendar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
'use client'

import * as React from 'react'
import { DayPicker } from 'react-day-picker'
import { cn } from '@/lib/utils'
import ArrowLeftIcon from '@/assets/build/arrow-left.icon'
import ArrowRightIcon from '@/assets/build/arrow-right.icon'
import { cva } from 'class-variance-authority'
export type CalendarProps = React.ComponentProps<typeof DayPicker>

const calendarVariants = cva([], {
variants: {
component: {
months: ['flex', 'flex-col', 'gap-2'],
month: ['flex', 'flex-col', 'gap-2'],
caption: ['flex justify-center py-0.5 relative items-center'],
caption_label: ['text-sm', 'font-medium'],
nav: ['h-7', 'bg-transparent', 'dark:text-foreground-muted-dark'],
nav_button: ['h-7', 'w-7', 'bg-transparent', 'p-0'],
head_cell: [
'text-foreground-muted',
'dark:text-foreground-muted-dark',
'px-1',
'py-2',
'w-9',
'font-semibold',
'text-xs',
'h-[34px]'
],
cell: [
[
'overflow-none',
'w-9',
'p-0',
'flex',
'flex-col',
'items-center',
'text-center',
'gap-1',
'text-sm',
'relative'
],
[
'focus-within:relative',
'focus-within:z-20',
'focus-visible:shadow-blue'
]
],
day: cn([
'aria-selected:bg-accent',
'rounded-lg',
'dark:aria-selected:bg-blue-400',
'px-1',
'w-full',
'py-2',
'h-[34px]',
'flex',
'flex-col',
'text-sm',
'font-bold',
'aria-selected:opacity-100',
'items-center',
'shrink-0',
'focus-visible:shadow-blue',
'outline-none'
]),
day_selected: ['text-foreground-inverse'],
day_outside: [
'day-outside',
'text-muted-foreground',
'opacity-50 aria-selected:bg-accent-alt'
]
}
}
})

function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn(
'p-4 rounded-lg bg-background-alt dark:bg-background-alt-dark dark:text-foreground-inverse shadow-[0_4px_6px_0px_rgba(0,0,0,0.08)] flex [&>*]:flex-row',
className
)}
classNames={{
months: calendarVariants({ component: 'months' }),
month: calendarVariants({ component: 'months' }),
caption: calendarVariants({ component: 'caption' }),
caption_label: calendarVariants({ component: 'caption_label' }),
nav: calendarVariants({ component: 'nav' }),
nav_button: calendarVariants({ component: 'nav_button' }),
nav_button_previous: 'absolute left-1',
nav_button_next: 'absolute right-0',
table: 'w-full border-collapse space-y-0',
head_row: 'flex',
head_cell: calendarVariants({ component: 'head_cell' }),
row: 'flex w-full',
cell: calendarVariants({ component: 'cell' }),
day: calendarVariants({ component: 'day' }),
day_range_end:
'day-range-end bg-accent dark:bg-blue-[400] rounded-r-full',
day_range_start:
'day-range-start rounded-l-full bg-accent dark:bg-blue-[400] text-foreground-inverse',
day_selected: calendarVariants({ component: 'day_selected' }),
day_outside: calendarVariants({ component: 'day_outside' }),
day_disabled: 'text-muted-foreground opacity-50',
day_range_middle:
'day-range-middle aria-selected:bg-accent-alt dark:aria-selected:bg-accent-alt rounded-none bg-grey-400 text-grey-900',
day_hidden: 'invisible',
...classNames
}}
components={{
IconLeft: () => (
<ArrowLeftIcon className="h-5 w-5 fill-current stroke-current" />
),
IconRight: () => (
<ArrowRightIcon className="h-5 w-5 fill-current stroke-current" />
)
}}
{...props}
/>
)
}
Calendar.displayName = 'Calendar'

export { Calendar }
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './Switch'
export * from './Chip'
export * from './WebsiteFooter'
export * from './Select'
export * from './Calendar'
37 changes: 37 additions & 0 deletions stories/Calendar/Calendar.example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
'use client'
import { addDays } from 'date-fns'
import { useState } from 'react'
import { DateRange } from 'react-day-picker'

import * as React from 'react'
import { Calendar, CalendarProps } from '@/index'

export function CalendarDemo({
mode = 'single',
numberOfMonths = 1
}: CalendarProps) {
if (mode === 'single' || mode === 'default') {
const [date, setDate] = React.useState<Date | undefined>(new Date())

return <Calendar mode={mode} selected={date} onSelect={setDate} />
} else if (mode === 'multiple') {
const [days, setDays] = React.useState<Date[] | undefined>()
return <Calendar mode={mode} selected={days} onSelect={setDays} />
} else if (mode === 'range') {
const today = new Date()
const defaultSelected: DateRange = {
from: today,
to: addDays(today, 7)
}
const [range, setRange] = useState<DateRange | undefined>(defaultSelected)
return (
<Calendar
mode={mode}
selected={range}
onSelect={setRange}
numberOfMonths={numberOfMonths}
showOutsideDays={false}
/>
)
}
}
40 changes: 40 additions & 0 deletions stories/Calendar/Calendar.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Meta, StoryObj } from '@storybook/react'

import { CalendarDemo } from './Calendar.example'

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
title: 'Components/Calendar',
component: CalendarDemo,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered'
}
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs

// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
} satisfies Meta<typeof CalendarDemo>

export default meta
type Story = StoryObj<typeof meta>

// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Default: Story = {}

export const MultipleWithMax: Story = {
args: {
mode: 'multiple'
}
}

export const Range: Story = {
args: {
mode: 'range'
}
}
export const TwoMonths: Story = {
args: {
mode: 'range',
numberOfMonths: 2
}
}
Loading

0 comments on commit 5867f19

Please sign in to comment.