time_table 0.1.3 → 0.1.5
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.
- checksums.yaml +4 -4
- data/app/assets/javascript/time_table/controllers/time_table_controller.js +119 -151
- data/app/assets/javascript/time_table/controllers/time_table_controller_old.js +569 -0
- data/app/assets/javascript/time_table/controllers/time_table_controller_pre_time_keeper.js +350 -0
- data/app/assets/javascript/time_table/helpers/drag_event_state.js +53 -0
- data/app/assets/javascript/time_table/helpers/scroll_frame.js +141 -0
- data/app/assets/javascript/time_table/helpers/time_keeper.js +98 -0
- data/app/assets/javascript/time_table/helpers/time_table_renderer.js +75 -0
- data/app/assets/stylesheets/time_table/time_table.css +16 -2
- data/app/helpers/time_table/time_table_helper.rb +4 -3
- data/app/views/time_table/_time_table.html.erb +3 -2
- data/lib/time_table/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37cadc0ccd49e70a955ee8a7ce0da2cec2dd07addc3753dbd0dc86254ddc502b
|
4
|
+
data.tar.gz: 597c585023837f77e79863fa2f8e0ebf0836463dfc6f67b95f7121511ec089e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 017310be8ccce3c0b4d64ee97de60329515fb1e58a1d01d83f9d3eac3ac40b53b5bd40f6c301a5afe77815d9e0ffe249adfa23a84fafbbe2ec619acdfb7051ec
|
7
|
+
data.tar.gz: 210f906e47ce4d2ccb088c216e3b856bab10163439628e3ee74e7b1adc72c289ac59809f1ac4958f659b31c0334f21d0500737a24da62686d2b810264f863660
|
@@ -1,21 +1,29 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import ScrollFrame from "../helpers/scroll_frame.js"
|
3
|
+
import TimeKeeper from "../helpers/time_keeper.js"
|
4
|
+
import DragEventState from "../helpers/drag_event_state.js"
|
5
|
+
import TimeTableRenderer from "../helpers/time_table_renderer.js"
|
2
6
|
|
3
|
-
// Connects to data-controller="dropdown"
|
4
7
|
export default class extends Controller {
|
5
|
-
static targets = ["canvas", "eventsList", "event", "eventName", "eventTime"]
|
8
|
+
static targets = ["canvas", "eventsList", "event", "eventName", "eventTime", "timestamp"]
|
6
9
|
|
7
10
|
connect() {
|
8
|
-
this.
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
this.timeKeeper = new TimeKeeper({
|
12
|
+
start: new Date(this.element.dataset.timetableStartTime),
|
13
|
+
end: new Date(this.element.dataset.timetableEndTime),
|
14
|
+
scope: this.element.dataset.timetableScope,
|
15
|
+
scale: this.element.dataset.timetableScale,
|
16
|
+
clip: this.element.dataset.timetableClipSize
|
17
|
+
});
|
18
|
+
|
19
|
+
let topPadding = parseInt(window.getComputedStyle(this.element).paddingTop, 10);
|
20
|
+
|
21
|
+
this.scrollFrame = new ScrollFrame(this.element, { origin: topPadding });
|
22
|
+
|
23
|
+
this.dragEvent = null;
|
16
24
|
|
17
25
|
this.eventTargets.forEach((event) => {
|
18
|
-
|
26
|
+
TimeTableRenderer.setSpecialClasses(event);
|
19
27
|
});
|
20
28
|
}
|
21
29
|
|
@@ -23,180 +31,145 @@ export default class extends Controller {
|
|
23
31
|
event.preventDefault();
|
24
32
|
event.stopPropagation();
|
25
33
|
|
26
|
-
this.
|
34
|
+
if(event.params.dragEvent != "rescale" && this.element.dataset.timetableFrozenParam == "true") {
|
35
|
+
return;
|
36
|
+
}
|
37
|
+
|
38
|
+
this.dragEvent = new DragEventState({
|
39
|
+
type: event.params.dragEvent,
|
40
|
+
scrollFrame: this.scrollFrame,
|
41
|
+
timeKeeper: this.timeKeeper,
|
42
|
+
target: event.currentTarget.closest(".event")
|
43
|
+
});
|
27
44
|
|
28
|
-
this.dragEvent.
|
29
|
-
this.dragEvent.originY = this.dragEvent.originY = this.#relativeCursorPosition(event).y;
|
30
|
-
this.dragEvent.originYTime = this.#timeAtPosition(this.dragEvent.originY);
|
31
|
-
this.dragEvent.currentY = this.dragEvent.originY;
|
32
|
-
this.dragEvent.currentYTime = this.dragEvent.originYTime;
|
45
|
+
this.dragEvent.captureOriginSnapshot(event);
|
33
46
|
|
34
|
-
this.#setDragEventTarget(event.currentTarget.closest(".event"));
|
35
47
|
this.#setDragEventListeners();
|
36
48
|
}
|
37
49
|
|
38
|
-
|
39
|
-
|
40
|
-
event.stopPropagation();
|
50
|
+
drag(event) {
|
51
|
+
if(this.dragEvent) {
|
41
52
|
|
42
|
-
|
43
|
-
this.dragEvent = {};
|
53
|
+
this.dragEvent.captureSnapshot(event);
|
44
54
|
|
45
|
-
|
55
|
+
// Calls dragResize, dragMove, dragCreate, etc.
|
56
|
+
this.#invokeDragMethodFor(this.dragEvent.type);
|
57
|
+
|
58
|
+
if(this.dragEvent.type != undefined && this.dragEvent.type != "rescale") {
|
59
|
+
this.scrollFrame.checkForScroll(this.dragEvent.current.position, this.handleScroll.bind(this));
|
60
|
+
}
|
61
|
+
}
|
46
62
|
}
|
47
63
|
|
48
|
-
|
49
|
-
|
50
|
-
this.dragEvent.currentY = this.#relativeCursorPosition(event).y;
|
51
|
-
this.dragEvent.currentYTime = this.#timeAtPosition(this.dragEvent.currentY);
|
64
|
+
stopDragEvent(event) {
|
65
|
+
event.preventDefault();
|
52
66
|
|
53
|
-
|
67
|
+
this.dragEvent = null;
|
54
68
|
|
55
|
-
|
56
|
-
|
57
|
-
}
|
58
|
-
if(event.clientY < this.element.getBoundingClientRect().top && this.element.scrollTop > 0) {
|
59
|
-
this.element.scrollTop -= 10;
|
60
|
-
}
|
61
|
-
}
|
69
|
+
this.scrollFrame.stopScrolling();
|
70
|
+
this.#removeDragEventListeners();
|
62
71
|
}
|
63
72
|
|
73
|
+
|
64
74
|
//////////////////
|
65
75
|
// Drag Methods //
|
66
76
|
//////////////////
|
67
77
|
|
68
|
-
|
69
78
|
dragCreate(event) {
|
70
|
-
let timeBoundaries = this
|
71
|
-
let
|
72
|
-
|
73
|
-
this.
|
79
|
+
let timeBoundaries = this.timeKeeper.timeBoundaries(this.dragEvent.current.time.clipped, this.dragEvent.origin.time.clipped);
|
80
|
+
let eventElement = TimeTableRenderer.createEventElement(timeBoundaries);
|
81
|
+
|
82
|
+
this.eventsListTarget.appendChild(eventElement);
|
83
|
+
|
84
|
+
this.dragEvent.shift({ type: "resize", target: eventElement });
|
85
|
+
this.handleOverlap(eventElement);
|
74
86
|
}
|
75
87
|
|
76
88
|
dragResize(event) {
|
77
|
-
let timeBoundaries = this
|
78
|
-
|
79
|
-
|
89
|
+
let timeBoundaries = this.timeKeeper.timeBoundaries(this.dragEvent.current.time.clipped, this.dragEvent.origin.target.startTime.real);
|
90
|
+
TimeTableRenderer.updateEventElement(this.dragEvent.target, timeBoundaries);
|
91
|
+
this.handleOverlap(this.dragEvent.target);
|
92
|
+
}
|
80
93
|
|
81
94
|
dragMove(event) {
|
82
|
-
let timeChange = (this.dragEvent.
|
95
|
+
let timeChange = (this.dragEvent.current.time.clipped - this.dragEvent.origin.time.clipped);
|
96
|
+
let updatedStartTime = new Date(this.dragEvent.origin.target.startTime.real.getTime() + timeChange)
|
97
|
+
let updatedEndTime = new Date(this.dragEvent.origin.target.endTime.real.getTime() + timeChange);
|
83
98
|
|
84
|
-
let
|
85
|
-
let endTime = new Date(this.dragEvent.originEventEndTime.real.getTime() + timeChange);
|
99
|
+
let timeBoundaries = this.timeKeeper.timeBoundaries(updatedStartTime, updatedEndTime);
|
86
100
|
|
87
|
-
|
101
|
+
TimeTableRenderer.updateEventElement(this.dragEvent.target, timeBoundaries);
|
102
|
+
this.handleOverlap(this.dragEvent.target);
|
88
103
|
}
|
89
104
|
|
90
|
-
|
91
|
-
|
92
|
-
|
105
|
+
dragRescale(event) {
|
106
|
+
let delta = this.dragEvent.previous.position.scroll.y - this.dragEvent.current.position.scroll.y;
|
107
|
+
let newScale = Math.max(30, Math.min(300, this.timeKeeper.scale - (delta)));
|
108
|
+
this.element.style.setProperty("--hour-scale", newScale);
|
109
|
+
let scaleChange = newScale / this.timeKeeper.scale;
|
93
110
|
|
94
|
-
|
95
|
-
let canvasArea = this.element.getBoundingClientRect();
|
96
|
-
return {
|
97
|
-
x: event.clientX - canvasArea.left,
|
98
|
-
y: (event.clientY - canvasArea.top - 20) + this.element.scrollTop
|
99
|
-
};
|
100
|
-
}
|
101
|
-
|
102
|
-
#relativeElementPosition(element) {
|
103
|
-
let canvasArea = this.element.getBoundingClientRect();
|
104
|
-
let rect = element.getBoundingClientRect();
|
105
|
-
return {
|
106
|
-
x: rect.left - canvasArea.left,
|
107
|
-
y: (rect.top - canvasArea.top - 20) + this.element.scrollTop
|
108
|
-
};
|
109
|
-
}
|
110
|
-
|
111
|
-
#timeBoundaries(timeA, timeB) {
|
112
|
-
let startTime = new Date(Math.min(timeA, timeB));
|
113
|
-
let endTime = new Date(Math.max(timeA, timeB));
|
114
|
-
|
115
|
-
if (endTime - startTime < this.clipSize * 60 * 1000) {
|
116
|
-
endTime = new Date(startTime.getTime() + (this.clipSize * 60 * 1000));
|
117
|
-
}
|
118
|
-
|
119
|
-
return { start: startTime, end: endTime };
|
120
|
-
}
|
121
|
-
|
122
|
-
#timeAtPosition(y, roundUp = false) {
|
123
|
-
let realSecond = ((y / this.scrollHeight) * this.duration) + 1;
|
124
|
-
let clippedSecond = (Math.floor((realSecond / 60).toFixed(0) / this.clipSize) * this.clipSize) * 60;
|
111
|
+
this.timeKeeper.rescale(newScale);
|
125
112
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
}
|
131
|
-
}
|
132
|
-
|
133
|
-
#positionOfTime(time) {
|
134
|
-
let positionInDuration = (time - this.startTime) / (this.endTime - this.startTime + 1000);
|
135
|
-
// console.log(positionInDuration);
|
136
|
-
// console.log(positionInDuration * 1440)
|
137
|
-
return positionInDuration * 1440;
|
113
|
+
this.eventTargets.forEach((event) => {
|
114
|
+
event.style.setProperty("height", `${event.offsetHeight * (scaleChange)}px`);
|
115
|
+
event.style.setProperty("top", `${event.offsetTop * (scaleChange)}px`);
|
116
|
+
TimeTableRenderer.setSpecialClasses(event);
|
117
|
+
});
|
138
118
|
}
|
139
119
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
/////////////////////////////////////////
|
144
|
-
|
145
|
-
#createEventDiv(startTime, endTime){
|
146
|
-
const eventDiv = document.createElement("div");
|
120
|
+
handleOverlap(element) {
|
121
|
+
let eventTop = element.offsetTop;
|
122
|
+
let eventBottom = eventTop + element.offsetHeight;
|
147
123
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
124
|
+
this.eventTargets.forEach((event) => {
|
125
|
+
if(event != element) {
|
126
|
+
let top = event.offsetTop;
|
127
|
+
let bottom = top + event.offsetHeight;
|
128
|
+
|
129
|
+
if(eventTop >= top && eventBottom <= bottom) {
|
130
|
+
element.style.setProperty("left", "50px");
|
131
|
+
event.style.setProperty("right", "20px");
|
132
|
+
} else if(eventTop < bottom && eventBottom > top) {
|
133
|
+
element.style.setProperty("left", "50%");
|
134
|
+
event.style.setProperty("right", "50%");
|
135
|
+
} else {
|
136
|
+
element.style.setProperty("left", "50px");
|
137
|
+
event.style.setProperty("right", "20px");
|
138
|
+
}
|
139
|
+
}
|
140
|
+
});
|
164
141
|
}
|
165
142
|
|
166
|
-
#updateEventDiv(target, startTime, endTime) {
|
167
|
-
let startLocation = this.#positionOfTime(startTime);
|
168
|
-
let endLocation = this.#positionOfTime(endTime);
|
169
143
|
|
170
|
-
target.style.setProperty("top", `${startLocation}px`);
|
171
|
-
target.style.setProperty("height", `${endLocation - startLocation}px`);
|
172
144
|
|
173
|
-
|
145
|
+
////////////////////
|
146
|
+
// Scroll Methods //
|
147
|
+
////////////////////
|
174
148
|
|
175
|
-
|
176
|
-
|
149
|
+
handleScroll(status) {
|
150
|
+
if(status == "ABOVE") {
|
151
|
+
console.log("ABOVE");
|
152
|
+
this.insertTimestampAbove();
|
153
|
+
} else if (status == "BELOW") {
|
154
|
+
console.log("BELOW");
|
155
|
+
this.insertTimestampBelow();
|
156
|
+
}
|
177
157
|
|
178
|
-
|
179
|
-
eventDiv.classList.toggle("tiny", eventDiv.offsetHeight < 15);
|
180
|
-
eventDiv.classList.toggle("small", eventDiv.offsetHeight < 45);
|
158
|
+
this.drag(this.dragEvent.current.event);
|
181
159
|
}
|
182
160
|
|
183
|
-
/////////////////////
|
184
|
-
// Time Formatters //
|
185
|
-
/////////////////////
|
186
|
-
|
187
|
-
#timeString(date) {
|
188
|
-
let hours = date.getUTCHours();
|
189
|
-
let minutes = date.getUTCMinutes();
|
190
|
-
let ampm = hours >= 12 ? 'PM' : 'AM';
|
191
|
-
hours = hours % 12;
|
192
|
-
hours = hours ? hours : 12; // the hour '0' should be '12'
|
193
161
|
|
194
|
-
|
195
|
-
return hours + ':' + minutes + ' ' + ampm;
|
162
|
+
insertTimestampAbove() {
|
196
163
|
}
|
197
164
|
|
198
|
-
|
199
|
-
|
165
|
+
insertTimestampBelow() {
|
166
|
+
// this.dragEvent.inserting = true;
|
167
|
+
// Add 1 hour
|
168
|
+
let time = new Date(this.timeKeeper.endTime.getTime());
|
169
|
+
time.setHours(time.getHours() + 1);
|
170
|
+
this.timeKeeper.endTime = time;
|
171
|
+
let timestampDiv = TimeTableRenderer.timestamp(time);
|
172
|
+
this.element.querySelector(".timestamps").appendChild(timestampDiv);
|
200
173
|
}
|
201
174
|
|
202
175
|
////////////////
|
@@ -204,6 +177,10 @@ export default class extends Controller {
|
|
204
177
|
////////////////
|
205
178
|
|
206
179
|
#invokeDragMethodFor(type) {
|
180
|
+
if(!type) {
|
181
|
+
console.error("No drag type provided");
|
182
|
+
return;
|
183
|
+
}
|
207
184
|
let capitalizedType = type.charAt(0).toUpperCase() + type.slice(1);
|
208
185
|
|
209
186
|
if (this[`drag${capitalizedType}`]) {
|
@@ -217,15 +194,6 @@ export default class extends Controller {
|
|
217
194
|
// Helpers //
|
218
195
|
/////////////
|
219
196
|
|
220
|
-
#setDragEventTarget(target) {
|
221
|
-
if(target) {
|
222
|
-
this.dragEvent.target = target;
|
223
|
-
this.dragEvent.originEventPosition = this.#relativeElementPosition(target);
|
224
|
-
this.dragEvent.originEventStartTime = this.#timeAtPosition(this.dragEvent.originEventPosition.y);
|
225
|
-
this.dragEvent.originEventEndTime = this.#timeAtPosition(this.dragEvent.originEventPosition.y + target.offsetHeight);
|
226
|
-
}
|
227
|
-
}
|
228
|
-
|
229
197
|
#setDragEventListeners() {
|
230
198
|
document.addEventListener("mousemove", this.drag.bind(this));
|
231
199
|
document.addEventListener("mouseup", this.stopDragEvent.bind(this));
|