@revolist/revogrid 4.23.8 → 4.23.9

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.
Files changed (41) hide show
  1. package/dist/cjs/{cell-renderer-Dcz022q7.js → cell-renderer-Dl9kKeKp.js} +1 -1
  2. package/dist/cjs/{column.drag.plugin-CHyc9aCK.js → column.drag.plugin-BRraLvz3.js} +2 -2
  3. package/dist/cjs/{column.service-C1Qvcf5l.js → column.service-BNWNiJW3.js} +62 -23
  4. package/dist/cjs/{header-cell-renderer-vVr4IWNV.js → header-cell-renderer-DyjOxArm.js} +1 -1
  5. package/dist/cjs/index.cjs.js +4 -4
  6. package/dist/cjs/revo-grid.cjs.entry.js +5 -5
  7. package/dist/cjs/revogr-attribution_7.cjs.entry.js +40 -4
  8. package/dist/cjs/revogr-data_4.cjs.entry.js +3 -3
  9. package/dist/collection/components/data/column.service.js +62 -23
  10. package/dist/collection/components/overlay/clipboard.utils.js +26 -0
  11. package/dist/collection/components/overlay/revogr-overlay-selection.js +27 -7
  12. package/dist/collection/components/revoGrid/revo-grid.js +14 -7
  13. package/dist/collection/serve/controller.js +1 -0
  14. package/dist/esm/{cell-renderer-BtN-NGCk.js → cell-renderer-CdF2Jm3B.js} +1 -1
  15. package/dist/esm/{column.drag.plugin-DEKk4rrp.js → column.drag.plugin-V9DDE3mU.js} +2 -2
  16. package/dist/esm/{column.service-CC_SD8W3.js → column.service-C6hByxPy.js} +62 -23
  17. package/dist/esm/{header-cell-renderer-B-LX2sgu.js → header-cell-renderer-BMmXRsd_.js} +1 -1
  18. package/dist/esm/index.js +5 -5
  19. package/dist/esm/revo-grid.entry.js +5 -5
  20. package/dist/esm/revogr-attribution_7.entry.js +40 -4
  21. package/dist/esm/revogr-data_4.entry.js +3 -3
  22. package/dist/revo-grid/{cell-renderer-BtN-NGCk.js → cell-renderer-CdF2Jm3B.js} +1 -1
  23. package/dist/revo-grid/{column.drag.plugin-DEKk4rrp.js → column.drag.plugin-V9DDE3mU.js} +2 -2
  24. package/dist/revo-grid/{column.service-CC_SD8W3.js → column.service-C6hByxPy.js} +62 -23
  25. package/dist/revo-grid/{header-cell-renderer-B-LX2sgu.js → header-cell-renderer-BMmXRsd_.js} +1 -1
  26. package/dist/revo-grid/index.esm.js +5 -5
  27. package/dist/revo-grid/revo-grid.entry.js +5 -5
  28. package/dist/revo-grid/revogr-attribution_7.entry.js +40 -4
  29. package/dist/revo-grid/revogr-data_4.entry.js +3 -3
  30. package/dist/types/components/data/column.service.d.ts +10 -1
  31. package/dist/types/components/overlay/clipboard.utils.d.ts +3 -0
  32. package/dist/types/components/overlay/revogr-overlay-selection.d.ts +4 -3
  33. package/dist/types/components/revoGrid/revo-grid.d.ts +4 -4
  34. package/dist/types/components.d.ts +14 -14
  35. package/dist/types/types/interfaces.d.ts +8 -0
  36. package/hydrate/index.js +103 -28
  37. package/hydrate/index.mjs +103 -28
  38. package/package.json +1 -1
  39. package/readme.md +20 -1
  40. package/standalone/column.service.js +1 -1
  41. package/standalone/revogr-overlay-selection2.js +1 -1
@@ -3,7 +3,7 @@ import ColumnService from '../data/column.service';
3
3
  import { type DSourceState } from "../../store/index";
4
4
  import { EventData } from './selection.utils';
5
5
  import { type Observable } from '../../utils';
6
- import type { SelectionStoreState, DimensionSettingsState, DataType, DimensionRows, ColumnRegular, DimensionCols, DragStartEvent, Cell, MultiDimensionType, Nullable, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, FocusRenderEvent, ApplyFocusEvent, AllDimensionType, Editors, BeforeSaveDataDetails, BeforeEdit, RangeArea, TempRange, ChangedRange, BeforeRangeSaveDataDetails, SaveDataDetails, EditCellStore } from "../../types/index";
6
+ import type { SelectionStoreState, DimensionSettingsState, DataType, DimensionRows, ColumnRegular, DimensionCols, DragStartEvent, Cell, MultiDimensionType, Nullable, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, FocusRenderEvent, ApplyFocusEvent, AllDimensionType, Editors, BeforeSaveDataDetails, BeforeEdit, RangeArea, TempRange, ChangedRange, BeforeRangeSaveDataDetails, SaveDataDetails, EditCellStore, ClipboardConfig } from "../../types/index";
7
7
  /**
8
8
  * Component for overlaying the grid with the selection.
9
9
  */
