@operato/gantt 2.0.0-beta.0 → 2.0.0-beta.1

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/src/locale.d.ts +1 -0
  3. package/dist/src/locale.js +49 -0
  4. package/dist/src/locale.js.map +1 -0
  5. package/dist/src/ox-gantt-chart.d.ts +39 -0
  6. package/dist/src/ox-gantt-chart.js +707 -0
  7. package/dist/src/ox-gantt-chart.js.map +1 -0
  8. package/dist/src/ox-gantt-resize-splitter.d.ts +13 -0
  9. package/dist/src/ox-gantt-resize-splitter.js +117 -0
  10. package/dist/src/ox-gantt-resize-splitter.js.map +1 -0
  11. package/dist/src/ox-gantt.d.ts +24 -0
  12. package/dist/src/ox-gantt.js +186 -0
  13. package/dist/src/ox-gantt.js.map +1 -0
  14. package/dist/src/types.d.ts +10 -5
  15. package/dist/src/types.js.map +1 -1
  16. package/dist/src/utils.d.ts +3 -0
  17. package/dist/src/utils.js +10 -0
  18. package/dist/src/utils.js.map +1 -0
  19. package/dist/stories/ox-gantt-chart.stories.d.ts +41 -0
  20. package/dist/stories/{ox-lite-gantt.stories.js → ox-gantt-chart.stories.js} +53 -21
  21. package/dist/stories/ox-gantt-chart.stories.js.map +1 -0
  22. package/dist/stories/ox-gantt.stories.d.ts +6 -1
  23. package/dist/stories/ox-gantt.stories.js +286 -15
  24. package/dist/stories/ox-gantt.stories.js.map +1 -1
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/package.json +12 -4
  27. package/src/locale.ts +51 -0
  28. package/src/ox-gantt-chart.ts +773 -0
  29. package/src/ox-gantt-resize-splitter.ts +137 -0
  30. package/src/ox-gantt.ts +177 -0
  31. package/src/types.ts +12 -6
  32. package/src/utils.ts +13 -0
  33. package/stories/{ox-lite-gantt.stories.ts → ox-gantt-chart.stories.ts} +65 -22
  34. package/stories/ox-gantt.stories.ts +289 -16
  35. package/themes/dark-hc.css +151 -0
  36. package/themes/dark-mc.css +151 -0
  37. package/themes/dark.css +151 -0
  38. package/themes/grist-theme.css +169 -0
  39. package/themes/light-hc.css +151 -0
  40. package/themes/light-mc.css +151 -0
  41. package/themes/light.css +151 -0
  42. package/themes/md-typescale-styles.css +100 -0
  43. package/themes/spacing.css +43 -0
  44. package/themes/state-color.css +6 -0
  45. package/dist/src/gantt.d.ts +0 -28
  46. package/dist/src/gantt.js +0 -178
  47. package/dist/src/gantt.js.map +0 -1
  48. package/dist/src/lite-gantt.d.ts +0 -27
  49. package/dist/src/lite-gantt.js +0 -146
  50. package/dist/src/lite-gantt.js.map +0 -1
  51. package/dist/stories/ox-lite-gantt.stories.d.ts +0 -22
  52. package/dist/stories/ox-lite-gantt.stories.js.map +0 -1
  53. package/src/gantt.ts +0 -191
  54. package/src/lite-gantt.ts +0 -152
  55. package/src/renderer.txt +0 -775
