tg-ganttchart 0.0.7 → 0.0.8

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 (66) hide show
  1. package/babel.config.js +5 -0
  2. package/package.json +1 -4
  3. package/src/.eslintrc.js +18 -0
  4. package/src/App.vue +780 -0
  5. package/src/GanttElastic.standalone.vue +48 -0
  6. package/src/GanttElastic.vue +2305 -0
  7. package/src/assets/logo.png +0 -0
  8. package/src/components/Calendar/Calendar.vue +559 -0
  9. package/src/components/Calendar/CalendarRow.vue +112 -0
  10. package/src/components/Chart/Chart.vue +117 -0
  11. package/src/components/Chart/DaysHighlight.vue +60 -0
  12. package/src/components/Chart/DependencyLines.vue +112 -0
  13. package/src/components/Chart/Grid.vue +205 -0
  14. package/src/components/Chart/ProgressBar.vue +110 -0
  15. package/src/components/Chart/Row/Epic.vue +131 -0
  16. package/src/components/Chart/Row/Milestone.vue +117 -0
  17. package/src/components/Chart/Row/Project.vue +132 -0
  18. package/src/components/Chart/Row/Story.vue +127 -0
  19. package/src/components/Chart/Row/Subtask.vue +117 -0
  20. package/src/components/Chart/Row/Task.mixin.js +47 -0
  21. package/src/components/Chart/Row/Task.vue +82 -0
  22. package/src/components/Chart/Text.vue +105 -0
  23. package/src/components/Expander.vue +114 -0
  24. package/src/components/GanttElastic.standalone.vue +48 -0
  25. package/src/components/GanttElastic.vue +1646 -0
  26. package/src/components/Header/GanttViewFilter.vue +154 -0
  27. package/src/components/Header/Header.vue +266 -0
  28. package/src/components/MainView.vue +283 -0
  29. package/src/components/TaskList/ItemColumn.vue +212 -0
  30. package/src/components/TaskList/TaskList.vue +45 -0
  31. package/src/components/TaskList/TaskListHeader.vue +143 -0
  32. package/src/components/TaskList/TaskListItem.vue +35 -0
  33. package/src/components/bundle.js +28 -0
  34. package/src/components/components/Calendar/Calendar.vue +332 -0
  35. package/src/components/components/Calendar/CalendarRow.vue +96 -0
  36. package/src/components/components/Chart/Chart.vue +111 -0
  37. package/src/components/components/Chart/DaysHighlight.vue +71 -0
  38. package/src/components/components/Chart/DependencyLines.vue +112 -0
  39. package/src/components/components/Chart/Grid.vue +164 -0
  40. package/src/components/components/Chart/ProgressBar.vue +110 -0
  41. package/src/components/components/Chart/Row/Milestone.vue +117 -0
  42. package/src/components/components/Chart/Row/Project.vue +131 -0
  43. package/src/components/components/Chart/Row/Task.mixin.js +46 -0
  44. package/src/components/components/Chart/Row/Task.vue +107 -0
  45. package/src/components/components/Chart/Text.vue +105 -0
  46. package/src/components/components/Expander.vue +126 -0
  47. package/src/components/components/Header/Header.vue +265 -0
  48. package/src/components/components/MainView.vue +282 -0
  49. package/src/components/components/TaskList/ItemColumn.vue +121 -0
  50. package/src/components/components/TaskList/TaskList.vue +45 -0
  51. package/src/components/components/TaskList/TaskListHeader.vue +143 -0
  52. package/src/components/components/TaskList/TaskListItem.vue +35 -0
  53. package/src/components/components/bundle.js +28 -0
  54. package/src/components/style.js +308 -0
  55. package/src/index.js +12 -0
  56. package/src/main.js +6 -0
  57. package/src/style.js +398 -0
  58. package/vue.config.js +42 -0
  59. package/dist/demo.html +0 -1
  60. package/dist/tgganttchart.common.js +0 -9232
  61. package/dist/tgganttchart.common.js.map +0 -1
  62. package/dist/tgganttchart.css +0 -1
  63. package/dist/tgganttchart.umd.js +0 -9243
  64. package/dist/tgganttchart.umd.js.map +0 -1
  65. package/dist/tgganttchart.umd.min.js +0 -7
  66. package/dist/tgganttchart.umd.min.js.map +0 -1
