diff --git a/cms/djangoapps/contentstore/features/course-outline.py b/cms/djangoapps/contentstore/features/course-outline.py index 8786cfcaf11f..f9ceb6ff680e 100644 --- a/cms/djangoapps/contentstore/features/course-outline.py +++ b/cms/djangoapps/contentstore/features/course-outline.py @@ -131,14 +131,3 @@ def all_sections_are_collapsed_or_expanded(step, text): def change_grading_status(step): world.css_find('a.menu-toggle').click() world.css_find('.menu li').first.click() - - -@step(u'I reorder subsections') -def reorder_subsections(_step): - draggable_css = '.subsection-drag-handle' - ele = world.css_find(draggable_css).first - ele.action_chains.drag_and_drop_by_offset( - ele._element, - 0, - 25 - ).perform() diff --git a/cms/static/js/spec/utils/drag_and_drop_spec.js b/cms/static/js/spec/utils/drag_and_drop_spec.js index 676e6b992a60..9ec7dd592481 100644 --- a/cms/static/js/spec/utils/drag_and_drop_spec.js +++ b/cms/static/js/spec/utils/drag_and_drop_spec.js @@ -1,10 +1,32 @@ -define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_helpers/create_sinon", "jquery"], - function (ContentDragger, Notification, create_sinon, $) { +define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_helpers/create_sinon", "jquery", "underscore"], + function (ContentDragger, Notification, create_sinon, $, _) { describe("Overview drag and drop functionality", function () { beforeEach(function () { setFixtures(readFixtures('mock/mock-outline.underscore')); - ContentDragger.makeDraggable('.unit', '.unit-drag-handle', 'ol.sortable-unit-list', 'li.courseware-subsection, article.subsection-body'); - ContentDragger.makeDraggable('.courseware-subsection', '.subsection-drag-handle', '.sortable-subsection-list', 'section'); + _.each( + $('.unit'), + function (element) { + ContentDragger.makeDraggable(element, { + type: '.unit', + handleClass: '.unit-drag-handle', + droppableClass: 'ol.sortable-unit-list', + parentLocationSelector: 'li.courseware-subsection', + refresh: jasmine.createSpy('Spy on Unit') + }); + } + ); + _.each( + $('.courseware-subsection'), + function (element) { + ContentDragger.makeDraggable(element, { + type: '.courseware-subsection', + handleClass: '.subsection-drag-handle', + droppableClass: '.sortable-subsection-list', + parentLocationSelector: 'section', + refresh: jasmine.createSpy('Spy on Subsection') + }); + } + ); }); describe("findDestination", function () { @@ -115,7 +137,7 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel }); it("can drag into a collapsed list", function () { var $ele, destination; - $('#subsection-2').addClass('collapsed'); + $('#subsection-2').addClass('is-collapsed'); $ele = $('#unit-2'); $ele.offset({ top: $('#subsection-2').offset().top + 3, @@ -142,11 +164,11 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel }); }); it("collapses expanded elements", function () { - expect($('#subsection-1')).not.toHaveClass('collapsed'); + expect($('#subsection-1')).not.toHaveClass('is-collapsed'); ContentDragger.onDragStart({ element: $('#subsection-1') }, null, null); - expect($('#subsection-1')).toHaveClass('collapsed'); + expect($('#subsection-1')).toHaveClass('is-collapsed'); expect($('#subsection-1')).toHaveClass('expand-on-drop'); }); }); @@ -246,16 +268,16 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel expect(['0px', 'auto']).toContain($('#unit-1').css('left')); }); it("expands an element if it was collapsed on drag start", function () { - $('#subsection-1').addClass('collapsed'); + $('#subsection-1').addClass('is-collapsed'); $('#subsection-1').addClass('expand-on-drop'); ContentDragger.onDragEnd({ element: $('#subsection-1') }, null, null); - expect($('#subsection-1')).not.toHaveClass('collapsed'); + expect($('#subsection-1')).not.toHaveClass('is-collapsed'); expect($('#subsection-1')).not.toHaveClass('expand-on-drop'); }); it("expands a collapsed element when something is dropped in it", function () { - $('#subsection-2').addClass('collapsed'); + $('#subsection-2').addClass('is-collapsed'); ContentDragger.dragState.dropDestination = $('#list-2'); ContentDragger.dragState.attachMethod = "prepend"; ContentDragger.dragState.parentList = $('#subsection-2'); @@ -264,7 +286,7 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel }, null, { clientX: $('#unit-1').offset().left }); - expect($('#subsection-2')).not.toHaveClass('collapsed'); + expect($('#subsection-2')).not.toHaveClass('is-collapsed'); }); }); describe("AJAX", function () { @@ -306,6 +328,10 @@ define(["js/utils/drag_and_drop", "js/views/feedback_notification", "js/spec_hel expect(this.savingSpies.hide).toHaveBeenCalled(); this.clock.tick(1001); expect($('#unit-1')).not.toHaveClass('was-dropped'); + // source + expect($('#subsection-1').data('refresh')).toHaveBeenCalled(); + // target + expect($('#subsection-2').data('refresh')).toHaveBeenCalled(); }); }); }); diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js index 30a7671176f7..0e75353199f6 100644 --- a/cms/static/js/spec/views/pages/course_outline_spec.js +++ b/cms/static/js/spec/views/pages/course_outline_spec.js @@ -179,7 +179,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" create_sinon.expectJsonRequest(requests, 'GET', '/xblock/outline/mock-course'); create_sinon.respondWithJson(requests, mockSingleSectionCourseJSON); expect(outlinePage.$('.no-content')).not.toExist(); - expect(outlinePage.$('.list-sections li').data('locator')).toEqual('mock-section'); + expect(outlinePage.$('.list-sections li.outline-section').data('locator')).toEqual('mock-section'); }); it('can add a second section', function() { @@ -237,7 +237,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" create_sinon.expectJsonRequest(requests, 'GET', '/xblock/outline/mock-course'); create_sinon.respondWithJson(requests, mockSingleSectionCourseJSON); expect(outlinePage.$('.no-content')).not.toExist(); - expect(outlinePage.$('.list-sections li').data('locator')).toEqual('mock-section'); + expect(outlinePage.$('.list-sections li.outline-section').data('locator')).toEqual('mock-section'); }); it('remains empty if an add fails', function() { @@ -303,7 +303,7 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" requestCount = requests.length; create_sinon.respondWithError(requests); expect(requests.length).toBe(requestCount); // No additional requests should be made - expect(outlinePage.$('.list-sections li').data('locator')).toEqual('mock-section'); + expect(outlinePage.$('.list-sections li.outline-section').data('locator')).toEqual('mock-section'); }); it('can add a subsection', function() { diff --git a/cms/static/js/utils/drag_and_drop.js b/cms/static/js/utils/drag_and_drop.js index 2479ec47ba7a..a23e941f3e32 100644 --- a/cms/static/js/utils/drag_and_drop.js +++ b/cms/static/js/utils/drag_and_drop.js @@ -6,6 +6,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif droppableClasses: 'drop-target drop-target-prepend drop-target-before drop-target-after', validDropClass: "valid-drop", expandOnDropClass: "expand-on-drop", + collapsedClass: "is-collapsed", /* * Determine information about where to drop the currently dragged @@ -14,7 +15,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif */ findDestination: function (ele, yChange) { var eleY = ele.offset().top; - var eleYEnd = eleY + ele.height(); + var eleYEnd = eleY + ele.outerHeight(); var containers = $(ele.data('droppable-class')); for (var i = 0; i < containers.length; i++) { @@ -28,7 +29,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif // element is on top of its parent list -- don't check the // position of the container var parentList = container.parents(ele.data('parent-location-selector')).first(); - if (parentList.hasClass('collapsed')) { + if (parentList.hasClass(this.collapsedClass)) { var parentListTop = parentList.offset().top; // To make it easier to drop subsections into collapsed sections (which have // a lot of visual padding around them), allow a fudge factor around the @@ -36,7 +37,7 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif var collapseFudge = 10; if (Math.abs(eleY - parentListTop) < collapseFudge || (eleY > parentListTop && - eleYEnd - collapseFudge <= parentListTop + parentList.height()) + eleYEnd - collapseFudge <= parentListTop + parentList.outerHeight()) ) { return { ele: container, @@ -65,12 +66,12 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif for (var j = 0; j < siblings.length; j++) { var $sibling = $(siblings[j]); var siblingY = $sibling.offset().top; - var siblingHeight = $sibling.height(); + var siblingHeight = $sibling.outerHeight(); var siblingYEnd = siblingY + siblingHeight; // Facilitate dropping into the beginning or end of a list // (coming from opposite direction) via a "fudge factor". Math.min is for Jasmine test. - var fudge = Math.min(Math.ceil(siblingHeight / 2), 20); + var fudge = Math.min(Math.ceil(siblingHeight / 2), 35); // Dragging to top or bottom of a list with only one element is tricky // because the element being dragged may be the same size as the sibling. @@ -158,12 +159,16 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif // The direction the drag is moving in (negative means up, positive down). dragDirection: 0 }; - if (!ele.hasClass('collapsed')) { - ele.addClass('collapsed'); + if (!ele.hasClass(this.collapsedClass)) { + ele.addClass(this.collapsedClass); ele.find('.expand-collapse').first().addClass('expand').removeClass('collapse'); // onDragStart gets called again after the collapse, so we can't just store a variable in the dragState. ele.addClass(this.expandOnDropClass); } + + // We should remove this class name before start dragging to + // avoid performance issues. + ele.removeClass('was-dragging'); }, onDragMove: function (draggie, event, pointer) { @@ -251,41 +256,46 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif }, pointerInBounds: function (pointer, ele) { - return pointer.clientX >= ele.offset().left && pointer.clientX < ele.offset().left + ele.width(); + return pointer.clientX >= ele.offset().left && pointer.clientX < ele.offset().left + ele.outerWidth(); }, expandElement: function (ele) { - ele.removeClass('collapsed'); + ele.removeClass(this.collapsedClass); ele.find('.expand-collapse').first().removeClass('expand').addClass('collapse'); }, /* * Find all parent-child changes and save them. */ - handleReorder: function (ele) { - var parentSelector = ele.data('parent-location-selector'); - var childrenSelector = ele.data('child-selector'); - var newParentEle = ele.parents(parentSelector).first(); - var newParentLocator = newParentEle.data('locator'); - var oldParentLocator = ele.data('parent'); + handleReorder: function (element) { + var parentSelector = element.data('parent-location-selector'), + childrenSelector = element.data('child-selector'), + newParentEle = element.parents(parentSelector).first(), + newParentLocator = newParentEle.data('locator'), + oldParentLocator = element.data('parent'), + oldParentEle, saving; // If the parent has changed, update the children of the old parent. if (newParentLocator !== oldParentLocator) { // Find the old parent element. - var oldParentEle = $(parentSelector).filter(function () { + oldParentEle = $(parentSelector).filter(function () { return $(this).data('locator') === oldParentLocator; }); this.saveItem(oldParentEle, childrenSelector, function () { - ele.data('parent', newParentLocator); + element.data('parent', newParentLocator); + _.each([oldParentEle, newParentEle], function (element) { + var refresh = element.data('refresh'); + if (_.isFunction(refresh)) { refresh(); } + }); }); } - var saving = new NotificationView.Mini({ + saving = new NotificationView.Mini({ title: gettext('Saving…') }); saving.show(); - ele.addClass('was-dropped'); + element.addClass('was-dropped'); // Timeout interval has to match what is in the CSS. setTimeout(function () { - ele.removeClass('was-dropped'); + element.removeClass('was-dropped'); }, 1000); this.saveItem(newParentEle, childrenSelector, function () { saving.hide(); @@ -318,27 +328,43 @@ define(["jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notif }, /* - * Make `type` draggable using `handleClass`, able to be dropped - * into `droppableClass`, and with parent type - * `parentLocationSelector`. + * Make DOM element with class `type` draggable using `handleClass`, able to be dropped + * into `droppableClass`, and with parent type `parentLocationSelector`. + * @param {DOM element, jQuery element} element + * @param {Object} options The list of options. Possible options: + * `type` - class name of the element. + * `handleClass` - specifies on what element the drag interaction starts. + * `droppableClass` - specifies on what elements draggable element can be dropped. + * `parentLocationSelector` - class name of a parent element with data-locator. + * `refresh` - method that will be called after dragging to refresh + * views of the target and source xblocks. */ - makeDraggable: function (type, handleClass, droppableClass, parentLocationSelector) { - _.each( - $(type), - function (ele) { - // Remember data necessary to reconstruct the parent-child relationships - $(ele).data('droppable-class', droppableClass); - $(ele).data('parent-location-selector', parentLocationSelector); - $(ele).data('child-selector', type); - var draggable = new Draggabilly(ele, { - handle: handleClass, - containment: '.wrapper-dnd' - }); - draggable.on('dragStart', _.bind(contentDragger.onDragStart, contentDragger)); - draggable.on('dragMove', _.bind(contentDragger.onDragMove, contentDragger)); - draggable.on('dragEnd', _.bind(contentDragger.onDragEnd, contentDragger)); - } - ); + makeDraggable: function (element, options) { + var draggable; + options = _.defaults({ + type: null, + handleClass: null, + droppableClass: null, + parentLocationSelector: null, + refresh: null + }, options); + + if ($(element).data('droppable-class') !== options.droppableClass) { + $(element).data({ + 'droppable-class': options.droppableClass, + 'parent-location-selector': options.parentLocationSelector, + 'child-selector': options.type, + 'refresh': options.refresh + }); + + draggable = new Draggabilly(element, { + handle: options.handleClass, + containment: '.wrapper-dnd' + }); + draggable.on('dragStart', _.bind(contentDragger.onDragStart, contentDragger)); + draggable.on('dragMove', _.bind(contentDragger.onDragMove, contentDragger)); + draggable.on('dragEnd', _.bind(contentDragger.onDragEnd, contentDragger)); + } } }; diff --git a/cms/static/js/views/course_outline.js b/cms/static/js/views/course_outline.js index adfc1ca365b5..22b222b50b8c 100644 --- a/cms/static/js/views/course_outline.js +++ b/cms/static/js/views/course_outline.js @@ -9,15 +9,20 @@ * - adding units will automatically redirect to the unit page rather than showing them inline */ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_utils", - "js/models/xblock_outline_info", - "js/views/modals/edit_outline_item"], - function($, _, XBlockOutlineView, ViewUtils, XBlockOutlineInfo, EditSectionXBlockModal) { + "js/models/xblock_outline_info", "js/views/modals/edit_outline_item", "js/utils/drag_and_drop"], + function($, _, XBlockOutlineView, ViewUtils, XBlockOutlineInfo, EditSectionXBlockModal, ContentDragger) { var CourseOutlineView = XBlockOutlineView.extend({ // takes XBlockOutlineInfo as a model templateName: 'course-outline', + render: function() { + var renderResult = XBlockOutlineView.prototype.render.call(this); + this.makeContentDraggable(this.el); + return renderResult; + }, + shouldExpandChildren: function() { // Expand the children if this xblock's locator is in the initially expanded state if (this.initialState && _.contains(this.initialState.expanded_locators, this.model.id)) { @@ -154,6 +159,36 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ event.preventDefault(); this.editXBlock(); }.bind(this)); + }, + + makeContentDraggable: function(element) { + if ($(element).hasClass("outline-section")) { + ContentDragger.makeDraggable(element, { + type: '.outline-section', + handleClass: '.section-drag-handle', + droppableClass: 'ol.list-sections', + parentLocationSelector: 'article.outline', + refresh: this.refresh.bind(this) + }); + } + else if ($(element).hasClass("outline-subsection")) { + ContentDragger.makeDraggable(element, { + type: '.outline-subsection', + handleClass: '.subsection-drag-handle', + droppableClass: 'ol.list-subsections', + parentLocationSelector: 'li.outline-section', + refresh: this.refresh.bind(this) + }); + } + else if ($(element).hasClass("outline-unit")) { + ContentDragger.makeDraggable(element, { + type: '.outline-unit', + handleClass: '.unit-drag-handle', + droppableClass: 'ol.list-units', + parentLocationSelector: 'li.outline-subsection', + refresh: this.refresh.bind(this) + }); + } } }); diff --git a/cms/static/js/views/overview.js b/cms/static/js/views/overview.js index f05ba24079e2..f33f0c908fef 100644 --- a/cms/static/js/views/overview.js +++ b/cms/static/js/views/overview.js @@ -1,6 +1,6 @@ -define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notification", "js/utils/drag_and_drop", +define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/feedback_notification", "js/utils/cancel_on_escape", "js/utils/date_utils", "js/utils/module"], - function (domReady, $, ui, _, gettext, NotificationView, ContentDragger, CancelOnEscape, + function (domReady, $, ui, _, gettext, NotificationView, CancelOnEscape, DateUtils, ModuleUtils) { var modalSelector = '.edit-section-publish-settings'; @@ -37,9 +37,9 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe var closeModalNew = function (e) { - if (e) { + if (e) { e.preventDefault(); - }; + } $('body').removeClass('modal-window-is-shown'); $('.edit-section-publish-settings').removeClass('is-shown'); }; @@ -230,27 +230,6 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe $('.new-courseware-section-button').bind('click', addNewSection); $('.new-subsection-item').bind('click', addNewSubsection); - // Section - ContentDragger.makeDraggable( - '.courseware-section', - '.section-drag-handle', - '.courseware-overview', - 'article.courseware-overview' - ); - // Subsection - ContentDragger.makeDraggable( - '.id-holder', - '.subsection-drag-handle', - '.subsection-list > ol', - '.courseware-section' - ); - // Unit - ContentDragger.makeDraggable( - '.unit', - '.unit-drag-handle', - 'ol.sortable-unit-list', - 'li.courseware-subsection, article.subsection-body' - ); }); return { diff --git a/cms/static/sass/_shame.scss b/cms/static/sass/_shame.scss index dbd6e61f9780..0549cbf7c5e1 100644 --- a/cms/static/sass/_shame.scss +++ b/cms/static/sass/_shame.scss @@ -25,15 +25,18 @@ // ==================== -// needed to override ui-window styling for dragging state (outline selectors get too specific) -.courseware-section.is-dragging { - box-shadow: 0 1px 2px 0 $shadow-d1 !important; - border: 1px solid $gray-d3 !important; -} +// overriding outline styling for general drag and drop cases +.outline-section, .outline-subsection { + + // STATE: is dragging + &.is-dragging { + border-color: $gray-d3; + } -.courseware-section.is-dragging.valid-drop { - border-color: $blue-s1 !important; - box-shadow: 0 1px 2px 0 $blue-t2 !important; + // STATE: is dragging + valid drop + &.valid-drop { + border-color: $ui-action-primary-color-focus; + } } // ==================== diff --git a/cms/static/sass/assets/_anims.scss b/cms/static/sass/assets/_anims.scss index cf3f49e59bd2..a8d054a0a50b 100644 --- a/cms/static/sass/assets/_anims.scss +++ b/cms/static/sass/assets/_anims.scss @@ -271,3 +271,26 @@ %anim-pulse { @include animation(pulse $tmg-f1 ease-in-out 1); } + +// ==================== + + +// was-dropped +@include keyframes(was-dropped) { + 0% { + opacity: 1.0; + } + + 50% { + opacity: 0.5; + } + + 100% { + opacity: 1.0; + } +} + +// canned animation - use if you want out of the box/non-customized anim +%anim-was-dropped { + @include animation(was-dropped $tmg-f1 ease-in-out 1); +} diff --git a/cms/static/sass/elements/_controls.scss b/cms/static/sass/elements/_controls.scss index f0f7179cb7e5..5e113f758660 100644 --- a/cms/static/sass/elements/_controls.scss +++ b/cms/static/sass/elements/_controls.scss @@ -1,8 +1,6 @@ // studio - elements - UI controls // ==================== -// ==================== - // general actions %action { @extend %ui-fake-link; @@ -350,7 +348,7 @@ // UI: elem is draggable .is-draggable { - @include transition(border-color $tmg-f2 ease-in-out 0, box-shadow $tmg-f2 ease-in-out 0); + @include transition(border-color $tmg-f2 ease-in-out 0, box-shadow $tmg-f2 ease-in-out 0, margin $tmg-f2 ease-in-out 0); position: relative; .draggable-drop-indicator { @@ -390,8 +388,8 @@ // UI: condition - valid drop &.valid-drop { - border-color: $blue-s1; - box-shadow: 0 1px 2px 0 $blue-t2; + border-color: $ui-action-primary-color-focus; + box-shadow: 0 1px 2px 0 rgba($ui-action-primary-color-focus, 0.50); } } @@ -404,6 +402,7 @@ .drop-target { &.drop-target-before { + margin-top: ($baseline*1.5); > .draggable-drop-indicator-before { opacity: 1.0; @@ -411,9 +410,17 @@ } &.drop-target-after { + margin-bottom: ($baseline*1.5); > .draggable-drop-indicator-after { opacity: 1.0; } } } + +// UI: drop state - was dropped +.was-dropped { + @include animation(was-dropped $tmg-avg ease-in-out 1); + border-color: $ui-action-primary-color-focus; + box-shadow: 0 1px 2px 0 rgba($ui-action-primary-color-focus, 0.50); +} diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss index 12a21123814c..a92893d0e0e1 100644 --- a/cms/static/sass/views/_outline.scss +++ b/cms/static/sass/views/_outline.scss @@ -224,6 +224,10 @@ color: $blue; } } + + &.is-dragging { + @include transition-property(none); + } } // item: title @@ -242,6 +246,16 @@ } } } + + // STATE: drag and drop + .drop-target-prepend .draggable-drop-indicator-initial { + opacity: 1.0; + } + + // STATE: was dropped + &.was-dropped { + border-color: $blue; + } } // outline: sections @@ -316,6 +330,11 @@ @extend %outline-item-content-hidden; } } + + // STATE: drag and drop - was dropped + &.was-dropped { + border-left-color: $ui-action-primary-color-focus; + } } // outline: subsections @@ -404,6 +423,7 @@ } .outline-unit { + @include transition(margin $tmg-f2 linear 0s); // needed for drag and drop transitions margin-left: $outline-indent-width; // header @@ -435,49 +455,36 @@ // -------------------- .outline-section { + .ui-splint-indicator { + height: ($baseline/10); + margin-left: ($baseline/4); + } + .draggable-drop-indicator-before { - top: -($baseline/2); + top: -($baseline*0.75); left: 0; } .draggable-drop-indicator-after { - bottom: -13px; + bottom: -($baseline*0.75); left: 0; } - - // CASE: DnD - empty subsection with unit dropping - .drop-target-prepend .draggable-drop-indicator-initial { - opacity: 1.0; - } - - // STATE: was dropped - &.was-dropped { - background-color: $ui-update-color; - } } // UI: drag and drop: subsection .outline-subsection { - .draggable-drop-indicator-before { - top: 0; - } - - .draggable-drop-indicator-after { - bottom: 0; + .ui-splint-indicator { + height: ($baseline/10); + margin-left: ($baseline*1.25); } - // CASE: DnD - empty subsection with unit dropping - .drop-target-prepend .draggable-drop-indicator-initial { - opacity: 1.0; + .draggable-drop-indicator-before { + top: -($baseline*0.75); } - // STATE: was dropped - &.was-dropped { - - > .section-item { - background-color: $ui-update-color !important; // nasty, but needed for specificity - } + .draggable-drop-indicator-after { + bottom: -($baseline*0.75); } } @@ -485,24 +492,41 @@ .outline-unit { .draggable-drop-indicator-before { - top: 0; + top: -($baseline*0.75); } .draggable-drop-indicator-after { - bottom: 0; - } - - // STATE: was dropped - &.was-dropped { - - > .section-item { - background-color: $ui-update-color !important; // nasty, but needed for specificity - } + bottom: -($baseline*0.75); } } // UI: drag and drop: splints .ui-splint-indicator { position: relative; + + .draggable-drop-indicator { + @extend %ui-depth3; + @include transition(opacity $tmg-f2 linear 0s); + @include size(100%, auto); + position: absolute; + border-top: 1px solid $blue-l1; + opacity: 0.0; + + *[class^="icon-caret"] { + @extend %t-icon5; + position: absolute; + top: -12px; + left: -($baseline/4); + color: $blue-s1; + } + } + + .draggable-drop-indicator-before { + top: -($baseline/2); + } + + .draggable-drop-indicator-after { + bottom: -($baseline/2); + } } } diff --git a/cms/templates/js/course-outline.underscore b/cms/templates/js/course-outline.underscore index 30037023418a..50294bb55c9c 100644 --- a/cms/templates/js/course-outline.underscore +++ b/cms/templates/js/course-outline.underscore @@ -35,6 +35,8 @@ if (statusType === 'warning') {