New view picker (#4610)

* Implement new view picker

* Complete feature

* Fixes according to review
This commit is contained in:
Charles Bochet
2024-03-22 15:04:17 +01:00
committed by GitHub
parent d876b40056
commit 4a493b6ecf
61 changed files with 1216 additions and 422 deletions

View File

@ -0,0 +1,81 @@
import { useRecoilValue } from 'recoil';
import { Button } from '@/ui/input/button/components/Button';
import { ViewType } from '@/views/types/ViewType';
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewPickerPersistView } from '@/views/view-picker/hooks/useViewPickerPersistView';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
export const ViewPickerCreateOrEditButton = () => {
const { availableFieldsForKanban, navigateToSelectSettings } =
useGetAvailableFieldsForKanban();
const {
viewPickerIsPersistingState,
viewPickerKanbanFieldMetadataIdState,
viewPickerTypeState,
} = useViewPickerStates();
const { viewPickerMode } = useViewPickerMode();
const viewPickerType = useRecoilValue(viewPickerTypeState);
const viewPickerIsPersisting = useRecoilValue(viewPickerIsPersistingState);
const viewPickerKanbanFieldMetadataId = useRecoilValue(
viewPickerKanbanFieldMetadataIdState,
);
const { handleCreate, handleDelete } = useViewPickerPersistView();
if (viewPickerMode === 'edit') {
return (
<Button
title="Delete"
onClick={handleDelete}
accent="danger"
fullWidth
size="small"
justify="center"
focus={false}
variant="secondary"
disabled={viewPickerIsPersisting}
/>
);
}
if (
viewPickerType === ViewType.Kanban &&
availableFieldsForKanban.length === 0
) {
return (
<Button
title="Go to Settings"
onClick={navigateToSelectSettings}
size="small"
accent="blue"
fullWidth
justify="center"
/>
);
}
if (
viewPickerType === ViewType.Table ||
viewPickerKanbanFieldMetadataId !== ''
) {
return (
<Button
title="Create"
onClick={handleCreate}
accent="blue"
fullWidth
size="small"
justify="center"
disabled={
viewPickerIsPersisting ||
(viewPickerType === ViewType.Kanban &&
viewPickerKanbanFieldMetadataId === '')
}
/>
);
}
};

View File

