@fullstackcolombia/roseselect
v1.0.3
Published
A beautiful React component for azimuth angle selection with radar-style interface and multiple themes. No Tailwind CSS required - includes all styles pre-compiled.
Maintainers
Readme
RoseSelector 🧭
A beautiful and highly customizable React component for azimuth angle selection with radar-style interface and multiple themes.
🚀 Features
- ✨ Intuitive radar-style user interface
- 🎨 7 predefined themes (light, dark, forest, ocean, sunset, matrix, solarized)
- 🔊 Optional sound effects with Tone.js
- 📱 Responsive design
- ⌨️ Full accessibility
- 🎯 Configurable intervals (5°, 15°, or 30°)
- 🧭 Customizable north offset
- 💾 Data copy functionality
- 🎵 Optional interactive sounds
📦 Installation
npm install @fullstackcolombia/roseselectyarn add @fullstackcolombia/roseselectpnpm add @fullstackcolombia/roseselect🎨 Style Imports
Component styles are automatically included when importing the component. However, if you need to import them manually, you can do so like this:
// Styles are automatically imported with the component
import { RoseSelector } from '@fullstackcolombia/roseselect'
// Or import manually if needed
import '@fullstackcolombia/roseselect/dist/roseselect.css'Note: This component includes all necessary CSS styles without depending on Tailwind CSS in your project. The styles are pre-compiled and optimized.
🎯 Basic Usage
import React, { useState } from 'react'
import { RoseSelector } from '@fullstackcolombia/roseselect'
// CSS is automatically imported
function App() {
const [selectedAngles, setSelectedAngles] = useState<number[]>([])
return (
<RoseSelector
setOptions={setSelectedAngles}
/>
)
}📖 Usage Examples
1. Basic Setup with Light Theme
The simplest use of the component with default configuration.
import React, { useState } from 'react'
import { RoseSelector } from '@fullstackcolombia/roseselect'
// CSS styles are automatically included
function BasicExample() {
const [selectedAngles, setSelectedAngles] = useState<number[]>([])
return (
<div style={{ padding: '2rem' }}>
<h2>Basic Azimuth Selection</h2>
<RoseSelector
setOptions={setSelectedAngles}
initialTheme="light"
initialInterval={15}
/>
<div style={{ marginTop: '1rem' }}>
<p>Selected angles: {selectedAngles.join(', ')}°</p>
</div>
</div>
)
}2. Dark Theme for Night Applications
Perfect for night navigation applications or astronomical interfaces.
import React, { useState } from 'react'
import { RoseSelector } from '@fullstackcolombia/roseselect'
// All styles including dark theme are automatically imported
function DarkThemeExample() {
const [windDirections, setWindDirections] = useState<number[]>([])
return (
<div style={{
minHeight: '100vh',
backgroundColor: '#0f172a',
padding: '2rem'
}}>
<h2 style={{
color: 'white',
fontSize: '1.5rem',
marginBottom: '1.5rem'
}}>
Wind Direction Monitor
</h2>
<RoseSelector
setOptions={setWindDirections}
initialTheme="dark"
initialInterval={30}
/>
<div style={{ marginTop: '1.5rem', color: 'white' }}>
<h3>Monitored wind directions:</h3>
<p style={{ color: '#93c5fd' }}>
{windDirections.length} active directions
</p>
</div>
</div>
)
}3. Matrix Theme for Futuristic Interfaces
Ideal for science fiction applications or gaming.
import React, { useState } from 'react'
import { RoseSelector } from '@fullstackcolombia/roseselect'
// Matrix theme styles are included automatically
function MatrixThemeExample() {
const [radarSectors, setRadarSectors] = useState<number[]>([])
const handleSectorChange = (angles: number[]) => {
setRadarSectors(angles)
console.log('Radar sectors updated:', angles)
}
return (
<div style={{
minHeight: '100vh',
backgroundColor: '#000',
padding: '2rem'
}}>
<h2 style={{
color: '#4ade80',
fontSize: '2rem',
fontFamily: 'monospace',
marginBottom: '1.5rem'
}}>
MATRIX RADAR SYSTEM
</h2>
<RoseSelector
setOptions={handleSectorChange}
initialTheme="matrix"
initialInterval={5}
/>
<div style={{
marginTop: '1.5rem',
color: '#4ade80',
fontFamily: 'monospace'
}}>
<p>ACTIVE SECTORS: {radarSectors.length}</p>
<p>COVERAGE: {radarSectors.length * 5}°</p>
</div>
</div>
)
}4. Nautical Configuration with Ocean Theme
Perfect for maritime and navigation applications.
import React, { useState } from 'react'
import { RoseSelector, ThemeType } from '@fullstackcolombia/roseselect'
// Ocean theme styles automatically imported
function NauticalExample() {
const [compassBearing, setCompassBearing] = useState<number[]>([])
const [currentTheme] = useState<ThemeType>('ocean')
const formatBearing = (angles: number[]) => {
return angles.map(angle => {
const cardinal = getCardinalDirection(angle)
return `${angle}° (${cardinal})`
}).join(', ')
}
const getCardinalDirection = (angle: number) => {
const directions = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
const index = Math.round(angle / 22.5) % 16
return directions[index]
}
return (
<div style={{
minHeight: '100vh',
backgroundColor: '#f0f9ff',
padding: '2rem'
}}>
<h2 style={{
color: '#1e40af',
fontSize: '1.5rem',
marginBottom: '1.5rem'
}}>
Nautical Navigation System
</h2>
<div style={{
backgroundColor: 'white',
borderRadius: '0.5rem',
boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1)',
padding: '1.5rem'
}}>
<RoseSelector
setOptions={setCompassBearing}
initialTheme={currentTheme}
initialInterval={15}
/>
<div style={{
marginTop: '1.5rem',
padding: '1rem',
backgroundColor: '#dbeafe',
borderRadius: '0.375rem'
}}>
<h3 style={{ color: '#1d4ed8', fontWeight: '600' }}>
Selected Course:
</h3>
<p style={{ color: '#2563eb' }}>
{formatBearing(compassBearing)}
</p>
</div>
</div>
</div>
)
}5. Advanced Setup with Multiple Features
Complete example showing all advanced component features.
import React, { useState, useEffect } from 'react'
import { RoseSelector, ThemeType } from '@fullstackcolombia/roseselect'
// All component styles are automatically included
function AdvancedExample() {
const [selectedAngles, setSelectedAngles] = useState<number[]>([])
const [currentTheme, setCurrentTheme] = useState<ThemeType>('sunset')
const [statistics, setStatistics] = useState({
totalSelected: 0,
coverage: 0,
avgAngle: 0
})
useEffect(() => {
const totalSelected = selectedAngles.length
const coverage = (totalSelected * 15) // Assuming 15° interval
const avgAngle = selectedAngles.length > 0
? selectedAngles.reduce((a, b) => a + b, 0) / selectedAngles.length
: 0
setStatistics({
totalSelected,
coverage,
avgAngle: Math.round(avgAngle)
})
}, [selectedAngles])
const themeOptions: ThemeType[] = ['light', 'dark', 'forest', 'ocean', 'sunset', 'matrix', 'solarized']
const exportData = () => {
const data = {
timestamp: new Date().toISOString(),
theme: currentTheme,
selectedAngles,
statistics
}
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'azimuth-selection.json'
a.click()
URL.revokeObjectURL(url)
}
return (
<div style={{
minHeight: '100vh',
padding: '2rem',
transition: 'colors 300ms'
}}>
<div style={{ maxWidth: '64rem', margin: '0 auto' }}>
<h1 style={{
fontSize: '1.875rem',
fontWeight: 'bold',
marginBottom: '2rem'
}}>
Advanced Azimuth Selection System
</h1>
{/* Theme Control */}
<div style={{
marginBottom: '1.5rem',
padding: '1rem',
backgroundColor: 'white',
borderRadius: '0.5rem',
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)'
}}>
<h3 style={{
fontSize: '1.125rem',
fontWeight: '600',
marginBottom: '0.75rem'
}}>
Theme Selector
</h3>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
{themeOptions.map(theme => (
<button
key={theme}
onClick={() => setCurrentTheme(theme)}
style={{
padding: '0.5rem 1rem',
borderRadius: '9999px',
fontSize: '0.875rem',
fontWeight: '500',
transition: 'all 150ms',
backgroundColor: currentTheme === theme ? '#3b82f6' : '#e5e7eb',
color: currentTheme === theme ? 'white' : 'black',
border: 'none',
cursor: 'pointer',
boxShadow: currentTheme === theme ? '0 10px 15px -3px rgba(0, 0, 0, 0.1)' : 'none'
}}
>
{theme.charAt(0).toUpperCase() + theme.slice(1)}
</button>
))}
</div>
</div>
{/* Main Component */}
<div style={{ marginBottom: '2rem' }}>
<RoseSelector
setOptions={setSelectedAngles}
initialTheme={currentTheme}
initialInterval={15}
/>
</div>
{/* Statistics Panel */}
<div style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '1.5rem'
}}>
<div style={{
backgroundColor: 'white',
padding: '1.5rem',
borderRadius: '0.5rem',
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)'
}}>
<h3 style={{
fontSize: '1.25rem',
fontWeight: '600',
marginBottom: '1rem'
}}>
Statistics
</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Selected Sectors:</span>
<span style={{ fontWeight: 'bold' }}>{statistics.totalSelected}</span>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Total Coverage:</span>
<span style={{ fontWeight: 'bold' }}>{statistics.coverage}°</span>
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>Average Angle:</span>
<span style={{ fontWeight: 'bold' }}>{statistics.avgAngle}°</span>
</div>
</div>
</div>
<div style={{
backgroundColor: 'white',
padding: '1.5rem',
borderRadius: '0.5rem',
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1)'
}}>
<h3 style={{
fontSize: '1.25rem',
fontWeight: '600',
marginBottom: '1rem'
}}>
Actions
</h3>
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
<button
onClick={exportData}
style={{
width: '100%',
backgroundColor: '#10b981',
color: 'white',
padding: '0.5rem 1rem',
borderRadius: '0.375rem',
border: 'none',
cursor: 'pointer',
transition: 'background-color 150ms'
}}
onMouseOver={(e) => e.currentTarget.style.backgroundColor = '#059669'}
onMouseOut={(e) => e.currentTarget.style.backgroundColor = '#10b981'}
>
Export Data
</button>
<div style={{
fontSize: '0.875rem',
color: '#6b7280'
}}>
<p>Current theme: <strong>{currentTheme}</strong></p>
<p>Angles: {selectedAngles.join(', ')}°</p>
</div>
</div>
</div>
</div>
</div>
</div>
)
}📋 Props API
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| setOptions | (value: number[]) => void | Required | Callback that receives the selected angles |
| initialTheme | ThemeType | 'light' | Initial component theme |
| initialInterval | number | 15 | Initial interval in degrees (5, 15, or 30) |
🎨 Available Themes
- light: Light theme for daytime use
- dark: Elegant dark theme
- forest: Nature-inspired green theme
- ocean: Maritime blue theme
- sunset: Warm orange theme
- matrix: Matrix-style green theme
- solarized: Soft yellow theme
🔊 Audio Features
The component includes optional sound effects using Tone.js:
- Click sounds when selecting elements
- Swoosh effects for group actions
- Unique sounds for each action (select all, clear, etc.)
- Integrated mute control in the context menu
🎯 TypeScript
The component is fully typed with TypeScript:
import type { ThemeType, QuadrantType } from '@fullstackcolombia/roseselect'💡 No Tailwind CSS Required
This component works standalone without requiring Tailwind CSS in your project:
- ✅ All styles are pre-compiled and included
- ✅ No external CSS dependencies
- ✅ Works with any React setup
- ✅ Compatible with Create React App, Vite, Next.js, etc.
For projects without any styling framework, see our No Tailwind Setup Guide.
🤝 Contributing
Contributions are welcome. Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
MIT © FullStack Colombia
🌐 More Information
For more projects and development resources, visit: FullStack Colombia
