SABRAMAN

SABRAMAN DANYA YUDIN КАРТОН SABRAMAN DANYA YUDIN КАРТОН SABRAMAN DANYA YUDIN КАРТОН SABRAMAN DANYA YUDIN КАРТОН SABRAMAN DANYA YUDIN КАРТОН SABRAMAN DANYA YUDIN КАРТОН

Legacy Wheel Picker

Use this for time pickers, counters, and finite option selectors with legacy chrome.

On this page
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 59
  • 00
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 00
  • AM
  • PM
"use client";

import { useState } from "react";

import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";

const hours = Array.from({ length: 12 }, (_, index) => ({
	label: String(index + 1),
	value: index + 1,
}));

const minutes = Array.from({ length: 60 }, (_, index) => ({
	label: String(index).padStart(2, "0"),
	value: index,
}));

const meridiem = [
	{ label: "AM", value: "AM" },
	{ label: "PM", value: "PM" },
];

export function LegacyTimePickerExample() {
	const [hour, setHour] = useState(10);
	const [minute, setMinute] = useState(41);
	const [ampm, setAmpm] = useState("AM");

	return (
		<LegacyPickerContainer
			className="relative w-full shrink-0 overflow-hidden"
			frameWidth="100%"
			width="100%"
		>
			<div className="relative z-[3] flex flex-[1.2] shrink-0 border-[#000]/20 border-r shadow-[1px_0_0_0_rgba(255,255,255,0.3)]">
				<LegacyPickerColumn
					align="center"
					fontSize={25}
					onValueChange={setHour}
					options={hours}
					padX={15}
					value={hour}
				/>
			</div>
			<div className="relative z-[2] flex flex-[1] shrink-0 border-[#000]/20 border-r shadow-[1px_0_0_0_rgba(255,255,255,0.3)]">
				<LegacyPickerColumn
					align="center"
					fontSize={25}
					onValueChange={setMinute}
					options={minutes}
					padX={10}
					value={minute}
				/>
			</div>
			<div className="relative z-[1] flex flex-[1.2] shrink-0">
				<LegacyPickerColumn
					align="center"
					fontSize={20}
					infinite={false}
					onValueChange={setAmpm}
					options={meridiem}
					padX={15}
					value={ampm}
				/>
			</div>
		</LegacyPickerContainer>
	);
}

Why Use It

  • Best for time pickers, numeric selectors, unit choosers, and any slot-machine style control.
  • Exposes the container shell and the wheel column separately, so you can compose one column or several.
  • Preserves the glossy selected row and shell framing while leaving option data and column layout in your control.
  • Supports both infinite looping and finite lists with disabled options.

Installation

Install the registry block, then compose the shell and one or more columns in your own picker layout.

$ bunx --bun shadcn@latest add https://sabraman.ru/r/legacy-wheel-picker.json

Quick Start

Start with one centered column. It is the simplest pattern and the right base for most finite selectors.

"use client";

import { useState } from "react";

import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";

const frameworkOptions = [
	{ label: "React", value: "react" },
	{ label: "Vue", value: "vue" },
	{ label: "Angular", value: "angular", disabled: true },
	{ label: "Svelte", value: "svelte" },
];

export function LegacyBasicPickerDemo() {
	const [value, setValue] = useState("react");

	return (
		<div className="flex w-full max-w-[320px] flex-col items-center gap-6">
			<LegacyPickerContainer className="w-full">
				<LegacyPickerColumn
					align="center"
					fontSize={22}
					onValueChange={setValue}
					options={frameworkOptions}
					value={value}
				/>
			</LegacyPickerContainer>
		</div>
	);
}

Examples

Single Column

A single-column framework picker for the default copy-first setup.

  • Svelte
  • React
  • Vue
  • Angular
  • Svelte
  • React
  • Vue
  • Angular
  • Svelte
  • React
"use client";

import { useState } from "react";

import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";

const frameworkOptions = [
	{ label: "React", value: "react" },
	{ label: "Vue", value: "vue" },
	{ label: "Angular", value: "angular", disabled: true },
	{ label: "Svelte", value: "svelte" },
];

export function LegacyBasicPickerDemo() {
	const [value, setValue] = useState("react");

	return (
		<div className="flex w-full max-w-[320px] flex-col items-center gap-6">
			<LegacyPickerContainer className="w-full">
				<LegacyPickerColumn
					align="center"
					fontSize={22}
					onValueChange={setValue}
					options={frameworkOptions}
					value={value}
				/>
			</LegacyPickerContainer>
		</div>
	);
}

Three-Column Time Picker

A three-column time picker with mixed widths and a finite meridiem column.

  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 59
  • 00
  • 01
  • 02
  • 03
  • 04
  • 05
  • 06
  • 07
  • 08
  • 09
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 00
  • AM
  • PM
"use client";

import { useState } from "react";

import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";

const hours = Array.from({ length: 12 }, (_, index) => ({
	label: String(index + 1),
	value: index + 1,
}));

const minutes = Array.from({ length: 60 }, (_, index) => ({
	label: String(index).padStart(2, "0"),
	value: index,
}));

const meridiem = [
	{ label: "AM", value: "AM" },
	{ label: "PM", value: "PM" },
];

