Fix major rework on view (#2262)

This commit is contained in:
Charles Bochet 2023-10-27 15:30:52 +02:00 committed by GitHub
parent 53e51aad52
commit ec3327ca81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 269 additions and 189 deletions

View File

@ -8,7 +8,6 @@ import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect
import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem';
import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown';
import { TableOptionsHotkeyScope } from '@/ui/data/data-table/types/TableOptionsHotkeyScope';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
@ -93,11 +92,7 @@ export const CompanyTable = () => {
<ViewBarEffect />
<ViewBar
optionsDropdownButton={
<TableOptionsDropdown
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
/>
}
optionsDropdownButton={<TableOptionsDropdown />}
optionsDropdownScopeId="table-dropdown-option"
/>
<CompanyTableEffect />

View File

@ -1,8 +1,11 @@
import { useRecoilCallback } from 'recoil';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { currentTableCellInEditModePositionState } from '../states/currentTableCellInEditModePositionState';
import { isTableCellInEditModeFamilyState } from '../states/isTableCellInEditModeFamilyState';
import { useSetSoftFocusOnCurrentTableCell } from '../table-cell/hooks/useSetSoftFocusOnCurrentTableCell';
import { TableHotkeyScope } from '../types/TableHotkeyScope';
export const useMoveSoftFocusToCurrentCellOnHover = () => {
const setSoftFocusOnCurrentTableCell = useSetSoftFocusOnCurrentTableCell();
@ -18,6 +21,17 @@ export const useMoveSoftFocusToCurrentCellOnHover = () => {
isTableCellInEditModeFamilyState(currentTableCellInEditModePosition),
);
const currentHotkeyScope = snapshot
.getLoadable(currentHotkeyScopeState)
.valueOrThrow();
if (
currentHotkeyScope.scope !== TableHotkeyScope.TableSoftFocus &&
currentHotkeyScope.scope !== TableHotkeyScope.CellEditMode
) {
return;
}
if (!isSomeCellInEditMode.contents) {
setSoftFocusOnCurrentTableCell();
}

View File

@ -1,27 +1,21 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useView } from '@/views/hooks/useView';
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
import { TableOptionsDropdownButton } from './TableOptionsDropdownButton';
import { TableOptionsDropdownContent } from './TableOptionsDropdownContent';
type TableOptionsDropdownProps = {
customHotkeyScope: HotkeyScope;
};
export const TableOptionsDropdown = ({
customHotkeyScope,
}: TableOptionsDropdownProps) => {
export const TableOptionsDropdown = () => {
const { setViewEditMode } = useView();
return (
<DropdownScope dropdownScopeId={TableOptionsDropdownId}>
<Dropdown
clickableComponent={<TableOptionsDropdownButton />}
dropdownHotkeyScope={customHotkeyScope}
dropdownHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
dropdownOffset={{ y: 8 }}
dropdownComponents={<TableOptionsDropdownContent />}
onClickOutside={() => setViewEditMode('none')}

View File

@ -81,6 +81,7 @@ export const TableOptionsDropdownContent = () => {
useScopedHotkeys(
Key.Enter,
() => {
console.log('enter');
const name = viewEditInputRef.current?.value;
handleViewNameSubmit(name);
resetMenu();

View File

@ -3,6 +3,7 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId';
import {
IconChevronDown,
IconList,
@ -76,23 +77,31 @@ export const ViewsDropdownButton = ({
entityCountInCurrentView,
} = useViewInternalStates(scopeId, currentViewId);
const { isDropdownOpen, closeDropdown } = useDropdown({
const {
isDropdownOpen: isViewsDropdownOpen,
closeDropdown: closeViewsDropdown,
} = useDropdown({
dropdownScopeId: ViewsDropdownId,
});
const { openDropdown: openOptionsDropdown } = useDropdown({
dropdownScopeId: TableOptionsDropdownId,
});
const handleViewSelect = useRecoilCallback(
() => async (viewId: string) => {
setCurrentViewId(viewId);
closeDropdown();
closeViewsDropdown();
},
[setCurrentViewId, closeDropdown],
[setCurrentViewId, closeViewsDropdown],
);
const handleAddViewButtonClick = () => {
setViewEditMode('create');
onViewEditModeChange?.();
closeDropdown();
closeViewsDropdown();
openOptionsDropdown();
};
const handleEditViewButtonClick = (
@ -103,7 +112,8 @@ export const ViewsDropdownButton = ({
setCurrentViewId(viewId);
setViewEditMode('edit');
onViewEditModeChange?.();
closeDropdown();
closeViewsDropdown();
openOptionsDropdown();
};
const handleDeleteViewButtonClick = async (
@ -113,7 +123,7 @@ export const ViewsDropdownButton = ({
event.stopPropagation();
await removeView(viewId);
closeDropdown();
closeViewsDropdown();
};
return (
@ -121,7 +131,7 @@ export const ViewsDropdownButton = ({
<Dropdown
dropdownHotkeyScope={hotkeyScope}
clickableComponent={
<StyledDropdownButtonContainer isUnfolded={isDropdownOpen}>
<StyledDropdownButtonContainer isUnfolded={isViewsDropdownOpen}>
<StyledViewIcon size={theme.icon.size.md} />
<StyledViewName>{currentView?.name}</StyledViewName>
<StyledDropdownLabelAdornments>

View File

@ -1,4 +1,3 @@
/* eslint-disable no-console */
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilCallback } from 'recoil';
@ -32,7 +31,10 @@ export const useViewFields = (viewScopeId: string) => {
const persistViewFields = useRecoilCallback(
({ snapshot }) =>
async (viewFieldsToPersist: ColumnDefinition<FieldMetadata>[]) => {
async (
viewFieldsToPersist: ColumnDefinition<FieldMetadata>[],
viewId?: string,
) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
@ -45,7 +47,7 @@ export const useViewFields = (viewScopeId: string) => {
.getLoadable(
savedViewFieldByKeyScopedFamilySelector({
viewScopeId: viewScopeId,
viewId: currentViewId,
viewId: viewId ?? currentViewId,
}),
)
.getValue();
@ -65,7 +67,7 @@ export const useViewFields = (viewScopeId: string) => {
variables: {
data: viewFieldsToCreate.map((viewField) => ({
...toViewFieldInput(objectId, viewField),
viewId: currentViewId,
viewId: viewId ?? currentViewId,
})),
},
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
@ -89,7 +91,10 @@ export const useViewFields = (viewScopeId: string) => {
index: viewField.index,
},
where: {
viewId_key: { key: viewField.key, viewId: currentViewId },
viewId_key: {
key: viewField.key,
viewId: viewId ?? currentViewId,
},
},
},
refetchQueries: [getOperationName(GET_VIEWS) ?? ''],

View File

@ -1,10 +1,11 @@
import { useCallback } from 'react';
import { useRecoilCallback } from 'recoil';
import { Filter } from '@/ui/data/filter/types/Filter';
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { availableFiltersScopedState } from '@/views/states/availableFiltersScopedState';
import { currentViewFiltersScopedFamilyState } from '@/views/states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState';
import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import {
useCreateViewFiltersMutation,
@ -12,94 +13,86 @@ import {
useUpdateViewFilterMutation,
} from '~/generated/graphql';
import { useViewStates } from '../useViewStates';
export const useViewFilters = (viewScopeId: string) => {
const { currentViewId } = useViewStates(viewScopeId);
const [createViewFiltersMutation] = useCreateViewFiltersMutation();
const [updateViewFilterMutation] = useUpdateViewFilterMutation();
const [deleteViewFiltersMutation] = useDeleteViewFiltersMutation();
const _createViewFilters = useCallback(
(
filters: Filter[],
availableFilters: FilterDefinition[] = [],
viewId = currentViewId,
) => {
if (!viewId || !filters.length) {
return;
}
if (!availableFilters) {
return;
}
return createViewFiltersMutation({
variables: {
data: filters.map((filter) => ({
displayValue: filter.displayValue ?? filter.value,
key: filter.key,
name:
availableFilters.find(({ key }) => key === filter.key)?.label ??
'',
operand: filter.operand,
value: filter.value,
viewId,
})),
},
});
},
[createViewFiltersMutation, currentViewId],
);
const _updateViewFilters = useCallback(
(filters: Filter[], viewId = currentViewId) => {
if (!viewId || !filters.length) return;
return Promise.all(
filters.map((filter) =>
updateViewFilterMutation({
variables: {
data: {
displayValue: filter.displayValue ?? filter.value,
operand: filter.operand,
value: filter.value,
},
where: {
viewId_key: { key: filter.key, viewId: viewId },
},
},
}),
),
);
},
[currentViewId, updateViewFilterMutation],
);
const _deleteViewFilters = useCallback(
(filterKeys: string[], viewId = currentViewId) => {
if (!viewId || !filterKeys.length) return;
return deleteViewFiltersMutation({
variables: {
where: {
key: { in: filterKeys },
viewId: { equals: viewId },
},
},
});
},
[currentViewId, deleteViewFiltersMutation],
);
const persistViewFilters = useRecoilCallback(
({ snapshot }) =>
async () => {
({ snapshot, set }) =>
async (viewId?: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
if (!currentViewId) {
return;
}
const _createViewFilters = (
filters: Filter[],
availableFilters: FilterDefinition[] = [],
) => {
if (!currentViewId || !filters.length) {
return;
}
if (!availableFilters) {
return;
}
return createViewFiltersMutation({
variables: {
data: filters.map((filter) => ({
displayValue: filter.displayValue ?? filter.value,
key: filter.key,
name:
availableFilters.find(({ key }) => key === filter.key)
?.label ?? '',
operand: filter.operand,
value: filter.value,
viewId: viewId ?? currentViewId,
})),
},
});
};
const _updateViewFilters = (filters: Filter[]) => {
if (!currentViewId || !filters.length) return;
return Promise.all(
filters.map((filter) =>
updateViewFilterMutation({
variables: {
data: {
displayValue: filter.displayValue ?? filter.value,
operand: filter.operand,
value: filter.value,
},
where: {
viewId_key: {
key: filter.key,
viewId: viewId ?? currentViewId,
},
},
},
}),
),
);
};
const _deleteViewFilters = (filterKeys: string[]) => {
if (!currentViewId || !filterKeys.length) return;
return deleteViewFiltersMutation({
variables: {
where: {
key: { in: filterKeys },
viewId: { equals: viewId ?? currentViewId },
},
},
});
};
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
@ -151,13 +144,19 @@ export const useViewFilters = (viewScopeId: string) => {
(previousFilterKey) => !filterKeys.includes(previousFilterKey),
);
await _deleteViewFilters(filterKeysToDelete);
set(
savedViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: viewId ?? currentViewId,
}),
currentViewFilters,
);
},
[
currentViewId,
viewScopeId,
_createViewFilters,
_updateViewFilters,
_deleteViewFilters,
createViewFiltersMutation,
updateViewFilterMutation,
deleteViewFiltersMutation,
],
);

View File

@ -1,10 +1,10 @@
/* eslint-disable no-console */
import { useCallback } from 'react';
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { Sort } from '@/ui/data/sort/types/Sort';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState';
import { savedViewSortsByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewSortsByKeyScopedFamilySelector';
import {
useCreateViewSortsMutation,
@ -16,75 +16,71 @@ import {
import { useViewStates } from '../useViewStates';
export const useViewSorts = (viewScopeId: string) => {
const { currentViewId, setCurrentViewSorts } = useViewStates(viewScopeId);
const { setCurrentViewSorts } = useViewStates(viewScopeId);
const [createViewSortsMutation] = useCreateViewSortsMutation();
const [updateViewSortMutation] = useUpdateViewSortMutation();
const [deleteViewSortsMutation] = useDeleteViewSortsMutation();
const _createViewSorts = useCallback(
(sorts: Sort[], viewId = currentViewId) => {
if (!viewId || !sorts.length) return;
return createViewSortsMutation({
variables: {
data: sorts.map((sort) => ({
key: sort.key,
direction: sort.direction as ViewSortDirection,
name: sort.definition.label,
viewId,
})),
},
});
},
[createViewSortsMutation, currentViewId],
);
const _updateViewSorts = useCallback(
(sorts: Sort[]) => {
if (!currentViewId || !sorts.length) return;
return Promise.all(
sorts.map((sort) =>
updateViewSortMutation({
variables: {
data: {
direction: sort.direction as ViewSortDirection,
},
where: {
viewId_key: { key: sort.key, viewId: currentViewId },
},
},
}),
),
);
},
[currentViewId, updateViewSortMutation],
);
const _deleteViewSorts = useCallback(
(sortKeys: string[]) => {
if (!currentViewId || !sortKeys.length) return;
return deleteViewSortsMutation({
variables: {
where: {
key: { in: sortKeys },
viewId: { equals: currentViewId },
},
},
});
},
[currentViewId, deleteViewSortsMutation],
);
const persistViewSorts = useRecoilCallback(
({ snapshot }) =>
async () => {
({ snapshot, set }) =>
async (viewId?: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
if (!currentViewId) {
return;
}
const _createViewSorts = (sorts: Sort[]) => {
if (!currentViewId || !sorts.length) return;
return createViewSortsMutation({
variables: {
data: sorts.map((sort) => ({
key: sort.key,
direction: sort.direction as ViewSortDirection,
name: sort.definition.label,
viewId: viewId ?? currentViewId,
})),
},
});
};
const _updateViewSorts = (sorts: Sort[]) => {
if (!currentViewId || !sorts.length) return;
return Promise.all(
sorts.map((sort) =>
updateViewSortMutation({
variables: {
data: {
direction: sort.direction as ViewSortDirection,
},
where: {
viewId_key: {
key: sort.key,
viewId: viewId ?? currentViewId,
},
},
},
}),
),
);
};
const _deleteViewSorts = (sortKeys: string[]) => {
if (!currentViewId || !sortKeys.length) return;
return deleteViewSortsMutation({
variables: {
where: {
key: { in: sortKeys },
viewId: { equals: viewId ?? currentViewId },
},
},
});
};
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
@ -127,13 +123,19 @@ export const useViewSorts = (viewScopeId: string) => {
(previousSortKey) => !sortKeys.includes(previousSortKey),
);
await _deleteViewSorts(sortKeysToDelete);
set(
savedViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: viewId ?? currentViewId,
}),
currentViewSorts,
);
},
[
currentViewId,
viewScopeId,
_createViewSorts,
_updateViewSorts,
_deleteViewSorts,
createViewSortsMutation,
updateViewSortMutation,
deleteViewSortsMutation,
],
);

View File

@ -1,4 +1,3 @@
import { useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback } from 'recoil';
import { v4 } from 'uuid';
@ -8,7 +7,9 @@ import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-i
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
@ -90,19 +91,77 @@ export const useView = (props?: UseViewProps) => {
setIsViewBarExpanded?.(false);
});
const createView = useCallback(
async (name: string) => {
const newViewId = v4();
await internalCreateView({ id: v4(), name });
const createView = useRecoilCallback(
({ snapshot, set }) =>
async (name: string) => {
const newViewId = v4();
await internalCreateView({ id: newViewId, name });
// await persistViewFields();
await persistViewFilters();
await persistViewSorts();
//setCurrentViewId(newViewId);
const currentViewFields = snapshot
.getLoadable(
currentViewFieldsScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
setSearchParams({ view: newViewId });
},
[internalCreateView, persistViewFilters, persistViewSorts, setSearchParams],
set(
currentViewFieldsScopedFamilyState({ scopeId, familyKey: newViewId }),
currentViewFields,
);
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
set(
currentViewFiltersScopedFamilyState({
scopeId,
familyKey: newViewId,
}),
currentViewFilters,
);
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
scopeId,
familyKey: currentViewId || '',
}),
)
.getValue();
set(
currentViewSortsScopedFamilyState({
scopeId,
familyKey: newViewId,
}),
currentViewSorts,
);
await persistViewFields(currentViewFields, newViewId);
await persistViewFilters(newViewId);
await persistViewSorts(newViewId);
setCurrentViewId(newViewId);
setSearchParams({ view: newViewId });
},
[
currentViewId,
internalCreateView,
persistViewFields,
persistViewFilters,
persistViewSorts,
scopeId,
setCurrentViewId,
setSearchParams,
],
);
const updateCurrentView = async () => {

View File

@ -46,7 +46,7 @@ export const useViewInternalStates = (
scopeId,
);
const familyItemId = viewId ? viewId : currentViewId;
const familyItemId = viewId ?? currentViewId;
const currentView = useRecoilValue(currentViewScopedSelector(scopeId));

View File

@ -13,6 +13,7 @@ export const canPersistViewFiltersScopedFamilySelector = selectorFamily({
if (!viewId) {
return;
}
return !isDeeplyEqual(
get(
savedViewFiltersScopedFamilyState({