@ -0,0 +1,180 @@
import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum';
import { IconChevronLeft, IconX } from '@/ui/display/icon';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { Select } from '@/ui/input/components/Select';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { ViewsHotkeyScope } from '@/views/types/ViewsHotkeyScope';
import { ViewType } from '@/views/types/ViewType';
import { ViewPickerCreateOrEditButton } from '@/views/view-picker/components/ViewPickerCreateOrEditButton';
import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId';
import { VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerViewTypeDropdownId';
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewPickerPersistView } from '@/views/view-picker/hooks/useViewPickerPersistView';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
const StyledIconAndNameContainer = styled.div`
align-items: center;
display: flex;
margin-left: ${({ theme }) => theme.spacing(1)};
gap: ${({ theme }) => theme.spacing(1)};
`;
const StyledSelectContainer = styled.div`
display: flex;
width: calc(100% - ${({ theme }) => theme.spacing(2)});
margin: ${({ theme }) => theme.spacing(1)};
color: ${({ theme }) => theme.font.color.light};
user-select: none;
`;
const StyledNoKanbanFieldAvailableContainer = styled.div`
color: ${({ theme }) => theme.font.color.light};
display: flex;
margin: ${({ theme }) => theme.spacing(1, 2)};
user-select: none;
width: calc(100% - ${({ theme }) => theme.spacing(4)});
`;
const StyledSaveButtonContainer = styled.div`
display: flex;
padding: ${({ theme }) => theme.spacing(1)};
width: calc(100% - ${({ theme }) => theme.spacing(2)});
`;
export const ViewPickerCreateOrEditContent = () => {
const { viewPickerMode, setViewPickerMode } = useViewPickerMode();
const {
viewPickerInputNameState,
viewPickerSelectedIconState,
viewPickerIsPersistingState,
viewPickerKanbanFieldMetadataIdState,
viewPickerTypeState,
} = useViewPickerStates();
const [viewPickerInputName, setViewPickerInputName] = useRecoilState(
viewPickerInputNameState,
);
const [viewPickerSelectedIcon, setViewPickerSelectedIcon] = useRecoilState(
viewPickerSelectedIconState,
);
const viewPickerIsPersisting = useRecoilValue(viewPickerIsPersistingState);
const [viewPickerKanbanFieldMetadataId, setViewPickerKanbanFieldMetadataId] =
useRecoilState(viewPickerKanbanFieldMetadataIdState);
const [viewPickerType, setViewPickerType] =
useRecoilState(viewPickerTypeState);
const setHotkeyScope = useSetHotkeyScope();
const { handleCreate, handleUpdate } = useViewPickerPersistView();
useScopedHotkeys(
Key.Enter,
async () => {
if (viewPickerMode === 'create') {
if (viewPickerIsPersisting) {
return;
}
await handleCreate();
}
if (viewPickerMode === 'edit') {
if (viewPickerIsPersisting) {
return;
}
await handleUpdate();
}
},
ViewsHotkeyScope.ListDropdown,
);
const onIconChange = ({ iconKey }: { iconKey: string }) => {
setViewPickerSelectedIcon(iconKey);
};
const { availableFieldsForKanban } = useGetAvailableFieldsForKanban();
return (
<>
<DropdownMenuHeader
StartIcon={viewPickerMode === 'create' ? IconX : IconChevronLeft}
onClick={() => setViewPickerMode('list')}
>
{viewPickerMode === 'create' ? 'Create view' : 'Edit view'}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<StyledIconAndNameContainer>
<IconPicker
onChange={onIconChange}
selectedIconKey={viewPickerSelectedIcon}
disableBlur
onClose={() => setHotkeyScope(ViewsHotkeyScope.ListDropdown)}
/>
<DropdownMenuInput
value={viewPickerInputName}
onChange={(event) => setViewPickerInputName(event.target.value)}
autoFocus
/>
</StyledIconAndNameContainer>
{viewPickerMode === 'create' && (
<StyledSelectContainer>
<Select
disableBlur
label="View type"
fullWidth
value={viewPickerType}
onChange={(value) => setViewPickerType(value)}
options={[
{ value: ViewType.Table, label: 'Table' },
{ value: ViewType.Kanban, label: 'Kanban' },
]}
dropdownId={VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID}
/>
</StyledSelectContainer>
)}
{viewPickerType === ViewType.Kanban && viewPickerMode === 'create' && (
<>
<StyledSelectContainer>
<Select
disableBlur
label="Stages"
fullWidth
value={viewPickerKanbanFieldMetadataId}
onChange={(value) => setViewPickerKanbanFieldMetadataId(value)}
options={
availableFieldsForKanban.length > 0
? availableFieldsForKanban.map((field) => ({
value: field.id,
label: field.label,
}))
: [{ value: '', label: 'No Select field' }]
}
dropdownId={VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID}
/>
</StyledSelectContainer>
{availableFieldsForKanban.length === 0 && (
<StyledNoKanbanFieldAvailableContainer>
Set up a Select field on Companies to create a Kanban
</StyledNoKanbanFieldAvailableContainer>
)}
</>
)}
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<StyledSaveButtonContainer>
<ViewPickerCreateOrEditButton />
</StyledSaveButtonContainer>
</DropdownMenuItemsContainer>
</>
);
};

View File

