Files
abode/src/tui/components/ui/Button.tsx
T
2026-06-29 23:07:14 +00:00

82 lines
1.9 KiB
TypeScript

import { useState, type ComponentProps, type ReactNode } from "react";
import { useManagedFocus } from "../../contexts/FocusManager.js";
import { Box, Text, useInput } from "ink";
export function Button({
children,
onClick,
autoFocus,
focusId,
}: {
children: ReactNode;
onClick: () => void;
autoFocus?: boolean;
focusId?: string;
}) {
const { isFocused } = useManagedFocus({ autoFocus, id: focusId });
useInput(
(input, key) => {
if (input === " " || key.return) onClick();
},
{ isActive: isFocused }
);
return <Text inverse={isFocused}>[{children}]</Text>;
}
export type ButtonListItem = {
children: ReactNode;
onClick: () => void;
};
export function ButtonList({
buttons,
autoFocus,
focusId,
isFocused: forceFocus,
horizontal = false,
vertical = false,
...props
}: {
buttons: ButtonListItem[];
autoFocus?: boolean;
focusId?: string;
isFocused?: boolean;
horizontal?: boolean;
vertical?: boolean;
} & Omit<ComponentProps<typeof Box>, "children">) {
if (!horizontal && !vertical) horizontal = true;
const [selected, setSelected] = useState(0);
const { isFocused } = useManagedFocus({
autoFocus,
id: focusId,
isActive: forceFocus === undefined,
});
useInput(
(input, key) => {
if (input === " " || key.return) {
buttons[selected].onClick();
} else if (
(vertical && key.downArrow) ||
(horizontal && key.rightArrow)
) {
setSelected((prev) => (prev + 1) % buttons.length);
} else if ((vertical && key.upArrow) || (horizontal && key.leftArrow)) {
setSelected((prev) => (prev - 1 + buttons.length) % buttons.length);
}
},
{ isActive: isFocused || forceFocus || false }
);
return (
<Box gap={1} {...props}>
{buttons.map((button, i) => (
<Text inverse={(isFocused || forceFocus) && i === selected}>
[{button.children}]
</Text>
))}
</Box>
);
}