basic textbased #tags

This commit is contained in:
Anton 2023-07-21 19:29:13 +02:00
parent 8bbb6bd1e2
commit bd57c87e95
6 changed files with 83 additions and 41 deletions

View File

@ -15,16 +15,32 @@ export const Layer = (props: LayerProps) => {
const tags = useTags(); const tags = useTags();
console.log(tags);
// create a JS-Map with all Tags // create a JS-Map with all Tags
const tagMap = new Map(tags?.map(key => [key.id, key])); const tagMap = new Map(tags?.map(key => [key.id, key]));
console.log(tagMap);
// returns all tags for passed item // returns all tags for passed item
const getTags = (item: Item) => { const getTags = (item: Item) => {
console.log(item.text);
const regex = /(^|\B)#(?![0-9_]+\b)([a-zA-Z0-9_]{1,30})(\b|\r)/g;
const strings = item.text.match(regex);
console.log(strings);
const tags: Tag[] = []; const tags: Tag[] = [];
item.tags && item.tags.forEach(element => { strings?.map(tag => {
if (tagMap.has(element)) { tags.push(tagMap.get(element)!) } console.log(tag.slice(1));
});
if (tagMap.has(tag.slice(1))) { tags.push(tagMap.get(tag.slice(1))!) }
})
console.log(tags);
return tags; return tags;
}; };
@ -50,12 +66,12 @@ export const Layer = (props: LayerProps) => {
if (result.data) { if (result.data) {
result.data.map(item => { result.data.map(item => {
if (item.position) { if (item.position) {
addItem(({layer: props, api: props.api, ...item})); addItem(({ layer: props, api: props.api, ...item }));
} }
}); });
} }
}) })
if(props.api || props.api) { if (props.api || props.api) {
addLayer(props); addLayer(props);
} }
@ -84,7 +100,7 @@ export const Layer = (props: LayerProps) => {
} }
{props.children} {props.children}
{props.itemFormPopup && props.itemFormPopup.layer.name == props.name && {props.itemFormPopup && props.itemFormPopup.layer.name == props.name &&
<ItemFormPopup position={props.itemFormPopup.position} layer={props.itemFormPopup.layer} setItemFormPopup={setItemFormPopup} item={props.itemFormPopup.item} api={props.api}/> <ItemFormPopup position={props.itemFormPopup.position} layer={props.itemFormPopup.layer} setItemFormPopup={setItemFormPopup} item={props.itemFormPopup.item} api={props.api} />
} }
</> </>
) )

View File

