2282 Rename components to use the new naming convention part 1 (#2293)
renaming in progress
This commit is contained in:
@ -0,0 +1,89 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
|
||||
import { FieldDisplay } from '../../field/components/FieldDisplay';
|
||||
import { FieldInput } from '../../field/components/FieldInput';
|
||||
import { FieldContext } from '../../field/contexts/FieldContext';
|
||||
import { useGetButtonIcon } from '../../field/hooks/useGetButtonIcon';
|
||||
import { useIsFieldEmpty } from '../../field/hooks/useIsFieldEmpty';
|
||||
import { useIsFieldInputOnly } from '../../field/hooks/useIsFieldInputOnly';
|
||||
import { FieldInputEvent } from '../../field/types/FieldInputEvent';
|
||||
import { isFieldRelation } from '../../field/types/guards/isFieldRelation';
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
import { InlineCellContainer } from './InlineCellContainer';
|
||||
|
||||
export const InlineCell = () => {
|
||||
const { fieldDefinition } = useContext(FieldContext);
|
||||
|
||||
const buttonIcon = useGetButtonIcon();
|
||||
|
||||
const isFieldEmpty = useIsFieldEmpty();
|
||||
|
||||
const isFieldInputOnly = useIsFieldInputOnly();
|
||||
|
||||
const { closeInlineCell } = useInlineCell();
|
||||
|
||||
const handleEnter: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleSubmit: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleEscape = () => {
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleShiftTab: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
const handleClickOutside: FieldInputEvent = (persistField) => {
|
||||
persistField();
|
||||
closeInlineCell();
|
||||
};
|
||||
|
||||
return (
|
||||
<InlineCellContainer
|
||||
buttonIcon={buttonIcon}
|
||||
customEditHotkeyScope={
|
||||
isFieldRelation(fieldDefinition)
|
||||
? {
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
IconLabel={fieldDefinition.Icon}
|
||||
editModeContent={
|
||||
<FieldInput
|
||||
onEnter={handleEnter}
|
||||
onCancel={handleCancel}
|
||||
onEscape={handleEscape}
|
||||
onSubmit={handleSubmit}
|
||||
onTab={handleTab}
|
||||
onShiftTab={handleShiftTab}
|
||||
onClickOutside={handleClickOutside}
|
||||
/>
|
||||
}
|
||||
displayModeContent={<FieldDisplay />}
|
||||
isDisplayModeContentEmpty={isFieldEmpty}
|
||||
isDisplayModeFixHeight
|
||||
editModeContentOnly={isFieldInputOnly}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,181 @@
|
||||
import { useState } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
import { InlineCellDisplayMode } from './InlineCellDisplayMode';
|
||||
import { InlineCellButton } from './InlineCellEditButton';
|
||||
import { InlineCellEditMode } from './InlineCellEditMode';
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
width: 16px;
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLabelAndIconContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledValueContainer = styled.div`
|
||||
display: flex;
|
||||
max-width: calc(100% - ${({ theme }) => theme.spacing(4)});
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.div<
|
||||
Pick<InlineCellContainerProps, 'labelFixedWidth'>
|
||||
>`
|
||||
align-items: center;
|
||||
|
||||
width: ${({ labelFixedWidth }) =>
|
||||
labelFixedWidth ? `${labelFixedWidth}px` : 'fit-content'};
|
||||
`;
|
||||
|
||||
const StyledEditButtonContainer = styled(motion.div)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StyledClickableContainer = styled.div`
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledInlineCellBaseContainer = styled.div`
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
display: flex;
|
||||
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
position: relative;
|
||||
user-select: none;
|
||||
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type InlineCellContainerProps = {
|
||||
IconLabel?: IconComponent;
|
||||
label?: string;
|
||||
labelFixedWidth?: number;
|
||||
buttonIcon?: IconComponent;
|
||||
editModeContent?: React.ReactNode;
|
||||
editModeContentOnly?: boolean;
|
||||
displayModeContent: React.ReactNode;
|
||||
customEditHotkeyScope?: HotkeyScope;
|
||||
isDisplayModeContentEmpty?: boolean;
|
||||
isDisplayModeFixHeight?: boolean;
|
||||
disableHoverEffect?: boolean;
|
||||
};
|
||||
|
||||
export const InlineCellContainer = ({
|
||||
IconLabel,
|
||||
label,
|
||||
labelFixedWidth,
|
||||
buttonIcon,
|
||||
editModeContent,
|
||||
displayModeContent,
|
||||
customEditHotkeyScope,
|
||||
isDisplayModeContentEmpty,
|
||||
editModeContentOnly,
|
||||
isDisplayModeFixHeight,
|
||||
disableHoverEffect,
|
||||
}: InlineCellContainerProps) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleContainerMouseEnter = () => {
|
||||
setIsHovered(true);
|
||||
};
|
||||
|
||||
const handleContainerMouseLeave = () => {
|
||||
setIsHovered(false);
|
||||
};
|
||||
|
||||
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
|
||||
|
||||
const handleDisplayModeClick = () => {
|
||||
if (!editModeContentOnly) {
|
||||
openInlineCell(customEditHotkeyScope);
|
||||
}
|
||||
};
|
||||
|
||||
const showEditButton =
|
||||
buttonIcon && !isInlineCellInEditMode && isHovered && !editModeContentOnly;
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<StyledInlineCellBaseContainer
|
||||
onMouseEnter={handleContainerMouseEnter}
|
||||
onMouseLeave={handleContainerMouseLeave}
|
||||
>
|
||||
<StyledLabelAndIconContainer>
|
||||
{IconLabel && (
|
||||
<StyledIconContainer>
|
||||
<IconLabel stroke={theme.icon.stroke.sm} />
|
||||
</StyledIconContainer>
|
||||
)}
|
||||
{label && (
|
||||
<StyledLabel labelFixedWidth={labelFixedWidth}>{label}</StyledLabel>
|
||||
)}
|
||||
</StyledLabelAndIconContainer>
|
||||
<StyledValueContainer>
|
||||
{isInlineCellInEditMode ? (
|
||||
<InlineCellEditMode>{editModeContent}</InlineCellEditMode>
|
||||
) : editModeContentOnly ? (
|
||||
<StyledClickableContainer>
|
||||
<InlineCellDisplayMode
|
||||
disableHoverEffect={disableHoverEffect}
|
||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||
isDisplayModeFixHeight={isDisplayModeFixHeight}
|
||||
isHovered={isHovered}
|
||||
>
|
||||
{editModeContent}
|
||||
</InlineCellDisplayMode>
|
||||
</StyledClickableContainer>
|
||||
) : (
|
||||
<StyledClickableContainer onClick={handleDisplayModeClick}>
|
||||
<InlineCellDisplayMode
|
||||
disableHoverEffect={disableHoverEffect}
|
||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||
isDisplayModeFixHeight={isDisplayModeFixHeight}
|
||||
isHovered={isHovered}
|
||||
>
|
||||
{displayModeContent}
|
||||
</InlineCellDisplayMode>
|
||||
{showEditButton && (
|
||||
<StyledEditButtonContainer
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.1 }}
|
||||
whileHover={{ scale: 1.04 }}
|
||||
>
|
||||
<InlineCellButton Icon={buttonIcon} />
|
||||
</StyledEditButtonContainer>
|
||||
)}
|
||||
</StyledClickableContainer>
|
||||
)}
|
||||
</StyledValueContainer>
|
||||
</StyledInlineCellBaseContainer>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,81 @@
|
||||
import { css } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledInlineCellNormalModeOuterContainer = styled.div<
|
||||
Pick<
|
||||
InlineCellDisplayModeProps,
|
||||
| 'isDisplayModeContentEmpty'
|
||||
| 'disableHoverEffect'
|
||||
| 'isDisplayModeFixHeight'
|
||||
| 'isHovered'
|
||||
>
|
||||
>`
|
||||
align-items: center;
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
height: ${({ isDisplayModeFixHeight }) =>
|
||||
isDisplayModeFixHeight ? '16px' : 'auto'};
|
||||
min-height: 16px;
|
||||
overflow: hidden;
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
${(props) => {
|
||||
if (props.isHovered) {
|
||||
return css`
|
||||
background-color: ${!props.disableHoverEffect
|
||||
? props.theme.background.transparent.light
|
||||
: 'transparent'};
|
||||
|
||||
cursor: pointer;
|
||||
`;
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
const StyledInlineCellNormalModeInnerContainer = styled.div`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
font-size: 'inherit';
|
||||
font-weight: 'inherit';
|
||||
|
||||
height: fit-content;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledEmptyField = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
`;
|
||||
|
||||
type InlineCellDisplayModeProps = {
|
||||
isDisplayModeContentEmpty?: boolean;
|
||||
disableHoverEffect?: boolean;
|
||||
isDisplayModeFixHeight?: boolean;
|
||||
isHovered?: boolean;
|
||||
};
|
||||
|
||||
export const InlineCellDisplayMode = ({
|
||||
children,
|
||||
isDisplayModeContentEmpty,
|
||||
disableHoverEffect,
|
||||
isDisplayModeFixHeight,
|
||||
isHovered,
|
||||
}: React.PropsWithChildren<InlineCellDisplayModeProps>) => (
|
||||
<StyledInlineCellNormalModeOuterContainer
|
||||
isDisplayModeContentEmpty={isDisplayModeContentEmpty}
|
||||
disableHoverEffect={disableHoverEffect}
|
||||
isDisplayModeFixHeight={isDisplayModeFixHeight}
|
||||
isHovered={isHovered}
|
||||
>
|
||||
<StyledInlineCellNormalModeInnerContainer>
|
||||
{isDisplayModeContentEmpty || !children ? (
|
||||
<StyledEmptyField>{'Empty'}</StyledEmptyField>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</StyledInlineCellNormalModeInnerContainer>
|
||||
</StyledInlineCellNormalModeOuterContainer>
|
||||
);
|
||||
@ -0,0 +1,21 @@
|
||||
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton';
|
||||
|
||||
import { useInlineCell } from '../hooks/useInlineCell';
|
||||
|
||||
export const InlineCellButton = ({ Icon }: { Icon: IconComponent }) => {
|
||||
const { openInlineCell } = useInlineCell();
|
||||
|
||||
const handleClick = () => {
|
||||
openInlineCell();
|
||||
};
|
||||
|
||||
return (
|
||||
<FloatingIconButton
|
||||
size="small"
|
||||
onClick={handleClick}
|
||||
Icon={Icon}
|
||||
data-testid="inline-cell-edit-mode-container"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledInlineCellEditModeContainer = styled.div<InlineCellEditModeProps>`
|
||||
align-items: center;
|
||||
|
||||
display: flex;
|
||||
height: 24px;
|
||||
|
||||
margin-left: -${({ theme }) => theme.spacing(1)};
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
const StyledInlineCellInput = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-left: -1px;
|
||||
|
||||
min-height: 32px;
|
||||
min-width: 200px;
|
||||
width: inherit;
|
||||
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
type InlineCellEditModeProps = {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const InlineCellEditMode = ({ children }: InlineCellEditModeProps) => (
|
||||
<StyledInlineCellEditModeContainer data-testid="inline-cell-edit-mode-container">
|
||||
<StyledInlineCellInput>{children}</StyledInlineCellInput>
|
||||
</StyledInlineCellEditModeContainer>
|
||||
);
|
||||
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const InlineCellMutationContext = createContext<any>(undefined);
|
||||
@ -0,0 +1,47 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
import { isInlineCellInEditModeScopedState } from '../states/isInlineCellInEditModeScopedState';
|
||||
import { InlineCellHotkeyScope } from '../types/InlineCellHotkeyScope';
|
||||
|
||||
export const useInlineCell = () => {
|
||||
const { recoilScopeId } = useContext(FieldContext);
|
||||
|
||||
const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState(
|
||||
isInlineCellInEditModeScopedState(recoilScopeId),
|
||||
);
|
||||
|
||||
const {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const closeInlineCell = () => {
|
||||
setIsInlineCellInEditMode(false);
|
||||
|
||||
goBackToPreviousHotkeyScope();
|
||||
};
|
||||
|
||||
const openInlineCell = (customEditHotkeyScopeForField?: HotkeyScope) => {
|
||||
setIsInlineCellInEditMode(true);
|
||||
|
||||
if (customEditHotkeyScopeForField) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
customEditHotkeyScopeForField.scope,
|
||||
customEditHotkeyScopeForField.customScopes,
|
||||
);
|
||||
} else {
|
||||
setHotkeyScopeAndMemorizePreviousScope(InlineCellHotkeyScope.InlineCell);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
isInlineCellInEditMode,
|
||||
closeInlineCell,
|
||||
openInlineCell,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,21 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledPropertyBoxContainer = styled.div`
|
||||
align-self: stretch;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
interface PropertyBoxProps {
|
||||
children: React.ReactNode;
|
||||
extraPadding?: boolean;
|
||||
}
|
||||
|
||||
export const PropertyBox = ({ children }: PropertyBoxProps) => (
|
||||
<StyledPropertyBoxContainer>{children}</StyledPropertyBoxContainer>
|
||||
);
|
||||
@ -0,0 +1,11 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
export const customEditHotkeyScopeForFieldScopedState = atomFamily<
|
||||
HotkeyScope | null,
|
||||
string
|
||||
>({
|
||||
key: 'customEditHotkeyScopeForFieldScopedState',
|
||||
default: null,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
export const isInlineCellInEditModeScopedState = atomFamily<boolean, string>({
|
||||
key: 'isInlineCellInEditModeScopedState',
|
||||
default: false,
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
|
||||
export const parentHotkeyScopeForFieldScopedState = atomFamily<
|
||||
HotkeyScope | null,
|
||||
string
|
||||
>({
|
||||
key: 'parentHotkeyScopeForFieldScopedState',
|
||||
default: null,
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const FieldRecoilScopeContext = createContext<string | null>(null);
|
||||
@ -0,0 +1,3 @@
|
||||
export enum InlineCellHotkeyScope {
|
||||
InlineCell = 'inline-cell',
|
||||
}
|
||||
Reference in New Issue
Block a user