handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
1,034 lines (1,008 loc) • 39.8 kB
JavaScript
"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();
}