@public-ui/components
Version:
Contains all web components that belong to KoliBri - The accessible HTML-Standard.
554 lines (553 loc) • 22.1 kB
JavaScript
/*!
* 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