Better markdown editor and SegmentedControl

This commit is contained in:
Gregory Schier
2025-01-13 10:46:13 -08:00
parent 84c3987c34
commit 587667fe79
4 changed files with 86 additions and 64 deletions

View File

@@ -7,7 +7,7 @@ import { Button } from './Button';
import type { IconProps } from './Icon';
import { Icon } from './Icon';
type Props = IconProps &
export type IconButtonProps = IconProps &
ButtonProps & {
showConfirm?: boolean;
iconClassName?: string;
@@ -16,7 +16,7 @@ type Props = IconProps &
showBadge?: boolean;
};
export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(
{
showConfirm,
icon,
@@ -30,7 +30,7 @@ export const IconButton = forwardRef<HTMLButtonElement, Props>(function IconButt
iconSize,
showBadge,
...props
}: Props,
}: IconButtonProps,
ref,
) {
const [confirmed, setConfirmed] = useTimedBoolean();

View File

@@ -0,0 +1,56 @@
import { useRef } from 'react';
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
import type { IconProps } from './Icon';
import type { IconButtonProps } from './IconButton';
import { IconButton } from './IconButton';
import { HStack } from './Stacks';
interface Props<T extends string> {
options: { value: T; label: string; icon: IconProps['icon']; event?: IconButtonProps['event'] }[];
onChange: (value: T) => void;
value: T;
name: string;
}
export function SegmentedControl<T extends string>({ value, onChange, options, name }: Props<T>) {
const [selectedValue, setSelectedValue] = useStateWithDeps<T>(value, [value]);
const containerRef = useRef<HTMLDivElement>(null);
return (
<HStack
ref={containerRef}
space={1}
role="group"
dir="ltr"
className="mb-auto bg-surface opacity-0 group-focus-within/markdown:opacity-100 group-hover/markdown:opacity-100 transition-opacity transform-gpu"
onKeyDown={(e) => {
const selectedIndex = options.findIndex((o) => o.value === selectedValue);
if (e.key === 'ArrowRight') {
const newIndex = Math.abs((selectedIndex + 1) % options.length);
setSelectedValue(options[newIndex]!.value);
const child = containerRef.current?.children[newIndex] as HTMLButtonElement;
child.focus();
} else if (e.key === 'ArrowLeft') {
const newIndex = Math.abs((selectedIndex - 1) % options.length);
setSelectedValue(options[newIndex]!.value);
const child = containerRef.current?.children[newIndex] as HTMLButtonElement;
child.focus();
}
}}
>
{options.map((o, i) => (
<IconButton
size="xs"
variant={value === o.value ? 'solid' : 'border'}
color={value === o.value ? 'secondary' : 'default'}
role="radio"
event={{ id: name, value: String(o.value) }}
tabIndex={selectedValue === o.value ? 0 : -1}
key={i}
title={o.label}
icon={o.icon}
onClick={() => onChange(o.value)}
/>
))}
</HStack>
);
}