{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "calendar-with-time", "title": "Calendar with Time", "description": "時間付きカレンダー", "dependencies": [ "dayjs", "react-day-picker", "lucide-react" ], "registryDependencies": [ "calendar", "card", "select", "button" ], "files": [ { "path": "registry/br-hr/blocks/calendar-with-time.tsx", "content": "\"use client\";\n\nimport dayjs from \"dayjs\";\nimport { Timer } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport type { Matcher, OnSelectHandler } from \"react-day-picker\";\nimport { Calendar } from \"@/registry/br-hr/ui/calendar\";\nimport { Card, CardContent } from \"@/registry/br-hr/ui/card\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"@/registry/br-hr/ui/select\";\nimport { Button } from \"../ui/button\";\n\ntype CalendarWithTimeProps = {\n\tdate: Date | undefined;\n\tonSelect: (date: Date) => void;\n\tdisabledDates?: Matcher[];\n\tminHour?: number;\n\tmaxHour?: number;\n\tminMinute?: number;\n\tmaxMinute?: number;\n\tclearButton?: boolean;\n\tonClear?: () => void;\n};\n\nconst HOUR_OPTIONS = Array.from({ length: 24 }, (_, i) => ({\n\tvalue: String(i),\n\tlabel: String(i).padStart(2, \"0\"),\n}));\nconst MINUTE_OPTIONS = Array.from({ length: 60 }, (_, i) => ({\n\tvalue: String(i),\n\tlabel: String(i).padStart(2, \"0\"),\n}));\n\nconst CalendarWithTime = ({\n\tdate,\n\tdisabledDates,\n\tonSelect,\n\tminHour,\n\tmaxHour,\n\tminMinute,\n\tmaxMinute,\n\tclearButton = false,\n\tonClear,\n}: CalendarWithTimeProps) => {\n\tconst [selectedHour, setSelectedHour] = useState(\n\t\tundefined,\n\t);\n\tconst [selectedMinute, setSelectedMinute] = useState(\n\t\tundefined,\n\t);\n\n\tconst onDayClick: OnSelectHandler = (date: Date) => {\n\t\tonSelect(date);\n\t};\n\tconst onHourChange = (value: string) => {\n\t\t// 日と分はそのままで時だけを変更\n\t\tconst current = dayjs(date)\n\t\t\t.hour(Number(value))\n\t\t\t.minute(selectedMinute ? Number(selectedMinute) : (minMinute ?? 0));\n\t\tconst next = current.toDate();\n\t\tonSelect(next);\n\t\tsetSelectedHour(value);\n\t};\n\tconst onMinuteChange = (value: string) => {\n\t\tconst current = dayjs(date)\n\t\t\t.hour(selectedHour ? Number(selectedHour) : (minHour ?? 0))\n\t\t\t.minute(Number(value));\n\t\tconst next = current.toDate();\n\t\tonSelect(next);\n\t\tsetSelectedMinute(value);\n\t};\n\tconst handleClear = () => {\n\t\tsetSelectedHour(undefined);\n\t\tsetSelectedMinute(undefined);\n\t\tif (onClear) {\n\t\t\tonClear();\n\t\t}\n\t};\n\tuseEffect(() => {\n\t\tif (minHour !== undefined && dayjs(date).hour() < minHour) {\n\t\t\treturn;\n\t\t}\n\t\t// 時間が最大値を超えていたら選択しない\n\t\tif (maxHour !== undefined && dayjs(date).hour() > maxHour) {\n\t\t\treturn;\n\t\t}\n\t\t// 分が最小値を超えていたら選択しない\n\t\tif (minMinute !== undefined && dayjs(date).minute() < minMinute) {\n\t\t\treturn;\n\t\t}\n\t\tif (maxMinute !== undefined && dayjs(date).minute() > maxMinute) {\n\t\t\treturn;\n\t\t}\n\t\tif (date) {\n\t\t\tsetSelectedHour(String(dayjs(date).hour()));\n\t\t\tsetSelectedMinute(String(dayjs(date).minute()));\n\t\t}\n\t}, [date, minHour, maxHour, minMinute, maxMinute]);\n\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t\t\t\n\t\t\t\t\t\t
\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t:\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{clearButton && (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t
\n\t\t
\n\t);\n};\n\nexport default CalendarWithTime;\n", "type": "registry:component" } ], "type": "registry:component" }