Files
mycode/jsonhero-web/app/components/ColumnItem.tsx
2026-02-04 12:18:35 +08:00

94 lines
2.8 KiB
TypeScript

import { ChevronRightIcon } from "@heroicons/react/outline";
import { Mono } from "./Primitives/Mono";
import { memo, useEffect, useMemo, useRef } from "react";
import { ColumnViewNode } from "~/useColumnView";
import { colorForItemAtPath } from "~/utilities/colors";
import { Body } from "./Primitives/Body";
export type ColumnItemProps = {
item: ColumnViewNode;
json: unknown;
isSelected: boolean;
isHighlighted: boolean;
onClick?: (id: string) => void;
};
function ColumnItemElement({
item,
json,
isSelected,
isHighlighted,
onClick,
}: ColumnItemProps) {
const htmlElement = useRef<HTMLDivElement>(null);
const showArrow = item.children.length > 0;
const stateStyle = useMemo<string>(() => {
if (isHighlighted) {
return "bg-slate-300 text-slate-700 hover:bg-slate-400 hover:bg-opacity-60 transition duration-75 ease-out dark:bg-white dark:bg-opacity-[15%] dark:text-slate-100";
}
if (isSelected) {
return "bg-slate-200 hover:bg-slate-300 transition duration-75 ease-out dark:bg-white dark:bg-opacity-[5%] dark:hover:bg-white dark:hover:bg-opacity-[10%] dark:text-slate-200";
}
return "hover:bg-slate-100 transition duration-75 ease-out dark:hover:bg-white dark:hover:bg-opacity-[5%] dark:text-slate-400";
}, [isSelected, isHighlighted]);
const iconColor = useMemo<string>(
() => colorForItemAtPath(item.id, json),
[item.id, json]
);
useEffect(() => {
if (isSelected || isHighlighted) {
htmlElement.current?.scrollIntoView({
block: "nearest",
inline: "center",
});
}
}, [isSelected, isHighlighted]);
return (
<div
className={`flex h-9 items-center justify-items-stretch mx-1 px-1 py-1 my-1 rounded-sm ${stateStyle}`}
onClick={() => onClick && onClick(item.id)}
ref={htmlElement}
>
<div className="w-4 flex-none flex-col justify-items-center">
{item.icon && (
<item.icon
className={`h-5 w-5 ${
isSelected && isHighlighted
? "text-slate-900 dark:text-slate-300"
: "text-slate-500"
}`}
/>
)}
</div>
<div className="flex flex-grow flex-shrink items-baseline justify-between truncate">
<Body className="flex-grow flex-shrink-0 pl-3 pr-2 ">{item.title}</Body>
{item.subtitle && (
<Mono
className={`truncate pr-1 transition duration-75 ${
isHighlighted
? "text-gray-500 dark:text-slate-100"
: "text-gray-400 dark:text-gray-500"
}`}
>
{item.subtitle}
</Mono>
)}
</div>
{showArrow && (
<ChevronRightIcon className="flex-none w-4 h-4 text-gray-400" />
)}
</div>
);
}
export const ColumnItem = memo(ColumnItemElement);