function dragDimensionEnd(d) {
    // Check if dragging is supported
    if(d.parcatsViewModel.arrangement === 'fixed') {
        return;
    }

    if(d.dragDimensionDisplayInd === null) {
        return;
    }

    d3.select(this).selectAll('text').attr('font-weight', 'normal');

    // Compute restyle command
    // -----------------------
    var restyleData = {};
    var traceInd = getTraceIndex(d.parcatsViewModel);

    // ### Handle dimension reordering ###
    var finalDragDimensionDisplayInds = d.parcatsViewModel.model.dimensions.map(function(d) {return d.displayInd;});
    var anyDimsReordered = d.initialDragDimensionDisplayInds.some(function(initDimDisplay, dimInd) {
        return initDimDisplay !== finalDragDimensionDisplayInds[dimInd];
    });

    if(anyDimsReordered) {
        finalDragDimensionDisplayInds.forEach(function(finalDimDisplay, dimInd) {
            var containerInd = d.parcatsViewModel.model.dimensions[dimInd].containerInd;
            restyleData['dimensions[' + containerInd + '].displayindex'] = finalDimDisplay;
        });
    }

    // ### Handle category reordering ###
    var anyCatsReordered = false;
    if(d.dragCategoryDisplayInd !== null) {
        var finalDragCategoryDisplayInds = d.model.categories.map(function(c) {
            return c.displayInd;
        });

        anyCatsReordered = d.initialDragCategoryDisplayInds.some(function(initCatDisplay, catInd) {
            return initCatDisplay !== finalDragCategoryDisplayInds[catInd];
        });

        if(anyCatsReordered) {
            // Sort a shallow copy of the category models by display index
            var sortedCategoryModels = d.model.categories.slice().sort(
                function(a, b) { return a.displayInd - b.displayInd; });

            // Get new categoryarray and ticktext values
            var newCategoryArray = sortedCategoryModels.map(function(v) { return v.categoryValue; });
            var newCategoryLabels = sortedCategoryModels.map(function(v) { return v.categoryLabel; });

            restyleData['dimensions[' + d.model.containerInd + '].categoryarray'] = [newCategoryArray];
            restyleData['dimensions[' + d.model.containerInd + '].ticktext'] = [newCategoryLabels];
            restyleData['dimensions[' + d.model.containerInd + '].categoryorder'] = 'array';
        }
    }

    // Handle potential click event
    // ----------------------------
    if(d.parcatsViewModel.hoverinfoItems.indexOf('skip') === -1) {
        if(!d.dragHasMoved && d.potentialClickBand) {
            if(d.parcatsViewModel.hoveron === 'color') {
                emitPointsEventColorHovermode(d.potentialClickBand, 'plotly_click', d3.event.sourceEvent);
            } else {
                emitPointsEventCategoryHovermode(d.potentialClickBand, 'plotly_click', d3.event.sourceEvent);
            }
        }
    }

    // Nullify drag states
    // -------------------
    d.model.dragX = null;
    if(d.dragCategoryDisplayInd !== null) {
        var dragCategory = d.parcatsViewModel
            .dimensions[d.dragDimensionDisplayInd]
            .categories[d.dragCategoryDisplayInd];

        dragCategory.model.dragY = null;
        d.dragCategoryDisplayInd = null;
    }

    d.dragDimensionDisplayInd = null;
    d.parcatsViewModel.dragDimension = null;
    d.dragHasMoved = null;
    d.potentialClickBand = null;

    // Update view models
    // ------------------
    updateDimensionViewModels(d.parcatsViewModel);
    updatePathViewModels(d.parcatsViewModel);

    // Perform transition
    // ------------------
    var transition = d3.transition()
        .duration(300)
        .ease('cubic-in-out');

    transition
        .each(function() {
            updateSvgCategories(d.parcatsViewModel, true);
            updateSvgPaths(d.parcatsViewModel, true);
        })
        .each('end', function() {
            if(anyDimsReordered || anyCatsReordered) {
                // Perform restyle if the order of categories or dimensions changed
                Plotly.restyle(d.parcatsViewModel.graphDiv, restyleData, [traceInd]);
            }
        });
}
Handle dimension drag end
@param {DimensionViewModel} d