'use client';
/**
* CronScheduler Client Component
*
* Compact cron builder. The default surface is a single popover trigger
* that reads like a value input ("Weekdays at 09:00") — the schedule
* editor lives inside the popover, following the ui-core combobox
* pattern. Pass `inline` to render the editor expanded without a popover.
*/
import { useState } from 'react';
import { Calendar, ChevronsUpDown, Check, Copy } from 'lucide-react';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@djangocfg/ui-core/components';
import { cn } from '@djangocfg/ui-core/lib';
import { CronSchedulerProvider } from './context/CronSchedulerContext';
import { useCronType, useCronPreview, useCronSize } from './context/hooks';
import {
ScheduleTypeSelector,
TimeSelector,
DayChips,
MonthDayGrid,
CustomInput,
} from './components';
import type { CronSchedulerProps } from './types';
// ============================================================================
// Schedule editor (shared by inline + popover modes)
// ============================================================================
interface ScheduleEditorProps {
timeFormat: '12h' | '24h';
disabled: boolean;
}
function ScheduleEditor({ timeFormat, disabled }: ScheduleEditorProps) {
const { type } = useCronType();
const size = useCronSize();
const isSm = size === 'sm';
return (
{type !== 'custom' && (
)}
{type === 'weekly' && }
{type === 'monthly' && }
{type === 'custom' && }
);
}
// ============================================================================
// Cron expression line (shared)
// ============================================================================
function CronExpressionLine({
allowCopy,
className,
}: {
allowCopy: boolean;
className?: string;
}) {
const [copied, setCopied] = useState(false);
const { cronExpression, isValid } = useCronPreview();
const isSm = useCronSize() === 'sm';
const handleCopy = async (e: React.MouseEvent) => {
e.stopPropagation();
try {
await navigator.clipboard.writeText(cronExpression);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error('Failed to copy:', err);
}
};
return (
{cronExpression}
{allowCopy && (
)}
);
}
// ============================================================================
// Compact popover trigger (default surface)
// ============================================================================
interface CompactTriggerProps {
showCronExpression: boolean;
allowCopy: boolean;
timeFormat: '12h' | '24h';
disabled: boolean;
placeholder: string;
className?: string;
}
function CompactTrigger({
showCronExpression,
allowCopy,
timeFormat,
disabled,
placeholder,
className,
}: CompactTriggerProps) {
const [open, setOpen] = useState(false);
const { humanDescription, isValid } = useCronPreview();
const isSm = useCronSize() === 'sm';
return (
{showCronExpression && (
)}
);
}
// ============================================================================
// Inline variant (editor expanded, no popover)
// ============================================================================
interface InlineSchedulerProps extends ScheduleEditorProps {
showCronExpression: boolean;
allowCopy: boolean;
className?: string;
}
function InlineScheduler({
timeFormat,
disabled,
showCronExpression,
allowCopy,
className,
}: InlineSchedulerProps) {
const isSm = useCronSize() === 'sm';
return (
);
}
function InlinePreview({
showCronExpression,
allowCopy,
}: {
showCronExpression: boolean;
allowCopy: boolean;
}) {
const { humanDescription, isValid } = useCronPreview();
const isSm = useCronSize() === 'sm';
return (
{humanDescription}
{showCronExpression && (
)}
);
}
// ============================================================================
// Main Component (with Provider)
// ============================================================================
/**
* CronScheduler - compact cron expression builder
*
* Default surface is a single popover trigger that reads the schedule as
* plain text ("Weekdays at 09:00"). Pass `inline` for the legacy expanded
* layout.
*
* @example
*
*
* @example
* // Legacy expanded layout
*
*/
export function CronScheduler({
value,
onChange,
defaultType = 'daily',
showCronExpression = false,
allowCopy = false,
timeFormat = '24h',
disabled = false,
inline = false,
placeholder = 'Set a schedule',
size = 'default',
className,
}: CronSchedulerProps) {
return (
{inline ? (
) : (
)}
);
}
export default CronScheduler;