Availability Scheduler
beui/availability-schedulerFreeBlock
Weekly availability editor where each day springs between available and unavailable, time ranges add and remove with blur-slide motion, times pick from a scrollable dropdown, and a copy menu clones hours to other days.
Install it
npx shadcn@latest add https://beui.dev/r/availability-scheduler.jsonSource
1"use client";2// beui.dev/components/blocks/availability-scheduler34import { LayoutGroup, useReducedMotion } from "motion/react";5import { useCallback, useId, useMemo, useRef, useState } from "react";6import { cn } from "@/lib/utils";7import { DayRow } from "./day-row";8import {9 type DayAvailability,10 type DayKey,11 WEEKDAYS,12 type WeekAvailability,13 buildOptions,14 defaultWeek,15} from "./types";1617export type {18 DayAvailability,19 DayKey,20 TimeRange,21 WeekAvailability,22} from "./types";23export { defaultWeek } from "./types";2425export interface AvailabilitySchedulerProps {26 value?: WeekAvailability;27 defaultValue?: WeekAvailability;28 onChange?: (value: WeekAvailability) => void;29 /** Minutes between selectable times. Default 30. */30 step?: number;31 className?: string;32}3334export function AvailabilityScheduler({35 value,36 defaultValue,37 onChange,38 step = 30,39 className,40}: AvailabilitySchedulerProps) {41 const reduce = useReducedMotion() ?? false;42 const groupId = useId();43 const options = useMemo(() => buildOptions(step), [step]);44 const idRef = useRef(0);4546 const [internal, setInternal] = useState<WeekAvailability>(47 () => defaultValue ?? defaultWeek(),48 );49 const controlled = value !== undefined;50 const week = controlled ? value : internal;5152 const commit = useCallback(53 (next: WeekAvailability) => {54 if (!controlled) setInternal(next);55 onChange?.(next);56 },57 [controlled, onChange],58 );5960 const setDay = useCallback(61 (day: DayKey, next: DayAvailability) => {62 commit({ ...week, [day]: next });63 },64 [commit, week],65 );6667 const copyDay = useCallback(68 (from: DayKey, targets: DayKey[]) => {69 const source = week[from];70 const next = { ...week };71 for (const t of targets) {72 next[t] = {73 enabled: source.enabled,74 ranges: source.ranges.map((r) => ({75 ...r,76 id: `${t}-c${idRef.current++}`,77 })),78 };79 }80 commit(next);81 },82 [commit, week],83 );8485 return (86 <LayoutGroup id={groupId}>87 <div className={cn("w-full max-w-xl divide-y divide-border", className)}>88 {WEEKDAYS.map(({ key, label }, i) => (89 <DayRow90 key={key}91 day={key}92 label={label}93 state={week[key]}94 options={options}95 reduce={reduce}96 depth={WEEKDAYS.length - i}97 onChange={(next) => setDay(key, next)}98// … truncated
What it pulls in
npm packages
Files it writes
More from beui
All 104 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| File Upload Attachment Uploadattachment-upload | beui | Blocks | Free | 4 deps · 5 files | |
| Bloom Menubloom-menu | beui | Blocks | Free | 4 deps · 3 files | |
| Command Palettecommand-palette | beui | Blocks | Free | 4 deps · 3 files | |
| Dynamic Islanddynamic-island | beui | Blocks | Free | 3 deps · 3 files | |
| Expandable Action Barexpandable-action-bar | beui | Blocks | Free | 3 deps · 2 files | |
| Expandable Tabsexpandable-tabs | beui | Blocks | Free | 3 deps · 3 files | |
| Feedback Widgetfeedback-widget | beui | Blocks | Free | 4 deps · 9 files | |
| File Upload Upload Queuefile-upload | beui | Blocks | Free | 4 deps · 3 files |
