Files
twenty/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx
vaibhav-devere 8127e4f635 Fix filter button height (#11237)
[#11218](https://github.com/twentyhq/twenty/issues/11218) Fixed Filter
Reset Button height

- Replaced the default button with LightButton to align with Figma
design specs which Restores expected button size and appearance.

- Ensured the correct height, padding, and styling are applied
automatically.

- Wrapped the button inside StyledChipcontainer for consistency.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
2025-03-28 09:28:05 +01:00

268 lines
9.8 KiB
TypeScript

import styled from '@emotion/styled';
import { ReactNode, useMemo } from 'react';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { AddObjectFilterFromDetailsButton } from '@/object-record/object-filter-dropdown/components/AddObjectFilterFromDetailsButton';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { useHandleToggleTrashColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleTrashColumnFilter';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { AdvancedFilterDropdownButton } from '@/views/components/AdvancedFilterDropdownButton';
import { EditableFilterDropdownButton } from '@/views/components/EditableFilterDropdownButton';
import { EditableSortChip } from '@/views/components/EditableSortChip';
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
import { useCheckIsSoftDeleteFilter } from '@/object-record/record-filter/hooks/useCheckIsSoftDeleteFilter';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
import { SoftDeleteFilterChip } from '@/views/components/SoftDeleteFilterChip';
import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '@/views/hooks/useApplyCurrentViewFiltersToCurrentRecordFilters';
import { useApplyCurrentViewSortsToCurrentRecordSorts } from '@/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts';
import { useAreViewFiltersDifferentFromRecordFilters } from '@/views/hooks/useAreViewFiltersDifferentFromRecordFilters';
import { useAreViewSortsDifferentFromRecordSorts } from '@/views/hooks/useAreViewSortsDifferentFromRecordSorts';
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
import { useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups } from '@/views/hooks/useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups';
import { useAreViewFilterGroupsDifferentFromRecordFilterGroups } from '@/views/hooks/useAreViewFilterGroupsDifferentFromRecordFilterGroups';
import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState';
import { t } from '@lingui/core/macro';
import { isNonEmptyArray } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
import { LightButton } from 'twenty-ui';
export type ViewBarDetailsProps = {
hasFilterButton?: boolean;
rightComponent?: ReactNode;
filterDropdownId?: string;
viewBarId: string;
objectNamePlural: string;
};
const StyledBar = styled.div`
align-items: center;
align-items: center;
border-top: 1px solid ${({ theme }) => theme.border.color.light};
border-top: 1px solid ${({ theme }) => theme.border.color.light};
display: flex;
flex-direction: row;
justify-content: space-between;
min-height: 32px;
padding-top: ${({ theme }) => theme.spacing(1)};
padding-bottom: ${({ theme }) => theme.spacing(1)};
padding-left: ${({ theme }) => theme.spacing(2)};
z-index: 4;
`;
const StyledChipcontainer = styled.div`
align-items: center;
display: flex;
flex-direction: row;
overflow: scroll;
gap: ${({ theme }) => theme.spacing(2)};
padding-top: ${({ theme }) => theme.spacing(1)};
z-index: 1;
`;
const StyledActionButtonContainer = styled.div`
display: flex;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
padding-top: ${({ theme }) => theme.spacing(1)};
`;
const StyledFilterContainer = styled.div`
display: flex;
align-items: center;
flex: 1;
overflow-x: hidden;
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledSeperatorContainer = styled.div`
align-items: flex-start;
align-self: stretch;
display: flex;
padding-bottom: ${({ theme }) => theme.spacing(2)};
padding-left: ${({ theme }) => theme.spacing(1)};
padding-right: ${({ theme }) => theme.spacing(1)};
padding-top: ${({ theme }) => theme.spacing(2)};
`;
const StyledSeperator = styled.div`
align-self: stretch;
background: ${({ theme }) => theme.background.quaternary};
width: 1px;
`;
const StyledAddFilterContainer = styled.div`
z-index: 5;
`;
export const ViewBarDetails = ({
hasFilterButton = false,
rightComponent,
filterDropdownId,
viewBarId,
objectNamePlural,
}: ViewBarDetailsProps) => {
const isViewBarExpanded = useRecoilComponentValueV2(
isViewBarExpandedComponentState,
);
const { hasFiltersQueryParams } = useViewFromQueryParams();
const currentRecordFilterGroups = useRecoilComponentValueV2(
currentRecordFilterGroupsComponentState,
);
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
);
const currentRecordSorts = useRecoilComponentValueV2(
currentRecordSortsComponentState,
);
const { objectNameSingular } = useObjectNameSingularFromPlural({
objectNamePlural: objectNamePlural,
});
const { toggleSoftDeleteFilterState } = useHandleToggleTrashColumnFilter({
objectNameSingular: objectNameSingular,
viewBarId: viewBarId,
});
const { viewFilterGroupsAreDifferentFromRecordFilterGroups } =
useAreViewFilterGroupsDifferentFromRecordFilterGroups();
const { viewFiltersAreDifferentFromRecordFilters } =
useAreViewFiltersDifferentFromRecordFilters();
const { viewSortsAreDifferentFromRecordSorts } =
useAreViewSortsDifferentFromRecordSorts();
const canResetView =
(viewFiltersAreDifferentFromRecordFilters ||
viewSortsAreDifferentFromRecordSorts ||
viewFilterGroupsAreDifferentFromRecordFilterGroups) &&
!hasFiltersQueryParams;
const { checkIsSoftDeleteFilter } = useCheckIsSoftDeleteFilter();
const softDeleteFilter = currentRecordFilters.find((recordFilter) =>
checkIsSoftDeleteFilter(recordFilter),
);
const recordFilters = useMemo(() => {
return currentRecordFilters.filter(
(recordFilter) =>
!recordFilter.recordFilterGroupId &&
!checkIsSoftDeleteFilter(recordFilter),
);
}, [currentRecordFilters, checkIsSoftDeleteFilter]);
const { applyCurrentViewFilterGroupsToCurrentRecordFilterGroups } =
useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups();
const { applyCurrentViewFiltersToCurrentRecordFilters } =
useApplyCurrentViewFiltersToCurrentRecordFilters();
const { applyCurrentViewSortsToCurrentRecordSorts } =
useApplyCurrentViewSortsToCurrentRecordSorts();
const handleCancelClick = () => {
applyCurrentViewFilterGroupsToCurrentRecordFilterGroups();
applyCurrentViewFiltersToCurrentRecordFilters();
applyCurrentViewSortsToCurrentRecordSorts();
toggleSoftDeleteFilterState(false);
};
const shouldExpandViewBar =
viewFiltersAreDifferentFromRecordFilters ||
viewSortsAreDifferentFromRecordSorts ||
viewFilterGroupsAreDifferentFromRecordFilterGroups ||
((currentRecordSorts.length > 0 ||
currentRecordFilters.length > 0 ||
currentRecordFilterGroups.length > 0) &&
isViewBarExpanded);
if (!shouldExpandViewBar) {
return null;
}
const shouldShowAdvancedFilterDropdownButton =
currentRecordFilterGroups.length > 0;
return (
<StyledBar>
<StyledFilterContainer>
<StyledChipcontainer>
{isDefined(softDeleteFilter) && (
<SoftDeleteFilterChip
key={softDeleteFilter.fieldMetadataId}
recordFilter={softDeleteFilter}
viewBarId={viewBarId}
/>
)}
{isDefined(softDeleteFilter) && (
<StyledSeperatorContainer>
<StyledSeperator />
</StyledSeperatorContainer>
)}
{currentRecordSorts.map((recordSort) => (
<EditableSortChip
key={recordSort.fieldMetadataId}
recordSort={recordSort}
/>
))}
{isNonEmptyArray(recordFilters) &&
isNonEmptyArray(currentRecordSorts) && (
<StyledSeperatorContainer>
<StyledSeperator />
</StyledSeperatorContainer>
)}
{shouldShowAdvancedFilterDropdownButton && (
<AdvancedFilterDropdownButton />
)}
{recordFilters.map((recordFilter) => (
<ObjectFilterDropdownComponentInstanceContext.Provider
key={recordFilter.id}
value={{ instanceId: recordFilter.id }}
>
<DropdownScope dropdownScopeId={recordFilter.id}>
<ViewBarFilterEffect filterDropdownId={recordFilter.id} />
<EditableFilterDropdownButton
viewFilter={recordFilter}
hotkeyScope={{
scope: recordFilter.id,
}}
viewFilterDropdownId={recordFilter.id}
/>
</DropdownScope>
</ObjectFilterDropdownComponentInstanceContext.Provider>
))}
</StyledChipcontainer>
{hasFilterButton && (
<StyledAddFilterContainer>
<AddObjectFilterFromDetailsButton
filterDropdownId={filterDropdownId}
/>
</StyledAddFilterContainer>
)}
</StyledFilterContainer>
<StyledActionButtonContainer>
{canResetView && (
<LightButton
data-testid="cancel-button"
accent="tertiary"
title={t`Reset`}
onClick={handleCancelClick}
/>
)}
{rightComponent}
</StyledActionButtonContainer>
</StyledBar>
);
};