finalize permission popup

Co-Authored-By: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
Nate Kelley 2025-01-17 10:50:10 -07:00
parent 43c96cbb09
commit 221a4a6280
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
9 changed files with 140 additions and 56 deletions

View File

@ -8,7 +8,6 @@ import {
useIndividualCollection useIndividualCollection
} from '@/context/Collections'; } from '@/context/Collections';
import { Breadcrumb, Button, Dropdown, MenuProps } from 'antd'; import { Breadcrumb, Button, Dropdown, MenuProps } from 'antd';
import { BreadcrumbSeperator } from '@/components';
import Link from 'next/link'; import Link from 'next/link';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout'; import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { BusterRoutes } from '@/routes'; import { BusterRoutes } from '@/routes';
@ -19,10 +18,10 @@ import { ShareMenu } from '../../_components/ShareMenu';
import { BusterCollection } from '@/api/buster-rest/collection'; import { BusterCollection } from '@/api/buster-rest/collection';
import { BusterShareAssetType } from '@/api/buster-rest'; import { BusterShareAssetType } from '@/api/buster-rest';
import { Text } from '@/components'; import { Text } from '@/components';
import { useAntToken } from '@/styles/useAntToken'; import { useAntToken } from '@/styles/useAntToken';
import { measureTextWidth } from '@/utils';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { BreadcrumbSeperator } from '@/components/breadcrumb';
import { measureTextWidth } from '@/utils/canvas';
export const CollectionsIndividualHeader: React.FC<{}> = () => { export const CollectionsIndividualHeader: React.FC<{}> = () => {
const selectedThreadId = useBusterThreadsContextSelector((x) => x.selectedThreadId); const selectedThreadId = useBusterThreadsContextSelector((x) => x.selectedThreadId);

View File

@ -1,11 +1,16 @@
import { BusterListSelectedOptionPopupContainer } from '@/components/list'; import { BusterListSelectedOptionPopupContainer } from '@/components/list';
import { Button } from 'antd'; import { Button, Dropdown, MenuProps } from 'antd';
import React from 'react'; import React, { useMemo } from 'react';
import { PERMISSION_GROUP_ASSIGNED_OPTIONS } from './config';
import { useMemoizedFn } from 'ahooks';
import { useDatasetUpdatePermissionGroups } from '@/api/buster-rest';
import { AppMaterialIcons } from '@/components/icons';
export const PermissionGroupSelectedPopup: React.FC<{ export const PermissionGroupSelectedPopup: React.FC<{
selectedRowKeys: string[]; selectedRowKeys: string[];
datasetId: string;
onSelectChange: (selectedRowKeys: string[]) => void; onSelectChange: (selectedRowKeys: string[]) => void;
}> = React.memo(({ selectedRowKeys, onSelectChange }) => { }> = React.memo(({ selectedRowKeys, onSelectChange, datasetId }) => {
return ( return (
<BusterListSelectedOptionPopupContainer <BusterListSelectedOptionPopupContainer
selectedRowKeys={selectedRowKeys} selectedRowKeys={selectedRowKeys}
@ -14,6 +19,7 @@ export const PermissionGroupSelectedPopup: React.FC<{
<PermissionGroupAssignButton <PermissionGroupAssignButton
key="assign" key="assign"
selectedRowKeys={selectedRowKeys} selectedRowKeys={selectedRowKeys}
datasetId={datasetId}
onSelectChange={onSelectChange} onSelectChange={onSelectChange}
/> />
]} ]}
@ -26,6 +32,45 @@ PermissionGroupSelectedPopup.displayName = 'PermissionGroupSelectedPopup';
const PermissionGroupAssignButton: React.FC<{ const PermissionGroupAssignButton: React.FC<{
selectedRowKeys: string[]; selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void; onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange }) => { datasetId: string;
return <Button>Assign</Button>; }> = ({ selectedRowKeys, onSelectChange, datasetId }) => {
const { mutateAsync: updatePermissionGroups, isPending } =
useDatasetUpdatePermissionGroups(datasetId);
const onClickItem = useMemoizedFn(async (assigned: boolean) => {
try {
await updatePermissionGroups(
selectedRowKeys.map((id) => ({
id,
assigned
}))
);
onSelectChange([]);
} catch (error) {
//
}
});
const menu: MenuProps = useMemo(() => {
return {
items: PERMISSION_GROUP_ASSIGNED_OPTIONS.map((option) => ({
label: option.label,
key: option.value ? 'true' : 'false',
icon: option.value ? (
<AppMaterialIcons icon="done_all" />
) : (
<AppMaterialIcons icon="remove_done" />
),
onClick: () => {
onClickItem(option.value);
}
}))
};
}, []);
return (
<Dropdown menu={menu} trigger={['click']}>
<Button loading={isPending}>Assign</Button>
</Dropdown>
);
}; };

View File

@ -10,6 +10,7 @@ import React, { useMemo, useState } from 'react';
import { Text } from '@/components/text'; import { Text } from '@/components/text';
import { PermissionGroupSelectedPopup } from './PermissionGroupSelectedPopup'; import { PermissionGroupSelectedPopup } from './PermissionGroupSelectedPopup';
import { BusterInfiniteList } from '@/components/list/BusterInfiniteList'; import { BusterInfiniteList } from '@/components/list/BusterInfiniteList';
import { PERMISSION_GROUP_ASSIGNED_OPTIONS } from './config';
export const PermissionListPermissionGroupContainer: React.FC<{ export const PermissionListPermissionGroupContainer: React.FC<{
filteredPermissionGroups: ListPermissionGroupsResponse[]; filteredPermissionGroups: ListPermissionGroupsResponse[];
@ -121,11 +122,13 @@ export const PermissionListPermissionGroupContainer: React.FC<{
selectedRowKeys={selectedRowKeys} selectedRowKeys={selectedRowKeys}
onSelectChange={setSelectedRowKeys} onSelectChange={setSelectedRowKeys}
emptyState={<EmptyState />} emptyState={<EmptyState />}
useRowClickSelectChange={true}
/> />
<div className="fixed bottom-1 left-0 right-0 w-full"> <div className="fixed bottom-0 left-0 right-0 w-full">
<div className="relative ml-[220px] mr-[55px]"> <div className="relative ml-[220px] mr-[55px]">
<PermissionGroupSelectedPopup <PermissionGroupSelectedPopup
datasetId={datasetId}
selectedRowKeys={selectedRowKeys} selectedRowKeys={selectedRowKeys}
onSelectChange={setSelectedRowKeys} onSelectChange={setSelectedRowKeys}
/> />
@ -149,18 +152,7 @@ const PermissionGroupInfoCell = React.memo(({ name }: { name: string }) => {
}); });
PermissionGroupInfoCell.displayName = 'PermissionGroupInfoCell'; PermissionGroupInfoCell.displayName = 'PermissionGroupInfoCell';
const options = [ export const PermissionGroupAssignedCell = React.memo(
{
label: 'Assigned',
value: true
},
{
label: 'Not Assigned',
value: false
}
];
const PermissionGroupAssignedCell = React.memo(
({ ({
id, id,
assigned, assigned,
@ -172,9 +164,13 @@ const PermissionGroupAssignedCell = React.memo(
}) => { }) => {
return ( return (
<Select <Select
options={options} options={PERMISSION_GROUP_ASSIGNED_OPTIONS}
defaultValue={assigned} defaultValue={assigned}
popupMatchSelectWidth popupMatchSelectWidth
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
onSelect={(value) => { onSelect={(value) => {
onSelect({ id, assigned: value }); onSelect({ id, assigned: value });
}} }}

View File

@ -0,0 +1,10 @@
export const PERMISSION_GROUP_ASSIGNED_OPTIONS = [
{
label: 'Assigned',
value: true
},
{
label: 'Not Assigned',
value: false
}
];

View File

@ -2,6 +2,7 @@ import React from 'react';
import { useMemoizedFn } from 'ahooks'; import { useMemoizedFn } from 'ahooks';
import { BusterListProps } from '../BusterList'; import { BusterListProps } from '../BusterList';
import { getAllIdsInSection } from '../BusterList/helpers'; import { getAllIdsInSection } from '../BusterList/helpers';
import { WindowVirtualizer } from 'virtua';
import { useEffect, useMemo, useRef, useCallback } from 'react'; import { useEffect, useMemo, useRef, useCallback } from 'react';
import { BusterListHeader } from '../BusterList/BusterListHeader'; import { BusterListHeader } from '../BusterList/BusterListHeader';
import { BusterListRowComponentSelector } from '../BusterList/BusterListRowComponentSelector'; import { BusterListRowComponentSelector } from '../BusterList/BusterListRowComponentSelector';
@ -19,6 +20,7 @@ export const BusterInfiniteList: React.FC<BusterInfiniteListProps> = ({
onSelectChange, onSelectChange,
emptyState, emptyState,
showHeader = true, showHeader = true,
useRowClickSelectChange = false,
contextMenu, contextMenu,
columnRowVariant = 'containerized', columnRowVariant = 'containerized',
showSelectAll = true, showSelectAll = true,
@ -26,8 +28,6 @@ export const BusterInfiniteList: React.FC<BusterInfiniteListProps> = ({
loadingNewContent, loadingNewContent,
scrollEndThreshold = 200 // Default threshold of 200px scrollEndThreshold = 200 // Default threshold of 200px
}) => { }) => {
const containerRef = useRef<HTMLDivElement>(null);
const showEmptyState = useMemo( const showEmptyState = useMemo(
() => (!rows || rows.length === 0 || !rows.some((row) => !row.rowSection)) && !!emptyState, () => (!rows || rows.length === 0 || !rows.some((row) => !row.rowSection)) && !!emptyState,
[rows, emptyState] [rows, emptyState]
@ -72,12 +72,14 @@ export const BusterInfiniteList: React.FC<BusterInfiniteListProps> = ({
onSelectChange: onSelectChange ? onSelectChangePreflight : undefined, onSelectChange: onSelectChange ? onSelectChangePreflight : undefined,
onSelectSectionChange: onSelectChange ? onSelectSectionChange : undefined, onSelectSectionChange: onSelectChange ? onSelectSectionChange : undefined,
onContextMenuClick: undefined, onContextMenuClick: undefined,
columnRowVariant columnRowVariant,
useRowClickSelectChange
}; };
}, [ }, [
columns, columns,
rows, rows,
onSelectChange, onSelectChange,
useRowClickSelectChange,
columnRowVariant, columnRowVariant,
onSelectSectionChange, onSelectSectionChange,
contextMenu, contextMenu,
@ -86,29 +88,25 @@ export const BusterInfiniteList: React.FC<BusterInfiniteListProps> = ({
// Add scroll handler // Add scroll handler
const handleScroll = useCallback(() => { const handleScroll = useCallback(() => {
if (!containerRef.current || !onScrollEnd) return; // const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
// const distanceToBottom = scrollHeight - scrollTop - clientHeight;
const { scrollTop, scrollHeight, clientHeight } = containerRef.current; // console.log('distanceToBottom', distanceToBottom);
const distanceToBottom = scrollHeight - scrollTop - clientHeight; // if (distanceToBottom <= scrollEndThreshold) {
// onScrollEnd();
if (distanceToBottom <= scrollEndThreshold) { // console.log('onScrollEnd');
onScrollEnd(); // }
}
}, [onScrollEnd, scrollEndThreshold]); }, [onScrollEnd, scrollEndThreshold]);
// Add scroll event listener // Add scroll event listener
useEffect(() => { useEffect(() => {
const container = containerRef.current; // const container = containerRef.current;
if (!container || !onScrollEnd) return; // if (!container || !onScrollEnd) return;
// container.addEventListener('scroll', handleScroll);
container.addEventListener('scroll', handleScroll); // return () => container.removeEventListener('scroll', handleScroll);
return () => container.removeEventListener('scroll', handleScroll);
}, [handleScroll, onScrollEnd]); }, [handleScroll, onScrollEnd]);
return ( return (
<div <div className="infinite-list-container relative flex h-full w-full flex-col">
className="infinite-list-container relative flex h-full w-full flex-col"
ref={containerRef}>
{showHeader && !showEmptyState && ( {showHeader && !showEmptyState && (
<BusterListHeader <BusterListHeader
columns={columns} columns={columns}

View File

@ -18,10 +18,20 @@ export const BusterListRowComponent = React.memo(
onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void; onContextMenuClick?: (e: React.MouseEvent<HTMLDivElement>, id: string) => void;
style?: React.CSSProperties; style?: React.CSSProperties;
columnRowVariant: BusterListProps['columnRowVariant']; columnRowVariant: BusterListProps['columnRowVariant'];
useRowClickSelectChange: boolean;
} }
>( >(
( (
{ style, columnRowVariant, row, columns, onSelectChange, checked, onContextMenuClick }, {
style,
columnRowVariant,
row,
columns,
onSelectChange,
checked,
onContextMenuClick,
useRowClickSelectChange
},
ref ref
) => { ) => {
const { styles, cx } = useStyles(); const { styles, cx } = useStyles();
@ -31,14 +41,21 @@ export const BusterListRowComponent = React.memo(
onContextMenuClick?.(e, row.id); onContextMenuClick?.(e, row.id);
}); });
const onChange = useMemoizedFn((checked: boolean) => { const onChange = useMemoizedFn((newChecked: boolean) => {
onSelectChange?.(checked, row.id); onSelectChange?.(newChecked, row.id);
});
const onContainerClick = useMemoizedFn(() => {
if (useRowClickSelectChange) {
onChange(!checked);
}
row.onClick?.();
}); });
return ( return (
<LinkWrapper href={link}> <LinkWrapper href={link}>
<div <div
onClick={row.onClick} onClick={onContainerClick}
style={style} style={style}
onContextMenu={onContextMenu} onContextMenu={onContextMenu}
className={cx( className={cx(
@ -46,7 +63,7 @@ export const BusterListRowComponent = React.memo(
'group flex items-center', 'group flex items-center',
checked ? 'checked' : '', checked ? 'checked' : '',
columnRowVariant, columnRowVariant,
{ clickable: !!(link || row.onClick) } { clickable: !!(link || row.onClick || (onSelectChange && useRowClickSelectChange)) }
)} )}
ref={ref}> ref={ref}>
{!!onSelectChange ? ( {!!onSelectChange ? (
@ -124,15 +141,14 @@ export const useStyles = createStyles(({ css, token }) => ({
row: css` row: css`
height: ${HEIGHT_OF_ROW}px; height: ${HEIGHT_OF_ROW}px;
min-height: ${HEIGHT_OF_ROW}px; min-height: ${HEIGHT_OF_ROW}px;
border-bottom: 0.5px solid ${token.colorBorder}; border-bottom: 0.5px solid ${token.colorBorder};
&.clickable {
cursor: pointer;
&:hover { &:hover {
background-color: ${token.controlItemBgHover}; background-color: ${token.controlItemBgHover};
} }
&.clickable {
cursor: pointer;
} }
.row-cell { .row-cell {
@ -149,9 +165,15 @@ export const useStyles = createStyles(({ css, token }) => ({
} }
} }
&.containerized { &.containerized:not(.checked) {
background-color: ${token.colorBgContainer}; background-color: ${token.colorBgContainer};
&.clickable {
&:hover {
background-color: ${token.controlItemBgHover};
}
}
&:last-child { &:last-child {
border-bottom: 0px; border-bottom: 0px;
} }

View File

@ -16,6 +16,7 @@ export const BusterListRowComponentSelector = React.forwardRef<
rows: BusterListRow[]; rows: BusterListRow[];
style?: React.CSSProperties; style?: React.CSSProperties;
columnRowVariant?: BusterListProps['columnRowVariant']; columnRowVariant?: BusterListProps['columnRowVariant'];
useRowClickSelectChange: boolean;
} }
>( >(
( (
@ -28,7 +29,8 @@ export const BusterListRowComponentSelector = React.forwardRef<
onSelectSectionChange, onSelectSectionChange,
selectedRowKeys, selectedRowKeys,
onContextMenuClick, onContextMenuClick,
columnRowVariant columnRowVariant,
useRowClickSelectChange = false
}, },
ref ref
) => { ) => {
@ -58,6 +60,7 @@ export const BusterListRowComponentSelector = React.forwardRef<
ref={ref} ref={ref}
onContextMenuClick={onContextMenuClick} onContextMenuClick={onContextMenuClick}
columnRowVariant={columnRowVariant} columnRowVariant={columnRowVariant}
useRowClickSelectChange={useRowClickSelectChange}
/> />
); );
} }

View File

@ -18,7 +18,8 @@ export const BusterListVirtua = React.memo(
emptyState, emptyState,
showHeader = true, showHeader = true,
contextMenu, contextMenu,
showSelectAll = true showSelectAll = true,
useRowClickSelectChange = false
}: BusterListProps) => { }: BusterListProps) => {
const contextMenuRef = useRef<HTMLDivElement>(null); const contextMenuRef = useRef<HTMLDivElement>(null);
const showEmptyState = (!rows || rows.length === 0) && !!emptyState; const showEmptyState = (!rows || rows.length === 0) && !!emptyState;
@ -70,9 +71,18 @@ export const BusterListVirtua = React.memo(
selectedRowKeys, selectedRowKeys,
onSelectChange: onSelectChange ? onSelectChangePreflight : undefined, onSelectChange: onSelectChange ? onSelectChangePreflight : undefined,
onSelectSectionChange: onSelectChange ? onSelectSectionChange : undefined, onSelectSectionChange: onSelectChange ? onSelectSectionChange : undefined,
onContextMenuClick onContextMenuClick,
useRowClickSelectChange
}; };
}, [columns, rows, selectedRowKeys, onSelectChange, onSelectSectionChange, onContextMenuClick]); }, [
columns,
rows,
useRowClickSelectChange,
selectedRowKeys,
onSelectChange,
onSelectSectionChange,
onContextMenuClick
]);
useEffect(() => { useEffect(() => {
if (contextMenu && contextMenuPosition?.show) { if (contextMenu && contextMenuPosition?.show) {

View File

@ -16,6 +16,7 @@ export interface BusterListProps {
selectedRowKeys?: string[]; selectedRowKeys?: string[];
contextMenu?: BusterListContextMenu; contextMenu?: BusterListContextMenu;
showSelectAll?: boolean; showSelectAll?: boolean;
useRowClickSelectChange?: boolean;
} }
export interface BusterListColumn { export interface BusterListColumn {