add date picker on calendar view (#14511)
<img width="1280" height="352" alt="Screenshot 2025-09-15 at 16 34 50" src="https://github.com/user-attachments/assets/b3108030-24e2-46d7-be3d-68a9095d79c6" />
This commit is contained in:
+126
-21
@@ -1,14 +1,42 @@
|
||||
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { recordIndexCalendarLayoutState } from '@/object-record/record-index/states/recordIndexCalendarLayoutState';
|
||||
import { DateTimePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { SelectControl } from '@/ui/input/components/SelectControl';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
import { type DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState';
|
||||
import styled from '@emotion/styled';
|
||||
import { addMonths, format, subMonths } from 'date-fns';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { type Nullable } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconChevronLeft, IconChevronRight } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { ViewCalendarLayout } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 24px;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledLeftSection = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledNavigationSection = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const RecordCalendarTopBar = () => {
|
||||
@@ -20,29 +48,106 @@ export const RecordCalendarTopBar = () => {
|
||||
recordIndexCalendarLayoutState,
|
||||
);
|
||||
|
||||
const [recordCalendarSelectedDate, setRecordCalendarSelectedDate] =
|
||||
useRecoilComponentState(recordCalendarSelectedDateComponentState);
|
||||
|
||||
const datePickerDropdownId = `record-calendar-date-picker-${recordCalendarId}`;
|
||||
const { closeDropdown } = useCloseDropdown();
|
||||
|
||||
const handleDateChange = (date: Nullable<Date>) => {
|
||||
if (isDefined(date)) {
|
||||
setRecordCalendarSelectedDate(date);
|
||||
}
|
||||
closeDropdown(datePickerDropdownId);
|
||||
};
|
||||
|
||||
const handlePreviousMonth = () => {
|
||||
setRecordCalendarSelectedDate(subMonths(recordCalendarSelectedDate, 1));
|
||||
};
|
||||
|
||||
const handleNextMonth = () => {
|
||||
setRecordCalendarSelectedDate(addMonths(recordCalendarSelectedDate, 1));
|
||||
};
|
||||
|
||||
const handleTodayClick = () => {
|
||||
setRecordCalendarSelectedDate(new Date());
|
||||
};
|
||||
|
||||
const formattedDate = format(recordCalendarSelectedDate, 'MMMM yyyy');
|
||||
|
||||
const dropdownContentOffset = { x: 140, y: 0 } satisfies DropdownOffset;
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<Select
|
||||
dropdownId={`record-calendar-top-bar-layout-select-${recordCalendarId}`}
|
||||
selectSizeVariant="small"
|
||||
options={[
|
||||
{
|
||||
label: 'Month',
|
||||
value: ViewCalendarLayout.MONTH,
|
||||
},
|
||||
{
|
||||
label: 'Week',
|
||||
value: ViewCalendarLayout.WEEK,
|
||||
},
|
||||
{
|
||||
label: 'Timeline',
|
||||
value: ViewCalendarLayout.DAY,
|
||||
},
|
||||
]}
|
||||
disabled
|
||||
value={recordIndexCalendarLayout}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<StyledLeftSection>
|
||||
<Select
|
||||
dropdownId={`record-calendar-top-bar-layout-select-${recordCalendarId}`}
|
||||
selectSizeVariant="small"
|
||||
options={[
|
||||
{
|
||||
label: 'Month',
|
||||
value: ViewCalendarLayout.MONTH,
|
||||
},
|
||||
{
|
||||
label: 'Week',
|
||||
value: ViewCalendarLayout.WEEK,
|
||||
},
|
||||
{
|
||||
label: 'Timeline',
|
||||
value: ViewCalendarLayout.DAY,
|
||||
},
|
||||
]}
|
||||
disabled
|
||||
value={recordIndexCalendarLayout}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<Dropdown
|
||||
dropdownId={datePickerDropdownId}
|
||||
clickableComponent={
|
||||
<SelectControl
|
||||
selectedOption={{
|
||||
label: formattedDate,
|
||||
value: formattedDate,
|
||||
}}
|
||||
selectSizeVariant="small"
|
||||
/>
|
||||
}
|
||||
dropdownComponents={
|
||||
<DropdownContent widthInPixels={280}>
|
||||
<DateTimePicker
|
||||
date={recordCalendarSelectedDate}
|
||||
isDateTimeInput={false}
|
||||
onChange={handleDateChange}
|
||||
onClose={handleDateChange}
|
||||
clearable={false}
|
||||
hideHeaderInput
|
||||
/>
|
||||
</DropdownContent>
|
||||
}
|
||||
dropdownOffset={dropdownContentOffset}
|
||||
/>
|
||||
</StyledLeftSection>
|
||||
|
||||
<StyledNavigationSection>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronLeft}
|
||||
onClick={handlePreviousMonth}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
title="Today"
|
||||
onClick={handleTodayClick}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
variant="tertiary"
|
||||
Icon={IconChevronRight}
|
||||
onClick={handleNextMonth}
|
||||
/>
|
||||
</StyledNavigationSection>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+7
-3
@@ -2,6 +2,7 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordCalendarContextOrThrow } from '@/object-record/record-calendar/contexts/RecordCalendarContext';
|
||||
import { useRecordCalendarQueryDateRangeFilter } from '@/object-record/record-calendar/month/hooks/useRecordCalendarQueryDateRangeFilter';
|
||||
import { recordCalendarSelectedDateComponentState } from '@/object-record/record-calendar/states/recordCalendarSelectedDateComponentState';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { anyFieldFilterValueComponentState } from '@/object-record/record-filter/states/anyFieldFilterValueComponentState';
|
||||
@@ -17,7 +18,9 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
const selectedDate = new Date();
|
||||
const recordCalendarSelectedDate = useRecoilComponentValue(
|
||||
recordCalendarSelectedDateComponentState,
|
||||
);
|
||||
const { objectMetadataItem } = useRecordCalendarContextOrThrow();
|
||||
|
||||
const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore();
|
||||
@@ -57,8 +60,9 @@ export const RecordIndexCalendarDataLoaderEffect = () => {
|
||||
filterValue: anyFieldFilterValue,
|
||||
});
|
||||
|
||||
const { dateRangeFilter } =
|
||||
useRecordCalendarQueryDateRangeFilter(selectedDate);
|
||||
const { dateRangeFilter } = useRecordCalendarQueryDateRangeFilter(
|
||||
recordCalendarSelectedDate,
|
||||
);
|
||||
|
||||
const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts);
|
||||
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { RecordCalendarComponentInstanceContext } from '@/object-record/record-calendar/states/contexts/RecordCalendarComponentInstanceContext';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const recordCalendarSelectedDateComponentState =
|
||||
createComponentState<Date>({
|
||||
key: 'recordCalendarSelectedDateComponentState',
|
||||
defaultValue: new Date(),
|
||||
componentInstanceContext: RecordCalendarComponentInstanceContext,
|
||||
});
|
||||
Reference in New Issue
Block a user