diff --git a/src/Components/Input/Autocomplete.tsx b/src/Components/Input/Autocomplete.tsx new file mode 100644 index 00000000..7f2843b4 --- /dev/null +++ b/src/Components/Input/Autocomplete.tsx @@ -0,0 +1,88 @@ +import * as React from 'react' +import { useEffect } from 'react'; + +export const Autocomplete = ({ inputProps, suggestions, onSelected, pushFilteredSuggestions, setFocus }: { inputProps: any, suggestions: Array, onSelected: (suggestion) => void, pushFilteredSuggestions?: Array, setFocus?: boolean }) => { + + const [filteredSuggestions, setFilteredSuggestions] = React.useState>([]); + const [heighlightedSuggestion, setHeighlightedSuggestion] = React.useState(0); + + + useEffect(() => { + pushFilteredSuggestions && setFilteredSuggestions(pushFilteredSuggestions) + }, [pushFilteredSuggestions]) + + useEffect(() => { + setFocus && inputRef.current?.focus(); + }, [setFocus]) + + const inputRef = React.useRef(); + + + const getSuggestionValue = suggestion => suggestion.name; + + // Use your imagination to render suggestions. + const renderSuggestion = suggestion => ( +
+
+
#{suggestion.name} +
+ ); + + const getSuggestions = value => { + const inputValue = value.trim().toLowerCase(); + const inputLength = inputValue.length; + + return inputLength === 0 ? [] : suggestions.filter(tag => + tag.name.toLowerCase().slice(0, inputLength) === inputValue + ); + }; + + const handleChange = (e) => { + setFilteredSuggestions(getSuggestions(e.target.value)) + + // Call the parent's onChange handler, if it exists + if (inputProps.onChange) { + inputProps.onChange(e); + } + }; + + + function handleSuggestionClick(suggestion) { + onSelected(suggestion) + } + + const handleKeyDown = (event) => { + console.log(filteredSuggestions); + + switch (event.key) { + case 'ArrowDown': + heighlightedSuggestion < filteredSuggestions.length-1 && setHeighlightedSuggestion(current => current +1) + break; + case 'ArrowUp': + heighlightedSuggestion>0 && setHeighlightedSuggestion(current => current -1) + break; + case 'Enter': + if(filteredSuggestions.length > 0) { + onSelected(filteredSuggestions[heighlightedSuggestion]); + setHeighlightedSuggestion(0); + } + filteredSuggestions.length == 0 && inputProps.onKeyDown(event); + break; + default: + inputProps.onKeyDown(event); + break; + } + + } + + return ( +
+ handleChange(e)} onKeyDown={handleKeyDown}/> +
    + {filteredSuggestions.map((suggestion, index) => ( +
  • handleSuggestionClick(suggestion)}>{renderSuggestion(suggestion)}{index == heighlightedSuggestion && "+"}
  • + ))} +
