@webwriter/timeline
Version:
Create/learn with a digital timeline and test your knowledge.
356 lines (333 loc) • 11.5 kB
text/typescript
import { html, PropertyValues, css } from "lit";
import { LitElementWw } from "@webwriter/lit";
import { customElement, property, query } from "lit/decorators.js";
import "@shoelace-style/shoelace/dist/themes/light.css";
import {
SlTab,
SlTabGroup,
SlTabPanel,
SlSelect,
SlOption,
SlTabShowEvent,
SlSwitch,
SlIcon,
SlTooltip,
} from "@shoelace-style/shoelace";
import { TimelineDialog } from "../tl-dialog";
import { EventManager } from "../event-manager";
import { MainQuiz } from "../quiz/q-main-quiz";
import IconCirclePlusFilled from "@tabler/icons/outline/circle-plus.svg";
import { HelpOverlay, HelpPopup } from "@webwriter/wui/dist/helpSystem/helpSystem.js";
("webwriter-timeline")
export class WebWriterTimeline extends LitElementWw {
({ type: Number, attribute: true, reflect: true }) accessor tabIndex = -1;
({ type: Boolean, attribute: true, reflect: true }) accessor quizTabOpen;
({ type: Boolean, attribute: true, reflect: true }) accessor quizPanelVisible;
({ type: Boolean, attribute: true, reflect: true }) accessor timelinePanelVisible;
({ type: Number, attribute: true, reflect: true }) accessor quizFeedbackOption;
("#quiz-selection") accessor quizFeedbackSelecter: SlSelect;
("#quiz-component") accessor quiz: MainQuiz;
("#tl-component") accessor dialog: TimelineDialog;
("#quiz-panel") accessor quizPanel: SlTab;
("#quiz-toggle") accessor quizToggle: SlSwitch;
("#feedback-error") accessor feedbackError: HTMLDivElement;
("#add-event-tooltip") accessor addToolTip: SlTooltip;
("#tab-group") accessor tabGroup: SlTabGroup;
static get styles() {
return css`
:host(:not([contenteditable="true"]):not([contenteditable=""]))
.author-only {
display: none;
}
.ww-timeline {
border-top: 1px solid darkgray;
border-bottom: 1px solid darkgray;
min-height: 500px;
}
.border {
min-zoom: 100%;
overflow-wrap: break-word;
width: 100%;
padding-left: 20px;
padding-right: 20px;
box-sizing: border-box;
margin-bottom: 20px;
margin-top: 20px;
max-height: 700px;
}
.timeline-container {
display: flex;
justify-content: start;
flex-direction: row;
position: relative;
vertical-align: center;
padding-bottom: 25px;
}
.timeline {
min-height: 15px;
height: auto;
width: 100%;
border-left: 2px solid #484848;
position: relative;
padding-bottom: 50px;
}
.timeline::after {
content: "";
position: absolute;
bottom: -10px;
transform: translateX(-58%);
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 10px solid #484848;
}
.quiz-selection {
padding-left: 10px;
width: 100%;
}
.add-event-icon {
position: absolute;
left: -17px;
bottom: 0;
transform: translateY(-15px);
background-color: white;
color: #83b9e0;
font-size: 32px;
cursor: pointer;
}
.text-error {
padding-left: 10px;
font-size: var(--sl-input-help-text-font-size-medium);
color: var(--sl-color-danger-700);
}
#timeline-toggle {
padding:8px;
}
`;
}
static get scopedElements() {
return {
"event-manager": EventManager,
"timeline-dialog": TimelineDialog,
"main-quiz": MainQuiz,
"sl-tab": SlTab,
"sl-tab-group": SlTabGroup,
"sl-tab-panel": SlTabPanel,
"sl-select": SlSelect,
"sl-option": SlOption,
"sl-switch": SlSwitch,
"sl-icon": SlIcon,
"webwriter-helpoverlay": HelpOverlay,
"webwriter-helppopup": HelpPopup,
};
}
private eventManager = new EventManager();
protected async firstUpdated(_changedProperties: PropertyValues) {
// set timelinePanel as visble for initial use
if (this.isContentEditable && this.timelinePanelVisible === undefined) {
this.timelinePanelVisible = true;
}
// button position after shaddow dom created https://lit.dev/docs/components/events/#async-events
await new Promise((resolve) => setTimeout(resolve, 0));
this.renderRoot
.querySelector("webwriter-helpoverlay")
.shadowRoot.querySelector("#helpButton")
.setAttribute("style", "right: 2.5%");
this.addEventListener("request-remove", (e) =>
this.eventManager.removeEvent(e)
);
this.addEventListener(
"show-quiz-feedback-error",
() => (this.feedbackError.hidden = false)
);
// set quizfeedback to 1 if undefined
if (this.isContentEditable && this.quizFeedbackOption === undefined){
this.quizFeedbackOption = 1;
}
this.quiz.findSelection(this.quizFeedbackOption)
// set quiz tab visible at first if content is not editable and timeline deactivated
if (!this.timelinePanelVisible && !this.isContentEditable) {
this.quizPanel.click();
this.tabGroup.setAttribute('active', 'quiz-panel');
}
}
render() {
return html`
<webwriter-helpoverlay class="ww-wui">
<webwriter-helppopup slot="popupContainer" target="quiz-panel">
<div slot="content">
<h4>Quiz Panel</h4>
<p>
Click on this panel to configure a quiz for your timeline. If
disabled, toggle the <b>"Use Timeline Quiz" toggle</b> in the
options field.
</p>
</div>
</webwriter-helppopup>
<webwriter-helppopup slot="popupContainer" target="timeline-panel">
<div slot="content">
<h4>Timeline Panel</h4>
<p>On this panel you can create a timeline.</p>
</div>
</webwriter-helppopup>
<webwriter-helppopup slot="popupContainer" target="quiz-toggle">
<div slot="content">
<h4>Quiz Toggle</h4>
<p>Toggle me to use the timeline quiz-mode in the second tab.</p>
</div>
</webwriter-helppopup>
<webwriter-helppopup slot="popupContainer" target="timeline-toggle">
<div slot="content">
<h4>Timeline Toggle</h4>
<p>Toggle me to use the timeline in the first tab.</p>
</div>
</webwriter-helppopup>
<webwriter-helppopup slot="popupContainer" target="addButton">
<div slot="content">
<h4>Add Event Button</h4>
<p>Click me to add events to the timeline.</p>
</div>
</webwriter-helppopup>
</webwriter-helpoverlay>
<sl-tab-group
class="ww-timeline"
id="tab-group"
@sl-tab-show=${(e: SlTabShowEvent) =>
this.checkSelectedTab(e.detail.name)}
@sl-tab-hide=${(e: SlTabShowEvent) =>
this.checkSelectedTab(e.detail.name)}
>
<sl-tab
slot="nav"
panel="timeline-panel"
id="timeline-panel"
?disabled=${!this.timelinePanelVisible}
>
Timeline
</sl-tab>
<sl-tab
slot="nav"
panel="quiz-panel"
id="quiz-panel"
?disabled=${!this.quizPanelVisible}
>
Quiz
</sl-tab>
<sl-tab-panel name="timeline-panel">
<div class="border">
<div class="timeline-container">
<div class="timeline">
<slot name="event-slot"></slot>
<sl-icon
src=${IconCirclePlusFilled}
class="add-event-icon author-only"
id="addButton"
class="buttton-left author-only"
@click=${this.openingTLDialog}
>
</sl-icon>
</div>
</div>
<timeline-dialog
id="tl-component"
class="author-only"
@request-add=${(e) => this.eventManager.addEvent(e, this)}
>
</timeline-dialog>
</div>
</sl-tab-panel>
<sl-tab-panel name="quiz-panel">
<main-quiz id="quiz-component"> </main-quiz>
</sl-tab-panel>
</sl-tab-group>
<div part="options" class="author-only">
${this.quizTabOpen
? html`<div class="quiz-options" id="quiz-options">
<sl-select
id="quiz-selection"
class="quiz-selection"
label="Select Quiz Feedback"
help-text="Please select which feedback the students should get."
@sl-change=${() => this.saveQuizSelection()}
.value=${this.quizFeedbackOption?.toString() || ''}
>
<sl-option value="1">Score and Correct Answers</sl-option>
<sl-option value="2">Correct Answers Only</sl-option>
<sl-option value="3">Score Only</sl-option>
<sl-option value="4">None</sl-option>
</sl-select>
<div class="text-error" id="feedback-error" hidden>
Please select a feedback option.
</div>
<sl-switch
id="timeline-toggle"
class="quiz-selection"
@sl-change=${(e) =>
(this.timelinePanelVisible = e.target.checked)}
.checked=${this.timelinePanelVisible}
>
Show Timeline
</sl-switch>
<sl-switch
id="quiz-toggle"
class="quiz-selection"
@sl-change=${(e) => (this.quizPanelVisible = e.target.checked)}
.checked=${this.quizPanelVisible}
>
Show Quiz
</sl-switch>
</div>`
: html`
<sl-switch
id="timeline-toggle"
class="quiz-selection"
@sl-change=${(e) =>
(this.timelinePanelVisible = e.target.checked)}
.checked=${this.timelinePanelVisible}
>
Show Timeline
</sl-switch>
<sl-switch
id="quiz-toggle"
class="quiz-selection"
@sl-change=${(e) => (this.quizPanelVisible = e.target.checked)}
.checked=${this.quizPanelVisible}
>
Show Quiz
</sl-switch>
`}
</div>
`;
}
// if quiz panel is selected start quiz + manage options window
checkSelectedTab(selectedTab) {
if (selectedTab === "quiz-panel" && this.quizPanelVisible) {
this.startQuiz();
this.quizTabOpen = true;
} else {
this.quizTabOpen = false;
}
}
// open dialog
openingTLDialog() {
this.dialog.showDialog();
}
// show quiz and add events to it
startQuiz() {
this.quiz.startQuiz([...this.children]);
}
// transmit selected option for quiz feedback
saveQuizSelection() {
this.quizFeedbackOption = Number(this.quizFeedbackSelecter.value);
if (
this.quizFeedbackOption !== undefined ||
this.quizFeedbackOption !== 0
) {
this.feedbackError.hidden = true;
this.quiz.findSelection(this.quizFeedbackOption);
} else {
this.feedbackError.hidden = false;
}
}
}