handsontable 16.1.1 → 16.2.0-next-90d1117-20251117
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.
- package/3rdparty/walkontable/src/overlays.js +1 -1
- package/3rdparty/walkontable/src/overlays.mjs +1 -1
- package/3rdparty/walkontable/src/selection/border/border.js +19 -3
- package/3rdparty/walkontable/src/selection/border/border.mjs +19 -3
- package/CHANGELOG.md +39 -0
- package/base.js +2 -2
- package/base.mjs +2 -2
- package/core/coordsMapper/index.js +11 -0
- package/core/coordsMapper/index.mjs +1 -0
- package/core/hooks/bucket.js +7 -1
- package/core/hooks/bucket.mjs +7 -1
- package/core/hooks/constants.js +54 -0
- package/core/hooks/constants.mjs +54 -0
- package/core/hooks/index.d.ts +6 -0
- package/core/index.js +10 -4
- package/core/index.mjs +2 -1
- package/core.d.ts +3 -2
- package/core.js +67 -26
- package/core.mjs +56 -15
- package/dataMap/metaManager/metaSchema.js +197 -20
- package/dataMap/metaManager/metaSchema.mjs +197 -20
- package/dist/handsontable.css +175 -3
- package/dist/handsontable.full.css +175 -3
- package/dist/handsontable.full.js +10864 -8426
- package/dist/handsontable.full.min.css +5 -4
- package/dist/handsontable.full.min.js +194 -193
- package/dist/handsontable.js +7126 -4827
- package/dist/handsontable.min.css +4 -4
- package/dist/handsontable.min.js +43 -42
- package/dist/languages/all.js +168 -21
- package/dist/languages/all.min.js +1 -1
- package/dist/languages/ar-AR.js +8 -1
- package/dist/languages/ar-AR.min.js +1 -1
- package/dist/languages/cs-CZ.js +8 -1
- package/dist/languages/cs-CZ.min.js +1 -1
- package/dist/languages/de-CH.js +8 -1
- package/dist/languages/de-CH.min.js +1 -1
- package/dist/languages/de-DE.js +8 -1
- package/dist/languages/de-DE.min.js +1 -1
- package/dist/languages/en-US.js +8 -1
- package/dist/languages/en-US.min.js +1 -1
- package/dist/languages/es-MX.js +8 -1
- package/dist/languages/es-MX.min.js +1 -1
- package/dist/languages/fa-IR.js +8 -1
- package/dist/languages/fa-IR.min.js +1 -1
- package/dist/languages/fr-FR.js +8 -1
- package/dist/languages/fr-FR.min.js +1 -1
- package/dist/languages/hr-HR.js +8 -1
- package/dist/languages/hr-HR.min.js +1 -1
- package/dist/languages/it-IT.js +8 -1
- package/dist/languages/it-IT.min.js +1 -1
- package/dist/languages/ja-JP.js +8 -1
- package/dist/languages/ja-JP.min.js +1 -1
- package/dist/languages/ko-KR.js +8 -1
- package/dist/languages/ko-KR.min.js +1 -1
- package/dist/languages/lv-LV.js +8 -1
- package/dist/languages/lv-LV.min.js +1 -1
- package/dist/languages/nb-NO.js +8 -1
- package/dist/languages/nb-NO.min.js +1 -1
- package/dist/languages/nl-NL.js +8 -1
- package/dist/languages/nl-NL.min.js +1 -1
- package/dist/languages/pl-PL.js +8 -1
- package/dist/languages/pl-PL.min.js +1 -1
- package/dist/languages/pt-BR.js +8 -1
- package/dist/languages/pt-BR.min.js +1 -1
- package/dist/languages/ru-RU.js +8 -1
- package/dist/languages/ru-RU.min.js +1 -1
- package/dist/languages/sr-SP.js +8 -1
- package/dist/languages/sr-SP.min.js +1 -1
- package/dist/languages/zh-CN.js +8 -1
- package/dist/languages/zh-CN.min.js +1 -1
- package/dist/languages/zh-TW.js +8 -1
- package/dist/languages/zh-TW.min.js +1 -1
- package/editors/autocompleteEditor/autocompleteEditor.js +21 -1
- package/editors/autocompleteEditor/autocompleteEditor.mjs +22 -2
- package/focusManager/constants.js +25 -0
- package/focusManager/constants.mjs +22 -0
- package/focusManager/eventListener.js +107 -0
- package/focusManager/eventListener.mjs +103 -0
- package/{focusManager.d.ts → focusManager/grid.d.ts} +1 -1
- package/{focusManager.js → focusManager/grid.js} +48 -17
- package/{focusManager.mjs → focusManager/grid.mjs} +47 -16
- package/focusManager/index.d.ts +2 -0
- package/focusManager/index.js +20 -0
- package/focusManager/index.mjs +20 -0
- package/focusManager/scope.js +133 -0
- package/focusManager/scope.mjs +129 -0
- package/focusManager/scopeManager.d.ts +19 -0
- package/focusManager/scopeManager.js +268 -0
- package/focusManager/scopeManager.mjs +263 -0
- package/focusManager/scopes/grid.js +120 -0
- package/focusManager/scopes/grid.mjs +116 -0
- package/focusManager/scopes/index.js +13 -0
- package/focusManager/scopes/index.mjs +9 -0
- package/{utils → focusManager/utils}/focusDetector.js +21 -31
- package/{utils → focusManager/utils}/focusDetector.mjs +21 -31
- package/focusManager/utils/utils.js +95 -0
- package/focusManager/utils/utils.mjs +89 -0
- package/helpers/dom/element.js +1 -1
- package/helpers/dom/element.mjs +2 -2
- package/helpers/dom/event.js +1 -1
- package/helpers/dom/event.mjs +1 -1
- package/helpers/mixed.js +2 -65
- package/helpers/mixed.mjs +2 -63
- package/i18n/constants.js +10 -1
- package/i18n/constants.mjs +10 -1
- package/i18n/languages/ar-AR.js +8 -1
- package/i18n/languages/ar-AR.mjs +8 -1
- package/i18n/languages/cs-CZ.js +8 -1
- package/i18n/languages/cs-CZ.mjs +8 -1
- package/i18n/languages/de-CH.js +8 -1
- package/i18n/languages/de-CH.mjs +8 -1
- package/i18n/languages/de-DE.js +8 -1
- package/i18n/languages/de-DE.mjs +8 -1
- package/i18n/languages/en-US.js +8 -1
- package/i18n/languages/en-US.mjs +8 -1
- package/i18n/languages/es-MX.js +8 -1
- package/i18n/languages/es-MX.mjs +8 -1
- package/i18n/languages/fa-IR.js +8 -1
- package/i18n/languages/fa-IR.mjs +8 -1
- package/i18n/languages/fr-FR.js +8 -1
- package/i18n/languages/fr-FR.mjs +8 -1
- package/i18n/languages/hr-HR.js +8 -1
- package/i18n/languages/hr-HR.mjs +8 -1
- package/i18n/languages/it-IT.js +8 -1
- package/i18n/languages/it-IT.mjs +8 -1
- package/i18n/languages/ja-JP.js +8 -1
- package/i18n/languages/ja-JP.mjs +8 -1
- package/i18n/languages/ko-KR.js +8 -1
- package/i18n/languages/ko-KR.mjs +8 -1
- package/i18n/languages/lv-LV.js +8 -1
- package/i18n/languages/lv-LV.mjs +8 -1
- package/i18n/languages/nb-NO.js +8 -1
- package/i18n/languages/nb-NO.mjs +8 -1
- package/i18n/languages/nl-NL.js +8 -1
- package/i18n/languages/nl-NL.mjs +8 -1
- package/i18n/languages/pl-PL.js +8 -1
- package/i18n/languages/pl-PL.mjs +8 -1
- package/i18n/languages/pt-BR.js +8 -1
- package/i18n/languages/pt-BR.mjs +8 -1
- package/i18n/languages/ru-RU.js +8 -1
- package/i18n/languages/ru-RU.mjs +8 -1
- package/i18n/languages/sr-SP.js +8 -1
- package/i18n/languages/sr-SP.mjs +8 -1
- package/i18n/languages/zh-CN.js +8 -1
- package/i18n/languages/zh-CN.mjs +8 -1
- package/i18n/languages/zh-TW.js +8 -1
- package/i18n/languages/zh-TW.mjs +8 -1
- package/index.d.ts +9 -0
- package/languages/all.js +168 -21
- package/languages/ar-AR.js +8 -1
- package/languages/ar-AR.mjs +8 -1
- package/languages/cs-CZ.js +8 -1
- package/languages/cs-CZ.mjs +8 -1
- package/languages/de-CH.js +8 -1
- package/languages/de-CH.mjs +8 -1
- package/languages/de-DE.js +8 -1
- package/languages/de-DE.mjs +8 -1
- package/languages/en-US.js +8 -1
- package/languages/en-US.mjs +8 -1
- package/languages/es-MX.js +8 -1
- package/languages/es-MX.mjs +8 -1
- package/languages/fa-IR.js +8 -1
- package/languages/fa-IR.mjs +8 -1
- package/languages/fr-FR.js +8 -1
- package/languages/fr-FR.mjs +8 -1
- package/languages/hr-HR.js +8 -1
- package/languages/hr-HR.mjs +8 -1
- package/languages/index.js +168 -21
- package/languages/it-IT.js +8 -1
- package/languages/it-IT.mjs +8 -1
- package/languages/ja-JP.js +8 -1
- package/languages/ja-JP.mjs +8 -1
- package/languages/ko-KR.js +8 -1
- package/languages/ko-KR.mjs +8 -1
- package/languages/lv-LV.js +8 -1
- package/languages/lv-LV.mjs +8 -1
- package/languages/nb-NO.js +8 -1
- package/languages/nb-NO.mjs +8 -1
- package/languages/nl-NL.js +8 -1
- package/languages/nl-NL.mjs +8 -1
- package/languages/pl-PL.js +8 -1
- package/languages/pl-PL.mjs +8 -1
- package/languages/pt-BR.js +8 -1
- package/languages/pt-BR.mjs +8 -1
- package/languages/ru-RU.js +8 -1
- package/languages/ru-RU.mjs +8 -1
- package/languages/sr-SP.js +8 -1
- package/languages/sr-SP.mjs +8 -1
- package/languages/zh-CN.js +8 -1
- package/languages/zh-CN.mjs +8 -1
- package/languages/zh-TW.js +8 -1
- package/languages/zh-TW.mjs +8 -1
- package/package.json +25 -7
- package/plugins/autoRowSize/autoRowSize.js +8 -1
- package/plugins/autoRowSize/autoRowSize.mjs +8 -1
- package/plugins/base/base.js +36 -10
- package/plugins/base/base.mjs +36 -10
- package/plugins/columnSummary/endpoints.js +13 -3
- package/plugins/columnSummary/endpoints.mjs +13 -3
- package/plugins/customBorders/customBorders.d.ts +1 -0
- package/plugins/customBorders/customBorders.js +32 -2
- package/plugins/customBorders/customBorders.mjs +32 -2
- package/plugins/dialog/constants.js +7 -0
- package/plugins/dialog/constants.mjs +4 -0
- package/plugins/dialog/dialog.d.ts +22 -2
- package/plugins/dialog/dialog.js +197 -81
- package/plugins/dialog/dialog.mjs +196 -81
- package/plugins/dialog/templates/base.js +60 -0
- package/plugins/dialog/templates/base.mjs +56 -0
- package/plugins/dialog/templates/confirm.js +106 -0
- package/plugins/dialog/templates/confirm.mjs +102 -0
- package/plugins/dialog/templates/index.js +6 -0
- package/plugins/dialog/templates/index.mjs +4 -0
- package/plugins/dialog/ui.js +125 -41
- package/plugins/dialog/ui.mjs +119 -35
- package/plugins/emptyDataState/emptyDataState.d.ts +24 -0
- package/plugins/emptyDataState/emptyDataState.js +526 -0
- package/plugins/emptyDataState/emptyDataState.mjs +521 -0
- package/plugins/emptyDataState/index.d.ts +1 -0
- package/plugins/emptyDataState/index.js +7 -0
- package/plugins/emptyDataState/index.mjs +1 -0
- package/plugins/emptyDataState/ui.js +282 -0
- package/plugins/emptyDataState/ui.mjs +278 -0
- package/plugins/filters/component/value.js +16 -1
- package/plugins/filters/component/value.mjs +16 -1
- package/plugins/filters/filters.d.ts +5 -1
- package/plugins/filters/filters.js +22 -1
- package/plugins/filters/filters.mjs +22 -1
- package/plugins/filters/ui/multipleSelect.js +90 -79
- package/plugins/filters/ui/multipleSelect.mjs +90 -79
- package/plugins/index.d.ts +3 -0
- package/plugins/index.js +3 -0
- package/plugins/index.mjs +3 -1
- package/plugins/mergeCells/utils.js +1 -5
- package/plugins/mergeCells/utils.mjs +1 -5
- package/plugins/pagination/pagination.js +37 -175
- package/plugins/pagination/pagination.mjs +37 -175
- package/plugins/pagination/strategies/autoPageSize.js +2 -2
- package/plugins/pagination/strategies/autoPageSize.mjs +2 -2
- package/plugins/pagination/ui.js +6 -10
- package/plugins/pagination/ui.mjs +7 -11
- package/plugins/stretchColumns/calculator.js +3 -1
- package/plugins/stretchColumns/calculator.mjs +3 -1
- package/plugins/undoRedo/undoRedo.js +16 -6
- package/plugins/undoRedo/undoRedo.mjs +16 -5
- package/renderers/checkboxRenderer/checkboxRenderer.js +12 -15
- package/renderers/checkboxRenderer/checkboxRenderer.mjs +12 -15
- package/selection/selection.js +1 -1
- package/selection/selection.mjs +1 -1
- package/settings.d.ts +3 -0
- package/shortcutContexts/commands/index.js +2 -1
- package/shortcutContexts/commands/index.mjs +2 -1
- package/shortcutContexts/commands/tabNavigation.js +51 -0
- package/shortcutContexts/commands/tabNavigation.mjs +48 -0
- package/shortcutContexts/constants.js +16 -1
- package/shortcutContexts/constants.mjs +16 -1
- package/shortcutContexts/editor.js +2 -2
- package/shortcutContexts/editor.mjs +3 -3
- package/shortcutContexts/grid.js +19 -3
- package/shortcutContexts/grid.mjs +20 -4
- package/shortcuts/manager.d.ts +1 -0
- package/shortcuts/manager.js +17 -2
- package/shortcuts/manager.mjs +17 -2
- package/styles/handsontable.css +192 -35
- package/styles/handsontable.min.css +3 -3
- package/styles/ht-icons-horizon.css +233 -0
- package/styles/ht-icons-horizon.min.css +30 -0
- package/styles/ht-icons-main.css +233 -0
- package/styles/ht-icons-main.min.css +30 -0
- package/styles/ht-theme-classic-no-icons.css +399 -0
- package/styles/ht-theme-classic-no-icons.min.css +30 -0
- package/styles/ht-theme-classic.css +308 -556
- package/styles/ht-theme-classic.min.css +3 -3
- package/styles/ht-theme-horizon-no-icons.css +405 -0
- package/styles/ht-theme-horizon-no-icons.min.css +30 -0
- package/styles/ht-theme-horizon.css +312 -556
- package/styles/ht-theme-horizon.min.css +3 -3
- package/styles/ht-theme-main-no-icons.css +396 -0
- package/styles/ht-theme-main-no-icons.min.css +30 -0
- package/styles/ht-theme-main.css +303 -556
- package/styles/ht-theme-main.min.css +3 -3
- package/tableView.js +23 -5
- package/tableView.mjs +23 -5
- package/utils/dataStructures/uniqueMap.js +10 -0
- package/utils/dataStructures/uniqueMap.mjs +10 -0
- package/utils/ghostTable.js +0 -3
- package/utils/ghostTable.mjs +0 -3
- package/utils/stylesHandler.js +19 -4
- package/utils/stylesHandler.mjs +19 -4
- package/core/focusCatcher/index.js +0 -131
- package/core/focusCatcher/index.mjs +0 -127
- package/core/focusCatcher/utils.js +0 -31
- package/core/focusCatcher/utils.mjs +0 -27
- package/plugins/pagination/focusController.js +0 -27
- package/plugins/pagination/focusController.mjs +0 -23
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.createFocusScopeManager = createFocusScopeManager;
|
|
5
|
+
require("core-js/modules/es.error.cause.js");
|
|
6
|
+
require("core-js/modules/esnext.iterator.constructor.js");
|
|
7
|
+
require("core-js/modules/esnext.iterator.filter.js");
|
|
8
|
+
require("core-js/modules/esnext.iterator.for-each.js");
|
|
9
|
+
var _uniqueMap = require("../utils/dataStructures/uniqueMap");
|
|
10
|
+
var _scope = require("./scope");
|
|
11
|
+
var _eventListener = require("./eventListener");
|
|
12
|
+
var _constants = require("./constants");
|
|
13
|
+
var _element = require("../helpers/dom/element");
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {object} FocusScopeManager
|
|
16
|
+
* @property {function(): string | null} getActiveScopeId Returns the ID of the active scope.
|
|
17
|
+
* @property {function(string, HTMLElement, object): void} registerScope Registers a new focus scope.
|
|
18
|
+
* @property {function(string): void} unregisterScope Unregisters a scope by its ID.
|
|
19
|
+
* @property {function(string): void} activateScope Activates a focus scope by its ID.
|
|
20
|
+
* @property {function(string): void} deactivateScope Deactivates a scope by its ID.
|
|
21
|
+
* @property {function(): void} destroy Destroys the focus scope manager.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a focus scope manager for a Handsontable instance. The manager handles focus
|
|
26
|
+
* scopes by listening to keydown, focusin, and click events on the document. Based on
|
|
27
|
+
* the currently focused element, it activates or deactivates the appropriate scope.
|
|
28
|
+
* Focus scope contains its own boundaries and logic that once activated allows to focus
|
|
29
|
+
* specific focusable element within the scope container element and/or switch to specific
|
|
30
|
+
* shortcuts context.
|
|
31
|
+
*
|
|
32
|
+
* The manager also automatically updates the {@link Core#isListening} state of the Handsontable
|
|
33
|
+
* instance based on the current state of the scopes.
|
|
34
|
+
*
|
|
35
|
+
* @alias FocusScopeManager
|
|
36
|
+
* @class FocusScopeManager
|
|
37
|
+
* @param {Core} hotInstance The Handsontable instance.
|
|
38
|
+
*/
|
|
39
|
+
function createFocusScopeManager(hotInstance) {
|
|
40
|
+
const SCOPES = (0, _uniqueMap.createUniqueMap)({
|
|
41
|
+
errorIdExists: name => `The "${name}" focus scope is already registered.`
|
|
42
|
+
});
|
|
43
|
+
const shortcutManager = hotInstance.getShortcutManager();
|
|
44
|
+
let activeScope = null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns the ID of the active scope.
|
|
48
|
+
*
|
|
49
|
+
* @memberof FocusScopeManager#
|
|
50
|
+
* @returns {string | null} The ID of the active scope.
|
|
51
|
+
*/
|
|
52
|
+
function getActiveScopeId() {
|
|
53
|
+
if (!activeScope) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return SCOPES.getId(activeScope);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Registers a new focus scope.
|
|
61
|
+
*
|
|
62
|
+
* @memberof FocusScopeManager#
|
|
63
|
+
* @param {string} scopeId Unique identifier for the scope.
|
|
64
|
+
* @param {HTMLElement} container Container element for the scope.
|
|
65
|
+
* @param {object} [options] Configuration options.
|
|
66
|
+
* @param {string} [options.shortcutsContextName='grid'] The name of the shortcuts context to switch to when
|
|
67
|
+
* the scope is activated.
|
|
68
|
+
* @param {'modal' | 'inline'} [options.type='inline'] The type of the scope:<br/>
|
|
69
|
+
* - `modal`: The scope is modal and blocks the rest of the grid from receiving focus.<br/>
|
|
70
|
+
* - `inline`: The scope is inline and allows the rest of the grid to receive focus in the order of the rendered elements in the DOM.
|
|
71
|
+
* @param {function(): boolean} [options.runOnlyIf] Whether the scope is enabled or not depends on the custom logic.
|
|
72
|
+
* @param {function(HTMLElement): boolean} [options.contains] Whether the target element is within the scope. If the option is not
|
|
73
|
+
* provided, the scope will be activated if the target element is within the container element.
|
|
74
|
+
* @param {function(): void} [options.onActivate] Callback function to be called when the scope is activated.
|
|
75
|
+
* The first argument is the source of the activation:<br/>
|
|
76
|
+
* - `unknown`: The scope is activated by an unknown source.<br/>
|
|
77
|
+
* - `click`: The scope is activated by a click event.<br/>
|
|
78
|
+
* - `tab_from_above`: The scope is activated by a tab key press.<br/>
|
|
79
|
+
* - `tab_from_below`: The scope is activated by a shift+tab key press.
|
|
80
|
+
* @param {function(): void} [options.onDeactivate] Callback function to be called when the scope is deactivated.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* For regular element (inline scope)
|
|
84
|
+
*
|
|
85
|
+
* ```js
|
|
86
|
+
* hot.getFocusScopeManager().registerScope('myPluginName', containerElement, {
|
|
87
|
+
* shortcutsContextName: 'plugin:myPluginName',
|
|
88
|
+
* onActivate: (focusSource) => {
|
|
89
|
+
* // Focus the internal focusable element within the plugin UI element
|
|
90
|
+
* },
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* or for modal scope
|
|
95
|
+
*
|
|
96
|
+
* ```js
|
|
97
|
+
* hot.getFocusScopeManager().registerScope('myPluginName', containerElement, {
|
|
98
|
+
* shortcutsContextName: 'plugin:myPluginName',
|
|
99
|
+
* type: 'modal',
|
|
100
|
+
* runOnlyIf: () => isDialogOpened(),
|
|
101
|
+
* onActivate: (focusSource) => {
|
|
102
|
+
* // Focus the internal focusable element within the plugin UI element
|
|
103
|
+
* },
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
function registerScope(scopeId, container) {
|
|
108
|
+
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
109
|
+
if (SCOPES.hasItem(scopeId)) {
|
|
110
|
+
throw new Error(`Scope with id "${scopeId}" already registered`);
|
|
111
|
+
}
|
|
112
|
+
const scope = (0, _scope.createFocusScope)(hotInstance, container, options);
|
|
113
|
+
SCOPES.addItem(scopeId, scope);
|
|
114
|
+
shortcutManager.getOrCreateContext(scope.getShortcutsContextName());
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Unregisters a scope completely.
|
|
119
|
+
*
|
|
120
|
+
* @memberof FocusScopeManager#
|
|
121
|
+
* @param {string} scopeId The scope to remove.
|
|
122
|
+
*/
|
|
123
|
+
function unregisterScope(scopeId) {
|
|
124
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
125
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
126
|
+
}
|
|
127
|
+
const scope = SCOPES.getItem(scopeId);
|
|
128
|
+
scope.destroy();
|
|
129
|
+
SCOPES.removeItem(scopeId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Activates a focus scope by its ID.
|
|
134
|
+
*
|
|
135
|
+
* @memberof FocusScopeManager#
|
|
136
|
+
* @alias FocusScopeManager#activateScope
|
|
137
|
+
* @param {string} scopeId The ID of the scope to activate.
|
|
138
|
+
*/
|
|
139
|
+
function activateScopeById(scopeId) {
|
|
140
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
141
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
142
|
+
}
|
|
143
|
+
activateScope(SCOPES.getItem(scopeId));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Deactivates a scope by its ID.
|
|
148
|
+
*
|
|
149
|
+
* @memberof FocusScopeManager#
|
|
150
|
+
* @alias FocusScopeManager#deactivateScope
|
|
151
|
+
* @param {string} scopeId The ID of the scope to deactivate.
|
|
152
|
+
*/
|
|
153
|
+
function deactivateScopeById(scopeId) {
|
|
154
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
155
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
156
|
+
}
|
|
157
|
+
deactivateScope(SCOPES.getItem(scopeId));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Activates a specific scope.
|
|
162
|
+
*
|
|
163
|
+
* @param {object} scope The scope to activate.
|
|
164
|
+
* @param {'unknown' | 'click' | 'tab_from_above' | 'tab_from_below'} focusSource The source of the focus event.
|
|
165
|
+
*/
|
|
166
|
+
function activateScope(scope) {
|
|
167
|
+
let focusSource = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _constants.FOCUS_SOURCES.UNKNOWN;
|
|
168
|
+
if (activeScope === scope) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (activeScope !== null) {
|
|
172
|
+
deactivateScope(activeScope);
|
|
173
|
+
}
|
|
174
|
+
activeScope = scope;
|
|
175
|
+
activeScope.activate(focusSource);
|
|
176
|
+
shortcutManager.setActiveContextName(scope.getShortcutsContextName());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Deactivates a scope by its ID.
|
|
181
|
+
*
|
|
182
|
+
* @param {object} scope The scope to deactivate.
|
|
183
|
+
*/
|
|
184
|
+
function deactivateScope(scope) {
|
|
185
|
+
updateScopesFocusVisibilityState();
|
|
186
|
+
if (activeScope !== scope) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
activeScope = null;
|
|
190
|
+
scope.deactivate();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Updates the focus scopes state by enabling or disabling them or their focus catchers to make sure
|
|
195
|
+
* that the next native focus move won't be disturbed.
|
|
196
|
+
*/
|
|
197
|
+
function updateScopesFocusVisibilityState() {
|
|
198
|
+
const scopes = SCOPES.getValues();
|
|
199
|
+
const modalScopes = scopes.filter(scope => scope.runOnlyIf() && scope.getType() === 'modal');
|
|
200
|
+
scopes.forEach(scope => {
|
|
201
|
+
if (modalScopes.length > 0 && modalScopes.includes(scope) || modalScopes.length === 0 || scope.hasContainerDetached()) {
|
|
202
|
+
scope.enable();
|
|
203
|
+
} else {
|
|
204
|
+
scope.disable();
|
|
205
|
+
}
|
|
206
|
+
if (scope === activeScope) {
|
|
207
|
+
if (scope.contains(hotInstance.rootDocument.activeElement)) {
|
|
208
|
+
scope.deactivateFocusCatchers();
|
|
209
|
+
} else {
|
|
210
|
+
scope.activateFocusCatchers();
|
|
211
|
+
}
|
|
212
|
+
} else if (scope.runOnlyIf()) {
|
|
213
|
+
scope.activateFocusCatchers();
|
|
214
|
+
} else {
|
|
215
|
+
scope.deactivateFocusCatchers();
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Activates or deactivates the appropriate scope based on the target element that was
|
|
222
|
+
* triggered by the focus or click event.
|
|
223
|
+
*
|
|
224
|
+
* @param {HTMLElement} target The target element.
|
|
225
|
+
* @param {'unknown' | 'click' | 'tab_from_above' | 'tab_from_below'} focusSource The source of the focus event.
|
|
226
|
+
*/
|
|
227
|
+
function processScopes(target, focusSource) {
|
|
228
|
+
if (!target.isConnected || !(0, _element.isVisible)(target)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const allEnabledScopes = SCOPES.getValues().filter(scope => scope.runOnlyIf());
|
|
232
|
+
let hasActiveScope = false;
|
|
233
|
+
allEnabledScopes.forEach(scope => {
|
|
234
|
+
if (!hasActiveScope && scope.contains(target)) {
|
|
235
|
+
hasActiveScope = true;
|
|
236
|
+
if (focusSource !== _constants.FOCUS_SOURCES.UNKNOWN) {
|
|
237
|
+
hotInstance.listen();
|
|
238
|
+
}
|
|
239
|
+
activateScope(scope, focusSource);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
if (!hasActiveScope && activeScope) {
|
|
243
|
+
deactivateScope(activeScope);
|
|
244
|
+
hotInstance.unlisten();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const eventListener = (0, _eventListener.useEventListener)(hotInstance.rootWindow, {
|
|
248
|
+
onFocus: event => {
|
|
249
|
+
var _event$target$dataset;
|
|
250
|
+
processScopes(event.target, (_event$target$dataset = event.target.dataset.htFocusSource) !== null && _event$target$dataset !== void 0 ? _event$target$dataset : _constants.FOCUS_SOURCES.UNKNOWN);
|
|
251
|
+
},
|
|
252
|
+
onClick: event => {
|
|
253
|
+
processScopes(event.target, _constants.FOCUS_SOURCES.CLICK);
|
|
254
|
+
},
|
|
255
|
+
onTabKeyDown: () => {
|
|
256
|
+
updateScopesFocusVisibilityState();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
eventListener.mount();
|
|
260
|
+
return {
|
|
261
|
+
getActiveScopeId,
|
|
262
|
+
registerScope,
|
|
263
|
+
unregisterScope,
|
|
264
|
+
activateScope: scopeId => activateScopeById(scopeId),
|
|
265
|
+
deactivateScope: scopeId => deactivateScopeById(scopeId),
|
|
266
|
+
destroy: () => eventListener.unmount()
|
|
267
|
+
};
|
|
268
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import "core-js/modules/es.error.cause.js";
|
|
2
|
+
import "core-js/modules/esnext.iterator.constructor.js";
|
|
3
|
+
import "core-js/modules/esnext.iterator.filter.js";
|
|
4
|
+
import "core-js/modules/esnext.iterator.for-each.js";
|
|
5
|
+
import { createUniqueMap } from "../utils/dataStructures/uniqueMap.mjs";
|
|
6
|
+
import { createFocusScope } from "./scope.mjs";
|
|
7
|
+
import { useEventListener } from "./eventListener.mjs";
|
|
8
|
+
import { FOCUS_SOURCES } from "./constants.mjs";
|
|
9
|
+
import { isVisible } from "../helpers/dom/element.mjs";
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} FocusScopeManager
|
|
12
|
+
* @property {function(): string | null} getActiveScopeId Returns the ID of the active scope.
|
|
13
|
+
* @property {function(string, HTMLElement, object): void} registerScope Registers a new focus scope.
|
|
14
|
+
* @property {function(string): void} unregisterScope Unregisters a scope by its ID.
|
|
15
|
+
* @property {function(string): void} activateScope Activates a focus scope by its ID.
|
|
16
|
+
* @property {function(string): void} deactivateScope Deactivates a scope by its ID.
|
|
17
|
+
* @property {function(): void} destroy Destroys the focus scope manager.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Creates a focus scope manager for a Handsontable instance. The manager handles focus
|
|
21
|
+
* scopes by listening to keydown, focusin, and click events on the document. Based on
|
|
22
|
+
* the currently focused element, it activates or deactivates the appropriate scope.
|
|
23
|
+
* Focus scope contains its own boundaries and logic that once activated allows to focus
|
|
24
|
+
* specific focusable element within the scope container element and/or switch to specific
|
|
25
|
+
* shortcuts context.
|
|
26
|
+
*
|
|
27
|
+
* The manager also automatically updates the {@link Core#isListening} state of the Handsontable
|
|
28
|
+
* instance based on the current state of the scopes.
|
|
29
|
+
*
|
|
30
|
+
* @alias FocusScopeManager
|
|
31
|
+
* @class FocusScopeManager
|
|
32
|
+
* @param {Core} hotInstance The Handsontable instance.
|
|
33
|
+
*/
|
|
34
|
+
export function createFocusScopeManager(hotInstance) {
|
|
35
|
+
const SCOPES = createUniqueMap({
|
|
36
|
+
errorIdExists: name => `The "${name}" focus scope is already registered.`
|
|
37
|
+
});
|
|
38
|
+
const shortcutManager = hotInstance.getShortcutManager();
|
|
39
|
+
let activeScope = null;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns the ID of the active scope.
|
|
43
|
+
*
|
|
44
|
+
* @memberof FocusScopeManager#
|
|
45
|
+
* @returns {string | null} The ID of the active scope.
|
|
46
|
+
*/
|
|
47
|
+
function getActiveScopeId() {
|
|
48
|
+
if (!activeScope) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
return SCOPES.getId(activeScope);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Registers a new focus scope.
|
|
56
|
+
*
|
|
57
|
+
* @memberof FocusScopeManager#
|
|
58
|
+
* @param {string} scopeId Unique identifier for the scope.
|
|
59
|
+
* @param {HTMLElement} container Container element for the scope.
|
|
60
|
+
* @param {object} [options] Configuration options.
|
|
61
|
+
* @param {string} [options.shortcutsContextName='grid'] The name of the shortcuts context to switch to when
|
|
62
|
+
* the scope is activated.
|
|
63
|
+
* @param {'modal' | 'inline'} [options.type='inline'] The type of the scope:<br/>
|
|
64
|
+
* - `modal`: The scope is modal and blocks the rest of the grid from receiving focus.<br/>
|
|
65
|
+
* - `inline`: The scope is inline and allows the rest of the grid to receive focus in the order of the rendered elements in the DOM.
|
|
66
|
+
* @param {function(): boolean} [options.runOnlyIf] Whether the scope is enabled or not depends on the custom logic.
|
|
67
|
+
* @param {function(HTMLElement): boolean} [options.contains] Whether the target element is within the scope. If the option is not
|
|
68
|
+
* provided, the scope will be activated if the target element is within the container element.
|
|
69
|
+
* @param {function(): void} [options.onActivate] Callback function to be called when the scope is activated.
|
|
70
|
+
* The first argument is the source of the activation:<br/>
|
|
71
|
+
* - `unknown`: The scope is activated by an unknown source.<br/>
|
|
72
|
+
* - `click`: The scope is activated by a click event.<br/>
|
|
73
|
+
* - `tab_from_above`: The scope is activated by a tab key press.<br/>
|
|
74
|
+
* - `tab_from_below`: The scope is activated by a shift+tab key press.
|
|
75
|
+
* @param {function(): void} [options.onDeactivate] Callback function to be called when the scope is deactivated.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* For regular element (inline scope)
|
|
79
|
+
*
|
|
80
|
+
* ```js
|
|
81
|
+
* hot.getFocusScopeManager().registerScope('myPluginName', containerElement, {
|
|
82
|
+
* shortcutsContextName: 'plugin:myPluginName',
|
|
83
|
+
* onActivate: (focusSource) => {
|
|
84
|
+
* // Focus the internal focusable element within the plugin UI element
|
|
85
|
+
* },
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* or for modal scope
|
|
90
|
+
*
|
|
91
|
+
* ```js
|
|
92
|
+
* hot.getFocusScopeManager().registerScope('myPluginName', containerElement, {
|
|
93
|
+
* shortcutsContextName: 'plugin:myPluginName',
|
|
94
|
+
* type: 'modal',
|
|
95
|
+
* runOnlyIf: () => isDialogOpened(),
|
|
96
|
+
* onActivate: (focusSource) => {
|
|
97
|
+
* // Focus the internal focusable element within the plugin UI element
|
|
98
|
+
* },
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
function registerScope(scopeId, container) {
|
|
103
|
+
let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
104
|
+
if (SCOPES.hasItem(scopeId)) {
|
|
105
|
+
throw new Error(`Scope with id "${scopeId}" already registered`);
|
|
106
|
+
}
|
|
107
|
+
const scope = createFocusScope(hotInstance, container, options);
|
|
108
|
+
SCOPES.addItem(scopeId, scope);
|
|
109
|
+
shortcutManager.getOrCreateContext(scope.getShortcutsContextName());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Unregisters a scope completely.
|
|
114
|
+
*
|
|
115
|
+
* @memberof FocusScopeManager#
|
|
116
|
+
* @param {string} scopeId The scope to remove.
|
|
117
|
+
*/
|
|
118
|
+
function unregisterScope(scopeId) {
|
|
119
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
120
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
121
|
+
}
|
|
122
|
+
const scope = SCOPES.getItem(scopeId);
|
|
123
|
+
scope.destroy();
|
|
124
|
+
SCOPES.removeItem(scopeId);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Activates a focus scope by its ID.
|
|
129
|
+
*
|
|
130
|
+
* @memberof FocusScopeManager#
|
|
131
|
+
* @alias FocusScopeManager#activateScope
|
|
132
|
+
* @param {string} scopeId The ID of the scope to activate.
|
|
133
|
+
*/
|
|
134
|
+
function activateScopeById(scopeId) {
|
|
135
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
136
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
137
|
+
}
|
|
138
|
+
activateScope(SCOPES.getItem(scopeId));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Deactivates a scope by its ID.
|
|
143
|
+
*
|
|
144
|
+
* @memberof FocusScopeManager#
|
|
145
|
+
* @alias FocusScopeManager#deactivateScope
|
|
146
|
+
* @param {string} scopeId The ID of the scope to deactivate.
|
|
147
|
+
*/
|
|
148
|
+
function deactivateScopeById(scopeId) {
|
|
149
|
+
if (!SCOPES.hasItem(scopeId)) {
|
|
150
|
+
throw new Error(`Scope with id "${scopeId}" not found`);
|
|
151
|
+
}
|
|
152
|
+
deactivateScope(SCOPES.getItem(scopeId));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Activates a specific scope.
|
|
157
|
+
*
|
|
158
|
+
* @param {object} scope The scope to activate.
|
|
159
|
+
* @param {'unknown' | 'click' | 'tab_from_above' | 'tab_from_below'} focusSource The source of the focus event.
|
|
160
|
+
*/
|
|
161
|
+
function activateScope(scope) {
|
|
162
|
+
let focusSource = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : FOCUS_SOURCES.UNKNOWN;
|
|
163
|
+
if (activeScope === scope) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (activeScope !== null) {
|
|
167
|
+
deactivateScope(activeScope);
|
|
168
|
+
}
|
|
169
|
+
activeScope = scope;
|
|
170
|
+
activeScope.activate(focusSource);
|
|
171
|
+
shortcutManager.setActiveContextName(scope.getShortcutsContextName());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Deactivates a scope by its ID.
|
|
176
|
+
*
|
|
177
|
+
* @param {object} scope The scope to deactivate.
|
|
178
|
+
*/
|
|
179
|
+
function deactivateScope(scope) {
|
|
180
|
+
updateScopesFocusVisibilityState();
|
|
181
|
+
if (activeScope !== scope) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
activeScope = null;
|
|
185
|
+
scope.deactivate();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Updates the focus scopes state by enabling or disabling them or their focus catchers to make sure
|
|
190
|
+
* that the next native focus move won't be disturbed.
|
|
191
|
+
*/
|
|
192
|
+
function updateScopesFocusVisibilityState() {
|
|
193
|
+
const scopes = SCOPES.getValues();
|
|
194
|
+
const modalScopes = scopes.filter(scope => scope.runOnlyIf() && scope.getType() === 'modal');
|
|
195
|
+
scopes.forEach(scope => {
|
|
196
|
+
if (modalScopes.length > 0 && modalScopes.includes(scope) || modalScopes.length === 0 || scope.hasContainerDetached()) {
|
|
197
|
+
scope.enable();
|
|
198
|
+
} else {
|
|
199
|
+
scope.disable();
|
|
200
|
+
}
|
|
201
|
+
if (scope === activeScope) {
|
|
202
|
+
if (scope.contains(hotInstance.rootDocument.activeElement)) {
|
|
203
|
+
scope.deactivateFocusCatchers();
|
|
204
|
+
} else {
|
|
205
|
+
scope.activateFocusCatchers();
|
|
206
|
+
}
|
|
207
|
+
} else if (scope.runOnlyIf()) {
|
|
208
|
+
scope.activateFocusCatchers();
|
|
209
|
+
} else {
|
|
210
|
+
scope.deactivateFocusCatchers();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Activates or deactivates the appropriate scope based on the target element that was
|
|
217
|
+
* triggered by the focus or click event.
|
|
218
|
+
*
|
|
219
|
+
* @param {HTMLElement} target The target element.
|
|
220
|
+
* @param {'unknown' | 'click' | 'tab_from_above' | 'tab_from_below'} focusSource The source of the focus event.
|
|
221
|
+
*/
|
|
222
|
+
function processScopes(target, focusSource) {
|
|
223
|
+
if (!target.isConnected || !isVisible(target)) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const allEnabledScopes = SCOPES.getValues().filter(scope => scope.runOnlyIf());
|
|
227
|
+
let hasActiveScope = false;
|
|
228
|
+
allEnabledScopes.forEach(scope => {
|
|
229
|
+
if (!hasActiveScope && scope.contains(target)) {
|
|
230
|
+
hasActiveScope = true;
|
|
231
|
+
if (focusSource !== FOCUS_SOURCES.UNKNOWN) {
|
|
232
|
+
hotInstance.listen();
|
|
233
|
+
}
|
|
234
|
+
activateScope(scope, focusSource);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
if (!hasActiveScope && activeScope) {
|
|
238
|
+
deactivateScope(activeScope);
|
|
239
|
+
hotInstance.unlisten();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const eventListener = useEventListener(hotInstance.rootWindow, {
|
|
243
|
+
onFocus: event => {
|
|
244
|
+
var _event$target$dataset;
|
|
245
|
+
processScopes(event.target, (_event$target$dataset = event.target.dataset.htFocusSource) !== null && _event$target$dataset !== void 0 ? _event$target$dataset : FOCUS_SOURCES.UNKNOWN);
|
|
246
|
+
},
|
|
247
|
+
onClick: event => {
|
|
248
|
+
processScopes(event.target, FOCUS_SOURCES.CLICK);
|
|
249
|
+
},
|
|
250
|
+
onTabKeyDown: () => {
|
|
251
|
+
updateScopesFocusVisibilityState();
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
eventListener.mount();
|
|
255
|
+
return {
|
|
256
|
+
getActiveScopeId,
|
|
257
|
+
registerScope,
|
|
258
|
+
unregisterScope,
|
|
259
|
+
activateScope: scopeId => activateScopeById(scopeId),
|
|
260
|
+
deactivateScope: scopeId => deactivateScopeById(scopeId),
|
|
261
|
+
destroy: () => eventListener.unmount()
|
|
262
|
+
};
|
|
263
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.focusGridScope = focusGridScope;
|
|
5
|
+
var _utils = require("../utils/utils");
|
|
6
|
+
var _shortcutContexts = require("../../shortcutContexts");
|
|
7
|
+
/**
|
|
8
|
+
* @param {Handsontable} hot The Handsontable instance.
|
|
9
|
+
*/
|
|
10
|
+
function focusGridScope(hot) {
|
|
11
|
+
var _hot$rootGridElement;
|
|
12
|
+
const clampCoordsIfNeeded = (0, _utils.normalizeCoordsIfNeeded)(hot);
|
|
13
|
+
const rowWrapState = {
|
|
14
|
+
wrapped: false,
|
|
15
|
+
flipped: false
|
|
16
|
+
};
|
|
17
|
+
let recentlyAddedFocusCoords;
|
|
18
|
+
let isSavingCoordsEnabled = true;
|
|
19
|
+
let isEmptyDataStateActive = false;
|
|
20
|
+
hot.addHook('afterSelection', () => {
|
|
21
|
+
if (isSavingCoordsEnabled) {
|
|
22
|
+
var _hot$getSelectedRange;
|
|
23
|
+
recentlyAddedFocusCoords = (_hot$getSelectedRange = hot.getSelectedRangeActive()) === null || _hot$getSelectedRange === void 0 ? void 0 : _hot$getSelectedRange.highlight;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
hot.addHook('beforeRowWrap', (interruptedByAutoInsertMode, newCoords, isFlipped) => {
|
|
27
|
+
rowWrapState.wrapped = true;
|
|
28
|
+
rowWrapState.flipped = isFlipped;
|
|
29
|
+
});
|
|
30
|
+
hot.addHook('beforeEmptyDataStateShow', () => {
|
|
31
|
+
isEmptyDataStateActive = true;
|
|
32
|
+
});
|
|
33
|
+
hot.addHook('beforeEmptyDataStateHide', () => {
|
|
34
|
+
isEmptyDataStateActive = false;
|
|
35
|
+
});
|
|
36
|
+
const context = hot.getShortcutManager().getContext(_shortcutContexts.GRID_SCOPE);
|
|
37
|
+
context.addShortcuts([{
|
|
38
|
+
keys: [['Tab'], ['Shift', 'Tab']],
|
|
39
|
+
preventDefault: false,
|
|
40
|
+
stopPropagation: false,
|
|
41
|
+
relativeToGroup: _shortcutContexts.GRID_GROUP,
|
|
42
|
+
group: _shortcutContexts.GRID_TAB_NAVIGATION_GROUP,
|
|
43
|
+
position: 'before',
|
|
44
|
+
callback() {
|
|
45
|
+
const {
|
|
46
|
+
tabNavigation
|
|
47
|
+
} = hot.getSettings();
|
|
48
|
+
if (hot.getSelectedRangeActive() && !tabNavigation) {
|
|
49
|
+
isSavingCoordsEnabled = false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, {
|
|
53
|
+
keys: [['Tab'], ['Shift', 'Tab']],
|
|
54
|
+
preventDefault: false,
|
|
55
|
+
stopPropagation: false,
|
|
56
|
+
relativeToGroup: _shortcutContexts.GRID_GROUP,
|
|
57
|
+
group: _shortcutContexts.GRID_TAB_NAVIGATION_GROUP,
|
|
58
|
+
callback(event) {
|
|
59
|
+
const {
|
|
60
|
+
tabNavigation,
|
|
61
|
+
autoWrapRow
|
|
62
|
+
} = hot.getSettings();
|
|
63
|
+
isSavingCoordsEnabled = true;
|
|
64
|
+
if (!tabNavigation || !hot.selection.isSelected() || autoWrapRow && rowWrapState.wrapped && rowWrapState.flipped || !autoWrapRow && rowWrapState.wrapped) {
|
|
65
|
+
if (autoWrapRow && rowWrapState.wrapped && rowWrapState.flipped) {
|
|
66
|
+
recentlyAddedFocusCoords = event.shiftKey ? (0, _utils.getMostTopStartPosition)(hot) : (0, _utils.getMostBottomEndPosition)(hot);
|
|
67
|
+
}
|
|
68
|
+
rowWrapState.wrapped = false;
|
|
69
|
+
rowWrapState.flipped = false;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
position: 'after'
|
|
73
|
+
}]);
|
|
74
|
+
const container = (_hot$rootGridElement = hot.rootGridElement) !== null && _hot$rootGridElement !== void 0 ? _hot$rootGridElement : hot.rootElement;
|
|
75
|
+
hot.getFocusScopeManager().registerScope('grid', container, {
|
|
76
|
+
contains: target => {
|
|
77
|
+
if (container === target || container.contains(target)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
if (target.closest('.htMenu') !== null) {
|
|
81
|
+
// TODO: Skip switching focus scope to 'grid' for context and dropdown menus since
|
|
82
|
+
// focus management is not implemented for them. Their focus management
|
|
83
|
+
// is handled manually.
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return hot.rootPortalElement.contains(target);
|
|
87
|
+
},
|
|
88
|
+
runOnlyIf: () => {
|
|
89
|
+
const {
|
|
90
|
+
navigableHeaders
|
|
91
|
+
} = hot.getSettings();
|
|
92
|
+
if ((isEmptyDataStateActive || !navigableHeaders) && hot.countRenderedRows() === 0 && hot.countRenderedCols() === 0 && hot.countRowHeaders() > 0 && hot.countColHeaders() > 0) {
|
|
93
|
+
// When the corner is only rendered, and the EmptyDataState is active, deactivate the scope.
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
return !navigableHeaders && hot.countRenderedRows() > 0 && hot.countRenderedCols() > 0 || navigableHeaders && (hot.countRowHeaders() > 0 || hot.countColHeaders() > 0);
|
|
97
|
+
},
|
|
98
|
+
onActivate: focusSource => {
|
|
99
|
+
if (focusSource === 'tab_from_above') {
|
|
100
|
+
var _clampCoordsIfNeeded;
|
|
101
|
+
const mostTopStartCoords = (_clampCoordsIfNeeded = clampCoordsIfNeeded(recentlyAddedFocusCoords)) !== null && _clampCoordsIfNeeded !== void 0 ? _clampCoordsIfNeeded : (0, _utils.getMostTopStartPosition)(hot);
|
|
102
|
+
if (mostTopStartCoords) {
|
|
103
|
+
const result = hot.runHooks('modifyFocusOnTabNavigation', 'from_above', mostTopStartCoords);
|
|
104
|
+
if (result !== false) {
|
|
105
|
+
hot.selectCell(mostTopStartCoords.row, mostTopStartCoords.col);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} else if (focusSource === 'tab_from_below') {
|
|
109
|
+
var _clampCoordsIfNeeded2;
|
|
110
|
+
const mostBottomEndCoords = (_clampCoordsIfNeeded2 = clampCoordsIfNeeded(recentlyAddedFocusCoords)) !== null && _clampCoordsIfNeeded2 !== void 0 ? _clampCoordsIfNeeded2 : (0, _utils.getMostBottomEndPosition)(hot);
|
|
111
|
+
if (mostBottomEndCoords) {
|
|
112
|
+
const result = hot.runHooks('modifyFocusOnTabNavigation', 'from_below', mostBottomEndCoords);
|
|
113
|
+
if (result !== false) {
|
|
114
|
+
hot.selectCell(mostBottomEndCoords.row, mostBottomEndCoords.col);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|