diff --git a/projects/ngx-grid-core/src/lib/controller/grid-controller.service.ts b/projects/ngx-grid-core/src/lib/controller/grid-controller.service.ts index 4dbe913..e690d15 100644 --- a/projects/ngx-grid-core/src/lib/controller/grid-controller.service.ts +++ b/projects/ngx-grid-core/src/lib/controller/grid-controller.service.ts @@ -21,6 +21,7 @@ import { ColumnOperationFactory } from './column-operations/_column-operation.fa import { GridOperationFactory } from './grid-operations/_grid-operation.factory' import { RowOperationFactory } from './row-operations/_row-operation.factory' import { GridSelectionController } from './selection/grid-selection.controller' +import { MatIconRegistry } from '@angular/material/icon' export const DATA_GRIDS_FOCUSED_TREE: string[] = [] @@ -41,6 +42,9 @@ export class GridControllerService { private _subs : Set = new Set() + public iconClass = this.iconRegistry.getDefaultFontSetClass().filter((fontSetClass) => fontSetClass.includes('material') || fontSetClass.includes('symbols'))[0] + ?? this.iconRegistry.getDefaultFontSetClass()[0]; + constructor( private readonly events : GridEventsService, public readonly prefs : LocalPreferencesService, @@ -48,6 +52,7 @@ export class GridControllerService { public readonly datePipe: DatePipe, public readonly dialogs : MatDialog, public readonly uploads : GridFileUploadService, + private readonly iconRegistry: MatIconRegistry, icons: IconsService, ) { diff --git a/projects/ngx-grid-core/src/lib/controller/selection/strategies/multi-row.selection-strategy.ts b/projects/ngx-grid-core/src/lib/controller/selection/strategies/multi-row.selection-strategy.ts index 654ae7c..a63a0e1 100644 --- a/projects/ngx-grid-core/src/lib/controller/selection/strategies/multi-row.selection-strategy.ts +++ b/projects/ngx-grid-core/src/lib/controller/selection/strategies/multi-row.selection-strategy.ts @@ -1,4 +1,4 @@ -import { fromEvent, merge, Subject } from 'rxjs' +import { fromEvent, merge, Observable, Subject } from 'rxjs' import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators' import { @@ -19,25 +19,24 @@ export class MultiRowSelectionStrategy implements IGridSelectionStrategy { constructor(public readonly controller: ISelectionController) { } public attach(el: HTMLElement): void { - this._keyboardHandling() this.controller.addSubscription(fromEvent(window.document.documentElement, 'mouseup').subscribe(_ => this._mouseReleased.next(true))) this.controller.addSubscription(fromEvent(el, 'mousedown').subscribe(e => this._startSelection(e))) + this._keyboardHandling() } private _startSelection(e: MouseEvent): void { - // button 0 = Left Mouse Button - if (e.button !== 0 || !HasParentOfClass('cell', e.target as HTMLElement)) return - this._gridEvents.CellSelectionStartedEvent.emit(true) - - let state = this.controller.CreateSelectionStateFromMouseEvent.run({ - ctrlKey: !e.shiftKey && typeof this.controller.state?.previousSelection !== 'undefined', - shiftKey: e.shiftKey, - target: e.target - }) - + if (!HasParentOfClass('cell', e.target as HTMLElement)) return + let state = this.controller.CreateSelectionStateFromMouseEvent.run(e) if (!state) return - e.preventDefault() // stops native html element dragging + const lastSelection = this.controller.latestSelection() + // button 2 = Right Mouse Button + if (e.button === 2 && lastSelection?.includes(state.startCellPos) === true) return + + this._gridEvents.CellSelectionStartedEvent.emit(true) + + // button 0 = Left Mouse Button + if (e.button === 0) e.preventDefault() // stops native html element dragging this.controller.state = state @@ -49,7 +48,9 @@ export class MultiRowSelectionStrategy implements IGridSelectionStrategy { this.controller.CalculateNextSelection.run() const focusChanged = this.controller.EmitFocusedCell.run() - if (focusChanged) state.previousSelection = state.currentSelection.clone() + if (focusChanged) { + state.previousSelection = state.currentSelection.clone() + } this._continueSelection() } @@ -59,12 +60,20 @@ export class MultiRowSelectionStrategy implements IGridSelectionStrategy { const state = this.controller.state if (!state) throw new Error('Selection state is undefined') - this._gridEvents.RowMouseEnteredEvent.on() - .pipe(removeNullish(), takeUntil(merge(this._windowFocusChanged, this._mouseReleased))) - .subscribe(rowComponent => { + const event: Observable = state.isRowSelection ? + this._gridEvents.RowMouseEnteredEvent.onWithInitialValue().pipe(removeNullish(), map(row => row.lastCellPosition)) : + this._gridEvents.CellMouseEnteredEvent.onWithInitialValue().pipe(removeNullish(), map(cell => new GridCellCoordinates( cell.rowComponent.rowKey, cell.column.columnKey ))) + + event + .pipe(takeUntil(merge(this._windowFocusChanged, this._mouseReleased))) + .subscribe(endCellPos => { const nextSelection = (state.hasShiftKey && state.previousSelection ? state.previousSelection : state.initialSelection).clone() - this.controller.CalculateNextSelection.run(nextSelection, state.startCellPos, rowComponent.lastCellPosition) + const columns = this.controller.gridController.dataSource.columns + const lastColumnKey = columns[columns.length - 1].columnKey + const firstColumnKey = columns[0].columnKey + this.controller.CalculateNextSelection.run(nextSelection, new GridCellCoordinates(state.startCellPos.rowKey, firstColumnKey), new GridCellCoordinates(endCellPos.rowKey, lastColumnKey)) this._emitSelection(nextSelection) + this.controller.EmitNextSelectionSlice.run() }) .add(() => { const finalSelection = this.controller.GetFinalSelection.run() diff --git a/projects/ngx-grid-core/src/lib/controller/selection/strategies/standard.selection-strategy.ts b/projects/ngx-grid-core/src/lib/controller/selection/strategies/standard.selection-strategy.ts index fc8dee1..ae885eb 100644 --- a/projects/ngx-grid-core/src/lib/controller/selection/strategies/standard.selection-strategy.ts +++ b/projects/ngx-grid-core/src/lib/controller/selection/strategies/standard.selection-strategy.ts @@ -18,7 +18,6 @@ export class StandardSelectionStrategy implements IGridSelectionStrategy { constructor(public readonly controller: ISelectionController) { } public attach(el: HTMLElement): void { - this.controller.addSubscription(fromEvent(window.document.documentElement, 'mouseup').subscribe(_ => this._mouseReleased.next(true))) this.controller.addSubscription(fromEvent(el, 'mousedown').subscribe(e => this._startSelection(e))) this._keyboardHandling() diff --git a/projects/ngx-grid-core/src/lib/events/row/index.ts b/projects/ngx-grid-core/src/lib/events/row/index.ts index 124fa63..fb831c2 100644 --- a/projects/ngx-grid-core/src/lib/events/row/index.ts +++ b/projects/ngx-grid-core/src/lib/events/row/index.ts @@ -5,3 +5,4 @@ export * from "./row-status-changed.event" export * from "./rows-committed.event" export * from "./rows-inserted.event" export * from "./rows-reverted.event" +export * from "./row-double-clicked.event" diff --git a/projects/ngx-grid-core/src/lib/events/row/row-double-clicked.event.ts b/projects/ngx-grid-core/src/lib/events/row/row-double-clicked.event.ts new file mode 100644 index 0000000..300c733 --- /dev/null +++ b/projects/ngx-grid-core/src/lib/events/row/row-double-clicked.event.ts @@ -0,0 +1,8 @@ +import { IGridRowComponent } from '../../typings/interfaces' +import { BaseGridEventAbstract } from '../base-grid-event.abstract' +import { GridEventsService } from '../grid-events.service' + +export class RowDoubleClickedEvent extends BaseGridEventAbstract { + public readonly eventName = 'RowDoubleClickedEvent' + constructor(eventService: GridEventsService) { super(eventService) } +} diff --git a/projects/ngx-grid-core/src/lib/typings/interfaces/icon-cell-value.interface.ts b/projects/ngx-grid-core/src/lib/typings/interfaces/icon-cell-value.interface.ts new file mode 100644 index 0000000..978354e --- /dev/null +++ b/projects/ngx-grid-core/src/lib/typings/interfaces/icon-cell-value.interface.ts @@ -0,0 +1,4 @@ +export interface IIconCellValue { + key: string + size: number +} diff --git a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/icon.cell-type.ts b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/icon.cell-type.ts new file mode 100644 index 0000000..bd0ac48 --- /dev/null +++ b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/icon.cell-type.ts @@ -0,0 +1,49 @@ +import { BehaviorSubject } from 'rxjs' + +import { GridControllerService } from '../../../controller/grid-controller.service' +import { GridOverlayService } from '../../../services/grid-overlay-service.service' +import { ECellMode } from '../../../typings/enums/cell-mode.enum' +import { IGridCellComponent } from '../../../typings/interfaces' +import { BaseCellType } from './abstractions/base-cell-type.abstract' +import { IIconCellValue } from '../../../typings/interfaces/icon-cell-value.interface' + +export class IconCellType extends BaseCellType { + + public mode = new BehaviorSubject(ECellMode.Readonly) + + private _displayNode?: HTMLElement + + constructor( + gridController: GridControllerService, + overlayService: GridOverlayService, + parentCell : IGridCellComponent + ) { super(overlayService, parentCell, gridController) } + + public get displayNode() { return this._displayNode ?? this._generateDisplayNode() } + public get editableNode() { return this.displayNode } + + public override receiveValue(value: IIconCellValue | null = this.value): void { + super.receiveValue(value) + if (!this._displayNode) return; + else + { + this._displayNode.innerText = value?.key ?? '' + this._displayNode.style.fontSize = value?.size + 'px' + } + if (!value) this._displayNode.style.display = 'none' + else this._displayNode.style.display = '' + } + + private _generateDisplayNode(): HTMLElement { + const node = document.createElement('span') + node.classList.add(this.gridController.iconClass) + this._displayNode = node + this.receiveValue() + return this._displayNode + } + + public override measureWidth(): number { + return this.value?.size ?? 0 + } + +} diff --git a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/index.ts b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/index.ts index 14efadf..90ae75b 100644 --- a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/index.ts +++ b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/index.ts @@ -10,6 +10,7 @@ import { NumberCellType as Number } from './number.cell-type' import { RichTextCellType as RichText } from './rich-text.cell-type' import { TextCellType as Text } from './text.cell-type' import { ButtonCellType as Button } from './button.cell-type' +import { IconCellType as Icon } from './icon.cell-type' /* eslint-disable @typescript-eslint/naming-convention */ const CELL_TYPES = { @@ -24,7 +25,8 @@ const CELL_TYPES = { DropdownMultiSelect, File, Color, - Button + Button, + Icon } export default CELL_TYPES diff --git a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-multi-editing/multi-editors-factory.ts b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-multi-editing/multi-editors-factory.ts index 82c904d..bf20ec3 100644 --- a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-multi-editing/multi-editors-factory.ts +++ b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-multi-editing/multi-editors-factory.ts @@ -16,4 +16,5 @@ export const CELL_MULTI_EDITORS: {[key in TCellTypeName]: Array { + const val = this.initialValue + // IIconCellValue + if (typeof val === 'object' && val !== null && !Array.isArray(val) && 'key' in val && 'size' in val && typeof val.size === 'number') { + return this.passed(val) + } + return this.failed() + } +} diff --git a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-parsing/parsers/index.ts b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-parsing/parsers/index.ts index 69d1168..d8b8d32 100644 --- a/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-parsing/parsers/index.ts +++ b/projects/ngx-grid-core/src/lib/ui/cell/cell-types/value-parsing/parsers/index.ts @@ -8,6 +8,7 @@ import { NumberParser as Number } from './number.parser' import { StringParser as String } from './string.parser' import { AnyParser as Any } from './any.parser' import { ButtonParser as Button } from './button.parser' +import { IconParser as Icon } from './icon.parser' export { Array, @@ -19,5 +20,6 @@ export { File, Color, Any, - Button + Button, + Icon } diff --git a/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-foreign-key-dropdown-overlay/multi-select-foreign-key-dropdown-overlay.component.ts b/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-foreign-key-dropdown-overlay/multi-select-foreign-key-dropdown-overlay.component.ts index a866ae8..f02c97b 100644 --- a/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-foreign-key-dropdown-overlay/multi-select-foreign-key-dropdown-overlay.component.ts +++ b/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-foreign-key-dropdown-overlay/multi-select-foreign-key-dropdown-overlay.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inje import { FormControl } from '@angular/forms' import { debounceTime, distinctUntilChanged, filter, map, pairwise, startWith } from 'rxjs/operators' -import { DataGridConfigs } from '../../../data-grid-configs.class' import { GridDataSource } from '../../../grid-data-source' import { GRID_OVERLAY_DATA } from '../../../services/grid-overlay-service.service' import { EForeignKeyDropdownState } from '../../../typings/enums' @@ -27,8 +26,6 @@ export class MultiSelectForeignKeyDropdownOverlayComponent extends BaseOverlayCo public values: TPrimaryKey[] = [] public searchCtrl = new FormControl() - public gridConfig = new DataGridConfigs().withRowMultiSelect().withConfigs({ scrollToPreselected: false }) - public loadingState = this.gridController.gridEvents .ForeignKeyDropdownStateChangedEvent .on() diff --git a/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-grid-selected-list/multi-select-grid-selected-list.component.ts b/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-grid-selected-list/multi-select-grid-selected-list.component.ts index 410142c..c2df1e2 100644 --- a/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-grid-selected-list/multi-select-grid-selected-list.component.ts +++ b/projects/ngx-grid-core/src/lib/ui/grid-overlays/multi-select-grid-selected-list/multi-select-grid-selected-list.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, In import { Subject } from 'rxjs' import { debounceTime, repeat, takeUntil } from 'rxjs/operators' -import { DataGridConfigs } from '../../../data-grid-configs.class' import { GridDataSource } from '../../../grid-data-source' import { GRID_OVERLAY_DATA } from '../../../services/grid-overlay-service.service' import { IGridDataSource, IGridOverlayData, IGridRecordSelectedEvent } from '../../../typings/interfaces' @@ -22,8 +21,6 @@ export class MultiSelectGridSelectedListComponent extends BaseOverlayComponent i public dataSource?: IGridDataSource public values : TPrimaryKey[] = [] public dataSetName = '' - - public gridConfig = new DataGridConfigs().withRowMultiSelect() private _valueUpdated = new Subject() private _updateDataNext = new Subject() diff --git a/projects/ngx-grid-core/src/lib/ui/row/row.component.ts b/projects/ngx-grid-core/src/lib/ui/row/row.component.ts index bffa4da..fc659cf 100644 --- a/projects/ngx-grid-core/src/lib/ui/row/row.component.ts +++ b/projects/ngx-grid-core/src/lib/ui/row/row.component.ts @@ -142,6 +142,9 @@ export class RowComponent extends AutoUnsubscribe implements OnInit, OnChanges, @HostListener('mouseenter') public mouseEntered = () => this.events.factory.RowMouseEnteredEvent.emit(this) + + @HostListener('dblclick') + public dblClick = () => this.events.factory.RowDoubleClickedEvent.emit(this) public indexOf = (columnKey: string) => { return this.columns.findIndex(c => c.columnKey === columnKey) } public cellColumnKey = (i: number) => this.columns[i] diff --git a/projects/ngx-grid-core/src/public-api.ts b/projects/ngx-grid-core/src/public-api.ts index 8f4369e..b827613 100644 --- a/projects/ngx-grid-core/src/public-api.ts +++ b/projects/ngx-grid-core/src/public-api.ts @@ -26,3 +26,4 @@ export * from './lib/services/grid-multi-cell-edit.service' export * from './lib/controller/transform-pipeline/transformer.abstract' export * from './lib/controller/transform-pipeline/transform-pipeline.abstract' export * from './lib/controller/transform-pipeline/generic-transformer' +export * from './lib/controller/selection/strategies'