Skip to content

Commit

Permalink
SearchKit - Add drag-sortable weight functionality
Browse files Browse the repository at this point in the history
Drag-sortable weights are similar to in-place edit in that it uses the API
to update records in the table. In this case it updates the "weight" column when
the user drags a row into a different position.
  • Loading branch information
colemanw committed Nov 29, 2021
1 parent 2031271 commit 6bd1017
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,11 @@ private function applyFilter(array $fieldNames, $value) {
* @return array
*/
protected function getOrderByFromSort() {
// Drag-sortable tables have a forced order
if (!empty($this->display['settings']['draggable'])) {
return [$this->display['settings']['draggable'] => 'ASC'];
}

$defaultSort = $this->display['settings']['sort'] ?? [];
$currentSort = $this->sort;

Expand Down Expand Up @@ -745,9 +750,13 @@ protected function augmentSelectClause(&$apiParams): void {
}, $apiParams['select']);
$additions = [];
// Add primary key field if actions are enabled
if (!empty($this->display['settings']['actions'])) {
if (!empty($this->display['settings']['actions']) || !empty($this->display['settings']['draggable'])) {
$additions = CoreUtil::getInfoItem($this->savedSearch['api_entity'], 'primary_key');
}
// Add draggable column (typically "weight")
if (!empty($this->display['settings']['draggable'])) {
$additions[] = $this->display['settings']['draggable'];
}
// Add style conditions for the display
foreach ($this->getCssRulesSelect($this->display['settings']['cssRules'] ?? []) as $addition) {
$additions[] = $addition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
},
controller: function($scope, $timeout, searchMeta) {
var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
ctrl = this,
afforms;
ctrl = this;

this.isSuperAdmin = CRM.checkPerm('all CiviCRM permissions and ACLs');
this.aclBypassHelp = ts('Only users with "all CiviCRM permissions and ACLs" can disable permission checks.');
Expand Down Expand Up @@ -186,6 +185,10 @@
// Checks if a column contains a sortable value
// Must be a real sql expression (not a pseudo-field like `result_row_num`)
this.canBeSortable = function(col) {
// Column-header sorting is incompatible with draggable sorting
if (ctrl.display.settings.draggable) {
return false;
}
var expr = ctrl.getExprFromSelect(col.key),
info = searchMeta.parseExpr(expr),
arg = (info && info.args && _.findWhere(info.args, {type: 'field'})) || {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,23 @@
}
};

this.toggleDraggable = function() {
if (ctrl.display.settings.draggable) {
delete ctrl.display.settings.draggable;
} else {
ctrl.display.settings.sort = [];
ctrl.display.settings.draggable = searchMeta.getEntity(ctrl.apiEntity).order_by;
}
};

this.$onInit = function () {
if (!ctrl.display.settings) {
ctrl.display.settings = _.extend({}, CRM.crmSearchAdmin.defaultDisplay.settings, {columns: null});
}
// Displays created prior to 5.43 may not have this property
ctrl.display.settings.classes = ctrl.display.settings.classes || [];
// Table can be draggable if the main entity is a SortableEntity.
ctrl.canBeDraggable = _.includes(searchMeta.getEntity(ctrl.apiEntity).type, 'SortableEntity');
ctrl.parent.initColumns({label: true, sortable: true});
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<fieldset ng-include="'~/crmSearchAdmin/crmSearchAdminDisplaySort.html'"></fieldset>
<fieldset ng-if="!$ctrl.display.settings.draggable" ng-include="'~/crmSearchAdmin/crmSearchAdminDisplaySort.html'"></fieldset>
<fieldset>
<div ng-if="$ctrl.canBeDraggable" class="form-inline">
<div class="checkbox-inline form-control">
<label>
<input type="checkbox" ng-checked="!!$ctrl.display.settings.draggable" ng-click="$ctrl.toggleDraggable()">
<span>{{:: ts('Drag and drop sorting') }}</span>
</label>
</div>
</div>
<div class="form-inline">
<div class="checkbox-inline form-control">
<label>
Expand Down Expand Up @@ -44,7 +52,7 @@
<option value="text-right">{{:: ts('Right') }}</option>
</select>
</div>
<div class="form-inline" ng-if=":: $ctrl.parent.canBeSortable(col)">
<div class="form-inline" ng-if="$ctrl.parent.canBeSortable(col)">
<label title="{{:: ts('Allow user to click on header to sort table by this column') }}">
<input type="checkbox" ng-checked="col.sortable !== false" ng-click="col.sortable = col.sortable === false" >
{{:: ts('Sortable Header') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
sort: [],

isSortable: function(col) {
return col.type === 'field' && col.sortable !== false;
return !this.settings.draggable && col.type === 'field' && col.sortable !== false;
},

getSort: function(col) {
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/ang/crmSearchDisplayTable.ang.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
'ang/crmSearchDisplayTable',
],
'basePages' => ['civicrm/search', 'civicrm/admin/search'],
'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap'],
'requires' => ['crmSearchDisplay', 'crmUi', 'crmSearchTasks', 'ui.bootstrap', 'ui.sortable'],
'bundles' => ['bootstrap3'],
'exports' => [
'crm-search-display-table' => 'E',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,45 @@
afFieldset: '?^^afFieldset'
},
templateUrl: '~/crmSearchDisplayTable/crmSearchDisplayTable.html',
controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait) {
controller: function($scope, $element, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait, crmApi4, crmStatus) {
var ts = $scope.ts = CRM.ts('org.civicrm.search_kit'),
// Mix in traits to this controller
ctrl = angular.extend(this, searchDisplayBaseTrait, searchDisplayTasksTrait, searchDisplaySortableTrait);


this.$onInit = function() {
this.initializeDisplay($scope, $element);

if (ctrl.settings.draggable) {
ctrl.draggableOptions = {
containment: 'table',
direction: 'vertical',
handle: '.crm-draggable',
forcePlaceholderSize: true,
helper: function(e, ui) {
// Prevent table row width from changing during drag
ui.children().each(function() {
$(this).width($(this).width());
});
return ui;
},
stop: function(e, ui) {
$scope.$apply(function() {
var movedItem = ui.item.sortable.model,
oldPosition = ui.item.sortable.index,
newPosition = ctrl.results.indexOf(movedItem),
displacement = newPosition < oldPosition ? -1 : 1,
displacedItem = ctrl.results[newPosition - displacement],
weightColumn = ctrl.settings.draggable,
updateParams = {where: [['id', '=', movedItem.data.id]], values: {}};
if (newPosition > -1 && oldPosition !== newPosition) {
updateParams.values[weightColumn] = displacedItem.data[weightColumn];
ctrl.runSearch([[ctrl.apiEntity, 'update', updateParams]], {}, movedItem);
}
});
}
};
}
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
<table class="{{:: $ctrl.settings.classes.join(' ') }}">
<thead>
<tr>
<th class="crm-search-result-select" ng-if=":: $ctrl.settings.actions">
<input type="checkbox" ng-disabled="$ctrl.loading || !$ctrl.results.length" ng-checked="$ctrl.allRowsSelected" ng-click="$ctrl.selectAllRows()" >
<th class="crm-search-result-select" ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable">
<i ng-if=":: $ctrl.settings.draggable" class="crm-i fa-sort-amount-asc" title="{{:: ts('Drag columns to reposition') }}"></i>
<input type="checkbox" ng-if=":: $ctrl.settings.actions" ng-disabled="$ctrl.loading || !$ctrl.results.length" ng-checked="$ctrl.allRowsSelected" ng-click="$ctrl.selectAllRows()" >
</th>
<th ng-repeat="col in $ctrl.settings.columns" ng-click="$ctrl.setSort(col, $event)" title="{{:: $ctrl.isSortable(col) ? ts('Click to sort results (shift-click to sort by multiple).') : '' }}">
<th ng-repeat="col in $ctrl.settings.columns" ng-click="$ctrl.setSort(col, $event)" class="{{:: $ctrl.isSortable(col) ? 'crm-sortable-col' : ''}}" title="{{:: $ctrl.isSortable(col) ? ts('Click to sort results (shift-click to sort by multiple).') : '' }}">
<i ng-if=":: $ctrl.isSortable(col)" class="crm-i {{ $ctrl.getSort(col) }}"></i>
<span>{{:: col.label }}</span>
</th>
</tr>
</thead>
<tbody ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTable' + ($ctrl.loading ? 'Loading' : 'Body') + '.html'"></tbody>
<tbody ng-if="$ctrl.loading" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableLoading.html'"></tbody>
<tbody ng-if="!$ctrl.loading && !$ctrl.settings.draggable" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableBody.html'"></tbody>
<tbody ng-if="!$ctrl.loading && $ctrl.settings.draggable" ng-include="'~/crmSearchDisplayTable/crmSearchDisplayTableBody.html'" ui-sortable="$ctrl.draggableOptions" ng-model="$ctrl.results"></tbody>
</table>
<div ng-include="'~/crmSearchDisplay/Pager.html'"></div>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<tr ng-repeat="(rowIndex, row) in $ctrl.results">
<td ng-if=":: $ctrl.settings.actions" class="{{:: row.cssClass }}">
<input type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row)" ng-disabled="!!$ctrl.loadingAllRows">
<td ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable" class="{{:: row.cssClass }}">
<span ng-if=":: $ctrl.settings.draggable" class="crm-draggable" title="{{:: ts('Drag to reposition') }}">
<i class="crm-i fa-arrows-v"></i>
</span>
<input ng-if=":: $ctrl.settings.actions" type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row)" ng-disabled="!!$ctrl.loadingAllRows">
</td>
<td ng-repeat="(colIndex, colData) in row.columns" ng-include="'~/crmSearchDisplay/colType/' + $ctrl.settings.columns[colIndex].type + '.html'" title="{{:: colData.title }}" class="{{:: row.cssClass }} {{:: colData.cssClass }}">
</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- Placeholder table rows shown during ajax loading -->
<tr ng-repeat="num in [1,2,3,4,5] track by $index">
<td ng-if=":: $ctrl.settings.actions">
<input type="checkbox" disabled>
<td ng-if=":: $ctrl.settings.actions || $ctrl.settings.draggable">
<input ng-if=":: $ctrl.settings.actions" type="checkbox" disabled>
</td>
<td ng-repeat="col in $ctrl.settings.columns">
<div class="crm-search-loading-placeholder"></div>
Expand Down
2 changes: 1 addition & 1 deletion ext/search_kit/css/crmSearchDisplay.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Sortable headers */
#bootstrap-theme .crm-search-display th[ng-click] {
#bootstrap-theme .crm-search-display th.crm-sortable-col {
cursor: pointer;
}
#bootstrap-theme .crm-search-display th i.fa-sort-desc,
Expand Down

0 comments on commit 6bd1017

Please sign in to comment.