@ -0,0 +1,65 @@
import { useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
import { isDefined } from '~/utils/isDefined';
export const ViewPickerCreateOrEditContentEffect = () => {
const {
viewPickerSelectedIconState,
viewPickerInputNameState,
viewPickerReferenceViewIdState,
viewPickerIsPersistingState,
viewPickerKanbanFieldMetadataIdState,
viewPickerTypeState,
} = useViewPickerStates();
const setViewPickerSelectedIcon = useSetRecoilState(
viewPickerSelectedIconState,
);
const setViewPickerInputName = useSetRecoilState(viewPickerInputNameState);
const setViewPickerKanbanFieldMetadataId = useSetRecoilState(
viewPickerKanbanFieldMetadataIdState,
);
const setViewPickerType = useSetRecoilState(viewPickerTypeState);
const viewPickerReferenceViewId = useRecoilValue(
viewPickerReferenceViewIdState,
);
const viewPickerIsPersisting = useRecoilValue(viewPickerIsPersistingState);
const { viewsOnCurrentObject } = useGetCurrentView();
const referenceView = viewsOnCurrentObject.find(
(view) => view.id === viewPickerReferenceViewId,
);
const { availableFieldsForKanban } = useGetAvailableFieldsForKanban();
useEffect(() => {
if (isDefined(referenceView) && !viewPickerIsPersisting) {
setViewPickerSelectedIcon(referenceView.icon);
setViewPickerInputName(referenceView.name);
setViewPickerKanbanFieldMetadataId(referenceView.kanbanFieldMetadataId);
setViewPickerType(referenceView.type);
}
}, [
referenceView,
setViewPickerInputName,
setViewPickerKanbanFieldMetadataId,
setViewPickerSelectedIcon,
setViewPickerType,
viewPickerIsPersisting,
]);
useEffect(() => {
if (availableFieldsForKanban.length > 0) {
setViewPickerKanbanFieldMetadataId(availableFieldsForKanban[0].id);
}
}, [availableFieldsForKanban, setViewPickerKanbanFieldMetadataId]);
return <></>;
};

View File

@ -0,0 +1,100 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { IconChevronDown, IconList } from '@/ui/display/icon';
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { ViewsHotkeyScope } from '@/views/types/ViewsHotkeyScope';
import { ViewPickerCreateOrEditContent } from '@/views/view-picker/components/ViewPickerCreateOrEditContent';
import { ViewPickerCreateOrEditContentEffect } from '@/views/view-picker/components/ViewPickerCreateOrEditContentEffect';
import { ViewPickerListContent } from '@/views/view-picker/components/ViewPickerListContent';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewStates } from '../../hooks/internal/useViewStates';
const StyledDropdownLabelAdornments = styled.span`
align-items: center;
color: ${({ theme }) => theme.grayScale.gray35};
display: inline-flex;
gap: ${({ theme }) => theme.spacing(1)};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
const StyledViewName = styled.span`
margin-left: ${({ theme }) => theme.spacing(1)};
display: inline-block;
max-width: 130px;
@media (max-width: 375px) {
max-width: 90px;
}
@media (min-width: 376px) and (max-width: ${MOBILE_VIEWPORT}px) {
max-width: 110px;
}
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
`;
export const ViewPickerDropdown = () => {
const theme = useTheme();
const { entityCountInCurrentViewState } = useViewStates();
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
const entityCountInCurrentView = useRecoilValue(
entityCountInCurrentViewState,
);
const { isDropdownOpen: isViewsListDropdownOpen } = useDropdown(
VIEW_PICKER_DROPDOWN_ID,
);
const { viewPickerMode, setViewPickerMode } = useViewPickerMode();
const { getIcon } = useIcons();
const CurrentViewIcon = getIcon(currentViewWithCombinedFiltersAndSorts?.icon);
return (
<Dropdown
dropdownId={VIEW_PICKER_DROPDOWN_ID}
dropdownHotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
dropdownOffset={{ x: 0, y: 8 }}
dropdownMenuWidth={200}
onClickOutside={() => setViewPickerMode('list')}
clickableComponent={
<StyledDropdownButtonContainer isUnfolded={isViewsListDropdownOpen}>
{currentViewWithCombinedFiltersAndSorts && CurrentViewIcon ? (
<CurrentViewIcon size={theme.icon.size.md} />
) : (
<IconList size={theme.icon.size.md} />
)}
<StyledViewName>
{currentViewWithCombinedFiltersAndSorts?.name ?? 'All'}
</StyledViewName>
<StyledDropdownLabelAdornments>
· {entityCountInCurrentView}{' '}
<IconChevronDown size={theme.icon.size.sm} />
</StyledDropdownLabelAdornments>
</StyledDropdownButtonContainer>
}
dropdownComponents={
viewPickerMode === 'list' ? (
<ViewPickerListContent />
) : (
<>
<ViewPickerCreateOrEditContent />
<ViewPickerCreateOrEditContentEffect />
</>
)
}
/>
);
};

View File

@ -0,0 +1,93 @@
import { MouseEvent } from 'react';
import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil';
import { IconLock, IconPencil, IconPlus } from '@/ui/display/icon';
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useHandleViews } from '@/views/hooks/useHandleViews';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
import { isDefined } from '~/utils/isDefined';
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
font-weight: ${({ theme }) => theme.font.weight.regular};
`;
export const ViewPickerListContent = () => {
const { selectView } = useHandleViews();
const { currentViewWithCombinedFiltersAndSorts, viewsOnCurrentObject } =
useGetCurrentView();
const { viewPickerReferenceViewIdState } = useViewPickerStates();
const setViewPickerReferenceViewId = useSetRecoilState(
viewPickerReferenceViewIdState,
);
const { setViewPickerMode } = useViewPickerMode();
const { closeDropdown } = useDropdown(VIEW_PICKER_DROPDOWN_ID);
const handleViewSelect = (viewId: string) => {
selectView(viewId);
closeDropdown();
};
const handleAddViewButtonClick = () => {
if (isDefined(currentViewWithCombinedFiltersAndSorts?.id)) {
setViewPickerReferenceViewId(currentViewWithCombinedFiltersAndSorts.id);
setViewPickerMode('create');
}
};
const handleEditViewButtonClick = (
event: MouseEvent<HTMLButtonElement>,
viewId: string,
) => {
event.stopPropagation();
setViewPickerReferenceViewId(viewId);
setViewPickerMode('edit');
};
const { getIcon } = useIcons();
return (
<>
<DropdownMenuItemsContainer>
{viewsOnCurrentObject.map((view) => (
<MenuItem
key={view.id}
iconButtons={[
view.key !== 'INDEX'
? {
Icon: IconPencil,
onClick: (event: MouseEvent<HTMLButtonElement>) =>
handleEditViewButtonClick(event, view.id),
}
: {
Icon: IconLock,
},
].filter(isDefined)}
onClick={() => handleViewSelect(view.id)}
LeftIcon={getIcon(view.icon)}
text={view.name}
/>
))}
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
<StyledBoldDropdownMenuItemsContainer>
<MenuItem
onClick={handleAddViewButtonClick}
LeftIcon={IconPlus}
text="Add view"
/>
</StyledBoldDropdownMenuItemsContainer>
</>
);
};

