salix/front/core/components/contextmenu/index.js

288 lines
7.5 KiB
JavaScript
Raw Permalink Normal View History

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
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
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
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
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;
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 = {[this.fieldName]: this.fieldValue};
where = buildFilter(where, (param, value) => {
const expr = this.exprBuilder({param, value});
const props = Object.keys(expr);
let newExpr = {};
for (let prop of props) {
if (expr[prop].like) {
const operator = expr[prop].like;
newExpr[prop] = {nlike: operator};
} else if (expr[prop].between) {
const operator = expr[prop].between;
newExpr = {
or: [
{[prop]: {lt: operator[0]}},
{[prop]: {gt: operator[1]}},
]
};
} else
newExpr[prop] = {neq: this.fieldValue};
}
return newExpr;
});
2020-06-09 10:16:40 +00:00
}
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;
}
function removeProp(obj, targetProp, prop) {
if (prop == targetProp)
delete obj[prop];
2020-06-09 10:16:40 +00:00
if (prop === 'and' || prop === 'or') {
const arrayCopy = obj[prop].slice();
for (let param of arrayCopy) {
2020-06-09 10:16:40 +00:00
const [key] = Object.keys(param);
const index = obj[prop].findIndex(param => {
return Object.keys(param)[0] == key;
});
if (key == targetProp)
obj[prop].splice(index, 1);
2020-06-09 10:16:40 +00:00
2020-06-09 10:43:14 +00:00
if (param[key] instanceof Array)
removeProp(param, filterKey, key);
if (Object.keys(param).length == 0)
obj[prop].splice(index, 1);
2020-06-09 10:16:40 +00:00
}
if (obj[prop].length == 0)
delete obj[prop];
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'
}
});