UNPKG

@webwriter/flowchart

Version:

Create programming flowcharts with interactive tasks. Use standardized Elements such as loops and Branchings.

305 lines (266 loc) 13.7 kB
import { ItemList } from '../../definitions/ItemList'; import { GraphNode } from '../../definitions/GraphNode'; import { Arrow } from '../../definitions/Arrow'; import { FlowchartWidget } from '../../..'; import { html } from 'lit'; import { drawButton } from '../drawer/drawButton'; import { createTooltip, removeTooltip } from './generalUI'; export function addTask( element: HTMLElement, taskList: ItemList[], selectedSequence: { id: string; order: number; type: string }[], getActiveSequenceButton: () => HTMLButtonElement | null, setActiveSequenceButton: (btn: HTMLButtonElement | null) => void, setSelectedSequence: (sequence: { id: string; order: number; type: string }[]) => void, getSelectedSequence: () => { id: string; order: number; type: string }[], getGraphNodes: () => GraphNode[], getArrows: () => Arrow[] ) { const taskContainer = element.shadowRoot.querySelector('.task-container'); const taskWrapper = document.createElement('div'); taskWrapper.style.position = 'relative'; taskWrapper.dataset.index = taskList.length.toString(); taskWrapper.className = 'task-wrapper'; const taskTitle = document.createElement('input'); taskTitle.type = 'text'; taskTitle.className = 'task-title'; taskTitle.placeholder = 'Überschrift'; taskTitle.addEventListener('change', (event) => { const index = Array.from(taskContainer.children).indexOf(taskWrapper); taskList[index].titel = (event.target as HTMLInputElement).value; }); const taskContent = document.createElement('textarea'); taskContent.className = 'task-content'; taskContent.placeholder = 'Inhalt... \nÄnderungen werden automatisch gespeichert.'; taskContent.addEventListener('input', (event) => { const target = event.target as HTMLTextAreaElement; target.style.height = 'auto'; target.style.height = `${target.scrollHeight}px`; }); taskContent.addEventListener('change', (event) => { const index = Array.from(taskContainer.children).indexOf(taskWrapper); taskList[index].content = (event.target as HTMLTextAreaElement).value; }); const taskButtonContainer = document.createElement('div'); taskButtonContainer.className = 'task-button-container editMode'; const selectButton = element.shadowRoot.getElementById('select-button'); selectButton.addEventListener('click', () => { if (!selectButton.classList.contains('active')) { let activeSequenceButton = getActiveSequenceButton(); if (activeSequenceButton) { activeSequenceButton.classList.remove('active'); setActiveSequenceButton(null); } } }); const selectSequenceEvent = new CustomEvent('startSelectSequence'); const cancelSequence = document.createElement('button'); cancelSequence.className = 'cancel-sequence-button editMode'; cancelSequence.style.display = 'none'; cancelSequence.textContent = 'Abbrechen'; cancelSequence.onclick = () => { // Breche das hinzufügen von einer Sequenz ab. setActiveSequenceButton(null); setSelectedSequence([]); element.dispatchEvent(selectSequenceEvent); cancelSequence.style.display = 'none'; saveSequence.style.display = 'none'; addSequence.classList.remove('active'); }; const saveSequence = document.createElement('button'); saveSequence.className = 'save-sequence-button editMode'; saveSequence.style.display = 'none'; saveSequence.textContent = 'Pfad speichern'; saveSequence.onclick = () => { // Speicher die ausgewählte Sequence im TaskList und beende den Auswahlmodus const taskIndex = Array.from(taskContainer.children).indexOf(taskWrapper); // console.log('Index', taskIndex); taskList[taskIndex].sequence = getSelectedSequence(); setActiveSequenceButton(null); setSelectedSequence([]); element.dispatchEvent(selectSequenceEvent); // Verstecke die Buttons "Abbrechen" und "Speichern" und deaktiviere active cancelSequence.style.display = 'none'; saveSequence.style.display = 'none'; addSequence.classList.remove('active'); }; const addSequence = document.createElement('button'); addSequence.className = 'add-sequence-button editMode'; addSequence.textContent = 'Pfad hinzufügen'; addSequence.onclick = () => { // Überprüfen, ob ein anderer Button bereits aktiv ist. let activeSequenceButton = getActiveSequenceButton(); if (activeSequenceButton && activeSequenceButton !== addSequence) { activeSequenceButton.classList.remove('active'); // Deaktiviere den vorherigen Button } //Überprüfe ob schon eine Lösung vorhanden ist, falls ja zeige diese an: const taskIndex = Array.from(taskContainer.children).indexOf(taskWrapper); if (taskList[taskIndex].sequence) { // Überprüfe ob alle Elemente der Sequenz noch in graphNodes oder arrows sind const allElementsExist = taskList[taskIndex].sequence.every((sequenceElement) => { if (sequenceElement.type === 'node') { return getGraphNodes().some((node) => node.id === sequenceElement.id); } else if (sequenceElement.type === 'arrow') { return getArrows().some((arrow) => arrow.id === sequenceElement.id); } else { return false; } }); // Wenn nicht alle Elemente existieren, lösche die Sequenz if (!allElementsExist) { taskList[taskIndex].sequence = []; } else { setSelectedSequence(taskList[taskIndex].sequence); selectedSequence = taskList[taskIndex].sequence; } } // Beginne mit der Auswahl der Sequenz, wenn der Button angeklickt wird. element.dispatchEvent(selectSequenceEvent); // Aktiviere den aktuellen Button und setze ihn als den aktiven Button if (selectButton.classList.contains('active')) { addSequence.classList.add('active'); setActiveSequenceButton(addSequence); // Zeige die Buttons "Abbrechen" und "Sequenz Speichern" an cancelSequence.style.display = 'block'; saveSequence.style.display = 'block'; } else { addSequence.classList.remove('active'); setActiveSequenceButton(null); // Setze den aktiven Button zurück, wenn keiner aktiv ist cancelSequence.style.display = 'none'; saveSequence.style.display = 'none'; } }; const deleteTask = document.createElement('button'); deleteTask.className = 'delete-task-button editMode'; deleteTask.textContent = 'Löschen'; deleteTask.onclick = () => { const index = Array.from(taskContainer.children).indexOf(taskWrapper); taskList.splice(index, 1); taskContainer.removeChild(taskWrapper); }; taskButtonContainer.appendChild(saveSequence); taskButtonContainer.appendChild(cancelSequence); taskButtonContainer.appendChild(addSequence); taskButtonContainer.appendChild(deleteTask); taskWrapper.appendChild(taskTitle); taskWrapper.appendChild(taskContent); taskWrapper.appendChild(taskButtonContainer); taskContainer.appendChild(taskWrapper); taskList.push({ titel: '', content: '' }); } // render alle Tasks in der UI export function renderTasks(this: FlowchartWidget, taskList: ItemList[]) { const renderTask = (task: ItemList, index: number) => { const selectSequenceEvent = new CustomEvent('startSelectSequence'); const deleteTask = (event: MouseEvent) => { taskList.splice(index, 1); this.taskList = [...taskList]; }; const onTitleChange = (event: Event) => { task.titel = (event.target as HTMLInputElement).value; this.taskList = [...taskList]; }; const onContentChange = (event: Event) => { task.content = (event.target as HTMLTextAreaElement).value; this.taskList = [...taskList]; }; const addSequence = (event: MouseEvent) => { let activeSequenceButton = this.getActiveSequenceButton(); if (activeSequenceButton && activeSequenceButton !== event.target) { activeSequenceButton.classList.remove('active'); } if (taskList[index].sequence) { const allElementsExist = taskList[index].sequence.every((sequenceElement) => { if (sequenceElement.type === 'node') { return this.getGraphNodes().some((node) => node.id === sequenceElement.id); } else if (sequenceElement.type === 'arrow') { return this.getArrows().some((arrow) => arrow.id === sequenceElement.id); } else { return false; } }); if (!allElementsExist) { taskList[index].sequence = []; } else { this.setSelectedSequence(taskList[index].sequence); } } this.dispatchEvent(selectSequenceEvent); // Aktiviere den aktuellen Button und setze ihn als den aktiven Button const selectButton = this.shadowRoot.getElementById('select-button'); if (selectButton.classList.contains('active')) { this.setActiveSequenceButton(event.target as HTMLButtonElement); setSelectionState(true); } else { this.setActiveSequenceButton(null); // Setze den aktiven Button zurück, wenn keiner aktiv ist setSelectionState(false); } }; const saveSequence = (event: MouseEvent) => { taskList[index].sequence = this.getSelectedSequence(); this.taskList = [...taskList]; this.setActiveSequenceButton(null); this.setSelectedSequence([]); this.dispatchEvent(selectSequenceEvent); setSelectionState(false); }; const cancelSequence = (event: MouseEvent) => { this.setActiveSequenceButton(null); this.setSelectedSequence([]); this.dispatchEvent(selectSequenceEvent); setSelectionState(false); }; const setSelectionState = (state: boolean) => { const taskContainer = this.shadowRoot.querySelector('.task-container'); const taskWrapper = taskContainer.children[index] as HTMLElement; const cancelButton = taskWrapper.querySelector('.cancel-sequence-button') as HTMLButtonElement; const saveButton = cancelButton.nextElementSibling as HTMLButtonElement; const addSequenceButton = saveButton.nextElementSibling as HTMLButtonElement; cancelButton.style.display = state ? 'block' : 'none'; saveButton.style.display = state ? 'block' : 'none'; addSequenceButton.classList.toggle('active', state); }; return html` <div class="task-wrapper" style="position:relative"> <input type="text" class="task-title" placeholder="Überschrift" value="${task.titel}" @change=${onTitleChange} /> <textarea class="task-content" placeholder="Inhalt... Änderungen werden automatisch gespeichert." @change=${onContentChange} > ${task.content}</textarea > <div class="task-button-container editMode"> <button class="add-sequence-button editMode" @click=${addSequence}>Pfad hinzufügen</button> <button class="cancel-sequence-button editMode" style="display:none" @click=${cancelSequence}> Abbrechen </button> <button class="save-sequence-button editMode" style="display:none" @click=${saveSequence}> Pfad speichern </button> <button class="delete-task-button editMode" @click=${deleteTask}>Löschen</button> </div> <div class="task-button-container" style=${this.isEditable() || !task.sequence ? 'display:none' : ''}> <button id="select-button" @click="${this.selectSequence}" class="select-sequence-button"> ${drawButton('select', 'tool')} Pfad auswählen </button> <button class="check-solution-button" style=${task.sequence ? 'block' : 'none'} @click=${() => this.checkSolution(task)} ?disabled=${!this.isSelectingSequence} > Lösung prüfen </button> </div> </div> `; }; return html` <div class="task-container">${taskList?.map((task, index) => renderTask(task, index))}</div> `; }