Feat: First column style update (#1746)
reimplemented as per suggestions by lucas
This commit is contained in:
@ -2,7 +2,9 @@ import { selectorFamily } from 'recoil';
|
||||
|
||||
import { FieldDefinition } from '../../types/FieldDefinition';
|
||||
import { FieldMetadata } from '../../types/FieldMetadata';
|
||||
import { isFieldChip } from '../../types/guards/isFieldChip';
|
||||
import { isFieldDate } from '../../types/guards/isFieldDate';
|
||||
import { isFieldDoubleTextChip } from '../../types/guards/isFieldDoubleTextChip';
|
||||
import { isFieldEmail } from '../../types/guards/isFieldEmail';
|
||||
import { isFieldMoney } from '../../types/guards/isFieldMoney';
|
||||
import { isFieldNumber } from '../../types/guards/isFieldNumber';
|
||||
@ -51,6 +53,41 @@ export const isEntityFieldEmptyFamilySelector = selectorFamily({
|
||||
if (isFieldRelationValue(fieldValue)) {
|
||||
return fieldValue === null || fieldValue === undefined;
|
||||
}
|
||||
} else if (isFieldChip(fieldDefinition)) {
|
||||
const contentFieldName = fieldDefinition.metadata.contentFieldName;
|
||||
|
||||
const contentFieldValue = get(entityFieldsFamilyState(entityId))?.[
|
||||
contentFieldName
|
||||
] as string | null;
|
||||
|
||||
return (
|
||||
contentFieldValue === null ||
|
||||
contentFieldValue === undefined ||
|
||||
contentFieldValue === ''
|
||||
);
|
||||
} else if (isFieldDoubleTextChip(fieldDefinition)) {
|
||||
const firstValueFieldName =
|
||||
fieldDefinition.metadata.firstValueFieldName;
|
||||
|
||||
const secondValueFieldName =
|
||||
fieldDefinition.metadata.secondValueFieldName;
|
||||
|
||||
const contentFieldFirstValue = get(entityFieldsFamilyState(entityId))?.[
|
||||
firstValueFieldName
|
||||
] as string | null;
|
||||
|
||||
const contentFieldSecondValue = get(
|
||||
entityFieldsFamilyState(entityId),
|
||||
)?.[secondValueFieldName] as string | null;
|
||||
|
||||
return (
|
||||
(contentFieldFirstValue === null ||
|
||||
contentFieldFirstValue === undefined ||
|
||||
contentFieldFirstValue === '') &&
|
||||
(contentFieldSecondValue === null ||
|
||||
contentFieldSecondValue === undefined ||
|
||||
contentFieldSecondValue === '')
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -9,5 +9,6 @@ export type FieldDefinition<T extends FieldMetadata> = {
|
||||
Icon?: IconComponent;
|
||||
type: FieldType;
|
||||
metadata: T;
|
||||
useEditButton?: boolean;
|
||||
buttonIcon?: IconComponent;
|
||||
basePathToShowPage?: string;
|
||||
};
|
||||
|
||||
@ -57,7 +57,7 @@ export const InlineCell = () => {
|
||||
|
||||
return (
|
||||
<InlineCellContainer
|
||||
useEditButton={fieldDefinition.useEditButton}
|
||||
buttonIcon={fieldDefinition.buttonIcon}
|
||||
customEditHotkeyScope={
|
||||
isFieldRelation(fieldDefinition)
|
||||
? {
|
||||
|
||||
@ -9,7 +9,7 @@ import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
import { InlineCellDisplayMode } from './InlineCellDisplayMode';
|
||||
import { InlineCellEditButton } from './InlineCellEditButton';
|
||||
import { InlineCellButton } from './InlineCellEditButton';
|
||||
import { InlineCellEditMode } from './InlineCellEditMode';
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
@ -76,7 +76,7 @@ type OwnProps = {
|
||||
IconLabel?: IconComponent;
|
||||
label?: string;
|
||||
labelFixedWidth?: number;
|
||||
useEditButton?: boolean;
|
||||
buttonIcon?: IconComponent;
|
||||
editModeContent?: React.ReactNode;
|
||||
editModeContentOnly?: boolean;
|
||||
displayModeContent: React.ReactNode;
|
||||
@ -90,7 +90,7 @@ export const InlineCellContainer = ({
|
||||
IconLabel,
|
||||
label,
|
||||
labelFixedWidth,
|
||||
useEditButton,
|
||||
buttonIcon,
|
||||
editModeContent,
|
||||
displayModeContent,
|
||||
customEditHotkeyScope,
|
||||
@ -118,10 +118,7 @@ export const InlineCellContainer = ({
|
||||
};
|
||||
|
||||
const showEditButton =
|
||||
!isInlineCellInEditMode &&
|
||||
isHovered &&
|
||||
useEditButton &&
|
||||
!editModeContentOnly;
|
||||
buttonIcon && !isInlineCellInEditMode && isHovered && !editModeContentOnly;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
@ -171,7 +168,7 @@ export const InlineCellContainer = ({
|
||||
transition={{ duration: 0.1 }}
|
||||
whileHover={{ scale: 1.04 }}
|
||||
>
|
||||
<InlineCellEditButton />
|
||||
<InlineCellButton Icon={buttonIcon} />
|
||||
</StyledEditButtonContainer>
|
||||
)}
|
||||
</StyledClickableContainer>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton';
|
||||
import { IconPencil } from '@/ui/icon';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
export const InlineCellEditButton = () => {
|
||||
export const InlineCellButton = ({ Icon }: { Icon: IconComponent }) => {
|
||||
const { openInlineCell } = useInlineCell();
|
||||
|
||||
const handleClick = () => {
|
||||
@ -14,7 +14,7 @@ export const InlineCellEditButton = () => {
|
||||
<FloatingIconButton
|
||||
size="small"
|
||||
onClick={handleClick}
|
||||
Icon={IconPencil}
|
||||
Icon={Icon}
|
||||
data-testid="inline-cell-edit-mode-container"
|
||||
/>
|
||||
);
|
||||
|
||||
@ -68,7 +68,7 @@ export const TableCell = ({
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={<FieldDisplay />}
|
||||
useEditButton={fieldDefinition.useEditButton}
|
||||
buttonIcon={fieldDefinition.buttonIcon}
|
||||
></TableCellContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
|
||||
const StyledEditButtonContainer = styled(motion.div)`
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
`;
|
||||
|
||||
type TableCellButtonProps = {
|
||||
onClick?: () => void;
|
||||
Icon: IconComponent;
|
||||
};
|
||||
|
||||
export const TableCellButton = ({ onClick, Icon }: TableCellButtonProps) => (
|
||||
<StyledEditButtonContainer
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.1 }}
|
||||
whileHover={{ scale: 1.04 }}
|
||||
>
|
||||
<FloatingIconButton size="small" onClick={onClick} Icon={Icon} />
|
||||
</StyledEditButtonContainer>
|
||||
);
|
||||
@ -1,18 +1,21 @@
|
||||
import { ReactElement, useState } from 'react';
|
||||
import { ReactElement, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useIsFieldEmpty } from '@/ui/field/hooks/useIsFieldEmpty';
|
||||
import { useIsFieldInputOnly } from '@/ui/field/hooks/useIsFieldInputOnly';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
import { useCurrentTableCellEditMode } from '../hooks/useCurrentTableCellEditMode';
|
||||
import { useIsSoftFocusOnCurrentTableCell } from '../hooks/useIsSoftFocusOnCurrentTableCell';
|
||||
import { useSetSoftFocusOnCurrentTableCell } from '../hooks/useSetSoftFocusOnCurrentTableCell';
|
||||
import { useTableCell } from '../hooks/useTableCell';
|
||||
|
||||
import { TableCellButton } from './TableCellButton';
|
||||
import { TableCellDisplayMode } from './TableCellDisplayMode';
|
||||
import { TableCellEditButton } from './TableCellEditButton';
|
||||
import { TableCellEditMode } from './TableCellEditMode';
|
||||
import { TableCellSoftFocusMode } from './TableCellSoftFocusMode';
|
||||
|
||||
@ -34,7 +37,7 @@ export type EditableCellProps = {
|
||||
editHotkeyScope?: HotkeyScope;
|
||||
transparent?: boolean;
|
||||
maxContentWidth?: number;
|
||||
useEditButton?: boolean;
|
||||
buttonIcon?: IconComponent;
|
||||
onSubmit?: () => void;
|
||||
onCancel?: () => void;
|
||||
};
|
||||
@ -51,16 +54,17 @@ export const TableCellContainer = ({
|
||||
editHotkeyScope,
|
||||
transparent = false,
|
||||
maxContentWidth,
|
||||
useEditButton,
|
||||
buttonIcon,
|
||||
}: EditableCellProps) => {
|
||||
const { isCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
|
||||
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const setSoftFocusOnCurrentTableCell = useSetSoftFocusOnCurrentTableCell();
|
||||
|
||||
const { openTableCell } = useTableCell();
|
||||
|
||||
const handlePenClick = () => {
|
||||
const handleButtonClick = () => {
|
||||
setSoftFocusOnCurrentTableCell();
|
||||
openTableCell();
|
||||
};
|
||||
@ -75,11 +79,16 @@ export const TableCellContainer = ({
|
||||
|
||||
const editModeContentOnly = useIsFieldInputOnly();
|
||||
|
||||
const showEditButton =
|
||||
useEditButton &&
|
||||
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const showButton =
|
||||
buttonIcon &&
|
||||
isHovered &&
|
||||
!isCurrentTableCellInEditMode &&
|
||||
!editModeContentOnly;
|
||||
!editModeContentOnly &&
|
||||
(!isFirstColumnCell || !isEmpty);
|
||||
|
||||
const hasSoftFocus = useIsSoftFocusOnCurrentTableCell();
|
||||
|
||||
@ -102,14 +111,18 @@ export const TableCellContainer = ({
|
||||
</TableCellEditMode>
|
||||
) : hasSoftFocus ? (
|
||||
<>
|
||||
{showEditButton && <TableCellEditButton onClick={handlePenClick} />}
|
||||
{showButton && (
|
||||
<TableCellButton onClick={handleButtonClick} Icon={buttonIcon} />
|
||||
)}
|
||||
<TableCellSoftFocusMode>
|
||||
{editModeContentOnly ? editModeContent : nonEditModeContent}
|
||||
</TableCellSoftFocusMode>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{showEditButton && <TableCellEditButton onClick={handlePenClick} />}
|
||||
{showButton && (
|
||||
<TableCellButton onClick={handleButtonClick} Icon={buttonIcon} />
|
||||
)}
|
||||
<TableCellDisplayMode isHovered={isHovered}>
|
||||
{editModeContentOnly ? editModeContent : nonEditModeContent}
|
||||
</TableCellDisplayMode>
|
||||
|
||||
@ -2,26 +2,25 @@ import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { FloatingIconButton } from '@/ui/button/components/FloatingIconButton';
|
||||
import { IconPencil } from '@/ui/icon';
|
||||
import { IconComponent } from '@/ui/icon/types/IconComponent';
|
||||
|
||||
const StyledEditButtonContainer = styled(motion.div)`
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
`;
|
||||
|
||||
type EditableCellEditButtonProps = {
|
||||
type TableCellButtonProps = {
|
||||
onClick?: () => void;
|
||||
Icon: IconComponent;
|
||||
};
|
||||
|
||||
export const TableCellEditButton = ({
|
||||
onClick,
|
||||
}: EditableCellEditButtonProps) => (
|
||||
export const TableCellButton = ({ onClick, Icon }: TableCellButtonProps) => (
|
||||
<StyledEditButtonContainer
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.1 }}
|
||||
whileHover={{ scale: 1.04 }}
|
||||
>
|
||||
<FloatingIconButton size="small" onClick={onClick} Icon={IconPencil} />
|
||||
<FloatingIconButton size="small" onClick={onClick} Icon={Icon} />
|
||||
</StyledEditButtonContainer>
|
||||
);
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import { useContext } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { FieldContext } from '@/ui/field/contexts/FieldContext';
|
||||
import { useIsFieldEmpty } from '@/ui/field/hooks/useIsFieldEmpty';
|
||||
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { CellHotkeyScopeContext } from '../../contexts/CellHotkeyScopeContext';
|
||||
import { ColumnIndexContext } from '../../contexts/ColumnIndexContext';
|
||||
import { useCloseCurrentTableCellInEditMode } from '../../hooks/useCloseCurrentTableCellInEditMode';
|
||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||
|
||||
@ -30,7 +34,20 @@ export const useTableCell = () => {
|
||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const isFirstColumnCell = useContext(ColumnIndexContext) === 0;
|
||||
|
||||
const isEmpty = useIsFieldEmpty();
|
||||
|
||||
const { entityId, fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const openTableCell = () => {
|
||||
if (isFirstColumnCell && !isEmpty && fieldDefinition.basePathToShowPage) {
|
||||
navigate(`${fieldDefinition.basePathToShowPage}${entityId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
setDragSelectionStartEnabled(false);
|
||||
setCurrentTableCellInEditMode();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user