@telekom/scale-components
Version:
Scale is the digital design system for Telekom products and experiences.
177 lines (173 loc) • 15.2 kB
JavaScript
import { proxyCustomElement, HTMLElement, h, Host } from '@stencil/core/internal/client';
import { c as classnames } from './index2.js';
import { h as hasShadowDom, i as isScaleIcon } from './utils.js';
const buttonCss = ":host{--width:auto;--spacing-x-right:var(--telekom-spacing-composition-space-07);--spacing-x-left:var(--telekom-spacing-composition-space-07);--spacing-x-icon-only:var(--telekom-spacing-composition-space-05);--min-height:var(--telekom-spacing-composition-space-13);--min-width:var(--telekom-spacing-composition-space-13);--radius:var(--telekom-radius-standard);--transition:all var(--telekom-motion-duration-transition)\n var(--telekom-motion-easing-standard);--color-focus:var(--telekom-color-functional-focus-standard);--font-weight:var(--telekom-typography-font-weight-bold);--font-size:var(--telekom-typography-font-size-body);--line-height:var(--telekom-typography-line-spacing-tight);--spacing-icon-x:var(--telekom-spacing-composition-space-04);--vertical-align:middle;--font-size-small:var(--telekom-typography-font-size-caption);--line-height-small:1.125rem;--min-height-small:var(--telekom-spacing-composition-space-10);--spacing-x-right-small:var(--telekom-spacing-composition-space-06);--spacing-x-left-small:var(--telekom-spacing-composition-space-06);--spacing-x-icon-only-small:var(--telekom-spacing-composition-space-00);--spacing-icon-x-small:var(--telekom-spacing-composition-space-03);--radius-primary:var(--radius);--background-primary:var(--telekom-color-primary-standard);--background-primary-hover:var(--telekom-color-primary-hovered);--background-primary-active:var(--telekom-color-primary-pressed);--background-primary-disabled:var(--telekom-color-ui-disabled);--color-primary:var(--telekom-color-text-and-icon-white-standard);--color-primary-disabled:var(--telekom-color-text-and-icon-disabled);--radius-secondary:var(--radius);--border-width-secondary:var(--telekom-spacing-composition-space-01);--background-secondary:transparent;--color-secondary:var(--telekom-color-text-and-icon-standard);--color-secondary-hover:var(--telekom-color-text-and-icon-standard);--color-secondary-active:var(--telekom-color-text-and-icon-standard);--color-secondary-disabled:var(--telekom-color-text-and-icon-disabled);--background-secondary:var(--telekom-color-ui-state-fill-standard);--background-secondary-hover:var(--telekom-color-ui-state-fill-hovered);--background-secondary-active:var(--telekom-color-ui-state-fill-pressed);--background-secondary-disabled:none;--border-secondary:var(--telekom-color-ui-border-standard);--border-secondary-hover:var(--telekom-color-ui-border-hovered);--border-secondary-active:var(--telekom-color-ui-border-pressed);--border-secondary-focus:var(--telekom-color-functional-focus-standard);--border-secondary-white:var(--telekom-color-ui-white);--color-secondary-white:var(--telekom-color-ui-white);--background-secondary-white-hover:var(\n --telekom-color-ui-state-fill-hovered-inverted\n );--background-secondary-white-active:var(\n --telekom-color-ui-state-fill-pressed-inverted\n );--secondary-white-opacity:0.5;--radius-ghost:var(--radius);--border-width-ghost:var(--telekom-spacing-composition-space-01);--spacing-x-ghost:var(--telekom-spacing-composition-space-04);--color-ghost:var(--telekom-color-text-and-icon-primary-standard);--color-ghost-hover:var(--telekom-color-text-and-icon-primary-hovered);--color-ghost-active:var(--telekom-color-text-and-icon-primary-pressed);--color-ghost-disabled:var(--telekom-color-text-and-icon-disabled);--background-ghost-hover:var(--telekom-color-ui-state-fill-hovered);--background-ghost-active:var(--telekom-color-ui-state-fill-pressed);display:inline-block}.button{box-sizing:border-box;display:inline-flex;align-items:center;position:relative;border:0;outline:none;cursor:pointer;user-select:none;font-family:inherit;word-spacing:inherit;letter-spacing:inherit;justify-content:center;text-decoration:none;font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);min-height:var(--min-height);min-width:var(--min-width);width:var(--width);padding-left:var(--spacing-x-left);padding-right:var(--spacing-x-right);vertical-align:var(--vertical-align);transition:var(--transition)}.button.button--size-small{font-size:var(--font-size-small);line-height:var(--line-height-small);min-height:var(--min-height-small);padding-left:var(--spacing-x-left-small);padding-right:var(--spacing-x-right-small)}.button:not(.button--disabled):focus{outline:var(--telekom-line-weight-highlight) solid var(--color-focus);outline-offset:var(--telekom-spacing-composition-space-01)}.button.button--icon-before:not(.button--icon-only) ::slotted(*){margin-right:var(--spacing-icon-x);margin-left:calc(var(--spacing-icon-x-small) * -1);margin-top:var(--spacing-icon-x);margin-bottom:var(--spacing-icon-x)}.button.button--icon-before:not(.button--icon-only).button--size-small ::slotted(*){margin-right:var(--spacing-icon-x-small);margin-left:calc(var(--spacing-icon-x) * -0.5)}.button.button--icon-after:not(.button--icon-only) ::slotted(*){margin-left:var(--spacing-icon-x);margin-right:calc(var(--spacing-icon-x-small) * -1);margin-top:var(--spacing-icon-x);margin-bottom:var(--spacing-icon-x)}.button.button--icon-after:not(.button--icon-only).button--size-small ::slotted(*){margin-left:var(--spacing-icon-x-small);margin-right:calc(var(--spacing-icon-x) * -0.5)}.button:after{top:0;left:0;width:100%;border:var(--telekom-spacing-composition-space-01) solid transparent;height:100%;content:'';display:block;position:absolute;box-sizing:border-box;pointer-events:none;border-radius:var(--radius)}.button--icon-only{padding-left:var(--spacing-x-icon-only);padding-right:var(--spacing-x-icon-only)}.button--icon-only.button--variant-secondary{padding-left:calc(var(--spacing-x-icon-only) - 1px);padding-right:calc(var(--spacing-x-icon-only) - 1px)}.button--icon-only.button--size-small{padding-left:var(--spacing-x-icon-only-small);padding-right:var(--spacing-x-icon-only-small);min-width:32px}.button--icon-only.button--size-small.button--variant-secondary{padding-left:calc(var(--spacing-x-icon-only-small) - 1px);padding-right:calc(var(--spacing-x-icon-only-small) - 1px)}.button--disabled{cursor:not-allowed}.button--variant-primary{text-align:center;border-radius:var(--radius);background:var(--background-primary);color:var(--color-primary)}.button--variant-primary:not(.button--disabled):hover{background:var(--background-primary-hover)}.button--variant-primary:not(.button--disabled):active{background:var(--background-primary-active)}.button--disabled.button--variant-primary{background:var(--background-primary-disabled);color:var(--color-primary-disabled)}.button--variant-secondary{background:var(--background-secondary);text-align:center;border-radius:var(--radius-secondary);border:var(--border-width-secondary) solid currentColor;color:var(--color-secondary);background-color:var(--background-secondary);border-color:var(--border-secondary)}.button--variant-secondary:not(.button--disabled):hover{color:var(--color-secondary-hover);background-color:var(--background-secondary-hover);border-color:var(--border-secondary-hover)}.button--variant-secondary:not(.button--disabled):active{color:var(--color-secondary-active);background-color:var(--background-secondary-active);border-color:var(--border-secondary-active)}.button--disabled.button--variant-secondary{color:var(--color-secondary-disabled);background-color:var(--background-secondary-disabled)}.button--variant-ghost{background:transparent;text-align:center;border-radius:var(--radius-ghost);border:var(--border-width-ghost) solid transparent;color:var(--color-ghost);padding-left:var(--spacing-x-ghost);padding-right:var(--spacing-x-ghost)}.button--variant-ghost:not(.button--disabled):hover{color:var(--color-ghost-hover);background-color:var(--background-ghost-hover)}.button--variant-ghost:not(.button--disabled):active{color:var(--color-ghost-active);background-color:var(--background-ghost-active)}.button--disabled.button--variant-ghost{color:var(--color-ghost-disabled)}.button--variant-secondary-white{background:var(--background-secondary);text-align:center;border-radius:var(--radius-secondary);border:var(--border-width-secondary) solid currentColor;color:var(--color-secondary-white);background-color:var(--background-secondary);border-color:var(--border-secondary-white)}.button--variant-secondary-white:not(.button--disabled):hover{background-color:var(--background-secondary-white-hover)}.button--variant-secondary-white:not(.button--disabled):active{background-color:var(--background-secondary-white-active)}.button--disabled.button--variant-secondary-white{opacity:var(--secondary-white-opacity)}";
const DEFAULT_ICON_SIZE = 24;
const buttonIconSizeMap = {
small: 16,
large: 20,
};
const Button = /*@__PURE__*/ proxyCustomElement(class extends HTMLElement {
constructor() {
super();
this.__registerHost();
this.__attachShadow();
/** (optional) The size of the button */
this.size = 'large';
/** (optional) Button variant */
this.variant = 'primary';
/** (optional) If `true`, the button is disabled */
this.disabled = false;
/** (optional) Set to `true` when the button contains only an icon */
this.iconOnly = false;
/** (optional) Icon position related to the label */
this.iconPosition = 'before';
/** (optional) The target attribute for the <a> tag */
this.target = '_self';
/**
* Hack to make the button behave has expected when inside forms.
* @see https://github.com/ionic-team/ionic-framework/blob/master/core/src/components/button/button.tsx#L155-L175
*/
this.handleClick = (ev) => {
// No need to check for `disabled` because disabled buttons won't emit clicks
if (hasShadowDom(this.hostElement)) {
const parentForm = this.hostElement.closest('form');
if (parentForm) {
ev.preventDefault();
const fakeButton = document.createElement('button');
if (this.type) {
fakeButton.type = this.type;
}
fakeButton.style.display = 'none';
parentForm.appendChild(fakeButton);
fakeButton.click();
fakeButton.remove();
}
}
};
}
/**
* Prevent clicks from being emitted from the host
* when the component is `disabled`.
*/
handleHostClick(event) {
if (this.disabled === true) {
event.stopImmediatePropagation();
}
}
async setFocus() {
this.focusableElement.focus();
}
componentDidLoad() {
this.setChildrenIconSize();
}
connectedCallback() {
this.setIconPositionProp();
this.appendEnterKeySubmitFallback();
}
disconnectedCallback() {
this.cleanUpEnterKeySubmitFallback();
}
/**
* In order for forms to be submitted with the Enter key
* there has to be a `button` or an `input[type="submit"]` in the form.
* Browsers do not take the <button> inside the Shadow DOM into account for this matter.
* So we carefully append an `input[type="submit"]` to overcome this.
*
* @see https://stackoverflow.com/a/35235768
* @see https://github.com/telekom/scale/issues/859
*/
appendEnterKeySubmitFallback() {
if (hasShadowDom(this.hostElement)) {
const parentForm = this.hostElement.closest('form');
if (parentForm == null) {
return;
}
const hasSubmitInputAlready = parentForm.querySelector('input[type="submit"]') != null;
if (hasSubmitInputAlready) {
return;
}
this.fallbackSubmitInputElement = document.createElement('input');
this.fallbackSubmitInputElement.type = 'submit';
this.fallbackSubmitInputElement.hidden = true;
parentForm.appendChild(this.fallbackSubmitInputElement);
}
}
cleanUpEnterKeySubmitFallback() {
if (this.fallbackSubmitInputElement != null) {
try {
this.fallbackSubmitInputElement.remove();
this.fallbackSubmitInputElement = null;
}
catch (err) { }
}
}
/**
* Detect whether the last node is an element (not text).
* If so, it's probably an icon, so we set `iconPosition` to `after`.
*/
setIconPositionProp() {
const nodes = Array.from(this.hostElement.childNodes).filter((node) => {
// ignore empty text nodes, which are probably due to formatting
return !(node.nodeType === 3 && node.nodeValue.trim() === '');
});
const lastNode = nodes.length > 1 ? nodes[nodes.length - 1] : null;
if (!this.iconOnly && lastNode && isScaleIcon(lastNode)) {
this.iconPosition = 'after';
}
}
/**
* Set any children icon's size according the button size.
*/
setChildrenIconSize() {
if (this.size != null && buttonIconSizeMap[this.size] != null) {
const icons = Array.from(this.hostElement.childNodes).filter(isScaleIcon);
icons.forEach((icon) => {
if (icon.size === DEFAULT_ICON_SIZE) {
icon.size = buttonIconSizeMap[this.size];
}
});
}
}
render() {
const basePart = classnames('base', this.variant && `variant-${this.variant}`, this.iconOnly && 'icon-only', !this.iconOnly && this.iconPosition, this.disabled && 'disabled');
return (h(Host, null, this.styles && h("style", null, this.styles), this.href ? (h("a", { ref: (el) => (this.focusableElement = el), class: this.getCssClassMap(), href: this.disabled ? null : this.href, download: this.download, target: this.target, rel: this.target === '_blank' ? 'noopener noreferrer' : undefined, part: basePart, tabIndex: this.innerTabindex, role: "link", "aria-disabled": this.disabled ? 'true' : null, "aria-label": this.innerAriaLabel }, h("slot", null))) : (h("button", { ref: (el) => (this.focusableElement = el), class: this.getCssClassMap(), onClick: this.handleClick, disabled: this.disabled, type: this.type, part: basePart, tabIndex: this.innerTabindex, name: this.name, value: this.value, "aria-label": this.innerAriaLabel }, h("slot", null)))));
}
getCssClassMap() {
return classnames('button', this.size && `button--size-${this.size}`, this.variant && `button--variant-${this.variant}`, this.iconOnly && `button--icon-only`, !this.iconOnly &&
this.iconPosition &&
`button--icon-${this.iconPosition}`, this.disabled && `button--disabled`);
}
get hostElement() { return this; }
static get style() { return buttonCss; }
}, [1, "scale-button", {
"size": [1],
"variant": [1],
"disabled": [4],
"type": [1],
"name": [1],
"value": [1],
"iconOnly": [4, "icon-only"],
"iconPosition": [513, "icon-position"],
"href": [1],
"target": [1],
"styles": [1],
"download": [1],
"innerTabindex": [2, "inner-tabindex"],
"innerAriaLabel": [1, "inner-aria-label"],
"setFocus": [64]
}, [[2, "click", "handleHostClick"]]]);
function defineCustomElement() {
if (typeof customElements === "undefined") {
return;
}
const components = ["scale-button"];
components.forEach(tagName => { switch (tagName) {
case "scale-button":
if (!customElements.get(tagName)) {
customElements.define(tagName, Button);
}
break;
} });
}
export { Button as B, defineCustomElement as d };