Add popover component

develop^2
Chewbacca 2023-03-14 15:58:11 -04:00
rodzic af69c7564e
commit 283935e837
4 zmienionych plików z 148 dodań i 18 usunięć

Wyświetl plik

@ -39,6 +39,7 @@ export {
} from './menu/menu';
export { default as Modal } from './modal/modal';
export { default as PhoneInput } from './phone-input/phone-input';
export { default as Popover } from './popover/popover';
export { default as Portal } from './portal/portal';
export { default as ProgressBar } from './progress-bar/progress-bar';
export { default as RadioButton } from './radio-button/radio-button';

Wyświetl plik

@ -0,0 +1,90 @@
import {
arrow,
FloatingArrow,
offset,
useClick,
useDismiss,
useFloating,
useInteractions,
useTransitionStyles,
} from '@floating-ui/react';
import React, { useRef, useState } from 'react';
interface IPopover {
children: React.ReactElement<any, string | React.JSXElementConstructor<any>>
content: React.ReactNode
}
/**
* Popover
*
* Similar to tooltip, but requires a click and is used for larger blocks
* of information.
*/
const Popover: React.FC<IPopover> = (props) => {
const { children, content } = props;
const [isOpen, setIsOpen] = useState<boolean>(false);
const arrowRef = useRef<SVGSVGElement>(null);
const { x, y, strategy, refs, context } = useFloating({
open: isOpen,
onOpenChange: setIsOpen,
placement: 'top',
middleware: [
offset(10),
arrow({
element: arrowRef,
}),
],
});
const click = useClick(context);
const dismiss = useDismiss(context);
const { isMounted, styles } = useTransitionStyles(context, {
initial: {
opacity: 0,
transform: 'scale(0.8)',
},
duration: {
open: 200,
close: 200,
},
});
const { getReferenceProps, getFloatingProps } = useInteractions([
click,
dismiss,
]);
return (
<>
{React.cloneElement(children, {
ref: refs.setReference,
...getReferenceProps(),
className: 'cursor-help',
})}
{(isMounted) && (
<div
ref={refs.setFloating}
style={{
position: strategy,
top: y ?? 0,
left: x ?? 0,
...styles,
}}
className='rounded-lg bg-white p-6 shadow-2xl dark:bg-gray-900 dark:ring-2 dark:ring-primary-700'
{...getFloatingProps()}
>
{content}
<FloatingArrow ref={arrowRef} context={context} className='fill-white dark:hidden' />
</div>
)}
</>
);
};
export default Popover;

Wyświetl plik

@ -1,7 +1,7 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { HStack, Icon, Text } from 'soapbox/components/ui';
import { HStack, Icon, Popover, Stack, Text } from 'soapbox/components/ui';
import { Group } from 'soapbox/types/entities';
interface IGroupPolicy {
@ -9,24 +9,59 @@ interface IGroupPolicy {
}
const GroupPrivacy = ({ group }: IGroupPolicy) => (
<HStack space={1} alignItems='center' data-testid='group-privacy'>
<Icon
className='h-4 w-4'
src={
group.locked
? require('@tabler/icons/lock.svg')
: require('@tabler/icons/world.svg')
}
/>
<Popover
content={
<Stack space={4} alignItems='center' className='w-72'>
<div className='rounded-full bg-gray-200 p-3 dark:bg-gray-800'>
<Icon
src={
group.locked
? require('@tabler/icons/lock.svg')
: require('@tabler/icons/world.svg')
}
className='h-6 w-6 text-gray-600 dark:text-gray-600'
/>
</div>
<Text theme='inherit' tag='span' size='sm' weight='medium'>
{group.locked ? (
<FormattedMessage id='group.privacy.locked' defaultMessage='Private' />
) : (
<FormattedMessage id='group.privacy.public' defaultMessage='Public' />
)}
</Text>
</HStack>
<Stack space={1} alignItems='center'>
<Text size='lg' weight='bold' align='center'>
{group.locked ? (
<FormattedMessage id='group.privacy.locked.full' defaultMessage='Private Group' />
) : (
<FormattedMessage id='group.privacy.public.full' defaultMessage='Public Group' />
)}
</Text>
<Text theme='muted' align='center'>
{group.locked ? (
<FormattedMessage id='group.privacy.locked.info' defaultMessage='Discoverable. Users can join after their request is approved.' />
) : (
<FormattedMessage id='group.privacy.public.info' defaultMessage='Discoverable. Anyone can join.' />
)}
</Text>
</Stack>
</Stack>
}
>
<HStack space={1} alignItems='center' data-testid='group-privacy'>
<Icon
className='h-4 w-4'
src={
group.locked
? require('@tabler/icons/lock.svg')
: require('@tabler/icons/world.svg')
}
/>
<Text theme='inherit' tag='span' size='sm' weight='medium'>
{group.locked ? (
<FormattedMessage id='group.privacy.locked' defaultMessage='Private' />
) : (
<FormattedMessage id='group.privacy.public' defaultMessage='Public' />
)}
</Text>
</HStack>
</Popover>
);
export default GroupPrivacy;

Wyświetl plik

@ -793,7 +793,11 @@
"group.leave.success": "Left the group",
"group.manage": "Manage Group",
"group.privacy.locked": "Private",
"group.privacy.locked.full": "Private Group",
"group.privacy.locked.info": "Discoverable. Users can join after their request is approved.",
"group.privacy.public": "Public",
"group.privacy.public.full": "Public Group",
"group.privacy.public.info": "Discoverable. Anyone can join.",
"group.role.admin": "Admin",
"group.role.moderator": "Moderator",
"group.tabs.all": "All",