gant-board 0.1.0

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/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # gant-board
2
+
3
+ ## Project setup
4
+ ```
5
+ npm install
6
+ ```
7
+
8
+ ### Compiles and hot-reloads for development
9
+ ```
10
+ npm run serve
11
+ ```
12
+
13
+ ### Compiles and minifies for production
14
+ ```
15
+ npm run build
16
+ ```
17
+
18
+ ### Lints and fixes files
19
+ ```
20
+ npm run lint
21
+ ```
22
+
23
+ ### Customize configuration
24
+ See [Configuration Reference](https://cli.vuejs.org/config/).
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ presets: [
3
+ '@vue/cli-plugin-babel/preset'
4
+ ]
5
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "gant-board",
3
+ "version": "0.1.0",
4
+ "scripts": {
5
+ "serve": "vue-cli-service serve",
6
+ "build": "vue-cli-service build",
7
+ "lint": "vue-cli-service lint",
8
+ "build-lib": "vue-cli-service build --target lib --name VueTimeline src/main.js --mode production",
9
+ "deploy": "npm publish"
10
+ },
11
+ "dependencies": {
12
+ "core-js": "^3.6.5",
13
+ "dayjs": "^1.10.7",
14
+ "vue": "^2.6.11"
15
+ },
16
+ "devDependencies": {
17
+ "@vue/cli-plugin-babel": "~4.5.0",
18
+ "@vue/cli-plugin-eslint": "~4.5.0",
19
+ "@vue/cli-service": "~4.5.0",
20
+ "babel-eslint": "^10.1.0",
21
+ "eslint": "^6.7.2",
22
+ "eslint-plugin-vue": "^6.2.2",
23
+ "vue-template-compiler": "^2.6.11"
24
+ },
25
+ "eslintConfig": {
26
+ "root": true,
27
+ "env": {
28
+ "node": true
29
+ },
30
+ "extends": [
31
+ "plugin:vue/essential",
32
+ "eslint:recommended"
33
+ ],
34
+ "parserOptions": {
35
+ "parser": "babel-eslint"
36
+ },
37
+ "rules": {}
38
+ },
39
+ "browserslist": [
40
+ "> 1%",
41
+ "last 2 versions",
42
+ "not dead"
43
+ ]
44
+ }
Binary file
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
7
+ <link rel="icon" href="<%= BASE_URL %>favicon.ico">
8
+ <title><%= htmlWebpackPlugin.options.title %></title>
9
+ </head>
10
+ <body>
11
+ <noscript>
12
+ <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
13
+ </noscript>
14
+ <div id="app"></div>
15
+ <!-- built files will be auto injected -->
16
+ </body>
17
+ </html>
package/src/App.vue ADDED
@@ -0,0 +1,64 @@
1
+ <template>
2
+ <div id="app">
3
+ <gant-board :elements="elements"></gant-board>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ import GantBoard from './components/gant-board.vue'
9
+ export default {
10
+ name: 'App',
11
+ components: {
12
+ GantBoard
13
+ },
14
+ data() {
15
+ return {
16
+ elements: [
17
+ {
18
+ id: 1,
19
+ name: "prova 1",
20
+ start: "2022-02-05",
21
+ end: "2022-02-18",
22
+ weigth: 1
23
+ },
24
+ {
25
+ id: 2,
26
+ name: "prova 2",
27
+ start: "2022-02-16",
28
+ end: "2022-02-30",
29
+ weigth: 2
30
+ }
31
+ ,
32
+ {
33
+ id: 3,
34
+ name: "prova 3",
35
+ start: "2022-02-13",
36
+ end: "2022-02-25",
37
+ weigth: 3
38
+ }
39
+ ,
40
+ {
41
+ id: 4,
42
+ name: "prova 4",
43
+ start: "2022-02-03",
44
+ end: "2022-02-25",
45
+ weigth: 4
46
+ }
47
+ ]
48
+ }
49
+ }
50
+ }
51
+
52
+
53
+ </script>
54
+
55
+ <style>
56
+ #app {
57
+ font-family: Avenir, Helvetica, Arial, sans-serif;
58
+ -webkit-font-smoothing: antialiased;
59
+ -moz-osx-font-smoothing: grayscale;
60
+ text-align: center;
61
+ color: #2c3e50;
62
+ margin-top: 60px;
63
+ }
64
+ </style>
Binary file
@@ -0,0 +1,363 @@
1
+ <template>
2
+ <div style="width: 100vw; overflow-x: scroll">
3
+ <div
4
+ class="events-container"
5
+ ref="container_wrapper"
6
+ @mousemove="moveEvent($event)"
7
+ @mouseup="endDrag($event)"
8
+ :style="{ width: options.day_width * days_number + 'px' }"
9
+ >
10
+ <div
11
+ style="width: 100%; display: flex; flex-direction: column; height: 100%"
12
+ >
13
+
14
+
15
+ <div
16
+ style="
17
+ display: flex;
18
+ width: 100%;
19
+ height: 20px;
20
+ background-color: #fff;
21
+ "
22
+ >
23
+ <div
24
+ v-for="m in months"
25
+ :key="m.index"
26
+ style="display: inline-block; box-sizing: border-box; font-size:14px"
27
+ :style="{
28
+ width: (options.day_width * m.length) + 'px',
29
+ }"
30
+ >
31
+ {{m.name}}
32
+ </div>
33
+ </div>
34
+
35
+ <div
36
+ style="
37
+ display: flex;
38
+ width: 100%;
39
+ height: 20px;
40
+ background-color: #fff;
41
+ "
42
+ >
43
+
44
+ <div
45
+ v-for="day in days_number"
46
+ :key="day"
47
+ style="display: inline-block; box-sizing: border-box; font-size:12px"
48
+ :style="{
49
+ 'border-left':
50
+ options.from_start.add(day, 'days').day() == 1
51
+ ? '1px solid #d1d1d1'
52
+ : 'none',
53
+ width: options.day_width + 'px',
54
+ left: day * options.day_width,
55
+ }"
56
+ >
57
+ {{getDay(day).format("ddd")[0]}} {{getDay(day).format("D")}}
58
+ </div>
59
+ </div>
60
+
61
+ <div style="display: flex; width: 100%; flex-grow: 1">
62
+ <div
63
+ v-for="day in days_number"
64
+ :key="day"
65
+ style="
66
+ display: inline-block;
67
+ border-left: 1px solid #d1d1d1;
68
+ box-sizing: border-box;
69
+ "
70
+ :style="{
71
+ width: options.day_width + 'px',
72
+ left: day * options.day_width,
73
+ 'background-color': options.gray_days.includes(
74
+ options.from_start.add(day, 'days').day()
75
+ )
76
+ ? '#e1e1e1'
77
+ : 'transparent',
78
+ }"
79
+ ></div>
80
+ </div>
81
+ </div>
82
+
83
+
84
+ <div
85
+ class="event"
86
+ v-for="event in render_elements" :key="event.id"
87
+ :class="{
88
+ ghost: drag_element != null && event.id == drag_element.id,
89
+ }"
90
+ @mousedown="startDrag($event, event)"
91
+ :style="{
92
+ width: event.width + 'px',
93
+ left: event.left + 'px',
94
+ top: event.top + 40 + 'px',
95
+ }"
96
+ >
97
+ {{ event.name }} {{ event.weigth }} {{ event.row }}
98
+ </div>
99
+
100
+
101
+ <div
102
+ class="event"
103
+ v-if="drag_element != null"
104
+ style="z-index: 99; transition: 0s all"
105
+ :style="{
106
+ width: drag_element.width + 'px',
107
+ top: drag_element.top + 'px',
108
+ left: drag_element.left + 'px',
109
+ }"
110
+ >
111
+ {{ drag_element.name }}
112
+ </div>
113
+ </div>
114
+ </div>
115
+ </template>
116
+
117
+
118
+ <script>
119
+ import dayjs from "dayjs";
120
+ import "dayjs/locale/it";
121
+
122
+ export default {
123
+ name: "gant-board",
124
+ props: ["elements"],
125
+ data() {
126
+ return {
127
+ end_calc: true,
128
+ render_elements: [],
129
+ drag_element: null,
130
+ options: {
131
+ gray_days: [0, 6],
132
+ day_width: 40,
133
+ from_start: dayjs().add(-90, "days"),
134
+ to_start: dayjs().add(90, "days"),
135
+ },
136
+ };
137
+ },
138
+ computed: {
139
+ days_number: function () {
140
+ return this.options.to_start.diff(this.options.from_start, "days");
141
+ },
142
+ months: function(){
143
+ var months = [];
144
+ var start = this.options.from_start;
145
+ var end = false;
146
+ var index = 0;
147
+ while (!end){
148
+ var length = start.daysInMonth();
149
+ if (months.length == 0){
150
+ length = start.startOf("month").add(1,"months").diff(start,"days");
151
+ }
152
+ months.push({ index: index, name : start.format("MMMM"), length : length});
153
+ start = start.add(1,"months");
154
+ index++;
155
+ if (start > this.options.to_start){
156
+ end = true;
157
+ }
158
+ }
159
+
160
+ return months;
161
+ }
162
+ },
163
+ methods: {
164
+ getDay(index){
165
+ return this.options.from_start.add(index, "days");
166
+ },
167
+ refresh_render() {
168
+ var ordered = [...this.render_elements].sort(
169
+ (a, b) => a.weigth - b.weigth
170
+ );
171
+
172
+ for (let event of ordered) {
173
+ this.refresh_event(event);
174
+ }
175
+ },
176
+ refresh_event(event) {
177
+ var near_element = [...this.render_elements]
178
+ .sort((a, b) => b.weigth - a.weigth)
179
+ .filter(
180
+ (x) =>
181
+ x.weigth <= event.weigth &&
182
+ x.start < event.end &&
183
+ x.id != event.id &&
184
+ event.start < x.end
185
+ ).map(x => x.row);
186
+ var row = 0;
187
+
188
+ if (near_element.length > 0) {
189
+ row = Math.max(...near_element) + 1;
190
+ }
191
+ event.row = row;
192
+
193
+ event.top = 40 * row;
194
+ event.left =
195
+ event.start.diff(this.options.from_start, "days") *
196
+ this.options.day_width;
197
+ event.width =
198
+ event.end.diff(event.start, "days") * this.options.day_width;
199
+ },
200
+ render() {
201
+ this.render_elements = [];
202
+ var ordered = this.elements.sort((a, b) => a.weigth - b.weigth);
203
+
204
+ for (var event of ordered) {
205
+ this.build_event(event);
206
+ }
207
+ },
208
+ build_event(event) {
209
+ var event_start = dayjs(event.start, "YYYY-MM-DD", true);
210
+ var event_end = dayjs(event.end, "YYYY-MM-DD", true);
211
+ var near_element = this.render_elements
212
+ .sort((a, b) => b.weigth - a.weigth)
213
+ .filter((x) => x.start < event_end && event_start < x.end).map(x => x.row);
214
+
215
+ var row = 0;
216
+
217
+ if (near_element.length > 0) {
218
+ row = Math.max(...near_element) + 1;
219
+ }
220
+
221
+ this.render_elements.push({
222
+ id: event.id,
223
+ name: event.name,
224
+ start: event_start,
225
+ end: event_end,
226
+ weigth: event.weigth,
227
+ row: row,
228
+ top: 40 * row,
229
+ left:
230
+ event_start.diff(this.options.from_start, "days") *
231
+ this.options.day_width,
232
+ width: event_end.diff(event_start, "days") * this.options.day_width,
233
+ });
234
+ },
235
+ startDrag(mouseevent, event) {
236
+ var event_start = dayjs(event.start, "YYYY-MM-DD", true);
237
+ var event_end = dayjs(event.end, "YYYY-MM-DD", true);
238
+ let bounds = this.$refs.container_wrapper.getBoundingClientRect();
239
+ this.drag_element = {
240
+ id: event.id,
241
+ name: event.name,
242
+ start: event_start,
243
+ end: event_end,
244
+ top: mouseevent.clientY - mouseevent.offsetY - bounds.top,
245
+ offset_position_x: mouseevent.offsetX,
246
+ offset_position_y: mouseevent.offsetY,
247
+ left:
248
+ event_start.diff(this.options.from_start, "days") *
249
+ this.options.day_width,
250
+ width: event_end.diff(event_start, "days") * this.options.day_width,
251
+ };
252
+ },
253
+ moveEvent(mouseevent) {
254
+ if (this.drag_element != null) {
255
+ let bounds = this.$refs.container_wrapper.getBoundingClientRect();
256
+ this.drag_element.left =
257
+ mouseevent.clientX -
258
+ bounds.left -
259
+ this.drag_element.offset_position_x;
260
+ this.drag_element.top =
261
+ mouseevent.clientY - this.drag_element.offset_position_y - (bounds.top);
262
+
263
+ var day = Math.round(
264
+ (mouseevent.clientX -
265
+ bounds.left -
266
+ this.drag_element.offset_position_x) /
267
+ this.options.day_width
268
+ );
269
+
270
+ var original_event = this.render_elements.filter(
271
+ (x) => x.id == this.drag_element.id
272
+ )[0];
273
+ var duration = original_event.end.diff(original_event.start, "days");
274
+ original_event.start = this.options.from_start.add(day, "days");
275
+ original_event.end = original_event.start.add(duration, "days");
276
+ var start = original_event.start;
277
+ var end = original_event.end;
278
+
279
+ var row_position = Math.floor((mouseevent.clientY - bounds.top) / 40);
280
+
281
+ var down_weigth = null;
282
+ var down_element = null;
283
+ var element_to_move = null;
284
+ var element_to_move_weigth = null;
285
+
286
+ down_element = this.render_elements
287
+ .sort((a, b) => b.weigth - a.weigth)
288
+ .filter(
289
+ (x) =>
290
+ x.start < end &&
291
+ start < x.end &&
292
+ original_event.id != x.id &&
293
+ x.row < row_position
294
+ );
295
+ if (down_element.length > 0) {
296
+ down_weigth = down_element[0].weigth;
297
+ }
298
+ element_to_move = this.render_elements
299
+ .sort((a, b) => a.weigth - b.weigth)
300
+ .filter(
301
+ (x) =>
302
+ x.start < end &&
303
+ start < x.end &&
304
+ original_event.id != x.id &&
305
+ x.row >= row_position
306
+ );
307
+ if (element_to_move.length > 0) {
308
+ element_to_move_weigth = element_to_move[0].weigth;
309
+ }
310
+
311
+ if (element_to_move_weigth == null) {
312
+ original_event.weigth = (down_weigth ?? 0) + 1;
313
+ } else {
314
+ original_event.weigth =
315
+ (down_weigth ?? 0) +
316
+ (element_to_move_weigth - (down_weigth ?? 0)) / 2;
317
+ }
318
+
319
+ this.refresh_render();
320
+ }
321
+ },
322
+ endDrag() {
323
+ this.drag_element = null;
324
+ },
325
+ },
326
+ created() {
327
+ dayjs.locale("it");
328
+ },
329
+ mounted() {
330
+ this.render();
331
+ },
332
+ };
333
+ </script>
334
+
335
+ <style scoped>
336
+ .events-container {
337
+ background: #ebebeb;
338
+ height: 200px;
339
+ position: relative;
340
+ }
341
+ .event.ghost {
342
+ background-color: #b5a0d8;
343
+ }
344
+
345
+ .event {
346
+ z-index: 1;
347
+ display: inline-block;
348
+ background-color: #673ab7;
349
+ color: #fff;
350
+ position: absolute;
351
+ border-radius: 3px;
352
+ padding: 10px 3px;
353
+ overflow: hidden;
354
+ box-sizing: border-box;
355
+ white-space: nowrap;
356
+ cursor: pointer;
357
+
358
+ -webkit-user-select: none; /* Safari */
359
+ -moz-user-select: none; /* Firefox */
360
+ -ms-user-select: none; /* IE10+/Edge */
361
+ user-select: none; /* Standard */
362
+ }
363
+ </style>
package/src/lib.js ADDED
@@ -0,0 +1,6 @@
1
+ import VueTimeLine from './components/gant-board.vue'
2
+
3
+ export function install(Vue) {
4
+ console.log('install');
5
+ Vue.component('vue-timeline', VueTimeLine);
6
+ }
package/src/main.js ADDED
@@ -0,0 +1,9 @@
1
+ import Vue from 'vue'
2
+ import App from './App.vue'
3
+
4
+ Vue.config.productionTip = false
5
+
6
+
7
+ new Vue({
8
+ render: h => h(App),
9
+ }).$mount('#app')
package/vue.config.js ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ css: {
3
+ extract: false,
4
+ },
5
+ }