export function LegacyTimePickerExample() {
	const [hour, setHour] = useState(10);
	const [minute, setMinute] = useState(41);
	const [ampm, setAmpm] = useState("AM");

	return (
		<LegacyPickerContainer
			className="relative w-full shrink-0 overflow-hidden"
			frameWidth="100%"
			width="100%"
		>
			<div className="relative z-[3] flex flex-[1.2] shrink-0 border-[#000]/20 border-r shadow-[1px_0_0_0_rgba(255,255,255,0.3)]">
				<LegacyPickerColumn
					align="center"
					fontSize={25}
					onValueChange={setHour}
					options={hours}
					padX={15}
					value={hour}
				/>
			</div>
			<div className="relative z-[2] flex flex-[1] shrink-0 border-[#000]/20 border-r shadow-[1px_0_0_0_rgba(255,255,255,0.3)]">
				<LegacyPickerColumn
					align="center"
					fontSize={25}
					onValueChange={setMinute}
					options={minutes}
					padX={10}
					value={minute}
				/>
			</div>
			<div className="relative z-[1] flex flex-[1.2] shrink-0">
				<LegacyPickerColumn
					align="center"
					fontSize={20}
					infinite={false}
					onValueChange={setAmpm}
					options={meridiem}
					padX={15}
					value={ampm}
				/>
			</div>
		</LegacyPickerContainer>
	);
}

Finite Options

A finite list with a disabled option for cases where the picker should stop at the ends.

  • Slow
  • Normal
  • Fast
  • Ludicrous

Selected pace: normal

"use client";

import { useState } from "react";

import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";

const paceOptions = [
	{ label: "Slow", value: "slow" },
	{ label: "Normal", value: "normal" },
	{ label: "Fast", value: "fast" },
	{ label: "Ludicrous", value: "ludicrous", disabled: true },
];

export function LegacyWheelPickerFiniteExample() {
	const [pace, setPace] =
		useState<(typeof paceOptions)[number]["value"]>("normal");

	return (
		<div className="flex w-full max-w-[320px] flex-col items-center gap-4">
			<LegacyPickerContainer className="w-full">
				<LegacyPickerColumn
					align="center"
					fontSize={22}
					infinite={false}
					onValueChange={setPace}
					options={paceOptions}
					value={pace}
				/>
			</LegacyPickerContainer>
			<p className="font-medium text-[#8b9bb4] text-sm">
				Selected pace: <span className="text-white capitalize">{pace}</span>
			</p>
		</div>
	);
}

React Hook Form

A wheel picker wired into React Hook Form so the selected value submits with the rest of the form.

  • Astro
  • Vite
  • Laravel
  • Next.js
  • Astro
  • Vite
  • Laravel
  • Next.js
  • Astro
  • Vite
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import type { SubmitHandler } from "react-hook-form";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { LegacyBarButton } from "@/components/legacy-bar-button";
import { showLegacyNotification } from "@/components/legacy-notification";
import {
	LegacyPickerColumn,
	LegacyPickerContainer,
} from "@/components/legacy-wheel-picker";
import {
	Form,
	FormControl,
	FormField,
	FormItem,
	FormLabel,
	FormMessage,
} from "@/components/ui/form";

const formSchema = z.object({
	framework: z.string(),
});

type FormSchema = z.infer<typeof formSchema>;

const formOptions = [
	{ label: "Vite", value: "vite" },
	{ label: "Laravel", value: "laravel", disabled: true },
	{ label: "Next.js", value: "nextjs" },
	{ label: "Astro", value: "astro" },
];

export function LegacyHookFormDemo() {
	const form = useForm<FormSchema>({
		resolver: zodResolver(formSchema),
		defaultValues: { framework: "nextjs" },
	});

	const onSubmit: SubmitHandler<FormSchema> = (values) => {
		const selectedFramework = formOptions.find(
			(option) => option.value === values.framework,
		)?.label;

		showLegacyNotification({
			body: "Selection synced with the form state.",
			showIcon: false,
			subtitle: selectedFramework ?? "Framework",
			time: "now",
			title: "Settings Saved",
		});
	};

	return (
		<Form {...form}>
			<form
				onSubmit={form.handleSubmit(onSubmit)}
				className="flex w-full max-w-[320px] flex-col gap-6"
			>
				<FormField
					control={form.control}
					name="framework"
					render={({ field }) => (
						<FormItem className="flex w-full flex-col items-center">
							<FormLabel className="sr-only">Framework</FormLabel>
							<FormControl>
								<LegacyPickerContainer className="w-full">
									<LegacyPickerColumn
										align="center"
										fontSize={22}
										onValueChange={field.onChange}
										options={formOptions}
										value={field.value}
									/>
								</LegacyPickerContainer>
							</FormControl>
							<FormMessage />
						</FormItem>
					)}
				/>
				<LegacyBarButton
					className="w-full justify-center"
					label="Submit"
					type="submit"
					variant="accent"
				/>
			</form>
		</Form>
	);
}

API Reference

LegacyPickerContainerProps controls the shell width and frame. LegacyPickerColumnProps controls the option list, alignment, and whether the wheel loops infinitely.

LegacyPickerContainerProps

PropTypeRequired
childrenReact.ReactNodeYes
classNamestringNo
frameWidthnumber | stringNo
widthnumber | stringNo

LegacyPickerColumnProps

PropTypeRequired
align"left" | "center" | "right"No
classNamestringNo
fontSizenumberNo
infinitebooleanNo
onValueChange(value: T) => voidYes
optionsWheelPickerOption<T>[]Yes
padXnumberNo
valueTYes
widthstring | numberNo
zIndexnumberNo

Notes

  • Use infinite={false} when the list should stop at the ends or include disabled options that should remain visible.
  • padX, fontSize, and align are the main controls for matching the wheel to your content density.
  • Multi-column layouts are just flex children inside LegacyPickerContainer, so separators and custom widths live in your own markup.