2020-05-28 06:07:26 +00:00
|
|
|
import ngModule from '../../module';
|
2020-06-09 10:16:40 +00:00
|
|
|
import {buildFilter} from 'vn-loopback/util/filter';
|
2020-06-08 11:15:48 +00:00
|
|
|
import './style.scss';
|
|
|
|
|
2020-05-28 06:07:26 +00:00
|
|
|
export default class Contextmenu {
|
2020-05-29 09:10:58 +00:00
|
|
|
constructor($element, $, $transclude) {
|
2020-05-28 06:07:26 +00:00
|
|
|
this.$element = $element;
|
|
|
|
this.element = $element[0];
|
|
|
|
this.$ = $;
|
2020-05-29 09:10:58 +00:00
|
|
|
this.$transclude = $transclude;
|
2020-05-28 06:07:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
get targets() {
|
|
|
|
return this._targets;
|
|
|
|
}
|
|
|
|
|
|
|
|
set targets(value) {
|
|
|
|
this._targets = value;
|
2020-05-28 06:07:26 +00:00
|
|
|
|
|
|
|
if (!value) return;
|
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
for (let selector of value) {
|
2020-06-08 10:14:27 +00:00
|
|
|
const target = document.querySelector(selector);
|
2020-05-29 09:10:58 +00:00
|
|
|
if (!target) continue;
|
2020-05-28 06:07:26 +00:00
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
target.addEventListener('contextmenu', event => {
|
2020-06-08 10:14:27 +00:00
|
|
|
this.target = event.target;
|
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
if (!event.defaultPrevented)
|
|
|
|
event.preventDefault();
|
2020-05-28 06:07:26 +00:00
|
|
|
|
2021-02-11 14:06:41 +00:00
|
|
|
if (!this.isMenuEnabled()) return;
|
2020-06-08 10:14:27 +00:00
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
const parent = this.$.contextmenu;
|
|
|
|
parent.style.top = event.pageY + 'px';
|
|
|
|
parent.style.left = event.pageX + 'px';
|
2020-05-28 06:07:26 +00:00
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
this.$.menu.show(parent);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
get row() {
|
|
|
|
if (!this.target) return null;
|
|
|
|
|
2021-06-15 11:48:42 +00:00
|
|
|
return this.target.closest('[ng-repeat]');
|
2020-06-08 10:14:27 +00:00
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
get rowIndex() {
|
|
|
|
if (!this.row) return null;
|
2021-06-15 11:48:42 +00:00
|
|
|
|
2021-10-28 07:06:03 +00:00
|
|
|
const table = this.row.closest('table, vn-table, .vn-table');
|
2021-06-15 11:48:42 +00:00
|
|
|
const rows = table.querySelectorAll('[ng-repeat]');
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
return Array.from(rows).findIndex(
|
|
|
|
rowItem => rowItem == this.row
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
get rowData() {
|
2020-05-29 09:10:58 +00:00
|
|
|
const model = this.model;
|
2020-06-08 10:14:27 +00:00
|
|
|
const rowData = model.data[this.rowIndex];
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
return rowData;
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
get cell() {
|
|
|
|
if (!this.target) return null;
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2021-10-28 07:06:03 +00:00
|
|
|
return this.target.closest('td, vn-td, .vn-td, vn-td-editable');
|
2020-06-08 10:14:27 +00:00
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
get cellIndex() {
|
|
|
|
if (!this.row) return null;
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2021-10-28 07:06:03 +00:00
|
|
|
const cells = this.row.querySelectorAll('td, vn-td, .vn-td, vn-td-editable');
|
2020-06-08 10:14:27 +00:00
|
|
|
return Array.from(cells).findIndex(
|
|
|
|
cellItem => cellItem == this.cell
|
|
|
|
);
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
get rowHeader() {
|
|
|
|
if (!this.row) return null;
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2021-10-28 07:06:03 +00:00
|
|
|
const table = this.row.closest('table, vn-table, .vn-table');
|
|
|
|
const headerCells = table && table.querySelectorAll('thead th, vn-thead vn-th');
|
2020-06-08 10:14:27 +00:00
|
|
|
const headerCell = headerCells && headerCells[this.cellIndex];
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
return headerCell;
|
2020-05-29 09:10:58 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Selected model field name
|
|
|
|
*
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
get fieldName() {
|
|
|
|
if (!this.rowHeader) return null;
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
return this.rowHeader.getAttribute('field');
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Selected field value
|
|
|
|
*
|
|
|
|
* @return {any}
|
|
|
|
*/
|
|
|
|
get fieldValue() {
|
|
|
|
return this.rowData[this.fieldName];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if filter is not disabled
|
|
|
|
*
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
isFilterEnabled() {
|
|
|
|
if (!this.rowHeader) return true;
|
|
|
|
const isEnabled = this.rowHeader.getAttribute('filter-enabled');
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
return isEnabled != 'false';
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2021-02-11 14:06:41 +00:00
|
|
|
isMenuEnabled() {
|
|
|
|
if (!this.rowHeader) return true;
|
|
|
|
const isEnabled = this.rowHeader.getAttribute('menu-enabled');
|
|
|
|
|
|
|
|
return isEnabled != 'false';
|
|
|
|
}
|
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Returns true if filter
|
2021-02-11 14:06:41 +00:00
|
|
|
* by selection is enabled and
|
|
|
|
* the menu can be interacted
|
2020-06-08 10:14:27 +00:00
|
|
|
*
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
isFilterAllowed() {
|
2021-02-11 14:06:41 +00:00
|
|
|
return this.isActionAllowed() && this.isFilterEnabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the
|
|
|
|
* context menu can be interacted
|
|
|
|
*
|
|
|
|
* @return {Boolean}
|
|
|
|
*/
|
|
|
|
isActionAllowed() {
|
2020-06-08 10:14:27 +00:00
|
|
|
if (!this.target) return false;
|
2021-10-28 07:06:03 +00:00
|
|
|
const isTableCell = this.target.closest('td, vn-td, .vn-td');
|
2020-06-08 10:14:27 +00:00
|
|
|
|
|
|
|
return isTableCell && this.fieldName;
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Filter by current field selection
|
|
|
|
*/
|
|
|
|
filterBySelection() {
|
2020-06-09 10:16:40 +00:00
|
|
|
let where = {[this.fieldName]: this.fieldValue};
|
|
|
|
if (this.exprBuilder) {
|
|
|
|
where = buildFilter(where, (param, value) =>
|
|
|
|
this.exprBuilder({param, value})
|
|
|
|
);
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-09 10:16:40 +00:00
|
|
|
this.model.addFilter({where});
|
2020-06-08 10:14:27 +00:00
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Exclude by current field selection
|
|
|
|
*/
|
|
|
|
excludeSelection() {
|
2020-06-09 10:16:40 +00:00
|
|
|
let where = {[this.fieldName]: {neq: this.fieldValue}};
|
|
|
|
if (this.exprBuilder) {
|
|
|
|
where = buildFilter(where, (param, value) =>
|
|
|
|
this.exprBuilder({param, value})
|
|
|
|
);
|
|
|
|
}
|
2020-05-29 09:10:58 +00:00
|
|
|
|
2020-06-09 10:16:40 +00:00
|
|
|
this.model.addFilter({where});
|
|
|
|
}
|
|
|
|
|
|
|
|
removeFilter() {
|
|
|
|
const userFilter = this.model.userFilter;
|
|
|
|
const userParams = this.model.userParams;
|
2020-11-13 06:38:18 +00:00
|
|
|
const where = userFilter && userFilter.where;
|
2020-06-09 10:16:40 +00:00
|
|
|
|
|
|
|
let filterKey = this.fieldName;
|
|
|
|
if (this.exprBuilder) {
|
|
|
|
const param = this.exprBuilder({
|
|
|
|
param: filterKey,
|
|
|
|
value: null
|
|
|
|
});
|
2020-11-13 06:38:18 +00:00
|
|
|
if (param) [filterKey] = Object.keys(param);
|
2020-06-09 10:16:40 +00:00
|
|
|
}
|
|
|
|
|
2020-11-13 06:38:18 +00:00
|
|
|
if (!where) return;
|
|
|
|
|
2020-06-09 10:16:40 +00:00
|
|
|
const whereKeys = Object.keys(where);
|
2021-02-11 14:06:41 +00:00
|
|
|
for (let key of whereKeys) {
|
2020-06-09 10:16:40 +00:00
|
|
|
removeProp(where, filterKey, key);
|
|
|
|
|
2021-02-11 14:06:41 +00:00
|
|
|
if (!Object.keys(where))
|
|
|
|
delete userFilter.where;
|
|
|
|
}
|
|
|
|
|
2020-06-09 10:16:40 +00:00
|
|
|
function removeProp(instance, findProp, prop) {
|
|
|
|
if (prop == findProp)
|
|
|
|
delete instance[prop];
|
|
|
|
|
|
|
|
if (prop === 'and') {
|
|
|
|
for (let [index, param] of instance[prop].entries()) {
|
|
|
|
const [key] = Object.keys(param);
|
|
|
|
if (key == findProp)
|
|
|
|
instance[prop].splice(index, 1);
|
|
|
|
|
2020-06-09 10:43:14 +00:00
|
|
|
if (param[key] instanceof Array)
|
|
|
|
removeProp(param, filterKey, key);
|
2020-06-09 10:16:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.model.applyFilter(userFilter, userParams);
|
2020-05-29 09:10:58 +00:00
|
|
|
}
|
2020-05-28 06:07:26 +00:00
|
|
|
|
2020-06-08 10:14:27 +00:00
|
|
|
/**
|
|
|
|
* Removes all applied filters
|
|
|
|
*/
|
2020-06-09 10:16:40 +00:00
|
|
|
removeAllFilters() {
|
|
|
|
const userParams = this.model.userParams;
|
|
|
|
this.model.applyFilter(null, userParams);
|
2020-05-28 06:07:26 +00:00
|
|
|
}
|
2021-02-11 14:06:41 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Copies the current field
|
|
|
|
* value to the clipboard
|
|
|
|
*/
|
|
|
|
copyValue() {
|
2021-02-12 11:19:42 +00:00
|
|
|
const cell = angular.element(this.cell);
|
2021-02-11 14:06:41 +00:00
|
|
|
if (navigator && navigator.clipboard)
|
2021-02-12 11:19:42 +00:00
|
|
|
navigator.clipboard.writeText(cell.text());
|
2021-02-11 14:06:41 +00:00
|
|
|
}
|
2020-05-28 06:07:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-29 09:10:58 +00:00
|
|
|
Contextmenu.$inject = ['$element', '$scope', '$transclude'];
|
2020-05-28 06:07:26 +00:00
|
|
|
|
|
|
|
ngModule.vnComponent('vnContextmenu', {
|
|
|
|
controller: Contextmenu,
|
|
|
|
template: require('./index.html'),
|
|
|
|
bindings: {
|
2020-05-29 09:10:58 +00:00
|
|
|
targets: '<?',
|
2020-06-09 10:16:40 +00:00
|
|
|
model: '<?',
|
|
|
|
exprBuilder: '&?'
|
2020-05-28 06:07:26 +00:00
|
|
|
},
|
|
|
|
transclude: {
|
|
|
|
menu: '?slotMenu'
|
|
|
|
}
|
|
|
|
});
|