@ -6,9 +6,6 @@ import { useLayers } from '../hooks/useLayers'
export default function AddButton({setSelectMode} : {setSelectMode: React.Dispatch<React.SetStateAction<any>>}) { export default function AddButton({setSelectMode} : {setSelectMode: React.Dispatch<React.SetStateAction<any>>}) {
const layers = useLayers(); const layers = useLayers();
console.log(layers);
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" >

View File

@ -5,6 +5,8 @@ import { Item, Tag } from '../../../types'
import { replaceURLs } from '../../../Utils/ReplaceURLs' import { replaceURLs } from '../../../Utils/ReplaceURLs'
import { useRemoveItem } from '../hooks/useItems' import { useRemoveItem } from '../hooks/useItems'
import { ItemFormPopupProps } from './ItemFormPopup' import { ItemFormPopupProps } from './ItemFormPopup'
import { heighlightTags } from '../../../Utils/HeighlightTags'
import { useTags } from '../hooks/useTags'
export interface ItemViewPopupProps { export interface ItemViewPopupProps {
item: Item, item: Item,
@ -13,12 +15,17 @@ export interface ItemViewPopupProps {
} }
const ItemViewPopup = (props: ItemViewPopupProps) => { const ItemViewPopup = (props: ItemViewPopupProps) => {
const item: Item = props.item; const item: Item = props.item;
const tags: Tag[] = props.tags; const tags: Tag[] = props.tags;
const removeItem = useRemoveItem(); const removeItem = useRemoveItem();
const map = useMap(); const map = useMap();
const all_tags = useTags();
const removeItemFromMap = (event: React.MouseEvent<HTMLElement>) => { const removeItemFromMap = (event: React.MouseEvent<HTMLElement>) => {
props.item.api?.deleteItem!(props.item.id) props.item.api?.deleteItem!(props.item.id)
.then( () => removeItem(item)) .then( () => removeItem(item))
@ -44,15 +51,15 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
</div> </div>
<div className='tw-col-span-1'> <div className='tw-col-span-1'>
{item.api && {item.api &&
<div className="tw-dropdown tw-dropdown-bottom"> <div className="tw-dropdown tw-dropdown-right">
<label tabIndex={0} className="tw-btn tw-m-1 tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700 tw-leading-3 tw-border-none"> <label tabIndex={0} className="tw-btn tw-m-1 tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700 tw-leading-3 tw-border-none tw-min-h-0 tw-h-4">
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" /> <path d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" />
</svg> </svg>
</label> </label>
<ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box"> <ul tabIndex={0} className="tw-dropdown-content tw-menu tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box">
{item.api.updateItem && <li> {item.api.updateItem && <li>
<a className='tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700' onClick={openEditPopup}> <a className='tw-bg-white hover:tw-bg-slate-200 !tw-text-blue-800 hover:tw-text-gray-700' onClick={openEditPopup}>
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" /> <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
</svg> </svg>
@ -60,7 +67,7 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
</li>} </li>}
{item.api.deleteItem && <li> {item.api.deleteItem && <li>
<a className='tw-bg-white hover:tw-bg-white tw-text-gray-500 hover:tw-text-gray-700' onClick={removeItemFromMap}> <a className='tw-bg-white hover:tw-bg-slate-200 !tw-text-red-800 hover:tw-text-red-950' onClick={removeItemFromMap}>
<svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="tw-h-5 tw-w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" /> <path fillRule="evenodd" d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z" clipRule="evenodd" />
</svg> </svg>
@ -91,12 +98,12 @@ const ItemViewPopup = (props: ItemViewPopupProps) => {
</div> </div>
} }
<p style={{ whiteSpace: "pre-wrap" }} dangerouslySetInnerHTML={{ __html: replaceURLs(item.text) }} /> <p style={{ whiteSpace: "pre-wrap" }} dangerouslySetInnerHTML={{ __html: replaceURLs(heighlightTags(item.text, all_tags)) }} />
<p> <p>
{item.tags && {item.tags &&
tags.map((tag: Tag) => ( tags.map((tag: Tag) => (
<span className="" style={{ fontWeight: "bold", display: "inline-block", color: "#fff", padding: ".3rem", borderRadius: ".5rem", backgroundColor: tag.color, margin: '.2rem', fontSize: "100%" }} key={tag.id}>#{tag.name}</span> <span className="" style={{ fontWeight: "bold", display: "inline-block", color: "#fff", padding: ".3rem", borderRadius: ".5rem", backgroundColor: tag.color, margin: '.2rem', fontSize: "100%" }} key={tag.id}>#{tag.id}</span>
)) ))
} }
</p> </p>

View File

@ -3,34 +3,39 @@ 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 }
| { type: "REMOVE"; id: number }; | { type: "REMOVE"; id: string };
type UseTagManagerResult = ReturnType<typeof useTagsManager>; type UseTagManagerResult = ReturnType<typeof useTagsManager>;
const TagContext = createContext<UseTagManagerResult>({ const TagContext = createContext<UseTagManagerResult>({
tags: [], tags: [],
addTag: () => {}, addTag: () => { },
removeTag: () => {} removeTag: () => { }
}); });
function useTagsManager (initialTags: Tag[]): { function useTagsManager(initialTags: Tag[]): {
tags: Tag[]; tags: Tag[];
addTag: (tag: Tag) => void; addTag: (tag: Tag) => void;
removeTag: (id: number) => void; removeTag: (id: string) => void;
} { } {
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":
return [ const exist = state.find((tag) =>
...state, tag.id === action.tag.id ? true : false
action.tag, );
]; if (!exist) return [
case "REMOVE": ...state,
return state.filter(({ id }) => id !== action.id); action.tag,
default: ];
throw new Error(); else return state;
}
case "REMOVE":
return state.filter(({ id }) => id !== action.id);
default:
throw new Error();
}
}, initialTags); }, initialTags);
const addTag = useCallback((tag: Tag) => { const addTag = useCallback((tag: Tag) => {
@ -40,7 +45,7 @@ function useTagsManager (initialTags: Tag[]): {
}); });
}, []); }, []);
const removeTag = useCallback((id: number) => { const removeTag = useCallback((id: string) => {
dispatch({ dispatch({
type: "REMOVE", type: "REMOVE",
id, id,
@ -50,7 +55,7 @@ function useTagsManager (initialTags: Tag[]): {
} }
export const TagsProvider: React.FunctionComponent<{ export const TagsProvider: React.FunctionComponent<{
initialTags: Tag[], children?: React.ReactNode initialTags: Tag[], children?: React.ReactNode
}> = ({ initialTags, children }) => ( }> = ({ initialTags, children }) => (
<TagContext.Provider value={useTagsManager(initialTags)}> <TagContext.Provider value={useTagsManager(initialTags)}>
{children} {children}

View File

@ -0,0 +1,18 @@
import { Tag } from "../types";
export function heighlightTags(message: string, tags: Tag[]): string {
if (!message) return "";
const hashTagRegex = /(^|\B)#(?![0-9_]+\b)([a-zA-Z0-9_]{1,30})(\b|\r)/g;
message = message.replace(hashTagRegex, function (string) {
console.log(string);
const tag = tags.find(t => t.id == string.slice(1))
return `<span style="background-color: ${tag ? tag.color : '#aaa' };padding: 0px 5px;border-radius: 7px;cursor: pointer;color:#fff">` + string + '</span>'
});
return message;
}

View File

@ -59,8 +59,7 @@ export class Geometry {
export interface Tag { export interface Tag {
color: string; color: string;
id: string | number; id: string;
name: string;
} }
export interface ItemsApi<T> { export interface ItemsApi<T> {