4040 * @property {VisIdType } id_item_1 start timeline item id
4141 * @property {VisIdType } id_item_2 end timeline item id
4242 * @property {string } [title] optional arrow title
43+ * @property {string } [color] optional arrow color
44+ * @property {number } [direction] arrow direction: 0=no arrows, 1=forward only, 2=backward only, 3=both directions
45+ * @property {number } [line] line type: 0=solid (default), 1=dashed
4346 */
4447
4548/**
@@ -78,16 +81,8 @@ export default class Arrow {
7881 /** @private @type {boolean } if true, arrows will be hidden when both items is not visible due to timeline zoom */
7982 this . _hideWhenItemsNotVisible = options ?. hideWhenItemsNotVisible ?? true ;
8083
81- /** @private @type {SVGMarkerElement } */
82- this . _arrowHead = document . createElementNS (
83- "http://www.w3.org/2000/svg" ,
84- "marker"
85- ) ;
86- /** @private @type {SVGPathElement } */
87- this . _arrowHeadPath = document . createElementNS (
88- "http://www.w3.org/2000/svg" ,
89- "path"
90- ) ;
84+ /** @private @type {Map<string, string> } map of color to marker id */
85+ this . _colorMarkers = new Map ( ) ;
9186
9287 this . _dependency = [ ...dependencies ] ;
9388
@@ -111,23 +106,9 @@ export default class Arrow {
111106 this . _svg . style . pointerEvents = "none" ; // To click through, if we decide to put it above other elements.
112107 this . _timeline . dom . center . appendChild ( this . _svg ) ;
113108
114- //Configure the arrowHead
115- this . _arrowHead . setAttribute ( "id" , this . _arrowHeadId ) ;
116- this . _arrowHead . setAttribute ( "viewBox" , "-10 -5 10 10" ) ;
117- this . _arrowHead . setAttribute ( "refX" , "-7" ) ;
118- this . _arrowHead . setAttribute ( "refY" , "0" ) ;
119- this . _arrowHead . setAttribute ( "markerUnits" , "strokeWidth" ) ;
120- this . _arrowHead . setAttribute ( "markerWidth" , "3" ) ;
121- this . _arrowHead . setAttribute ( "markerHeight" , "3" ) ;
122- this . _arrowHead . setAttribute ( "orient" , "auto-start-reverse" ) ;
123- //Configure the path of the arrowHead (arrowHeadPath)
124- this . _arrowHeadPath . setAttribute ( "d" , "M 0 0 L -10 -5 L -7.5 0 L -10 5 z" ) ;
125- this . _arrowHeadPath . style . fill = this . _arrowsColor ;
126- this . _arrowHead . appendChild ( this . _arrowHeadPath ) ;
127- this . _svg . appendChild ( this . _arrowHead ) ;
128109 //Create paths for the started dependency array
129110 for ( let i = 0 ; i < this . _dependency . length ; i ++ ) {
130- this . _createPath ( ) ;
111+ this . _createPath ( this . _dependency [ i ] . color , this . _dependency [ i ] . line ) ;
131112 }
132113
133114 //NOTE: We hijack the on "changed" event to draw the arrows.
@@ -136,19 +117,68 @@ export default class Arrow {
136117 } ) ;
137118
138119 }
120+
121+ /** @private */
122+ _getOrCreateMarker ( color ) {
123+ const arrowColor = color || this . _arrowsColor ;
124+
125+ if ( ! this . _colorMarkers . has ( arrowColor ) ) {
126+ const markerId = `arrowhead-${ arrowColor . replace ( '#' , '' ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 2 ) } ` ;
127+
128+ const arrowHead = document . createElementNS ( "http://www.w3.org/2000/svg" , "marker" ) ;
129+ arrowHead . setAttribute ( "id" , markerId ) ;
130+ arrowHead . setAttribute ( "viewBox" , "-10 -5 10 10" ) ;
131+ arrowHead . setAttribute ( "refX" , "-7" ) ;
132+ arrowHead . setAttribute ( "refY" , "0" ) ;
133+ arrowHead . setAttribute ( "markerUnits" , "strokeWidth" ) ;
134+ arrowHead . setAttribute ( "markerWidth" , "3" ) ;
135+ arrowHead . setAttribute ( "markerHeight" , "3" ) ;
136+ arrowHead . setAttribute ( "orient" , "auto-start-reverse" ) ;
137+
138+ const arrowHeadPath = document . createElementNS ( "http://www.w3.org/2000/svg" , "path" ) ;
139+ arrowHeadPath . setAttribute ( "d" , "M 0 0 L -10 -5 L -7.5 0 L -10 5 z" ) ;
140+ arrowHeadPath . style . fill = arrowColor ;
141+
142+ arrowHead . appendChild ( arrowHeadPath ) ;
143+ this . _svg . appendChild ( arrowHead ) ;
144+
145+ this . _colorMarkers . set ( arrowColor , markerId ) ;
146+ }
147+
148+ return this . _colorMarkers . get ( arrowColor ) ;
149+ }
139150
140151 /** @private */
141- _createPath ( ) {
152+ _createPath ( color , lineType ) {
142153 //Add a new path to array dependencyPath and to svg
143154 let somePath = document . createElementNS (
144155 "http://www.w3.org/2000/svg" ,
145156 "path"
146157 ) ;
147158 somePath . setAttribute ( "d" , "M 0 0" ) ;
148- somePath . style . stroke = this . _arrowsColor ;
159+ somePath . style . stroke = color || this . _arrowsColor ;
149160 somePath . style . strokeWidth = this . _arrowsStrokeWidth + "px" ;
150161 somePath . style . fill = "none" ;
151162 somePath . style . pointerEvents = "auto" ;
163+
164+ // Устанавливаем тип линии
165+ const line = lineType !== undefined ? lineType : 0 ; // По умолчанию сплошная линия
166+ if ( line === 0 ) {
167+ // Тип 0: Сплошная линия (по умолчанию)
168+ somePath . style . strokeDasharray = "none" ;
169+ } else if ( line === 1 ) {
170+ // Тип 1: Пунктирная линия
171+ somePath . style . strokeDasharray = "7,5" ;
172+ }
173+ // Здесь можно добавить дополнительные типы линий:
174+ // } else if (line === 2) {
175+ // // Тип 2: Точечная линия
176+ // somePath.style.strokeDasharray = "2,3";
177+ // } else if (line === 3) {
178+ // // Тип 3: Штрих-пунктир
179+ // somePath.style.strokeDasharray = "10,5,2,5";
180+ // }
181+
152182 this . _dependencyPath . push ( somePath ) ;
153183 this . _svg . appendChild ( somePath ) ;
154184 }
@@ -215,10 +245,48 @@ export default class Arrow {
215245
216246 var curveLen = item_1 . height * 2 ; // Length of straight Bezier segment out of the item.
217247
248+ const markerId = this . _getOrCreateMarker ( dep . color ) ;
249+ const direction = dep . direction !== undefined ? dep . direction : 1 ; // По умолчанию направление вперед
250+
251+ // Определяем какие маркеры нужно установить в зависимости от direction
252+ let markerStart = "" ;
253+ let markerEnd = "" ;
254+
255+ if ( direction === 0 ) {
256+ // Без стрелок
257+ markerStart = "" ;
258+ markerEnd = "" ;
259+ } else if ( direction === 1 ) {
260+ // Направление вперед (по умолчанию)
261+ if ( this . _followRelationships && item_2 . mid_x < item_1 . mid_x ) {
262+ markerStart = `url(#${ markerId } )` ;
263+ markerEnd = "" ;
264+ } else {
265+ markerStart = "" ;
266+ markerEnd = `url(#${ markerId } )` ;
267+ }
268+ } else if ( direction === 2 ) {
269+ // Направление назад
270+ if ( this . _followRelationships && item_2 . mid_x < item_1 . mid_x ) {
271+ markerStart = "" ;
272+ markerEnd = `url(#${ markerId } )` ;
273+ } else {
274+ markerStart = `url(#${ markerId } )` ;
275+ markerEnd = "" ;
276+ }
277+ } else if ( direction === 3 ) {
278+ // Обе стороны
279+ markerStart = `url(#${ markerId } )` ;
280+ markerEnd = `url(#${ markerId } )` ;
281+ }
282+
218283 if ( this . _followRelationships && item_2 . mid_x < item_1 . mid_x ) {
219- item_2 . right += 10 ; // Space for the arrowhead.
220- this . _dependencyPath [ index ] . setAttribute ( "marker-start" , `url(#${ this . _arrowHeadId } )` ) ;
221- this . _dependencyPath [ index ] . setAttribute ( "marker-end" , "" ) ;
284+ // Добавляем отступы только там, где есть стрелки
285+ if ( markerStart !== "" ) item_2 . right += 10 ; // Space for the arrowhead at start
286+ if ( markerEnd !== "" ) item_1 . left -= 10 ; // Space for the arrowhead at end
287+
288+ this . _dependencyPath [ index ] . setAttribute ( "marker-start" , markerStart ) ;
289+ this . _dependencyPath [ index ] . setAttribute ( "marker-end" , markerEnd ) ;
222290 this . _dependencyPath [ index ] . setAttribute (
223291 "d" ,
224292 "M " +
@@ -239,9 +307,12 @@ export default class Arrow {
239307 item_1 . mid_y
240308 ) ;
241309 } else {
242- item_2 . left -= 10 ; // Space for the arrowhead.
243- this . _dependencyPath [ index ] . setAttribute ( "marker-end" , `url(#${ this . _arrowHeadId } )` ) ;
244- this . _dependencyPath [ index ] . setAttribute ( "marker-start" , "" ) ;
310+ // Добавляем отступы только там, где есть стрелки
311+ if ( markerEnd !== "" ) item_2 . left -= 10 ; // Space for the arrowhead at end
312+ if ( markerStart !== "" ) item_1 . right += 10 ; // Space for the arrowhead at start
313+
314+ this . _dependencyPath [ index ] . setAttribute ( "marker-end" , markerEnd ) ;
315+ this . _dependencyPath [ index ] . setAttribute ( "marker-start" , markerStart ) ;
245316 this . _dependencyPath [ index ] . setAttribute (
246317 "d" ,
247318 "M " +
@@ -304,7 +375,7 @@ export default class Arrow {
304375 */
305376 addArrow ( dep ) {
306377 this . _dependency . push ( dep ) ;
307- this . _createPath ( ) ;
378+ this . _createPath ( dep . color , dep . line ) ;
308379 this . _timeline . redraw ( ) ;
309380 }
310381
@@ -332,19 +403,21 @@ export default class Arrow {
332403 * Función que recibe el id de una flecha y la elimina.
333404 * @param {ArrowIdType } id arrow id
334405 */
335- removeArrow ( id ) {
406+ removeArrow ( id ) {
336407 const index = this . _dependency . findIndex ( dep => dep . id === id ) ;
337408
338409 if ( index >= 0 ) {
410+ // Get the path element from our specific array before modifying the arrays
411+ const pathToRemove = this . _dependencyPath [ index ] ;
339412
340- //var list = document.getElementsByTagName("path"); //FALTA QUE ESTA SELECCION LA HAGA PARA EL DOM DEL TIMELINE INSTANCIADO!!!!
341- const list = document . querySelectorAll ( "#" + this . _timeline . dom . container . id + " path" ) ;
413+ // Remove the SVG element from the DOM
414+ if ( pathToRemove && pathToRemove . parentNode ) {
415+ pathToRemove . parentNode . removeChild ( pathToRemove ) ;
416+ }
342417
343- this . _dependency . splice ( index , 1 ) ; //Elimino del array dependency
344- this . _dependencyPath . splice ( index , 1 ) ; //Elimino del array dependencyPath
345-
346- list [ index + 1 ] . parentNode ?. removeChild ( list [ index + 1 ] ) ; //Lo elimino del dom
347-
418+ // Now, remove the arrow from internal arrays
419+ this . _dependency . splice ( index , 1 ) ;
420+ this . _dependencyPath . splice ( index , 1 ) ;
348421 }
349422 }
350423
@@ -391,4 +464,4 @@ export default class Arrow {
391464 this . removeItemArrows ( id ) ;
392465 }
393466
394- }
467+ }
0 commit comments