@@ -23,9 +23,9 @@ export declare class OverlaySelection {
23
23
  canDrag: boolean;
24
24
  /**
25
25
  * Enable revogr-clipboard component (read more in revogr-clipboard component).
26
- * Allows copy/paste.
26
+ * Allows copy/paste. Can be boolean or clipboard config.
27
27
  */
28
- useClipboard: boolean;
28
+ useClipboard: boolean | ClipboardConfig;
29
29
  /** Stores */
30
30
  /** Selection, range, focus. */
31
31
  selectionStore: Observable<SelectionStoreState>;
@@ -256,6 +256,7 @@ export declare class OverlaySelection {
256
256
  private getRegion;
257
257
  private onCopy;
258
258
  private onPaste;
259
+ private getClipboardPasteTargetRange;
259
260
  private focusNext;
260
261
  protected clearCell(): void;
261
262
  private rowDragStart;
@@ -1,5 +1,5 @@
1
1
  import { type VNode, EventEmitter } from '../../stencil-public-runtime';
2
- import type { MultiDimensionType, DimensionRows, DimensionCols, DimensionTypeCol, RowHeaders, ColumnRegular, ColumnGrouping, DataType, RowDefinition, ColumnType, FocusTemplateFunc, PositionItem, ColumnProp, ViewPortScrollEvent, InitialHeaderClick, AllDimensionType, Editors, BeforeSaveDataDetails, BeforeRangeSaveDataDetails, Cell, ChangedRange, RangeArea, AfterEditEvent, Theme, PluginBaseComponent, PluginProviders, FocusAfterRenderEvent, ExtraNodeFuncConfig, RowDragStartDetails, AdditionalData } from "../../types/index";
2
+ import type { MultiDimensionType, DimensionRows, DimensionCols, DimensionTypeCol, RowHeaders, ColumnRegular, ColumnGrouping, DataType, RowDefinition, ColumnType, FocusTemplateFunc, PositionItem, ColumnProp, ViewPortScrollEvent, InitialHeaderClick, AllDimensionType, Editors, BeforeSaveDataDetails, BeforeRangeSaveDataDetails, Cell, ChangedRange, RangeArea, AfterEditEvent, Theme, PluginBaseComponent, PluginProviders, FocusAfterRenderEvent, ExtraNodeFuncConfig, RowDragStartDetails, AdditionalData, ClipboardConfig } from "../../types/index";
3
3
  import ColumnDataProvider from '../../services/column.data.provider';
4
4
  import { DataProvider } from '../../services/data.provider';
5
5
  import { DSourceState } from "../../store/index";
@@ -59,7 +59,7 @@ export declare class RevoGridComponent {
59
59
  rowSize: number;
60
60
  /** Indicates default column size. */
61
61
  colSize: number;
62
- /** When true, user can range selection. */
62
+ /** When true, user can select a cell range. Required for range-based clipboard fill. */
63
63
  range: boolean;
64
64
  /** When true, grid in read only mode. */
65
65
  readonly: boolean;
@@ -71,8 +71,8 @@ export declare class RevoGridComponent {
71
71
  noHorizontalScrollTransfer: boolean;
72
72
  /** When true cell focus appear. */
73
73
  canFocus: boolean;
74
- /** When true enable clipboard. */
75
- useClipboard: boolean;
74
+ /** When true enable clipboard. Can be boolean or clipboard config. */
75
+ useClipboard: boolean | ClipboardConfig;
76
76
  /**
77
77
  * Columns - defines an array of grid columns.
78
78
  * Can be column or grouped column.
@@ -5,7 +5,7 @@
5
5
  * It contains typing information for all components that exist in this project.
6
6
  */
7
7
  import { HTMLStencilElement, JSXBase } from "./stencil-public-runtime";
8
- import { AdditionalData, AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, CellTemplateProp, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, ExtraNodeFuncConfig, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PluginProviders, PositionItem, ProvidersColumns, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowDragStartDetails, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
8
+ import { AdditionalData, AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, CellTemplateProp, ChangedRange, ClipboardConfig, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, ExtraNodeFuncConfig, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PluginProviders, PositionItem, ProvidersColumns, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowDragStartDetails, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
9
9
  import { GridPlugin } from "./plugins/base.plugin";
10
10
  import { AutoSizeColumnConfig } from "./plugins/column.auto-size.plugin";
11
11
  import { ColumnFilterConfig, FilterCaptions, FilterCollectionItem, LogicFunction, MultiFilterItem, ShowData } from "./plugins/filter/filter.types";
@@ -21,7 +21,7 @@ import { ResizeProps } from "./components/header/resizable.directive";
21
21
  import { HeaderRenderProps } from "./components/header/header-renderer";
22
22
  import { HeaderGroupRendererProps } from "./components/header/header-group-renderer";
23
23
  import { EventData } from "./components/overlay/selection.utils";
24
- export { AdditionalData, AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, CellTemplateProp, ChangedRange, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, ExtraNodeFuncConfig, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PluginProviders, PositionItem, ProvidersColumns, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowDragStartDetails, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
24
+ export { AdditionalData, AfterEditEvent, AllDimensionType, ApplyFocusEvent, BeforeCellRenderEvent, BeforeEdit, BeforeRangeSaveDataDetails, BeforeRowRenderEvent, BeforeSaveDataDetails, Cell, CellTemplateProp, ChangedRange, ClipboardConfig, ColumnDataSchemaModel, ColumnGrouping, ColumnProp, ColumnRegular, ColumnType, DataFormat, DataType, DimensionCols, DimensionRows, DimensionSettingsState, DimensionType, DimensionTypeCol, DragStartEvent, EditCell, EditorCtr, Editors, ElementScroll, ExtraNodeFuncConfig, FocusAfterRenderEvent, FocusRenderEvent, FocusTemplateFunc, InitialHeaderClick, MultiDimensionType, Nullable, PluginBaseComponent, PluginProviders, PositionItem, ProvidersColumns, RangeArea, RangeClipboardCopyEventProps, RangeClipboardPasteEvent, RowDefinition, RowDragStartDetails, RowHeaders, SaveDataDetails, SelectionStoreState, TempRange, Theme, ViewportData, ViewPortResizeEvent, ViewPortScrollEvent, ViewportState, ViewSettingSizeProp } from "./types/index";
25
25
  export { GridPlugin } from "./plugins/base.plugin";
26
26
  export { AutoSizeColumnConfig } from "./plugins/column.auto-size.plugin";
27
27
  export { ColumnFilterConfig, FilterCaptions, FilterCollectionItem, LogicFunction, MultiFilterItem, ShowData } from "./plugins/filter/filter.types";
@@ -225,7 +225,7 @@ export namespace Components {
225
225
  */
226
226
  "plugins": GridPlugin[];
227
227
  /**
228
- * When true, user can range selection.
228
+ * When true, user can select a cell range. Required for range-based clipboard fill.
229
229
  * @default false
230
230
  */
231
231
  "range": boolean;
@@ -344,10 +344,10 @@ export namespace Components {
344
344
  */
345
345
  "updateColumns": (cols: ColumnRegular[]) => Promise<void>;
346
346
  /**
347
- * When true enable clipboard.
347
+ * When true enable clipboard. Can be boolean or clipboard config.
348
348
  * @default true
349
349
  */
350
- "useClipboard": boolean;
350
+ "useClipboard": boolean | ClipboardConfig;
351
351
  }
352
352
  interface RevogrAttribution {
353
353
  }
@@ -685,9 +685,9 @@ export namespace Components {
685
685
  */
686
686
  "selectionStore": Observable<SelectionStoreState>;
687
687
  /**
688
- * Enable revogr-clipboard component (read more in revogr-clipboard component). Allows copy/paste.
688
+ * Enable revogr-clipboard component (read more in revogr-clipboard component). Allows copy/paste. Can be boolean or clipboard config.
689
689
  */
690
- "useClipboard": boolean;
690
+ "useClipboard": boolean | ClipboardConfig;
691
691
  }
692
692
  /**
693
693
  * Row headers component
@@ -1687,7 +1687,7 @@ declare namespace LocalJSX {
1687
1687
  */
1688
1688
  "plugins"?: GridPlugin[];
1689
1689
  /**
1690
- * When true, user can range selection.
1690
+ * When true, user can select a cell range. Required for range-based clipboard fill.
1691
1691
  * @default false
1692
1692
  */
1693
1693
  "range"?: boolean;
@@ -1758,10 +1758,10 @@ declare namespace LocalJSX {
1758
1758
  */
1759
1759
  "trimmedRows"?: Record<number, boolean>;
1760
1760
  /**
1761
- * When true enable clipboard.
1761
+ * When true enable clipboard. Can be boolean or clipboard config.
1762
1762
  * @default true
1763
1763
  */
1764
- "useClipboard"?: boolean;
1764
+ "useClipboard"?: boolean | ClipboardConfig;
1765
1765
  }
1766
1766
  interface RevogrAttribution {
1767
1767
  }
@@ -2343,9 +2343,9 @@ declare namespace LocalJSX {
2343
2343
  */
2344
2344
  "selectionStore": Observable<SelectionStoreState>;
2345
2345
  /**
2346
- * Enable revogr-clipboard component (read more in revogr-clipboard component). Allows copy/paste.
2346
+ * Enable revogr-clipboard component (read more in revogr-clipboard component). Allows copy/paste. Can be boolean or clipboard config.
2347
2347
  */
2348
- "useClipboard"?: boolean;
2348
+ "useClipboard"?: boolean | ClipboardConfig;
2349
2349
  }
2350
2350
  /**
2351
2351
  * Row headers component
@@ -2498,7 +2498,7 @@ declare namespace LocalJSX {
2498
2498
  "resize": boolean;
2499
2499
  "noHorizontalScrollTransfer": boolean;
2500
2500
  "canFocus": boolean;
2501
- "useClipboard": boolean;
2501
+ "useClipboard": boolean | ClipboardConfig;
2502
2502
  "applyOnClose": boolean;
2503
2503
  "theme": Theme;
2504
2504
  "rowClass": string;
@@ -2552,7 +2552,7 @@ declare namespace LocalJSX {
2552
2552
  "readonly": boolean;
2553
2553
  "range": boolean;
2554
2554
  "canDrag": boolean;
2555
- "useClipboard": boolean;
2555
+ "useClipboard": boolean | ClipboardConfig;
2556
2556
  "applyChangesOnClose": boolean;
2557
2557
  "additionalData": string;
2558
2558
  "isMobileDevice": boolean;
@@ -788,6 +788,14 @@ export interface RangeClipboardCopyEventProps<T = any> extends AllDimensionType
788
788
  range: RangeArea;
789
789
  mapping: OldNewRangeMapping;
790
790
  }
791
+ /** Clipboard behavior configuration. */
792
+ export interface ClipboardConfig {
793
+ /**
794
+ * When true, pasting a single clipboard cell fills the selected range.
795
+ * Requires range selection to be enabled on the grid.
796
+ */
797
+ rangeFill?: boolean;
798
+ }
791
799
  export interface AdditionalData {
792
800
  /**
793
801
  * Additional data for grid and plugins
package/hydrate/index.js CHANGED
@@ -13048,43 +13048,82 @@ class ColumnService {
13048
13048
  mapping,
13049
13049
  };
13050
13050
  }
13051
- getTransformedDataToApply(start, data) {
13051
+ getTransformedDataToApply({ start, data, targetRange, }) {
13052
13052
  const changed = {};
13053
13053
  const copyRowLength = data.length;
13054
+ if (!copyRowLength) {
13055
+ return {
13056
+ changed,
13057
+ range: null,
13058
+ };
13059
+ }
13054
13060
  const colLength = this.columns.length;
13055
13061
  const rowLength = this.dataStore.get('items').length;
13062
+ const bounds = this.getDataApplyBounds(start, targetRange, copyRowLength, rowLength, colLength);
13063
+ if (!bounds) {
13064
+ return {
13065
+ changed,
13066
+ range: null,
13067
+ };
13068
+ }
13069
+ const { startRow, startCol, endRow } = bounds;
13070
+ let maxCol = startCol - 1;
13071
+ let lastRow = startRow - 1;
13056
13072
  // rows
13057
- let rowIndex = start.y;
13058
- let maxCol = 0;
13059
- for (let i = 0; rowIndex < rowLength && i < copyRowLength; rowIndex++, i++) {
13073
+ for (let rowIndex = startRow, i = 0; rowIndex <= endRow; rowIndex++, i++) {
13060
13074
  // copy original data link
13061
13075
  const copyRow = data[i % copyRowLength];
13062
13076
  const copyColLength = (copyRow === null || copyRow === void 0 ? void 0 : copyRow.length) || 0;
13063
- // columns
13064
- let colIndex = start.x;
13065
- for (let j = 0; colIndex < colLength && j < copyColLength; colIndex++, j++) {
13066
- const p = this.columns[colIndex].prop;
13067
- const currentCol = j % colLength;
13068
- /** if can write */
13069
- if (!this.isReadOnly(rowIndex, colIndex)) {
13070
- /** to show before save */
13071
- if (!changed[rowIndex]) {
13072
- changed[rowIndex] = {};
13073
- }
13074
- changed[rowIndex][p] = copyRow[currentCol];
13075
- }
13077
+ if (!copyColLength) {
13078
+ continue;
13076
13079
  }
13077
- maxCol = Math.max(maxCol, colIndex - 1);
13080
+ maxCol = Math.max(maxCol, this.applyClipboardRow(changed, {
13081
+ bounds,
13082
+ copyColLength,
13083
+ copyRow,
13084
+ rowIndex,
13085
+ start,
13086
+ targetRange,
13087
+ }));
13088
+ lastRow = rowIndex;
13078
13089
  }
13079
- const range = getRange(start, {
13080
- y: rowIndex - 1,
13081
- x: maxCol,
13082
- });
13083
13090
  return {
13084
13091
  changed,
13085
- range,
13092
+ range: this.getAppliedRange(bounds, lastRow, maxCol),
13086
13093
  };
13087
13094
  }
13095
+ getDataApplyBounds(start, targetRange, copyRowLength, rowLength, colLength) {
13096
+ var _a, _b, _c;
13097
+ const startRow = (_a = targetRange === null || targetRange === void 0 ? void 0 : targetRange.y) !== null && _a !== void 0 ? _a : start.y;
13098
+ const startCol = (_b = targetRange === null || targetRange === void 0 ? void 0 : targetRange.x) !== null && _b !== void 0 ? _b : start.x;
13099
+ const endRow = Math.min(rowLength - 1, (_c = targetRange === null || targetRange === void 0 ? void 0 : targetRange.y1) !== null && _c !== void 0 ? _c : start.y + copyRowLength - 1);
13100
+ if (endRow < startRow || startCol >= colLength) {
13101
+ return null;
13102
+ }
13103
+ return { startRow, startCol, endRow, colLength };
13104
+ }
13105
+ applyClipboardRow(changed, { bounds, copyColLength, copyRow, rowIndex, start, targetRange, }) {
13106
+ var _a;
13107
+ const endCol = Math.min(bounds.colLength - 1, (_a = targetRange === null || targetRange === void 0 ? void 0 : targetRange.x1) !== null && _a !== void 0 ? _a : start.x + copyColLength - 1);
13108
+ for (let colIndex = bounds.startCol, j = 0; colIndex <= endCol; colIndex++, j++) {
13109
+ if (this.isReadOnly(rowIndex, colIndex)) {
13110
+ continue;
13111
+ }
13112
+ const prop = this.columns[colIndex].prop;
13113
+ changed[rowIndex] = changed[rowIndex] || {};
13114
+ changed[rowIndex][prop] = copyRow[j % copyColLength];
13115
+ }
13116
+ return endCol;
13117
+ }
13118
+ getAppliedRange({ startRow, startCol }, lastRow, maxCol) {
13119
+ if (lastRow < startRow || maxCol < startCol) {
13120
+ return null;
13121
+ }
13122
+ return getRange({ x: startCol, y: startRow }, {
13123
+ y: lastRow,
13124
+ x: maxCol,
13125
+ });
13126
+ }
13088
13127
  getRangeStaticData(d, value) {
13089
13128
  const changed = {};
13090
13129
  // rows
@@ -13751,6 +13790,30 @@ class AutoFillService {
13751
13790
  }
13752
13791
  }
13753
13792
 
13793
+ function getRangeFillClipboardData(data, useClipboard) {
13794
+ var _a;
13795
+ if (!isClipboardRangeFillEnabled(useClipboard)) {
13796
+ return null;
13797
+ }
13798
+ const normalized = trimTrailingEmptyClipboardRows(data);
13799
+ return normalized.length === 1 && ((_a = normalized[0]) === null || _a === void 0 ? void 0 : _a.length) === 1
13800
+ ? normalized
13801
+ : null;
13802
+ }
13803
+ function isClipboardRangeFillEnabled(useClipboard) {
13804
+ return (typeof useClipboard === 'object' && useClipboard.rangeFill === true);
13805
+ }
13806
+ function trimTrailingEmptyClipboardRows(data) {
13807
+ const rows = [...data];
13808
+ while (rows.length > 1 && isEmptyClipboardRow(rows[rows.length - 1])) {
13809
+ rows.pop();
13810
+ }
13811
+ return rows;
13812
+ }
13813
+ function isEmptyClipboardRow(row) {
13814
+ return !row || row.every(cell => cell === '');
13815
+ }
13816
+
13754
13817
  const revogrOverlayStyleCss = () => `revogr-overlay-selection{display:block;position:relative;width:100%}revogr-overlay-selection .autofill-handle{position:absolute;width:14px;height:14px;margin-left:-13px;margin-top:-13px;z-index:10;cursor:crosshair}revogr-overlay-selection .autofill-handle::before{content:"";position:absolute;right:0;bottom:0;width:10px;height:10px;background:#0d63e8;border:1px solid white;box-sizing:border-box}revogr-overlay-selection.mobile .autofill-handle{position:absolute;width:30px;height:30px;margin-left:-29px;margin-top:-29px;z-index:10;cursor:crosshair}revogr-overlay-selection.mobile .autofill-handle::before{content:"";position:absolute;right:0;bottom:0;width:12px;height:12px;background:#0d63e8;border:1px solid white;box-sizing:border-box}revogr-overlay-selection .selection-border-range{position:absolute;pointer-events:none;z-index:9;box-shadow:-1px 0 0 #0d63e8 inset, 1px 0 0 #0d63e8 inset, 0 -1px 0 #0d63e8 inset, 0 1px 0 #0d63e8 inset}revogr-overlay-selection .selection-border-range .range-handlers{height:100%;background-color:transparent;width:75%;max-width:50px;min-width:20px;left:50%;transform:translateX(-50%);position:absolute}revogr-overlay-selection .selection-border-range .range-handlers>span{pointer-events:auto;height:20px;width:20px;position:absolute;left:50%;transform:translateX(-50%)}revogr-overlay-selection .selection-border-range .range-handlers>span:before,revogr-overlay-selection .selection-border-range .range-handlers>span:after{position:absolute;border-radius:5px;width:15px;height:5px;left:50%;transform:translateX(-50%);background-color:rgba(0, 0, 0, 0.2)}revogr-overlay-selection .selection-border-range .range-handlers>span:first-child{top:-7px}revogr-overlay-selection .selection-border-range .range-handlers>span:first-child:before{content:"";top:0}revogr-overlay-selection .selection-border-range .range-handlers>span:last-child{bottom:-7px}revogr-overlay-selection .selection-border-range .range-handlers>span:last-child:after{content:"";bottom:0}revogr-overlay-selection revogr-edit{z-index:10}`;
13755
13818
 
13756
13819
  /**
@@ -14001,9 +14064,9 @@ class OverlaySelection {
14001
14064
  nodes.push(hAsync("revogr-order-editor", { ref: e => (this.orderEditor = e), dataStore: this.dataStore, dimensionRow: this.dimensionRow, dimensionCol: this.dimensionCol, parent: this.element, rowType: this.types.rowType, onRowdragstartinit: e => this.rowDragStart(e) }));
14002
14065
  }
14003
14066
  }
14004
- return (hAsync(Host, { key: 'd936e8452e84c7a25ecd6502e929f1a5af69467f', class: { mobile: this.isMobileDevice }, onDblClick: (e) => this.onElementDblClick(e), onMouseDown: (e) => this.onElementMouseDown(e), onTouchStart: (e) => this.onElementMouseDown(e, true), onCloseedit: (e) => this.closeEdit(e),
14067
+ return (hAsync(Host, { key: 'ff303c39d59e4ef217421fa11b9a80de07311b07', class: { mobile: this.isMobileDevice }, onDblClick: (e) => this.onElementDblClick(e), onMouseDown: (e) => this.onElementMouseDown(e), onTouchStart: (e) => this.onElementMouseDown(e, true), onCloseedit: (e) => this.closeEdit(e),
14005
14068
  // it's done to be able to throw events from different levels, not just from editor
14006
- onCelledit: (e) => this.onEditCell(e) }, nodes, hAsync("slot", { key: 'cd3525d404aa44fd8d06e7fc459777acb8a9d585', name: "data" })));
14069
+ onCelledit: (e) => this.onEditCell(e) }, nodes, hAsync("slot", { key: '3cbe4c3ad7d447f779e9e20f73eec2e3107275e0', name: "data" })));
14007
14070
  }
14008
14071
  /**
14009
14072
  * Executes the focus operation on the specified range of cells.
@@ -14168,13 +14231,25 @@ class OverlaySelection {
14168
14231
  if (!focus || isEditing) {
14169
14232
  return;
14170
14233
  }
14171
- let { changed, range } = this.columnService.getTransformedDataToApply(focus, data);
14234
+ const rangeFillData = getRangeFillClipboardData(data, this.useClipboard);
14235
+ const targetRange = rangeFillData
14236
+ ? this.getClipboardPasteTargetRange()
14237
+ : null;
14238
+ let { changed, range } = this.columnService.getTransformedDataToApply({
14239
+ start: focus,
14240
+ data: rangeFillData || data,
14241
+ targetRange,
14242
+ });
14172
14243
  const { defaultPrevented: canPaste } = this.rangeClipboardPaste.emit(Object.assign({ data: changed, models: collectModelsOfRange(changed, this.dataStore), range }, this.types));
14173
14244
  if (canPaste) {
14174
14245
  return;
14175
14246
  }
14176
14247
  (_a = this.autoFillService) === null || _a === void 0 ? void 0 : _a.onRangeApply(changed, range, range);
14177
14248
  }
14249
+ getClipboardPasteTargetRange() {
14250
+ const range = this.selectionStore.get('range');
14251
+ return range && !isRangeSingleCell(range) ? range : null;
14252
+ }
14178
14253
  async focusNext() {
14179
14254
  var _a;
14180
14255
  const canFocus = await ((_a = this.keyboardService) === null || _a === void 0 ? void 0 : _a.keyChangeSelection(new KeyboardEvent('keydown', {
@@ -19013,7 +19088,7 @@ class RevoGridComponent {
19013
19088
  this.rowSize = 0;
19014
19089
  /** Indicates default column size. */
19015
19090
  this.colSize = 100;
19016
- /** When true, user can range selection. */
19091
+ /** When true, user can select a cell range. Required for range-based clipboard fill. */
19017
19092
  this.range = false;
19018
19093
  /** When true, grid in read only mode. */
19019
19094
  this.readonly = false;
@@ -19025,7 +19100,7 @@ class RevoGridComponent {
19025
19100
  this.noHorizontalScrollTransfer = false;
19026
19101
  /** When true cell focus appear. */
19027
19102
  this.canFocus = true;
19028
- /** When true enable clipboard. */
19103
+ /** When true enable clipboard. Can be boolean or clipboard config. */
19029
19104
  this.useClipboard = true;
19030
19105
  /**
19031
19106
  * Columns - defines an array of grid columns.
package/hydrate/index.mjs CHANGED
@@ -13046,43 +13046,82 @@ class ColumnService {
13046
13046
  mapping,
13047
13047
  };
13048
13048
  }
13049
- getTransformedDataToApply(start, data) {
13049
+ getTransformedDataToApply({ start, data, targetRange, }) {
13050
13050
  const changed = {};
13051
13051
  const copyRowLength = data.length;
13052
+ if (!copyRowLength) {
13053
+ return {
13054
+ changed,
13055
+ range: null,
13056
+ };
13057
+ }
13052
13058
  const colLength = this.columns.length;
13053
13059
  const rowLength = this.dataStore.get('items').length;
13060
+ const bounds = this.getDataApplyBounds(start, targetRange, copyRowLength, rowLength, colLength);
13061
+ if (!bounds) {
13062
+ return {
13063
+ changed,
13064
+ range: null,
13065
+ };
13066
+ }
13067
+ const { startRow, startCol, endRow } = bounds;
13068
+ let maxCol = startCol - 1;
13069
+ let lastRow = startRow - 1;
13054
13070
  // rows
13055
- let rowIndex = start.y;
13056
- let maxCol = 0;
13057
- for (let i = 0; rowIndex < rowLength && i < copyRowLength; rowIndex++, i++) {
13071
+ for (let rowIndex = startRow, i = 0; rowIndex <= endRow; rowIndex++, i++) {
13058
13072
  // copy original data link
13059
13073
  const copyRow = data[i % copyRowLength];
13060
13074
  const copyColLength = (copyRow === null || copyRow === void 0 ? void 0 : copyRow.length) || 0;
13061
- // columns
13062
- let colIndex = start.x;
13063
- for (let j = 0; colIndex < colLength && j < copyColLength; colIndex++, j++) {
13064
- const p = this.columns[colIndex].prop;
13065
- const currentCol = j % colLength;
13066
- /** if can write */
13067
- if (!this.isReadOnly(rowIndex, colIndex)) {
13068
- /** to show before save */
13069
- if (!changed[rowIndex]) {
13070
- changed[rowIndex] = {};
13071
- }
13072
- changed[rowIndex][p] = copyRow[currentCol];
13073
- }
13075
+ if (!copyColLength) {
13076
+ continue;
13074
13077
  }
13075
- maxCol = Math.max(maxCol, colIndex - 1);
13078
+ maxCol = Math.max(maxCol, this.applyClipboardRow(changed, {
13079
+ bounds,
13080
+ copyColLength,
13081
+ copyRow,
13082
+ rowIndex,
13083
+ start,
13084
+ targetRange,
13085
+ }));
13086
+ lastRow = rowIndex;
13076
13087
  }
13077
- const range = getRange(start, {
13078
- y: rowIndex - 1,
13079
- x: maxCol,
13080
- });
13081
13088
  return {
13082
13089
  changed,
13083
- range,
13090
+ range: this.getAppliedRange(bounds, lastRow, maxCol),
13084
13091
  };
13085
13092
  }
13093
+ getDataApplyBounds(start, targetRange, copyRowLength, rowLength, colLength) {
13094
+ var _a, _b, _c;
13095
+ const startRow = (_a = targetRange === null || targetRange === void 0 ? void 0 : targetRange.y) !== null && _a !== void 0 ? _a : start.y;
13096
+ const startCol = (_b = targetRange === null || targetRange === void 0 ? void 0 : targetRange.x) !== null && _b !== void 0 ? _b : start.x;
13097
+ const endRow = Math.min(rowLength - 1, (_c = targetRange === null || targetRange === void 0 ? void 0 : targetRange.y1) !== null && _c !== void 0 ? _c : start.y + copyRowLength - 1);
13098
+ if (endRow < startRow || startCol >= colLength) {
13099
+ return null;
13100
+ }
13101
+ return { startRow, startCol, endRow, colLength };
13102
+ }
13103
+ applyClipboardRow(changed, { bounds, copyColLength, copyRow, rowIndex, start, targetRange, }) {
13104
+ var _a;
13105
+ const endCol = Math.min(bounds.colLength - 1, (_a = targetRange === null || targetRange === void 0 ? void 0 : targetRange.x1) !== null && _a !== void 0 ? _a : start.x + copyColLength - 1);
13106
+ for (let colIndex = bounds.startCol, j = 0; colIndex <= endCol; colIndex++, j++) {
13107
+ if (this.isReadOnly(rowIndex, colIndex)) {
13108
+ continue;
13109
+ }
13110
+ const prop = this.columns[colIndex].prop;
13111
+ changed[rowIndex] = changed[rowIndex] || {};
13112
+ changed[rowIndex][prop] = copyRow[j % copyColLength];
13113
+ }
13114
+ return endCol;
13115
+ }
13116
+ getAppliedRange({ startRow, startCol }, lastRow, maxCol) {
13117
+ if (lastRow < startRow || maxCol < startCol) {
13118
+ return null;
13119
+ }
13120
+ return getRange({ x: startCol, y: startRow }, {
13121
+ y: lastRow,
13122
+ x: maxCol,
13123
+ });
13124
+ }
13086
13125
  getRangeStaticData(d, value) {
13087
13126
  const changed = {};
13088
13127
  // rows
@@ -13749,6 +13788,30 @@ class AutoFillService {
13749
13788
  }
13750
13789
  }
13751
13790
 
13791
+ function getRangeFillClipboardData(data, useClipboard) {
13792
+ var _a;
13793
+ if (!isClipboardRangeFillEnabled(useClipboard)) {
13794
+ return null;
13795
+ }
13796
+ const normalized = trimTrailingEmptyClipboardRows(data);
13797
+ return normalized.length === 1 && ((_a = normalized[0]) === null || _a === void 0 ? void 0 : _a.length) === 1
13798
+ ? normalized
13799
+ : null;
13800
+ }
13801
+ function isClipboardRangeFillEnabled(useClipboard) {
13802
+ return (typeof useClipboard === 'object' && useClipboard.rangeFill === true);
13803
+ }
13804
+ function trimTrailingEmptyClipboardRows(data) {
13805
+ const rows = [...data];
13806
+ while (rows.length > 1 && isEmptyClipboardRow(rows[rows.length - 1])) {
13807
+ rows.pop();
13808
+ }
13809
+ return rows;
13810
+ }
13811
+ function isEmptyClipboardRow(row) {
13812
+ return !row || row.every(cell => cell === '');
13813
+ }
13814
+
13752
13815
  const revogrOverlayStyleCss = () => `revogr-overlay-selection{display:block;position:relative;width:100%}revogr-overlay-selection .autofill-handle{position:absolute;width:14px;height:14px;margin-left:-13px;margin-top:-13px;z-index:10;cursor:crosshair}revogr-overlay-selection .autofill-handle::before{content:"";position:absolute;right:0;bottom:0;width:10px;height:10px;background:#0d63e8;border:1px solid white;box-sizing:border-box}revogr-overlay-selection.mobile .autofill-handle{position:absolute;width:30px;height:30px;margin-left:-29px;margin-top:-29px;z-index:10;cursor:crosshair}revogr-overlay-selection.mobile .autofill-handle::before{content:"";position:absolute;right:0;bottom:0;width:12px;height:12px;background:#0d63e8;border:1px solid white;box-sizing:border-box}revogr-overlay-selection .selection-border-range{position:absolute;pointer-events:none;z-index:9;box-shadow:-1px 0 0 #0d63e8 inset, 1px 0 0 #0d63e8 inset, 0 -1px 0 #0d63e8 inset, 0 1px 0 #0d63e8 inset}revogr-overlay-selection .selection-border-range .range-handlers{height:100%;background-color:transparent;width:75%;max-width:50px;min-width:20px;left:50%;transform:translateX(-50%);position:absolute}revogr-overlay-selection .selection-border-range .range-handlers>span{pointer-events:auto;height:20px;width:20px;position:absolute;left:50%;transform:translateX(-50%)}revogr-overlay-selection .selection-border-range .range-handlers>span:before,revogr-overlay-selection .selection-border-range .range-handlers>span:after{position:absolute;border-radius:5px;width:15px;height:5px;left:50%;transform:translateX(-50%);background-color:rgba(0, 0, 0, 0.2)}revogr-overlay-selection .selection-border-range .range-handlers>span:first-child{top:-7px}revogr-overlay-selection .selection-border-range .range-handlers>span:first-child:before{content:"";top:0}revogr-overlay-selection .selection-border-range .range-handlers>span:last-child{bottom:-7px}revogr-overlay-selection .selection-border-range .range-handlers>span:last-child:after{content:"";bottom:0}revogr-overlay-selection revogr-edit{z-index:10}`;
13753
13816
 
13754
13817
  /**
@@ -13999,9 +14062,9 @@ class OverlaySelection {
13999
14062
  nodes.push(hAsync("revogr-order-editor", { ref: e => (this.orderEditor = e), dataStore: this.dataStore, dimensionRow: this.dimensionRow, dimensionCol: this.dimensionCol, parent: this.element, rowType: this.types.rowType, onRowdragstartinit: e => this.rowDragStart(e) }));
14000
14063
  }
14001
14064
  }
14002
- return (hAsync(Host, { key: 'd936e8452e84c7a25ecd6502e929f1a5af69467f', class: { mobile: this.isMobileDevice }, onDblClick: (e) => this.onElementDblClick(e), onMouseDown: (e) => this.onElementMouseDown(e), onTouchStart: (e) => this.onElementMouseDown(e, true), onCloseedit: (e) => this.closeEdit(e),
14065
+ return (hAsync(Host, { key: 'ff303c39d59e4ef217421fa11b9a80de07311b07', class: { mobile: this.isMobileDevice }, onDblClick: (e) => this.onElementDblClick(e), onMouseDown: (e) => this.onElementMouseDown(e), onTouchStart: (e) => this.onElementMouseDown(e, true), onCloseedit: (e) => this.closeEdit(e),
14003
14066
  // it's done to be able to throw events from different levels, not just from editor
14004
- onCelledit: (e) => this.onEditCell(e) }, nodes, hAsync("slot", { key: 'cd3525d404aa44fd8d06e7fc459777acb8a9d585', name: "data" })));
14067
+ onCelledit: (e) => this.onEditCell(e) }, nodes, hAsync("slot", { key: '3cbe4c3ad7d447f779e9e20f73eec2e3107275e0', name: "data" })));
14005
14068
  }
14006
14069
  /**
14007
14070
  * Executes the focus operation on the specified range of cells.
@@ -14166,13 +14229,25 @@ class OverlaySelection {
14166
14229
  if (!focus || isEditing) {
14167
14230
  return;
14168
14231
  }
14169
- let { changed, range } = this.columnService.getTransformedDataToApply(focus, data);
14232
+ const rangeFillData = getRangeFillClipboardData(data, this.useClipboard);
14233
+ const targetRange = rangeFillData
14234
+ ? this.getClipboardPasteTargetRange()
14235
+ : null;
14236
+ let { changed, range } = this.columnService.getTransformedDataToApply({
14237
+ start: focus,
14238
+ data: rangeFillData || data,
14239
+ targetRange,
14240
+ });
14170
14241
  const { defaultPrevented: canPaste } = this.rangeClipboardPaste.emit(Object.assign({ data: changed, models: collectModelsOfRange(changed, this.dataStore), range }, this.types));
14171
14242
  if (canPaste) {
14172
14243
  return;
14173
14244
  }
14174
14245
  (_a = this.autoFillService) === null || _a === void 0 ? void 0 : _a.onRangeApply(changed, range, range);
14175
14246
  }
14247
+ getClipboardPasteTargetRange() {
14248
+ const range = this.selectionStore.get('range');
14249
+ return range && !isRangeSingleCell(range) ? range : null;
14250
+ }
14176
14251
  async focusNext() {
14177
14252
  var _a;
14178
14253
  const canFocus = await ((_a = this.keyboardService) === null || _a === void 0 ? void 0 : _a.keyChangeSelection(new KeyboardEvent('keydown', {
@@ -19011,7 +19086,7 @@ class RevoGridComponent {
19011
19086
  this.rowSize = 0;
19012
19087
  /** Indicates default column size. */
19013
19088
  this.colSize = 100;
19014
- /** When true, user can range selection. */
19089
+ /** When true, user can select a cell range. Required for range-based clipboard fill. */
19015
19090
  this.range = false;
19016
19091
  /** When true, grid in read only mode. */
19017
19092
  this.readonly = false;
@@ -19023,7 +19098,7 @@ class RevoGridComponent {
19023
19098
  this.noHorizontalScrollTransfer = false;
19024
19099
  /** When true cell focus appear. */
19025
19100
  this.canFocus = true;
19026
- /** When true enable clipboard. */
19101
+ /** When true enable clipboard. Can be boolean or clipboard config. */
19027
19102
  this.useClipboard = true;
19028
19103
  /**
19029
19104
  * Columns - defines an array of grid columns.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revolist/revogrid",
3
- "version": "4.23.8",
3
+ "version": "4.23.9",
4
4
  "type": "module",
5
5
  "description": "Virtual reactive data grid spreadsheet component - RevoGrid.",
6
6
  "license": "MIT",
package/readme.md CHANGED
@@ -76,7 +76,10 @@ Used by some of the largest companies in Europe and the United States.
76
76
  - Header filtering.
77
77
  - Custom filters to extend system filters with your own set.
78
78
 
79
- - **[Export](https://rv-grid.com/guide/export.plugin)**: Export data to file.
79
+ - **[Export](https://rv-grid.com/guide/export.plugin)**:
80
+ - **[CSV](https://rv-grid.com/guide/export.plugin)**: Built-in file export for core RevoGrid data workflows.
81
+ - **[PDF](https://rv-grid.com/guide/pdf-export)**: Browser-side PDF export with the lightweight [`@revolist/revogrid-pdf-export`](https://www.npmjs.com/package/@revolist/revogrid-pdf-export) plugin.
82
+ - **[Excel (Pro)](https://rv-grid.com/guide/data-grid-export-excel)**: Workbook export for RevoGrid Pro with layout, styles, frozen panes, merged cells, and formulas.
80
83
 
81
84
  - **Custom Sizes**: Define custom sizes for [columns](https://rv-grid.com/guide/column/#Column-Size) and [rows](https://rv-grid.com/guide/row/height). Automatic sizing based on content.
82
85
 
@@ -428,6 +431,22 @@ npm run test:e2e
428
431
 
429
432
  Test files live in `e2e/` and share helpers from `e2e/helpers.ts`:
430
433
 
434
+ ### Local startup troubleshooting
435
+
436
+ For targeted local work, confirm a new or changed test is discoverable before starting the dev server:
437
+
438
+ ```bash
439
+ ./node_modules/.bin/playwright test e2e/pinning.spec.ts --grep "test name" --list
440
+ ```
441
+
442
+ Then run a non-watch Stencil build to catch compile errors without invoking the Playwright web-server lifecycle:
443
+
444
+ ```bash
445
+ ./node_modules/.bin/stencil build --dev --serve --no-open
446
+ ```
447
+
448
+ If Playwright fails before any tests run with a Stencil dev-server startup error such as `ERR_SOCKET_BAD_PORT` and port `65536`, treat it as an environment/startup issue rather than an e2e assertion failure. Check `node -v` and whether another local server is already using `localhost:3333`, then retry only after changing that environment state.
449
+
431
450
 
432
451
  ## Contributing
433
452