From 8b2e43c448afb7d02e5186ab53c2f4e9da29653f Mon Sep 17 00:00:00 2001 From: Saurav Jain Date: Tue, 30 Sep 2025 22:18:49 +0530 Subject: [PATCH] Added all the initial selection logic (#14789) Added initial implementation of calendar card selection logic --- Video -> [Screencast from 2025-09-30 19-50-56.webm](https://github.com/user-attachments/assets/757e5dbe-9936-4633-9178-d42a20401b82) --- @Weiko --------- Co-authored-by: Thomas Trompette --- .../components/RecordCalendar.tsx | 27 ++++++++ .../RecordCalendarClickOutsideListenerId.ts | 1 + .../components/RecordCalendarCard.tsx | 12 +++- .../components/RecordCalendarCardHeader.tsx | 14 ++++- .../RecordCalendarCardClickOutsideId.ts | 1 + ...alendarCardSelectedComponentFamilyState.ts | 9 +++ ...endarSelectedRecordIdsComponentSelector.ts | 29 +++++++++ .../selectors/useRecordCalendarSelection.ts | 62 +++++++++++++++++++ 8 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-calendar/constants/RecordCalendarClickOutsideListenerId.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/useRecordCalendarSelection.ts diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordCalendar.tsx b/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordCalendar.tsx index f1404692df..6fdb16ec0c 100644 --- a/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordCalendar.tsx +++ b/packages/twenty-front/src/modules/object-record/record-calendar/components/RecordCalendar.tsx @@ -5,6 +5,15 @@ import { RecordCalendarMonth } from '@/object-record/record-calendar/month/compo import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/action-menu/constants/ActionMenuDropdownClickOutsideId'; +import { COMMAND_MENU_CLICK_OUTSIDE_ID } from '@/command-menu/constants/CommandMenuClickOutsideId'; +import { MODAL_BACKDROP_CLICK_OUTSIDE_ID } from '@/ui/layout/modal/constants/ModalBackdropClickOutsideId'; +import { PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID } from '@/ui/layout/page/constants/PageActionContainerClickOutsideId'; +import { RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId'; +import { LINK_CHIP_CLICK_OUTSIDE_ID } from 'twenty-ui/components'; +import { useRecordCalendarSelection } from '@/object-record/record-calendar/states/selectors/useRecordCalendarSelection'; +import { RECORD_CALENDAR_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-calendar/constants/RecordCalendarClickOutsideListenerId'; const StyledContainerContainer = styled.div` display: flex; @@ -20,6 +29,24 @@ export const RecordCalendar = () => { RecordCalendarComponentInstanceContext, ); + const { resetRecordSelection } = useRecordCalendarSelection(recordCalendarId); + + useListenClickOutside({ + excludedClickOutsideIds: [ + ACTION_MENU_DROPDOWN_CLICK_OUTSIDE_ID, + COMMAND_MENU_CLICK_OUTSIDE_ID, + MODAL_BACKDROP_CLICK_OUTSIDE_ID, + PAGE_ACTION_CONTAINER_CLICK_OUTSIDE_ID, + RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID, + LINK_CHIP_CLICK_OUTSIDE_ID, + ], + listenerId: RECORD_CALENDAR_CLICK_OUTSIDE_LISTENER_ID, + refs: [], + callback: () => { + resetRecordSelection(); + }, + }); + return ( { const { currentView } = useGetCurrentViewOnly(); const isCompactModeActive = currentView?.isCompact ?? false; + const isCurrentCardSelected = useRecoilComponentFamilyValue( + isRecordCalendarCardSelectedComponentFamilyState, + recordId, + ); return ( { }} > - + { if (dragState.isDragging) { return; @@ -75,8 +83,10 @@ export const RecordCalendarCardHeader = ({ {}} + checked={isCurrentCardSelected} + onChange={(value) => { + setIsCurrentCardSelected(value.target.checked); + }} variant={CheckboxVariant.Secondary} /> diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId.ts b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId.ts new file mode 100644 index 0000000000..93dd269579 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId.ts @@ -0,0 +1 @@ +export const RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID = 'record-calendar-card'; diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState.ts new file mode 100644 index 0000000000..c8c4dc1d10 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState.ts @@ -0,0 +1,9 @@ +import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; +import { RecordCalendarComponentInstanceContext } from '../../states/contexts/RecordCalendarComponentInstanceContext'; + +export const isRecordCalendarCardSelectedComponentFamilyState = + createComponentFamilyState({ + key: 'isRecordCalendarCardSelectedComponentFamilyState', + defaultValue: false, + componentInstanceContext: RecordCalendarComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector.ts new file mode 100644 index 0000000000..cfc87e6eb6 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/recordCalendarSelectedRecordIdsComponentSelector.ts @@ -0,0 +1,29 @@ +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; +import { createComponentSelector } from '@/ui/utilities/state/component-state/utils/createComponentSelector'; +import { RecordCalendarComponentInstanceContext } from '../contexts/RecordCalendarComponentInstanceContext'; +import { isRecordCalendarCardSelectedComponentFamilyState } from '../../record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState'; + +export const recordCalendarSelectedRecordIdsComponentSelector = + createComponentSelector({ + key: 'recordCalendarSelectedRecordIdsSelector', + componentInstanceContext: RecordCalendarComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const allRecordIds = get( + recordIndexAllRecordIdsComponentSelector.selectorFamily({ + instanceId, + }), + ); + + return allRecordIds.filter( + (recordId) => + get( + isRecordCalendarCardSelectedComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordId, + }), + ) === true, + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/useRecordCalendarSelection.ts b/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/useRecordCalendarSelection.ts new file mode 100644 index 0000000000..f3d2a36642 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-calendar/states/selectors/useRecordCalendarSelection.ts @@ -0,0 +1,62 @@ +import { useRecoilCallback } from 'recoil'; + +import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; +import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { isRecordCalendarCardSelectedComponentFamilyState } from '../../record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState'; +import { RecordCalendarComponentInstanceContext } from '../contexts/RecordCalendarComponentInstanceContext'; +import { recordCalendarSelectedRecordIdsComponentSelector } from './recordCalendarSelectedRecordIdsComponentSelector'; + +export const useRecordCalendarSelection = (recordCalendarId?: string) => { + const instanceIdFromProps = useAvailableComponentInstanceIdOrThrow( + RecordCalendarComponentInstanceContext, + recordCalendarId, + ); + + const isRecordCalendarCardSelectedFamilyState = + useRecoilComponentCallbackState( + isRecordCalendarCardSelectedComponentFamilyState, + recordCalendarId, + ); + + const recordCalendarSelectedRecordIdsSelector = + useRecoilComponentCallbackState( + recordCalendarSelectedRecordIdsComponentSelector, + recordCalendarId, + ); + + const { closeDropdown } = useCloseDropdown(); + + const dropdownId = getActionMenuDropdownIdFromActionMenuId( + getActionMenuIdFromRecordIndexId(instanceIdFromProps), + ); + + const resetRecordSelection = useRecoilCallback( + ({ snapshot, set }) => + () => { + closeDropdown(dropdownId); + + const recordIds = getSnapshotValue( + snapshot, + recordCalendarSelectedRecordIdsSelector, + ); + + for (const recordId of recordIds) { + set(isRecordCalendarCardSelectedFamilyState(recordId), false); + } + }, + [ + closeDropdown, + dropdownId, + recordCalendarSelectedRecordIdsSelector, + isRecordCalendarCardSelectedFamilyState, + ], + ); + + return { + resetRecordSelection, + }; +};