package/src/renderer.txt DELETED
@@ -1,775 +0,0 @@
1
- import dayjs from 'dayjs'
2
- import {
3
- select,
4
- scaleTime,
5
- min,
6
- max,
7
- scaleLinear,
8
- interpolateHcl,
9
- axisBottom,
10
- axisTop,
11
- timeFormat,
12
- timeMillisecond,
13
- timeSecond,
14
- timeMinute,
15
- timeHour,
16
- timeDay,
17
- timeMonday,
18
- timeTuesday,
19
- timeWednesday,
20
- timeThursday,
21
- timeFriday,
22
- timeSaturday,
23
- timeSunday,
24
- timeMonth
25
- } from 'd3'
26
- import common from '../common/common.js'
27
- import { getConfig } from '../../diagram-api/diagramAPI.js'
28
- import { configureSvgSize } from '../../setupGraphViewbox.js'
29
-
30
- export const setConf = function () {
31
- console.log('Something is calling, setConf, remove the call')
32
- }
33
-
34
- /**
35
- * This will map any day of the week that can be set in the `weekday` option to
36
- * the corresponding d3-time function that is used to calculate the ticks.
37
- */
38
- const mapWeekdayToTimeFunction = {
39
- monday: timeMonday,
40
- tuesday: timeTuesday,
41
- wednesday: timeWednesday,
42
- thursday: timeThursday,
43
- friday: timeFriday,
44
- saturday: timeSaturday,
45
- sunday: timeSunday
46
- }
47
-
48
- /**
49
- * For this issue:
50
- * https://github.com/mermaid-js/mermaid/issues/1618
51
- *
52
- * Finds the number of intersections between tasks that happen at any point in time.
53
- * Used to figure out how many rows are needed to display the tasks when the display
54
- * mode is set to 'compact'.
55
- *
56
- * @param tasks
57
- * @param orderOffset
58
- */
59
- const getMaxIntersections = (tasks, orderOffset) => {
60
- let timeline = [...tasks].map(() => -Infinity)
61
- let sorted = [...tasks].sort((a, b) => a.startTime - b.startTime || a.order - b.order)
62
- let maxIntersections = 0
63
- for (const element of sorted) {
64
- for (let j = 0; j < timeline.length; j++) {
65
- if (element.startTime >= timeline[j]) {
66
- timeline[j] = element.endTime
67
- element.order = j + orderOffset
68
- if (j > maxIntersections) {
69
- maxIntersections = j
70
- }
71
- break
72
- }
73
- }
74
- }
75
-
76
- return maxIntersections
77
- }
78
-
79
- let w
80
- export const draw = function (text, id, version, diagObj) {
81
- const conf = getConfig().gantt
82
-
83
- const securityLevel = getConfig().securityLevel
84
- // Handle root and Document for when rendering in sandbox mode
85
- let sandboxElement
86
- if (securityLevel === 'sandbox') {
87
- sandboxElement = select('#i' + id)
88
- }
89
- const root = securityLevel === 'sandbox' ? select(sandboxElement.nodes()[0].contentDocument.body) : select('body')
90
- const doc = securityLevel === 'sandbox' ? sandboxElement.nodes()[0].contentDocument : document
91
-
92
- const elem = doc.getElementById(id)
93
- w = elem.parentElement.offsetWidth
94
-
95
- if (w === undefined) {
96
- w = 1200
97
- }
98
-
99
- if (conf.useWidth !== undefined) {
100
- w = conf.useWidth
101
- }
102
-
103
- const taskArray = diagObj.db.getTasks()
104
-
105
- // Set height based on number of tasks
106
-
107
- let categories = []
108
-
109
- for (const element of taskArray) {
110
- categories.push(element.type)
111
- }
112
-
113
- categories = checkUnique(categories)
114
- const categoryHeights = {}
115
-
116
- let h = 2 * conf.topPadding
117
- if (diagObj.db.getDisplayMode() === 'compact' || conf.displayMode === 'compact') {
118
- const categoryElements = {}
119
- for (const element of taskArray) {
120
- if (categoryElements[element.section] === undefined) {
121
- categoryElements[element.section] = [element]
122
- } else {
123
- categoryElements[element.section].push(element)
124
- }
125
- }
126
-
127
- let intersections = 0
128
- for (const category of Object.keys(categoryElements)) {
129
- const categoryHeight = getMaxIntersections(categoryElements[category], intersections) + 1
130
- intersections += categoryHeight
131
- h += categoryHeight * (conf.barHeight + conf.barGap)
132
- categoryHeights[category] = categoryHeight
133
- }
134
- } else {
135
- h += taskArray.length * (conf.barHeight + conf.barGap)
136
- for (const category of categories) {
137
- categoryHeights[category] = taskArray.filter(task => task.type === category).length
138
- }
139
- }
140
-
141
- // Set viewBox
142
- elem.setAttribute('viewBox', '0 0 ' + w + ' ' + h)
143
- const svg = root.select(`[id="${id}"]`)
144
-
145
- // Set timescale
146
- const timeScale = scaleTime()
147
- .domain([
148
- min(taskArray, function (d) {
149
- return d.startTime
150
- }),
151
- max(taskArray, function (d) {
152
- return d.endTime
153
- })
154
- ])
155
- .rangeRound([0, w - conf.leftPadding - conf.rightPadding])
156
-
157
- /**
158
- * @param a
159
- * @param b
160
- */
161
- function taskCompare(a, b) {
162
- const taskA = a.startTime
163
- const taskB = b.startTime
164
- let result = 0
165
- if (taskA > taskB) {
166
- result = 1
167
- } else if (taskA < taskB) {
168
- result = -1
169
- }
170
- return result
171
- }
172
-
173
- // Sort the task array using the above taskCompare() so that
174
- // tasks are created based on their order of startTime
175
- taskArray.sort(taskCompare)
176
-
177
- makeGantt(taskArray, w, h)
178
-
179
- configureSvgSize(svg, h, w, conf.useMaxWidth)
180
-
181
- svg
182
- .append('text')
183
- .text(diagObj.db.getDiagramTitle())
184
- .attr('x', w / 2)
185
- .attr('y', conf.titleTopMargin)
186
- .attr('class', 'titleText')
187
-
188
- /**
189
- * @param tasks
190
- * @param pageWidth
191
- * @param pageHeight
192
- */
193
- function makeGantt(tasks, pageWidth, pageHeight) {
194
- const barHeight = conf.barHeight
195
- const gap = barHeight + conf.barGap
196
- const topPadding = conf.topPadding
197
- const leftPadding = conf.leftPadding
198
-
199
- const colorScale = scaleLinear()
200
- .domain([0, categories.length])
201
- .range(['#00B9FA', '#F95002'])
202
- .interpolate(interpolateHcl)
203
-
204
- drawExcludeDays(
205
- gap,
206
- topPadding,
207
- leftPadding,
208
- pageWidth,
209
- pageHeight,
210
- tasks,
211
- diagObj.db.getExcludes(),
212
- diagObj.db.getIncludes()
213
- )
214
- makeGrid(leftPadding, topPadding, pageWidth, pageHeight)
215
- drawRects(tasks, gap, topPadding, leftPadding, barHeight, colorScale, pageWidth, pageHeight)
216
- vertLabels(gap, topPadding, leftPadding, barHeight, colorScale)
217
- drawToday(leftPadding, topPadding, pageWidth, pageHeight)
218
- }
219
-
220
- /**
221
- * @param theArray
222
- * @param theGap
223
- * @param theTopPad
224
- * @param theSidePad
225
- * @param theBarHeight
226
- * @param theColorScale
227
- * @param w
228
- */
229
- function drawRects(theArray, theGap, theTopPad, theSidePad, theBarHeight, theColorScale, w) {
230
- // Get unique task orders. Required to draw the background rects when display mode is compact.
231
- const uniqueTaskOrderIds = [...new Set(theArray.map(item => item.order))]
232
- const uniqueTasks = uniqueTaskOrderIds.map(id => theArray.find(item => item.order === id))
233
-
234
- // Draw background rects covering the entire width of the graph, these form the section rows.
235
- svg
236
- .append('g')
237
- .selectAll('rect')
238
- .data(uniqueTasks)
239
- .enter()
240
- .append('rect')
241
- .attr('x', 0)
242
- .attr('y', function (d, i) {
243
- // Ignore the incoming i value and use our order instead
244
- i = d.order
245
- return i * theGap + theTopPad - 2
246
- })
247
- .attr('width', function () {
248
- return w - conf.rightPadding / 2
249
- })
250
- .attr('height', theGap)
251
- .attr('class', function (d) {
252
- for (const [i, category] of categories.entries()) {
253
- if (d.type === category) {
254
- return 'section section' + (i % conf.numberSectionStyles)
255
- }
256
- }
257
- return 'section section0'
258
- })
259
-
260
- // Draw the rects representing the tasks
261
- const rectangles = svg.append('g').selectAll('rect').data(theArray).enter()
262
-
263
- const links = diagObj.db.getLinks()
264
-
265
- // Render the tasks with links
266
- // Render the other tasks
267
- rectangles
268
- .append('rect')
269
- .attr('id', function (d) {
270
- return d.id
271
- })
272
- .attr('rx', 3)
273
- .attr('ry', 3)
274
- .attr('x', function (d) {
275
- if (d.milestone) {
276
- return (
277
- timeScale(d.startTime) +
278
- theSidePad +
279
- 0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) -
280
- 0.5 * theBarHeight
281
- )
282
- }
283
- return timeScale(d.startTime) + theSidePad
284
- })
285
- .attr('y', function (d, i) {
286
- // Ignore the incoming i value and use our order instead
287
- i = d.order
288
- return i * theGap + theTopPad
289
- })
290
- .attr('width', function (d) {
291
- if (d.milestone) {
292
- return theBarHeight
293
- }
294
- return timeScale(d.renderEndTime || d.endTime) - timeScale(d.startTime)
295
- })
296
- .attr('height', theBarHeight)
297
- .attr('transform-origin', function (d, i) {
298
- // Ignore the incoming i value and use our order instead
299
- i = d.order
300
-
301
- return (
302
- (timeScale(d.startTime) + theSidePad + 0.5 * (timeScale(d.endTime) - timeScale(d.startTime))).toString() +
303
- 'px ' +
304
- (i * theGap + theTopPad + 0.5 * theBarHeight).toString() +
305
- 'px'
306
- )
307
- })
308
- .attr('class', function (d) {
309
- const res = 'task'
310
-
311
- let classStr = ''
312
- if (d.classes.length > 0) {
313
- classStr = d.classes.join(' ')
314
- }
315
-
316
- let secNum = 0
317
- for (const [i, category] of categories.entries()) {
318
- if (d.type === category) {
319
- secNum = i % conf.numberSectionStyles
320
- }
321
- }
322
-
323
- let taskClass = ''
324
- if (d.active) {
325
- if (d.crit) {
326
- taskClass += ' activeCrit'
327
- } else {
328
- taskClass = ' active'
329
- }
330
- } else if (d.done) {
331
- if (d.crit) {
332
- taskClass = ' doneCrit'
333
- } else {
334
- taskClass = ' done'
335
- }
336
- } else {
337
- if (d.crit) {
338
- taskClass += ' crit'
339
- }
340
- }
341
-
342
- if (taskClass.length === 0) {
343
- taskClass = ' task'
344
- }
345
-
346
- if (d.milestone) {
347
- taskClass = ' milestone ' + taskClass
348
- }
349
-
350
- taskClass += secNum
351
-
352
- taskClass += ' ' + classStr
353
-
354
- return res + taskClass
355
- })
356
-
357
- // Append task labels
358
- rectangles
359
- .append('text')
360
- .attr('id', function (d) {
361
- return d.id + '-text'
362
- })
363
- .text(function (d) {
364
- return d.task
365
- })
366
- .attr('font-size', conf.fontSize)
367
- .attr('x', function (d) {
368
- let startX = timeScale(d.startTime)
369
- let endX = timeScale(d.renderEndTime || d.endTime)
370
- if (d.milestone) {
371
- startX += 0.5 * (timeScale(d.endTime) - timeScale(d.startTime)) - 0.5 * theBarHeight
372
- }
373
- if (d.milestone) {
374
- endX = startX + theBarHeight
375
- }
376
- const textWidth = this.getBBox().width
377
-
378
- // Check id text width > width of rectangle
379
- if (textWidth > endX - startX) {
380
- if (endX + textWidth + 1.5 * conf.leftPadding > w) {
381
- return startX + theSidePad - 5
382
- } else {
383
- return endX + theSidePad + 5
384
- }
385
- } else {
386
- return (endX - startX) / 2 + startX + theSidePad
387
- }
388
- })
389
- .attr('y', function (d, i) {
390
- // Ignore the incoming i value and use our order instead
391
- i = d.order
392
- return i * theGap + conf.barHeight / 2 + (conf.fontSize / 2 - 2) + theTopPad
393
- })
394
- .attr('text-height', theBarHeight)
395
- .attr('class', function (d) {
396
- const startX = timeScale(d.startTime)
397
- let endX = timeScale(d.endTime)
398
- if (d.milestone) {
399
- endX = startX + theBarHeight
400
- }
401
- const textWidth = this.getBBox().width
402
-
403
- let classStr = ''
404
- if (d.classes.length > 0) {
405
- classStr = d.classes.join(' ')
406
- }
407
-
408
- let secNum = 0
409
- for (const [i, category] of categories.entries()) {
410
- if (d.type === category) {
411
- secNum = i % conf.numberSectionStyles
412
- }
413
- }
414
-
415
- let taskType = ''
416
- if (d.active) {
417
- if (d.crit) {
418
- taskType = 'activeCritText' + secNum
419
- } else {
420
- taskType = 'activeText' + secNum
421
- }
422
- }
423
-
424
- if (d.done) {
425
- if (d.crit) {
426
- taskType = taskType + ' doneCritText' + secNum
427
- } else {
428
- taskType = taskType + ' doneText' + secNum
429
- }
430
- } else {
431
- if (d.crit) {
432
- taskType = taskType + ' critText' + secNum
433
- }
434
- }
435
-
436
- if (d.milestone) {
437
- taskType += ' milestoneText'
438
- }
439
-
440
- // Check id text width > width of rectangle
441
- if (textWidth > endX - startX) {
442
- if (endX + textWidth + 1.5 * conf.leftPadding > w) {
443
- return classStr + ' taskTextOutsideLeft taskTextOutside' + secNum + ' ' + taskType
444
- } else {
445
- return classStr + ' taskTextOutsideRight taskTextOutside' + secNum + ' ' + taskType + ' width-' + textWidth
446
- }
447
- } else {
448
- return classStr + ' taskText taskText' + secNum + ' ' + taskType + ' width-' + textWidth
449
- }
450
- })
451
-
452
- const securityLevel = getConfig().securityLevel
453
-
454
- // Wrap the tasks in an a tag for working links without javascript
455
- if (securityLevel === 'sandbox') {
456
- let sandboxElement
457
- sandboxElement = select('#i' + id)
458
- const doc = sandboxElement.nodes()[0].contentDocument
459
-
460
- rectangles
461
- .filter(function (d) {
462
- return links[d.id] !== undefined
463
- })
464
- .each(function (o) {
465
- var taskRect = doc.querySelector('#' + o.id)
466
- var taskText = doc.querySelector('#' + o.id + '-text')
467
- const oldParent = taskRect.parentNode
468
- var Link = doc.createElement('a')
469
- Link.setAttribute('xlink:href', links[o.id])
470
- Link.setAttribute('target', '_top')
471
- oldParent.appendChild(Link)
472
- Link.appendChild(taskRect)
473
- Link.appendChild(taskText)
474
- })
475
- }
476
- }
477
- /**
478
- * @param theGap
479
- * @param theTopPad
480
- * @param theSidePad
481
- * @param w
482
- * @param h
483
- * @param tasks
484
- * @param {unknown[]} excludes
485
- * @param {unknown[]} includes
486
- */
487
- function drawExcludeDays(theGap, theTopPad, theSidePad, w, h, tasks, excludes, includes) {
488
- if (excludes.length === 0 && includes.length === 0) {
489
- return
490
- }
491
-
492
- let minTime
493
- let maxTime
494
- for (const { startTime, endTime } of tasks) {
495
- if (minTime === undefined || startTime < minTime) {
496
- minTime = startTime
497
- }
498
- if (maxTime === undefined || endTime > maxTime) {
499
- maxTime = endTime
500
- }
501
- }
502
-
503
- if (!minTime || !maxTime) {
504
- return
505
- }
506
-
507
- if (dayjs(maxTime).diff(dayjs(minTime), 'year') > 5) {
508
- console.warn(
509
- 'The difference between the min and max time is more than 5 years. This will cause performance issues. Skipping drawing exclude days.'
510
- )
511
- return
512
- }
513
-
514
- const dateFormat = diagObj.db.getDateFormat()
515
- const excludeRanges = []
516
- let range = null
517
- let d = dayjs(minTime)
518
- while (d.valueOf() <= maxTime) {
519
- if (diagObj.db.isInvalidDate(d, dateFormat, excludes, includes)) {
520
- if (!range) {
521
- range = {
522
- start: d,
523
- end: d
524
- }
525
- } else {
526
- range.end = d
527
- }
528
- } else {
529
- if (range) {
530
- excludeRanges.push(range)
531
- range = null
532
- }
533
- }
534
- d = d.add(1, 'd')
535
- }
536
-
537
- const rectangles = svg.append('g').selectAll('rect').data(excludeRanges).enter()
538
-
539
- rectangles
540
- .append('rect')
541
- .attr('id', function (d) {
542
- return 'exclude-' + d.start.format('YYYY-MM-DD')
543
- })
544
- .attr('x', function (d) {
545
- return timeScale(d.start) + theSidePad
546
- })
547
- .attr('y', conf.gridLineStartPadding)
548
- .attr('width', function (d) {
549
- const renderEnd = d.end.add(1, 'day')
550
- return timeScale(renderEnd) - timeScale(d.start)
551
- })
552
- .attr('height', h - theTopPad - conf.gridLineStartPadding)
553
- .attr('transform-origin', function (d, i) {
554
- return (
555
- (timeScale(d.start) + theSidePad + 0.5 * (timeScale(d.end) - timeScale(d.start))).toString() +
556
- 'px ' +
557
- (i * theGap + 0.5 * h).toString() +
558
- 'px'
559
- )
560
- })
561
- .attr('class', 'exclude-range')
562
- }
563
-
564
- /**
565
- * @param theSidePad
566
- * @param theTopPad
567
- * @param w
568
- * @param h
569
- */
570
- function makeGrid(theSidePad, theTopPad, w, h) {
571
- let bottomXAxis = axisBottom(timeScale)
572
- .tickSize(-h + theTopPad + conf.gridLineStartPadding)
573
- .tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'))
574
-
575
- const reTickInterval = /^([1-9]\d*)(millisecond|second|minute|hour|day|week|month)$/
576
- const resultTickInterval = reTickInterval.exec(diagObj.db.getTickInterval() || conf.tickInterval)
577
-
578
- if (resultTickInterval !== null) {
579
- const every = resultTickInterval[1]
580
- const interval = resultTickInterval[2]
581
- const weekday = diagObj.db.getWeekday() || conf.weekday
582
-
583
- switch (interval) {
584
- case 'millisecond':
585
- bottomXAxis.ticks(timeMillisecond.every(every))
586
- break
587
- case 'second':
588
- bottomXAxis.ticks(timeSecond.every(every))
589
- break
590
- case 'minute':
591
- bottomXAxis.ticks(timeMinute.every(every))
592
- break
593
- case 'hour':
594
- bottomXAxis.ticks(timeHour.every(every))
595
- break
596
- case 'day':
597
- bottomXAxis.ticks(timeDay.every(every))
598
- break
599
- case 'week':
600
- bottomXAxis.ticks(mapWeekdayToTimeFunction[weekday].every(every))
601
- break
602
- case 'month':
603
- bottomXAxis.ticks(timeMonth.every(every))
604
- break
605
- }
606
- }
607
-
608
- svg
609
- .append('g')
610
- .attr('class', 'grid')
611
- .attr('transform', 'translate(' + theSidePad + ', ' + (h - 50) + ')')
612
- .call(bottomXAxis)
613
- .selectAll('text')
614
- .style('text-anchor', 'middle')
615
- .attr('fill', '#000')
616
- .attr('stroke', 'none')
617
- .attr('font-size', 10)
618
- .attr('dy', '1em')
619
-
620
- if (diagObj.db.topAxisEnabled() || conf.topAxis) {
621
- let topXAxis = axisTop(timeScale)
622
- .tickSize(-h + theTopPad + conf.gridLineStartPadding)
623
- .tickFormat(timeFormat(diagObj.db.getAxisFormat() || conf.axisFormat || '%Y-%m-%d'))
624
-
625
- if (resultTickInterval !== null) {
626
- const every = resultTickInterval[1]
627
- const interval = resultTickInterval[2]
628
- const weekday = diagObj.db.getWeekday() || conf.weekday
629
-
630
- switch (interval) {
631
- case 'millisecond':
632
- topXAxis.ticks(timeMillisecond.every(every))
633
- break
634
- case 'second':
635
- topXAxis.ticks(timeSecond.every(every))
636
- break
637
- case 'minute':
638
- topXAxis.ticks(timeMinute.every(every))
639
- break
640
- case 'hour':
641
- topXAxis.ticks(timeHour.every(every))
642
- break
643
- case 'day':
644
- topXAxis.ticks(timeDay.every(every))
645
- break
646
- case 'week':
647
- topXAxis.ticks(mapWeekdayToTimeFunction[weekday].every(every))
648
- break
649
- case 'month':
650
- topXAxis.ticks(timeMonth.every(every))
651
- break
652
- }
653
- }
654
-
655
- svg
656
- .append('g')
657
- .attr('class', 'grid')
658
- .attr('transform', 'translate(' + theSidePad + ', ' + theTopPad + ')')
659
- .call(topXAxis)
660
- .selectAll('text')
661
- .style('text-anchor', 'middle')
662
- .attr('fill', '#000')
663
- .attr('stroke', 'none')
664
- .attr('font-size', 10)
665
- // .attr('dy', '1em');
666
- }
667
- }
668
-
669
- /**
670
- * @param theGap
671
- * @param theTopPad
672
- */
673
- function vertLabels(theGap, theTopPad) {
674
- let prevGap = 0
675
-
676
- const numOccurrences = Object.keys(categoryHeights).map(d => [d, categoryHeights[d]])
677
-
678
- svg
679
- .append('g') // without doing this, impossible to put grid lines behind text
680
- .selectAll('text')
681
- .data(numOccurrences)
682
- .enter()
683
- .append(function (d) {
684
- const rows = d[0].split(common.lineBreakRegex)
685
- const dy = -(rows.length - 1) / 2
686
-
687
- const svgLabel = doc.createElementNS('http://www.w3.org/2000/svg', 'text')
688
- svgLabel.setAttribute('dy', dy + 'em')
689
-
690
- for (const [j, row] of rows.entries()) {
691
- const tspan = doc.createElementNS('http://www.w3.org/2000/svg', 'tspan')
692
- tspan.setAttribute('alignment-baseline', 'central')
693
- tspan.setAttribute('x', '10')
694
- if (j > 0) {
695
- tspan.setAttribute('dy', '1em')
696
- }
697
- tspan.textContent = row
698
- svgLabel.appendChild(tspan)
699
- }
700
- return svgLabel
701
- })
702
- .attr('x', 10)
703
- .attr('y', function (d, i) {
704
- if (i > 0) {
705
- for (let j = 0; j < i; j++) {
706
- prevGap += numOccurrences[i - 1][1]
707
- return (d[1] * theGap) / 2 + prevGap * theGap + theTopPad
708
- }
709
- } else {
710
- return (d[1] * theGap) / 2 + theTopPad
711
- }
712
- })
713
- .attr('font-size', conf.sectionFontSize)
714
- .attr('class', function (d) {
715
- for (const [i, category] of categories.entries()) {
716
- if (d[0] === category) {
717
- return 'sectionTitle sectionTitle' + (i % conf.numberSectionStyles)
718
- }
719
- }
720
- return 'sectionTitle'
721
- })
722
- }
723
-
724
- /**
725
- * @param theSidePad
726
- * @param theTopPad
727
- * @param w
728
- * @param h
729
- */
730
- function drawToday(theSidePad, theTopPad, w, h) {
731
- const todayMarker = diagObj.db.getTodayMarker()
732
- if (todayMarker === 'off') {
733
- return
734
- }
735
-
736
- const todayG = svg.append('g').attr('class', 'today')
737
- const today = new Date()
738
- const todayLine = todayG.append('line')
739
-
740
- todayLine
741
- .attr('x1', timeScale(today) + theSidePad)
742
- .attr('x2', timeScale(today) + theSidePad)
743
- .attr('y1', conf.titleTopMargin)
744
- .attr('y2', h - conf.titleTopMargin)
745
- .attr('class', 'today')
746
-
747
- if (todayMarker !== '') {
748
- todayLine.attr('style', todayMarker.replace(/,/g, ';'))
749
- }
750
- }
751
-
752
- /**
753
- * From this stack exchange question:
754
- * http://stackoverflow.com/questions/1890203/unique-for-arrays-in-javascript
755
- *
756
- * @param arr
757
- */
758
- function checkUnique(arr) {
759
- const hash = {}
760
- const result = []
761
- for (let i = 0, l = arr.length; i < l; ++i) {
762
- if (!Object.prototype.hasOwnProperty.call(hash, arr[i])) {
763
- // it works with objects! in FF, at least
764
- hash[arr[i]] = true
765
- result.push(arr[i])
766
- }
767
- }
768
- return result
769
- }
770
- }
771
-
772
- export default {
773
- setConf,
774
- draw
775
- }