Skip to content

Commit

Permalink
feat(plugins): move external Header Menu into Slickgrid-Universal
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Aug 29, 2021
1 parent 3f08162 commit aeba480
Show file tree
Hide file tree
Showing 35 changed files with 2,089 additions and 1,339 deletions.
27 changes: 14 additions & 13 deletions examples/webpack-demo-vanilla-bundle/src/examples/example07.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ <h3 class="title is-3">
<span class="icon mdi mdi-eye-off-outline"></span>
<span>Dynamically Hide "Duration"</span>
</button>
<button class="button is-small" data-test="add-item-btn" onclick.delegate="addItem()"
title="Clear Filters &amp; Sorting to see it better">
<span class="icon mdi mdi-plus"></span>
<span>Add item</span>
</button>
<button class="button is-small" data-test="delete-item-btn" onclick.delegate="deleteItem()">
<span class="icon mdi mdi-minus"></span>
<span>Delete item</span>
</button>

<div style="margin: 5px 0"></div>

<button class="button is-small" data-test="disable-filters-btn" onclick.delegate="disableFilters()">
<span class="icon mdi mdi-filter-off-outline"></span>
<span>Disable Filters</span>
Expand All @@ -30,25 +42,14 @@ <h3 class="title is-3">
<span class="icon mdi mdi-sort-variant-remove"></span>
<span>Disable Sorting</span>
</button>
<div style="margin: 5px 0"></div>
<button class="button is-small" data-test="toggle-filtering-btn" onclick.delegate="toggleFilter()">
<span class="icon mdi mdi-swap-vertical"></span>
<span class.bind="filteringEnabledClass"></span>
<span>Toggle Filtering</span>
</button>
<button class="button is-small" data-test="toggle-sorting-btn" onclick.delegate="toggleSorting()">
<span class="icon mdi mdi-swap-vertical"></span>
<span class.bind="sortingEnabledClass"></span>
<span>Toggle Sorting</span>
</button>

