layer control

This commit is contained in:
Anton 2023-09-15 12:11:59 +02:00
parent 2fec9b05d7
commit 5fbfdfec43
5 changed files with 157 additions and 7 deletions

View File

@ -6,7 +6,7 @@ import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems' import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { ItemFormPopupProps, ItemFormPopup } from './Subcomponents/ItemFormPopup' import { ItemFormPopupProps, ItemFormPopup } from './Subcomponents/ItemFormPopup'
import { useFilterTags, useSearchPhrase } from './hooks/useFilter' import { useFilterTags, useIsLayerVisible, useSearchPhrase } from './hooks/useFilter'
import { useGetItemTags } from './hooks/useTags' import { useGetItemTags } from './hooks/useTags'
import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs' import { useAddMarker, useAddPopup, useLeafletRefs } from './hooks/useLeafletRefs'
import { Popup } from 'leaflet' import { Popup } from 'leaflet'
@ -32,6 +32,8 @@ export const Layer = (props: LayerProps) => {
const map = useMap(); const map = useMap();
const isLayerVisible = useIsLayerVisible();
useEffect(() => { useEffect(() => {
@ -91,6 +93,7 @@ export const Layer = (props: LayerProps) => {
? item : ? item :
item.name.toLowerCase().includes(searchPhrase.toLowerCase()) || item.text.toLowerCase().includes(searchPhrase.toLowerCase()) item.name.toLowerCase().includes(searchPhrase.toLowerCase()) || item.text.toLowerCase().includes(searchPhrase.toLowerCase())
}). }).
filter(item => item.layer && isLayerVisible(item.layer)).
map((item: Item) => { map((item: Item) => {
const tags = getItemTags(item); const tags = getItemTags(item);

View File

@ -11,7 +11,7 @@ export default function AddButton({ setSelectNewItemPosition }: { setSelectNewIt
return ( return (
<div className="tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-5 tw-bottom-5" > <div className="tw-dropdown tw-dropdown-top tw-dropdown-end tw-dropdown-hover tw-z-500 tw-absolute tw-right-4 tw-bottom-4" >
<label tabIndex={0} className="tw-z-500 tw-btn tw-btn-circle tw-shadow tw-bg-base-100"> <label tabIndex={0} className="tw-z-500 tw-btn tw-btn-circle tw-shadow tw-bg-base-100">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="2" stroke="currentColor" className="tw-w-5 tw-h-5"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth="2" stroke="currentColor" className="tw-w-5 tw-h-5">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" /> <path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />

View File

@ -0,0 +1,51 @@
import * as React from 'react'
import { useLayers } from '../hooks/useLayers';
import { useAddVisibleLayer, useIsLayerVisible, useToggleVisibleLayer } from '../hooks/useFilter';
import { useEffect } from 'react';
export function LayerControl() {
const [open, setOpen] = React.useState(false);
const layers = useLayers();
useEffect(() => {
layers.map(layer =>
addVisibleLayer(layer)
)
}, [layers])
const isLayerVisible = useIsLayerVisible();
const toggleVisibleLayer = useToggleVisibleLayer();
const addVisibleLayer = useAddVisibleLayer();
return (
<div className="tw-card tw-bg-base-100 tw-shadow-xl tw-absolute tw-bottom-4 tw-left-4 tw-z-1000 " onClick={e=> e.stopPropagation()}>
{
open ?
<div className="tw-card-body tw-p-2 tw-w-32 tw-transition-all tw-duration-300" onClick={e=> e.stopPropagation()}>
<label className="tw-h-6 tw-w-6 tw-rounded-2xl tw-items-center tw-inline-flex tw-cursor-pointer tw- tw-absolute tw-right-0 tw-top-0 tw-bg-white tw-text-gray-600" onClick={()=>setOpen(false)}><p className='tw-text-center '></p></label>
<ul className='tw-flex-row tw-pb-1'>
{
layers.map(layer =>
<li key={layer.name}><label className="tw-label tw-justify-normal tw-pt-1 tw-pb-0"><input type="checkbox" className="tw-checkbox tw-checkbox-xs tw-checkbox-success" checked={isLayerVisible(layer)} onChange={() => toggleVisibleLayer(layer)} /><span className='tw-text-sm tw-label-text tw-mx-2'>{layer.name}</span></label></li>
)
}
</ul>
</div>
:
<div className="tw-card-body tw-p-2 tw-h-10 tw-w-10 tw-transition-all tw-duration-300 hover:tw-cursor-pointer" onClick={() => setOpen(true)}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" />
</svg>
</div>
}
</div>
)
}

View File

@ -15,6 +15,7 @@ import { FilterProvider } from "./hooks/useFilter";
import { FilterControl } from "./Subcomponents/FilterControl"; import { FilterControl } from "./Subcomponents/FilterControl";
import { PermissionsProvider } from "./hooks/usePermissions"; import { PermissionsProvider } from "./hooks/usePermissions";
import { LeafletRefsProvider } from "./hooks/useLeafletRefs"; import { LeafletRefsProvider } from "./hooks/useLeafletRefs";
import { LayerControl } from "./Subcomponents/LayerControl";
export interface MapEventListenerProps { export interface MapEventListenerProps {
@ -77,6 +78,8 @@ function UtopiaMap({
<div className={(selectNewItemPosition != null ? "crosshair-cursor-enabled" : undefined)}> <div className={(selectNewItemPosition != null ? "crosshair-cursor-enabled" : undefined)}>
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={center} zoom={zoom} zoomControl={false}> <MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={center} zoom={zoom} zoomControl={false}>
<FilterControl></FilterControl> <FilterControl></FilterControl>
<LayerControl></LayerControl>
<TileLayer <TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://tile.osmand.net/hd/{z}/{x}/{y}.png" /> url="https://tile.osmand.net/hd/{z}/{x}/{y}.png" />

View File

@ -1,30 +1,45 @@
import { useCallback, useReducer, createContext, useContext } from "react"; import { useCallback, useReducer, createContext, useContext } from "react";
import * as React from "react"; import * as React from "react";
import {Tag} from "../../../types"; import { LayerProps, Tag } from "../../../types";
import { useLayers } from "./useLayers";
type ActionType = type ActionType =
| { type: "ADD_TAG"; tag: Tag } | { type: "ADD_TAG"; tag: Tag }
| { type: "REMOVE_TAG"; id: string } | { type: "REMOVE_TAG"; id: string }
| { type: "RESET_TAGS"}; | { type: "RESET_TAGS" }
| { type: "TOGGLE_LAYER"; layer: LayerProps }
| { type: "ADD_LAYER"; layer: LayerProps }
| { type: "RESET_LAYERS" }
;
type UseFilterManagerResult = ReturnType<typeof useFilterManager>; type UseFilterManagerResult = ReturnType<typeof useFilterManager>;
const FilterContext = createContext<UseFilterManagerResult>({ const FilterContext = createContext<UseFilterManagerResult>({
filterTags: [], filterTags: [],
searchPhrase: "", searchPhrase: "",
visibleLayers: [],
addFilterTag: () => { }, addFilterTag: () => { },
removeFilterTag: () => { }, removeFilterTag: () => { },
resetFilterTags: () => { }, resetFilterTags: () => { },
setSearchPhrase: () => { }, setSearchPhrase: () => { },
addVisibleLayer: () => { },
toggleVisibleLayer: () => { },
resetVisibleLayers: () => { },
isLayerVisible: () => true
}); });
function useFilterManager(initialTags: Tag[]): { function useFilterManager(initialTags: Tag[]): {
filterTags: Tag[]; filterTags: Tag[];
searchPhrase: string; searchPhrase: string;
visibleLayers: LayerProps[];
addFilterTag: (tag: Tag) => void; addFilterTag: (tag: Tag) => void;
removeFilterTag: (id: string) => void; removeFilterTag: (id: string) => void;
resetFilterTags: () => void; resetFilterTags: () => void;
setSearchPhrase: (phrase: string) => void; setSearchPhrase: (phrase: string) => void;
addVisibleLayer: (layer: LayerProps) => void;
toggleVisibleLayer: (layer: LayerProps) => void;
resetVisibleLayers: () => void;
isLayerVisible: (layer: LayerProps) => boolean;
} { } {
const [filterTags, dispatchTags] = useReducer((state: Tag[], action: ActionType) => { const [filterTags, dispatchTags] = useReducer((state: Tag[], action: ActionType) => {
switch (action.type) { switch (action.type) {
@ -46,6 +61,31 @@ function useFilterManager(initialTags: Tag[]): {
} }
}, initialTags); }, initialTags);
const initialLayers = useLayers()
const [visibleLayers, dispatchLayers] = useReducer((state: LayerProps[], action: ActionType) => {
switch (action.type) {
case "ADD_LAYER":
const exist1 = state.find((layer) =>
layer.name === action.layer.name ? true : false
);
if (!exist1) return [
...state,
action.layer,
];
else return state;
case "TOGGLE_LAYER":
const exist2 = state.some((layer) =>
layer.name === action.layer.name);
if(exist2) return state.filter(({name}) => name != action.layer.name);
else return [... state, action.layer];
case "RESET_LAYERS":
return initialLayers;
default:
throw new Error();
}
}, initialLayers);
const [searchPhrase, searchPhraseSet] = React.useState<string>(""); const [searchPhrase, searchPhraseSet] = React.useState<string>("");
const addFilterTag = (tag: Tag) => { const addFilterTag = (tag: Tag) => {
@ -69,11 +109,38 @@ function useFilterManager(initialTags: Tag[]): {
}); });
}, []); }, []);
const addVisibleLayer = (layer: LayerProps) => {
dispatchLayers({
type: "ADD_LAYER",
layer,
});
};
const toggleVisibleLayer = (layer: LayerProps) => {
dispatchLayers({
type: "TOGGLE_LAYER",
layer,
});
};
const resetVisibleLayers = useCallback(() => {
dispatchLayers({
type: "RESET_LAYERS",
});
}, []);
const isLayerVisible = useCallback((layer: LayerProps) => {
return visibleLayers.some(l => l.name === layer.name)
}, [visibleLayers]);
const setSearchPhrase = useCallback((phrase: string) => { const setSearchPhrase = useCallback((phrase: string) => {
searchPhraseSet(phrase) searchPhraseSet(phrase)
}, []); }, []);
return { filterTags, addFilterTag, removeFilterTag, resetFilterTags, setSearchPhrase, searchPhrase }; return { filterTags, addFilterTag, removeFilterTag, resetFilterTags, setSearchPhrase, searchPhrase, visibleLayers, toggleVisibleLayer, resetVisibleLayers, isLayerVisible, addVisibleLayer };
} }
export const FilterProvider: React.FunctionComponent<{ export const FilterProvider: React.FunctionComponent<{
@ -113,3 +180,29 @@ export const useSetSearchPhrase = (): UseFilterManagerResult["setSearchPhrase"]
const { setSearchPhrase } = useContext(FilterContext); const { setSearchPhrase } = useContext(FilterContext);
return setSearchPhrase; return setSearchPhrase;
}; };
export const useVisibleLayer = (): UseFilterManagerResult["visibleLayers"] => {
const { visibleLayers } = useContext(FilterContext);
return visibleLayers;
};
export const useAddVisibleLayer = (): UseFilterManagerResult["addVisibleLayer"] => {
const { addVisibleLayer } = useContext(FilterContext);
return addVisibleLayer;
};
export const useToggleVisibleLayer = (): UseFilterManagerResult["toggleVisibleLayer"] => {
const { toggleVisibleLayer } = useContext(FilterContext);
return toggleVisibleLayer;
};
export const useResetVisibleLayers = (): UseFilterManagerResult["resetVisibleLayers"] => {
const { resetVisibleLayers } = useContext(FilterContext);
return resetVisibleLayers;
};
export const useIsLayerVisible = (): UseFilterManagerResult["isLayerVisible"] => {
const { isLayerVisible } = useContext(FilterContext);
return isLayerVisible;
};