"use client"; import { DesignInput, DesignSelectorDropdown, designPopoverSurfaceClasses } from "@/components/design-components"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui"; import { cn } from "@/lib/utils"; import { CaretUpDownIcon } from "@phosphor-icons/react"; import type { DayInterval } from "@stackframe/stack-shared/dist/utils/dates"; import { useState } from "react"; const DEFAULT_INTERVAL_UNITS: DayInterval[1][] = ['day', 'week', 'month', 'year']; type IntervalSelection = 'one-time' | 'custom' | DayInterval[1]; export type RepeatingInputProps = { value: string, onValueChange: (value: string) => void, inputType?: 'text' | 'number', placeholder?: string, prefix?: string, inputClassName?: string, intervalSelection: IntervalSelection, intervalCount: number, intervalUnit?: DayInterval[1], onIntervalChange: (interval: DayInterval | null) => void, onIntervalSelectionChange: (selection: IntervalSelection) => void, onIntervalCountChange: (count: number) => void, onIntervalUnitChange: (unit: DayInterval[1] | undefined) => void, allowedUnits?: DayInterval[1][], noneLabel?: string, useDurationLabels?: boolean, readOnly?: boolean, disabled?: boolean, className?: string, }; function getIntervalLabel( intervalSelection: IntervalSelection, intervalCount: number, intervalUnit?: DayInterval[1], useDurationLabels = false ): string { if (intervalSelection === 'one-time') { return 'One-time'; } const unit = intervalUnit || 'month'; const count = intervalCount || 1; if (count === 1) { if (useDurationLabels) { return `1 ${unit}`; } const labels: Record = { day: 'Daily', week: 'Weekly', month: 'Monthly', year: 'Yearly', }; return labels[unit]; } return `Every ${count} ${unit}s`; } export function RepeatingInput({ value, onValueChange, inputType = 'text', placeholder, prefix, inputClassName, intervalSelection, intervalCount, intervalUnit, onIntervalChange, onIntervalSelectionChange, onIntervalCountChange, onIntervalUnitChange, allowedUnits, noneLabel = 'One-time', useDurationLabels = false, readOnly, disabled, className, }: RepeatingInputProps) { const [popoverOpen, setPopoverOpen] = useState(false); const units = allowedUnits ?? DEFAULT_INTERVAL_UNITS; const normalizedUnits = units.length > 0 ? units : DEFAULT_INTERVAL_UNITS; const defaultUnit = (normalizedUnits[0] ?? 'month') as DayInterval[1]; const effectiveUnit = intervalUnit && normalizedUnits.includes(intervalUnit) ? intervalUnit : defaultUnit; const isIntervalUnit = intervalSelection !== 'custom' && intervalSelection !== 'one-time'; const effectiveSelection: IntervalSelection = isIntervalUnit && !normalizedUnits.includes(intervalSelection as DayInterval[1]) ? 'custom' : intervalSelection; const buttonLabels: Record = useDurationLabels ? { day: '1 day', week: '1 week', month: '1 month', year: '1 year', } : { day: 'Daily', week: 'Weekly', month: 'Monthly', year: 'Yearly', }; const selectOneTime = () => { onIntervalSelectionChange('one-time'); onIntervalUnitChange(undefined); onIntervalCountChange(1); if (!readOnly) onIntervalChange(null); setPopoverOpen(false); }; const selectFixed = (unitOption: DayInterval[1]) => { if (!normalizedUnits.includes(unitOption)) return; onIntervalSelectionChange(unitOption); onIntervalUnitChange(unitOption); onIntervalCountChange(1); if (!readOnly) onIntervalChange([1, unitOption]); setPopoverOpen(false); }; const applyCustom = (countValue: number, maybeUnit?: DayInterval[1]) => { const safeUnit = maybeUnit && normalizedUnits.includes(maybeUnit) ? maybeUnit : defaultUnit; onIntervalSelectionChange('custom'); onIntervalUnitChange(safeUnit); onIntervalCountChange(countValue); if (!readOnly) onIntervalChange([countValue, safeUnit]); }; const triggerLabel = getIntervalLabel(effectiveSelection, intervalCount, effectiveUnit, useDurationLabels); return (
{prefix && (
{prefix}
)} onValueChange(e.target.value)} placeholder={placeholder} disabled={disabled || readOnly} className={cn( "min-w-0 flex-1 bg-transparent px-3 text-sm text-foreground", "placeholder:text-muted-foreground/50", "focus:outline-none", "disabled:cursor-not-allowed", inputClassName )} /> !readOnly && !disabled && setPopoverOpen(isOpen)}>
{normalizedUnits.map((unitOption) => ( ))}
Custom
{ const val = parseInt(e.target.value, 10); if (val > 0) { applyCustom(val, effectiveUnit); } }} /> { const newUnit = v as DayInterval[1]; applyCustom(effectiveSelection === 'custom' ? intervalCount : 1, newUnit); }} options={normalizedUnits.map((u) => ({ value: u, label: `${u}${(effectiveSelection === 'custom' ? intervalCount : 1) !== 1 ? 's' : ''}`, }))} size="sm" className="min-w-0 flex-1" />
); }