mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-24 02:11:10 +01:00
Better markdown editor and SegmentedControl
This commit is contained in:
@@ -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();
|
||||
|
||||
56
src-web/components/core/SegmentedControl.tsx
Normal file
56
src-web/components/core/SegmentedControl.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user