@crocogiciel/react-web-radial-menu
v1.0.6
Published
A beautiful and customizable radial menu component for React with multi-level navigation support
Downloads
57
Maintainers
Readme
React Radial Menu
A beautiful and customizable radial (circular) menu component for React with multi-level navigation support.
Features
- Beautiful radial menu design with smooth animations
- Multi-level navigation support (nested submenus)
- Multiple selection with visual feedback
- Customizable badge counters for parent items with children
- Per-item color customization (background, hover, selected, badges)
- Color inheritance from parent to children
- Automatic gradient generation for submenus with customizable ranges
- Fully customizable colors and sizes
- TypeScript support with full type definitions
- Configurable animation presets (fast, smooth, elastic, slow, none)
- Text wrapping for long labels
- Hover and selection states
- Validation and cancel callbacks
- Click outside to validate selection
Installation
From npm
npm install @crocogiciel/react-web-radial-menuFrom GitHub
npm install crocogiciel/react-web-radial-menuPeer Dependencies
This package requires the following peer dependencies:
{
"react": "^18.3.1",
"react-dom": "^18.3.1",
"lucide-react": "^0.344.0"
}Make sure these are installed in your project:
npm install react react-dom lucide-reactBasic Usage
import { RadialMenu } from '@crocogiciel/react-web-radial-menu';
import '@crocogiciel/react-web-radial-menu/styles.css';
function App() {
const [showMenu, setShowMenu] = useState(false);
const menuItems = [
{ id: '1', label: 'Home' },
{ id: '2', label: 'Settings' },
{ id: '3', label: 'Profile' },
{ id: '4', label: 'Help' },
];
const handleValidate = (selectedIds: string[]) => {
console.log('Selected:', selectedIds);
setShowMenu(false);
};
const handleCancel = () => {
console.log('Cancelled');
setShowMenu(false);
};
return (
<>
<button onClick={() => setShowMenu(true)}>Open Menu</button>
{showMenu && (
<RadialMenu
items={menuItems}
position={{ x: 400, y: 400 }}
centerText="Menu"
onValidate={handleValidate}
onCancel={handleCancel}
/>
)}
</>
);
}Advanced Usage with Submenus
import { RadialMenu, RadialMenuItem } from '@crocogiciel/react-web-radial-menu';
function App() {
const [showMenu, setShowMenu] = useState(false);
const menuItems: RadialMenuItem[] = [
{
id: '1',
label: 'File',
children: [
{ id: '1-1', label: 'New' },
{ id: '1-2', label: 'Open' },
{ id: '1-3', label: 'Save' },
],
},
{
id: '2',
label: 'Edit',
children: [
{ id: '2-1', label: 'Cut' },
{ id: '2-2', label: 'Copy' },
{ id: '2-3', label: 'Paste' },
],
},
{ id: '3', label: 'View' },
{ id: '4', label: 'Help' },
];
const handleValidate = (selectedIds: string[]) => {
console.log('Selected items:', selectedIds);
setShowMenu(false);
};
const handleCancel = () => {
console.log('Menu cancelled');
setShowMenu(false);
};
return (
<>
<button onClick={() => setShowMenu(true)}>Open Menu</button>
{showMenu && (
<RadialMenu
items={menuItems}
position={{ x: 400, y: 400 }}
centerText="Commands"
radius={160}
innerRadius={70}
itemHeight={50}
colors={{
selected: '#10b981',
unselected: '#374151',
hover: '#4b5563',
background: 'rgba(17, 24, 39, 0.95)',
text: '#ffffff',
}}
badgeConfig={{
backgroundColor: '#ef4444',
textColor: '#ffffff',
size: 20,
fontSize: 12,
show: true,
}}
animationPreset="smooth"
animationDuration={300}
onValidate={handleValidate}
onCancel={handleCancel}
/>
)}
</>
);
}API Reference
RadialMenu Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| items | RadialMenuItem[] | required | Array of menu items |
| position | { x: number, y: number } | { x: 400, y: 400 } | Position of the menu center |
| colors | Partial<RadialMenuColors> | Default colors | Custom color scheme |
| centerText | string | '' | Text displayed in the center |
| onValidate | (selectedIds: string[]) => void | - | Callback when validation is triggered (includes clicking outside) |
| onCancel | () => void | - | Callback when cancel button is clicked |
| radius | number | 160 | Outer radius of the menu |
| innerRadius | number | 70 | Inner radius of the menu |
| itemHeight | number | 50 | Height of each menu item |
| badgeConfig | BadgeConfig | {} | Configuration for selection count badges |
| animationPreset | AnimationPreset | 'none' | Animation style: 'fast', 'smooth', 'elastic', 'slow', or 'none' |
| animationDuration | number | 300 | Duration of animations in milliseconds |
| autoGradient | boolean | false | Enable automatic gradient colors for submenus |
| gradientLightnessRange | number | 20 | Lightness variation range for gradient (0-50%) |
| gradientSaturationRange | number | 10 | Saturation variation range for gradient (0-30%) |
RadialMenuItem
interface RadialMenuItem {
id: string;
label: string;
icon?: string;
children?: RadialMenuItem[];
category?: string;
backgroundColor?: string;
selectedColor?: string;
hoverColor?: string;
badgeConfig?: BadgeConfig;
}RadialMenuColors
interface RadialMenuColors {
selected: string; // Color for selected items
unselected: string; // Color for unselected items
hover: string; // Color on hover
background: string; // Background color
text: string; // Text color
}BadgeConfig
interface BadgeConfig {
backgroundColor?: string; // Badge background color
textColor?: string; // Badge text color
size?: number; // Badge diameter in pixels
fontSize?: number; // Badge font size in pixels
show?: boolean; // Whether to show badges
}AnimationPreset
type AnimationPreset = 'fast' | 'smooth' | 'elastic' | 'slow' | 'none';SelectedItems
interface SelectedItems {
[key: string]: boolean;
}An object mapping item IDs to their selection state.
Customization
Custom Colors
<RadialMenu
items={items}
colors={{
selected: '#3b82f6',
unselected: '#1e293b',
hover: '#334155',
background: 'rgba(0, 0, 0, 0.9)',
text: '#f1f5f9',
}}
/>Custom Size
<RadialMenu
items={items}
radius={200}
innerRadius={80}
itemHeight={60}
/>Badge Configuration
<RadialMenu
items={items}
badgeConfig={{
backgroundColor: '#ef4444',
textColor: '#ffffff',
size: 24,
fontSize: 14,
show: true,
}}
/>Badges show the count of selected children for parent items with submenus.
Badge Positioning: Badges are automatically positioned at the first third (33%) of each arc segment, rather than at the center. This optimized placement provides better visual balance, improves readability, and prevents potential overlapping when multiple items with badges are present. The positioning is calculated dynamically based on the menu's radius and the configured badge size.
Custom Item Colors
You can specify custom colors for each menu item, including background, hover, selection, and badge colors. By default, submenus inherit their parent's colors.
const menuItems: RadialMenuItem[] = [
{
id: '1',
label: 'File',
backgroundColor: '#3b82f6',
selectedColor: '#60a5fa',
hoverColor: '#2563eb',
badgeConfig: {
backgroundColor: '#ef4444',
textColor: '#ffffff',
size: 20,
fontSize: 12,
show: true,
},
children: [
{ id: '1-1', label: 'New' },
{ id: '1-2', label: 'Open' },
],
},
{
id: '2',
label: 'Edit',
backgroundColor: '#10b981',
selectedColor: '#34d399',
hoverColor: '#059669',
badgeConfig: {
backgroundColor: '#f59e0b',
textColor: '#ffffff',
size: 20,
fontSize: 12,
show: true,
},
children: [
{ id: '2-1', label: 'Cut' },
{ id: '2-2', label: 'Copy' },
],
},
];
<RadialMenu items={menuItems} />Color Inheritance:
- If an item doesn't specify colors, it falls back to global colors
- Children inherit their parent's custom colors (selectedColor, hoverColor, badgeConfig)
- Individual children can override inherited colors by specifying their own
Auto Gradient
Enable automatic gradient generation for submenus. When enabled, each submenu level gets a lighter shade of its parent's color, creating a cohesive visual hierarchy.
<RadialMenu
items={menuItems}
autoGradient={true}
gradientLightnessRange={20}
gradientSaturationRange={10}
/>With autoGradient enabled:
- Level 0 (main menu): Uses the specified
backgroundColoror default color - Level 1 (submenu): Automatically lighter shade of parent color
- Each submenu level progressively gets lighter while maintaining the same color tone
Gradient Customization:
gradientLightnessRange(default: 20): Controls how much lighter the colors become (0-50%)- Higher values create stronger contrast between items
- Lower values create more subtle transitions
gradientSaturationRange(default: 10): Controls saturation variation (0-30%)- Higher values create more vibrant progressions
- Lower values maintain consistent saturation
This creates a beautiful, harmonious color scheme without manual configuration.
Animation Presets
<RadialMenu
items={items}
animationPreset="smooth"
animationDuration={400}
/>Available presets:
'fast'- Quick, snappy animations'smooth'- Balanced, smooth transitions'elastic'- Bouncy, playful animations'slow'- Gentle, slow-motion animations'none'- No animations
TypeScript Support
This library is written in TypeScript and provides full type definitions. All types are exported from the main package:
import type {
RadialMenuItem,
RadialMenuColors,
RadialMenuPosition,
RadialMenuConfig,
SelectedItems,
BadgeConfig,
AnimationPreset,
} from '@crocogiciel/react-web-radial-menu';Color Behavior
Custom Colors
- Each menu item can have custom properties:
backgroundColor- Base color for the itemselectedColor- Color when the item is selectedhoverColor- Color when hovering over the itembadgeConfig- Custom badge styling for this item
- If not specified, items use the default colors from the global color scheme
- Children inherit their parent item's custom colors by default
- Individual children can override inherited colors
Auto Gradient
- When
autoGradientis enabled, submenus automatically receive lighter shades of their parent's color - The gradient algorithm adjusts both lightness and saturation to maintain visual harmony
- Customizable with
gradientLightnessRange(default: 20%) andgradientSaturationRange(default: 10%) - Each submenu item becomes progressively lighter in a clockwise direction
- The gradient stays within the same color tone for consistency
- Works with both default colors and custom per-item colors
Behavior
Selection
- Click on any item without children to select/deselect it
- Items with children will open a submenu when clicked
- Selected items are highlighted with the configured selection color
- A count of selected items is displayed in the center
Validation
- Click the checkmark button in the center to validate the selection
- Click outside the menu to automatically validate and close
- The
onValidatecallback receives an array of selected item IDs
Cancellation
- Click the X button in the center to cancel without validating
- The
onCancelcallback is triggered - No selection is saved
Demo
Check out the live demo to see the radial menu in action with all its features.
You can view the demo source code here.
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
