mirror of https://github.com/buster-so/buster.git
share menu popover
This commit is contained in:
parent
3840d64360
commit
74b832a9df
|
@ -2,7 +2,6 @@ import type { DatasetPermissionOverviewUser } from '@/api/asset_interfaces';
|
||||||
import { ChevronRight } from '@/components/ui/icons';
|
import { ChevronRight } from '@/components/ui/icons';
|
||||||
import { Popover } from '@/components/ui/tooltip/Popover';
|
import { Popover } from '@/components/ui/tooltip/Popover';
|
||||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
import { ShareMenu } from './ShareMenu';
|
||||||
|
import { BusterShare, ShareAssetType, ShareRole } from '@/api/asset_interfaces';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
|
||||||
|
const meta: Meta<typeof ShareMenu> = {
|
||||||
|
title: 'Features/Share/ShareMenu',
|
||||||
|
component: ShareMenu,
|
||||||
|
parameters: {
|
||||||
|
layout: 'centered'
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '400px',
|
||||||
|
height: '400px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ShareMenu>;
|
||||||
|
|
||||||
|
const mockShareConfig: BusterShare = {
|
||||||
|
sharingKey: 'mock-sharing-key',
|
||||||
|
individual_permissions: null,
|
||||||
|
team_permissions: null,
|
||||||
|
organization_permissions: null,
|
||||||
|
password_secret_id: null,
|
||||||
|
public_expiry_date: null,
|
||||||
|
public_enabled_by: null,
|
||||||
|
publicly_accessible: false,
|
||||||
|
public_password: null,
|
||||||
|
permission: ShareRole.OWNER
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
args: {
|
||||||
|
shareAssetConfig: mockShareConfig,
|
||||||
|
assetId: '123',
|
||||||
|
assetType: ShareAssetType.DASHBOARD,
|
||||||
|
children: <Button>Share</Button>
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ViewerPermission: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
shareAssetConfig: {
|
||||||
|
...mockShareConfig,
|
||||||
|
permission: ShareRole.VIEWER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PublicAsset: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
shareAssetConfig: {
|
||||||
|
...mockShareConfig,
|
||||||
|
publicly_accessible: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,8 +1,7 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { PropsWithChildren } from 'react';
|
import React, { PropsWithChildren } from 'react';
|
||||||
import { PopoverProps } from 'antd';
|
import { Popover } from '@/components/ui/tooltip/Popover';
|
||||||
import { AppPopover } from '@/components/ui/tooltip/AppPopover';
|
|
||||||
import { AppTooltip } from '@/components/ui/tooltip';
|
import { AppTooltip } from '@/components/ui/tooltip';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { BusterShare, ShareAssetType } from '@/api/asset_interfaces';
|
import { BusterShare, ShareAssetType } from '@/api/asset_interfaces';
|
||||||
|
@ -11,12 +10,11 @@ import { isShareMenuVisible } from './publicHelpers';
|
||||||
|
|
||||||
export const ShareMenu: React.FC<
|
export const ShareMenu: React.FC<
|
||||||
PropsWithChildren<{
|
PropsWithChildren<{
|
||||||
placement?: PopoverProps['placement'];
|
|
||||||
shareAssetConfig: BusterShare | null;
|
shareAssetConfig: BusterShare | null;
|
||||||
assetId: string;
|
assetId: string;
|
||||||
assetType: ShareAssetType;
|
assetType: ShareAssetType;
|
||||||
}>
|
}>
|
||||||
> = ({ children, shareAssetConfig, assetId, assetType, placement = 'bottomLeft' }) => {
|
> = ({ children, shareAssetConfig, assetId, assetType }) => {
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
|
|
||||||
const onOpenChange = useMemoizedFn((v: boolean) => {
|
const onOpenChange = useMemoizedFn((v: boolean) => {
|
||||||
|
@ -32,10 +30,8 @@ export const ShareMenu: React.FC<
|
||||||
const permission = shareAssetConfig?.permission;
|
const permission = shareAssetConfig?.permission;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppPopover
|
<Popover
|
||||||
trigger={['click']}
|
size={'none'}
|
||||||
destroyTooltipOnHide
|
|
||||||
placement={placement}
|
|
||||||
onOpenChange={onOpenChange}
|
onOpenChange={onOpenChange}
|
||||||
content={
|
content={
|
||||||
shareAssetConfig ? (
|
shareAssetConfig ? (
|
||||||
|
@ -50,6 +46,6 @@ export const ShareMenu: React.FC<
|
||||||
<AppTooltip title={!isOpen ? 'Share item' : ''}>
|
<AppTooltip title={!isOpen ? 'Share item' : ''}>
|
||||||
<div className="flex">{children}</div>
|
<div className="flex">{children}</div>
|
||||||
</AppTooltip>
|
</AppTooltip>
|
||||||
</AppPopover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { BusterShare, ShareRole, ShareAssetType } from '@/api/asset_interfaces';
|
import { type BusterShare, ShareRole, ShareAssetType } from '@/api/asset_interfaces';
|
||||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { ShareMenuTopBar, ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
import { ShareMenuTopBar, ShareMenuTopBarOptions } from './ShareMenuTopBar';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes';
|
import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes';
|
||||||
|
@ -15,7 +15,7 @@ export const ShareMenuContent: React.FC<{
|
||||||
}> = React.memo(({ assetId, assetType, shareAssetConfig, permission }) => {
|
}> = React.memo(({ assetId, assetType, shareAssetConfig, permission }) => {
|
||||||
const { openSuccessMessage } = useBusterNotifications();
|
const { openSuccessMessage } = useBusterNotifications();
|
||||||
const isOwner = permission === ShareRole.OWNER;
|
const isOwner = permission === ShareRole.OWNER;
|
||||||
const [selectedOptions, setSelectedOptions] = React.useState<ShareMenuTopBarOptions>(
|
const [selectedOptions, setSelectedOptions] = useState<ShareMenuTopBarOptions>(
|
||||||
isOwner ? ShareMenuTopBarOptions.Share : ShareMenuTopBarOptions.Embed
|
isOwner ? ShareMenuTopBarOptions.Share : ShareMenuTopBarOptions.Embed
|
||||||
);
|
);
|
||||||
const previousSelection = React.useRef<ShareMenuTopBarOptions>(selectedOptions);
|
const previousSelection = React.useRef<ShareMenuTopBarOptions>(selectedOptions);
|
||||||
|
|
|
@ -87,7 +87,7 @@ const ShareMenuContentShare: React.FC<{
|
||||||
ShareRole.VIEWER
|
ShareRole.VIEWER
|
||||||
);
|
);
|
||||||
const disableSubmit = !inputHasText(inputValue) || !validate(inputValue);
|
const disableSubmit = !inputHasText(inputValue) || !validate(inputValue);
|
||||||
const hasUserTeams = userTeams.length > 0;
|
const hasUserTeams = userTeams?.length > 0;
|
||||||
|
|
||||||
const onSubmitNewEmail = useMemoizedFn(async () => {
|
const onSubmitNewEmail = useMemoizedFn(async () => {
|
||||||
const isValidEmail = validate(inputValue);
|
const isValidEmail = validate(inputValue);
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const StatusDropdownContent: React.FC<{
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
selectType="single"
|
selectType="single"
|
||||||
menuHeader="Verification status...">
|
menuHeader="Verification status...">
|
||||||
<span className="inline-block">{children}</span>
|
{children}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -71,8 +71,8 @@ const DropdownMenuContent = React.forwardRef<
|
||||||
footerContent?: React.ReactNode;
|
footerContent?: React.ReactNode;
|
||||||
}
|
}
|
||||||
>(({ className, children, sideOffset = 4, footerContent, ...props }, ref) => {
|
>(({ className, children, sideOffset = 4, footerContent, ...props }, ref) => {
|
||||||
const NodeWrapper = footerContent ? 'div' : React.Fragment;
|
const NodeWrapper = footerContent ? 'div' : 'span';
|
||||||
const nodeWrapperProps = footerContent ? { className: 'p-2' } : {};
|
const nodeWrapperProps = footerContent ? { className: 'p-2' } : { className: 'inline-block' };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuPrimitive.Portal>
|
<DropdownMenuPrimitive.Portal>
|
||||||
|
|
|
@ -33,14 +33,16 @@ export const Popover = React.memo<PopoverProps>(
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<PopoverBase trigger={trigger} {...props}>
|
<PopoverBase trigger={trigger} {...props}>
|
||||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
<PopoverTrigger asChild>
|
||||||
|
<span className="inline-block">{children}</span>
|
||||||
|
</PopoverTrigger>
|
||||||
<PopoverContent
|
<PopoverContent
|
||||||
align={align}
|
align={align}
|
||||||
side={side}
|
side={side}
|
||||||
className={className}
|
className={className}
|
||||||
size={size}
|
size={size}
|
||||||
headerContent={headerContent && <PopoverHeaderContent title={headerContent} />}>
|
headerContent={headerContent && <PopoverHeaderContent title={headerContent} />}>
|
||||||
{content}
|
<>{content}</>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</PopoverBase>
|
</PopoverBase>
|
||||||
);
|
);
|
||||||
|
|
|
@ -88,7 +88,9 @@ const PopoverContent = React.forwardRef<
|
||||||
)}
|
)}
|
||||||
{...props}>
|
{...props}>
|
||||||
{headerContent && <>{headerContent}</>}
|
{headerContent && <>{headerContent}</>}
|
||||||
<div className={cn(popoverContentVariant({ size }), className)}>{children}</div>
|
<div className={cn(popoverContentVariant({ size }), className)}>
|
||||||
|
<>{children}</>
|
||||||
|
</div>
|
||||||
</PopoverPrimitive.Content>
|
</PopoverPrimitive.Content>
|
||||||
</PopoverPrimitive.Portal>
|
</PopoverPrimitive.Portal>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue