tg-ganttchart 0.0.3 → 0.0.4

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 (35) hide show
  1. package/babel.config.js +5 -0
  2. package/package.json +8 -8
  3. package/src/components/GanttElastic.standalone.vue +48 -0
  4. package/src/components/GanttElastic.vue +1646 -0
  5. package/src/components/components/Calendar/Calendar.vue +332 -0
  6. package/src/components/components/Calendar/CalendarRow.vue +96 -0
  7. package/src/components/components/Chart/Chart.vue +111 -0
  8. package/src/components/components/Chart/DaysHighlight.vue +71 -0
  9. package/src/components/components/Chart/DependencyLines.vue +112 -0
  10. package/src/components/components/Chart/Grid.vue +164 -0
  11. package/src/components/components/Chart/ProgressBar.vue +110 -0
  12. package/src/components/components/Chart/Row/Milestone.vue +117 -0
  13. package/src/components/components/Chart/Row/Project.vue +131 -0
  14. package/src/components/components/Chart/Row/Task.mixin.js +46 -0
  15. package/src/components/components/Chart/Row/Task.vue +107 -0
  16. package/src/components/components/Chart/Text.vue +105 -0
  17. package/src/components/components/Expander.vue +126 -0
  18. package/src/components/components/Header/Header.vue +264 -0
  19. package/src/components/components/MainView.vue +282 -0
  20. package/src/components/components/TaskList/ItemColumn.vue +121 -0
  21. package/src/components/components/TaskList/TaskList.vue +45 -0
  22. package/src/components/components/TaskList/TaskListHeader.vue +143 -0
  23. package/src/components/components/TaskList/TaskListItem.vue +35 -0
  24. package/src/components/components/bundle.js +28 -0
  25. package/src/components/style.js +308 -0
  26. package/src/index.js +12 -0
  27. package/vue.config.js +42 -0
  28. package/dist/demo.html +0 -1
  29. package/dist/tgganttchart.common.js +0 -9529
  30. package/dist/tgganttchart.common.js.map +0 -1
  31. package/dist/tgganttchart.css +0 -1
  32. package/dist/tgganttchart.umd.js +0 -9540
  33. package/dist/tgganttchart.umd.js.map +0 -1
  34. package/dist/tgganttchart.umd.min.js +0 -7
  35. package/dist/tgganttchart.umd.min.js.map +0 -1
