- 18 more icons (gamepad, computer, food, ...) with a test that keeps the server whitelist and the client picker identical. - The ⋯ menu of a message moves into the bubble foot (next to the time); its popup opens upwards inside the bubble and is limited to the bubble's width. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
import { useT, type Key } from "../i18n";
|
|
import { Icon } from "./Icon";
|
|
|
|
/** Colour tags of chats: names, drawn in the colours of the active theme (see `[data-color]` in styles.css). */
|
|
export const SESSION_COLORS = ["red", "orange", "yellow", "green", "aqua", "blue", "purple", "pink"] as const;
|
|
|
|
/** Icons a chat can get (must match SESSION_ICONS on the server). */
|
|
export const SESSION_ICONS = [
|
|
"robot", "brain", "terminal", "wrench", "gear", "chart", "shield", "image", "music", "cube", "file-text", "folder", "search", "sparkle", "clock", "lock",
|
|
"users", "toolbox", "plug", "scroll", "eye", "template",
|
|
"gamepad", "computer", "food", "coffee", "home", "book", "code", "camera", "cart", "heart", "star", "globe", "calendar", "car", "plane", "bulb", "money", "palette",
|
|
] as const;
|
|
|
|
/** Row of colour dots; the selected one is ringed, a second click removes the tag. */
|
|
export function ColorSwatches({ value, onChange }: { value?: string; onChange: (color: string) => void }) {
|
|
const t = useT();
|
|
return (
|
|
<div className="color-swatches" role="radiogroup" aria-label={t("color.label")}>
|
|
{SESSION_COLORS.map((c) => (
|
|
<button
|
|
key={c}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={value === c}
|
|
className={`swatch ${value === c ? "on" : ""}`}
|
|
data-color={c}
|
|
title={t(`color.${c}` as Key)}
|
|
aria-label={t(`color.${c}` as Key)}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onChange(value === c ? "" : c);
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
/** Grid of icons for a chat (in the colour of the chat); a second click removes the icon. */
|
|
export function IconPicker({ value, color, onChange }: { value?: string; color?: string; onChange: (icon: string) => void }) {
|
|
const t = useT();
|
|
return (
|
|
<div className="icon-picker" role="radiogroup" aria-label={t("icon.label")} data-color={color}>
|
|
{SESSION_ICONS.map((i) => (
|
|
<button
|
|
key={i}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={value === i}
|
|
className={`icon-choice ${value === i ? "on" : ""}`}
|
|
title={i}
|
|
aria-label={i}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onChange(value === i ? "" : i);
|
|
}}
|
|
>
|
|
<Icon name={i} />
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|