react-custom-kanban-board
Version:
A customizable Kanban board component for React with advanced features like search, filtering, and WIP limits.
375 lines (322 loc) • 17.9 kB
Markdown
# KanbanBoard Component
A flexible and customizable Kanban board component for applications. The `KanbanBoard` component allows you to create interactive and draggable task cards organized into columns, making it ideal for project management and task tracking.
### KanbanBoard Component
Here are some visual examples of the Kanban board component:
- 
- 
## Features
- **Drag and Drop Functionality:** Move task cards between columns with ease
- **Customizable Card Rendering:** Tailor the appearance of task cards to fit your design
- **Add, Edit, and Delete Tasks:** Manage tasks directly from the Kanban board
- **Support for Avatars:** Display avatars on task cards for better team representation
- **Custom Search Interface:** Replace the default search with your own implementation
- **Custom Filter Menus:** Create advanced filter controls using your preferred UI library
- **WIP Limits:** Set limits on the number of cards allowed in each column
- **Card Details with Expand/Collapse:** Show or hide detailed information for each card
- **Dynamic Filtering:** Filter cards by various properties (priority, assignee, status)
- **Search Functionality:** Search for cards by title
- **UI Library Integration:** Seamlessly integrate with popular UI libraries like Chakra UI
- **Responsive Design:** Works on desktop and mobile devices
- **Accessibility Features:** Built with accessibility in mind
## Installation
Install the `KanbanBoard` component via NPM:
```bash
npm install react-custom-kanban-board
```
or using yarn:
```bash
yarn add react-custom-kanban-board
```
## Usage
### Basic Example
Import and use the Kanban board in your React project:
```jsx
import React from "react";
import KanbanBoard from "react-custom-kanban-board";
const columns = [
{ title: "To Do", key: "todo", color: "#BDBDCD" },
{ title: "In Progress", key: "in-progress", color: "#FDDDE3" },
{ title: "Done", key: "done", color: "#71C781" },
];
const initialCards = [
{
id: "1",
title: "Task 1",
status: "todo",
avatarPath: "https://i.pravatar.cc/40?img=1",
},
{
id: "2",
title: "Task 2",
status: "in-progress",
avatarPath: "https://i.pravatar.cc/40?img=2",
},
];
const App = () => {
return (
<div>
<h1>My Kanban Board</h1>
<KanbanBoard
columns={columns}
initialCards={initialCards}
columnForAddCard="todo"
/>
</div>
);
};
export default App;
```
### Advanced Example with Chakra UI Integration
Here's how to integrate the Kanban board with Chakra UI for enhanced UI components:
```jsx
import React, { useState, useEffect, useRef } from "react";
import KanbanBoard from "react-custom-kanban-board";
import {
ChakraProvider,
InputGroup,
Input,
InputLeftElement,
InputRightElement,
IconButton,
Box,
Text,
Menu,
MenuButton,
MenuList,
MenuItem,
Button,
Divider,
extendTheme,
} from "@chakra-ui/react";
import { SearchIcon, ChevronDownIcon, CloseIcon } from "@chakra-ui/icons";
import "./App.css";
// Create a theme
const theme = extendTheme({
// Your theme customizations here (optional)
});
const App = () => {
// Sample columns with WIP limits
const columns = [
{ title: "To Do", key: "todo", color: "#B8C2CC" },
{ title: "In Progress", key: "in-progress", color: "#FFB1C1", limit: 3 }, // WIP limit
{ title: "Review", key: "review", color: "#FFD580" },
{ title: "Done", key: "done", color: "#91D18B" },
];
// Sample cards data
const initialCards = [
{
id: "1",
title: "Create user authentication flow",
status: "todo",
avatarPath: "https://i.pravatar.cc/40?img=1",
priority: "High",
dueDate: "2023-06-30",
tags: ["frontend", "auth"],
description:
"Implement user registration, login, and password reset flows.",
assignee: "john",
},
// ... more cards
];
// Filter configurations
const filterConfigs = [
{
field: "priority",
label: "Priority",
options: [
{ value: "High", label: "High Priority" },
{ value: "Medium", label: "Medium Priority" },
{ value: "Low", label: "Low Priority" },
],
},
// ... more filter configs
];
const [cards, setCards] = useState(initialCards);
const [loading, setLoading] = useState(true);
// Simulate loading data
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 1500);
}, []);
// Custom Search Input Component using Chakra UI
const CustomSearchInput = ({ searchTerm, setSearchTerm }) => {
const inputRef = useRef(null);
return (
<InputGroup size="md" maxW="400px">
<InputLeftElement pointerEvents="none">
<SearchIcon color="gray.400" />
</InputLeftElement>
<Input
ref={inputRef}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Find tasks..."
borderRadius="full"
bg="white"
borderColor="gray.300"
/>
{searchTerm && (
<InputRightElement>
<IconButton
size="sm"
variant="ghost"
icon={<CloseIcon w={3} h={3} />}
onClick={() => {
setSearchTerm("");
inputRef.current?.focus();
}}
aria-label="Clear search"
/>
</InputRightElement>
)}
</InputGroup>
);
};
// Custom Filter Menu Component using Chakra UI
const CustomFilterMenu = ({ config, selectedValue, onChange }) => {
const selectedOption = config.options.find(
(opt) => opt.value === selectedValue
);
const displayValue = selectedOption
? selectedOption.label
: `All ${config.label}s`;
return (
<Box mx={2}>
<Text fontSize="xs" color="gray.500" mb={1}>
{config.label}
</Text>
<Menu closeOnSelect={true}>
<MenuButton
as={Button}
rightIcon={<ChevronDownIcon />}
size="sm"
variant="outline"
w="160px"
>
{displayValue}
</MenuButton>
<MenuList zIndex={100}>
<MenuItem
onClick={() => onChange(config.field, null)}
fontWeight={!selectedValue ? "bold" : "normal"}
>
All {config.label}s
</MenuItem>
<Divider />
{config.options.map((option) => (
<MenuItem
key={option.value}
onClick={() => onChange(config.field, option.value)}
fontWeight={selectedValue === option.value ? "bold" : "normal"}
>
{option.label}
</MenuItem>
))}
</MenuList>
</Menu>
</Box>
);
};
// Wrapper functions to match the expected function signature
const customSearchWrapper = (searchTerm, setSearchTerm) => {
return (
<CustomSearchInput
searchTerm={searchTerm}
setSearchTerm={setSearchTerm}
/>
);
};
const customFilterWrapper = (config, selectedValue, onChange) => {
return (
<CustomFilterMenu
config={config}
selectedValue={selectedValue}
onChange={onChange}
/>
);
};
return (
<ChakraProvider theme={theme}>
<div className="app-container">
<header className="app-header">
<h1>Project Task Board</h1>
</header>
<main className="board-container">
<KanbanBoard
columns={columns}
initialCards={cards}
columnForAddCard="todo"
isLoading={loading}
emptyColumnMessage="No tasks yet"
enableSearch={true}
enableFiltering={true}
filterConfigs={filterConfigs}
renderSearchInput={customSearchWrapper}
renderFilterMenu={customFilterWrapper}
/>
</main>
</div>
</ChakraProvider>
);
};
export default App;
```
## Props
### KanbanBoard Component Props
| Prop | Type | Default | Description |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
| `columns` | `Column[]` | `[]` | Array of columns to display. Each object should include `title`, `key`, and `color`. |
| `initialCards` | `Card[]` | `[]` | Array of cards to display initially. Each object should include `id`, `title`, `status`, and optionally `avatarPath`. |
| `columnForAddCard` | `string` | - | Key of the column where new cards will be added. |
| `onCardMove` | `(cardId: string, newStatus: string) => void` | - | Callback function when a card is moved. |
| `onCardEdit` | `(cardId: string, newTitle: string) => void` | - | Callback function when a card is edited. |
| `onCardDelete` | `(cardId: string) => void` | - | Callback function when a card is deleted. |
| `onTaskAddedCallback` | `(title: string) => void` | - | Callback function when a new task is added. |
| `renderCard` | `(card: Card, handleDragStart: (e: React.DragEvent<HTMLDivElement>, card: Card) => void, isExpanded?: boolean, toggleExpand?: (id: string) => void) => ReactNode` | - | Custom function to render cards. |
| `renderAvatar` | `(avatarPath?: string) => ReactNode` | - | Custom function to render avatars. |
| `renderAddCard` | `(column: string, setCards: React.Dispatch<React.SetStateAction<Card[]>>) => ReactNode` | - | Custom function to render the add card button. |
| `isLoading` | `boolean` | `false` | Shows loading spinner when true. |
| `loadingComponent` | `ReactNode` | - | Custom loading component. |
| `emptyColumnMessage` | `string` | `"No cards yet"` | Message to display when a column is empty. |
| `enableSearch` | `boolean` | `false` | Enable search functionality. |
| `enableFiltering` | `boolean` | `false` | Enable filtering functionality. |
| `filterConfigs` | `FilterConfig[]` | `[]` | Configuration for filters. |
| `onFilterChange` | `(filters: Record<string, string \| null>) => void` | - | Callback when filters change. |
| `renderSearchInput` | `(searchTerm: string, setSearchTerm: (term: string) => void) => ReactNode` | - | Custom function to render search input. |
| `renderFilterMenu` | `(config: FilterConfig, value: string \| null, handleFilterChange: (field: string, value: string \| null) => void) => ReactNode` | - | Custom function to render filter menu. |
### Column Interface
| Property | Type | Description |
| -------- | -------- | ---------------------------------- |
| `title` | `string` | Title of the column. |
| `key` | `string` | Unique key for the column. |
| `color` | `string` | Background color for the column. |
| `limit` | `number` | Optional WIP limit for the column. |
### Card Interface
| Property | Type | Description |
| --------------- | ---------- | ----------------------------------------------- |
| `id` | `string` | Unique identifier for the card. |
| `title` | `string` | Title of the card. |
| `status` | `string` | Status of the card, corresponds to column key. |
| `avatarPath` | `string` | URL to the avatar image. |
| `priority` | `string` | Priority level (e.g., "High", "Medium", "Low"). |
| `dueDate` | `string` | Due date for the card. |
| `tags` | `string[]` | Array of tags for the card. |
| `description` | `string` | Detailed description of the card. |
| `assignee` | `string` | Person assigned to the card. |
| `[key: string]` | `any` | Any additional custom properties you need. |
### FilterConfig Interface
| Property | Type | Description |
| --------- | ---------------- | -------------------------------------------------- |
| `field` | `string` | Field to filter by (e.g., "priority", "assignee"). |
| `label` | `string` | Display label for the filter. |
| `options` | `FilterOption[]` | Array of available filter options. |
### FilterOption Interface
| Property | Type | Description |
| -------- | -------- | ------------------------------------ |
| `value` | `string` | Value of the filter option. |
| `label` | `string` | Display label for the filter option. |
## Contributing
Contributions are welcome! Please open an issue or submit a pull request on GitHub.
## Contact
For any questions or issues, please contact [HamdyIIbarhim](mailto:hamdyfarouk444.com).