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 <thomas.trompette@sfr.fr>
This commit is contained in:
+27
@@ -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 (
|
||||
<ScrollWrapper
|
||||
componentInstanceId={`scroll-wrapper-record-calendar-${recordCalendarId}`}
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const RECORD_CALENDAR_CLICK_OUTSIDE_LISTENER_ID = 'record-calendar';
|
||||
+11
-1
@@ -2,8 +2,11 @@ import { RecordCalendarCardCellEditModePortal } from '@/object-record/record-cal
|
||||
import { RecordCalendarCardCellHoveredPortal } from '@/object-record/record-calendar/record-calendar-card/anchored-portal/components/RecordCalendarCardCellHoveredPortal';
|
||||
import { RecordCalendarCardBody } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardBody';
|
||||
import { RecordCalendarCardHeader } from '@/object-record/record-calendar/record-calendar-card/components/RecordCalendarCardHeader';
|
||||
import { RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-calendar/record-calendar-card/constants/RecordCalendarCardClickOutsideId';
|
||||
import { RecordCalendarCardComponentInstanceContext } from '@/object-record/record-calendar/record-calendar-card/states/contexts/RecordCalendarCardComponentInstanceContext';
|
||||
import { isRecordCalendarCardSelectedComponentFamilyState } from '@/object-record/record-calendar/record-calendar-card/states/isRecordCalendarCardSelectedComponentFamilyState';
|
||||
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
|
||||
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import styled from '@emotion/styled';
|
||||
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
|
||||
@@ -24,6 +27,10 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const isCompactModeActive = currentView?.isCompact ?? false;
|
||||
const isCurrentCardSelected = useRecoilComponentFamilyValue(
|
||||
isRecordCalendarCardSelectedComponentFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
return (
|
||||
<RecordCalendarCardComponentInstanceContext.Provider
|
||||
@@ -32,7 +39,10 @@ export const RecordCalendarCard = ({ recordId }: RecordCalendarCardProps) => {
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<StyledRecordCard>
|
||||
<StyledRecordCard
|
||||
data-selected={isCurrentCardSelected}
|
||||
data-click-outside-id={RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID}
|
||||
>
|
||||
<RecordCalendarCardHeader recordId={recordId} />
|
||||
<AnimatedEaseInOut isOpen={!isCompactModeActive} initial={false}>
|
||||
<RecordCalendarCardBody
|
||||
|
||||
+12
-2
@@ -7,11 +7,13 @@ import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/u
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
|
||||
import { useRecoilComponentFamilyState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyState';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { ChipVariant } from 'twenty-ui/components';
|
||||
import { Checkbox, CheckboxVariant } from 'twenty-ui/input';
|
||||
import { isRecordCalendarCardSelectedComponentFamilyState } from '../states/isRecordCalendarCardSelectedComponentFamilyState';
|
||||
|
||||
const StyledCheckboxContainer = styled.div`
|
||||
margin-left: auto;
|
||||
@@ -46,6 +48,12 @@ export const RecordCalendarCardHeader = ({
|
||||
|
||||
const dragState = useRecordDragState('calendar', viewBarInstanceId);
|
||||
|
||||
const [isCurrentCardSelected, setIsCurrentCardSelected] =
|
||||
useRecoilComponentFamilyState(
|
||||
isRecordCalendarCardSelectedComponentFamilyState,
|
||||
recordId,
|
||||
);
|
||||
|
||||
const handleChipClick = () => {
|
||||
if (dragState.isDragging) {
|
||||
return;
|
||||
@@ -75,8 +83,10 @@ export const RecordCalendarCardHeader = ({
|
||||
<StopPropagationContainer>
|
||||
<Checkbox
|
||||
hoverable
|
||||
checked={false}
|
||||
onChange={() => {}}
|
||||
checked={isCurrentCardSelected}
|
||||
onChange={(value) => {
|
||||
setIsCurrentCardSelected(value.target.checked);
|
||||
}}
|
||||
variant={CheckboxVariant.Secondary}
|
||||
/>
|
||||
</StopPropagationContainer>
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const RECORD_CALENDAR_CARD_CLICK_OUTSIDE_ID = 'record-calendar-card';
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
import { RecordCalendarComponentInstanceContext } from '../../states/contexts/RecordCalendarComponentInstanceContext';
|
||||
|
||||
export const isRecordCalendarCardSelectedComponentFamilyState =
|
||||
createComponentFamilyState<boolean, string>({
|
||||
key: 'isRecordCalendarCardSelectedComponentFamilyState',
|
||||
defaultValue: false,
|
||||
componentInstanceContext: RecordCalendarComponentInstanceContext,
|
||||
});
|
||||
+29
@@ -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<string[]>({
|
||||
key: 'recordCalendarSelectedRecordIdsSelector',
|
||||
componentInstanceContext: RecordCalendarComponentInstanceContext,
|
||||
get:
|
||||
({ instanceId }) =>
|
||||
({ get }) => {
|
||||
const allRecordIds = get(
|
||||
recordIndexAllRecordIdsComponentSelector.selectorFamily({
|
||||
instanceId,
|
||||
}),
|
||||
);
|
||||
|
||||
return allRecordIds.filter(
|
||||
(recordId) =>
|
||||
get(
|
||||
isRecordCalendarCardSelectedComponentFamilyState.atomFamily({
|
||||
instanceId,
|
||||
familyKey: recordId,
|
||||
}),
|
||||
) === true,
|
||||
);
|
||||
},
|
||||
});
|
||||
+62
@@ -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,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user