mirror of
https://github.com/cssgunc/compass.git
synced 2025-04-10 06:10:17 -04:00
Edited the dropdown action
This commit is contained in:
parent
a050d58e91
commit
167fa009ae
|
@ -21,17 +21,17 @@ type User = {
|
||||||
id: number;
|
id: number;
|
||||||
created_at: any;
|
created_at: any;
|
||||||
username: string;
|
username: string;
|
||||||
role: "ADMIN" | "EMPLOYEE" | "VOLUNTEER";
|
role: "administrator" | "employee" | "volunteer";
|
||||||
email: string;
|
email: string;
|
||||||
program: "DOMESTIC" | "ECONOMIC" | "COMMUNITY";
|
program: "domestic" | "economic" | "community";
|
||||||
experience: number;
|
experience: number;
|
||||||
group?: string;
|
group?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const Table = () => {
|
export const Table = () => {
|
||||||
const columnHelper = createColumnHelper<User>();
|
const columnHelper = createColumnHelper<User>();
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
columnHelper.display({
|
columnHelper.display({
|
||||||
id: "options",
|
id: "options",
|
||||||
|
|
10
compass/components/TagsInput/CreateNewTagAction.tsx
Normal file
10
compass/components/TagsInput/CreateNewTagAction.tsx
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Tag } from "./Tag"
|
||||||
|
|
||||||
|
export const CreateNewTagAction = ({ input }) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row space-x-2 hover:bg-gray-100 rounded-md py-2 p-2 items-center">
|
||||||
|
<p className="capitalize">Create</p>
|
||||||
|
<Tag active={false} onDelete={null} >{input}</Tag>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,51 +1,48 @@
|
||||||
import { EllipsisHorizontalIcon, TrashIcon } from "@heroicons/react/24/solid";
|
import { EllipsisHorizontalIcon, TrashIcon } from "@heroicons/react/24/solid";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const DropdownAction = ({ tag, handleDeleteTag, handleAddTag }) => {
|
export const DropdownAction = ({ tag, handleDeleteTag, handleEditTag }) => {
|
||||||
const [isVisible, setVisible] = useState(false);
|
const [isVisible, setVisible] = useState(false);
|
||||||
const [inputValue, setInputValue] = useState(tag);
|
const [inputValue, setInputValue] = useState(tag);
|
||||||
|
|
||||||
const handleBlur = () => {
|
|
||||||
setVisible(false);
|
const editTagOption = (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
handleEditTag(tag, inputValue)
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const editTagOption = () => {
|
|
||||||
handleDeleteTag(tag)
|
|
||||||
handleAddTag(inputValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
setInputValue(e.target.value);
|
setInputValue(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
|
|
||||||
event.stopPropagation(); // This stops the click event from propagating
|
|
||||||
setVisible(!isVisible); // Toggle visibility
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{isVisible ? (
|
<EllipsisHorizontalIcon
|
||||||
|
className="w-5 text-gray-500"
|
||||||
|
onClick={() => setVisible(!isVisible)}
|
||||||
|
/>
|
||||||
|
{isVisible && (
|
||||||
<div className="absolute flex flex-col justify-start z-50 rounded-md bg-white border border-gray-200 shadow p-2 space-y-2">
|
<div className="absolute flex flex-col justify-start z-50 rounded-md bg-white border border-gray-200 shadow p-2 space-y-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
onKeyDown={editTagOption}
|
onKeyDown={editTagOption}
|
||||||
onBlur={handleBlur}
|
|
||||||
autoFocus
|
autoFocus
|
||||||
className="bg-gray-50 text-2xs focus:outline-none rounded-md font-normal text-gray-800 p-1 border-2 focus:border-blue-200"
|
className="bg-gray-50 text-2xs focus:outline-none rounded-md font-normal text-gray-800 p-1 border-2 focus:border-blue-200"
|
||||||
/>
|
/>
|
||||||
<button onClick={() => handleDeleteTag(inputValue)} className="justify-start flex flex-row space-x-4 hover:bg-gray-100 rounded-md items-center p-1 px-2">
|
<button onClick={() => {
|
||||||
|
handleDeleteTag(inputValue)
|
||||||
|
setVisible(false)
|
||||||
|
}} className="justify-start flex flex-row space-x-4 hover:bg-gray-100 rounded-md items-center p-2 px-2">
|
||||||
<TrashIcon className="w-3 h-3" />
|
<TrashIcon className="w-3 h-3" />
|
||||||
<p>Delete</p>
|
<p>Delete</p>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<EllipsisHorizontalIcon
|
|
||||||
className="w-5 text-gray-500"
|
|
||||||
onClick={handleClick} // Attach the click event handler here
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
import 'tailwindcss/tailwind.css';
|
import "tailwindcss/tailwind.css";
|
||||||
import { TagsArray } from './TagsArray'; // Corrected path assumption
|
import { TagsArray } from "./TagsArray";
|
||||||
import { TagDropdown } from './TagDropdown';
|
import { TagDropdown } from "./TagDropdown";
|
||||||
|
import { CreateNewTagAction } from "./CreateNewTagAction";
|
||||||
|
|
||||||
interface TagsInputProps {
|
interface TagsInputProps {
|
||||||
presetOptions: string[];
|
presetOptions: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagsInput: React.FC<TagsInputProps> = ({ presetValue, presetOptions }) => {
|
const TagsInput: React.FC<TagsInputProps> = ({
|
||||||
const [inputValue, setInputValue] = useState('');
|
presetValue,
|
||||||
|
presetOptions,
|
||||||
|
}) => {
|
||||||
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [cellSelected, setCellSelected] = useState(false);
|
const [cellSelected, setCellSelected] = useState(false);
|
||||||
const [tags, setTags] = useState<Set<string>>(new Set(presetValue ? [presetValue] : []));
|
const [tags, setTags] = useState<Set<string>>(
|
||||||
|
new Set(presetValue ? [presetValue] : [])
|
||||||
|
);
|
||||||
const [options, setOptions] = useState<Set<string>>(new Set(presetOptions));
|
const [options, setOptions] = useState<Set<string>>(new Set(presetOptions));
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
@ -18,31 +24,63 @@ const TagsInput: React.FC<TagsInputProps> = ({ presetValue, presetOptions }) =>
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
if (e.key === 'Enter' && inputValue.trim()) {
|
if (e.key === "Enter" && inputValue.trim()) {
|
||||||
// To add to a Set, you need to create a new Set for immutability in React state updates.
|
setTags((prevTags) => new Set(prevTags).add(inputValue));
|
||||||
setTags(prevTags => new Set(prevTags).add(inputValue));
|
setOptions((prevOptions) => new Set(prevOptions).add(inputValue));
|
||||||
setOptions(prevOptions => new Set(prevOptions).add(inputValue));
|
setInputValue("");
|
||||||
setInputValue('');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectTag = (tagToAdd: string) => {
|
const handleSelectTag = (tagToAdd: string) => {
|
||||||
if (!tags.has(tagToAdd)){ // Corrected syntax for checking if a Set contains an item
|
if (!tags.has(tagToAdd)) {
|
||||||
setTags(prevTags => new Set(prevTags).add(tagToAdd));
|
// Corrected syntax for checking if a Set contains an item
|
||||||
|
setTags((prevTags) => new Set(prevTags).add(tagToAdd));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleDeleteTag = (tagToDelete: string) => {
|
const handleDeleteTag = (tagToDelete: string) => {
|
||||||
setTags(prevTags => {
|
setTags((prevTags) => {
|
||||||
const updatedTags = new Set(prevTags);
|
const updatedTags = new Set(prevTags);
|
||||||
updatedTags.delete(tagToDelete);
|
updatedTags.delete(tagToDelete);
|
||||||
return updatedTags;
|
return updatedTags;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const handleBlur = () => {
|
|
||||||
setCellSelected(false);
|
const handleDeleteTagOption = (tagToDelete: string) => {
|
||||||
|
setOptions((prevOptions) => {
|
||||||
|
const updatedOptions = new Set(prevOptions);
|
||||||
|
updatedOptions.delete(tagToDelete);
|
||||||
|
return updatedOptions;
|
||||||
|
});
|
||||||
|
if (tags.has(tagToDelete)) {
|
||||||
|
handleDeleteTag(tagToDelete);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEditTag = (oldTag: string, newTag: string) => {
|
||||||
|
if (oldTag !== newTag) {
|
||||||
|
setTags((prevTags) => {
|
||||||
|
const tagsArray = Array.from(prevTags);
|
||||||
|
const oldTagIndex = tagsArray.indexOf(oldTag);
|
||||||
|
if (oldTagIndex !== -1) {
|
||||||
|
tagsArray.splice(oldTagIndex, 1, newTag);
|
||||||
|
}
|
||||||
|
return new Set(tagsArray);
|
||||||
|
});
|
||||||
|
|
||||||
|
setOptions((prevOptions) => {
|
||||||
|
const optionsArray = Array.from(prevOptions);
|
||||||
|
const oldTagIndex = optionsArray.indexOf(oldTag);
|
||||||
|
if (oldTagIndex !== -1) {
|
||||||
|
optionsArray.splice(oldTagIndex, 1, newTag);
|
||||||
|
}
|
||||||
|
return new Set(optionsArray);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cursor-pointer" onClick={() => setCellSelected(true)}>
|
<div className="cursor-pointer" onClick={() => setCellSelected(true)}>
|
||||||
{!cellSelected ? (
|
{!cellSelected ? (
|
||||||
|
@ -50,7 +88,7 @@ const TagsInput: React.FC<TagsInputProps> = ({ presetValue, presetOptions }) =>
|
||||||
) : (
|
) : (
|
||||||
<div className="absolute w-64 z-50 -ml-3 -mt-7">
|
<div className="absolute w-64 z-50 -ml-3 -mt-7">
|
||||||
<div className="rounded-md border border-gray-200 shadow">
|
<div className="rounded-md border border-gray-200 shadow">
|
||||||
<div className="flex flex-wrap rounded-t-md items-center gap-2 bg-gray-50 p-2">
|
<div className="flex flex-wrap rounded-t-md items-center gap-2 bg-gray-50 p-2">
|
||||||
<TagsArray handleDelete={handleDeleteTag} active tags={tags} />
|
<TagsArray handleDelete={handleDeleteTag} active tags={tags} />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -63,8 +101,16 @@ const TagsInput: React.FC<TagsInputProps> = ({ presetValue, presetOptions }) =>
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex rounded-b-md bg-white flex-col border-t border-gray-100 text-2xs font-medium text-gray-500 p-2">
|
<div className="flex rounded-b-md bg-white flex-col border-t border-gray-100 text-2xs font-medium text-gray-500 p-2">
|
||||||
Select an option or create one
|
<p className="capitalize">Select an option or create one</p>
|
||||||
<TagDropdown handleAdd={handleSelectTag} tags={options} />
|
<TagDropdown
|
||||||
|
handleDeleteTag={handleDeleteTagOption}
|
||||||
|
handleEditTag={handleEditTag}
|
||||||
|
handleAdd={handleSelectTag}
|
||||||
|
tags={options}
|
||||||
|
/>
|
||||||
|
{inputValue.length > 0 && (
|
||||||
|
<CreateNewTagAction input={inputValue} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
import { XMarkIcon } from "@heroicons/react/24/solid"
|
import { XMarkIcon } from "@heroicons/react/24/solid";
|
||||||
|
|
||||||
export const Tag = ({children, handleDelete, active = false}) => {
|
export const Tag = ({ children, handleDelete, active = false }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="font-normal text-gray-800 flex flex-row p-1 px-2 rounded-lg bg-blue-100">{children}
|
<span className={`font-normal bg-cyan-100 text-gray-800 flex flex-row p-1 px-2 rounded-lg`}>
|
||||||
{active && <button className="hover:bg-gray-200" onClick={() => handleDelete(children)}><XMarkIcon className=" ml-1 w-3 text-blue-500" /></button>}
|
{children}
|
||||||
</span>
|
{active && handleDelete && (
|
||||||
)
|
<button onClick={() => handleDelete(children)}>
|
||||||
}
|
<XMarkIcon className={`ml-1 w-3 text-cyan-500`} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
import { Tag } from "./Tag";
|
import { Tag } from "./Tag";
|
||||||
import { DropdownAction } from "./DropdownAction";
|
import { DropdownAction } from "./DropdownAction";
|
||||||
|
|
||||||
export const TagDropdown = ({ tags, handleAdd }) => {
|
export const TagDropdown = ({ tags, handleEditTag,handleDeleteTag,handleAdd }) => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col space-y-2 mt-2">
|
<div className="z-50 flex flex-col space-y-2 mt-2">
|
||||||
{Array.from(tags).map((tag, index) => (
|
{Array.from(tags).map((tag, index) => (
|
||||||
<div onClick={() => handleAdd(tag)} key={index} className="items-center rounded-md p-1 flex flex-row justify-between hover:bg-gray-100">
|
<div key={index} className="items-center rounded-md p-1 flex flex-row justify-between hover:bg-gray-100">
|
||||||
<Tag>{tag}</Tag>
|
<button onClick={() => handleAdd(tag)}><Tag >{tag}</Tag></button>
|
||||||
<DropdownAction tag={tag} />
|
<DropdownAction handleDeleteTag={handleDeleteTag} handleEditTag={handleEditTag} tag={tag} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Tag } from "./Tag"
|
import { Tag } from "./Tag"
|
||||||
|
|
||||||
export const TagsArray = ({ tags, handleDelete, active = false }) => {
|
export const TagsArray = ({ tags, handleDelete, active = false }) => {
|
||||||
|
|
||||||
return(
|
return(
|
||||||
<div className="flex flex-wrap gap-2 items-center">
|
<div className="flex flex-wrap gap-2 items-center">
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue
Block a user