mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-02-06 09:55:47 +00:00
more customizable item fields @ layer component and improved tag handling
This commit is contained in:
parent
f9ffc7b739
commit
fbe19d1994
@ -1,35 +1,38 @@
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet'
|
import { Marker, Tooltip, useMap, useMapEvents } from 'react-leaflet'
|
||||||
import { Item, LayerProps } from '../../types'
|
import { Item, LayerProps, Tag } from '../../types'
|
||||||
import MarkerIconFactory from '../../Utils/MarkerIconFactory'
|
import MarkerIconFactory from '../../Utils/MarkerIconFactory'
|
||||||
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
import { ItemViewPopup } from './Subcomponents/ItemViewPopup'
|
||||||
import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
import { useItems, useSetItemsApi, useSetItemsData } from './hooks/useItems'
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
import { ItemFormPopup } from './Subcomponents/ItemFormPopup'
|
||||||
import { useFilterTags, useIsLayerVisible } from './hooks/useFilter'
|
import { useFilterTags, useIsLayerVisible } from './hooks/useFilter'
|
||||||
import { useGetItemTags } from './hooks/useTags'
|
import { useAddTag, useAllTagsLoaded, useGetItemTags, useTags } 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'
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useAssetApi } from '../AppShell/hooks/useAssets'
|
|
||||||
import { getValue } from '../../Utils/GetValue'
|
import { getValue } from '../../Utils/GetValue'
|
||||||
|
import { hashTagRegex } from '../../Utils/HashTagRegex'
|
||||||
|
import { randomColor } from '../../Utils/RandomColor'
|
||||||
|
|
||||||
export const Layer = ( {
|
export const Layer = ({
|
||||||
data,
|
data,
|
||||||
children,
|
children,
|
||||||
name='places',
|
name = 'places',
|
||||||
menuIcon='MapPinIcon',
|
menuIcon = 'MapPinIcon',
|
||||||
menuText='add new place',
|
menuText = 'add new place',
|
||||||
menuColor='#2E7D32',
|
menuColor = '#2E7D32',
|
||||||
markerIcon='circle-solid',
|
markerIcon = 'circle-solid',
|
||||||
markerShape='circle',
|
markerShape = 'circle',
|
||||||
markerDefaultColor='#777',
|
markerDefaultColor = '#777',
|
||||||
api,
|
api,
|
||||||
itemTitleField='name',
|
itemNameField = 'name',
|
||||||
itemTextField='text',
|
itemTextField = 'text',
|
||||||
itemAvatarField,
|
itemAvatarField,
|
||||||
itemColorField,
|
itemColorField,
|
||||||
itemOwnerField,
|
itemOwnerField,
|
||||||
|
itemLatitudeField = 'position.coordinates.1',
|
||||||
|
itemLongitudeField = 'position.coordinates.0',
|
||||||
setItemFormPopup,
|
setItemFormPopup,
|
||||||
itemFormPopup,
|
itemFormPopup,
|
||||||
clusterRef
|
clusterRef
|
||||||
@ -47,28 +50,31 @@ export const Layer = ( {
|
|||||||
|
|
||||||
let location = useLocation();
|
let location = useLocation();
|
||||||
|
|
||||||
|
const allTagsLoaded = useAllTagsLoaded();
|
||||||
|
const tags = useTags();
|
||||||
|
const addTag = useAddTag();
|
||||||
|
const [newTagsToAdd, setNewTagsToAdd] = useState<Tag[]>([]);
|
||||||
|
const [tagsReady, setTagsReady] = useState<boolean>(false);
|
||||||
|
|
||||||
|
|
||||||
const map = useMap();
|
const map = useMap();
|
||||||
|
|
||||||
const isLayerVisible = useIsLayerVisible();
|
const isLayerVisible = useIsLayerVisible();
|
||||||
|
|
||||||
const assetsApi = useAssetApi();
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
data && setItemsData({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, setItemFormPopup, itemFormPopup, clusterRef });
|
||||||
data && setItemsData({data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemTitleField, itemTextField, itemAvatarField, itemColorField, setItemFormPopup, itemFormPopup, clusterRef});
|
api && setItemsApi({ data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemNameField, itemTextField, itemAvatarField, itemColorField, itemOwnerField, setItemFormPopup, itemFormPopup, clusterRef });
|
||||||
api && setItemsApi({data, children, name, menuIcon, menuText, menuColor, markerIcon, markerShape, markerDefaultColor, api, itemTitleField, itemTextField, itemAvatarField, itemColorField, setItemFormPopup, itemFormPopup, clusterRef});
|
|
||||||
}, [data, api])
|
}, [data, api])
|
||||||
|
|
||||||
useMapEvents({
|
useMapEvents({
|
||||||
popupopen: (e) => {
|
popupopen: (e) => {
|
||||||
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
|
const item = Object.entries(leafletRefs).find(r => r[1].popup == e.popup)?.[1].item;
|
||||||
if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
|
if (item?.layer?.name == name && window.location.pathname.split("/")[2] != item.id) {
|
||||||
window.history.pushState({}, "", `/${name}/${item.id}`)
|
window.history.pushState({}, "", `/${name}/${item.id}`)
|
||||||
let title = "";
|
let title = "";
|
||||||
if(item.name) title = item.name;
|
if (item.name) title = item.name;
|
||||||
else if (item.layer?.itemTitleField) title = getValue(item, item.layer.itemTitleField);
|
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
||||||
document.title = `${document.title.split("-")[0]} - ${title}`;
|
document.title = `${document.title.split("-")[0]} - ${title}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -90,8 +96,8 @@ export const Layer = ( {
|
|||||||
});
|
});
|
||||||
const item = leafletRefs[id]?.item;
|
const item = leafletRefs[id]?.item;
|
||||||
let title = "";
|
let title = "";
|
||||||
if(item.name) title = item.name;
|
if (item.name) title = item.name;
|
||||||
else if (item.layer?.itemTitleField) title = getValue(item, item.layer.itemTitleField);
|
else if (item.layer?.itemNameField) title = getValue(item, item.layer.itemNameField);
|
||||||
document.title = `${document.title.split("-")[0]} - ${title}`;
|
document.title = `${document.title.split("-")[0]} - ${title}`;
|
||||||
document.querySelector('meta[property="og:title"]')?.setAttribute("content", item.name);
|
document.querySelector('meta[property="og:title"]')?.setAttribute("content", item.name);
|
||||||
document.querySelector('meta[property="og:description"]')?.setAttribute("content", item.text);
|
document.querySelector('meta[property="og:description"]')?.setAttribute("content", item.text);
|
||||||
@ -106,68 +112,102 @@ export const Layer = ( {
|
|||||||
openPopup();
|
openPopup();
|
||||||
}, [leafletRefs, location])
|
}, [leafletRefs, location])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, [allTagsLoaded])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(tagsReady){
|
||||||
|
newTagsToAdd.map(newtag => {
|
||||||
|
addTag(newtag);
|
||||||
|
setNewTagsToAdd(current =>
|
||||||
|
current.filter(tag => {
|
||||||
|
return tag.id !== newtag.id;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [tagsReady])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{items &&
|
{items &&
|
||||||
items.
|
items.
|
||||||
filter(item => item.text).
|
|
||||||
filter(item => item.layer?.name === name)?.
|
filter(item => item.layer?.name === name)?.
|
||||||
filter(item =>
|
filter(item =>
|
||||||
filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id === tag.id)))?.
|
filterTags.length == 0 ? item : filterTags.every(tag => getItemTags(item).some(filterTag => filterTag.id.toLocaleLowerCase() === tag.id.toLocaleLowerCase())))?.
|
||||||
filter(item => item.layer && isLayerVisible(item.layer)).
|
filter(item => item.layer && isLayerVisible(item.layer)).
|
||||||
map((item: Item) => {
|
map((item: Item) => {
|
||||||
const tags = getItemTags(item);
|
if (getValue(item, itemLongitudeField) && getValue(item, itemLatitudeField)) {
|
||||||
|
if (item?.tags) {
|
||||||
|
item[itemTextField] = getValue(item, itemTextField) + '\n\n';
|
||||||
|
item.tags.map(tag => {
|
||||||
|
if(!item[itemTextField].includes(`#${tag}`))
|
||||||
|
return (item[itemTextField] = item[itemTextField] + `#${tag} `)
|
||||||
|
return item[itemTextField]
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(allTagsLoaded) {
|
||||||
|
item[itemTextField].toLocaleLowerCase().match(hashTagRegex)?.map(tag=> {
|
||||||
|
if ((!tags.find((t) => t.id.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase()))&& !newTagsToAdd.find((t) => t.id.toLocaleLowerCase() === tag.slice(1).toLocaleLowerCase())) {
|
||||||
|
const newTag = {id: tag.slice(1).toLocaleLowerCase(), color: randomColor()};
|
||||||
|
setNewTagsToAdd(current => [...current, newTag]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
!tagsReady && setTagsReady(true);
|
||||||
|
}
|
||||||
|
|
||||||
let color1 = markerDefaultColor;
|
|
||||||
let color2 = "RGBA(35, 31, 32, 0.2)";
|
const itemTtags = getItemTags(item);
|
||||||
if (itemColorField) color1 = getValue(item, itemColorField);
|
|
||||||
if(color1 == null) color1 = markerDefaultColor;
|
const latitude = itemLatitudeField && item ? getValue(item, itemLatitudeField) : undefined;
|
||||||
|
const longitude = itemLongitudeField && item ? getValue(item, itemLongitudeField) : undefined;
|
||||||
else if (tags && tags[0]) {
|
|
||||||
color1 = tags[0].color;
|
let color1 = markerDefaultColor;
|
||||||
}
|
let color2 = "RGBA(35, 31, 32, 0.2)";
|
||||||
if (tags && tags[0] && itemColorField) color2 = tags[0].color;
|
if (itemColorField) color1 = getValue(item, itemColorField);
|
||||||
else if (tags && tags[1]) {
|
|
||||||
color2 = tags[1].color;
|
else if (itemTtags && itemTtags[0]) {
|
||||||
}
|
color1 = itemTtags[0].color;
|
||||||
return (
|
}
|
||||||
<Marker ref={(r) => {
|
if (itemTtags && itemTtags[0] && itemColorField) color2 = itemTtags[0].color;
|
||||||
if (!(item.id in leafletRefs && leafletRefs[item.id].marker == r))
|
else if (itemTtags && itemTtags[1]) {
|
||||||
r && addMarker(item, r);
|
color2 = itemTtags[1].color;
|
||||||
}} icon={MarkerIconFactory(markerShape, color1, color2, markerIcon)} key={item.id} position={[item.position.coordinates[1], item.position.coordinates[0]]}>
|
}
|
||||||
{
|
return (
|
||||||
(children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ?
|
<Marker ref={(r) => {
|
||||||
React.Children.toArray(children).map((child) =>
|
if (!(item.id in leafletRefs && leafletRefs[item.id].marker == r))
|
||||||
React.isValidElement(child) && child.props.__TYPE === "ItemView" ?
|
r && addMarker(item, r);
|
||||||
<ItemViewPopup ref={(r) => {
|
}} icon={MarkerIconFactory(markerShape, color1, color2, markerIcon)} key={item.id} position={[latitude, longitude]}>
|
||||||
|
{
|
||||||
|
(children && React.Children.toArray(children).some(child => React.isValidElement(child) && child.props.__TYPE === "ItemView") ?
|
||||||
|
React.Children.toArray(children).map((child) =>
|
||||||
|
React.isValidElement(child) && child.props.__TYPE === "ItemView" ?
|
||||||
|
<ItemViewPopup ref={(r) => {
|
||||||
|
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
|
||||||
|
r && addPopup(item, r as Popup);
|
||||||
|
}} key={item.id + item.name}
|
||||||
|
item={item}
|
||||||
|
setItemFormPopup={setItemFormPopup}>
|
||||||
|
{child}
|
||||||
|
</ItemViewPopup>
|
||||||
|
: ""
|
||||||
|
)
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<ItemViewPopup key={item.id + item.name} ref={(r) => {
|
||||||
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
|
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
|
||||||
r && addPopup(item, r as Popup);
|
r && addPopup(item, r as Popup);
|
||||||
}} key={item.id + item.name}
|
}}
|
||||||
title={itemTitleField && item ? getValue(item, itemTitleField) : undefined}
|
|
||||||
avatar={itemAvatarField && item && getValue(item, itemAvatarField)? assetsApi.url + getValue(item, itemAvatarField) : undefined}
|
|
||||||
owner={itemOwnerField && item ? getValue(item, itemOwnerField) : undefined}
|
|
||||||
item={item}
|
item={item}
|
||||||
setItemFormPopup={setItemFormPopup}>
|
setItemFormPopup={setItemFormPopup} />
|
||||||
{child}
|
</>)
|
||||||
</ItemViewPopup>
|
}
|
||||||
: ""
|
<Tooltip offset={[0, -38]} direction='top'>{item.name ? item.name : getValue(item, itemNameField)}</Tooltip>
|
||||||
)
|
</Marker>
|
||||||
:
|
);
|
||||||
<>
|
}
|
||||||
<ItemViewPopup key={item.id + item.name} ref={(r) => {
|
else return null;
|
||||||
if (!(item.id in leafletRefs && leafletRefs[item.id].popup == r))
|
|
||||||
r && addPopup(item, r as Popup);
|
|
||||||
}} title={itemTitleField && item ? getValue(item, itemTitleField) : undefined}
|
|
||||||
avatar={itemAvatarField && item && getValue(item, itemAvatarField)? assetsApi.url + getValue(item, itemAvatarField) : undefined}
|
|
||||||
owner={itemOwnerField && item ? getValue(item, itemOwnerField) : undefined}
|
|
||||||
item={item}
|
|
||||||
setItemFormPopup={setItemFormPopup} />
|
|
||||||
</>)
|
|
||||||
}
|
|
||||||
<Tooltip offset={[0, -38]} direction='top'>{item.name? item.name : getValue(item, itemTitleField)}</Tooltip>
|
|
||||||
</Marker>
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
{//{children}}
|
{//{children}}
|
||||||
|
|||||||
@ -47,7 +47,8 @@ export const SearchControl = ({ clusterRef }) => {
|
|||||||
};
|
};
|
||||||
searchGeo();
|
searchGeo();
|
||||||
setItemsResults(items.filter(item => {
|
setItemsResults(items.filter(item => {
|
||||||
if (item.layer?.itemTitleField) item.name = getValue(item, item.layer.itemTitleField)
|
if (item.layer?.itemNameField) item.name = getValue(item, item.layer.itemNameField)
|
||||||
|
if (item.layer?.itemTextField) item.text = getValue(item, item.layer.itemTextField)
|
||||||
return item.name?.toLowerCase().includes(value.toLowerCase()) || item.text?.toLowerCase().includes(value.toLowerCase())
|
return item.name?.toLowerCase().includes(value.toLowerCase()) || item.text?.toLowerCase().includes(value.toLowerCase())
|
||||||
}))
|
}))
|
||||||
setTagsResults(tags.filter(tag => tag.id?.toLowerCase().includes(value.toLowerCase())))
|
setTagsResults(tags.filter(tag => tag.id?.toLowerCase().includes(value.toLowerCase())))
|
||||||
|
|||||||
@ -7,14 +7,14 @@ import { Item } from "../../../../types";
|
|||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { useHasUserPermission } from "../../hooks/usePermissions";
|
import { useHasUserPermission } from "../../hooks/usePermissions";
|
||||||
import { useAuth } from "../../../Auth";
|
import { useAuth } from "../../../Auth";
|
||||||
|
import { getValue } from "../../../../Utils/GetValue";
|
||||||
|
import { useAssetApi } from '../../../AppShell/hooks/useAssets'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function HeaderView({ item, title, avatar, owner, setItemFormPopup }: {
|
|
||||||
|
export function HeaderView({ item, setItemFormPopup }: {
|
||||||
item: Item,
|
item: Item,
|
||||||
title?: string,
|
|
||||||
avatar?: string,
|
|
||||||
owner?: string,
|
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
@ -26,6 +26,13 @@ export function HeaderView({ item, title, avatar, owner, setItemFormPopup }: {
|
|||||||
|
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
|
const assetsApi = useAssetApi();
|
||||||
|
|
||||||
|
const avatar = item.layer?.itemAvatarField && item && getValue(item, item.layer?.itemAvatarField)? assetsApi.url + getValue(item, item.layer?.itemAvatarField ) : undefined;
|
||||||
|
const title = item.layer?.itemNameField && item ? getValue(item, item.layer?.itemNameField) : undefined;
|
||||||
|
const owner = item.layer?.itemOwnerField && item ? getValue(item, item.layer?.itemOwnerField) : undefined;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => {
|
const removeItemFromMap = async (event: React.MouseEvent<HTMLElement>) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@ -6,14 +6,17 @@ import { hashTagRegex } from '../../../../Utils/HashTagRegex';
|
|||||||
import { fixUrls, mailRegex } from '../../../../Utils/ReplaceURLs';
|
import { fixUrls, mailRegex } from '../../../../Utils/ReplaceURLs';
|
||||||
import Markdown from 'react-markdown'
|
import Markdown from 'react-markdown'
|
||||||
import rehypeVideo from 'rehype-video';
|
import rehypeVideo from 'rehype-video';
|
||||||
|
import { getValue } from '../../../../Utils/GetValue';
|
||||||
|
|
||||||
export const TextView = ({ item }: { item?: Item }) => {
|
export const TextView = ({ item }: { item?: Item }) => {
|
||||||
const tags = useTags();
|
const tags = useTags();
|
||||||
const addFilterTag = useAddFilterTag();
|
const addFilterTag = useAddFilterTag();
|
||||||
|
|
||||||
|
const text = item?.layer?.itemTextField && item ? getValue(item, item.layer?.itemTextField) : undefined;
|
||||||
|
|
||||||
let replacedText;
|
let replacedText;
|
||||||
|
|
||||||
if (item && item.text) replacedText = fixUrls(item.text);
|
if (item && text) replacedText = fixUrls(text);
|
||||||
|
|
||||||
replacedText = replacedText.replace(/(?<!\]?\()https?:\/\/[^\s\)]+(?!\))/g, (url) => {
|
replacedText = replacedText.replace(/(?<!\]?\()https?:\/\/[^\s\)]+(?!\))/g, (url) => {
|
||||||
let shortUrl = url;
|
let shortUrl = url;
|
||||||
@ -63,6 +66,9 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
const CustomOrderdList = ({ children }) => (
|
const CustomOrderdList = ({ children }) => (
|
||||||
<ol className="tw-list-decimal tw-list-inside">{children}</ol>
|
<ol className="tw-list-decimal tw-list-inside">{children}</ol>
|
||||||
);
|
);
|
||||||
|
const CustomHorizontalRow = ({ children }) => (
|
||||||
|
<hr className="tw-border-current">{children}</hr>
|
||||||
|
);
|
||||||
const CustomImage = ({ alt, src, title }) => (
|
const CustomImage = ({ alt, src, title }) => (
|
||||||
<img
|
<img
|
||||||
className="max-w-full rounded-lg shadow-md"
|
className="max-w-full rounded-lg shadow-md"
|
||||||
@ -80,22 +86,18 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
{children}
|
{children}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
const CustomHashTagLink = ({ children, tag, item }) => (
|
const CustomHashTagLink = ({ children, tag, item }) => {
|
||||||
|
return (
|
||||||
<a
|
<a
|
||||||
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
style={{ color: tag ? tag.color : '#faa', fontWeight: 'bold', cursor: 'pointer' }}
|
||||||
key={tag ? tag.id + item!.id : item.id}
|
key={tag ? tag.id + item!.id : item.id}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
addFilterTag(tag!);
|
addFilterTag(tag!);
|
||||||
// map.fitBounds(items)
|
// map.fitBounds(items)
|
||||||
// map.closePopup();
|
// map.closePopup();
|
||||||
}}>{children}</a>
|
}}>{children}</a>
|
||||||
);
|
)};
|
||||||
|
|
||||||
const isSpecialYouTubeLink = (url) => {
|
|
||||||
return /(?<=!\()[^)]+(?=\))/g.test(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@ -111,15 +113,15 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<iframe className='tw-w-full'
|
<iframe className='tw-w-full'
|
||||||
src={youtubeEmbedUrl}
|
src={youtubeEmbedUrl}
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
/>
|
/>
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (href?.startsWith("#")) {
|
if (href?.startsWith("#")) {
|
||||||
const tag = tags.find(t => t.id.toLowerCase() == href.slice(1).toLowerCase())
|
const tag = tags.find(t => t.id.toLowerCase() == decodeURI(href).slice(1).toLowerCase())
|
||||||
return <CustomHashTagLink tag={tag} item={item}>{children}</CustomHashTagLink>;
|
return <CustomHashTagLink tag={tag} item={item}>{children}</CustomHashTagLink>;
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
@ -130,6 +132,7 @@ export const TextView = ({ item }: { item?: Item }) => {
|
|||||||
ul: CustomUnorderdList,
|
ul: CustomUnorderdList,
|
||||||
ol: CustomOrderdList,
|
ol: CustomOrderdList,
|
||||||
img: CustomImage,
|
img: CustomImage,
|
||||||
|
hr: CustomHorizontalRow,
|
||||||
h1: CustomH1,
|
h1: CustomH1,
|
||||||
h2: CustomH2,
|
h2: CustomH2,
|
||||||
h3: CustomH3,
|
h3: CustomH3,
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { Item } from '../../../types'
|
|||||||
import { ItemFormPopupProps } from './ItemFormPopup'
|
import { ItemFormPopupProps } from './ItemFormPopup'
|
||||||
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
import { HeaderView } from './ItemPopupComponents/HeaderView'
|
||||||
import { TextView } from './ItemPopupComponents/TextView'
|
import { TextView } from './ItemPopupComponents/TextView'
|
||||||
import { useAssetApi } from '../../AppShell/hooks/useAssets'
|
|
||||||
import { timeAgo } from '../../../Utils/TimeAgo'
|
import { timeAgo } from '../../../Utils/TimeAgo'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
@ -12,9 +11,6 @@ import { useState } from 'react'
|
|||||||
export interface ItemViewPopupProps {
|
export interface ItemViewPopupProps {
|
||||||
item: Item,
|
item: Item,
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
title?: string;
|
|
||||||
avatar?: string;
|
|
||||||
owner?: string,
|
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +22,7 @@ export const ItemViewPopup = React.forwardRef((props: ItemViewPopupProps, ref: a
|
|||||||
return (
|
return (
|
||||||
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
|
<LeafletPopup ref={ref} maxHeight={377} minWidth={275} maxWidth={275} autoPanPadding={[20, 80]}>
|
||||||
<div className='tw-bg-base-100 tw-text-base-content'>
|
<div className='tw-bg-base-100 tw-text-base-content'>
|
||||||
<HeaderView item={props.item} title={props.title} avatar={props.avatar} owner={props.owner} setItemFormPopup={props.setItemFormPopup} />
|
<HeaderView item={props.item} setItemFormPopup={props.setItemFormPopup} />
|
||||||
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64'>
|
<div className='tw-overflow-y-auto tw-overflow-x-hidden tw-max-h-64'>
|
||||||
{props.children ?
|
{props.children ?
|
||||||
|
|
||||||
|
|||||||
@ -66,7 +66,7 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
|
|
||||||
|
|
||||||
const setItemsApi = useCallback(async (layer: LayerProps) => {
|
const setItemsApi = useCallback(async (layer: LayerProps) => {
|
||||||
layer.api?.createItem && addLayer(layer);
|
addLayer(layer);
|
||||||
const result = await toast.promise(
|
const result = await toast.promise(
|
||||||
layer.api!.getItems(),
|
layer.api!.getItems(),
|
||||||
{
|
{
|
||||||
@ -79,9 +79,9 @@ function useItemsManager(initialItems: Item[]): {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (result) {
|
if (result) {
|
||||||
result.map(item => {
|
result.map(item => {
|
||||||
dispatch({ type: "ADD", item: { ...item, layer: layer } });
|
dispatch({ type: "ADD", item: { ...item, layer: layer } });
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { useCallback, useReducer, createContext, useContext } from "react";
|
import { useCallback, useReducer, createContext, useContext, useState } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Item, ItemsApi, Tag } from "../../../types";
|
import { Item, ItemsApi, Tag } from "../../../types";
|
||||||
import { hashTagRegex } from "../../../Utils/HashTagRegex";
|
import { hashTagRegex } from "../../../Utils/HashTagRegex";
|
||||||
|
import { getValue } from "../../../Utils/GetValue";
|
||||||
|
|
||||||
type ActionType =
|
type ActionType =
|
||||||
| { type: "ADD"; tag: Tag }
|
| { type: "ADD"; tag: Tag }
|
||||||
@ -15,7 +16,8 @@ const TagContext = createContext<UseTagManagerResult>({
|
|||||||
removeTag: () => { },
|
removeTag: () => { },
|
||||||
setTagApi: () => { },
|
setTagApi: () => { },
|
||||||
setTagData: () => { },
|
setTagData: () => { },
|
||||||
getItemTags: () => []
|
getItemTags: () => [],
|
||||||
|
allTagsLoaded: false
|
||||||
});
|
});
|
||||||
|
|
||||||
function useTagsManager(initialTags: Tag[]): {
|
function useTagsManager(initialTags: Tag[]): {
|
||||||
@ -25,7 +27,11 @@ function useTagsManager(initialTags: Tag[]): {
|
|||||||
setTagApi: (api: ItemsApi<Tag>) => void;
|
setTagApi: (api: ItemsApi<Tag>) => void;
|
||||||
setTagData: (data: Tag[]) => void;
|
setTagData: (data: Tag[]) => void;
|
||||||
getItemTags: (item: Item) => Tag[];
|
getItemTags: (item: Item) => Tag[];
|
||||||
|
allTagsLoaded: boolean
|
||||||
} {
|
} {
|
||||||
|
|
||||||
|
const [allTagsLoaded, setallTagsLoaded] = useState<boolean>(false);
|
||||||
|
|
||||||
const [tags, dispatch] = useReducer((state: Tag[], action: ActionType) => {
|
const [tags, dispatch] = useReducer((state: Tag[], action: ActionType) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "ADD":
|
case "ADD":
|
||||||
@ -34,7 +40,7 @@ function useTagsManager(initialTags: Tag[]): {
|
|||||||
);
|
);
|
||||||
if (!exist) return [
|
if (!exist) return [
|
||||||
...state,
|
...state,
|
||||||
{...action.tag, id: action.tag.id.toLocaleLowerCase()}
|
{ ...action.tag, id: action.tag.id.toLocaleLowerCase() }
|
||||||
];
|
];
|
||||||
else return state;
|
else return state;
|
||||||
|
|
||||||
@ -55,6 +61,7 @@ function useTagsManager(initialTags: Tag[]): {
|
|||||||
tag.id = tag.id.toLocaleLowerCase();
|
tag.id = tag.id.toLocaleLowerCase();
|
||||||
dispatch({ type: "ADD", tag })
|
dispatch({ type: "ADD", tag })
|
||||||
})
|
})
|
||||||
|
setallTagsLoaded(true);
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -70,7 +77,6 @@ function useTagsManager(initialTags: Tag[]): {
|
|||||||
type: "ADD",
|
type: "ADD",
|
||||||
tag,
|
tag,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!tags.some((t) => t.id.toLocaleLowerCase() === tag.id.toLocaleLowerCase())) {
|
if (!tags.some((t) => t.id.toLocaleLowerCase() === tag.id.toLocaleLowerCase())) {
|
||||||
api?.createItem && api.createItem(tag);
|
api?.createItem && api.createItem(tag);
|
||||||
}
|
}
|
||||||
@ -84,19 +90,20 @@ function useTagsManager(initialTags: Tag[]): {
|
|||||||
api?.deleteItem && api.deleteItem(id);
|
api?.deleteItem && api.deleteItem(id);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getItemTags = useCallback((item: Item) => {
|
const getItemTags = useCallback((item: Item) => {
|
||||||
const itemTagStrings = item.text.toLocaleLowerCase().match(hashTagRegex);
|
const text = item?.layer?.itemTextField && item ? getValue(item, item.layer?.itemTextField) : undefined;
|
||||||
const itemTags: Tag[] = [];
|
const itemTagStrings = text.toLocaleLowerCase().match(hashTagRegex);
|
||||||
itemTagStrings?.map(tag => {
|
const itemTags: Tag[] = [];
|
||||||
if (tags.find(t => t.id === tag.slice(1))) {
|
itemTagStrings?.map(tag => {
|
||||||
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 itemTags
|
})
|
||||||
|
return itemTags
|
||||||
}, [tags]);
|
}, [tags]);
|
||||||
|
|
||||||
|
|
||||||
return { tags, addTag, removeTag, setTagApi, setTagData, getItemTags };
|
return { tags, addTag, removeTag, setTagApi, setTagData, getItemTags, allTagsLoaded };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TagsProvider: React.FunctionComponent<{
|
export const TagsProvider: React.FunctionComponent<{
|
||||||
@ -136,4 +143,9 @@ export const useSetTagData = (): UseTagManagerResult["setTagData"] => {
|
|||||||
export const useGetItemTags = (): UseTagManagerResult["getItemTags"] => {
|
export const useGetItemTags = (): UseTagManagerResult["getItemTags"] => {
|
||||||
const { getItemTags } = useContext(TagContext);
|
const { getItemTags } = useContext(TagContext);
|
||||||
return getItemTags;
|
return getItemTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAllTagsLoaded = (): UseTagManagerResult["allTagsLoaded"] => {
|
||||||
|
const { allTagsLoaded } = useContext(TagContext);
|
||||||
|
return allTagsLoaded;
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
export function getValue(obj, path) {
|
export function getValue(obj, path) {
|
||||||
if (obj) {
|
if (obj) {
|
||||||
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++) {
|
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++) {
|
||||||
obj = obj[path[i]];
|
if(obj) obj = obj[path[i]];
|
||||||
};
|
};
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,11 +21,14 @@ export interface LayerProps {
|
|||||||
markerShape: string,
|
markerShape: string,
|
||||||
markerDefaultColor: string,
|
markerDefaultColor: string,
|
||||||
api?: ItemsApi<any>,
|
api?: ItemsApi<any>,
|
||||||
itemTitleField?: string,
|
itemNameField?: string,
|
||||||
itemTextField?: string,
|
itemTextField?: string,
|
||||||
itemAvatarField?: string,
|
itemAvatarField?: string,
|
||||||
itemColorField?: string,
|
itemColorField?: string,
|
||||||
itemOwnerField?: string,
|
itemOwnerField?: string,
|
||||||
|
itemTagField?: string,
|
||||||
|
itemLatitudeField?: any,
|
||||||
|
itemLongitudeField?: any,
|
||||||
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
|
setItemFormPopup?: React.Dispatch<React.SetStateAction<ItemFormPopupProps | null>>,
|
||||||
itemFormPopup?: ItemFormPopupProps | null,
|
itemFormPopup?: ItemFormPopupProps | null,
|
||||||
clusterRef?: React.MutableRefObject<any>
|
clusterRef?: React.MutableRefObject<any>
|
||||||
@ -41,7 +44,7 @@ export class Item {
|
|||||||
start?: string;
|
start?: string;
|
||||||
end?: string;
|
end?: string;
|
||||||
api?: ItemsApi<any>;
|
api?: ItemsApi<any>;
|
||||||
tags?: Tag[];
|
tags?: string[];
|
||||||
layer?: LayerProps;
|
layer?: LayerProps;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
constructor(id:string,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi<any>){
|
constructor(id:string,name:string,text:string,position:Geometry, layer?: LayerProps, api?: ItemsApi<any>){
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user