Skip to content

Commit

Permalink
Move dropped item to different zone in assessment mode.
Browse files Browse the repository at this point in the history
In standard mode, once you drop an item into the correct zone, you can
no longer move it until you reset the exercise.

In assessment mode, we want users to be able to freely move items
between zones until they explicitly submit their solution.

This commit implements the ability to move dropped items between zones
in assessment mode. It does not make any changes to standard mode.
  • Loading branch information
mtyaka committed Jul 25, 2016
1 parent ff7ddfd commit c097fdd
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 59 deletions.
13 changes: 9 additions & 4 deletions drag_and_drop_v2/public/css/drag_and_drop.css
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@
margin: 0;
transform: translate(-50%, -50%); /* These blocks are to be centered on their absolute x,y position */
}
/* Dragging placed option to a different zone in assessment mode */
.xblock--drag-and-drop .drag-container .target .option.grabbed-with-mouse {
transform: none; /* The transform messes with jQuery UI Draggable positioning, */
/* so remove it while dragging with the mouse. */
}

/* Placed options in an aligned zone */
.xblock--drag-and-drop .zone .item-wrapper {
Expand Down Expand Up @@ -172,15 +177,15 @@
}

/* Focused option */
.xblock--drag-and-drop .drag-container .item-bank .option:focus,
.xblock--drag-and-drop .drag-container .item-bank .option:hover,
.xblock--drag-and-drop .drag-container .item-bank .option[aria-grabbed='true'] {
.xblock--drag-and-drop .drag-container .option[draggable='true']:focus,
.xblock--drag-and-drop .drag-container .option[draggable='true']:hover,
.xblock--drag-and-drop .drag-container .option[draggable='true'][aria-grabbed='true'] {
outline-width: 2px;
outline-style: solid;
outline-offset: -4px;
}

.xblock--drag-and-drop .drag-container .ui-draggable-dragging.option {
.xblock--drag-and-drop .drag-container .option.grabbed-with-mouse {
box-shadow: 0 16px 32px 0 rgba(0, 0, 0, 0.3);
border: 1px solid #ccc;
opacity: .65;
Expand Down
81 changes: 55 additions & 26 deletions drag_and_drop_v2/public/js/drag_and_drop.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,15 @@ function DragAndDropTemplates(configuration) {
if (item.widthPercent) {
className += " specified-width"; // The author has specified a width for this item.
}
if (item.grabbed_with) {
className += " grabbed-with-" + item.grabbed_with;
}
var attributes = {
'role': 'button',
'draggable': !item.drag_disabled,
'aria-grabbed': item.grabbed,
'data-value': item.value,
'data-drag-disabled': item.drag_disabled
'tabindex': item.focusable ? 0 : undefined
};
var style = {};
if (item.background_color) {
Expand Down Expand Up @@ -84,7 +87,6 @@ function DragAndDropTemplates(configuration) {
}
} else {
// If an item has not been placed it must be possible to move focus to it using the keyboard:
attributes.tabindex = 0;
if (item.widthPercent) {
// The item bank container is often wider than the background image, and the
// widthPercent is specified relative to the background image so we have to
Expand Down Expand Up @@ -161,6 +163,7 @@ function DragAndDropTemplates(configuration) {
h(
selector,
{
key: zone.prefixed_uid,
id: zone.prefixed_uid,
attributes: {
'tabindex': 0,
Expand Down Expand Up @@ -281,10 +284,9 @@ function DragAndDropTemplates(configuration) {
h('img.target-img', {src: ctx.target_img_src, alt: ctx.target_img_description}),
]
),
renderCollection(zoneTemplate, ctx.zones, ctx),
renderCollection(itemTemplate, items_placed_unaligned, ctx)
]
),
renderCollection(itemTemplate, items_placed_unaligned, ctx),
renderCollection(zoneTemplate, ctx.zones, ctx)
]),
]),
keyboardHelpTemplate(ctx),
feedbackTemplate(ctx),
Expand Down Expand Up @@ -658,7 +660,7 @@ function DragAndDropBlock(runtime, element, configuration) {

// Make zone accept items that are dropped using the mouse
$root.find('.zone').droppable({
accept: '.item-bank .option',
accept: '.drag-container .option',
tolerance: 'pointer',
drop: function(evt, ui) {
var $zone = $(this);
Expand All @@ -669,15 +671,15 @@ function DragAndDropBlock(runtime, element, configuration) {
};

var initDraggable = function() {
$root.find('.item-bank .option').not('[data-drag-disabled=true]').each(function() {
$root.find('.drag-container .option[draggable=true]').each(function() {
var $item = $(this);

// Allow item to be "picked up" using the keyboard
$item.on('keydown', function(evt) {
if (isActionKey(evt)) {
evt.preventDefault();
placementMode = true;
grabItem($item);
grabItem($item, 'keyboard');
$selectedItem = $item;
$root.find('.target .zone').first().focus();
}
Expand All @@ -686,20 +688,32 @@ function DragAndDropBlock(runtime, element, configuration) {
// Make item draggable using the mouse
try {
$item.draggable({
addClasses: false, // don't add ui-draggable-* classes as they don't play well with virtual DOM.
containment: $root.find('.drag-container'),
cursor: 'move',
stack: $root.find('.item-bank .option'),
stack: $root.find('.drag-container .option'),
revert: 'invalid',
revertDuration: 150,
start: function(evt, ui) {
var $item = $(this);
grabItem($item);
// Store initial position of dragged item to be able to revert back to it on cancelled drag
// (when user drops the item onto an area that is not a droppable zone).
// The jQuery UI draggable library usually knows how to revert correctly, but our dropped items
// have a translation transform that confuses jQuery UI draggable, so we "help" it do the right
// thing by manually storing the initial position and resetting it in the 'stop' handler below.
$item.data('initial-position', {
left: $item.css('left'),
top: $item.css('top')
});
grabItem($item, 'mouse');
publishEvent({
event_type: 'edx.drag_and_drop_v2.item.picked_up',
item_id: $item.data('value'),
});
},
stop: function(evt, ui) {
// Revert to original position.
$item.css($item.data('initial-position'));
releaseItem($(this));
}
});
Expand All @@ -710,9 +724,9 @@ function DragAndDropBlock(runtime, element, configuration) {
});
};

var grabItem = function($item) {
var grabItem = function($item, interaction_type) {
var item_id = $item.data('value');
setGrabbedState(item_id, true);
setGrabbedState(item_id, true, interaction_type);
updateDOM();
};

Expand All @@ -722,16 +736,23 @@ function DragAndDropBlock(runtime, element, configuration) {
updateDOM();
};

var setGrabbedState = function(item_id, grabbed) {
for (var i = 0; i < configuration.items.length; i++) {
if (configuration.items[i].id === item_id) {
configuration.items[i].grabbed = grabbed;
var setGrabbedState = function(item_id, grabbed, interaction_type) {
for (var i = 0, item; i < configuration.items.length; i++) {
item = configuration.items[i];
if (item.id === item_id) {
if (grabbed) {
item.grabbed = true;
item.grabbed_with = interaction_type;
} else {
item.grabbed = false;
delete item.grabbed_with;
}
}
}
};

var destroyDraggable = function() {
$root.find('.item-bank .option[data-drag-disabled=true]').each(function() {
$root.find('.drag-container .option[draggable=false]').each(function() {
var $item = $(this);

$item.off();
Expand Down Expand Up @@ -764,12 +785,10 @@ function DragAndDropBlock(runtime, element, configuration) {
// In assessment mode we leave it in the chosen zone until explicit answer submission.
if (configuration.mode === DragAndDropBlock.STANDARD_MODE) {
state.last_action_correct = data.correct;
if (data.correct) {
state.items[item_id].correct = true;
} else {
state.feedback = data.feedback;
if (!data.correct) {
delete state.items[item_id];
}
state.feedback = data.feedback;
if (data.finished) {
state.finished = true;
state.overall_feedback = data.overall_feedback;
Expand Down Expand Up @@ -832,22 +851,32 @@ function DragAndDropBlock(runtime, element, configuration) {
if (item.grabbed !== undefined) {
grabbed = item.grabbed;
}
var placed = item_user_state && item_user_state.correct;
var drag_disabled;
// In standard mode items placed in correct zone are no longer draggable.
// In assessment mode items are draggable and can be moved between zones
// until user explicitly submits the problem.
if (configuration.mode === DragAndDropBlock.STANDARD_MODE) {
drag_disabled = Boolean(state.finished || item_user_state);
} else {
drag_disabled = Boolean(state.finished);
}
var itemProperties = {
value: item.id,
drag_disabled: Boolean(item_user_state || state.finished),
class_name: placed || state.finished ? 'fade' : undefined,
drag_disabled: drag_disabled,
focusable: !drag_disabled,
class_name: drag_disabled ? 'fade' : undefined,
xhr_active: (item_user_state && item_user_state.submitting_location),
displayName: item.displayName,
imageURL: item.expandedImageURL,
imageDescription: item.imageDescription,
has_image: !!item.expandedImageURL,
grabbed: grabbed,
grabbed_with: item.grabbed_with,
is_placed: Boolean(item_user_state),
widthPercent: item.widthPercent, // widthPercent may be undefined (auto width)
imgNaturalWidth: item.imgNaturalWidth,
};
if (item_user_state) {
itemProperties.is_placed = true;
itemProperties.zone = item_user_state.zone;
itemProperties.zone_align = item_user_state.zone_align;
itemProperties.x_percent = item_user_state.x_percent;
Expand Down
Loading

0 comments on commit c097fdd

Please sign in to comment.