mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-24 06:48:42 +03:00
Board compact view and Company Picker for opportunity special case (#3713)
* Re-enabled board compact mode * Add specific case for opportunity to display company picker * Add infinite scroll * Remove useEffect * Fix * Fix
This commit is contained in:
parent
29339ef99a
commit
c8e4d0ab9a
@ -3,6 +3,8 @@ import { isFirstRecordBoardColumnFamilyStateScopeMap } from '@/object-record/rec
|
||||
import { isLastRecordBoardColumnFamilyStateScopeMap } from '@/object-record/record-board/states/isLastRecordBoardColumnFamilyStateScopeMap';
|
||||
import { isRecordBoardCardSelectedFamilyStateScopeMap } from '@/object-record/record-board/states/isRecordBoardCardSelectedFamilyStateScopeMap';
|
||||
import { isRecordBoardCompactModeActiveStateScopeMap } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveStateScopeMap';
|
||||
import { isRecordBoardFetchingRecordsStateScopeMap } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsStateScopeMap';
|
||||
import { onRecordBoardFetchMoreVisibilityChangeStateScopeMap } from '@/object-record/record-board/states/onRecordBoardFetchMoreVisibilityChangeStateScopeMap';
|
||||
import { recordBoardColumnIdsStateScopeMap } from '@/object-record/record-board/states/recordBoardColumnIdsStateScopeMap';
|
||||
import { recordBoardFieldDefinitionsStateScopeMap } from '@/object-record/record-board/states/recordBoardFieldDefinitionsStateScopeMap';
|
||||
import { recordBoardFiltersStateScopeMap } from '@/object-record/record-board/states/recordBoardFiltersStateScopeMap';
|
||||
@ -30,6 +32,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => {
|
||||
recordBoardObjectSingularNameStateScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
getIsFetchingRecordState: getState(
|
||||
isRecordBoardFetchingRecordsStateScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
getColumnIdsState: getState(recordBoardColumnIdsStateScopeMap, scopeId),
|
||||
isFirstColumnFamilyState: getFamilyState(
|
||||
isFirstRecordBoardColumnFamilyStateScopeMap,
|
||||
@ -72,5 +78,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => {
|
||||
isRecordBoardCompactModeActiveStateScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
|
||||
getOnFetchMoreVisibilityChangeState: getState(
|
||||
onRecordBoardFetchMoreVisibilityChangeStateScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
@ -10,6 +10,8 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
||||
getFieldDefinitionsState,
|
||||
getObjectSingularNameState,
|
||||
getSelectedRecordIdsSelector,
|
||||
getIsCompactModeActiveState,
|
||||
getOnFetchMoreVisibilityChangeState,
|
||||
} = useRecordBoardStates(recordBoardId);
|
||||
|
||||
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
|
||||
@ -24,5 +26,7 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
||||
setFieldDefinitions,
|
||||
setObjectSingularName,
|
||||
getSelectedRecordIdsSelector,
|
||||
getIsCompactModeActiveState,
|
||||
getOnFetchMoreVisibilityChangeState,
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ReactNode, useContext, useState } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
@ -20,6 +21,7 @@ import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
||||
import { ScrollWrapperContext } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
|
||||
const StyledBoardCard = styled.div<{ selected: boolean }>`
|
||||
background-color: ${({ theme, selected }) =>
|
||||
@ -119,22 +121,26 @@ const StyledCompactIconContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledRecordInlineCellPlaceholder = styled.div`
|
||||
height: 24px;
|
||||
`;
|
||||
|
||||
export const RecordBoardCard = () => {
|
||||
const { recordId } = useContext(RecordBoardCardContext);
|
||||
const { updateOneRecord } = useContext(RecordBoardContext);
|
||||
const { updateOneRecord, objectMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
const {
|
||||
getObjectSingularNameState,
|
||||
getIsCompactModeActiveState,
|
||||
isRecordBoardCardSelectedFamilyState,
|
||||
getVisibleFieldDefinitionsState,
|
||||
} = useRecordBoardStates();
|
||||
|
||||
const isCompactModeActive = useRecoilValue(getIsCompactModeActiveState());
|
||||
const objectNameSingular = useRecoilValue(getObjectSingularNameState());
|
||||
const [isCardInCompactMode, setIsCardInCompactMode] =
|
||||
useState(isCompactModeActive);
|
||||
|
||||
const [isCardInCompactMode, setIsCardInCompactMode] = useState(true);
|
||||
|
||||
const [isCurrentCardSelected, setIsCurrentCardSelected] = useRecoilState(
|
||||
isRecordBoardCardSelectedFamilyState(recordId),
|
||||
@ -190,13 +196,21 @@ export const RecordBoardCard = () => {
|
||||
return [updateEntity, { loading: false }];
|
||||
};
|
||||
|
||||
if (!objectNameSingular || !record) {
|
||||
const scrollWrapperRef = useContext(ScrollWrapperContext);
|
||||
|
||||
const { ref: cardRef, inView } = useInView({
|
||||
root: scrollWrapperRef.current,
|
||||
rootMargin: '1000px',
|
||||
});
|
||||
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledBoardCardWrapper onContextMenu={handleContextMenu}>
|
||||
<StyledBoardCard
|
||||
ref={cardRef}
|
||||
selected={isCurrentCardSelected}
|
||||
onMouseLeave={onMouseLeaveBoard}
|
||||
onClick={() => {
|
||||
@ -204,7 +218,10 @@ export const RecordBoardCard = () => {
|
||||
}}
|
||||
>
|
||||
<StyledBoardCardHeader showCompactView={isCompactModeActive}>
|
||||
<RecordChip objectNameSingular={objectNameSingular} record={record} />
|
||||
<RecordChip
|
||||
objectNameSingular={objectMetadataItem.nameSingular}
|
||||
record={record}
|
||||
/>
|
||||
{isCompactModeActive && (
|
||||
<StyledCompactIconContainer className="compact-icon-container">
|
||||
<LightIconButton
|
||||
@ -226,7 +243,10 @@ export const RecordBoardCard = () => {
|
||||
</StyledCheckboxContainer>
|
||||
</StyledBoardCardHeader>
|
||||
<StyledBoardCardBody>
|
||||
<AnimatedEaseInOut isOpen={!isCardInCompactMode} initial={false}>
|
||||
<AnimatedEaseInOut
|
||||
isOpen={!isCardInCompactMode || !isCompactModeActive}
|
||||
initial={false}
|
||||
>
|
||||
{visibleBoardCardFieldDefinitions.map((fieldDefinition) => (
|
||||
<PreventSelectOnClickContainer
|
||||
key={fieldDefinition.fieldMetadataId}
|
||||
@ -249,7 +269,11 @@ export const RecordBoardCard = () => {
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
<RecordInlineCell />
|
||||
{inView ? (
|
||||
<RecordInlineCell />
|
||||
) : (
|
||||
<StyledRecordInlineCellPlaceholder />
|
||||
)}
|
||||
</FieldContext.Provider>
|
||||
</PreventSelectOnClickContainer>
|
||||
))}
|
||||
|
@ -2,14 +2,14 @@ import React, { useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { Draggable, DroppableProvided } from '@hello-pangea/dnd';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo';
|
||||
import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader';
|
||||
import { RecordBoardColumnNewButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton';
|
||||
import { RecordBoardColumnNewOpportunityButton } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
|
||||
const StyledPlaceholder = styled.div`
|
||||
min-height: 1px;
|
||||
`;
|
||||
|
||||
const StyledColumnCardsContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
@ -30,6 +30,7 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
droppableProvided,
|
||||
}: RecordBoardColumnCardsContainerProps) => {
|
||||
const { columnDefinition } = useContext(RecordBoardColumnContext);
|
||||
const { objectMetadataItem } = useContext(RecordBoardContext);
|
||||
|
||||
return (
|
||||
<StyledColumnCardsContainer
|
||||
@ -38,7 +39,7 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
{...droppableProvided?.droppableProps}
|
||||
>
|
||||
<RecordBoardColumnCardsMemo recordIds={recordIds} />
|
||||
<StyledPlaceholder>{droppableProvided?.placeholder}</StyledPlaceholder>
|
||||
<RecordBoardColumnFetchMoreLoader />
|
||||
<Draggable
|
||||
draggableId={`new-${columnDefinition.id}`}
|
||||
index={recordIds.length}
|
||||
@ -51,7 +52,12 @@ export const RecordBoardColumnCardsContainer = ({
|
||||
{...draggableProvided?.draggableProps}
|
||||
>
|
||||
<StyledNewButtonContainer>
|
||||
<RecordBoardColumnNewButton />
|
||||
{objectMetadataItem.nameSingular ===
|
||||
CoreObjectNameSingular.Opportunity ? (
|
||||
<RecordBoardColumnNewOpportunityButton />
|
||||
) : (
|
||||
<RecordBoardColumnNewButton />
|
||||
)}
|
||||
</StyledNewButtonContainer>
|
||||
</div>
|
||||
)}
|
||||
|
@ -0,0 +1,36 @@
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
|
||||
import { grayScale } from '@/ui/theme/constants/colors';
|
||||
|
||||
const StyledText = styled.div`
|
||||
align-items: center;
|
||||
box-shadow: none;
|
||||
color: ${grayScale.gray40};
|
||||
display: flex;
|
||||
height: 32px;
|
||||
margin-left: ${({ theme }) => theme.spacing(8)};
|
||||
padding-left: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const RecordBoardColumnFetchMoreLoader = () => {
|
||||
const { getIsFetchingRecordState, getOnFetchMoreVisibilityChangeState } =
|
||||
useRecordBoardStates();
|
||||
const isFetchingRecords = useRecoilValue(getIsFetchingRecordState());
|
||||
|
||||
const onFetchMoreVisibilityChange = useRecoilValue(
|
||||
getOnFetchMoreVisibilityChangeState(),
|
||||
);
|
||||
|
||||
const { ref } = useInView({
|
||||
onChange: onFetchMoreVisibilityChange,
|
||||
});
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
{isFetchingRecords && <StyledText>Loading more...</StyledText>}
|
||||
</div>
|
||||
);
|
||||
};
|
@ -0,0 +1,90 @@
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
|
||||
import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext';
|
||||
import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect';
|
||||
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
|
||||
const StyledButton = styled.button`
|
||||
align-items: center;
|
||||
align-self: baseline;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border: none;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ theme }) => theme.background.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
export const RecordBoardColumnNewOpportunityButton = () => {
|
||||
const [isCreatingCard, setIsCreatingCard] = useState(false);
|
||||
|
||||
const theme = useTheme();
|
||||
const { columnDefinition } = useContext(RecordBoardColumnContext);
|
||||
const { createOneRecord, selectFieldMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const handleEntitySelect = (company?: EntityForSelect) => {
|
||||
setIsCreatingCard(false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
|
||||
if (!company) {
|
||||
return;
|
||||
}
|
||||
|
||||
createOneRecord({
|
||||
name: company.name,
|
||||
companyId: company.id,
|
||||
[selectFieldMetadataItem.name]: columnDefinition.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleNewClick = useCallback(() => {
|
||||
setIsCreatingCard(true);
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
RelationPickerHotkeyScope.RelationPicker,
|
||||
);
|
||||
}, [setIsCreatingCard, setHotkeyScopeAndMemorizePreviousScope]);
|
||||
|
||||
const handleCancel = () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
setIsCreatingCard(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCreatingCard ? (
|
||||
<SingleEntitySelect
|
||||
disableBackgroundBlur
|
||||
onCancel={handleCancel}
|
||||
onEntitySelected={handleEntitySelect}
|
||||
relationObjectNameSingular={CoreObjectNameSingular.Company}
|
||||
relationPickerScopeId="relation-picker"
|
||||
selectedRelationRecordIds={[]}
|
||||
/>
|
||||
) : (
|
||||
<StyledButton onClick={handleNewClick}>
|
||||
<IconPlus size={theme.icon.size.md} />
|
||||
New
|
||||
</StyledButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const isRecordBoardFetchingRecordsStateScopeMap =
|
||||
createStateScopeMap<boolean>({
|
||||
key: 'isRecordBoardFetchingRecordsStateScopeMap',
|
||||
defaultValue: false,
|
||||
});
|
@ -0,0 +1,7 @@
|
||||
import { createStateScopeMap } from '@/ui/utilities/recoil-scope/utils/createStateScopeMap';
|
||||
|
||||
export const onRecordBoardFetchMoreVisibilityChangeStateScopeMap =
|
||||
createStateScopeMap<(visbility: boolean) => void>({
|
||||
key: 'onRecordBoardFetchMoreVisibilityChangeStateScopeMap',
|
||||
defaultValue: () => {},
|
||||
});
|
@ -4,6 +4,7 @@ import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldIn
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { isFieldCurrency } from '@/object-record/record-field/types/guards/isFieldCurrency';
|
||||
import { isFieldCurrencyValue } from '@/object-record/record-field/types/guards/isFieldCurrencyValue';
|
||||
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
|
||||
import { computeEmptyDraftValue } from '@/object-record/record-field/utils/computeEmptyDraftValue';
|
||||
import { isFieldValueEmpty } from '@/object-record/record-field/utils/isFieldValueEmpty';
|
||||
|
||||
@ -21,18 +22,20 @@ export const computeDraftValueFromFieldValue = <FieldValue>({
|
||||
// than the intputDraftValue type as string can be typed anywhere
|
||||
|
||||
if (isFieldCurrency(fieldDefinition)) {
|
||||
if (isFieldValueEmpty({ fieldValue, fieldDefinition })) {
|
||||
if (
|
||||
isFieldValueEmpty({ fieldValue, fieldDefinition }) ||
|
||||
!isFieldCurrencyValue(fieldValue)
|
||||
) {
|
||||
return computeEmptyDraftValue<FieldValue>({ fieldDefinition });
|
||||
}
|
||||
|
||||
if (isFieldCurrencyValue(fieldValue)) {
|
||||
return {
|
||||
amount: fieldValue?.amountMicros
|
||||
? fieldValue.amountMicros / 1000000
|
||||
: '',
|
||||
currenyCode: CurrencyCode.USD,
|
||||
} as unknown as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
return {
|
||||
amount: fieldValue?.amountMicros ? fieldValue.amountMicros / 1000000 : '',
|
||||
currenyCode: CurrencyCode.USD,
|
||||
} as unknown as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
if (isFieldRelation(fieldDefinition)) {
|
||||
return computeEmptyDraftValue<FieldValue>({ fieldDefinition });
|
||||
}
|
||||
|
||||
return fieldValue as FieldInputDraftValue<FieldValue>;
|
||||
|
@ -8,6 +8,7 @@ import { isFieldEmail } from '@/object-record/record-field/types/guards/isFieldE
|
||||
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
|
||||
import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink';
|
||||
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
|
||||
import { isFieldRelationValue } from '@/object-record/record-field/types/guards/isFieldRelationValue';
|
||||
import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText';
|
||||
import { isFieldUuid } from '@/object-record/record-field/types/guards/isFieldUuid';
|
||||
|
||||
@ -24,7 +25,8 @@ export const computeEmptyDraftValue = <FieldValue>({
|
||||
isFieldText(fieldDefinition) ||
|
||||
isFieldDateTime(fieldDefinition) ||
|
||||
isFieldNumber(fieldDefinition) ||
|
||||
isFieldEmail(fieldDefinition)
|
||||
isFieldEmail(fieldDefinition) ||
|
||||
isFieldRelationValue(fieldDefinition)
|
||||
) {
|
||||
return '' as FieldInputDraftValue<FieldValue>;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
@ -19,7 +19,9 @@ export const RecordIndexBoardContainer = ({
|
||||
recordBoardId,
|
||||
objectNameSingular,
|
||||
}: RecordIndexBoardContainerProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||
(field) => field.type === FieldMetadataType.Select,
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { useRecordActionBar } from '@/object-record/record-action-bar/hooks/useRecordActionBar';
|
||||
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
|
||||
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
||||
@ -21,11 +21,35 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
recordBoardId,
|
||||
viewBarId,
|
||||
}: RecordIndexBoardContainerEffectProps) => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
useLoadRecordIndexBoard({ objectNameSingular, recordBoardId, viewBarId });
|
||||
const {
|
||||
setColumns,
|
||||
setObjectSingularName,
|
||||
getSelectedRecordIdsSelector,
|
||||
setFieldDefinitions,
|
||||
getOnFetchMoreVisibilityChangeState,
|
||||
} = useRecordBoard(recordBoardId);
|
||||
|
||||
const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({
|
||||
objectNameSingular,
|
||||
recordBoardId,
|
||||
viewBarId,
|
||||
});
|
||||
|
||||
const setOnFetchMoreVisibilityChange = useSetRecoilState(
|
||||
getOnFetchMoreVisibilityChangeState(),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setOnFetchMoreVisibilityChange(() => () => {
|
||||
if (!loading) {
|
||||
fetchMoreRecords?.();
|
||||
}
|
||||
});
|
||||
}, [fetchMoreRecords, loading, setOnFetchMoreVisibilityChange]);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -33,12 +57,6 @@ export const RecordIndexBoardContainerEffect = ({
|
||||
navigate(`/settings/objects/${objectMetadataItem.namePlural}`);
|
||||
}, [navigate, objectMetadataItem.namePlural]);
|
||||
|
||||
const {
|
||||
setColumns,
|
||||
setObjectSingularName,
|
||||
getSelectedRecordIdsSelector,
|
||||
setFieldDefinitions,
|
||||
} = useRecordBoard(recordBoardId);
|
||||
const { resetRecordSelection } = useRecordBoardSelection(recordBoardId);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -7,13 +7,19 @@ import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/opti
|
||||
import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable';
|
||||
import { useRecordIndexOptionsImport } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsImport';
|
||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/display/icon';
|
||||
import {
|
||||
IconBaselineDensitySmall,
|
||||
IconChevronLeft,
|
||||
IconFileImport,
|
||||
IconTag,
|
||||
} from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
@ -86,8 +92,11 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
hiddenBoardFields,
|
||||
handleReorderBoardFields,
|
||||
handleBoardFieldVisibilityChange,
|
||||
isCompactModeActive,
|
||||
setIsCompactModeActive,
|
||||
} = useRecordIndexOptionsForBoard({
|
||||
objectNameSingular,
|
||||
recordBoardId: recordIndexId,
|
||||
viewBarId: recordIndexId,
|
||||
});
|
||||
|
||||
@ -170,6 +179,20 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{viewType === ViewType.Kanban && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemToggle
|
||||
LeftIcon={IconBaselineDensitySmall}
|
||||
onToggleChange={setIsCompactModeActive}
|
||||
toggled={isCompactModeActive}
|
||||
text="Compact view"
|
||||
toggleSize="small"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -5,32 +5,48 @@ import { useRecoilState } from 'recoil';
|
||||
import { mapBoardFieldDefinitionsToViewFields } from '@/companies/utils/mapBoardFieldDefinitionsToViewFields';
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItemOnly } from '@/object-metadata/hooks/useObjectMetadataItemOnly';
|
||||
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||
|
||||
type useRecordIndexOptionsForBoardParams = {
|
||||
objectNameSingular: string;
|
||||
recordBoardId: string;
|
||||
viewBarId: string;
|
||||
};
|
||||
|
||||
export const useRecordIndexOptionsForBoard = ({
|
||||
objectNameSingular,
|
||||
recordBoardId,
|
||||
viewBarId,
|
||||
}: useRecordIndexOptionsForBoardParams) => {
|
||||
const [recordIndexFieldDefinitions, setRecordIndexFieldDefinitions] =
|
||||
useRecoilState(recordIndexFieldDefinitionsState);
|
||||
|
||||
const { persistViewFields } = useViewFields(viewBarId);
|
||||
const { getIsCompactModeActiveState } = useRecordBoard(recordBoardId);
|
||||
|
||||
const [isCompactModeActive, setIsCompactModeActive] = useRecoilState(
|
||||
getIsCompactModeActiveState(),
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemOnly({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions } =
|
||||
const { columnDefinitions: availableColumnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
// Todo replace this with label identifier logic
|
||||
const columnDefinitions = availableColumnDefinitions
|
||||
.filter(
|
||||
(columnDefinition) => columnDefinition.metadata.fieldName !== 'name',
|
||||
)
|
||||
.filter(filterAvailableTableColumns);
|
||||
|
||||
const visibleBoardFields = useMemo(
|
||||
() =>
|
||||
columnDefinitions.filter((columnDefinition) => {
|
||||
@ -152,5 +168,7 @@ export const useRecordIndexOptionsForBoard = ({
|
||||
handleBoardFieldVisibilityChange,
|
||||
visibleBoardFields,
|
||||
hiddenBoardFields,
|
||||
isCompactModeActive,
|
||||
setIsCompactModeActive,
|
||||
};
|
||||
};
|
||||
|
@ -5,8 +5,6 @@ import { useRecoilValue } from 'recoil';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
import { contextMenuIsOpenState } from '@/ui/navigation/context-menu/states/contextMenuIsOpenState';
|
||||
|
||||
import { actionBarOpenState } from '../states/actionBarIsOpenState';
|
||||
|
||||
import { ActionBarItem } from './ActionBarItem';
|
||||
|
||||
const StyledContainerActionBar = styled.div`
|
||||
@ -30,12 +28,11 @@ const StyledContainerActionBar = styled.div`
|
||||
`;
|
||||
|
||||
export const ActionBar = () => {
|
||||
const actionBarOpen = useRecoilValue(actionBarOpenState);
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const actionBarEntries = useRecoilValue(actionBarEntriesState);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
if (!actionBarOpen || contextMenuIsOpen) {
|
||||
if (contextMenuIsOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { actionBarOpenState } from '@/ui/navigation/action-bar/states/actionBarIsOpenState';
|
||||
import { contextMenuPositionState } from '@/ui/navigation/context-menu/states/contextMenuPositionState';
|
||||
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
|
||||
|
||||
@ -42,14 +41,12 @@ export const ContextMenu = () => {
|
||||
const contextMenuIsOpen = useRecoilValue(contextMenuIsOpenState);
|
||||
const contextMenuEntries = useRecoilValue(contextMenuEntriesState);
|
||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||
const setActionBarOpenState = useSetRecoilState(actionBarOpenState);
|
||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useListenClickOutside({
|
||||
refs: [wrapperRef],
|
||||
callback: () => {
|
||||
setContextMenuOpenState(false);
|
||||
setActionBarOpenState(true);
|
||||
},
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user