UNPKG

28.1 kBJavaScriptView Raw
1/***
2 * A control to add a Grid Menu (hambuger menu on top-right of the grid)
3 *
4 * USAGE:
5 *
6 * Add the slick.gridmenu.(js|css) files and register it with the grid.
7 *
8 * To specify a menu in a column header, extend the column definition like so:
9 * var gridMenuControl = new Slick.Controls.GridMenu(columns, grid, options);
10 *
11 * Available grid options, by defining a gridMenu object:
12 *
13 * var options = {
14 * enableCellNavigation: true,
15 * gridMenu: {
16 * customTitle: "Custom Menus", // default to empty string
17 * columnTitle: "Columns", // default to empty string
18 * iconImage: "../images/drag-handle.png", // this is the Grid Menu icon (hamburger icon)
19 * iconCssClass: "fa fa-bars", // you can provide iconImage OR iconCssClass
20 * leaveOpen: false, // do we want to leave the Grid Menu open after a command execution? (false by default)
21 * menuWidth: 18, // width (icon) that will be use to resize the column header container (18 by default)
22 * contentMinWidth: 0, // defaults to 0 (auto), minimum width of grid menu content (command, column list)
23 * marginBottom: 15, // defaults to 15, margin to use at the bottom of the grid when using max-height (default)
24 * resizeOnShowHeaderRow: false, // false by default
25 * useClickToRepositionMenu: true, // true by default
26 *
27 * // the last 2 checkboxes titles
28 * hideForceFitButton: false, // show/hide checkbox near the end "Force Fit Columns"
29 * hideSyncResizeButton: false, // show/hide checkbox near the end "Synchronous Resize"
30 * forceFitTitle: "Force fit columns", // default to "Force fit columns"
31 * syncResizeTitle: "Synchronous resize", // default to "Synchronous resize"
32 *
33 * customItems: [
34 * {
35 * // custom menu item options
36 * },
37 * {
38 * // custom menu item options
39 * }
40 * ]
41 * }
42 * };
43 *
44 *
45 * Available menu options:
46 * hideForceFitButton: Hide the "Force fit columns" button (defaults to false)
47 * hideSyncResizeButton: Hide the "Synchronous resize" button (defaults to false)
48 * forceFitTitle: Text of the title "Force fit columns"
49 * contentMinWidth: minimum width of grid menu content (command, column list), defaults to 0 (auto)
50 * height: Height of the Grid Menu content, when provided it will be used instead of the max-height (defaults to undefined)
51 * menuWidth: Grid menu button width (defaults to 18)
52 * resizeOnShowHeaderRow: Do we want to resize on the show header row event
53 * syncResizeTitle: Text of the title "Synchronous resize"
54 * useClickToRepositionMenu: Use the Click offset to reposition the Grid Menu (defaults to true), when set to False it will use the icon offset to reposition the grid menu
55 * menuUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling the menu from being usable (must be combined with a custom formatter)
56 * marginBottom: Margin to use at the bottom of the grid menu, only in effect when height is undefined (defaults to 15)
57 *
58 * Available custom menu item options:
59 * action: Optionally define a callback function that gets executed when item is chosen (and/or use the onCommand event)
60 * title: Menu item text.
61 * divider: Whether the current item is a divider, not an actual command.
62 * disabled: Whether the item/command is disabled.
63 * hidden: Whether the item/command is hidden.
64 * tooltip: Item tooltip.
65 * command: A command identifier to be passed to the onCommand event handlers.
66 * cssClass: A CSS class to be added to the menu item container.
67 * iconCssClass: A CSS class to be added to the menu item icon.
68 * iconImage: A url to the icon image.
69 * textCssClass: A CSS class to be added to the menu item text.
70 * itemVisibilityOverride: Callback method that user can override the default behavior of showing/hiding an item from the list
71 * itemUsabilityOverride: Callback method that user can override the default behavior of enabling/disabling an item from the list
72 *
73 *
74 * The plugin exposes the following events:
75 *
76 * onAfterMenuShow: Fired after the menu is shown. You can customize the menu or dismiss it by returning false.
77 * * ONLY works with a jQuery event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)
78 * Event args:
79 * grid: Reference to the grid.
80 * column: Column definition.
81 * menu: Menu options. Note that you can change the menu items here.
82 *
83 * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.
84 * * ONLY works with a jQuery event (as per slick.core code), so we cannot notify when it's a button event (when grid menu is attached to an external button, not the hamburger menu)
85 * Event args:
86 * grid: Reference to the grid.
87 * column: Column definition.
88 * menu: Menu options. Note that you can change the menu items here.
89 *
90 * onMenuClose: Fired when the menu is closing.
91 * Event args:
92 * grid: Reference to the grid.
93 * column: Column definition.
94 * menu: Menu options. Note that you can change the menu items here.
95 *
96 * onCommand: Fired on menu item click for buttons with 'command' specified.
97 * Event args:
98 * grid: Reference to the grid.
99 * column: Column definition.
100 * command: Button command identified.
101 * button: Button options. Note that you can change the button options in your
102 * event handler, and the column header will be automatically updated to
103 * reflect them. This is useful if you want to implement something like a
104 * toggle button.
105 *
106 *
107 * @param options {Object} Options:
108 * buttonCssClass: an extra CSS class to add to the menu button
109 * buttonImage: a url to the menu button image (default '../images/down.gif')
110 * @class Slick.Controls.GridMenu
111 * @constructor
112 */
113
114'use strict';
115
116(function ($) {
117 // register namespace
118 $.extend(true, window, {
119 "Slick": {
120 "Controls": {
121 "GridMenu": SlickGridMenu
122 }
123 }
124 });
125
126 function SlickGridMenu(columns, grid, options) {
127 var _grid = grid;
128 var _gridOptions;
129 var _gridUid = (grid && grid.getUID) ? grid.getUID() : '';
130 var _isMenuOpen = false;
131 var _options = options;
132 var _self = this;
133 var $customTitleElm;
134 var $columnTitleElm;
135 var $customMenu;
136 var $header;
137 var $list;
138 var $button;
139 var $menu;
140 var columnCheckboxes;
141 var _defaults = {
142 hideForceFitButton: false,
143 hideSyncResizeButton: false,
144 forceFitTitle: "Force fit columns",
145 marginBottom: 15,
146 menuWidth: 18,
147 contentMinWidth: 0,
148 resizeOnShowHeaderRow: false,
149 syncResizeTitle: "Synchronous resize",
150 useClickToRepositionMenu: true,
151 headerColumnValueExtractor: function (columnDef) {
152 return columnDef.name;
153 }
154 };
155
156 // when a grid changes from a regular grid to a frozen grid, we need to destroy & recreate the grid menu
157 // we do this change because the Grid Menu is on the left container on a regular grid but is on the right container on a frozen grid
158 grid.onSetOptions.subscribe(function (e, args) {
159 if (args && args.optionsBefore && args.optionsAfter) {
160 var switchedFromRegularToFrozen = args.optionsBefore.frozenColumn >= 0 && args.optionsAfter.frozenColumn === -1;
161 var switchedFromFrozenToRegular = args.optionsBefore.frozenColumn === -1 && args.optionsAfter.frozenColumn >= 0;
162 if (switchedFromRegularToFrozen || switchedFromFrozenToRegular) {
163 recreateGridMenu();
164 }
165 }
166 });
167
168 function init(grid) {
169 _gridOptions = grid.getOptions();
170 createGridMenu();
171
172 // subscribe to the grid, when it's destroyed, we should also destroy the Grid Menu
173 grid.onBeforeDestroy.subscribe(destroy);
174 }
175
176 function setOptions(newOptions) {
177 options = $.extend({}, options, newOptions);
178 }
179
180 function createGridMenu() {
181 var gridMenuWidth = (_options.gridMenu && _options.gridMenu.menuWidth) || _defaults.menuWidth;
182 if (_gridOptions && _gridOptions.hasOwnProperty('frozenColumn') && _gridOptions.frozenColumn >= 0) {
183 $header = $('.' + _gridUid + ' .slick-header-right');
184 } else {
185 $header = $('.' + _gridUid + ' .slick-header-left');
186 }
187 $header.attr('style', 'width: calc(100% - ' + gridMenuWidth + 'px)');
188
189 // if header row is enabled, we need to resize it's width also
190 var enableResizeHeaderRow = (_options.gridMenu && _options.gridMenu.resizeOnShowHeaderRow != undefined) ? _options.gridMenu.resizeOnShowHeaderRow : _defaults.resizeOnShowHeaderRow;
191 if (enableResizeHeaderRow && _options.showHeaderRow) {
192 $('.' + _gridUid + '.slick-headerrow').attr('style', 'width: calc(100% - ' + gridMenuWidth + 'px)');
193 }
194
195 $button = $('<button class="slick-gridmenu-button"/>');
196 if (_options.gridMenu && _options.gridMenu.iconCssClass) {
197 $button.addClass(_options.gridMenu.iconCssClass);
198 } else {
199 var iconImage = (_options.gridMenu && _options.gridMenu.iconImage) ? _options.gridMenu.iconImage : "../images/drag-handle.png";
200 $('<img src="' + iconImage + '"/>').appendTo($button);
201 }
202 $button.insertBefore($header);
203
204 $menu = $('<div class="slick-gridmenu ' + _gridUid + '" style="display: none" />').appendTo(document.body);
205 $('<button type="button" class="close" data-dismiss="slick-gridmenu" aria-label="Close"><span class="close" aria-hidden="true">&times;</span></button>').appendTo($menu);
206
207 $customMenu = $('<div class="slick-gridmenu-custom" />');
208 $customMenu.appendTo($menu);
209
210 populateCustomMenus(_options, $customMenu);
211 populateColumnPicker();
212
213 // Hide the menu on outside click.
214 $(document.body).on("mousedown." + _gridUid, handleBodyMouseDown);
215
216 // destroy the picker if user leaves the page
217 $(window).on("beforeunload", destroy);
218
219 // add on click handler for the Grid Menu itself
220 $button.on("click." + _gridUid, showGridMenu);
221 }
222
223 /** Destroy the plugin by unsubscribing every events & also delete the menu DOM elements */
224 function destroy() {
225 _self.onAfterMenuShow.unsubscribe();
226 _self.onBeforeMenuShow.unsubscribe();
227 _self.onMenuClose.unsubscribe();
228 _self.onCommand.unsubscribe();
229 _self.onColumnsChanged.unsubscribe();
230 _grid.onColumnsReordered.unsubscribe(updateColumnOrder);
231 _grid.onBeforeDestroy.unsubscribe();
232 _grid.onSetOptions.unsubscribe();
233 $("div.slick-gridmenu." + _gridUid).remove();
234 deleteMenu();
235 $(window).off("beforeunload");
236 }
237
238 /** Delete the menu DOM element but without unsubscribing any events */
239 function deleteMenu() {
240 $(document.body).off("mousedown." + _gridUid, handleBodyMouseDown);
241 $("div.slick-gridmenu." + _gridUid).hide();
242 if ($button) {
243 $button.remove();
244 }
245 if ($menu) {
246 $menu.remove();
247 }
248 if ($customMenu) {
249 $customMenu.remove();
250 }
251 if ($header) {
252 $header.attr('style', 'width: 100%'); // put back original width
253 }
254 $customTitleElm = null;
255 $columnTitleElm = null;
256 $customMenu = null;
257 $header = null;
258 $list = null;
259 $button = null;
260 $menu = null;
261 }
262
263 function populateCustomMenus(options, $customMenu) {
264 // Construct the custom menu items.
265 if (!options.gridMenu || !options.gridMenu.customItems) {
266 return;
267 }
268
269 // user could pass a title on top of the custom section
270 if (_options.gridMenu && _options.gridMenu.customTitle) {
271 $customTitleElm = $('<div class="title"/>').append(_options.gridMenu.customTitle);
272 $customTitleElm.appendTo($customMenu);
273 }
274
275 for (var i = 0, ln = options.gridMenu.customItems.length; i < ln; i++) {
276 var item = options.gridMenu.customItems[i];
277 var callbackArgs = {
278 "grid": _grid,
279 "menu": $menu,
280 "columns": columns,
281 "visibleColumns": getVisibleColumns()
282 };
283
284 // run each override functions to know if the item is visible and usable
285 var isItemVisible = runOverrideFunctionWhenExists(item.itemVisibilityOverride, callbackArgs);
286 var isItemUsable = runOverrideFunctionWhenExists(item.itemUsabilityOverride, callbackArgs);
287
288 // if the result is not visible then there's no need to go further
289 if (!isItemVisible) {
290 continue;
291 }
292
293 // when the override is defined, we need to use its result to update the disabled property
294 // so that "handleMenuItemCommandClick" has the correct flag and won't trigger a command clicked event
295 if (Object.prototype.hasOwnProperty.call(item, "itemUsabilityOverride")) {
296 item.disabled = isItemUsable ? false : true;
297 }
298
299 var $li = $("<div class='slick-gridmenu-item'></div>")
300 .data("command", item.command || '')
301 .data("item", item)
302 .on("click", handleMenuItemClick)
303 .appendTo($customMenu);
304
305 if (item.divider || item === "divider") {
306 $li.addClass("slick-gridmenu-item-divider");
307 continue;
308 }
309 if (item.disabled) {
310 $li.addClass("slick-gridmenu-item-disabled");
311 }
312
313 if (item.hidden) {
314 $li.addClass("slick-header-menuitem-hidden");
315 }
316
317 if (item.cssClass) {
318 $li.addClass(item.cssClass);
319 }
320
321 if (item.tooltip) {
322 $li.attr("title", item.tooltip);
323 }
324
325 var $icon = $("<div class='slick-gridmenu-icon'></div>")
326 .appendTo($li);
327
328 if (item.iconCssClass) {
329 $icon.addClass(item.iconCssClass);
330 }
331
332 if (item.iconImage) {
333 $icon.css("background-image", "url(" + item.iconImage + ")");
334 }
335
336 var $text = $("<span class='slick-gridmenu-content'></span>")
337 .text(item.title)
338 .appendTo($li);
339
340 if (item.textCssClass) {
341 $text.addClass(item.textCssClass);
342 }
343 $icon = null;
344 $li = null;
345 $text = null;
346 }
347 }
348
349 /** Build the column picker, the code comes almost untouched from the file "slick.columnpicker.js" */
350 function populateColumnPicker() {
351 _grid.onColumnsReordered.subscribe(updateColumnOrder);
352 _options = $.extend({}, _defaults, _options);
353
354 // user could pass a title on top of the columns list
355 if (_options.gridMenu && _options.gridMenu.columnTitle) {
356 $columnTitleElm = $('<div class="title"/>').append(_options.gridMenu.columnTitle);
357 $columnTitleElm.appendTo($menu);
358 }
359
360 $menu.on("click", updateColumn);
361 $list = $('<span class="slick-gridmenu-list" />');
362 }
363
364 /** Delete and then Recreate the Grid Menu (for example when we switch from regular to a frozen grid) */
365 function recreateGridMenu() {
366 deleteMenu();
367 init(_grid);
368 }
369
370 function showGridMenu(e) {
371 e.preventDefault();
372
373 // empty both the picker list & the command list
374 $list.empty();
375 $customMenu.empty();
376
377 populateCustomMenus(_options, $customMenu);
378 updateColumnOrder();
379 columnCheckboxes = [];
380
381 var callbackArgs = {
382 "grid": _grid,
383 "menu": $menu,
384 "allColumns": columns,
385 "visibleColumns": getVisibleColumns()
386 };
387
388 // run the override function (when defined), if the result is false it won't go further
389 if (_options && _options.gridMenu && !runOverrideFunctionWhenExists(_options.gridMenu.menuUsabilityOverride, callbackArgs)) {
390 return;
391 }
392
393 // notify of the onBeforeMenuShow only works when it's a jQuery event (as per slick.core code)
394 // this mean that we cannot notify when the grid menu is attach to a button event
395 if (typeof e.isPropagationStopped === "function") {
396 if (_self.onBeforeMenuShow.notify(callbackArgs, e, _self) == false) {
397 return;
398 }
399 }
400
401 var $li, $input, columnId, columnLabel, excludeCssClass;
402 for (var i = 0; i < columns.length; i++) {
403 columnId = columns[i].id;
404 excludeCssClass = columns[i].excludeFromGridMenu ? "hidden" : "";
405 $li = $('<li class="' + excludeCssClass + '" />').appendTo($list);
406
407 $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-" + columnId + "' />").data("column-id", columns[i].id).appendTo($li);
408 columnCheckboxes.push($input);
409
410 if (_grid.getColumnIndex(columns[i].id) != null) {
411 $input.attr("checked", "checked");
412 }
413
414 // get the column label from the picker value extractor (user can optionally provide a custom extractor)
415 if (_options && _options.gridMenu && _options.gridMenu.headerColumnValueExtractor) {
416 columnLabel = _options.gridMenu.headerColumnValueExtractor(columns[i], _gridOptions);
417 } else {
418 columnLabel = _defaults.headerColumnValueExtractor(columns[i], _gridOptions);
419 }
420
421 $("<label for='" + _gridUid + "-gridmenu-colpicker-" + columnId + "' />")
422 .html(columnLabel)
423 .appendTo($li);
424 }
425
426 if (_options.gridMenu && (!_options.gridMenu.hideForceFitButton || !_options.gridMenu.hideSyncResizeButton)) {
427 $("<hr/>").appendTo($list);
428 }
429
430 if (!(_options.gridMenu && _options.gridMenu.hideForceFitButton)) {
431 var forceFitTitle = (_options.gridMenu && _options.gridMenu.forceFitTitle) || _defaults.forceFitTitle;
432 $li = $("<li />").appendTo($list);
433 $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-forcefit' />").data("option", "autoresize").appendTo($li);
434 $("<label for='" + _gridUid + "-gridmenu-colpicker-forcefit' />").text(forceFitTitle).appendTo($li);
435
436 if (_grid.getOptions().forceFitColumns) {
437 $input.attr("checked", "checked");
438 }
439 }
440
441 if (!(_options.gridMenu && _options.gridMenu.hideSyncResizeButton)) {
442 var syncResizeTitle = (_options.gridMenu && _options.gridMenu.syncResizeTitle) || _defaults.syncResizeTitle;
443 $li = $("<li />").appendTo($list);
444 $input = $("<input type='checkbox' id='" + _gridUid + "-gridmenu-colpicker-syncresize' />").data("option", "syncresize").appendTo($li);
445 $("<label for='" + _gridUid + "-gridmenu-colpicker-syncresize' />").text(syncResizeTitle).appendTo($li);
446
447 if (_grid.getOptions().syncColumnCellResize) {
448 $input.attr("checked", "checked");
449 }
450 }
451
452 var menuIconOffset = $(e.target).prop('nodeName') == "button" ? $(e.target).offset() : $(e.target).parent("button").offset(); // get button offset position
453 if (!menuIconOffset) {
454 menuIconOffset = $(e.target).offset(); // external grid menu might fall in this last case
455 }
456 var menuWidth = $menu.width();
457 var useClickToRepositionMenu = (_options.gridMenu && _options.gridMenu.useClickToRepositionMenu !== undefined) ? _options.gridMenu.useClickToRepositionMenu : _defaults.useClickToRepositionMenu;
458 var gridMenuIconWidth = (_options.gridMenu && _options.gridMenu.menuWidth) || _defaults.menuWidth;
459 var contentMinWidth = (_options.gridMenu && _options.gridMenu.contentMinWidth) ? _options.gridMenu.contentMinWidth : _defaults.contentMinWidth;
460 var currentMenuWidth = (contentMinWidth > menuWidth) ? contentMinWidth : (menuWidth + gridMenuIconWidth);
461 var nextPositionTop = (useClickToRepositionMenu && e.pageY > 0) ? e.pageY : menuIconOffset.top + 10;
462 var nextPositionLeft = (useClickToRepositionMenu && e.pageX > 0) ? e.pageX : menuIconOffset.left + 10;
463 var menuMarginBottom = (_options.gridMenu && _options.gridMenu.marginBottom !== undefined) ? _options.gridMenu.marginBottom : _defaults.marginBottom;
464
465 $menu
466 .css("top", nextPositionTop + 10)
467 .css("left", nextPositionLeft - currentMenuWidth + 10);
468
469 if (contentMinWidth > 0) {
470 $menu.css("min-width", contentMinWidth);
471 }
472
473 // set "height" when defined OR ELSE use the "max-height" with available window size and optional margin bottom
474 if (_options.gridMenu && _options.gridMenu.height !== undefined) {
475 $menu.css("height", _options.gridMenu.height);
476 } else {
477 $menu.css("max-height", $(window).height() - e.clientY - menuMarginBottom);
478 }
479
480 $menu.show();
481 $list.appendTo($menu);
482 _isMenuOpen = true;
483
484 if (typeof e.isPropagationStopped === "function") {
485 if (_self.onAfterMenuShow.notify(callbackArgs, e, _self) == false) {
486 return;
487 }
488 }
489 $input = null;
490 }
491
492 function handleBodyMouseDown(e) {
493 if (($menu && $menu[0] != e.target && !$.contains($menu[0], e.target) && _isMenuOpen) || e.target.className == "close") {
494 hideMenu(e);
495 }
496 }
497
498 function handleMenuItemClick(e) {
499 var command = $(this).data("command");
500 var item = $(this).data("item");
501
502 if (item.disabled || item.divider || item === "divider") {
503 return;
504 }
505
506 if (command != null && command != '') {
507 var callbackArgs = {
508 "grid": _grid,
509 "command": command,
510 "item": item,
511 "allColumns": columns,
512 "visibleColumns": getVisibleColumns()
513 };
514 _self.onCommand.notify(callbackArgs, e, _self);
515
516 // execute action callback when defined
517 if (typeof item.action === "function") {
518 item.action.call(this, e, callbackArgs);
519 }
520 }
521
522 // does the user want to leave open the Grid Menu after executing a command?
523 var leaveOpen = (_options.gridMenu && _options.gridMenu.leaveOpen) ? true : false;
524 if (!leaveOpen && !e.isDefaultPrevented()) {
525 hideMenu(e);
526 }
527
528 // Stop propagation so that it doesn't register as a header click event.
529 e.preventDefault();
530 e.stopPropagation();
531 }
532
533 function hideMenu(e) {
534 if ($menu) {
535 $menu.hide();
536 _isMenuOpen = false;
537
538 var callbackArgs = {
539 "grid": _grid,
540 "menu": $menu,
541 "allColumns": columns,
542 "visibleColumns": getVisibleColumns()
543 };
544 if (_self.onMenuClose.notify(callbackArgs, e, _self) == false) {
545 return;
546 }
547 }
548 }
549
550 /** Update the Titles of each sections (command, customTitle, ...) */
551 function updateAllTitles(gridMenuOptions) {
552 if ($customTitleElm && $customTitleElm.text) {
553 $customTitleElm.text(gridMenuOptions.customTitle);
554 }
555 if ($columnTitleElm && $columnTitleElm.text) {
556 $columnTitleElm.text(gridMenuOptions.columnTitle);
557 }
558 }
559
560 function updateColumnOrder() {
561 // Because columns can be reordered, we have to update the `columns`
562 // to reflect the new order, however we can't just take `grid.getColumns()`,
563 // as it does not include columns currently hidden by the picker.
564 // We create a new `columns` structure by leaving currently-hidden
565 // columns in their original ordinal position and interleaving the results
566 // of the current column sort.
567 var current = _grid.getColumns().slice(0);
568 var ordered = new Array(columns.length);
569 for (var i = 0; i < ordered.length; i++) {
570 if (_grid.getColumnIndex(columns[i].id) === undefined) {
571 // If the column doesn't return a value from getColumnIndex,
572 // it is hidden. Leave it in this position.
573 ordered[i] = columns[i];
574 } else {
575 // Otherwise, grab the next visible column.
576 ordered[i] = current.shift();
577 }
578 }
579 columns = ordered;
580 }
581
582 function updateColumn(e) {
583 if ($(e.target).data("option") == "autoresize") {
584 // when calling setOptions, it will resize with ALL Columns (even the hidden ones)
585 // we can avoid this problem by keeping a reference to the visibleColumns before setOptions and then setColumns after
586 var previousVisibleColumns = getVisibleColumns();
587 var isChecked = e.target.checked;
588 _grid.setOptions({ forceFitColumns: isChecked });
589 _grid.setColumns(previousVisibleColumns);
590 return;
591 }
592
593 if ($(e.target).data("option") == "syncresize") {
594 if (e.target.checked) {
595 _grid.setOptions({ syncColumnCellResize: true });
596 } else {
597 _grid.setOptions({ syncColumnCellResize: false });
598 }
599 return;
600 }
601
602 if ($(e.target).is(":checkbox")) {
603 var isChecked = e.target.checked;
604 var columnId = $(e.target).data("column-id") || "";
605 var visibleColumns = [];
606 $.each(columnCheckboxes, function (i) {
607 if ($(this).is(":checked")) {
608 visibleColumns.push(columns[i]);
609 }
610 });
611
612 if (!visibleColumns.length) {
613 $(e.target).attr("checked", "checked");
614 return;
615 }
616
617 var callbackArgs = {
618 "columnId": columnId,
619 "showing": isChecked,
620 "grid": _grid,
621 "allColumns": columns,
622 "columns": visibleColumns
623 };
624 _grid.setColumns(visibleColumns);
625 _self.onColumnsChanged.notify(callbackArgs, e, _self);
626 }
627 }
628
629 init(_grid);
630
631 function getAllColumns() {
632 return columns;
633 }
634
635 /** visible columns, we can simply get them directly from the grid */
636 function getVisibleColumns() {
637 return _grid.getColumns();
638 }
639
640 /**
641 * Method that user can pass to override the default behavior.
642 * In order word, user can choose or an item is (usable/visible/enable) by providing his own logic.
643 * @param overrideFn: override function callback
644 * @param args: multiple arguments provided to the override (cell, row, columnDef, dataContext, grid)
645 */
646 function runOverrideFunctionWhenExists(overrideFn, args) {
647 if (typeof overrideFn === 'function') {
648 return overrideFn.call(this, args);
649 }
650 return true;
651 }
652
653 $.extend(this, {
654 "init": init,
655 "getAllColumns": getAllColumns,
656 "getVisibleColumns": getVisibleColumns,
657 "destroy": destroy,
658 "deleteMenu": deleteMenu,
659 "recreateGridMenu": recreateGridMenu,
660 "showGridMenu": showGridMenu,
661 "setOptions": setOptions,
662 "updateAllTitles": updateAllTitles,
663
664 "onAfterMenuShow": new Slick.Event(),
665 "onBeforeMenuShow": new Slick.Event(),
666 "onMenuClose": new Slick.Event(),
667 "onCommand": new Slick.Event(),
668 "onColumnsChanged": new Slick.Event()
669 });
670 }
671})(jQuery);