103 lines
2.7 KiB
TypeScript
103 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { memo, useState, useEffect, useRef } from "react";
|
|
import ChipView from "./Chip.view";
|
|
import type { ChipProps } from "./Chip.types";
|
|
|
|
/**
|
|
* Figma: "Control / Chip" (TODO(figma)). Compact pill-shaped tag with
|
|
* selectable, removable, and inline-editable (custom) states.
|
|
*/
|
|
const ChipContainer = memo<ChipProps>(
|
|
({
|
|
label,
|
|
state: stateProp = "unselected",
|
|
palette: paletteProp = "default",
|
|
size: sizeProp = "s",
|
|
className = "",
|
|
disabled,
|
|
onClick,
|
|
onRemove,
|
|
onCheck,
|
|
onClose,
|
|
ariaLabel,
|
|
}) => {
|
|
const state = stateProp;
|
|
const palette = paletteProp;
|
|
const size = sizeProp;
|
|
|
|
const isDisabled = disabled ?? state === "disabled";
|
|
const isCustom = state === "custom";
|
|
|
|
// Manage input value for custom state
|
|
const [inputValue, setInputValue] = useState("");
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
|
|
// Focus input when custom state is active
|
|
useEffect(() => {
|
|
if (isCustom && inputRef.current) {
|
|
inputRef.current.focus();
|
|
}
|
|
}, [isCustom]);
|
|
|
|
const handleCheck = (
|
|
value: string,
|
|
event: React.MouseEvent<HTMLButtonElement>,
|
|
) => {
|
|
if (onCheck && value.trim()) {
|
|
onCheck(value.trim(), event);
|
|
// Reset input after successful check
|
|
setInputValue("");
|
|
}
|
|
};
|
|
|
|
const handleClose = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
if (onClose) {
|
|
onClose(event);
|
|
} else if (onRemove) {
|
|
// Fallback to onRemove if onClose not provided
|
|
onRemove(event);
|
|
}
|
|
// Reset input value when closing
|
|
setInputValue("");
|
|
};
|
|
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key === "Enter" && inputValue.trim() && onCheck) {
|
|
event.preventDefault();
|
|
handleCheck(
|
|
inputValue.trim(),
|
|
event as unknown as React.MouseEvent<HTMLButtonElement>,
|
|
);
|
|
} else if (event.key === "Escape" && onClose) {
|
|
event.preventDefault();
|
|
handleClose(event as unknown as React.MouseEvent<HTMLButtonElement>);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<ChipView
|
|
label={label}
|
|
state={state}
|
|
palette={palette}
|
|
size={size}
|
|
className={className}
|
|
disabled={isDisabled}
|
|
onClick={onClick}
|
|
onRemove={onRemove}
|
|
onCheck={handleCheck}
|
|
onClose={handleClose}
|
|
inputValue={isCustom ? inputValue : undefined}
|
|
onInputChange={isCustom ? setInputValue : undefined}
|
|
onInputKeyDown={isCustom ? handleKeyDown : undefined}
|
|
inputRef={isCustom ? inputRef : undefined}
|
|
ariaLabel={ariaLabel}
|
|
/>
|
|
);
|
|
},
|
|
);
|
|
|
|
ChipContainer.displayName = "Chip";
|
|
|
|
export default ChipContainer;
|