@slickgrid-universal/composite-editor-component 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/compositeEditor.factory.js +237 -237
- package/dist/commonjs/index.js +18 -18
- package/dist/commonjs/slick-composite-editor.component.js +890 -890
- package/dist/esm/compositeEditor.factory.js +233 -233
- package/dist/esm/index.js +2 -2
- package/dist/esm/slick-composite-editor.component.js +886 -886
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/{commonjs → types}/compositeEditor.factory.d.ts +35 -34
- package/dist/types/compositeEditor.factory.d.ts.map +1 -0
- package/dist/{esm → types}/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -0
- package/dist/{esm → types}/slick-composite-editor.component.d.ts +178 -177
- package/dist/types/slick-composite-editor.component.d.ts.map +1 -0
- package/package.json +9 -9
- package/dist/commonjs/index.d.ts +0 -2
- package/dist/commonjs/slick-composite-editor.component.d.ts +0 -177
- package/dist/esm/compositeEditor.factory.d.ts +0 -34
|
@@ -1,891 +1,891 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SlickCompositeEditorComponent = void 0;
|
|
4
|
-
const utils_1 = require("@slickgrid-universal/utils");
|
|
5
|
-
const common_1 = require("@slickgrid-universal/common");
|
|
6
|
-
const compositeEditor_factory_1 = require("./compositeEditor.factory");
|
|
7
|
-
const DEFAULT_ON_ERROR = (error) => console.log(error.message);
|
|
8
|
-
class SlickCompositeEditorComponent {
|
|
9
|
-
get eventHandler() {
|
|
10
|
-
return this._eventHandler;
|
|
11
|
-
}
|
|
12
|
-
get dataView() {
|
|
13
|
-
return this.grid.getData();
|
|
14
|
-
}
|
|
15
|
-
get dataViewLength() {
|
|
16
|
-
return this.dataView.getLength();
|
|
17
|
-
}
|
|
18
|
-
get formValues() {
|
|
19
|
-
return this._formValues;
|
|
20
|
-
}
|
|
21
|
-
get editors() {
|
|
22
|
-
return this._editors;
|
|
23
|
-
}
|
|
24
|
-
set editors(editors) {
|
|
25
|
-
this._editors = editors;
|
|
26
|
-
}
|
|
27
|
-
get gridOptions() {
|
|
28
|
-
var _a;
|
|
29
|
-
return (_a = this.grid) === null || _a === void 0 ? void 0 : _a.getOptions();
|
|
30
|
-
}
|
|
31
|
-
constructor() {
|
|
32
|
-
this._columnDefinitions = [];
|
|
33
|
-
this._lastActiveRowNumber = -1;
|
|
34
|
-
this._formValues = null;
|
|
35
|
-
this.gridService = null;
|
|
36
|
-
this._eventHandler = new Slick.EventHandler();
|
|
37
|
-
this._bindEventService = new common_1.BindingEventService();
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* initialize the Composite Editor by passing the SlickGrid object and the container service
|
|
41
|
-
*
|
|
42
|
-
* Note: we aren't using DI in the constructor simply to be as framework agnostic as possible,
|
|
43
|
-
* we are simply using this init() function with a very basic container service to do the job
|
|
44
|
-
*/
|
|
45
|
-
init(grid, containerService) {
|
|
46
|
-
var _a, _b;
|
|
47
|
-
this.grid = grid;
|
|
48
|
-
this.gridService = containerService.get('GridService');
|
|
49
|
-
this.translaterService = containerService.get('TranslaterService');
|
|
50
|
-
if (!this.gridService) {
|
|
51
|
-
throw new Error('[Slickgrid-Universal] it seems that the GridService is not being loaded properly, make sure the Container Service is properly implemented.');
|
|
52
|
-
}
|
|
53
|
-
if (this.gridOptions.enableTranslate && (!this.translaterService || !this.translaterService.translate)) {
|
|
54
|
-
throw new Error('[Slickgrid-Universal] requires a Translate Service to be installed and configured when the grid option "enableTranslate" is enabled.');
|
|
55
|
-
}
|
|
56
|
-
// get locales provided by user in forRoot or else use default English locales via the Constants
|
|
57
|
-
this._locales = (_b = (_a = this.gridOptions) === null || _a === void 0 ? void 0 : _a.locales) !== null && _b !== void 0 ? _b : common_1.Constants.locales;
|
|
58
|
-
}
|
|
59
|
-
/** Dispose of the Component & unsubscribe all events */
|
|
60
|
-
dispose() {
|
|
61
|
-
this._eventHandler.unsubscribeAll();
|
|
62
|
-
this._bindEventService.unbindAll();
|
|
63
|
-
this._formValues = null;
|
|
64
|
-
this.disposeComponent();
|
|
65
|
-
}
|
|
66
|
-
/** Dispose of the Component without unsubscribing any events */
|
|
67
|
-
disposeComponent() {
|
|
68
|
-
var _a, _b, _c;
|
|
69
|
-
// protected _editorContainers!: Array<HTMLElement | null>;
|
|
70
|
-
(_a = this._modalBodyTopValidationElm) === null || _a === void 0 ? void 0 : _a.remove();
|
|
71
|
-
(_b = this._modalSaveButtonElm) === null || _b === void 0 ? void 0 : _b.remove();
|
|
72
|
-
if (typeof ((_c = this._modalElm) === null || _c === void 0 ? void 0 : _c.remove) === 'function') {
|
|
73
|
-
this._modalElm.remove();
|
|
74
|
-
// remove the body backdrop click listener, every other listeners will be dropped automatically since we destroy the component
|
|
75
|
-
document.body.classList.remove('slick-modal-open');
|
|
76
|
-
}
|
|
77
|
-
this._editorContainers = [];
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Dynamically change value of an input from the Composite Editor form.
|
|
81
|
-
*
|
|
82
|
-
* NOTE: user might get an error thrown when trying to apply a value on a Composite Editor that was not found in the form,
|
|
83
|
-
* but in some cases the user might still want the value to be applied to the formValues so that it will be sent to the save in final item data context
|
|
84
|
-
* and when that happens, you can just skip that error so it won't throw.
|
|
85
|
-
* @param {String | Column} columnIdOrDef - column id or column definition
|
|
86
|
-
* @param {*} newValue - the new value
|
|
87
|
-
* @param {Boolean} skipMissingEditorError - defaults to False, skipping the error when the Composite Editor was not found will allow to still apply the value into the formValues object
|
|
88
|
-
* @param {Boolean} triggerOnCompositeEditorChange - defaults to True, will this change trigger a onCompositeEditorChange event?
|
|
89
|
-
*/
|
|
90
|
-
changeFormInputValue(columnIdOrDef, newValue, skipMissingEditorError = false, triggerOnCompositeEditorChange = true) {
|
|
91
|
-
var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
92
|
-
const columnDef = this.getColumnByObjectOrId(columnIdOrDef);
|
|
93
|
-
const columnId = typeof columnIdOrDef === 'string' ? columnIdOrDef : (_a = columnDef === null || columnDef === void 0 ? void 0 : columnDef.id) !== null && _a !== void 0 ? _a : '';
|
|
94
|
-
const editor = (_b = this._editors) === null || _b === void 0 ? void 0 : _b[columnId];
|
|
95
|
-
let outputValue = newValue;
|
|
96
|
-
if (!editor && !skipMissingEditorError) {
|
|
97
|
-
throw new Error(`Composite Editor with column id "${columnId}" not found.`);
|
|
98
|
-
}
|
|
99
|
-
if (typeof (editor === null || editor === void 0 ? void 0 : editor.setValue) === 'function' && Array.isArray(this._editorContainers)) {
|
|
100
|
-
editor.setValue(newValue, true, triggerOnCompositeEditorChange);
|
|
101
|
-
const editorContainerElm = this._editorContainers.find(editorElm => editorElm.dataset.editorid === columnId);
|
|
102
|
-
const excludeDisabledFieldFormValues = (_f = (_d = (_c = this.gridOptions) === null || _c === void 0 ? void 0 : _c.compositeEditorOptions) === null || _d === void 0 ? void 0 : _d.excludeDisabledFieldFormValues) !== null && _f !== void 0 ? _f : false;
|
|
103
|
-
if (!editor.disabled || (editor.disabled && !excludeDisabledFieldFormValues)) {
|
|
104
|
-
(_g = editorContainerElm === null || editorContainerElm === void 0 ? void 0 : editorContainerElm.classList) === null || _g === void 0 ? void 0 : _g.add('modified');
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
outputValue = '';
|
|
108
|
-
(_h = editorContainerElm === null || editorContainerElm === void 0 ? void 0 : editorContainerElm.classList) === null || _h === void 0 ? void 0 : _h.remove('modified');
|
|
109
|
-
}
|
|
110
|
-
// when the field is disabled, we will only allow a blank value anything else will be disregarded
|
|
111
|
-
if (editor.disabled && (outputValue !== '' || outputValue !== null || outputValue !== undefined || outputValue !== 0)) {
|
|
112
|
-
outputValue = '';
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// is the field a complex object, like "address.streetNumber"
|
|
116
|
-
// we'll set assign the value as a complex object following the `field` dot notation
|
|
117
|
-
const fieldName = (_j = columnDef === null || columnDef === void 0 ? void 0 : columnDef.field) !== null && _j !== void 0 ? _j : '';
|
|
118
|
-
if (columnDef && (fieldName === null || fieldName === void 0 ? void 0 : fieldName.includes('.'))) {
|
|
119
|
-
// when it's a complex object, user could override the object path (where the editable object is located)
|
|
120
|
-
// else we use the path provided in the Field Column Definition
|
|
121
|
-
const objectPath = (_m = (_l = (_k = columnDef.internalColumnEditor) === null || _k === void 0 ? void 0 : _k.complexObjectPath) !== null && _l !== void 0 ? _l : fieldName) !== null && _m !== void 0 ? _m : '';
|
|
122
|
-
(0, utils_1.setDeepValue)((_o = this._formValues) !== null && _o !== void 0 ? _o : {}, objectPath, newValue);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
this._formValues = { ...this._formValues, [columnId]: outputValue };
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Dynamically update the `formValues` object directly without triggering the onCompositeEditorChange event.
|
|
130
|
-
* The fact that this doesn't trigger an event, might not always be good though, in these cases you are probably better with using the changeFormInputValue() method
|
|
131
|
-
* @param {String | Column} columnIdOrDef - column id or column definition
|
|
132
|
-
* @param {*} newValue - the new value
|
|
133
|
-
*/
|
|
134
|
-
changeFormValue(columnIdOrDef, newValue) {
|
|
135
|
-
var _a, _b, _c, _d, _f;
|
|
136
|
-
const columnDef = this.getColumnByObjectOrId(columnIdOrDef);
|
|
137
|
-
const columnId = typeof columnIdOrDef === 'string' ? columnIdOrDef : (_a = columnDef === null || columnDef === void 0 ? void 0 : columnDef.id) !== null && _a !== void 0 ? _a : '';
|
|
138
|
-
// is the field a complex object, like "address.streetNumber"
|
|
139
|
-
// we'll set assign the value as a complex object following the `field` dot notation
|
|
140
|
-
const fieldName = (_b = columnDef === null || columnDef === void 0 ? void 0 : columnDef.field) !== null && _b !== void 0 ? _b : columnIdOrDef;
|
|
141
|
-
if (fieldName === null || fieldName === void 0 ? void 0 : fieldName.includes('.')) {
|
|
142
|
-
// when it's a complex object, user could override the object path (where the editable object is located)
|
|
143
|
-
// else we use the path provided in the Field Column Definition
|
|
144
|
-
const objectPath = (_f = (_d = (_c = columnDef === null || columnDef === void 0 ? void 0 : columnDef.internalColumnEditor) === null || _c === void 0 ? void 0 : _c.complexObjectPath) !== null && _d !== void 0 ? _d : fieldName) !== null && _f !== void 0 ? _f : '';
|
|
145
|
-
(0, utils_1.setDeepValue)(this._formValues, objectPath, newValue);
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
this._formValues = { ...this._formValues, [columnId]: newValue };
|
|
149
|
-
}
|
|
150
|
-
this._formValues = (0, utils_1.deepMerge)({}, this._itemDataContext, this._formValues);
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Dynamically change an Editor option of the Composite Editor form
|
|
154
|
-
* For example, a use case could be to dynamically change the "minDate" of another Date Editor in the Composite Editor form.
|
|
155
|
-
* @param {String} columnId - column id
|
|
156
|
-
* @param {*} newValue - the new value
|
|
157
|
-
*/
|
|
158
|
-
changeFormEditorOption(columnId, optionName, newOptionValue) {
|
|
159
|
-
var _a;
|
|
160
|
-
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
161
|
-
// change an Editor option (not all Editors have that method, so make sure it exists before trying to call it)
|
|
162
|
-
if (editor === null || editor === void 0 ? void 0 : editor.changeEditorOption) {
|
|
163
|
-
editor.changeEditorOption(optionName, newOptionValue);
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
throw new Error(`Editor with column id "${columnId}" not found OR the Editor does not support "changeEditorOption" (current only available with AutoComplete, Date, MultipleSelect & SingleSelect Editors).`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Disable (or enable) an input of the Composite Editor form
|
|
171
|
-
* @param {String} columnId - column definition id
|
|
172
|
-
* @param isDisabled - defaults to True, are we disabling the associated form input
|
|
173
|
-
*/
|
|
174
|
-
disableFormInput(columnId, isDisabled = true) {
|
|
175
|
-
var _a;
|
|
176
|
-
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
177
|
-
if ((editor === null || editor === void 0 ? void 0 : editor.disable) && Array.isArray(this._editorContainers)) {
|
|
178
|
-
editor.disable(isDisabled);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/** Entry point to initialize and open the Composite Editor modal window */
|
|
182
|
-
openDetails(options) {
|
|
183
|
-
var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
184
|
-
const onError = (_a = options.onError) !== null && _a !== void 0 ? _a : DEFAULT_ON_ERROR;
|
|
185
|
-
const defaultOptions = {
|
|
186
|
-
backdrop: 'static',
|
|
187
|
-
showCloseButtonOutside: true,
|
|
188
|
-
shouldClearRowSelectionAfterMassAction: true,
|
|
189
|
-
viewColumnLayout: 'auto',
|
|
190
|
-
modalType: 'edit',
|
|
191
|
-
};
|
|
192
|
-
try {
|
|
193
|
-
if (!this.grid || (this.grid.getEditorLock().isActive() && !this.grid.getEditorLock().commitCurrentEdit())) {
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
this._formValues = null; // make sure there's no leftover from previous change
|
|
197
|
-
this._options = { ...defaultOptions, ...this.gridOptions.compositeEditorOptions, ...options, labels: { ...(_b = this.gridOptions.compositeEditorOptions) === null || _b === void 0 ? void 0 : _b.labels, ...options === null || options === void 0 ? void 0 : options.labels } }; // merge default options with user options
|
|
198
|
-
this._options.backdrop = options.backdrop !== undefined ? options.backdrop : 'static';
|
|
199
|
-
const viewColumnLayout = this._options.viewColumnLayout || 1;
|
|
200
|
-
const activeCell = this.grid.getActiveCell();
|
|
201
|
-
const activeColIndex = (_c = activeCell === null || activeCell === void 0 ? void 0 : activeCell.cell) !== null && _c !== void 0 ? _c : 0;
|
|
202
|
-
const activeRow = (_d = activeCell === null || activeCell === void 0 ? void 0 : activeCell.row) !== null && _d !== void 0 ? _d : 0;
|
|
203
|
-
const gridUid = this.grid.getUID() || '';
|
|
204
|
-
let headerTitle = options.headerTitle || '';
|
|
205
|
-
// execute callback before creating the modal window (that is in short the first event in the lifecycle)
|
|
206
|
-
if (typeof this._options.onBeforeOpen === 'function') {
|
|
207
|
-
this._options.onBeforeOpen();
|
|
208
|
-
}
|
|
209
|
-
if (this.hasRowSelectionEnabled() && this._options.modalType === 'auto-mass' && this.grid.getSelectedRows) {
|
|
210
|
-
const selectedRowsIndexes = this.grid.getSelectedRows() || [];
|
|
211
|
-
if (selectedRowsIndexes.length > 0) {
|
|
212
|
-
this._options.modalType = 'mass-selection';
|
|
213
|
-
if (options === null || options === void 0 ? void 0 : options.headerTitleMassSelection) {
|
|
214
|
-
headerTitle = options === null || options === void 0 ? void 0 : options.headerTitleMassSelection;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
this._options.modalType = 'mass-update';
|
|
219
|
-
if (options === null || options === void 0 ? void 0 : options.headerTitleMassUpdate) {
|
|
220
|
-
headerTitle = options === null || options === void 0 ? void 0 : options.headerTitleMassUpdate;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
const modalType = this._options.modalType || 'edit';
|
|
225
|
-
if (!this.gridOptions.editable) {
|
|
226
|
-
onError({ type: 'error', code: 'EDITABLE_GRID_REQUIRED', message: 'Your grid must be editable in order to use the Composite Editor Modal.' });
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
else if (!this.gridOptions.enableCellNavigation) {
|
|
230
|
-
onError({ type: 'error', code: 'ENABLE_CELL_NAVIGATION_REQUIRED', message: 'Composite Editor requires the flag "enableCellNavigation" to be set to True in your Grid Options.' });
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
else if (!this.gridOptions.enableAddRow && (modalType === 'clone' || modalType === 'create')) {
|
|
234
|
-
onError({ type: 'error', code: 'ENABLE_ADD_ROW_REQUIRED', message: 'Composite Editor requires the flag "enableAddRow" to be set to True in your Grid Options when cloning/creating a new item.' });
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
else if (!activeCell && (modalType === 'clone' || modalType === 'edit')) {
|
|
238
|
-
onError({ type: 'warning', code: 'NO_RECORD_FOUND', message: 'No records selected for edit or clone operation.' });
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
const isWithMassChange = (modalType === 'mass-update' || modalType === 'mass-selection');
|
|
243
|
-
const dataContext = !isWithMassChange ? this.grid.getDataItem(activeRow) : {};
|
|
244
|
-
this._originalDataContext = (0, utils_1.deepCopy)(dataContext);
|
|
245
|
-
this._columnDefinitions = this.grid.getColumns();
|
|
246
|
-
const selectedRowsIndexes = this.hasRowSelectionEnabled() ? this.grid.getSelectedRows() : [];
|
|
247
|
-
const fullDatasetLength = (_g = (_f = this.dataView) === null || _f === void 0 ? void 0 : _f.getItemCount()) !== null && _g !== void 0 ? _g : 0;
|
|
248
|
-
this._lastActiveRowNumber = activeRow;
|
|
249
|
-
const dataContextIds = this.dataView.getAllSelectedIds();
|
|
250
|
-
// focus on a first cell with an Editor (unless current cell already has an Editor then do nothing)
|
|
251
|
-
// also when it's a "Create" modal, we'll scroll to the end of the grid
|
|
252
|
-
const rowIndex = modalType === 'create' ? this.dataViewLength : activeRow;
|
|
253
|
-
const hasFoundEditor = this.focusOnFirstColumnCellWithEditor(this._columnDefinitions, dataContext, activeColIndex, rowIndex, isWithMassChange);
|
|
254
|
-
if (!hasFoundEditor) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
if (modalType === 'edit' && !dataContext) {
|
|
258
|
-
onError({ type: 'warning', code: 'ROW_NOT_EDITABLE', message: 'Current row is not editable.' });
|
|
259
|
-
return null;
|
|
260
|
-
}
|
|
261
|
-
else if (modalType === 'mass-selection') {
|
|
262
|
-
if (selectedRowsIndexes.length < 1) {
|
|
263
|
-
onError({ type: 'warning', code: 'ROW_SELECTION_REQUIRED', message: 'You must select some rows before trying to apply new value(s).' });
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
let modalColumns = [];
|
|
268
|
-
if (isWithMassChange) {
|
|
269
|
-
// when using Mass Update, we only care about the columns that have the "massUpdate: true", we disregard anything else
|
|
270
|
-
modalColumns = this._columnDefinitions.filter(col => { var _a; return col.editor && ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.massUpdate) === true; });
|
|
271
|
-
}
|
|
272
|
-
else {
|
|
273
|
-
modalColumns = this._columnDefinitions.filter(col => col.editor);
|
|
274
|
-
}
|
|
275
|
-
// user could optionally show the form inputs in a specific order instead of using default column definitions order
|
|
276
|
-
if (modalColumns.some(col => { var _a; return ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.compositeEditorFormOrder) !== undefined; })) {
|
|
277
|
-
modalColumns.sort((col1, col2) => {
|
|
278
|
-
var _a, _b, _c, _d;
|
|
279
|
-
const val1 = (_b = (_a = col1 === null || col1 === void 0 ? void 0 : col1.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.compositeEditorFormOrder) !== null && _b !== void 0 ? _b : Infinity;
|
|
280
|
-
const val2 = (_d = (_c = col2 === null || col2 === void 0 ? void 0 : col2.internalColumnEditor) === null || _c === void 0 ? void 0 : _c.compositeEditorFormOrder) !== null && _d !== void 0 ? _d : Infinity;
|
|
281
|
-
return (0, common_1.numericSortComparer)(val1, val2, common_1.SortDirectionNumber.asc);
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
// open the editor modal and we can also provide a header title with optional parsing pulled from the dataContext, via template {{ }}
|
|
285
|
-
// for example {{title}} => display the item title, or even complex object works {{product.name}} => display item product name
|
|
286
|
-
const parsedHeaderTitle = headerTitle.replace(/\{\{(.*?)\}\}/g, (_match, group) => (0, common_1.getDescendantProperty)(dataContext, group));
|
|
287
|
-
const layoutColCount = viewColumnLayout === 'auto' ? this.autoCalculateLayoutColumnCount(modalColumns.length) : viewColumnLayout;
|
|
288
|
-
this._modalElm = (0, common_1.createDomElement)('div', { className: `slick-editor-modal ${gridUid}` });
|
|
289
|
-
const modalContentElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-content' });
|
|
290
|
-
if ((!isNaN(viewColumnLayout) && +viewColumnLayout > 1) || (viewColumnLayout === 'auto' && layoutColCount > 1)) {
|
|
291
|
-
const splitClassName = layoutColCount === 2 ? 'split-view' : 'triple-split-view';
|
|
292
|
-
modalContentElm.classList.add(splitClassName);
|
|
293
|
-
}
|
|
294
|
-
const modalHeaderTitleElm = (0, common_1.createDomElement)('div', {
|
|
295
|
-
className: 'slick-editor-modal-title',
|
|
296
|
-
innerHTML: (0, common_1.sanitizeTextByAvailableSanitizer)(this.gridOptions, parsedHeaderTitle),
|
|
297
|
-
});
|
|
298
|
-
const modalCloseButtonElm = (0, common_1.createDomElement)('button', { type: 'button', textContent: '×', className: 'close', dataset: { action: 'close' } });
|
|
299
|
-
modalCloseButtonElm.setAttribute('aria-label', 'Close');
|
|
300
|
-
if (this._options.showCloseButtonOutside) {
|
|
301
|
-
(_h = modalHeaderTitleElm === null || modalHeaderTitleElm === void 0 ? void 0 : modalHeaderTitleElm.classList) === null || _h === void 0 ? void 0 : _h.add('outside');
|
|
302
|
-
(_j = modalCloseButtonElm === null || modalCloseButtonElm === void 0 ? void 0 : modalCloseButtonElm.classList) === null || _j === void 0 ? void 0 : _j.add('outside');
|
|
303
|
-
}
|
|
304
|
-
const modalHeaderElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-header' });
|
|
305
|
-
modalHeaderElm.setAttribute('aria-label', 'Close');
|
|
306
|
-
modalHeaderElm.appendChild(modalHeaderTitleElm);
|
|
307
|
-
modalHeaderElm.appendChild(modalCloseButtonElm);
|
|
308
|
-
const modalBodyElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-body' });
|
|
309
|
-
this._modalBodyTopValidationElm = (0, common_1.createDomElement)('div', { className: 'validation-summary', style: { display: 'none' } });
|
|
310
|
-
modalBodyElm.appendChild(this._modalBodyTopValidationElm);
|
|
311
|
-
const modalFooterElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-footer' });
|
|
312
|
-
const modalCancelButtonElm = (0, common_1.createDomElement)('button', {
|
|
313
|
-
type: 'button',
|
|
314
|
-
className: 'btn btn-cancel btn-default btn-sm',
|
|
315
|
-
textContent: this.getLabelText('cancelButton', 'TEXT_CANCEL', 'Cancel'),
|
|
316
|
-
dataset: { action: 'cancel' },
|
|
317
|
-
});
|
|
318
|
-
modalCancelButtonElm.setAttribute('aria-label', this.getLabelText('cancelButton', 'TEXT_CANCEL', 'Cancel'));
|
|
319
|
-
let leftFooterText = '';
|
|
320
|
-
let saveButtonText = '';
|
|
321
|
-
switch (modalType) {
|
|
322
|
-
case 'clone':
|
|
323
|
-
saveButtonText = this.getLabelText('cloneButton', 'TEXT_CLONE', 'Clone');
|
|
324
|
-
break;
|
|
325
|
-
case 'mass-update':
|
|
326
|
-
const footerUnparsedText = this.getLabelText('massUpdateStatus', 'TEXT_ALL_X_RECORDS_SELECTED', 'All {{x}} records selected');
|
|
327
|
-
leftFooterText = this.parseText(footerUnparsedText, { x: fullDatasetLength });
|
|
328
|
-
saveButtonText = this.getLabelText('massUpdateButton', 'TEXT_APPLY_MASS_UPDATE', 'Mass Update');
|
|
329
|
-
break;
|
|
330
|
-
case 'mass-selection':
|
|
331
|
-
const selectionUnparsedText = this.getLabelText('massSelectionStatus', 'TEXT_X_OF_Y_MASS_SELECTED', '{{x}} of {{y}} selected');
|
|
332
|
-
leftFooterText = this.parseText(selectionUnparsedText, { x: dataContextIds.length, y: fullDatasetLength });
|
|
333
|
-
saveButtonText = this.getLabelText('massSelectionButton', 'TEXT_APPLY_TO_SELECTION', 'Update Selection');
|
|
334
|
-
break;
|
|
335
|
-
default:
|
|
336
|
-
saveButtonText = this.getLabelText('saveButton', 'TEXT_SAVE', 'Save');
|
|
337
|
-
}
|
|
338
|
-
const selectionCounterElm = (0, common_1.createDomElement)('div', { className: 'footer-status-text', textContent: leftFooterText });
|
|
339
|
-
this._modalSaveButtonElm = (0, common_1.createDomElement)('button', {
|
|
340
|
-
type: 'button', className: 'btn btn-save btn-primary btn-sm',
|
|
341
|
-
textContent: saveButtonText,
|
|
342
|
-
dataset: {
|
|
343
|
-
action: (modalType === 'create' || modalType === 'edit') ? 'save' : modalType,
|
|
344
|
-
ariaLabel: saveButtonText
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
this._modalSaveButtonElm.setAttribute('aria-label', saveButtonText);
|
|
348
|
-
const footerContainerElm = (0, common_1.createDomElement)('div', { className: 'footer-buttons' });
|
|
349
|
-
if (modalType === 'mass-update' || modalType === 'mass-selection') {
|
|
350
|
-
modalFooterElm.appendChild(selectionCounterElm);
|
|
351
|
-
}
|
|
352
|
-
footerContainerElm.appendChild(modalCancelButtonElm);
|
|
353
|
-
footerContainerElm.appendChild(this._modalSaveButtonElm);
|
|
354
|
-
modalFooterElm.appendChild(footerContainerElm);
|
|
355
|
-
modalContentElm.appendChild(modalHeaderElm);
|
|
356
|
-
modalContentElm.appendChild(modalBodyElm);
|
|
357
|
-
modalContentElm.appendChild(modalFooterElm);
|
|
358
|
-
this._modalElm.appendChild(modalContentElm);
|
|
359
|
-
for (const columnDef of modalColumns) {
|
|
360
|
-
if (columnDef.editor) {
|
|
361
|
-
const itemContainer = (0, common_1.createDomElement)('div', { className: `item-details-container editor-${columnDef.id}` });
|
|
362
|
-
if (layoutColCount === 1) {
|
|
363
|
-
itemContainer.classList.add('slick-col-medium-12');
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
itemContainer.classList.add('slick-col-medium-6', `slick-col-xlarge-${12 / layoutColCount}`);
|
|
367
|
-
}
|
|
368
|
-
const templateItemLabelElm = (0, common_1.createDomElement)('div', {
|
|
369
|
-
className: `item-details-label editor-${columnDef.id}`,
|
|
370
|
-
innerHTML: (0, common_1.sanitizeTextByAvailableSanitizer)(this.gridOptions, this.getColumnLabel(columnDef) || 'n/a')
|
|
371
|
-
});
|
|
372
|
-
const templateItemEditorElm = (0, common_1.createDomElement)('div', {
|
|
373
|
-
className: 'item-details-editor-container slick-cell',
|
|
374
|
-
dataset: { editorid: `${columnDef.id}` },
|
|
375
|
-
});
|
|
376
|
-
const templateItemValidationElm = (0, common_1.createDomElement)('div', { className: `item-details-validation editor-${columnDef.id}` });
|
|
377
|
-
// optionally add a reset button beside each editor
|
|
378
|
-
if ((_k = this._options) === null || _k === void 0 ? void 0 : _k.showResetButtonOnEachEditor) {
|
|
379
|
-
const editorResetButtonElm = this.createEditorResetButtonElement(`${columnDef.id}`);
|
|
380
|
-
this._bindEventService.bind(editorResetButtonElm, 'click', this.handleResetInputValue.bind(this));
|
|
381
|
-
templateItemLabelElm.appendChild(editorResetButtonElm);
|
|
382
|
-
}
|
|
383
|
-
itemContainer.appendChild(templateItemLabelElm);
|
|
384
|
-
itemContainer.appendChild(templateItemEditorElm);
|
|
385
|
-
itemContainer.appendChild(templateItemValidationElm);
|
|
386
|
-
modalBodyElm.appendChild(itemContainer);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
// optionally add a form reset button
|
|
390
|
-
if ((_l = this._options) === null || _l === void 0 ? void 0 : _l.showFormResetButton) {
|
|
391
|
-
const resetButtonContainerElm = this.createFormResetButtonElement();
|
|
392
|
-
this._bindEventService.bind(resetButtonContainerElm, 'click', this.handleResetFormClicked.bind(this));
|
|
393
|
-
modalBodyElm.appendChild(resetButtonContainerElm);
|
|
394
|
-
}
|
|
395
|
-
document.body.appendChild(this._modalElm);
|
|
396
|
-
document.body.classList.add('slick-modal-open'); // add backdrop to body
|
|
397
|
-
this._bindEventService.bind(document.body, 'click', this.handleBodyClicked.bind(this));
|
|
398
|
-
this._editors = {};
|
|
399
|
-
this._editorContainers = modalColumns.map(col => modalBodyElm.querySelector(`[data-editorid=${col.id}]`)) || [];
|
|
400
|
-
this._compositeOptions = { destroy: this.disposeComponent.bind(this), modalType, validationMsgPrefix: '* ', formValues: {}, editors: this._editors };
|
|
401
|
-
const compositeEditor = new compositeEditor_factory_1.CompositeEditor(modalColumns, this._editorContainers, this._compositeOptions);
|
|
402
|
-
this.grid.editActiveCell(compositeEditor);
|
|
403
|
-
// --
|
|
404
|
-
// Add a few Event Handlers
|
|
405
|
-
// keyboard, blur & button event handlers
|
|
406
|
-
this._bindEventService.bind(modalCloseButtonElm, 'click', this.cancelEditing.bind(this));
|
|
407
|
-
this._bindEventService.bind(modalCancelButtonElm, 'click', this.cancelEditing.bind(this));
|
|
408
|
-
this._bindEventService.bind(this._modalSaveButtonElm, 'click', this.handleSaveClicked.bind(this));
|
|
409
|
-
this._bindEventService.bind(this._modalElm, 'keydown', this.handleKeyDown.bind(this));
|
|
410
|
-
this._bindEventService.bind(this._modalElm, 'focusout', this.validateCurrentEditor.bind(this));
|
|
411
|
-
this._bindEventService.bind(this._modalElm, 'blur', this.validateCurrentEditor.bind(this));
|
|
412
|
-
// when any of the input of the composite editor form changes, we'll add/remove a "modified" CSS className for styling purposes
|
|
413
|
-
this._eventHandler.subscribe(this.grid.onCompositeEditorChange, this.handleOnCompositeEditorChange.bind(this));
|
|
414
|
-
// when adding a new row to the grid, we need to invalidate that row and re-render the grid
|
|
415
|
-
this._eventHandler.subscribe(this.grid.onAddNewRow, (_e, args) => {
|
|
416
|
-
this.insertNewItemInDataView(args.item);
|
|
417
|
-
this._originalDataContext = args.item; // this becomes the new data context
|
|
418
|
-
this.dispose();
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
return this;
|
|
422
|
-
}
|
|
423
|
-
catch (error) {
|
|
424
|
-
this.dispose();
|
|
425
|
-
const errorMsg = (typeof error === 'string') ? error : ((_p = (_m = error === null || error === void 0 ? void 0 : error.message) !== null && _m !== void 0 ? _m : (_o = error === null || error === void 0 ? void 0 : error.body) === null || _o === void 0 ? void 0 : _o.message) !== null && _p !== void 0 ? _p : '');
|
|
426
|
-
const errorCode = (typeof error === 'string') ? error : (_s = (_q = error === null || error === void 0 ? void 0 : error.status) !== null && _q !== void 0 ? _q : (_r = error === null || error === void 0 ? void 0 : error.body) === null || _r === void 0 ? void 0 : _r.status) !== null && _s !== void 0 ? _s : errorMsg;
|
|
427
|
-
onError({ type: 'error', code: errorCode, message: errorMsg });
|
|
428
|
-
return null;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
/** Cancel the Editing which will also close the modal window */
|
|
432
|
-
async cancelEditing() {
|
|
433
|
-
var _a, _b;
|
|
434
|
-
let confirmed = true;
|
|
435
|
-
if (this.formValues && Object.keys(this.formValues).length > 0 && typeof this._options.onClose === 'function') {
|
|
436
|
-
confirmed = await this._options.onClose();
|
|
437
|
-
}
|
|
438
|
-
if (confirmed) {
|
|
439
|
-
this.grid.getEditController().cancelCurrentEdit();
|
|
440
|
-
// cancel current edit is not enough when editing/cloning,
|
|
441
|
-
// we also need to reset with the original item data context to undo/reset the entire row
|
|
442
|
-
if (((_a = this._options) === null || _a === void 0 ? void 0 : _a.modalType) === 'edit' || ((_b = this._options) === null || _b === void 0 ? void 0 : _b.modalType) === 'clone') {
|
|
443
|
-
this.resetCurrentRowDataContext();
|
|
444
|
-
}
|
|
445
|
-
this.grid.setActiveRow(this._lastActiveRowNumber);
|
|
446
|
-
this.dispose();
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
/** Show a Validation Summary text (as a <div>) when a validation fails or simply hide it when there's no error */
|
|
450
|
-
showValidationSummaryText(isShowing, errorMsg = '') {
|
|
451
|
-
var _a, _b;
|
|
452
|
-
if (isShowing && errorMsg !== '') {
|
|
453
|
-
this._modalBodyTopValidationElm.textContent = errorMsg;
|
|
454
|
-
this._modalBodyTopValidationElm.style.display = 'block';
|
|
455
|
-
(_b = (_a = this._modalBodyTopValidationElm).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
456
|
-
this._modalSaveButtonElm.disabled = false;
|
|
457
|
-
this._modalSaveButtonElm.classList.remove('saving');
|
|
458
|
-
}
|
|
459
|
-
else {
|
|
460
|
-
this._modalBodyTopValidationElm.style.display = 'none';
|
|
461
|
-
this._modalBodyTopValidationElm.textContent = errorMsg;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
// --
|
|
465
|
-
// protected methods
|
|
466
|
-
// ----------------
|
|
467
|
-
/** Apply Mass Update Changes (form values) to the entire dataset */
|
|
468
|
-
applySaveMassUpdateChanges(formValues, _selection, applyToDataview = true) {
|
|
469
|
-
// not applying to dataView means that we're doing a preview of dataset and we should use a deep copy of it instead of applying changes directly to it
|
|
470
|
-
const data = applyToDataview ? this.dataView.getItems() : (0, utils_1.deepCopy)(this.dataView.getItems());
|
|
471
|
-
// from the "lastCompositeEditor" object that we kept as reference, it contains all the changes inside the "formValues" property
|
|
472
|
-
// we can loop through these changes and apply them on the selected row indexes
|
|
473
|
-
for (const itemProp in formValues) {
|
|
474
|
-
if (itemProp in formValues) {
|
|
475
|
-
data.forEach((dataContext) => {
|
|
476
|
-
var _a;
|
|
477
|
-
if (itemProp in formValues && (((_a = this._options) === null || _a === void 0 ? void 0 : _a.validateMassUpdateChange) === undefined || this._options.validateMassUpdateChange(itemProp, dataContext, formValues) !== false)) {
|
|
478
|
-
dataContext[itemProp] = formValues[itemProp];
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
// change the entire dataset with our updated dataset
|
|
484
|
-
if (applyToDataview) {
|
|
485
|
-
this.dataView.setItems(data, this.gridOptions.datasetIdPropertyName);
|
|
486
|
-
this.grid.invalidate();
|
|
487
|
-
}
|
|
488
|
-
return data;
|
|
489
|
-
}
|
|
490
|
-
/** Apply Mass Changes to the Selected rows in the grid (form values) */
|
|
491
|
-
applySaveMassSelectionChanges(formValues, selection, applyToDataview = true) {
|
|
492
|
-
var _a, _b;
|
|
493
|
-
const selectedItemIds = (_a = selection === null || selection === void 0 ? void 0 : selection.dataContextIds) !== null && _a !== void 0 ? _a : [];
|
|
494
|
-
const selectedTmpItems = selectedItemIds.map(itemId => this.dataView.getItemById(itemId));
|
|
495
|
-
// not applying to dataView means that we're doing a preview of dataset and we should use a deep copy of it instead of applying changes directly to it
|
|
496
|
-
const selectedItems = applyToDataview ? selectedTmpItems : (0, utils_1.deepCopy)(selectedTmpItems);
|
|
497
|
-
// from the "lastCompositeEditor" object that we kept as reference, it contains all the changes inside the "formValues" property
|
|
498
|
-
// we can loop through these changes and apply them on the selected row indexes
|
|
499
|
-
for (const itemProp in formValues) {
|
|
500
|
-
if (itemProp in formValues) {
|
|
501
|
-
selectedItems.forEach((dataContext) => {
|
|
502
|
-
var _a;
|
|
503
|
-
if (itemProp in formValues && (((_a = this._options) === null || _a === void 0 ? void 0 : _a.validateMassUpdateChange) === undefined || this._options.validateMassUpdateChange(itemProp, dataContext, formValues) !== false)) {
|
|
504
|
-
dataContext[itemProp] = formValues[itemProp];
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
// update all items in the grid with the grid service
|
|
510
|
-
if (applyToDataview) {
|
|
511
|
-
(_b = this.gridService) === null || _b === void 0 ? void 0 : _b.updateItems(selectedItems);
|
|
512
|
-
}
|
|
513
|
-
return selectedItems;
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Auto-Calculate how many columns to display in the view layout (1, 2, or 3).
|
|
517
|
-
* We'll display a 1 column layout for 8 or less Editors, 2 columns layout for less than 15 Editors or 3 columns when more than 15 Editors
|
|
518
|
-
* @param {number} editorCount - how many Editors do we have in total
|
|
519
|
-
* @returns {number} count - calculated column count (1, 2 or 3)
|
|
520
|
-
*/
|
|
521
|
-
autoCalculateLayoutColumnCount(editorCount) {
|
|
522
|
-
if (editorCount >= 15) {
|
|
523
|
-
return 3;
|
|
524
|
-
}
|
|
525
|
-
else if (editorCount >= 8) {
|
|
526
|
-
return 2;
|
|
527
|
-
}
|
|
528
|
-
return 1;
|
|
529
|
-
}
|
|
530
|
-
/**
|
|
531
|
-
* Create a reset button for each editor and attach a button click handler
|
|
532
|
-
* @param {String} columnId - column id
|
|
533
|
-
* @returns {Object} - html button
|
|
534
|
-
*/
|
|
535
|
-
createEditorResetButtonElement(columnId) {
|
|
536
|
-
var _a, _b, _c, _d, _f;
|
|
537
|
-
const resetButtonElm = (0, common_1.createDomElement)('button', {
|
|
538
|
-
type: 'button', name: columnId,
|
|
539
|
-
title: (_c = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b.resetFormButton) !== null && _c !== void 0 ? _c : 'Reset Form Input',
|
|
540
|
-
className: 'btn btn-xs btn-editor-reset'
|
|
541
|
-
});
|
|
542
|
-
resetButtonElm.setAttribute('aria-label', 'Reset');
|
|
543
|
-
if ((_d = this._options) === null || _d === void 0 ? void 0 : _d.resetEditorButtonCssClass) {
|
|
544
|
-
const resetBtnClasses = (_f = this._options) === null || _f === void 0 ? void 0 : _f.resetEditorButtonCssClass.split(' ');
|
|
545
|
-
for (const cssClass of resetBtnClasses) {
|
|
546
|
-
resetButtonElm.classList.add(cssClass);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
return resetButtonElm;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Create a form reset button and attach a button click handler
|
|
553
|
-
* @param {String} columnId - column id
|
|
554
|
-
* @returns {Object} - html button
|
|
555
|
-
*/
|
|
556
|
-
createFormResetButtonElement() {
|
|
557
|
-
var _a, _b;
|
|
558
|
-
const resetButtonContainerElm = (0, common_1.createDomElement)('div', { className: 'reset-container' });
|
|
559
|
-
const resetButtonElm = (0, common_1.createDomElement)('button', { type: 'button', className: 'btn btn-sm reset-form' });
|
|
560
|
-
const resetIconSpanElm = (0, common_1.createDomElement)('span', { className: (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.resetFormButtonIconCssClass) !== null && _b !== void 0 ? _b : '' });
|
|
561
|
-
resetButtonElm.appendChild(resetIconSpanElm);
|
|
562
|
-
resetButtonElm.appendChild(document.createTextNode(' Reset Form'));
|
|
563
|
-
resetButtonContainerElm.appendChild(resetButtonElm);
|
|
564
|
-
return resetButtonContainerElm;
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Execute the onError callback when defined
|
|
568
|
-
* or use the default onError callback which is to simply display the error in the console
|
|
569
|
-
*/
|
|
570
|
-
executeOnError(error) {
|
|
571
|
-
var _a, _b;
|
|
572
|
-
const onError = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.onError) !== null && _b !== void 0 ? _b : DEFAULT_ON_ERROR;
|
|
573
|
-
onError(error);
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* A simple and generic method to execute the "OnSave" callback if it's defined by the user OR else simply execute built-in apply changes callback.
|
|
577
|
-
* This method deals with multiple callbacks as shown below
|
|
578
|
-
* @param {Function} applyChangesCallback - first callback to apply the changes into the grid (this could be a user custom callback)
|
|
579
|
-
* @param {Function} executePostCallback - second callback to execute right after the "onSave"
|
|
580
|
-
* @param {Function} beforeClosingCallback - third and last callback to execute after Saving but just before closing the modal window
|
|
581
|
-
* @param {Object} itemDataContext - item data context when modal type is (create/clone/edit)
|
|
582
|
-
*/
|
|
583
|
-
async executeOnSave(applyChangesCallback, executePostCallback, beforeClosingCallback, itemDataContext) {
|
|
584
|
-
var _a, _b, _c, _d, _f, _g;
|
|
585
|
-
try {
|
|
586
|
-
this.showValidationSummaryText(false, '');
|
|
587
|
-
const validationResults = this.validateCompositeEditors();
|
|
588
|
-
if (validationResults.valid) {
|
|
589
|
-
this._modalSaveButtonElm.classList.add('saving');
|
|
590
|
-
this._modalSaveButtonElm.disabled = true;
|
|
591
|
-
if (typeof ((_a = this._options) === null || _a === void 0 ? void 0 : _a.onSave) === 'function') {
|
|
592
|
-
const isMassChange = (this._options.modalType === 'mass-update' || this._options.modalType === 'mass-selection');
|
|
593
|
-
// apply the changes in the grid early when that option is enabled (that is before the await of `onSave`)
|
|
594
|
-
let updatedDataset;
|
|
595
|
-
if (isMassChange && ((_b = this._options) === null || _b === void 0 ? void 0 : _b.shouldPreviewMassChangeDataset)) {
|
|
596
|
-
updatedDataset = applyChangesCallback(this.formValues, this.getCurrentRowSelections(), false);
|
|
597
|
-
}
|
|
598
|
-
// call the custon onSave callback when defined and note that the item data context will only be filled for create/clone/edit
|
|
599
|
-
const dataContextOrUpdatedDatasetPreview = isMassChange ? updatedDataset : itemDataContext;
|
|
600
|
-
const successful = await ((_c = this._options) === null || _c === void 0 ? void 0 : _c.onSave(this.formValues, this.getCurrentRowSelections(), dataContextOrUpdatedDatasetPreview));
|
|
601
|
-
if (successful) {
|
|
602
|
-
// apply the changes in the grid (if it's not yet applied)
|
|
603
|
-
applyChangesCallback(this.formValues, this.getCurrentRowSelections());
|
|
604
|
-
// once we're done doing the mass update, we can cancel the current editor since we don't want to add any new row
|
|
605
|
-
// that will also destroy/close the modal window
|
|
606
|
-
executePostCallback();
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
applyChangesCallback(this.formValues, this.getCurrentRowSelections());
|
|
611
|
-
executePostCallback();
|
|
612
|
-
}
|
|
613
|
-
// run any function before closing the modal
|
|
614
|
-
if (typeof beforeClosingCallback === 'function') {
|
|
615
|
-
beforeClosingCallback();
|
|
616
|
-
}
|
|
617
|
-
// close the modal only when successful
|
|
618
|
-
this.dispose();
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
catch (error) {
|
|
622
|
-
const errorMsg = (typeof error === 'string') ? error : ((_g = (_d = error === null || error === void 0 ? void 0 : error.message) !== null && _d !== void 0 ? _d : (_f = error === null || error === void 0 ? void 0 : error.body) === null || _f === void 0 ? void 0 : _f.message) !== null && _g !== void 0 ? _g : '');
|
|
623
|
-
this.showValidationSummaryText(true, errorMsg);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
// For the Composite Editor to work, the current active cell must have an Editor (because it calls editActiveCell() and that only works with a cell with an Editor)
|
|
627
|
-
// so if current active cell doesn't have an Editor, we'll find the first column with an Editor and focus on it (from left to right starting at index 0)
|
|
628
|
-
focusOnFirstColumnCellWithEditor(columns, dataContext, columnIndex, rowIndex, isWithMassChange) {
|
|
629
|
-
// make sure we're not trying to activate a cell outside of the grid, that can happen when using MassUpdate without `enableAddRow` flag enabled
|
|
630
|
-
const activeCellIndex = (isWithMassChange && !this.gridOptions.enableAddRow && (rowIndex >= this.dataViewLength)) ? this.dataViewLength - 1 : rowIndex;
|
|
631
|
-
let columnIndexWithEditor = columnIndex;
|
|
632
|
-
const cellEditor = columns[columnIndex].editor;
|
|
633
|
-
let activeEditorCellNode = this.grid.getCellNode(activeCellIndex, columnIndex);
|
|
634
|
-
if (!cellEditor || !activeEditorCellNode || !this.getActiveCellEditor(activeCellIndex, columnIndex)) {
|
|
635
|
-
columnIndexWithEditor = this.findNextAvailableEditorColumnIndex(columns, dataContext, rowIndex, isWithMassChange);
|
|
636
|
-
if (columnIndexWithEditor === -1) {
|
|
637
|
-
this.executeOnError({ type: 'error', code: 'NO_EDITOR_FOUND', message: 'We could not find any Editor in your Column Definition' });
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
else {
|
|
641
|
-
this.grid.setActiveCell(activeCellIndex, columnIndexWithEditor, false);
|
|
642
|
-
if (isWithMassChange) {
|
|
643
|
-
// when it's a mass change, we'll activate the last row without scrolling to it
|
|
644
|
-
// that is possible via the 3rd argument "suppressScrollIntoView" set to "true"
|
|
645
|
-
this.grid.setActiveRow(this.dataViewLength, columnIndexWithEditor, true);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
// check again if the cell node is now being created, if it is then we're good
|
|
650
|
-
activeEditorCellNode = this.grid.getCellNode(activeCellIndex, columnIndexWithEditor);
|
|
651
|
-
return !!activeEditorCellNode;
|
|
652
|
-
}
|
|
653
|
-
findNextAvailableEditorColumnIndex(columns, dataContext, rowIndex, isWithMassUpdate) {
|
|
654
|
-
var _a;
|
|
655
|
-
let columnIndexWithEditor = -1;
|
|
656
|
-
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
|
657
|
-
const col = columns[colIndex];
|
|
658
|
-
if (col.editor && (!isWithMassUpdate || (isWithMassUpdate && ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.massUpdate)))) {
|
|
659
|
-
// we can check that the cell is really editable by checking the onBeforeEditCell event not returning false (returning undefined, null also mean it is editable)
|
|
660
|
-
const isCellEditable = this.grid.onBeforeEditCell.notify({ row: rowIndex, cell: colIndex, item: dataContext, column: col, grid: this.grid, target: 'composite', compositeEditorOptions: this._compositeOptions });
|
|
661
|
-
this.grid.setActiveCell(rowIndex, colIndex, false);
|
|
662
|
-
if (isCellEditable !== false) {
|
|
663
|
-
columnIndexWithEditor = colIndex;
|
|
664
|
-
break;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
return columnIndexWithEditor;
|
|
669
|
-
}
|
|
670
|
-
/**
|
|
671
|
-
* Get a column definition by providing a column id OR a column definition.
|
|
672
|
-
* If the input is a string, we'll assume it's a columnId and we'll simply search for the column in the column definitions list
|
|
673
|
-
*/
|
|
674
|
-
getColumnByObjectOrId(columnIdOrDef) {
|
|
675
|
-
let column;
|
|
676
|
-
if (typeof columnIdOrDef === 'object') {
|
|
677
|
-
column = columnIdOrDef;
|
|
678
|
-
}
|
|
679
|
-
else if (typeof columnIdOrDef === 'string') {
|
|
680
|
-
column = this._columnDefinitions.find(col => col.id === columnIdOrDef);
|
|
681
|
-
}
|
|
682
|
-
return column;
|
|
683
|
-
}
|
|
684
|
-
getActiveCellEditor(row, cell) {
|
|
685
|
-
this.grid.setActiveCell(row, cell, false);
|
|
686
|
-
return this.grid.getCellEditor();
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* Get the column label, the label might have an optional "columnGroup" (or "columnGroupKey" which need to be translated)
|
|
690
|
-
* @param {object} columnDef - column definition
|
|
691
|
-
* @returns {string} label - column label
|
|
692
|
-
*/
|
|
693
|
-
getColumnLabel(columnDef) {
|
|
694
|
-
var _a;
|
|
695
|
-
const columnGroupSeparator = this.gridOptions.columnGroupSeparator || ' - ';
|
|
696
|
-
let columnName = columnDef.nameCompositeEditor || columnDef.name || '';
|
|
697
|
-
let columnGroup = columnDef.columnGroup || '';
|
|
698
|
-
if (this.gridOptions.enableTranslate && this.translaterService) {
|
|
699
|
-
const translationKey = columnDef.nameCompositeEditorKey || columnDef.nameKey;
|
|
700
|
-
if (translationKey) {
|
|
701
|
-
columnName = this.translaterService.translate(translationKey);
|
|
702
|
-
}
|
|
703
|
-
if (columnDef.columnGroupKey && ((_a = this.translaterService) === null || _a === void 0 ? void 0 : _a.translate)) {
|
|
704
|
-
columnGroup = this.translaterService.translate(columnDef.columnGroupKey);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
const columnLabel = columnGroup ? `${columnGroup}${columnGroupSeparator}${columnName}` : columnName;
|
|
708
|
-
return columnLabel || '';
|
|
709
|
-
}
|
|
710
|
-
/** Get the correct label text depending, if we use a Translater Service then translate the text when possible else use default text */
|
|
711
|
-
getLabelText(labelProperty, localeText, defaultText) {
|
|
712
|
-
var _a, _b, _c, _d, _f, _g, _h;
|
|
713
|
-
const textLabels = { ...(_a = this.gridOptions.compositeEditorOptions) === null || _a === void 0 ? void 0 : _a.labels, ...(_b = this._options) === null || _b === void 0 ? void 0 : _b.labels };
|
|
714
|
-
if (((_c = this.gridOptions) === null || _c === void 0 ? void 0 : _c.enableTranslate) && ((_d = this.translaterService) === null || _d === void 0 ? void 0 : _d.translate) && textLabels.hasOwnProperty(`${labelProperty}Key`)) {
|
|
715
|
-
const translationKey = textLabels[`${labelProperty}Key`];
|
|
716
|
-
return this.translaterService.translate(translationKey || '');
|
|
717
|
-
}
|
|
718
|
-
return (_h = (_f = textLabels === null || textLabels === void 0 ? void 0 : textLabels[labelProperty]) !== null && _f !== void 0 ? _f : (_g = this._locales) === null || _g === void 0 ? void 0 : _g[localeText]) !== null && _h !== void 0 ? _h : defaultText;
|
|
719
|
-
}
|
|
720
|
-
/** Retrieve the current selection of row indexes & data context Ids */
|
|
721
|
-
getCurrentRowSelections() {
|
|
722
|
-
const dataContextIds = this.dataView.getAllSelectedIds();
|
|
723
|
-
const gridRowIndexes = this.dataView.mapIdsToRows(dataContextIds);
|
|
724
|
-
return { gridRowIndexes, dataContextIds };
|
|
725
|
-
}
|
|
726
|
-
handleBodyClicked(event) {
|
|
727
|
-
var _a, _b, _c;
|
|
728
|
-
if ((_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.contains('slick-editor-modal')) {
|
|
729
|
-
if (((_c = this._options) === null || _c === void 0 ? void 0 : _c.backdrop) !== 'static') {
|
|
730
|
-
this.dispose();
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
handleKeyDown(event) {
|
|
735
|
-
if (event.code === 'Escape') {
|
|
736
|
-
this.cancelEditing();
|
|
737
|
-
event.stopPropagation();
|
|
738
|
-
event.preventDefault();
|
|
739
|
-
}
|
|
740
|
-
else if (event.code === 'Tab') {
|
|
741
|
-
this.validateCurrentEditor();
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
handleResetInputValue(event) {
|
|
745
|
-
var _a, _b;
|
|
746
|
-
const columnId = event.target.name;
|
|
747
|
-
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
748
|
-
if (editor === null || editor === void 0 ? void 0 : editor.reset) {
|
|
749
|
-
editor.reset();
|
|
750
|
-
}
|
|
751
|
-
(_b = this._formValues) === null || _b === void 0 ? true : delete _b[columnId];
|
|
752
|
-
}
|
|
753
|
-
/** Callback which processes a Mass Update or Mass Selection Changes */
|
|
754
|
-
async handleMassSaving(modalType, executePostCallback) {
|
|
755
|
-
if (!this.formValues || Object.keys(this.formValues).length === 0) {
|
|
756
|
-
this.executeOnError({ type: 'warning', code: 'NO_CHANGES_DETECTED', message: 'Sorry we could not detect any changes.' });
|
|
757
|
-
}
|
|
758
|
-
else {
|
|
759
|
-
const applyCallbackFnName = (modalType === 'mass-update') ? 'applySaveMassUpdateChanges' : 'applySaveMassSelectionChanges';
|
|
760
|
-
this.executeOnSave(this[applyCallbackFnName].bind(this), executePostCallback.bind(this));
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
/** Anytime an input of the Composite Editor form changes, we'll add/remove a "modified" CSS className for styling purposes */
|
|
764
|
-
handleOnCompositeEditorChange(_e, args) {
|
|
765
|
-
var _a, _b, _c, _d, _f, _g, _h, _j;
|
|
766
|
-
const columnId = (_b = (_a = args.column) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
|
|
767
|
-
this._formValues = { ...this._formValues, ...args.formValues };
|
|
768
|
-
const editor = (_c = this._editors) === null || _c === void 0 ? void 0 : _c[columnId];
|
|
769
|
-
const isEditorValueTouched = (_h = (_f = (_d = editor === null || editor === void 0 ? void 0 : editor.isValueTouched) === null || _d === void 0 ? void 0 : _d.call(editor)) !== null && _f !== void 0 ? _f : (_g = editor === null || editor === void 0 ? void 0 : editor.isValueChanged) === null || _g === void 0 ? void 0 : _g.call(editor)) !== null && _h !== void 0 ? _h : false;
|
|
770
|
-
this._itemDataContext = (_j = editor === null || editor === void 0 ? void 0 : editor.dataContext) !== null && _j !== void 0 ? _j : {}; // keep reference of the item data context
|
|
771
|
-
// add extra css styling to the composite editor input(s) that got modified
|
|
772
|
-
const editorElm = this._modalElm.querySelector(`[data-editorid=${columnId}]`);
|
|
773
|
-
if (editorElm === null || editorElm === void 0 ? void 0 : editorElm.classList) {
|
|
774
|
-
if (isEditorValueTouched) {
|
|
775
|
-
editorElm.classList.add('modified');
|
|
776
|
-
}
|
|
777
|
-
else {
|
|
778
|
-
editorElm.classList.remove('modified');
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
// after any input changes we'll re-validate all fields
|
|
782
|
-
this.validateCompositeEditors();
|
|
783
|
-
}
|
|
784
|
-
/** Check wether the grid has the Row Selection enabled */
|
|
785
|
-
hasRowSelectionEnabled() {
|
|
786
|
-
const selectionModel = this.grid.getSelectionModel();
|
|
787
|
-
const isRowSelectionEnabled = this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector;
|
|
788
|
-
return (isRowSelectionEnabled && selectionModel);
|
|
789
|
-
}
|
|
790
|
-
/** Reset Form button handler */
|
|
791
|
-
handleResetFormClicked() {
|
|
792
|
-
for (const columnId of Object.keys(this._editors)) {
|
|
793
|
-
const editor = this._editors[columnId];
|
|
794
|
-
if (editor === null || editor === void 0 ? void 0 : editor.reset) {
|
|
795
|
-
editor.reset();
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
this._formValues = (0, utils_1.emptyObject)(this._formValues);
|
|
799
|
-
}
|
|
800
|
-
/** switch case handler to determine which code to execute depending on the modal type */
|
|
801
|
-
handleSaveClicked() {
|
|
802
|
-
var _a, _b, _c;
|
|
803
|
-
const modalType = (_a = this._options) === null || _a === void 0 ? void 0 : _a.modalType;
|
|
804
|
-
switch (modalType) {
|
|
805
|
-
case 'mass-update':
|
|
806
|
-
this.handleMassSaving(modalType, () => {
|
|
807
|
-
this.grid.getEditController().cancelCurrentEdit();
|
|
808
|
-
this.grid.setActiveCell(0, 0, false);
|
|
809
|
-
if (this._options.shouldClearRowSelectionAfterMassAction) {
|
|
810
|
-
this.grid.setSelectedRows([]);
|
|
811
|
-
}
|
|
812
|
-
});
|
|
813
|
-
break;
|
|
814
|
-
case 'mass-selection':
|
|
815
|
-
this.handleMassSaving(modalType, () => {
|
|
816
|
-
this.grid.getEditController().cancelCurrentEdit();
|
|
817
|
-
this.grid.setActiveRow(this._lastActiveRowNumber);
|
|
818
|
-
if (this._options.shouldClearRowSelectionAfterMassAction) {
|
|
819
|
-
this.grid.setSelectedRows([]);
|
|
820
|
-
}
|
|
821
|
-
});
|
|
822
|
-
break;
|
|
823
|
-
case 'clone':
|
|
824
|
-
// the clone object will be a merge of the selected data context (original object) with the changed form values
|
|
825
|
-
const clonedItemDataContext = { ...this._originalDataContext, ...this.formValues };
|
|
826
|
-
// post save callback (before closing modal)
|
|
827
|
-
const postSaveCloneCallback = () => {
|
|
828
|
-
this.grid.getEditController().cancelCurrentEdit();
|
|
829
|
-
this.grid.setActiveCell(0, 0, false);
|
|
830
|
-
};
|
|
831
|
-
// call the onSave execution and provide the item data context so that it's available to the user
|
|
832
|
-
this.executeOnSave(this.insertNewItemInDataView.bind(this, clonedItemDataContext), postSaveCloneCallback, this.resetCurrentRowDataContext.bind(this), clonedItemDataContext);
|
|
833
|
-
break;
|
|
834
|
-
case 'create':
|
|
835
|
-
case 'edit':
|
|
836
|
-
default:
|
|
837
|
-
// commit the changes into the grid
|
|
838
|
-
// if it's a "create" then it will triggered the "onAddNewRow" event which will in term push it to the grid
|
|
839
|
-
// while an "edit" will simply applies the changes directly on the same row
|
|
840
|
-
this.grid.getEditController().commitCurrentEdit();
|
|
841
|
-
// if the user provided the "onSave" callback, let's execute it with the item data context
|
|
842
|
-
if (typeof ((_b = this._options) === null || _b === void 0 ? void 0 : _b.onSave) === 'function') {
|
|
843
|
-
const itemDataContext = this.grid.getDataItem(this._lastActiveRowNumber); // we can get item data context directly from DataView
|
|
844
|
-
(_c = this._options) === null || _c === void 0 ? void 0 : _c.onSave(this.formValues, this.getCurrentRowSelections(), itemDataContext);
|
|
845
|
-
}
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
/** Insert an item into the DataView or throw an error when finding duplicate id in the dataset */
|
|
850
|
-
insertNewItemInDataView(item) {
|
|
851
|
-
var _a, _b, _c, _d;
|
|
852
|
-
const fullDatasetLength = (_b = (_a = this.dataView) === null || _a === void 0 ? void 0 : _a.getItemCount()) !== null && _b !== void 0 ? _b : 0;
|
|
853
|
-
const newId = (_c = this._options.insertNewId) !== null && _c !== void 0 ? _c : fullDatasetLength + 1;
|
|
854
|
-
item[this.gridOptions.datasetIdPropertyName || 'id'] = newId;
|
|
855
|
-
if (!this.dataView.getItemById(newId)) {
|
|
856
|
-
(_d = this.gridService) === null || _d === void 0 ? void 0 : _d.addItem(item, this._options.insertOptions);
|
|
857
|
-
}
|
|
858
|
-
else {
|
|
859
|
-
this.executeOnError({ type: 'error', code: 'ITEM_ALREADY_EXIST', message: `The item object which you are trying to add already exist with the same Id:: ${newId}` });
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
parseText(inputText, mappedArgs) {
|
|
863
|
-
return inputText.replace(/\{\{(.*?)\}\}/g, (match, group) => {
|
|
864
|
-
return mappedArgs[group] !== undefined ? mappedArgs[group] : match;
|
|
865
|
-
});
|
|
866
|
-
}
|
|
867
|
-
/** Put back the current row to its original item data context using the DataView without triggering a change */
|
|
868
|
-
resetCurrentRowDataContext() {
|
|
869
|
-
const idPropName = this.gridOptions.datasetIdPropertyName || 'id';
|
|
870
|
-
const dataView = this.grid.getData();
|
|
871
|
-
dataView.updateItem(this._originalDataContext[idPropName], this._originalDataContext);
|
|
872
|
-
}
|
|
873
|
-
/** Validate all the Composite Editors that are defined in the form */
|
|
874
|
-
validateCompositeEditors(targetElm) {
|
|
875
|
-
let validationResults = { valid: true, msg: '' };
|
|
876
|
-
const currentEditor = this.grid.getCellEditor();
|
|
877
|
-
if (currentEditor) {
|
|
878
|
-
validationResults = currentEditor.validate(targetElm);
|
|
879
|
-
}
|
|
880
|
-
return validationResults;
|
|
881
|
-
}
|
|
882
|
-
/** Validate the current cell editor */
|
|
883
|
-
validateCurrentEditor() {
|
|
884
|
-
const currentEditor = this.grid.getCellEditor();
|
|
885
|
-
if (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.validate) {
|
|
886
|
-
currentEditor.validate();
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
exports.SlickCompositeEditorComponent = SlickCompositeEditorComponent;
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SlickCompositeEditorComponent = void 0;
|
|
4
|
+
const utils_1 = require("@slickgrid-universal/utils");
|
|
5
|
+
const common_1 = require("@slickgrid-universal/common");
|
|
6
|
+
const compositeEditor_factory_1 = require("./compositeEditor.factory");
|
|
7
|
+
const DEFAULT_ON_ERROR = (error) => console.log(error.message);
|
|
8
|
+
class SlickCompositeEditorComponent {
|
|
9
|
+
get eventHandler() {
|
|
10
|
+
return this._eventHandler;
|
|
11
|
+
}
|
|
12
|
+
get dataView() {
|
|
13
|
+
return this.grid.getData();
|
|
14
|
+
}
|
|
15
|
+
get dataViewLength() {
|
|
16
|
+
return this.dataView.getLength();
|
|
17
|
+
}
|
|
18
|
+
get formValues() {
|
|
19
|
+
return this._formValues;
|
|
20
|
+
}
|
|
21
|
+
get editors() {
|
|
22
|
+
return this._editors;
|
|
23
|
+
}
|
|
24
|
+
set editors(editors) {
|
|
25
|
+
this._editors = editors;
|
|
26
|
+
}
|
|
27
|
+
get gridOptions() {
|
|
28
|
+
var _a;
|
|
29
|
+
return (_a = this.grid) === null || _a === void 0 ? void 0 : _a.getOptions();
|
|
30
|
+
}
|
|
31
|
+
constructor() {
|
|
32
|
+
this._columnDefinitions = [];
|
|
33
|
+
this._lastActiveRowNumber = -1;
|
|
34
|
+
this._formValues = null;
|
|
35
|
+
this.gridService = null;
|
|
36
|
+
this._eventHandler = new Slick.EventHandler();
|
|
37
|
+
this._bindEventService = new common_1.BindingEventService();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* initialize the Composite Editor by passing the SlickGrid object and the container service
|
|
41
|
+
*
|
|
42
|
+
* Note: we aren't using DI in the constructor simply to be as framework agnostic as possible,
|
|
43
|
+
* we are simply using this init() function with a very basic container service to do the job
|
|
44
|
+
*/
|
|
45
|
+
init(grid, containerService) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
this.grid = grid;
|
|
48
|
+
this.gridService = containerService.get('GridService');
|
|
49
|
+
this.translaterService = containerService.get('TranslaterService');
|
|
50
|
+
if (!this.gridService) {
|
|
51
|
+
throw new Error('[Slickgrid-Universal] it seems that the GridService is not being loaded properly, make sure the Container Service is properly implemented.');
|
|
52
|
+
}
|
|
53
|
+
if (this.gridOptions.enableTranslate && (!this.translaterService || !this.translaterService.translate)) {
|
|
54
|
+
throw new Error('[Slickgrid-Universal] requires a Translate Service to be installed and configured when the grid option "enableTranslate" is enabled.');
|
|
55
|
+
}
|
|
56
|
+
// get locales provided by user in forRoot or else use default English locales via the Constants
|
|
57
|
+
this._locales = (_b = (_a = this.gridOptions) === null || _a === void 0 ? void 0 : _a.locales) !== null && _b !== void 0 ? _b : common_1.Constants.locales;
|
|
58
|
+
}
|
|
59
|
+
/** Dispose of the Component & unsubscribe all events */
|
|
60
|
+
dispose() {
|
|
61
|
+
this._eventHandler.unsubscribeAll();
|
|
62
|
+
this._bindEventService.unbindAll();
|
|
63
|
+
this._formValues = null;
|
|
64
|
+
this.disposeComponent();
|
|
65
|
+
}
|
|
66
|
+
/** Dispose of the Component without unsubscribing any events */
|
|
67
|
+
disposeComponent() {
|
|
68
|
+
var _a, _b, _c;
|
|
69
|
+
// protected _editorContainers!: Array<HTMLElement | null>;
|
|
70
|
+
(_a = this._modalBodyTopValidationElm) === null || _a === void 0 ? void 0 : _a.remove();
|
|
71
|
+
(_b = this._modalSaveButtonElm) === null || _b === void 0 ? void 0 : _b.remove();
|
|
72
|
+
if (typeof ((_c = this._modalElm) === null || _c === void 0 ? void 0 : _c.remove) === 'function') {
|
|
73
|
+
this._modalElm.remove();
|
|
74
|
+
// remove the body backdrop click listener, every other listeners will be dropped automatically since we destroy the component
|
|
75
|
+
document.body.classList.remove('slick-modal-open');
|
|
76
|
+
}
|
|
77
|
+
this._editorContainers = [];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Dynamically change value of an input from the Composite Editor form.
|
|
81
|
+
*
|
|
82
|
+
* NOTE: user might get an error thrown when trying to apply a value on a Composite Editor that was not found in the form,
|
|
83
|
+
* but in some cases the user might still want the value to be applied to the formValues so that it will be sent to the save in final item data context
|
|
84
|
+
* and when that happens, you can just skip that error so it won't throw.
|
|
85
|
+
* @param {String | Column} columnIdOrDef - column id or column definition
|
|
86
|
+
* @param {*} newValue - the new value
|
|
87
|
+
* @param {Boolean} skipMissingEditorError - defaults to False, skipping the error when the Composite Editor was not found will allow to still apply the value into the formValues object
|
|
88
|
+
* @param {Boolean} triggerOnCompositeEditorChange - defaults to True, will this change trigger a onCompositeEditorChange event?
|
|
89
|
+
*/
|
|
90
|
+
changeFormInputValue(columnIdOrDef, newValue, skipMissingEditorError = false, triggerOnCompositeEditorChange = true) {
|
|
91
|
+
var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
92
|
+
const columnDef = this.getColumnByObjectOrId(columnIdOrDef);
|
|
93
|
+
const columnId = typeof columnIdOrDef === 'string' ? columnIdOrDef : (_a = columnDef === null || columnDef === void 0 ? void 0 : columnDef.id) !== null && _a !== void 0 ? _a : '';
|
|
94
|
+
const editor = (_b = this._editors) === null || _b === void 0 ? void 0 : _b[columnId];
|
|
95
|
+
let outputValue = newValue;
|
|
96
|
+
if (!editor && !skipMissingEditorError) {
|
|
97
|
+
throw new Error(`Composite Editor with column id "${columnId}" not found.`);
|
|
98
|
+
}
|
|
99
|
+
if (typeof (editor === null || editor === void 0 ? void 0 : editor.setValue) === 'function' && Array.isArray(this._editorContainers)) {
|
|
100
|
+
editor.setValue(newValue, true, triggerOnCompositeEditorChange);
|
|
101
|
+
const editorContainerElm = this._editorContainers.find(editorElm => editorElm.dataset.editorid === columnId);
|
|
102
|
+
const excludeDisabledFieldFormValues = (_f = (_d = (_c = this.gridOptions) === null || _c === void 0 ? void 0 : _c.compositeEditorOptions) === null || _d === void 0 ? void 0 : _d.excludeDisabledFieldFormValues) !== null && _f !== void 0 ? _f : false;
|
|
103
|
+
if (!editor.disabled || (editor.disabled && !excludeDisabledFieldFormValues)) {
|
|
104
|
+
(_g = editorContainerElm === null || editorContainerElm === void 0 ? void 0 : editorContainerElm.classList) === null || _g === void 0 ? void 0 : _g.add('modified');
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
outputValue = '';
|
|
108
|
+
(_h = editorContainerElm === null || editorContainerElm === void 0 ? void 0 : editorContainerElm.classList) === null || _h === void 0 ? void 0 : _h.remove('modified');
|
|
109
|
+
}
|
|
110
|
+
// when the field is disabled, we will only allow a blank value anything else will be disregarded
|
|
111
|
+
if (editor.disabled && (outputValue !== '' || outputValue !== null || outputValue !== undefined || outputValue !== 0)) {
|
|
112
|
+
outputValue = '';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// is the field a complex object, like "address.streetNumber"
|
|
116
|
+
// we'll set assign the value as a complex object following the `field` dot notation
|
|
117
|
+
const fieldName = (_j = columnDef === null || columnDef === void 0 ? void 0 : columnDef.field) !== null && _j !== void 0 ? _j : '';
|
|
118
|
+
if (columnDef && (fieldName === null || fieldName === void 0 ? void 0 : fieldName.includes('.'))) {
|
|
119
|
+
// when it's a complex object, user could override the object path (where the editable object is located)
|
|
120
|
+
// else we use the path provided in the Field Column Definition
|
|
121
|
+
const objectPath = (_m = (_l = (_k = columnDef.internalColumnEditor) === null || _k === void 0 ? void 0 : _k.complexObjectPath) !== null && _l !== void 0 ? _l : fieldName) !== null && _m !== void 0 ? _m : '';
|
|
122
|
+
(0, utils_1.setDeepValue)((_o = this._formValues) !== null && _o !== void 0 ? _o : {}, objectPath, newValue);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
this._formValues = { ...this._formValues, [columnId]: outputValue };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Dynamically update the `formValues` object directly without triggering the onCompositeEditorChange event.
|
|
130
|
+
* The fact that this doesn't trigger an event, might not always be good though, in these cases you are probably better with using the changeFormInputValue() method
|
|
131
|
+
* @param {String | Column} columnIdOrDef - column id or column definition
|
|
132
|
+
* @param {*} newValue - the new value
|
|
133
|
+
*/
|
|
134
|
+
changeFormValue(columnIdOrDef, newValue) {
|
|
135
|
+
var _a, _b, _c, _d, _f;
|
|
136
|
+
const columnDef = this.getColumnByObjectOrId(columnIdOrDef);
|
|
137
|
+
const columnId = typeof columnIdOrDef === 'string' ? columnIdOrDef : (_a = columnDef === null || columnDef === void 0 ? void 0 : columnDef.id) !== null && _a !== void 0 ? _a : '';
|
|
138
|
+
// is the field a complex object, like "address.streetNumber"
|
|
139
|
+
// we'll set assign the value as a complex object following the `field` dot notation
|
|
140
|
+
const fieldName = (_b = columnDef === null || columnDef === void 0 ? void 0 : columnDef.field) !== null && _b !== void 0 ? _b : columnIdOrDef;
|
|
141
|
+
if (fieldName === null || fieldName === void 0 ? void 0 : fieldName.includes('.')) {
|
|
142
|
+
// when it's a complex object, user could override the object path (where the editable object is located)
|
|
143
|
+
// else we use the path provided in the Field Column Definition
|
|
144
|
+
const objectPath = (_f = (_d = (_c = columnDef === null || columnDef === void 0 ? void 0 : columnDef.internalColumnEditor) === null || _c === void 0 ? void 0 : _c.complexObjectPath) !== null && _d !== void 0 ? _d : fieldName) !== null && _f !== void 0 ? _f : '';
|
|
145
|
+
(0, utils_1.setDeepValue)(this._formValues, objectPath, newValue);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this._formValues = { ...this._formValues, [columnId]: newValue };
|
|
149
|
+
}
|
|
150
|
+
this._formValues = (0, utils_1.deepMerge)({}, this._itemDataContext, this._formValues);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Dynamically change an Editor option of the Composite Editor form
|
|
154
|
+
* For example, a use case could be to dynamically change the "minDate" of another Date Editor in the Composite Editor form.
|
|
155
|
+
* @param {String} columnId - column id
|
|
156
|
+
* @param {*} newValue - the new value
|
|
157
|
+
*/
|
|
158
|
+
changeFormEditorOption(columnId, optionName, newOptionValue) {
|
|
159
|
+
var _a;
|
|
160
|
+
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
161
|
+
// change an Editor option (not all Editors have that method, so make sure it exists before trying to call it)
|
|
162
|
+
if (editor === null || editor === void 0 ? void 0 : editor.changeEditorOption) {
|
|
163
|
+
editor.changeEditorOption(optionName, newOptionValue);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
throw new Error(`Editor with column id "${columnId}" not found OR the Editor does not support "changeEditorOption" (current only available with AutoComplete, Date, MultipleSelect & SingleSelect Editors).`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Disable (or enable) an input of the Composite Editor form
|
|
171
|
+
* @param {String} columnId - column definition id
|
|
172
|
+
* @param isDisabled - defaults to True, are we disabling the associated form input
|
|
173
|
+
*/
|
|
174
|
+
disableFormInput(columnId, isDisabled = true) {
|
|
175
|
+
var _a;
|
|
176
|
+
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
177
|
+
if ((editor === null || editor === void 0 ? void 0 : editor.disable) && Array.isArray(this._editorContainers)) {
|
|
178
|
+
editor.disable(isDisabled);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/** Entry point to initialize and open the Composite Editor modal window */
|
|
182
|
+
openDetails(options) {
|
|
183
|
+
var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
184
|
+
const onError = (_a = options.onError) !== null && _a !== void 0 ? _a : DEFAULT_ON_ERROR;
|
|
185
|
+
const defaultOptions = {
|
|
186
|
+
backdrop: 'static',
|
|
187
|
+
showCloseButtonOutside: true,
|
|
188
|
+
shouldClearRowSelectionAfterMassAction: true,
|
|
189
|
+
viewColumnLayout: 'auto',
|
|
190
|
+
modalType: 'edit',
|
|
191
|
+
};
|
|
192
|
+
try {
|
|
193
|
+
if (!this.grid || (this.grid.getEditorLock().isActive() && !this.grid.getEditorLock().commitCurrentEdit())) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
this._formValues = null; // make sure there's no leftover from previous change
|
|
197
|
+
this._options = { ...defaultOptions, ...this.gridOptions.compositeEditorOptions, ...options, labels: { ...(_b = this.gridOptions.compositeEditorOptions) === null || _b === void 0 ? void 0 : _b.labels, ...options === null || options === void 0 ? void 0 : options.labels } }; // merge default options with user options
|
|
198
|
+
this._options.backdrop = options.backdrop !== undefined ? options.backdrop : 'static';
|
|
199
|
+
const viewColumnLayout = this._options.viewColumnLayout || 1;
|
|
200
|
+
const activeCell = this.grid.getActiveCell();
|
|
201
|
+
const activeColIndex = (_c = activeCell === null || activeCell === void 0 ? void 0 : activeCell.cell) !== null && _c !== void 0 ? _c : 0;
|
|
202
|
+
const activeRow = (_d = activeCell === null || activeCell === void 0 ? void 0 : activeCell.row) !== null && _d !== void 0 ? _d : 0;
|
|
203
|
+
const gridUid = this.grid.getUID() || '';
|
|
204
|
+
let headerTitle = options.headerTitle || '';
|
|
205
|
+
// execute callback before creating the modal window (that is in short the first event in the lifecycle)
|
|
206
|
+
if (typeof this._options.onBeforeOpen === 'function') {
|
|
207
|
+
this._options.onBeforeOpen();
|
|
208
|
+
}
|
|
209
|
+
if (this.hasRowSelectionEnabled() && this._options.modalType === 'auto-mass' && this.grid.getSelectedRows) {
|
|
210
|
+
const selectedRowsIndexes = this.grid.getSelectedRows() || [];
|
|
211
|
+
if (selectedRowsIndexes.length > 0) {
|
|
212
|
+
this._options.modalType = 'mass-selection';
|
|
213
|
+
if (options === null || options === void 0 ? void 0 : options.headerTitleMassSelection) {
|
|
214
|
+
headerTitle = options === null || options === void 0 ? void 0 : options.headerTitleMassSelection;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
this._options.modalType = 'mass-update';
|
|
219
|
+
if (options === null || options === void 0 ? void 0 : options.headerTitleMassUpdate) {
|
|
220
|
+
headerTitle = options === null || options === void 0 ? void 0 : options.headerTitleMassUpdate;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const modalType = this._options.modalType || 'edit';
|
|
225
|
+
if (!this.gridOptions.editable) {
|
|
226
|
+
onError({ type: 'error', code: 'EDITABLE_GRID_REQUIRED', message: 'Your grid must be editable in order to use the Composite Editor Modal.' });
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
else if (!this.gridOptions.enableCellNavigation) {
|
|
230
|
+
onError({ type: 'error', code: 'ENABLE_CELL_NAVIGATION_REQUIRED', message: 'Composite Editor requires the flag "enableCellNavigation" to be set to True in your Grid Options.' });
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
else if (!this.gridOptions.enableAddRow && (modalType === 'clone' || modalType === 'create')) {
|
|
234
|
+
onError({ type: 'error', code: 'ENABLE_ADD_ROW_REQUIRED', message: 'Composite Editor requires the flag "enableAddRow" to be set to True in your Grid Options when cloning/creating a new item.' });
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
else if (!activeCell && (modalType === 'clone' || modalType === 'edit')) {
|
|
238
|
+
onError({ type: 'warning', code: 'NO_RECORD_FOUND', message: 'No records selected for edit or clone operation.' });
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const isWithMassChange = (modalType === 'mass-update' || modalType === 'mass-selection');
|
|
243
|
+
const dataContext = !isWithMassChange ? this.grid.getDataItem(activeRow) : {};
|
|
244
|
+
this._originalDataContext = (0, utils_1.deepCopy)(dataContext);
|
|
245
|
+
this._columnDefinitions = this.grid.getColumns();
|
|
246
|
+
const selectedRowsIndexes = this.hasRowSelectionEnabled() ? this.grid.getSelectedRows() : [];
|
|
247
|
+
const fullDatasetLength = (_g = (_f = this.dataView) === null || _f === void 0 ? void 0 : _f.getItemCount()) !== null && _g !== void 0 ? _g : 0;
|
|
248
|
+
this._lastActiveRowNumber = activeRow;
|
|
249
|
+
const dataContextIds = this.dataView.getAllSelectedIds();
|
|
250
|
+
// focus on a first cell with an Editor (unless current cell already has an Editor then do nothing)
|
|
251
|
+
// also when it's a "Create" modal, we'll scroll to the end of the grid
|
|
252
|
+
const rowIndex = modalType === 'create' ? this.dataViewLength : activeRow;
|
|
253
|
+
const hasFoundEditor = this.focusOnFirstColumnCellWithEditor(this._columnDefinitions, dataContext, activeColIndex, rowIndex, isWithMassChange);
|
|
254
|
+
if (!hasFoundEditor) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
if (modalType === 'edit' && !dataContext) {
|
|
258
|
+
onError({ type: 'warning', code: 'ROW_NOT_EDITABLE', message: 'Current row is not editable.' });
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
else if (modalType === 'mass-selection') {
|
|
262
|
+
if (selectedRowsIndexes.length < 1) {
|
|
263
|
+
onError({ type: 'warning', code: 'ROW_SELECTION_REQUIRED', message: 'You must select some rows before trying to apply new value(s).' });
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
let modalColumns = [];
|
|
268
|
+
if (isWithMassChange) {
|
|
269
|
+
// when using Mass Update, we only care about the columns that have the "massUpdate: true", we disregard anything else
|
|
270
|
+
modalColumns = this._columnDefinitions.filter(col => { var _a; return col.editor && ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.massUpdate) === true; });
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
modalColumns = this._columnDefinitions.filter(col => col.editor);
|
|
274
|
+
}
|
|
275
|
+
// user could optionally show the form inputs in a specific order instead of using default column definitions order
|
|
276
|
+
if (modalColumns.some(col => { var _a; return ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.compositeEditorFormOrder) !== undefined; })) {
|
|
277
|
+
modalColumns.sort((col1, col2) => {
|
|
278
|
+
var _a, _b, _c, _d;
|
|
279
|
+
const val1 = (_b = (_a = col1 === null || col1 === void 0 ? void 0 : col1.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.compositeEditorFormOrder) !== null && _b !== void 0 ? _b : Infinity;
|
|
280
|
+
const val2 = (_d = (_c = col2 === null || col2 === void 0 ? void 0 : col2.internalColumnEditor) === null || _c === void 0 ? void 0 : _c.compositeEditorFormOrder) !== null && _d !== void 0 ? _d : Infinity;
|
|
281
|
+
return (0, common_1.numericSortComparer)(val1, val2, common_1.SortDirectionNumber.asc);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// open the editor modal and we can also provide a header title with optional parsing pulled from the dataContext, via template {{ }}
|
|
285
|
+
// for example {{title}} => display the item title, or even complex object works {{product.name}} => display item product name
|
|
286
|
+
const parsedHeaderTitle = headerTitle.replace(/\{\{(.*?)\}\}/g, (_match, group) => (0, common_1.getDescendantProperty)(dataContext, group));
|
|
287
|
+
const layoutColCount = viewColumnLayout === 'auto' ? this.autoCalculateLayoutColumnCount(modalColumns.length) : viewColumnLayout;
|
|
288
|
+
this._modalElm = (0, common_1.createDomElement)('div', { className: `slick-editor-modal ${gridUid}` });
|
|
289
|
+
const modalContentElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-content' });
|
|
290
|
+
if ((!isNaN(viewColumnLayout) && +viewColumnLayout > 1) || (viewColumnLayout === 'auto' && layoutColCount > 1)) {
|
|
291
|
+
const splitClassName = layoutColCount === 2 ? 'split-view' : 'triple-split-view';
|
|
292
|
+
modalContentElm.classList.add(splitClassName);
|
|
293
|
+
}
|
|
294
|
+
const modalHeaderTitleElm = (0, common_1.createDomElement)('div', {
|
|
295
|
+
className: 'slick-editor-modal-title',
|
|
296
|
+
innerHTML: (0, common_1.sanitizeTextByAvailableSanitizer)(this.gridOptions, parsedHeaderTitle),
|
|
297
|
+
});
|
|
298
|
+
const modalCloseButtonElm = (0, common_1.createDomElement)('button', { type: 'button', textContent: '×', className: 'close', dataset: { action: 'close' } });
|
|
299
|
+
modalCloseButtonElm.setAttribute('aria-label', 'Close');
|
|
300
|
+
if (this._options.showCloseButtonOutside) {
|
|
301
|
+
(_h = modalHeaderTitleElm === null || modalHeaderTitleElm === void 0 ? void 0 : modalHeaderTitleElm.classList) === null || _h === void 0 ? void 0 : _h.add('outside');
|
|
302
|
+
(_j = modalCloseButtonElm === null || modalCloseButtonElm === void 0 ? void 0 : modalCloseButtonElm.classList) === null || _j === void 0 ? void 0 : _j.add('outside');
|
|
303
|
+
}
|
|
304
|
+
const modalHeaderElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-header' });
|
|
305
|
+
modalHeaderElm.setAttribute('aria-label', 'Close');
|
|
306
|
+
modalHeaderElm.appendChild(modalHeaderTitleElm);
|
|
307
|
+
modalHeaderElm.appendChild(modalCloseButtonElm);
|
|
308
|
+
const modalBodyElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-body' });
|
|
309
|
+
this._modalBodyTopValidationElm = (0, common_1.createDomElement)('div', { className: 'validation-summary', style: { display: 'none' } });
|
|
310
|
+
modalBodyElm.appendChild(this._modalBodyTopValidationElm);
|
|
311
|
+
const modalFooterElm = (0, common_1.createDomElement)('div', { className: 'slick-editor-modal-footer' });
|
|
312
|
+
const modalCancelButtonElm = (0, common_1.createDomElement)('button', {
|
|
313
|
+
type: 'button',
|
|
314
|
+
className: 'btn btn-cancel btn-default btn-sm',
|
|
315
|
+
textContent: this.getLabelText('cancelButton', 'TEXT_CANCEL', 'Cancel'),
|
|
316
|
+
dataset: { action: 'cancel' },
|
|
317
|
+
});
|
|
318
|
+
modalCancelButtonElm.setAttribute('aria-label', this.getLabelText('cancelButton', 'TEXT_CANCEL', 'Cancel'));
|
|
319
|
+
let leftFooterText = '';
|
|
320
|
+
let saveButtonText = '';
|
|
321
|
+
switch (modalType) {
|
|
322
|
+
case 'clone':
|
|
323
|
+
saveButtonText = this.getLabelText('cloneButton', 'TEXT_CLONE', 'Clone');
|
|
324
|
+
break;
|
|
325
|
+
case 'mass-update':
|
|
326
|
+
const footerUnparsedText = this.getLabelText('massUpdateStatus', 'TEXT_ALL_X_RECORDS_SELECTED', 'All {{x}} records selected');
|
|
327
|
+
leftFooterText = this.parseText(footerUnparsedText, { x: fullDatasetLength });
|
|
328
|
+
saveButtonText = this.getLabelText('massUpdateButton', 'TEXT_APPLY_MASS_UPDATE', 'Mass Update');
|
|
329
|
+
break;
|
|
330
|
+
case 'mass-selection':
|
|
331
|
+
const selectionUnparsedText = this.getLabelText('massSelectionStatus', 'TEXT_X_OF_Y_MASS_SELECTED', '{{x}} of {{y}} selected');
|
|
332
|
+
leftFooterText = this.parseText(selectionUnparsedText, { x: dataContextIds.length, y: fullDatasetLength });
|
|
333
|
+
saveButtonText = this.getLabelText('massSelectionButton', 'TEXT_APPLY_TO_SELECTION', 'Update Selection');
|
|
334
|
+
break;
|
|
335
|
+
default:
|
|
336
|
+
saveButtonText = this.getLabelText('saveButton', 'TEXT_SAVE', 'Save');
|
|
337
|
+
}
|
|
338
|
+
const selectionCounterElm = (0, common_1.createDomElement)('div', { className: 'footer-status-text', textContent: leftFooterText });
|
|
339
|
+
this._modalSaveButtonElm = (0, common_1.createDomElement)('button', {
|
|
340
|
+
type: 'button', className: 'btn btn-save btn-primary btn-sm',
|
|
341
|
+
textContent: saveButtonText,
|
|
342
|
+
dataset: {
|
|
343
|
+
action: (modalType === 'create' || modalType === 'edit') ? 'save' : modalType,
|
|
344
|
+
ariaLabel: saveButtonText
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
this._modalSaveButtonElm.setAttribute('aria-label', saveButtonText);
|
|
348
|
+
const footerContainerElm = (0, common_1.createDomElement)('div', { className: 'footer-buttons' });
|
|
349
|
+
if (modalType === 'mass-update' || modalType === 'mass-selection') {
|
|
350
|
+
modalFooterElm.appendChild(selectionCounterElm);
|
|
351
|
+
}
|
|
352
|
+
footerContainerElm.appendChild(modalCancelButtonElm);
|
|
353
|
+
footerContainerElm.appendChild(this._modalSaveButtonElm);
|
|
354
|
+
modalFooterElm.appendChild(footerContainerElm);
|
|
355
|
+
modalContentElm.appendChild(modalHeaderElm);
|
|
356
|
+
modalContentElm.appendChild(modalBodyElm);
|
|
357
|
+
modalContentElm.appendChild(modalFooterElm);
|
|
358
|
+
this._modalElm.appendChild(modalContentElm);
|
|
359
|
+
for (const columnDef of modalColumns) {
|
|
360
|
+
if (columnDef.editor) {
|
|
361
|
+
const itemContainer = (0, common_1.createDomElement)('div', { className: `item-details-container editor-${columnDef.id}` });
|
|
362
|
+
if (layoutColCount === 1) {
|
|
363
|
+
itemContainer.classList.add('slick-col-medium-12');
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
itemContainer.classList.add('slick-col-medium-6', `slick-col-xlarge-${12 / layoutColCount}`);
|
|
367
|
+
}
|
|
368
|
+
const templateItemLabelElm = (0, common_1.createDomElement)('div', {
|
|
369
|
+
className: `item-details-label editor-${columnDef.id}`,
|
|
370
|
+
innerHTML: (0, common_1.sanitizeTextByAvailableSanitizer)(this.gridOptions, this.getColumnLabel(columnDef) || 'n/a')
|
|
371
|
+
});
|
|
372
|
+
const templateItemEditorElm = (0, common_1.createDomElement)('div', {
|
|
373
|
+
className: 'item-details-editor-container slick-cell',
|
|
374
|
+
dataset: { editorid: `${columnDef.id}` },
|
|
375
|
+
});
|
|
376
|
+
const templateItemValidationElm = (0, common_1.createDomElement)('div', { className: `item-details-validation editor-${columnDef.id}` });
|
|
377
|
+
// optionally add a reset button beside each editor
|
|
378
|
+
if ((_k = this._options) === null || _k === void 0 ? void 0 : _k.showResetButtonOnEachEditor) {
|
|
379
|
+
const editorResetButtonElm = this.createEditorResetButtonElement(`${columnDef.id}`);
|
|
380
|
+
this._bindEventService.bind(editorResetButtonElm, 'click', this.handleResetInputValue.bind(this));
|
|
381
|
+
templateItemLabelElm.appendChild(editorResetButtonElm);
|
|
382
|
+
}
|
|
383
|
+
itemContainer.appendChild(templateItemLabelElm);
|
|
384
|
+
itemContainer.appendChild(templateItemEditorElm);
|
|
385
|
+
itemContainer.appendChild(templateItemValidationElm);
|
|
386
|
+
modalBodyElm.appendChild(itemContainer);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// optionally add a form reset button
|
|
390
|
+
if ((_l = this._options) === null || _l === void 0 ? void 0 : _l.showFormResetButton) {
|
|
391
|
+
const resetButtonContainerElm = this.createFormResetButtonElement();
|
|
392
|
+
this._bindEventService.bind(resetButtonContainerElm, 'click', this.handleResetFormClicked.bind(this));
|
|
393
|
+
modalBodyElm.appendChild(resetButtonContainerElm);
|
|
394
|
+
}
|
|
395
|
+
document.body.appendChild(this._modalElm);
|
|
396
|
+
document.body.classList.add('slick-modal-open'); // add backdrop to body
|
|
397
|
+
this._bindEventService.bind(document.body, 'click', this.handleBodyClicked.bind(this));
|
|
398
|
+
this._editors = {};
|
|
399
|
+
this._editorContainers = modalColumns.map(col => modalBodyElm.querySelector(`[data-editorid=${col.id}]`)) || [];
|
|
400
|
+
this._compositeOptions = { destroy: this.disposeComponent.bind(this), modalType, validationMsgPrefix: '* ', formValues: {}, editors: this._editors };
|
|
401
|
+
const compositeEditor = new compositeEditor_factory_1.CompositeEditor(modalColumns, this._editorContainers, this._compositeOptions);
|
|
402
|
+
this.grid.editActiveCell(compositeEditor);
|
|
403
|
+
// --
|
|
404
|
+
// Add a few Event Handlers
|
|
405
|
+
// keyboard, blur & button event handlers
|
|
406
|
+
this._bindEventService.bind(modalCloseButtonElm, 'click', this.cancelEditing.bind(this));
|
|
407
|
+
this._bindEventService.bind(modalCancelButtonElm, 'click', this.cancelEditing.bind(this));
|
|
408
|
+
this._bindEventService.bind(this._modalSaveButtonElm, 'click', this.handleSaveClicked.bind(this));
|
|
409
|
+
this._bindEventService.bind(this._modalElm, 'keydown', this.handleKeyDown.bind(this));
|
|
410
|
+
this._bindEventService.bind(this._modalElm, 'focusout', this.validateCurrentEditor.bind(this));
|
|
411
|
+
this._bindEventService.bind(this._modalElm, 'blur', this.validateCurrentEditor.bind(this));
|
|
412
|
+
// when any of the input of the composite editor form changes, we'll add/remove a "modified" CSS className for styling purposes
|
|
413
|
+
this._eventHandler.subscribe(this.grid.onCompositeEditorChange, this.handleOnCompositeEditorChange.bind(this));
|
|
414
|
+
// when adding a new row to the grid, we need to invalidate that row and re-render the grid
|
|
415
|
+
this._eventHandler.subscribe(this.grid.onAddNewRow, (_e, args) => {
|
|
416
|
+
this.insertNewItemInDataView(args.item);
|
|
417
|
+
this._originalDataContext = args.item; // this becomes the new data context
|
|
418
|
+
this.dispose();
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
catch (error) {
|
|
424
|
+
this.dispose();
|
|
425
|
+
const errorMsg = (typeof error === 'string') ? error : ((_p = (_m = error === null || error === void 0 ? void 0 : error.message) !== null && _m !== void 0 ? _m : (_o = error === null || error === void 0 ? void 0 : error.body) === null || _o === void 0 ? void 0 : _o.message) !== null && _p !== void 0 ? _p : '');
|
|
426
|
+
const errorCode = (typeof error === 'string') ? error : (_s = (_q = error === null || error === void 0 ? void 0 : error.status) !== null && _q !== void 0 ? _q : (_r = error === null || error === void 0 ? void 0 : error.body) === null || _r === void 0 ? void 0 : _r.status) !== null && _s !== void 0 ? _s : errorMsg;
|
|
427
|
+
onError({ type: 'error', code: errorCode, message: errorMsg });
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/** Cancel the Editing which will also close the modal window */
|
|
432
|
+
async cancelEditing() {
|
|
433
|
+
var _a, _b;
|
|
434
|
+
let confirmed = true;
|
|
435
|
+
if (this.formValues && Object.keys(this.formValues).length > 0 && typeof this._options.onClose === 'function') {
|
|
436
|
+
confirmed = await this._options.onClose();
|
|
437
|
+
}
|
|
438
|
+
if (confirmed) {
|
|
439
|
+
this.grid.getEditController().cancelCurrentEdit();
|
|
440
|
+
// cancel current edit is not enough when editing/cloning,
|
|
441
|
+
// we also need to reset with the original item data context to undo/reset the entire row
|
|
442
|
+
if (((_a = this._options) === null || _a === void 0 ? void 0 : _a.modalType) === 'edit' || ((_b = this._options) === null || _b === void 0 ? void 0 : _b.modalType) === 'clone') {
|
|
443
|
+
this.resetCurrentRowDataContext();
|
|
444
|
+
}
|
|
445
|
+
this.grid.setActiveRow(this._lastActiveRowNumber);
|
|
446
|
+
this.dispose();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/** Show a Validation Summary text (as a <div>) when a validation fails or simply hide it when there's no error */
|
|
450
|
+
showValidationSummaryText(isShowing, errorMsg = '') {
|
|
451
|
+
var _a, _b;
|
|
452
|
+
if (isShowing && errorMsg !== '') {
|
|
453
|
+
this._modalBodyTopValidationElm.textContent = errorMsg;
|
|
454
|
+
this._modalBodyTopValidationElm.style.display = 'block';
|
|
455
|
+
(_b = (_a = this._modalBodyTopValidationElm).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
456
|
+
this._modalSaveButtonElm.disabled = false;
|
|
457
|
+
this._modalSaveButtonElm.classList.remove('saving');
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
this._modalBodyTopValidationElm.style.display = 'none';
|
|
461
|
+
this._modalBodyTopValidationElm.textContent = errorMsg;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// --
|
|
465
|
+
// protected methods
|
|
466
|
+
// ----------------
|
|
467
|
+
/** Apply Mass Update Changes (form values) to the entire dataset */
|
|
468
|
+
applySaveMassUpdateChanges(formValues, _selection, applyToDataview = true) {
|
|
469
|
+
// not applying to dataView means that we're doing a preview of dataset and we should use a deep copy of it instead of applying changes directly to it
|
|
470
|
+
const data = applyToDataview ? this.dataView.getItems() : (0, utils_1.deepCopy)(this.dataView.getItems());
|
|
471
|
+
// from the "lastCompositeEditor" object that we kept as reference, it contains all the changes inside the "formValues" property
|
|
472
|
+
// we can loop through these changes and apply them on the selected row indexes
|
|
473
|
+
for (const itemProp in formValues) {
|
|
474
|
+
if (itemProp in formValues) {
|
|
475
|
+
data.forEach((dataContext) => {
|
|
476
|
+
var _a;
|
|
477
|
+
if (itemProp in formValues && (((_a = this._options) === null || _a === void 0 ? void 0 : _a.validateMassUpdateChange) === undefined || this._options.validateMassUpdateChange(itemProp, dataContext, formValues) !== false)) {
|
|
478
|
+
dataContext[itemProp] = formValues[itemProp];
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// change the entire dataset with our updated dataset
|
|
484
|
+
if (applyToDataview) {
|
|
485
|
+
this.dataView.setItems(data, this.gridOptions.datasetIdPropertyName);
|
|
486
|
+
this.grid.invalidate();
|
|
487
|
+
}
|
|
488
|
+
return data;
|
|
489
|
+
}
|
|
490
|
+
/** Apply Mass Changes to the Selected rows in the grid (form values) */
|
|
491
|
+
applySaveMassSelectionChanges(formValues, selection, applyToDataview = true) {
|
|
492
|
+
var _a, _b;
|
|
493
|
+
const selectedItemIds = (_a = selection === null || selection === void 0 ? void 0 : selection.dataContextIds) !== null && _a !== void 0 ? _a : [];
|
|
494
|
+
const selectedTmpItems = selectedItemIds.map(itemId => this.dataView.getItemById(itemId));
|
|
495
|
+
// not applying to dataView means that we're doing a preview of dataset and we should use a deep copy of it instead of applying changes directly to it
|
|
496
|
+
const selectedItems = applyToDataview ? selectedTmpItems : (0, utils_1.deepCopy)(selectedTmpItems);
|
|
497
|
+
// from the "lastCompositeEditor" object that we kept as reference, it contains all the changes inside the "formValues" property
|
|
498
|
+
// we can loop through these changes and apply them on the selected row indexes
|
|
499
|
+
for (const itemProp in formValues) {
|
|
500
|
+
if (itemProp in formValues) {
|
|
501
|
+
selectedItems.forEach((dataContext) => {
|
|
502
|
+
var _a;
|
|
503
|
+
if (itemProp in formValues && (((_a = this._options) === null || _a === void 0 ? void 0 : _a.validateMassUpdateChange) === undefined || this._options.validateMassUpdateChange(itemProp, dataContext, formValues) !== false)) {
|
|
504
|
+
dataContext[itemProp] = formValues[itemProp];
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// update all items in the grid with the grid service
|
|
510
|
+
if (applyToDataview) {
|
|
511
|
+
(_b = this.gridService) === null || _b === void 0 ? void 0 : _b.updateItems(selectedItems);
|
|
512
|
+
}
|
|
513
|
+
return selectedItems;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Auto-Calculate how many columns to display in the view layout (1, 2, or 3).
|
|
517
|
+
* We'll display a 1 column layout for 8 or less Editors, 2 columns layout for less than 15 Editors or 3 columns when more than 15 Editors
|
|
518
|
+
* @param {number} editorCount - how many Editors do we have in total
|
|
519
|
+
* @returns {number} count - calculated column count (1, 2 or 3)
|
|
520
|
+
*/
|
|
521
|
+
autoCalculateLayoutColumnCount(editorCount) {
|
|
522
|
+
if (editorCount >= 15) {
|
|
523
|
+
return 3;
|
|
524
|
+
}
|
|
525
|
+
else if (editorCount >= 8) {
|
|
526
|
+
return 2;
|
|
527
|
+
}
|
|
528
|
+
return 1;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Create a reset button for each editor and attach a button click handler
|
|
532
|
+
* @param {String} columnId - column id
|
|
533
|
+
* @returns {Object} - html button
|
|
534
|
+
*/
|
|
535
|
+
createEditorResetButtonElement(columnId) {
|
|
536
|
+
var _a, _b, _c, _d, _f;
|
|
537
|
+
const resetButtonElm = (0, common_1.createDomElement)('button', {
|
|
538
|
+
type: 'button', name: columnId,
|
|
539
|
+
title: (_c = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.labels) === null || _b === void 0 ? void 0 : _b.resetFormButton) !== null && _c !== void 0 ? _c : 'Reset Form Input',
|
|
540
|
+
className: 'btn btn-xs btn-editor-reset'
|
|
541
|
+
});
|
|
542
|
+
resetButtonElm.setAttribute('aria-label', 'Reset');
|
|
543
|
+
if ((_d = this._options) === null || _d === void 0 ? void 0 : _d.resetEditorButtonCssClass) {
|
|
544
|
+
const resetBtnClasses = (_f = this._options) === null || _f === void 0 ? void 0 : _f.resetEditorButtonCssClass.split(' ');
|
|
545
|
+
for (const cssClass of resetBtnClasses) {
|
|
546
|
+
resetButtonElm.classList.add(cssClass);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return resetButtonElm;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Create a form reset button and attach a button click handler
|
|
553
|
+
* @param {String} columnId - column id
|
|
554
|
+
* @returns {Object} - html button
|
|
555
|
+
*/
|
|
556
|
+
createFormResetButtonElement() {
|
|
557
|
+
var _a, _b;
|
|
558
|
+
const resetButtonContainerElm = (0, common_1.createDomElement)('div', { className: 'reset-container' });
|
|
559
|
+
const resetButtonElm = (0, common_1.createDomElement)('button', { type: 'button', className: 'btn btn-sm reset-form' });
|
|
560
|
+
const resetIconSpanElm = (0, common_1.createDomElement)('span', { className: (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.resetFormButtonIconCssClass) !== null && _b !== void 0 ? _b : '' });
|
|
561
|
+
resetButtonElm.appendChild(resetIconSpanElm);
|
|
562
|
+
resetButtonElm.appendChild(document.createTextNode(' Reset Form'));
|
|
563
|
+
resetButtonContainerElm.appendChild(resetButtonElm);
|
|
564
|
+
return resetButtonContainerElm;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Execute the onError callback when defined
|
|
568
|
+
* or use the default onError callback which is to simply display the error in the console
|
|
569
|
+
*/
|
|
570
|
+
executeOnError(error) {
|
|
571
|
+
var _a, _b;
|
|
572
|
+
const onError = (_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.onError) !== null && _b !== void 0 ? _b : DEFAULT_ON_ERROR;
|
|
573
|
+
onError(error);
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* A simple and generic method to execute the "OnSave" callback if it's defined by the user OR else simply execute built-in apply changes callback.
|
|
577
|
+
* This method deals with multiple callbacks as shown below
|
|
578
|
+
* @param {Function} applyChangesCallback - first callback to apply the changes into the grid (this could be a user custom callback)
|
|
579
|
+
* @param {Function} executePostCallback - second callback to execute right after the "onSave"
|
|
580
|
+
* @param {Function} beforeClosingCallback - third and last callback to execute after Saving but just before closing the modal window
|
|
581
|
+
* @param {Object} itemDataContext - item data context when modal type is (create/clone/edit)
|
|
582
|
+
*/
|
|
583
|
+
async executeOnSave(applyChangesCallback, executePostCallback, beforeClosingCallback, itemDataContext) {
|
|
584
|
+
var _a, _b, _c, _d, _f, _g;
|
|
585
|
+
try {
|
|
586
|
+
this.showValidationSummaryText(false, '');
|
|
587
|
+
const validationResults = this.validateCompositeEditors();
|
|
588
|
+
if (validationResults.valid) {
|
|
589
|
+
this._modalSaveButtonElm.classList.add('saving');
|
|
590
|
+
this._modalSaveButtonElm.disabled = true;
|
|
591
|
+
if (typeof ((_a = this._options) === null || _a === void 0 ? void 0 : _a.onSave) === 'function') {
|
|
592
|
+
const isMassChange = (this._options.modalType === 'mass-update' || this._options.modalType === 'mass-selection');
|
|
593
|
+
// apply the changes in the grid early when that option is enabled (that is before the await of `onSave`)
|
|
594
|
+
let updatedDataset;
|
|
595
|
+
if (isMassChange && ((_b = this._options) === null || _b === void 0 ? void 0 : _b.shouldPreviewMassChangeDataset)) {
|
|
596
|
+
updatedDataset = applyChangesCallback(this.formValues, this.getCurrentRowSelections(), false);
|
|
597
|
+
}
|
|
598
|
+
// call the custon onSave callback when defined and note that the item data context will only be filled for create/clone/edit
|
|
599
|
+
const dataContextOrUpdatedDatasetPreview = isMassChange ? updatedDataset : itemDataContext;
|
|
600
|
+
const successful = await ((_c = this._options) === null || _c === void 0 ? void 0 : _c.onSave(this.formValues, this.getCurrentRowSelections(), dataContextOrUpdatedDatasetPreview));
|
|
601
|
+
if (successful) {
|
|
602
|
+
// apply the changes in the grid (if it's not yet applied)
|
|
603
|
+
applyChangesCallback(this.formValues, this.getCurrentRowSelections());
|
|
604
|
+
// once we're done doing the mass update, we can cancel the current editor since we don't want to add any new row
|
|
605
|
+
// that will also destroy/close the modal window
|
|
606
|
+
executePostCallback();
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
applyChangesCallback(this.formValues, this.getCurrentRowSelections());
|
|
611
|
+
executePostCallback();
|
|
612
|
+
}
|
|
613
|
+
// run any function before closing the modal
|
|
614
|
+
if (typeof beforeClosingCallback === 'function') {
|
|
615
|
+
beforeClosingCallback();
|
|
616
|
+
}
|
|
617
|
+
// close the modal only when successful
|
|
618
|
+
this.dispose();
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
catch (error) {
|
|
622
|
+
const errorMsg = (typeof error === 'string') ? error : ((_g = (_d = error === null || error === void 0 ? void 0 : error.message) !== null && _d !== void 0 ? _d : (_f = error === null || error === void 0 ? void 0 : error.body) === null || _f === void 0 ? void 0 : _f.message) !== null && _g !== void 0 ? _g : '');
|
|
623
|
+
this.showValidationSummaryText(true, errorMsg);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
// For the Composite Editor to work, the current active cell must have an Editor (because it calls editActiveCell() and that only works with a cell with an Editor)
|
|
627
|
+
// so if current active cell doesn't have an Editor, we'll find the first column with an Editor and focus on it (from left to right starting at index 0)
|
|
628
|
+
focusOnFirstColumnCellWithEditor(columns, dataContext, columnIndex, rowIndex, isWithMassChange) {
|
|
629
|
+
// make sure we're not trying to activate a cell outside of the grid, that can happen when using MassUpdate without `enableAddRow` flag enabled
|
|
630
|
+
const activeCellIndex = (isWithMassChange && !this.gridOptions.enableAddRow && (rowIndex >= this.dataViewLength)) ? this.dataViewLength - 1 : rowIndex;
|
|
631
|
+
let columnIndexWithEditor = columnIndex;
|
|
632
|
+
const cellEditor = columns[columnIndex].editor;
|
|
633
|
+
let activeEditorCellNode = this.grid.getCellNode(activeCellIndex, columnIndex);
|
|
634
|
+
if (!cellEditor || !activeEditorCellNode || !this.getActiveCellEditor(activeCellIndex, columnIndex)) {
|
|
635
|
+
columnIndexWithEditor = this.findNextAvailableEditorColumnIndex(columns, dataContext, rowIndex, isWithMassChange);
|
|
636
|
+
if (columnIndexWithEditor === -1) {
|
|
637
|
+
this.executeOnError({ type: 'error', code: 'NO_EDITOR_FOUND', message: 'We could not find any Editor in your Column Definition' });
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
this.grid.setActiveCell(activeCellIndex, columnIndexWithEditor, false);
|
|
642
|
+
if (isWithMassChange) {
|
|
643
|
+
// when it's a mass change, we'll activate the last row without scrolling to it
|
|
644
|
+
// that is possible via the 3rd argument "suppressScrollIntoView" set to "true"
|
|
645
|
+
this.grid.setActiveRow(this.dataViewLength, columnIndexWithEditor, true);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
// check again if the cell node is now being created, if it is then we're good
|
|
650
|
+
activeEditorCellNode = this.grid.getCellNode(activeCellIndex, columnIndexWithEditor);
|
|
651
|
+
return !!activeEditorCellNode;
|
|
652
|
+
}
|
|
653
|
+
findNextAvailableEditorColumnIndex(columns, dataContext, rowIndex, isWithMassUpdate) {
|
|
654
|
+
var _a;
|
|
655
|
+
let columnIndexWithEditor = -1;
|
|
656
|
+
for (let colIndex = 0; colIndex < columns.length; colIndex++) {
|
|
657
|
+
const col = columns[colIndex];
|
|
658
|
+
if (col.editor && (!isWithMassUpdate || (isWithMassUpdate && ((_a = col.internalColumnEditor) === null || _a === void 0 ? void 0 : _a.massUpdate)))) {
|
|
659
|
+
// we can check that the cell is really editable by checking the onBeforeEditCell event not returning false (returning undefined, null also mean it is editable)
|
|
660
|
+
const isCellEditable = this.grid.onBeforeEditCell.notify({ row: rowIndex, cell: colIndex, item: dataContext, column: col, grid: this.grid, target: 'composite', compositeEditorOptions: this._compositeOptions });
|
|
661
|
+
this.grid.setActiveCell(rowIndex, colIndex, false);
|
|
662
|
+
if (isCellEditable !== false) {
|
|
663
|
+
columnIndexWithEditor = colIndex;
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return columnIndexWithEditor;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Get a column definition by providing a column id OR a column definition.
|
|
672
|
+
* If the input is a string, we'll assume it's a columnId and we'll simply search for the column in the column definitions list
|
|
673
|
+
*/
|
|
674
|
+
getColumnByObjectOrId(columnIdOrDef) {
|
|
675
|
+
let column;
|
|
676
|
+
if (typeof columnIdOrDef === 'object') {
|
|
677
|
+
column = columnIdOrDef;
|
|
678
|
+
}
|
|
679
|
+
else if (typeof columnIdOrDef === 'string') {
|
|
680
|
+
column = this._columnDefinitions.find(col => col.id === columnIdOrDef);
|
|
681
|
+
}
|
|
682
|
+
return column;
|
|
683
|
+
}
|
|
684
|
+
getActiveCellEditor(row, cell) {
|
|
685
|
+
this.grid.setActiveCell(row, cell, false);
|
|
686
|
+
return this.grid.getCellEditor();
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Get the column label, the label might have an optional "columnGroup" (or "columnGroupKey" which need to be translated)
|
|
690
|
+
* @param {object} columnDef - column definition
|
|
691
|
+
* @returns {string} label - column label
|
|
692
|
+
*/
|
|
693
|
+
getColumnLabel(columnDef) {
|
|
694
|
+
var _a;
|
|
695
|
+
const columnGroupSeparator = this.gridOptions.columnGroupSeparator || ' - ';
|
|
696
|
+
let columnName = columnDef.nameCompositeEditor || columnDef.name || '';
|
|
697
|
+
let columnGroup = columnDef.columnGroup || '';
|
|
698
|
+
if (this.gridOptions.enableTranslate && this.translaterService) {
|
|
699
|
+
const translationKey = columnDef.nameCompositeEditorKey || columnDef.nameKey;
|
|
700
|
+
if (translationKey) {
|
|
701
|
+
columnName = this.translaterService.translate(translationKey);
|
|
702
|
+
}
|
|
703
|
+
if (columnDef.columnGroupKey && ((_a = this.translaterService) === null || _a === void 0 ? void 0 : _a.translate)) {
|
|
704
|
+
columnGroup = this.translaterService.translate(columnDef.columnGroupKey);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
const columnLabel = columnGroup ? `${columnGroup}${columnGroupSeparator}${columnName}` : columnName;
|
|
708
|
+
return columnLabel || '';
|
|
709
|
+
}
|
|
710
|
+
/** Get the correct label text depending, if we use a Translater Service then translate the text when possible else use default text */
|
|
711
|
+
getLabelText(labelProperty, localeText, defaultText) {
|
|
712
|
+
var _a, _b, _c, _d, _f, _g, _h;
|
|
713
|
+
const textLabels = { ...(_a = this.gridOptions.compositeEditorOptions) === null || _a === void 0 ? void 0 : _a.labels, ...(_b = this._options) === null || _b === void 0 ? void 0 : _b.labels };
|
|
714
|
+
if (((_c = this.gridOptions) === null || _c === void 0 ? void 0 : _c.enableTranslate) && ((_d = this.translaterService) === null || _d === void 0 ? void 0 : _d.translate) && textLabels.hasOwnProperty(`${labelProperty}Key`)) {
|
|
715
|
+
const translationKey = textLabels[`${labelProperty}Key`];
|
|
716
|
+
return this.translaterService.translate(translationKey || '');
|
|
717
|
+
}
|
|
718
|
+
return (_h = (_f = textLabels === null || textLabels === void 0 ? void 0 : textLabels[labelProperty]) !== null && _f !== void 0 ? _f : (_g = this._locales) === null || _g === void 0 ? void 0 : _g[localeText]) !== null && _h !== void 0 ? _h : defaultText;
|
|
719
|
+
}
|
|
720
|
+
/** Retrieve the current selection of row indexes & data context Ids */
|
|
721
|
+
getCurrentRowSelections() {
|
|
722
|
+
const dataContextIds = this.dataView.getAllSelectedIds();
|
|
723
|
+
const gridRowIndexes = this.dataView.mapIdsToRows(dataContextIds);
|
|
724
|
+
return { gridRowIndexes, dataContextIds };
|
|
725
|
+
}
|
|
726
|
+
handleBodyClicked(event) {
|
|
727
|
+
var _a, _b, _c;
|
|
728
|
+
if ((_b = (_a = event.target) === null || _a === void 0 ? void 0 : _a.classList) === null || _b === void 0 ? void 0 : _b.contains('slick-editor-modal')) {
|
|
729
|
+
if (((_c = this._options) === null || _c === void 0 ? void 0 : _c.backdrop) !== 'static') {
|
|
730
|
+
this.dispose();
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
handleKeyDown(event) {
|
|
735
|
+
if (event.code === 'Escape') {
|
|
736
|
+
this.cancelEditing();
|
|
737
|
+
event.stopPropagation();
|
|
738
|
+
event.preventDefault();
|
|
739
|
+
}
|
|
740
|
+
else if (event.code === 'Tab') {
|
|
741
|
+
this.validateCurrentEditor();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
handleResetInputValue(event) {
|
|
745
|
+
var _a, _b;
|
|
746
|
+
const columnId = event.target.name;
|
|
747
|
+
const editor = (_a = this._editors) === null || _a === void 0 ? void 0 : _a[columnId];
|
|
748
|
+
if (editor === null || editor === void 0 ? void 0 : editor.reset) {
|
|
749
|
+
editor.reset();
|
|
750
|
+
}
|
|
751
|
+
(_b = this._formValues) === null || _b === void 0 ? true : delete _b[columnId];
|
|
752
|
+
}
|
|
753
|
+
/** Callback which processes a Mass Update or Mass Selection Changes */
|
|
754
|
+
async handleMassSaving(modalType, executePostCallback) {
|
|
755
|
+
if (!this.formValues || Object.keys(this.formValues).length === 0) {
|
|
756
|
+
this.executeOnError({ type: 'warning', code: 'NO_CHANGES_DETECTED', message: 'Sorry we could not detect any changes.' });
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
const applyCallbackFnName = (modalType === 'mass-update') ? 'applySaveMassUpdateChanges' : 'applySaveMassSelectionChanges';
|
|
760
|
+
this.executeOnSave(this[applyCallbackFnName].bind(this), executePostCallback.bind(this));
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/** Anytime an input of the Composite Editor form changes, we'll add/remove a "modified" CSS className for styling purposes */
|
|
764
|
+
handleOnCompositeEditorChange(_e, args) {
|
|
765
|
+
var _a, _b, _c, _d, _f, _g, _h, _j;
|
|
766
|
+
const columnId = (_b = (_a = args.column) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : '';
|
|
767
|
+
this._formValues = { ...this._formValues, ...args.formValues };
|
|
768
|
+
const editor = (_c = this._editors) === null || _c === void 0 ? void 0 : _c[columnId];
|
|
769
|
+
const isEditorValueTouched = (_h = (_f = (_d = editor === null || editor === void 0 ? void 0 : editor.isValueTouched) === null || _d === void 0 ? void 0 : _d.call(editor)) !== null && _f !== void 0 ? _f : (_g = editor === null || editor === void 0 ? void 0 : editor.isValueChanged) === null || _g === void 0 ? void 0 : _g.call(editor)) !== null && _h !== void 0 ? _h : false;
|
|
770
|
+
this._itemDataContext = (_j = editor === null || editor === void 0 ? void 0 : editor.dataContext) !== null && _j !== void 0 ? _j : {}; // keep reference of the item data context
|
|
771
|
+
// add extra css styling to the composite editor input(s) that got modified
|
|
772
|
+
const editorElm = this._modalElm.querySelector(`[data-editorid=${columnId}]`);
|
|
773
|
+
if (editorElm === null || editorElm === void 0 ? void 0 : editorElm.classList) {
|
|
774
|
+
if (isEditorValueTouched) {
|
|
775
|
+
editorElm.classList.add('modified');
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
editorElm.classList.remove('modified');
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// after any input changes we'll re-validate all fields
|
|
782
|
+
this.validateCompositeEditors();
|
|
783
|
+
}
|
|
784
|
+
/** Check wether the grid has the Row Selection enabled */
|
|
785
|
+
hasRowSelectionEnabled() {
|
|
786
|
+
const selectionModel = this.grid.getSelectionModel();
|
|
787
|
+
const isRowSelectionEnabled = this.gridOptions.enableRowSelection || this.gridOptions.enableCheckboxSelector;
|
|
788
|
+
return (isRowSelectionEnabled && selectionModel);
|
|
789
|
+
}
|
|
790
|
+
/** Reset Form button handler */
|
|
791
|
+
handleResetFormClicked() {
|
|
792
|
+
for (const columnId of Object.keys(this._editors)) {
|
|
793
|
+
const editor = this._editors[columnId];
|
|
794
|
+
if (editor === null || editor === void 0 ? void 0 : editor.reset) {
|
|
795
|
+
editor.reset();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
this._formValues = (0, utils_1.emptyObject)(this._formValues);
|
|
799
|
+
}
|
|
800
|
+
/** switch case handler to determine which code to execute depending on the modal type */
|
|
801
|
+
handleSaveClicked() {
|
|
802
|
+
var _a, _b, _c;
|
|
803
|
+
const modalType = (_a = this._options) === null || _a === void 0 ? void 0 : _a.modalType;
|
|
804
|
+
switch (modalType) {
|
|
805
|
+
case 'mass-update':
|
|
806
|
+
this.handleMassSaving(modalType, () => {
|
|
807
|
+
this.grid.getEditController().cancelCurrentEdit();
|
|
808
|
+
this.grid.setActiveCell(0, 0, false);
|
|
809
|
+
if (this._options.shouldClearRowSelectionAfterMassAction) {
|
|
810
|
+
this.grid.setSelectedRows([]);
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
break;
|
|
814
|
+
case 'mass-selection':
|
|
815
|
+
this.handleMassSaving(modalType, () => {
|
|
816
|
+
this.grid.getEditController().cancelCurrentEdit();
|
|
817
|
+
this.grid.setActiveRow(this._lastActiveRowNumber);
|
|
818
|
+
if (this._options.shouldClearRowSelectionAfterMassAction) {
|
|
819
|
+
this.grid.setSelectedRows([]);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
break;
|
|
823
|
+
case 'clone':
|
|
824
|
+
// the clone object will be a merge of the selected data context (original object) with the changed form values
|
|
825
|
+
const clonedItemDataContext = { ...this._originalDataContext, ...this.formValues };
|
|
826
|
+
// post save callback (before closing modal)
|
|
827
|
+
const postSaveCloneCallback = () => {
|
|
828
|
+
this.grid.getEditController().cancelCurrentEdit();
|
|
829
|
+
this.grid.setActiveCell(0, 0, false);
|
|
830
|
+
};
|
|
831
|
+
// call the onSave execution and provide the item data context so that it's available to the user
|
|
832
|
+
this.executeOnSave(this.insertNewItemInDataView.bind(this, clonedItemDataContext), postSaveCloneCallback, this.resetCurrentRowDataContext.bind(this), clonedItemDataContext);
|
|
833
|
+
break;
|
|
834
|
+
case 'create':
|
|
835
|
+
case 'edit':
|
|
836
|
+
default:
|
|
837
|
+
// commit the changes into the grid
|
|
838
|
+
// if it's a "create" then it will triggered the "onAddNewRow" event which will in term push it to the grid
|
|
839
|
+
// while an "edit" will simply applies the changes directly on the same row
|
|
840
|
+
this.grid.getEditController().commitCurrentEdit();
|
|
841
|
+
// if the user provided the "onSave" callback, let's execute it with the item data context
|
|
842
|
+
if (typeof ((_b = this._options) === null || _b === void 0 ? void 0 : _b.onSave) === 'function') {
|
|
843
|
+
const itemDataContext = this.grid.getDataItem(this._lastActiveRowNumber); // we can get item data context directly from DataView
|
|
844
|
+
(_c = this._options) === null || _c === void 0 ? void 0 : _c.onSave(this.formValues, this.getCurrentRowSelections(), itemDataContext);
|
|
845
|
+
}
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/** Insert an item into the DataView or throw an error when finding duplicate id in the dataset */
|
|
850
|
+
insertNewItemInDataView(item) {
|
|
851
|
+
var _a, _b, _c, _d;
|
|
852
|
+
const fullDatasetLength = (_b = (_a = this.dataView) === null || _a === void 0 ? void 0 : _a.getItemCount()) !== null && _b !== void 0 ? _b : 0;
|
|
853
|
+
const newId = (_c = this._options.insertNewId) !== null && _c !== void 0 ? _c : fullDatasetLength + 1;
|
|
854
|
+
item[this.gridOptions.datasetIdPropertyName || 'id'] = newId;
|
|
855
|
+
if (!this.dataView.getItemById(newId)) {
|
|
856
|
+
(_d = this.gridService) === null || _d === void 0 ? void 0 : _d.addItem(item, this._options.insertOptions);
|
|
857
|
+
}
|
|
858
|
+
else {
|
|
859
|
+
this.executeOnError({ type: 'error', code: 'ITEM_ALREADY_EXIST', message: `The item object which you are trying to add already exist with the same Id:: ${newId}` });
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
parseText(inputText, mappedArgs) {
|
|
863
|
+
return inputText.replace(/\{\{(.*?)\}\}/g, (match, group) => {
|
|
864
|
+
return mappedArgs[group] !== undefined ? mappedArgs[group] : match;
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
/** Put back the current row to its original item data context using the DataView without triggering a change */
|
|
868
|
+
resetCurrentRowDataContext() {
|
|
869
|
+
const idPropName = this.gridOptions.datasetIdPropertyName || 'id';
|
|
870
|
+
const dataView = this.grid.getData();
|
|
871
|
+
dataView.updateItem(this._originalDataContext[idPropName], this._originalDataContext);
|
|
872
|
+
}
|
|
873
|
+
/** Validate all the Composite Editors that are defined in the form */
|
|
874
|
+
validateCompositeEditors(targetElm) {
|
|
875
|
+
let validationResults = { valid: true, msg: '' };
|
|
876
|
+
const currentEditor = this.grid.getCellEditor();
|
|
877
|
+
if (currentEditor) {
|
|
878
|
+
validationResults = currentEditor.validate(targetElm);
|
|
879
|
+
}
|
|
880
|
+
return validationResults;
|
|
881
|
+
}
|
|
882
|
+
/** Validate the current cell editor */
|
|
883
|
+
validateCurrentEditor() {
|
|
884
|
+
const currentEditor = this.grid.getCellEditor();
|
|
885
|
+
if (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.validate) {
|
|
886
|
+
currentEditor.validate();
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
exports.SlickCompositeEditorComponent = SlickCompositeEditorComponent;
|
|
891
891
|
//# sourceMappingURL=slick-composite-editor.component.js.map
|