View File

@ -0,0 +1 @@
export const VIEW_PICKER_DROPDOWN_ID = 'view-picker';

View File

@ -0,0 +1 @@
export const VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID = 'view-picker-kanban-field';

View File

@ -0,0 +1 @@
export const VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID = 'view-picker-view-type';

View File

@ -0,0 +1,46 @@
import { useCallback } from 'react';
import { useSetRecoilState } from 'recoil';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { VIEW_PICKER_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerDropdownId';
import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId';
import { VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerViewTypeDropdownId';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
export const useCloseAndResetViewPicker = () => {
const { setViewPickerMode } = useViewPickerMode();
const { viewPickerIsPersistingState } = useViewPickerStates();
const setViewPickerIsPersisting = useSetRecoilState(
viewPickerIsPersistingState,
);
const { closeDropdown: closeViewPickerDropdown } = useDropdown(
VIEW_PICKER_DROPDOWN_ID,
);
const { closeDropdown: closeKanbanFieldDropdown } = useDropdown(
VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID,
);
const { closeDropdown: closeTypeDropdown } = useDropdown(
VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID,
);
const closeAndResetViewPicker = useCallback(() => {
setViewPickerIsPersisting(false);
setViewPickerMode('list');
closeKanbanFieldDropdown();
closeTypeDropdown();
closeViewPickerDropdown();
}, [
closeKanbanFieldDropdown,
closeTypeDropdown,
closeViewPickerDropdown,
setViewPickerIsPersisting,
setViewPickerMode,
]);
return { closeAndResetViewPicker };
};

View File

@ -0,0 +1,34 @@
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { useViewStates } from '@/views/hooks/internal/useViewStates';
import { FieldMetadataType } from '~/generated-metadata/graphql';
export const useGetAvailableFieldsForKanban = () => {
const { viewObjectMetadataIdState } = useViewStates();
const viewObjectMetadataId = useRecoilValue(viewObjectMetadataIdState);
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(objectMetadata) => objectMetadata.id === viewObjectMetadataId,
);
const availableFieldsForKanban =
objectMetadataItem?.fields.filter(
(field) => field.type === FieldMetadataType.Select,
) ?? [];
const navigate = useNavigate();
const navigateToSelectSettings = useCallback(() => {
navigate(`/settings/objects/${objectMetadataItem?.namePlural}`);
}, [navigate, objectMetadataItem?.namePlural]);
return {
availableFieldsForKanban,
navigateToSelectSettings,
};
};

View File

@ -0,0 +1,15 @@
import { useRecoilState } from 'recoil';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
export const useViewPickerMode = (viewBarComponentId?: string) => {
const { viewPickerModeState } = useViewPickerStates(viewBarComponentId);
const [viewPickerMode, setViewPickerMode] =
useRecoilState(viewPickerModeState);
return {
viewPickerMode,
setViewPickerMode,
};
};

View File

@ -0,0 +1,125 @@
import { useRecoilCallback } from 'recoil';
import { v4 } from 'uuid';
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { useHandleViews } from '@/views/hooks/useHandleViews';
import { useCloseAndResetViewPicker } from '@/views/view-picker/hooks/useCloseAndResetViewPicker';
import { useViewPickerStates } from '@/views/view-picker/hooks/useViewPickerStates';
export const useViewPickerPersistView = () => {
const {
viewPickerInputNameState,
viewPickerSelectedIconState,
viewPickerIsPersistingState,
viewPickerReferenceViewIdState,
viewPickerKanbanFieldMetadataIdState,
viewPickerTypeState,
} = useViewPickerStates();
const { createView, selectView, removeView, updateView } = useHandleViews();
const { viewsOnCurrentObject } = useGetCurrentView();
const { closeAndResetViewPicker } = useCloseAndResetViewPicker();
const handleCreate = useRecoilCallback(
({ snapshot, set }) =>
async () => {
const name = getSnapshotValue(snapshot, viewPickerInputNameState);
const iconKey = getSnapshotValue(snapshot, viewPickerSelectedIconState);
const type = getSnapshotValue(snapshot, viewPickerTypeState);
const kanbanFieldMetadataId = getSnapshotValue(
snapshot,
viewPickerKanbanFieldMetadataIdState,
);
const id = v4();
set(viewPickerIsPersistingState, true);
await createView({
id,
name,
icon: iconKey,
type,
kanbanFieldMetadataId,
});
closeAndResetViewPicker();
selectView(id);
},
[
closeAndResetViewPicker,
createView,
selectView,
viewPickerInputNameState,
viewPickerIsPersistingState,
viewPickerKanbanFieldMetadataIdState,
viewPickerSelectedIconState,
viewPickerTypeState,
],
);
const handleDelete = useRecoilCallback(
({ set, snapshot }) =>
async () => {
set(viewPickerIsPersistingState, true);
const viewPickerReferenceViewId = getSnapshotValue(
snapshot,
viewPickerReferenceViewIdState,
);
selectView(
viewsOnCurrentObject.filter(
(view) => view.id !== viewPickerReferenceViewId,
)[0].id,
);
await removeView(viewPickerReferenceViewId);
closeAndResetViewPicker();
},
[
closeAndResetViewPicker,
removeView,
selectView,
viewPickerIsPersistingState,
viewPickerReferenceViewIdState,
viewsOnCurrentObject,
],
);
const handleUpdate = useRecoilCallback(
({ set, snapshot }) =>
async () => {
set(viewPickerIsPersistingState, true);
const viewPickerReferenceViewId = getSnapshotValue(
snapshot,
viewPickerReferenceViewIdState,
);
const viewPickerInputName = getSnapshotValue(
snapshot,
viewPickerInputNameState,
);
const viewPickerSelectedIcon = getSnapshotValue(
snapshot,
viewPickerSelectedIconState,
);
await updateView({
id: viewPickerReferenceViewId,
name: viewPickerInputName,
icon: viewPickerSelectedIcon,
});
selectView(viewPickerReferenceViewId);
closeAndResetViewPicker();
},
[
viewPickerIsPersistingState,
viewPickerReferenceViewIdState,
viewPickerInputNameState,
viewPickerSelectedIconState,
updateView,
selectView,
closeAndResetViewPicker,
],
);
return { handleCreate, handleDelete, handleUpdate };
};

View File

@ -0,0 +1,50 @@
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerModeComponentState } from '@/views/view-picker/states/viewPickerModeComponentState';
import { viewPickerReferenceViewIdComponentState } from '@/views/view-picker/states/viewPickerReferenceViewIdComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
export const useViewPickerStates = (viewComponentId?: string) => {
const componentId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
viewComponentId,
);
return {
componentId,
viewPickerModeState: extractComponentState(
viewPickerModeComponentState,
componentId,
),
viewPickerInputNameState: extractComponentState(
viewPickerInputNameComponentState,
componentId,
),
viewPickerSelectedIconState: extractComponentState(
viewPickerSelectedIconComponentState,
componentId,
),
viewPickerKanbanFieldMetadataIdState: extractComponentState(
viewPickerKanbanFieldMetadataIdComponentState,
componentId,
),
viewPickerReferenceViewIdState: extractComponentState(
viewPickerReferenceViewIdComponentState,
componentId,
),
viewPickerIsPersistingState: extractComponentState(
viewPickerIsPersistingComponentState,
componentId,
),
viewPickerTypeState: extractComponentState(
viewPickerTypeComponentState,
componentId,
),
};
};

