handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
116 lines • 4.94 kB
JavaScript
import { normalizeCoordsIfNeeded, getMostTopStartPosition, getMostBottomEndPosition } from "../utils/utils.mjs";
import { GRID_SCOPE, GRID_GROUP, GRID_TAB_NAVIGATION_GROUP } from "../../shortcutContexts/index.mjs";
/**
* @param {Handsontable} hot The Handsontable instance.
*/
export function focusGridScope(hot) {
var _hot$rootGridElement;
const clampCoordsIfNeeded = normalizeCoordsIfNeeded(hot);
const rowWrapState = {
wrapped: false,
flipped: false
};
let recentlyAddedFocusCoords;
let isSavingCoordsEnabled = true;
let isEmptyDataStateActive = false;
hot.addHook('afterSelection', () => {
if (isSavingCoordsEnabled) {
var _hot$getSelectedRange;
recentlyAddedFocusCoords = (_hot$getSelectedRange = hot.getSelectedRangeActive()) === null || _hot$getSelectedRange === void 0 ? void 0 : _hot$getSelectedRange.highlight;
}
});
hot.addHook('beforeRowWrap', (interruptedByAutoInsertMode, newCoords, isFlipped) => {
rowWrapState.wrapped = true;
rowWrapState.flipped = isFlipped;
});
hot.addHook('beforeEmptyDataStateShow', () => {
isEmptyDataStateActive = true;
});
hot.addHook('beforeEmptyDataStateHide', () => {
isEmptyDataStateActive = false;
});
const context = hot.getShortcutManager().getContext(GRID_SCOPE);
context.addShortcuts([{
keys: [['Tab'], ['Shift', 'Tab']],
preventDefault: false,
stopPropagation: false,
relativeToGroup: GRID_GROUP,
group: GRID_TAB_NAVIGATION_GROUP,
position: 'before',
callback() {
const {
tabNavigation
} = hot.getSettings();
if (hot.getSelectedRangeActive() && !tabNavigation) {
isSavingCoordsEnabled = false;
}
}
}, {
keys: [['Tab'], ['Shift', 'Tab']],
preventDefault: false,
stopPropagation: false,
relativeToGroup: GRID_GROUP,
group: GRID_TAB_NAVIGATION_GROUP,
callback(event) {
const {
tabNavigation,
autoWrapRow
} = hot.getSettings();
isSavingCoordsEnabled = true;
if (!tabNavigation || !hot.selection.isSelected() || autoWrapRow && rowWrapState.wrapped && rowWrapState.flipped || !autoWrapRow && rowWrapState.wrapped) {
if (autoWrapRow && rowWrapState.wrapped && rowWrapState.flipped) {
recentlyAddedFocusCoords = event.shiftKey ? getMostTopStartPosition(hot) : getMostBottomEndPosition(hot);
}
rowWrapState.wrapped = false;
rowWrapState.flipped = false;
}
},
position: 'after'
}]);
const container = (_hot$rootGridElement = hot.rootGridElement) !== null && _hot$rootGridElement !== void 0 ? _hot$rootGridElement : hot.rootElement;
hot.getFocusScopeManager().registerScope('grid', container, {
contains: target => {
if (container === target || container.contains(target)) {
return true;
}
if (target.closest('.htMenu') !== null) {
// TODO: Skip switching focus scope to 'grid' for context and dropdown menus since
// focus management is not implemented for them. Their focus management
// is handled manually.
return false;
}
return hot.rootPortalElement.contains(target);
},
runOnlyIf: () => {
const {
navigableHeaders
} = hot.getSettings();
if ((isEmptyDataStateActive || !navigableHeaders) && hot.countRenderedRows() === 0 && hot.countRenderedCols() === 0 && hot.countRowHeaders() > 0 && hot.countColHeaders() > 0) {
// When the corner is only rendered, and the EmptyDataState is active, deactivate the scope.
return false;
}
return !navigableHeaders && hot.countRenderedRows() > 0 && hot.countRenderedCols() > 0 || navigableHeaders && (hot.countRowHeaders() > 0 || hot.countColHeaders() > 0);
},
onActivate: focusSource => {
if (focusSource === 'tab_from_above') {
var _clampCoordsIfNeeded;
const mostTopStartCoords = (_clampCoordsIfNeeded = clampCoordsIfNeeded(recentlyAddedFocusCoords)) !== null && _clampCoordsIfNeeded !== void 0 ? _clampCoordsIfNeeded : getMostTopStartPosition(hot);
if (mostTopStartCoords) {
const result = hot.runHooks('modifyFocusOnTabNavigation', 'from_above', mostTopStartCoords);
if (result !== false) {
hot.selectCell(mostTopStartCoords.row, mostTopStartCoords.col);
}
}
} else if (focusSource === 'tab_from_below') {
var _clampCoordsIfNeeded2;
const mostBottomEndCoords = (_clampCoordsIfNeeded2 = clampCoordsIfNeeded(recentlyAddedFocusCoords)) !== null && _clampCoordsIfNeeded2 !== void 0 ? _clampCoordsIfNeeded2 : getMostBottomEndPosition(hot);
if (mostBottomEndCoords) {
const result = hot.runHooks('modifyFocusOnTabNavigation', 'from_below', mostBottomEndCoords);
if (result !== false) {
hot.selectCell(mostBottomEndCoords.row, mostBottomEndCoords.col);
}
}
}
}
});
}