1 | (function ($) {
|
2 |
|
3 | $.extend(true, window, {
|
4 | "Slick": {
|
5 | "Plugins": {
|
6 | "CellMenu": CellMenu
|
7 | }
|
8 | }
|
9 | });
|
10 |
|
11 | |
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | function CellMenu(optionProperties) {
|
133 | var _cellMenuProperties;
|
134 | var _currentCell = -1;
|
135 | var _currentRow = -1;
|
136 | var _grid;
|
137 | var _gridOptions;
|
138 | var _gridUid = "";
|
139 | var _handler = new Slick.EventHandler();
|
140 | var _self = this;
|
141 | var $commandTitleElm;
|
142 | var $optionTitleElm;
|
143 | var $menu;
|
144 |
|
145 | var _defaults = {
|
146 | autoAdjustDrop: true,
|
147 | autoAlignSide: true,
|
148 | autoAdjustDropOffset: 0,
|
149 | autoAlignSideOffset: 0,
|
150 | hideMenuOnScroll: true,
|
151 | maxHeight: "none",
|
152 | width: "auto",
|
153 | };
|
154 |
|
155 | function init(grid) {
|
156 | _grid = grid;
|
157 | _gridOptions = grid.getOptions();
|
158 | _cellMenuProperties = $.extend({}, _defaults, optionProperties);
|
159 | _gridUid = (grid && grid.getUID) ? grid.getUID() : "";
|
160 | _handler.subscribe(_grid.onClick, handleCellClick);
|
161 | if (_cellMenuProperties.hideMenuOnScroll) {
|
162 | _handler.subscribe(_grid.onScroll, destroyMenu);
|
163 | }
|
164 | }
|
165 |
|
166 | function setOptions(newOptions) {
|
167 | _cellMenuProperties = $.extend({}, _cellMenuProperties, newOptions);
|
168 | }
|
169 |
|
170 | function destroy() {
|
171 | _self.onAfterMenuShow.unsubscribe();
|
172 | _self.onBeforeMenuShow.unsubscribe();
|
173 | _self.onBeforeMenuClose.unsubscribe();
|
174 | _self.onCommand.unsubscribe();
|
175 | _self.onOptionSelected.unsubscribe();
|
176 | _handler.unsubscribeAll();
|
177 | if ($menu && $menu.remove) {
|
178 | $menu.remove();
|
179 | }
|
180 | $commandTitleElm = null;
|
181 | $optionTitleElm = null;
|
182 | $menu = null;
|
183 | }
|
184 |
|
185 | function createMenu(e) {
|
186 | var cell = _grid.getCellFromEvent(e);
|
187 | _currentCell = cell && cell.cell;
|
188 | _currentRow = cell && cell.row;
|
189 | var columnDef = _grid.getColumns()[_currentCell];
|
190 | var dataContext = _grid.getDataItem(_currentRow);
|
191 |
|
192 | var commandItems = _cellMenuProperties.commandItems || [];
|
193 | var optionItems = _cellMenuProperties.optionItems || [];
|
194 |
|
195 |
|
196 | if (!columnDef || !columnDef.cellMenu || (!commandItems.length && optionItems.length)) {
|
197 | return;
|
198 | }
|
199 |
|
200 |
|
201 | destroyMenu();
|
202 |
|
203 |
|
204 |
|
205 | if (_self.onBeforeMenuShow.notify({
|
206 | "cell": _currentCell,
|
207 | "row": _currentRow,
|
208 | "grid": _grid
|
209 | }, e, _self) == false) {
|
210 | return;
|
211 | }
|
212 |
|
213 |
|
214 | var maxHeight = isNaN(_cellMenuProperties.maxHeight) ? _cellMenuProperties.maxHeight : _cellMenuProperties.maxHeight + "px";
|
215 | var width = isNaN(_cellMenuProperties.width) ? _cellMenuProperties.width : _cellMenuProperties.width + "px";
|
216 | var menuStyle = "width: " + width + "; max-height: " + maxHeight;
|
217 | var menu = $('<div class="slick-cell-menu ' + _gridUid + '" style="' + menuStyle + '" />')
|
218 | .css("top", e.pageY + 5)
|
219 | .css("left", e.pageX)
|
220 | .css("display", "none");
|
221 |
|
222 | var closeButtonHtml = '<button type="button" class="close" data-dismiss="slick-cell-menu" aria-label="Close">'
|
223 | + '<span class="close" aria-hidden="true">×</span></button>';
|
224 |
|
225 |
|
226 | if (!_cellMenuProperties.hideOptionSection && optionItems.length > 0) {
|
227 | var $optionMenu = $('<div class="slick-cell-menu-option-list" />');
|
228 | if (!_cellMenuProperties.hideCloseButton) {
|
229 | $(closeButtonHtml).on("click", handleCloseButtonClicked).appendTo(menu);
|
230 | }
|
231 | $optionMenu.appendTo(menu);
|
232 | populateOptionItems(
|
233 | _cellMenuProperties,
|
234 | $optionMenu,
|
235 | optionItems,
|
236 | { cell: _currentCell, row: _currentRow, column: columnDef, dataContext: dataContext, grid: _grid }
|
237 | );
|
238 | }
|
239 |
|
240 |
|
241 | if (!_cellMenuProperties.hideCommandSection && commandItems.length > 0) {
|
242 | var $commandMenu = $('<div class="slick-cell-menu-command-list" />');
|
243 | if (!_cellMenuProperties.hideCloseButton && (optionItems.length === 0 || _cellMenuProperties.hideOptionSection)) {
|
244 | $(closeButtonHtml).on("click", handleCloseButtonClicked).appendTo(menu);
|
245 | }
|
246 | $commandMenu.appendTo(menu);
|
247 | populateCommandItems(
|
248 | _cellMenuProperties,
|
249 | $commandMenu,
|
250 | commandItems,
|
251 | { cell: _currentCell, row: _currentRow, column: columnDef, dataContext: dataContext, grid: _grid }
|
252 | );
|
253 | }
|
254 |
|
255 | menu.show();
|
256 | menu.appendTo("body");
|
257 |
|
258 | if (_self.onAfterMenuShow.notify({
|
259 | "cell": _currentCell,
|
260 | "row": _currentRow,
|
261 | "grid": _grid
|
262 | }, e, _self) == false) {
|
263 | return;
|
264 | }
|
265 |
|
266 | return menu;
|
267 | }
|
268 |
|
269 | function calculateAvailableSpaceBottom(element) {
|
270 | var windowHeight = $(window).innerHeight() || 0;
|
271 | var pageScroll = $(window).scrollTop() || 0;
|
272 | if (element && element.offset && element.length > 0) {
|
273 | var elementOffsetTop = element.offset().top;
|
274 | return windowHeight - (elementOffsetTop - pageScroll);
|
275 | }
|
276 | return 0;
|
277 | }
|
278 |
|
279 | function calculateAvailableSpaceTop(element) {
|
280 | var pageScroll = $(window).scrollTop() || 0;
|
281 | if (element && element.offset && element.length > 0) {
|
282 | var elementOffsetTop = element.offset().top;
|
283 | return elementOffsetTop - pageScroll;
|
284 | }
|
285 | return 0;
|
286 | }
|
287 |
|
288 | function handleCloseButtonClicked(e) {
|
289 | if(!e.isDefaultPrevented()) {
|
290 | destroyMenu(e);
|
291 | }
|
292 | }
|
293 |
|
294 | function destroyMenu(e, args) {
|
295 | $menu = $menu || $(".slick-cell-menu." + _gridUid);
|
296 |
|
297 | if ($menu && $menu.remove) {
|
298 | if ($menu.length > 0) {
|
299 | if (_self.onBeforeMenuClose.notify({
|
300 | "cell": args && args.cell,
|
301 | "row": args && args.row,
|
302 | "grid": _grid,
|
303 | "menu": $menu
|
304 | }, e, _self) == false) {
|
305 | return;
|
306 | }
|
307 | }
|
308 | $menu.remove();
|
309 | $menu = null;
|
310 | }
|
311 | }
|
312 |
|
313 | |
314 |
|
315 |
|
316 |
|
317 | function repositionMenu(e) {
|
318 | var $parent = $(e.target).closest(".slick-cell");
|
319 | var menuOffsetLeft = $parent ? $parent.offset().left : e.pageX;
|
320 | var menuOffsetTop = $parent ? $parent.offset().top : e.pageY;
|
321 | var parentCellWidth = $parent.outerWidth();
|
322 | var menuHeight = $menu.outerHeight() || 0;
|
323 | var menuWidth = $menu.outerWidth() || _cellMenuProperties.width || 0;
|
324 | var rowHeight = _gridOptions.rowHeight;
|
325 | var dropOffset = _cellMenuProperties.autoAdjustDropOffset;
|
326 | var sideOffset = _cellMenuProperties.autoAlignSideOffset;
|
327 |
|
328 |
|
329 |
|
330 | if (_cellMenuProperties.autoAdjustDrop) {
|
331 |
|
332 | var spaceBottom = calculateAvailableSpaceBottom($parent);
|
333 | var spaceTop = calculateAvailableSpaceTop($parent);
|
334 | var spaceBottomRemaining = spaceBottom + dropOffset - rowHeight;
|
335 | var spaceTopRemaining = spaceTop - dropOffset + rowHeight;
|
336 | var dropPosition = (spaceBottomRemaining < menuHeight && spaceTopRemaining > spaceBottomRemaining) ? 'top' : 'bottom';
|
337 | if (dropPosition === 'top') {
|
338 | $menu.removeClass("dropdown").addClass("dropup");
|
339 | menuOffsetTop = menuOffsetTop - menuHeight - dropOffset;
|
340 | } else {
|
341 | $menu.removeClass("dropup").addClass("dropdown");
|
342 | menuOffsetTop = menuOffsetTop + rowHeight + dropOffset;
|
343 | }
|
344 | }
|
345 |
|
346 |
|
347 |
|
348 |
|
349 | if (_cellMenuProperties.autoAlignSide) {
|
350 | var gridPos = _grid.getGridPosition();
|
351 | var dropSide = ((menuOffsetLeft + menuWidth) >= gridPos.width) ? 'left' : 'right';
|
352 | if (dropSide === 'left') {
|
353 | $menu.removeClass("dropright").addClass("dropleft");
|
354 | menuOffsetLeft = (menuOffsetLeft - (menuWidth - parentCellWidth) - sideOffset);
|
355 | } else {
|
356 | $menu.removeClass("dropleft").addClass("dropright");
|
357 | menuOffsetLeft = menuOffsetLeft + sideOffset;
|
358 | }
|
359 | }
|
360 |
|
361 |
|
362 | $menu.css("top", menuOffsetTop);
|
363 | $menu.css("left", menuOffsetLeft);
|
364 | }
|
365 |
|
366 | function handleCellClick(e, args) {
|
367 | var cell = _grid.getCellFromEvent(e);
|
368 | var dataContext = _grid.getDataItem(cell.row);
|
369 | var columnDef = _grid.getColumns()[cell.cell];
|
370 |
|
371 |
|
372 | if (columnDef && columnDef.cellMenu) {
|
373 | e.preventDefault();
|
374 | }
|
375 |
|
376 |
|
377 | _cellMenuProperties = $.extend({}, _cellMenuProperties, columnDef.cellMenu);
|
378 |
|
379 |
|
380 | if (!args) {
|
381 | args = {};
|
382 | }
|
383 | args.columnDef = columnDef;
|
384 | args.dataContext = dataContext;
|
385 | args.grid = _grid;
|
386 | if (!runOverrideFunctionWhenExists(_cellMenuProperties.menuUsabilityOverride, args)) {
|
387 | return;
|
388 | }
|
389 |
|
390 |
|
391 | $menu = createMenu(e, args);
|
392 |
|
393 |
|
394 | if ($menu) {
|
395 | repositionMenu(e);
|
396 | $menu
|
397 | .data("cell", _currentCell)
|
398 | .data("row", _currentRow)
|
399 | .show();
|
400 | }
|
401 |
|
402 |
|
403 | $("body").on("mousedown." + _gridUid, handleBodyMouseDown);
|
404 | }
|
405 |
|
406 | function handleBodyMouseDown(e) {
|
407 | if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
|
408 | if(!e.isDefaultPrevented()) {
|
409 | closeMenu(e, { cell: _currentCell, row: _currentRow });
|
410 | }
|
411 | }
|
412 | }
|
413 |
|
414 | function closeMenu(e, args) {
|
415 | if ($menu && $menu.length > 0) {
|
416 | if (_self.onBeforeMenuClose.notify({
|
417 | "cell": args && args.cell,
|
418 | "row": args && args.row,
|
419 | "grid": _grid,
|
420 | "menu": $menu
|
421 | }, e, _self) == false) {
|
422 | return;
|
423 | }
|
424 | if ($menu && $menu.remove) {
|
425 | $menu.remove();
|
426 | $menu = null;
|
427 | }
|
428 | }
|
429 | }
|
430 |
|
431 |
|
432 | function populateOptionItems(cellMenu, optionMenuElm, optionItems, args) {
|
433 | if (!args || !optionItems || !cellMenu) {
|
434 | return;
|
435 | }
|
436 |
|
437 |
|
438 | if (cellMenu && cellMenu.optionTitle) {
|
439 | $optionTitleElm = $('<div class="title"/>').append(cellMenu.optionTitle);
|
440 | $optionTitleElm.appendTo(optionMenuElm);
|
441 | }
|
442 |
|
443 | for (var i = 0, ln = optionItems.length; i < ln; i++) {
|
444 | var item = optionItems[i];
|
445 |
|
446 |
|
447 | var isItemVisible = runOverrideFunctionWhenExists(item.itemVisibilityOverride, args);
|
448 | var isItemUsable = runOverrideFunctionWhenExists(item.itemUsabilityOverride, args);
|
449 |
|
450 |
|
451 | if (!isItemVisible) {
|
452 | continue;
|
453 | }
|
454 |
|
455 |
|
456 |
|
457 | if (Object.prototype.hasOwnProperty.call(item, "itemUsabilityOverride")) {
|
458 | item.disabled = isItemUsable ? false : true;
|
459 | }
|
460 |
|
461 | var $li = $('<div class="slick-cell-menu-item"></div>')
|
462 | .data("option", item.option || "")
|
463 | .data("item", item)
|
464 | .on("click", handleMenuItemOptionClick)
|
465 | .appendTo(optionMenuElm);
|
466 |
|
467 | if (item.divider || item === "divider") {
|
468 | $li.addClass("slick-cell-menu-item-divider");
|
469 | continue;
|
470 | }
|
471 |
|
472 |
|
473 | if (item.disabled || !isItemUsable) {
|
474 | $li.addClass("slick-cell-menu-item-disabled");
|
475 | }
|
476 |
|
477 |
|
478 | if (item.hidden) {
|
479 | $li.addClass("slick-cell-menu-item-hidden");
|
480 | }
|
481 |
|
482 | if (item.cssClass) {
|
483 | $li.addClass(item.cssClass);
|
484 | }
|
485 |
|
486 | if (item.tooltip) {
|
487 | $li.attr("title", item.tooltip);
|
488 | }
|
489 |
|
490 | var $icon = $('<div class="slick-cell-menu-icon"></div>')
|
491 | .appendTo($li);
|
492 |
|
493 | if (item.iconCssClass) {
|
494 | $icon.addClass(item.iconCssClass);
|
495 | }
|
496 |
|
497 | if (item.iconImage) {
|
498 | $icon.css("background-image", "url(" + item.iconImage + ")");
|
499 | }
|
500 |
|
501 | var $text = $('<span class="slick-cell-menu-content"></span>')
|
502 | .text(item.title)
|
503 | .appendTo($li);
|
504 |
|
505 | if (item.textCssClass) {
|
506 | $text.addClass(item.textCssClass);
|
507 | }
|
508 | }
|
509 | }
|
510 |
|
511 |
|
512 | function populateCommandItems(cellMenu, commandMenuElm, commandItems, args) {
|
513 | if (!args || !commandItems || !cellMenu) {
|
514 | return;
|
515 | }
|
516 |
|
517 |
|
518 | if (cellMenu && cellMenu.commandTitle) {
|
519 | $commandTitleElm = $('<div class="title"/>').append(cellMenu.commandTitle);
|
520 | $commandTitleElm.appendTo(commandMenuElm);
|
521 | }
|
522 |
|
523 | for (var i = 0, ln = commandItems.length; i < ln; i++) {
|
524 | var item = commandItems[i];
|
525 |
|
526 |
|
527 | var isItemVisible = runOverrideFunctionWhenExists(item.itemVisibilityOverride, args);
|
528 | var isItemUsable = runOverrideFunctionWhenExists(item.itemUsabilityOverride, args);
|
529 |
|
530 |
|
531 | if (!isItemVisible) {
|
532 | continue;
|
533 | }
|
534 |
|
535 |
|
536 |
|
537 | if (Object.prototype.hasOwnProperty.call(item, "itemUsabilityOverride")) {
|
538 | item.disabled = isItemUsable ? false : true;
|
539 | }
|
540 |
|
541 | var $li = $('<div class="slick-cell-menu-item"></div>')
|
542 | .data("command", item.command || "")
|
543 | .data("item", item)
|
544 | .on("click", handleMenuItemCommandClick)
|
545 | .appendTo(commandMenuElm);
|
546 |
|
547 | if (item.divider || item === "divider") {
|
548 | $li.addClass("slick-cell-menu-item-divider");
|
549 | continue;
|
550 | }
|
551 |
|
552 |
|
553 | if (item.disabled || !isItemUsable) {
|
554 | $li.addClass("slick-cell-menu-item-disabled");
|
555 | }
|
556 |
|
557 |
|
558 | if (item.hidden) {
|
559 | $li.addClass("slick-cell-menu-item-hidden");
|
560 | }
|
561 |
|
562 | if (item.cssClass) {
|
563 | $li.addClass(item.cssClass);
|
564 | }
|
565 |
|
566 | if (item.tooltip) {
|
567 | $li.attr("title", item.tooltip);
|
568 | }
|
569 |
|
570 | var $icon = $('<div class="slick-cell-menu-icon"></div>')
|
571 | .appendTo($li);
|
572 |
|
573 | if (item.iconCssClass) {
|
574 | $icon.addClass(item.iconCssClass);
|
575 | }
|
576 |
|
577 | if (item.iconImage) {
|
578 | $icon.css("background-image", "url(" + item.iconImage + ")");
|
579 | }
|
580 |
|
581 | var $text = $('<span class="slick-cell-menu-content"></span>')
|
582 | .text(item.title)
|
583 | .appendTo($li);
|
584 |
|
585 | if (item.textCssClass) {
|
586 | $text.addClass(item.textCssClass);
|
587 | }
|
588 | }
|
589 | }
|
590 |
|
591 | function handleMenuItemCommandClick(e) {
|
592 | var command = $(this).data("command");
|
593 | var item = $(this).data("item");
|
594 |
|
595 | if (!item || item.disabled || item.divider || item === "divider") {
|
596 | return;
|
597 | }
|
598 |
|
599 | var row = $menu.data("row");
|
600 | var cell = $menu.data("cell");
|
601 |
|
602 | var columnDef = _grid.getColumns()[cell];
|
603 | var dataContext = _grid.getDataItem(row);
|
604 |
|
605 | if (command !== null && command !== "") {
|
606 |
|
607 |
|
608 | var callbackArgs = {
|
609 | "cell": cell,
|
610 | "row": row,
|
611 | "grid": _grid,
|
612 | "command": command,
|
613 | "item": item,
|
614 | "column": columnDef,
|
615 | "dataContext": dataContext
|
616 | };
|
617 | _self.onCommand.notify(callbackArgs, e, _self);
|
618 |
|
619 |
|
620 | if (typeof item.action === "function") {
|
621 | item.action.call(this, e, callbackArgs);
|
622 | }
|
623 |
|
624 | if(!e.isDefaultPrevented()) {
|
625 | closeMenu(e, { cell: cell, row: row });
|
626 | }
|
627 | }
|
628 | }
|
629 |
|
630 | function handleMenuItemOptionClick(e) {
|
631 | var option = $(this).data("option");
|
632 | var item = $(this).data("item");
|
633 |
|
634 | if (!item || item.disabled || item.divider || item === "divider") {
|
635 | return;
|
636 | }
|
637 | if (!_grid.getEditorLock().commitCurrentEdit()) {
|
638 | return;
|
639 | }
|
640 |
|
641 | var row = $menu.data("row");
|
642 | var cell = $menu.data("cell");
|
643 |
|
644 | var columnDef = _grid.getColumns()[cell];
|
645 | var dataContext = _grid.getDataItem(row);
|
646 |
|
647 | if (option !== undefined) {
|
648 |
|
649 |
|
650 | var callbackArgs = {
|
651 | "cell": cell,
|
652 | "row": row,
|
653 | "grid": _grid,
|
654 | "option": option,
|
655 | "item": item,
|
656 | "column": columnDef,
|
657 | "dataContext": dataContext
|
658 | };
|
659 | _self.onOptionSelected.notify(callbackArgs, e, _self);
|
660 |
|
661 |
|
662 | if (typeof item.action === "function") {
|
663 | item.action.call(this, e, callbackArgs);
|
664 | }
|
665 |
|
666 | if(!e.isDefaultPrevented()) {
|
667 | closeMenu(e, { cell: cell, row: row });
|
668 | }
|
669 | }
|
670 | }
|
671 |
|
672 | |
673 |
|
674 |
|
675 |
|
676 |
|
677 |
|
678 | function runOverrideFunctionWhenExists(overrideFn, args) {
|
679 | if (typeof overrideFn === 'function') {
|
680 | return overrideFn.call(this, args);
|
681 | }
|
682 | return true;
|
683 | }
|
684 |
|
685 | $.extend(this, {
|
686 | "init": init,
|
687 | "closeMenu": destroyMenu,
|
688 | "destroy": destroy,
|
689 | "pluginName": "CellMenu",
|
690 | "setOptions": setOptions,
|
691 |
|
692 | "onAfterMenuShow": new Slick.Event(),
|
693 | "onBeforeMenuShow": new Slick.Event(),
|
694 | "onBeforeMenuClose": new Slick.Event(),
|
695 | "onCommand": new Slick.Event(),
|
696 | "onOptionSelected": new Slick.Event()
|
697 | });
|
698 | }
|
699 | })(jQuery);
|