+
+ ) +} diff --git a/src/Components/Profile/Editor.tsx b/src/Components/Profile/Editor.tsx index bbf0bac9..a3ce1733 100644 --- a/src/Components/Profile/Editor.tsx +++ b/src/Components/Profile/Editor.tsx @@ -1,42 +1,14 @@ import * as React from "react"; -import MDEditor from '@uiw/react-md-editor'; -import rehypeSanitize from "rehype-sanitize"; -import { useEffect } from "react"; export function TextEditor({ value, updateFormValue }: { value: string, updateFormValue: (string) => void }) { - useEffect(() => { - setHeightFromSourceToTarget("wmde-markdown-color","w-md-editor-text-input"); - }, [value]) - + console.log(value); + console.log(updateFormValue); + return (
- updateFormValue(val)} - preview="edit" - height="100%" - previewOptions={{ - rehypePlugins: [[rehypeSanitize]], - }} - />
); } - -// TypeScript -const setHeightFromSourceToTarget = (sourceClassName: string, targetClassName: string): void => { - // Select the source element and get its height - const sourceElement = document.querySelector(`.${sourceClassName}`) as HTMLElement; - const height = sourceElement ? (sourceElement.clientHeight +10) + 'px' : '0'; // Convert to string to use in style - - // Select all target elements - const targetElements = document.querySelectorAll(`.${targetClassName}`); - - // Set the height of all target elements to match the source element's height - targetElements.forEach((element) => { - (element as HTMLElement).style.height = height; - }); -} \ No newline at end of file diff --git a/src/Components/Profile/OverlayProfileSettings.tsx b/src/Components/Profile/OverlayProfileSettings.tsx index 2c40a922..672e50e7 100644 --- a/src/Components/Profile/OverlayProfileSettings.tsx +++ b/src/Components/Profile/OverlayProfileSettings.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { useItems, useUpdateItem } from '../Map/hooks/useItems' -import { useState } from 'react'; +import { useState } from 'react'; import { getValue } from '../../Utils/GetValue'; import ReactCrop, { Crop, centerCrop, makeAspectCrop } from 'react-image-crop'; import { toast } from 'react-toastify'; @@ -15,6 +15,7 @@ import { randomColor } from '../../Utils/RandomColor'; import { useNavigate } from 'react-router-dom'; import { UserItem } from '../../types'; import { MapOverlayPage } from '../Templates'; +import { TagsWidget } from './TagsWidget'; export function OverlayProfileSettings() { @@ -215,18 +216,24 @@ export function OverlayProfileSettings() {
setActiveTab(1)} />
- setText(v)} containerStyle='tw-h-full' inputStyle='tw-h-full'/> + setText(v)} containerStyle='tw-h-full' inputStyle='tw-h-full tw-border-t-0' />
setActiveTab(2)} /> -
- - - +
+
+
+ +
+
+ +
+
setActiveTab(3)} /> -
+
+
@@ -234,13 +241,14 @@ export function OverlayProfileSettings() { +
+
-
{ + + const [input, setInput] = useState(''); + const [localTags, setLocalTags] = useState>([]); + const [isKeyReleased, setIsKeyReleased] = useState(false); + const tags = useTags(); + const [pushFilteredSuggestions, setPushFilteredSuggestions] = useState>([]); + + const [focusInput, setFocusInput] = useState(false); + + + const onChange = (e) => { + const { value } = e.target; + setInput(value); + }; + + const onKeyDown = (e) => { + const { key } = e; + const trimmedInput = input.trim(); + + if ((key === 'Enter' || key === ',' ) && trimmedInput.length && !localTags.some(tag => tag.name.toLocaleLowerCase() === trimmedInput.toLocaleLowerCase())) { + e.preventDefault(); + const newTag = tags.find(t => t.name === trimmedInput.toLocaleLowerCase()) + newTag && setLocalTags(prevState => [...prevState, newTag]); + !newTag && setLocalTags(prevState => [...prevState, { id: crypto.randomUUID(), name: trimmedInput.toLocaleLowerCase(), color: randomColor() }]); + setInput(''); + setPushFilteredSuggestions([]); + } + + if (key === "Backspace" && !input.length && localTags.length && isKeyReleased) { + const localTagsCopy = [...localTags]; + const poppedTag = localTagsCopy.pop(); + e.preventDefault(); + setLocalTags(localTagsCopy); + poppedTag && setInput(poppedTag.name); + } + + setIsKeyReleased(false); + }; + + const onKeyUp = () => { + setIsKeyReleased(true); + } + + const deleteTag = (tag) => { + setLocalTags(prevState => prevState.filter((t) => t !== tag)) + } + + + const onSelected = (tag) => { + if(!localTags.some(t => t.name.toLocaleLowerCase() === tag.name.toLocaleLowerCase())) { + const newTag = tags.find(t => t.name === tag.name.toLocaleLowerCase()) + newTag && setLocalTags(prevState => [...prevState, newTag]); + !newTag && setLocalTags(prevState => [...prevState, { id: crypto.randomUUID(), name: tag.name.toLocaleLowerCase(), color: randomColor() }]); + setInput(''); + setPushFilteredSuggestions([]); + } + } + + const inputProps = { + value: input, + placeholder: placeholder, + onKeyDown: onKeyDown, + onKeyUp: onKeyUp, + onChange: onChange, + className: 'tw-bg-transparent tw-w-fit tw-mt-5 tw-h-fit' + } + + return ( +
{ + setFocusInput(true); + setTimeout(()=> { + setFocusInput(false) + }, 200) + }} className={`tw-input tw-input-bordered tw-cursor-text ${containerStyle}`}> +
+ {localTags.map((tag) => ( +
+
+ +
#{tag.name} +
+ + ))} + onSelected(tag)}/> +
+
+ ) +} \ No newline at end of file diff --git a/src/index.css b/src/index.css index 07fb153e..329ba0f8 100644 --- a/src/index.css +++ b/src/index.css @@ -35,6 +35,14 @@ color: theme('colors.base-content'); } +.Toastify__toast-container { + z-index: 1999 !important; +} + +.Toastify__toast-container--top-right { + top: 4.75em !important; +} + :root { --toastify-color-info: theme('colors.info'); @@ -67,11 +75,3 @@ input[type="file"] { .tw-tab-content .container { height: 100%; } - -.w-md-editor-text { - height: 100%; -} - -.w-md-editor-text-input { - min-height: 100%; -} \ No newline at end of file