diff --git a/src/common/i18n/en.json b/src/common/i18n/en.json index 33e7af48..a024eee7 100644 --- a/src/common/i18n/en.json +++ b/src/common/i18n/en.json @@ -76,6 +76,7 @@ "action.tab-views.search-results.tooltip": "Search results tab", "action.tab-views.create-best.tooltip": "BEST draft creation/edit tab", "action.tab-views.export-layer.tooltip": "Export layer tab", + "action.tab-views.basemaps.tooltip": "Basemaps tab", "action.point.tooltip": "Search by POI", "action.box.tooltip": "Search by bounding box", @@ -207,6 +208,7 @@ "tab-views.catalog.site.slave": "Secondary", "tab-views.create-best": "BEST edit", "tab-views.export-layer": "Export Layer", + "tab-views.basemaps": "Basemaps", "tab-views.catalog.top-categories.unpublished": "Unpublished", "tab-views.catalog.top-categories.bests": "BESTs", diff --git a/src/common/i18n/he.json b/src/common/i18n/he.json index 92827a43..33e1a752 100644 --- a/src/common/i18n/he.json +++ b/src/common/i18n/he.json @@ -76,6 +76,7 @@ "action.tab-views.search-results.tooltip": "טאב תוצאות חיפוש", "action.tab-views.create-best.tooltip": "טאב עריכת טיוטת BEST", "action.tab-views.export-layer.tooltip": "טאב ייצוא שכבה", + "action.tab-views.basemaps.tooltip": "טאב מפות בסיס", "action.point.tooltip": "חיפוש בקטלוג בעזרת קואורדינטות נקודת ציון", "action.box.tooltip": "חיפוש בקטלוג בעזרת מלבן חוסם", @@ -207,6 +208,7 @@ "tab-views.catalog.site.slave": "משני", "tab-views.create-best": "עריכת טיוטת BEST", "tab-views.export-layer": "ייצוא שכבה", + "tab-views.basemaps": "מפות בסיס", "tab-views.catalog.top-categories.unpublished": "לא מפורסמים", "tab-views.catalog.top-categories.bests": "BESTs", diff --git a/src/common/navigation/react-navigation.proxy.ts b/src/common/navigation/react-navigation.proxy.ts index 080a841e..66d40ede 100644 --- a/src/common/navigation/react-navigation.proxy.ts +++ b/src/common/navigation/react-navigation.proxy.ts @@ -1,3 +1,7 @@ // Single import point for @react-navigation/*. No other file should import from those packages directly. -export { NavigationIndependentTree, NavigationContainer } from '@react-navigation/native'; +export { + DefaultTheme, + NavigationIndependentTree, + NavigationContainer, +} from '@react-navigation/native'; export { createStackNavigator } from '@react-navigation/stack'; diff --git a/src/discrete-layer/models/RootStore.ts b/src/discrete-layer/models/RootStore.ts index 01d42464..c9b555cb 100644 --- a/src/discrete-layer/models/RootStore.ts +++ b/src/discrete-layer/models/RootStore.ts @@ -14,6 +14,7 @@ import { mapMenusManagerStore } from './mapMenusManagerStore'; import { exportStore } from './exportStore'; import { servicesAvailabilityStore } from './servicesAvailabilityStore'; import { jobsStore } from './jobsStore'; +import { basemapsStore } from './basemapsStore'; type FetchAction = ( url: string, @@ -51,6 +52,9 @@ export const baseRootStore = RootStoreBase jobsStore: types.optional(jobsStore, { state: ResponseState.IDLE, }), + basemapsStore: types.optional(basemapsStore, { + state: ResponseState.IDLE, + }), }) .views((self) => ({ get fetch(): FetchAction { diff --git a/src/discrete-layer/models/basemapsStore.ts b/src/discrete-layer/models/basemapsStore.ts new file mode 100644 index 00000000..3f292aa1 --- /dev/null +++ b/src/discrete-layer/models/basemapsStore.ts @@ -0,0 +1,24 @@ +import { types, getParent } from 'mobx-state-tree'; +import { ResponseState } from '../../common/models/response-state.enum'; +import { ModelBase } from './ModelBase'; +import { IRootStore, RootStoreType } from './RootStore'; + +export const basemapsStore = ModelBase + .props({ + state: types.enumeration( + 'State', + Object.values(ResponseState) + ), + }) + .views((self) => ({ + get store(): IRootStore { + return self.__getStore() + }, + get root(): IRootStore { + return getParent(self); + }, + })) + .actions(() => { + + return {}; + }); diff --git a/src/discrete-layer/models/discreteLayersStore.ts b/src/discrete-layer/models/discreteLayersStore.ts index e51b9aca..5843f941 100644 --- a/src/discrete-layer/models/discreteLayersStore.ts +++ b/src/discrete-layer/models/discreteLayersStore.ts @@ -57,7 +57,7 @@ const INITIAL_STATE = { highlightedLayer: null, selectedLayer: null, selectedLayerOperationMode: undefined, - tabViews: [{idx: TabViews.CATALOG}, {idx: TabViews.SEARCH_RESULTS}, {idx: TabViews.EXPORT_LAYER}], + tabViews: [{idx: TabViews.CATALOG}, {idx: TabViews.SEARCH_RESULTS}, {idx: TabViews.EXPORT_LAYER}, {idx: TabViews.BASEMAPS_MANAGEMENT}], entityDescriptors: [], entityTooltipFields: new Map(), previewedLayers: [], diff --git a/src/discrete-layer/models/userStore.ts b/src/discrete-layer/models/userStore.ts index ad9c7924..d346a95e 100644 --- a/src/discrete-layer/models/userStore.ts +++ b/src/discrete-layer/models/userStore.ts @@ -19,6 +19,7 @@ export enum UserAction { SYSTEM_ACTION_FILTER = 'system_action.filter', SYSTEM_ACTION_FREETEXTSEARCH = 'system_action.freeTextSearch', SYSTEM_ACTION_SIDEBARCOLLAPSEEXPAND = 'system_action.sidebarCollapseExapnd', + SYSTEM_ACTION_BASEMAPS_MANAGEMENT = 'system_action.baseMapsManagement', ENTITY_ACTION_LAYERRASTERRECORD_CREATE = 'entity_action.LayerRasterRecord.create', ENTITY_ACTION_LAYER3DRECORD_CREATE = 'entity_action.Layer3DRecord.create', @@ -110,6 +111,7 @@ const ROLES: IRole[] = [ [UserAction.SYSTEM_ACTION_FILTER]: {enabled: false}, [UserAction.SYSTEM_ACTION_FREETEXTSEARCH]: {enabled: true}, [UserAction.SYSTEM_ACTION_SIDEBARCOLLAPSEEXPAND]: {enabled: true}, + [UserAction.SYSTEM_ACTION_BASEMAPS_MANAGEMENT]: {enabled: true}, [UserAction.ENTITY_ACTION_LAYERRASTERRECORD_CREATE]: {enabled: true}, [UserAction.ENTITY_ACTION_LAYER3DRECORD_CREATE]: {enabled: true}, [UserAction.ENTITY_ACTION_LAYERDEMRECORD_CREATE]: {enabled: false}, @@ -167,6 +169,7 @@ const ROLES: IRole[] = [ [UserAction.SYSTEM_ACTION_FILTER]: {enabled: false}, [UserAction.SYSTEM_ACTION_FREETEXTSEARCH]: {enabled: true}, [UserAction.SYSTEM_ACTION_SIDEBARCOLLAPSEEXPAND]: {enabled: true}, + [UserAction.SYSTEM_ACTION_BASEMAPS_MANAGEMENT]: {enabled: false}, [UserAction.ENTITY_ACTION_LAYERRASTERRECORD_CREATE]: {enabled: false}, [UserAction.ENTITY_ACTION_LAYER3DRECORD_CREATE]: {enabled: false}, [UserAction.ENTITY_ACTION_LAYERDEMRECORD_CREATE]: {enabled: false}, diff --git a/src/discrete-layer/navigation/base-maps-router.component.tsx b/src/discrete-layer/navigation/base-maps-router.component.tsx index 786bc36d..3e9b5670 100644 --- a/src/discrete-layer/navigation/base-maps-router.component.tsx +++ b/src/discrete-layer/navigation/base-maps-router.component.tsx @@ -1,42 +1,118 @@ import React from 'react'; +import { Button, useTheme } from '@map-colonies/react-core'; import { Box } from '@map-colonies/react-components'; import { + DefaultTheme, NavigationIndependentTree, NavigationContainer, createStackNavigator, } from '../../common/navigation/react-navigation.proxy'; -// TODO: replace `undefined` params once BaseMapsListScreen / EditBaseMapScreen / AddLayerScreen land. -type BaseMapsStackParamList = { - BaseMapsList: undefined; - EditBaseMap: undefined; - AddLayer: undefined; +interface BasemapsPanelProps { + navigation: { + navigate: (routeName: BasemapsRouteName) => void; + }; +} + +type PanelParams = BasemapsPanelProps & T; + +type BasemapsStackPanel = { + BaseMapsList: PanelParams; + EditBaseMap: PanelParams; + AddLayer: PanelParams; +}; + +type BasemapsRouteName = keyof BasemapsStackPanel; + +//#region TODO: Should be deleted when real implementation is exist +interface PlaceholderPanelProps extends BasemapsPanelProps { + currentScreen: BasemapsRouteName; + label: string; +} +const PlaceholderPanel: React.FC = ({ + currentScreen, + label, + navigation, +}) => { + const STACK_SCREENS: Array<{ name: BasemapsRouteName; label: string }> = [ + { name: 'BaseMapsList', label: 'Base maps list' }, + { name: 'EditBaseMap', label: 'Edit base map' }, + { name: 'AddLayer', label: 'Add layer' }, + ]; + + return ( + + {label} + + {STACK_SCREENS.filter(({ name }) => name !== currentScreen).map( + ({ name, label: targetLabel }) => ( + + ) + )} + + + ); }; +//#endregion + +const BaseMapsListScreen: React.FC = ({ navigation }) => ( + +); -const Stack = createStackNavigator(); +const EditBaseMapScreen: React.FC = ({ navigation }) => ( + +); + +const AddLayerScreen: React.FC = ({ navigation }) => ( + +); + +const Stack = createStackNavigator(); export const BasemapsRouter: React.FC = () => { + const theme = useTheme(); + const navigationTheme = { + ...DefaultTheme, + colors: { + ...DefaultTheme.colors, + background: theme.custom?.GC_TAB_ACTIVE_BACKGROUND, + }, + }; + return ( - - - PLACEHOLDER_BASEMAPS} - /> - PLACEHOLDER_EDIT_BASEMAP} - /> - PLACEHOLDER_ADD_LAYER} - /> - - + + + + + + + + + ); }; diff --git a/src/discrete-layer/views/components/tabs-views-switcher.component.tsx b/src/discrete-layer/views/components/tabs-views-switcher.component.tsx index 32f575f1..7b946647 100644 --- a/src/discrete-layer/views/components/tabs-views-switcher.component.tsx +++ b/src/discrete-layer/views/components/tabs-views-switcher.component.tsx @@ -1,12 +1,12 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useEffect } from 'react'; import { isEmpty } from 'lodash'; import { observer } from 'mobx-react-lite'; import { useIntl } from 'react-intl'; import { Box } from '@map-colonies/react-components'; import { IconButton, Tooltip, useTheme } from '@map-colonies/react-core'; -import { isRtl } from '../../../common/i18n/helpers'; import { useStore } from '../../models/RootStore'; -import { TabViews } from '../tab-views'; +import { ITabViewConfig, TabViews } from '../tab-views'; +import { useTabViewsConfig } from '../useTabViewsConfig.hook'; import './tabs-views-switcher.component.css'; @@ -15,13 +15,6 @@ interface TabViewsSwitcherComponentProps { activeTabView: TabViews; } -export interface ITabView { - idx: TabViews; - title: string; - iconClassName: string; - dependentValue?: unknown; -} - export const TabViewsSwitcher: React.FC = observer((props) => { const store = useStore(); const intl = useIntl(); @@ -30,46 +23,20 @@ export const TabViewsSwitcher: React.FC = observ const layerToExport = store.exportStore.layerToExport; - const tabViews: ITabView[] = useMemo( - () => [ - { - idx: TabViews.CATALOG, - title: 'tab-views.catalog', - iconClassName: 'mc-icon-Catalog', - }, - { - idx: TabViews.SEARCH_RESULTS, - title: 'tab-views.search-results', - iconClassName: 'mc-icon-Search-History', - }, - { - idx: TabViews.EXPORT_LAYER, - title: 'tab-views.export-layer', - iconClassName: isRtl(intl.locale) ? 'mc-icon-Export-Left' : 'mc-icon-Export', - dependentValue: store.exportStore.layerToExport, - }, - ], - [layerToExport] - ); - - const [availableTabs, setAvailableTabs] = useState(tabViews); + const tabViews: ITabViewConfig[] = useTabViewsConfig(intl.locale); - useEffect(() => { - const dependentTabs = tabViews.filter((tab) => { - if ('dependentValue' in tab) { - return !isEmpty(tab.dependentValue); - } - - return tab; - }); + const availableTabs = tabViews.filter((tab) => { + if ('dependentValue' in tab) { + return !isEmpty(tab.dependentValue) || tab.dependentValue === true; + } - setAvailableTabs(dependentTabs); - }, [tabViews]); + return true; + }); useEffect(() => { if (layerToExport !== undefined) { handleTabViewChange(TabViews.EXPORT_LAYER); - } else { + } else if (activeTabView === TabViews.EXPORT_LAYER) { handleTabViewChange(TabViews.CATALOG); } }, [layerToExport]); diff --git a/src/discrete-layer/views/discrete-layer-view.css b/src/discrete-layer/views/discrete-layer-view.css index e619f48c..95495934 100644 --- a/src/discrete-layer/views/discrete-layer-view.css +++ b/src/discrete-layer/views/discrete-layer-view.css @@ -138,6 +138,7 @@ body[dir='rtl'] .headerSystemAreaContainer { flex-direction: row; align-items: center; gap: 8px; + white-space: nowrap; } .tabTitle { diff --git a/src/discrete-layer/views/discrete-layer-view.tsx b/src/discrete-layer/views/discrete-layer-view.tsx index 0f036fc5..d9806515 100644 --- a/src/discrete-layer/views/discrete-layer-view.tsx +++ b/src/discrete-layer/views/discrete-layer-view.tsx @@ -50,6 +50,7 @@ import { MenuDimensions } from '../../common/hooks/mapMenus/useGetMenuDimensions import { LinkType } from '../../common/models/link-type.enum'; import { Mode } from '../../common/models/mode.enum'; import { ActiveLayersIcon } from '../../icons/4font/ActiveLayers'; +import { BasemapsRouter } from '../navigation/base-maps-router.component'; import { CatalogTreeComponent } from '../components/catalog-tree/catalog-tree'; import ExportDrawingHandler from '../components/export-layer/export-drawing-handler.component'; import { ExportLayerComponent } from '../components/export-layer/export-layer.component'; @@ -99,6 +100,7 @@ import { TabViewsSwitcher } from './components/tabs-views-switcher.component'; import UserModeSwitch from './components/user-mode-switch/user-mode-switch.component'; import { EntityDelete3DDialog } from '../components/layer-details/3D/entity.3d.delete-dialog'; import { TabViews } from './tab-views'; +import { useTabViewsConfig } from './useTabViewsConfig.hook'; import '@material/tab-bar/dist/mdc.tab-bar.css'; import '@material/tab/dist/mdc.tab.css'; @@ -136,6 +138,8 @@ const noDrawing: IDrawingObject = { const getTimeStamp = (): string => new Date().getTime().toString(); +const TABS_WITH_SIDE_DETAILS_PANEL = [TabViews.CATALOG, TabViews.SEARCH_RESULTS]; + const DiscreteLayerView: React.FC = observer(() => { const [isFilterSearchLoading, setIsFilterSearchLoading] = useState(false); const [data, setData] = useState(); @@ -654,23 +658,7 @@ const DiscreteLayerView: React.FC = observer(() => { store.discreteLayersStore.setSelectedLayerOperationMode(undefined); }, [store.discreteLayersStore.setSelectedLayerOperationMode]); - const tabViews = [ - { - idx: TabViews.CATALOG, - title: 'tab-views.catalog', - iconClassName: 'mc-icon-Catalog', - }, - { - idx: TabViews.SEARCH_RESULTS, - title: 'tab-views.search-results', - iconClassName: 'mc-icon-Search-History', - }, - { - idx: TabViews.EXPORT_LAYER, - title: 'tab-views.export-layer', - iconClassName: isRtl(intl.locale) ? 'mc-icon-Export-Left' : 'mc-icon-Export', - }, - ]; + const tabViews = useTabViewsConfig(intl.locale); const permissions = useMemo(() => { return { @@ -1177,6 +1165,8 @@ const DiscreteLayerView: React.FC = observer(() => { [intl] ); + const showSideDetailsPanel = TABS_WITH_SIDE_DETAILS_PANEL.includes(activeTabView); + return ( <> @@ -1208,7 +1198,10 @@ const DiscreteLayerView: React.FC = observer(() => { onPoiUpdate={onPoiSelection} poi={poi} corners={corners} - disabled={activeTabView === TabViews.EXPORT_LAYER} + disabled={ + activeTabView === TabViews.EXPORT_LAYER || + activeTabView === TabViews.BASEMAPS_MANAGEMENT + } /> @@ -1313,12 +1306,7 @@ const DiscreteLayerView: React.FC = observer(() => { className="sidePanelContainer" style={{ backgroundColor: theme.custom?.GC_ALTERNATIVE_SURFACE as string, - height: - activeTabView !== TabViews.EXPORT_LAYER - ? detailsPanelExpanded - ? '50%' - : '75%' - : '100%', + height: showSideDetailsPanel ? (detailsPanelExpanded ? '50%' : '75%') : '100%', display: tabsPanelExpanded ? 'block' : 'none', }} > @@ -1355,8 +1343,14 @@ const DiscreteLayerView: React.FC = observer(() => { /> )} + {activeTabView === TabViews.BASEMAPS_MANAGEMENT && ( + + {getActiveTabHeader(activeTabView, site)} + + + )} - {activeTabView !== TabViews.EXPORT_LAYER && ( + {showSideDetailsPanel && ( { {...(CONFIG.MAP.SHOW_GEOCODER_TOOL ? { geocoderPanel: GEOCODER_OPTIONS } : {})} {...(drapingLayerPredicate !== undefined ? { drapingLayerPredicate } : {})} > - {activeTabView !== TabViews.EXPORT_LAYER && ( + {showSideDetailsPanel && ( { + const store = useStore(); + const layerToExport = store.exportStore.layerToExport; + const isBasemapsManagementAllowed = store.userStore.isActionAllowed( + UserAction.SYSTEM_ACTION_BASEMAPS_MANAGEMENT + ); + const user = store.userStore.user; + + return useMemo( + () => [ + { + idx: TabViews.CATALOG, + title: 'tab-views.catalog', + iconClassName: 'mc-icon-Catalog', + }, + { + idx: TabViews.SEARCH_RESULTS, + title: 'tab-views.search-results', + iconClassName: 'mc-icon-Search-History', + }, + { + idx: TabViews.EXPORT_LAYER, + title: 'tab-views.export-layer', + iconClassName: isRtl(locale) ? 'mc-icon-Export-Left' : 'mc-icon-Export', + dependentValue: layerToExport, + }, + { + idx: TabViews.BASEMAPS_MANAGEMENT, + title: 'tab-views.basemaps', + iconClassName: 'mc-icon-Bests', + dependentValue: isBasemapsManagementAllowed, + }, + ], + [locale, layerToExport, isBasemapsManagementAllowed, user] + ); +};