Availability
abui/availabilityFreeBlock
A weekly availability designer with draggable and resizable time slots.
Live preview
live · rendered by the registry
Rendered by abui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://abui.io/r/availability.jsonSource
1"use client"23import * as React from "react"4import { Clock, Settings, X } from "lucide-react"5import { cn } from "@/lib/utils"6import { Button } from "@/components/ui/button"7import {8 DndContext,9 DragOverlay,10 useSensor,11 useSensors,12 PointerSensor,13 useDraggable,14 useDroppable,15 DragStartEvent,16 DragEndEvent,17 DragMoveEvent,18 DragOverEvent,19} from "@dnd-kit/core"20import tunnel from "tunnel-rat"21import { nanoid } from "nanoid"2223// --- Types ---2425export interface TimeSpan {26 active?: boolean27 id: string28 week_day: number // 0-6 - Sunday-Saturday29 start_time: string // "HH:mm"30 end_time: string // "HH:mm"31}3233interface AvailabilityProps {34 value?: TimeSpan[]35 onValueChange?: (value: TimeSpan[]) => void36 disabled?: TimeSpan[] // Disabled regions where no events can exist37 days?: number[] // 0-638 showAllDays?: boolean // If true, renders all 7 days, but disables interactions on days not in 'days' array39 timeIncrements?: number // minutes, default 3040 startTime?: number // hour 0-23, default 741 endTime?: number // hour 0-23, default 2342 useAmPm?: boolean43 mergeAdjacent?: boolean // default true - merge spans that touch end-to-end44 slotClassName?: string // className for time slot items (default: "bg-muted")45 className?: string46}4748// --- Utils ---4950const timeToMinutes = (time: string) => {51 const [h, m] = time.split(":").map(Number)52 return h * 60 + m53}5455const minutesToTime = (minutes: number) => {56 const h = Math.floor(minutes / 60)57 const m = minutes % 6058 return `${h.toString().padStart(2, "0")}:${m.toString().padStart(2, "0")}`59}6061const formatDisplayTime = (time: string, useAmPm: boolean) => {62 if (!useAmPm) return time63 const [h, m] = time.split(":").map(Number)64 const ampm = h >= 12 ? "PM" : "AM"65 const h12 = h % 12 || 1266 return `${h12}:${m.toString().padStart(2, "0")} ${ampm}`67}6869// Helper to generate a simple unique ID (not crypto secure but sufficient for UI)70const generateId = () => nanoid()7172const DAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]7374/**75 * Merges adjacent (contiguous) time spans on the same day.76 * Adjacent means one span's end_time equals another's start_time.77 * The merged span keeps the id of the earliest span.78 */79const mergeAdjacentSpans = (spans: TimeSpan[]): TimeSpan[] => {80 if (spans.length === 0) return spans8182 // Group by day83 const byDay = new Map<number, TimeSpan[]>()84 spans.forEach(span => {85 const daySpans = byDay.get(span.week_day) || []86// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from abui
All 20 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Chartanimated-chart | abui | Blocks | Free | 1 dep | |
| Calendar Yearcalendar-year | abui | Blocks | Free | 1 dep | |
| Scroll Reveal Content Ascroll-reveal-content-a | abui | Blocks | Free | 1 dep | |
| Timelinetimeline | abui | Blocks | Free | 3 deps |