<button class="button is-small" data-test="add-item-btn" onclick.delegate="addItem()"
title="Clear Filters &amp; Sorting to see it better">
<span class="icon mdi mdi-plus"></span>
<span>Add item</span>
</button>
<button class="button is-small" data-test="delete-item-btn" onclick.delegate="deleteItem()">
<span class="icon mdi mdi-minus"></span>
<span>Delete item</span>
</button>
<button class="button is-small" data-test="delete-clear-filters-btn" onclick.delegate="clearFilters()">
<span class="icon mdi mdi-close"></span>
<span>Clear Filters</span>
Expand Down
15 changes: 15 additions & 0 deletions examples/webpack-demo-vanilla-bundle/src/examples/example07.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,28 @@ export class Example7 {
dataset: any[];
sgb: SlickVanillaGridBundle;
duplicateTitleHeaderCount = 1;
filteringEnabledClass = '';
sortingEnabledClass = '';
selectedLanguage: string;
selectedLanguageFile: string;
translateService: TranslateService;

set isFilteringEnabled(enabled: boolean) {
this.filteringEnabledClass = enabled ? 'icon mdi mdi-toggle-switch' : 'icon mdi mdi-toggle-switch-off-outline';
}
set isSortingEnabled(enabled: boolean) {
this.sortingEnabledClass = enabled ? 'icon mdi mdi-toggle-switch' : 'icon mdi mdi-toggle-switch-off-outline';
}

constructor() {
this._bindingEventService = new BindingEventService();
// get the Translate Service from the window object,
// it might be better with proper Dependency Injection but this project doesn't have any at this point
this.translateService = (<any>window).TranslateService;
this.selectedLanguage = this.translateService.getCurrentLanguage();
this.selectedLanguageFile = `${this.selectedLanguage}.json`;
this.isFilteringEnabled = true;
this.isSortingEnabled = true;
}

attached() {
Expand Down Expand Up @@ -445,10 +456,12 @@ export class Example7 {
// --------------------------------------------------

disableFilters() {
this.isFilteringEnabled = false;
this.sgb.filterService.disableFilterFunctionality(true);
}

disableSorting() {
this.isSortingEnabled = false;
this.sgb.sortService.disableSortFunctionality(true);
}

Expand All @@ -457,9 +470,11 @@ export class Example7 {

toggleFilter() {
this.sgb.filterService.toggleFilterFunctionality();
this.isFilteringEnabled = this.sgb.slickGrid.getOptions().enableFiltering;
}

toggleSorting() {
this.sgb.sortService.toggleSortFunctionality();
this.isSortingEnabled = this.sgb.slickGrid.getOptions().enableSorting;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ <h4 class="title is-4">Container Width (1000px)</h4>
</div>
</div>

<div style="width: 1000px" class="grid-container">
<div style="width: 1000px; margin-left: 25px" class="grid-container">
<div class="grid14">
</div>
</div>
67 changes: 36 additions & 31 deletions packages/common/src/controls/columnPicker.control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ declare const Slick: SlickNamespace;

/**
* A control to add a Column Picker (right+click on any column header to reveal the column picker)
* To specify a custom button in a column header, extend the column definition like so:
* this.gridOptions = {
* enableColumnPicker: true,
* columnPicker: {
* ... column picker options ...
* }
* }];
* @class ColumnPickerControl
* @constructor
*/
Expand Down Expand Up @@ -159,15 +166,40 @@ export class ColumnPickerControl {
return this.grid.getColumns();
}

/** Translate the Column Picker headers and also the last 2 checkboxes */
translateColumnPicker() {
// update the properties by pointers, that is the only way to get Column Picker Control to see the new values
if (this.controlOptions) {
this.controlOptions.columnTitle = '';
this.controlOptions.forceFitTitle = '';
this.controlOptions.syncResizeTitle = '';
this.controlOptions.columnTitle = this.extensionUtility.getPickerTitleOutputString('columnTitle', 'columnPicker');
this.controlOptions.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'columnPicker');
this.controlOptions.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'columnPicker');
}

// translate all columns (including hidden columns)
this.extensionUtility.translateItems(this._columns, 'nameKey', 'name');

// update the Titles of each sections (command, customTitle, ...)
if (this.controlOptions) {
this.updateAllTitles(this.controlOptions);
}
}

// --
// protected functions
// ------------------

/** Mouse down handler when clicking anywhere in the DOM body */
handleBodyMouseDown(e: DOMEvent<HTMLDivElement>) {
protected handleBodyMouseDown(e: DOMEvent<HTMLDivElement>) {
if ((this._menuElm !== e.target && !this._menuElm.contains(e.target)) || e.target.className === 'close') {
this._menuElm.style.visibility = 'hidden';
}
}

/** Mouse header context handler when doing a right+click on any of the header column title */
handleHeaderContextMenu(e: DOMEvent<HTMLDivElement>) {
protected handleHeaderContextMenu(e: DOMEvent<HTMLDivElement>) {
e.preventDefault();
emptyElement(this._listElm);
updateColumnPickerOrder.call(this);
Expand All @@ -177,7 +209,7 @@ export class ColumnPickerControl {
this.repositionMenu(e);
}

repositionMenu(e: DOMEvent<HTMLDivElement>) {
protected repositionMenu(e: DOMEvent<HTMLDivElement>) {
this._menuElm.style.top = `${(e as any).pageY - 10}px`;
this._menuElm.style.left = `${(e as any).pageX - 10}px`;
this._menuElm.style.maxHeight = `${document.body.clientHeight - (e as any).pageY - 10}px`;
Expand All @@ -186,36 +218,9 @@ export class ColumnPickerControl {
}

/** Update the Titles of each sections (command, customTitle, ...) */
updateAllTitles(options: ColumnPickerOption) {
protected updateAllTitles(options: ColumnPickerOption) {
if (this._columnTitleElm?.textContent && options.columnTitle) {
this._columnTitleElm.textContent = options.columnTitle;
}
}

/** Translate the Column Picker headers and also the last 2 checkboxes */
translateColumnPicker() {
// update the properties by pointers, that is the only way to get Column Picker Control to see the new values
if (this.controlOptions) {
this.emptyColumnPickerTitles();
this.controlOptions.columnTitle = this.extensionUtility.getPickerTitleOutputString('columnTitle', 'columnPicker');
this.controlOptions.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'columnPicker');
this.controlOptions.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'columnPicker');
}

// translate all columns (including hidden columns)
this.extensionUtility.translateItems(this._columns, 'nameKey', 'name');

// update the Titles of each sections (command, customTitle, ...)
if (this.controlOptions) {
this.updateAllTitles(this.controlOptions);
}
}

protected emptyColumnPickerTitles() {
if (this.controlOptions) {
this.controlOptions.columnTitle = '';
this.controlOptions.forceFitTitle = '';
this.controlOptions.syncResizeTitle = '';
}
}
}
114 changes: 54 additions & 60 deletions packages/common/src/controls/gridMenu.control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ import { handleColumnPickerItemClick, populateColumnPicker, updateColumnPickerOr
declare const Slick: SlickNamespace;

/**
* A control to add a Grid Menu (hambuger menu on top-right of the grid)
* A control to add a Grid Menu with Extra Commands & Column Picker (hambuger menu on top-right of the grid)
* To specify a custom button in a column header, extend the column definition like so:
* this.gridOptions = {
* enableGridMenu: true,
* gridMenu: {
* ... grid menu options ...
* customItems: [{ ...command... }, { ...command... }]
* }
* }];
* @class GridMenuControl
* @constructor
*/
Expand Down Expand Up @@ -284,47 +292,6 @@ export class GridMenuControl {
return this.grid.getColumns();
}

handleMenuCustomItemClick(event: Event, item: GridMenuItem) {
if (item && item.command && !item.disabled && !item.divider) {
const callbackArgs = {
grid: this.grid,
command: item.command,
item,
allColumns: this.columns,
visibleColumns: this.getVisibleColumns()
} as GridMenuCommandItemCallbackArgs;

// execute Grid Menu callback with command,
// we'll also execute optional user defined onCommand callback when provided
this.executeGridMenuInternalCustomCommands(event, callbackArgs);
this.pubSubService.publish('gridMenu:onCommand', callbackArgs);
if (typeof this.controlOptions?.onCommand === 'function') {
this.controlOptions.onCommand(event, callbackArgs);
}

// execute action callback when defined
if (typeof item.action === 'function') {
item.action.call(this, event, callbackArgs);
}
}

// does the user want to leave open the Grid Menu after executing a command?
if (!this.controlOptions.leaveOpen && !event.defaultPrevented) {
this.hideMenu(event);
}

// Stop propagation so that it doesn't register as a header click event.
event.preventDefault();
event.stopPropagation();
}

/** Mouse down handler when clicking anywhere in the DOM body */
handleBodyMouseDown(event: DOMEvent<HTMLDivElement>) {
if ((this._gridMenuElm !== event.target && !this._gridMenuElm.contains(event.target) && this._isMenuOpen) || event.target.className === 'close') {
this.hideMenu(event);
}
}

/**
* Hide the Grid Menu but only if it does detect as open prior to executing anything.
* @param event
Expand Down Expand Up @@ -390,8 +357,8 @@ export class GridMenuControl {
let isItemVisible = true;
let isItemUsable = true;
if (typeof item === 'object') {
isItemVisible = this.runOverrideFunctionWhenExists<typeof callbackArgs>(item.itemVisibilityOverride, callbackArgs);
isItemUsable = this.runOverrideFunctionWhenExists<typeof callbackArgs>(item.itemUsabilityOverride, callbackArgs);
isItemVisible = this.extensionUtility.runOverrideFunctionWhenExists<typeof callbackArgs>(item.itemVisibilityOverride, callbackArgs);
isItemUsable = this.extensionUtility.runOverrideFunctionWhenExists<typeof callbackArgs>(item.itemUsabilityOverride, callbackArgs);
}

// if the result is not visible then there's no need to go further
Expand Down Expand Up @@ -521,7 +488,7 @@ export class GridMenuControl {
} as GridMenuEventWithElementCallbackArgs;

// run the override function (when defined), if the result is false then we won't go further
if (controlOptions && !this.runOverrideFunctionWhenExists<typeof callbackArgs>(controlOptions.menuUsabilityOverride, callbackArgs)) {
if (controlOptions && !this.extensionUtility.runOverrideFunctionWhenExists<typeof callbackArgs>(controlOptions.menuUsabilityOverride, callbackArgs)) {
return;
}

Expand Down Expand Up @@ -560,7 +527,10 @@ export class GridMenuControl {
// we also need to call the control init so that it takes the new Grid object with latest values
if (this.controlOptions) {
this.controlOptions.customItems = [];
this.emptyGridMenuTitles();
this.controlOptions.customTitle = '';
this.controlOptions.columnTitle = '';
this.controlOptions.forceFitTitle = '';
this.controlOptions.syncResizeTitle = '';

// merge original user grid menu items with internal items
// then sort all Grid Menu Custom Items (sorted by pointer, no need to use the return)
Expand Down Expand Up @@ -588,15 +558,6 @@ export class GridMenuControl {
// protected functions
// ------------------

protected emptyGridMenuTitles() {
if (this.controlOptions) {
this.controlOptions.customTitle = '';
this.controlOptions.columnTitle = '';
this.controlOptions.forceFitTitle = '';
this.controlOptions.syncResizeTitle = '';
}
}

/** Create Grid Menu with Custom Commands if user has enabled Filters and/or uses a Backend Service (OData, GraphQL) */
protected addGridMenuCustomCommands(originalCustomItems: Array<GridMenuItem | 'divider'>) {
const backendApi = this.gridOptions.backendServiceApi || null;
Expand Down Expand Up @@ -873,11 +834,44 @@ export class GridMenuControl {
};
}

/** Run the Override function when it exists, if it returns True then it is usable/visible */
protected runOverrideFunctionWhenExists<T = any>(overrideFn: any, args: T): boolean {
if (typeof overrideFn === 'function') {
return overrideFn.call(this, args);
/** Mouse down handler when clicking anywhere in the DOM body */
protected handleBodyMouseDown(event: DOMEvent<HTMLDivElement>) {
if ((this._gridMenuElm !== event.target && !this._gridMenuElm.contains(event.target) && this._isMenuOpen) || event.target.className === 'close') {
this.hideMenu(event);
}
return true;
}

protected handleMenuCustomItemClick(event: Event, item: GridMenuItem) {
if (item && item.command && !item.disabled && !item.divider) {
const callbackArgs = {
grid: this.grid,
command: item.command,
item,
allColumns: this.columns,
visibleColumns: this.getVisibleColumns()
} as GridMenuCommandItemCallbackArgs;

// execute Grid Menu callback with command,
// we'll also execute optional user defined onCommand callback when provided
this.executeGridMenuInternalCustomCommands(event, callbackArgs);
this.pubSubService.publish('gridMenu:onCommand', callbackArgs);
if (typeof this.controlOptions?.onCommand === 'function') {
this.controlOptions.onCommand(event, callbackArgs);
}

// execute action callback when defined
if (typeof item.action === 'function') {
item.action.call(this, event, callbackArgs);
}
}

// does the user want to leave open the Grid Menu after executing a command?
if (!this.controlOptions.leaveOpen && !event.defaultPrevented) {
this.hideMenu(event);
}

// Stop propagation so that it doesn't register as a header click event.
event.preventDefault();
event.stopPropagation();
}
}
Loading

0 comments on commit aeba480

Please sign in to comment.