|  | 
|  | 1 | +/** | 
|  | 2 | + * @summary     pfEmpty for DataTables | 
|  | 3 | + * @description A collection of API methods providing functionality to hide and show elements when DataTables is empty. | 
|  | 4 | + * This ensures DataTables meets the Patternfly design pattern with a toolbar and empty state. | 
|  | 5 | + * | 
|  | 6 | + * When DataTable is redrawn and data length is zero, controls under the toolbar-pf-actions and toolbar-pf-results | 
|  | 7 | + * classes are disabled; including the filter drop down, filter input, action buttons, kebab, find, etc. (You may | 
|  | 8 | + * re-enable specific controls via the DataTables "draw.dt" event.) In addition, the DataTables empty table header and | 
|  | 9 | + * row are hidden while the empty state (i.e., blank slate) layout is shown. | 
|  | 10 | + * | 
|  | 11 | + * The toolbar and empty state layouts are expected to contain the classes as shown in the example below. | 
|  | 12 | + * | 
|  | 13 | + * Example: | 
|  | 14 | + * | 
|  | 15 | + * <!-- NOTE: Some configuration may be omitted for clarity --> | 
|  | 16 | + * <div class="row toolbar-pf table-view-pf-toolbar" id="toolbar1"> | 
|  | 17 | + *   <div class="col-sm-12"> | 
|  | 18 | + *     <form class="toolbar-pf-actions"> | 
|  | 19 | + *       ... | 
|  | 20 | + *     </form> | 
|  | 21 | + *     <div class="row toolbar-pf-results"> | 
|  | 22 | + *       ... | 
|  | 23 | + *     </div> | 
|  | 24 | + *   </div> | 
|  | 25 | + * </div> | 
|  | 26 | + * <table class="table table-striped table-bordered table-hover" id="table1"> | 
|  | 27 | + *   <thead> | 
|  | 28 | + *     <tr> | 
|  | 29 | + *       <th><input type="checkbox" name="selectAll"></th> | 
|  | 30 | + *       <th>Rendering Engine</th> | 
|  | 31 | + *       <th>Browser</th> | 
|  | 32 | + *     </tr> | 
|  | 33 | + *   </thead> | 
|  | 34 | + * </table> | 
|  | 35 | + * <div class="blank-slate-pf table-view-pf-empty hidden" id="emptyState1"> | 
|  | 36 | + *   <div class="blank-slate-pf-icon"> | 
|  | 37 | + *     <span class="pficon pficon pficon-add-circle-o"></span> | 
|  | 38 | + *   </div> | 
|  | 39 | + *   <h1>Empty State Title</h1> | 
|  | 40 | + *   ... | 
|  | 41 | + * </div> | 
|  | 42 | + * <script> | 
|  | 43 | + * // NOTE: Some properties may be omitted for clarity | 
|  | 44 | + * $(document).ready(function() { | 
|  | 45 | + *   var dt = $("#table1").DataTable({ | 
|  | 46 | + *     columns: [ | 
|  | 47 | + *       { data: null, ... }, | 
|  | 48 | + *       { data: "engine" }, | 
|  | 49 | + *       { data: "browser" } | 
|  | 50 | + *     ], | 
|  | 51 | + *     data: null, | 
|  | 52 | + *     dom: "t", | 
|  | 53 | + *     pfConfig: { | 
|  | 54 | + *       emptyStateSelector: "#{{include.emptyStateId}}", | 
|  | 55 | + *       ... | 
|  | 56 | + *       toolbarSelector: "#{{include.toolbarId}}" | 
|  | 57 | + *     } | 
|  | 58 | + *   }); | 
|  | 59 | + *   dt.table().pfEmpty.updateState(); // Optional API to force update | 
|  | 60 | + * }); | 
|  | 61 | + * </script> | 
|  | 62 | + * | 
|  | 63 | + * Note: This functionality requires the following Javascript library files to be loaded: | 
|  | 64 | + * | 
|  | 65 | + * https://cdn.datatables.net/select/1.2.0/js/dataTables.select.min.js | 
|  | 66 | + */ | 
|  | 67 | +(function (factory) { | 
|  | 68 | +  "use strict"; | 
|  | 69 | +  if (typeof define === "function" && define.amd ) { | 
|  | 70 | +    // AMD | 
|  | 71 | +    define (["jquery", "datatables.net"], function ($) { | 
|  | 72 | +      return factory ($, window, document); | 
|  | 73 | +    }); | 
|  | 74 | +  } else if (typeof exports === "object") { | 
|  | 75 | +    // CommonJS | 
|  | 76 | +    module.exports = function (root, $) { | 
|  | 77 | +      if (!root) { | 
|  | 78 | +        root = window; | 
|  | 79 | +      } | 
|  | 80 | +      if (!$ || !$.fn.dataTable) { | 
|  | 81 | +        $ = require("datatables.net")(root, $).$; | 
|  | 82 | +      } | 
|  | 83 | +      return factory($, root, root.document); | 
|  | 84 | +    }; | 
|  | 85 | +  } else { | 
|  | 86 | +    // Browser | 
|  | 87 | +    factory(jQuery, window, document); | 
|  | 88 | +  } | 
|  | 89 | +}(function ($, window, document, undefined) { | 
|  | 90 | +  "use strict"; | 
|  | 91 | +  var DataTable = $.fn.dataTable; | 
|  | 92 | +  var ACTIONS_SELECTOR = ".toolbar-pf-actions"; // Toolbar actions | 
|  | 93 | +  var RESULTS_SELECTOR = ".toolbar-pf-results"; // Toolbar results row | 
|  | 94 | + | 
|  | 95 | +  DataTable.pfEmpty = {}; | 
|  | 96 | + | 
|  | 97 | +  /** | 
|  | 98 | +   * Initialize | 
|  | 99 | +   * | 
|  | 100 | +   * @param {DataTable.Api} dt DataTable | 
|  | 101 | +   * @private | 
|  | 102 | +   */ | 
|  | 103 | +  DataTable.pfEmpty.init = function (dt) { | 
|  | 104 | +    var ctx = dt.settings()[0]; | 
|  | 105 | +    var opts = (ctx.oInit.pfConfig) ? ctx.oInit.pfConfig : {}; | 
|  | 106 | + | 
|  | 107 | +    ctx._pfEmpty = {}; | 
|  | 108 | +    ctx._pfEmpty.emptyState = $(opts.emptyStateSelector); // Empty state (Blank slate) | 
|  | 109 | +    ctx._pfEmpty.isEmptyState = false; // Flag indicating DatTable entered an empty state | 
|  | 110 | +    ctx._pfEmpty.tbody = $("tbody", dt.table().container()); // Table body | 
|  | 111 | +    ctx._pfEmpty.thead = $("thead", dt.table().container()); // Table head | 
|  | 112 | +    ctx._pfEmpty.toolbarActions = $(ACTIONS_SELECTOR, opts.toolbarSelector); // Toolbar actions | 
|  | 113 | +    ctx._pfEmpty.toolbarResults = $(RESULTS_SELECTOR, opts.toolbarSelector); // Toolbar results row | 
|  | 114 | + | 
|  | 115 | +    // Update table on DataTables draw event | 
|  | 116 | +    dt.on("draw.dt", function () { | 
|  | 117 | +      updateState(dt); | 
|  | 118 | +    }); | 
|  | 119 | + | 
|  | 120 | +    // Initialize | 
|  | 121 | +    updateState(dt); | 
|  | 122 | +  }; | 
|  | 123 | + | 
|  | 124 | +  // Local functions | 
|  | 125 | + | 
|  | 126 | +  /** | 
|  | 127 | +   * Disable and hide elements when DataTables has no data | 
|  | 128 | +   * | 
|  | 129 | +   * @param {DataTable.Api} dt DataTable | 
|  | 130 | +   * @private | 
|  | 131 | +   */ | 
|  | 132 | +  function updateEmptyState (dt) { | 
|  | 133 | +    var ctx = dt.settings()[0]; | 
|  | 134 | + | 
|  | 135 | +    // Show blank slate | 
|  | 136 | +    if (ctx._pfEmpty.emptyState !== undefined && ctx._pfEmpty.emptyState.length !== 0) { | 
|  | 137 | +      ctx._pfEmpty.emptyState.removeClass("hidden"); | 
|  | 138 | +    } | 
|  | 139 | +    // Hide zero records message | 
|  | 140 | +    if (ctx._pfEmpty.tbody !== undefined && ctx._pfEmpty.tbody.length !== 0) { | 
|  | 141 | +      ctx._pfEmpty.tbody.addClass("hidden"); | 
|  | 142 | +    } | 
|  | 143 | +    // Hide column headers | 
|  | 144 | +    if (ctx._pfEmpty.thead !== undefined && ctx._pfEmpty.thead.length !== 0) { | 
|  | 145 | +      ctx._pfEmpty.thead.addClass("hidden"); | 
|  | 146 | +    } | 
|  | 147 | +    // Disable all buttons | 
|  | 148 | +    if (ctx._pfEmpty.toolbarActions !== undefined && ctx._pfEmpty.toolbarActions.length !== 0) { | 
|  | 149 | +      $("button", ctx._pfEmpty.toolbarActions).prop("disabled", true); | 
|  | 150 | +    } | 
|  | 151 | +    // Disable all inputs | 
|  | 152 | +    if (ctx._pfEmpty.toolbarActions !== undefined && ctx._pfEmpty.toolbarActions.length !== 0) { | 
|  | 153 | +      $("input", ctx._pfEmpty.toolbarActions).prop("disabled", true); | 
|  | 154 | +    } | 
|  | 155 | +    // Hide results container | 
|  | 156 | +    if (ctx._pfEmpty.toolbarResults !== undefined && ctx._pfEmpty.toolbarResults.length !== 0) { | 
|  | 157 | +      ctx._pfEmpty.toolbarResults.children().addClass("hidden"); | 
|  | 158 | +    } | 
|  | 159 | +    // Enable on empty | 
|  | 160 | +    if (ctx._pfEmpty.enableOnEmpty !== undefined) { | 
|  | 161 | +      $(ctx._pfEmpty.enableOnEmpty).prop("disabled", false); | 
|  | 162 | +    } | 
|  | 163 | +    // Enable on empty | 
|  | 164 | +    if (ctx._pfEmpty.enableOnEmpty !== undefined) { | 
|  | 165 | +      $(ctx._pfEmpty.enableOnEmpty).prop("disabled", false); | 
|  | 166 | +    } | 
|  | 167 | +  } | 
|  | 168 | + | 
|  | 169 | +  /** | 
|  | 170 | +   * Enable and show elements when DataTables has data | 
|  | 171 | +   * | 
|  | 172 | +   * @param {DataTable.Api} dt DataTable | 
|  | 173 | +   * @private | 
|  | 174 | +   */ | 
|  | 175 | +  function updateNonEmptyState (dt) { | 
|  | 176 | +    var ctx = dt.settings()[0]; | 
|  | 177 | + | 
|  | 178 | +    // Hide blank slate | 
|  | 179 | +    if (ctx._pfEmpty.emptyState !== undefined && ctx._pfEmpty.emptyState.length !== 0) { | 
|  | 180 | +      ctx._pfEmpty.emptyState.addClass("hidden"); | 
|  | 181 | +    } | 
|  | 182 | +    // Show table body | 
|  | 183 | +    if (ctx._pfEmpty.tbody !== undefined && ctx._pfEmpty.tbody.length !== 0) { | 
|  | 184 | +      ctx._pfEmpty.tbody.removeClass("hidden"); | 
|  | 185 | +    } | 
|  | 186 | +    // Show column headers | 
|  | 187 | +    if (ctx._pfEmpty.thead !== undefined && ctx._pfEmpty.thead.length !== 0) { | 
|  | 188 | +      ctx._pfEmpty.thead.removeClass("hidden"); | 
|  | 189 | +    } | 
|  | 190 | +    // Enable all buttons | 
|  | 191 | +    if (ctx._pfEmpty.toolbarActions !== undefined && ctx._pfEmpty.toolbarActions.length !== 0) { | 
|  | 192 | +      $("button", ctx._pfEmpty.toolbarActions).prop("disabled", false); | 
|  | 193 | +    } | 
|  | 194 | +    // Enable all inputs | 
|  | 195 | +    if (ctx._pfEmpty.toolbarActions !== undefined && ctx._pfEmpty.toolbarActions.length !== 0) { | 
|  | 196 | +      $("input", ctx._pfEmpty.toolbarActions).prop("disabled", false); | 
|  | 197 | +    } | 
|  | 198 | +    // Show results container | 
|  | 199 | +    if (ctx._pfEmpty.toolbarResults !== undefined && ctx._pfEmpty.toolbarResults.length !== 0) { | 
|  | 200 | +      ctx._pfEmpty.toolbarResults.children().removeClass("hidden"); | 
|  | 201 | +    } | 
|  | 202 | +  } | 
|  | 203 | + | 
|  | 204 | +  /** | 
|  | 205 | +   * Update elements upon empty DataTable state | 
|  | 206 | +   * | 
|  | 207 | +   * @param {DataTable.Api} dt DataTable | 
|  | 208 | +   * @private | 
|  | 209 | +   */ | 
|  | 210 | +  function updateState (dt) { | 
|  | 211 | +    var ctx = dt.settings()[0]; | 
|  | 212 | + | 
|  | 213 | +    // Don't enable or show elements unless DataTable was empty | 
|  | 214 | +    if (dt.data().length === 0) { | 
|  | 215 | +      ctx._pfEmpty.isEmptyState = true; | 
|  | 216 | +      updateEmptyState(dt); | 
|  | 217 | +    } else if (ctx._pfEmpty.isEmptyState === true) { | 
|  | 218 | +      ctx._pfEmpty.isEmptyState = false; | 
|  | 219 | +      updateNonEmptyState(dt); | 
|  | 220 | +    } | 
|  | 221 | +  } | 
|  | 222 | + | 
|  | 223 | +  // DataTables API | 
|  | 224 | + | 
|  | 225 | +  /** | 
|  | 226 | +   * Update state upon empty or non-empty DataTable | 
|  | 227 | +   * | 
|  | 228 | +   * Example: dt.table().pfEmpty.updateState(); | 
|  | 229 | +   */ | 
|  | 230 | +  DataTable.Api.register("pfEmpty.updateState()", function () { | 
|  | 231 | +    return this.iterator("table", function (ctx) { | 
|  | 232 | +      update(new DataTable.Api(ctx)); | 
|  | 233 | +    }); | 
|  | 234 | +  }); | 
|  | 235 | + | 
|  | 236 | +  // DataTables creation | 
|  | 237 | +  $(document).on("init.dt", function (e, ctx, json) { | 
|  | 238 | +    if (e.namespace !== "dt") { | 
|  | 239 | +      return; | 
|  | 240 | +    } | 
|  | 241 | +    DataTable.pfEmpty.init(new DataTable.Api(ctx)); | 
|  | 242 | +  }); | 
|  | 243 | +  return DataTable.pfEmpty; | 
|  | 244 | +})); | 
0 commit comments