Skip to content
Snippets Groups Projects
Commit f5005a0f authored by pnogal's avatar pnogal
Browse files

ROENROLL-1369 Improve feel and look of drag and drop functionality

parent 4be46dff
No related branches found
No related tags found
No related merge requests found
Showing
with 91 additions and 100 deletions
......@@ -3,6 +3,7 @@
id="saved-courses"
class="course-list"
cdkDropList
[cdkDropListData]="courses$ | async"
[cdkDropListConnectedTo]="dropZoneIds$ | async"
(cdkDropListDropped)="drop($event)">
<div class="course-wrapper" *ngFor="let course of courses$ | async" cdkDrag [cdkDragData]="course">
......
#saved-for-later-container {
padding: 20px 0 0px 0;
margin-top: 0px;
border-top: 1px solid rgba(0, 0, 0, 0.54);
}
.cdk-drop-list-dragging {
position: relative;
&:after {
content: 'Save course for later';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: white;
border: dashed 2px #2879a8;
color: #2879a8;
font-weight: bold;
text-transform: uppercase;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}
}
......@@ -51,6 +51,7 @@ export class SavedForLaterContainerComponent implements OnInit {
subjectCode: course.subjectCode,
title: course.title,
catalogNumber: course.catalogNumber,
newIndex: event.currentIndex,
}),
);
......
......@@ -77,6 +77,7 @@ export class CourseItemComponent implements OnInit {
subjectCode: course.subjectCode,
title: course.title,
catalogNumber: course.catalogNumber,
newIndex: 0,
}),
);
}
......@@ -94,7 +95,7 @@ export class CourseItemComponent implements OnInit {
termCode: string;
};
this.store.dispatch(
new MoveCourseBetweenTerms({ to: termCode, from, id }),
new MoveCourseBetweenTerms({ to: termCode, from, id, newIndex: 0 }),
);
break;
......@@ -131,6 +132,7 @@ export class CourseItemComponent implements OnInit {
subjectCode: subjectCode,
title: title,
catalogNumber: catalogNumber,
newIndex: 0,
}),
);
......@@ -202,7 +204,17 @@ export class CourseItemComponent implements OnInit {
const { subjectCode, courseId } = course;
const termCode = term;
this.store.dispatch(new AddCourse({ subjectCode, courseId, termCode }));
const newIndex = 0;
this.store.dispatch(
new AddCourse({ subjectCode, courseId, termCode, newIndex }),
);
}
switchTerm(course, term) {
const { id, termCode: from } = course;
const to = term;
const newIndex = 0;
this.store.dispatch(new MoveCourseBetweenTerms({ to, from, id, newIndex }));
}
openCourseDetailsDialog(course) {
......
......@@ -25,24 +25,33 @@ export enum CourseActionTypes {
export class MoveCourseBetweenTerms implements Action {
public readonly type = CourseActionTypes.MoveCourseBetweenTerms;
constructor(public payload: { to: string; from: string; id: number }) {}
constructor(
public payload: { to: string; from: string; id: number; newIndex: number },
) {}
}
export class MoveCourseBetweenTermsSuccess implements Action {
public readonly type = CourseActionTypes.MoveCourseBetweenTermsSuccess;
constructor(public payload: { to: string; from: string; id: number }) {}
constructor(
public payload: { to: string; from: string; id: number; newIndex: number },
) {}
}
export class AddCourse implements Action {
public readonly type = CourseActionTypes.AddCourse;
constructor(
public payload: { subjectCode: string; courseId: string; termCode: string },
public payload: {
subjectCode: string;
courseId: string;
termCode: string;
newIndex: number;
},
) {}
}
export class AddCourseSuccess implements Action {
public readonly type = CourseActionTypes.AddCourseSuccess;
constructor(public payload: { course: Course }) {}
constructor(public payload: { course: Course; newIndex: number }) {}
}
export class RemoveCourse implements Action {
......@@ -63,6 +72,7 @@ export class AddSaveForLater implements Action {
courseId: string;
title: string;
catalogNumber: string;
newIndex: number;
},
) {}
}
......@@ -75,6 +85,7 @@ export class AddSaveForLaterSuccess implements Action {
courseId: string;
title: string;
catalogNumber: string;
newIndex: number;
},
) {}
}
......
......@@ -102,7 +102,7 @@ export class CourseEffects {
flatMap(([[action, visibleDegreePlan], subjects]) => {
// TODO error handle the API calls
const roadmapId = (visibleDegreePlan as DegreePlan).roadmapId;
const { subjectCode, termCode, courseId } = action.payload;
const { subjectCode, termCode, courseId, newIndex } = action.payload;
const addCourse$ = this.api.addCourse(
roadmapId,
......@@ -118,9 +118,8 @@ export class CourseEffects {
subject: subjects[courseBase.subjectCode],
})),
);
const toSuccessAction$ = courseBaseToCourse$.pipe(
map(course => new AddCourseSuccess({ course })),
map(course => new AddCourseSuccess({ course, newIndex })),
);
return toSuccessAction$;
......
......@@ -208,7 +208,12 @@ export function degreePlannerReducer(
}
case CourseActionTypes.MoveCourseBetweenTerms: {
const { to: toTermCode, from: fromTermCode, id } = action.payload;
const {
to: toTermCode,
from: fromTermCode,
id,
newIndex,
} = action.payload;
const { yearCode: fromYearCode } = parseTermCode(fromTermCode);
const { yearCode: toYearCode } = parseTermCode(toTermCode);
const course = findCourse(state.visibleYears, fromTermCode, id);
......@@ -224,6 +229,7 @@ export function degreePlannerReducer(
const toYear = createYearWithCourse(
toTermCode,
course,
newIndex,
state.activeTermCodes,
fromYearCode === toYearCode
? fromYear
......@@ -243,13 +249,14 @@ export function degreePlannerReducer(
}
case CourseActionTypes.AddCourseSuccess: {
const { course } = action.payload;
const { course, newIndex } = action.payload;
const { termCode } = course;
const { yearCode } = parseTermCode(termCode);
const year: Year = createYearWithCourse(
termCode,
course,
newIndex,
state.activeTermCodes,
state.visibleYears[yearCode],
);
......@@ -294,21 +301,22 @@ export function degreePlannerReducer(
}
case CourseActionTypes.AddSaveForLater: {
const newSavedForLater: SavedForLaterCourse[] = [
...state.savedForLaterCourses,
{
id: null,
courseId: action.payload.courseId,
termCode: '0000',
topicId: 0,
subjectCode: action.payload.subjectCode,
title: action.payload.title,
catalogNumber: action.payload.catalogNumber,
courseOrder: 0,
subject: state.subjects[action.payload.subjectCode],
},
];
return { ...state, savedForLaterCourses: newSavedForLater };
const { newIndex } = action.payload;
const newSavedCourse: SavedForLaterCourse = {
id: null,
courseId: action.payload.courseId,
termCode: '0000',
topicId: 0,
subjectCode: action.payload.subjectCode,
title: action.payload.title,
catalogNumber: action.payload.catalogNumber,
courseOrder: 0,
subject: state.subjects[action.payload.subjectCode],
};
const savedForLaterCoursesArr = state.savedForLaterCourses.slice();
savedForLaterCoursesArr.splice(newIndex, 0, newSavedCourse);
return { ...state, savedForLaterCourses: savedForLaterCoursesArr };
}
case PlanActionTypes.CreatePlanSuccess: {
......@@ -469,12 +477,14 @@ const findCourse = (years: YearMapping, termCode: string, recordId: number) => {
const createYearWithCourse = (
termCode: string,
course: Course,
newIndex: number,
activeTermCodes: string[],
year = generateYearForTermCode(termCode, activeTermCodes),
): Year => {
const { termName } = parseTermCode(termCode);
const term = year[termName];
const courses = term.courses.concat({ ...course, termCode });
const courses = term.courses.slice();
courses.splice(newIndex, 0, course);
return { ...year, [termName]: { ...term, courses } };
};
......
......@@ -45,27 +45,6 @@
font-weight: 400;
}
.cdk-drop-list-dragging {
position: relative;
&:after {
content: 'Add course to term';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #eef1f3;
border: dashed 2px #2879a8;
color: #2879a8;
font-weight: bold;
text-transform: uppercase;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
}
}
.add-new-wrapper {
border-top: solid #e0e4e7 1px;
padding: 8px;
......
// Libraries
import { Component, Input, OnInit } from '@angular/core';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import {
CdkDragDrop,
moveItemInArray,
transferArrayItem,
} from '@angular/cdk/drag-drop';
import { MatDialog } from '@angular/material';
import { Observable } from 'rxjs';
import { filter, map, distinctUntilChanged } from 'rxjs/operators';
......@@ -129,6 +133,11 @@ export class TermContainerComponent implements OnInit {
if (newContainer === previousContainer) {
// If the user dropped a course into the same container do nothing
moveItemInArray(
event.container.data,
event.previousIndex,
event.currentIndex,
);
return;
} else if (previousContainer.indexOf('term-') === 0) {
// If moving from term to term
......@@ -136,15 +145,18 @@ export class TermContainerComponent implements OnInit {
// Get the pervious and new term code, and the record ID
const to = newContainer.substr(5);
const { termCode: from, id } = event.item.data;
const newIndex = event.currentIndex;
// Dispatch a new change request
this.store.dispatch(new actions.MoveCourseBetweenTerms({ to, from, id }));
this.store.dispatch(
new actions.MoveCourseBetweenTerms({ to, from, id, newIndex }),
);
} else if (previousContainer === 'saved-courses') {
// If moving from saved courses to term
// Get the term code from the new term dropzone's ID
const termCode = newContainer.substr(5);
const newIndex = event.currentIndex;
// Pull the course data from the moved item
const { subjectCode, courseId } = event.item.data;
......@@ -153,6 +165,7 @@ export class TermContainerComponent implements OnInit {
subjectCode,
courseId,
termCode,
newIndex,
}),
);
this.store.dispatch(
......@@ -164,8 +177,9 @@ export class TermContainerComponent implements OnInit {
) {
const termCode = newContainer.substr(5);
const { subjectCode, courseId } = event.item.data;
const newIndex = event.currentIndex;
this.store.dispatch(
new actions.AddCourse({ subjectCode, courseId, termCode }),
new actions.AddCourse({ subjectCode, courseId, termCode, newIndex }),
);
}
}
......
......@@ -94,32 +94,22 @@ body {
}
}
#favoriteCourse-dropZone {
.course-favorite {
.course-item {
border: 1px solid #b7b7b7;
border-radius: 4px;
padding: 5px 10px;
}
}
}
.cdk-drag-placeholder {
.cdk-drop-list-dragging {
position: relative;
opacity: 0;
height: 0;
.cdk-drag-placeholder {
opacity: 0.4;
min-height: 100% !important;
height: 100% !important;
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
border-radius: 5px;
}
}
.course-wrapper:not(.cdk-drag-preview) {
transform: translate(0) !important;
.cdk-drag-animating {
transition: transform 200ms cubic-bezier(0, 0, 0.2, 1);
}
.cdk-drag-preview {
.course-wrapper-inner {
transform: rotate(-3deg);
border-radius: 5px;
box-shadow: -3px 3px 3px 2px rgba(0, 0, 0, 0.26);
}
.course-list.cdk-drop-list-dragging .course-wrapper:not(.cdk-drag-placeholder) {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1) !important;
}
.no-courses {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment