More styling improvements to highlight control

This commit is contained in:
Tom Moor
2026-04-19 18:07:08 -04:00
parent c52c96dc96
commit 7b182f9038
4 changed files with 47 additions and 8 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ type Props = {
};
/**
* Wraps children in a <Fade> if loading is true on mount.
* Wraps children in a <Fade> if animate is true on mount.
*/
export const ConditionalFade = ({ animate, children }: Props) => {
const [isAnimated] = useState(animate);
+25 -1
View File
@@ -67,6 +67,8 @@ type Props = Omit<React.HTMLAttributes<HTMLButtonElement>, "onChange"> & {
short?: boolean;
/** Display a tooltip with the descriptive help text about the select menu. */
help?: string;
/** Render function to override the selected value shown in the trigger. Receives the currently selected option, or undefined when none is selected. */
displayValue?: (selectedOption: Item | undefined) => React.ReactNode;
} & TriggerButtonProps;
export const InputSelect = React.forwardRef<HTMLButtonElement, Props>(
@@ -79,6 +81,7 @@ export const InputSelect = React.forwardRef<HTMLButtonElement, Props>(
labelHidden,
short,
help,
displayValue,
...triggerProps
} = props;
@@ -95,6 +98,20 @@ export const InputSelect = React.forwardRef<HTMLButtonElement, Props>(
(opt) => opt.type === "item" && !!opt.icon
);
const selectedOption = React.useMemo(
() =>
localValue
? (options.find(
(opt) => opt.type === "item" && opt.value === localValue
) as Item | undefined)
: undefined,
[localValue, options]
);
const resolvedDisplayValue = displayValue
? displayValue(selectedOption)
: undefined;
const renderOption = React.useCallback(
(option: Option, idx: number) => {
if (option.type === "separator") {
@@ -143,6 +160,7 @@ export const InputSelect = React.forwardRef<HTMLButtonElement, Props>(
onChange={onValueChange}
placeholder={placeholder}
optionsHaveIcon={optionsHaveIcon}
resolvedDisplayValue={resolvedDisplayValue}
/>
);
}
@@ -159,6 +177,7 @@ export const InputSelect = React.forwardRef<HTMLButtonElement, Props>(
<InputSelectTrigger
ref={ref}
placeholder={placeholder}
displayValue={resolvedDisplayValue}
{...triggerProps}
/>
<InputSelectContent
@@ -179,6 +198,7 @@ InputSelect.displayName = "InputSelect";
type MobileSelectProps = Props & {
placeholder: string;
optionsHaveIcon: boolean;
resolvedDisplayValue?: React.ReactNode;
};
const MobileSelect = React.forwardRef<HTMLButtonElement, MobileSelectProps>(
@@ -193,6 +213,8 @@ const MobileSelect = React.forwardRef<HTMLButtonElement, MobileSelectProps>(
short,
placeholder,
optionsHaveIcon,
displayValue: _displayValue,
resolvedDisplayValue,
...triggerProps
} = props;
@@ -262,7 +284,9 @@ const MobileSelect = React.forwardRef<HTMLButtonElement, MobileSelectProps>(
disclosure
data-placeholder={selectedOption ? false : ""}
>
{selectedOption ? (
{resolvedDisplayValue !== undefined ? (
resolvedDisplayValue
) : selectedOption ? (
<Option
option={selectedOption as Item}
optionsHaveIcon={optionsHaveIcon}
+11 -3
View File
@@ -22,19 +22,27 @@ export type TriggerButtonProps = {
className?: string;
} & Pick<ButtonProps<unknown>, "borderOnHover">;
type InputSelectTriggerProps = { placeholder: string } & TriggerButtonProps &
type InputSelectTriggerProps = {
placeholder: string;
/** When provided, overrides the selected value rendered inside the trigger. */
displayValue?: React.ReactNode;
} & TriggerButtonProps &
React.ComponentPropsWithoutRef<typeof InputSelectPrimitive.Trigger>;
const InputSelectTrigger = React.forwardRef<
React.ElementRef<typeof InputSelectPrimitive.Trigger>,
InputSelectTriggerProps
>((props, ref) => {
const { placeholder, children, nude, ...buttonProps } = props;
const { placeholder, children, nude, displayValue, ...buttonProps } = props;
return (
<InputSelectPrimitive.Trigger ref={ref} asChild>
<SelectButton neutral disclosure $nude={nude} {...buttonProps}>
<InputSelectPrimitive.Value placeholder={placeholder} />
{displayValue !== undefined ? (
<>{displayValue}</>
) : (
<InputSelectPrimitive.Value placeholder={placeholder} />
)}
</SelectButton>
</InputSelectPrimitive.Trigger>
);
@@ -13,7 +13,7 @@ import Switch from "~/components/Switch";
import useUserLocale from "~/hooks/useUserLocale";
import { revisionCollaboratorText } from "./utils";
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
import Fade from "~/components/Fade";
import { ConditionalFade } from "~/components/Fade";
export const COMPARE_TO_PREVIOUS = "previous";
@@ -38,6 +38,7 @@ export function HighlightChangesControl({
}: Props) {
const { t } = useTranslation();
const userLocale = useUserLocale();
const skipFadeRef = React.useRef(showChanges);
const compareOptions = React.useMemo((): Option[] => {
const revisionItems = items.filter(
@@ -94,17 +95,22 @@ export function HighlightChangesControl({
/>
</Text>
{showChanges && (
<Fade as="div">
<ConditionalFade animate={!skipFadeRef.current}>
<StyledInputSelect
options={compareOptions}
value={compareTo}
onChange={onCompareToChange}
label={t("Compare to")}
displayValue={(item) => (
<>
<Text weight="bold">{t("Compare to")}</Text> {item?.label}
</>
)}
labelHidden
nude
short
/>
</Fade>
</ConditionalFade>
)}
</ResizingHeightContainer>
</Content>
@@ -118,6 +124,7 @@ const StyledInputSelect = styled(InputSelect)`
border-top-right-radius: 0;
position: relative;
inset-block-end: -1px;
height: 40px;
`;
const Content = styled.div`