|
|
@@ -0,0 +1,686 @@
|
|
|
+"use strict";
|
|
|
+var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
|
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
|
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
|
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
|
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
|
+};
|
|
|
+var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
+ return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
|
+};
|
|
|
+Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
+exports.HorizontalResizer = void 0;
|
|
|
+const dayjs_1 = __importDefault(require("dayjs"));
|
|
|
+const lit_element_1 = require("lit-element");
|
|
|
+const style_map_1 = require("lit-html/directives/style-map");
|
|
|
+const simplebar_1 = __importDefault(require("simplebar"));
|
|
|
+const SimplbeBar_styles_1 = require("./styles/SimplbeBar.styles");
|
|
|
+const Event_1 = require("./Event");
|
|
|
+const Ressource_1 = require("./Ressource");
|
|
|
+var horizontal_resizer_1 = require("./components/horizontal-resizer");
|
|
|
+Object.defineProperty(exports, "HorizontalResizer", { enumerable: true, get: function () { return horizontal_resizer_1.HorizontalResizer; } });
|
|
|
+const syncroScroll_1 = __importDefault(require("./utils/syncroScroll"));
|
|
|
+const Timeline_style_1 = require("./styles/Timeline.style");
|
|
|
+let Timeline = class Timeline extends lit_element_1.LitElement {
|
|
|
+ constructor(options = {}) {
|
|
|
+ super();
|
|
|
+ this._slotDuration = 30;
|
|
|
+ this._legendSpan = 2;
|
|
|
+ this.rowHeight = 32;
|
|
|
+ this.slotWidth = 20;
|
|
|
+ this.legendUnitFormat = { "y": "YYYY", "M": "MMMM", "d": 'D', "h": "H[h]", "m": "m'", 's': "s[s]" };
|
|
|
+ this._clearSelectionHandler = (_e) => {
|
|
|
+ this._clearSelectedItems();
|
|
|
+ window.removeEventListener("click", this._clearSelectionHandler);
|
|
|
+ };
|
|
|
+ this.rows = [];
|
|
|
+ this.items = [];
|
|
|
+ this._start = dayjs_1.default().startOf("day");
|
|
|
+ this._end = this._start.endOf("day");
|
|
|
+ this.rowsTitle = "Ressources";
|
|
|
+ this.ressourceWidth = 200;
|
|
|
+ this.selectedList = [];
|
|
|
+ this.legend = [];
|
|
|
+ this.defaultBackground = "";
|
|
|
+ if (options.ressources) {
|
|
|
+ this.addRessources(options.ressources);
|
|
|
+ }
|
|
|
+ if (options.items) {
|
|
|
+ this.addEvents(options.items);
|
|
|
+ }
|
|
|
+ this.updateLegend();
|
|
|
+ this.render();
|
|
|
+ }
|
|
|
+ get start() {
|
|
|
+ return this._start.toISOString();
|
|
|
+ }
|
|
|
+ set start(value) {
|
|
|
+ const d = dayjs_1.default(value);
|
|
|
+ if (d.isValid()) {
|
|
|
+ this._start = d < this._end ? d : this._end;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ console.log("Invalid Date :", value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ get end() {
|
|
|
+ return this._end.toISOString();
|
|
|
+ }
|
|
|
+ set end(value) {
|
|
|
+ const d = dayjs_1.default(value);
|
|
|
+ if (d.isValid()) {
|
|
|
+ this._end = this._start < d ? d : this._start;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ get slotDuration() {
|
|
|
+ return this._slotDuration;
|
|
|
+ }
|
|
|
+ set slotDuration(value) {
|
|
|
+ this._slotDuration = value;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ get legendSpan() {
|
|
|
+ return this._legendSpan;
|
|
|
+ }
|
|
|
+ set legendSpan(value) {
|
|
|
+ this._legendSpan = value;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ set defaultBackground(value) {
|
|
|
+ this.style.setProperty("--default-background", value);
|
|
|
+ }
|
|
|
+ get defaultBackground() {
|
|
|
+ return this.style.getPropertyValue("--default-background");
|
|
|
+ }
|
|
|
+ setLegendUnitFormatAll(legend) {
|
|
|
+ this.legendUnitFormat = Object.assign(Object.assign({}, this.legendUnitFormat), legend);
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ setLegendUnitFormat(unit, format) {
|
|
|
+ this.legendUnitFormat[unit] = format;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
+ addRessources(list) {
|
|
|
+ return list.map(r => this.addRessource(r));
|
|
|
+ }
|
|
|
+ addRessource(ressource) {
|
|
|
+ var _a;
|
|
|
+ const existingRessource = this.getRessourceFromId(ressource.id);
|
|
|
+ if (existingRessource) {
|
|
|
+ return existingRessource;
|
|
|
+ }
|
|
|
+ const r = Ressource_1.Ressource.toRessource(ressource);
|
|
|
+ if (r.parent !== undefined) {
|
|
|
+ r.parent = (_a = this.getRessourceFromId(r.parent.id)) !== null && _a !== void 0 ? _a : this.addRessource(r.parent);
|
|
|
+ const idx = this.rows.indexOf(r.parent);
|
|
|
+ if (idx > -1) {
|
|
|
+ this.rows[idx].children.push(r);
|
|
|
+ this.rows.splice(idx + 1, 0, r);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ throw new Error("Not able to create ressource parent.\n" + r.id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this.rows = [...this.rows, r];
|
|
|
+ }
|
|
|
+ this.addRessources(r.children);
|
|
|
+ this._updateEventPosition(r);
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+ removeRessourceById(id) {
|
|
|
+ return this._removeRessourceById(id);
|
|
|
+ }
|
|
|
+ _removeRessourceById(id, depth = 0) {
|
|
|
+ const output = { ressources: [], items: [] };
|
|
|
+ for (let i = 0; i < this.rows.length; i) {
|
|
|
+ const ressource = this.rows[i];
|
|
|
+ if (ressource.id === id) {
|
|
|
+ output.ressources.push(ressource);
|
|
|
+ if (ressource.parent && depth === 0) {
|
|
|
+ ressource.parent.children = ressource.parent.children.filter(o => o.id !== ressource.id);
|
|
|
+ }
|
|
|
+ this.rows.splice(i, 1);
|
|
|
+ }
|
|
|
+ else if (ressource.parentId === id) {
|
|
|
+ const partialOutput = this._removeRessourceById(ressource.id, depth + 1);
|
|
|
+ output.ressources.push(...partialOutput.ressources);
|
|
|
+ output.items.push(...partialOutput.items);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ output.items.push(...this.items.filter(i => i.ressourceId === id));
|
|
|
+ this.items = this.items.filter(i => i.ressourceId !== id);
|
|
|
+ return output;
|
|
|
+ }
|
|
|
+ getRessources() {
|
|
|
+ return this.rows;
|
|
|
+ }
|
|
|
+ getRessourceFromId(id) {
|
|
|
+ const tmp = this.rows.filter(r => r.id === id);
|
|
|
+ return tmp.length > 0 ? tmp[0] : null;
|
|
|
+ }
|
|
|
+ setRowsTitle(title) {
|
|
|
+ this.rowsTitle = title;
|
|
|
+ }
|
|
|
+ getEventById(id) {
|
|
|
+ return this.items.find(o => o.id == id);
|
|
|
+ }
|
|
|
+ addEvents(list) {
|
|
|
+ return list.map((e) => this.addEvent(e));
|
|
|
+ }
|
|
|
+ addEvent(event) {
|
|
|
+ const existingEvent = this.getEventById(event.id);
|
|
|
+ if (existingEvent) {
|
|
|
+ return existingEvent;
|
|
|
+ }
|
|
|
+ const ressource = this.rows.find(r => r.id === event.ressourceId);
|
|
|
+ if (ressource === undefined) {
|
|
|
+ return undefined;
|
|
|
+ }
|
|
|
+ const timeslot = Event_1.Event.toTimeSlot(event);
|
|
|
+ this.items = [...this.items, timeslot];
|
|
|
+ timeslot.isDisplayed = timeslot.end > this._start.toDate() || timeslot.start < this._end.toDate();
|
|
|
+ this._updateEventPosition(ressource);
|
|
|
+ return timeslot;
|
|
|
+ }
|
|
|
+ removeEventById(id) {
|
|
|
+ const output = this.items.filter(o => o.id === id);
|
|
|
+ this.items = this.items.filter(o => o.id !== id);
|
|
|
+ return output;
|
|
|
+ }
|
|
|
+ updateEventById(id) {
|
|
|
+ const output = this.removeEventById(id);
|
|
|
+ if (output.length > 0) {
|
|
|
+ this.addEvent(output[0]);
|
|
|
+ return output[0];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _updateEventPosition(ressource) {
|
|
|
+ const timeslots = this.items.filter(i => i.ressourceId === ressource.id);
|
|
|
+ if (timeslots.length === 0) {
|
|
|
+ ressource.height = this.rowHeight + (ressource.collapseChildren ? 5 : 0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const start = this._start.toDate().getTime();
|
|
|
+ const end = this._end.toDate().getTime();
|
|
|
+ const points = [start, end];
|
|
|
+ const populateInterval = (d) => {
|
|
|
+ const t = d.getTime();
|
|
|
+ if (start < t && t < end && !points.includes(t)) {
|
|
|
+ points.push(t);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ timeslots.forEach(element => {
|
|
|
+ populateInterval(element.start);
|
|
|
+ populateInterval(element.end);
|
|
|
+ });
|
|
|
+ points.sort();
|
|
|
+ const intervals = [];
|
|
|
+ for (let i = 0; i < points.length - 1; i++) {
|
|
|
+ const startTime = points[i];
|
|
|
+ const endTime = points[i + 1];
|
|
|
+ intervals.push({
|
|
|
+ start: points[i],
|
|
|
+ end: points[i + 1],
|
|
|
+ slots: timeslots.filter(slot => (slot.start.getTime() <= startTime && endTime <= slot.end.getTime()))
|
|
|
+ });
|
|
|
+ }
|
|
|
+ const lineCount = intervals.reduce((acc, interval) => Math.max(acc, interval.slots.length), 0);
|
|
|
+ ressource.height = this.rowHeight * Math.max(lineCount, 1) + (ressource.collapseChildren ? 5 : 0);
|
|
|
+ const sortTimeslots = (a, b) => {
|
|
|
+ const t = a.start.getTime() - b.start.getTime();
|
|
|
+ if (t === 0) {
|
|
|
+ const tend = b.end.getTime() - a.end.getTime();
|
|
|
+ return tend === 0 ? ('' + a.id).localeCompare(b.id) : tend;
|
|
|
+ }
|
|
|
+ return t;
|
|
|
+ };
|
|
|
+ timeslots.forEach(slot => slot.offset = -1);
|
|
|
+ timeslots.sort(sortTimeslots);
|
|
|
+ timeslots[0].offset = 0;
|
|
|
+ const potentialOffset = [];
|
|
|
+ for (let i = 0; i < lineCount; i++) {
|
|
|
+ potentialOffset.push(i);
|
|
|
+ }
|
|
|
+ intervals.forEach(intervals => {
|
|
|
+ intervals.slots.sort(sortTimeslots);
|
|
|
+ const usedOffset = intervals.slots.map(o => o.offset).filter(i => i > -1);
|
|
|
+ const availableOffset = potentialOffset.filter(i => !usedOffset.includes(i));
|
|
|
+ intervals.slots.forEach(slot => {
|
|
|
+ if (slot.offset === -1) {
|
|
|
+ slot.offset = availableOffset.shift() || 0;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ getEvents() {
|
|
|
+ return this.items;
|
|
|
+ }
|
|
|
+ updateLegend() {
|
|
|
+ const legend = [];
|
|
|
+ const legendUnitList = ["y", "M", "d", "h", "m", 's'];
|
|
|
+ const legendMinUnitSpan = this.slotDuration * this.legendSpan;
|
|
|
+ for (const legendUnit of legendUnitList) {
|
|
|
+ let currentDate = dayjs_1.default(this._start);
|
|
|
+ let nextColumn = currentDate.add(legendMinUnitSpan, "m");
|
|
|
+ const isLegendPossible = this._end.diff(this._start, legendUnit) > 0 &&
|
|
|
+ (nextColumn.format(this.legendUnitFormat[legendUnit]) !== currentDate.format(this.legendUnitFormat[legendUnit])
|
|
|
+ || currentDate.add(1, legendUnit).diff(currentDate, "m") >= legendMinUnitSpan);
|
|
|
+ if (isLegendPossible) {
|
|
|
+ const row = [];
|
|
|
+ let i = 0;
|
|
|
+ while (currentDate.isBefore(this._end)) {
|
|
|
+ i += this.legendSpan;
|
|
|
+ if (nextColumn.diff(currentDate, legendUnit) > 0) {
|
|
|
+ row.push({ colspan: i, title: '' + currentDate.format(this.legendUnitFormat[legendUnit]) });
|
|
|
+ i = 0;
|
|
|
+ currentDate = nextColumn;
|
|
|
+ }
|
|
|
+ nextColumn = nextColumn.add(legendMinUnitSpan, "m");
|
|
|
+ }
|
|
|
+ legend.push(row);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.legend = legend;
|
|
|
+ }
|
|
|
+ _handleResizeX(e) {
|
|
|
+ e.stopPropagation();
|
|
|
+ this.ressourceWidth += e.detail;
|
|
|
+ if (this.ressourceWidth < 0) {
|
|
|
+ this.ressourceWidth = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _grabHeader(e) {
|
|
|
+ const root = this.shadowRoot;
|
|
|
+ if (root !== null) {
|
|
|
+ const gridContainer = root.querySelector(".jc-timeline-grid-container");
|
|
|
+ const headerContainer = root.querySelector(".jc-timeline-grid-title-container");
|
|
|
+ let lastPosX = e.clientX;
|
|
|
+ const scroll = function (e) {
|
|
|
+ const scrollLeft = (lastPosX - e.clientX);
|
|
|
+ headerContainer.scrollLeft += scrollLeft;
|
|
|
+ gridContainer.scrollLeft += scrollLeft;
|
|
|
+ lastPosX = e.clientX;
|
|
|
+ };
|
|
|
+ const mouseUpListener = function (_e) {
|
|
|
+ window.removeEventListener("mousemove", scroll);
|
|
|
+ window.removeEventListener("mouseup", mouseUpListener);
|
|
|
+ };
|
|
|
+ window.addEventListener("mousemove", scroll);
|
|
|
+ window.addEventListener("mouseup", mouseUpListener);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _getEventResizerHandler(slot, direction) {
|
|
|
+ return (evt) => {
|
|
|
+ evt.stopPropagation();
|
|
|
+ evt.preventDefault();
|
|
|
+ const startPos = evt.clientX;
|
|
|
+ const localSlot = slot;
|
|
|
+ const localDir = direction;
|
|
|
+ const initialDate = slot[direction];
|
|
|
+ const resizeListener = (e) => {
|
|
|
+ const newDate = dayjs_1.default(initialDate).add(Math.round((e.clientX - startPos) / this.slotWidth) * this.slotDuration, "m").toDate();
|
|
|
+ if (direction === "start" ? (newDate < localSlot.end) : (localSlot.start < newDate)) {
|
|
|
+ if (localSlot[localDir] !== newDate) {
|
|
|
+ localSlot[localDir] = newDate;
|
|
|
+ this.updateEventById(slot.id);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const mouseUpListener = (_e) => {
|
|
|
+ window.removeEventListener("mousemove", resizeListener);
|
|
|
+ window.removeEventListener("mouseup", mouseUpListener);
|
|
|
+ localSlot.moving = false;
|
|
|
+ this.updateEventById(localSlot.id);
|
|
|
+ if (initialDate !== localSlot[localDir]) {
|
|
|
+ const cEvt = new CustomEvent("event-change", {
|
|
|
+ detail: { items: [localSlot] }
|
|
|
+ });
|
|
|
+ this.dispatchEvent(cEvt);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ localSlot.moving = true;
|
|
|
+ window.addEventListener("mousemove", resizeListener);
|
|
|
+ window.addEventListener("mouseup", mouseUpListener);
|
|
|
+ };
|
|
|
+ }
|
|
|
+ _getEventGrabHandler(slot, editable, ressourceEditable, callback) {
|
|
|
+ return (evt) => {
|
|
|
+ evt.stopPropagation();
|
|
|
+ evt.preventDefault();
|
|
|
+ const startPos = evt.clientX;
|
|
|
+ let hasChanged = false;
|
|
|
+ const localSlot = slot;
|
|
|
+ let localSlots = this.selectedList.filter(s => s instanceof Event_1.Event).map(s => s);
|
|
|
+ if (!localSlots.includes(localSlot)) {
|
|
|
+ localSlots = [localSlot];
|
|
|
+ }
|
|
|
+ const startDates = localSlots.map(slot => slot.start);
|
|
|
+ const endDates = localSlots.map(slot => slot.end);
|
|
|
+ const updatePosition = editable ? (e) => {
|
|
|
+ const changeTime = Math.round((e.clientX - startPos) / this.slotWidth) * this.slotDuration;
|
|
|
+ return localSlots.map((slot, index) => {
|
|
|
+ const prevStart = slot.start;
|
|
|
+ slot.start = dayjs_1.default(startDates[index]).add(changeTime, "m").toDate();
|
|
|
+ slot.end = dayjs_1.default(endDates[index]).add(changeTime, "m").toDate();
|
|
|
+ return prevStart.getTime() !== slot.start.getTime();
|
|
|
+ }).reduce((prev, curr) => prev || curr);
|
|
|
+ } : (_e) => { return false; };
|
|
|
+ const updateRessource = ressourceEditable ? (e) => {
|
|
|
+ var _a, _b, _c;
|
|
|
+ const rowId = (_c = (_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.elementsFromPoint(e.clientX, e.clientY).find((e) => e.tagName == "TD")) === null || _b === void 0 ? void 0 : _b.parentElement) === null || _c === void 0 ? void 0 : _c.getAttribute('row-id');
|
|
|
+ if (rowId) {
|
|
|
+ const ressourceId = this.rows[Number(rowId)].id;
|
|
|
+ if (ressourceId !== localSlot.ressourceId) {
|
|
|
+ const oldRessource = this.getRessourceFromId(localSlot.ressourceId);
|
|
|
+ localSlot.ressourceId = ressourceId;
|
|
|
+ this._updateEventPosition(oldRessource);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ } : (_e) => { return false; };
|
|
|
+ const moveListener = (e) => {
|
|
|
+ const a = updatePosition(e);
|
|
|
+ if (updateRessource(e) || a) {
|
|
|
+ hasChanged = true;
|
|
|
+ this.updateEventById(localSlot.id);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ const mouseUpListener = (e) => {
|
|
|
+ window.removeEventListener("mousemove", moveListener);
|
|
|
+ window.removeEventListener("mouseup", mouseUpListener);
|
|
|
+ localSlot.moving = false;
|
|
|
+ this.updateEventById(slot.id);
|
|
|
+ if (hasChanged) {
|
|
|
+ const cEvt = new CustomEvent("event-change", {
|
|
|
+ detail: { items: localSlots }
|
|
|
+ });
|
|
|
+ this.dispatchEvent(cEvt);
|
|
|
+ }
|
|
|
+ callback(e, hasChanged);
|
|
|
+ };
|
|
|
+ localSlot.moving = true;
|
|
|
+ window.addEventListener("mousemove", moveListener);
|
|
|
+ window.addEventListener("mouseup", mouseUpListener);
|
|
|
+ };
|
|
|
+ }
|
|
|
+ _clearSelectedItems() {
|
|
|
+ this.selectedList.map(selectable => {
|
|
|
+ selectable.selected = false;
|
|
|
+ this.updateEventById(selectable.id);
|
|
|
+ });
|
|
|
+ this.selectedList = [];
|
|
|
+ }
|
|
|
+ _getEventClickHandler(clickedItem) {
|
|
|
+ const item = clickedItem;
|
|
|
+ return (e, wasModified = false) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ const idx = this.selectedList.indexOf(item);
|
|
|
+ if (idx > -1) {
|
|
|
+ if (!wasModified) {
|
|
|
+ if (e.ctrlKey) {
|
|
|
+ this.selectedList.splice(idx, 1);
|
|
|
+ item.selected = false;
|
|
|
+ this.updateEventById(item.id);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this._clearSelectedItems();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (this.selectedList.length > 0 && !e.ctrlKey) {
|
|
|
+ this._clearSelectedItems();
|
|
|
+ }
|
|
|
+ item.selected = true;
|
|
|
+ this.selectedList.push(item);
|
|
|
+ this.updateEventById(item.id);
|
|
|
+ }
|
|
|
+ const myEvent = new CustomEvent('item-selected', {
|
|
|
+ detail: { items: this.selectedList },
|
|
|
+ bubbles: true,
|
|
|
+ composed: true
|
|
|
+ });
|
|
|
+ this.dispatchEvent(myEvent);
|
|
|
+ };
|
|
|
+ }
|
|
|
+ firstUpdated() {
|
|
|
+ const root = this.shadowRoot;
|
|
|
+ if (root !== null) {
|
|
|
+ const gridContainer = root.querySelector(".jc-timeline-grid-container");
|
|
|
+ const simplebar = new simplebar_1.default(gridContainer).getScrollElement();
|
|
|
+ syncroScroll_1.default([simplebar, root.querySelector(".jc-timeline-grid-title-container")], "h");
|
|
|
+ syncroScroll_1.default([simplebar, root.querySelector(".jc-timeline-rows")], "v");
|
|
|
+ }
|
|
|
+ if (this.defaultBackground === "") {
|
|
|
+ this.style.setProperty("--default-background", "SteelBlue");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ renderTimeslot(evt) {
|
|
|
+ if (!evt.isDisplayed) {
|
|
|
+ return lit_element_1.html ``;
|
|
|
+ }
|
|
|
+ let rowTop = 0;
|
|
|
+ let ressource;
|
|
|
+ let i;
|
|
|
+ for (i = 0; i < this.rows.length && this.rows[i].id !== evt.ressourceId; i++) {
|
|
|
+ ressource = this.rows[i];
|
|
|
+ if (ressource.show) {
|
|
|
+ rowTop += ressource.height ? ressource.height : this.rowHeight;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ressource = this.rows[i];
|
|
|
+ const minute2pixel = this.slotWidth / this.slotDuration;
|
|
|
+ const left = dayjs_1.default(evt.start).diff(this._start, "m") * minute2pixel;
|
|
|
+ const right = -dayjs_1.default(evt.end).diff(this._end, "m") * minute2pixel;
|
|
|
+ const style = {
|
|
|
+ height: this.rowHeight - 4 + "px",
|
|
|
+ top: rowTop + evt.offset * this.rowHeight + "px",
|
|
|
+ left: left + "px",
|
|
|
+ right: right + "px",
|
|
|
+ backgroundColor: ""
|
|
|
+ };
|
|
|
+ const bgColor = evt.bgColor ? evt.bgColor : ressource.eventBgColor;
|
|
|
+ if (bgColor) {
|
|
|
+ style.backgroundColor = bgColor;
|
|
|
+ }
|
|
|
+ if (!ressource.show) {
|
|
|
+ style.height = "";
|
|
|
+ style.top = rowTop - 6 + "px";
|
|
|
+ return lit_element_1.html `<div class="jc-timeslot empty" style="${style_map_1.styleMap(style)}"></div>`;
|
|
|
+ }
|
|
|
+ let content = lit_element_1.html `${evt.title}`;
|
|
|
+ const resizer = evt.editable === null ? ressource.eventEditable : evt.editable;
|
|
|
+ const editableRessource = evt.ressourceEditable === null ? ressource.eventRessourceEditable : evt.ressourceEditable;
|
|
|
+ if (resizer) {
|
|
|
+ content = lit_element_1.html `<div class="jc-timeslot-resizer-start" @mousedown="${this._getEventResizerHandler(evt, "start")}"></div>${content}
|
|
|
+ <div class="jc-timeslot-resizer-end" @mousedown="${this._getEventResizerHandler(evt, "end")}"></div>`;
|
|
|
+ }
|
|
|
+ return lit_element_1.html `<div class="jc-timeslot ${evt.moving ? "moving" : ""} ${evt.selected ? "selected" : ""}"
|
|
|
+ start="${evt.start.getHours()}"
|
|
|
+ end="${evt.end.getHours()}"
|
|
|
+ style="${style_map_1.styleMap(style)}"
|
|
|
+ @mousedown="${this._getEventGrabHandler(evt, resizer, editableRessource, this._getEventClickHandler(evt))}"
|
|
|
+ >${content}</div>`;
|
|
|
+ }
|
|
|
+ _getCollapseRessourceHandler(item) {
|
|
|
+ return (_e) => {
|
|
|
+ item.collapseChildren = !item.collapseChildren;
|
|
|
+ this._updateEventPosition(item);
|
|
|
+ this.rows = [...this.rows];
|
|
|
+ };
|
|
|
+ }
|
|
|
+ _onRessourceDragStart(item) {
|
|
|
+ return (event) => {
|
|
|
+ var _a;
|
|
|
+ (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.setData("text", item.id);
|
|
|
+ };
|
|
|
+ }
|
|
|
+ _onRessourceDragEnter(event) {
|
|
|
+ if (event.target instanceof HTMLElement) {
|
|
|
+ const tgt = event.target;
|
|
|
+ tgt.classList.add("target");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _onRessourceDragLeave(event) {
|
|
|
+ if (event.target instanceof HTMLElement) {
|
|
|
+ event.target.classList.remove("target");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ _onRessourceDrop(event) {
|
|
|
+ var _a, _b;
|
|
|
+ event.preventDefault();
|
|
|
+ if (event.target instanceof HTMLElement) {
|
|
|
+ event.target.classList.remove("target");
|
|
|
+ const srcId = (_a = event.dataTransfer) === null || _a === void 0 ? void 0 : _a.getData("text");
|
|
|
+ const destinationId = (_b = event.target.parentElement) === null || _b === void 0 ? void 0 : _b.getAttribute("ressourceId");
|
|
|
+ if (srcId && destinationId && (destinationId !== srcId)) {
|
|
|
+ const src = this.getRessourceFromId(srcId);
|
|
|
+ const destination = this.getRessourceFromId(destinationId);
|
|
|
+ if (destination.descendantOf(src)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const movedContent = this.removeRessourceById(src.id);
|
|
|
+ if (event.target.classList.contains("jc-ressource")) {
|
|
|
+ movedContent.ressources[0].parent = destination;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ movedContent.ressources[0].parent = destination.parent;
|
|
|
+ let idx = this.rows.findIndex(v => v.id === destinationId);
|
|
|
+ if (event.target.classList.contains("jc-ressource-below")) {
|
|
|
+ idx += 1;
|
|
|
+ while ((idx < this.rows.length)
|
|
|
+ && this.rows[idx].descendantOf(destination)) {
|
|
|
+ idx += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const arr = this.rows;
|
|
|
+ this.rows = [...arr.splice(0, idx), src, ...arr];
|
|
|
+ }
|
|
|
+ this.addRessources(movedContent.ressources);
|
|
|
+ this.addEvents(movedContent.items);
|
|
|
+ this.dispatchEvent(new CustomEvent("reorder-ressource", {
|
|
|
+ detail: { ressources: this.rows }
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ renderRessource(item) {
|
|
|
+ const depth = item.depth;
|
|
|
+ const style = `--depth:${depth};` + (item.height ? `height:${item.height}px;` : "");
|
|
|
+ const hasChild = item.children.length > 0;
|
|
|
+ const collapseHandler = this._getCollapseRessourceHandler(item);
|
|
|
+ return lit_element_1.html `<tr>
|
|
|
+ <td class="${item.selected ? "jc-ressource-selected" : ""}" style="${style}" ressourceId="${item.id}" @click="${this._getEventClickHandler(item)}">
|
|
|
+ <div class="jc-ressource-above"></div>
|
|
|
+ <div class="jc-ressource" draggable="true" @dragstart="${this._onRessourceDragStart(item)}">
|
|
|
+ ${Array(depth).fill(0).map(_i => lit_element_1.html `<i class="jc-spacer"></i>`)}${hasChild ? lit_element_1.html `<i class="jc-spacer ${item.collapseChildren ? "extend" : "collapse"}" @click="${collapseHandler}"></i>` : lit_element_1.html `<i class="jc-spacer"></i>`}
|
|
|
+ <span>${item.title}</span>
|
|
|
+ </div>
|
|
|
+ <div class="jc-ressource-below"></div>
|
|
|
+ </td>
|
|
|
+ </tr>`;
|
|
|
+ }
|
|
|
+ renderGridRow(columns, rowId = -1, height = 30) {
|
|
|
+ return lit_element_1.html `<tr row-id="${rowId}">${columns.map((d, i) => lit_element_1.html `<td style="height:${height}px;" class="jc-slot ${(i % this.legendSpan) === 0 ? "jc-major-slot" : ""}" start="${d.toISOString()}"> </td>`)}</tr>`;
|
|
|
+ }
|
|
|
+ render() {
|
|
|
+ const nCol = Math.floor(this._end.diff(this._start, 'm') / this.slotDuration) + 1;
|
|
|
+ const columns = [];
|
|
|
+ for (let i = 0; i < nCol; i++) {
|
|
|
+ columns.push(this._start.add(this.slotDuration * i, 'm'));
|
|
|
+ }
|
|
|
+ const displayedRows = this.rows.map((r, i) => { return { i: i, r: r }; }).filter(o => o.r.show);
|
|
|
+ return lit_element_1.html `
|
|
|
+ <div class="jc-timeline-header">
|
|
|
+ <div class="jc-timeline-rows-title" style=${style_map_1.styleMap({ minWidth: this.ressourceWidth + "px", width: this.ressourceWidth + "px" })}>${this.rowsTitle}</div>
|
|
|
+ <horizontal-resizer @resize-x="${this._handleResizeX}"></horizontal-resizer>
|
|
|
+ <div class="jc-timeline-grid-title-container">
|
|
|
+ <table @mousedown="${this._grabHeader}" style="width:${nCol * this.slotWidth}px;">
|
|
|
+ <colgroup>${columns.map(_o => lit_element_1.html `<col style="min-width:${this.slotWidth}px">`)}</colgroup>
|
|
|
+ <tbody>
|
|
|
+ ${this.legend.map(arr => lit_element_1.html `<tr class="jc-timeline-grid-title">${arr.map(o => lit_element_1.html `<th colspan="${o.colspan}">${o.title}</th>`)}</tr>`)}
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="jc-timeline-content">
|
|
|
+ <table class="jc-timeline-rows"
|
|
|
+ style="${style_map_1.styleMap({ "--width": this.ressourceWidth + "px" })}"
|
|
|
+ @dragover="${(e) => e.preventDefault()}"
|
|
|
+ @dragenter="${this._onRessourceDragEnter}"
|
|
|
+ @dragleave="${this._onRessourceDragLeave}"
|
|
|
+ @drop="${this._onRessourceDrop}">
|
|
|
+ ${this.rows.length > 0 ? displayedRows.map(o => this.renderRessource(o.r)) : lit_element_1.html `<tr class="empty"><td>No ressource</td></tr>`}
|
|
|
+ </table>
|
|
|
+ <horizontal-resizer @resize-x="${this._handleResizeX}"></horizontal-resizer>
|
|
|
+ <div class="jc-timeline-grid-container">
|
|
|
+ <table style="width:${nCol * this.slotWidth}px;">
|
|
|
+ <colgroup>${columns.map(_o => lit_element_1.html `<col style="min-width:${this.slotWidth}px">`)}</colgroup>
|
|
|
+
|
|
|
+ <tbody>
|
|
|
+ ${this.rows.length > 0 ? displayedRows.map(o => this.renderGridRow(columns, o.i, o.r.height)) : this.renderGridRow(columns)}
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ <div class="jc-timeslots" style="width:${nCol * this.slotWidth}px;">
|
|
|
+ ${this.items.map(slot => this.renderTimeslot(slot))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ `;
|
|
|
+ }
|
|
|
+};
|
|
|
+Timeline.styles = [Timeline_style_1.TimelineStyle, SimplbeBar_styles_1.SimpleBarStyle];
|
|
|
+__decorate([
|
|
|
+ lit_element_1.internalProperty()
|
|
|
+], Timeline.prototype, "rows", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.internalProperty()
|
|
|
+], Timeline.prototype, "items", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Number })
|
|
|
+], Timeline.prototype, "ressourceWidth", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Object })
|
|
|
+], Timeline.prototype, "_start", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: String })
|
|
|
+], Timeline.prototype, "start", null);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: String })
|
|
|
+], Timeline.prototype, "end", null);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Number })
|
|
|
+], Timeline.prototype, "slotDuration", null);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Number })
|
|
|
+], Timeline.prototype, "legendSpan", null);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Number })
|
|
|
+], Timeline.prototype, "rowHeight", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Number })
|
|
|
+], Timeline.prototype, "slotWidth", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: String })
|
|
|
+], Timeline.prototype, "rowsTitle", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: Array })
|
|
|
+], Timeline.prototype, "legend", void 0);
|
|
|
+__decorate([
|
|
|
+ lit_element_1.property({ type: String })
|
|
|
+], Timeline.prototype, "defaultBackground", null);
|
|
|
+Timeline = __decorate([
|
|
|
+ lit_element_1.customElement('jc-timeline')
|
|
|
+], Timeline);
|
|
|
+exports.default = Timeline;
|
|
|
+//# sourceMappingURL=Timeline.js.map
|