mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2025-12-13 07:46:10 +00:00
Filter Control and more styling
This commit is contained in:
parent
bbe755a034
commit
3a9c3de127
@ -133,7 +133,7 @@ export default function NavBar({ appName, useAuth }: { appName: string, useAuth:
|
|||||||
</label>
|
</label>
|
||||||
<ul tabIndex={1} className="tw-menu tw-menu-compact tw-dropdown-content tw-mt-3 tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-w-52 !tw-z-[1500]">
|
<ul tabIndex={1} className="tw-menu tw-menu-compact tw-dropdown-content tw-mt-3 tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-w-52 !tw-z-[1500]">
|
||||||
<li><a onClick={() => setLoginOpen(true)}>Login</a></li>
|
<li><a onClick={() => setLoginOpen(true)}>Login</a></li>
|
||||||
<li><a onClick={() => setSignupOpen(true)}>Sign In</a></li>
|
<li><a onClick={() => setSignupOpen(true)}>Sign Up</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
|||||||
import { useItems, useResetItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
import { useItems, useResetItems, 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 { useAddFilterTag, useFilterTags } from './hooks/useFilter'
|
import { useAddFilterTag, useFilterTags, useSearchPhrase } from './hooks/useFilter'
|
||||||
|
|
||||||
|
|
||||||
export const Layer = (props: LayerProps) => {
|
export const Layer = (props: LayerProps) => {
|
||||||
@ -26,6 +26,8 @@ export const Layer = (props: LayerProps) => {
|
|||||||
|
|
||||||
const resetItems = useResetItems();
|
const resetItems = useResetItems();
|
||||||
|
|
||||||
|
const searchPhrase = useSearchPhrase();
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
resetItems(props);
|
resetItems(props);
|
||||||
@ -36,8 +38,17 @@ export const Layer = (props: LayerProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{items &&
|
{items &&
|
||||||
items.filter(item => item.layer?.name === props.name)?.filter(item =>
|
items.
|
||||||
filterTags.length == 0 ? item : item.tags?.some(tag => filterTags.some(filterTag => filterTag.id === tag.id)))?.map((place: Item) => {
|
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;
|
const tags = place.tags;
|
||||||
|
|
||||||
let color1 = "#666";
|
let color1 = "#666";
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export default function AddButton({setSelectMode} : {setSelectMode: React.Dispat
|
|||||||
|
|
||||||
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-5 tw-bottom-5" >
|
||||||
<button tabIndex={0} className="tw-z-500 tw-border-0 tw-m-0 tw-mt-2 tw-p-0 tw-w-14 tw-h-14 tw-cursor-pointer tw-bg-white tw-rounded-full hover:tw-bg-gray-100 tw-mouse tw-drop-shadow-md tw-transition tw-ease-in tw-duration-200 focus:tw-outline-none">
|
<button tabIndex={0} className="tw-z-500 tw-border-0 tw-m-0 tw-mt-2 tw-p-0 tw-w-14 tw-h-14 tw-cursor-pointer tw-bg-white tw-rounded-full hover:tw-bg-gray-100 tw-mouse tw-drop-shadow-md tw-transition tw-ease-in tw-duration-200 focus:tw-outline-none tw-shadow-xl">
|
||||||
<svg viewBox="0 0 20 20" enableBackground="new 0 0 20 20" className="tw-w-6 tw-h-6 tw-inline-block">
|
<svg viewBox="0 0 20 20" enableBackground="new 0 0 20 20" className="tw-w-6 tw-h-6 tw-inline-block">
|
||||||
<path fill="#2e8555" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
<path fill="#2e8555" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
||||||
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
||||||
|
|||||||
28
src/Components/Map/Subcomponents/FilterControl.tsx
Normal file
28
src/Components/Map/Subcomponents/FilterControl.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import * as React from 'react'
|
||||||
|
import { useFilterTags, useRemoveFilterTag, useSetSearchPhrase } from '../hooks/useFilter'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const FilterControl = () => {
|
||||||
|
const filterTags = useFilterTags();
|
||||||
|
const removeFilterTag = useRemoveFilterTag();
|
||||||
|
const setSearchPhrase = useSetSearchPhrase();
|
||||||
|
return (
|
||||||
|
<div className='tw-flex tw-flex-col tw-absolute tw-top-4 tw-left-4 tw-z-1000 tw-right-4'>
|
||||||
|
<input type="text" placeholder="search ..." className="tw-input tw-input-bordered tw-w-full tw-max-w-sm tw-shadow-xl tw-rounded-2xl" onChange={(e) => setSearchPhrase(e.target.value)} />
|
||||||
|
<div className='tw-flex tw-flex-wrap tw-mt-4'>
|
||||||
|
{
|
||||||
|
filterTags.map(tag =>
|
||||||
|
<div key={tag.id} className='tw-rounded-2xl tw-text-white tw-p-2 tw-px-4 tw-shadow-xl tw-card tw-mr-2 tw-mb-2' style={{ backgroundColor: tag.color }}>
|
||||||
|
<div className="tw-card-actions tw-justify-end">
|
||||||
|
<label className="tw-btn tw-btn-xs tw-btn-circle tw-absolute tw--right-2 tw--top-2 tw-bg-white tw-text-gray-600" onClick={() => (removeFilterTag(tag.id))}>✕</label>
|
||||||
|
</div><b>#{tag.id}</b>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -71,7 +71,7 @@ export function ItemFormPopup(props: ItemFormPopupProps) {
|
|||||||
}, [props.position])
|
}, [props.position])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LeafletPopup minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}
|
<LeafletPopup minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}
|
||||||
eventHandlers={{
|
eventHandlers={{
|
||||||
remove: () => {
|
remove: () => {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
|||||||
@ -1,25 +1,44 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { Item } from '../../../../types'
|
import { Item } from '../../../../types'
|
||||||
import { useTags } from '../../hooks/useTags';
|
import { useAddTag, useTags } from '../../hooks/useTags';
|
||||||
import reactStringReplace from 'react-string-replace';
|
import reactStringReplace from 'react-string-replace';
|
||||||
import { useAddFilterTag, useResetFilterTags } from '../../hooks/useFilter';
|
import { useAddFilterTag, useResetFilterTags } from '../../hooks/useFilter';
|
||||||
import { hashTagRegex } from '../../../../Utils/HashTagRegex';
|
import { hashTagRegex } from '../../../../Utils/HashTagRegex';
|
||||||
import { fixUrls, mailRegex, urlRegex } from '../../../../Utils/ReplaceURLs';
|
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 }) => {
|
export const TextView = ({ item }: { item?: Item }) => {
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
|
const addTag = useAddTag();
|
||||||
|
|
||||||
const addFilterTag = useAddFilterTag();
|
const addFilterTag = useAddFilterTag();
|
||||||
const resetFilterTags = useResetFilterTags();
|
const resetFilterTags = useResetFilterTags();
|
||||||
|
|
||||||
|
const map = useMap();
|
||||||
|
|
||||||
let replacedText;
|
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);
|
if (item && item.text) replacedText = fixUrls(item.text);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
replacedText = reactStringReplace(replacedText, /(https?:\/\/\S+)/g, (url, i) => {
|
replacedText = reactStringReplace(replacedText, /(https?:\/\/\S+)/g, (url, i) => {
|
||||||
let shortUrl = url;
|
let shortUrl = url;
|
||||||
if (url.match('^https:\/\/')) {
|
if (url.match('^https:\/\/')) {
|
||||||
@ -39,19 +58,15 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
//ts-ignore
|
|
||||||
replacedText = reactStringReplace(replacedText, hashTagRegex, (match, i) => {
|
replacedText = reactStringReplace(replacedText, hashTagRegex, (match, i) => {
|
||||||
|
|
||||||
const tag = tags.find(t => t.id.toLowerCase() == match.slice(1).toLowerCase())
|
const tag = tags.find(t => t.id.toLowerCase() == match.slice(1).toLowerCase())
|
||||||
return (
|
return (
|
||||||
<a style={{ color: tag ? tag.color : '#aaa' , fontWeight: 'bold', cursor: 'pointer' }} key={tag ? tag.id+item!.id+i : i} onClick={() => {
|
<a style={{ color: tag ? tag.color : '#aaa' , fontWeight: 'bold', cursor: 'pointer' }} key={tag ? tag.id+item!.id+i : i} onClick={() => {
|
||||||
resetFilterTags();
|
|
||||||
addFilterTag(tag!);
|
addFilterTag(tag!);
|
||||||
|
map.closePopup();
|
||||||
}}>{match}</a>
|
}}>{match}</a>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { Popup as LeafletPopup, useMap } from 'react-leaflet'
|
|||||||
import { Item } from '../../../types'
|
import { Item } from '../../../types'
|
||||||
import { ItemFormPopupProps } from './ItemFormPopup'
|
import { ItemFormPopupProps } from './ItemFormPopup'
|
||||||
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
||||||
import { StartEndView } from './ItemPopupComponents/StartEndView'
|
|
||||||
import { TextView } from './ItemPopupComponents/TextView'
|
import { TextView } from './ItemPopupComponents/TextView'
|
||||||
|
|
||||||
export interface ItemViewPopupProps {
|
export interface ItemViewPopupProps {
|
||||||
@ -12,13 +11,11 @@ export interface ItemViewPopupProps {
|
|||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const ItemViewPopup = (props: ItemViewPopupProps) => {
|
export const ItemViewPopup = (props: ItemViewPopupProps) => {
|
||||||
const item: Item = props.item;
|
const item: Item = props.item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LeafletPopup maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 5]}>
|
<LeafletPopup maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
|
||||||
<div>
|
<div>
|
||||||
<HeaderView item={props.item} setItemFormPopup={props.setItemFormPopup} />
|
<HeaderView item={props.item} setItemFormPopup={props.setItemFormPopup} />
|
||||||
<div className='tw-overflow-y-auto tw-max-h-72'>
|
<div className='tw-overflow-y-auto tw-max-h-72'>
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import { ItemsProvider } from "./hooks/useItems";
|
|||||||
import { TagsProvider } from "./hooks/useTags";
|
import { TagsProvider } from "./hooks/useTags";
|
||||||
import { LayersProvider } from "./hooks/useLayers";
|
import { LayersProvider } from "./hooks/useLayers";
|
||||||
import { FilterProvider } from "./hooks/useFilter";
|
import { FilterProvider } from "./hooks/useFilter";
|
||||||
|
import { FilterControl } from "./Subcomponents/FilterControl";
|
||||||
|
|
||||||
|
|
||||||
export interface MapEventListenerProps {
|
export interface MapEventListenerProps {
|
||||||
@ -59,7 +60,8 @@ function UtopiaMap({
|
|||||||
<FilterProvider initialTags={[]}>
|
<FilterProvider initialTags={[]}>
|
||||||
<ItemsProvider initialItems={[]}>
|
<ItemsProvider initialItems={[]}>
|
||||||
<div className={(selectMode != null ? "crosshair-cursor-enabled" : undefined)}>
|
<div className={(selectMode != null ? "crosshair-cursor-enabled" : undefined)}>
|
||||||
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={center} zoom={zoom}>
|
<MapContainer ref={mapDivRef} style={{ height: height, width: width }} center={center} zoom={zoom} zoomControl={false}>
|
||||||
|
<FilterControl></FilterControl>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution='© <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" />
|
||||||
|
|||||||
@ -3,28 +3,32 @@ import * as React from "react";
|
|||||||
import {Tag} from "../../../types";
|
import {Tag} from "../../../types";
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| { type: "ADD"; tag: Tag }
|
| { type: "ADD_TAG"; tag: Tag }
|
||||||
| { type: "REMOVE"; id: string }
|
| { type: "REMOVE_TAG"; id: string }
|
||||||
| { type: "RESET"};
|
| { type: "RESET_TAGS"};
|
||||||
|
|
||||||
type UseFilterManagerResult = ReturnType<typeof useFilterManager>;
|
type UseFilterManagerResult = ReturnType<typeof useFilterManager>;
|
||||||
|
|
||||||
const FilterContext = createContext<UseFilterManagerResult>({
|
const FilterContext = createContext<UseFilterManagerResult>({
|
||||||
filterTags: [],
|
filterTags: [],
|
||||||
|
searchPhrase: "",
|
||||||
addFilterTag: () => { },
|
addFilterTag: () => { },
|
||||||
removeFilterTag: () => { },
|
removeFilterTag: () => { },
|
||||||
resetFilterTags: () => { },
|
resetFilterTags: () => { },
|
||||||
|
setSearchPhrase: () => { },
|
||||||
});
|
});
|
||||||
|
|
||||||
function useFilterManager(initialTags: Tag[]): {
|
function useFilterManager(initialTags: Tag[]): {
|
||||||
filterTags: Tag[];
|
filterTags: Tag[];
|
||||||
|
searchPhrase: string;
|
||||||
addFilterTag: (tag: Tag) => void;
|
addFilterTag: (tag: Tag) => void;
|
||||||
removeFilterTag: (id: string) => void;
|
removeFilterTag: (id: string) => void;
|
||||||
resetFilterTags: () => 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) {
|
switch (action.type) {
|
||||||
case "ADD":
|
case "ADD_TAG":
|
||||||
const exist = state.find((tag) =>
|
const exist = state.find((tag) =>
|
||||||
tag.id === action.tag.id ? true : false
|
tag.id === action.tag.id ? true : false
|
||||||
);
|
);
|
||||||
@ -33,40 +37,43 @@ function useFilterManager(initialTags: Tag[]): {
|
|||||||
action.tag,
|
action.tag,
|
||||||
];
|
];
|
||||||
else return state;
|
else return state;
|
||||||
case "REMOVE":
|
case "REMOVE_TAG":
|
||||||
return state.filter(({ id }) => id !== action.id);
|
return state.filter(({ id }) => id !== action.id);
|
||||||
case "RESET":
|
case "RESET_TAGS":
|
||||||
return initialTags;
|
return initialTags;
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
}, initialTags);
|
}, initialTags);
|
||||||
|
|
||||||
|
const [searchPhrase, searchPhraseSet] = React.useState<string>("");
|
||||||
|
|
||||||
const addFilterTag = (tag: Tag) => {
|
const addFilterTag = (tag: Tag) => {
|
||||||
dispatch({
|
dispatchTags({
|
||||||
type: "ADD",
|
type: "ADD_TAG",
|
||||||
tag,
|
tag,
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFilterTag = useCallback((id: string) => {
|
const removeFilterTag = useCallback((id: string) => {
|
||||||
dispatch({
|
dispatchTags({
|
||||||
type: "REMOVE",
|
type: "REMOVE_TAG",
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const resetFilterTags = useCallback(() => {
|
const resetFilterTags = useCallback(() => {
|
||||||
dispatch({
|
dispatchTags({
|
||||||
type: "RESET",
|
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<{
|
export const FilterProvider: React.FunctionComponent<{
|
||||||
@ -96,3 +103,13 @@ export const useResetFilterTags = (): UseFilterManagerResult["resetFilterTags"]
|
|||||||
const { resetFilterTags } = useContext(FilterContext);
|
const { resetFilterTags } = useContext(FilterContext);
|
||||||
return resetFilterTags;
|
return resetFilterTags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useSearchPhrase = (): UseFilterManagerResult["searchPhrase"] => {
|
||||||
|
const { searchPhrase } = useContext(FilterContext);
|
||||||
|
return searchPhrase;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSetSearchPhrase = (): UseFilterManagerResult["setSearchPhrase"] => {
|
||||||
|
const { setSearchPhrase } = useContext(FilterContext);
|
||||||
|
return setSearchPhrase;
|
||||||
|
};
|
||||||
@ -3,8 +3,9 @@ import * as React from "react";
|
|||||||
import { Item, ItemsApi, LayerProps, Tag } from "../../../types";
|
import { Item, ItemsApi, LayerProps, Tag } from "../../../types";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { useAddLayer } from "./useLayers";
|
import { useAddLayer } from "./useLayers";
|
||||||
import { useTags } from "./useTags";
|
import { useAddTag, useTags } from "./useTags";
|
||||||
import { hashTagRegex } from "../../../Utils/HashTagRegex";
|
import { hashTagRegex } from "../../../Utils/HashTagRegex";
|
||||||
|
import { randomColor } from "../../../Utils/RandomColor";
|
||||||
|
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
@ -39,6 +40,7 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
|
|
||||||
const addLayer = useAddLayer();
|
const addLayer = useAddLayer();
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
|
const addTag = useAddTag();
|
||||||
|
|
||||||
const [items, dispatch] = useReducer((state: Item[], action: ActionType) => {
|
const [items, dispatch] = useReducer((state: Item[], action: ActionType) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -67,7 +69,9 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
const itemTagStrings = item.text.toLocaleLowerCase().match(hashTagRegex);
|
const itemTagStrings = item.text.toLocaleLowerCase().match(hashTagRegex);
|
||||||
const itemTags: Tag[] = [];
|
const itemTags: Tag[] = [];
|
||||||
itemTagStrings?.map(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 }
|
return { ...item, tags: itemTags }
|
||||||
})
|
})
|
||||||
@ -97,7 +101,7 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
|
|
||||||
const setItemsData = useCallback((layer: LayerProps) => {
|
const setItemsData = useCallback((layer: LayerProps) => {
|
||||||
layer.data?.map(item => {
|
layer.data?.map(item => {
|
||||||
dispatch({ type: "ADD", item: { ...item, layer: layer } })
|
dispatch({ type: "ADD", item: { ...item, layer: layer } });
|
||||||
})
|
})
|
||||||
dispatch({ type: "ADD_TAGS" })
|
dispatch({ type: "ADD_TAGS" })
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@ -17,3 +17,14 @@
|
|||||||
.tw-modal-box {
|
.tw-modal-box {
|
||||||
max-height: calc(100vh - 2em);
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user