UNPKG

handsontable

Version:

Handsontable is a JavaScript Data Grid available for React, Angular and Vue.

1,034 lines (1,008 loc) • 39.8 kB
"use strict"; exports.__esModule = true; require("core-js/modules/es.error.cause.js"); require("core-js/modules/esnext.iterator.constructor.js"); require("core-js/modules/esnext.iterator.for-each.js"); require("core-js/modules/esnext.iterator.map.js"); var _base = require("../base"); var _number = require("../../helpers/number"); var _element = require("../../helpers/dom/element"); var _ui2 = require("./ui"); var _utils = require("./utils"); var _a11yAnnouncer = require("../../utils/a11yAnnouncer"); var _strategies = require("./strategies"); var _templateLiteralTag = require("../../helpers/templateLiteralTag"); var _console = require("../../helpers/console"); var _focusController2 = require("./focusController"); var _focusDetector2 = require("../../utils/focusDetector"); function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldSet(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } const PLUGIN_KEY = exports.PLUGIN_KEY = 'pagination'; const PLUGIN_PRIORITY = exports.PLUGIN_PRIORITY = 900; const SHORTCUTS_GROUP = PLUGIN_KEY; const SHORTCUTS_CONTEXT_NAME = `plugin:${PLUGIN_KEY}`; const AUTO_PAGE_SIZE_WARNING = (0, _templateLiteralTag.toSingleLine)`The \`auto\` page size setting requires the \`autoRowSize\`\x20 plugin to be enabled. Set the \`autoRowSize: true\` in the configuration to ensure correct behavior.`; /* eslint-disable jsdoc/require-description-complete-sentence */ /** * @plugin Pagination * @class Pagination * * @description * The plugin adds full-featured pagination capabilities to a table component. * It manages splitting rows into pages, rendering navigation controls, and exposing * methods and configuration for initializing and updating pagination state. * * Core responsibilities: * - Calculate which rows should be visible based on current `page` and `pageSize`. * - Render a toolbar area containing: * - a page size dropdown section (if `showPageSize` = `true`) * - a row counter section ("1 - 10 of 50", if `showCounter` = `true`) * - page navigation section (if `showNavigation` = `true`) * - Emit hooks when: * - the user navigates to a different page * - the user changes the number of rows per page * - the user changes the visibility of any sections * - Allow external code to programmatically: * - jump to a specific page * - change the page size * - change the visibility of UI sections * * @example * * ::: only-for javascript * ```js * const hot = new Handsontable(document.getElementById('example'), { * data: getData(), * pagination: { * pageSize: 10, * pageSizeList: ['auto', 5, 10, 20, 50, 100], * initialPage: 1, * showPageSize: true, * showCounter: true, * showNavigation: true, * }, * }); * ``` * ::: * * ::: only-for react * ```jsx * <HotTable * data={getData()} * pagination={{ * pageSize: 10, * pageSizeList: ['auto', 5, 10, 20, 50, 100], * initialPage: 1, * showPageSize: true, * showCounter: true, * showNavigation: true, * }} * /> * ``` * ::: * * ::: only-for angular * ```ts * settings = { * pagination: { * pageSize: 10, * pageSizeList: ['auto', 5, 10, 20, 50, 100], * initialPage: 1, * showPageSize: true, * showCounter: true, * showNavigation: true, * }, * }; * ``` * ::: */ var _pagedRowsMap = /*#__PURE__*/new WeakMap(); var _currentPage = /*#__PURE__*/new WeakMap(); var _pageSize = /*#__PURE__*/new WeakMap(); var _ui = /*#__PURE__*/new WeakMap(); var _calcStrategy = /*#__PURE__*/new WeakMap(); var _internalExecutionCall = /*#__PURE__*/new WeakMap(); var _internalRenderCall = /*#__PURE__*/new WeakMap(); var _focusController = /*#__PURE__*/new WeakMap(); var _focusDetector = /*#__PURE__*/new WeakMap(); var _Pagination_brand = /*#__PURE__*/new WeakSet(); var _onIndexCacheUpdate = /*#__PURE__*/new WeakMap(); class Pagination extends _base.BasePlugin { constructor() { super(...arguments); /** * Bind the events used by the plugin. */ _classPrivateMethodInitSpec(this, _Pagination_brand); /** * Map of hidden rows controlled by the pagination plugin. * * @type {HiddenMap | null} */ _classPrivateFieldInitSpec(this, _pagedRowsMap, null); /** * Current page number. * * @type {number} */ _classPrivateFieldInitSpec(this, _currentPage, 1); /** * Page size setup by the user. It can be a number or 'auto' (in which case the plugin will * calculate the page size based on the viewport size and row heights). * * @type {number | 'auto'} */ _classPrivateFieldInitSpec(this, _pageSize, 10); /** * UI instance of the pagination plugin. * * @type {PaginationUI} */ _classPrivateFieldInitSpec(this, _ui, null); /** * Pagination calculation strategy instance. It is used to calculate the pagination state * based on the user-defined settings. The result of the state is used to update the * pagination index mapper. * * @type {AutoPageSizeStrategy | FixedPageSizeStrategy | null} */ _classPrivateFieldInitSpec(this, _calcStrategy, null); /** * Flag indicating if the plugin is in the process of updating the index cache (execution operation). * Prevents circular calls when the index cache is updated. * * @type {boolean} */ _classPrivateFieldInitSpec(this, _internalExecutionCall, false); /** * Flag indicating if the plugin is in the process of updating the internal state (render operation). * Prevents circular calls when the render call is triggered by the pagination plugin itself. * * @type {boolean} */ _classPrivateFieldInitSpec(this, _internalRenderCall, false); /** * Pagination focus controller instance. * * @type {PaginationController} */ _classPrivateFieldInitSpec(this, _focusController, null); /** * Pagination focus detector instance. * * @type {object} */ _classPrivateFieldInitSpec(this, _focusDetector, null); /** * IndexMapper cache update listener. Once the cache is updated, we need to recompute * the pagination state. * * The method uses arrow function to keep the reference to the class method. Necessary for * the `removeLocalHook` method of the row index mapper. */ _classPrivateFieldInitSpec(this, _onIndexCacheUpdate, () => { var _this$hot; if (!_classPrivateFieldGet(_internalExecutionCall, this) && (_this$hot = this.hot) !== null && _this$hot !== void 0 && _this$hot.view) { _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); } }); } static get PLUGIN_KEY() { return PLUGIN_KEY; } static get PLUGIN_PRIORITY() { return PLUGIN_PRIORITY; } static get DEFAULT_SETTINGS() { return { pageSize: 10, pageSizeList: ['auto', 5, 10, 20, 50, 100], initialPage: 1, showPageSize: true, showCounter: true, showNavigation: true, uiContainer: null }; } /** * Checks if the plugin is enabled in the handsontable settings. This method is executed in {@link Hooks#beforeInit} * hook and if it returns `true` than the {@link Pagination#enablePlugin} method is called. * * @returns {boolean} */ isEnabled() { return !!this.hot.getSettings()[PLUGIN_KEY]; } /** * Enables the plugin functionality for this Handsontable instance. */ enablePlugin() { var _this$hot$getPlugin, _this = this; if ((0, _utils.checkPluginSettingsConflict)(this.hot.getSettings())) { this.hot.getSettings()[PLUGIN_KEY] = false; return; } if (this.enabled) { return; } const settings = this.hot.getSettings()[PLUGIN_KEY]; if ((settings === null || settings === void 0 ? void 0 : settings.initialPage) !== undefined) { _classPrivateFieldSet(_currentPage, this, this.getSetting('initialPage')); } if ((settings === null || settings === void 0 ? void 0 : settings.pageSize) !== undefined) { _classPrivateFieldSet(_pageSize, this, this.getSetting('pageSize')); } _classPrivateFieldSet(_pagedRowsMap, this, this.hot.rowIndexMapper.createAndRegisterIndexMap(this.pluginName, 'hiding', false)); if (_classPrivateFieldGet(_pageSize, this) === 'auto' && !((_this$hot$getPlugin = this.hot.getPlugin('autoRowSize')) !== null && _this$hot$getPlugin !== void 0 && _this$hot$getPlugin.enabled)) { (0, _console.warn)(AUTO_PAGE_SIZE_WARNING); } _classPrivateFieldSet(_calcStrategy, this, (0, _strategies.createPaginatorStrategy)(_classPrivateFieldGet(_pageSize, this) === 'auto' ? 'auto' : 'fixed')); if (!_classPrivateFieldGet(_ui, this)) { _classPrivateFieldSet(_ui, this, new _ui2.PaginationUI({ rootElement: this.hot.rootGridElement, uiContainer: this.getSetting('uiContainer'), isRtl: this.hot.isRtl(), themeName: this.hot.getSettings().themeName, phraseTranslator: function () { return _this.hot.getTranslatedPhrase(...arguments); }, shouldHaveBorder: () => _assertClassBrand(_Pagination_brand, this, _computeNeedsBorder).call(this), a11yAnnouncer: message => (0, _a11yAnnouncer.announce)(message) })); _assertClassBrand(_Pagination_brand, this, _updateSectionsVisibilityState).call(this); _classPrivateFieldGet(_ui, this).addLocalHook('firstPageClick', () => this.firstPage()).addLocalHook('prevPageClick', () => this.prevPage()).addLocalHook('nextPageClick', () => this.nextPage()).addLocalHook('lastPageClick', () => this.lastPage()).addLocalHook('pageSizeChange', pageSize => this.setPageSize(pageSize)).addLocalHook('focus', element => { _classPrivateFieldGet(_focusController, this).setCurrentPage(_classPrivateFieldGet(_ui, this).getFocusableElements().indexOf(element)); this.hot.unlisten(); this.hot.getShortcutManager().setActiveContextName(SHORTCUTS_CONTEXT_NAME); this.hot.listen(); _classPrivateFieldGet(_focusDetector, this).deactivate(); }); } if (!_classPrivateFieldGet(_focusController, this)) { _classPrivateFieldSet(_focusController, this, (0, _focusController2.createPaginationFocusController)({ focusableElements: () => _classPrivateFieldGet(_ui, this).getFocusableElements() })); } if (!_classPrivateFieldGet(_focusDetector, this)) { _classPrivateFieldSet(_focusDetector, this, (0, _focusDetector2.installFocusDetector)(this.hot, _classPrivateFieldGet(_ui, this).getContainer(), { onFocus: from => { this.hot.getShortcutManager().setActiveContextName(SHORTCUTS_CONTEXT_NAME); this.hot.listen(); if (from === 'from_above') { _classPrivateFieldGet(_focusController, this).toFirstItem(); } else { _classPrivateFieldGet(_focusController, this).toLastItem(); } _classPrivateFieldGet(_focusDetector, this).deactivate(); } })); } _assertClassBrand(_Pagination_brand, this, _registerEvents).call(this); _assertClassBrand(_Pagination_brand, this, _registerShortcuts).call(this); // Place the onInit hook before others to make sure that the pagination state is computed // and applied to the index mapper before AutoColumnSize plugin begins calculate the column sizes. this.addHook('init', function () { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _assertClassBrand(_Pagination_brand, _this, _onInit).call(_this, ...args); }, -1); this.addHook('beforeSelectAll', function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforeSelectAllRows).call(_this, ...args); }); this.addHook('beforeSelectColumns', function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforeSelectAllRows).call(_this, ...args); }); this.addHook('beforeSetRangeEnd', function () { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforeSetRangeEnd).call(_this, ...args); }); this.addHook('beforeSelectionHighlightSet', function () { for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforeSelectionHighlightSet).call(_this, ...args); }); this.addHook('beforePaste', function () { for (var _len6 = arguments.length, args = new Array(_len6), _key6 = 0; _key6 < _len6; _key6++) { args[_key6] = arguments[_key6]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforePaste).call(_this, ...args); }); this.addHook('afterViewRender', function () { for (var _len7 = arguments.length, args = new Array(_len7), _key7 = 0; _key7 < _len7; _key7++) { args[_key7] = arguments[_key7]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterViewRender).call(_this, ...args); }); this.addHook('afterRender', function () { for (var _len8 = arguments.length, args = new Array(_len8), _key8 = 0; _key8 < _len8; _key8++) { args[_key8] = arguments[_key8]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterRender).call(_this, ...args); }); this.addHook('afterScrollVertically', function () { for (var _len9 = arguments.length, args = new Array(_len9), _key9 = 0; _key9 < _len9; _key9++) { args[_key9] = arguments[_key9]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterScrollVertically).call(_this, ...args); }); this.addHook('afterLanguageChange', function () { for (var _len0 = arguments.length, args = new Array(_len0), _key0 = 0; _key0 < _len0; _key0++) { args[_key0] = arguments[_key0]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterLanguageChange).call(_this, ...args); }); this.addHook('modifyRowHeight', function () { for (var _len1 = arguments.length, args = new Array(_len1), _key1 = 0; _key1 < _len1; _key1++) { args[_key1] = arguments[_key1]; } return _assertClassBrand(_Pagination_brand, _this, _onModifyRowHeight).call(_this, ...args); }); this.addHook('beforeHeightChange', function () { for (var _len10 = arguments.length, args = new Array(_len10), _key10 = 0; _key10 < _len10; _key10++) { args[_key10] = arguments[_key10]; } return _assertClassBrand(_Pagination_brand, _this, _onBeforeHeightChange).call(_this, ...args); }); this.addHook('afterSetTheme', function () { for (var _len11 = arguments.length, args = new Array(_len11), _key11 = 0; _key11 < _len11; _key11++) { args[_key11] = arguments[_key11]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterSetTheme).call(_this, ...args); }); this.addHook('afterDialogShow', function () { for (var _len12 = arguments.length, args = new Array(_len12), _key12 = 0; _key12 < _len12; _key12++) { args[_key12] = arguments[_key12]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterDialogShow).call(_this, ...args); }); this.addHook('beforeDialogHide', function () { for (var _len13 = arguments.length, args = new Array(_len13), _key13 = 0; _key13 < _len13; _key13++) { args[_key13] = arguments[_key13]; } return _assertClassBrand(_Pagination_brand, _this, _onAfterDialogHide).call(_this, ...args); }); this.hot.rowIndexMapper.addLocalHook('cacheUpdated', _classPrivateFieldGet(_onIndexCacheUpdate, this)); super.enablePlugin(); } /** * Updates the plugin state. This method is executed when {@link Core#updateSettings} is invoked. */ updatePlugin() { this.disablePlugin(); this.enablePlugin(); _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); super.updatePlugin(); } /** * Disables the plugin functionality for this Handsontable instance. */ disablePlugin() { this.hot.rowIndexMapper.removeLocalHook('cacheUpdated', _classPrivateFieldGet(_onIndexCacheUpdate, this)).unregisterMap(this.pluginName); _classPrivateFieldGet(_ui, this).destroy(); _classPrivateFieldSet(_ui, this, null); _assertClassBrand(_Pagination_brand, this, _unregisterShortcuts).call(this); super.disablePlugin(); } /** * Gets the pagination current state. Returns an object with the following properties: * - `currentPage`: The current page number. * - `totalPages`: The total number of pages. * - `pageSize`: The page size. * - `pageSizeList`: The list of page sizes. * - `autoPageSize`: Whether the page size is calculated automatically. * - `numberOfRenderedRows`: The number of rendered rows. * - `firstVisibleRowIndex`: The index of the first visible row. * - `lastVisibleRowIndex`: The index of the last visible row. * * @returns {{ * currentPage: number, * totalPages: number, * pageSize: number, * pageSizeList: Array<number | 'auto'>, * autoPageSize: boolean, * numberOfRenderedRows: number, * firstVisibleRowIndex: number, * lastVisibleRowIndex: number * }} */ getPaginationData() { const totalPages = _classPrivateFieldGet(_calcStrategy, this).getTotalPages(); let firstVisibleRowIndex = -1; let lastVisibleRowIndex = -1; const { pageSize, startIndex } = _classPrivateFieldGet(_calcStrategy, this).getState(_classPrivateFieldGet(_currentPage, this)); const countRows = this.hot.countRows(); let visibleCount = 0; for (let rowIndex = startIndex; visibleCount < pageSize; rowIndex++) { if (rowIndex >= countRows) { break; } if (this.hot.rowIndexMapper.isHidden(this.hot.toPhysicalRow(rowIndex))) { // eslint-disable-next-line no-continue continue; } if (firstVisibleRowIndex === -1) { firstVisibleRowIndex = rowIndex; } lastVisibleRowIndex = rowIndex; visibleCount += 1; } return { currentPage: _classPrivateFieldGet(_currentPage, this), totalPages, pageSize, pageSizeList: [...this.getSetting('pageSizeList')], autoPageSize: _classPrivateFieldGet(_pageSize, this) === 'auto', numberOfRenderedRows: this.hot.rowIndexMapper.getRenderableIndexesLength(), firstVisibleRowIndex, lastVisibleRowIndex }; } /** * Allows changing the page for specified page number. * * @param {number} pageNumber The page number to set (from 1 to N). If `0` is passed, it * will be transformed to `1`. * @fires Hooks#beforePageChange * @fires Hooks#afterPageChange */ setPage(pageNumber) { const oldPage = _classPrivateFieldGet(_currentPage, this); const shouldProceed = this.hot.runHooks('beforePageChange', oldPage, pageNumber); if (shouldProceed === false) { return; } _classPrivateFieldSet(_currentPage, this, pageNumber); _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); this.hot.scrollViewportTo({ row: 0 }); this.hot.runHooks('afterPageChange', oldPage, _classPrivateFieldGet(_currentPage, this)); this.hot.view.adjustElementsSize(); this.hot.render(); } /** * Resets the current page to the initial page (`initialValue`) defined in the settings. */ resetPage() { this.setPage(this.getSetting('initialPage')); } /** * Changes the page size for the pagination. The method recalculates the state based * on the new page size and re-renders the table. If `'auto'` is passed, the plugin will * calculate the page size based on the viewport size and row heights to make sure * that there will be no vertical scrollbar in the table. * * @param {number | 'auto'} pageSize The page size to set. * @fires Hooks#beforePageSizeChange * @fires Hooks#afterPageSizeChange */ setPageSize(pageSize) { var _this$hot$getPlugin2; const oldPageSize = _classPrivateFieldGet(_pageSize, this); const shouldProceed = this.hot.runHooks('beforePageSizeChange', oldPageSize, pageSize); if (shouldProceed === false) { return; } if (pageSize === 'auto' && !((_this$hot$getPlugin2 = this.hot.getPlugin('autoRowSize')) !== null && _this$hot$getPlugin2 !== void 0 && _this$hot$getPlugin2.enabled)) { (0, _console.warn)(AUTO_PAGE_SIZE_WARNING); } _classPrivateFieldSet(_calcStrategy, this, (0, _strategies.createPaginatorStrategy)(pageSize === 'auto' ? 'auto' : 'fixed')); _classPrivateFieldSet(_pageSize, this, pageSize); _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); this.hot.runHooks('afterPageSizeChange', oldPageSize, _classPrivateFieldGet(_pageSize, this)); this.hot.view.adjustElementsSize(); this.hot.render(); } /** * Resets the page size to the initial value (`pageSize`) defined in the settings. */ resetPageSize() { this.setPageSize(this.getSetting('pageSize')); } /** * Resets the pagination state to the initial values defined in the settings. */ resetPagination() { this.resetPage(); this.resetPageSize(); _assertClassBrand(_Pagination_brand, this, _updateSectionsVisibilityState).call(this); } /** * Switches the page to the next one. */ nextPage() { this.setPage(_classPrivateFieldGet(_currentPage, this) + 1); } /** * Switches the page to the previous one. */ prevPage() { this.setPage(_classPrivateFieldGet(_currentPage, this) - 1); } /** * Switches the page to the first one. */ firstPage() { this.setPage(1); } /** * Switches the page to the last one. */ lastPage() { this.setPage(_classPrivateFieldGet(_calcStrategy, this).getTotalPages()); } /** * Checks, based on the current internal state, if there is a previous page. * * @returns {boolean} */ hasPreviousPage() { return _classPrivateFieldGet(_currentPage, this) > 1; } /** * Checks, based on the current internal state, if there is a next page. * * @returns {boolean} */ hasNextPage() { return _classPrivateFieldGet(_currentPage, this) < _classPrivateFieldGet(_calcStrategy, this).getTotalPages(); } /** * Gets the visual data for the current page. The returned data may be longer than the defined * page size as the data may contain hidden rows (rows that are not rendered in the table). * * @returns {Array<Array>} Returns the data for the current page. */ getCurrentPageData() { const { firstVisibleRowIndex, lastVisibleRowIndex } = this.getPaginationData(); if (firstVisibleRowIndex === -1 || lastVisibleRowIndex === -1) { return []; } return this.hot.getData(firstVisibleRowIndex, 0, lastVisibleRowIndex, this.hot.countCols() - 1); } /** * Shows the page size section in the pagination UI. * * @fires Hooks#afterPageSizeVisibilityChange */ showPageSizeSection() { _classPrivateFieldGet(_ui, this).setPageSizeSectionVisibility(true); this.hot.runHooks('afterPageSizeVisibilityChange', true); } /** * Hides the page size section in the pagination UI. * * @fires Hooks#afterPageSizeVisibilityChange */ hidePageSizeSection() { _classPrivateFieldGet(_ui, this).setPageSizeSectionVisibility(false); this.hot.runHooks('afterPageSizeVisibilityChange', false); } /** * Shows the page counter section in the pagination UI. * * @fires Hooks#afterPageCounterVisibilityChange */ showPageCounterSection() { _classPrivateFieldGet(_ui, this).setCounterSectionVisibility(true); this.hot.runHooks('afterPageCounterVisibilityChange', true); } /** * Hides the page counter section in the pagination UI. * * @fires Hooks#afterPageCounterVisibilityChange */ hidePageCounterSection() { _classPrivateFieldGet(_ui, this).setCounterSectionVisibility(false); this.hot.runHooks('afterPageCounterVisibilityChange', false); } /** * Shows the page navigation section in the pagination UI. * * @fires Hooks#afterPageNavigationVisibilityChange */ showPageNavigationSection() { _classPrivateFieldGet(_ui, this).setNavigationSectionVisibility(true); this.hot.runHooks('afterPageNavigationVisibilityChange', true); } /** * Hides the page navigation section in the pagination UI. * * @fires Hooks#afterPageNavigationVisibilityChange */ hidePageNavigationSection() { _classPrivateFieldGet(_ui, this).setNavigationSectionVisibility(false); this.hot.runHooks('afterPageNavigationVisibilityChange', false); } /** * Updates the visibility state of the pagination sections based on the current settings. */ /** * Destroys the plugin instance. */ destroy() { var _classPrivateFieldGet2; _classPrivateFieldSet(_pagedRowsMap, this, null); _classPrivateFieldSet(_calcStrategy, this, null); (_classPrivateFieldGet2 = _classPrivateFieldGet(_ui, this)) === null || _classPrivateFieldGet2 === void 0 || _classPrivateFieldGet2.destroy(); _classPrivateFieldSet(_ui, this, null); super.destroy(); } } exports.Pagination = Pagination; function _registerEvents() { // TODO: move to general focus manager module this.eventManager.addEventListener(this.hot.rootDocument, 'mouseup', event => { const container = _classPrivateFieldGet(_ui, this).getContainer(); if (!container.contains(event.target) && this.hot.getShortcutManager().getActiveContextName() === SHORTCUTS_CONTEXT_NAME) { _classPrivateFieldGet(_focusDetector, this).activate(); _classPrivateFieldGet(_focusController, this).clear(); this.hot.getShortcutManager().setActiveContextName('grid'); } }); } /** * Register shortcuts responsible for navigating through the pagination. */ function _registerShortcuts() { var _manager$getContext; const manager = this.hot.getShortcutManager(); const pluginContext = (_manager$getContext = manager.getContext(SHORTCUTS_CONTEXT_NAME)) !== null && _manager$getContext !== void 0 ? _manager$getContext : manager.addContext(SHORTCUTS_CONTEXT_NAME, 'global'); pluginContext.addShortcut({ keys: [['Shift', 'Tab'], ['Tab']], preventDefault: false, callback: event => { let previousIndex = _classPrivateFieldGet(_focusController, this).getCurrentPage(); if (event.shiftKey) { _classPrivateFieldGet(_focusController, this).toPreviousItem(); const currentPage = _classPrivateFieldGet(_focusController, this).getCurrentPage(); if (currentPage >= previousIndex) { _assertClassBrand(_Pagination_brand, this, _unFocusPagination).call(this); return; } previousIndex = currentPage; } else { _classPrivateFieldGet(_focusController, this).toNextItem(); const currentPage = _classPrivateFieldGet(_focusController, this).getCurrentPage(); if (currentPage <= previousIndex) { _assertClassBrand(_Pagination_brand, this, _unFocusPagination).call(this); return; } previousIndex = currentPage; } event.preventDefault(); }, group: SHORTCUTS_GROUP }); } /** * Unregister shortcuts responsible for navigating through the pagination. */ function _unregisterShortcuts() { const shortcutManager = this.hot.getShortcutManager(); const pluginContext = shortcutManager.getContext(SHORTCUTS_CONTEXT_NAME); pluginContext.removeShortcutsByGroup(SHORTCUTS_GROUP); } function _updateSectionsVisibilityState() { if (this.getSetting('showPageSize')) { this.showPageSizeSection(); } else { this.hidePageSizeSection(); } if (this.getSetting('showCounter')) { this.showPageCounterSection(); } else { this.hidePageCounterSection(); } if (this.getSetting('showNavigation')) { this.showPageNavigationSection(); } else { this.hidePageNavigationSection(); } } /** * Applies the current pagination state to the internal index mapper and updates the UI. */ function _computeAndApplyState() { _classPrivateFieldSet(_internalExecutionCall, this, true); _classPrivateFieldGet(_pagedRowsMap, this).clear(); const renderableIndexes = this.hot.rowIndexMapper.getRenderableIndexes(); const renderableRowsLength = renderableIndexes.length; const { stylesHandler } = this.hot; _classPrivateFieldGet(_calcStrategy, this).calculate({ pageSize: _classPrivateFieldGet(_pageSize, this), totalItems: renderableRowsLength, viewportSizeProvider: () => { const { view } = this.hot; if (view.isVerticallyScrollableByWindow()) { const bodyStyle = getComputedStyle(this.hot.rootDocument.body); const margin = Number.parseInt(bodyStyle.marginTop, 10) + Number.parseInt(bodyStyle.marginBottom, 10); const columnHeaderHeight = this.hot.hasColHeaders() ? view._wt.wtTable.getColumnHeaderHeight() : 0; const paginationContainerHeight = _classPrivateFieldGet(_ui, this).getHeight(); const workspaceHeight = view.getWorkspaceHeight(); return workspaceHeight - paginationContainerHeight - columnHeaderHeight - margin; } const scrollbarWidth = view.hasHorizontalScroll() ? (0, _element.getScrollbarWidth)() : 0; return view.getViewportHeight() - scrollbarWidth; }, itemsSizeProvider: () => { const defaultRowHeight = stylesHandler.getDefaultRowHeight(); const rowHeights = this.hot.rowIndexMapper.getRenderableIndexes().map(physicalIndex => { var _this$hot$getRowHeigh; return (_this$hot$getRowHeigh = this.hot.getRowHeight(this.hot.toVisualRow(physicalIndex))) !== null && _this$hot$getRowHeigh !== void 0 ? _this$hot$getRowHeigh : defaultRowHeight; }); return rowHeights; } }); const totalPages = _classPrivateFieldGet(_calcStrategy, this).getTotalPages(); _classPrivateFieldSet(_currentPage, this, (0, _number.clamp)(_classPrivateFieldGet(_currentPage, this), 1, totalPages)); if (renderableIndexes.length > 0) { const { startIndex, pageSize } = _classPrivateFieldGet(_calcStrategy, this).getState(_classPrivateFieldGet(_currentPage, this)); renderableIndexes.splice(startIndex, pageSize); } if (renderableIndexes.length > 0) { this.hot.batchExecution(() => { renderableIndexes.forEach(index => _classPrivateFieldGet(_pagedRowsMap, this).setValueAtIndex(index, true)); }, true); } else { this.hot.rowIndexMapper.updateCache(true); } _classPrivateFieldSet(_internalExecutionCall, this, false); const paginationData = this.getPaginationData(); _classPrivateFieldGet(_ui, this).updateState({ ...paginationData, totalRenderedRows: renderableRowsLength }); if ((this.getSetting('showPageSize') || this.getSetting('showNavigation')) && paginationData.totalPages > 1) { _classPrivateFieldGet(_focusDetector, this).activate(); } else { _classPrivateFieldGet(_focusDetector, this).deactivate(); } } /** * Based on the external factors (like the scroll position of the table, size etc.) it computes * the need for the top border of the pagination UI container. * * @returns {boolean} Returns `true` if the pagination UI should have a top border, `false` otherwise. */ function _computeNeedsBorder() { if (!this.hot.view) { return true; } const { view } = this.hot; if (view.isVerticallyScrollableByWindow()) { return false; } if (view.hasHorizontalScroll() || view.getTableHeight() < view.getWorkspaceHeight()) { return true; } const { lastVisibleRowIndex } = this.getPaginationData(); return view.getLastFullyVisibleRow() !== lastVisibleRowIndex; } /** * Called before the selection of columns or all table is made. It modifies the selection rows range * to the range of the current page. * * @param {CellCoords} from Starting cell coordinates. * @param {CellCoords} to Ending cell coordinates. */ function _onBeforeSelectAllRows(from, to) { const { firstVisibleRowIndex, lastVisibleRowIndex } = this.getPaginationData(); if (_classPrivateFieldGet(_currentPage, this) > 1 || from.row >= 0) { from.row = firstVisibleRowIndex; } to.row = lastVisibleRowIndex; } /** * Called before the selection end is fired. It modifies the selection to the range of * the current page. * * @param {CellCoords} coords Ending cell coordinates. */ function _onBeforeSetRangeEnd(coords) { if (this.hot.selection.isSelectedByColumnHeader()) { const { lastVisibleRowIndex } = this.getPaginationData(); coords.row = lastVisibleRowIndex; } } /** * The hook corrects the focus position (before drawing it) after the selection was made * (the visual coordinates was collected). */ function _onBeforeSelectionHighlightSet() { if (!this.hot.getSettings().navigableHeaders) { return; } const selectedRange = this.hot.getSelectedRangeLast(); if (!selectedRange.isSingle()) { const { highlight } = selectedRange; highlight.row = (0, _number.clamp)(highlight.row, selectedRange.getTopStartCorner().row, selectedRange.getBottomEndCorner().row); } } /** * Called before the paste operation is performed. It removes the rows that are not visible * from the pasted data. * * @param {Array} pastedData The data that was pasted. * @param {Array<{startRow: number, endRow: number}>} ranges The ranges of the pasted data. * @returns {boolean} Returns `false` to prevent the paste operation. */ function _onBeforePaste(pastedData, ranges) { const { firstVisibleRowIndex, lastVisibleRowIndex } = this.getPaginationData(); if (firstVisibleRowIndex === -1 || lastVisibleRowIndex === -1) { return false; } ranges.forEach(_ref => { let { startRow } = _ref; if (pastedData.length === 0) { return; } const rowsToRemove = Math.min(pastedData.length - (lastVisibleRowIndex - startRow + 1), pastedData.length); pastedData.splice(0, rowsToRemove); }); } /** * Called when the row height is modified. It adds 1px border top compensation for * the first row of the each page to make sure that the table's hider element * height is correctly calculated. * * @param {number | undefined} height Row height. * @param {number} row Visual row index. * @returns {number} */ function _onModifyRowHeight(height, row) { if (height === undefined || !_classPrivateFieldGet(_calcStrategy, this).getState(_classPrivateFieldGet(_currentPage, this))) { return; } const { firstVisibleRowIndex } = this.getPaginationData(); if (row !== 0 && row === firstVisibleRowIndex) { height += 1; // 1px border top compensation for the first row of the page. } return height; } /** * Called after the view is rendered. It recalculates the pagination state only when * the `pageSize` is set to `'auto'`. In this case, the plugin will compute the * page size based on the viewport size and row heights for each render cycle to make sure * that each row resize, multiline cell value, or other factors that may affect the * rows height will be taken into account. */ function _onAfterViewRender() { if (_classPrivateFieldGet(_pageSize, this) !== 'auto' || _classPrivateFieldGet(_internalRenderCall, this)) { _classPrivateFieldSet(_internalRenderCall, this, false); return; } _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); _classPrivateFieldSet(_internalRenderCall, this, true); // there is need to re-render the table as on the initial the engine returns incorrect // values about table and column header sizes. this.hot.view.adjustElementsSize(); this.hot.render(); } /** * Called after the rendering of the table is completed. It updates the width of * the pagination container to the same size as the table. */ function _onAfterRender() { const { view } = this.hot; const width = view.isHorizontallyScrollableByWindow() ? view.getTotalTableWidth() : view.getWorkspaceWidth(); _classPrivateFieldGet(_ui, this).updateWidth(width).refreshBorderState(); } /** * Called before the height of the table is changed. It adjusts the table height to fit the pagination container * in declared height. * * @param {number|string} height Table height. * @returns {string} Returns the new table height. */ function _onBeforeHeightChange(height) { if (this.getSetting('uiContainer')) { return height; } const isPixelValue = typeof height === 'number' || typeof height === 'string' && /^\d+$/.test(height) || typeof height === 'string' && height.endsWith('px'); if (!isPixelValue) { return height; } const heightValue = typeof height === 'string' && height.endsWith('px') ? height : `${height}px`; return `calc(${heightValue} - ${_classPrivateFieldGet(_ui, this).getHeight()}px)`; } /** * Called after the initialization of the plugin. It computes the initial state of the pagination. */ function _onInit() { if (_classPrivateFieldGet(_pageSize, this) === 'auto') { return; } _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); } /** * Called after the vertical scrolling of the table is completed. It refreshes * the border state of the pagination UI. */ function _onAfterScrollVertically() { _classPrivateFieldGet(_ui, this).refreshBorderState(); } /** * Called after the language change. It recomputes the pagination state which updates the UI. */ function _onAfterLanguageChange() { _assertClassBrand(_Pagination_brand, this, _computeAndApplyState).call(this); } /** * Called after the theme is set. It updates the theme of the pagination container. * * @param {string | undefined} themeName The name of the theme to use. */ function _onAfterSetTheme(themeName) { _classPrivateFieldGet(_ui, this).updateTheme(themeName); } /** * Unfocuses the pagination and sets the active context for the shortcuts. */ function _unFocusPagination() { _classPrivateFieldGet(_focusDetector, this).activate(); _classPrivateFieldGet(_focusController, this).clear(); this.hot.unlisten(); this.hot.getShortcutManager().setActiveContextName('grid'); } /** * Called after the dialog is shown. It sets the active context for the shortcuts. */ function _onAfterDialogShow() { _classPrivateFieldGet(_focusDetector, this).deactivate(); } /** * Called after the dialog is hidden. It sets the active context for the shortcuts. */ function _onAfterDialogHide() { _classPrivateFieldGet(_focusDetector, this).activate(); }