|
|
@@ -1,19 +1,19 @@
|
|
|
import dayjs, { Dayjs } from 'dayjs'
|
|
|
-import { Event, IEvent } from './Event'
|
|
|
+import { LitElement, html, customElement, property, TemplateResult, internalProperty } from 'lit-element';
|
|
|
+import { styleMap } from 'lit-html/directives/style-map';
|
|
|
+import SimpleBar from 'simplebar';
|
|
|
+import {SimpleBarStyle} from './styles/SimplbeBar.styles';
|
|
|
|
|
|
+import { Event, IEvent } from './Event'
|
|
|
import { Ressource, IRessource } from './Ressource'
|
|
|
|
|
|
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
-import { HorizontalResizer } from './components/horizontal-resizer'
|
|
|
-import { LitElement, html, customElement, property, TemplateResult } from 'lit-element';
|
|
|
-import { styleMap } from 'lit-html/directives/style-map';
|
|
|
-
|
|
|
+export { HorizontalResizer } from './components/horizontal-resizer'
|
|
|
import syncronizeElementsScrolling from './utils/syncroScroll';
|
|
|
import Selectable from './utils/selectable';
|
|
|
-
|
|
|
import { TimelineStyle } from './styles/Timeline.style';
|
|
|
|
|
|
-interface TimelineOptions {
|
|
|
+
|
|
|
+export interface TimelineOptions {
|
|
|
ressources?: Array<IRessource>
|
|
|
items?: Array<IEvent>
|
|
|
}
|
|
|
@@ -28,7 +28,7 @@ interface TimeInterval {
|
|
|
slots: Array<Event>
|
|
|
}
|
|
|
type dayjsUnit = "y"|"M"|"d"|"h"|"m"|'s'
|
|
|
-type UnitLegend = {
|
|
|
+export type UnitLegend = {
|
|
|
[k in dayjsUnit]: string;
|
|
|
};
|
|
|
interface legendItem {
|
|
|
@@ -38,28 +38,36 @@ interface legendItem {
|
|
|
// TODO define std zoom level
|
|
|
// TODO add selectable Slot
|
|
|
// TODO enable to rearrange between different component.
|
|
|
-
|
|
|
+/**
|
|
|
+ * This is a component that can display event in a Timeline.
|
|
|
+ *
|
|
|
+ */
|
|
|
@customElement('jc-timeline')
|
|
|
class Timeline extends LitElement {
|
|
|
- static styles = [TimelineStyle]
|
|
|
- @property({ type: Array })
|
|
|
+ static styles = [TimelineStyle,SimpleBarStyle]
|
|
|
+ @internalProperty() // important for the refresh
|
|
|
private rows: Array<Ressource>
|
|
|
- @property({ type: Array })
|
|
|
+ @internalProperty() // important for the refresh
|
|
|
private items: Array<Event>
|
|
|
-
|
|
|
private selectedList: Array<Selectable>
|
|
|
+
|
|
|
@property({ type: Number })
|
|
|
- private ressourceWidth: number
|
|
|
- @property({ type: Object })
|
|
|
+ public ressourceWidth: number
|
|
|
|
|
|
+ @property({ type: Object })
|
|
|
private _start: Dayjs
|
|
|
@property({ type: String })
|
|
|
public get start(): string {
|
|
|
return this._start.toISOString();
|
|
|
}
|
|
|
public set start(value: string){
|
|
|
- this._start = dayjs(value);
|
|
|
- this.updateLegend();
|
|
|
+ const d = dayjs(value);
|
|
|
+ if( d.isValid()){
|
|
|
+ this._start = d < this._end ? d : this._end;
|
|
|
+ this.updateLegend();
|
|
|
+ }else{
|
|
|
+ console.log("Invalid Date :" ,value)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private _end: Dayjs
|
|
|
@@ -68,8 +76,11 @@ class Timeline extends LitElement {
|
|
|
return this._end.toISOString();
|
|
|
}
|
|
|
public set end(value: string){
|
|
|
- this._end = dayjs(value);
|
|
|
- this.updateLegend();
|
|
|
+ const d = dayjs(value);
|
|
|
+ if( d.isValid()){
|
|
|
+ this._end = this._start < d ? d:this._start;
|
|
|
+ this.updateLegend();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private _slotDuration = 30; // in minute
|
|
|
@@ -96,7 +107,7 @@ class Timeline extends LitElement {
|
|
|
private rowHeight = 32 // in px
|
|
|
|
|
|
@property({ type: Number })
|
|
|
- private slotWidth = 20 // in px
|
|
|
+ public slotWidth = 20 // in px
|
|
|
|
|
|
@property({ type: String })
|
|
|
private rowsTitle: string
|
|
|
@@ -108,9 +119,8 @@ class Timeline extends LitElement {
|
|
|
|
|
|
constructor(options: TimelineOptions = {}) {
|
|
|
super()
|
|
|
- this.rows = options.ressources ? options.ressources.map(Ressource.toRessource) : [];
|
|
|
- this.items = options.items ? options.items.map(Event.toTimeSlot) : []
|
|
|
-
|
|
|
+ this.rows = [];
|
|
|
+ this.items = [];
|
|
|
this._start = dayjs().startOf("day");
|
|
|
this._end = this._start.endOf("day");
|
|
|
this.rowsTitle = "Ressources"
|
|
|
@@ -118,6 +128,12 @@ class Timeline extends LitElement {
|
|
|
this.selectedList = [];
|
|
|
this.legend = [];
|
|
|
this.defaultBackground = "";
|
|
|
+ if (options.ressources){
|
|
|
+ this.addRessources(options.ressources)
|
|
|
+ }
|
|
|
+ if (options.items){
|
|
|
+ this.addEvents(options.items)
|
|
|
+ }
|
|
|
this.updateLegend();
|
|
|
this.render();
|
|
|
}
|
|
|
@@ -128,7 +144,6 @@ class Timeline extends LitElement {
|
|
|
}
|
|
|
get defaultBackground(): string {
|
|
|
return this.style.getPropertyValue("--default-background");
|
|
|
-
|
|
|
}
|
|
|
setLegendUnitFormatAll(legend:Partial<UnitLegend>):void{
|
|
|
this.legendUnitFormat = {...this.legendUnitFormat, ...legend}
|
|
|
@@ -145,7 +160,7 @@ class Timeline extends LitElement {
|
|
|
/**
|
|
|
* Add the ressource the the timeline or return the existing ressource with the same id
|
|
|
* @param ressource
|
|
|
- * @returns
|
|
|
+ * @returns The Ressource object registered to in the timeline
|
|
|
*/
|
|
|
addRessource(ressource: IRessource): Ressource {
|
|
|
const existingRessource = this.getRessourceFromId(ressource.id)
|
|
|
@@ -522,25 +537,27 @@ class Timeline extends LitElement {
|
|
|
const root = this.shadowRoot;
|
|
|
if (root !== null) {
|
|
|
const gridContainer = root.querySelector(".jc-timeline-grid-container") as HTMLBaseElement;
|
|
|
- syncronizeElementsScrolling([gridContainer,
|
|
|
+ const simplebar = new SimpleBar(gridContainer).getScrollElement() as HTMLBaseElement;
|
|
|
+ syncronizeElementsScrolling([simplebar,
|
|
|
root.querySelector(".jc-timeline-grid-title-container") as HTMLBaseElement], "h")
|
|
|
- syncronizeElementsScrolling([gridContainer,
|
|
|
+ syncronizeElementsScrolling([simplebar,
|
|
|
root.querySelector(".jc-timeline-rows") as HTMLBaseElement], "v")
|
|
|
+
|
|
|
+
|
|
|
}
|
|
|
if (this.defaultBackground === "") {
|
|
|
this.style.setProperty("--default-background", "SteelBlue");
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
// RENDERING
|
|
|
- renderTimeslot(slot: Event): TemplateResult {
|
|
|
- if (!slot.isDisplayed) {
|
|
|
+ renderTimeslot(evt: Event): TemplateResult {
|
|
|
+ if (!evt.isDisplayed) {
|
|
|
return html``
|
|
|
}
|
|
|
let rowTop = 0
|
|
|
let ressource: Ressource;
|
|
|
let i: number;
|
|
|
- for (i = 0; i < this.rows.length && this.rows[i].id !== slot.ressourceId; 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;
|
|
|
@@ -548,16 +565,16 @@ class Timeline extends LitElement {
|
|
|
}
|
|
|
ressource = this.rows[i];
|
|
|
const minute2pixel = this.slotWidth / this.slotDuration;
|
|
|
- const left = dayjs(slot.start).diff(this._start, "m") * minute2pixel ;
|
|
|
- const right = - dayjs(slot.end).diff(this._end, "m") * minute2pixel ;
|
|
|
+ const left = dayjs(evt.start).diff(this._start, "m") * minute2pixel ;
|
|
|
+ const right = - dayjs(evt.end).diff(this._end, "m") * minute2pixel ;
|
|
|
const style = {
|
|
|
height: this.rowHeight - 4 + "px",
|
|
|
- top: rowTop + slot.offset * this.rowHeight + "px",
|
|
|
+ top: rowTop + evt.offset * this.rowHeight + "px",
|
|
|
left: left + "px",
|
|
|
right: right + "px",
|
|
|
backgroundColor:""
|
|
|
};
|
|
|
- const bgColor = slot.bgColor ? slot.bgColor : ressource.eventBgColor;
|
|
|
+ const bgColor = evt.bgColor ? evt.bgColor : ressource.eventBgColor;
|
|
|
if (bgColor) {
|
|
|
style.backgroundColor = bgColor;
|
|
|
}
|
|
|
@@ -569,18 +586,18 @@ class Timeline extends LitElement {
|
|
|
return html`<div class="jc-timeslot empty" style="${styleMap(style)}"></div>`
|
|
|
}
|
|
|
|
|
|
- let content: TemplateResult = html`${slot.title}`
|
|
|
- const resizer = slot.editable === null ? ressource.eventEditable : slot.editable;
|
|
|
- const editableRessource = slot.ressourceEditable === null ? ressource.eventRessourceEditable : slot.ressourceEditable;
|
|
|
+ let content: TemplateResult = html`${evt.title}`
|
|
|
+ const resizer = evt.editable === null ? ressource.eventEditable : evt.editable;
|
|
|
+ const editableRessource = evt.ressourceEditable === null ? ressource.eventRessourceEditable : evt.ressourceEditable;
|
|
|
if (resizer) {
|
|
|
- content = html`<div class="jc-timeslot-resizer-start" @mousedown="${this._getEventResizerHandler(slot, "start")}"></div>${content}
|
|
|
- <div class="jc-timeslot-resizer-end" @mousedown="${this._getEventResizerHandler(slot, "end")}"></div>`;
|
|
|
+ content = 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 html`<div class="jc-timeslot ${slot.moving ? "moving" : ""} ${slot.selected ? "selected" : ""}"
|
|
|
- start="${slot.start.getHours()}"
|
|
|
- end="${slot.end.getHours()}"
|
|
|
+ return html`<div class="jc-timeslot ${evt.moving ? "moving" : ""} ${evt.selected ? "selected" : ""}"
|
|
|
+ start="${evt.start.getHours()}"
|
|
|
+ end="${evt.end.getHours()}"
|
|
|
style="${styleMap(style)}"
|
|
|
- @mousedown="${this._getEventGrabHandler(slot, resizer, editableRessource, this._getEventClickHandler(slot))}"
|
|
|
+ @mousedown="${this._getEventGrabHandler(evt, resizer, editableRessource, this._getEventClickHandler(evt))}"
|
|
|
>${content}</div>`;
|
|
|
|
|
|
|