refactor: add ViewBar and move view components to ui/view-bar (#1495)

Closes #1494
This commit is contained in:
Thaïs
2023-09-08 11:57:16 +02:00
committed by GitHub
parent ccb57c91a3
commit df17da80fc
22 changed files with 325 additions and 376 deletions

View File

@ -8,15 +8,16 @@ import {
useListenClickOutsideByClassName,
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { SortType } from '@/ui/view-bar/types/interface';
import type { View } from '@/ui/view-bar/types/View';
import { EntityUpdateMutationContext } from '../contexts/EntityUpdateMutationHookContext';
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus';
import { useResetTableRowSelection } from '../hooks/useResetTableRowSelection';
import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState';
import { TableHeader } from '../table-header/components/TableHeader';
import {
TableHeader,
type TableHeaderProps,
} from '../table-header/components/TableHeader';
import { TableHotkeyScope } from '../types/TableHotkeyScope';
import { EntityTableBody } from './EntityTableBody';
@ -85,21 +86,22 @@ const StyledTableContainer = styled.div`
`;
type OwnProps<SortField> = {
viewName: string;
viewIcon?: React.ReactNode;
availableSorts?: Array<SortType<SortField>>;
onViewsChange?: (views: View[]) => void;
onViewSubmit?: () => void;
onImport?: () => void;
updateEntityMutation: any;
};
} & Pick<
TableHeaderProps<SortField>,
| 'availableSorts'
| 'defaultViewName'
| 'onImport'
| 'onViewsChange'
| 'onViewSubmit'
>;
export function EntityTable<SortField>({
viewName,
availableSorts,
defaultViewName,
onImport,
onViewsChange,
onViewSubmit,
onImport,
updateEntityMutation,
}: OwnProps<SortField>) {
const tableBodyRef = useRef<HTMLDivElement>(null);
@ -139,11 +141,11 @@ export function EntityTable<SortField>({
<StyledTableWithHeader>
<StyledTableContainer ref={tableBodyRef}>
<TableHeader
viewName={viewName}
availableSorts={availableSorts}
availableSorts={availableSorts ?? []}
defaultViewName={defaultViewName}
onImport={onImport}
onViewsChange={onViewsChange}
onViewSubmit={onViewSubmit}
onImport={onImport}
/>
<ScrollWrapper>
<div>

View File

@ -2,6 +2,8 @@ import { DropdownButton } from '@/ui/dropdown/components/DropdownButton';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import type { View } from '@/ui/view-bar/types/View';
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
@ -20,7 +22,7 @@ export function TableOptionsDropdown({
<DropdownButton
buttonComponents={<TableOptionsDropdownButton />}
dropdownHotkeyScope={customHotkeyScope}
dropdownKey="options"
dropdownKey={TableOptionsDropdownKey}
dropdownComponents={
<TableOptionsDropdownContent
onImport={onImport}

View File

@ -1,9 +1,11 @@
import { StyledHeaderDropdownButton } from '@/ui/dropdown/components/StyledHeaderDropdownButton';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
export function TableOptionsDropdownButton() {
const { isDropdownButtonOpen, toggleDropdownButton } = useDropdownButton({
key: 'options',
key: TableOptionsDropdownKey,
});
return (

View File

@ -29,6 +29,7 @@ import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/Tabl
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
import { TableOptionsDropdownColumnVisibility } from './TableOptionsDropdownSection';
@ -48,7 +49,9 @@ export function TableOptionsDropdownContent({
}: TableOptionsDropdownButtonProps) {
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
const { closeDropdownButton } = useDropdownButton({ key: 'options' });
const { closeDropdownButton } = useDropdownButton({
key: TableOptionsDropdownKey,
});
const [selectedOption, setSelectedOption] = useState<Option | undefined>(
undefined,

View File

@ -1,159 +0,0 @@
import { useCallback, useState } from 'react';
import styled from '@emotion/styled';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
import { Button } from '@/ui/button/components/Button';
import { ButtonGroup } from '@/ui/button/components/ButtonGroup';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { IconChevronDown, IconPlus } from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { DropdownMenuContainer } from '@/ui/view-bar/components/DropdownMenuContainer';
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
import { savedSortsFamilyState } from '@/ui/view-bar/states/savedSortsFamilyState';
import { canPersistFiltersScopedFamilySelector } from '@/ui/view-bar/states/selectors/canPersistFiltersScopedFamilySelector';
import { canPersistSortsScopedFamilySelector } from '@/ui/view-bar/states/selectors/canPersistSortsScopedFamilySelector';
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
import { canPersistTableColumnsScopedFamilySelector } from '../../states/selectors/canPersistTableColumnsScopedFamilySelector';
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
const StyledContainer = styled.div`
display: inline-flex;
margin-right: ${({ theme }) => theme.spacing(2)};
position: relative;
`;
type TableUpdateViewButtonGroupProps = {
onViewSubmit?: () => void;
HotkeyScope: string;
};
export const TableUpdateViewButtonGroup = ({
onViewSubmit,
HotkeyScope,
}: TableUpdateViewButtonGroupProps) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
const currentViewId = useRecoilScopedValue(
currentViewIdScopedState,
TableRecoilScopeContext,
);
const tableColumns = useRecoilScopedValue(
tableColumnsScopedState,
TableRecoilScopeContext,
);
const setSavedColumns = useSetRecoilState(
savedTableColumnsFamilyState(currentViewId),
);
const canPersistColumns = useRecoilValue(
canPersistTableColumnsScopedFamilySelector([tableScopeId, currentViewId]),
);
const filters = useRecoilScopedValue(
filtersScopedState,
TableRecoilScopeContext,
);
const setSavedFilters = useSetRecoilState(
savedFiltersFamilyState(currentViewId),
);
const canPersistFilters = useRecoilValue(
canPersistFiltersScopedFamilySelector([tableScopeId, currentViewId]),
);
const sorts = useRecoilScopedValue(sortsScopedState, TableRecoilScopeContext);
const setSavedSorts = useSetRecoilState(savedSortsFamilyState(currentViewId));
const canPersistSorts = useRecoilValue(
canPersistSortsScopedFamilySelector([tableScopeId, currentViewId]),
);
const setViewEditMode = useSetRecoilState(viewEditModeState);
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
key: 'options',
});
const handleArrowDownButtonClick = useCallback(() => {
setIsDropdownOpen((previousIsOpen) => !previousIsOpen);
}, []);
const handleCreateViewButtonClick = useCallback(() => {
setViewEditMode({ mode: 'create', viewId: undefined });
openOptionsDropdownButton();
setIsDropdownOpen(false);
}, [setViewEditMode, openOptionsDropdownButton]);
const handleDropdownClose = useCallback(() => {
setIsDropdownOpen(false);
}, []);
const handleViewSubmit = useCallback(async () => {
if (canPersistColumns) setSavedColumns(tableColumns);
if (canPersistFilters) setSavedFilters(filters);
if (canPersistSorts) setSavedSorts(sorts);
await Promise.resolve(onViewSubmit?.());
}, [
canPersistColumns,
canPersistFilters,
canPersistSorts,
filters,
onViewSubmit,
setSavedColumns,
setSavedFilters,
setSavedSorts,
sorts,
tableColumns,
]);
useScopedHotkeys(
[Key.Enter, Key.Escape],
handleDropdownClose,
HotkeyScope,
[],
);
return (
<StyledContainer>
<ButtonGroup size="small" accent="blue">
<Button
title="Update view"
disabled={
!currentViewId ||
(!canPersistColumns && !canPersistFilters && !canPersistSorts)
}
onClick={handleViewSubmit}
/>
<Button
size="small"
icon={<IconChevronDown />}
onClick={handleArrowDownButtonClick}
/>
</ButtonGroup>
{isDropdownOpen && (
<DropdownMenuContainer onClose={handleDropdownClose}>
<StyledDropdownMenuItemsContainer>
<MenuItem
onClick={handleCreateViewButtonClick}
LeftIcon={IconPlus}
text="Create view"
/>
</StyledDropdownMenuItemsContainer>
</DropdownMenuContainer>
)}
</StyledContainer>
);
};

View File

@ -1,229 +0,0 @@
import { type MouseEvent, useCallback, useEffect, useState } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { StyledDropdownMenuSeparator } from '@/ui/dropdown/components/StyledDropdownMenuSeparator';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import {
IconChevronDown,
IconList,
IconPencil,
IconPlus,
IconTrash,
} from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/theme';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import DropdownButton from '@/ui/view-bar/components/DropdownButton';
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
import { savedFiltersFamilyState } from '@/ui/view-bar/states/savedFiltersFamilyState';
import { savedSortsFamilyState } from '@/ui/view-bar/states/savedSortsFamilyState';
import { currentViewScopedSelector } from '@/ui/view-bar/states/selectors/currentViewScopedSelector';
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import { viewsScopedState } from '@/ui/view-bar/states/viewsScopedState';
import type { View } from '@/ui/view-bar/types/View';
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
import { assertNotNull } from '~/utils/assert';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
const StyledBoldDropdownMenuItemsContainer = styled(
StyledDropdownMenuItemsContainer,
)`
font-weight: ${({ theme }) => theme.font.weight.regular};
`;
const StyledDropdownLabelAdornments = styled.span`
align-items: center;
color: ${({ theme }) => theme.grayScale.gray35};
display: inline-flex;
gap: ${({ theme }) => theme.spacing(1)};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
const StyledViewIcon = styled(IconList)`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
const StyledViewName = styled.span`
display: inline-block;
max-width: 200px;
@media (max-width: 375px) {
max-width: 90px;
}
@media (min-width: 376px) and (max-width: ${MOBILE_VIEWPORT}px) {
max-width: 110px;
}
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
`;
type TableViewsDropdownButtonProps = {
defaultViewName: string;
HotkeyScope: ViewsHotkeyScope;
onViewsChange?: (views: View[]) => void;
};
export const TableViewsDropdownButton = ({
defaultViewName,
HotkeyScope,
onViewsChange,
}: TableViewsDropdownButtonProps) => {
const theme = useTheme();
const [isUnfolded, setIsUnfolded] = useState(false);
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
const { openDropdownButton: openOptionsDropdownButton } = useDropdownButton({
key: 'options',
});
const [, setCurrentViewId] = useRecoilScopedState(
currentViewIdScopedState,
TableRecoilScopeContext,
);
const currentView = useRecoilScopedValue(
currentViewScopedSelector,
TableRecoilScopeContext,
);
const [views, setViews] = useRecoilScopedState(
viewsScopedState,
TableRecoilScopeContext,
);
const setViewEditMode = useSetRecoilState(viewEditModeState);
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const handleViewSelect = useRecoilCallback(
({ set, snapshot }) =>
async (viewId: string) => {
const savedColumns = await snapshot.getPromise(
savedTableColumnsFamilyState(viewId),
);
const savedFilters = await snapshot.getPromise(
savedFiltersFamilyState(viewId),
);
const savedSorts = await snapshot.getPromise(
savedSortsFamilyState(viewId),
);
set(tableColumnsScopedState(tableScopeId), savedColumns);
set(filtersScopedState(tableScopeId), savedFilters);
set(sortsScopedState(tableScopeId), savedSorts);
set(currentViewIdScopedState(tableScopeId), viewId);
setIsUnfolded(false);
},
[tableScopeId],
);
const handleAddViewButtonClick = useCallback(() => {
setViewEditMode({ mode: 'create', viewId: undefined });
openOptionsDropdownButton();
setIsUnfolded(false);
}, [setViewEditMode, openOptionsDropdownButton]);
const handleEditViewButtonClick = useCallback(
(event: MouseEvent<HTMLButtonElement>, viewId: string) => {
event.stopPropagation();
setViewEditMode({ mode: 'edit', viewId });
openOptionsDropdownButton();
setIsUnfolded(false);
},
[setViewEditMode, openOptionsDropdownButton],
);
const handleDeleteViewButtonClick = useCallback(
async (event: MouseEvent<HTMLButtonElement>, viewId: string) => {
event.stopPropagation();
if (currentView?.id === viewId) setCurrentViewId(undefined);
const nextViews = views.filter((view) => view.id !== viewId);
setViews(nextViews);
await Promise.resolve(onViewsChange?.(nextViews));
setIsUnfolded(false);
},
[currentView?.id, onViewsChange, setCurrentViewId, setViews, views],
);
useEffect(() => {
isUnfolded
? setHotkeyScopeAndMemorizePreviousScope(HotkeyScope)
: goBackToPreviousHotkeyScope();
}, [
HotkeyScope,
goBackToPreviousHotkeyScope,
isUnfolded,
setHotkeyScopeAndMemorizePreviousScope,
]);
return (
<DropdownButton
label={
<>
<StyledViewIcon size={theme.icon.size.md} />
<StyledViewName>
{currentView?.name || defaultViewName}
</StyledViewName>
<StyledDropdownLabelAdornments>
· {views.length} <IconChevronDown size={theme.icon.size.sm} />
</StyledDropdownLabelAdornments>
</>
}
isActive={false}
isUnfolded={isUnfolded}
onIsUnfoldedChange={setIsUnfolded}
anchor="left"
HotkeyScope={HotkeyScope}
menuWidth="auto"
>
<StyledDropdownMenuItemsContainer>
{views.map((view) => (
<MenuItem
key={view.id}
iconButtons={[
{
Icon: IconPencil,
onClick: (event: MouseEvent<HTMLButtonElement>) =>
handleEditViewButtonClick(event, view.id),
},
views.length > 1
? {
Icon: IconTrash,
onClick: (event: MouseEvent<HTMLButtonElement>) =>
handleDeleteViewButtonClick(event, view.id),
}
: null,
].filter(assertNotNull)}
onClick={() => handleViewSelect(view.id)}
LeftIcon={IconList}
text={view.name}
/>
))}
</StyledDropdownMenuItemsContainer>
<StyledDropdownMenuSeparator />
<StyledBoldDropdownMenuItemsContainer>
<MenuItem
onClick={handleAddViewButtonClick}
LeftIcon={IconPlus}
text="Add view"
/>
</StyledBoldDropdownMenuItemsContainer>
</DropdownButton>
);
};

View File

@ -1,151 +1,90 @@
import { useCallback } from 'react';
import { useRecoilValue } from 'recoil';
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { TopBar } from '@/ui/top-bar/TopBar';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { FilterDropdownButton } from '@/ui/view-bar/components/FilterDropdownButton';
import { SortDropdownButton } from '@/ui/view-bar/components/SortDropdownButton';
import ViewBarDetails from '@/ui/view-bar/components/ViewBarDetails';
import { ViewBar, type ViewBarProps } from '@/ui/view-bar/components/ViewBar';
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
import { canPersistFiltersScopedFamilySelector } from '@/ui/view-bar/states/selectors/canPersistFiltersScopedFamilySelector';
import { canPersistSortsScopedFamilySelector } from '@/ui/view-bar/states/selectors/canPersistSortsScopedFamilySelector';
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
import { FiltersHotkeyScope } from '@/ui/view-bar/types/FiltersHotkeyScope';
import { SelectedSortType, SortType } from '@/ui/view-bar/types/interface';
import type { View } from '@/ui/view-bar/types/View';
import { ViewsHotkeyScope } from '@/ui/view-bar/types/ViewsHotkeyScope';
import { TableOptionsDropdown } from '../../options/components/TableOptionsDropdown';
import { TableUpdateViewButtonGroup } from '../../options/components/TableUpdateViewButtonGroup';
import { TableViewsDropdownButton } from '../../options/components/TableViewsDropdownButton';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
import { canPersistTableColumnsScopedFamilySelector } from '../../states/selectors/canPersistTableColumnsScopedFamilySelector';
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
type OwnProps<SortField> = {
viewName: string;
availableSorts?: Array<SortType<SortField>>;
onViewsChange?: (views: View[]) => void;
onViewSubmit?: () => void;
export type TableHeaderProps<SortField> = {
onImport?: () => void;
};
} & Pick<
ViewBarProps<SortField>,
'availableSorts' | 'defaultViewName' | 'onViewsChange' | 'onViewSubmit'
>;
export function TableHeader<SortField>({
viewName,
availableSorts,
onImport,
onViewsChange,
onViewSubmit,
onImport,
}: OwnProps<SortField>) {
...props
}: TableHeaderProps<SortField>) {
const tableScopeId = useContextScopeId(TableRecoilScopeContext);
const currentViewId = useRecoilScopedValue(
currentViewIdScopedState,
TableRecoilScopeContext,
);
const [sorts, setSorts] = useRecoilScopedState<SelectedSortType<SortField>[]>(
sortsScopedState,
TableRecoilScopeContext,
);
const canPersistTableColumns = useRecoilValue(
canPersistTableColumnsScopedFamilySelector([tableScopeId, currentViewId]),
);
const canPersistFilters = useRecoilValue(
canPersistFiltersScopedFamilySelector([tableScopeId, currentViewId]),
const tableColumns = useRecoilScopedValue(
tableColumnsScopedState,
TableRecoilScopeContext,
);
const setSavedTableColumns = useSetRecoilState(
savedTableColumnsFamilyState(currentViewId),
);
const canPersistSorts = useRecoilValue(
canPersistSortsScopedFamilySelector([tableScopeId, currentViewId]),
const handleViewSelect = useRecoilCallback(
({ set, snapshot }) =>
async (viewId: string) => {
const savedTableColumns = await snapshot.getPromise(
savedTableColumnsFamilyState(viewId),
);
set(tableColumnsScopedState(tableScopeId), savedTableColumns);
},
[tableScopeId],
);
const sortSelect = useCallback(
(newSort: SelectedSortType<SortField>) => {
const newSorts = updateSortOrFilterByKey(sorts, newSort);
setSorts(newSorts);
},
[setSorts, sorts],
);
const handleViewSubmit = async () => {
if (canPersistTableColumns) setSavedTableColumns(tableColumns);
const sortUnselect = useCallback(
(sortKey: string) => {
const newSorts = sorts.filter((sort) => sort.key !== sortKey);
setSorts(newSorts);
},
[setSorts, sorts],
await onViewSubmit?.();
};
const OptionsDropdownButton = useCallback(
() => (
<TableOptionsDropdown
onImport={onImport}
onViewsChange={onViewsChange}
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
/>
),
[onImport, onViewsChange],
);
return (
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
<TopBar
leftComponent={
<TableViewsDropdownButton
defaultViewName={viewName}
onViewsChange={onViewsChange}
HotkeyScope={ViewsHotkeyScope.ListDropdown}
/>
}
displayBottomBorder={false}
rightComponent={
<>
<FilterDropdownButton
context={TableRecoilScopeContext}
HotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
isPrimaryButton
/>
<SortDropdownButton<SortField>
context={TableRecoilScopeContext}
isSortSelected={sorts.length > 0}
availableSorts={availableSorts || []}
onSortSelect={sortSelect}
HotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
isPrimaryButton
/>
<TableOptionsDropdown
onImport={onImport}
onViewsChange={onViewsChange}
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
/>
</>
}
bottomComponent={
<ViewBarDetails
canPersistView={
canPersistTableColumns || canPersistFilters || canPersistSorts
}
context={TableRecoilScopeContext}
sorts={sorts}
onRemoveSort={sortUnselect}
onCancelClick={() => setSorts([])}
hasFilterButton
rightComponent={
<TableUpdateViewButtonGroup
onViewSubmit={onViewSubmit}
HotkeyScope={ViewsHotkeyScope.CreateDropdown}
/>
}
/>
}
<ViewBar
{...props}
canPersistViewFields={canPersistTableColumns}
onViewSelect={handleViewSelect}
onViewSubmit={handleViewSubmit}
OptionsDropdownButton={OptionsDropdownButton}
optionsDropdownKey={TableOptionsDropdownKey}
scopeContext={TableRecoilScopeContext}
/>
</RecoilScope>
);
}
function updateSortOrFilterByKey<SortOrFilter extends { key: string }>(
sorts: Readonly<SortOrFilter[]>,
newSort: SortOrFilter,
): SortOrFilter[] {
const newSorts = [...sorts];
const existingSortIndex = sorts.findIndex((sort) => sort.key === newSort.key);
if (existingSortIndex !== -1) {
newSorts[existingSortIndex] = newSort;
} else {
newSorts.push(newSort);
}
return newSorts;
}

View File

@ -0,0 +1 @@
export const TableOptionsDropdownKey = 'table-options';