diff --git a/src/Components/AppShell/NavBar.tsx b/src/Components/AppShell/NavBar.tsx index d2b270c1..02692ecb 100644 --- a/src/Components/AppShell/NavBar.tsx +++ b/src/Components/AppShell/NavBar.tsx @@ -133,7 +133,7 @@ export default function NavBar({ appName, useAuth }: { appName: string, useAuth: diff --git a/src/Components/Map/Layer.tsx b/src/Components/Map/Layer.tsx index fb52ed7c..8ac0514b 100644 --- a/src/Components/Map/Layer.tsx +++ b/src/Components/Map/Layer.tsx @@ -6,7 +6,7 @@ import { ItemViewPopup } from './Subcomponents/ItemViewPopup' import { useItems, useResetItems, useSetItemsApi, useSetItemsData } from './hooks/useItems' import { useEffect, useState } from 'react' import { ItemFormPopupProps, ItemFormPopup } from './Subcomponents/ItemFormPopup' -import { useAddFilterTag, useFilterTags } from './hooks/useFilter' +import { useAddFilterTag, useFilterTags, useSearchPhrase } from './hooks/useFilter' export const Layer = (props: LayerProps) => { @@ -15,9 +15,9 @@ export const Layer = (props: LayerProps) => { const filterTags = useFilterTags(); const setFilterTag = useAddFilterTag(); - - // setFilterTag({id: "healing", color: "#000"}) + + // setFilterTag({id: "healing", color: "#000"}) const items = useItems(); @@ -26,6 +26,8 @@ export const Layer = (props: LayerProps) => { const resetItems = useResetItems(); + const searchPhrase = useSearchPhrase(); + useEffect(() => { resetItems(props); @@ -36,36 +38,45 @@ export const Layer = (props: LayerProps) => { return ( <> {items && - items.filter(item => item.layer?.name === props.name)?.filter(item => - filterTags.length == 0 ? item : item.tags?.some(tag => filterTags.some(filterTag => filterTag.id === tag.id)))?.map((place: Item) => { - const tags = place.tags; - - let color1 = "#666"; - let color2 = "RGBA(35, 31, 32, 0.2)"; - if (tags && tags[0]) { - color1 = tags[0].color; - } - if (tags && tags[1]) { - color2 = tags[1].color; - } - return ( - - { - (props.children && React.Children.toArray(props.children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ? - React.Children.toArray(props.children).map((child) => - React.isValidElement(child) && child.props.__TYPE === "ItemView" ? - {child} - : "" - ) - : - <> - - ) - } + items. + filter(item => item.layer?.name === props.name)?. + filter(item => + // filterTags.length == 0 ? item : item.tags?.some(tag => filterTags.some(filterTag => filterTag.id === tag.id)))?. + filterTags.length == 0 ? item : filterTags.every(tag => item.tags?.some(filterTag => filterTag.id === tag.id)))?. + filter(item => { + return searchPhrase === '' + ? item : + item.name.toLowerCase().includes(searchPhrase.toLowerCase()) + }). + map((place: Item) => { + const tags = place.tags; - - ); - }) + let color1 = "#666"; + let color2 = "RGBA(35, 31, 32, 0.2)"; + if (tags && tags[0]) { + color1 = tags[0].color; + } + if (tags && tags[1]) { + color2 = tags[1].color; + } + return ( + + { + (props.children && React.Children.toArray(props.children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ? + React.Children.toArray(props.children).map((child) => + React.isValidElement(child) && child.props.__TYPE === "ItemView" ? + {child} + : "" + ) + : + <> + + ) + } + + + ); + }) } {//{props.children}} } diff --git a/src/Components/Map/Subcomponents/AddButton.tsx b/src/Components/Map/Subcomponents/AddButton.tsx index 2599abdb..c9b48411 100644 --- a/src/Components/Map/Subcomponents/AddButton.tsx +++ b/src/Components/Map/Subcomponents/AddButton.tsx @@ -9,7 +9,7 @@ export default function AddButton({setSelectMode} : {setSelectMode: React.Dispat return (
-
+ ) +} diff --git a/src/Components/Map/Subcomponents/ItemFormPopup.tsx b/src/Components/Map/Subcomponents/ItemFormPopup.tsx index 120a5091..67618dd9 100644 --- a/src/Components/Map/Subcomponents/ItemFormPopup.tsx +++ b/src/Components/Map/Subcomponents/ItemFormPopup.tsx @@ -71,7 +71,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) { }, [props.position]) return ( - { setTimeout(function () { diff --git a/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx b/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx index fe4f7aef..ac95e4bd 100644 --- a/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx +++ b/src/Components/Map/Subcomponents/ItemPopupComponents/TextView.tsx @@ -1,25 +1,44 @@ import * as React from 'react' import { Item } from '../../../../types' -import { useTags } from '../../hooks/useTags'; +import { useAddTag, useTags } from '../../hooks/useTags'; import reactStringReplace from 'react-string-replace'; import { useAddFilterTag, useResetFilterTags } from '../../hooks/useFilter'; import { hashTagRegex } from '../../../../Utils/HashTagRegex'; import { fixUrls, mailRegex, urlRegex } from '../../../../Utils/ReplaceURLs'; +import { useMap } from 'react-leaflet'; +import { randomColor } from '../../../../Utils/RandomColor'; +import { useEffect, useRef } from 'react'; export const TextView = ({ item }: { item?: Item }) => { const tags = useTags(); + const addTag = useAddTag(); const addFilterTag = useAddFilterTag(); const resetFilterTags = useResetFilterTags(); - + const map = useMap(); let replacedText; + + // use init-Ref to prevent react18 from calling useEffect twice + const init = useRef(false) + useEffect(() => { + if (!init.current) { + item?.text.toLocaleLowerCase().match(hashTagRegex)?.map(tag=> { + if (!tags.find((t) => t.id === tag.slice(1))) { + console.log(tag); + addTag({id: tag.slice(1), color: randomColor()}) + } + }); + init.current = true; + } + }, []) + + + if (item && item.text) replacedText = fixUrls(item.text); - - replacedText = reactStringReplace(replacedText, /(https?:\/\/\S+)/g, (url, i) => { let shortUrl = url; if (url.match('^https:\/\/')) { @@ -39,19 +58,15 @@ export const TextView = ({ item }: { item?: Item }) => { ) }); - //ts-ignore replacedText = reactStringReplace(replacedText, hashTagRegex, (match, i) => { const tag = tags.find(t => t.id.toLowerCase() == match.slice(1).toLowerCase()) return ( { - resetFilterTags(); addFilterTag(tag!); + map.closePopup(); }}>{match} ) - - - }) return ( diff --git a/src/Components/Map/Subcomponents/ItemViewPopup.tsx b/src/Components/Map/Subcomponents/ItemViewPopup.tsx index b6dbe2df..9e03428b 100644 --- a/src/Components/Map/Subcomponents/ItemViewPopup.tsx +++ b/src/Components/Map/Subcomponents/ItemViewPopup.tsx @@ -3,7 +3,6 @@ import { Popup as LeafletPopup, useMap } from 'react-leaflet' import { Item } from '../../../types' import { ItemFormPopupProps } from './ItemFormPopup' import { HeaderView } from './ItemPopupComponents/HeaderView' -import { StartEndView } from './ItemPopupComponents/StartEndView' import { TextView } from './ItemPopupComponents/TextView' export interface ItemViewPopupProps { @@ -12,13 +11,11 @@ export interface ItemViewPopupProps { setItemFormPopup?: React.Dispatch> } - - export const ItemViewPopup = (props: ItemViewPopupProps) => { const item: Item = props.item; return ( - +
diff --git a/src/Components/Map/UtopiaMap.tsx b/src/Components/Map/UtopiaMap.tsx index b8119d94..0ea64b7c 100644 --- a/src/Components/Map/UtopiaMap.tsx +++ b/src/Components/Map/UtopiaMap.tsx @@ -12,6 +12,7 @@ import { ItemsProvider } from "./hooks/useItems"; import { TagsProvider } from "./hooks/useTags"; import { LayersProvider } from "./hooks/useLayers"; import { FilterProvider } from "./hooks/useFilter"; +import { FilterControl } from "./Subcomponents/FilterControl"; export interface MapEventListenerProps { @@ -59,7 +60,8 @@ function UtopiaMap({
- + + diff --git a/src/Components/Map/hooks/useFilter.tsx b/src/Components/Map/hooks/useFilter.tsx index cee13e13..f5b21e25 100644 --- a/src/Components/Map/hooks/useFilter.tsx +++ b/src/Components/Map/hooks/useFilter.tsx @@ -3,28 +3,32 @@ import * as React from "react"; import {Tag} from "../../../types"; type ActionType = - | { type: "ADD"; tag: Tag } - | { type: "REMOVE"; id: string } - | { type: "RESET"}; + | { type: "ADD_TAG"; tag: Tag } + | { type: "REMOVE_TAG"; id: string } + | { type: "RESET_TAGS"}; type UseFilterManagerResult = ReturnType; const FilterContext = createContext({ filterTags: [], + searchPhrase: "", addFilterTag: () => { }, removeFilterTag: () => { }, resetFilterTags: () => { }, + setSearchPhrase: () => { }, }); function useFilterManager(initialTags: Tag[]): { filterTags: Tag[]; + searchPhrase: string; addFilterTag: (tag: Tag) => void; removeFilterTag: (id: string) => void; resetFilterTags: () => void; + setSearchPhrase: (phrase: string) => void; } { - const [filterTags, dispatch] = useReducer((state: Tag[], action: ActionType) => { + const [filterTags, dispatchTags] = useReducer((state: Tag[], action: ActionType) => { switch (action.type) { - case "ADD": + case "ADD_TAG": const exist = state.find((tag) => tag.id === action.tag.id ? true : false ); @@ -33,40 +37,43 @@ function useFilterManager(initialTags: Tag[]): { action.tag, ]; else return state; - case "REMOVE": + case "REMOVE_TAG": return state.filter(({ id }) => id !== action.id); - case "RESET": + case "RESET_TAGS": return initialTags; default: throw new Error(); } }, initialTags); - + const [searchPhrase, searchPhraseSet] = React.useState(""); const addFilterTag = (tag: Tag) => { - dispatch({ - type: "ADD", + dispatchTags({ + type: "ADD_TAG", tag, }); }; const removeFilterTag = useCallback((id: string) => { - dispatch({ - type: "REMOVE", + dispatchTags({ + type: "REMOVE_TAG", id, }); }, []); const resetFilterTags = useCallback(() => { - dispatch({ - type: "RESET", + dispatchTags({ + type: "RESET_TAGS", }); }, []); + const setSearchPhrase = useCallback((phrase:string) => { + searchPhraseSet(phrase) + }, []); - return { filterTags, addFilterTag, removeFilterTag, resetFilterTags }; + return { filterTags, addFilterTag, removeFilterTag, resetFilterTags, setSearchPhrase, searchPhrase }; } export const FilterProvider: React.FunctionComponent<{ @@ -96,3 +103,13 @@ export const useResetFilterTags = (): UseFilterManagerResult["resetFilterTags"] const { resetFilterTags } = useContext(FilterContext); return resetFilterTags; }; + +export const useSearchPhrase = (): UseFilterManagerResult["searchPhrase"] => { + const { searchPhrase } = useContext(FilterContext); + return searchPhrase; +}; + +export const useSetSearchPhrase = (): UseFilterManagerResult["setSearchPhrase"] => { + const { setSearchPhrase } = useContext(FilterContext); + return setSearchPhrase; +}; \ No newline at end of file diff --git a/src/Components/Map/hooks/useItems.tsx b/src/Components/Map/hooks/useItems.tsx index 5476fcec..d6ebfa9e 100644 --- a/src/Components/Map/hooks/useItems.tsx +++ b/src/Components/Map/hooks/useItems.tsx @@ -3,8 +3,9 @@ import * as React from "react"; import { Item, ItemsApi, LayerProps, Tag } from "../../../types"; import { toast } from "react-toastify"; import { useAddLayer } from "./useLayers"; -import { useTags } from "./useTags"; +import { useAddTag, useTags } from "./useTags"; import { hashTagRegex } from "../../../Utils/HashTagRegex"; +import { randomColor } from "../../../Utils/RandomColor"; type ActionType = @@ -39,6 +40,7 @@ function useItemsManager(initialItems: Item[]): { const addLayer = useAddLayer(); const tags = useTags(); + const addTag = useAddTag(); const [items, dispatch] = useReducer((state: Item[], action: ActionType) => { switch (action.type) { @@ -67,7 +69,9 @@ function useItemsManager(initialItems: Item[]): { const itemTagStrings = item.text.toLocaleLowerCase().match(hashTagRegex); const itemTags: Tag[] = []; itemTagStrings?.map(tag => { - if (tags.find(t => t.id === tag.slice(1))) { itemTags.push(tags.find(t => t.id === tag.slice(1))!) } + if (tags.find(t => t.id === tag.slice(1))) { + itemTags.push(tags.find(t => t.id === tag.slice(1))!) + } }) return { ...item, tags: itemTags } }) @@ -97,7 +101,7 @@ function useItemsManager(initialItems: Item[]): { const setItemsData = useCallback((layer: LayerProps) => { layer.data?.map(item => { - dispatch({ type: "ADD", item: { ...item, layer: layer } }) + dispatch({ type: "ADD", item: { ...item, layer: layer } }); }) dispatch({ type: "ADD_TAGS" }) }, []); diff --git a/src/index.css b/src/index.css index 1f795086..762e2fa5 100644 --- a/src/index.css +++ b/src/index.css @@ -16,4 +16,15 @@ .tw-modal-box { max-height: calc(100vh - 2em); +} + +.Toastify__toast { + border-radius: 1rem; + --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); + margin-left: 1rem; + margin-right: 1rem; + margin-bottom: 1rem; + } \ No newline at end of file