From 8ef4d3e363711299089c1c82393c071ba196167a Mon Sep 17 00:00:00 2001 From: Ashish-Pandey62 Date: Sat, 4 Oct 2025 13:31:49 +0545 Subject: [PATCH] Added bold, italic, underline, and text alignment features in notes --- package-lock.json | 20 +--------- src/components/NoteEditor.tsx | 71 +++++++++++++++++++++++++++-------- 2 files changed, 56 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9580f2b..9e13c72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2917,7 +2917,6 @@ "integrity": "sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2948,7 +2947,6 @@ "integrity": "sha512-oSVZmGtDPmRZtVDqvdKUi/qgCsWp5IDY29wp8na8Bj4B3cc99hfNzvNhlMkVVxctkAOGUA3Km7MMpBHAnWfcIA==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2960,7 +2958,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -3018,7 +3015,6 @@ "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", @@ -3251,7 +3247,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3476,7 +3471,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001741", @@ -3855,7 +3849,6 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" @@ -3947,8 +3940,7 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -4049,7 +4041,6 @@ "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5181,7 +5172,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5398,7 +5388,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5425,7 +5414,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5439,7 +5427,6 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.63.0.tgz", "integrity": "sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6039,7 +6026,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6172,7 +6158,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -6236,7 +6221,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6426,7 +6410,6 @@ "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -6520,7 +6503,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/src/components/NoteEditor.tsx b/src/components/NoteEditor.tsx index 03d94a5..18dffb9 100644 --- a/src/components/NoteEditor.tsx +++ b/src/components/NoteEditor.tsx @@ -1,7 +1,6 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { Note, formatTimestamp, getRelativeTime } from '@/types/note'; import { Input } from '@/components/ui/input'; -import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; import { Trash2, Calendar, Clock } from 'lucide-react'; import { @@ -25,21 +24,57 @@ interface NoteEditorProps { export function NoteEditor({ note, onUpdate, onDelete }: NoteEditorProps) { const [title, setTitle] = useState(note.title); const [content, setContent] = useState(note.content); + const contentRef = useRef(null); + const timeoutRef = useRef(); + const [alignmentCycle, setAlignmentCycle] = useState(0); useEffect(() => { - setTitle(note.title); - setContent(note.content); - }, [note.id, note.title, note.content]); + setTitle(note.title); + setContent(note.content); + if (contentRef.current) { + contentRef.current.innerHTML = note.content || '
'; + } +}, [note.id]); - useEffect(() => { - const timer = setTimeout(() => { - if (title !== note.title || content !== note.content) { - onUpdate(note.id, { title, content }); + + const handleInput = () => { + clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => { + if (contentRef.current) { + onUpdate(note.id, { title, content: contentRef.current.innerHTML }); } }, 500); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.ctrlKey) { + switch (e.key) { + case 'b': + e.preventDefault(); + document.execCommand('bold'); + break; + case 'i': + e.preventDefault(); + document.execCommand('italic'); + break; + case 'u': + e.preventDefault(); + document.execCommand('underline'); + break; + case 'j': + e.preventDefault(); + setAlignmentCycle((prev) => { + const next = (prev + 1) % 3; + if (next === 0) document.execCommand('justifyLeft'); + else if (next === 1) document.execCommand('justifyCenter'); + else document.execCommand('justifyRight'); + return next; + }); + break; + } + } + }; - return () => clearTimeout(timer); - }, [title, content, note.id, note.title, note.content, onUpdate]); return (
@@ -85,11 +120,15 @@ export function NoteEditor({ note, onUpdate, onDelete }: NoteEditorProps) { Edited {getRelativeTime(note.updatedAt)}
-