Files
homelable/frontend/src/utils/edgeMarkers.ts
T
Pouzor c95d104245 feat: selectable marker shapes per edge endpoint
Replace the on/off arrowhead toggle with a per-end shape picker. Each end
(start/end) independently selects: none, arrow, arrow-open, circle, diamond,
or square. Markers still recolor live from the resolved stroke color.

Frontend:
- MarkerShape type + edgeMarkers util (normalizeMarker, MARKER_GEOMETRY);
  legacy boolean coerces to 'arrow' on read.
- Per-shape <marker> inner geometry; symmetric shapes use fixed orient.
- MarkerShapePicker reused in EdgeModal and CustomStyleModal.
- Serializer normalizes to shape strings.

Backend:
- Edge marker columns Boolean -> String (default 'none'); TEXT migration.
- normalize_marker() + validators coerce legacy bool / unknown values.

ha-relevant: yes
2026-07-05 11:30:49 +02:00

43 lines
1.5 KiB
TypeScript

import type { MarkerShape } from '@/types'
/** All selectable marker shapes, in picker order. */
export const MARKER_SHAPES: MarkerShape[] = [
'none', 'arrow', 'arrow-open', 'circle', 'diamond', 'square',
]
const SHAPE_SET = new Set<string>(MARKER_SHAPES)
/**
* Coerce any stored/legacy marker value into a MarkerShape.
* - legacy boolean `true` → 'arrow'
* - legacy boolean `false` / null / undefined → 'none'
* - a valid shape string passes through
* - anything unknown → 'none'
*/
export function normalizeMarker(v: unknown): MarkerShape {
if (v === true) return 'arrow'
if (v === false || v == null) return 'none'
if (typeof v === 'string' && SHAPE_SET.has(v)) return v as MarkerShape
return 'none'
}
export type NonNoneMarkerShape = Exclude<MarkerShape, 'none'>
/**
* SVG <marker> geometry per shape, drawn in a 0..10 viewBox.
* - `refX` positions the shape on the endpoint: directional shapes (arrow,
* arrow-open) put their tip on the point; symmetric caps (circle, diamond,
* square) centre on it.
* - `directional` shapes rotate with the edge; symmetric ones don't care.
*/
export const MARKER_GEOMETRY: Record<
NonNoneMarkerShape,
{ refX: number; directional: boolean }
> = {
arrow: { refX: 9, directional: true },
'arrow-open': { refX: 8.5, directional: true },
circle: { refX: 5, directional: false },
diamond: { refX: 5, directional: false },
square: { refX: 5, directional: false },
}