@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
148 lines (147 loc) • 6.05 kB
JavaScript
import * as InternalRedux from '../../Redux/ActionsReducers/InternalRedux';
import ArrayExtensions from '../Extensions/ArrayExtensions';
/**
* This service controls the interaction between Notes & Comments
* If there are active Notes or Comments it listens to Cell Selection and Mouse Enter events
*/
export class AnnotationsService {
constructor(api) {
this.api = api;
this.isListeningToEvents = false;
this.adaptable = api.internalApi.getAdaptableInstance();
this.adaptable.api.eventApi.on('AdaptableReady', () => this.checkListenToEvents());
}
destroy() { }
getAdaptableState() {
return this.api.internalApi.getAdaptableState();
}
dispatchAction(action) {
this.api.internalApi.dispatchReduxAction(action);
}
static isSameAddress(a, b) {
if (!a || !b) {
return false;
}
if (a?.ColumnId === b?.ColumnId && a?.PrimaryKeyValue === b?.PrimaryKeyValue) {
return true;
}
// Primary keys retreived from the grid dom are always strings, so we must also consider them strings
if ((typeof a.PrimaryKeyValue === 'number' && typeof b.PrimaryKeyValue === 'string') ||
(typeof b.PrimaryKeyValue === 'string' && typeof a.PrimaryKeyValue === 'number')) {
return (a.PrimaryKeyValue.toString() === b.PrimaryKeyValue.toString() && a.ColumnId === b.ColumnId);
}
return false;
}
checkListenToEvents() {
if (!this.isListeningToEvents && this.shouldListenToEvents()) {
this.setUpEventListeners();
this.isListeningToEvents = true;
}
}
shouldListenToEvents() {
// Listen to events if we either have Notes in state or if Comments have been set up
return (ArrayExtensions.IsNotNullOrEmpty(this.api.noteApi.getAllNotes()) ||
this.api.internalApi.getModuleService().isModuleAvailable('Comment'));
}
setUpEventListeners() {
this.adaptable._on('MouseEnter', (event) => this.handleMouseEnter(event));
this.api.eventApi.on('CellSelectionChanged', (params) => {
const cells = params.selectedCellInfo.gridCells;
if (ArrayExtensions.hasOneItem(cells)) {
const selectedCell = cells[0];
if (selectedCell.column?.columnId && selectedCell.primaryKeyValue) {
const selectedCellAddress = {
PrimaryKeyValue: selectedCell.primaryKeyValue,
ColumnId: selectedCell.column.columnId,
};
this.handleCellSelected(selectedCellAddress);
}
}
});
}
getCellPositionFromEvent(event) {
const target = event.target;
if (!target?.classList?.contains?.('ag-cell')) {
return null;
}
try {
const PrimaryKeyValue = target.parentElement.getAttribute('row-id');
return {
PrimaryKeyValue,
ColumnId: target.getAttribute('col-id'),
};
}
catch (e) {
return null;
}
}
handleMouseEnter(event) {
const editMode = this.getEditMode();
if (editMode) {
// ignore
return;
}
const cellPosition = this.getCellPositionFromEvent(event);
const openCellAddress = this.getOpenCellAddress();
// Hovering over something that is not a cell and there is no open note
if (!cellPosition && openCellAddress) {
// need to close if the new cell does not have a note
this.hidePopup();
return;
}
// call only if cell address is different
if (AnnotationsService.isSameAddress(openCellAddress, cellPosition)) {
return;
}
const cellNote = this.api.noteApi.getNoteForCell(cellPosition);
const cellComments = this.api.commentApi.getCommentThreadForCell(cellPosition);
if (!cellNote && !cellComments) {
return;
}
this.showPopup(cellPosition, false);
}
handleCellSelected(cellAddress) {
// if already opened - do nothing
const openCellAddress = this.getOpenCellAddress();
if (AnnotationsService.isSameAddress(openCellAddress, cellAddress)) {
return;
}
// if open but this has no note, close
const cellNote = this.api.noteApi.getNoteForCell(cellAddress);
const cellComments = this.api.commentApi.getCommentThreadForCell(cellAddress);
const hasNotesOrComments = cellNote || cellComments;
if (openCellAddress && !hasNotesOrComments) {
// hide only if in edit mode
// because if you select a cell and move the mouse fast over another cell
// with a note, you do not want to close that note
// the selection is debounced, so the moment goes over the new cell this event is triggered
if (this.getEditMode()) {
this.hidePopup();
}
return;
}
// if open but the new selection has a note open that one in edit mode (because of click)
if (openCellAddress && hasNotesOrComments) {
this.showPopup(cellAddress, true);
return;
}
}
getOpenCellAddress() {
return InternalRedux.CommentsAndNotesSelector(this.getAdaptableState().Internal);
}
getEditMode() {
return InternalRedux.CommentsAndNotesEditModeSelector(this.getAdaptableState().Internal);
}
hidePopup() {
this.dispatchAction(InternalRedux.CellPopupHide());
}
showPopup(cellAddress, editMode) {
this.dispatchAction(InternalRedux.CellPopupShow(cellAddress, editMode));
}
editFocusedEntity(entity) {
this.dispatchAction(InternalRedux.CellPopupEditFocusedEntity(entity));
}
getNotePopupEditMode() {
return InternalRedux.CommentsAndNotesEditModeSelector(this.getAdaptableState().Internal);
}
}