UNPKG

@public-ui/components

Version:

Contains all web components that belong to KoliBri - The accessible HTML-Standard.

554 lines (553 loc) 22.1 kB
/*! * KoliBri - The accessible HTML-Standard */ import { __decorate } from "tslib"; import { h } from "@stencil/core"; import { devHint, koliBriQuerySelector, setState, uiUxHintMillerscheZahl, validateAlign, validateLabel, validateTabBehavior, watchJsonArrayString, watchNumber, } from "../../schema"; import { KolButtonWcTag } from "../../core/component-names"; import { translate } from "../../i18n"; import { KeyboardKey } from "../../schema/enums"; import { validateHasCreateButton } from "../../schema/props/has-create-button"; import clsx from "../../utils/clsx"; import { createCtaRef, delegateClick, delegateFocus } from "../../utils/element-interaction"; import { dispatchDomEvent, KolEvent } from "../../utils/events"; export class KolTabs { constructor() { this.onCreateLabel = `${translate('kol-new')} …`; this.ctaRef = createCtaRef(); this.nextPossibleTabIndex = (tabs, offset, step = 1) => { const nextOffset = offset + step; if (nextOffset < tabs.length) { if (tabs[nextOffset]._disabled) { return this.nextPossibleTabIndex(tabs, offset, step + 1); } return nextOffset; } return offset; }; this.prevPossibleTabIndex = (tabs, offset, step = 1) => { const nextOffset = offset - step; if (nextOffset >= 0) { if (tabs[nextOffset]._disabled) { return this.prevPossibleTabIndex(tabs, offset, step + 1); } return nextOffset; } return offset; }; this.onKeyDown = (event) => { switch (event.key) { case KeyboardKey.ArrowRight: this.goToNextTab(event); break; case KeyboardKey.ArrowLeft: this.goToPreviousTab(event); break; case KeyboardKey.Space: case KeyboardKey.Enter: this.activateFocusedTab(event); break; } }; this.onClickSelect = (event, index) => { this.selectNextTabEvent(event, index); }; this.onMouseDown = (event) => { event.preventDefault(); }; this.callbacks = { onClick: this.onClickSelect, onMouseDown: this.onMouseDown, }; this.catchTabPanelHost = (el) => { this.tabPanelHost = el; }; this._align = 'top'; this._hasCreateButton = false; this._selected = 0; this.state = { _align: 'top', _label: '', _selected: 0, _tabs: [], }; this.selectNextNotDisabledTab = (selected, tabs, upOrDown = true, initialSelected) => { if (selected > tabs.length - 1) { selected = tabs.length - 1; } if (selected < 0) { selected = 0; } if (Array.isArray(tabs) && tabs[selected]) { if (tabs[selected]._disabled) { if (upOrDown === true) { if (selected < tabs.length - 1) { return this.selectNextNotDisabledTab(selected + 1, tabs, true, initialSelected || selected); } else { selected = initialSelected || selected; upOrDown = false; } } if (upOrDown === false) { if (selected > 0) { return this.selectNextNotDisabledTab(selected - 1, tabs, false, initialSelected || selected); } else { devHint(`[KolTabs] All tabs are disabled, and therefore no tab can be displayed.`); } } } } return selected; }; this.syncSelectedAndTabs = (nextValue, nextState, _component, key) => { let selected; if (key === '_selected') { selected = nextValue; } else { selected = this.state._selected; } let tabs; if (key === '_tabs') { tabs = nextValue; } else { tabs = this.state._tabs; } if (tabs.length > 0) { nextState.set('_selected', this.selectNextNotDisabledTab(selected, tabs)); } }; this.refreshTabPanels = () => { var _a, _b, _c, _d; if (!this.tabPanelHost) return; while (this.tabPanelHost.firstChild) { this.tabPanelHost.removeChild(this.tabPanelHost.firstChild); } for (let i = 0; i < ((_a = this.state._tabs) === null || _a === void 0 ? void 0 : _a.length); i++) { const div = document.createElement('div'); div.setAttribute('aria-labelledby', `${this.state._label.replace(/\s/g, '-')}-tab-${i}`); div.setAttribute('id', `tabpanel-${i}`); div.setAttribute('role', 'tabpanel'); div.setAttribute('hidden', ''); div.setAttribute('tabindex', '0'); const slot = document.createElement('slot'); slot.setAttribute('name', `tabpanel-slot-${i}`); div.appendChild(slot); (_b = this.tabPanelHost) === null || _b === void 0 ? void 0 : _b.appendChild(div); if (typeof HTMLCollection !== 'undefined' && ((_c = this.host) === null || _c === void 0 ? void 0 : _c.children) instanceof HTMLCollection && ((_d = this.host) === null || _d === void 0 ? void 0 : _d.children[i])) { this.host.children[i].setAttribute('slot', `tabpanel-slot-${i}`); } } this.updateVisiblePanel(); }; this.updateVisiblePanel = () => { if (!this.tabPanelHost) return; Array.from(this.tabPanelHost.children).forEach((child, i) => { if (i === this.state._selected) { child.removeAttribute('hidden'); } else { child.setAttribute('hidden', ''); } }); }; this.onCreate = (event) => { var _a, _b; event.preventDefault(); (_b = (_a = this.state._on) === null || _a === void 0 ? void 0 : _a.onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, event); if (this.host) { dispatchDomEvent(this.host, KolEvent.create); } }; this.onBlur = () => { this.currentFocusIndex = undefined; }; } getCurrentFocusIndex() { if (typeof this.currentFocusIndex === 'number') { return this.currentFocusIndex; } return this.state._selected; } getKeyboardTabChangeMode() { if (this._behavior === 'select-manual') { return 'selectFocusOnly'; } return 'activateCompletely'; } goToNextTab(event) { const nextFocusIndex = this.nextPossibleTabIndex(this.state._tabs, this.getCurrentFocusIndex()); this.selectNextTabEvent(event, nextFocusIndex, this.getKeyboardTabChangeMode()); } goToPreviousTab(event) { const nextFocusIndex = this.prevPossibleTabIndex(this.state._tabs, this.getCurrentFocusIndex()); this.selectNextTabEvent(event, nextFocusIndex, this.getKeyboardTabChangeMode()); } activateFocusedTab(event) { if (typeof this.currentFocusIndex === 'number') { this.onSelect(event, this.currentFocusIndex); } } selectNextTabEvent(event, nextTabIndex, changeMode = 'activateCompletely') { var _a, _b; this.currentFocusIndex = nextTabIndex; this.focusTabById(nextTabIndex); if (changeMode === 'activateCompletely') { this._selected = nextTabIndex; const tab = this.state._tabs[nextTabIndex]; (_b = (_a = tab._on) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a, event, nextTabIndex); this.onSelect(event, nextTabIndex); } } async focus() { } async click() { } renderButtonGroup() { return (h("div", { "aria-label": this.state._label, class: "kol-tabs__button-group", role: "tablist", onKeyDown: this.onKeyDown, onBlur: this.onBlur }, this.state._tabs.map((button, index) => (h(KolButtonWcTag, { ref: this.state._selected === index ? this.ctaRef : undefined, _disabled: button._disabled, _icons: button._icons, _hideLabel: button._hideLabel, _label: button._label, _on: this.callbacks, _tabIndex: this.state._selected === index ? 0 : -1, _tooltipAlign: button._tooltipAlign, _variant: this.state._selected === index ? 'custom' : undefined, _customClass: this.state._selected === index ? 'selected' : '', _ariaControls: `tabpanel-${index}`, _ariaSelected: this.state._selected === index, _id: `${this.state._label.replace(/\s/g, '-')}-tab-${index}`, _role: "tab", _value: index }))), this.state._hasCreateButton && (h(KolButtonWcTag, { class: "kol-tabs__button-create", _label: this.onCreateLabel, _on: { onClick: this.onCreate, }, _icons: "kolicon-plus", "data-testid": "tabs-create-button" })))); } render() { return (h("div", { key: '03faa28ca436d156581f60b422a46eca57ff9d48', ref: (el) => { this.tabPanelsElement = el; }, class: clsx('kol-tabs', `kol-tabs--align-${this.state._align}`) }, this.renderButtonGroup(), h("div", { key: '61e93a5f157c7d31a60ec9fef085422955cb7311', class: "kol-tabs__content", ref: this.catchTabPanelHost }))); } validateAlign(value) { validateAlign(this, value); } validateBehavior(value) { validateTabBehavior(this, value); } validateHasCreateButton(value) { validateHasCreateButton(this, value); } validateLabel(value) { validateLabel(this, value, { required: true, }); } validateOn(value) { if (typeof value === 'object' && value !== null) { const callbacks = {}; if (typeof value.onCreate === 'function') { callbacks.onCreate = value.onCreate; } if (typeof value.onSelect === 'function') { callbacks.onSelect = value.onSelect; } setState(this, '_on', callbacks); } } validateSelected(value) { watchNumber(this, '_selected', value, { hooks: { beforePatch: this.syncSelectedAndTabs, }, }); } validateTabs(value) { watchJsonArrayString(this, '_tabs', (item) => typeof item === 'object' && item !== null && typeof item._label === 'string' && item._label.length > 0, value, undefined, { hooks: { beforePatch: this.syncSelectedAndTabs, afterPatch: this.refreshTabPanels, }, }); uiUxHintMillerscheZahl('KolTabs', this.state._tabs.length); } componentWillLoad() { this.validateAlign(this._align); this.validateLabel(this._label); this.validateOn(this._on); this.validateSelected(this._selected); this.validateTabs(this._tabs); this.validateBehavior(this._behavior); this.validateHasCreateButton(this._hasCreateButton); } componentDidRender() { this.refreshTabPanels(); } focusTabById(index) { if (this.tabPanelsElement) { const button = koliBriQuerySelector(`button#${this.state._label.replace(/\s/g, '-')}-tab-${index}`, this.tabPanelsElement); button === null || button === void 0 ? void 0 : button.focus(); } } onSelect(event, index) { var _a, _b; (_b = (_a = this._on) === null || _a === void 0 ? void 0 : _a.onSelect) === null || _b === void 0 ? void 0 : _b.call(_a, event, index); if (this.host) { dispatchDomEvent(this.host, KolEvent.select, index); } this.focusTabById(index); } static get is() { return "kol-tabs"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "default": ["./style.scss"] }; } static get styleUrls() { return { "default": ["style.css"] }; } static get properties() { return { "_align": { "type": "string", "mutable": false, "complexType": { "original": "AlignPropType", "resolved": "\"bottom\" | \"left\" | \"right\" | \"top\" | undefined", "references": { "AlignPropType": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::AlignPropType" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Defines the visual orientation of the component." }, "getter": false, "setter": false, "reflect": false, "attribute": "_align", "defaultValue": "'top'" }, "_behavior": { "type": "string", "mutable": false, "complexType": { "original": "TabBehaviorPropType", "resolved": "\"select-automatic\" | \"select-manual\" | undefined", "references": { "TabBehaviorPropType": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::TabBehaviorPropType" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Defines which behavior is active." }, "getter": false, "setter": false, "reflect": false, "attribute": "_behavior" }, "_hasCreateButton": { "type": "boolean", "mutable": false, "complexType": { "original": "HasCreateButtonPropType", "resolved": "boolean | undefined", "references": { "HasCreateButtonPropType": { "location": "import", "path": "../../schema/props/has-create-button", "id": "src/schema/props/has-create-button.ts::HasCreateButtonPropType" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Defines whether the element has a create button." }, "getter": false, "setter": false, "reflect": false, "attribute": "_has-create-button", "defaultValue": "false" }, "_label": { "type": "string", "mutable": false, "complexType": { "original": "LabelPropType", "resolved": "string", "references": { "LabelPropType": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::LabelPropType" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Defines the visible or semantic label of the component (e.g. aria-label, label, headline, caption, summary, etc.)." }, "getter": false, "setter": false, "reflect": false, "attribute": "_label" }, "_on": { "type": "unknown", "mutable": false, "complexType": { "original": "KoliBriTabsCallbacks", "resolved": "undefined | { onCreate?: EventCallback<Event> | undefined; } & { onSelect?: EventValueOrEventCallback<MouseEvent | KeyboardEvent | CustomEvent<any> | PointerEvent, number> | undefined; }", "references": { "KoliBriTabsCallbacks": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::KoliBriTabsCallbacks" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Gibt die Liste der Callback-Funktionen an, die auf Events aufgerufen werden sollen." }, "getter": false, "setter": false }, "_selected": { "type": "number", "mutable": true, "complexType": { "original": "number", "resolved": "number | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Defines which tab is active." }, "getter": false, "setter": false, "reflect": true, "attribute": "_selected", "defaultValue": "0" }, "_tabs": { "type": "string", "mutable": false, "complexType": { "original": "Stringified<TabButtonProps[]>", "resolved": "TabButtonProps[] | string", "references": { "Stringified": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::Stringified" }, "TabButtonProps": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::TabButtonProps" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Defines the tab captions." }, "getter": false, "setter": false, "reflect": false, "attribute": "_tabs" } }; } static get states() { return { "state": {} }; } static get methods() { return { "focus": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Sets focus on the current tab button.", "tags": [] } }, "click": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Triggers a click on the currently selected tab.", "tags": [] } } }; } static get elementRef() { return "host"; } static get watchers() { return [{ "propName": "_align", "methodName": "validateAlign" }, { "propName": "_behavior", "methodName": "validateBehavior" }, { "propName": "_hasCreateButton", "methodName": "validateHasCreateButton" }, { "propName": "_label", "methodName": "validateLabel" }, { "propName": "_on", "methodName": "validateOn" }, { "propName": "_selected", "methodName": "validateSelected" }, { "propName": "_tabs", "methodName": "validateTabs" }]; } } __decorate([ delegateFocus('ctaRef') ], KolTabs.prototype, "focus", null); __decorate([ delegateClick('ctaRef') ], KolTabs.prototype, "click", null); //# sourceMappingURL=shadow.js.map