"use client";
import * as React from "react";
import { cn } from "../../lib/utils";
import { Button } from "./button";
import { Input } from "./input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "./select";
import { Tip } from "./tip";
interface InlineEditShellProps {
children: React.ReactNode;
className?: string;
}
function InlineEditShell({ children, className }: InlineEditShellProps) {
return (
event.stopPropagation()}
>
{children}
);
}
function InlineEditActions({
label,
onSave,
onCancel,
}: {
label: string;
onSave: () => void;
onCancel: () => void;
}) {
return (
);
}
export interface InlineInputEditProps extends Omit<
React.ComponentProps,
"value" | "defaultValue" | "onChange" | "onBlur"
> {
value: string;
label: string;
onCommit: (value: string) => void;
emptyLabel?: string;
displayClassName?: string;
shellClassName?: string;
}
/**
* Compact text editing for a value shown in place. Enter saves and Escape
* cancels. Use a regular Field when the value needs help text or validation.
*/
function InlineInputEdit({
value,
label,
onCommit,
emptyLabel = "None",
displayClassName,
shellClassName,
disabled,
readOnly,
className,
onKeyDown,
...inputProps
}: InlineInputEditProps) {
const [editing, setEditing] = React.useState(false);
const [draft, setDraft] = React.useState(value);
const displayButtonRef = React.useRef(null);
const restoreFocusRef = React.useRef(false);
React.useEffect(() => {
if (!editing) setDraft(value);
}, [editing, value]);
React.useLayoutEffect(() => {
if (!editing && restoreFocusRef.current) {
restoreFocusRef.current = false;
displayButtonRef.current?.focus();
}
}, [editing]);
const cancel = React.useCallback(() => {
setDraft(value);
restoreFocusRef.current = true;
setEditing(false);
}, [value]);
const save = React.useCallback(() => {
onCommit(draft);
restoreFocusRef.current = true;
setEditing(false);
}, [draft, onCommit]);
if (!editing) {
return (
);
}
return (
setDraft(event.target.value)}
onKeyDown={(event) => {
onKeyDown?.(event);
event.stopPropagation();
if (event.defaultPrevented) return;
if (event.key === "Enter") {
event.preventDefault();
save();
}
if (event.key === "Escape") {
event.preventDefault();
cancel();
}
}}
/>
);
}
export interface InlineSelectEditOption {
value: string;
label: string;
}
export interface InlineSelectEditProps {
value: string;
label: string;
options: readonly InlineSelectEditOption[];
onCommit: (value: string) => void;
emptyLabel?: string;
disabled?: boolean;
readOnly?: boolean;
className?: string;
triggerClassName?: string;
}
/**
* Compact enumerated editing for detail rows and table cells. Selection stays
* a draft until the user confirms it.
*/
function InlineSelectEdit({
value,
label,
options,
onCommit,
emptyLabel = "None",
disabled,
readOnly,
className,
triggerClassName,
}: InlineSelectEditProps) {
const [editing, setEditing] = React.useState(false);
const [draft, setDraft] = React.useState(value);
const selectedLabel = options.find((option) => option.value === value)?.label;
const displayButtonRef = React.useRef(null);
const restoreFocusRef = React.useRef(false);
React.useEffect(() => {
if (!editing) setDraft(value);
}, [editing, value]);
React.useLayoutEffect(() => {
if (!editing && restoreFocusRef.current) {
restoreFocusRef.current = false;
displayButtonRef.current?.focus();
}
}, [editing]);
const cancel = React.useCallback(() => {
setDraft(value);
restoreFocusRef.current = true;
setEditing(false);
}, [value]);
const save = React.useCallback(() => {
onCommit(draft);
restoreFocusRef.current = true;
setEditing(false);
}, [draft, onCommit]);
if (!editing) {
return (
);
}
return (
);
}
export {
InlineEditActions,
InlineEditShell,
InlineInputEdit,
InlineSelectEdit,
};