@@ -0,0 +1,107 @@
1
+
2
+ <template>
3
+ <g
4
+ class="gantt-elastic__chart-row-bar-wrapper gantt-elastic__chart-row-task-wrapper"
5
+ :style="{
6
+ ...root.style['chart-row-bar-wrapper'],
7
+ ...root.style['chart-row-task-wrapper'],
8
+ ...task.style['chart-row-bar-wrapper']
9
+ }"
10
+ >
11
+ <foreignObject
12
+ class="gantt-elastic__chart-expander gantt-elastic__chart-expander--task"
13
+ :style="{
14
+ ...root.style['chart-expander'],
15
+ ...root.style['chart-expander--task'],
16
+ ...task.style['chart-expander']
17
+ }"
18
+ :x="task.x - root.state.options.chart.expander.offset - root.state.options.chart.expander.size"
19
+ :y="task.y + (root.state.options.row.height - root.state.options.chart.expander.size) / 2"
20
+ :width="root.state.options.chart.expander.size"
21
+ :height="root.state.options.chart.expander.size"
22
+ v-if="displayExpander"
23
+ >
24
+ <expander :tasks="[task]" :options="root.state.options.chart.expander" type="chart"></expander>
25
+ </foreignObject>
26
+ <svg
27
+ class="gantt-elastic__chart-row-bar gantt-elastic__chart-row-task"
28
+ :style="{ ...root.style['chart-row-bar'], ...root.style['chart-row-task'], ...task.style['chart-row-bar'] }"
29
+ :x="task.x"
30
+ :y="task.y"
31
+ :width="task.width"
32
+ :height="task.height"
33
+ :viewBox="`0 0 ${task.width} ${task.height}`"
34
+ @click="emitEvent('click', $event)"
35
+ @mouseenter="emitEvent('mouseenter', $event)"
36
+ @mouseover="emitEvent('mouseover', $event)"
37
+ @mouseout="emitEvent('mouseout', $event)"
38
+ @mousemove="emitEvent('mousemove', $event)"
39
+ @mousedown="emitEvent('mousedown', $event)"
40
+ @mouseup="emitEvent('mouseup', $event)"
41
+ @mousewheel="emitEvent('mousewheel', $event)"
42
+ @touchstart="emitEvent('touchstart', $event)"
43
+ @touchmove="emitEvent('touchmove', $event)"
44
+ @touchend="emitEvent('touchend', $event)"
45
+ xmlns="http://www.w3.org/2000/svg"
46
+ >
47
+ <defs>
48
+ <clipPath :id="clipPathId">
49
+ <polygon :points="getPoints"></polygon>
50
+ </clipPath>
51
+ </defs>
52
+ <polygon
53
+ class="gantt-elastic__chart-row-bar-polygon gantt-elastic__chart-row-task-polygon"
54
+ :style="{
55
+ ...root.style['chart-row-bar-polygon'],
56
+ ...root.style['chart-row-task-polygon'],
57
+ ...task.style['base'],
58
+ ...task.style['chart-row-bar-polygon']
59
+ }"
60
+ :points="getPoints"
61
+ ></polygon>
62
+ <progress-bar :task="task" :clip-path="'url(#' + clipPathId + ')'"></progress-bar>
63
+ </svg>
64
+ <chart-text :task="task" v-if="root.state.options.chart.text.display"></chart-text>
65
+ </g>
66
+ </template>
67
+
68
+ <script>
69
+ import ChartText from '../Text.vue';
70
+ import ProgressBar from '../ProgressBar.vue';
71
+ import Expander from '../../Expander.vue';
72
+ import taskMixin from './Task.mixin.js';
73
+ export default {
74
+ name: 'Task',
75
+ components: {
76
+ ChartText,
77
+ ProgressBar,
78
+ Expander
79
+ },
80
+ inject: ['root'],
81
+ props: ['task'],
82
+ mixins: [taskMixin],
83
+ data() {
84
+ return {};
85
+ },
86
+ computed: {
87
+ /**
88
+ * Get clip path id
89
+ *
90
+ * @returns {string}
91
+ */
92
+ clipPathId() {
93
+ return 'gantt-elastic__task-clip-path-' + this.task.id;
94
+ },
95
+
96
+ /**
97
+ * Get points
98
+ *
99
+ * @returns {string}
100
+ */
101
+ getPoints() {
102
+ const task = this.task;
103
+ return `0,0 ${task.width},0 ${task.width},${task.height} 0,${task.height}`;
104
+ }
105
+ }
106
+ };
107
+ </script>
@@ -0,0 +1,105 @@
1
+
2
+ <template>
3
+ <svg
4
+ class="gantt-elastic__chart-row-text-wrapper"
5
+ :style="{ ...root.style['chart-row-text-wrapper'] }"
6
+ :x="task.x + task.width + root.state.options.chart.text.offset"
7
+ :y="task.y - root.state.options.chart.grid.horizontal.gap"
8
+ :width="getWidth"
9
+ :height="getHeight"
10
+ >
11
+ <foreignObject x="0" y="0" width="100%" :height="getHeight">
12
+ <div
13
+ xmlns="http://www.w3.org/1999/xhtml"
14
+ class="gantt-elastic__chart-row-text"
15
+ :style="{ ...root.style['chart-row-text'] }"
16
+ >
17
+ <div
18
+ class="gantt-elastic__chart-row-text-content gantt-elastic__chart-row-text-content--text"
19
+ :style="{
20
+ ...root.style['chart-row-text-content'],
21
+ ...root.style['chart-row-text-content--text'],
22
+ ...contentStyle
23
+ }"
24
+ v-if="!html"
25
+ >
26
+ <div>{{ task.label }}</div>
27
+ </div>
28
+ <div
29
+ class="gantt-elastic__chart-row-text-content gantt-elastic__chart-row-text-content--html"
30
+ :style="{
31
+ ...root.style['chart-row-text-content'],
32
+ ...root.style['chart-row-text-content--html'],
33
+ ...contentStyle
34
+ }"
35
+ v-if="html"
36
+ v-html="task.label"
37
+ ></div>
38
+ </div>
39
+ </foreignObject>
40
+ </svg>
41
+ </template>
42
+
43
+ <script>
44
+ export default {
45
+ name: 'ChartText',
46
+ inject: ['root'],
47
+ props: ['task'],
48
+ data() {
49
+ return {};
50
+ },
51
+ computed: {
52
+ /**
53
+ * Get width
54
+ *
55
+ * @returns {number}
56
+ */
57
+ getWidth() {
58
+ const textStyle = this.root.style['chart-row-text'];
59
+ const font = `${textStyle['font-weight']} ${textStyle['font-size']} ${textStyle['font-family']}`;
60
+ this.setFont(font); // Set font in a method
61
+ const textWidth = this.root.state.ctx.measureText(this.task.label).width;
62
+ return textWidth + this.root.state.options.chart.text.xPadding * 2;
63
+ },
64
+
65
+ /**
66
+ * Get height
67
+ *
68
+ * @returns {number}
69
+ */
70
+ getHeight() {
71
+ return this.task.height + this.root.state.options.chart.grid.horizontal.gap * 2;
72
+ },
73
+
74
+ /**
75
+ * Get content style
76
+ *
77
+ * @returns {object}
78
+ */
79
+ contentStyle() {
80
+ return { height: '100%', 'line-height': this.getHeight + 'px' };
81
+ },
82
+
83
+ /**
84
+ * Should we render text as html?
85
+ *
86
+ * @returns {boolean}
87
+ */
88
+ html() {
89
+ const cols = this.root.state.options.taskList.columns;
90
+ for (let i = 0, len = cols.length; i < len; i++) {
91
+ const col = cols[i];
92
+ if (col.value === 'label' && typeof col.html !== 'undefined' && col.html) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+ },
99
+ methods: {
100
+ setFont(font) {
101
+ this.root.state.ctx.font = font; // Set font in a method
102
+ }
103
+ }
104
+ };
105
+ </script>
@@ -0,0 +1,126 @@
1
+
2
+ <template>
3
+ <div :class="getClassPrefix() + '-wrapper'" :style="{ ...root.style[getClassPrefix(false) + '-wrapper'], ...style }">
4
+ <svg
5
+ :class="getClassPrefix() + '-content'"
6
+ :style="{ ...root.style[getClassPrefix(false) + '-content'] }"
7
+ :width="options.size"
8
+ :height="options.size"
9
+ v-if="allChildren.length"
10
+ @click="toggle"
11
+ >
12
+ <rect
13
+ :class="getClassPrefix() + '-border'"
14
+ :style="{ ...root.style[getClassPrefix(false) + '-border'], ...borderStyle }"
15
+ :x="border"
16
+ :y="border"
17
+ :width="options.size - border * 2"
18
+ :height="options.size - border * 2"
19
+ rx="2"
20
+ ry="2"
21
+ ></rect>
22
+ <line
23
+ :class="getClassPrefix() + '-line'"
24
+ :style="{ ...root.style[getClassPrefix(false) + '-line'] }"
25
+ v-if="allChildren.length"
26
+ :x1="lineOffset"
27
+ :y1="options.size / 2"
28
+ :x2="options.size - lineOffset"
29
+ :y2="options.size / 2"
30
+ ></line>
31
+ <line
32
+ :class="getClassPrefix() + '-line'"
33
+ :style="{ ...root.style[getClassPrefix(false) + '-line'] }"
34
+ v-if="collapsed"
35
+ :x1="options.size / 2"
36
+ :y1="lineOffset"
37
+ :x2="options.size / 2"
38
+ :y2="options.size - lineOffset"
39
+ ></line>
40
+ </svg>
41
+ </div>
42
+ </template>
43
+
44
+ <script>
45
+ export default {
46
+ name: 'Expander',
47
+ inject: ['root'],
48
+ props: ['tasks', 'options', 'type'],
49
+ data() {
50
+ const border = 0.5;
51
+ return {
52
+ border,
53
+ borderStyle: {
54
+ 'stroke-width': border
55
+ },
56
+ lineOffset: 5
57
+ };
58
+ },
59
+ computed: {
60
+ style() {
61
+ if (this.type !== 'taskList') {
62
+ return {};
63
+ }
64
+ const margin = this.root.state.options.taskList.expander.margin;
65
+ const padding = this.tasks[0].parents.length * this.root.state.options.taskList.expander.padding;
66
+ return {
67
+ 'padding-left': padding + margin + 'px',
68
+ margin: 'auto 0'
69
+ };
70
+ },
71
+ /**
72
+ * Get all tasks
73
+ *
74
+ * @returns {array}
75
+ */
76
+ allChildren() {
77
+ const children = [];
78
+ this.tasks.forEach(task => {
79
+ task.allChildren.forEach(childId => {
80
+ children.push(childId);
81
+ });
82
+ });
83
+ return children;
84
+ },
85
+ /**
86
+ * Is current expander collapsed?
87
+ *
88
+ * @returns {boolean}
89
+ */
90
+ collapsed() {
91
+ if (this.tasks.length === 0) {
92
+ return false;
93
+ }
94
+ let collapsed = 0;
95
+ for (let i = 0, len = this.tasks.length; i < len; i++) {
96
+ if (this.tasks[i].collapsed) {
97
+ collapsed++;
98
+ }
99
+ }
100
+ return collapsed === this.tasks.length;
101
+ }
102
+ },
103
+ methods: {
104
+ /**
105
+ * Get specific class prefix
106
+ *
107
+ * @returns {string}
108
+ */
109
+ getClassPrefix(full = true) {
110
+ return `${full ? 'gantt-elastic__' : ''}${this.options.type}-expander`;
111
+ },
112
+ /**
113
+ * Toggle expander
114
+ */
115
+ toggle() {
116
+ if (this.tasks.length === 0) {
117
+ return;
118
+ }
119
+ const collapsed = !this.collapsed;
120
+ this.tasks.forEach(task => {
121
+ task.collapsed = collapsed;
122
+ });
123
+ }
124
+ }
125
+ };
126
+ </script>
@@ -0,0 +1,264 @@
1
+ <template>
2
+ <div class="gantt-elastic__header" :style="{ ...style['header'] }">
3
+ <div class="gantt-elastic__header-title" :style="{ ...style['header-title'] }">
4
+ <div class="gantt-elastic__header-title--text" :style="{ ...style['header-title--text'] }"
5
+ v-if="!opts.title.html">
6
+ {{ opts.title.label }}
7
+ </div>
8
+ <div class="gantt-elastic__header-title--html" :style="{ ...style['header-title--html'] }" v-if="opts.title.html"
9
+ v-html="opts.title.label"></div>
10
+ </div>
11
+ <div class="gantt-elastic__header-options" :style="{ ...style['header-options'] }">
12
+ <button class="gantt-elastic__header-btn-recenter" :style="{ ...style['header-btn-recenter'] }"
13
+ @click.prevent="recenterPosition">
14
+ {{ opts.locale.Now }}
15
+ </button>
16
+ <label class="gantt-elastic__header-label" :style="{ ...style['header-label'] }">
17
+ {{ opts.locale["X-Scale"] }}
18
+ <div class="gantt-elastic__header-slider-wrapper" :style="{ ...style['header-slider-wrapper'] }">
19
+ <vue-slider class="gantt-elastic__header-slider" tooltip="none" :style="{ ...style['header-slider'] }"
20
+ :process-style="{ ...style['header-slider--process'] }"
21
+ :slider-style="{ ...style['header-slider--slider'] }" v-model="scale" :max="24" :min="2"
22
+ width="100px"></vue-slider>
23
+ </div>
24
+ </label>
25
+ <label class="gantt-elastic__header-label" :style="{ ...style['header-label'] }">
26
+ {{ opts.locale["Y-Scale"] }}
27
+ <div class="gantt-elastic__header-slider-wrapper" :style="{ ...style['header-slider-wrapper'] }">
28
+ <vue-slider class="gantt-elastic__header-slider" tooltip="none" :style="{ ...style['header-slider'] }"
29
+ :process-style="{ ...style['header-slider--process'] }"
30
+ :slider-style="{ ...style['header-slider--slider'] }" v-model="height" :max="100" :min="7"
31
+ width="100px"></vue-slider>
32
+ </div>
33
+ </label>
34
+ <label class="gantt-elastic__header-label" :style="{ ...style['header-label'] }">
35
+ {{ opts.locale["Before/After"] }}
36
+ <div class="gantt-elastic__header-slider-wrapper" :style="{ ...style['header-slider-wrapper'] }">
37
+ <vue-slider class="gantt-elastic__header-slider" tooltip="none" :style="{ ...style['header-slider'] }"
38
+ :process-style="{ ...style['header-slider--process'] }"
39
+ :slider-style="{ ...style['header-slider--slider'] }" v-model="scope" :max="31" :min="0"
40
+ width="100px"></vue-slider>
41
+ </div>
42
+ </label>
43
+ <label class="gantt-elastic__header-label" :style="{ ...style['header-label'] }">
44
+ {{ opts.locale["Task list width"] }}
45
+ <div class="gantt-elastic__header-slider-wrapper" :style="{ ...style['header-slider-wrapper'] }">
46
+ <vue-slider class="gantt-elastic__header-slider" tooltip="none" :style="{ ...style['header-slider'] }"
47
+ :process-style="{ ...style['header-slider--process'] }"
48
+ :slider-style="{ ...style['header-slider--slider'] }" v-model="divider" :max="100" :min="0"
49
+ width="100px"></vue-slider>
50
+ </div>
51
+ </label>
52
+ <label class="gantt-elastic__header-task-list-switch--wrapper"
53
+ :style="{ ...style['header-task-list-switch--label'] }">
54
+ <switches class="gantt-elastic__header-task-list-switch" :style="{ ...style['header-task-list-switch'] }"
55
+ v-model="root.state.options.taskList.display"></switches>
56
+ {{ opts.locale["Display task list"] }}
57
+ </label>
58
+ </div>
59
+ </div>
60
+ </template>
61
+
62
+ <script>
63
+ import vueSlider from "vue-slider-component";
64
+ import "vue-slider-component/theme/default.css";
65
+ import Switches from "vue-switches";
66
+
67
+ const defaultStyle = {
68
+ header: {
69
+ margin: "0px auto",
70
+ background: "#f3f5f747",
71
+ padding: "10px",
72
+ overflow: "hidden",
73
+ clear: "both",
74
+ display: "flex",
75
+ "justify-content": "space-between"
76
+ },
77
+ "header-title": { float: "left" },
78
+ "header-options": { float: "right" },
79
+ "header-title--text": {
80
+ "font-size": "20px",
81
+ "vertical-align": "middle",
82
+ "font-weight": "400",
83
+ "line-height": "35px",
84
+ "padding-left": "22px",
85
+ "letter-spacing": "1px"
86
+ },
87
+ "header-title--html": {
88
+ "font-size": "20px",
89
+ "vertical-align": "middle",
90
+ "font-weight": "400",
91
+ "line-height": "35px",
92
+ "padding-left": "22px",
93
+ "letter-spacing": "1px"
94
+ },
95
+ "header-btn-recenter": {
96
+ background: "#95A5A6",
97
+ border: "none",
98
+ outline: "none",
99
+ cursor: "pointer",
100
+ color: "white",
101
+ "border-radius": "3px",
102
+ "margin-right": "27px",
103
+ "font-size": "16px",
104
+ padding: "8px 12px"
105
+ },
106
+ "header-slider": {
107
+ "box-sizing": "content-box"
108
+ },
109
+ "header-slider-wrapper": {
110
+ display: "inline-block",
111
+ "vertical-align": "middle"
112
+ },
113
+ "header-slider--slider": { "box-sizing": "content-box" },
114
+ "header-slider--process": { "box-sizing": "content-box" },
115
+ "header-task-list-switch--label": { "box-sizing": "content-box" },
116
+ "header-task-list-switch": {
117
+ margin: "0px 15px",
118
+ "vertical-align": "middle"
119
+ },
120
+ "header-label": {}
121
+ };
122
+ const defaultOptions = {
123
+ title: {
124
+ label: "tg-gantt-chart",
125
+ html: false
126
+ },
127
+ locale: {
128
+ Now: "Now",
129
+ "X-Scale": "Zoom-X",
130
+ "Y-Scale": "Zoom-Y",
131
+ "Task list width": "Task list",
132
+ "Before/After": "Expand",
133
+ "Display task list": "Show task list"
134
+ }
135
+ };
136
+ export default {
137
+ name: "GanttHeader",
138
+ components: {
139
+ vueSlider,
140
+ Switches
141
+ },
142
+ props: ["options", "dynamicStyle"],
143
+ inject: ["root"],
144
+ data() {
145
+ return {
146
+ scaleTimeoutId: null,
147
+ firstScale: false,
148
+ localScale: 0,
149
+ localHeight: 0,
150
+ localBefore: 0,
151
+ localPercent: 0,
152
+ sliderOptions: {
153
+ xScale: {
154
+ value: 0
155
+ }
156
+ },
157
+ style: {},
158
+ opts: {}
159
+ };
160
+ },
161
+ created() {
162
+ this.localScale = this.root.state.options.times.timeZoom;
163
+ this.localHeight = this.root.state.options.row.height;
164
+ this.localBefore = this.root.state.options.scope.before;
165
+ this.localPercent = this.root.state.options.taskList.percent;
166
+ this.sliderOptions.xScale.value = this.root.state.options.times.timeZoom;
167
+ this.style = this.root.mergeDeep({}, defaultStyle, this.dynamicStyle);
168
+ this.opts = this.root.mergeDeep({}, defaultOptions, this.options);
169
+ },
170
+ methods: {
171
+ getImage() {
172
+ this.root.getImage("image/png").then(imgB64 => {
173
+ const link = document.createElement("a");
174
+ link.href = imgB64;
175
+ link.download = "gantt-elastic.png";
176
+ document.body.appendChild(link);
177
+ link.click();
178
+ document.body.removeChild(link);
179
+ });
180
+ },
181
+ recenterPosition() {
182
+ this.root.$emit("recenterPosition");
183
+ },
184
+ setScale(value) {
185
+ if (this.scaleTimeoutId !== null) {
186
+ clearTimeout(this.scaleTimeoutId);
187
+ this.scaleTimeoutId = null;
188
+ }
189
+ // debouncing
190
+ if (this.firstScale) {
191
+ this.scaleTimeoutId = setTimeout(() => {
192
+ this.root.$emit("times-timeZoom-change", value);
193
+ this.scaleTimeoutId = null;
194
+ }, 50);
195
+ } else {
196
+ this.root.$emit("times-timeZoom-change", value);
197
+ this.firstScale = true;
198
+ }
199
+ }
200
+ },
201
+ computed: {
202
+ /**
203
+ * If there is a component slot specified for header
204
+ * @returns {bool}
205
+ */
206
+ beforeOptionsIsComponent() {
207
+ const headerSlot = this.options.slots.header;
208
+ if (
209
+ typeof headerSlot.beforeOptions === "object" &&
210
+ !Array.isArray(headerSlot.beforeOptions)
211
+ ) {
212
+ return true;
213
+ }
214
+ return false;
215
+ },
216
+ /**
217
+ * If there is a slot with beforeOptions html content
218
+ * @returns {bool}
219
+ */
220
+ beforeOptionsIsHtml() {
221
+ if (typeof this.options.slots.header.beforeOptions === "string") {
222
+ return true;
223
+ }
224
+ return false;
225
+ },
226
+ scale: {
227
+ get() {
228
+ return this.localScale;
229
+ },
230
+ set(value) {
231
+ this.localScale = Number(value);
232
+ this.setScale(this.localScale);
233
+ }
234
+ },
235
+ height: {
236
+ get() {
237
+ return this.localHeight;
238
+ },
239
+ set(value) {
240
+ this.localHeight = Number(value);
241
+ this.root.$emit("row-height-change", Number(value));
242
+ }
243
+ },
244
+ scope: {
245
+ get() {
246
+ return this.localBefore;
247
+ },
248
+ set(value) {
249
+ this.localBefore = Number(value);
250
+ this.root.$emit("scope-change", Number(value));
251
+ }
252
+ },
253
+ divider: {
254
+ get() {
255
+ return this.localPercent;
256
+ },
257
+ set(value) {
258
+ this.localPercent = Number(value);
259
+ this.root.$emit("taskList-width-change", Number(value));
260
+ }
261
+ }
262
+ }
263
+ };
264
+ </script>