Aller au contenu principal

UI Components

Overview

The Notes App frontend uses a collection of reusable UI components that handle specific interface elements and user interactions. These components follow React best practices for modularity, reusability, and clear separation of concerns.

What are UI Components?

UI Components are self-contained pieces of the user interface that:

  • Encapsulate specific functionality like displaying data or handling user input
  • Accept props to customize their behavior and appearance
  • Emit events through callback functions to communicate with parent components
  • Maintain their own internal state when needed
  • Provide consistent styling and behavior across the application

Card Components

NoteCard Component

File: frontend/notes-app/src/components/Cards/NoteCard.jsx

The NoteCard component displays individual notes in a card format with actions for editing, deleting, and pinning.

const NoteCard = ({ title, date, content, tags, isPinned, onEdit, onDelete, onPinNote }) => {
return (
<div className="border rounded p-4 bg-white hover:shadow:xl transition-all ease-in-out">
<div className="flex items-center justify-between">
<div>
<h6 className="text-sm font-medium">{title}</h6>
<span className="text-xs text-slate-500">{moment(date).format("Do MMM YYYY")}</span>
</div>

<MdOutlinePushPin className={`icon-btn ${isPinned ? 'text-primary' : 'text-slate-300'}`} onClick={onPinNote}/>
</div>

<p className="text-xs text-slate-600 mt-2">{content?.slice(0,60)}</p>
<div className="flex items-center justify-between mt-2">
<div className="text-xs text-slate-500">{tags.map((item) => `#${item} `)}</div>
<div className="flex items-center gap-2">
<MdCreate
className="icon-btn hover:text-green-600"
onClick={onEdit}
/>
<MdDelete
className="icon-btn hover:text-red-500"
onClick={onDelete}
/>
</div>
</div>
</div>
);
};

Props Interface

  • title: The note's title text
  • date: Creation or modification timestamp
  • content: The note's main content text
  • tags: Array of tag strings
  • isPinned: Boolean indicating if the note is pinned
  • onEdit: Callback function triggered when edit button is clicked
  • onDelete: Callback function triggered when delete button is clicked
  • onPinNote: Callback function triggered when pin button is clicked

Key Features

Date Formatting: Uses Moment.js library to format dates

{moment(date).format("Do MMM YYYY")}
  • moment(date): Creates a Moment object from the date
  • .format("Do MMM YYYY"): Formats as "1st Jan 2024"

Content Truncation: Limits displayed content to 60 characters

{content?.slice(0,60)}
  • ?.: Optional chaining prevents errors if content is null/undefined
  • .slice(0,60): Takes first 60 characters of the content

Conditional Styling: Pin button changes appearance based on state

className={`icon-btn ${isPinned ? 'text-primary' : 'text-slate-300'}`}
  • Template literals: Uses backticks for string interpolation
  • Conditional classes: Different colors for pinned vs unpinned states

Tag Display: Maps over tags array to display formatted tags

{tags.map((item) => `#${item} `)}
  • .map(): Transforms each tag into a formatted string
  • Template literals: Adds # prefix to each tag

ProfileInfo Component

File: frontend/notes-app/src/components/Cards/ProfileInfo.jsx

Displays user profile information with avatar and logout functionality.

const ProfileInfo = ({userInfo, onLogout}) => {
return (
<div className="flex items-center gap-3">
<div className="w-12 h-12 flex items-center justify-center rounded-full text-slate-950 font-medium bg-slate-100">
{getInitials(userInfo?.fullName)}
</div>

<div>
<p className="text-sm font-medium">{userInfo?.fullName}</p>
<button className="" onClick={onLogout}>
Logout
</button>
</div>
</div>
)
}

Component Features

Avatar Generation: Creates a circular avatar with user initials

  • Fixed dimensions: w-12 h-12 creates a 48x48 pixel circle
  • Centered content: Flexbox centers the initials within the circle
  • Helper function: getInitials() extracts initials from full name

User Information Display: Shows full name and logout option

  • Optional chaining: userInfo?.fullName safely accesses nested properties
  • Callback integration: onLogout prop allows parent to handle logout logic

EmptyCard Component

File: frontend/notes-app/src/components/EmptyCard/EmptyCard.jsx

A reusable component for displaying empty states with images and messages.

const EmptyCard = ({imgSrc, message}) => {
return (
<div className="flex flex-col items-center justify-center mt-20">
<img src={imgSrc} alt="No notes" className="w-60" />
<p className="w-1/2 text-sm font-medium text-slate-700 text-center leading-7 mt-5">
{message}
</p>
</div>
)
}

Usage Scenarios

No Notes State: When user has no notes created No Search Results: When search query returns no matches Loading States: While data is being fetched

Design Features

  • Centered layout: Flexbox centers content vertically and horizontally
  • Visual hierarchy: Large image followed by descriptive text
  • Responsive text: w-1/2 makes text width responsive to container
  • Accessibility: Proper alt text for screen readers

Input Components

TagInput Component

