signal-desktop/ts/components/fun/base/FunItem.tsx

77 lines
2.2 KiB
TypeScript
Raw Normal View History

// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
2025-04-24 15:17:35 -07:00
import type { ForwardedRef, ReactNode } from 'react';
import React, { forwardRef, useEffect, useRef } from 'react';
import { type PressEvent, useLongPress } from 'react-aria';
import type { LongPressEvent } from '@react-types/shared';
2025-04-24 15:17:35 -07:00
import { Button } from 'react-aria-components';
import { mergeRefs } from '@react-aria/utils';
import { PressResponder } from '@react-aria/interactions';
import { strictAssert } from '../../../util/assert';
/**
* Button
*/
export type FunItemButtonLongPressProps = Readonly<
| {
longPressAccessibilityDescription?: never;
onLongPress?: never;
}
| {
longPressAccessibilityDescription: string;
onLongPress: (event: LongPressEvent) => void;
}
>;
export type FunItemButtonProps = Readonly<
{
'aria-label': string;
2025-04-24 15:17:35 -07:00
excludeFromTabOrder: boolean;
onPress: (event: PressEvent) => void;
onContextMenu?: (event: MouseEvent) => void;
children: ReactNode;
} & FunItemButtonLongPressProps
>;
export const FunItemButton = forwardRef(function FunItemButton(
props: FunItemButtonProps,
2025-04-24 15:17:35 -07:00
outerRef: ForwardedRef<HTMLButtonElement>
): JSX.Element {
2025-04-24 15:17:35 -07:00
const { onContextMenu } = props;
const innerRef = useRef<HTMLButtonElement>(null);
const { longPressProps } = useLongPress({
isDisabled: props.onLongPress == null,
accessibilityDescription: props.longPressAccessibilityDescription,
onLongPress: props.onLongPress,
});
2025-04-24 15:17:35 -07:00
useEffect(() => {
strictAssert(innerRef.current, 'Missing ref element');
const element = innerRef.current;
if (onContextMenu == null) {
return () => null;
}
element.addEventListener('contextmenu', onContextMenu);
return () => {
element.removeEventListener('contextmenu', onContextMenu);
};
}, [onContextMenu]);
return (
2025-04-24 15:17:35 -07:00
<PressResponder {...longPressProps}>
<Button
ref={mergeRefs(innerRef, outerRef)}
type="button"
className="FunItem__Button"
aria-label={props['aria-label']}
excludeFromTabOrder={props.excludeFromTabOrder}
onPress={props.onPress}
>
{props.children}
</Button>
</PressResponder>
);
});