Added generic relation cell (#969)
* Added generic relation cell * Deactivated debug * Added default warning * Put back display component * Removed unused types
This commit is contained in:
@ -3,6 +3,8 @@ import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { internalHotkeysEnabledScopesState } from '../states/internal/internalHotkeysEnabledScopesState';
|
||||
|
||||
const DEBUG_HOTKEY_SCOPE = false;
|
||||
|
||||
export function useScopedHotkeyCallback() {
|
||||
return useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
@ -24,9 +26,31 @@ export function useScopedHotkeyCallback() {
|
||||
.valueOrThrow();
|
||||
|
||||
if (!currentHotkeyScopes.includes(scope)) {
|
||||
if (DEBUG_HOTKEY_SCOPE) {
|
||||
console.debug(
|
||||
`%cI can't call hotkey (${
|
||||
hotkeysEvent.keys
|
||||
}) because I'm in scope [${scope}] and the active scopes are : [${currentHotkeyScopes.join(
|
||||
', ',
|
||||
)}]`,
|
||||
'color: gray; ',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (DEBUG_HOTKEY_SCOPE) {
|
||||
console.debug(
|
||||
`%cI can call hotkey (${
|
||||
hotkeysEvent.keys
|
||||
}) because I'm in scope [${scope}] and the active scopes are : [${currentHotkeyScopes.join(
|
||||
', ',
|
||||
)}]`,
|
||||
'color: green;',
|
||||
);
|
||||
}
|
||||
|
||||
if (preventDefault) {
|
||||
keyboardEvent.stopPropagation();
|
||||
keyboardEvent.preventDefault();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { GenericEditableCell } from '@/people/table/components/GenericEditableCell';
|
||||
import { GenericEditableCell } from '@/ui/table/components/GenericEditableCell';
|
||||
|
||||
import { RecoilScope } from '../../recoil-scope/components/RecoilScope';
|
||||
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
||||
|
||||
@ -6,9 +6,8 @@ import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
|
||||
|
||||
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
|
||||
import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus';
|
||||
import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext';
|
||||
import { EntityUpdateMutationHookContext } from '../states/EntityUpdateMutationHookContext';
|
||||
import { TableHeader } from '../table-header/components/TableHeader';
|
||||
import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook';
|
||||
|
||||
import { EntityTableBody } from './EntityTableBodyV2';
|
||||
import { EntityTableHeader } from './EntityTableHeaderV2';
|
||||
@ -90,7 +89,7 @@ type OwnProps<SortField> = {
|
||||
availableSorts?: Array<SortType<SortField>>;
|
||||
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||
onRowSelectionChange?: (rowSelection: string[]) => void;
|
||||
useUpdateField: EntityUpdateFieldHook;
|
||||
useUpdateEntityMutation: any;
|
||||
};
|
||||
|
||||
export function EntityTable<SortField>({
|
||||
@ -98,7 +97,7 @@ export function EntityTable<SortField>({
|
||||
viewIcon,
|
||||
availableSorts,
|
||||
onSortsUpdate,
|
||||
useUpdateField,
|
||||
useUpdateEntityMutation,
|
||||
}: OwnProps<SortField>) {
|
||||
const tableBodyRef = React.useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -114,7 +113,7 @@ export function EntityTable<SortField>({
|
||||
});
|
||||
|
||||
return (
|
||||
<EntityUpdateFieldHookContext.Provider value={useUpdateField}>
|
||||
<EntityUpdateMutationHookContext.Provider value={useUpdateEntityMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer ref={tableBodyRef}>
|
||||
<TableHeader
|
||||
@ -131,6 +130,6 @@ export function EntityTable<SortField>({
|
||||
</StyledTableWrapper>
|
||||
</StyledTableContainer>
|
||||
</StyledTableWithHeader>
|
||||
</EntityUpdateFieldHookContext.Provider>
|
||||
</EntityUpdateMutationHookContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||
|
||||
import { GenericEditableRelationCell } from './GenericEditableRelationCell';
|
||||
import { GenericEditableTextCell } from './GenericEditableTextCell';
|
||||
|
||||
type OwnProps = {
|
||||
entityFieldMetadata: EntityFieldMetadata;
|
||||
};
|
||||
|
||||
export function GenericEditableCell({ entityFieldMetadata }: OwnProps) {
|
||||
switch (entityFieldMetadata.type) {
|
||||
case 'text':
|
||||
return (
|
||||
<GenericEditableTextCell
|
||||
fieldName={entityFieldMetadata.fieldName}
|
||||
placeholder={entityFieldMetadata.label}
|
||||
editModeHorizontalAlign="left"
|
||||
/>
|
||||
);
|
||||
case 'relation': {
|
||||
return (
|
||||
<GenericEditableRelationCell fieldMetadata={entityFieldMetadata} />
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown field type: ${entityFieldMetadata.type} in GenericEditableCell`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||
|
||||
import { GenericEditableRelationCellDisplayMode } from './GenericEditableRelationCellDisplayMode';
|
||||
import { GenericEditableRelationCellEditMode } from './GenericEditableRelationCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
fieldMetadata: EntityFieldMetadata;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export function GenericEditableRelationCell({
|
||||
fieldMetadata,
|
||||
editModeHorizontalAlign,
|
||||
placeholder,
|
||||
}: OwnProps) {
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
|
||||
editModeContent={
|
||||
<GenericEditableRelationCellEditMode fieldMetadata={fieldMetadata} />
|
||||
}
|
||||
nonEditModeContent={
|
||||
<GenericEditableRelationCellDisplayMode
|
||||
fieldMetadata={fieldMetadata}
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
type OwnProps = {
|
||||
fieldMetadata: EntityFieldMetadata;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export function GenericEditableRelationCellDisplayMode({
|
||||
fieldMetadata,
|
||||
}: OwnProps) {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: fieldMetadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
switch (fieldMetadata.relationType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyChip
|
||||
id={fieldValue?.id ?? ''}
|
||||
name={fieldValue?.name ?? ''}
|
||||
pictureUrl={getLogoUrlFromDomainName(fieldValue?.domainName)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${fieldMetadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||
);
|
||||
return <> </>;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { CompanyPickerCell } from '@/companies/components/CompanyPickerCell';
|
||||
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
|
||||
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||
|
||||
type OwnProps = {
|
||||
fieldMetadata: EntityFieldMetadata;
|
||||
};
|
||||
|
||||
export function GenericEditableRelationCellEditMode({
|
||||
fieldMetadata,
|
||||
}: OwnProps) {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const { closeEditableCell } = useEditableCell();
|
||||
|
||||
const [fieldValueEntity] = useRecoilState<any | null>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName: fieldMetadata.fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateEntityField = useUpdateEntityField();
|
||||
|
||||
function handleEntitySubmit(newFieldEntity: EntityForSelect | null) {
|
||||
if (
|
||||
newFieldEntity?.id !== fieldValueEntity?.id &&
|
||||
currentRowEntityId &&
|
||||
updateEntityField
|
||||
) {
|
||||
updateEntityField(
|
||||
currentRowEntityId,
|
||||
fieldMetadata.fieldName,
|
||||
newFieldEntity,
|
||||
);
|
||||
}
|
||||
|
||||
closeEditableCell();
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
closeEditableCell();
|
||||
}
|
||||
|
||||
switch (fieldMetadata.relationType) {
|
||||
case Entity.Company: {
|
||||
return (
|
||||
<CompanyPickerCell
|
||||
companyId={fieldValueEntity?.id ?? null}
|
||||
onSubmit={handleEntitySubmit}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default:
|
||||
console.warn(
|
||||
`Unknown relation type: "${fieldMetadata.relationType}" in GenericEditableRelationCellEditMode`,
|
||||
);
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
|
||||
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||
|
||||
import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMode';
|
||||
|
||||
type OwnProps = {
|
||||
fieldName: string;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export function GenericEditableTextCell({
|
||||
fieldName,
|
||||
editModeHorizontalAlign,
|
||||
placeholder,
|
||||
}: OwnProps) {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const fieldValue = useRecoilValue<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<EditableCell
|
||||
editModeHorizontalAlign={editModeHorizontalAlign}
|
||||
editModeContent={
|
||||
<GenericEditableTextCellEditMode
|
||||
fieldName={fieldName}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
}
|
||||
nonEditModeContent={
|
||||
<InplaceInputTextDisplayMode>{fieldValue}</InplaceInputTextDisplayMode>
|
||||
}
|
||||
></EditableCell>
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
|
||||
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
||||
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
|
||||
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
|
||||
|
||||
type OwnProps = {
|
||||
fieldName: string;
|
||||
placeholder?: string;
|
||||
};
|
||||
|
||||
export function GenericEditableTextCellEditMode({
|
||||
fieldName,
|
||||
placeholder,
|
||||
}: OwnProps) {
|
||||
const currentRowEntityId = useCurrentRowEntityId();
|
||||
|
||||
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||
tableEntityFieldFamilySelector({
|
||||
entityId: currentRowEntityId ?? '',
|
||||
fieldName,
|
||||
}),
|
||||
);
|
||||
|
||||
const updateField = useUpdateEntityField();
|
||||
|
||||
function handleSubmit(newText: string) {
|
||||
if (newText === fieldValue) return;
|
||||
|
||||
setFieldValue(newText);
|
||||
|
||||
if (currentRowEntityId && updateField) {
|
||||
updateField(currentRowEntityId, fieldName, newText);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<InplaceInputTextEditMode
|
||||
placeholder={placeholder ?? ''}
|
||||
autoFocus
|
||||
value={fieldValue ?? ''}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext';
|
||||
|
||||
export function useEntityUpdateFieldHook() {
|
||||
return useContext(EntityUpdateFieldHookContext);
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook';
|
||||
|
||||
export const EntityUpdateFieldHookContext =
|
||||
createContext<EntityUpdateFieldHook | null>(null);
|
||||
@ -0,0 +1,3 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export const EntityUpdateMutationHookContext = createContext<any | null>(null);
|
||||
@ -1,10 +1,6 @@
|
||||
export type EntityFieldType =
|
||||
| 'text'
|
||||
| 'number'
|
||||
| 'date'
|
||||
| 'select'
|
||||
| 'checkbox'
|
||||
| 'icon';
|
||||
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export type EntityFieldType = 'text' | 'relation';
|
||||
|
||||
export type EntityFieldMetadata = {
|
||||
fieldName: string;
|
||||
@ -13,4 +9,5 @@ export type EntityFieldMetadata = {
|
||||
icon: JSX.Element;
|
||||
columnSize: number;
|
||||
filterIcon?: JSX.Element;
|
||||
relationType?: Entity; // TODO: condition this type with type === "relation"
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user