File: frontend/notes-app/src/components/Input/TagInput.jsx

A specialized input component for managing tags with add and remove functionality.

const TagInput = ({ tags, setTags }) => {
const [inputValue, setInputValue] = useState("");

const handleInputChange = (e) => {
setInputValue(e.target.value);
};

const addNewTag = () => {
setTags([...tags, inputValue.trim()]);
setInputValue("");
};

const handleKeyDown = (e) => {
if (e.key === "Enter") {
addNewTag();
}
};

const handleRemoveTag = (tagToRemove) => {
setTags(tags.filter((tag) => tag !== tagToRemove))
};

State Management

Local State: inputValue manages the current input field value Parent State: tags and setTags are controlled by parent component

Tag Operations

Adding Tags:

const addNewTag = () => {
setTags([...tags, inputValue.trim()]);
setInputValue("");
};
  • Spread operator: ...tags creates a new array with existing tags
  • .trim(): Removes whitespace from input
  • State reset: Clears input field after adding

Removing Tags:

const handleRemoveTag = (tagToRemove) => {
setTags(tags.filter((tag) => tag !== tagToRemove))
};
  • .filter(): Creates new array excluding the specified tag
  • Immutable updates: Doesn't modify original array

Keyboard Support:

const handleKeyDown = (e) => {
if (e.key === "Enter") {
addNewTag();
}
};
  • Enter key: Allows adding tags without clicking button
  • Event handling: Checks specific key press

User Interface

Tag Display:

{tags?.length > 0 && (
<div className="flex items-center gap-2 flex-wrap mt-2">
{tags.map((tag, index) => (
<span key={index} className="flex items-center gap-2 text-sm text-slate-900 bg-slate-100 px-3 py-1 rounded">
# {tag}
<button onClick={() => {
handleRemoveTag(tag);
}}>
<MdClose />
</button>
</span>
))}
</div>
)}

Features Explained:

  • Conditional rendering: Only shows tags section if tags exist
  • Flex wrap: Tags wrap to new lines when needed
  • Individual removal: Each tag has its own remove button
  • Visual styling: Tags appear as rounded chips with # prefix

Input Section:

<div className="flex items-center gap-4 mt-3">
<input
type="text"
value={inputValue}
className="text-sm bg-transparent border px-3 py-2 rounded outline-none"
placeholder="Add tags"
onChange={handleInputChange}
onKeyDown={handleKeyDown}
/>
<button className="w-8 h-8 flex items-center justify-center rounded border border-blue-700 hover:bg-blue-700"
onClick={() => {
addNewTag();
}}
>
<MdAdd className="text-2xl text-blue-700 hover:text-white" />
</button>
</div>

Input Features:

  • Controlled input: Value managed by React state
  • Event handlers: Responds to typing and key presses
  • Add button: Visual button for adding tags
  • Hover effects: Button changes appearance on hover

Component Design Patterns

Props Interface Design

Callback Props: Components receive functions to communicate with parents

// NoteCard receives multiple callback functions
const NoteCard = ({ title, date, content, tags, isPinned, onEdit, onDelete, onPinNote })

Data Props: Components receive data to display

// EmptyCard receives display data
const EmptyCard = ({imgSrc, message})

State Props: Components receive state and state setters

// TagInput receives both state and setter
const TagInput = ({ tags, setTags })

State Management Patterns

Local State: Components manage their own internal state when needed

const [inputValue, setInputValue] = useState("");

Lifted State: Important state is managed by parent components

// Tags state managed by parent, passed down as props
const [tags, setTags] = useState(noteData?.tags || []);

Event Handling Patterns

Event Delegation: Parent components handle events from children

// Parent defines the handler, child calls it
<NoteCard onEdit={() => handleEdit(note)} />

Event Prevention: Components handle their own simple events

// TagInput handles its own input changes
const handleInputChange = (e) => {
setInputValue(e.target.value);
};

Styling and Visual Design

CSS Framework

The application uses Tailwind CSS for styling:

  • Utility classes: flex, items-center, justify-between
  • Responsive design: w-1/2, mt-20
  • Color system: text-slate-500, bg-slate-100
  • Interactive states: hover:shadow-xl, hover:text-green-600

Design Consistency

Spacing: Consistent use of margin and padding classes Colors: Standardized color palette using Tailwind's slate colors Typography: Consistent text sizes and weights Interactive elements: Hover effects on clickable items

Connection to Other Components

Parent-Child Communication

  • Props down: Data flows from parent to child components
  • Events up: User interactions bubble up through callback functions
  • State lifting: Shared state managed at appropriate parent level

Integration Points

  • Home component: Uses NoteCard to display notes
  • Navbar: Uses ProfileInfo for user display
  • AddEditNotes: Uses TagInput for tag management
  • Dashboard: Uses EmptyCard for empty states

Reusability

  • EmptyCard: Used for multiple empty states
  • TagInput: Reusable across different forms
  • ProfileInfo: Could be used in multiple locations
  • NoteCard: Template for displaying any note data