Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Hybrid Selection Model and Drag-Replace #1091

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/example-spreadsheet.html
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ <h2>Range Selection</h2>
enableAddRow: true,
enableCellNavigation: true,
asyncEditorLoading: false,
autoEdit: false
autoEdit: true
};

var columns = [
Expand Down
3 changes: 3 additions & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
keyCode,
preClickClassName,
RowSelectionMode,
CellSelectionMode,
SlickEvent,
SlickEventData,
SlickEventHandler,
Expand Down Expand Up @@ -108,9 +109,11 @@ declare global {
},
preClickClassName: typeof preClickClassName,
Range: typeof SlickRange,
DragExtendHandle: typeof SlickDragExtendHandle,
Resizable: typeof Resizable,
RowMoveManager: typeof SlickRowMoveManager,
RowSelectionMode: typeof RowSelectionMode,
CellSelectionMode: typeof CellSelectionMode,
RowSelectionModel: typeof SlickRowSelectionModel,
State: typeof SlickState,
Utils: typeof Utils,
Expand Down
2 changes: 2 additions & 0 deletions src/models/drag.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface DragPosition {
startX: number;
startY: number;
range: DragRange;
matchClassTag: string;
}

export interface DragRange {
Expand Down Expand Up @@ -50,4 +51,5 @@ export interface DragRowMove {
startX: number;
startY: number;
range: DragRange;
matchClassTag: string;
}
14 changes: 14 additions & 0 deletions src/models/interactions.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ import type { DragPosition } from './drag.interface.js';
export interface InteractionBase {
destroy: () => void;
}
export interface ClassDetectElement {
/** tag to be returned on match */
tag: string

/** element id to match */
id?: string;

/** element cssSelector to match */
cssSelector?: string;
}


export interface DraggableOption {
/** container DOM element, defaults to "document" */
containerElement?: HTMLElement | Document;
Expand All @@ -30,6 +42,8 @@ export interface DraggableOption {

/** drag ended callback */
onDragEnd?: (e: DragEvent, dd: DragPosition) => boolean | void;

dragFromClassDetectArr?: Array<ClassDetectElement>
}

export interface MouseWheelOption {
Expand Down
14 changes: 12 additions & 2 deletions src/plugins/slick.cellrangedecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class SlickCellRangeDecorator implements SlickPlugin {
// protected props
protected _options: CellRangeDecoratorOption;
protected _elem?: HTMLDivElement | null;
protected _selectionCss: CSSStyleDeclaration;
protected _defaults = {
selectionCssClass: 'slick-range-decorator',
selectionCss: {
Expand All @@ -36,12 +37,21 @@ export class SlickCellRangeDecorator implements SlickPlugin {

constructor(protected readonly grid: SlickGrid, options?: Partial<CellRangeDecoratorOption>) {
this._options = Utils.extend(true, {}, this._defaults, options);
this._selectionCss = options?.selectionCss || {} as CSSStyleDeclaration;
}

destroy() {
this.hide();
}

getSelectionCss() {
return this._selectionCss;
}

setSelectionCss(cssProps: CSSStyleDeclaration) {
this._selectionCss = cssProps;
}

init() { }

hide() {
Expand All @@ -53,8 +63,8 @@ export class SlickCellRangeDecorator implements SlickPlugin {
if (!this._elem) {
this._elem = document.createElement('div');
this._elem.className = this._options.selectionCssClass;
Object.keys(this._options.selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => {
this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._options.selectionCss[cssStyleKey as CSSStyleDeclarationWritable];
Object.keys(this._selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => {
this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._selectionCss[cssStyleKey as CSSStyleDeclarationWritable];
});
this._elem.style.position = 'absolute';
const canvasNode = this.grid.getActiveCanvasNode();
Expand Down
45 changes: 38 additions & 7 deletions src/plugins/slick.cellrangeselector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';
import { CellSelectionMode as CellSelectionMode_, SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';
import { Draggable as Draggable_ } from '../slick.interactions.js';
import { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';
import type { CellRangeSelectorOption, DragPosition, DragRange, DragRowMove, GridOption, MouseOffsetViewport, OnScrollEventArgs, SlickPlugin } from '../models/index.js';
Expand All @@ -11,14 +11,15 @@ const SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;
const Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;
const SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;
const Utils = IIFE_ONLY ? Slick.Utils : Utils_;
const CellSelectionMode = IIFE_ONLY ? Slick.CellSelectionMode : CellSelectionMode_;

export class SlickCellRangeSelector implements SlickPlugin {
// --
// public API
pluginName = 'CellRangeSelector' as const;
onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>('onBeforeCellRangeSelected');
onCellRangeSelected = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelected');
onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelecting');
onCellRangeSelected = new SlickEvent<{ range: SlickRange_; selectionMode?: string; }>('onCellRangeSelected');
onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; selectionMode?: string; }>('onCellRangeSelecting');

// --
// protected props
Expand All @@ -31,6 +32,7 @@ export class SlickCellRangeSelector implements SlickPlugin {
protected _dragging = false;
protected _handler = new SlickEventHandler();
protected _options: CellRangeSelectorOption;
protected _selectionMode: string = CellSelectionMode.Select;
protected _defaults = {
autoScroll: true,
minIntervalToShowNextCell: 30,
Expand Down Expand Up @@ -96,6 +98,14 @@ export class SlickCellRangeSelector implements SlickPlugin {
return this._decorator;
}

getSelectionMode() {
return this._selectionMode;
}

setSelectionMode(mode: string) {
this._selectionMode = mode;
}

protected handleScroll(_e: SlickEventData, args: OnScrollEventArgs) {
this._scrollTop = args.scrollTop;
this._scrollLeft = args.scrollLeft;
Expand Down Expand Up @@ -138,12 +148,19 @@ export class SlickCellRangeSelector implements SlickPlugin {
}
}

console.log('CellRangeSelector.handleDragInit() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));

// prevent the grid from cancelling drag'n'drop by default
e.stopImmediatePropagation();
e.preventDefault();
}

protected handleDragStart(e: SlickEventData, dd: DragRowMove) {
console.log('CellRangeSelector.handleDragStart() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));
if (!this._activeViewport) {
// const x = 1;
}

const cell = this._grid.getCellFromEvent(e);
if (cell && this.onBeforeCellRangeSelected.notify(cell).getReturnValue() !== false && this._grid.canCellBeSelected(cell.row, cell.cell)) {
this._dragging = true;
Expand All @@ -167,11 +184,18 @@ export class SlickCellRangeSelector implements SlickPlugin {
startY += this._scrollTop;
}

const start = this._grid.getCellFromPoint(startX, startY);
let start: { row: number | undefined, cell: number | undefined; } | null;
this._selectionMode = CellSelectionMode.Select;
if (dd.matchClassTag !== 'dragReplaceHandle') {
start = this._grid.getCellFromPoint(startX, startY);
} else {
start = this._grid.getActiveCell() || { row: undefined, cell: undefined };
this._selectionMode = CellSelectionMode.Replace;
}

dd.range = { start, end: {} };
this._currentlySelectedRange = dd.range;
return this._decorator.show(new SlickRange(start.row, start.cell));
return this._decorator.show(new SlickRange(start.row ?? 0, start.cell ?? 0));
}

protected handleDrag(evt: SlickEventData, dd: DragRowMove) {
Expand All @@ -194,6 +218,11 @@ export class SlickCellRangeSelector implements SlickPlugin {
}

protected getMouseOffsetViewport(e: MouseEvent | TouchEvent, dd: DragRowMove): MouseOffsetViewport {
console.log('Drag.getMouseOffsetViewport() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));
if (!this._activeViewport) {
// const x = 1;
}

const targetEvent: MouseEvent | Touch = (e as TouchEvent)?.touches?.[0] ?? e;
const viewportLeft = this._activeViewport.scrollLeft;
const viewportTop = this._activeViewport.scrollTop;
Expand Down Expand Up @@ -356,7 +385,7 @@ export class SlickCellRangeSelector implements SlickPlugin {
const range = new SlickRange(dd.range.start.row ?? 0, dd.range.start.cell ?? 0, end.row, end.cell);
this._decorator.show(range);
this.onCellRangeSelecting.notify({
range
range,
});
}
}
Expand All @@ -381,8 +410,10 @@ export class SlickCellRangeSelector implements SlickPlugin {
dd.range.start.cell ?? 0,
dd.range.end.row,
dd.range.end.cell
)
),
selectionMode: this._selectionMode
});
console.log('handleDragEnd');
}

getCurrentRange() {
Expand Down
16 changes: 8 additions & 8 deletions src/plugins/slick.cellselectionmodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export class SlickCellSelectionModel {
this._cachedPageRowCount = 0;
}

setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges') {
setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges', selectionMode: string) {
// simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged
if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }

Expand All @@ -113,7 +113,7 @@ export class SlickCellSelectionModel {
if (rangeHasChanged) {
// provide extra "caller" argument through SlickEventData event to avoid breaking the previous pubsub event structure
// that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`
const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges);
const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges);
this.onSelectedRangesChanged.notify(this._ranges, eventData);
}
}
Expand All @@ -123,7 +123,7 @@ export class SlickCellSelectionModel {
}

refreshSelections() {
this.setSelectedRanges(this.getSelectedRanges());
this.setSelectedRanges(this.getSelectedRanges(), undefined, '');
}

protected handleBeforeCellRangeSelected(e: SlickEventData_): boolean | void {
Expand All @@ -133,9 +133,9 @@ export class SlickCellSelectionModel {
}
}

protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }) {
protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }) {
this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);
this.setSelectedRanges([args.range]);
this.setSelectedRanges([args.range], undefined, args.selectionMode);
}

protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {
Expand All @@ -144,10 +144,10 @@ export class SlickCellSelectionModel {
const isRowDefined = Utils.isDefined(args.row);

if (this._options?.selectActiveCell && isRowDefined && isCellDefined) {
this.setSelectedRanges([new SlickRange(args.row, args.cell)]);
this.setSelectedRanges([new SlickRange(args.row, args.cell)], undefined, '');
} else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) {
// clear the previous selection once the cell changes
this.setSelectedRanges([]);
this.setSelectedRanges([], undefined, '');
}
}

Expand Down Expand Up @@ -272,7 +272,7 @@ export class SlickCellSelectionModel {
ranges.push(last);
}

this.setSelectedRanges(ranges);
this.setSelectedRanges(ranges, undefined, '');

e.preventDefault();
e.stopPropagation();
Expand Down
Loading
Loading