Binary file
@@ -0,0 +1,559 @@
1
+ <template>
2
+ <div class="gantt-elastic__calendar-wrapper"
3
+ :style="{ ...root.style['calendar-wrapper'], width: root.state.options.width + 'px' }">
4
+ <div class="gantt-elastic__calendar" :style="{ ...root.style['calendar'], width: root.state.options.width + 'px' }">
5
+ <!-- Quarterly View -->
6
+ <calendar-row :items="dates.quarters" which="quarter" v-if="viewMode === 'quarter'"></calendar-row>
7
+
8
+ <!-- Monthly View -->
9
+ <calendar-row :items="dates.months" which="month" v-if="viewMode === 'month'"></calendar-row>
10
+
11
+ <!-- Weekly View -->
12
+ <calendar-row :items="dates.weeks" which="week" v-if="viewMode === 'week'"></calendar-row>
13
+
14
+ <!-- Daily View
15
+ <calendar-row :items="dates.days" which="day" v-if="viewMode === 'day' || viewMode === 'week'"></calendar-row> -->
16
+
17
+ <!-- Hourly View -->
18
+ <calendar-row :items="dates.days" which="day" v-if="viewMode === 'day'"></calendar-row>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script>
24
+ import dayjs from 'dayjs';
25
+ import isoWeek from 'dayjs/plugin/weekOfYear';
26
+ import quarterOfYear from 'dayjs/plugin/quarterOfYear';
27
+ import CalendarRow from './CalendarRow.vue';
28
+
29
+ // Extend dayjs with required plugins
30
+ dayjs.extend(isoWeek);
31
+ dayjs.extend(quarterOfYear);
32
+
33
+ export default {
34
+ name: 'Calendar',
35
+ components: {
36
+ CalendarRow
37
+ },
38
+ inject: ['root'],
39
+ data() {
40
+ return {
41
+ dayjsInstance: dayjs // Store dayjs instance for consistent usage
42
+ };
43
+ },
44
+
45
+ computed: {
46
+ viewMode() {
47
+ return this.root.state.options.calendar.viewMode || 'day';
48
+ },
49
+
50
+ dates() {
51
+ const days = this.generateDays();
52
+ const weeks = this.generateWeeks();
53
+ const months = this.generateMonths();
54
+ const quarters = this.generateQuarters()
55
+ const allDates = { days, weeks, months, quarters };
56
+ this.calculateCalendarDimensions(allDates);
57
+ return allDates;
58
+ }
59
+ },
60
+
61
+ methods: {
62
+
63
+ calculateCalendarDimensions({ days, weeks, months, quarters }) {
64
+ let height = 0;
65
+ if (this.viewMode === 'quarter' && quarters.length > 0) {
66
+ height += this.root.state.options.calendar.quarter?.height || 30;
67
+ }
68
+
69
+ // Add month height if visible
70
+ if ((this.viewMode === 'month') && months.length > 0) {
71
+ height += this.root.state.options.calendar.month.height || 25;
72
+ }
73
+
74
+ // Add week height if visible
75
+ if (this.viewMode === 'week' && weeks.length > 0) {
76
+ height += this.root.state.options.calendar.week?.height || 25;
77
+ }
78
+
79
+ // Add day height if visible
80
+ if ((this.viewMode === 'day') && days.length > 0) {
81
+ height += this.root.state.options.calendar.day.height;
82
+ }
83
+
84
+ this.root.state.options.calendar.height = height;
85
+ },
86
+
87
+ generateDays() {
88
+ let days = [];
89
+ if (!(this.viewMode === 'day' || this.viewMode === 'week')) {
90
+ return days;
91
+ }
92
+
93
+ const daysCount = this.howManyDaysFit();
94
+ if (daysCount.count === 0) {
95
+ return days;
96
+ }
97
+
98
+ const steps = this.root.state.options.times.steps;
99
+ const localeName = this.root.state.options.locale.name;
100
+ const dayStep = Math.ceil(steps.length / daysCount.count);
101
+
102
+ for (let dayIndex = 0, len = steps.length; dayIndex < len; dayIndex += dayStep) {
103
+ let dayWidthPx = 0;
104
+
105
+ // Calculate total width for this day group
106
+ for (let currentStep = 0; currentStep < dayStep; currentStep++) {
107
+ if (typeof steps[dayIndex + currentStep] !== 'undefined') {
108
+ dayWidthPx += steps[dayIndex + currentStep].width.px;
109
+ }
110
+ }
111
+
112
+ const date = this.dayjsInstance(steps[dayIndex].time);
113
+ let textWidth = 0;
114
+
115
+ if (typeof this.root.state.options.calendar.day.widths[dayIndex] !== 'undefined') {
116
+ textWidth = this.root.state.options.calendar.day.widths[dayIndex][daysCount.type];
117
+ }
118
+
119
+ // Use exact step offset for perfect alignment with grid lines
120
+ let x = Math.round(steps[dayIndex].offset.px); // Round to ensure exact pixel alignment
121
+ days.push({
122
+ index: dayIndex,
123
+ key: steps[dayIndex].time + 'd',
124
+ x,
125
+ y: this.getYPosition('day'),
126
+ width: dayWidthPx,
127
+ textWidth,
128
+ height: this.root.state.options.calendar.day.height,
129
+ label: this.root.state.options.calendar.day.format[daysCount.type](date.locale(localeName))
130
+ });
131
+ }
132
+
133
+ return days.map(item => ({
134
+ key: item.key,
135
+ children: [item]
136
+ }));
137
+ },
138
+
139
+ generateWeeks() {
140
+ let weeks = [];
141
+ if (this.viewMode !== 'week') {
142
+ return weeks;
143
+ }
144
+
145
+ const steps = this.root.state.options.times.steps;
146
+
147
+ let currentWeek = null;
148
+ let weekStartX = 0;
149
+ let weekWidth = 0;
150
+ let weekStartDate = null;
151
+ let weekEndDate = null;
152
+
153
+ for (let i = 0; i < steps.length; i++) {
154
+ try {
155
+ const stepDate = this.dayjsInstance(steps[i].time);
156
+ const weekNumber = stepDate.week();
157
+ const year = stepDate.year();
158
+ const weekKey = `${year}-W${weekNumber}`;
159
+
160
+ if (weekKey !== currentWeek) {
161
+ // Save previous week
162
+ if (currentWeek !== null) {
163
+ // Format the label as "Month Day-Day"
164
+ const startMonth = weekStartDate.format('MMMM');
165
+ const startDay = weekStartDate.date();
166
+ const endDay = weekEndDate.date();
167
+ let label = `${startMonth} ${startDay}`;
168
+
169
+ // If the week spans multiple months, show both months
170
+ if (weekStartDate.month() !== weekEndDate.month()) {
171
+ const endMonth = weekEndDate.format('MMMM');
172
+ label = `${startMonth} ${startDay} - ${endMonth} ${endDay}`;
173
+ } else if (startDay !== endDay) {
174
+ label = `${startMonth} ${startDay}-${endDay}`;
175
+ }
176
+
177
+ weeks.push({
178
+ key: currentWeek,
179
+ x: weekStartX, // Use exact step offset for perfect alignment
180
+ y: this.getYPosition('week'),
181
+ width: weekWidth,
182
+ textWidth: 50,
183
+ height: this.root.state.options.calendar.week?.height || 25,
184
+ label: label
185
+ });
186
+ }
187
+
188
+ // Start new week - use exact step offset
189
+ currentWeek = weekKey;
190
+ weekStartX = Math.round(steps[i].offset.px); // Round to ensure exact pixel alignment
191
+ weekWidth = steps[i].width.px;
192
+ weekStartDate = stepDate.clone().startOf('week');
193
+ weekEndDate = stepDate.clone().endOf('week');
194
+ } else {
195
+ // Same week, accumulate width and update end date
196
+ weekWidth += steps[i].width.px;
197
+ weekEndDate = stepDate.clone().endOf('week');
198
+ }
199
+ } catch (error) {
200
+ console.error('Error processing week:', error);
201
+ continue;
202
+ }
203
+ }
204
+
205
+ // Add the last week
206
+ if (currentWeek !== null) {
207
+ // Format the label as "Month Day-Day"
208
+ const startMonth = weekStartDate.format('MMMM');
209
+ const startDay = weekStartDate.date();
210
+ const endDay = weekEndDate.date();
211
+ let label = `${startMonth} ${startDay}`;
212
+
213
+ // If the week spans multiple months, show both months
214
+ if (weekStartDate.month() !== weekEndDate.month()) {
215
+ const endMonth = weekEndDate.format('MMMM');
216
+ label = `${startMonth} ${startDay} - ${endMonth} ${endDay}`;
217
+ } else if (startDay !== endDay) {
218
+ label = `${startMonth} ${startDay}-${endDay}`;
219
+ }
220
+
221
+ weeks.push({
222
+ key: currentWeek,
223
+ x: weekStartX, // Use exact step offset for perfect alignment
224
+ y: this.getYPosition('week'),
225
+ width: weekWidth,
226
+ textWidth: 60,
227
+ height: this.root.state.options.calendar.week?.height || 25,
228
+ label: label
229
+ });
230
+ }
231
+
232
+ return weeks.map(week => ({
233
+ key: week.key,
234
+ children: [week]
235
+ }));
236
+ },
237
+
238
+ generateMonths() {
239
+ let months = [];
240
+ if (!this.root.state.options.calendar.month.display) {
241
+ return months;
242
+ }
243
+ const monthsCount = this.howManyMonthsFit();
244
+ if (monthsCount.count === 0) {
245
+ return months;
246
+ }
247
+ const steps = this.root.state.options.times.steps;
248
+ const localeName = this.root.state.options.locale.name;
249
+ let formatNames = Object.keys(this.root.state.options.calendar.month.format);
250
+ let currentDate = dayjs(this.root.state.options.times.firstTime);
251
+ for (let monthIndex = 0; monthIndex < monthsCount.count; monthIndex++) {
252
+ let monthWidth = 0;
253
+ let monthOffset = Number.MAX_SAFE_INTEGER;
254
+ let finalDate = dayjs(currentDate)
255
+ .add(1, 'month')
256
+ .startOf('month');
257
+ if (finalDate.valueOf() > this.root.state.options.times.lastTime) {
258
+ finalDate = dayjs(this.root.state.options.times.lastTime);
259
+ }
260
+ // we must find first and last step to get the offsets / widths
261
+ for (let step = 0, len = this.root.state.options.times.steps.length; step < len; step++) {
262
+ let currentStep = this.root.state.options.times.steps[step];
263
+ if (currentStep.time >= currentDate.valueOf() && currentStep.time < finalDate.valueOf()) {
264
+ monthWidth += currentStep.width.px;
265
+ if (currentStep.offset.px < monthOffset) {
266
+ monthOffset = currentStep.offset.px;
267
+ }
268
+ }
269
+ }
270
+ let label = '';
271
+ let choosenFormatName;
272
+ for (let formatName of formatNames) {
273
+ if (this.root.state.options.calendar.month.maxWidths[formatName] + 2 <= monthWidth) {
274
+ label = this.root.state.options.calendar.month.format[formatName](currentDate.locale(localeName));
275
+ choosenFormatName = formatName;
276
+ }
277
+ }
278
+ let textWidth = 0;
279
+ if (typeof this.root.state.options.calendar.month.widths[monthIndex] !== 'undefined') {
280
+ textWidth = this.root.state.options.calendar.month.widths[monthIndex][choosenFormatName];
281
+ }
282
+ // Use exact step offset for perfect alignment with grid lines
283
+ let x = Math.round(monthOffset); // Round to ensure exact pixel alignment
284
+ months.push({
285
+ index: monthIndex,
286
+ key: monthIndex + 'm',
287
+ x,
288
+ y: 0,
289
+ width: monthWidth,
290
+ textWidth,
291
+ choosenFormatName,
292
+ height: this.root.state.options.calendar.month.height,
293
+ label
294
+ });
295
+ currentDate = currentDate.add(1, 'month').startOf('month');
296
+ if (currentDate.valueOf() > this.root.state.options.times.lastTime) {
297
+ currentDate = dayjs(this.root.state.options.times.lastTime);
298
+ }
299
+ }
300
+ return months.map(item => ({
301
+ key: item.key,
302
+ children: [item]
303
+ }));
304
+ },
305
+
306
+
307
+ generateQuarters() {
308
+ let quarters = [];
309
+ if (this.viewMode !== 'quarter') {
310
+ return quarters;
311
+ }
312
+
313
+ const steps = this.root.state.options.times.steps;
314
+
315
+ // Debug: Log quarter generation info
316
+ console.log('Quarter Generation Debug:', {
317
+ stepsCount: steps.length,
318
+ firstStepTime: this.dayjsInstance(steps[0]?.time).format('YYYY-MM-DD'),
319
+ lastStepTime: this.dayjsInstance(steps[steps.length - 1]?.time).format('YYYY-MM-DD'),
320
+ firstStepQuarter: this.dayjsInstance(steps[0]?.time).quarter(),
321
+ lastStepQuarter: this.dayjsInstance(steps[steps.length - 1]?.time).quarter()
322
+ });
323
+
324
+ let currentQuarter = null;
325
+ let quarterStartX = 0;
326
+ let quarterWidth = 0;
327
+ let quarterStartDate = null;
328
+ let quarterEndDate = null;
329
+
330
+ for (let i = 0; i < steps.length; i++) {
331
+ try {
332
+ // Use UTC to avoid locale/timezone issues
333
+ const stepDate = this.dayjsInstance(steps[i].time);
334
+ const quarter = stepDate.quarter();
335
+ const year = stepDate.year();
336
+ const quarterKey = `Q${quarter}-${year}`;
337
+
338
+ console.log(`Step ${i}:`, {
339
+ time: stepDate.format('YYYY-MM-DD'),
340
+ quarter: quarter,
341
+ year: year,
342
+ quarterKey: quarterKey,
343
+ currentQuarter: currentQuarter
344
+ });
345
+
346
+ if (quarterKey !== currentQuarter) {
347
+ // Save previous quarter
348
+ if (currentQuarter !== null) {
349
+ // Format the label as "Month – Month 'Year"
350
+ const startMonth = quarterStartDate.format('MMMM');
351
+ const endMonth = quarterEndDate.format('MMMM');
352
+ const shortYear = quarterEndDate.format("YY");
353
+ const label = `${startMonth} – ${endMonth} '${shortYear}`;
354
+
355
+
356
+ quarters.push({
357
+ key: currentQuarter,
358
+ x: quarterStartX, // Use exact step offset for perfect alignment
359
+ y: this.getYPosition('quarter'),
360
+ width: quarterWidth,
361
+ textWidth: 50,
362
+ height: this.root.state.options.calendar.quarter?.height || 30,
363
+ label: label
364
+ });
365
+ }
366
+
367
+ // Start new quarter
368
+ currentQuarter = quarterKey;
369
+ quarterStartX = Math.round(steps[i].offset.px); // Round to ensure exact pixel alignment
370
+ quarterWidth = steps[i].width.px;
371
+ quarterStartDate = stepDate.clone().startOf('quarter');
372
+ quarterEndDate = stepDate.clone().endOf('quarter');
373
+ } else {
374
+ // Same quarter, accumulate width and update end date
375
+ quarterWidth += steps[i].width.px;
376
+ quarterEndDate = stepDate.clone().endOf('quarter');
377
+ }
378
+ } catch (error) {
379
+ console.error('Error processing quarter:', error);
380
+ continue;
381
+ }
382
+ }
383
+
384
+ // Add the last quarter
385
+ if (currentQuarter !== null) {
386
+ // Format the label as "Month – Month 'Year"
387
+ const startMonth = quarterStartDate.format('MMMM');
388
+ const endMonth = quarterEndDate.format('MMMM');
389
+ const shortYear = quarterEndDate.format("YY");
390
+ const label = `${startMonth} – ${endMonth} '${shortYear}`;
391
+
392
+
393
+ quarters.push({
394
+ key: currentQuarter,
395
+ x: quarterStartX, // Use exact step offset for perfect alignment
396
+ y: this.getYPosition('quarter'),
397
+ width: quarterWidth,
398
+ textWidth: 60,
399
+ height: this.root.state.options.calendar.quarter?.height || 30,
400
+ label: label
401
+ });
402
+ }
403
+
404
+
405
+ return quarters.map(quarter => ({
406
+ key: quarter.key,
407
+ children: [quarter]
408
+ }));
409
+ },
410
+
411
+ getYPosition(type) {
412
+ let y = 0;
413
+ switch (type) {
414
+ case 'quarter':
415
+ return 0;
416
+ case 'month':
417
+ // Put month row below quarter if in quarterly view
418
+ return this.viewMode === 'quarter'
419
+ ? (this.root.state.options.calendar.quarter?.height || 30)
420
+ : 0;
421
+ case 'week':
422
+ // Put week row below month and quarter if applicable
423
+
424
+ if (this.viewMode === 'quarter') {
425
+ y += this.root.state.options.calendar.quarter?.height || 30;
426
+ }
427
+ if (this.viewMode === 'month' || this.viewMode === 'quarter') {
428
+ y += this.root.state.options.calendar.month.height;
429
+ }
430
+ return y;
431
+ case 'day':
432
+ // Put day below week if applicable
433
+ y = 0;
434
+ if (this.viewMode === 'quarter') {
435
+ y += this.root.state.options.calendar.quarter?.height || 30;
436
+ }
437
+ if (this.viewMode === 'month' || this.viewMode === 'quarter') {
438
+ y += this.root.state.options.calendar.month.height;
439
+ }
440
+ if (this.viewMode === 'week') {
441
+ y += this.root.state.options.calendar.week?.height || 25;
442
+ }
443
+ return y;
444
+ case 'hour':
445
+ // Put hour row below day if applicable
446
+ y = 0;
447
+ if (this.viewMode === 'quarter') {
448
+ y += this.root.state.options.calendar.quarter?.height || 30;
449
+ }
450
+ if (this.viewMode === 'month' || this.viewMode === 'quarter') {
451
+ y += this.root.state.options.calendar.month.height;
452
+ }
453
+ if (this.viewMode === 'week') {
454
+ y += this.root.state.options.calendar.week?.height || 25;
455
+ }
456
+ if (this.viewMode === 'day') {
457
+ y += this.root.state.options.calendar.day.height;
458
+ }
459
+ return y;
460
+ default:
461
+ return 0;
462
+ }
463
+ },
464
+
465
+ howManyHoursFit(dayIndex) {
466
+ const stroke = 1;
467
+ const additionalSpace = stroke + 2;
468
+ let fullCellWidth = this.root.state.options.times.steps[dayIndex].width.px;
469
+ let formatNames = Object.keys(this.root.state.options.calendar.hour.format);
470
+ for (let hours = 24; hours > 1; hours = Math.ceil(hours / 2)) {
471
+ for (let formatName of formatNames) {
472
+ if (
473
+ (this.root.state.options.calendar.hour.maxWidths[formatName] + additionalSpace) * hours <= fullCellWidth &&
474
+ hours > 1
475
+ ) {
476
+ return {
477
+ count: hours,
478
+ type: formatName
479
+ };
480
+ }
481
+ }
482
+ }
483
+ return {
484
+ count: 0,
485
+ type: ''
486
+ };
487
+ },
488
+
489
+ howManyDaysFit() {
490
+ const stroke = 1;
491
+ const additionalSpace = stroke + 2;
492
+ let fullWidth = this.root.state.options.width;
493
+ let formatNames = Object.keys(this.root.state.options.calendar.day.format);
494
+ for (let days = this.root.state.options.times.steps.length; days > 1; days = Math.ceil(days / 2)) {
495
+ for (let formatName of formatNames) {
496
+ if (
497
+ (this.root.state.options.calendar.day.maxWidths[formatName] + additionalSpace) * days <= fullWidth &&
498
+ days > 1
499
+ ) {
500
+ return {
501
+ count: days,
502
+ type: formatName
503
+ };
504
+ }
505
+ }
506
+ }
507
+ return {
508
+ count: 0,
509
+ type: ''
510
+ };
511
+ },
512
+
513
+
514
+ howManyMonthsFit() {
515
+ const stroke = 1;
516
+ const additionalSpace = stroke + 2;
517
+ let fullWidth = this.root.state.options.width;
518
+ let formatNames = Object.keys(this.root.state.options.calendar.month.format);
519
+ let currentMonth = dayjs(this.root.state.options.times.firstTime);
520
+ let previousMonth = currentMonth.clone();
521
+ const lastTime = this.root.state.options.times.lastTime;
522
+ let monthsCount = this.root.monthsCount(
523
+ this.root.state.options.times.firstTime,
524
+ this.root.state.options.times.lastTime
525
+ );
526
+ if (monthsCount === 1) {
527
+ for (let formatName of formatNames) {
528
+ if (this.root.state.options.calendar.month.maxWidths[formatName] + additionalSpace <= fullWidth) {
529
+ return {
530
+ count: 1,
531
+ type: formatName
532
+ };
533
+ }
534
+ }
535
+ }
536
+ for (let months = monthsCount; months > 1; months = Math.ceil(months / 2)) {
537
+ for (let formatName of formatNames) {
538
+ if (
539
+ (this.root.state.options.calendar.month.maxWidths[formatName] + additionalSpace) * months <= fullWidth &&
540
+ months > 1
541
+ ) {
542
+ return {
543
+ count: months,
544
+ type: formatName
545
+ };
546
+ }
547
+ }
548
+ }
549
+ return {
550
+ count: 0,
551
+ type: formatNames[0]
552
+ };
553
+
554
+ },
555
+
556
+
557
+ }
558
+ };
559
+ </script>
@@ -0,0 +1,112 @@
1
+ <template>
2
+ <section>
3
+ <!-- <div>
4
+ {{ items }}
5
+ </div> -->
6
+
7
+ <div :class="'gantt-elastic__calendar-row gantt-elastic__calendar-row--' + which" :style="rowStyle">
8
+ <div v-for="(item, itemIndex) in items" :key="item.key"
9
+ :class="'gantt-elastic__calendar-row-rect gantt-elastic__calendar-row-rect--' + which" :style="rectStyle">
10
+ <div :class="'gantt-elastic__calendar-row-rect-child gantt-elastic__calendar-row-rect-child--' + which"
11
+ v-for="(child, childIndex) in item.children" :key="child.key" :style="rectChildStyle[itemIndex][childIndex]">
12
+ <div :class="'gantt-elastic__calendar-row-text gantt-elastic__calendar-row-text--' + which"
13
+ :style="textStyle(child)">
14
+ {{ child.label }}
15
+ </div>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ </section>
20
+ </template>
21
+
22
+ <script>
23
+ export default {
24
+ name: 'CalendarRow',
25
+ inject: ['root'],
26
+ props: ['items', 'which'],
27
+ data() {
28
+ return {};
29
+ },
30
+ methods: {
31
+ /**
32
+ * Get x position
33
+ *
34
+ * @returns {number}
35
+ */
36
+ getTextX(item) {
37
+ // Calculate center position within the item
38
+ let x = item.width / 2 - item.textWidth / 2;
39
+
40
+ // Add padding to avoid overlap with grid lines
41
+ const gridLineWidth = this.root.style['grid-line-vertical']['stroke-width'] || 1;
42
+ const padding = Math.max(gridLineWidth / 2 + 2, 4); // At least 4px padding
43
+
44
+ // For month view, use perfect centering with bounds checking
45
+ if (this.which === 'month') {
46
+ // Perfect center calculation
47
+ x = (item.width - item.textWidth) / 2;
48
+
49
+ // Ensure text stays within bounds with padding
50
+ if (x < padding) {
51
+ x = padding;
52
+ } else if (x + item.textWidth + padding > item.width) {
53
+ x = item.width - item.textWidth - padding;
54
+ }
55
+ } else {
56
+ // For other view types, ensure text stays within bounds
57
+ if (x + item.textWidth + padding > item.width) {
58
+ x = item.width - item.textWidth - padding;
59
+ } else if (x < padding) {
60
+ x = padding;
61
+ }
62
+ }
63
+
64
+ return x;
65
+ }
66
+ },
67
+ computed: {
68
+ rowStyle() {
69
+ return {
70
+ ...this.root.style['calendar-row'],
71
+ ...this.root.style['calendar-row--' + this.which],
72
+ position: 'relative' // Ensure relative positioning for absolute children
73
+ };
74
+ },
75
+ rectStyle() {
76
+ return { ...this.root.style['calendar-row-rect'], ...this.root.style['calendar-row-rect--' + this.which] };
77
+ },
78
+ rectChildStyle() {
79
+ const basicStyle = {
80
+ ...this.root.style['calendar-row-rect-child'],
81
+ ...this.root.style['calendar-row-rect-child--' + this.which]
82
+ };
83
+ const style = [];
84
+ for (let item of this.items) {
85
+ const childrenStyle = [];
86
+ for (let child of item.children) {
87
+ childrenStyle.push({
88
+ ...basicStyle,
89
+ width: child.width + 'px',
90
+ height: child.height + 'px',
91
+ left: child.x + 'px', // Use exact x position for perfect alignment
92
+ position: 'absolute' // Ensure absolute positioning
93
+ });
94
+ }
95
+ style.push(childrenStyle);
96
+ }
97
+ return style;
98
+ },
99
+ textStyle() {
100
+ const basicStyle = {
101
+ ...this.root.style['calendar-row-text'],
102
+ ...this.root.style['calendar-row-text--' + this.which]
103
+ };
104
+ return child => {
105
+ const style = { ...basicStyle };
106
+ // Remove left positioning for month view - let CSS handle centering
107
+ return style;
108
+ };
109
+ }
110
+ }
111
+ };
112
+ </script>