2424 * timeline-arrows may be distributed under either license.
2525 */
2626
27+ // @ts -check
28+
29+ /**
30+ * @typedef {(number | string) } VisIdType Timeline view item id. Equivalent to vis.IdType.
31+ */
32+
33+ /**
34+ * @typedef {(number | string) } ArrowIdType arrow id.
35+ */
36+
37+ /**
38+ * @typedef ArrowSpec Arrow specification
39+ * @property {ArrowIdType } id arrow id
40+ * @property {VisIdType } id_item_1 start timeline item id
41+ * @property {VisIdType } id_item_2 end timeline item id
42+ * @property {string } [title] optional arrow title
43+ */
44+
45+ /**
46+ * @typedef ArrowOptions Arrow configuration options
47+ * @property {boolean } [followRelationships] if true, arrows can point backwards and will follow the relationships set in the data
48+ * @property {(el: SVGPathElement, title: string) => string } [tooltipConfig] if arrows have a `title` property, the default behavior will add a title attribute that shows on hover. However, you might not want to use the title attribute, but instead your own tooltip configuration.
49+ This method takes two arguments, `el` - the arrow - and `title` - the content of the `title` property set in the arrow data.
50+ * @property {string } [color] arrow color
51+ * @property {number } [strokeWidth] arrow thickness in pixels
52+ */
53+
54+ /** Arrow set for a vis.js Timeline. */
2755export default class Arrow {
2856
57+ /**
58+ * Creates arrows.
59+ * @param {* } timeline timeline object
60+ * @param {ArrowSpec[] } dependencies arrows
61+ * @param {ArrowOptions } [options]
62+ */
2963 constructor ( timeline , dependencies , options ) {
3064 this . _svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" ) ;
3165 this . _timeline = timeline ;
3266
67+ /** @private @type {boolean | undefined } if true, arrows can point backwards and will follow the relationships set in the data */
3368 this . _followRelationships = options ?. followRelationships ;
69+ /** @private @type {((el: SVGPathElement, title: string) => string) | undefined } */
70+ this . _tooltipConfig = options ?. tooltipConfig ;
3471
72+ /** @private @type {string } color */
3573 this . _arrowsColor = options ?. color ? options . color : "#9c0000"
74+ /** @private @type {number } arrow thickness in pixels */
75+ this . _arrowsStrokeWidth = options ?. strokeWidth ?? 3 ;
3676
77+ /** @private @type {SVGMarkerElement } */
3778 this . _arrowHead = document . createElementNS (
3879 "http://www.w3.org/2000/svg" ,
3980 "marker"
4081 ) ;
82+ /** @private @type {SVGPathElement } */
4183 this . _arrowHeadPath = document . createElementNS (
4284 "http://www.w3.org/2000/svg" ,
4385 "path"
4486 ) ;
4587
4688 this . _dependency = dependencies ;
4789
90+ /** @private @type {SVGPathElement[] } */
4891 this . _dependencyPath = [ ] ;
4992
5093 this . _initialize ( ) ;
@@ -87,6 +130,7 @@ export default class Arrow {
87130
88131 }
89132
133+ /** @private */
90134 _createPath ( ) {
91135 //Add a new path to array dependencyPath and to svg
92136 let somePath = document . createElementNS (
@@ -95,36 +139,37 @@ export default class Arrow {
95139 ) ;
96140 somePath . setAttribute ( "d" , "M 0 0" ) ;
97141 somePath . style . stroke = this . _arrowsColor ;
98- somePath . style . strokeWidth = "3px ";
142+ somePath . style . strokeWidth = this . _arrowsStrokeWidth + "px ";
99143 somePath . style . fill = "none" ;
100144 somePath . style . pointerEvents = "auto" ;
101145 this . _dependencyPath . push ( somePath ) ;
102146 this . _svg . appendChild ( somePath ) ;
103147 }
104148
105149
106-
150+ /** @private */
107151 _drawDependencies ( ) {
108152 //Create paths for the started dependency array
109153 for ( let i = 0 ; i < this . _dependency . length ; i ++ ) {
110154 this . _drawArrows ( this . _dependency [ i ] , i ) ;
111155 }
112156 }
113157
158+ /**
159+ * @private
160+ * @param {ArrowSpec } dep arrow specification
161+ * @param {number } index arrow index
162+ */
114163 _drawArrows ( dep , index ) {
115164 //Checks if both items exist
116165 //if( (typeof this._timeline.itemsData._data[dep.id_item_1] !== "undefined") && (typeof this._timeline.itemsData._data[dep.id_item_2] !== "undefined") ) {
117166 //debugger;
118- if ( ( this . _timeline . itemsData . get ( dep . id_item_1 ) !== null ) && ( this . _timeline . itemsData . get ( dep . id_item_2 ) !== null ) ) {
119- var bothItemsExist = true ;
120- } else {
121- var bothItemsExist = false ;
122- }
167+ const bothItemsExist = ( this . _timeline . itemsData . get ( dep . id_item_1 ) !== null ) && ( this . _timeline . itemsData . get ( dep . id_item_2 ) !== null ) ;
123168
124169 //Checks if at least one item is visible in screen
125- var oneItemVisible = false ; //Iniciamos a false
170+ let oneItemVisible = false ; //Iniciamos a false
126171 if ( bothItemsExist ) {
127- var visibleItems = this . _timeline . getVisibleItems ( ) ;
172+ const visibleItems = this . _timeline . getVisibleItems ( ) ;
128173 for ( let k = 0 ; k < visibleItems . length ; k ++ ) {
129174 if ( dep . id_item_1 == visibleItems [ k ] ) oneItemVisible = true ;
130175 if ( dep . id_item_2 == visibleItems [ k ] ) oneItemVisible = true ;
@@ -212,7 +257,9 @@ export default class Arrow {
212257
213258 // Adding the title if property title has been added in the dependency
214259 if ( dep . hasOwnProperty ( "title" ) ) {
215- this . _dependencyPath [ index ] . innerHTML = "<title>" + dep . title + "</title>"
260+ this . _tooltipConfig
261+ ? this . _tooltipConfig ( this . _dependencyPath [ index ] , dep . title ?? '' )
262+ : this . _dependencyPath [ index ] . innerHTML = "<title>" + dep . title + "</title>" ;
216263 }
217264 } else {
218265 this . _dependencyPath [ index ] . setAttribute ( "marker-end" , "" ) ;
@@ -221,7 +268,7 @@ export default class Arrow {
221268
222269 }
223270
224- // Función que recibe in Item y devuelve la posición en pantalla del item.
271+ /** @private Función que recibe in Item y devuelve la posición en pantalla del item. */
225272 _getItemPos ( item ) {
226273 let left_x = item . left ;
227274 let top_y ;
@@ -243,38 +290,52 @@ export default class Arrow {
243290 }
244291
245292
246- addArrow ( dep ) {
293+ /**
294+ * Adds arrow between two timeline items.
295+ * @param {ArrowSpec } dep item dependency
296+ */
297+ addArrow ( dep ) {
247298 this . _dependency . push ( dep ) ;
248299 this . _createPath ( ) ;
249300 this . _timeline . redraw ( ) ;
250301 }
251302
252- getArrow ( id ) {
253- for ( let i = 0 ; i < this . _dependency . length ; i ++ ) {
254- if ( this . _dependency [ i ] . id == id ) {
255- return this . _dependency [ i ] ;
256- }
257- }
258- return null ;
303+ /**
304+ * Get arrow by ID.
305+ * @param { ArrowIdType } id arrow ID
306+ * @returns { ArrowSpec | null } arrow spec, or null
307+ */
308+ getArrow ( id ) {
309+ return this . _dependency . find ( dep => dep . id === id ) ?? null ;
259310 }
260311
261- //Función que recibe el id de una flecha y la elimina.
312+ /**
313+ * Finds arrow with the given id and removes it.
314+ * Función que recibe el id de una flecha y la elimina.
315+ * @param {ArrowIdType } id arrow id
316+ */
262317 removeArrow ( id ) {
263- for ( let i = 0 ; i < this . _dependency . length ; i ++ ) {
264- if ( this . _dependency [ i ] . id == id ) var index = i ;
265- }
318+ const index = this . _dependency . findIndex ( dep => dep . id === id ) ;
266319
267- //var list = document.getElementsByTagName("path"); //FALTA QUE ESTA SELECCION LA HAGA PARA EL DOM DEL TIMELINE INSTANCIADO!!!!
268- var list = document . querySelectorAll ( "#" + this . _timeline . dom . container . id + " path" ) ;
320+ if ( index >= 0 ) {
269321
270- this . _dependency . splice ( index , 1 ) ; //Elimino del array dependency
271- this . _dependencyPath . splice ( index , 1 ) ; //Elimino del array dependencyPath
322+ //var list = document.getElementsByTagName("path"); //FALTA QUE ESTA SELECCION LA HAGA PARA EL DOM DEL TIMELINE INSTANCIADO!!!!
323+ const list = document . querySelectorAll ( "#" + this . _timeline . dom . container . id + " path" ) ;
324+
325+ this . _dependency . splice ( index , 1 ) ; //Elimino del array dependency
326+ this . _dependencyPath . splice ( index , 1 ) ; //Elimino del array dependencyPath
272327
273- list [ index + 1 ] . parentNode . removeChild ( list [ index + 1 ] ) ; //Lo elimino del dom
328+ list [ index + 1 ] . parentNode . removeChild ( list [ index + 1 ] ) ; //Lo elimino del dom
329+ }
274330 }
275331
276- //Función que recibe el id de un item y elimina la flecha.
277- removeArrowbyItemId ( id ) {
332+ /**
333+ * Finds all arrows related to one view item and removes them all.
334+ * Función que recibe el id de un item y elimina la flecha.
335+ * @param {VisIdType } id view item id
336+ * @returns {(ArrowIdType)[] } list of removed arrow ids
337+ */
338+ removeItemArrows ( id ) {
278339 let listOfRemovedArrows = [ ] ;
279340 for ( let i = 0 ; i < this . _dependency . length ; i ++ ) {
280341 if ( ( this . _dependency [ i ] . id_item_1 == id ) || ( this . _dependency [ i ] . id_item_2 == id ) ) {
@@ -286,6 +347,12 @@ export default class Arrow {
286347 return listOfRemovedArrows ;
287348 }
288349
289-
350+ /**
351+ * For backward compatibility
352+ * @deprecated use the removeItemArrows method instead.
353+ */
354+ removeArrowbyItemId ( id ) {
355+ this . removeItemArrows ( id ) ;
356+ }
290357
291358 }
0 commit comments