UNPKG

@public-ui/components

Version:

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

385 lines (384 loc) 16.3 kB
/*! * KoliBri - The accessible HTML-Standard */ import { h } from "@stencil/core"; import { a11yHintLabelingLandmarks, devHint, validateCollapsible, validateHasCompactButton, validateHasIconsWhenExpanded, validateHideLabel, validateLabel, } from "../../schema"; import { KolButtonWcTag, KolLinkWcTag } from "../../core/component-names"; import { translate } from "../../i18n"; import clsx from "../../utils/clsx"; import { createRelatedUniqueId, createUniqueId } from "../../utils/dev.utils"; import { addNavLabel, removeNavLabel } from "../../utils/unique-nav-labels"; import { watchNavLinks } from "./validation"; const linkValidator = (link) => { if (typeof link === 'object' && typeof link._label === 'string') { if (Array.isArray(link._children)) { return linksValidator(link._children); } return false; } return true; }; const linksValidator = (links) => { if (Array.isArray(links)) { return links.find(linkValidator) !== undefined; } return true; }; const entryIsLink = (entryProps) => { return typeof entryProps._href === 'string'; }; const entryIsButton = (entryProps) => { var _a; return entryProps._href === undefined && typeof ((_a = entryProps._on) === null || _a === void 0 ? void 0 : _a.onClick) === 'function'; }; export class KolNav { constructor() { this.navId = createUniqueId('kol-nav'); this.listId = createRelatedUniqueId(this.navId, 'list'); this.handleToggleExpansionClick = (children) => { if (children) { if (this.state._expandedChildren.includes(children)) { this.collapseChildren(children); } else { this.expandChildren(children); } } }; this.linkList = (props) => { return (h("ul", { class: clsx('kol-nav__list', { 'kol-nav__list--nested': props.deep > 0, 'kol-nav__list--vertical': props.deep !== 0, }), id: props.deep > 0 ? props.id : undefined }, props.links.map((link, index) => { return this.li(props.collapsible, props.deep, index, link, props.id); }))); }; this._collapsible = true; this._hasCompactButton = false; this._hasIconsWhenExpanded = false; this._hideLabel = false; this.state = { _collapsible: true, _hasCompactButton: false, _hasIconsWhenExpanded: false, _hideLabel: false, _label: '', _links: [], _expandedChildren: [], }; } expandChildren(children) { this.state = Object.assign(Object.assign({}, this.state), { _expandedChildren: [...this.state._expandedChildren, children] }); } collapseChildren(children) { this.state = Object.assign(Object.assign({}, this.state), { _expandedChildren: this.state._expandedChildren.filter((searchChildren) => searchChildren !== children) }); } buildIconObject(collapsible, expanded, leftIcon) { const icon = { left: '', right: '', }; if (this.state._hasIconsWhenExpanded && leftIcon) { icon.left = leftIcon; } if (this.state._hideLabel) { if (leftIcon) { icon.left = leftIcon; } else { icon.left = 'kolicon-link'; } } if (collapsible) { if (expanded) { icon.right = 'kolicon-minus'; } else { icon.right = 'kolicon-plus'; } } return icon; } entry(collapsible, hasChildren, entry, expanded, ariaID) { var _a; const leftIcon = typeof entry._icons === 'string' ? entry._icons : typeof ((_a = entry._icons) === null || _a === void 0 ? void 0 : _a.left) === 'string' ? entry._icons.left : undefined; const icons = this.buildIconObject(collapsible && hasChildren, expanded, leftIcon); return (h("div", { class: "kol-nav__entry-wrapper" }, entryIsLink(entry) ? (h(KolLinkWcTag, Object.assign({ class: clsx('kol-nav__entry kol-nav__entry--link', { 'kol-nav__entry--collapsible': collapsible, }) }, entry, { _hideLabel: this.state._hideLabel, _icons: icons, _ariaControls: collapsible && hasChildren && expanded ? ariaID : undefined, _ariaExpanded: collapsible && hasChildren ? expanded : undefined }))) : (h(KolButtonWcTag, { class: clsx('kol-nav__entry kol-nav__entry--button', { 'kol-nav__entry--collapsible': collapsible, }), _label: entry._label, _hideLabel: this.state._hideLabel, _icons: icons, _ariaControls: collapsible && hasChildren && expanded ? ariaID : undefined, _ariaExpanded: collapsible && hasChildren ? expanded : undefined, _on: { onClick: (event, value) => { if (entryIsButton(entry) && typeof entry._on.onClick === 'function') { entry._on.onClick(event, value); } this.handleToggleExpansionClick(entry._children); }, } })))); } li(collapsible, deep, index, link, ariaIDparent) { const active = !!link._active; const hasChildren = Array.isArray(link._children) && link._children.length > 0; const expanded = Boolean(link._children && this.state._expandedChildren.includes(link._children)); const ariaID = createRelatedUniqueId(ariaIDparent, `${deep}-${index}`); return (h("li", { class: clsx('kol-nav__list-item', { 'kol-nav__list-item--active': active, 'kol-nav__list-item--expanded': expanded, 'kol-nav__list-item--has-children': hasChildren, }), key: index }, this.entry(collapsible, hasChildren, link, expanded, ariaID), expanded && h(this.linkList, { collapsible: collapsible, deep: deep + 1, links: link._children || [], id: ariaID }))); } initializeExpandedChildren() { this.state = Object.assign(Object.assign({}, this.state), { _expandedChildren: [] }); const handleBranch = (branch) => { if (branch._active) { if (branch._children) { this.expandChildren(branch._children); } return true; } else if (branch._children) { for (const childBranch of branch._children) { if (handleBranch(childBranch)) { this.expandChildren(branch._children); return true; } } } return false; }; this.state._links.forEach(handleBranch); } render() { const collapsible = this.state._collapsible === true; return (h("div", { key: '168e77bd4e310d31ec72afcc5dc4633df3a5852c', class: clsx('kol-nav', { 'kol-nav--is-compact': this.state._hideLabel, }) }, h("nav", { key: 'd27ea32253853f5ab13df4cceec8245a69564179', "aria-label": this.state._label, class: "kol-nav__navigation", id: this.navId }, h(this.linkList, { key: '43080dfc66978e4161adb904094f8b7a4585798c', collapsible: collapsible, deep: 0, links: this.state._links, id: this.listId })), this.state._hasCompactButton && (h("div", { key: '917c25c8c29c3f980dd95c19cac2b3857c9f06fd', class: "kol-nav__compact" }, h(KolButtonWcTag, { key: 'c08ed259ed0721edaa769a44e7b9b96c50091f30', class: "kol-nav__toggle-button", _ariaControls: this.navId, _ariaExpanded: !this.state._hideLabel, _icons: this.state._hideLabel ? 'kolicon-chevron-right' : 'kolicon-chevron-left', _hideLabel: true, _label: translate(this.state._hideLabel ? 'kol-nav-maximize' : 'kol-nav-minimize'), _on: { onClick: () => { this.state = Object.assign(Object.assign({}, this.state), { _hideLabel: !this.state._hideLabel }); }, }, _tooltipAlign: "right" }))))); } validateCollapsible(value) { validateCollapsible(this, value); } validateHasCompactButton(value) { validateHasCompactButton(this, value); } validateHasIconsWhenExpanded(value) { validateHasIconsWhenExpanded(this, value); } validateHideLabel(value) { validateHideLabel(this, value); } validateLabel(value, _oldValue, initial = false) { if (!initial) { removeNavLabel(this.state._label); } validateLabel(this, value, { required: true, }); a11yHintLabelingLandmarks(value); addNavLabel(this.state._label); } validateLinks(value) { watchNavLinks('KolNav', this, value); devHint(`[KolNav] The navigation structure is not yet validated recursively.`); this.initializeExpandedChildren(); } componentWillLoad() { this.validateCollapsible(this._collapsible); this.validateHideLabel(this._hideLabel); this.validateHasCompactButton(this._hasCompactButton); this.validateHasIconsWhenExpanded(this._hasIconsWhenExpanded); this.validateLabel(this._label, undefined, true); this.validateLinks(this._links); this.initializeExpandedChildren(); } disconnectedCallback() { removeNavLabel(this.state._label); } static get is() { return "kol-nav"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "default": ["./style.scss"] }; } static get styleUrls() { return { "default": ["style.css"] }; } static get properties() { return { "_collapsible": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "TODO", "text": ": Change type back to `CollapsiblePropType` after Stencil#4663 has been resolved." }], "text": "Defines if navigation nodes can be collapsed or not. Enabled by default." }, "getter": false, "setter": false, "reflect": false, "attribute": "_collapsible", "defaultValue": "true" }, "_hasCompactButton": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Creates a button below the navigation, that toggles _collapsible." }, "getter": false, "setter": false, "reflect": false, "attribute": "_has-compact-button", "defaultValue": "false" }, "_hasIconsWhenExpanded": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Shows icons next to the navigation item labels, even when the navigation is not collapsed." }, "getter": false, "setter": false, "reflect": false, "attribute": "_has-icons-when-expanded", "defaultValue": "false" }, "_hideLabel": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean | undefined", "references": {} }, "required": false, "optional": true, "docs": { "tags": [{ "name": "TODO", "text": ": Change type back to `HideLabelPropType` after Stencil#4663 has been resolved." }], "text": "Hides the caption by default and displays the caption text with a tooltip when the\ninteractive element is focused or the mouse is over it." }, "getter": false, "setter": false, "reflect": false, "attribute": "_hide-label", "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" }, "_links": { "type": "string", "mutable": false, "complexType": { "original": "Stringified<ButtonOrLinkOrTextWithChildrenProps[]>", "resolved": "ButtonOrLinkOrTextWithChildrenProps[] | string", "references": { "Stringified": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::Stringified" }, "ButtonOrLinkOrTextWithChildrenProps": { "location": "import", "path": "../../schema", "id": "src/schema/index.ts::ButtonOrLinkOrTextWithChildrenProps" } } }, "required": true, "optional": false, "docs": { "tags": [], "text": "Defines the list of links, buttons or texts to render." }, "getter": false, "setter": false, "reflect": false, "attribute": "_links" } }; } static get states() { return { "state": {} }; } static get watchers() { return [{ "propName": "_collapsible", "methodName": "validateCollapsible" }, { "propName": "_hasCompactButton", "methodName": "validateHasCompactButton" }, { "propName": "_hasIconsWhenExpanded", "methodName": "validateHasIconsWhenExpanded" }, { "propName": "_hideLabel", "methodName": "validateHideLabel" }, { "propName": "_label", "methodName": "validateLabel" }, { "propName": "_links", "methodName": "validateLinks" }]; } } //# sourceMappingURL=shadow.js.map