mirror of https://github.com/buster-so/buster.git
popover lineage breadcrumbs
This commit is contained in:
parent
f7bd0abdff
commit
3840d64360
|
@ -100,8 +100,6 @@ export const PolicyCheck: React.FC<{
|
|||
}
|
||||
}, [placement]);
|
||||
|
||||
console.log(placement);
|
||||
|
||||
const alignMemo: PopoverProps['align'] = useMemo(() => {
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { PermissionLineageBreadcrumb } from './PermissionLineageBreadcrumb';
|
||||
import type { DatasetPermissionOverviewUser } from '@/api/asset_interfaces';
|
||||
|
||||
const meta: Meta<typeof PermissionLineageBreadcrumb> = {
|
||||
title: 'Features/Permissions/PermissionLineageBreadcrumb',
|
||||
component: PermissionLineageBreadcrumb,
|
||||
tags: ['autodocs'],
|
||||
parameters: {
|
||||
layout: 'centered'
|
||||
}
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PermissionLineageBreadcrumb>;
|
||||
|
||||
// Sample data for different scenarios
|
||||
const singleLineage: DatasetPermissionOverviewUser['lineage'] = [
|
||||
[
|
||||
{ type: 'user', name: 'John Doe', id: 'user1' },
|
||||
{ type: 'datasets', name: 'Sales Dataset', id: 'dataset1' },
|
||||
{ type: 'permissionGroups', name: 'Analysts', id: 'group1' }
|
||||
]
|
||||
];
|
||||
|
||||
const multipleLineage: DatasetPermissionOverviewUser['lineage'] = [
|
||||
[
|
||||
{ type: 'user', name: 'John Doe', id: 'user1' },
|
||||
{ type: 'datasets', name: 'Sales Dataset', id: 'dataset1' }
|
||||
],
|
||||
[
|
||||
{ type: 'user', name: 'John Doe', id: 'user1' },
|
||||
{ type: 'datasetGroups', name: 'Marketing Data', id: 'datasetGroup1' }
|
||||
]
|
||||
];
|
||||
|
||||
const emptyLineage: DatasetPermissionOverviewUser['lineage'] = [];
|
||||
|
||||
export const SingleLineageCanQuery: Story = {
|
||||
args: {
|
||||
lineage: singleLineage,
|
||||
canQuery: true
|
||||
}
|
||||
};
|
||||
|
||||
export const SingleLineageCannotQuery: Story = {
|
||||
args: {
|
||||
lineage: singleLineage,
|
||||
canQuery: false
|
||||
}
|
||||
};
|
||||
|
||||
export const MultipleLineageCanQuery: Story = {
|
||||
args: {
|
||||
lineage: multipleLineage,
|
||||
canQuery: true
|
||||
}
|
||||
};
|
||||
|
||||
export const MultipleLineageCannotQuery: Story = {
|
||||
args: {
|
||||
lineage: multipleLineage,
|
||||
canQuery: false
|
||||
}
|
||||
};
|
||||
|
||||
export const NoLineage: Story = {
|
||||
args: {
|
||||
lineage: emptyLineage,
|
||||
canQuery: false
|
||||
}
|
||||
};
|
|
@ -1,10 +1,12 @@
|
|||
import type { DatasetPermissionOverviewUser } from '@/api/asset_interfaces';
|
||||
import { AppMaterialIcons, AppPopover } from '@/components/ui';
|
||||
import { ChevronRight } from '@/components/ui/icons';
|
||||
import { Popover } from '@/components/ui/tooltip/Popover';
|
||||
import { BusterRoutes, createBusterRoute } from '@/routes';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { createStyles } from 'antd-style';
|
||||
import Link from 'next/link';
|
||||
import React, { useMemo } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const PermissionLineageBreadcrumb: React.FC<{
|
||||
lineage: DatasetPermissionOverviewUser['lineage'];
|
||||
|
@ -56,10 +58,10 @@ const MultipleLineage: React.FC<{
|
|||
lineage: DatasetPermissionOverviewUser['lineage'];
|
||||
canQuery: DatasetPermissionOverviewUser['can_query'];
|
||||
}> = ({ lineage, canQuery }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
// const { styles, cx } = useStyles();
|
||||
const Content = useMemo(() => {
|
||||
return (
|
||||
<div className="flex min-w-[200px] flex-col space-y-2 p-2">
|
||||
<div className="flex min-w-[200px] flex-col space-y-2">
|
||||
{lineage.map((item, lineageindex) => {
|
||||
const items = item.map((v, index) => {
|
||||
return <SelectedComponent key={index} item={v} />;
|
||||
|
@ -71,17 +73,10 @@ const MultipleLineage: React.FC<{
|
|||
);
|
||||
}, [lineage]);
|
||||
|
||||
const onClickPreflight = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
return (
|
||||
<AppPopover placement="topRight" destroyTooltipOnHide trigger="click" content={Content}>
|
||||
<div className={cx(styles.linearItem, 'clickable')} onClick={onClickPreflight}>
|
||||
Multiple access sources
|
||||
</div>
|
||||
</AppPopover>
|
||||
<Popover side="top" align="start" trigger="click" content={Content} size="sm">
|
||||
<div className={linearItem({ clickable: true })}>Multiple access sources</div>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -91,39 +86,30 @@ interface LineageItemProps {
|
|||
}
|
||||
|
||||
const UserLineageItem: React.FC<LineageItemProps> = ({ name, id }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return <div className={cx(styles.linearItem)}>{name}</div>;
|
||||
return <div className={linearItem({ clickable: false })}>{name}</div>;
|
||||
};
|
||||
|
||||
const DatasetLineageItem: React.FC<LineageItemProps> = ({ name, id }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return (
|
||||
<Link href={createBusterRoute({ route: BusterRoutes.APP_DATASETS_ID, datasetId: id })}>
|
||||
<div className={cx(styles.linearItem, 'clickable')}>{name}</div>
|
||||
<div className={linearItem({ clickable: true })}>{name}</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const PermissionGroupLineageItem: React.FC<LineageItemProps> = ({ name, id }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return <div className={cx(styles.linearItem)}>{name}</div>;
|
||||
return <div className={linearItem({ clickable: false })}>{name}</div>;
|
||||
};
|
||||
|
||||
const DatasetGroupLineageItem: React.FC<LineageItemProps> = ({ name, id }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return <div className={cx(styles.linearItem)}>{name}</div>;
|
||||
return <div className={linearItem({ clickable: false })}>{name}</div>;
|
||||
};
|
||||
|
||||
const CanQueryTag: React.FC<{
|
||||
canQuery: boolean;
|
||||
}> = ({ canQuery }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
styles.canQueryTag,
|
||||
canQuery ? styles.canQueryTagSuccess : styles.canQueryTagError
|
||||
)}>
|
||||
<div className={canQueryTag({ status: canQuery ? 'success' : 'error' })}>
|
||||
{canQuery ? 'Can query' : 'Cannot query'}
|
||||
</div>
|
||||
);
|
||||
|
@ -133,13 +119,12 @@ const LineageBreadcrumb: React.FC<{
|
|||
items: React.ReactNode[];
|
||||
canQuery: boolean;
|
||||
}> = ({ items, canQuery }) => {
|
||||
const { styles, cx } = useStyles();
|
||||
const BreadcrumbIcon = <AppMaterialIcons icon="chevron_right" />;
|
||||
const BreadcrumbIcon = <ChevronRight />;
|
||||
|
||||
const allItems = [...items, <CanQueryTag key="can-query" canQuery={canQuery} />];
|
||||
|
||||
return (
|
||||
<div className={cx(styles.linearContainer, 'flex justify-end space-x-0')}>
|
||||
<div className={cn('text-text-secondary', 'flex justify-end space-x-0')}>
|
||||
{allItems.map((item, index) => {
|
||||
return (
|
||||
<div key={index} className="flex items-center space-x-0">
|
||||
|
@ -152,33 +137,36 @@ const LineageBreadcrumb: React.FC<{
|
|||
);
|
||||
};
|
||||
|
||||
const useStyles = createStyles(({ token, css }) => ({
|
||||
linearContainer: css`
|
||||
color: ${token.colorTextSecondary};
|
||||
`,
|
||||
linearItem: css`
|
||||
color: ${token.colorTextSecondary};
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
|
||||
&.clickable {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: ${token.colorText};
|
||||
background-color: ${token.colorFillSecondary};
|
||||
}
|
||||
const linearItem = cva('text-text-secondary text-base px-1 py-1.5 rounded-sm', {
|
||||
variants: {
|
||||
clickable: {
|
||||
true: 'cursor-pointer hover:text-text hover:bg-item-hover-active',
|
||||
false: ''
|
||||
}
|
||||
`,
|
||||
canQueryTag: css`
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
`,
|
||||
canQueryTagSuccess: css`
|
||||
color: #34a32d;
|
||||
background-color: #edfff0;
|
||||
`,
|
||||
canQueryTagError: css`
|
||||
color: #ff9e00;
|
||||
background-color: #fff7ed;
|
||||
`
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
const canQueryTag = cva('rounded-sm px-1 py-1.5 text-base', {
|
||||
variants: {
|
||||
status: {
|
||||
success: 'bg-success-background text-success-foreground',
|
||||
error: 'bg-danger-background text-danger-foreground'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// const useStyles = createStyles(({ token, css }) => ({
|
||||
|
||||
// canQueryTag: css`
|
||||
// border-radius: 4px;
|
||||
// padding: 4px 6px;
|
||||
// `,
|
||||
// canQueryTagSuccess: css`
|
||||
// color: #34a32d;
|
||||
// background-color: #edfff0;
|
||||
// `,
|
||||
// canQueryTagError: css`
|
||||
// color: #ff9e00;
|
||||
// background-color: #fff7ed;
|
||||
// `
|
||||
// }));
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
PopoverRoot as PopoverBase,
|
||||
PopoverContent,
|
||||
PopoverContentVariant,
|
||||
PopoverTrigger,
|
||||
PopoverTriggerType
|
||||
} from './PopoverBase';
|
||||
|
@ -14,7 +15,8 @@ export interface PopoverProps
|
|||
content: React.ReactNode;
|
||||
className?: string;
|
||||
headerContent?: string | React.ReactNode;
|
||||
triggerType?: PopoverTriggerType;
|
||||
trigger?: PopoverTriggerType;
|
||||
size?: PopoverContentVariant['size'];
|
||||
}
|
||||
|
||||
export const Popover = React.memo<PopoverProps>(
|
||||
|
@ -25,16 +27,18 @@ export const Popover = React.memo<PopoverProps>(
|
|||
side,
|
||||
className = '',
|
||||
headerContent,
|
||||
triggerType = 'click',
|
||||
trigger = 'click',
|
||||
size = 'default',
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<PopoverBase triggerType={triggerType} {...props}>
|
||||
<PopoverBase trigger={trigger} {...props}>
|
||||
<PopoverTrigger asChild>{children}</PopoverTrigger>
|
||||
<PopoverContent
|
||||
align={align}
|
||||
side={side}
|
||||
className={className}
|
||||
size={size}
|
||||
headerContent={headerContent && <PopoverHeaderContent title={headerContent} />}>
|
||||
{content}
|
||||
</PopoverContent>
|
||||
|
|
|
@ -12,26 +12,26 @@ export type PopoverTriggerType = 'click' | 'hover';
|
|||
const Popover = PopoverPrimitive.Root;
|
||||
|
||||
interface PopoverProps extends React.ComponentPropsWithoutRef<typeof Popover> {
|
||||
triggerType?: PopoverTriggerType;
|
||||
trigger?: PopoverTriggerType;
|
||||
}
|
||||
|
||||
const PopoverRoot: React.FC<PopoverProps> = ({ children, triggerType = 'click', ...props }) => {
|
||||
const PopoverRoot: React.FC<PopoverProps> = ({ children, trigger = 'click', ...props }) => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
|
||||
const handleMouseEnter = useMemoizedFn(() => {
|
||||
if (triggerType === 'hover') {
|
||||
if (trigger === 'hover') {
|
||||
setIsOpen(true);
|
||||
}
|
||||
});
|
||||
|
||||
const handleMouseLeave = useMemoizedFn(() => {
|
||||
if (triggerType === 'hover') {
|
||||
if (trigger === 'hover') {
|
||||
setIsOpen(false);
|
||||
}
|
||||
});
|
||||
|
||||
const content =
|
||||
triggerType === 'hover' ? (
|
||||
trigger === 'hover' ? (
|
||||
<div className="relative" onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||
<div className="absolute -inset-[4px]" />
|
||||
<div className="relative z-10">{children}</div>
|
||||
|
@ -41,7 +41,7 @@ const PopoverRoot: React.FC<PopoverProps> = ({ children, triggerType = 'click',
|
|||
);
|
||||
|
||||
return (
|
||||
<Popover {...props} open={triggerType === 'hover' ? isOpen : undefined}>
|
||||
<Popover {...props} open={trigger === 'hover' ? isOpen : undefined}>
|
||||
{content}
|
||||
</Popover>
|
||||
);
|
||||
|
@ -64,11 +64,13 @@ const popoverContentVariant = cva('', {
|
|||
}
|
||||
});
|
||||
|
||||
export type PopoverContentVariant = VariantProps<typeof popoverContentVariant>;
|
||||
|
||||
const PopoverContent = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {
|
||||
headerContent?: React.ReactNode;
|
||||
} & VariantProps<typeof popoverContentVariant>
|
||||
} & PopoverContentVariant
|
||||
>(
|
||||
(
|
||||
{ className, align = 'center', children, sideOffset = 4, headerContent, size, ...props },
|
||||
|
|
Loading…
Reference in New Issue