Feat/improve editable cell (#959)
* Removed isSomeInputInEditMode * Removed console.log * Added a first version of generic cell text * Removed metadata from entity table V1 * Fix * Fix * Fix
This commit is contained in:
@ -0,0 +1,31 @@
|
|||||||
|
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
import { useSetEntityTableData } from '../hooks/useSetEntityTableData';
|
||||||
|
import { defaultOrderBy } from '../queries';
|
||||||
|
|
||||||
|
export function GenericEntityTableData({
|
||||||
|
useGetRequest,
|
||||||
|
getRequestResultKey,
|
||||||
|
orderBy = defaultOrderBy,
|
||||||
|
whereFilters,
|
||||||
|
fieldMetadataArray,
|
||||||
|
}: {
|
||||||
|
useGetRequest: any;
|
||||||
|
getRequestResultKey: string;
|
||||||
|
orderBy?: any;
|
||||||
|
whereFilters?: any;
|
||||||
|
fieldMetadataArray: EntityFieldMetadata[];
|
||||||
|
}) {
|
||||||
|
const setEntityTableData = useSetEntityTableData();
|
||||||
|
|
||||||
|
useGetRequest({
|
||||||
|
variables: { orderBy, where: whereFilters },
|
||||||
|
onCompleted: (data: any) => {
|
||||||
|
const entities = data[getRequestResultKey] ?? [];
|
||||||
|
|
||||||
|
setEntityTableData(entities, fieldMetadataArray);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
@ -1,7 +1,3 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { isFetchingEntityTableDataState } from '@/ui/table/states/isFetchingEntityTableDataState';
|
|
||||||
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
|
|
||||||
import {
|
import {
|
||||||
PersonOrderByWithRelationInput,
|
PersonOrderByWithRelationInput,
|
||||||
useGetPeopleQuery,
|
useGetPeopleQuery,
|
||||||
@ -17,12 +13,6 @@ export function PeopleEntityTableData({
|
|||||||
orderBy?: PersonOrderByWithRelationInput[];
|
orderBy?: PersonOrderByWithRelationInput[];
|
||||||
whereFilters?: any;
|
whereFilters?: any;
|
||||||
}) {
|
}) {
|
||||||
const [, setTableRowIds] = useRecoilState(tableRowIdsState);
|
|
||||||
|
|
||||||
const [, setIsFetchingEntityTableData] = useRecoilState(
|
|
||||||
isFetchingEntityTableDataState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setPeopleEntityTable = useSetPeopleEntityTable();
|
const setPeopleEntityTable = useSetPeopleEntityTable();
|
||||||
|
|
||||||
useGetPeopleQuery({
|
useGetPeopleQuery({
|
||||||
@ -30,19 +20,7 @@ export function PeopleEntityTableData({
|
|||||||
onCompleted: (data) => {
|
onCompleted: (data) => {
|
||||||
const people = data.people ?? [];
|
const people = data.people ?? [];
|
||||||
|
|
||||||
const peopleIds = people.map((person) => person.id);
|
|
||||||
|
|
||||||
setTableRowIds((currentRowIds) => {
|
|
||||||
if (JSON.stringify(currentRowIds) !== JSON.stringify(peopleIds)) {
|
|
||||||
return peopleIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
return currentRowIds;
|
|
||||||
});
|
|
||||||
|
|
||||||
setPeopleEntityTable(people);
|
setPeopleEntityTable(people);
|
||||||
|
|
||||||
setIsFetchingEntityTableData(false);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { IconBriefcase, IconMap } from '@tabler/icons-react';
|
||||||
|
|
||||||
|
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
export const peopleFieldMetadataArray: EntityFieldMetadata[] = [
|
||||||
|
{
|
||||||
|
fieldName: 'city',
|
||||||
|
label: 'City',
|
||||||
|
icon: <IconMap size={16} />,
|
||||||
|
columnSize: 150,
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'jobTitle',
|
||||||
|
label: 'Job title',
|
||||||
|
icon: <IconBriefcase size={16} />,
|
||||||
|
columnSize: 150,
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
];
|
||||||
72
front/src/modules/people/hooks/useSetEntityTableData.ts
Normal file
72
front/src/modules/people/hooks/useSetEntityTableData.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { useRecoilCallback } from 'recoil';
|
||||||
|
|
||||||
|
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
|
||||||
|
import { entityFieldMetadataArrayState } from '@/ui/table/states/entityFieldMetadataArrayState';
|
||||||
|
import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState';
|
||||||
|
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
import { availableFiltersScopedState } from '../../ui/filter-n-sort/states/availableFiltersScopedState';
|
||||||
|
import { useContextScopeId } from '../../ui/recoil-scope/hooks/useContextScopeId';
|
||||||
|
import { useResetTableRowSelection } from '../../ui/table/hooks/useResetTableRowSelection';
|
||||||
|
import { entityTableDimensionsState } from '../../ui/table/states/entityTableDimensionsState';
|
||||||
|
import { isFetchingEntityTableDataState } from '../../ui/table/states/isFetchingEntityTableDataState';
|
||||||
|
import { TableContext } from '../../ui/table/states/TableContext';
|
||||||
|
import { tableRowIdsState } from '../../ui/table/states/tableRowIdsState';
|
||||||
|
|
||||||
|
export function useSetEntityTableData() {
|
||||||
|
const resetTableRowSelection = useResetTableRowSelection();
|
||||||
|
|
||||||
|
const tableContextScopeId = useContextScopeId(TableContext);
|
||||||
|
|
||||||
|
return useRecoilCallback(
|
||||||
|
({ set, snapshot }) =>
|
||||||
|
<T extends { id: string }>(
|
||||||
|
newEntityArray: T[],
|
||||||
|
entityFieldMetadataArray: EntityFieldMetadata[],
|
||||||
|
) => {
|
||||||
|
for (const entity of newEntityArray) {
|
||||||
|
const currentEntity = snapshot
|
||||||
|
.getLoadable(tableEntitiesFamilyState(entity.id))
|
||||||
|
.valueOrThrow();
|
||||||
|
|
||||||
|
if (JSON.stringify(currentEntity) !== JSON.stringify(entity)) {
|
||||||
|
set(tableEntitiesFamilyState(entity.id), entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityIds = newEntityArray.map((entity) => entity.id);
|
||||||
|
|
||||||
|
set(tableRowIdsState, (currentRowIds) => {
|
||||||
|
if (JSON.stringify(currentRowIds) !== JSON.stringify(entityIds)) {
|
||||||
|
return entityIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentRowIds;
|
||||||
|
});
|
||||||
|
|
||||||
|
resetTableRowSelection();
|
||||||
|
|
||||||
|
set(entityTableDimensionsState, {
|
||||||
|
numberOfColumns: entityFieldMetadataArray.length,
|
||||||
|
numberOfRows: entityIds.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
const filters = entityFieldMetadataArray.map(
|
||||||
|
(fieldMetadata) =>
|
||||||
|
({
|
||||||
|
field: fieldMetadata.fieldName,
|
||||||
|
icon: fieldMetadata.filterIcon,
|
||||||
|
label: fieldMetadata.label,
|
||||||
|
type: fieldMetadata.type,
|
||||||
|
} as FilterDefinition),
|
||||||
|
);
|
||||||
|
|
||||||
|
set(availableFiltersScopedState(tableContextScopeId), filters);
|
||||||
|
|
||||||
|
set(entityFieldMetadataArrayState, entityFieldMetadataArray);
|
||||||
|
|
||||||
|
set(isFetchingEntityTableDataState, false);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
}
|
||||||
18
front/src/modules/people/hooks/useUpdatePeopleField.ts
Normal file
18
front/src/modules/people/hooks/useUpdatePeopleField.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { useUpdateOnePersonMutation } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export function useUpdatePeopleField() {
|
||||||
|
const [updatePeople] = useUpdateOnePersonMutation();
|
||||||
|
|
||||||
|
return function updatePeopleField(
|
||||||
|
peopleId: string,
|
||||||
|
fieldName: string,
|
||||||
|
fieldValue: unknown,
|
||||||
|
) {
|
||||||
|
updatePeople({
|
||||||
|
variables: {
|
||||||
|
where: { id: peopleId },
|
||||||
|
data: { [fieldName]: fieldValue },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
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"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
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,47 @@
|
|||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
|
||||||
|
import { useEntityUpdateFieldHook } from '@/ui/table/hooks/useCellUpdateFieldHook';
|
||||||
|
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 useUpdateField = useEntityUpdateFieldHook();
|
||||||
|
const updateField = useUpdateField?.();
|
||||||
|
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
front/src/modules/people/table/components/PeopleTableV2.tsx
Normal file
53
front/src/modules/people/table/components/PeopleTableV2.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
import { defaultOrderBy } from '@/companies/queries';
|
||||||
|
import { GenericEntityTableData } from '@/people/components/GenericEntityTableData';
|
||||||
|
import { peopleFieldMetadataArray } from '@/people/constants/peopleFieldMetadataArray';
|
||||||
|
import { useUpdatePeopleField } from '@/people/hooks/useUpdatePeopleField';
|
||||||
|
import { PeopleSelectedSortType } from '@/people/queries';
|
||||||
|
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
|
||||||
|
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||||
|
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
|
||||||
|
import { IconList } from '@/ui/icon';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { EntityTable } from '@/ui/table/components/EntityTableV2';
|
||||||
|
import { TableContext } from '@/ui/table/states/TableContext';
|
||||||
|
import {
|
||||||
|
PersonOrderByWithRelationInput,
|
||||||
|
useGetPeopleQuery,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
import { availableSorts } from '~/pages/people/people-sorts';
|
||||||
|
|
||||||
|
export function PeopleTable() {
|
||||||
|
const [orderBy, setOrderBy] =
|
||||||
|
useState<PersonOrderByWithRelationInput[]>(defaultOrderBy);
|
||||||
|
|
||||||
|
const updateSorts = useCallback((sorts: Array<PeopleSelectedSortType>) => {
|
||||||
|
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const filters = useRecoilScopedValue(filtersScopedState, TableContext);
|
||||||
|
|
||||||
|
const whereFilters = useMemo(() => {
|
||||||
|
return { AND: filters.map(turnFilterIntoWhereClause) };
|
||||||
|
}, [filters]) as any;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<GenericEntityTableData
|
||||||
|
getRequestResultKey="people"
|
||||||
|
useGetRequest={useGetPeopleQuery}
|
||||||
|
orderBy={orderBy}
|
||||||
|
whereFilters={whereFilters}
|
||||||
|
fieldMetadataArray={peopleFieldMetadataArray}
|
||||||
|
/>
|
||||||
|
<EntityTable
|
||||||
|
viewName="All People"
|
||||||
|
viewIcon={<IconList size={16} />}
|
||||||
|
availableSorts={availableSorts}
|
||||||
|
onSortsUpdate={updateSorts}
|
||||||
|
useUpdateField={useUpdatePeopleField}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
36
front/src/modules/ui/table/components/EntityTableBodyV2.tsx
Normal file
36
front/src/modules/ui/table/components/EntityTableBodyV2.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { isNavbarSwitchingSizeState } from '@/ui/layout/states/isNavbarSwitchingSizeState';
|
||||||
|
|
||||||
|
import { isFetchingEntityTableDataState } from '../states/isFetchingEntityTableDataState';
|
||||||
|
import { RowIdContext } from '../states/RowIdContext';
|
||||||
|
import { RowIndexContext } from '../states/RowIndexContext';
|
||||||
|
import { tableRowIdsState } from '../states/tableRowIdsState';
|
||||||
|
|
||||||
|
import { EntityTableRow } from './EntityTableRowV2';
|
||||||
|
|
||||||
|
export function EntityTableBody() {
|
||||||
|
const rowIds = useRecoilValue(tableRowIdsState);
|
||||||
|
|
||||||
|
const isNavbarSwitchingSize = useRecoilValue(isNavbarSwitchingSizeState);
|
||||||
|
|
||||||
|
const isFetchingEntityTableData = useRecoilValue(
|
||||||
|
isFetchingEntityTableDataState,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isFetchingEntityTableData || isNavbarSwitchingSize) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tbody>
|
||||||
|
{rowIds.map((rowId, index) => (
|
||||||
|
<RowIdContext.Provider value={rowId} key={rowId}>
|
||||||
|
<RowIndexContext.Provider value={index}>
|
||||||
|
<EntityTableRow rowId={rowId} />
|
||||||
|
</RowIndexContext.Provider>
|
||||||
|
</RowIdContext.Provider>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
);
|
||||||
|
}
|
||||||
50
front/src/modules/ui/table/components/EntityTableCellV2.tsx
Normal file
50
front/src/modules/ui/table/components/EntityTableCellV2.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { GenericEditableCell } from '@/people/table/components/GenericEditableCell';
|
||||||
|
|
||||||
|
import { RecoilScope } from '../../recoil-scope/components/RecoilScope';
|
||||||
|
import { useCurrentRowSelected } from '../hooks/useCurrentRowSelected';
|
||||||
|
import { ColumnIndexContext } from '../states/ColumnIndexContext';
|
||||||
|
import { contextMenuPositionState } from '../states/contextMenuPositionState';
|
||||||
|
import { EntityFieldMetadataContext } from '../states/EntityFieldMetadataContext';
|
||||||
|
|
||||||
|
export function EntityTableCell({ cellIndex }: { cellIndex: number }) {
|
||||||
|
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||||
|
|
||||||
|
const { setCurrentRowSelected } = useCurrentRowSelected();
|
||||||
|
|
||||||
|
function handleContextMenu(event: React.MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
setCurrentRowSelected(true);
|
||||||
|
|
||||||
|
setContextMenuPosition({
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityFieldMetadata = useContext(EntityFieldMetadataContext);
|
||||||
|
|
||||||
|
if (!entityFieldMetadata) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope>
|
||||||
|
<ColumnIndexContext.Provider value={cellIndex}>
|
||||||
|
<td
|
||||||
|
onContextMenu={(event) => handleContextMenu(event)}
|
||||||
|
style={{
|
||||||
|
width: entityFieldMetadata.columnSize,
|
||||||
|
minWidth: entityFieldMetadata.columnSize,
|
||||||
|
maxWidth: entityFieldMetadata.columnSize,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GenericEditableCell entityFieldMetadata={entityFieldMetadata} />
|
||||||
|
</td>
|
||||||
|
</ColumnIndexContext.Provider>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldMetadataArrayState } from '../states/entityFieldMetadataArrayState';
|
||||||
|
|
||||||
|
import { ColumnHead } from './ColumnHead';
|
||||||
|
import { SelectAllCheckbox } from './SelectAllCheckbox';
|
||||||
|
|
||||||
|
export function EntityTableHeader() {
|
||||||
|
const fieldMetadataArray = useRecoilValue(entityFieldMetadataArrayState);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
minWidth: 30,
|
||||||
|
maxWidth: 30,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectAllCheckbox />
|
||||||
|
</th>
|
||||||
|
{fieldMetadataArray.map((fieldMetadata) => (
|
||||||
|
<th
|
||||||
|
key={fieldMetadata.fieldName.toString()}
|
||||||
|
style={{
|
||||||
|
width: fieldMetadata.columnSize,
|
||||||
|
minWidth: fieldMetadata.columnSize,
|
||||||
|
maxWidth: fieldMetadata.columnSize,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ColumnHead
|
||||||
|
viewName={fieldMetadata.label}
|
||||||
|
viewIcon={fieldMetadata.icon}
|
||||||
|
/>
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
);
|
||||||
|
}
|
||||||
38
front/src/modules/ui/table/components/EntityTableRowV2.tsx
Normal file
38
front/src/modules/ui/table/components/EntityTableRowV2.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { entityFieldMetadataArrayState } from '../states/entityFieldMetadataArrayState';
|
||||||
|
import { EntityFieldMetadataContext } from '../states/EntityFieldMetadataContext';
|
||||||
|
|
||||||
|
import { CheckboxCell } from './CheckboxCell';
|
||||||
|
import { EntityTableCell } from './EntityTableCellV2';
|
||||||
|
|
||||||
|
const StyledRow = styled.tr<{ selected: boolean }>`
|
||||||
|
background: ${(props) =>
|
||||||
|
props.selected ? props.theme.background.secondary : 'none'};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function EntityTableRow({ rowId }: { rowId: string }) {
|
||||||
|
const entityFieldMetadataArray = useRecoilValue(
|
||||||
|
entityFieldMetadataArrayState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledRow data-testid={`row-id-${rowId}`} selected={false}>
|
||||||
|
<td>
|
||||||
|
<CheckboxCell />
|
||||||
|
</td>
|
||||||
|
{entityFieldMetadataArray.map((entityFieldMetadata, columnIndex) => {
|
||||||
|
return (
|
||||||
|
<EntityFieldMetadataContext.Provider
|
||||||
|
value={entityFieldMetadata}
|
||||||
|
key={entityFieldMetadata.fieldName}
|
||||||
|
>
|
||||||
|
<EntityTableCell cellIndex={columnIndex} />
|
||||||
|
</EntityFieldMetadataContext.Provider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<td></td>
|
||||||
|
</StyledRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
136
front/src/modules/ui/table/components/EntityTableV2.tsx
Normal file
136
front/src/modules/ui/table/components/EntityTableV2.tsx
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { SelectedSortType, SortType } from '@/ui/filter-n-sort/types/interface';
|
||||||
|
import { useListenClickOutside } from '@/ui/hooks/useListenClickOutside';
|
||||||
|
|
||||||
|
import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
|
||||||
|
import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus';
|
||||||
|
import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext';
|
||||||
|
import { TableHeader } from '../table-header/components/TableHeader';
|
||||||
|
import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook';
|
||||||
|
|
||||||
|
import { EntityTableBody } from './EntityTableBodyV2';
|
||||||
|
import { EntityTableHeader } from './EntityTableHeaderV2';
|
||||||
|
|
||||||
|
const StyledTable = styled.table`
|
||||||
|
border-collapse: collapse;
|
||||||
|
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
border-spacing: 0;
|
||||||
|
margin-left: ${({ theme }) => theme.table.horizontalCellMargin};
|
||||||
|
margin-right: ${({ theme }) => theme.table.horizontalCellMargin};
|
||||||
|
table-layout: fixed;
|
||||||
|
width: calc(100% - ${({ theme }) => theme.table.horizontalCellMargin} * 2);
|
||||||
|
|
||||||
|
th {
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
|
border-collapse: collapse;
|
||||||
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
|
padding: 0;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
:first-of-type {
|
||||||
|
border-left-color: transparent;
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
:last-of-type {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
|
border-collapse: collapse;
|
||||||
|
color: ${({ theme }) => theme.font.color.primary};
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
:first-of-type {
|
||||||
|
border-left-color: transparent;
|
||||||
|
border-right-color: transparent;
|
||||||
|
}
|
||||||
|
:last-of-type {
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableWithHeader = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledTableWrapper = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
overflow: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type OwnProps<SortField> = {
|
||||||
|
viewName: string;
|
||||||
|
viewIcon?: React.ReactNode;
|
||||||
|
availableSorts?: Array<SortType<SortField>>;
|
||||||
|
onSortsUpdate?: (sorts: Array<SelectedSortType<SortField>>) => void;
|
||||||
|
onRowSelectionChange?: (rowSelection: string[]) => void;
|
||||||
|
useUpdateField: EntityUpdateFieldHook;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EntityTable<SortField>({
|
||||||
|
viewName,
|
||||||
|
viewIcon,
|
||||||
|
availableSorts,
|
||||||
|
onSortsUpdate,
|
||||||
|
useUpdateField,
|
||||||
|
}: OwnProps<SortField>) {
|
||||||
|
const tableBodyRef = React.useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useMapKeyboardToSoftFocus();
|
||||||
|
|
||||||
|
const leaveTableFocus = useLeaveTableFocus();
|
||||||
|
|
||||||
|
useListenClickOutside({
|
||||||
|
refs: [tableBodyRef],
|
||||||
|
callback: () => {
|
||||||
|
leaveTableFocus();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EntityUpdateFieldHookContext.Provider value={useUpdateField}>
|
||||||
|
<StyledTableWithHeader>
|
||||||
|
<StyledTableContainer ref={tableBodyRef}>
|
||||||
|
<TableHeader
|
||||||
|
viewName={viewName}
|
||||||
|
viewIcon={viewIcon}
|
||||||
|
availableSorts={availableSorts}
|
||||||
|
onSortsUpdate={onSortsUpdate}
|
||||||
|
/>
|
||||||
|
<StyledTableWrapper>
|
||||||
|
<StyledTable>
|
||||||
|
<EntityTableHeader />
|
||||||
|
<EntityTableBody />
|
||||||
|
</StyledTable>
|
||||||
|
</StyledTableWrapper>
|
||||||
|
</StyledTableContainer>
|
||||||
|
</StyledTableWithHeader>
|
||||||
|
</EntityUpdateFieldHookContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,12 +1,10 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
import { useRecoilCallback } from 'recoil';
|
|
||||||
|
|
||||||
import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
||||||
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||||
|
|
||||||
import { useCloseCurrentCellInEditMode } from '../../hooks/useClearCellInEditMode';
|
import { useCloseCurrentCellInEditMode } from '../../hooks/useClearCellInEditMode';
|
||||||
import { CellHotkeyScopeContext } from '../../states/CellHotkeyScopeContext';
|
import { CellHotkeyScopeContext } from '../../states/CellHotkeyScopeContext';
|
||||||
import { isSomeInputInEditModeState } from '../../states/isSomeInputInEditModeState';
|
|
||||||
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
import { TableHotkeyScope } from '../../types/TableHotkeyScope';
|
||||||
|
|
||||||
import { useCurrentCellEditMode } from './useCurrentCellEditMode';
|
import { useCurrentCellEditMode } from './useCurrentCellEditMode';
|
||||||
@ -29,33 +27,18 @@ export function useEditableCell() {
|
|||||||
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
const openEditableCell = useRecoilCallback(
|
function openEditableCell() {
|
||||||
({ snapshot, set }) =>
|
setCurrentCellInEditMode();
|
||||||
() => {
|
|
||||||
const isSomeInputInEditMode = snapshot
|
|
||||||
.getLoadable(isSomeInputInEditModeState)
|
|
||||||
.valueOrThrow();
|
|
||||||
|
|
||||||
if (!isSomeInputInEditMode) {
|
if (customCellHotkeyScope) {
|
||||||
set(isSomeInputInEditModeState, true);
|
setHotkeyScope(
|
||||||
|
customCellHotkeyScope.scope,
|
||||||
setCurrentCellInEditMode();
|
customCellHotkeyScope.customScopes,
|
||||||
|
);
|
||||||
if (customCellHotkeyScope) {
|
} else {
|
||||||
setHotkeyScope(
|
setHotkeyScope(DEFAULT_CELL_SCOPE.scope, DEFAULT_CELL_SCOPE.customScopes);
|
||||||
customCellHotkeyScope.scope,
|
}
|
||||||
customCellHotkeyScope.customScopes,
|
}
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setHotkeyScope(
|
|
||||||
DEFAULT_CELL_SCOPE.scope,
|
|
||||||
DEFAULT_CELL_SCOPE.customScopes,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setCurrentCellInEditMode, setHotkeyScope, customCellHotkeyScope],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
closeEditableCell,
|
closeEditableCell,
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
|
||||||
|
import { EntityUpdateFieldHookContext } from '../states/EntityUpdateFieldHookContext';
|
||||||
|
|
||||||
|
export function useEntityUpdateFieldHook() {
|
||||||
|
return useContext(EntityUpdateFieldHookContext);
|
||||||
|
}
|
||||||
@ -2,7 +2,6 @@ import { useRecoilCallback } from 'recoil';
|
|||||||
|
|
||||||
import { currentCellInEditModePositionState } from '../states/currentCellInEditModePositionState';
|
import { currentCellInEditModePositionState } from '../states/currentCellInEditModePositionState';
|
||||||
import { isCellInEditModeFamilyState } from '../states/isCellInEditModeFamilyState';
|
import { isCellInEditModeFamilyState } from '../states/isCellInEditModeFamilyState';
|
||||||
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
|
|
||||||
|
|
||||||
export function useCloseCurrentCellInEditMode() {
|
export function useCloseCurrentCellInEditMode() {
|
||||||
return useRecoilCallback(({ set, snapshot }) => {
|
return useRecoilCallback(({ set, snapshot }) => {
|
||||||
@ -12,11 +11,6 @@ export function useCloseCurrentCellInEditMode() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
set(isCellInEditModeFamilyState(currentCellInEditModePosition), false);
|
set(isCellInEditModeFamilyState(currentCellInEditModePosition), false);
|
||||||
|
|
||||||
// TODO: find a way to remove this
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
||||||
|
|
||||||
set(isSomeInputInEditModeState, false);
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
|||||||
import { currentHotkeyScopeState } from '@/ui/hotkey/states/internal/currentHotkeyScopeState';
|
import { currentHotkeyScopeState } from '@/ui/hotkey/states/internal/currentHotkeyScopeState';
|
||||||
|
|
||||||
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
|
import { isSoftFocusActiveState } from '../states/isSoftFocusActiveState';
|
||||||
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
|
|
||||||
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
||||||
|
|
||||||
import { useCloseCurrentCellInEditMode } from './useClearCellInEditMode';
|
import { useCloseCurrentCellInEditMode } from './useClearCellInEditMode';
|
||||||
@ -23,19 +22,11 @@ export function useLeaveTableFocus() {
|
|||||||
.getLoadable(isSoftFocusActiveState)
|
.getLoadable(isSoftFocusActiveState)
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
const isSomeInputInEditMode = snapshot
|
|
||||||
.getLoadable(isSomeInputInEditModeState)
|
|
||||||
.valueOrThrow();
|
|
||||||
|
|
||||||
const currentHotkeyScope = snapshot
|
const currentHotkeyScope = snapshot
|
||||||
.getLoadable(currentHotkeyScopeState)
|
.getLoadable(currentHotkeyScopeState)
|
||||||
.valueOrThrow();
|
.valueOrThrow();
|
||||||
|
|
||||||
if (isSomeInputInEditMode) {
|
if (!isSoftFocusActive) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isSoftFocusActive && !isSomeInputInEditMode) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,8 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
|
|
||||||
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
|
||||||
import { isSomeInputInEditModeState } from '../states/isSomeInputInEditModeState';
|
|
||||||
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
||||||
|
|
||||||
import { useDisableSoftFocus } from './useDisableSoftFocus';
|
import { useDisableSoftFocus } from './useDisableSoftFocus';
|
||||||
@ -16,50 +14,40 @@ export function useMapKeyboardToSoftFocus() {
|
|||||||
const disableSoftFocus = useDisableSoftFocus();
|
const disableSoftFocus = useDisableSoftFocus();
|
||||||
const setHotkeyScope = useSetHotkeyScope();
|
const setHotkeyScope = useSetHotkeyScope();
|
||||||
|
|
||||||
const [isSomeInputInEditMode] = useRecoilState(isSomeInputInEditModeState);
|
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.ArrowUp, `${Key.Shift}+${Key.Enter}`],
|
[Key.ArrowUp, `${Key.Shift}+${Key.Enter}`],
|
||||||
() => {
|
() => {
|
||||||
if (!isSomeInputInEditMode) {
|
moveUp();
|
||||||
moveUp();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TableHotkeyScope.TableSoftFocus,
|
TableHotkeyScope.TableSoftFocus,
|
||||||
[moveUp, isSomeInputInEditMode],
|
[moveUp],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
Key.ArrowDown,
|
Key.ArrowDown,
|
||||||
() => {
|
() => {
|
||||||
if (!isSomeInputInEditMode) {
|
moveDown();
|
||||||
moveDown();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TableHotkeyScope.TableSoftFocus,
|
TableHotkeyScope.TableSoftFocus,
|
||||||
[moveDown, isSomeInputInEditMode],
|
[moveDown],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`],
|
[Key.ArrowLeft, `${Key.Shift}+${Key.Tab}`],
|
||||||
() => {
|
() => {
|
||||||
if (!isSomeInputInEditMode) {
|
moveLeft();
|
||||||
moveLeft();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TableHotkeyScope.TableSoftFocus,
|
TableHotkeyScope.TableSoftFocus,
|
||||||
[moveLeft, isSomeInputInEditMode],
|
[moveLeft],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.ArrowRight, Key.Tab],
|
[Key.ArrowRight, Key.Tab],
|
||||||
() => {
|
() => {
|
||||||
if (!isSomeInputInEditMode) {
|
moveRight();
|
||||||
moveRight();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TableHotkeyScope.TableSoftFocus,
|
TableHotkeyScope.TableSoftFocus,
|
||||||
[moveRight, isSomeInputInEditMode],
|
[moveRight],
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import { EntityFieldMetadata } from '../types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
export const EntityFieldMetadataContext =
|
||||||
|
createContext<EntityFieldMetadata | null>(null);
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
import { EntityUpdateFieldHook } from '../types/CellUpdateFieldHook';
|
||||||
|
|
||||||
|
export const EntityUpdateFieldHookContext =
|
||||||
|
createContext<EntityUpdateFieldHook | null>(null);
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { atom } from 'recoil';
|
||||||
|
|
||||||
|
import { EntityFieldMetadata } from '../types/EntityFieldMetadata';
|
||||||
|
|
||||||
|
export const entityFieldMetadataArrayState = atom<EntityFieldMetadata[]>({
|
||||||
|
key: 'entityFieldMetadataArrayState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
export const isSomeInputInEditModeState = atom<boolean>({
|
|
||||||
key: 'isSomeInputInEditModeState',
|
|
||||||
default: false,
|
|
||||||
});
|
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
export const tableEntitiesFamilyState = atomFamily<
|
||||||
|
Record<string, unknown> | null,
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'tableEntitiesFamilyState',
|
||||||
|
default: null,
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { tableEntitiesFamilyState } from './tableEntitiesFamilyState';
|
||||||
|
|
||||||
|
export const tableEntityFieldFamilySelector = selectorFamily({
|
||||||
|
key: 'tableEntityFieldFamilySelector',
|
||||||
|
get:
|
||||||
|
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(tableEntitiesFamilyState(entityId))?.[fieldName] as T,
|
||||||
|
set:
|
||||||
|
<T>({ fieldName, entityId }: { fieldName: string; entityId: string }) =>
|
||||||
|
({ set }, newValue: T) =>
|
||||||
|
set(tableEntitiesFamilyState(entityId), (prevState) => ({
|
||||||
|
...prevState,
|
||||||
|
[fieldName]: newValue,
|
||||||
|
})),
|
||||||
|
});
|
||||||
5
front/src/modules/ui/table/types/CellUpdateFieldHook.ts
Normal file
5
front/src/modules/ui/table/types/CellUpdateFieldHook.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type EntityUpdateFieldHook = () => <T>(
|
||||||
|
entityId: string,
|
||||||
|
fieldName: string,
|
||||||
|
value: T,
|
||||||
|
) => void | Promise<void>;
|
||||||
16
front/src/modules/ui/table/types/EntityFieldMetadata.ts
Normal file
16
front/src/modules/ui/table/types/EntityFieldMetadata.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export type EntityFieldType =
|
||||||
|
| 'text'
|
||||||
|
| 'number'
|
||||||
|
| 'date'
|
||||||
|
| 'select'
|
||||||
|
| 'checkbox'
|
||||||
|
| 'icon';
|
||||||
|
|
||||||
|
export type EntityFieldMetadata = {
|
||||||
|
fieldName: string;
|
||||||
|
label: string;
|
||||||
|
type: EntityFieldType;
|
||||||
|
icon: JSX.Element;
|
||||||
|
columnSize: number;
|
||||||
|
filterIcon?: JSX.Element;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user