handsontable
Version:
Handsontable is a JavaScript Data Grid available for React, Angular and Vue.
449 lines (435 loc) • 19.1 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");
var _templateLiteralTag = require("../../helpers/templateLiteralTag");
var _object = require("../../helpers/object");
var _localHooks = _interopRequireDefault(require("../../mixins/localHooks"));
var C = _interopRequireWildcard(require("../../i18n/constants"));
var _element = require("../../helpers/dom/element");
var _a11y = require("../../helpers/a11y");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
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 TEMPLATE = `
<div data-ref="container" class="ht-pagination handsontable">
<div class="ht-pagination__inner">
<div data-ref="pageSizeSection" class="ht-page-size-section">
<span data-ref="pageSizeLabel" class="ht-page-size-section__label"></span>
<div class="ht-page-size-section__select-wrapper">
<select data-ref="pageSizeSelect" name="pageSize" data-hot-input></select>
</div>
</div>
<div data-ref="pageCounterSection" class="ht-page-counter-section"></div>
<nav data-ref="pageNavSection" class="ht-page-navigation-section">
<button data-ref="first" class="ht-page-navigation-section__button ht-page-first"></button>
<button data-ref="prev" class="ht-page-navigation-section__button ht-page-prev"></button>
<span data-ref="pageNavLabel" class="ht-page-navigation-section__label"></span>
<button data-ref="next" class="ht-page-navigation-section__button ht-page-next"></button>
<button data-ref="last" class="ht-page-navigation-section__button ht-page-last"></button>
</nav>
</div>
</div>
`;
/**
* PaginationUI is a UI component that renders and manages pagination controls.
* It handles user interactions (navigation and page size changes), and exposes methods to
* toggle visibility of pagination sections and update the state of the pagination controls.
*
* @private
* @class PaginationUI
*/
var _rootElement = /*#__PURE__*/new WeakMap();
var _uiContainer = /*#__PURE__*/new WeakMap();
var _isRtl = /*#__PURE__*/new WeakMap();
var _refs = /*#__PURE__*/new WeakMap();
var _themeName = /*#__PURE__*/new WeakMap();
var _phraseTranslator = /*#__PURE__*/new WeakMap();
var _shouldHaveBorder = /*#__PURE__*/new WeakMap();
var _a11yAnnouncer = /*#__PURE__*/new WeakMap();
var _PaginationUI_brand = /*#__PURE__*/new WeakSet();
class PaginationUI {
constructor(_ref) {
let {
rootElement,
uiContainer,
isRtl,
themeName,
phraseTranslator,
shouldHaveBorder,
a11yAnnouncer
} = _ref;
/**
* Updates the visibility of the pagination container based on the visibility of its sections.
*/
_classPrivateMethodInitSpec(this, _PaginationUI_brand);
/**
* The root element where the pagination UI will be installed.
*
* @type {HTMLElement}
*/
_classPrivateFieldInitSpec(this, _rootElement, void 0);
/**
* The container element where the pagination UI will be installed.
* If not provided, the pagination container will be injected after the root element.
*
* @type {HTMLElement}
*/
_classPrivateFieldInitSpec(this, _uiContainer, void 0);
/**
* Indicates if the UI is in RTL mode.
*
* @type {boolean}
*/
_classPrivateFieldInitSpec(this, _isRtl, false);
/**
* The references to the UI elements.
*
* @type {object}
*/
_classPrivateFieldInitSpec(this, _refs, void 0);
/**
* The name of the current theme.
*
* @type {string | undefined}
*/
_classPrivateFieldInitSpec(this, _themeName, void 0);
/**
* A function to translate phrases used in the UI.
*
* @type {function(string): string}
*/
_classPrivateFieldInitSpec(this, _phraseTranslator, void 0);
/**
* A function that determines whether the pagination should have a border.
*
* @type {function(): void}
*/
_classPrivateFieldInitSpec(this, _shouldHaveBorder, void 0);
/**
* A function allowing to announce accessibility messages.
*
* @type {function(string): void}
*/
_classPrivateFieldInitSpec(this, _a11yAnnouncer, void 0);
_classPrivateFieldSet(_rootElement, this, rootElement);
_classPrivateFieldSet(_uiContainer, this, uiContainer);
_classPrivateFieldSet(_isRtl, this, isRtl);
_classPrivateFieldSet(_themeName, this, themeName);
_classPrivateFieldSet(_phraseTranslator, this, phraseTranslator);
_classPrivateFieldSet(_shouldHaveBorder, this, shouldHaveBorder);
_classPrivateFieldSet(_a11yAnnouncer, this, a11yAnnouncer);
this.install();
}
/**
* Creates the pagination UI elements and sets up event listeners.
*/
install() {
var _classPrivateFieldGet2;
if ((_classPrivateFieldGet2 = _classPrivateFieldGet(_refs, this)) !== null && _classPrivateFieldGet2 !== void 0 && _classPrivateFieldGet2.container) {
return;
}
const elements = (0, _templateLiteralTag.html)`${TEMPLATE}`;
const {
container,
first,
prev,
next,
last,
pageSizeSelect
} = elements.refs;
_classPrivateFieldSet(_refs, this, elements.refs);
container.setAttribute('dir', _classPrivateFieldGet(_isRtl, this) ? 'rtl' : 'ltr');
const isDisabled = event => event.currentTarget.disabled;
const addClickListener = (eventName, element, callback) => {
element.addEventListener(eventName, event => {
if (!isDisabled(event)) {
callback();
}
});
};
addClickListener('click', first, () => this.runLocalHooks('firstPageClick'));
addClickListener('focus', first, () => this.runLocalHooks('focus', first));
addClickListener('click', prev, () => this.runLocalHooks('prevPageClick'));
addClickListener('focus', prev, () => this.runLocalHooks('focus', prev));
addClickListener('click', next, () => this.runLocalHooks('nextPageClick'));
addClickListener('focus', next, () => this.runLocalHooks('focus', next));
addClickListener('click', last, () => this.runLocalHooks('lastPageClick'));
addClickListener('focus', last, () => this.runLocalHooks('focus', last));
addClickListener('focus', pageSizeSelect, () => this.runLocalHooks('focus', pageSizeSelect));
pageSizeSelect.addEventListener('change', () => {
const value = pageSizeSelect.value === 'auto' ? 'auto' : Number.parseInt(pageSizeSelect.value, 10);
this.runLocalHooks('pageSizeChange', value);
});
this.setCounterSectionVisibility(false);
this.setNavigationSectionVisibility(false);
this.setPageSizeSectionVisibility(false);
if (_classPrivateFieldGet(_uiContainer, this)) {
_classPrivateFieldGet(_uiContainer, this).appendChild(elements.fragment);
(0, _element.addClass)(container, [_classPrivateFieldGet(_themeName, this), 'handsontable']);
} else {
_classPrivateFieldGet(_rootElement, this).after(elements.fragment);
}
}
/**
* Gets the pagination element.
*
* @returns {HTMLElement} The pagination element.
*/
getContainer() {
return _classPrivateFieldGet(_refs, this).container;
}
/**
* Gets the focusable elements.
*
* @returns {HTMLElement[]} The focusable elements.
*/
getFocusableElements() {
const {
first,
prev,
next,
last,
pageSizeSelect
} = _classPrivateFieldGet(_refs, this);
return [pageSizeSelect, first, prev, next, last].filter(element => !element.disabled);
}
/**
* Updates the width of the pagination container.
*
* @param {number} width The new width of the pagination container.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
updateWidth(width) {
_classPrivateFieldGet(_refs, this).container.style.width = `${width}px`;
return this;
}
/**
* Updates the theme of the pagination container.
*
* @param {string | false | undefined} themeName The name of the theme to use.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
updateTheme(themeName) {
_classPrivateFieldSet(_themeName, this, themeName);
if (_classPrivateFieldGet(_uiContainer, this)) {
const {
container
} = _classPrivateFieldGet(_refs, this);
(0, _element.removeClass)(container, /ht-theme-.*/g);
if (_classPrivateFieldGet(_themeName, this)) {
(0, _element.addClass)(container, _classPrivateFieldGet(_themeName, this));
}
}
return this;
}
/**
* Gets the height of the pagination container element.
*
* @returns {number}
*/
getHeight() {
return _classPrivateFieldGet(_refs, this).container.offsetHeight;
}
/**
* Refreshes the border state of the pagination container based on the external condition.
*
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
refreshBorderState() {
const {
container
} = _classPrivateFieldGet(_refs, this);
if (_classPrivateFieldGet(_uiContainer, this) || _classPrivateFieldGet(_shouldHaveBorder, this).call(this)) {
(0, _element.addClass)(container, 'ht-pagination--bordered');
} else {
(0, _element.removeClass)(container, 'ht-pagination--bordered');
}
return this;
}
/**
* Updates the state of the pagination UI.
*
* @param {object} state The pagination state.
* @param {number} state.currentPage The current page number.
* @param {number} state.totalPages The total number of pages.
* @param {number} state.firstVisibleRowIndex The index of the first visible row on the current page.
* @param {number} state.lastVisibleRowIndex The index of the last visible row on the current page.
* @param {number} state.totalRenderedRows The total number of renderable rows.
* @param {Array<number | 'auto'>} state.pageSizeList The list of available page sizes.
* @param {number} state.pageSize The current page size.
* @param {boolean} state.autoPageSize Indicates if the page size is set to 'auto'.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
updateState(_ref2) {
let {
currentPage,
totalPages,
firstVisibleRowIndex,
lastVisibleRowIndex,
totalRenderedRows,
pageSizeList,
pageSize,
autoPageSize
} = _ref2;
const {
first,
prev,
next,
last,
pageCounterSection,
pageNavSection,
pageNavLabel,
pageSizeSelect,
pageSizeLabel
} = _classPrivateFieldGet(_refs, this);
const counterSectionText = _classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_COUNTER_SECTION, {
start: firstVisibleRowIndex + 1,
end: lastVisibleRowIndex + 1,
total: totalRenderedRows
});
const navLabelText = _classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_NAV_SECTION, {
currentPage,
totalPages
});
const pageSizeLabelText = _classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_PAGE_SIZE_SECTION);
pageCounterSection.textContent = counterSectionText;
pageNavLabel.textContent = navLabelText;
pageSizeSelect.textContent = '';
pageSizeLabel.textContent = `${pageSizeLabelText}:`;
(0, _element.setAttribute)(pageNavSection, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_SECTION))]]);
(0, _element.setAttribute)(pageSizeSelect, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_PAGE_SIZE_SECTION))], ...[(0, _a11y.A11Y_TABINDEX)(-1)]]);
_classPrivateFieldGet(_a11yAnnouncer, this).call(this, navLabelText);
this.refreshBorderState();
pageSizeList.forEach(pageSizeItem => {
const label = pageSizeItem === 'auto' ? _classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_PAGE_SIZE_AUTO) : pageSizeItem;
const option = new Option(label, pageSizeItem);
if (autoPageSize && pageSizeItem === 'auto' || !autoPageSize && pageSizeItem === pageSize) {
option.selected = true;
}
pageSizeSelect.add(option);
});
const isFirstPage = currentPage === 1;
const isLastPage = currentPage === totalPages;
if (pageNavSection.style.display !== 'none') {
const activeElement = _classPrivateFieldGet(_rootElement, this).ownerDocument.activeElement;
if (isFirstPage) {
(0, _element.addClass)(first, 'ht-page-navigation-section__button--disabled');
(0, _element.addClass)(prev, 'ht-page-navigation-section__button--disabled');
first.disabled = true;
prev.disabled = true;
} else {
(0, _element.removeClass)(first, 'ht-page-navigation-section__button--disabled');
(0, _element.removeClass)(prev, 'ht-page-navigation-section__button--disabled');
first.disabled = false;
prev.disabled = false;
}
if (isLastPage) {
(0, _element.addClass)(next, 'ht-page-navigation-section__button--disabled');
(0, _element.addClass)(last, 'ht-page-navigation-section__button--disabled');
next.disabled = true;
last.disabled = true;
} else {
(0, _element.removeClass)(next, 'ht-page-navigation-section__button--disabled');
(0, _element.removeClass)(last, 'ht-page-navigation-section__button--disabled');
next.disabled = false;
last.disabled = false;
}
if ([first, prev, next, last].includes(activeElement)) {
if (prev.disabled) {
next.focus();
} else if (next.disabled) {
prev.focus();
}
}
}
(0, _element.setAttribute)(first, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_FIRST_PAGE))], ...[(0, _a11y.A11Y_DISABLED)(isFirstPage)], ...[(0, _a11y.A11Y_TABINDEX)(-1)]]);
(0, _element.setAttribute)(prev, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_PREV_PAGE))], ...[(0, _a11y.A11Y_DISABLED)(isFirstPage)], ...[(0, _a11y.A11Y_TABINDEX)(-1)]]);
(0, _element.setAttribute)(next, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_NEXT_PAGE))], ...[(0, _a11y.A11Y_DISABLED)(isLastPage)], ...[(0, _a11y.A11Y_TABINDEX)(-1)]]);
(0, _element.setAttribute)(last, [...[(0, _a11y.A11Y_LABEL)(_classPrivateFieldGet(_phraseTranslator, this).call(this, C.PAGINATION_LAST_PAGE))], ...[(0, _a11y.A11Y_DISABLED)(isLastPage)], ...[(0, _a11y.A11Y_TABINDEX)(-1)]]);
return this;
}
/**
* Sets the visibility of the page size section.
*
* @param {boolean} isVisible True to show the page size section, false to hide it.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
setPageSizeSectionVisibility(isVisible) {
const {
pageSizeSection,
pageSizeSelect
} = _classPrivateFieldGet(_refs, this);
pageSizeSection.style.display = isVisible ? '' : 'none';
pageSizeSelect.disabled = !isVisible;
_assertClassBrand(_PaginationUI_brand, this, _updateContainerVisibility).call(this);
return this;
}
/**
* Sets the visibility of the page counter section.
*
* @param {boolean} isVisible True to show the page size section, false to hide it.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
setCounterSectionVisibility(isVisible) {
_classPrivateFieldGet(_refs, this).pageCounterSection.style.display = isVisible ? '' : 'none';
_assertClassBrand(_PaginationUI_brand, this, _updateContainerVisibility).call(this);
return this;
}
/**
* Sets the visibility of the page navigation section.
*
* @param {boolean} isVisible True to show the page size section, false to hide it.
* @returns {PaginationUI} The instance of the PaginationUI for method chaining.
*/
setNavigationSectionVisibility(isVisible) {
const {
pageNavSection,
first,
prev,
next,
last
} = _classPrivateFieldGet(_refs, this);
pageNavSection.style.display = isVisible ? '' : 'none';
first.disabled = !isVisible;
prev.disabled = !isVisible;
next.disabled = !isVisible;
last.disabled = !isVisible;
_assertClassBrand(_PaginationUI_brand, this, _updateContainerVisibility).call(this);
return this;
}
/**
* Removes the pagination UI elements from the DOM and clears the refs.
*/
destroy() {
var _classPrivateFieldGet3;
(_classPrivateFieldGet3 = _classPrivateFieldGet(_refs, this)) === null || _classPrivateFieldGet3 === void 0 || _classPrivateFieldGet3.container.remove();
_classPrivateFieldSet(_refs, this, null);
}
}
exports.PaginationUI = PaginationUI;
function _updateContainerVisibility() {
const {
container,
pageSizeSection,
pageCounterSection,
pageNavSection
} = _classPrivateFieldGet(_refs, this);
const isSectionVisible = pageSizeSection.style.display !== 'none' || pageCounterSection.style.display !== 'none' || pageNavSection.style.display !== 'none';
// adds or removes the corner around the Handsontable root element
if (!_classPrivateFieldGet(_uiContainer, this)) {
if (isSectionVisible) {
(0, _element.addClass)(_classPrivateFieldGet(_rootElement, this).querySelector('.ht-wrapper'), 'htPagination');
} else {
(0, _element.removeClass)(_classPrivateFieldGet(_rootElement, this).querySelector('.ht-wrapper'), 'htPagination');
}
}
container.style.display = isSectionVisible ? '' : 'none';
}
(0, _object.mixin)(PaginationUI, _localHooks.default);