New view picker (#4610)
* Implement new view picker * Complete feature * Fixes according to review
This commit is contained in:
@ -6,6 +6,7 @@ import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
|||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
|
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
||||||
|
|
||||||
export const ObjectMetadataNavItems = () => {
|
export const ObjectMetadataNavItems = () => {
|
||||||
const { activeObjectMetadataItems } = useObjectMetadataItemForSettings();
|
const { activeObjectMetadataItems } = useObjectMetadataItemForSettings();
|
||||||
@ -13,7 +14,9 @@ export const ObjectMetadataNavItems = () => {
|
|||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
const currentPath = useLocation().pathname;
|
const currentPath = useLocation().pathname;
|
||||||
|
|
||||||
const { records } = usePrefetchedData<GraphQLView>(PrefetchKey.AllViews);
|
const { records: views } = usePrefetchedData<GraphQLView>(
|
||||||
|
PrefetchKey.AllViews,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -45,9 +48,11 @@ export const ObjectMetadataNavItems = () => {
|
|||||||
: -1;
|
: -1;
|
||||||
}),
|
}),
|
||||||
].map((objectMetadataItem) => {
|
].map((objectMetadataItem) => {
|
||||||
const viewId = records?.find(
|
const objectMetadataViews = getObjectMetadataItemViews(
|
||||||
(view: any) => view?.objectMetadataId === objectMetadataItem.id,
|
objectMetadataItem.id,
|
||||||
)?.id;
|
views,
|
||||||
|
);
|
||||||
|
const viewId = objectMetadataViews[0]?.id;
|
||||||
|
|
||||||
const navigationPath = `/objects/${objectMetadataItem.namePlural}${
|
const navigationPath = `/objects/${objectMetadataItem.namePlural}${
|
||||||
viewId ? `?view=${viewId}` : ''
|
viewId ? `?view=${viewId}` : ''
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { onRecordBoardFetchMoreVisibilityChangeComponentState } from '@/object-r
|
|||||||
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
|
import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState';
|
||||||
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState';
|
||||||
import { recordBoardFiltersComponentState } from '@/object-record/record-board/states/recordBoardFiltersComponentState';
|
import { recordBoardFiltersComponentState } from '@/object-record/record-board/states/recordBoardFiltersComponentState';
|
||||||
|
import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState';
|
||||||
import { recordBoardObjectSingularNameComponentState } from '@/object-record/record-board/states/recordBoardObjectSingularNameComponentState';
|
import { recordBoardObjectSingularNameComponentState } from '@/object-record/record-board/states/recordBoardObjectSingularNameComponentState';
|
||||||
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState';
|
import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState';
|
||||||
import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState';
|
import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState';
|
||||||
@ -32,6 +33,10 @@ export const useRecordBoardStates = (recordBoardId?: string) => {
|
|||||||
recordBoardObjectSingularNameComponentState,
|
recordBoardObjectSingularNameComponentState,
|
||||||
scopeId,
|
scopeId,
|
||||||
),
|
),
|
||||||
|
kanbanFieldMetadataNameState: extractComponentState(
|
||||||
|
recordBoardKanbanFieldMetadataNameComponentState,
|
||||||
|
scopeId,
|
||||||
|
),
|
||||||
isFetchingRecordState: extractComponentState(
|
isFetchingRecordState: extractComponentState(
|
||||||
isRecordBoardFetchingRecordsComponentState,
|
isRecordBoardFetchingRecordsComponentState,
|
||||||
scopeId,
|
scopeId,
|
||||||
|
|||||||
@ -10,6 +10,7 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
|||||||
recordIdsByColumnIdFamilyState,
|
recordIdsByColumnIdFamilyState,
|
||||||
columnsFamilySelector,
|
columnsFamilySelector,
|
||||||
columnIdsState,
|
columnIdsState,
|
||||||
|
kanbanFieldMetadataNameState,
|
||||||
} = useRecordBoardStates(recordBoardId);
|
} = useRecordBoardStates(recordBoardId);
|
||||||
|
|
||||||
const setRecordIds = useRecoilCallback(
|
const setRecordIds = useRecoilCallback(
|
||||||
@ -26,8 +27,18 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
|||||||
.getLoadable(recordIdsByColumnIdFamilyState(columnId))
|
.getLoadable(recordIdsByColumnIdFamilyState(columnId))
|
||||||
.getValue();
|
.getValue();
|
||||||
|
|
||||||
|
const kanbanFieldMetadataName = snapshot
|
||||||
|
.getLoadable(kanbanFieldMetadataNameState)
|
||||||
|
.getValue();
|
||||||
|
|
||||||
|
if (!kanbanFieldMetadataName) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const columnRecordIds = records
|
const columnRecordIds = records
|
||||||
.filter((record) => record.stage === column?.value)
|
.filter(
|
||||||
|
(record) => record[kanbanFieldMetadataName] === column?.value,
|
||||||
|
)
|
||||||
.sort(sortRecordsByPosition)
|
.sort(sortRecordsByPosition)
|
||||||
.map((record) => record.id);
|
.map((record) => record.id);
|
||||||
|
|
||||||
@ -36,7 +47,12 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[columnsFamilySelector, columnIdsState, recordIdsByColumnIdFamilyState],
|
[
|
||||||
|
columnIdsState,
|
||||||
|
columnsFamilySelector,
|
||||||
|
recordIdsByColumnIdFamilyState,
|
||||||
|
kanbanFieldMetadataNameState,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -12,12 +12,16 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
|||||||
selectedRecordIdsSelector,
|
selectedRecordIdsSelector,
|
||||||
isCompactModeActiveState,
|
isCompactModeActiveState,
|
||||||
onFetchMoreVisibilityChangeState,
|
onFetchMoreVisibilityChangeState,
|
||||||
|
kanbanFieldMetadataNameState,
|
||||||
} = useRecordBoardStates(recordBoardId);
|
} = useRecordBoardStates(recordBoardId);
|
||||||
|
|
||||||
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
|
const { setColumns } = useSetRecordBoardColumns(recordBoardId);
|
||||||
const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId);
|
const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId);
|
||||||
const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState);
|
const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState);
|
||||||
const setObjectSingularName = useSetRecoilState(objectSingularNameState);
|
const setObjectSingularName = useSetRecoilState(objectSingularNameState);
|
||||||
|
const setKanbanFieldMetadataName = useSetRecoilState(
|
||||||
|
kanbanFieldMetadataNameState,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
scopeId,
|
scopeId,
|
||||||
@ -25,6 +29,7 @@ export const useRecordBoard = (recordBoardId?: string) => {
|
|||||||
setRecordIds,
|
setRecordIds,
|
||||||
setFieldDefinitions,
|
setFieldDefinitions,
|
||||||
setObjectSingularName,
|
setObjectSingularName,
|
||||||
|
setKanbanFieldMetadataName,
|
||||||
selectedRecordIdsSelector,
|
selectedRecordIdsSelector,
|
||||||
isCompactModeActiveState,
|
isCompactModeActiveState,
|
||||||
onFetchMoreVisibilityChangeState,
|
onFetchMoreVisibilityChangeState,
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const recordBoardKanbanFieldMetadataNameComponentState =
|
||||||
|
createComponentState<string | undefined>({
|
||||||
|
key: 'recordBoardKanbanFieldMetadataNameComponentState',
|
||||||
|
defaultValue: undefined,
|
||||||
|
});
|
||||||
@ -8,7 +8,10 @@ import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoar
|
|||||||
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
|
||||||
import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard';
|
import { useLoadRecordIndexBoard } from '@/object-record/record-index/hooks/useLoadRecordIndexBoard';
|
||||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||||
|
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||||
import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata';
|
import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata';
|
||||||
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
type RecordIndexBoardContainerEffectProps = {
|
type RecordIndexBoardContainerEffectProps = {
|
||||||
objectNameSingular: string;
|
objectNameSingular: string;
|
||||||
@ -31,6 +34,7 @@ export const RecordIndexBoardContainerEffect = ({
|
|||||||
selectedRecordIdsSelector,
|
selectedRecordIdsSelector,
|
||||||
setFieldDefinitions,
|
setFieldDefinitions,
|
||||||
onFetchMoreVisibilityChangeState,
|
onFetchMoreVisibilityChangeState,
|
||||||
|
setKanbanFieldMetadataName,
|
||||||
} = useRecordBoard(recordBoardId);
|
} = useRecordBoard(recordBoardId);
|
||||||
|
|
||||||
const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({
|
const { fetchMoreRecords, loading } = useLoadRecordIndexBoard({
|
||||||
@ -43,6 +47,10 @@ export const RecordIndexBoardContainerEffect = ({
|
|||||||
onFetchMoreVisibilityChangeState,
|
onFetchMoreVisibilityChangeState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const recordIndexKanbanFieldMetadataId = useRecoilValue(
|
||||||
|
recordIndexKanbanFieldMetadataIdState,
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOnFetchMoreVisibilityChange(() => () => {
|
setOnFetchMoreVisibilityChange(() => () => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
@ -67,6 +75,7 @@ export const RecordIndexBoardContainerEffect = ({
|
|||||||
setColumns(
|
setColumns(
|
||||||
computeRecordBoardColumnDefinitionsFromObjectMetadata(
|
computeRecordBoardColumnDefinitionsFromObjectMetadata(
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
|
recordIndexKanbanFieldMetadataId ?? '',
|
||||||
navigateToSelectSettings,
|
navigateToSelectSettings,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -74,6 +83,7 @@ export const RecordIndexBoardContainerEffect = ({
|
|||||||
navigateToSelectSettings,
|
navigateToSelectSettings,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
|
recordIndexKanbanFieldMetadataId,
|
||||||
setColumns,
|
setColumns,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -85,6 +95,24 @@ export const RecordIndexBoardContainerEffect = ({
|
|||||||
setFieldDefinitions(recordIndexFieldDefinitions);
|
setFieldDefinitions(recordIndexFieldDefinitions);
|
||||||
}, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]);
|
}, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isDefined(recordIndexKanbanFieldMetadataId)) {
|
||||||
|
const kanbanFieldMetadataName = objectMetadataItem?.fields.find(
|
||||||
|
(field) =>
|
||||||
|
field.type === FieldMetadataType.Select &&
|
||||||
|
field.id === recordIndexKanbanFieldMetadataId,
|
||||||
|
)?.name;
|
||||||
|
|
||||||
|
if (isDefined(kanbanFieldMetadataName)) {
|
||||||
|
setKanbanFieldMetadataName(kanbanFieldMetadataName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
objectMetadataItem,
|
||||||
|
recordIndexKanbanFieldMetadataId,
|
||||||
|
setKanbanFieldMetadataName,
|
||||||
|
]);
|
||||||
|
|
||||||
const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector());
|
const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector());
|
||||||
|
|
||||||
const { setActionBarEntries, setContextMenuEntries } = useRecordActionBar({
|
const { setActionBarEntries, setContextMenuEntries } = useRecordActionBar({
|
||||||
|
|||||||
@ -10,10 +10,10 @@ import { RecordIndexTableContainer } from '@/object-record/record-index/componen
|
|||||||
import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect';
|
import { RecordIndexTableContainerEffect } from '@/object-record/record-index/components/RecordIndexTableContainerEffect';
|
||||||
import { RecordIndexViewBarEffect } from '@/object-record/record-index/components/RecordIndexViewBarEffect';
|
import { RecordIndexViewBarEffect } from '@/object-record/record-index/components/RecordIndexViewBarEffect';
|
||||||
import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options/components/RecordIndexOptionsDropdown';
|
import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options/components/RecordIndexOptionsDropdown';
|
||||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
|
||||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||||
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState';
|
||||||
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
|
import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState';
|
||||||
|
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
|
||||||
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
|
import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState';
|
||||||
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
import { recordIndexViewTypeState } from '@/object-record/record-index/states/recordIndexViewTypeState';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
@ -65,6 +65,9 @@ export const RecordIndexContainer = ({
|
|||||||
const setRecordIndexIsCompactModeActive = useSetRecoilState(
|
const setRecordIndexIsCompactModeActive = useSetRecoilState(
|
||||||
recordIndexIsCompactModeActiveState,
|
recordIndexIsCompactModeActiveState,
|
||||||
);
|
);
|
||||||
|
const setRecordIndexViewKanbanFieldMetadataIdState = useSetRecoilState(
|
||||||
|
recordIndexKanbanFieldMetadataIdState,
|
||||||
|
);
|
||||||
|
|
||||||
const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({
|
const { setTableFilters, setTableSorts, setTableColumns } = useRecordTable({
|
||||||
recordTableId: recordIndexId,
|
recordTableId: recordIndexId,
|
||||||
@ -129,9 +132,11 @@ export const RecordIndexContainer = ({
|
|||||||
mapViewSortsToSorts(view.viewSorts, sortDefinitions),
|
mapViewSortsToSorts(view.viewSorts, sortDefinitions),
|
||||||
);
|
);
|
||||||
setRecordIndexViewType(view.type);
|
setRecordIndexViewType(view.type);
|
||||||
|
setRecordIndexViewKanbanFieldMetadataIdState(
|
||||||
|
view.kanbanFieldMetadataId,
|
||||||
|
);
|
||||||
setRecordIndexIsCompactModeActive(view.isCompact);
|
setRecordIndexIsCompactModeActive(view.isCompact);
|
||||||
}}
|
}}
|
||||||
optionsDropdownScopeId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
|
||||||
/>
|
/>
|
||||||
<RecordIndexViewBarEffect
|
<RecordIndexViewBarEffect
|
||||||
objectNamePlural={objectNamePlural}
|
objectNamePlural={objectNamePlural}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { RecordIndexOptionsDropdownContent } from '@/object-record/record-index/
|
|||||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
||||||
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { useViewBarEditMode } from '@/views/hooks/useViewBarEditMode';
|
|
||||||
import { ViewType } from '@/views/types/ViewType';
|
import { ViewType } from '@/views/types/ViewType';
|
||||||
|
|
||||||
type RecordIndexOptionsDropdownProps = {
|
type RecordIndexOptionsDropdownProps = {
|
||||||
@ -17,8 +16,6 @@ export const RecordIndexOptionsDropdown = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
viewType,
|
viewType,
|
||||||
}: RecordIndexOptionsDropdownProps) => {
|
}: RecordIndexOptionsDropdownProps) => {
|
||||||
const { setViewEditMode } = useViewBarEditMode(recordIndexId);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
dropdownId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
||||||
@ -32,7 +29,6 @@ export const RecordIndexOptionsDropdown = ({
|
|||||||
recordIndexId={recordIndexId}
|
recordIndexId={recordIndexId}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onClickOutside={() => setViewEditMode('none')}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { useRef, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
||||||
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
|
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
|
||||||
@ -14,7 +13,6 @@ import {
|
|||||||
IconTag,
|
IconTag,
|
||||||
} from '@/ui/display/icon';
|
} from '@/ui/display/icon';
|
||||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
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 { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
@ -23,8 +21,6 @@ import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemTog
|
|||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { useHandleViews } from '@/views/hooks/useHandleViews';
|
|
||||||
import { useViewBarEditMode } from '@/views/hooks/useViewBarEditMode';
|
|
||||||
import { ViewType } from '@/views/types/ViewType';
|
import { ViewType } from '@/views/types/ViewType';
|
||||||
|
|
||||||
type RecordIndexOptionsMenu = 'fields';
|
type RecordIndexOptionsMenu = 'fields';
|
||||||
@ -40,9 +36,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
|||||||
recordIndexId,
|
recordIndexId,
|
||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
}: RecordIndexOptionsDropdownContentProps) => {
|
}: RecordIndexOptionsDropdownContentProps) => {
|
||||||
const { updateCurrentView, createEmptyView, selectView } =
|
|
||||||
useHandleViews(recordIndexId);
|
|
||||||
const { viewEditMode, setViewEditMode } = useViewBarEditMode(recordIndexId);
|
|
||||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||||
|
|
||||||
const { closeDropdown } = useDropdown(RECORD_INDEX_OPTIONS_DROPDOWN_ID);
|
const { closeDropdown } = useDropdown(RECORD_INDEX_OPTIONS_DROPDOWN_ID);
|
||||||
@ -53,8 +46,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
|||||||
|
|
||||||
const resetMenu = () => setCurrentMenu(undefined);
|
const resetMenu = () => setCurrentMenu(undefined);
|
||||||
|
|
||||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
const handleSelectMenu = (option: RecordIndexOptionsMenu) => {
|
const handleSelectMenu = (option: RecordIndexOptionsMenu) => {
|
||||||
setCurrentMenu(option);
|
setCurrentMenu(option);
|
||||||
};
|
};
|
||||||
@ -67,25 +58,6 @@ export const RecordIndexOptionsDropdownContent = ({
|
|||||||
TableOptionsHotkeyScope.Dropdown,
|
TableOptionsHotkeyScope.Dropdown,
|
||||||
);
|
);
|
||||||
|
|
||||||
useScopedHotkeys(
|
|
||||||
Key.Enter,
|
|
||||||
async () => {
|
|
||||||
const name = viewEditInputRef.current?.value;
|
|
||||||
if (viewEditMode === 'create') {
|
|
||||||
const id = v4();
|
|
||||||
await createEmptyView(id, name ?? '');
|
|
||||||
selectView(id);
|
|
||||||
} else {
|
|
||||||
updateCurrentView({ name });
|
|
||||||
}
|
|
||||||
|
|
||||||
resetMenu();
|
|
||||||
setViewEditMode('none');
|
|
||||||
closeDropdown();
|
|
||||||
},
|
|
||||||
TableOptionsHotkeyScope.Dropdown,
|
|
||||||
);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handleColumnVisibilityChange,
|
handleColumnVisibilityChange,
|
||||||
handleReorderColumns,
|
handleReorderColumns,
|
||||||
@ -128,37 +100,18 @@ export const RecordIndexOptionsDropdownContent = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!currentMenu && (
|
{!currentMenu && (
|
||||||
<>
|
<DropdownMenuItemsContainer>
|
||||||
<DropdownMenuInput
|
<MenuItem
|
||||||
ref={viewEditInputRef}
|
onClick={() => handleSelectMenu('fields')}
|
||||||
autoFocus={viewEditMode !== 'none'}
|
LeftIcon={IconTag}
|
||||||
placeholder={
|
text="Fields"
|
||||||
viewEditMode === 'create'
|
|
||||||
? 'New view'
|
|
||||||
: viewEditMode === 'edit'
|
|
||||||
? 'View name'
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
defaultValue={
|
|
||||||
viewEditMode === 'create'
|
|
||||||
? ''
|
|
||||||
: currentViewWithCombinedFiltersAndSorts?.name
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<DropdownMenuSeparator />
|
<MenuItem
|
||||||
<DropdownMenuItemsContainer>
|
onClick={() => openRecordSpreadsheetImport()}
|
||||||
<MenuItem
|
LeftIcon={IconFileImport}
|
||||||
onClick={() => handleSelectMenu('fields')}
|
text="Import"
|
||||||
LeftIcon={IconTag}
|
/>
|
||||||
text="Fields"
|
</DropdownMenuItemsContainer>
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => openRecordSpreadsheetImport()}
|
|
||||||
LeftIcon={IconFileImport}
|
|
||||||
text="Import"
|
|
||||||
/>
|
|
||||||
</DropdownMenuItemsContainer>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{currentMenu === 'fields' && (
|
{currentMenu === 'fields' && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { createState } from '@/ui/utilities/state/utils/createState';
|
||||||
|
|
||||||
|
export const recordIndexKanbanFieldMetadataIdState = createState<string | null>(
|
||||||
|
{
|
||||||
|
key: 'recordIndexKanbanFieldMetadataIdState',
|
||||||
|
defaultValue: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -5,10 +5,13 @@ import { FieldMetadataType } from '~/generated-metadata/graphql';
|
|||||||
|
|
||||||
export const computeRecordBoardColumnDefinitionsFromObjectMetadata = (
|
export const computeRecordBoardColumnDefinitionsFromObjectMetadata = (
|
||||||
objectMetadataItem: ObjectMetadataItem,
|
objectMetadataItem: ObjectMetadataItem,
|
||||||
|
kanbanFieldMetadataId: string,
|
||||||
navigateToSelectSettings: () => void,
|
navigateToSelectSettings: () => void,
|
||||||
): RecordBoardColumnDefinition[] => {
|
): RecordBoardColumnDefinition[] => {
|
||||||
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
const selectFieldMetadataItem = objectMetadataItem.fields.find(
|
||||||
(field) => field.type === FieldMetadataType.Select,
|
(field) =>
|
||||||
|
field.id === kanbanFieldMetadataId &&
|
||||||
|
field.type === FieldMetadataType.Select,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!selectFieldMetadataItem) {
|
if (!selectFieldMetadataItem) {
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options/components/RecordIndexOptionsDropdown';
|
import { RecordIndexOptionsDropdown } from '@/object-record/record-index/options/components/RecordIndexOptionsDropdown';
|
||||||
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
|
|
||||||
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
|
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
|
||||||
import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect';
|
import { SignInBackgroundMockContainerEffect } from '@/sign-in-background-mock/components/SignInBackgroundMockContainerEffect';
|
||||||
import { ViewBar } from '@/views/components/ViewBar';
|
import { ViewBar } from '@/views/components/ViewBar';
|
||||||
@ -32,7 +31,6 @@ export const SignInBackgroundMockContainer = () => {
|
|||||||
viewType={ViewType.Table}
|
viewType={ViewType.Table}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
optionsDropdownScopeId={RECORD_INDEX_OPTIONS_DROPDOWN_ID}
|
|
||||||
/>
|
/>
|
||||||
<SignInBackgroundMockContainerEffect
|
<SignInBackgroundMockContainerEffect
|
||||||
objectNamePlural={objectNamePlural}
|
objectNamePlural={objectNamePlural}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export type ButtonProps = {
|
|||||||
position?: ButtonPosition;
|
position?: ButtonPosition;
|
||||||
accent?: ButtonAccent;
|
accent?: ButtonAccent;
|
||||||
soon?: boolean;
|
soon?: boolean;
|
||||||
|
justify?: 'center' | 'flex-start' | 'flex-end';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
@ -28,7 +29,13 @@ export type ButtonProps = {
|
|||||||
const StyledButton = styled.button<
|
const StyledButton = styled.button<
|
||||||
Pick<
|
Pick<
|
||||||
ButtonProps,
|
ButtonProps,
|
||||||
'fullWidth' | 'variant' | 'size' | 'position' | 'accent' | 'focus'
|
| 'fullWidth'
|
||||||
|
| 'variant'
|
||||||
|
| 'size'
|
||||||
|
| 'position'
|
||||||
|
| 'accent'
|
||||||
|
| 'focus'
|
||||||
|
| 'justify'
|
||||||
>
|
>
|
||||||
>`
|
>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -177,9 +184,7 @@ const StyledButton = styled.button<
|
|||||||
`;
|
`;
|
||||||
case 'danger':
|
case 'danger':
|
||||||
return css`
|
return css`
|
||||||
background: ${!disabled
|
background: transparent;
|
||||||
? theme.background.transparent.primary
|
|
||||||
: 'transparent'};
|
|
||||||
border-color: ${variant === 'secondary'
|
border-color: ${variant === 'secondary'
|
||||||
? focus
|
? focus
|
||||||
? theme.color.red
|
? theme.color.red
|
||||||
@ -236,6 +241,7 @@ const StyledButton = styled.button<
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
height: ${({ size }) => (size === 'small' ? '24px' : '32px')};
|
height: ${({ size }) => (size === 'small' ? '24px' : '32px')};
|
||||||
|
justify-content: ${({ justify }) => justify};
|
||||||
padding: ${({ theme }) => {
|
padding: ${({ theme }) => {
|
||||||
return `0 ${theme.spacing(2)}`;
|
return `0 ${theme.spacing(2)}`;
|
||||||
}};
|
}};
|
||||||
@ -266,6 +272,7 @@ export const Button = ({
|
|||||||
position = 'standalone',
|
position = 'standalone',
|
||||||
soon = false,
|
soon = false,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
justify = 'flex-start',
|
||||||
focus = false,
|
focus = false,
|
||||||
onClick,
|
onClick,
|
||||||
}: ButtonProps) => {
|
}: ButtonProps) => {
|
||||||
@ -279,6 +286,7 @@ export const Button = ({
|
|||||||
position={position}
|
position={position}
|
||||||
disabled={soon || disabled}
|
disabled={soon || disabled}
|
||||||
focus={focus}
|
focus={focus}
|
||||||
|
justify={justify}
|
||||||
accent={accent}
|
accent={accent}
|
||||||
className={className}
|
className={className}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ export const LightIconButtonGroup = ({
|
|||||||
<LightIconButton
|
<LightIconButton
|
||||||
key={`light-icon-button-${index}`}
|
key={`light-icon-button-${index}`}
|
||||||
Icon={Icon}
|
Icon={Icon}
|
||||||
|
disabled={!onClick}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
size={size}
|
size={size}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -30,6 +30,7 @@ type IconPickerProps = {
|
|||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
variant?: IconButtonVariant;
|
variant?: IconButtonVariant;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
disableBlur?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StyledMenuIconItemsContainer = styled.div`
|
const StyledMenuIconItemsContainer = styled.div`
|
||||||
@ -86,6 +87,7 @@ export const IconPicker = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onOpen,
|
onOpen,
|
||||||
variant = 'secondary',
|
variant = 'secondary',
|
||||||
|
disableBlur = false,
|
||||||
className,
|
className,
|
||||||
}: IconPickerProps) => {
|
}: IconPickerProps) => {
|
||||||
const [searchString, setSearchString] = useState('');
|
const [searchString, setSearchString] = useState('');
|
||||||
@ -148,6 +150,7 @@ export const IconPicker = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownMenuWidth={176}
|
dropdownMenuWidth={176}
|
||||||
|
disableBlur={disableBlur}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<SelectableList
|
<SelectableList
|
||||||
selectableListId="icon-list"
|
selectableListId="icon-list"
|
||||||
|
|||||||
@ -22,6 +22,7 @@ export type SelectOption<Value extends string | number | null> = {
|
|||||||
export type SelectProps<Value extends string | number | null> = {
|
export type SelectProps<Value extends string | number | null> = {
|
||||||
className?: string;
|
className?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
disableBlur?: boolean;
|
||||||
dropdownId: string;
|
dropdownId: string;
|
||||||
dropdownWidth?: `${string}px` | 'auto' | number;
|
dropdownWidth?: `${string}px` | 'auto' | number;
|
||||||
emptyOption?: SelectOption<Value>;
|
emptyOption?: SelectOption<Value>;
|
||||||
@ -75,6 +76,7 @@ const StyledIconChevronDown = styled(IconChevronDown)<{ disabled?: boolean }>`
|
|||||||
export const Select = <Value extends string | number | null>({
|
export const Select = <Value extends string | number | null>({
|
||||||
className,
|
className,
|
||||||
disabled: disabledFromProps,
|
disabled: disabledFromProps,
|
||||||
|
disableBlur = false,
|
||||||
dropdownId,
|
dropdownId,
|
||||||
dropdownWidth = 176,
|
dropdownWidth = 176,
|
||||||
emptyOption,
|
emptyOption,
|
||||||
@ -141,6 +143,7 @@ export const Select = <Value extends string | number | null>({
|
|||||||
dropdownMenuWidth={dropdownWidth}
|
dropdownMenuWidth={dropdownWidth}
|
||||||
dropdownPlacement="bottom-start"
|
dropdownPlacement="bottom-start"
|
||||||
clickableComponent={selectControl}
|
clickableComponent={selectControl}
|
||||||
|
disableBlur={disableBlur}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<>
|
<>
|
||||||
{!!withSearchInput && (
|
{!!withSearchInput && (
|
||||||
|
|||||||
@ -35,6 +35,7 @@ type DropdownProps = {
|
|||||||
dropdownPlacement?: Placement;
|
dropdownPlacement?: Placement;
|
||||||
dropdownMenuWidth?: `${string}px` | `${number}%` | 'auto' | number;
|
dropdownMenuWidth?: `${string}px` | `${number}%` | 'auto' | number;
|
||||||
dropdownOffset?: { x?: number; y?: number };
|
dropdownOffset?: { x?: number; y?: number };
|
||||||
|
disableBlur?: boolean;
|
||||||
onClickOutside?: () => void;
|
onClickOutside?: () => void;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
onOpen?: () => void;
|
onOpen?: () => void;
|
||||||
@ -50,6 +51,7 @@ export const Dropdown = ({
|
|||||||
dropdownHotkeyScope,
|
dropdownHotkeyScope,
|
||||||
dropdownPlacement = 'bottom-end',
|
dropdownPlacement = 'bottom-end',
|
||||||
dropdownOffset = { x: 0, y: 0 },
|
dropdownOffset = { x: 0, y: 0 },
|
||||||
|
disableBlur = false,
|
||||||
onClickOutside,
|
onClickOutside,
|
||||||
onClose,
|
onClose,
|
||||||
onOpen,
|
onOpen,
|
||||||
@ -109,7 +111,10 @@ export const Dropdown = ({
|
|||||||
{clickableComponent && (
|
{clickableComponent && (
|
||||||
<div
|
<div
|
||||||
ref={refs.setReference}
|
ref={refs.setReference}
|
||||||
onClick={toggleDropdown}
|
onClick={() => {
|
||||||
|
toggleDropdown();
|
||||||
|
onClickOutside?.();
|
||||||
|
}}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{clickableComponent}
|
{clickableComponent}
|
||||||
@ -123,6 +128,7 @@ export const Dropdown = ({
|
|||||||
)}
|
)}
|
||||||
{isDropdownOpen && (
|
{isDropdownOpen && (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
|
disableBlur={disableBlur}
|
||||||
width={dropdownMenuWidth ?? dropdownWidth}
|
width={dropdownMenuWidth ?? dropdownWidth}
|
||||||
data-select-disable
|
data-select-disable
|
||||||
ref={refs.setFloating}
|
ref={refs.setFloating}
|
||||||
|
|||||||
@ -9,7 +9,10 @@ const StyledDropdownMenu = styled.div<{
|
|||||||
? 'none'
|
? 'none'
|
||||||
: 'blur(12px) saturate(200%) contrast(50%) brightness(130%)'};
|
: 'blur(12px) saturate(200%) contrast(50%) brightness(130%)'};
|
||||||
|
|
||||||
background: ${({ theme }) => theme.background.transparent.forBackdropFilter};
|
background: ${({ theme, disableBlur }) =>
|
||||||
|
disableBlur
|
||||||
|
? theme.background.primary
|
||||||
|
: theme.background.transparent.secondary};
|
||||||
|
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||||
|
|||||||
@ -11,14 +11,11 @@ const StyledHeader = styled.li`
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-size: ${({ theme }) => theme.font.size.sm};
|
font-size: ${({ theme }) => theme.font.size.sm};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
|
||||||
padding: ${({ theme }) => theme.spacing(1)};
|
padding: ${({ theme }) => theme.spacing(1)};
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: ${({ theme }) => theme.background.transparent.light};
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledChildrenWrapper = styled.span`
|
const StyledChildrenWrapper = styled.span`
|
||||||
@ -46,9 +43,10 @@ export const DropdownMenuHeader = ({
|
|||||||
testId,
|
testId,
|
||||||
}: DropdownMenuHeaderProps) => {
|
}: DropdownMenuHeaderProps) => {
|
||||||
return (
|
return (
|
||||||
<StyledHeader data-testid={testId} onClick={onClick}>
|
<StyledHeader data-testid={testId}>
|
||||||
{StartIcon && (
|
{StartIcon && (
|
||||||
<LightIconButton
|
<LightIconButton
|
||||||
|
onClick={onClick}
|
||||||
testId="dropdown-menu-header-end-icon"
|
testId="dropdown-menu-header-end-icon"
|
||||||
Icon={StartIcon}
|
Icon={StartIcon}
|
||||||
accent="tertiary"
|
accent="tertiary"
|
||||||
@ -58,6 +56,7 @@ export const DropdownMenuHeader = ({
|
|||||||
<StyledChildrenWrapper>{children}</StyledChildrenWrapper>
|
<StyledChildrenWrapper>{children}</StyledChildrenWrapper>
|
||||||
{EndIcon && (
|
{EndIcon && (
|
||||||
<StyledLightIconButton
|
<StyledLightIconButton
|
||||||
|
onClick={onClick}
|
||||||
testId="dropdown-menu-header-end-icon"
|
testId="dropdown-menu-header-end-icon"
|
||||||
Icon={EndIcon}
|
Icon={EndIcon}
|
||||||
accent="tertiary"
|
accent="tertiary"
|
||||||
|
|||||||
@ -30,12 +30,12 @@ const StyledInputContainer = styled.div`
|
|||||||
export const DropdownMenuInput = forwardRef<
|
export const DropdownMenuInput = forwardRef<
|
||||||
HTMLInputElement,
|
HTMLInputElement,
|
||||||
InputHTMLAttributes<HTMLInputElement>
|
InputHTMLAttributes<HTMLInputElement>
|
||||||
>(({ autoFocus, defaultValue, placeholder, onChange }, ref) => {
|
>(({ autoFocus, value, placeholder, onChange }, ref) => {
|
||||||
return (
|
return (
|
||||||
<StyledInputContainer>
|
<StyledInputContainer>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
defaultValue={defaultValue}
|
value={value}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|||||||
@ -274,7 +274,7 @@ export const WithInput: Story = {
|
|||||||
args: {
|
args: {
|
||||||
dropdownComponents: (
|
dropdownComponents: (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuInput defaultValue="Lorem ipsum" autoFocus />
|
<DropdownMenuInput value="Lorem ipsum" autoFocus />
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItemsContainer hasMaxHeight>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{optionsMock.map(({ name }) => (
|
{optionsMock.map(({ name }) => (
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const meta: Meta<typeof DropdownMenuInput> = {
|
|||||||
title: 'UI/Layout/Dropdown/DropdownMenuInput',
|
title: 'UI/Layout/Dropdown/DropdownMenuInput',
|
||||||
component: DropdownMenuInput,
|
component: DropdownMenuInput,
|
||||||
decorators: [ComponentDecorator],
|
decorators: [ComponentDecorator],
|
||||||
args: { defaultValue: 'Lorem ipsum' },
|
args: { value: 'Lorem ipsum' },
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
|
|||||||
@ -1,33 +1,24 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { isUndefined } from '@sniptt/guards';
|
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||||
|
|
||||||
export const FilterQueryParamsEffect = () => {
|
export const QueryParamsFiltersEffect = () => {
|
||||||
const { hasFiltersQueryParams, getFiltersFromQueryParams, viewIdQueryParam } =
|
const { hasFiltersQueryParams, getFiltersFromQueryParams } =
|
||||||
useViewFromQueryParams();
|
useViewFromQueryParams();
|
||||||
const { unsavedToUpsertViewFiltersState, currentViewIdState } =
|
const { unsavedToUpsertViewFiltersState } = useViewStates();
|
||||||
useViewStates();
|
|
||||||
const setUnsavedViewFilter = useSetRecoilState(
|
const setUnsavedViewFilter = useSetRecoilState(
|
||||||
unsavedToUpsertViewFiltersState,
|
unsavedToUpsertViewFiltersState,
|
||||||
);
|
);
|
||||||
const setCurrentViewId = useSetRecoilState(currentViewIdState);
|
|
||||||
const { resetCurrentView } = useResetCurrentView();
|
const { resetCurrentView } = useResetCurrentView();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isUndefined(viewIdQueryParam) || !viewIdQueryParam) {
|
if (!hasFiltersQueryParams) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentViewId(viewIdQueryParam);
|
|
||||||
}, [getFiltersFromQueryParams, setCurrentViewId, viewIdQueryParam]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!hasFiltersQueryParams) return;
|
|
||||||
|
|
||||||
getFiltersFromQueryParams().then((filtersFromParams) => {
|
getFiltersFromQueryParams().then((filtersFromParams) => {
|
||||||
if (Array.isArray(filtersFromParams)) {
|
if (Array.isArray(filtersFromParams)) {
|
||||||
setUnsavedViewFilter(filtersFromParams);
|
setUnsavedViewFilter(filtersFromParams);
|
||||||
@ -44,5 +35,5 @@ export const FilterQueryParamsEffect = () => {
|
|||||||
setUnsavedViewFilter,
|
setUnsavedViewFilter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return null;
|
return <></>;
|
||||||
};
|
};
|
||||||
@ -0,0 +1,38 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { isUndefined } from '@sniptt/guards';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||||
|
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||||
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
|
export const QueryParamsViewIdEffect = () => {
|
||||||
|
const { getFiltersFromQueryParams, viewIdQueryParam } =
|
||||||
|
useViewFromQueryParams();
|
||||||
|
const { currentViewIdState } = useViewStates();
|
||||||
|
|
||||||
|
const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState);
|
||||||
|
const { viewsOnCurrentObject } = useGetCurrentView();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const indexView = viewsOnCurrentObject.find((view) => view.key === 'INDEX');
|
||||||
|
|
||||||
|
if (isUndefined(viewIdQueryParam) && isDefined(indexView)) {
|
||||||
|
setCurrentViewId(indexView.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDefined(viewIdQueryParam)) {
|
||||||
|
setCurrentViewId(viewIdQueryParam);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
currentViewId,
|
||||||
|
getFiltersFromQueryParams,
|
||||||
|
setCurrentViewId,
|
||||||
|
viewIdQueryParam,
|
||||||
|
viewsOnCurrentObject,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
||||||
@ -7,14 +7,18 @@ import { Button } from '@/ui/input/button/components/Button';
|
|||||||
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
|
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||||
|
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
import { UPDATE_VIEW_DROPDOWN_ID } from '@/views/constants/UpdateViewDropdownId';
|
import { UPDATE_VIEW_BUTTON_DROPDOWN_ID } from '@/views/constants/UpdateViewButtonDropdownId';
|
||||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||||
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { useSaveCurrentViewFiltersAndSorts } from '@/views/hooks/useSaveCurrentViewFiltersAndSorts';
|
import { useSaveCurrentViewFiltersAndSorts } from '@/views/hooks/useSaveCurrentViewFiltersAndSorts';
|
||||||
|
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';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
background: ${({ theme }) => theme.color.blue};
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||||
@ -23,25 +27,50 @@ const StyledContainer = styled.div`
|
|||||||
|
|
||||||
export type UpdateViewButtonGroupProps = {
|
export type UpdateViewButtonGroupProps = {
|
||||||
hotkeyScope: HotkeyScope;
|
hotkeyScope: HotkeyScope;
|
||||||
onViewEditModeChange?: () => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const UpdateViewButtonGroup = ({
|
export const UpdateViewButtonGroup = ({
|
||||||
hotkeyScope,
|
hotkeyScope,
|
||||||
onViewEditModeChange,
|
|
||||||
}: UpdateViewButtonGroupProps) => {
|
}: UpdateViewButtonGroupProps) => {
|
||||||
const { canPersistViewSelector, viewEditModeState } = useViewStates();
|
const { canPersistViewSelector, currentViewIdState } = useViewStates();
|
||||||
const { saveCurrentViewFilterAndSorts } = useSaveCurrentViewFiltersAndSorts();
|
const { saveCurrentViewFilterAndSorts } = useSaveCurrentViewFiltersAndSorts();
|
||||||
|
|
||||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
const { setViewPickerMode } = useViewPickerMode();
|
||||||
|
const { viewPickerReferenceViewIdState } = useViewPickerStates();
|
||||||
const canPersistView = useRecoilValue(canPersistViewSelector());
|
const canPersistView = useRecoilValue(canPersistViewSelector());
|
||||||
|
|
||||||
const handleCreateViewButtonClick = useCallback(() => {
|
const { closeDropdown: closeUpdateViewButtonDropdown } = useDropdown(
|
||||||
setViewEditMode('create');
|
UPDATE_VIEW_BUTTON_DROPDOWN_ID,
|
||||||
onViewEditModeChange?.();
|
);
|
||||||
}, [setViewEditMode, onViewEditModeChange]);
|
const { openDropdown: openViewPickerDropdown } = useDropdown(
|
||||||
|
VIEW_PICKER_DROPDOWN_ID,
|
||||||
|
);
|
||||||
|
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||||
|
|
||||||
const handleViewSubmit = async () => {
|
const currentViewId = useRecoilValue(currentViewIdState);
|
||||||
|
|
||||||
|
const setViewPickerReferenceViewId = useSetRecoilState(
|
||||||
|
viewPickerReferenceViewIdState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleViewCreate = useCallback(() => {
|
||||||
|
if (!currentViewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
openViewPickerDropdown();
|
||||||
|
setViewPickerReferenceViewId(currentViewId);
|
||||||
|
setViewPickerMode('create');
|
||||||
|
|
||||||
|
closeUpdateViewButtonDropdown();
|
||||||
|
}, [
|
||||||
|
closeUpdateViewButtonDropdown,
|
||||||
|
currentViewId,
|
||||||
|
openViewPickerDropdown,
|
||||||
|
setViewPickerMode,
|
||||||
|
setViewPickerReferenceViewId,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const handleViewUpdate = async () => {
|
||||||
await saveCurrentViewFilterAndSorts();
|
await saveCurrentViewFilterAndSorts();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,27 +80,42 @@ export const UpdateViewButtonGroup = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<ButtonGroup size="small" accent="blue">
|
{currentViewWithCombinedFiltersAndSorts?.key !== 'INDEX' ? (
|
||||||
<Button title="Update view" onClick={handleViewSubmit} />
|
<ButtonGroup size="small" accent="blue">
|
||||||
<Dropdown
|
<Button title="Update view" onClick={handleViewUpdate} />
|
||||||
dropdownId={UPDATE_VIEW_DROPDOWN_ID}
|
<Dropdown
|
||||||
dropdownHotkeyScope={hotkeyScope}
|
dropdownId={UPDATE_VIEW_BUTTON_DROPDOWN_ID}
|
||||||
clickableComponent={
|
dropdownHotkeyScope={hotkeyScope}
|
||||||
<Button size="small" accent="blue" Icon={IconChevronDown} />
|
clickableComponent={
|
||||||
}
|
<Button
|
||||||
dropdownComponents={
|
size="small"
|
||||||
<>
|
accent="blue"
|
||||||
<DropdownMenuItemsContainer>
|
Icon={IconChevronDown}
|
||||||
<MenuItem
|
position="right"
|
||||||
onClick={handleCreateViewButtonClick}
|
/>
|
||||||
LeftIcon={IconPlus}
|
}
|
||||||
text="Create view"
|
dropdownComponents={
|
||||||
/>
|
<>
|
||||||
</DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
</>
|
<MenuItem
|
||||||
}
|
onClick={handleViewCreate}
|
||||||
|
LeftIcon={IconPlus}
|
||||||
|
text="Create view"
|
||||||
|
/>
|
||||||
|
</DropdownMenuItemsContainer>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
title="Save as new view"
|
||||||
|
onClick={handleViewCreate}
|
||||||
|
accent="blue"
|
||||||
|
size="small"
|
||||||
|
variant="secondary"
|
||||||
/>
|
/>
|
||||||
</ButtonGroup>
|
)}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,26 +4,25 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { ObjectFilterDropdownButton } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton';
|
import { ObjectFilterDropdownButton } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownButton';
|
||||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||||
import { ObjectSortDropdownButton } from '@/object-record/object-sort-dropdown/components/ObjectSortDropdownButton';
|
import { ObjectSortDropdownButton } from '@/object-record/object-sort-dropdown/components/ObjectSortDropdownButton';
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|
||||||
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
||||||
import { FilterQueryParamsEffect } from '@/views/components/FilterQueryParamsEffect';
|
import { QueryParamsFiltersEffect } from '@/views/components/QueryParamsFiltersEffect';
|
||||||
|
import { QueryParamsViewIdEffect } from '@/views/components/QueryParamsViewIdEffect';
|
||||||
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
||||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||||
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
|
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
|
||||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
|
import { ViewPickerDropdown } from '@/views/view-picker/components/ViewPickerDropdown';
|
||||||
|
|
||||||
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||||
|
|
||||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||||
import { ViewBarDetails } from './ViewBarDetails';
|
import { ViewBarDetails } from './ViewBarDetails';
|
||||||
import { ViewsDropdownButton } from './ViewsDropdownButton';
|
|
||||||
|
|
||||||
export type ViewBarProps = {
|
export type ViewBarProps = {
|
||||||
viewBarId: string;
|
viewBarId: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
optionsDropdownButton: ReactNode;
|
optionsDropdownButton: ReactNode;
|
||||||
optionsDropdownScopeId: string;
|
|
||||||
onCurrentViewChange: (view: GraphQLView | undefined) => void | Promise<void>;
|
onCurrentViewChange: (view: GraphQLView | undefined) => void | Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -31,18 +30,17 @@ export const ViewBar = ({
|
|||||||
viewBarId,
|
viewBarId,
|
||||||
className,
|
className,
|
||||||
optionsDropdownButton,
|
optionsDropdownButton,
|
||||||
optionsDropdownScopeId,
|
|
||||||
onCurrentViewChange,
|
onCurrentViewChange,
|
||||||
}: ViewBarProps) => {
|
}: ViewBarProps) => {
|
||||||
const { openDropdown: openOptionsDropdownButton } = useDropdown(
|
|
||||||
optionsDropdownScopeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { objectNamePlural } = useParams();
|
const { objectNamePlural } = useParams();
|
||||||
|
|
||||||
const filterDropdownId = 'view-filter';
|
const filterDropdownId = 'view-filter';
|
||||||
const sortDropdownId = 'view-sort';
|
const sortDropdownId = 'view-sort';
|
||||||
|
|
||||||
|
if (!objectNamePlural) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewScope
|
<ViewScope
|
||||||
viewScopeId={viewBarId}
|
viewScopeId={viewBarId}
|
||||||
@ -51,16 +49,15 @@ export const ViewBar = ({
|
|||||||
<ViewBarEffect viewBarId={viewBarId} />
|
<ViewBarEffect viewBarId={viewBarId} />
|
||||||
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
|
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
|
||||||
<ViewBarSortEffect sortDropdownId={sortDropdownId} />
|
<ViewBarSortEffect sortDropdownId={sortDropdownId} />
|
||||||
{!!objectNamePlural && <FilterQueryParamsEffect />}
|
<QueryParamsFiltersEffect />
|
||||||
|
<QueryParamsViewIdEffect />
|
||||||
|
|
||||||
<TopBar
|
<TopBar
|
||||||
className={className}
|
className={className}
|
||||||
leftComponent={
|
leftComponent={
|
||||||
<ViewsDropdownButton
|
<>
|
||||||
onViewEditModeChange={openOptionsDropdownButton}
|
<ViewPickerDropdown />
|
||||||
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
|
</>
|
||||||
optionsDropdownScopeId={optionsDropdownScopeId}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
displayBottomBorder={false}
|
displayBottomBorder={false}
|
||||||
rightComponent={
|
rightComponent={
|
||||||
@ -87,8 +84,9 @@ export const ViewBar = ({
|
|||||||
viewBarId={viewBarId}
|
viewBarId={viewBarId}
|
||||||
rightComponent={
|
rightComponent={
|
||||||
<UpdateViewButtonGroup
|
<UpdateViewButtonGroup
|
||||||
onViewEditModeChange={openOptionsDropdownButton}
|
hotkeyScope={{
|
||||||
hotkeyScope={{ scope: ViewsHotkeyScope.CreateDropdown }}
|
scope: ViewsHotkeyScope.UpdateViewButtonDropdown,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { isUndefined } from '@sniptt/guards';
|
import { isUndefined } from '@sniptt/guards';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
|
|
||||||
type ViewBarEffectProps = {
|
type ViewBarEffectProps = {
|
||||||
viewBarId: string;
|
viewBarId: string;
|
||||||
@ -17,7 +16,6 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
|
|||||||
useGetCurrentView(viewBarId);
|
useGetCurrentView(viewBarId);
|
||||||
const {
|
const {
|
||||||
onCurrentViewChangeState,
|
onCurrentViewChangeState,
|
||||||
currentViewIdState,
|
|
||||||
availableFilterDefinitionsState,
|
availableFilterDefinitionsState,
|
||||||
isPersistingViewFieldsState,
|
isPersistingViewFieldsState,
|
||||||
} = useViewStates(viewBarId);
|
} = useViewStates(viewBarId);
|
||||||
@ -30,7 +28,6 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
|
|||||||
availableFilterDefinitionsState,
|
availableFilterDefinitionsState,
|
||||||
);
|
);
|
||||||
const isPersistingViewFields = useRecoilValue(isPersistingViewFieldsState);
|
const isPersistingViewFields = useRecoilValue(isPersistingViewFieldsState);
|
||||||
const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
@ -39,14 +36,14 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
|
|||||||
currentViewSnapshot,
|
currentViewSnapshot,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
setCurrentViewSnapshot(currentViewWithCombinedFiltersAndSorts);
|
|
||||||
|
|
||||||
if (isUndefined(currentViewWithCombinedFiltersAndSorts)) {
|
if (isUndefined(currentViewWithCombinedFiltersAndSorts)) {
|
||||||
|
setCurrentViewSnapshot(currentViewWithCombinedFiltersAndSorts);
|
||||||
onCurrentViewChange?.(undefined);
|
onCurrentViewChange?.(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isPersistingViewFields) {
|
if (!isPersistingViewFields) {
|
||||||
|
setCurrentViewSnapshot(currentViewWithCombinedFiltersAndSorts);
|
||||||
onCurrentViewChange?.(currentViewWithCombinedFiltersAndSorts);
|
onCurrentViewChange?.(currentViewWithCombinedFiltersAndSorts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,14 +55,5 @@ export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
|
|||||||
onCurrentViewChange,
|
onCurrentViewChange,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
isDefined(currentViewWithCombinedFiltersAndSorts) &&
|
|
||||||
!isDefined(currentViewId)
|
|
||||||
) {
|
|
||||||
setCurrentViewId(currentViewWithCombinedFiltersAndSorts.id);
|
|
||||||
}
|
|
||||||
}, [currentViewWithCombinedFiltersAndSorts, currentViewId, setCurrentViewId]);
|
|
||||||
|
|
||||||
return <></>;
|
return <></>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,189 +0,0 @@
|
|||||||
import { MouseEvent } from 'react';
|
|
||||||
import { useTheme } from '@emotion/react';
|
|
||||||
import styled from '@emotion/styled';
|
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import {
|
|
||||||
IconChevronDown,
|
|
||||||
IconList,
|
|
||||||
IconPencil,
|
|
||||||
IconPlus,
|
|
||||||
IconTrash,
|
|
||||||
} from '@/ui/display/icon';
|
|
||||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
|
||||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
|
||||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
|
||||||
import { StyledDropdownButtonContainer } from '@/ui/layout/dropdown/components/StyledDropdownButtonContainer';
|
|
||||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
|
||||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
|
||||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
|
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
|
||||||
import { VIEWS_DROPDOWN_ID } from '@/views/constants/ViewsDropdownId';
|
|
||||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
|
||||||
import { useHandleViews } from '@/views/hooks/useHandleViews';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
|
|
||||||
import { useViewStates } from '../hooks/internal/useViewStates';
|
|
||||||
|
|
||||||
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
|
||||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
|
||||||
`;
|
|
||||||
|
|
||||||
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 type ViewsDropdownButtonProps = {
|
|
||||||
hotkeyScope: HotkeyScope;
|
|
||||||
onViewEditModeChange?: () => void;
|
|
||||||
optionsDropdownScopeId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ViewsDropdownButton = ({
|
|
||||||
hotkeyScope,
|
|
||||||
onViewEditModeChange,
|
|
||||||
optionsDropdownScopeId,
|
|
||||||
}: ViewsDropdownButtonProps) => {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const { removeView, selectView } = useHandleViews();
|
|
||||||
const { entityCountInCurrentViewState, viewEditModeState } = useViewStates();
|
|
||||||
|
|
||||||
const { currentViewWithCombinedFiltersAndSorts, viewsOnCurrentObject } =
|
|
||||||
useGetCurrentView();
|
|
||||||
|
|
||||||
const entityCountInCurrentView = useRecoilValue(
|
|
||||||
entityCountInCurrentViewState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
|
||||||
|
|
||||||
const {
|
|
||||||
isDropdownOpen: isViewsDropdownOpen,
|
|
||||||
closeDropdown: closeViewsDropdown,
|
|
||||||
} = useDropdown(VIEWS_DROPDOWN_ID);
|
|
||||||
|
|
||||||
const { openDropdown: openOptionsDropdown } = useDropdown(
|
|
||||||
optionsDropdownScopeId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleViewSelect = (viewId: string) => {
|
|
||||||
selectView(viewId);
|
|
||||||
closeViewsDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddViewButtonClick = () => {
|
|
||||||
setViewEditMode('create');
|
|
||||||
onViewEditModeChange?.();
|
|
||||||
closeViewsDropdown();
|
|
||||||
openOptionsDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditViewButtonClick = (
|
|
||||||
event: MouseEvent<HTMLButtonElement>,
|
|
||||||
viewId: string,
|
|
||||||
) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
selectView(viewId);
|
|
||||||
setViewEditMode('edit');
|
|
||||||
onViewEditModeChange?.();
|
|
||||||
closeViewsDropdown();
|
|
||||||
openOptionsDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteViewButtonClick = async (
|
|
||||||
event: MouseEvent<HTMLButtonElement>,
|
|
||||||
viewId: string,
|
|
||||||
) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
|
|
||||||
await removeView(viewId);
|
|
||||||
selectView(viewsOnCurrentObject.filter((view) => view.id !== viewId)[0].id);
|
|
||||||
closeViewsDropdown();
|
|
||||||
};
|
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
|
||||||
const CurrentViewIcon = getIcon(currentViewWithCombinedFiltersAndSorts?.icon);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
dropdownId={VIEWS_DROPDOWN_ID}
|
|
||||||
dropdownHotkeyScope={hotkeyScope}
|
|
||||||
clickableComponent={
|
|
||||||
<StyledDropdownButtonContainer isUnfolded={isViewsDropdownOpen}>
|
|
||||||
{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={
|
|
||||||
<>
|
|
||||||
<DropdownMenuItemsContainer>
|
|
||||||
{viewsOnCurrentObject.map((view) => (
|
|
||||||
<MenuItem
|
|
||||||
key={view.id}
|
|
||||||
iconButtons={[
|
|
||||||
currentViewWithCombinedFiltersAndSorts?.id === view.id
|
|
||||||
? {
|
|
||||||
Icon: IconPencil,
|
|
||||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
|
||||||
handleEditViewButtonClick(event, view.id),
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
viewsOnCurrentObject.length > 1
|
|
||||||
? {
|
|
||||||
Icon: IconTrash,
|
|
||||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
|
||||||
handleDeleteViewButtonClick(event, view.id),
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
].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>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const UPDATE_VIEW_BUTTON_DROPDOWN_ID = 'update-view-button';
|
||||||
@ -1 +0,0 @@
|
|||||||
export const UPDATE_VIEW_DROPDOWN_ID = 'update-view';
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export const VIEWS_DROPDOWN_ID = 'views';
|
|
||||||
@ -52,7 +52,10 @@ export const useViewFromQueryParams = () => {
|
|||||||
[queryParamsValidation],
|
[queryParamsValidation],
|
||||||
);
|
);
|
||||||
const viewIdQueryParam = useMemo(
|
const viewIdQueryParam = useMemo(
|
||||||
() => queryParamsValidation.success && queryParamsValidation.data.view,
|
() =>
|
||||||
|
queryParamsValidation.success
|
||||||
|
? queryParamsValidation.data.view
|
||||||
|
: undefined,
|
||||||
[queryParamsValidation],
|
[queryParamsValidation],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,6 @@ import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsav
|
|||||||
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
|
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
|
||||||
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
|
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
|
||||||
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
|
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
|
||||||
import { viewEditModeComponentState } from '@/views/states/viewEditModeComponentState';
|
|
||||||
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
|
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
|
||||||
|
|
||||||
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
|
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
|
||||||
@ -60,10 +59,6 @@ export const useViewStates = (viewComponentId?: string) => {
|
|||||||
entityCountInCurrentViewComponentState,
|
entityCountInCurrentViewComponentState,
|
||||||
componentId,
|
componentId,
|
||||||
),
|
),
|
||||||
viewEditModeState: extractComponentState(
|
|
||||||
viewEditModeComponentState,
|
|
||||||
componentId,
|
|
||||||
),
|
|
||||||
viewObjectMetadataIdState: extractComponentState(
|
viewObjectMetadataIdState: extractComponentState(
|
||||||
viewObjectMetadataIdComponentState,
|
viewObjectMetadataIdComponentState,
|
||||||
componentId,
|
componentId,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { ViewScopeInternalContext } from '@/views/scopes/scope-internal-context/
|
|||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { combinedViewFilters } from '@/views/utils/combinedViewFilters';
|
import { combinedViewFilters } from '@/views/utils/combinedViewFilters';
|
||||||
import { combinedViewSorts } from '@/views/utils/combinedViewSorts';
|
import { combinedViewSorts } from '@/views/utils/combinedViewSorts';
|
||||||
|
import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const useGetCurrentView = (viewBarComponentId?: string) => {
|
export const useGetCurrentView = (viewBarComponentId?: string) => {
|
||||||
@ -51,16 +52,10 @@ export const useGetCurrentView = (viewBarComponentId?: string) => {
|
|||||||
setIsCurrentViewKeyIndex(currentView?.key === 'INDEX');
|
setIsCurrentViewKeyIndex(currentView?.key === 'INDEX');
|
||||||
}, [currentView, setIsCurrentViewKeyIndex]);
|
}, [currentView, setIsCurrentViewKeyIndex]);
|
||||||
|
|
||||||
const viewsOnCurrentObject = views
|
const viewsOnCurrentObject = getObjectMetadataItemViews(
|
||||||
.filter((view) => view.objectMetadataId === viewObjectMetadataId)
|
viewObjectMetadataId ?? '',
|
||||||
.map((view) => ({
|
views,
|
||||||
id: view.id,
|
);
|
||||||
name: view.name,
|
|
||||||
type: view.type,
|
|
||||||
key: view.key,
|
|
||||||
objectMetadataId: view.objectMetadataId,
|
|
||||||
icon: view.icon,
|
|
||||||
}));
|
|
||||||
|
|
||||||
const unsavedToUpsertViewFilters = useRecoilValue(
|
const unsavedToUpsertViewFilters = useRecoilValue(
|
||||||
unsavedToUpsertViewFiltersState,
|
unsavedToUpsertViewFiltersState,
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { usePersistViewFieldRecords } from '@/views/hooks/internal/usePersistVie
|
|||||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||||
|
import { useSaveCurrentViewFiltersAndSorts } from '@/views/hooks/useSaveCurrentViewFiltersAndSorts';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||||
@ -19,7 +20,8 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
|||||||
export const useHandleViews = (viewBarComponentId?: string) => {
|
export const useHandleViews = (viewBarComponentId?: string) => {
|
||||||
const { resetCurrentView } = useResetCurrentView(viewBarComponentId);
|
const { resetCurrentView } = useResetCurrentView(viewBarComponentId);
|
||||||
|
|
||||||
const { currentViewIdState } = useViewStates(viewBarComponentId);
|
const { currentViewIdState, isPersistingViewFieldsState } =
|
||||||
|
useViewStates(viewBarComponentId);
|
||||||
|
|
||||||
const { getViewFromCache } = useGetViewFromCache();
|
const { getViewFromCache } = useGetViewFromCache();
|
||||||
|
|
||||||
@ -36,8 +38,8 @@ export const useHandleViews = (viewBarComponentId?: string) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { createViewFieldRecords } = usePersistViewFieldRecords();
|
const { createViewFieldRecords } = usePersistViewFieldRecords();
|
||||||
|
const { saveCurrentViewFilterAndSorts } =
|
||||||
const createViewFromCurrent = useRecoilCallback(() => () => {}, []);
|
useSaveCurrentViewFiltersAndSorts(viewBarComponentId);
|
||||||
|
|
||||||
const [_, setSearchParams] = useSearchParams();
|
const [_, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
@ -48,9 +50,20 @@ export const useHandleViews = (viewBarComponentId?: string) => {
|
|||||||
[deleteOneRecord],
|
[deleteOneRecord],
|
||||||
);
|
);
|
||||||
|
|
||||||
const createEmptyView = useRecoilCallback(
|
const createView = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot, set }) =>
|
||||||
async (id: string, name: string) => {
|
async ({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
icon,
|
||||||
|
kanbanFieldMetadataId,
|
||||||
|
type,
|
||||||
|
}: Partial<
|
||||||
|
Pick<
|
||||||
|
GraphQLView,
|
||||||
|
'id' | 'name' | 'icon' | 'kanbanFieldMetadataId' | 'type'
|
||||||
|
>
|
||||||
|
>) => {
|
||||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||||
|
|
||||||
if (!isDefined(currentViewId)) {
|
if (!isDefined(currentViewId)) {
|
||||||
@ -63,11 +76,17 @@ export const useHandleViews = (viewBarComponentId?: string) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set(isPersistingViewFieldsState, true);
|
||||||
|
|
||||||
const newView = await createOneRecord({
|
const newView = await createOneRecord({
|
||||||
id: id ?? v4(),
|
id: id ?? v4(),
|
||||||
name: name,
|
name: name ?? view.name,
|
||||||
|
icon: icon ?? view.icon,
|
||||||
|
key: null,
|
||||||
|
kanbanFieldMetadataId:
|
||||||
|
kanbanFieldMetadataId ?? view.kanbanFieldMetadataId,
|
||||||
|
type: type ?? view.type,
|
||||||
objectMetadataId: view.objectMetadataId,
|
objectMetadataId: view.objectMetadataId,
|
||||||
type: view.type,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isUndefinedOrNull(newView)) {
|
if (isUndefinedOrNull(newView)) {
|
||||||
@ -75,33 +94,36 @@ export const useHandleViews = (viewBarComponentId?: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await createViewFieldRecords(view.viewFields, newView);
|
await createViewFieldRecords(view.viewFields, newView);
|
||||||
|
await saveCurrentViewFilterAndSorts(newView.id);
|
||||||
|
set(isPersistingViewFieldsState, false);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
createOneRecord,
|
createOneRecord,
|
||||||
createViewFieldRecords,
|
createViewFieldRecords,
|
||||||
currentViewIdState,
|
currentViewIdState,
|
||||||
getViewFromCache,
|
getViewFromCache,
|
||||||
|
isPersistingViewFieldsState,
|
||||||
|
saveCurrentViewFilterAndSorts,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const changeViewInUrl = useCallback(
|
const changeViewInUrl = useCallback(
|
||||||
(viewId: string) => {
|
(viewId: string) => {
|
||||||
setSearchParams((previousSearchParams) => {
|
setSearchParams(() => {
|
||||||
previousSearchParams.set('view', viewId);
|
const searchParams = new URLSearchParams();
|
||||||
return previousSearchParams;
|
searchParams.set('view', viewId);
|
||||||
|
return searchParams;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setSearchParams],
|
[setSearchParams],
|
||||||
);
|
);
|
||||||
|
|
||||||
const selectView = useRecoilCallback(
|
const selectView = useRecoilCallback(
|
||||||
({ set }) =>
|
() => async (viewId: string) => {
|
||||||
async (viewId: string) => {
|
changeViewInUrl(viewId);
|
||||||
set(currentViewIdState, viewId);
|
resetCurrentView();
|
||||||
changeViewInUrl(viewId);
|
},
|
||||||
resetCurrentView();
|
[changeViewInUrl, resetCurrentView],
|
||||||
},
|
|
||||||
[changeViewInUrl, currentViewIdState, resetCurrentView],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateCurrentView = useRecoilCallback(
|
const updateCurrentView = useRecoilCallback(
|
||||||
@ -120,11 +142,23 @@ export const useHandleViews = (viewBarComponentId?: string) => {
|
|||||||
[currentViewIdState, updateOneRecord],
|
[currentViewIdState, updateOneRecord],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const updateView = useRecoilCallback(
|
||||||
|
() => async (view: Partial<GraphQLView>) => {
|
||||||
|
if (isDefined(view.id)) {
|
||||||
|
await updateOneRecord({
|
||||||
|
idToUpdate: view.id,
|
||||||
|
updateOneRecordInput: view,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateOneRecord],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
selectView,
|
selectView,
|
||||||
updateCurrentView,
|
updateCurrentView,
|
||||||
|
updateView,
|
||||||
removeView,
|
removeView,
|
||||||
createEmptyView,
|
createView,
|
||||||
createViewFromCurrent,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -125,7 +125,7 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
|||||||
|
|
||||||
const saveCurrentViewFilterAndSorts = useRecoilCallback(
|
const saveCurrentViewFilterAndSorts = useRecoilCallback(
|
||||||
({ snapshot }) =>
|
({ snapshot }) =>
|
||||||
async () => {
|
async (viewId?: string) => {
|
||||||
const currentViewId = snapshot
|
const currentViewId = snapshot
|
||||||
.getLoadable(currentViewIdState)
|
.getLoadable(currentViewIdState)
|
||||||
.getValue();
|
.getValue();
|
||||||
@ -134,8 +134,8 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await saveViewFilters(currentViewId);
|
await saveViewFilters(viewId ?? currentViewId);
|
||||||
await saveViewSorts(currentViewId);
|
await saveViewSorts(viewId ?? currentViewId);
|
||||||
resetCurrentView();
|
resetCurrentView();
|
||||||
},
|
},
|
||||||
[currentViewIdState, resetCurrentView, saveViewFilters, saveViewSorts],
|
[currentViewIdState, resetCurrentView, saveViewFilters, saveViewSorts],
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
|
||||||
|
|
||||||
export const useViewBarEditMode = (viewBarComponentId?: string) => {
|
|
||||||
const { viewEditModeState } = useViewStates(viewBarComponentId);
|
|
||||||
|
|
||||||
const [viewEditMode, setViewEditMode] = useRecoilState(viewEditModeState);
|
|
||||||
|
|
||||||
return {
|
|
||||||
viewEditMode,
|
|
||||||
setViewEditMode,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { Position } from '@/object-metadata/types/Position';
|
|
||||||
import { ViewField } from '@/views/types/ViewField';
|
import { ViewField } from '@/views/types/ViewField';
|
||||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||||
import { ViewKey } from '@/views/types/ViewKey';
|
import { ViewKey } from '@/views/types/ViewKey';
|
||||||
@ -10,11 +9,12 @@ export type GraphQLView = {
|
|||||||
name: string;
|
name: string;
|
||||||
type: ViewType;
|
type: ViewType;
|
||||||
key: ViewKey | null;
|
key: ViewKey | null;
|
||||||
|
kanbanFieldMetadataId: string;
|
||||||
objectMetadataId: string;
|
objectMetadataId: string;
|
||||||
isCompact: boolean;
|
isCompact: boolean;
|
||||||
viewFields: ViewField[];
|
viewFields: ViewField[];
|
||||||
viewFilters: ViewFilter[];
|
viewFilters: ViewFilter[];
|
||||||
viewSorts: ViewSort[];
|
viewSorts: ViewSort[];
|
||||||
position: Position;
|
position: number;
|
||||||
icon: string;
|
icon: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
export enum ViewsHotkeyScope {
|
export enum ViewsHotkeyScope {
|
||||||
ListDropdown = 'views-list-dropdown',
|
ListDropdown = 'views-list-dropdown',
|
||||||
CreateDropdown = 'views-create-dropdown',
|
UpdateViewButtonDropdown = 'update-view-button-dropdown',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
|
|
||||||
|
export const getObjectMetadataItemViews = (
|
||||||
|
viewObjectMetadataId: string,
|
||||||
|
views: GraphQLView[],
|
||||||
|
) => {
|
||||||
|
const indexView = views.find(
|
||||||
|
(view) =>
|
||||||
|
view.key === 'INDEX' && view.objectMetadataId === viewObjectMetadataId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return [
|
||||||
|
...views
|
||||||
|
.filter((view) => view.objectMetadataId === viewObjectMetadataId)
|
||||||
|
.filter((view) => view.key !== 'INDEX'),
|
||||||
|
]
|
||||||
|
.sort((a, b) => a.position - b.position)
|
||||||
|
.concat(indexView ? [indexView] : [])
|
||||||
|
.map((view) => ({
|
||||||
|
id: view.id,
|
||||||
|
name: view.name,
|
||||||
|
type: view.type,
|
||||||
|
key: view.key,
|
||||||
|
position: view.position,
|
||||||
|
objectMetadataId: view.objectMetadataId,
|
||||||
|
kanbanFieldMetadataId: view.kanbanFieldMetadataId,
|
||||||
|
icon: view.icon,
|
||||||
|
}));
|
||||||
|
};
|
||||||
@ -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 === '')
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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 <></>;
|
||||||
|
};
|
||||||
@ -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 />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const VIEW_PICKER_DROPDOWN_ID = 'view-picker';
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID = 'view-picker-kanban-field';
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export const VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID = 'view-picker-view-type';
|
||||||
@ -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 };
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -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 };
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const viewPickerInputNameComponentState = createComponentState<string>({
|
||||||
|
key: 'viewPickerInputNameComponentState',
|
||||||
|
defaultValue: '',
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const viewPickerIsPersistingComponentState =
|
||||||
|
createComponentState<boolean>({
|
||||||
|
key: 'viewPickerIsPersistingComponentState',
|
||||||
|
defaultValue: false,
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const viewPickerKanbanFieldMetadataIdComponentState =
|
||||||
|
createComponentState<string>({
|
||||||
|
key: 'viewPickerKanbanFieldMetadataIdComponentState',
|
||||||
|
defaultValue: '',
|
||||||
|
});
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
export const viewEditModeComponentState = createComponentState<
|
export const viewPickerModeComponentState = createComponentState<
|
||||||
'none' | 'edit' | 'create'
|
'list' | 'edit' | 'create'
|
||||||
>({
|
>({
|
||||||
key: 'viewEditModeComponentState',
|
key: 'viewEditModeComponentState',
|
||||||
defaultValue: 'none',
|
defaultValue: 'list',
|
||||||
});
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const viewPickerReferenceViewIdComponentState =
|
||||||
|
createComponentState<string>({
|
||||||
|
key: 'viewPickerReferenceViewIdComponentState',
|
||||||
|
defaultValue: '',
|
||||||
|
});
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||||
|
|
||||||
|
export const viewPickerSelectedIconComponentState =
|
||||||
|
createComponentState<string>({
|
||||||
|
key: 'viewPickerSelectedIconComponentState',
|
||||||
|
defaultValue: '',
|
||||||
|
});
|
||||||
@ -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,
|
||||||
|
});
|
||||||
@ -85,14 +85,12 @@ export const viewPrefillData = async (
|
|||||||
'size',
|
'size',
|
||||||
])
|
])
|
||||||
.values([
|
.values([
|
||||||
...viewCompanyFields(viewIdMap['Index Companies'], objectMetadataMap),
|
|
||||||
...viewPersonFields(viewIdMap['Index People'], objectMetadataMap),
|
|
||||||
...viewOpportunityFields(
|
|
||||||
viewIdMap['Index Opportunities'],
|
|
||||||
objectMetadataMap,
|
|
||||||
),
|
|
||||||
...viewCompanyFields(viewIdMap['All Companies'], objectMetadataMap),
|
...viewCompanyFields(viewIdMap['All Companies'], objectMetadataMap),
|
||||||
...viewPersonFields(viewIdMap['All People'], objectMetadataMap),
|
...viewPersonFields(viewIdMap['All People'], objectMetadataMap),
|
||||||
|
...viewOpportunityFields(
|
||||||
|
viewIdMap['All Opportunities'],
|
||||||
|
objectMetadataMap,
|
||||||
|
),
|
||||||
...viewOpportunityFields(viewIdMap['By Stage'], objectMetadataMap),
|
...viewOpportunityFields(viewIdMap['By Stage'], objectMetadataMap),
|
||||||
])
|
])
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
Reference in New Issue
Block a user