View File

@ -0,0 +1,6 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerInputNameComponentState = createComponentState<string>({
key: 'viewPickerInputNameComponentState',
defaultValue: '',
});

View File

@ -0,0 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerIsPersistingComponentState =
createComponentState<boolean>({
key: 'viewPickerIsPersistingComponentState',
defaultValue: false,
});

View File

@ -0,0 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerKanbanFieldMetadataIdComponentState =
createComponentState<string>({
key: 'viewPickerKanbanFieldMetadataIdComponentState',
defaultValue: '',
});

View File

@ -0,0 +1,8 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerModeComponentState = createComponentState<
'list' | 'edit' | 'create'
>({
key: 'viewEditModeComponentState',
defaultValue: 'list',
});

View File

@ -0,0 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerReferenceViewIdComponentState =
createComponentState<string>({
key: 'viewPickerReferenceViewIdComponentState',
defaultValue: '',
});

View File

@ -0,0 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const viewPickerSelectedIconComponentState =
createComponentState<string>({
key: 'viewPickerSelectedIconComponentState',
defaultValue: '',
});

View File

@ -0,0 +1,7 @@
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
import { ViewType } from '@/views/types/ViewType';
export const viewPickerTypeComponentState = createComponentState<ViewType>({
key: 'viewPickerTypeComponentState',
defaultValue: ViewType.Table,
});