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