SearchKit - Allow searching by range
authorColeman Watts <coleman@civicrm.org>
Mon, 7 Mar 2022 07:31:20 +0000 (02:31 -0500)
committerColeman Watts <coleman@civicrm.org>
Wed, 9 Mar 2022 19:00:01 +0000 (14:00 -0500)
ext/search_kit/ang/crmSearchDisplayTable/crmSearchDisplayTableBody.html
ext/search_kit/ang/crmSearchTasks/traits/searchDisplayTasksTrait.service.js

index e1310ae63b39c3c626bd919470a96978173025ff..8dcb6a27986264bd067195d47f82c14cc43a0b1a 100644 (file)
@@ -3,7 +3,7 @@
     <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">
+    <input ng-if=":: $ctrl.settings.actions" type="checkbox" ng-checked="$ctrl.isRowSelected(row)" ng-click="$ctrl.selectRow(row, $event)" 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>
index 17c02d5d08283f8f96582e4740b5294431b40c70..c93167a8977ae177b5c1976149179980a2176c54 100644 (file)
       },
 
       // Toggle row selection
-      selectRow: function(row) {
-        var index = this.selectedRows.indexOf(row.key);
+      selectRow: function(row, event) {
+        var ctrl = this,
+          index = ctrl.selectedRows.indexOf(row.key);
+
+        // See if any boxes are checked above/below this one
+        function checkRange(allRows, checkboxPosition, dir) {
+          for (var row = checkboxPosition; row >= 0 && row <= allRows.length; row += dir) {
+            if (ctrl.selectedRows.indexOf(allRows[row]) > -1) {
+              return row;
+            }
+          }
+        }
+
+        // Check a bunch of boxes
+        function selectRange(allRows, start, end) {
+          for (var row = start; row <= end; ++row) {
+            ctrl.selectedRows.push(allRows[row]);
+          }
+        }
+
         if (index < 0) {
-          this.selectedRows.push(row.key);
-          this.allRowsSelected = (this.rowCount === this.selectedRows.length);
+          // Shift-click - select range between clicked checkbox and the nearest selected row
+          if (event.shiftKey && ctrl.selectedRows.length) {
+            var allRows = _.pluck(ctrl.results, 'key'),
+              checkboxPosition = allRows.indexOf(row.key);
+
+            var nearestBefore = checkRange(allRows, checkboxPosition, -1),
+              nearestAfter = checkRange(allRows, checkboxPosition, 1);
+
+            // Select range between clicked box and the previous/next checked box
+            // In the ambiguous situation where there are checked boxes both above AND below the clicked box,
+            // choose the direction of the box which was most recently clicked.
+            if (nearestAfter !== undefined && (nearestBefore === undefined || nearestAfter === allRows.indexOf(_.last(ctrl.selectedRows)))) {
+              selectRange(allRows, checkboxPosition + 1, nearestAfter - 1);
+            } else if (nearestBefore !== undefined && (nearestAfter === undefined || nearestBefore === allRows.indexOf(_.last(ctrl.selectedRows)))) {
+              selectRange(allRows, nearestBefore + 1, checkboxPosition -1);
+            }
+          }
+          ctrl.selectedRows.push(row.key);
+          ctrl.allRowsSelected = (ctrl.rowCount === ctrl.selectedRows.length);
         } else {
-          this.allRowsSelected = false;
-          this.selectedRows.splice(index, 1);
+          ctrl.allRowsSelected = false;
+          ctrl.selectedRows.splice(index, 1);
         }
       },