@trudb/tru-common-lib 0.2.542 → 0.2.543

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.
@@ -2757,13 +2757,24 @@ class TruSearchViewBase {
2757
2757
  }
2758
2758
  onPkeyCellDoubleClicked = (gridConfig) => {
2759
2759
  if (this.hasDetailView) {
2760
+ let clickedEntity = gridConfig.data?.$entity;
2761
+ if (!clickedEntity) {
2762
+ return;
2763
+ }
2760
2764
  let entities = [];
2761
- gridConfig.api.forEachNodeAfterFilterAndSort((rowNode) => {
2762
- entities.push(rowNode.data.$entity);
2763
- });
2765
+ for (let i = 0; i < gridConfig.api.getDisplayedRowCount(); i++) {
2766
+ let rowNode = gridConfig.api.getDisplayedRowAtIndex(i);
2767
+ if (rowNode?.data?.$entity) {
2768
+ entities.push(rowNode.data.$entity);
2769
+ }
2770
+ }
2771
+ let entityIndex = entities.indexOf(clickedEntity);
2772
+ if (entityIndex < 0) {
2773
+ return;
2774
+ }
2764
2775
  let windowAddViewEventArgs = new TruDesktopViewConfig();
2765
2776
  windowAddViewEventArgs.entities = entities;
2766
- windowAddViewEventArgs.entityIndex = gridConfig.rowIndex;
2777
+ windowAddViewEventArgs.entityIndex = entityIndex;
2767
2778
  windowAddViewEventArgs.componentName = this.tableName + 'DetailView';
2768
2779
  this.windowEventHandler.addView(windowAddViewEventArgs);
2769
2780
  this.view.active = false;
@@ -5141,13 +5152,24 @@ class TruSearchResultViewBase {
5141
5152
  }
5142
5153
  onPkeyCellDoubleClicked = (gridConfig) => {
5143
5154
  if (this.hasDetailView) {
5155
+ let clickedEntity = gridConfig.data?.$entity;
5156
+ if (!clickedEntity) {
5157
+ return;
5158
+ }
5144
5159
  let entities = [];
5145
- gridConfig.api.forEachNodeAfterFilterAndSort((rowNode) => {
5146
- entities.push(rowNode.data.$entity);
5147
- });
5160
+ for (let i = 0; i < gridConfig.api.getDisplayedRowCount(); i++) {
5161
+ let rowNode = gridConfig.api.getDisplayedRowAtIndex(i);
5162
+ if (rowNode?.data?.$entity) {
5163
+ entities.push(rowNode.data.$entity);
5164
+ }
5165
+ }
5166
+ let entityIndex = entities.indexOf(clickedEntity);
5167
+ if (entityIndex < 0) {
5168
+ return;
5169
+ }
5148
5170
  let windowAddViewEventArgs = new TruDesktopViewConfig();
5149
5171
  windowAddViewEventArgs.entities = entities;
5150
- windowAddViewEventArgs.entityIndex = gridConfig.rowIndex;
5172
+ windowAddViewEventArgs.entityIndex = entityIndex;
5151
5173
  windowAddViewEventArgs.componentName = this.tableName + 'DetailView';
5152
5174
  this.truWindowEventHandler.addView(windowAddViewEventArgs);
5153
5175
  }
@@ -7253,7 +7275,9 @@ class TruDataGridClipboard {
7253
7275
  copiedCellValue = null;
7254
7276
  copiedCellEntity = null;
7255
7277
  copiedCellParams = null;
7278
+ copiedCellText = null;
7256
7279
  copiedRows = [];
7280
+ copiedRowsText = null;
7257
7281
  gridElement = null;
7258
7282
  constructor(util, formatter, uiNotification, modelPropertyLookup) {
7259
7283
  this.util = util;
@@ -7269,6 +7293,46 @@ class TruDataGridClipboard {
7269
7293
  else
7270
7294
  return this.formatter.excelFormula(propertyConfig.formatter(controlConfig));
7271
7295
  };
7296
+ normalizeClipboardText = (value) => String(value ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n$/, '');
7297
+ readClipboardText = async () => {
7298
+ if (!navigator.clipboard?.readText)
7299
+ throw new Error('The browser does not support reading from the clipboard.');
7300
+ return navigator.clipboard.readText();
7301
+ };
7302
+ parseExternalCellValue = (clipboardText, controlConfig) => {
7303
+ const value = this.normalizeClipboardText(clipboardText);
7304
+ const currentValue = controlConfig.$;
7305
+ const property = controlConfig.property;
7306
+ const typeName = property?.typeName;
7307
+ if (!value && property?.isNullable)
7308
+ return null;
7309
+ const isDate = currentValue instanceof Date || typeName === 'date' || typeName === 'date-time';
7310
+ if (isDate) {
7311
+ const isoDate = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(value);
7312
+ const parsedValue = isoDate
7313
+ ? new Date(Number(isoDate[1]), Number(isoDate[2]) - 1, Number(isoDate[3]))
7314
+ : new Date(value);
7315
+ if (isNaN(parsedValue.getTime()))
7316
+ throw new Error('Clipboard data is not a valid date.');
7317
+ return parsedValue;
7318
+ }
7319
+ const numericTypes = ['primary-key', 'integer', 'big-integer', 'decimal', 'usa-dollars', 'percentage', 'scientific', 'foreign-key', 'user-foreign-key'];
7320
+ if (typeof currentValue === 'number' || numericTypes.includes(typeName)) {
7321
+ const numericText = value.replace(/[$,%\s]/g, '');
7322
+ const parsedValue = Number(numericText);
7323
+ if (!numericText || !Number.isFinite(parsedValue))
7324
+ throw new Error('Clipboard data is not a valid number.');
7325
+ return typeName === 'percentage' && value.includes('%') ? parsedValue / 100 : parsedValue;
7326
+ }
7327
+ if (typeof currentValue === 'boolean') {
7328
+ if (/^(true|yes|1)$/i.test(value))
7329
+ return true;
7330
+ if (/^(false|no|0)$/i.test(value))
7331
+ return false;
7332
+ throw new Error('Clipboard data is not a valid yes/no value.');
7333
+ }
7334
+ return value;
7335
+ };
7272
7336
  removeBorderAnimation = () => {
7273
7337
  var elementsWithAnimatedBorders = this.gridElement?.querySelectorAll('.animated-border');
7274
7338
  if (elementsWithAnimatedBorders && elementsWithAnimatedBorders.length)
@@ -7308,6 +7372,7 @@ class TruDataGridClipboard {
7308
7372
  this.copiedCellData = copiedCellData;
7309
7373
  this.copiedCellValue = copiedCellData[columnName].$;
7310
7374
  this.copiedCellEntity = copiedCellEntity;
7375
+ this.copiedCellText = this.normalizeClipboardText(params.value);
7311
7376
  this.removeBorderAnimation();
7312
7377
  this.addCellBorderAnimation();
7313
7378
  try {
@@ -7317,28 +7382,49 @@ class TruDataGridClipboard {
7317
7382
  console.error('Failed to copy text: ', err);
7318
7383
  }
7319
7384
  };
7320
- pasteCell = (params, pastedCellData, pastedCell) => {
7385
+ pasteCell = async (params, pastedCellData, pastedCell) => {
7321
7386
  let colDef = params.colDef;
7322
7387
  let columnName = colDef.field;
7323
- let copiedCellDataTypeName = this.copiedCellData[this.copiedCellParams?.colDef?.field].property.typeName;
7324
- let pastedCellDataTypeName = pastedCellData[params.colDef?.field].property.typeName;
7325
7388
  if (!colDef.editable) {
7326
7389
  this.uiNotification.error('Cannot paste into a non-editable column: ' + colDef.field);
7327
7390
  return;
7328
7391
  }
7329
- if (copiedCellDataTypeName !== pastedCellDataTypeName) {
7330
- this.uiNotification.error('Clipboard data type does not match the current cell data type: ' + copiedCellDataTypeName + ' vs ' + pastedCellDataTypeName);
7392
+ let clipboardText;
7393
+ try {
7394
+ clipboardText = this.normalizeClipboardText(await this.readClipboardText());
7395
+ }
7396
+ catch (err) {
7397
+ this.uiNotification.error('Unable to read data from the system clipboard.');
7331
7398
  return;
7332
7399
  }
7400
+ const pastedCellConfig = pastedCellData[columnName];
7401
+ const isInternalCopy = this.copiedCellText !== null && clipboardText === this.copiedCellText;
7402
+ if (isInternalCopy && this.copiedCellData && this.copiedCellParams) {
7403
+ const copiedColumnName = this.copiedCellParams.colDef?.field;
7404
+ const copiedCellDataTypeName = this.copiedCellData[copiedColumnName].property.typeName;
7405
+ const pastedCellDataTypeName = pastedCellConfig.property.typeName;
7406
+ if (copiedCellDataTypeName !== pastedCellDataTypeName) {
7407
+ this.uiNotification.error('Clipboard data type does not match the current cell data type: ' + copiedCellDataTypeName + ' vs ' + pastedCellDataTypeName);
7408
+ return;
7409
+ }
7410
+ pastedCellConfig.$ = this.copiedCellValue;
7411
+ }
7333
7412
  else {
7334
- pastedCellData[params.colDef?.field].$ = this.copiedCellValue;
7335
- this.removeBorderAnimation();
7336
- params.api.refreshCells({ force: true });
7413
+ try {
7414
+ pastedCellConfig.$ = this.parseExternalCellValue(clipboardText, pastedCellConfig);
7415
+ }
7416
+ catch (err) {
7417
+ this.uiNotification.error(err.message);
7418
+ return;
7419
+ }
7337
7420
  }
7421
+ this.removeBorderAnimation();
7422
+ params.api.refreshCells({ force: true });
7338
7423
  };
7339
7424
  copyRow = async (tableName, columnDefs, copiedRows, includeHeaders = false) => {
7340
7425
  this.tableName = tableName;
7341
7426
  this.copiedRows = copiedRows;
7427
+ this.copiedRowsText = null;
7342
7428
  this.removeBorderAnimation();
7343
7429
  this.addRowBorderAnimation();
7344
7430
  if (copiedRows.length) {
@@ -7364,6 +7450,7 @@ class TruDataGridClipboard {
7364
7450
  });
7365
7451
  multiRowString += rowPropertyValues.join('\t') + '\n';
7366
7452
  });
7453
+ this.copiedRowsText = this.normalizeClipboardText(multiRowString);
7367
7454
  try {
7368
7455
  await navigator.clipboard.writeText(multiRowString);
7369
7456
  }
@@ -7372,7 +7459,19 @@ class TruDataGridClipboard {
7372
7459
  }
7373
7460
  }
7374
7461
  };
7375
- pasteRow = (gridApi, config, selectedRows) => {
7462
+ pasteRow = async (gridApi, config, selectedRows) => {
7463
+ let clipboardText;
7464
+ try {
7465
+ clipboardText = this.normalizeClipboardText(await this.readClipboardText());
7466
+ }
7467
+ catch (err) {
7468
+ this.uiNotification.error('Unable to read data from the system clipboard.');
7469
+ return;
7470
+ }
7471
+ if (this.copiedRowsText === null || clipboardText !== this.copiedRowsText) {
7472
+ this.uiNotification.error('External row data cannot be pasted as an internal grid row copy. Paste into an individual cell instead.');
7473
+ return;
7474
+ }
7376
7475
  if (this.tableName !== config.tableName) {
7377
7476
  this.uiNotification.error('Clipboard data does not match the current table name: ' + this.tableName + ' vs ' + config.tableName);
7378
7477
  return;
@@ -9127,13 +9226,15 @@ class TruDataGrid {
9127
9226
  this.dataGridClipboard.copyRow(this.config.tableName, columnDefs, selectedRows);
9128
9227
  }
9129
9228
  else if (params.column.isPinned() && event.ctrlKey && event.code === 'KeyV') {
9130
- this.dataGridClipboard.pasteRow(this.api, this.config, this.api.getSelectedRows());
9229
+ event.preventDefault();
9230
+ void this.dataGridClipboard.pasteRow(this.api, this.config, this.api.getSelectedRows());
9131
9231
  }
9132
9232
  else if (!params.column.isPinned() && event.ctrlKey && event.code === 'KeyC') {
9133
9233
  this.dataGridClipboard.copyCell(params, this.config.tableName, params.colDef?.field, params.data, params.data.$entity);
9134
9234
  }
9135
9235
  else if (!params.column.isPinned() && event.ctrlKey && event.code === 'KeyV') {
9136
- this.dataGridClipboard.pasteCell(params, params.data, params.data.$entity);
9236
+ event.preventDefault();
9237
+ void this.dataGridClipboard.pasteCell(params, params.data, params.data.$entity);
9137
9238
  }
9138
9239
  };
9139
9240
  onCellMouseOver(e) {