@telekom/scale-components
Version:
Scale is the digital design system for Telekom products and experiences.
244 lines (243 loc) • 6.94 kB
JavaScript
/**
* @license
* Scale https://github.com/telekom/scale
*
* Copyright (c) 2021 Egor Kirpichev and contributors, Deutsche Telekom AG
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { Component, Element, Event, h, Host, Listen, Prop, State, Watch, } from '@stencil/core';
import classNames from 'classnames';
import { isScaleIcon } from '../../utils/utils';
import statusNote from '../../utils/status-note';
const PER_SPEC_ICON_SIZE = 20;
let i = 0;
export class TabHeader {
constructor() {
this.generatedId = i++;
/** True for a disabled Tabnavigation */
this.disabled = false;
/** True for smaller height and font size */
/** @deprecated - size should replace small */
this.small = false;
/** (optional) size */
this.size = 'small';
this.hasFocus = false;
}
handleClick(event) {
event.stopPropagation();
if (this.disabled) {
return;
}
this.scaleSelect.emit();
}
selectedChanged(newValue) {
if (!this.hostElement.isConnected) {
return;
}
if (!this.disabled) {
if (newValue === true && this.tabsHaveFocus()) {
// Having focus on the host element, and not on inner elements,
// is required because screen readers.
this.hostElement.focus();
}
this.updateSlottedIcon();
}
}
componentDidLoad() {
this.setChildrenIconSize();
}
componentDidRender() {
if (this.small !== false) {
statusNote({
tag: 'deprecated',
message: 'Property "small" is deprecated. Please use css overwrites.',
type: 'warn',
source: this.hostElement,
});
}
}
/**
* Whether current focused element is within parent `scale-tab-nav`.
* Only if `true`, we imperatively focus the selected element.
* @returns boolean
*/
tabsHaveFocus() {
const tabs = this.hostElement.closest('.scale-tab-nav');
if (!tabs) {
return false;
}
return tabs.contains(document.activeElement);
}
/**
* Find slotted icons, and if any, add the `selected` attribute accordingly.
*/
updateSlottedIcon() {
const icons = Array.from(this.hostElement.childNodes).filter(isScaleIcon);
const action = this.selected ? 'setAttribute' : 'removeAttribute';
icons.forEach((child) => child[action]('selected', ''));
}
/**
* Set any children icon's size according the button size.
*/
setChildrenIconSize() {
const icons = Array.from(this.hostElement.childNodes).filter(isScaleIcon);
icons.forEach((icon) => {
// This is meh people might actually want 24
if (icon.size !== PER_SPEC_ICON_SIZE) {
icon.size = PER_SPEC_ICON_SIZE;
}
});
}
render() {
return (h(Host, { id: `scale-tab-header-${this.generatedId}`, role: this.disabled ? false : 'tab', "aria-selected": this.selected ? 'true' : 'false', tabindex: this.disabled ? false : this.selected ? '0' : '-1', onFocus: () => (this.hasFocus = true), onBlur: () => (this.hasFocus = false) },
this.styles && h("style", null, this.styles),
h("span", { part: this.getBasePartMap(), class: this.getCssClassMap() },
h("slot", null))));
}
getBasePartMap() {
return this.getCssOrBasePartMap('basePart');
}
getCssClassMap() {
return this.getCssOrBasePartMap('css');
}
getCssOrBasePartMap(mode) {
const component = 'tab-header';
const prefix = mode === 'basePart' ? '' : `${component}--`;
return classNames(component, this.selected && `${prefix}selected`, this.size === 'large' && `${prefix}large`, this.hasFocus && `${prefix}has-focus`, this.disabled && `${prefix}disabled`);
}
static get is() { return "scale-tab-header"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() { return {
"$": ["./tab-header.css"]
}; }
static get styleUrls() { return {
"$": ["tab-header.css"]
}; }
static get properties() { return {
"disabled": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "True for a disabled Tabnavigation"
},
"attribute": "disabled",
"reflect": false,
"defaultValue": "false"
},
"small": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [{
"name": "deprecated",
"text": "- size should replace small"
}],
"text": ""
},
"attribute": "small",
"reflect": false,
"defaultValue": "false"
},
"size": {
"type": "string",
"mutable": false,
"complexType": {
"original": "'small' | 'large'",
"resolved": "\"large\" | \"small\"",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "(optional) size"
},
"attribute": "size",
"reflect": false,
"defaultValue": "'small'"
},
"selected": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "(optional) Whether the tab is selected"
},
"attribute": "selected",
"reflect": false
},
"styles": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "(optional) Injected CSS styles"
},
"attribute": "styles",
"reflect": false
}
}; }
static get states() { return {
"hasFocus": {}
}; }
static get events() { return [{
"method": "scaleSelect",
"name": "scale-select",
"bubbles": true,
"cancelable": true,
"composed": true,
"docs": {
"tags": [],
"text": ""
},
"complexType": {
"original": "any",
"resolved": "any",
"references": {}
}
}]; }
static get elementRef() { return "hostElement"; }
static get watchers() { return [{
"propName": "selected",
"methodName": "selectedChanged"
}]; }
static get listeners() { return [{
"name": "click",
"method": "handleClick",
"target": undefined,
"capture": false,
"passive": false
}]; }
}