feat: button, calendar, datePicker, input, pagination, icons 추가
This commit is contained in:
79
web-app/app/shared/components/datePicker/DatePicker.tsx
Normal file
79
web-app/app/shared/components/datePicker/DatePicker.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { DatePicker as AriaDatePicker, Button, Dialog, Group, OverlayArrow, Popover } from 'react-aria-components';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
import { Calendar } from '../calendar';
|
||||
import { calendarDateToDate, dateToCalendarDate } from '../calendar/utils';
|
||||
import { CalendarIcon } from '../icons';
|
||||
|
||||
const trigger = tv({
|
||||
base: 'flex h-9 w-[156px] cursor-pointer items-center border border-kc-gray-be bg-white text-left outline-none transition',
|
||||
variants: {
|
||||
isHovered: {
|
||||
true: 'border-kc-black-34',
|
||||
},
|
||||
isFocused: {
|
||||
true: 'border-kc-black-34',
|
||||
},
|
||||
isDisabled: {
|
||||
true: 'cursor-default border-kc-gray-99 bg-kc-gray-eb',
|
||||
},
|
||||
isFocusVisible: {
|
||||
true: 'ring-2 ring-offset-2 ring-primary',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type DatePickerProps = {
|
||||
value: Date | null;
|
||||
onChange: (date: Date | null) => void;
|
||||
maxValue?: Date | null;
|
||||
minValue?: Date | null;
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export function DatePicker({ value, onChange, maxValue, minValue, isDisabled, className, name }: DatePickerProps) {
|
||||
return (
|
||||
<AriaDatePicker
|
||||
aria-label="날짜 선택"
|
||||
value={dateToCalendarDate(value)}
|
||||
onChange={(val) => onChange(calendarDateToDate(val))}
|
||||
maxValue={maxValue !== undefined ? dateToCalendarDate(maxValue) : undefined}
|
||||
minValue={minValue !== undefined ? dateToCalendarDate(minValue) : undefined}
|
||||
isDisabled={isDisabled}
|
||||
granularity="day"
|
||||
firstDayOfWeek="sun"
|
||||
className={className}
|
||||
name={name}
|
||||
>
|
||||
<Group>
|
||||
<Button className={(renderProps) => trigger(renderProps)}>
|
||||
<span className={`flex-1 px-3 text-sm ${value ? 'text-kc-black-34' : 'text-kc-gray-99'}`}>
|
||||
{value ? dayjs(value).format('YYYY.MM.DD') : 'YYYY.MM.DD'}
|
||||
</span>
|
||||
<span className="flex h-full w-8 shrink-0 items-center justify-center text-kc-gray-99">
|
||||
<CalendarIcon className="h-4 w-4" />
|
||||
</span>
|
||||
</Button>
|
||||
</Group>
|
||||
<Popover className="bg-white border border-kc-gray-be p-3 px-6 pb-6 drop-shadow-modal outline-none" offset={12}>
|
||||
<OverlayArrow className="group/arrow">
|
||||
<svg
|
||||
width="12"
|
||||
height="8"
|
||||
viewBox="0 0 12 8"
|
||||
className="block fill-white stroke-kc-gray-be group-data-[placement=bottom]/arrow:rotate-180"
|
||||
>
|
||||
<path d="M0 0 L6 8 L12 0" />
|
||||
</svg>
|
||||
</OverlayArrow>
|
||||
<Dialog className="outline-none">
|
||||
<Calendar />
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</AriaDatePicker>
|
||||
);
|
||||
}
|
||||
113
web-app/app/shared/components/datePicker/DateRangePicker.tsx
Normal file
113
web-app/app/shared/components/datePicker/DateRangePicker.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import {
|
||||
DateRangePicker as AriaDateRangePicker,
|
||||
Button,
|
||||
Dialog,
|
||||
Group,
|
||||
OverlayArrow,
|
||||
Popover,
|
||||
} from 'react-aria-components';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
import { CalendarIcon } from '@/components/icons';
|
||||
|
||||
import { RangeCalendar } from '../calendar/RangeCalendar';
|
||||
import { calendarDateToDate, dateToCalendarDate } from '../calendar/utils';
|
||||
|
||||
const trigger = tv({
|
||||
base: 'flex h-9 w-[260px] cursor-pointer items-center border border-kc-gray-be bg-white text-left outline-none transition',
|
||||
variants: {
|
||||
isHovered: {
|
||||
true: 'border-kc-black-34',
|
||||
},
|
||||
isFocused: {
|
||||
true: 'border-kc-black-34',
|
||||
},
|
||||
isDisabled: {
|
||||
true: 'cursor-default border-kc-gray-99 bg-kc-gray-eb',
|
||||
},
|
||||
isFocusVisible: {
|
||||
true: 'ring-2 ring-offset-2 ring-primary',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type DateRangePickerProps = {
|
||||
value: { start: Date; end: Date } | null;
|
||||
onChange: (value: { start: Date; end: Date } | null) => void;
|
||||
maxValue?: Date | null;
|
||||
minValue?: Date | null;
|
||||
isDisabled?: boolean;
|
||||
className?: string;
|
||||
startName?: string;
|
||||
endName?: string;
|
||||
};
|
||||
|
||||
export function DateRangePicker({
|
||||
value,
|
||||
onChange,
|
||||
maxValue,
|
||||
minValue,
|
||||
isDisabled,
|
||||
className,
|
||||
startName,
|
||||
endName,
|
||||
}: DateRangePickerProps) {
|
||||
const ariaValue = value ? { start: dateToCalendarDate(value.start)!, end: dateToCalendarDate(value.end)! } : null;
|
||||
|
||||
return (
|
||||
<AriaDateRangePicker
|
||||
aria-label="날짜 범위 선택"
|
||||
value={ariaValue}
|
||||
onChange={(range) => {
|
||||
if (!range) {
|
||||
onChange(null);
|
||||
} else {
|
||||
onChange({
|
||||
start: calendarDateToDate(range.start)!,
|
||||
end: calendarDateToDate(range.end)!,
|
||||
});
|
||||
}
|
||||
}}
|
||||
maxValue={maxValue !== undefined ? dateToCalendarDate(maxValue) : undefined}
|
||||
minValue={minValue !== undefined ? dateToCalendarDate(minValue) : undefined}
|
||||
isDisabled={isDisabled}
|
||||
granularity="day"
|
||||
firstDayOfWeek="sun"
|
||||
className={className}
|
||||
startName={startName}
|
||||
endName={endName}
|
||||
>
|
||||
<Group>
|
||||
<Button className={(renderProps) => trigger(renderProps)}>
|
||||
<span className={`tabular-nums px-3 text-sm ${value?.start ? 'text-kc-black-34' : 'text-kc-gray-99'}`}>
|
||||
{value?.start ? dayjs(value.start).format('YYYY. MM. DD') : 'YYYY.MM.DD'}
|
||||
</span>
|
||||
<span className="text-sm text-kc-gray-99">~</span>
|
||||
<span className={`tabular-nums flex-1 px-3 text-sm ${value?.end ? 'text-kc-black-34' : 'text-kc-gray-99'}`}>
|
||||
{value?.end ? dayjs(value.end).format('YYYY. MM. DD') : 'YYYY.MM.DD'}
|
||||
</span>
|
||||
<span className="flex h-full w-8 shrink-0 items-center justify-center text-kc-gray-99">
|
||||
<CalendarIcon className="h-4 w-4" />
|
||||
</span>
|
||||
</Button>
|
||||
</Group>
|
||||
<Popover className="bg-white border border-kc-gray-be p-3 px-6 pb-6 drop-shadow-modal outline-none" offset={12}>
|
||||
<OverlayArrow className="group/arrow">
|
||||
<svg
|
||||
width="12"
|
||||
height="8"
|
||||
viewBox="0 0 12 8"
|
||||
className="block fill-white stroke-kc-gray-be group-data-[placement=bottom]/arrow:rotate-180"
|
||||
>
|
||||
<path d="M0 0 L6 8 L12 0" />
|
||||
</svg>
|
||||
</OverlayArrow>
|
||||
<Dialog className="outline-none">
|
||||
<RangeCalendar />
|
||||
</Dialog>
|
||||
</Popover>
|
||||
</AriaDateRangePicker>
|
||||
);
|
||||
}
|
||||
4
web-app/app/shared/components/datePicker/index.ts
Normal file
4
web-app/app/shared/components/datePicker/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { DatePicker } from './DatePicker';
|
||||
export type { DatePickerProps } from './DatePicker';
|
||||
export { DateRangePicker } from './DateRangePicker';
|
||||
export type { DateRangePickerProps } from './DateRangePicker';
|
||||
Reference in New Issue
Block a user