Package Exports
- nachtify
Readme
Nachtify
A production-ready React library for adding interactive color palette context menus to data tables and displays. Nachtify provides column-based and group-based color application with automatic localStorage persistence.
Features
- Context menu with integrated color palette triggered by right-click
- Column-based color application for individual cells
- Group-based color application for row categories
- Automatic localStorage persistence across sessions
- 11 predefined theme colors with circular swatch UI
- Scrollable color palette with arrow navigation
- Customizable menu items for extended functionality
- JSON-based color scheme export and import
Installation
npm install nachtifyOr with yarn:
yarn add nachtifyQuick Start
1. Import the component and library
import {
ColorPaletteContextMenu,
THEME_COLORS,
loadColors,
saveColors,
applyColumnColor,
getColumnColor,
applyGroupColor,
getGroupColor
} from 'nachtify';2. Set up state
import { useState, useEffect } from 'react';
export default function MyTable() {
const [colors, setColors] = useState(() => loadColors());
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0, row: null, column: null });
const [colorScrollIndex, setColorScrollIndex] = useState(0);
// Auto-save colors to localStorage
useEffect(() => {
saveColors(colors);
}, [colors]);
const handleRowContextMenu = (e, rowId, column) => {
e.preventDefault();
setContextMenu({
visible: true,
x: e.clientX,
y: e.clientY,
row: rowId,
column: column
});
};
const closeContextMenu = () => {
setContextMenu({ visible: false, x: 0, y: 0, row: null, column: null });
};
return (
<div>
<table>
<tbody>
{data.map(row => (
<tr
key={row.id}
onContextMenu={(e) => handleRowContextMenu(e, row.id, 'name')}
style={{ backgroundColor: getGroupColor(colors, `group-${row.group}`) }}
>
<td style={{ backgroundColor: getColumnColor(colors, row.id, 'name') }}>
{row.name}
</td>
</tr>
))}
</tbody>
</table>
<ColorPaletteContextMenu
visible={contextMenu.visible}
x={contextMenu.x}
y={contextMenu.y}
colorScrollIndex={colorScrollIndex}
onColorScrollChange={setColorScrollIndex}
onColorSelect={(color) => {
setColors(prev => applyColumnColor(prev, contextMenu.row, contextMenu.column, color));
closeContextMenu();
}}
onClose={closeContextMenu}
menuItems={[
{
label: 'Edit',
icon: <Edit3 size={16} />,
onClick: () => console.log('Edit'),
hasBorder: true
},
{
label: 'Delete',
icon: <Trash2 size={16} />,
onClick: () => console.log('Delete'),
isDanger: true
}
]}
/>
</div>
);
}API Reference
Components
ColorPaletteContextMenu
A context menu component with integrated color palette.
Props:
visible(boolean) - Show/hide the menux(number) - X positiony(number) - Y positioncolorScrollIndex(number) - Current scroll position in color paletteonColorScrollChange(function) - Called when scrolling colorsonColorSelect(function) - Called when a color is selectedonClose(function) - Called when menu should closemenuItems(array) - Custom menu itemsshowColorPalette(boolean) - Show/hide color palette (default: true)title(string) - Color palette section title (default: 'Theme Color')
Menu Item Structure:
{
label: 'Item Label',
icon: <IconComponent />,
onClick: () => {},
hasBorder: true, // Show border below item
isDanger: false // Red color for dangerous actions
}Library Functions
loadColors()
Load colors from localStorage.
const colors = loadColors();saveColors(colors)
Save colors to localStorage.
saveColors(colors);clearColors()
Clear all colors from localStorage.
clearColors();applyColumnColor(colors, rowId, column, color)
Apply color to a specific row and column.
const updated = applyColumnColor(colors, 1, 'name', THEME_COLORS[2]);getColumnColor(colors, rowId, column, fallbackColor)
Get color for a specific row and column.
const color = getColumnColor(colors, 1, 'name', 'transparent');applyGroupColor(colors, groupKey, color)
Apply color to all rows in a group.
const updated = applyGroupColor(colors, 'gender-Male', THEME_COLORS[2]);getGroupColor(colors, groupKey)
Get color for a group.
const color = getGroupColor(colors, 'gender-Male');exportColorsToFile(colors, filename)
Export colors to a JSON file.
exportColorsToFile(colors, 'my-colors.json');importColorsFromFile(file)
Import colors from a JSON file.
const colors = await importColorsFromFile(fileInput.files[0]);Constants
THEME_COLORS
Array of 11 predefined colors:
[
{ name: 'Default', value: 'default', primary: '#FFFFFF', ... },
{ name: 'Indigo', value: 'indigo', primary: '#4F46E5', ... },
{ name: 'Blue', value: 'blue', primary: '#3B82F6', ... },
{ name: 'Purple', value: 'purple', primary: '#9333EA', ... },
{ name: 'Pink', value: 'pink', primary: '#EC4899', ... },
{ name: 'Red', value: 'red', primary: '#EF4444', ... },
{ name: 'Orange', value: 'orange', primary: '#F97316', ... },
{ name: 'Amber', value: 'amber', primary: '#F59E0B', ... },
{ name: 'Green', value: 'green', primary: '#10B981', ... },
{ name: 'Teal', value: 'teal', primary: '#14B8A6', ... },
{ name: 'Cyan', value: 'cyan', primary: '#06B6D4', ... }
]localStorage Structure
Colors are stored under the key colorPaletteData:
{
"1-name": "#4F46E5",
"2-name": "#3B82F6",
"gender-Male": "#10B981",
"gender-Female": "#EC4899"
}Example: Complete Table with Color Palette
import { useState, useEffect } from 'react';
import { Edit3, Trash2 } from 'lucide-react';
import {
ColorPaletteContextMenu,
loadColors,
saveColors,
applyColumnColor,
getColumnColor,
applyGroupColor,
getGroupColor
} from 'nachtify';
export default function DataTable() {
const [colors, setColors] = useState(() => loadColors());
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0, row: null, column: null });
const [colorScrollIndex, setColorScrollIndex] = useState(0);
const data = [
{ id: 1, name: 'John Doe', gender: 'Male', department: 'Sales' },
{ id: 2, name: 'Jane Smith', gender: 'Female', department: 'Marketing' },
{ id: 3, name: 'Bob Johnson', gender: 'Male', department: 'IT' }
];
useEffect(() => {
saveColors(colors);
}, [colors]);
const handleContextMenu = (e, rowId, column) => {
e.preventDefault();
setContextMenu({
visible: true,
x: e.clientX,
y: e.clientY,
row: rowId,
column: column
});
};
const closeContextMenu = () => {
setContextMenu({ visible: false, x: 0, y: 0, row: null, column: null });
};
return (
<div className="p-8">
<table className="w-full border-collapse">
<thead>
<tr className="bg-gray-100">
<th className="px-4 py-2 text-left">Name</th>
<th className="px-4 py-2 text-left">Gender</th>
<th className="px-4 py-2 text-left">Department</th>
</tr>
</thead>
<tbody>
{data.map(row => (
<tr
key={row.id}
style={{ backgroundColor: getGroupColor(colors, `gender-${row.gender}`) }}
>
<td
className="px-4 py-2 cursor-context-menu"
onContextMenu={(e) => handleContextMenu(e, row.id, 'name')}
style={{ backgroundColor: getColumnColor(colors, row.id, 'name') }}
>
{row.name}
</td>
<td className="px-4 py-2">{row.gender}</td>
<td className="px-4 py-2">{row.department}</td>
</tr>
))}
</tbody>
</table>
<ColorPaletteContextMenu
visible={contextMenu.visible}
x={contextMenu.x}
y={contextMenu.y}
colorScrollIndex={colorScrollIndex}
onColorScrollChange={setColorScrollIndex}
onColorSelect={(color) => {
setColors(prev => applyColumnColor(prev, contextMenu.row, contextMenu.column, color));
closeContextMenu();
}}
onClose={closeContextMenu}
menuItems={[
{
label: 'Edit',
icon: <Edit3 size={16} />,
onClick: () => alert('Edit clicked'),
hasBorder: true
},
{
label: 'Delete',
icon: <Trash2 size={16} />,
onClick: () => alert('Delete clicked'),
isDanger: true
}
]}
/>
</div>
);
}How It Works
- Right-click on a table cell to open the context menu with color palette
- Select a color from the available theme colors
- The selected color is immediately applied to the target cell or group
- Colors are automatically persisted to localStorage
- On page refresh, previously saved colors are restored
Styling
The component uses Tailwind CSS classes. Make sure Tailwind CSS is installed in your project:
npm install -D tailwindcss postcss autoprefixerBrowser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
License
MIT
Contributing
Contributions are welcome. Please submit pull requests with clear descriptions of changes and ensure all tests pass.
Support
For issues, feature requests, or questions, please visit the project repository.