@osfarm/itineraire-technique 1.1.8 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/styles-rendering.css +1 -1
- package/css/styles-rendering.css.map +1 -1
- package/js/chart-render.js +269 -195
- package/js/editor-wiki-editor.js +7 -0
- package/package.json +1 -1
- package/scss/styles-rendering.scss +17 -5
- package/test/test.json +442 -427
package/css/styles-rendering.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
div.mainITKContainer{container-type:inline-size;container-name:myparent;line-height:1rem}div.mainITKContainer .left-transcript{display:none;width:50%;font-family:Segoe UI}div.mainITKContainer .chart-div{width:100%}div.mainITKContainer .bottom-transcript{display:none}div.mainITKContainer.withTranscript .bottom-transcript{display:block;font-family:Segoe UI}@container myparent (min-width: 800px){div.mainITKContainer.withTranscript .left-transcript{display:block}div.mainITKContainer.withTranscript .chart-div{width:50%}div.mainITKContainer.withTranscript .bottom-transcript{display:none}}@container myparent (min-width:
|
|
1
|
+
div.mainITKContainer{container-type:inline-size;container-name:myparent;line-height:1rem}div.mainITKContainer .left-transcript{display:none;width:50%;font-family:Segoe UI}div.mainITKContainer .chart-div{width:100%}div.mainITKContainer .bottom-transcript{display:none}div.mainITKContainer.withTranscript .bottom-transcript{display:block;font-family:Segoe UI}@container myparent (min-width: 800px){div.mainITKContainer.withTranscript .left-transcript{display:block}div.mainITKContainer.withTranscript .chart-div{width:50%}div.mainITKContainer.withTranscript .bottom-transcript{display:none}}@container myparent (min-width: 1000px){div.mainITKContainer.withTranscript .left-transcript{display:block;width:40%}div.mainITKContainer.withTranscript .chart-div{width:60%}div.mainITKContainer.withTranscript .bottom-transcript{display:none}}div.mainITKContainer div.rotation_item{background-color:#f8fafc;border-left:#fff 14px solid;border-radius:7px;padding:8px 3px 5px 9px;margin:12px 0;overflow-x:hidden}div.mainITKContainer div.rotation_item.highlighted{box-shadow:0px 4px 4px 0px var(--UI-Shadow, rgba(17, 36, 69, 0.16)),0px 1px 16px 0px var(--UI-Shadow, rgba(17, 36, 69, 0.16));background-color:#f0f3f5}div.mainITKContainer div.rotation_item div.step-header h4{font-size:20px;margin-bottom:0;margin-top:0}div.mainITKContainer div.rotation_item div.step-header div.step_dates{font-size:11px;background-color:#cdcccc;color:#000;margin:5px;padding:2px 5px;border-radius:5px;height:20px;float:right}div.mainITKContainer div.rotation_item div.step-header div.collapse-button{border-radius:50%;width:30px;height:30px;text-align:center;padding:5px 0px;margin:0 5px 3px 0;cursor:pointer;background:#dfe6f7;color:#7a8bad;float:right;transition:transform .3s ease-in-out}div.mainITKContainer div.rotation_item .step_description{clear:both}div.mainITKContainer div.rotation_item.show-all div.collapse-button{transform:rotate(180deg)}div.mainITKContainer div.rotation_item.show-all div.details{max-height:10000px}div.mainITKContainer div.rotation_item div.details{max-height:0px;overflow:hidden;transition:max-height .3s ease-in-out}div.mainITKContainer div.rotation_item div.details div.intervention{background-color:#fff;border-radius:5px;margin-bottom:11px;padding:13px;cursor:pointer}div.mainITKContainer div.rotation_item div.details div.intervention span.intervention_title{font-weight:bold}div.mainITKContainer div.rotation_item div.details div.intervention span.intervention_date{color:#707070;background-color:#f0f3f5;float:right}div.mainITKContainer div.rotation_item div.details div.intervention div.intervention_description{margin-top:5px}div.mainITKContainer div.rotation_item .step-edit{display:none}div.mainITKContainer .charts{width:100%;height:500px;display:inline-block}div.mainITKContainer .transcript{font-size:80%;width:100%;max-height:450px;overflow-y:scroll;scroll-behavior:smooth;padding:3px}.rotation-tooltip{max-width:400px;text-align:left}.rotation-tooltip div.step_dates{font-size:9px;background-color:#cdcccc;color:#000;margin:5px;padding:0px 5px;border-radius:5px;float:right}/*# sourceMappingURL=styles-rendering.css.map */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../scss/styles-rendering.scss"],"names":[],"mappings":"AAAA,qBACI,2BACA,wBACA,iBAEA,sCACI,aACA,UACA,qBAGJ,gCACI,WAGJ,wCACI,aAIA,uDACI,cACA,qBAIR,uCAEQ,qDACI,cAGJ,+CACI,UAGJ,uDACI,cAKZ,wCAEQ,qDACI,cACA,UAGJ,+CACI,UAGJ,uDACI,cAKZ,uCACI,yBACA,4BACA,kBACA,wBACA,cACA,kBAEA,mDACI,8HAEA,yBAIA,0DACI,gBACA,aAGJ,sEACI,eACA,yBACA,WACA,WACA,gBACA,kBACA,YACA,YAGJ,2EACI,kBACA,WACA,YACA,kBACA,gBACA,mBACA,eACA,mBACA,cACA,YACA,qCAIR,yDACI,WAIA,oEACI,yBAGJ,4DACI,mBAKR,mDACI,eACA,gBACA,sCAEA,oEACI,sBACA,kBACA,mBACA,aACA,eAEA,4FACI,iBAGJ,2FACI,cACA,yBACA,YAGJ,iGACI,eAKZ,kDACI,aAIR,6BACI,WACA,aACA,qBAGJ,iCACI,cACA,WACA,iBACA,kBACA,uBACA,YAIR,kBACI","file":"styles-rendering.css"}
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../scss/styles-rendering.scss"],"names":[],"mappings":"AAAA,qBACI,2BACA,wBACA,iBAEA,sCACI,aACA,UACA,qBAGJ,gCACI,WAGJ,wCACI,aAIA,uDACI,cACA,qBAIR,uCAEQ,qDACI,cAGJ,+CACI,UAGJ,uDACI,cAKZ,wCAEQ,qDACI,cACA,UAGJ,+CACI,UAGJ,uDACI,cAKZ,uCACI,yBACA,4BACA,kBACA,wBACA,cACA,kBAEA,mDACI,8HAEA,yBAIA,0DACI,eACA,gBACA,aAGJ,sEACI,eACA,yBACA,WACA,WACA,gBACA,kBACA,YACA,YAGJ,2EACI,kBACA,WACA,YACA,kBACA,gBACA,mBACA,eACA,mBACA,cACA,YACA,qCAIR,yDACI,WAIA,oEACI,yBAGJ,4DACI,mBAKR,mDACI,eACA,gBACA,sCAEA,oEACI,sBACA,kBACA,mBACA,aACA,eAEA,4FACI,iBAGJ,2FACI,cACA,yBACA,YAGJ,iGACI,eAKZ,kDACI,aAIR,6BACI,WACA,aACA,qBAGJ,iCACI,cACA,WACA,iBACA,kBACA,uBACA,YAIR,kBACI,gBACA,gBAEA,iCACI,cACA,yBACA,WACA,WACA,gBACA,kBACA","file":"styles-rendering.css"}
|
package/js/chart-render.js
CHANGED
|
@@ -39,17 +39,35 @@ class RotationRenderer {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
fixRotationData(rotationData) {
|
|
42
|
+
if (!rotationData || typeof rotationData !== 'object') return rotationData;
|
|
43
|
+
|
|
42
44
|
if (rotationData.options == undefined)
|
|
43
45
|
rotationData.options = {};
|
|
44
46
|
|
|
45
47
|
if (rotationData.options?.view == undefined || rotationData?.options?.view == '')
|
|
46
48
|
rotationData.options.view = 'horizontal';
|
|
47
49
|
|
|
50
|
+
// Safety check for steps array
|
|
51
|
+
if (!rotationData.steps || !Array.isArray(rotationData.steps)) {
|
|
52
|
+
rotationData.steps = [];
|
|
53
|
+
return rotationData;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
// Map rotationData items to make sure that the startDate and endDate are proper Date objects
|
|
49
57
|
rotationData.steps.map((item) => {
|
|
58
|
+
if (!item) return item;
|
|
59
|
+
|
|
50
60
|
item.startDate = new Date(item.startDate);
|
|
51
61
|
item.endDate = new Date(item.endDate);
|
|
52
62
|
|
|
63
|
+
// Safety check for invalid dates
|
|
64
|
+
if (isNaN(item.startDate.getTime())) {
|
|
65
|
+
item.startDate = new Date();
|
|
66
|
+
}
|
|
67
|
+
if (isNaN(item.endDate.getTime()) || item.endDate <= item.startDate) {
|
|
68
|
+
item.endDate = new Date(item.startDate.getTime() + (30 * 24 * 60 * 60 * 1000)); // Default to 30 days later
|
|
69
|
+
}
|
|
70
|
+
|
|
53
71
|
// Add a duration in months
|
|
54
72
|
item.duration = Math.round((item.endDate - item.startDate) / (30 * 1000 * 60 * 60 * 24));
|
|
55
73
|
|
|
@@ -142,7 +160,7 @@ class RotationRenderer {
|
|
|
142
160
|
self.noFocusUpdate = false;
|
|
143
161
|
}, 1500);
|
|
144
162
|
|
|
145
|
-
element[0].scrollIntoView({ block: "start" });
|
|
163
|
+
element[0].scrollIntoView({ block: "start", block: "nearest" });
|
|
146
164
|
|
|
147
165
|
self.getVisibleTranscriptDiv().find('.' + params.data.divId).closest('.rotation_item').addClass("show-all");
|
|
148
166
|
|
|
@@ -256,11 +274,14 @@ class RotationRenderer {
|
|
|
256
274
|
let maxDate = null;
|
|
257
275
|
|
|
258
276
|
steps.forEach((item) => {
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
const stepStart = item.startDate.valueOf() - 86400000 * 30; // Subtract some space for the start of the arrow
|
|
278
|
+
const stepEnd = item.endDate.valueOf() + 86400000 * 50; // Add some space for the end of the arrow
|
|
261
279
|
|
|
262
|
-
if (!
|
|
263
|
-
|
|
280
|
+
if (!minDate || minDate > stepStart)
|
|
281
|
+
minDate = stepStart;
|
|
282
|
+
|
|
283
|
+
if (!maxDate || maxDate < stepEnd)
|
|
284
|
+
maxDate = stepEnd;
|
|
264
285
|
});
|
|
265
286
|
|
|
266
287
|
return { min: minDate, max: maxDate };
|
|
@@ -276,6 +297,15 @@ class RotationRenderer {
|
|
|
276
297
|
if (item.name == Number(item.name))
|
|
277
298
|
item.name = "Etape " + item.name; // Force the item name to be a string
|
|
278
299
|
|
|
300
|
+
let description = self.getHTMLFormatedDescription(item.description);
|
|
301
|
+
if (item.interventions?.length > 0 || item.attributes?.length > 0) {
|
|
302
|
+
description += '<br/>';
|
|
303
|
+
item.attributes?.forEach(attr => {
|
|
304
|
+
description += `<br><b>${attr.name} :</b> ${attr.value}`;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
279
309
|
data.push({
|
|
280
310
|
name: item.name,
|
|
281
311
|
divId: 'Step_' + index,
|
|
@@ -283,7 +313,7 @@ class RotationRenderer {
|
|
|
283
313
|
startDate: new Date(item.startDate.valueOf()), // Date de début
|
|
284
314
|
endDate: new Date(item.endDate.valueOf()), // Date de fin
|
|
285
315
|
duration: item.duration,
|
|
286
|
-
description:
|
|
316
|
+
description: description,
|
|
287
317
|
value: [
|
|
288
318
|
1, // Parcelle (index de la série)
|
|
289
319
|
item.startDate.valueOf(), // Date de début
|
|
@@ -340,9 +370,14 @@ class RotationRenderer {
|
|
|
340
370
|
|
|
341
371
|
// Avoid words that are too long
|
|
342
372
|
testWidth = echarts.format.getTextRect(line).width;
|
|
343
|
-
|
|
373
|
+
let trimCount = 0;
|
|
374
|
+
const maxTrimCount = line.length; // Prevent infinite loop
|
|
375
|
+
|
|
376
|
+
while (testWidth > maxWidth && line.length > 0 && trimCount < maxTrimCount) {
|
|
344
377
|
line = line.slice(0, -1);
|
|
378
|
+
if (line.length === 0) break; // Safety check
|
|
345
379
|
testWidth = echarts.format.getTextRect(line).width;
|
|
380
|
+
trimCount++;
|
|
346
381
|
}
|
|
347
382
|
}
|
|
348
383
|
|
|
@@ -376,101 +411,221 @@ class RotationRenderer {
|
|
|
376
411
|
let maxXPositions = new Map();
|
|
377
412
|
|
|
378
413
|
function renderItem(params, api) {
|
|
414
|
+
// Safety checks to prevent crashes
|
|
415
|
+
if (!params || !api) return null;
|
|
416
|
+
|
|
417
|
+
try {
|
|
418
|
+
var categoryIndex = api.value(0);
|
|
419
|
+
var start = api.coord([api.value(1), categoryIndex]);
|
|
420
|
+
var end = api.coord([api.value(2), categoryIndex]);
|
|
421
|
+
var name = api.value(3);
|
|
422
|
+
var type = api.value(4);
|
|
423
|
+
let secondary_crop = api.value(5);
|
|
424
|
+
let bHasSecondaryCrops = api.value(6);
|
|
425
|
+
|
|
426
|
+
// Safety check for invalid coordinates
|
|
427
|
+
if (!start || !end || start.length < 2 || end.length < 2) return null;
|
|
428
|
+
if (isNaN(start[0]) || isNaN(start[1]) || isNaN(end[0]) || isNaN(end[1])) return null;
|
|
429
|
+
|
|
430
|
+
const x = start[0];
|
|
431
|
+
let y = start[1];
|
|
432
|
+
|
|
433
|
+
const style = api.style();
|
|
434
|
+
style.opacity = 0.5;
|
|
435
|
+
|
|
436
|
+
if (params.context.rendered == undefined) {
|
|
437
|
+
// Start of a new rendering round
|
|
438
|
+
maxXPositions = new Map();
|
|
439
|
+
|
|
440
|
+
for (let catIndex = 0; catIndex < 3; catIndex++) {
|
|
441
|
+
for (let track = 0; track < 3; track++) {
|
|
442
|
+
maxXPositions.set('track_left_' + catIndex + '_' + track, params.coordSys.width);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
379
446
|
|
|
380
|
-
|
|
381
|
-
var start = api.coord([api.value(1), categoryIndex]);
|
|
382
|
-
var end = api.coord([api.value(2), categoryIndex]);
|
|
383
|
-
var name = api.value(3);
|
|
384
|
-
var type = api.value(4);
|
|
385
|
-
let secondary_crop = api.value(5);
|
|
386
|
-
let bHasSecondaryCrops = api.value(6);
|
|
447
|
+
params.context.rendered = true;
|
|
387
448
|
|
|
388
|
-
|
|
389
|
-
|
|
449
|
+
// Remove the default emphasis style
|
|
450
|
+
api.styleEmphasis({});
|
|
390
451
|
|
|
391
|
-
|
|
392
|
-
style.opacity = 0.5;
|
|
452
|
+
if (type == 'rotation_item') {
|
|
393
453
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
454
|
+
// start[0] // abscisse gauche de l'élément (après zoom)
|
|
455
|
+
// start[1] // ordonnée gauche de l'élément
|
|
456
|
+
// end[0] // abscisse droite de l'élément (après zoom)
|
|
457
|
+
// end[1] // ordonnée droite de l'élément
|
|
458
|
+
// height // Hauteur de l'élément
|
|
397
459
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
460
|
+
// params.coordSys.x // début du canva
|
|
461
|
+
// params.coordSys.y // début du canva
|
|
462
|
+
// params.coordSys.width, // largeur du canva
|
|
463
|
+
// params.coordSys.height // hauteur du canva
|
|
464
|
+
|
|
465
|
+
let height = self.barHeight - 20; // 20 px margin top and bottom
|
|
466
|
+
let top = y - height / 2;
|
|
467
|
+
let textXMargin = 2;
|
|
468
|
+
let textYMargin = 10;
|
|
469
|
+
|
|
470
|
+
if (bHasSecondaryCrops) {
|
|
471
|
+
height = self.barHeight - 40; // 20 px margin top and bottom
|
|
472
|
+
top = y - height / 2 - 15;
|
|
473
|
+
textXMargin = 2;
|
|
474
|
+
textYMargin = 10;
|
|
401
475
|
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
476
|
|
|
405
|
-
|
|
477
|
+
if (secondary_crop) {
|
|
478
|
+
// Move secondary crops a bit down and reduce their size
|
|
479
|
+
top = top + height + 5;
|
|
480
|
+
height = height / 3;
|
|
481
|
+
textXMargin = 5;
|
|
482
|
+
textYMargin = 5;
|
|
483
|
+
}
|
|
406
484
|
|
|
407
|
-
|
|
408
|
-
|
|
485
|
+
const arrowWidth = height / 3;
|
|
486
|
+
const border = 3;
|
|
409
487
|
|
|
410
|
-
|
|
488
|
+
var points = [
|
|
489
|
+
[x, top],
|
|
490
|
+
[end[0] - border, top],
|
|
491
|
+
[end[0] + arrowWidth - border, top + height / 2],
|
|
492
|
+
[end[0] - border, top + height],
|
|
493
|
+
[x, top + height],
|
|
494
|
+
[x + arrowWidth, top + height / 2],
|
|
495
|
+
];
|
|
411
496
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
// end[0] // abscisse droite de l'élément (après zoom)
|
|
415
|
-
// end[1] // ordonnée droite de l'élément
|
|
416
|
-
// height // Hauteur de l'élément
|
|
497
|
+
//const itemLabelWidth = echarts.format.getTextRect(name).width + textMargin * 2;
|
|
498
|
+
const itemWidth = end[0] - x;
|
|
417
499
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// params.coordSys.width, // largeur du canva
|
|
421
|
-
// params.coordSys.height // hauteur du canva
|
|
500
|
+
// if (itemLabelWidth > itemWidth)
|
|
501
|
+
// name = ''; // Hide the label as we won't have the room to show it
|
|
422
502
|
|
|
423
|
-
|
|
424
|
-
let top = y - height / 2;
|
|
425
|
-
let textXMargin = 2;
|
|
426
|
-
let textYMargin = 10;
|
|
503
|
+
name = wrapText(echarts, name, itemWidth - arrowWidth, height);
|
|
427
504
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
top = y - height / 2 - 15;
|
|
431
|
-
textXMargin = 2;
|
|
432
|
-
textYMargin = 10;
|
|
433
|
-
}
|
|
505
|
+
// See this for clip regions : https://stackoverflow.com/questions/71735038/setting-border-and-label-in-custom-apache-echarts
|
|
506
|
+
// https://stackoverflow.com/questions/73653691/how-to-draw-a-custom-triangle-in-renderitem-in-apache-echarts
|
|
434
507
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
508
|
+
return (
|
|
509
|
+
{
|
|
510
|
+
type: 'polygon',
|
|
511
|
+
transition: ['shape'],
|
|
512
|
+
shape: {
|
|
513
|
+
points: points
|
|
514
|
+
},
|
|
515
|
+
style: style,
|
|
516
|
+
emphasis: {
|
|
517
|
+
style: {
|
|
518
|
+
shadowBlur: 4,
|
|
519
|
+
shadowOffsetX: 1,
|
|
520
|
+
shadowOffsetY: 2,
|
|
521
|
+
shadowColor: 'rgba(0, 0, 0, 0.2)'
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
textConfig: {
|
|
525
|
+
position: [arrowWidth + textXMargin, textYMargin]
|
|
526
|
+
},
|
|
527
|
+
textContent: {
|
|
528
|
+
style: {
|
|
529
|
+
text: name,
|
|
530
|
+
fill: '#000',
|
|
531
|
+
width: 80,
|
|
532
|
+
fontWeight: 'bold'
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
);
|
|
441
537
|
}
|
|
442
538
|
|
|
443
|
-
|
|
444
|
-
|
|
539
|
+
if (type == 'intervention_bottom' || type == 'intervention_top') {
|
|
540
|
+
|
|
541
|
+
const height = 20;
|
|
542
|
+
const margin = 10;
|
|
543
|
+
const textMargin = 5;
|
|
544
|
+
|
|
545
|
+
// Maintain a list of max x for each row. If the max x is further right than the label we try to push,
|
|
546
|
+
// use another row. If for all rows the space is taken, just drop this item
|
|
547
|
+
|
|
548
|
+
let trackToUse = null;
|
|
549
|
+
const itemLabelWidth = echarts.format.getTextRect(name).width + textMargin * 2;
|
|
550
|
+
|
|
551
|
+
for (let track = 0; track < 3; track++) {
|
|
552
|
+
if (!maxXPositions.has('track_right_' + categoryIndex + '_' + track)) {
|
|
553
|
+
// Situation where the track is empty
|
|
554
|
+
trackToUse = track;
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
let trackLeft = maxXPositions.get('track_left_' + categoryIndex + '_' + track);
|
|
559
|
+
if (trackLeft > (x + itemLabelWidth)) {
|
|
560
|
+
// Situation where the drawing has started right of the current element
|
|
561
|
+
trackToUse = track;
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
let trackRight = maxXPositions.get('track_right_' + categoryIndex + '_' + track);
|
|
566
|
+
if (trackRight < x) {
|
|
567
|
+
// Situation where the last painted element is sufficiently far on the left
|
|
568
|
+
trackToUse = track;
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (trackToUse == null)
|
|
574
|
+
return null;
|
|
575
|
+
|
|
576
|
+
let currentRight = maxXPositions.get('track_right_' + categoryIndex + '_' + trackToUse);
|
|
577
|
+
if (currentRight == undefined || currentRight < x + itemLabelWidth)
|
|
578
|
+
maxXPositions.set('track_right_' + categoryIndex + '_' + trackToUse, x + itemLabelWidth);
|
|
445
579
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
[end[0] + arrowWidth - border, top + height / 2],
|
|
450
|
-
[end[0] - border, top + height],
|
|
451
|
-
[x, top + height],
|
|
452
|
-
[x + arrowWidth, top + height / 2],
|
|
453
|
-
];
|
|
580
|
+
let currentLeft = maxXPositions.get('track_left_' + categoryIndex + '_' + trackToUse);
|
|
581
|
+
if (currentLeft > x)
|
|
582
|
+
maxXPositions.set('track_left_' + categoryIndex + '_' + trackToUse, x);
|
|
454
583
|
|
|
455
|
-
|
|
456
|
-
|
|
584
|
+
// A nicer solution could be to draw large items in a reduced format until there is enough space for
|
|
585
|
+
// drawing them fully. Unfortunately that would require a two pass drawing which does not exist with Echarts ?
|
|
457
586
|
|
|
458
|
-
|
|
459
|
-
// name = ''; // Hide the label as we won't have the room to show it
|
|
587
|
+
const arrowWidth = 3;
|
|
460
588
|
|
|
461
|
-
|
|
589
|
+
let arrowTop = y + 55;
|
|
590
|
+
let arrowBottom = y - 55;
|
|
462
591
|
|
|
463
|
-
|
|
464
|
-
// https://stackoverflow.com/questions/73653691/how-to-draw-a-custom-triangle-in-renderitem-in-apache-echarts
|
|
592
|
+
y = margin + y + trackToUse * (height + margin) - (self.barHeight / 2);
|
|
465
593
|
|
|
466
|
-
|
|
467
|
-
|
|
594
|
+
var points = [];
|
|
595
|
+
var textPosition = [];
|
|
596
|
+
|
|
597
|
+
if (type == 'intervention_top') {
|
|
598
|
+
textPosition = [textMargin, textMargin];
|
|
599
|
+
points = [
|
|
600
|
+
[x, y],
|
|
601
|
+
[x + itemLabelWidth, y],
|
|
602
|
+
[x + itemLabelWidth, y + height],
|
|
603
|
+
[x + arrowWidth, y + height],
|
|
604
|
+
[x, arrowTop]
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
textPosition = [textMargin, textMargin + y - arrowBottom];
|
|
609
|
+
points = [
|
|
610
|
+
[x, arrowBottom],
|
|
611
|
+
[x + arrowWidth, y],
|
|
612
|
+
[x + itemLabelWidth, y],
|
|
613
|
+
[x + itemLabelWidth, y + height],
|
|
614
|
+
[x, y + height]
|
|
615
|
+
];
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return ({
|
|
468
619
|
type: 'polygon',
|
|
469
620
|
transition: ['shape'],
|
|
470
621
|
shape: {
|
|
471
622
|
points: points
|
|
472
623
|
},
|
|
473
|
-
style: style
|
|
624
|
+
style: api.style({
|
|
625
|
+
fill: style.fill,
|
|
626
|
+
stroke: style.fill,
|
|
627
|
+
textFill: '#000',
|
|
628
|
+
}),
|
|
474
629
|
emphasis: {
|
|
475
630
|
style: {
|
|
476
631
|
shadowBlur: 4,
|
|
@@ -480,130 +635,21 @@ class RotationRenderer {
|
|
|
480
635
|
},
|
|
481
636
|
},
|
|
482
637
|
textConfig: {
|
|
483
|
-
position:
|
|
638
|
+
position: textPosition
|
|
484
639
|
},
|
|
485
640
|
textContent: {
|
|
486
641
|
style: {
|
|
487
642
|
text: name,
|
|
488
643
|
fill: '#000',
|
|
489
|
-
width: 80,
|
|
490
|
-
fontWeight: 'bold'
|
|
491
644
|
}
|
|
492
645
|
}
|
|
493
646
|
}
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
if (type == 'intervention_bottom' || type == 'intervention_top') {
|
|
498
|
-
|
|
499
|
-
const height = 20;
|
|
500
|
-
const margin = 10;
|
|
501
|
-
const textMargin = 5;
|
|
502
|
-
|
|
503
|
-
// Maintain a list of max x for each row. If the max x is further right than the label we try to push,
|
|
504
|
-
// use another row. If for all rows the space is taken, just drop this item
|
|
505
|
-
|
|
506
|
-
let trackToUse = null;
|
|
507
|
-
const itemLabelWidth = echarts.format.getTextRect(name).width + textMargin * 2;
|
|
508
|
-
|
|
509
|
-
for (let track = 0; track < 3; track++) {
|
|
510
|
-
if (!maxXPositions.has('track_right_' + categoryIndex + '_' + track)) {
|
|
511
|
-
// Situation where the track is empty
|
|
512
|
-
trackToUse = track;
|
|
513
|
-
break;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
let trackLeft = maxXPositions.get('track_left_' + categoryIndex + '_' + track);
|
|
517
|
-
if (trackLeft > (x + itemLabelWidth)) {
|
|
518
|
-
// Situation where the drawing has started right of the current element
|
|
519
|
-
trackToUse = track;
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
let trackRight = maxXPositions.get('track_right_' + categoryIndex + '_' + track);
|
|
524
|
-
if (trackRight < x) {
|
|
525
|
-
// Situation where the last painted element is sufficiently far on the left
|
|
526
|
-
trackToUse = track;
|
|
527
|
-
break;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
if (trackToUse == null)
|
|
532
|
-
return null;
|
|
533
|
-
|
|
534
|
-
let currentRight = maxXPositions.get('track_right_' + categoryIndex + '_' + trackToUse);
|
|
535
|
-
if (currentRight == undefined || currentRight < x + itemLabelWidth)
|
|
536
|
-
maxXPositions.set('track_right_' + categoryIndex + '_' + trackToUse, x + itemLabelWidth);
|
|
537
|
-
|
|
538
|
-
let currentLeft = maxXPositions.get('track_left_' + categoryIndex + '_' + trackToUse);
|
|
539
|
-
if (currentLeft > x)
|
|
540
|
-
maxXPositions.set('track_left_' + categoryIndex + '_' + trackToUse, x);
|
|
541
|
-
|
|
542
|
-
// A nicer solution could be to draw large items in a reduced format until there is enough space for
|
|
543
|
-
// drawing them fully. Unfortunately that would require a two pass drawing which does not exist with Echarts ?
|
|
647
|
+
);
|
|
544
648
|
|
|
545
|
-
const arrowWidth = 3;
|
|
546
|
-
|
|
547
|
-
let arrowTop = y + 55;
|
|
548
|
-
let arrowBottom = y - 55;
|
|
549
|
-
|
|
550
|
-
y = margin + y + trackToUse * (height + margin) - (self.barHeight / 2);
|
|
551
|
-
|
|
552
|
-
var points = [];
|
|
553
|
-
var textPosition = [];
|
|
554
|
-
|
|
555
|
-
if (type == 'intervention_top') {
|
|
556
|
-
textPosition = [textMargin, textMargin];
|
|
557
|
-
points = [
|
|
558
|
-
[x, y],
|
|
559
|
-
[x + itemLabelWidth, y],
|
|
560
|
-
[x + itemLabelWidth, y + height],
|
|
561
|
-
[x + arrowWidth, y + height],
|
|
562
|
-
[x, arrowTop]
|
|
563
|
-
];
|
|
564
|
-
}
|
|
565
|
-
else {
|
|
566
|
-
textPosition = [textMargin, textMargin + y - arrowBottom];
|
|
567
|
-
points = [
|
|
568
|
-
[x, arrowBottom],
|
|
569
|
-
[x + arrowWidth, y],
|
|
570
|
-
[x + itemLabelWidth, y],
|
|
571
|
-
[x + itemLabelWidth, y + height],
|
|
572
|
-
[x, y + height]
|
|
573
|
-
];
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
return ({
|
|
577
|
-
type: 'polygon',
|
|
578
|
-
transition: ['shape'],
|
|
579
|
-
shape: {
|
|
580
|
-
points: points
|
|
581
|
-
},
|
|
582
|
-
style: api.style({
|
|
583
|
-
fill: style.fill,
|
|
584
|
-
stroke: style.fill,
|
|
585
|
-
textFill: '#000',
|
|
586
|
-
}),
|
|
587
|
-
emphasis: {
|
|
588
|
-
style: {
|
|
589
|
-
shadowBlur: 4,
|
|
590
|
-
shadowOffsetX: 1,
|
|
591
|
-
shadowOffsetY: 2,
|
|
592
|
-
shadowColor: 'rgba(0, 0, 0, 0.2)'
|
|
593
|
-
},
|
|
594
|
-
},
|
|
595
|
-
textConfig: {
|
|
596
|
-
position: textPosition
|
|
597
|
-
},
|
|
598
|
-
textContent: {
|
|
599
|
-
style: {
|
|
600
|
-
text: name,
|
|
601
|
-
fill: '#000',
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
649
|
}
|
|
605
|
-
|
|
606
|
-
|
|
650
|
+
} catch (error) {
|
|
651
|
+
console.error('Error in renderItem:', error);
|
|
652
|
+
return null;
|
|
607
653
|
}
|
|
608
654
|
}
|
|
609
655
|
|
|
@@ -823,8 +869,29 @@ class RotationRenderer {
|
|
|
823
869
|
];
|
|
824
870
|
|
|
825
871
|
let monthsPerYear = new Map();
|
|
872
|
+
|
|
873
|
+
// Safety check to prevent infinite loops
|
|
874
|
+
if (!steps || steps.length === 0) {
|
|
875
|
+
series.push(months);
|
|
876
|
+
return series;
|
|
877
|
+
}
|
|
878
|
+
|
|
826
879
|
let startMonth = new Date(steps.at(0).startDate.valueOf());
|
|
827
|
-
|
|
880
|
+
let endDate = steps.at(-1).endDate;
|
|
881
|
+
|
|
882
|
+
// Safety check for valid dates and reasonable duration (max 20 years)
|
|
883
|
+
if (isNaN(startMonth.getTime()) ||
|
|
884
|
+
isNaN(endDate.getTime()) ||
|
|
885
|
+
startMonth >= endDate ||
|
|
886
|
+
(endDate - startMonth) > (20 * 365 * 24 * 60 * 60 * 1000)) {
|
|
887
|
+
series.push(months);
|
|
888
|
+
return series;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
let loopCount = 0;
|
|
892
|
+
const maxLoops = 240; // Max 20 years * 12 months
|
|
893
|
+
|
|
894
|
+
while (startMonth < endDate && loopCount < maxLoops) {
|
|
828
895
|
const monthName = startMonth.toLocaleDateString(undefined, { month: 'short' });
|
|
829
896
|
const year = startMonth.getFullYear();
|
|
830
897
|
|
|
@@ -843,6 +910,7 @@ class RotationRenderer {
|
|
|
843
910
|
|
|
844
911
|
// increment the current month
|
|
845
912
|
startMonth.setMonth(startMonth.getMonth() + 1);
|
|
913
|
+
loopCount++;
|
|
846
914
|
}
|
|
847
915
|
|
|
848
916
|
series.push(months);
|
|
@@ -912,13 +980,21 @@ class RotationRenderer {
|
|
|
912
980
|
|
|
913
981
|
option.tooltip = {
|
|
914
982
|
extraCssText: "text-wrap: wrap;",
|
|
983
|
+
position: function (pos, params, el, elRect, size) {
|
|
984
|
+
var obj = { top: 10 };
|
|
985
|
+
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
|
|
986
|
+
return obj;
|
|
987
|
+
},
|
|
915
988
|
className: "rotation-tooltip",
|
|
916
989
|
formatter: function (params) {
|
|
917
990
|
if (params.data.type == 'rotation_item') {
|
|
918
991
|
let start = params.data.startDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
|
|
919
992
|
let end = params.data.endDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: '2-digit' });
|
|
993
|
+
let duration = params.data.duration + ' mois';
|
|
994
|
+
if (params.data.duration > 20)
|
|
995
|
+
duration = (Math.round(params.data.duration / 1.2)/10 + ' années').replace('.', ',');
|
|
920
996
|
|
|
921
|
-
return params.marker + params.name + '
|
|
997
|
+
return params.marker + ' <b>' + params.name + '</b><div class="step_dates"><b>' + duration + '</b> (' + start + ' ➜ ' + end + ')</div><br style="clear:both">' + params.data.description.replace('’', '\'');
|
|
922
998
|
}
|
|
923
999
|
else {
|
|
924
1000
|
let interventionDate = params.data.interventionDate;
|
|
@@ -930,13 +1006,13 @@ class RotationRenderer {
|
|
|
930
1006
|
else
|
|
931
1007
|
dateString += ' (J' + days + ')';
|
|
932
1008
|
|
|
933
|
-
return params.marker + params.name + ' - ' + dateString + '<br>' + params.data.description
|
|
1009
|
+
return params.marker + ' <b>'+ params.name + '</b> - ' + dateString + '<br>' + params.data.description.replace('’', '\'');;
|
|
934
1010
|
}
|
|
935
1011
|
}
|
|
936
1012
|
};
|
|
937
1013
|
|
|
938
1014
|
option.toolbox = {
|
|
939
|
-
"itemSize":
|
|
1015
|
+
"itemSize": 20,
|
|
940
1016
|
"iconStyle": {
|
|
941
1017
|
"borderColor": "#AAA",
|
|
942
1018
|
"borderWidth": 1
|
|
@@ -984,8 +1060,6 @@ class RotationRenderer {
|
|
|
984
1060
|
|
|
985
1061
|
// If a line has a column in it, split the line in two and add bold to the first part:
|
|
986
1062
|
description = description.replace(/^([^:\n]+):/gm, "<b>$1:</b>");
|
|
987
|
-
description = description.replace(/\n([^:\n]+):/gm, "\n<b>$1:</b>");
|
|
988
|
-
|
|
989
1063
|
description = description.replace(/\n/g, '<br/>');
|
|
990
1064
|
|
|
991
1065
|
return description;
|
package/js/editor-wiki-editor.js
CHANGED
|
@@ -103,5 +103,12 @@ class WikiEditor {
|
|
|
103
103
|
|
|
104
104
|
const editData = await editResp.json();
|
|
105
105
|
console.log(editData);
|
|
106
|
+
|
|
107
|
+
if (editData.edit && editData.edit.result === 'Success') {
|
|
108
|
+
return Promise.resolve();
|
|
109
|
+
} else {
|
|
110
|
+
return Promise.reject(editData);
|
|
111
|
+
}
|
|
106
112
|
}
|
|
113
|
+
|
|
107
114
|
}
|