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
This commit is contained in:
@@ -17,6 +17,7 @@ import type {
|
||||
NodeType, EdgeType, NodeTypeStyle, EdgeTypeStyle, CustomStyleDef, EdgePathStyle,
|
||||
} from '@/types'
|
||||
import { NODE_TYPE_LABELS, EDGE_TYPE_LABELS } from '@/types'
|
||||
import { MarkerShapePicker } from './MarkerShapePicker'
|
||||
|
||||
// ── Node types exposed for custom style, grouped by category (skip groupRect/group) ──
|
||||
|
||||
@@ -64,8 +65,8 @@ function defaultEdgeStyle(edgeType: EdgeType): EdgeTypeStyle {
|
||||
opacity: 1,
|
||||
pathStyle: 'bezier',
|
||||
animated: 'none',
|
||||
arrowStart: false,
|
||||
arrowEnd: false,
|
||||
arrowStart: 'none',
|
||||
arrowEnd: 'none',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,24 +284,10 @@ function EdgeEditor({ edgeType, style, onChange, onApplyToExisting }: EdgeEditor
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-xs text-[#8b949e] mb-2">Arrows</div>
|
||||
<div className="flex gap-2">
|
||||
{([['Start', 'arrowStart'], ['End', 'arrowEnd']] as [string, 'arrowStart' | 'arrowEnd'][]).map(([label, key]) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
onClick={() => set(key, !style[key])}
|
||||
aria-pressed={style[key]}
|
||||
className="px-3 py-1 text-xs rounded border transition-colors"
|
||||
style={{
|
||||
borderColor: style[key] ? '#00d4ff' : '#30363d',
|
||||
background: style[key] ? '#00d4ff22' : 'transparent',
|
||||
color: style[key] ? '#00d4ff' : '#8b949e',
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
<div className="text-xs text-[#8b949e] mb-2">Endpoints</div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<MarkerShapePicker label="Start" value={style.arrowStart} onChange={(s) => set('arrowStart', s)} />
|
||||
<MarkerShapePicker label="End" value={style.arrowEnd} onChange={(s) => set('arrowEnd', s)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user