changed the color sort to use hue so that when custom colors are introduced it will sort in a way that is logically consistent.
This commit is contained in:
parent
b043016299
commit
a09d945b41
|
|
@ -167,11 +167,19 @@ export default class MapsController {
|
|||
vine.object({
|
||||
name: vine.string().trim().minLength(1).maxLength(255).optional(),
|
||||
color: vine.string().trim().maxLength(20).optional(),
|
||||
longitude: vine.number().min(-180).max(180).optional(),
|
||||
latitude: vine.number().min(-90).max(90).optional(),
|
||||
notes: vine.string().trim().nullable().optional(),
|
||||
marker_type: vine.string().trim().maxLength(20).optional(),
|
||||
})
|
||||
)
|
||||
)
|
||||
if (payload.name !== undefined) marker.name = payload.name
|
||||
if (payload.color !== undefined) marker.color = payload.color
|
||||
if (payload.longitude !== undefined) marker.longitude = payload.longitude
|
||||
if (payload.latitude !== undefined) marker.latitude = payload.latitude
|
||||
if (payload.notes !== undefined) marker.notes = payload.notes
|
||||
if (payload.marker_type !== undefined) marker.marker_type = payload.marker_type
|
||||
await marker.save()
|
||||
return marker
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import MarkerPin from './MarkerPin'
|
|||
import MarkerPanel from './MarkerPanel'
|
||||
import ViewMapMarkerPopup from './ViewMapMarkerPopup'
|
||||
import MapMarkerFormPopup from './MapMarkerFormPopup'
|
||||
import ScaleUnitControl from './ScaleUnitControl'
|
||||
import ScaleUnitToggle from './ScaleUnitToggle'
|
||||
|
||||
type ScaleUnit = 'imperial' | 'metric'
|
||||
|
||||
|
|
@ -94,7 +94,7 @@ export default function MapComponent() {
|
|||
<NavigationControl style={{ marginTop: '110px', marginRight: '36px' }} />
|
||||
<FullscreenControl style={{ marginTop: '30px', marginRight: '36px' }} />
|
||||
<ScaleControl position="bottom-left" maxWidth={150} unit={scaleUnit} />
|
||||
<ScaleUnitControl scaleUnit={scaleUnit} onChange={handleScaleUnitChange} />
|
||||
<ScaleUnitToggle scaleUnit={scaleUnit} onChange={handleScaleUnitChange} />
|
||||
|
||||
{markers.map((marker) => (
|
||||
<Marker
|
||||
|
|
@ -136,14 +136,6 @@ export default function MapComponent() {
|
|||
/>
|
||||
)}
|
||||
|
||||
{selectedMarker && editingMarkerId !== selectedMarker.id && (
|
||||
<ViewMapMarkerPopup
|
||||
marker={selectedMarker}
|
||||
onClose={() => setSelectedMarkerId(null)}
|
||||
onEdit={() => setEditingMarkerId(selectedMarker.id)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedMarker && editingMarkerId === selectedMarker.id && (
|
||||
<MapMarkerFormPopup
|
||||
longitude={selectedMarker.longitude}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,42 @@ interface MarkerPanelProps {
|
|||
selectedMarkerId: number | null
|
||||
}
|
||||
|
||||
const getColorSortValue = (color: string) => {
|
||||
const preset = PIN_COLORS.find((pinColor) => pinColor.id === color)
|
||||
|
||||
if (preset) return preset.label
|
||||
|
||||
return color.replace('#', '').toLowerCase()
|
||||
}
|
||||
|
||||
type SortField = 'name' | 'color'
|
||||
type SortDirection = 'asc' | 'desc'
|
||||
|
||||
const normalizeColorHex = (color: string) => {
|
||||
const preset = PIN_COLORS.find((pinColor) => pinColor.id === color)
|
||||
return preset?.hex ?? color
|
||||
}
|
||||
|
||||
const getHueSortValue = (color: string) => {
|
||||
const hex = normalizeColorHex(color).replace('#', '')
|
||||
|
||||
if (!/^[0-9a-fA-F]{6}$/.test(hex)) return 0
|
||||
|
||||
const r = parseInt(hex.slice(0, 2), 16) / 255
|
||||
const g = parseInt(hex.slice(2, 4), 16) / 255
|
||||
const b = parseInt(hex.slice(4, 6), 16) / 255
|
||||
|
||||
const max = Math.max(r, g, b)
|
||||
const min = Math.min(r, g, b)
|
||||
const delta = max - min
|
||||
|
||||
if (delta === 0) return 0
|
||||
|
||||
let hue = 0
|
||||
|
||||
if (max === r) {
|
||||
hue = ((g - b) / delta) % 6
|
||||
} else if (max === g) {
|
||||
hue = (b - r) / delta + 2
|
||||
} else {
|
||||
hue = (r - g) / delta + 4
|
||||
}
|
||||
|
||||
return Math.round(hue * 60 + 360) % 360
|
||||
}
|
||||
|
||||
export default function MarkerPanel({
|
||||
markers,
|
||||
onDelete,
|
||||
|
|
@ -35,6 +60,15 @@ export default function MarkerPanel({
|
|||
const [sortField, setSortField] = useState<SortField>('name')
|
||||
const [sortDirection, setSortDirection] = useState<SortDirection>('asc')
|
||||
|
||||
const sortDirectionLabel =
|
||||
sortField === 'name'
|
||||
? sortDirection === 'asc'
|
||||
? 'A → Z'
|
||||
: 'Z → A'
|
||||
: sortDirection === 'asc'
|
||||
? 'Hue ↑'
|
||||
: 'Hue ↓'
|
||||
|
||||
const visibleMarkers = useMemo(() => {
|
||||
const query = searchQuery.trim().toLowerCase()
|
||||
|
||||
|
|
@ -44,12 +78,10 @@ export default function MarkerPanel({
|
|||
return marker.name.toLowerCase().includes(query)
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const aValue = sortField === 'name' ? a.name : getColorSortValue(a.color)
|
||||
const bValue = sortField === 'name' ? b.name : getColorSortValue(b.color)
|
||||
|
||||
const result = aValue.localeCompare(bValue, undefined, {
|
||||
sensitivity: 'base',
|
||||
})
|
||||
const result =
|
||||
sortField === 'name'
|
||||
? a.name.localeCompare(b.name, undefined, { sensitivity: 'base' })
|
||||
: getHueSortValue(a.color) - getHueSortValue(b.color)
|
||||
|
||||
return sortDirection === 'asc' ? result : -result
|
||||
})
|
||||
|
|
@ -116,7 +148,7 @@ export default function MarkerPanel({
|
|||
className="flex-1 rounded border border-border-default bg-surface-primary px-2 py-1 text-xs text-text-primary focus:border-desert-green focus:outline-none"
|
||||
>
|
||||
<option value="name">Sort by name</option>
|
||||
<option value="color">Sort by color</option>
|
||||
<option value="color">Sort by hue</option>
|
||||
</select>
|
||||
|
||||
<button
|
||||
|
|
@ -124,7 +156,7 @@ export default function MarkerPanel({
|
|||
onClick={() => setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc'))}
|
||||
className="rounded border border-border-default px-2 py-1 text-xs text-text-secondary transition-colors hover:bg-surface-secondary"
|
||||
>
|
||||
{sortDirection === 'asc' ? 'A → Z' : 'Z → A'}
|
||||
{sortDirectionLabel}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -154,7 +186,7 @@ export default function MarkerPanel({
|
|||
size={16}
|
||||
className="shrink-0"
|
||||
style={{
|
||||
color: PIN_COLORS.find((color) => color.id === marker.color)?.hex ?? '#a84a12',
|
||||
color: normalizeColorHex(marker.color),
|
||||
}}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ type ScaleUnitControlProps = {
|
|||
onChange: (unit: ScaleUnit) => void
|
||||
}
|
||||
|
||||
export default function ScaleUnitControl({ scaleUnit, onChange }: ScaleUnitControlProps) {
|
||||
export default function ScaleUnitToggle({ scaleUnit, onChange }: ScaleUnitControlProps) {
|
||||
return (
|
||||
<div style={{ position: 'absolute', bottom: '30px', left: '10px', zIndex: 2 }}>
|
||||
<div
|
||||
|
|
@ -28,7 +28,21 @@ export default function ViewMapMarkerPopup({
|
|||
|
||||
{marker.notes && (
|
||||
<div className="mt-1 text-xs text-gray-500">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
a: ({ href, children }) => (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 underline hover:text-blue-700"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{marker.notes}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue