Commit 6a9f84e2 by Sujeeth AV

Update Input.jsx

parent 3887296f
import React, { createContext, useState, useRef, useEffect } from 'react'; import React, { createContext, useState, useRef, useEffect, useCallback } from 'react';
import './Input.css'; import './Input.css';
import { getTodo, addTodo, delTodo, uptTodo } from '../API/Api'; import { getTodo, addTodo, delTodo, uptTodo } from '../API/Api';
import { MdCancel } from "react-icons/md"; import { MdCancel } from "react-icons/md";
import { Header } from '../Header/Header'; import { Header } from '../Header/Header';
import { Checkbox } from './Checkbox'; import { Checkbox } from './Checkbox';
import { ErrorBoundary } from '../ErrorBoundary';
export const MyTask = createContext(); export const MyTask = createContext();
const debounce = (func, delay) => {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
};
export const Input = () => { export const Input = () => {
const [task, setTask] = useState(""); const [task, setTask] = useState("");
const [store, setStore] = useState([]); const [store, setStore] = useState([]);
const [editIndex, setEditIndex] = useState(null); const [editIndex, setEditIndex] = useState(null);
const [editedTask, setEditedTask] = useState(""); const [editedTask, setEditedTask] = useState("");
const [loading, setLoading] = useState(false);
const editableRef = useRef(null); const editableRef = useRef(null);
const lastCursorPos = useRef(0); const lastCursorPos = useRef(0);
useEffect(() => { const fetchTodos = useCallback(async () => {
getTodo().then(res => setStore(res.data)); try {
setLoading(true);
const res = await getTodo();
setStore(res.data || []);
} catch (error) {
console.error("Failed to fetch todos:", error);
} finally {
setLoading(false);
}
}, []); }, []);
useEffect(() => {
fetchTodos();
}, [fetchTodos]);
const saveCursorPosition = () => { const saveCursorPosition = () => {
if (editableRef.current) { try {
const selection = window.getSelection(); if (editableRef.current) {
if (selection.rangeCount > 0) { const selection = window.getSelection();
const range = selection.getRangeAt(0); if (selection.rangeCount > 0) {
const preCaretRange = range.cloneRange(); const range = selection.getRangeAt(0);
preCaretRange.selectNodeContents(editableRef.current); const preCaretRange = range.cloneRange();
preCaretRange.setEnd(range.endContainer, range.endOffset); preCaretRange.selectNodeContents(editableRef.current);
lastCursorPos.current = preCaretRange.toString().length; preCaretRange.setEnd(range.endContainer, range.endOffset);
lastCursorPos.current = preCaretRange.toString().length;
}
} }
} catch (error) {
console.error("Error saving cursor position:", error);
} }
}; };
const restoreCursorPosition = () => { const restoreCursorPosition = () => {
if (editableRef.current) { try {
const textNode = editableRef.current.firstChild || editableRef.current; if (editableRef.current) {
const range = document.createRange(); const range = document.createRange();
const selection = window.getSelection(); const selection = window.getSelection();
let pos = 0; let found = false;
let found = false; const findPosition = (node, remainingPos) => {
const findPosition = (node, remainingPos) => { if (node.nodeType === Node.TEXT_NODE) {
if (node.nodeType === Node.TEXT_NODE) { const length = node.nodeValue.length;
const length = node.nodeValue.length; if (remainingPos <= length) {
if (remainingPos <= length) { range.setStart(node, remainingPos);
range.setStart(node, remainingPos); found = true;
found = true; return;
return; }
remainingPos -= length;
} else {
for (let i = 0; i < node.childNodes.length && !found; i++) {
findPosition(node.childNodes[i], remainingPos);
}
} }
remainingPos -= length; };
findPosition(editableRef.current, lastCursorPos.current);
if (!found) {
range.selectNodeContents(editableRef.current);
range.collapse(false);
} else { } else {
for (let i = 0; i < node.childNodes.length && !found; i++) { range.collapse(true);
findPosition(node.childNodes[i], remainingPos);
}
} }
};
findPosition(editableRef.current, lastCursorPos.current); selection.removeAllRanges();
if (!found) { selection.addRange(range);
range.selectNodeContents(editableRef.current);
range.collapse(false);
} else {
range.collapse(true);
} }
} catch (error) {
selection.removeAllRanges(); console.error("Error restoring cursor position:", error);
selection.addRange(range);
} }
}; };
useEffect(() => { useEffect(() => {
if (editableRef.current && editIndex !== null) { if (editableRef.current && editIndex !== null) {
editableRef.current.focus(); try {
setTimeout(restoreCursorPosition, 0); editableRef.current.focus();
setTimeout(restoreCursorPosition, 0);
} catch (error) {
console.error("Error focusing editable element:", error);
setEditIndex(null);
}
} }
}, [editIndex, editedTask]); }, [editIndex, editedTask]);
const Change = (e) => setTask(e.target.value); const debouncedSave = useCallback(
debounce(async (id, newTask) => {
try {
const todo = store.find(item => item.id === id);
if (todo && todo.task !== newTask) {
await uptTodo(id, {
id: todo.id,
task: newTask,
completed: todo.completed
});
}
} catch (error) {
console.error("Error saving todo:", error);
}
}, 500),
[store]
);
const Add = async () => { const handleChange = (e) => setTask(e.target.value);
if (task.trim() === '') return;
const res = await addTodo(task);
setStore([...store, res.data]);
setTask("");
};
const Delete = async (id) => { const handleEdit = (e) => {
const confirmDelete = window.confirm("Are you sure you want to delete?"); try {
if (confirmDelete) { saveCursorPosition();
await delTodo(id); const newValue = e.currentTarget.textContent;
setStore(store.filter(item => item.id !== id)); setEditedTask(newValue);
const newStore = [...store];
newStore[editIndex] = { ...newStore[editIndex], task: newValue };
setStore(newStore);
debouncedSave(store[editIndex].id, newValue);
} catch (error) {
console.error("Error during edit:", error);
setEditIndex(null);
} }
}; };
...@@ -98,80 +152,123 @@ export const Input = () => { ...@@ -98,80 +152,123 @@ export const Input = () => {
setEditIndex(index); setEditIndex(index);
setEditedTask(store[index].task); setEditedTask(store[index].task);
} catch (error) { } catch (error) {
console.error("Error in startEdit:", error); console.error("Error starting edit:", error);
setEditIndex(null); setEditIndex(null);
} }
}; };
const saveEdit = async (index) => { const endEdit = () => {
setEditIndex(null);
};
const Add = async () => {
if (task.trim() === '') return;
try {
const res = await addTodo(task);
setStore([...store, res.data]);
setTask("");
} catch (error) {
console.error("Error adding todo:", error);
}
};
const Delete = async (id) => {
const confirmDelete = window.confirm("Are you sure you want to delete?");
if (confirmDelete) {
try {
await delTodo(id);
setStore(store.filter(item => item.id !== id));
} catch (error) {
console.error("Error deleting todo:", error);
}
}
};
const toggleComplete = async (index) => {
try { try {
if (!editedTask.trim()) return;
const todo = store[index];
const updated = { ...todo, task: editedTask };
await uptTodo(todo.id, updated);
const newStore = [...store]; const newStore = [...store];
newStore[index] = updated; newStore[index] = {
...newStore[index],
completed: !newStore[index].completed
};
setStore(newStore); setStore(newStore);
setEditIndex(null);
await uptTodo(newStore[index].id, newStore[index]);
} catch (error) { } catch (error) {
console.error("Error saving edit:", error); console.error("Error toggling complete:", error);
fetchTodos();
} }
}; };
const handleEditInput = (e) => { const renderEditableContent = () => {
saveCursorPosition(); try {
setEditedTask(e.currentTarget.textContent); return (
}; <div
const toggleComplete = (index) => { ref={editableRef}
const newStore = [...store]; contentEditable
newStore[index].completed = !newStore[index].completed; suppressContentEditableWarning
setStore(newStore); onBlur={endEdit}
onInput={handleEdit}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
e.currentTarget.blur();
}
}}
className={`task ${store[editIndex]?.completed ? 'completed' : ''}`}
>
{editedTask}
</div>
);
} catch (error) {
console.error("Error rendering editable content:", error);
return (
<span
className={`task ${store[editIndex]?.completed ? 'completed' : ''}`}
onClick={() => startEdit(editIndex)}
>
{typeof store[editIndex]?.task === 'string'
? store[editIndex].task
: JSON.stringify(store[editIndex]?.task)}
</span>
);
}
}; };
return ( return (
<MyTask.Provider value={{ store, setStore }}> <MyTask.Provider value={{ store, setStore }}>
<> <div className="todo-container">
<Header /> <Header />
<ul className='scroll'> <ul className='scroll'>
{store.map((item, index) => ( {store.map((item, index) => {
<li key={item.id} className={`todo ${item.completed ? "completed" : ""}`}> console.log("Rendering task:", item.task);
<div className="check"> return (
<Checkbox <li key={item.id} className={`todo ${item.completed ? "completed" : ""}`}>
checked={item.completed} <div className="check">
onChange={() => toggleComplete(index)} <Checkbox
id={item.id} checked={item.completed}
/> onChange={() => toggleComplete(index)}
<ErrorBoundary> id={item.id}
{editIndex === index ? ( />
<span {editIndex === index ? (
ref={editableRef} renderEditableContent()
contentEditable ) : (
suppressContentEditableWarning <span
onBlur={() => saveEdit(index)} className={`task ${item.completed ? 'completed' : ''}`}
onInput={(e) => setEditedTask(e.currentTarget.textContent)} onClick={() => startEdit(index)}
onKeyDown={(e) => { >
if (e.key === 'Enter') { {typeof item.task === 'string'
e.preventDefault(); ? item.task
e.currentTarget.blur(); : JSON.stringify(item.task)}
} </span>
}} )}
className={`task ${item.completed ? 'completed' : ''}`} </div>
dangerouslySetInnerHTML={{ __html: editedTask }} <button className="delete" onClick={() => Delete(item.id)}>
/> <MdCancel />
) : ( </button>
<span </li>
className={`task ${item.completed ? 'completed' : ''}`} );
onClick={() => startEdit(index)} })}
>
{item.task}
</span>
)}
</ErrorBoundary>
</div>
<button className="delete" onClick={() => Delete(item.id)}>
<MdCancel />
</button>
</li>
))}
</ul> </ul>
<form onSubmit={(e) => e.preventDefault()}> <form onSubmit={(e) => e.preventDefault()}>
...@@ -181,14 +278,19 @@ export const Input = () => { ...@@ -181,14 +278,19 @@ export const Input = () => {
className="input" className="input"
placeholder="Enter Items" placeholder="Enter Items"
value={task} value={task}
onChange={Change} onChange={handleChange}
disabled={loading}
/> />
<button onClick={Add} className="button"> <button
Submit onClick={Add}
className="button"
disabled={loading || !task.trim()}
>
{loading ? 'Adding...' : 'Submit'}
</button> </button>
</div> </div>
</form> </form>
</> </div>
</MyTask.Provider> </MyTask.Provider>
); );
}; };
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment