Fix action menu dropdown (#8368)

Fix action menu dropdown not closing when clicking outside the table or
board and introduce helper functions to get the action menu component
ids.
This commit is contained in:
Raphaël Bosi
2024-11-06 16:11:31 +01:00
committed by GitHub
parent 278ab4c513
commit a6007d4376
21 changed files with 166 additions and 78 deletions

View File

@ -5,6 +5,7 @@ import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIn
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope';
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar'; import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
@ -39,7 +40,7 @@ export const RecordIndexActionMenuBar = () => {
return ( return (
<BottomBar <BottomBar
bottomBarId={`action-bar-${actionMenuId}`} bottomBarId={getActionBarIdFromActionMenuId(actionMenuId)}
bottomBarHotkeyScopeFromParent={{ bottomBarHotkeyScopeFromParent={{
scope: ActionBarHotkeyScope.ActionBar, scope: ActionBarHotkeyScope.ActionBar,
}} }}

View File

@ -7,6 +7,7 @@ import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionM
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope'; import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
@ -47,14 +48,10 @@ export const RecordIndexActionMenuDropdown = () => {
const actionMenuDropdownPosition = useRecoilValue( const actionMenuDropdownPosition = useRecoilValue(
extractComponentState( extractComponentState(
recordIndexActionMenuDropdownPositionComponentState, recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${actionMenuId}`, getActionMenuDropdownIdFromActionMenuId(actionMenuId),
), ),
); );
if (actionMenuEntries.length === 0) {
return null;
}
//TODO: remove this //TODO: remove this
const width = actionMenuEntries.some( const width = actionMenuEntries.some(
(actionMenuEntry) => actionMenuEntry.label === 'Remove from favorites', (actionMenuEntry) => actionMenuEntry.label === 'Remove from favorites',
@ -68,7 +65,7 @@ export const RecordIndexActionMenuDropdown = () => {
className="action-menu-dropdown" className="action-menu-dropdown"
> >
<Dropdown <Dropdown
dropdownId={`action-menu-dropdown-${actionMenuId}`} dropdownId={getActionMenuDropdownIdFromActionMenuId(actionMenuId)}
dropdownHotkeyScope={{ dropdownHotkeyScope={{
scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown, scope: ActionMenuDropdownHotkeyScope.ActionMenuDropdown,
}} }}

View File

@ -1,5 +1,7 @@
import { useActionMenu } from '@/action-menu/hooks/useActionMenu'; import { useActionMenu } from '@/action-menu/hooks/useActionMenu';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState'; import { isCommandMenuOpenedState } from '@/command-menu/states/isCommandMenuOpenedState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState'; import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
@ -27,13 +29,13 @@ export const RecordIndexActionMenuEffect = () => {
// previous hotkey scope, and we don't want that here. // previous hotkey scope, and we don't want that here.
const setIsBottomBarOpened = useSetRecoilComponentStateV2( const setIsBottomBarOpened = useSetRecoilComponentStateV2(
isBottomBarOpenedComponentState, isBottomBarOpenedComponentState,
`action-bar-${actionMenuId}`, getActionBarIdFromActionMenuId(actionMenuId),
); );
const isDropdownOpen = useRecoilValue( const isDropdownOpen = useRecoilValue(
extractComponentState( extractComponentState(
isDropdownOpenComponentState, isDropdownOpenComponentState,
`action-menu-dropdown-${actionMenuId}`, getActionMenuDropdownIdFromActionMenuId(actionMenuId),
), ),
); );
const { isRightDrawerOpen } = useRightDrawer(); const { isRightDrawerOpen } = useRightDrawer();

View File

@ -6,6 +6,7 @@ import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexAc
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState'; import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
@ -64,7 +65,7 @@ const meta: Meta<typeof RecordIndexActionMenuBar> = {
set( set(
isBottomBarOpenedComponentState.atomFamily({ isBottomBarOpenedComponentState.atomFamily({
instanceId: 'action-bar-story-action-menu', instanceId: getActionBarIdFromActionMenuId('story-action-menu'),
}), }),
true, true,
); );

View File

@ -1,3 +1,5 @@
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { renderHook } from '@testing-library/react'; import { renderHook } from '@testing-library/react';
import { act } from 'react'; import { act } from 'react';
import { useActionMenu } from '../useActionMenu'; import { useActionMenu } from '../useActionMenu';
@ -23,6 +25,9 @@ jest.mock('@/ui/layout/dropdown/hooks/useDropdownV2', () => ({
describe('useActionMenu', () => { describe('useActionMenu', () => {
const actionMenuId = 'test-action-menu'; const actionMenuId = 'test-action-menu';
const actionBarId = getActionBarIdFromActionMenuId(actionMenuId);
const actionMenuDropdownId =
getActionMenuDropdownIdFromActionMenuId(actionMenuId);
it('should return the correct functions', () => { it('should return the correct functions', () => {
const { result } = renderHook(() => useActionMenu(actionMenuId)); const { result } = renderHook(() => useActionMenu(actionMenuId));
@ -40,10 +45,8 @@ describe('useActionMenu', () => {
result.current.openActionMenuDropdown(); result.current.openActionMenuDropdown();
}); });
expect(closeBottomBar).toHaveBeenCalledWith(`action-bar-${actionMenuId}`); expect(closeBottomBar).toHaveBeenCalledWith(actionBarId);
expect(openDropdown).toHaveBeenCalledWith( expect(openDropdown).toHaveBeenCalledWith(actionMenuDropdownId);
`action-menu-dropdown-${actionMenuId}`,
);
}); });
it('should call the correct functions when opening action bar', () => { it('should call the correct functions when opening action bar', () => {
@ -53,10 +56,8 @@ describe('useActionMenu', () => {
result.current.openActionBar(); result.current.openActionBar();
}); });
expect(closeDropdown).toHaveBeenCalledWith( expect(closeDropdown).toHaveBeenCalledWith(actionMenuDropdownId);
`action-menu-dropdown-${actionMenuId}`, expect(openBottomBar).toHaveBeenCalledWith(actionBarId);
);
expect(openBottomBar).toHaveBeenCalledWith(`action-bar-${actionMenuId}`);
}); });
it('should call the correct function when closing action menu dropdown', () => { it('should call the correct function when closing action menu dropdown', () => {
@ -66,9 +67,7 @@ describe('useActionMenu', () => {
result.current.closeActionMenuDropdown(); result.current.closeActionMenuDropdown();
}); });
expect(closeDropdown).toHaveBeenCalledWith( expect(closeDropdown).toHaveBeenCalledWith(actionMenuDropdownId);
`action-menu-dropdown-${actionMenuId}`,
);
}); });
it('should call the correct function when closing action bar', () => { it('should call the correct function when closing action bar', () => {
@ -78,6 +77,6 @@ describe('useActionMenu', () => {
result.current.closeActionBar(); result.current.closeActionBar();
}); });
expect(closeBottomBar).toHaveBeenCalledWith(`action-bar-${actionMenuId}`); expect(closeBottomBar).toHaveBeenCalledWith(actionBarId);
}); });
}); });

View File

@ -1,3 +1,5 @@
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { useBottomBar } from '@/ui/layout/bottom-bar/hooks/useBottomBar'; import { useBottomBar } from '@/ui/layout/bottom-bar/hooks/useBottomBar';
import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2';
@ -5,22 +7,26 @@ export const useActionMenu = (actionMenuId: string) => {
const { openDropdown, closeDropdown } = useDropdownV2(); const { openDropdown, closeDropdown } = useDropdownV2();
const { openBottomBar, closeBottomBar } = useBottomBar(); const { openBottomBar, closeBottomBar } = useBottomBar();
const actionBarId = getActionBarIdFromActionMenuId(actionMenuId);
const actionMenuDropdownId =
getActionMenuDropdownIdFromActionMenuId(actionMenuId);
const openActionMenuDropdown = () => { const openActionMenuDropdown = () => {
closeBottomBar(`action-bar-${actionMenuId}`); closeBottomBar(actionBarId);
openDropdown(`action-menu-dropdown-${actionMenuId}`); openDropdown(actionMenuDropdownId);
}; };
const openActionBar = () => { const openActionBar = () => {
closeDropdown(`action-menu-dropdown-${actionMenuId}`); closeDropdown(actionMenuDropdownId);
openBottomBar(`action-bar-${actionMenuId}`); openBottomBar(actionBarId);
}; };
const closeActionMenuDropdown = () => { const closeActionMenuDropdown = () => {
closeDropdown(`action-menu-dropdown-${actionMenuId}`); closeDropdown(actionMenuDropdownId);
}; };
const closeActionBar = () => { const closeActionBar = () => {
closeBottomBar(`action-bar-${actionMenuId}`); closeBottomBar(actionBarId);
}; };
return { return {

View File

@ -0,0 +1,9 @@
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
describe('getActionBarIdFromActionMenuId', () => {
it('should return the correct action bar id', () => {
expect(getActionBarIdFromActionMenuId('action-menu-id')).toBe(
'action-bar-action-menu-id',
);
});
});

View File

@ -0,0 +1,9 @@
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
describe('getActionMenuDropdownIdFromActionMenuId', () => {
it('should return the correct action menu dropdown id', () => {
expect(getActionMenuDropdownIdFromActionMenuId('action-menu-id')).toBe(
'action-menu-dropdown-action-menu-id',
);
});
});

View File

@ -0,0 +1,9 @@
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
describe('getActionMenuIdFromRecordIndexId', () => {
it('should return the correct action menu id', () => {
expect(getActionMenuIdFromRecordIndexId('record-index-id')).toBe(
'action-menu-record-index-record-index-id',
);
});
});

View File

@ -0,0 +1,3 @@
export const getActionBarIdFromActionMenuId = (actionMenuId: string) => {
return `action-bar-${actionMenuId}`;
};

View File

@ -0,0 +1,5 @@
export const getActionMenuDropdownIdFromActionMenuId = (
actionMenuId: string,
) => {
return `action-menu-dropdown-${actionMenuId}`;
};

View File

@ -0,0 +1,3 @@
export const getActionMenuIdFromRecordIndexId = (recordIndexId: string) => {
return `action-menu-record-index-${recordIndexId}`;
};

View File

@ -1,5 +1,7 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
@ -8,14 +10,16 @@ export const useRecordBoardSelection = (recordBoardId: string) => {
const { selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } = const { selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } =
useRecordBoardStates(recordBoardId); useRecordBoardStates(recordBoardId);
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
getActionMenuDropdownIdFromActionMenuId(
getActionMenuIdFromRecordIndexId(recordBoardId),
),
);
const resetRecordSelection = useRecoilCallback( const resetRecordSelection = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
() => { () => {
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
`action-menu-dropdown-${recordBoardId}`,
);
set(isActionMenuDropdownOpenState, false); set(isActionMenuDropdownOpenState, false);
const recordIds = snapshot const recordIds = snapshot
@ -27,7 +31,7 @@ export const useRecordBoardSelection = (recordBoardId: string) => {
} }
}, },
[ [
recordBoardId, isActionMenuDropdownOpenState,
selectedRecordIdsSelector, selectedRecordIdsSelector,
isRecordBoardCardSelectedFamilyState, isRecordBoardCardSelectedFamilyState,
], ],

View File

@ -1,5 +1,7 @@
import { useActionMenu } from '@/action-menu/hooks/useActionMenu'; import { useActionMenu } from '@/action-menu/hooks/useActionMenu';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext'; import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
@ -183,14 +185,19 @@ export const RecordBoardCard = ({
RecordBoardScopeInternalContext, RecordBoardScopeInternalContext,
); );
const actionMenuId = getActionMenuIdFromRecordIndexId(recordBoardId);
const actionMenuDropdownId =
getActionMenuDropdownIdFromActionMenuId(actionMenuId);
const setActionMenuDropdownPosition = useSetRecoilState( const setActionMenuDropdownPosition = useSetRecoilState(
extractComponentState( extractComponentState(
recordIndexActionMenuDropdownPositionComponentState, recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${recordBoardId}`, actionMenuDropdownId,
), ),
); );
const { openActionMenuDropdown } = useActionMenu(recordBoardId); const { openActionMenuDropdown } = useActionMenu(actionMenuId);
const handleActionMenuDropdown = (event: React.MouseEvent) => { const handleActionMenuDropdown = (event: React.MouseEvent) => {
event.preventDefault(); event.preventDefault();

View File

@ -3,19 +3,28 @@ import { useRecoilCallback } from 'recoil';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection'; import { useResetTableRowSelection } from '@/object-record/record-table/hooks/internal/useResetTableRowSelection';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState'; import { isSoftFocusActiveComponentState } from '@/object-record/record-table/states/isSoftFocusActiveComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useDisableSoftFocus } from './useDisableSoftFocus'; import { useDisableSoftFocus } from './useDisableSoftFocus';
export const useLeaveTableFocus = (recordTableId?: string) => { export const useLeaveTableFocus = (recordTableId?: string) => {
const disableSoftFocus = useDisableSoftFocus(recordTableId); const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow(
RecordTableComponentInstanceContext,
const isSoftFocusActiveState = useRecoilComponentCallbackStateV2(
isSoftFocusActiveComponentState,
recordTableId, recordTableId,
); );
const resetTableRowSelection = useResetTableRowSelection(recordTableId); const disableSoftFocus = useDisableSoftFocus(recordTableIdFromContext);
const isSoftFocusActiveState = useRecoilComponentCallbackStateV2(
isSoftFocusActiveComponentState,
recordTableIdFromContext,
);
const resetTableRowSelection = useResetTableRowSelection(
recordTableIdFromContext,
);
return useRecoilCallback( return useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>

View File

@ -1,25 +1,43 @@
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState'; import { tableRowIdsComponentState } from '@/object-record/record-table/states/tableRowIdsComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
export const useResetTableRowSelection = (recordTableId?: string) => { export const useResetTableRowSelection = (recordTableId?: string) => {
const recordTableIdFromContext = useAvailableComponentInstanceIdOrThrow(
RecordTableComponentInstanceContext,
recordTableId,
);
const tableRowIdsState = useRecoilComponentCallbackStateV2( const tableRowIdsState = useRecoilComponentCallbackStateV2(
tableRowIdsComponentState, tableRowIdsComponentState,
recordTableId, recordTableIdFromContext,
); );
const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2(
isRowSelectedComponentFamilyState, isRowSelectedComponentFamilyState,
recordTableId, recordTableIdFromContext,
); );
const hasUserSelectedAllRowsState = useRecoilComponentCallbackStateV2( const hasUserSelectedAllRowsState = useRecoilComponentCallbackStateV2(
hasUserSelectedAllRowsComponentState, hasUserSelectedAllRowsComponentState,
recordTableId, recordTableIdFromContext,
);
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
getActionMenuDropdownIdFromActionMenuId(
getActionMenuIdFromRecordIndexId(recordTableIdFromContext),
),
); );
return useRecoilCallback( return useRecoilCallback(
@ -33,17 +51,12 @@ export const useResetTableRowSelection = (recordTableId?: string) => {
set(hasUserSelectedAllRowsState, false); set(hasUserSelectedAllRowsState, false);
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
`action-menu-dropdown-${recordTableId}`,
);
set(isActionMenuDropdownOpenState, false); set(isActionMenuDropdownOpenState, false);
}, },
[ [
tableRowIdsState, tableRowIdsState,
hasUserSelectedAllRowsState, hasUserSelectedAllRowsState,
recordTableId, isActionMenuDropdownOpenState,
isRowSelectedFamilyState, isRowSelectedFamilyState,
], ],
); );

View File

@ -16,6 +16,7 @@ import { ColumnDefinition } from '../types/ColumnDefinition';
import { TableHotkeyScope } from '../types/TableHotkeyScope'; import { TableHotkeyScope } from '../types/TableHotkeyScope';
import { availableTableColumnsComponentState } from '@/object-record/record-table/states/availableTableColumnsComponentState'; import { availableTableColumnsComponentState } from '@/object-record/record-table/states/availableTableColumnsComponentState';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
import { onColumnsChangeComponentState } from '@/object-record/record-table/states/onColumnsChangeComponentState'; import { onColumnsChangeComponentState } from '@/object-record/record-table/states/onColumnsChangeComponentState';
import { onEntityCountChangeComponentState } from '@/object-record/record-table/states/onEntityCountChangeComponentState'; import { onEntityCountChangeComponentState } from '@/object-record/record-table/states/onEntityCountChangeComponentState';
@ -27,6 +28,7 @@ import { tableFiltersComponentState } from '@/object-record/record-table/states/
import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState'; import { tableLastRowVisibleComponentState } from '@/object-record/record-table/states/tableLastRowVisibleComponentState';
import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState'; import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState';
import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState'; import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useDisableSoftFocus } from './internal/useDisableSoftFocus'; import { useDisableSoftFocus } from './internal/useDisableSoftFocus';
@ -42,7 +44,10 @@ type useRecordTableProps = {
}; };
export const useRecordTable = (props?: useRecordTableProps) => { export const useRecordTable = (props?: useRecordTableProps) => {
const recordTableId = props?.recordTableId; const recordTableId = useAvailableComponentInstanceIdOrThrow(
RecordTableComponentInstanceContext,
props?.recordTableId,
);
const availableTableColumnsState = useRecoilComponentCallbackStateV2( const availableTableColumnsState = useRecoilComponentCallbackStateV2(
availableTableColumnsComponentState, availableTableColumnsComponentState,

View File

@ -16,7 +16,7 @@ export const RecordTableBodyUnselectEffect = ({
tableBodyRef, tableBodyRef,
recordTableId, recordTableId,
}: RecordTableBodyUnselectEffectProps) => { }: RecordTableBodyUnselectEffectProps) => {
const leaveTableFocus = useLeaveTableFocus(); const leaveTableFocus = useLeaveTableFocus(recordTableId);
const { resetTableRowSelection, useMapKeyboardToSoftFocus } = useRecordTable({ const { resetTableRowSelection, useMapKeyboardToSoftFocus } = useRecordTable({
recordTableId, recordTableId,

View File

@ -2,6 +2,8 @@ import { useRecoilCallback } from 'recoil';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState'; import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { getActionBarIdFromActionMenuId } from '@/action-menu/utils/getActionBarIdFromActionMenuId';
import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState'; import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
@ -24,21 +26,29 @@ export const useTriggerActionMenuDropdown = ({
recordTableId, recordTableId,
); );
const recordIndexActionMenuDropdownPositionState = extractComponentState(
recordIndexActionMenuDropdownPositionComponentState,
getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId),
);
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
getActionMenuDropdownIdFromActionMenuId(actionMenuInstanceId),
);
const isActionBarOpenState = isBottomBarOpenedComponentState.atomFamily({
instanceId: getActionBarIdFromActionMenuId(actionMenuInstanceId),
});
const triggerActionMenuDropdown = useRecoilCallback( const triggerActionMenuDropdown = useRecoilCallback(
({ set, snapshot }) => ({ set, snapshot }) =>
(event: React.MouseEvent, recordId: string) => { (event: React.MouseEvent, recordId: string) => {
event.preventDefault(); event.preventDefault();
set( set(recordIndexActionMenuDropdownPositionState, {
extractComponentState( x: event.clientX,
recordIndexActionMenuDropdownPositionComponentState, y: event.clientY,
`action-menu-dropdown-${actionMenuInstanceId}`, });
),
{
x: event.clientX,
y: event.clientY,
},
);
const isRowSelected = getSnapshotValue( const isRowSelected = getSnapshotValue(
snapshot, snapshot,
@ -49,21 +59,15 @@ export const useTriggerActionMenuDropdown = ({
set(isRowSelectedFamilyState(recordId), true); set(isRowSelectedFamilyState(recordId), true);
} }
const isActionMenuDropdownOpenState = extractComponentState(
isDropdownOpenComponentState,
`action-menu-dropdown-${actionMenuInstanceId}`,
);
const isActionBarOpenState = isBottomBarOpenedComponentState.atomFamily(
{
instanceId: `action-bar-${actionMenuInstanceId}`,
},
);
set(isActionBarOpenState, false); set(isActionBarOpenState, false);
set(isActionMenuDropdownOpenState, true); set(isActionMenuDropdownOpenState, true);
}, },
[actionMenuInstanceId, isRowSelectedFamilyState], [
isActionBarOpenState,
isActionMenuDropdownOpenState,
isRowSelectedFamilyState,
recordIndexActionMenuDropdownPositionState,
],
); );
return { triggerActionMenuDropdown }; return { triggerActionMenuDropdown };

View File

@ -15,13 +15,13 @@ import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect'; import { HotkeyEffect } from '@/ui/utilities/hotkey/components/HotkeyEffect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
import { useDropdown } from '../hooks/useDropdown'; import { useDropdown } from '../hooks/useDropdown';
import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement'; import { useInternalHotkeyScopeManagement } from '../hooks/useInternalHotkeyScopeManagement';
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
import { DropdownMenu } from './DropdownMenu'; import { DropdownMenu } from './DropdownMenu';
import { DropdownOnToggleEffect } from './DropdownOnToggleEffect'; import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
@ -112,8 +112,9 @@ export const Dropdown = ({
onClickOutside?.(); onClickOutside?.();
}; };
useListenClickOutside({ useListenClickOutsideV2({
refs: [refs.floating], refs: [refs.floating],
listenerId: dropdownId,
callback: () => { callback: () => {
onClickOutside?.(); onClickOutside?.();

View File

@ -2,6 +2,7 @@ import styled from '@emotion/styled';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { MainContextStoreComponentInstanceIdSetterEffect } from '@/context-store/components/MainContextStoreComponentInstanceIdSetterEffect'; import { MainContextStoreComponentInstanceIdSetterEffect } from '@/context-store/components/MainContextStoreComponentInstanceIdSetterEffect';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
@ -82,12 +83,12 @@ export const RecordIndexPage = () => {
<StyledIndexContainer> <StyledIndexContainer>
<ContextStoreComponentInstanceContext.Provider <ContextStoreComponentInstanceContext.Provider
value={{ value={{
instanceId: `record-index-${objectMetadataItem.id}`, instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
}} }}
> >
<ActionMenuComponentInstanceContext.Provider <ActionMenuComponentInstanceContext.Provider
value={{ value={{
instanceId: `record-index-${objectMetadataItem.id}`, instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
}} }}
> >
<RecordIndexContainerContextStoreObjectMetadataEffect /> <RecordIndexContainerContextStoreObjectMetadataEffect />