UNPKG

@hashicorp/design-system-components

Version:
156 lines (153 loc) 7.7 kB
import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { assert } from '@ember/debug'; import { service } from '@ember/service'; import { HdsPaginationDirectionValues } from '../types.js'; import HdsPaginationControlArrow from '../nav/arrow.js'; import HdsPaginationSizeSelector from '../size-selector/index.js'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@ember/component'; import { g, i } from 'decorator-transforms/runtime'; /** * Copyright IBM Corp. 2021, 2025 * SPDX-License-Identifier: MPL-2.0 */ // for context about the decision to use these values, see: // https://hashicorp.slack.com/archives/C03A0N1QK8S/p1673546329082759 const DEFAULT_PAGE_SIZES = [10, 30, 50]; class HdsPaginationCompact extends Component { static { g(this.prototype, "hdsIntl", [service]); } #hdsIntl = (i(this, "hdsIntl"), void 0); static { g(this.prototype, "_currentPageSize", [tracked]); } #_currentPageSize = (i(this, "_currentPageSize"), void 0); // This private variable is used to differentiate between // "uncontrolled" component (where the state is handled internally) and // "controlled" component (where the state is handled externally, by the consumer's code). // In the first case, the variable stores the internal state of the component at any moment, // and its value is updated internally according to the user's interaction with the component. // In the second case, the variable stores *only* the initial state of the component (coming from the arguments) // at rendering time, but from that moment on it's not updated anymore, no matter what interaction the user // has with the component (the state is controlled externally, eg. via query parameters) static { g(this.prototype, "_isControlled", [tracked]); } #_isControlled = (i(this, "_isControlled"), void 0); showLabels = this.args.showLabels ?? true; showSizeSelector = this.args.showSizeSelector ?? false; constructor(owner, args) { super(owner, args); const { queryFunction } = this.args; // This component works in two different ways, depending if we need to support // routing through links (`LinkTo`) for the "navigation controls", or not. // If there's no routing then the component behaves as "uncontrolled" // (the state updates are handled by its internal logic). // If instead the component needs to update the routing (and we infer this via the "query" arguments) // then the component behaves as "controlled", where the state is // initialized and updated using the arguments passed to it. if (queryFunction === undefined) { this._isControlled = false; } else { assert('@model, @models, or @route for "Hds::Pagination::Compact" must be provided when using the `@queryFunction` argument', this.args.model !== undefined || this.args.models !== undefined || this.args.route !== undefined); assert('@queryFunction for "Hds::Pagination::Compact" must be a function', typeof queryFunction === 'function'); this._isControlled = true; } // we assert that `this.pageSizes` will always be an array with at least one item this._currentPageSize = this.args.currentPageSize ?? this.pageSizes[0]; } get ariaLabel() { return this.args.ariaLabel ?? this.hdsIntl.t('hds.components.pagination.compact.aria-label', { default: 'Pagination' }); } // This very specific `get/set` pattern is used to handle the two different use cases of the component // being "controlled" (when it has routing, meaning it needs to support pagination controls as links/`LinkTo`) // vs being "uncontrolled" (see comments above for details). // // If it has routing (and so it's "controlled"), than the value ("state") of the `currentPageSize` variable // is *always* determined by the controller via arguments (most of the times, connected to query parameters in the URL). // For this reason the "get" method always returns the value from the `args`, // while the "set" method never updates the private internal state (_variable). // // If instead it doesn't have routing (and so it's "uncontrolled") than the value ("state") of the `currentPageSize` variables // is *always* determined by the component's internal logic (and updated according to the user interaction with it). // For this reason the "get" and "set" methods always read from or write to the private internal state (_variable). get currentPageSize() { if (this._isControlled) { return this.args.currentPageSize; } else { return this._currentPageSize; } } set currentPageSize(value) { if (this._isControlled) ; else { this._currentPageSize = value; } } get pageSizes() { const { pageSizes = DEFAULT_PAGE_SIZES } = this.args; assert( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `pageSizes argument must be an array. Received: ${pageSizes}`, Array.isArray(pageSizes) === true && pageSizes.length > 0); return pageSizes; } buildQueryParamsObject(page, pageSize) { if (this._isControlled) { // if the component is controlled, we can assert that the queryFunction is defined return this.args.queryFunction(page, pageSize); } else { return {}; } } get routing() { const routing = { route: this.args.route ?? undefined, model: this.args.model ?? undefined, models: this.args.models ?? undefined, replace: this.args.replace ?? undefined }; // the "query" is dynamic and needs to be calculated if (this._isControlled) { routing.queryPrev = this.buildQueryParamsObject(HdsPaginationDirectionValues.Prev, this.currentPageSize); routing.queryNext = this.buildQueryParamsObject(HdsPaginationDirectionValues.Next, this.currentPageSize); } else { routing.queryPrev = undefined; routing.queryNext = undefined; } return routing; } onPageChange = newPage => { const { onPageChange } = this.args; if (typeof onPageChange === 'function') { onPageChange(newPage); } }; onPageSizeChange = newPageSize => { const { onPageSizeChange } = this.args; // invoke the callback function if (typeof onPageSizeChange === 'function') { onPageSizeChange(newPageSize); } }; static { setComponentTemplate(precompileTemplate("<div class=\"hds-pagination\" ...attributes>\n <nav class=\"hds-pagination-nav\" aria-label={{this.ariaLabel}}>\n <HdsPaginationNavArrow @direction=\"prev\" @showLabel={{this.showLabels}} @route={{this.routing.route}} @query={{this.routing.queryPrev}} @model={{this.routing.model}} @models={{this.routing.models}} @replace={{this.routing.replace}} @onClick={{this.onPageChange}} @disabled={{@isDisabledPrev}} />\n <HdsPaginationNavArrow @direction=\"next\" @showLabel={{this.showLabels}} @route={{this.routing.route}} @query={{this.routing.queryNext}} @model={{this.routing.model}} @models={{this.routing.models}} @replace={{this.routing.replace}} @onClick={{this.onPageChange}} @disabled={{@isDisabledNext}} />\n </nav>\n\n {{#if this.showSizeSelector}}\n <HdsPaginationSizeSelector @pageSizes={{this.pageSizes}} @label={{@sizeSelectorLabel}} @selectedSize={{this.currentPageSize}} @onChange={{this.onPageSizeChange}} />\n {{/if}}\n</div>", { strictMode: true, scope: () => ({ HdsPaginationNavArrow: HdsPaginationControlArrow, HdsPaginationSizeSelector }) }), this); } } export { DEFAULT_PAGE_SIZES, HdsPaginationCompact as default }; //# sourceMappingURL=index.js.map