src/app/shared/widgets/data-list/default-footer-component/default-data-list-footer.component.ts
Interface defining the footer configuration
Properties |
buttonSelectors |
buttonSelectors:
|
Type : Selectors
|
Selector configuration to look up actions entries |
clipboardOptions |
clipboardOptions:
|
Type : ClipboardOptions
|
Optional |
JSON object containing options to use when copying list cells |
hidden |
hidden:
|
Type : boolean
|
Hides/Shows the footer |
selectors |
selectors:
|
Type : Selectors
|
Selector configuration to look up menu entries |
showAsButton |
showAsButton:
|
Type : number
|
Number of interactions defined by |
showFavoriteInteractions |
showFavoriteInteractions:
|
Type : boolean
|
Shows/Hides the footer favorites list |
translations |
translations:
|
Type : TranslationHash | TranslationDefinition[]
|
Either, a json object, containing properties for the available translations, or an array of objects, each defining the message for a possible message type and data type or view mode combination. |
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Inject,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild,
} from "@angular/core";
import { Observable, of, ReplaySubject } from "rxjs";
import { first } from "rxjs/operators";
import { Column, SelectionParams } from "../../interfaces/list.interfaces";
import { Selectors } from "../../../components/app-context/api";
import { AppContext } from "../../../components/app-context/app.context";
import { MatMenuTrigger } from "@angular/material/menu";
import { DialogService } from "../../../components/dialog/dialog.service";
import { DataListExportComponent } from "../data-list-dialog-component/data-list-export.component";
import {
DeletionMode,
Scope,
} from "../../../components/local-storage/local-storage-constants";
import {
LocalStorageEntry,
LocalStorageService,
} from "../../../components/local-storage/local-storage.service";
import { getOrDefault } from "../../widget.configuration";
import {
deepCopy,
firstNonNull,
UtilService,
} from "../../../components/util/util.service";
import { DataListActionsService } from "./data-list-actions.service";
import { DataListApi } from "../data-list.api";
import {
NmModuleConfiguration,
NOVOMIND_MODULE_CONFIGURATION,
} from "../../../../nm.module-descriptor";
import { ValidationService } from "../../../components/validation/validation.service";
import { CustomNotificationService } from "../../../components/notification/customnotification.service";
import { TranslateService } from "@ngx-translate/core";
import { DataListComponent } from "../data-list-component/data-list.component";
import { Attributes } from "../../../components/edit-attribute";
import { IgxGridComponent } from "@infragistics/igniteui-angular";
import { IgxGridCell } from "@infragistics/igniteui-angular";
export interface DataListState {
/**
* Number of rows in data-list
*/
total: number;
/**
* Number of selected rows of data-list
*/
selected: number;
/**
* Selected rows of data-list
*/
selection?: SelectionParams;
/**
* Data url without paging
*/
link?: string;
}
const DEFAULT_CLIPBOARD_OPTIONS = {
enabled: true,
copyHeaders: false,
copyFormatters: false,
separator: "\t",
};
const DEFAULT_ACTION_CONFIGURATION = {
hidden: false,
selectors: { target: "default-data-list-footer" },
showAsButton: 0,
showFavoriteInteractions: true,
clipboardOptions: DEFAULT_CLIPBOARD_OPTIONS,
};
const CONDITION_HAS_SELECTION = [
(config, param) => param && param.selected > 0,
];
const CONDITION_HAS_CONTENT = [
(config, param) => {
return param && (param.total > 0 || param?.grid?.grid?.data?.length > 0);
},
];
const CONDITION_IS_EDITABLE_ATTRIBUTE = [
(config, param) => {
if (
!param.column ||
!param.column.editable ||
param.column.type !== "attribute"
) {
return false;
}
return true;
},
];
const DEFAULT_TRANSLATIONS = {
"no-content": "data-list.footer.no-content",
"no-selection": "data-list.footer.no-selection",
selection: "data-list.footer.selection",
};
const LOCAL_STORAGE_EXPORT_SETTINGS = "data-list-export-settings";
const APPLY_FOR_SELECTORS = { "@toolbox-config": "apply-for" };
/**
* Translation keys for message types
*/
interface TranslationHash {
/**
* Translation map using the message type as the key and a translation label or description as the value.
* (message types: no-content, no-selection or selection)
*/
[key: string]: string;
}
/**
* Single translation key that is scoped to a message type and possibly a view mode or a data type
*/
interface TranslationDefinition {
/**
* Translation message type identifier (options: no-content, no-selection or selection)
*/
type: "no-content" | "no-selection" | "selection";
/**
* View mode identifier to apply translation to
*/
viewMode?: string;
/**
* Data type identifier to apply translation to
*/
dataType?: string;
/**
* The translation label or description
*/
message: string;
}
/**
* Definition of options when copying
* data list cells
*/
interface ClipboardOptions {
enabled: boolean;
copyHeaders: boolean;
copyFormatters: boolean;
separator: string;
}
/**
* Interface defining the footer configuration
*/
interface FooterConfiguration {
/**
* Either, a json object, containing properties for the available translations,
* or an array of objects, each defining the message for a possible message type and data type or view mode
* combination.
*/
translations: TranslationHash | TranslationDefinition[];
/**
* Hides/Shows the footer
*/
hidden: boolean;
/**
* Selector configuration to look up menu entries
*/
selectors: Selectors;
/**
* Selector configuration to look up actions entries
*/
buttonSelectors: Selectors;
/**
* Number of interactions defined by `selectors` that should be displayed as buttons
* (instead of as part of the menu)
*/
showAsButton: number;
/**
* Shows/Hides the footer favorites list
*/
showFavoriteInteractions: boolean;
/**
* JSON object containing options to use when copying list cells
*/
clipboardOptions?: ClipboardOptions;
}
@Component({
selector: "nm-default-data-list-footer",
templateUrl: "./default-data-list-footer.component.html",
styleUrls: ["./default-data-list-footer.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DefaultDataListFooterComponent
implements OnInit, OnDestroy, OnChanges
{
@ViewChild(MatMenuTrigger) menuTrigger;
/**
* Deprecated: feature is now removed.
*/
@Input()
public set refreshButtons(value) {
// No need to do anything, angular will automtically trigger change detection when inputs change
}
/**
* Service that allows to directly interact with the data-list
*/
@Input()
public api: DataListApi;
/**
* Data-list configuration
*/
@Input()
public set configuration(configuration: any) {
this.tableConfiguration = configuration;
this._configuration = Object.assign(
{},
DEFAULT_ACTION_CONFIGURATION,
configuration.footer || {}
);
const clipboard =
configuration.footer && configuration.footer.clipboardOptions
? configuration.footer.clipboardOptions
: {};
this._configuration.clipboardOptions = Object.assign(
{},
DEFAULT_CLIPBOARD_OPTIONS,
clipboard
);
if (configuration.islands) {
let islands = <any[]>configuration.islands;
this._islands = islands.map((island) => island.key);
}
}
public get configuration(): any {
return this._configuration;
}
/**
* Data-list all selected items
*/
@Input("selected-items")
selectedItems: Observable<SelectionParams>;
/**
* Number of entries (rows) in the data-list
*/
@Input()
total: Observable<number>;
/**
* Data-list grid instance
*/
@Input()
grid: DataListComponent;
/**
* Data-list data url without paging
*/
@Input()
link: Observable<string>;
/**
* Emits export settings
*/
@Output()
export = new EventEmitter<any>();
/**
* Data-list selected view mode
*/
@Input()
viewMode: string;
/**
* Data-list selected data type
*/
@Input()
dataType: string;
@Input()
public set column(column: Column) {
this.columnSubject.next(column);
}
@Input()
public set cell(cell) {
this.cellSubject.next(cell);
}
public _configuration: FooterConfiguration;
public tableConfiguration;
private _islands: string[] = [];
private columnSubject = new ReplaySubject<Column>(1);
private cellSubject = new ReplaySubject<IgxGridCell>(1);
visible: boolean = false;
showFavoriteInteractions: boolean = true;
stateObservable: Observable<DataListState>;
translations: { [key: string]: string };
private localstorageExportSettings: LocalStorageEntry;
constructor(
private appContext: AppContext,
protected localStorageService: LocalStorageService,
private dialog: DialogService,
private dataListActionsService: DataListActionsService,
@Inject(NOVOMIND_MODULE_CONFIGURATION)
private moduleConfig: NmModuleConfiguration,
private validationService: ValidationService,
private notificationService: CustomNotificationService,
private translateService: TranslateService,
private cdr: ChangeDetectorRef
) {}
ngOnChanges(changes: SimpleChanges): void {
if (changes.configuration || changes.viewMode || changes.dataType) {
this.updateTranslations();
}
//Wait till the api is here before initializing
if (changes.api && this.api) {
this.stateObservable = this.dataListActionsService.mapSelectionState(
this.total,
this.selectedItems,
this.link,
this._islands,
this.columnSubject,
this.cellSubject,
this.grid,
this.api,
this.export
);
}
}
ngOnInit(): void {
this.initializeDefaultActions();
this.visible =
!this._configuration.hidden &&
(this.tableConfiguration.rowSelectable || true);
this.showFavoriteInteractions =
this._configuration.showFavoriteInteractions;
this.localstorageExportSettings =
this.localStorageService.getLocalStorageEntry(
LOCAL_STORAGE_EXPORT_SETTINGS,
Scope.GLOBAL,
DeletionMode.NEVER
);
if (
!this.localstorageExportSettings.exists() ||
!this.localstorageExportSettings.value ||
this.localstorageExportSettings.value == "undefined"
) {
this.localstorageExportSettings.value = JSON.stringify({
assetFormat: "thumbnail",
exportFormat: "CSV",
csvSeparator: ";",
csvEncoding: "ISO-8859-15",
csvHeaders: true,
onlySelectedRows: true,
});
}
}
private updateTranslations() {
let translations = (this.translations = Object.assign(
{},
DEFAULT_TRANSLATIONS
));
if (Array.isArray(this._configuration.translations)) {
let definitions = this._configuration.translations;
Object.keys(DEFAULT_TRANSLATIONS).forEach((type) => {
let definition = definitions.find((def) => {
return (
def.type === type &&
(def.dataType === this.dataType ||
(def.dataType == null && this.dataType == null)) &&
(def.viewMode === this.viewMode ||
(def.viewMode == null && this.viewMode == null))
);
});
if (definition) {
translations[type] = definition.message;
}
});
} else {
Object.assign(translations, this._configuration.translations);
}
this.translations = translations;
this.cdr.markForCheck();
}
private initializeDefaultActions() {
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-clipboard",
],
content: "*",
action: "copySelectionToClipBoard",
},
{
type: "action",
menu: "menu.clipboard",
order: -300,
description: "placeholder.copySelectionToClipBoard",
name: "copySelectionToClipBoard",
conditions: CONDITION_HAS_SELECTION,
onClick: (configuration, params) => {
copySelectionToClipBoard(
params,
this._configuration.clipboardOptions
);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-clipboard",
],
content: "*",
action: "copySelectedCellsToClipBoard",
},
{
type: "action",
menu: "menu.clipboard",
order: -300,
description: "placeholder.copySelectedCellsToClipBoard",
name: "copySelectedCellsToClipBoard",
conditions: CONDITION_HAS_SELECTION,
onClick: (configuration, params) => {
copySelectedCellsToClipBoard(params);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-clipboard",
],
content: "*",
action: "copyColumnCellsToClipBoard",
},
{
type: "action",
menu: "menu.clipboard",
order: -300,
description: "placeholder.copyColumnCellsToClipBoard",
name: "copyColumnCellsToClipBoard",
conditions: CONDITION_HAS_CONTENT,
onClick: (configuration, params) => {
copySeletedColumnCellsToClipBoard(
params,
this._configuration.clipboardOptions
);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-clipboard",
],
content: "*",
action: "copyAllToClipBoard",
},
{
type: "action",
menu: "menu.clipboard",
order: -200,
description: "placeholder.copyAllToClipBoard",
name: "copyAllToClipBoard",
conditions: CONDITION_HAS_CONTENT,
onClick: (configuration, params) => {
const options = deepCopy(this._configuration.clipboardOptions);
options.copyHeaders = false;
copyAllToClipBoard(params, options);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-clipboard",
],
content: "*",
action: "copyAllWithHeaderToClipBoard",
},
{
type: "action",
menu: "menu.clipboard",
order: -200,
description: "placeholder.copyAllWithHeaderToClipBoard",
name: "copyAllWithHeaderToClipBoard",
conditions: CONDITION_HAS_CONTENT,
onClick: (configuration, params) => {
const options = deepCopy(this._configuration.clipboardOptions);
options.copyHeaders = true;
copyAllToClipBoard(params, options);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: ["default-data-list-footer", "default-data-list-footer-export"],
content: "*",
action: "export-csv-or-excel",
},
{
type: "action",
menu: "menu.export",
order: -100,
description: "button.export",
name: "export",
conditions: CONDITION_HAS_CONTENT,
onClick: (configuration, params) => {
exportList(
this.dialog,
params,
this.localstorageExportSettings,
this.moduleConfig
);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: [
"default-data-list-footer",
"default-data-list-footer-selection",
],
content: "*",
action: "invertSelection",
},
{
order: -400,
type: "action",
description: "placeholder.invertSelection",
name: "invertSelection",
conditions: CONDITION_HAS_SELECTION,
onClick: (configuration, params) => {
return invertSelection(params);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: ["default-data-list-context-menu"],
content: "*",
action: "select.same.values",
},
{
type: "action",
order: -400,
description: "button.select.same.values",
name: "export",
onClick: (configuration, params) => {
selectSameValues(configuration, params);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: "edit-data-list-context-menu",
content: "*",
action: "apply-for-all",
},
{
type: "action",
order: -50,
description: "button.apply.for.all",
name: "export",
conditions: CONDITION_IS_EDITABLE_ATTRIBUTE,
onClick: (config, params) => {
applyForAll(
config,
params,
this.notificationService,
this.translateService,
this.appContext,
this.validationService
);
},
}
);
this.appContext.browserContext.addIfNotExists(
{
target: "edit-data-list-context-menu",
content: "*",
action: "apply-for-selection",
},
{
type: "action",
order: -40,
description: "button.apply.for.selection",
name: "export",
conditions: CONDITION_IS_EDITABLE_ATTRIBUTE,
onClick: (configuration, params) => {
const applyingRowData = params.cell.row.rowData;
const applyingAttribute =
applyingRowData[Attributes.toSourceField(params.column.field)];
if (
!params.column ||
!params.column.field ||
(applyingAttribute.errors && applyingAttribute.errors.length > 0)
) {
this.notificationService.error(
this.translateService.instant("ERROR"),
this.translateService.instant(
"message.default-data-list-footer.error"
)
);
return;
}
if (params.column.type !== "attribute") {
console.error(
"Apply for selection is not available for non attribute editable fields"
);
}
this.appContext.browserContext
.subscribe(APPLY_FOR_SELECTORS)
.pipe(first())
.subscribe((selectors) => {
if (selectors.length !== 0 && selectors[0].horizontal) {
applyHorizontally(params, this.validationService, true);
} else {
applyForSelectedVertically(params, this.validationService);
}
});
},
}
);
}
ngOnDestroy(): void {}
}
//Below this are all functions registered for the table footer.
// You are not allowed to use the this context in those functions, since all you need should be passed via the DataListActionParameter
function applyForAll(
configuration,
params: DataListActionParameter,
notificationService: CustomNotificationService,
translateService: TranslateService,
appContext: AppContext,
validationService: ValidationService
) {
const applyingRowData = params.cell.row.rowData;
const applyingAttribute =
applyingRowData[Attributes.toSourceField(params.column.field)];
if (
!params.column ||
!params.column.field ||
(applyingAttribute.errors && applyingAttribute.errors.length > 0)
) {
notificationService.error(
translateService.instant("ERROR"),
translateService.instant("default-data-list-footer.error.message")
);
return;
}
if (params.column.type !== "attribute") {
console.error(
"Apply for selection is not available for non attribute editable fields"
);
}
appContext.browserContext
.subscribe(APPLY_FOR_SELECTORS)
.pipe(first())
.subscribe((selectors) => {
if (selectors.length !== 0 && selectors[0].horizontal) {
applyHorizontally(params, validationService, false);
} else {
applyForAllVertically(params, validationService);
}
});
}
function selectSameValues(configuration, params: DataListActionParameter) {
if (!params.column || !params.column.field) {
return;
}
const applyingRowData = params.cell.row.rowData;
const field = Attributes.toSourceField(params.column.field);
const applyingAttribute = applyingRowData[field];
const applyingValue = applyingAttribute ? applyingAttribute.source : null;
const primaryKey = params.grid.primaryKey(params.grid.configuration);
params.grid.listService.data.pipe(first()).subscribe((data) => {
const newSelections = [];
data.forEach((entry) => {
const key = entry[primaryKey];
const rowData = entry[field];
if (applyingValue === null) {
if (!rowData) {
newSelections.push(key);
}
return;
}
if (!rowData) {
return;
}
if (params.column.type === "attribute") {
if (rowData.value === applyingAttribute.value) {
newSelections.push(key);
}
} else {
if (rowData === applyingAttribute) {
newSelections.push(key);
}
}
});
params.grid.grid.deselectAllRows();
params.grid.grid.selectRows(newSelections);
// @ts-ignore ignites interface on what it really needs is wrong - tsignore this
params.grid.grid.rowSelected.emit({
newSelection: params.grid.grid.selectedRows,
});
});
}
function applyForAllVertically(
params: DataListActionParameter,
validationService: ValidationService
) {
const applyingRowData = params.cell.row.rowData;
const valueField = params.column.field;
const sourceField = Attributes.toSourceField(valueField);
const applyingAttribute = applyingRowData[sourceField];
const applyingValue = applyingAttribute.source;
//TODO This will make problems for nested tables in the hierachy grid
const primaryKey = params.grid.primaryKey(params.grid.configuration);
params.grid.listService.data.pipe(first()).subscribe((data) => {
const keysToUpdate = [];
data.forEach((entry) => {
const key = entry[primaryKey];
if (key === applyingRowData[primaryKey]) {
return;
}
const rowAttribute = deepCopy(entry[sourceField]);
if (!rowAttribute) {
return;
}
if (rowAttribute["read-only"]) {
return;
}
rowAttribute.source = applyingValue;
const eventAttributeData = {
[applyingAttribute.identifier]: rowAttribute,
};
params.api.fireEditedRowsEvent(key, entry, null, eventAttributeData);
const row = params.grid.grid.getRowByKey(key);
// no row found? then the data is outside the current view port
// we only need to fire the correct event
if (!row) {
entry[sourceField] = rowAttribute;
return;
}
updateRowOrCell(rowAttribute, row, valueField, params, validationService);
keysToUpdate.push(key);
});
params.grid.validateList(null, null);
params.api.forceReRender(keysToUpdate);
});
}
function applyHorizontally(
params: DataListActionParameter,
validationService: ValidationService,
onlySelectedCells: boolean
) {
const row = params.cell.row;
const applyingRowData = row.rowData;
let selectedItemsIdentifier = [];
if (onlySelectedCells) {
selectedItemsIdentifier = getSelectedItemsIdentifier(params.grid.grid);
}
const applyingAttribute =
applyingRowData[Attributes.toSourceField(params.column.field)];
const applyingValue = applyingAttribute.source;
Attributes.rowAttributes(applyingRowData).forEach((attribute) => {
if (!attribute || Attributes.isReadOnly(attribute)) {
return;
}
if (
onlySelectedCells &&
!selectedItemsIdentifier.includes(attribute.identifier)
) {
return;
}
attribute.source = deepCopy(applyingValue);
const valueField = Attributes.toValueField(attribute.identifier);
const cell = params.grid.grid.getCellByColumn(row.index, valueField);
if (cell) {
updateRowOrCell(attribute, row, valueField, params, validationService);
}
const eventAttributeData = {
[attribute.identifier]: deepCopy(attribute),
};
params.api.fireEditedRowsEvent(
applyingRowData.attributeId,
applyingRowData,
null,
eventAttributeData
);
});
const primaryKey =
applyingRowData[params.grid.primaryKey(params.grid.configuration)];
params.grid.validateList(null, null);
params.api.forceReRender([primaryKey]);
}
function getSelectedItemsIdentifier(grid: IgxGridComponent): string[] {
const columnFields = getColumnFields(grid);
const selections: string[] = [];
grid.getSelectedRanges().forEach((selectedRange) => {
columnFields
.slice(
<number>selectedRange.columnStart,
<number>selectedRange.columnEnd + 1
)
.forEach((field) => selections.push(field.split("#")[0]));
});
return selections;
}
function applyForSelectedVertically(
params: DataListActionParameter,
validationService: ValidationService
) {
const applyingRowData = params.cell.row.rowData;
const valueField = params.column.field;
const sourceField = Attributes.toSourceField(valueField);
const applyingAttribute = applyingRowData[sourceField];
const applyingValue = applyingAttribute.source;
const keysToUpdate = [];
const primaryKey = params.grid.primaryKey(params.grid.configuration);
const missingRows = [];
params.grid.grid.selectedRows.forEach((key) => {
if (key === applyingRowData[primaryKey]) {
return;
}
const gridRow = params.grid.grid.getRowByKey(key);
if (!gridRow) {
missingRows.push(key);
return;
}
const row = gridRow.rowData;
const rowAttribute = deepCopy(row[sourceField]);
if (rowAttribute["read-only"]) {
return;
}
const eventAttributeData = {
[applyingAttribute.identifier]: rowAttribute,
};
params.api.fireEditedRowsEvent(key, row, null, eventAttributeData);
rowAttribute.source = applyingValue;
keysToUpdate.push(key);
updateRowOrCell(
rowAttribute,
gridRow,
valueField,
params,
validationService
);
});
if (missingRows.length !== 0) {
params.grid.listService.data.pipe(first()).subscribe((rows) => {
const rowsToUpdate = rows.filter(
(entry) => missingRows.indexOf(entry[primaryKey]) !== -1
);
rowsToUpdate.forEach((row) => {
const key = row[primaryKey];
const rowAttribute = row[sourceField];
rowAttribute.source = deepCopy(applyingValue);
row[sourceField] = rowAttribute;
validationService.validateAttribute(rowAttribute);
const eventAttributeData = {
[applyingAttribute.identifier]: deepCopy(applyingAttribute),
};
params.api.fireEditedRowsEvent(key, row, null, eventAttributeData);
});
});
}
params.grid.validateList(null, null);
params.api.forceReRender(keysToUpdate);
}
/**
* `field` must be a #value field to ensure, that we find the corresponding cell
*
* @param rowAttribute
* @param row
* @param field #value field for the attribute to update
* @param params
* @param validationService
*/
export function updateRowOrCell(
rowAttribute,
row,
field: string,
params: DataListActionParameter,
validationService: ValidationService
) {
// validate newly updated attribute
validationService.validateAttribute(rowAttribute);
if (params.grid.configuration.rowEditable) {
// Trigger edit event
const rowData = deepCopy(row.rowData);
rowData[field] = rowAttribute;
row.update(rowData);
} else {
let cell = params.grid.grid.getCellByColumn(row.index, field);
if (cell) {
Attributes.updateCell(cell, rowAttribute);
}
}
}
function invertSelection(params: DataListActionParameter) {
params.grid.grid.clearCellSelection();
if (params.grid.grid.selectedRows.length > 0) {
let selectedRowIds = params.grid.grid.selectedRows;
params.grid.grid.selectAllRows();
params.grid.grid.deselectRows(selectedRowIds);
} else {
params.grid.grid.selectAllRows();
}
params.grid.grid.cdr.detectChanges();
// @ts-ignore it looks like the ignite api is demanding values that are not needed - ts ignore this so
params.grid.grid.rowSelected.emit({
newSelection: params.grid.grid.selectedRows,
});
}
function copySelectedCellsToClipBoard(params: DataListActionParameter) {
const columnFields = getColumnFields(params.grid.grid);
const primaryKey = params.grid._primaryKey;
const selections = new Map<number, string[]>();
const selectedValues = [];
params.grid.grid.getSelectedRanges().forEach((selectedRange) => {
let fields = columnFields.slice(
<number>selectedRange.columnStart,
<number>selectedRange.columnEnd + 1
);
for (
let rowIndex = selectedRange.rowStart;
rowIndex <= selectedRange.rowEnd;
rowIndex++
) {
if (selections.has(rowIndex)) {
let mapFields = selections.get(rowIndex);
fields.forEach((field) => mapFields.push(field));
} else {
selections.set(rowIndex, fields);
}
}
});
selections.forEach((fields, rowIndex) => {
const gridRow = params.grid.grid.getRowByIndex(rowIndex).rowData;
let valueString = `${
selections.size > 1 ? gridRow[primaryKey] + "->\t" : ""
} ${fields.map((field) => mapFieldValue(gridRow[field])).join("\t")}`;
selectedValues.push(valueString);
});
UtilService.copyTextToClipboard(selectedValues.join("\n"));
}
function getColumnFields(grid): string[] {
return grid.columns
.filter((column) => !column.hidden)
.map((column) => column.field);
}
function copySelectionToClipBoard(
params: DataListActionParameter,
options: ClipboardOptions
) {
if (params.grid.isEagerLoading) {
let primaryKey = params.grid.grid.primaryKey;
let data = params.grid.grid.data;
let filtered = data.filter((row) =>
params.grid.grid.selectedRows.includes(row[primaryKey].toString())
);
copyToClipboard(filtered, params, options);
} else {
let filtered = params.grid.listService.cachedData.filter(
(data) => data.selected
);
copyToClipboard(filtered, params, options);
}
}
function copySeletedColumnCellsToClipBoard(
params: DataListActionParameter,
options: ClipboardOptions
) {
const data = params.grid.isEagerLoading
? params.grid.grid.data
: params.grid.listService.cachedData.filter((cd) => !cd.cache);
const columns = [params.column.field];
copyColumnsToClipboard(data, columns, params, options);
}
function copyAllToClipBoard(
params: DataListActionParameter,
options: ClipboardOptions
) {
let data = params.grid.isEagerLoading
? params.grid.grid.data
: params.grid.listService.cachedData.filter((cd) => !cd.cache);
copyToClipboard(data, params, options);
}
function copyToClipboard(
data,
params: DataListActionParameter,
options: ClipboardOptions
) {
// only export visible columns, that have a set "field" property
// and actually exist in the data model (i.e., if only some cells/columns have been selected)
let columns = params.grid.grid.visibleColumns
.map((col) => col.field)
.filter((field) => field && field !== "")
.filter(
(field) =>
data &&
data[0] &&
(data[0].hasOwnProperty(field) ||
Attributes.hasRowAttribute(data[0], field))
);
copyColumnsToClipboard(data, columns, params, options);
}
function copyColumnsToClipboard(
data,
columns,
params: DataListActionParameter,
options: ClipboardOptions
) {
let clipboardtext = "";
if (options.copyHeaders) {
let columnsHeaders = params.grid.grid.visibleColumns
.map((col) => col.header)
.filter((header) => header && header !== "")
.join(options.separator);
clipboardtext = columnsHeaders + "\n";
}
clipboardtext =
clipboardtext +
data
.map((datarow) =>
columns
.map((field) => datarow[field])
.map((value) => mapFieldValue(value))
.join(options.separator)
)
.join("\n");
UtilService.copyTextToClipboard(clipboardtext);
}
function mapFieldValue(value: any): string {
if (!value && value !== 0) {
return "";
} else if (Array.isArray(value) && value.length > 0) {
return value[0].hasOwnProperty("exportValue")
? value.map((v) => v.exportValue).join(", ")
: value.join(", ");
} else if (typeof value === "object") {
return value.hasOwnProperty("exportValue")
? String(value.exportValue)
: value.toString();
} else {
return value.toString().trim();
}
}
function exportList(
dialog: DialogService,
params: DataListActionParameter,
localstorageExportSettings: LocalStorageEntry,
moduleConfig: NmModuleConfiguration
) {
let dialogRef = dialog.open(DataListExportComponent, {
autoFocus: true,
width: "450px",
height: "530px",
});
let exportSettings;
if (localstorageExportSettings.exists()) {
exportSettings = JSON.parse(localstorageExportSettings.value);
}
dialogRef.componentInstance.showDownload = firstNonNull(
params.grid.configuration["show-download-button"],
moduleConfig["nm-default-data-list-footer.show-download-button"],
true
);
dialogRef.componentInstance.showExport = firstNonNull(
params.grid.configuration["show-export-button"],
moduleConfig["nm-default-data-list-footer.show-export-button"],
true
);
dialogRef.componentInstance.exportFormat = getOrDefault(
exportSettings.exportFormat,
"CSV"
);
dialogRef.componentInstance.assetFormat = getOrDefault(
exportSettings.assetFormat,
"thumbnail"
);
dialogRef.componentInstance.csvSeparator = getOrDefault(
exportSettings.csvSeparator,
";"
);
dialogRef.componentInstance.csvEncoding = getOrDefault(
exportSettings.csvEncoding,
"ISO-8859-15"
);
dialogRef.componentInstance.csvHeaders = getOrDefault(
exportSettings.csvHeaders,
true
);
dialogRef.componentInstance.onlySelectedRows = getOrDefault(
exportSettings.onlySelectedRows,
false
);
dialogRef.componentInstance.showExportAssetFormats = getOrDefault(
params.grid.configuration.showExportAssetFormats,
true
);
dialogRef.componentInstance.showCsvHeaders = getOrDefault(
params.grid.configuration.showCsvHeaders,
true
);
dialogRef.afterClosed().subscribe((data) => {
if (data) {
localstorageExportSettings.value = JSON.stringify(data);
params.export.emit(data);
}
});
}
export interface DataListActionParameter {
grid: DataListComponent;
api: DataListApi;
export: EventEmitter<any>;
[key: string]: any;
}