Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="flex flex-row justify-start items-center gap-1">
<node-icon [nodeId]="nodeId" size="18" />&nbsp;
@if (showPosition) {
<span class="step-number">{{ getNodePosition(nodeId) }}: </span>
<span class="step-number">{{ nodePosition }}: </span>
}
<span class="step-title">{{ getNodeTitle(nodeId) }}</span>
<span class="step-title">{{ nodeTitle }}</span>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, Input } from '@angular/core';
import { TeacherProjectService } from '../../../services/teacherProjectService';
import { NodeIconComponent } from '../../../vle/node-icon/node-icon.component';
import { TeacherProjectTranslationService } from '../../../services/teacherProjectTranslationService';
import { Subscription } from 'rxjs';

@Component({
imports: [NodeIconComponent],
Expand All @@ -11,18 +12,34 @@ import { TeacherProjectTranslationService } from '../../../services/teacherProje
})
export class NodeIconAndTitleComponent {
@Input() protected nodeId: string;
protected nodePosition: string;
protected nodeTitle: string;
@Input() protected showPosition: boolean;
private subscriptions: Subscription;

constructor(
private projectService: TeacherProjectService,
private projectTranslationService: TeacherProjectTranslationService
) {}

protected getNodePosition(nodeId: string): string {
ngOnInit(): void {
this.nodePosition = this.getNodePosition(this.nodeId);
this.nodeTitle = this.getNodeTitle(this.nodeId);
this.subscriptions = this.projectService.projectParsed$.subscribe(() => {
this.nodePosition = this.getNodePosition(this.nodeId);
this.nodeTitle = this.getNodeTitle(this.nodeId);
});
}

ngOnDestroy(): void {
this.subscriptions.unsubscribe();
}

private getNodePosition(nodeId: string): string {
return this.projectService.getNodePositionById(nodeId);
}

protected getNodeTitle(nodeId: string): string {
private getNodeTitle(nodeId: string): string {
return this.projectService.isDefaultLocale()
? this.projectService.getNodeTitle(nodeId)
: this.translateNodeTitle(nodeId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@
class="lesson flex grow"
(opened)="toggleExpanded(true)"
(closed)="toggleExpanded(false)"
cdkDrag
[cdkDragData]="{ type: 'group', id: lesson.id }"
[id]="lesson.id"
>
<div class="drag-placeholder flex grow" *cdkDragPlaceholder></div>
<mat-expansion-panel-header aria-label="Expand/collapse lesson" i18n-aria-label>
<mat-panel-title>
<mat-checkbox
color="primary"
(change)="selectNode($event.checked)"
(click)="$event.stopPropagation()"
(keydown)="$event.stopPropagation()"
[disabled]="nodeTypeSelected() === 'step'"
aria-label="Select lesson"
i18n-aria-label
/>
@if (batchEditMode) {
<mat-checkbox
color="primary"
(change)="selectNode($event.checked)"
(click)="$event.stopPropagation()"
(keydown)="$event.stopPropagation()"
[disabled]="nodeTypeSelected() === 'step'"
aria-label="Select lesson"
i18n-aria-label
/>
} @else {
<mat-icon
cdkDragHandle
(click)="$event.stopPropagation()"
title="Drag to reorder"
i18n-title
>drag_indicator</mat-icon
>
}
<node-icon-and-title [nodeId]="lesson.id" [showPosition]="showPosition" />
</mat-panel-title>
<mat-panel-description class="text flex justify-end items-center gap-1">
Expand Down Expand Up @@ -51,10 +65,19 @@
</button>
</mat-panel-description>
</mat-expansion-panel-header>
<div>
<div
cdkDropList
[id]="lesson.id"
[cdkDropListData]="{ groupId: lesson.id, nodes: lesson.ids, idToNode: idToNode }"
cdkDropListLockAxis="y"
[cdkDropListConnectedTo]="allGroupIds"
[cdkDropListSortPredicate]="notAfterBranchingNode"
(cdkDropListDropped)="dropNode($event)"
>
@for (childId of lesson.ids; track childId) {
<div class="flex flex-row flex-wrap justify-start items-center">
<project-authoring-step
[batchEditMode]="batchEditMode"
[step]="idToNode[childId]"
(selectNodeEvent)="selectNodeEvent.emit($event)"
[showPosition]="showPosition"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,10 @@
padding: 0 8px;
}
}

.drag-placeholder {
background: #ccc;
border: dotted 3px #999;
min-height: 60px;
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { CopyTranslationsService } from '../../services/copyTranslationsService'
import { ConstraintService } from '../../services/constraintService';
import { Node } from '../../common/Node';
import { NodeIconAndTitleComponent } from '../choose-node-location/node-icon-and-title/node-icon-and-title.component';
import { MoveNodesService } from '../../services/moveNodesService';
import { of } from 'rxjs';

let component: ProjectAuthoringLessonComponent;
let fixture: ComponentFixture<ProjectAuthoringLessonComponent>;
Expand Down Expand Up @@ -45,12 +47,14 @@ describe('ProjectAuthoringLessonComponent', () => {
CopyTranslationsService,
DeleteNodeService,
DeleteTranslationsService,
MoveNodesService,
ProjectService,
TeacherDataService,
TeacherProjectTranslationService
),
MockProvider(TeacherProjectService, {
getNodeTypeSelected: () => signal<NodeTypeSelected>(NodeTypeSelected.lesson)
getNodeTypeSelected: () => signal<NodeTypeSelected>(NodeTypeSelected.lesson),
projectParsed$: of()
}),
provideRouter([])
]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, EventEmitter, Input, Output, Signal, ViewEncapsulation } from '@angular/core';
import { CdkDrag, CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
Expand All @@ -17,11 +18,13 @@ import { DeleteNodeService } from '../../services/deleteNodeService';
import { ActivatedRoute, Router } from '@angular/router';
import { DeleteTranslationsService } from '../../services/deleteTranslationsService';
import { AddStepTarget } from '../../../../app/domain/addStepTarget';
import { MoveNodesService } from '../../services/moveNodesService';

@Component({
encapsulation: ViewEncapsulation.None,
imports: [
FormsModule,
DragDropModule,
MatExpansionModule,
MatCheckboxModule,
MatIconModule,
Expand All @@ -36,6 +39,8 @@ import { AddStepTarget } from '../../../../app/domain/addStepTarget';
templateUrl: './project-authoring-lesson.component.html'
})
export class ProjectAuthoringLessonComponent {
allGroupIds: string[];
@Input() batchEditMode: boolean;
@Input() expanded: boolean = true;
@Output() onExpandedChanged: EventEmitter<ExpandEvent> = new EventEmitter<ExpandEvent>();
protected idToNode: any = {};
Expand All @@ -49,12 +54,14 @@ export class ProjectAuthoringLessonComponent {
private dataService: TeacherDataService,
private deleteNodeService: DeleteNodeService,
private deleteTranslationsService: DeleteTranslationsService,
private moveNodesService: MoveNodesService,
private projectService: TeacherProjectService,
private route: ActivatedRoute,
private router: Router
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function with many parameters (count = 7): constructor [qlty:function-parameters]

) {}

ngOnInit(): void {
this.allGroupIds = this.projectService.getAllGroupIds();
this.idToNode = this.projectService.idToNode;
this.nodeTypeSelected = this.projectService.getNodeTypeSelected();
}
Expand Down Expand Up @@ -100,4 +107,32 @@ export class ProjectAuthoringLessonComponent {
this.projectService.saveProject();
this.projectService.refreshProject();
}

protected dropNode(event: CdkDragDrop<any>): void {
const { container, currentIndex, item, previousContainer, previousIndex } = event;
if (previousContainer === container) {
moveItemInArray(container.data.nodes, previousIndex, currentIndex);
} else {
// do nothing. the UI will be updated by moveNodesAfter() and refreshProject() calls
}
if (currentIndex == 0) {
this.moveNodesService.moveNodesInsideGroup([item.data.id], container.data.groupId);
} else {
this.moveNodesService.moveNodesAfter([item.data.id], container.data.nodes[currentIndex - 1]);
}
this.projectService.checkPotentialStartNodeIdChangeThenSaveProject().then(() => {
this.projectService.refreshProject();
});
}

// allow a step to drop anywhere except the first step in a first path of a branch activity
// otherwise, the step will be placed after a node that has multiple transitions, which is not allowed
protected notAfterBranchingNode(index: number, item: CdkDrag<any>): boolean {
if (index === 0) return true;
const nodesExceptItem = item.dropContainer.data.nodes.filter(
(nodeId) => nodeId !== item.data.id
);
const nodeBefore = item.dropContainer.data.idToNode[nodesExceptItem[index - 1]];
return nodeBefore.transitionLogic.transitions.length <= 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,32 @@
(keyup.enter)="setCurrentNode(step.id)"
role="button"
tabindex="0"
cdkDrag
[cdkDragDisabled]="branchPoint"
[cdkDragData]="{ type: 'step', id: step.id }"
aria-label="Edit step"
i18n-aria-label
>
<mat-checkbox
color="primary"
(change)="selectNode($event.checked)"
(click)="$event.stopPropagation()"
[disabled]="nodeTypeSelected() === 'lesson'"
aria-label="Select step"
i18n-aria-label
/>
@if (batchEditMode) {
<mat-checkbox
color="primary"
(change)="selectNode($event.checked)"
(click)="$event.stopPropagation()"
[disabled]="nodeTypeSelected() === 'lesson'"
aria-label="Select step"
i18n-aria-label
/>
} @else if (!branchPoint) {
<div class="drag-placeholder flex grow" *cdkDragPlaceholder></div>
<mat-icon cdkDragHandle (click)="$event.stopPropagation()" title="Drag to reorder" i18n-title
>drag_indicator</mat-icon
>
}
<strong>
<node-icon-and-title [nodeId]="step.id" [showPosition]="showPosition" />
</strong>
<div class="flex justify-start items-center gap-2">
@if (isBranchPoint(step.id)) {
@if (branchPoint) {
<button
mat-icon-button
(click)="goToEditBranch(step.id); $event.stopPropagation()"
Expand Down Expand Up @@ -66,16 +76,18 @@
></div>
<!-- <div class="dynamic-step-buttons"> -->
<div class="flex justify-start items-center">
<button
class="step-action"
mat-icon-button
(click)="move(); $event.stopPropagation()"
matTooltip="Move step"
matTooltipPosition="above"
i18n-matTooltip
>
<mat-icon>redo</mat-icon>
</button>
@if (!branchPoint) {
<button
class="step-action"
mat-icon-button
(click)="move(); $event.stopPropagation()"
matTooltip="Move step"
matTooltipPosition="above"
i18n-matTooltip
>
<mat-icon>redo</mat-icon>
</button>
}
<button
class="step-action"
mat-icon-button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@
.tooltip-helper {
height: 100%;
}

.drag-placeholder {
background: #ccc;
border: dotted 3px #999;
min-height: 60px;
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import { MatTooltipModule } from '@angular/material/tooltip';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { NodeIconAndTitleComponent } from '../choose-node-location/node-icon-and-title/node-icon-and-title.component';
import { DragDropModule } from '@angular/cdk/drag-drop';

@Component({
imports: [
CommonModule,
DragDropModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
Expand All @@ -32,6 +34,8 @@ import { NodeIconAndTitleComponent } from '../choose-node-location/node-icon-and
templateUrl: './project-authoring-step.component.html'
})
export class ProjectAuthoringStepComponent {
@Input() batchEditMode: boolean;
protected branchPoint: boolean;
protected nodeTypeSelected: Signal<NodeTypeSelected>;
@Input() projectId: number;
@Output() selectNodeEvent: EventEmitter<SelectNodeEvent> = new EventEmitter<SelectNodeEvent>();
Expand All @@ -51,6 +55,7 @@ export class ProjectAuthoringStepComponent {
) {}

ngOnInit(): void {
this.branchPoint = this.projectService.isBranchPoint(this.step.id);
this.nodeTypeSelected = this.projectService.getNodeTypeSelected();
}

Expand Down Expand Up @@ -107,10 +112,6 @@ export class ProjectAuthoringStepComponent {
this.dataService.setCurrentNodeByNodeId(nodeId);
}

protected isBranchPoint(nodeId: string): boolean {
return this.projectService.isBranchPoint(nodeId);
}

protected nodeHasConstraint(nodeId: string): boolean {
return this.getNumberOfConstraintsOnNode(nodeId) > 0;
}
Expand Down
Loading
Loading