diff --git a/package-lock.json b/package-lock.json index 870036ef8b0e49d20371c5eeff7701b746ae6160..d2146a9d1e33426db12ead0084cc306d7c819024 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,13 @@ "react-router-dom": "^5.2.0", "react-select": "^5.5.0", "tailwindcss": "^3.1.8", + "usehooks-ts": "^2.9.1", "zustand": "^4.1.2" }, "devDependencies": { + "@carbon/icons-react": "^11.9.0", + "@fchh/fcos-suite-ui": "^0.1.13", + "@types/carbon__icons-react": "^11.15.0", "@types/mapbox-gl": "^2.7.6", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", @@ -481,6 +485,36 @@ "node": ">=6.9.0" } }, + "node_modules/@carbon/icon-helpers": { + "version": "10.39.0", + "resolved": "https://registry.npmjs.org/@carbon/icon-helpers/-/icon-helpers-10.39.0.tgz", + "integrity": "sha512-iFWIfjKABjusb+gUz6s0FdEBHe8Ms63CKDxozhtiSZ9LfF9X5QQztO8df3szqcNsmw30pYhSnm+zJwifO9tdRw==", + "dev": true + }, + "node_modules/@carbon/icons-react": { + "version": "11.17.0", + "resolved": "https://registry.npmjs.org/@carbon/icons-react/-/icons-react-11.17.0.tgz", + "integrity": "sha512-PheX1aGh12mfybHeLB9w8Y8UzARuqpVanJ3zTDCUQKfPFaOezAt+6E9lqjj5xq3QgpyZcrCEOFiaPV90n3MqGQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@carbon/icon-helpers": "^10.39.0", + "@carbon/telemetry": "0.1.0", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@carbon/telemetry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@carbon/telemetry/-/telemetry-0.1.0.tgz", + "integrity": "sha512-kNWt0bkgPwGW0i5h7HFuljbKRXPvIhsKbB+1tEURAYLXoJg9iJLF1eGvWN5iVoFCS2zje4GR3OGOsvvKVe7Hlg==", + "dev": true, + "bin": { + "carbon-telemetry": "bin/carbon-telemetry.js" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.10.6", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.6.tgz", @@ -674,6 +708,12 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@fchh/fcos-suite-ui": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@fchh/fcos-suite-ui/-/fcos-suite-ui-0.1.13.tgz", + "integrity": "sha512-Ln1+6kKcDBKRga4kU2NNfNuXtjC1adgUc89WJli3RIzOUZv9pOdzG52qogG2iFg2Q/ea36VCIsccIIxn9bBLAw==", + "dev": true + }, "node_modules/@floating-ui/core": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.2.tgz", @@ -866,6 +906,12 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "node_modules/@types/carbon__icons-react": { + "version": "11.16.0", + "resolved": "https://registry.npmjs.org/@types/carbon__icons-react/-/carbon__icons-react-11.16.0.tgz", + "integrity": "sha512-vmNWdtv2D/Lve2giSZ6XYVSJLxoB5pmGAGWI4DdDgvpr4RJmu72Kf3Wy+LbyaJBOPuYlfQWa/X1LWmdqjAyXOw==", + "dev": true + }, "node_modules/@types/geojson": { "version": "7946.0.10", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", @@ -4929,6 +4975,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.9.1.tgz", + "integrity": "sha512-2FAuSIGHlY+apM9FVlj8/oNhd+1y+Uwv5QNkMQz1oSfdHk4PXo1qoCw9I5M7j0vpH8CSWFJwXbVPeYDjLCx9PA==", + "engines": { + "node": ">=16.15.0", + "npm": ">=8" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index e16ffb361246ace25df5b74f6730382986b0b45f..04685f038ab76d50aabad83e4df348538d3d6d26 100644 --- a/package.json +++ b/package.json @@ -37,9 +37,13 @@ "react-router-dom": "^5.2.0", "react-select": "^5.5.0", "tailwindcss": "^3.1.8", + "usehooks-ts": "^2.9.1", "zustand": "^4.1.2" }, "devDependencies": { + "@carbon/icons-react": "^11.9.0", + "@fchh/fcos-suite-ui": "^0.1.13", + "@types/carbon__icons-react": "^11.15.0", "@types/mapbox-gl": "^2.7.6", "@types/react": "^18.0.21", "@types/react-dom": "^18.0.6", diff --git a/src/components/FabCityMap.tsx b/src/components/FabCityMap.tsx index 89475afd6d9fab689d3295c067ff09c6d0f81dfe..f0637d737152a331ebd44c579c7b2baa9c52bc1a 100644 --- a/src/components/FabCityMap.tsx +++ b/src/components/FabCityMap.tsx @@ -4,12 +4,12 @@ import { useStore } from '../hooks'; import ErrorModal from './ErrorModal'; import Notification from './Notification'; import Map from './Map/Map'; -import SidebarListView from './Sidebar/SidebarListView'; -import SidebarSingleView from './Sidebar/SidebarSingleView'; import PoiLoader from './PoiLoader'; import { BrowserRouter as Router } from 'react-router-dom'; import { MapProvider } from 'react-map-gl'; import '../index.css'; +import MapLayerControl from './Map/MapLayerControl'; +import Sidebar from "./Sidebar/Sidebar" interface Props { data: any; @@ -20,7 +20,6 @@ interface Props { } const FabCityMap: React.FC<Props> = ({ data, mapboxToken, className, baseUrl, mapStyle }) => { - const selectedPoi = useStore((state) => state.selectedPoi); const setPoiData = useStore((state) => state.setPoiData); useEffect(() => { @@ -32,7 +31,7 @@ const FabCityMap: React.FC<Props> = ({ data, mapboxToken, className, baseUrl, ma <Router {...(baseUrl ? { basename: baseUrl } : {})}> <ErrorModal /> <Notification /> - <div className={`fcmap-relative fcmap-h-full fcmap-bg-white fcmap-overflow-hidden ${className || ''}`}> + <div className={`fcmap-h-full fcmap-bg-white fcmap-overflow-hidden ${className || ''}`}> <Route path="/"> {/* This route will always match, so the Map is always visible */} <Map mapboxToken={mapboxToken} mapStyle={mapStyle} /> @@ -44,9 +43,18 @@ const FabCityMap: React.FC<Props> = ({ data, mapboxToken, className, baseUrl, ma <Route exact path="/"> <PoiLoader poiId={null} /> </Route> - <Switch> - <Route>{selectedPoi ? <SidebarSingleView /> : <SidebarListView />}</Route> - </Switch> + <div className="fcmap-absolute fcmap-w-full fcmap-h-full fcmap-top-0 fcmap-left-0 fcmap-flex fcmap-flex-col md:fcmap-flex-row fcmap-justify-end fcmap-px-5 fcmap-pt-7 fcmap-gap-4 fcmap-pointer-events-none"> + <Route exact path="/"> + <div className="fcmap-flex fcmap-justify-end fcmap-items-start md:fcmap-flex-1 fcmap-pointer-events-none"> + <MapLayerControl /> + </div> + </Route> + <Switch> + <Route> + <Sidebar /> + </Route> + </Switch> + </div> </div> </Router> </MapProvider> diff --git a/src/components/Map/Map.tsx b/src/components/Map/Map.tsx index 313c61a6553821a6a362dcb4e03dbca29f65fa66..2ec7f729dc92fd676ad3c866b8158a5191ee469c 100644 --- a/src/components/Map/Map.tsx +++ b/src/components/Map/Map.tsx @@ -3,9 +3,9 @@ import { useEffect, useState } from 'react'; import { useStore, useFilteredPoiData } from '../../hooks'; import ReactMapGl, { Marker, useMap, AttributionControl } from 'react-map-gl'; import { useHistory } from 'react-router-dom'; -import MapLayerControl from './MapLayerControl'; import { calcBoundsFromCoordinates } from '../../util/geo'; import 'mapbox-gl/dist/mapbox-gl.css'; +import { useWindowSize } from 'usehooks-ts'; interface Props { mapboxToken: string; @@ -35,12 +35,27 @@ const Map: React.FC<Props> = ({ mapboxToken, mapStyle }) => { const hoveredPoi = useStore((state) => state.hoveredPoi); const setHoveredPoi = useStore((state) => state.setHoveredPoi); const isSidebarHidden = useStore((state) => state.isSidebarHidden); + const isDesktop = useStore((state) => state.isDesktop); + const setIsDesktop = useStore((state) => state.setIsDesktop); const { data: filteredData } = useFilteredPoiData(); const DEFAULT_CENTER: [number, number] = [9.986701, 53.550359]; const { fcmap } = useMap(); const [bounds, setBounds] = useState<[[number, number], [number, number]]>(); const DEFAULT_MAP_PADDING = 50; - const OVERLAY_WIDTH_PADDING = 300; + const OVERLAY_WIDTH_PADDING = 330; + const window = useWindowSize(); + + const calculateMapPadding = (sidebarHidden: boolean, desktop: boolean) => { + if (desktop && !sidebarHidden) { + return { + right: DEFAULT_MAP_PADDING + OVERLAY_WIDTH_PADDING, + left: DEFAULT_MAP_PADDING, + top: DEFAULT_MAP_PADDING, + bottom: DEFAULT_MAP_PADDING, + }; + } + return DEFAULT_MAP_PADDING; + }; useEffect(() => { const newBounds = calcBoundsFromCoordinates(data?.map((poi) => [poi.lng, poi.lat]) || [DEFAULT_CENTER]); @@ -50,29 +65,33 @@ const Map: React.FC<Props> = ({ mapboxToken, mapStyle }) => { useEffect(() => { if (fcmap) { if (selectedPoi) { - fcmap?.easeTo({ - center: [selectedPoi.lng, selectedPoi.lat], + const options = { + center: [selectedPoi.lng, selectedPoi.lat] as [number, number], zoom: 14, duration: 1500, - ...(!isSidebarHidden ? { padding: { right: OVERLAY_WIDTH_PADDING, left: 0, top: 0, bottom: 0 } } : {}), - }); + padding: calculateMapPadding(isSidebarHidden, isDesktop), + }; + fcmap?.easeTo(options); } else if (!selectedPoi && bounds) { - fcmap?.fitBounds(bounds, { - ...(!isSidebarHidden - ? { - padding: { - right: DEFAULT_MAP_PADDING + OVERLAY_WIDTH_PADDING, - left: DEFAULT_MAP_PADDING, - top: DEFAULT_MAP_PADDING, - bottom: DEFAULT_MAP_PADDING, - }, - } - : {}), - maxZoom: 16, - }); + const options = { + padding: calculateMapPadding(isSidebarHidden, isDesktop), + }; + fcmap?.fitBounds(bounds, options); } } - }, [selectedPoi, bounds, fcmap, isSidebarHidden]); + }, [selectedPoi, bounds, fcmap, isSidebarHidden, isDesktop]); + + useEffect(() => { + let newValue; + if ((window?.width ?? 0) > 768) { + newValue = true; + } else { + newValue = false; + } + if (newValue !== isDesktop) { + setIsDesktop(newValue); + } + }, [window]); return ( <ReactMapGl @@ -89,7 +108,6 @@ const Map: React.FC<Props> = ({ mapboxToken, mapStyle }) => { attributionControl={false} > <AttributionControl position="top-left" /> - <MapLayerControl /> {selectedPoi ? ( <Marker key={selectedPoi.id} longitude={selectedPoi.lng} latitude={selectedPoi.lat} anchor="bottom"> <MarkerSvg className="fcmap-w-8 fcmap-h-8 fcmap-opacity-100 fcmap-scale-125 fcmap-text-fabcity-red" /> diff --git a/src/components/Map/MapLayerControl.tsx b/src/components/Map/MapLayerControl.tsx index 524c4fb8549566a48f7f36339548036e85957579..67e6d79ef49a6c345411d7651313fe0938820815 100644 --- a/src/components/Map/MapLayerControl.tsx +++ b/src/components/Map/MapLayerControl.tsx @@ -1,50 +1,39 @@ -import { useState } from 'react'; import { useFilteredPoiData } from '../../hooks/useFilteredPoiData'; -import { ChevronDownOutline as DownIcon } from 'heroicons-react'; import { useStore } from '../../hooks'; +import { Pill } from '@fchh/fcos-suite-ui'; const MapLayerControl: React.FC = () => { const data = useStore((state) => state.poiData); const { filterCategories, setFilterCategories } = useFilteredPoiData(); const layers = Array.from(new Set(data?.map((poi) => poi.category))); - const [isOpen, setIsOpen] = useState(false); - const onChangeCheckbox = (state: boolean, layer: string) => { - if (state) { - setFilterCategories([...filterCategories, layer]); + const onChangePills = (layer: string) => { + const isFilterActive = filterCategories.includes(layer); + if (isFilterActive) { + const updatedFilters = filterCategories.filter((filter) => filter !== layer); + setFilterCategories(updatedFilters); } else { - setFilterCategories(filterCategories.filter((cat) => cat !== layer)); + setFilterCategories([...filterCategories, layer]); } }; return ( - <div className="fcmap-shadow-lg fcmap-w-52 fcmap-absolute fcmap-left-5 fcmap-bottom-0 fcmap-mb-5 fcmap-mr-4 fcmap-bg-white fcmap-p-4 fcmap-z-10"> - <div - className={`fcmap-flex fcmap-justify-between fcmap-cursor-pointer ${isOpen ? 'fcmap-mb-2' : ''}`} - onClick={() => setIsOpen(!isOpen)} - > - <h3 className="fcmap-text-base fcmap-font-semibold fcmap-text-gray-900">Kategorien:</h3> - <DownIcon - className={`fcmap-w-6 fcmap-h-6 fcmap-text-gray-400 fcmap-transform fcmap-transition fcmap-duration-300 ${ - isOpen ? 'fcmap-rotate-180' : 'fcmap-rotate-0' - }`} - /> - </div> - {isOpen && - layers.map((layer) => { - return ( - <div key={layer} className="fcmap-my-2"> - <label className="fcmap-inline-flex fcmap-items-center"> - <input - type="checkbox" - checked={!!filterCategories?.find((cat) => cat === layer)} - onChange={(e) => onChangeCheckbox(e.target.checked, layer)} - /> - <span className="fcmap-ml-2 fcmap-text-black fcmap-text-sm">{layer}</span> - </label> - </div> - ); - })} + <div className="fcmap-flex fcmap-items-start fcmap-justify-center md:fcmap-justify-end fcmap-flex-wrap fcmap-gap-[9px] fcmap-pointer-events-auto"> + {layers.map((layer) => { + return ( + <Pill + key={layer} + type={filterCategories.includes(layer) ? 'info' : 'white'} + size="sm" + rounded + title={layer} + className="fcmap-shadow-lg fcmap-text-sm fcmap-font-plex fcmap-font-medium fcmap-text-gray-800" + onClick={() => { + onChangePills(layer); + }} + /> + ); + })} </div> ); }; diff --git a/src/components/Sidebar/CloseButton.tsx b/src/components/Sidebar/CloseButton.tsx index 4a2f46b765944403b668690560b8eeaba864fbbf..78e02171f7292c058d7da4880bba323deb1415cc 100644 --- a/src/components/Sidebar/CloseButton.tsx +++ b/src/components/Sidebar/CloseButton.tsx @@ -6,11 +6,9 @@ interface Props { const CloseButton: React.FC<Props> = ({ onClick }) => { return ( - <CloseIcon - size={28} - className="fcmap-p-1 fcmap-text-gray-400 fcmap-inline-block fcmap-cursor-pointer" - onClick={onClick} - /> + <div className="fcmap-p-1 fcmap-flex fcmap-items-center fcmap-justify-center"> + <CloseIcon size={24} className="fcmap-text-gray-400 fcmap-inline-block fcmap-cursor-pointer" onClick={onClick} /> + </div> ); }; diff --git a/src/components/Sidebar/MinimizeButton.tsx b/src/components/Sidebar/MinimizeButton.tsx index 8aacffa9d7056f7c2eed8ed729db7a4ad2a75b1a..e1265531f6c0bd01e1a8b731a804b1e770525e86 100644 --- a/src/components/Sidebar/MinimizeButton.tsx +++ b/src/components/Sidebar/MinimizeButton.tsx @@ -1,14 +1,15 @@ import type { SyntheticEvent } from 'react'; import { ChevronDown } from 'heroicons-react'; interface Props { - onClick: (event: SyntheticEvent) => void; + onClick?: (event: SyntheticEvent) => void; isMinimized?: boolean; + className?: string; } -const MinimizeButton: React.FC<Props> = ({ onClick, isMinimized = false }) => { +const MinimizeButton: React.FC<Props> = ({ onClick, isMinimized = false, className }) => { return ( - <button onClick={onClick}> - <ChevronDown className={`${isMinimized ? 'fcmap-rotate-180' : ''} fcmap-text-gray-400 fcmap-w-6 fcmap-h-6`} /> + <button type="button" onClick={onClick} className={className}> + <ChevronDown size={24} className={`${isMinimized ? 'fcmap-rotate-180' : ''} fcmap-text-gray-400`} /> </button> ); }; diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ae4efcf52be5ad975254586f6947d901fec09929 --- /dev/null +++ b/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,37 @@ +import { useStore } from '../../hooks'; +import SidebarListView from './SidebarListView'; +import SidebarSingleView from './SidebarSingleView'; +import SidebarContainer from './SidebarContainer'; + +interface Props { + className?: string; +} + +const Sidebar: React.FC<Props> = ({ className }) => { + const selectedPoi = useStore((state) => state.selectedPoi); + const isSidebarHidden = useStore((state) => state.isSidebarHidden); + + const getPositionClasses = (isSidebarHidden: boolean, isSingleView: boolean) => { + if (isSidebarHidden && isSingleView) { + return '-fcmap-bottom-[calc(100%-70px)]'; + } else if (isSidebarHidden && !isSingleView) { + return '-fcmap-bottom-[calc(100%-150px)] md:fcmap-top-[calc(100vh-108px)]'; + } else if (!isSidebarHidden) { + return 'fcmap-bottom-0 md:fcmap-top-0'; + } + }; + + return ( + <SidebarContainer + className={`fcmap-absolute fcmap-overflow-hidden fcmap-transition-[bottom] md:fcmap-transition-[top] fcmap-duration-700 fcmap-ease-in-out ${getPositionClasses( + isSidebarHidden, + Boolean(selectedPoi), + )} ${className || ''}`} + clickable={!isSidebarHidden} + > + {selectedPoi ? <SidebarSingleView /> : <SidebarListView />} + </SidebarContainer> + ); +}; + +export default Sidebar; diff --git a/src/components/Sidebar/SidebarContainer.tsx b/src/components/Sidebar/SidebarContainer.tsx index c2c798908ab41ed4ca118f57c200ef9bb4b543ba..21262a1b2534e911b01bb133dad7edac18578a8f 100644 --- a/src/components/Sidebar/SidebarContainer.tsx +++ b/src/components/Sidebar/SidebarContainer.tsx @@ -1,16 +1,21 @@ interface Props { className?: string; children: React.ReactNode; + clickable?: boolean; } -const SidebarContainer: React.FC<Props> = ({ className, children }) => { +const SidebarContainer: React.FC<Props> = ({ className, children, clickable = true }) => { return ( <aside - className={`fcmap-absolute fcmap-right-0 fcmap-m-5 sidebar-height fcmap-min-h-min fcmap-w-[336px] fcmap-shadow-lg fcmap-bg-white fcmap-box-border fcmap-flex fcmap-flex-col ${ - className ?? '' - }`} + className={`fcmap-relative fcmap-w-full md:fcmap-w-[336px] fcmap-h-full md:fcmap-h-auto fcmap-overflow-y-hidden fcmap-z-10 fcmap-shadow-lg ${ + clickable ? 'fcmap-pointer-events-auto' : 'fcmap-pointer-events-none' + } ${className ?? ''}`} > - {children} + <div + className={`fcmap-w-full fcmap-max-h-full md:sidebar-height fcmap-min-h-min fcmap-bg-white fcmap-box-border fcmap-flex fcmap-flex-col`} + > + {children} + </div> </aside> ); }; diff --git a/src/components/Sidebar/SidebarListView.tsx b/src/components/Sidebar/SidebarListView.tsx index d4ef6c96d3d831eddc3082d44220f052eca7810d..86179e9921cf753cde9993bf100dd055b95a6096 100644 --- a/src/components/Sidebar/SidebarListView.tsx +++ b/src/components/Sidebar/SidebarListView.tsx @@ -1,7 +1,6 @@ import { useState } from 'react'; import { useStore } from '../../hooks'; import ListElement from './ListElement'; -import SidebarContainer from './SidebarContainer'; import { removeDuplicateObjects } from '../../util/array'; import { useFilteredPoiData } from '../../hooks/useFilteredPoiData'; import type { Tag } from '../../types/PointOfInterest'; @@ -31,12 +30,8 @@ const SidebarListView: React.FC = () => { const options = tagsToSelectOptions(tags); return ( - <SidebarContainer - className={`fcmap-overflow-hidden fcmap-transition-[top] fcmap-duration-700 fcmap-ease-in-out ${ - isSidebarHidden ? 'fcmap-top-[calc(100%-98px)]' : 'fcmap-top-0' - }`} - > - <div className="fcmap-flex fcmap-flex-col fcmap-m-4 fcmap-mb-2 fcmap-pb-2 fcmap-gap-2"> + <> + <div className="fcmap-flex fcmap-flex-col fcmap-m-4 fcmap-mb-2 fcmap-pb-2 fcmap-gap-2 fcmap-pointer-events-auto"> <div className="fcmap-flex fcmap-justify-end"> <MinimizeButton isMinimized={isSidebarHidden} @@ -79,7 +74,7 @@ const SidebarListView: React.FC = () => { /> ))} </div> - </SidebarContainer> + </> ); }; diff --git a/src/components/Sidebar/SidebarSingleView.tsx b/src/components/Sidebar/SidebarSingleView.tsx index e6f2050de988262daf920ed45dbbaf99822a5906..703856992137896036ec5667c4019990bf412124 100644 --- a/src/components/Sidebar/SidebarSingleView.tsx +++ b/src/components/Sidebar/SidebarSingleView.tsx @@ -1,28 +1,34 @@ -import { - HomeOutline as HomeIcon, - LocationMarkerOutline as AddressIcon, - UserGroupOutline as RealtionStatusIcon, -} from 'heroicons-react'; import { useStore } from '../../hooks'; -import SidebarContainer from './SidebarContainer'; import Tag from '../Tag'; import CloseButton from './CloseButton'; +import MinimizeButton from './MinimizeButton'; import { useHistory } from 'react-router-dom'; const SidebarSingleView: React.FC = () => { const selectedPoi = useStore((state) => state.selectedPoi); const strippedUrl = selectedPoi?.website?.replace(/(^\w+:|^)\/\//, ''); const history = useHistory(); + const isSidebarHidden = useStore((state) => state.isSidebarHidden); + const setIsSidebarHidden = useStore((state) => state.setIsSidebarHidden); return ( - <SidebarContainer className={`fcmap-top-0 fcmap-p-0 fcmap-overflow-y-auto md:fcmap-mt-9 md:fcmap-mb-4`}> + <> <div className="fcmap-flex fcmap-justify-between fcmap-items-center fcmap-px-4 fcmap-py-6"> <h1 className="fcmap-text-lg fcmap-font-medium fcmap-font-plex fcmap-text-gray-900">Profil</h1> - <CloseButton - onClick={() => { - history.push('/'); - }} - /> + <div className="fcmap-flex fcmap-pointer-events-auto"> + <MinimizeButton + className="fcmap-p-1" + isMinimized={isSidebarHidden} + onClick={() => { + setIsSidebarHidden(!isSidebarHidden); + }} + /> + <CloseButton + onClick={() => { + history.push('/'); + }} + /> + </div> </div> <div className={`${selectedPoi?.image ? '' : 'fcmap-pl-5 fcmap-pt-5 '}`}></div> {selectedPoi?.image && ( @@ -38,7 +44,7 @@ const SidebarSingleView: React.FC = () => { </h1> <h2 className="fcmap-text-sm fcmap-font-plex fcmap-font-normal fcmap-text-gray-500">{selectedPoi?.category}</h2> </div> - <div className="fcmap-px-7 fcmap-pb-14 fcmap-font-plex"> + <div className="fcmap-px-7 fcmap-pb-14 fcmap-font-plex fcmap-overflow-y-auto"> <h3 className="fcmap-text-sm fcmap-font-medium fcmap-text-gray-500 fcmap-mb-1">Info</h3> <p className="fcmap-leading-relaxed fcmap-mb-8">{selectedPoi?.description}</p> {selectedPoi?.address && ( @@ -70,7 +76,7 @@ const SidebarSingleView: React.FC = () => { </div> )} </div> - </SidebarContainer> + </> ); }; diff --git a/src/hooks/useStore.ts b/src/hooks/useStore.ts index 39516b27c81520c685fe49c983502cc53a98ee7f..07d89c6f6b590d6c021def7c92588252db460c83 100644 --- a/src/hooks/useStore.ts +++ b/src/hooks/useStore.ts @@ -1,7 +1,7 @@ import type { Error } from 'src/types/Error'; import type { PointOfInterest, Tag } from 'src/types/PointOfInterest'; import type { Notification } from 'src/types/Notification'; -import create from 'zustand'; +import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; interface Store { @@ -21,6 +21,8 @@ interface Store { setFilterCategories: (categories: string[]) => void; isSidebarHidden: boolean; setIsSidebarHidden: (value: boolean) => void; + isDesktop: boolean; + setIsDesktop: (value: boolean) => void; } export const useStore = create<Store>()( @@ -60,8 +62,12 @@ export const useStore = create<Store>()( set({ filterCategories: categories }); }, isSidebarHidden: false, - setIsSidebarHidden: (value) => { - set({ isSidebarHidden: value }); + setIsSidebarHidden: (isSidebarHidden) => { + set({ isSidebarHidden }); }, + isDesktop: false, + setIsDesktop: (isDesktop) => { + set({ isDesktop }) + } })), ); diff --git a/src/index.css b/src/index.css index 253327f8ff40f51ec47122be03d4ea956a2cec7f..53823678f1184633db9f9b1a146e3d765001ba9f 100644 --- a/src/index.css +++ b/src/index.css @@ -60,10 +60,3 @@ code { -webkit-print-color-adjust: exact; print-color-adjust: exact; } - -.mapboxgl-ctrl-logo { - position: absolute; - left: 50vw; - bottom: 0; - transform: translateX(-50%); -} diff --git a/src/index.tsx b/src/index.tsx index a356d1e74541e54277b6d447b7b2588e8897322b..ad51816745384597b6559409ff06e1f47cd1b645 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { createRoot } from 'react-dom/client'; import FabCityMap from './components/FabCityMap'; +import '@fchh/fcos-suite-ui/dist/style.css'; const exampleData = [ { @@ -162,7 +163,7 @@ const exampleData = [ address: 'Holstenhofweg 85, 22043 Hamburg', lat: 53.567516952488, lng: 10.113037061205, - image: 'https://picsum.photos/id/86/800/600', + image: 'https://picsum.photos/id/96/800/600', category: 'Fab Lab / Offene Werkstatt', relationStatus: 'Vereinsmitglied Fab City Hamburg e.V.', tags: [