mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-05-08 08:58:38 +01:00
Add pin message item to message context menu
This commit is contained in:
@@ -188,7 +188,7 @@ export namespace AxoDialog {
|
||||
|
||||
export const Close: FC<CloseProps> = memo(props => {
|
||||
return (
|
||||
<div className={tw('col-[close-slot] text-end')}>
|
||||
<div className={tw('col-[close-slot] text-end leading-none')}>
|
||||
<Dialog.Close asChild>
|
||||
<AxoIconButton.Root
|
||||
size="sm"
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
import React, { useState } from 'react';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { AxoRadioGroup } from './AxoRadioGroup.dom.js';
|
||||
|
||||
export default {
|
||||
title: 'Axo/AxoRadioGroup',
|
||||
} satisfies Meta;
|
||||
|
||||
export function Default(): JSX.Element {
|
||||
const [value, setValue] = useState('foo');
|
||||
return (
|
||||
<AxoRadioGroup.Root value={value} onValueChange={setValue}>
|
||||
<AxoRadioGroup.Item value="foo">
|
||||
<AxoRadioGroup.Indicator />
|
||||
<AxoRadioGroup.Label>Foo</AxoRadioGroup.Label>
|
||||
</AxoRadioGroup.Item>
|
||||
<AxoRadioGroup.Item value="bar">
|
||||
<AxoRadioGroup.Indicator />
|
||||
<AxoRadioGroup.Label>Bar</AxoRadioGroup.Label>
|
||||
</AxoRadioGroup.Item>
|
||||
<AxoRadioGroup.Item value="baz">
|
||||
<AxoRadioGroup.Indicator />
|
||||
<AxoRadioGroup.Label>
|
||||
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Veniam
|
||||
accusantium a aperiam quas perferendis error velit ipsam animi natus
|
||||
deserunt iste voluptatem asperiores voluptates rem odio necessitatibus
|
||||
delectus, optio officia?
|
||||
</AxoRadioGroup.Label>
|
||||
</AxoRadioGroup.Item>
|
||||
</AxoRadioGroup.Root>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2025 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { RadioGroup } from 'radix-ui';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import React, { memo, useId, useMemo } from 'react';
|
||||
import { tw } from './tw.dom.js';
|
||||
import {
|
||||
createStrictContext,
|
||||
useStrictContext,
|
||||
} from './_internal/StrictContext.dom.js';
|
||||
|
||||
export const Namespace = 'AxoRadioGroup';
|
||||
|
||||
/**
|
||||
* @example Anatomy
|
||||
* ```tsx
|
||||
* <AxoRadioGroup.Root>
|
||||
* <AxoRadioGroup.Item>
|
||||
* <AxoRadioGroup.Indicator/>
|
||||
* <AxoRadioGroup.Label>...</AxoRadioGroup.Label>
|
||||
* </AxoRadioGroup.Item>
|
||||
* </AxoAlertDialog.Root>
|
||||
* ```
|
||||
*/
|
||||
export namespace AxoRadioGroup {
|
||||
/**
|
||||
* Component: <AxoRadioGroup.Root>
|
||||
* -------------------------------
|
||||
*/
|
||||
|
||||
export type RootProps = Readonly<{
|
||||
value: string | null;
|
||||
onValueChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
children: ReactNode;
|
||||
}>;
|
||||
|
||||
export const Root: FC<RootProps> = memo(props => {
|
||||
return (
|
||||
<RadioGroup.Root
|
||||
value={props.value}
|
||||
onValueChange={props.onValueChange}
|
||||
disabled={props.disabled}
|
||||
className={tw('flex flex-col')}
|
||||
>
|
||||
{props.children}
|
||||
</RadioGroup.Root>
|
||||
);
|
||||
});
|
||||
|
||||
Root.displayName = `${Namespace}.Root`;
|
||||
|
||||
/**
|
||||
* Component: <AxoRadioGroup.Item>
|
||||
* -------------------------------
|
||||
*/
|
||||
|
||||
type ItemContextType = Readonly<{
|
||||
id: string;
|
||||
value: string;
|
||||
disabled: boolean;
|
||||
}>;
|
||||
|
||||
const ItemContext = createStrictContext<ItemContextType>(`${Namespace}.Item`);
|
||||
|
||||
export type ItemProps = Readonly<{
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
children: ReactNode;
|
||||
}>;
|
||||
|
||||
export const Item: FC<ItemProps> = memo(props => {
|
||||
const { value, disabled = false } = props;
|
||||
const id = useId();
|
||||
|
||||
const context = useMemo((): ItemContextType => {
|
||||
return { id, value, disabled };
|
||||
}, [id, value, disabled]);
|
||||
|
||||
return (
|
||||
<ItemContext.Provider value={context}>
|
||||
<label htmlFor={id} className={tw('flex gap-3 py-2.5')}>
|
||||
{props.children}
|
||||
</label>
|
||||
</ItemContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
Item.displayName = `${Namespace}.Item`;
|
||||
|
||||
/**
|
||||
* Component: <AxoRadioGroup.Indicator>
|
||||
* ------------------------------------
|
||||
*/
|
||||
|
||||
export type IndicatorProps = Readonly<{
|
||||
// ...
|
||||
}>;
|
||||
|
||||
export const Indicator: FC<IndicatorProps> = memo(() => {
|
||||
const context = useStrictContext(ItemContext);
|
||||
return (
|
||||
<RadioGroup.Item
|
||||
id={context.id}
|
||||
value={context.value}
|
||||
disabled={context.disabled}
|
||||
className={tw(
|
||||
'flex size-5 shrink-0 items-center justify-center rounded-full',
|
||||
'border border-border-primary inset-shadow-on-color',
|
||||
'data-[state=unchecked]:bg-fill-primary',
|
||||
'data-[state=unchecked]:pressed:bg-fill-primary-pressed',
|
||||
'data-[state=checked]:bg-color-fill-primary',
|
||||
'data-[state=checked]:pressed:bg-color-fill-primary-pressed',
|
||||
'data-[disabled]:border-border-secondary',
|
||||
'outline-0 outline-border-focused focused:outline-[2.5px]',
|
||||
'overflow-hidden',
|
||||
'forced-colors:data-[state=checked]:bg-[SelectedItem]'
|
||||
)}
|
||||
>
|
||||
<RadioGroup.Indicator asChild>
|
||||
<span
|
||||
className={tw(
|
||||
'size-[9px] rounded-full',
|
||||
'data-[state=checked]:bg-label-primary-on-color',
|
||||
'data-[state=checked]:data-[disabled]:bg-label-disabled-on-color',
|
||||
'forced-colors:data-[state=checked]:bg-[SelectedItemText]'
|
||||
)}
|
||||
/>
|
||||
</RadioGroup.Indicator>
|
||||
</RadioGroup.Item>
|
||||
);
|
||||
});
|
||||
|
||||
Indicator.displayName = `${Namespace}.Indicator`;
|
||||
|
||||
/**
|
||||
* Component: <AxoRadioGroup.Indicator>
|
||||
* ------------------------------------
|
||||
*/
|
||||
|
||||
export type LabelProps = Readonly<{
|
||||
children: ReactNode;
|
||||
}>;
|
||||
|
||||
export const Label: FC<LabelProps> = memo(props => {
|
||||
return (
|
||||
<span className={tw('truncate type-body-large text-label-primary')}>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
});
|
||||
|
||||
Label.displayName = `${Namespace}.Label`;
|
||||
}
|
||||
Reference in New Issue
Block a user