UNPKG

@limetech/lime-elements

Version:
608 lines (607 loc) • 19.8 kB
import { h, Host, } from '@stencil/core'; import { getRel } from '../../util/link-helper'; import { getIconName } from '../icon/get-icon-props'; import { makeEnterClickable, removeEnterClickable, } from '../../util/make-enter-clickable'; import translate from '../../global/translations'; import { BACKSPACE, DELETE } from '../../util/keycodes'; import { isEmpty } from 'lodash-es'; /** * Chips and buttons are both interactive elements in UI design, * but they serve different purposes and are used in different contexts. * * :::warning * Do not use the chip component carelessly, as an alternative for * [`limel-button`](#/component/limel-button/) in the UI design! * * **Buttons:** * Buttons are used to trigger actions. They are typically used to * submit forms, open dialogs, initiate a process, or perform any action * that changes the state of the application. * Buttons' labels usually contain action words, in other words, the labels is * a _verb in imperative mood_ such as "Submit" or "Delete". * Buttons are placed in areas where it's clear they will initiate * an action when clicked. * * **Chips:** * Chips however are elements which may look like buttons, but they are * representing choices, filters, or tags, in a small block * or clearly bundled into a group. Chips are rarely used alone in the * user interface. * They are often used in a so called "chip-set", or placed together in * a section of the UI, where the user can expect more than one chip to be present. * * For example, a chip may represent a filter in a filter bar, or a tag in a tag list, * or an item in a shopping list. * Clicking a chip can also trigger an action, for example toggling a filter ON or OFF, * or opening a page with all posts tagged with the tag represented by the chip, * or navigating to a page with more information about the item in the shopping list. * ::: * * @exampleComponent limel-example-chip-button * @exampleComponent limel-example-chip-link * @exampleComponent limel-example-chip-icon-colors * @exampleComponent limel-example-chip-image * @exampleComponent limel-example-chip-badge * @exampleComponent limel-example-chip-filter * @exampleComponent limel-example-chip-removable * @exampleComponent limel-example-chip-menu * @exampleComponent limel-example-chip-loading * @exampleComponent limel-example-chip-progress * @exampleComponent limel-example-chip-size * @exampleComponent limel-example-chip-readonly-border * @exampleComponent limel-example-chip-aria-role */ export class Chip { constructor() { this.renderAsButton = () => { return [ h("button", { id: 'chip-' + this.identifier, class: "chip", role: "button", disabled: this.disabled || this.readonly, "aria-busy": this.loading ? 'true' : 'false', "aria-live": "polite", onKeyDown: this.handleDeleteKeyDown }, this.renderSpinner(), this.renderPicture(), this.renderLabel(), this.renderBadge(), this.renderProgressBar()), this.renderRemoveButton(), this.renderActionsMenu(), ]; }; this.renderAsLink = () => { var _a, _b; const rel = getRel((_a = this.link) === null || _a === void 0 ? void 0 : _a.target, (_b = this.link) === null || _b === void 0 ? void 0 : _b.rel); return [ h("a", { id: 'chip-' + this.identifier, class: "chip", href: this.link.href, title: this.link.title, target: this.link.target, rel: rel, "aria-disabled": this.disabled || this.readonly, tabindex: this.disabled || this.readonly ? -1 : 0, onKeyDown: this.handleDeleteKeyDown }, this.renderSpinner(), this.renderPicture(), this.renderLabel(), this.renderBadge(), this.renderProgressBar()), this.renderRemoveButton(), this.renderActionsMenu(), ]; }; this.renderLabel = () => { return h("span", { class: "text" }, this.text); }; this.filterClickWhenDisabled = (e) => { if (this.disabled || this.readonly) { e.preventDefault(); } }; this.handleRemoveClick = (event) => { event.stopPropagation(); this.remove.emit(this.identifier); }; this.handleDeleteKeyDown = (event) => { if (!this.removable) { return; } const keys = [DELETE, BACKSPACE]; if (keys.includes(event.key)) { this.handleRemoveClick(event); } }; this.removeChipLabel = () => { return `${this.getTranslation('remove')} ${this.text}`; }; this.actionMenuLabel = () => { return this.getTranslation('file-viewer.more-actions'); }; this.getTranslation = (key) => { return translate.get(key, this.language); }; this.handleActionMenuSelect = (event) => { const menuItem = event.detail; if (!menuItem) { return; } if (menuItem.value === '_remove') { this.remove.emit(this.identifier); return; } this.menuItemSelected.emit(menuItem); }; this.handleActionMenuCancel = (event) => { event.stopPropagation(); }; this.language = 'en'; this.text = undefined; this.icon = undefined; this.image = undefined; this.link = undefined; this.badge = undefined; this.disabled = false; this.readonly = false; this.selected = false; this.invalid = false; this.removable = false; this.type = 'default'; this.loading = false; this.progress = undefined; this.identifier = crypto.randomUUID(); this.size = 'default'; this.menuItems = []; } componentWillLoad() { makeEnterClickable(this.host); } disconnectedCallback() { removeEnterClickable(this.host); } render() { return (h(Host, { onClick: this.filterClickWhenDisabled }, this.link ? this.renderAsLink() : this.renderAsButton())); } renderPicture() { var _a, _b; const icon = getIconName(this.icon); if (!icon && !this.image) { return; } if (!isEmpty(this.image)) { return (h("img", { src: this.image.src, alt: this.image.alt, loading: "lazy" })); } return (h("limel-icon", { badge: true, name: icon, style: { color: `${(_a = this.icon) === null || _a === void 0 ? void 0 : _a.color}`, 'background-color': `${(_b = this.icon) === null || _b === void 0 ? void 0 : _b.backgroundColor}`, } })); } renderBadge() { if (!this.badge) { return; } return h("limel-badge", { label: this.badge }); } renderRemoveButton() { var _a; if (!this.removable || this.readonly || this.disabled || !!((_a = this.menuItems) === null || _a === void 0 ? void 0 : _a.length)) { return; } const svgData = '<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-width="2" d="m8 8 16 16M24 8 8 24"/></svg>'; return (h("button", { class: "trailing-button remove-button", tabIndex: -1, "aria-label": this.removeChipLabel(), "aria-controls": 'chip-' + this.identifier, innerHTML: svgData, onClick: this.handleRemoveClick })); } renderActionsMenu() { var _a; if (!((_a = this.menuItems) === null || _a === void 0 ? void 0 : _a.length)) { return; } const svgData = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" xml:space="preserve"><circle fill="currentColor" cx="16" cy="16" r="2"/><circle fill="currentColor" cx="16" cy="24" r="2"/><circle fill="currentColor" cx="16" cy="8" r="2"/></svg>'; const menuItems = this.getMenuItems(); return (h("limel-menu", { items: menuItems, onSelect: this.handleActionMenuSelect, openDirection: "bottom-end", onCancel: this.handleActionMenuCancel }, h("button", { slot: "trigger", disabled: this.disabled, class: "trailing-button", "aria-label": this.actionMenuLabel(), innerHTML: svgData }))); } getMenuItems() { let menuItems = [...this.menuItems]; if (this.removable) { menuItems = [ ...menuItems, { separator: true }, { text: this.removeChipLabel(), icon: { name: 'delete_sign', color: 'rgb(var(--color-red-default))', }, value: '_remove', }, ]; } return menuItems; } renderSpinner() { if (!this.loading) { return; } return h("limel-linear-progress", { indeterminate: true }); } renderProgressBar() { if (!this.progress) { return; } const currentPercentage = this.progress + '%'; return (h("div", { role: "progressbar", "aria-label": "%", "aria-valuemin": "0", "aria-valuemax": "100", "aria-valuenow": this.progress, style: { '--limel-chip-progress-percentage': currentPercentage, } })); } static get is() { return "limel-chip"; } static get encapsulation() { return "shadow"; } static get delegatesFocus() { return true; } static get originalStyleUrls() { return { "$": ["chip.scss"] }; } static get styleUrls() { return { "$": ["chip.css"] }; } static get properties() { return { "language": { "type": "string", "mutable": false, "complexType": { "original": "Languages", "resolved": "\"da\" | \"de\" | \"en\" | \"fi\" | \"fr\" | \"nb\" | \"nl\" | \"no\" | \"sv\"", "references": { "Languages": { "location": "import", "path": "../date-picker/date.types" } } }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines the language for translations.\nWill translate the translatable strings on the components." }, "attribute": "language", "reflect": true, "defaultValue": "'en'" }, "text": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Label displayed on the chip" }, "attribute": "text", "reflect": true }, "icon": { "type": "string", "mutable": false, "complexType": { "original": "string | Icon", "resolved": "Icon | string", "references": { "Icon": { "location": "import", "path": "../../global/shared-types/icon.types" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Icon of the chip." }, "attribute": "icon", "reflect": false }, "image": { "type": "unknown", "mutable": false, "complexType": { "original": "Image", "resolved": "Image", "references": { "Image": { "location": "import", "path": "../../global/shared-types/image.types" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "A picture to be displayed instead of the icon on the chip." } }, "link": { "type": "unknown", "mutable": false, "complexType": { "original": "Omit<Link, 'text'>", "resolved": "{ title?: string; target?: string; href: string; rel?: string; }", "references": { "Omit": { "location": "global" }, "Link": { "location": "import", "path": "../../global/shared-types/link.types" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "If supplied, the chip will become a clickable link." } }, "badge": { "type": "any", "mutable": false, "complexType": { "original": "string | number", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The value of the badge, displayed on the chip." }, "attribute": "badge", "reflect": true }, "disabled": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to disable the chip." }, "attribute": "disabled", "reflect": true, "defaultValue": "false" }, "readonly": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to render the chip as a static UI element.\nUseful when the parent component has a `readonly` state." }, "attribute": "readonly", "reflect": true, "defaultValue": "false" }, "selected": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to visualize the chip in a \"selected\" state.\nThis is typically used when the chip is used in a chip-set\nalong with other chips." }, "attribute": "selected", "reflect": true, "defaultValue": "false" }, "invalid": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to visualize the chip in an \"invalid\" or \"error\" state." }, "attribute": "invalid", "reflect": true, "defaultValue": "false" }, "removable": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Set to `true` to render a remove button on the chip." }, "attribute": "removable", "reflect": true, "defaultValue": "false" }, "type": { "type": "string", "mutable": false, "complexType": { "original": "ChipType", "resolved": "\"default\" | \"filter\"", "references": { "ChipType": { "location": "import", "path": "../chip-set/chip.types" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Set to `filter` to render the chip with a distinct style\nsuitable for visualizing filters." }, "attribute": "type", "reflect": true, "defaultValue": "'default'" }, "loading": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Set to `true` to put the component in the `loading` state,\nand render an indeterminate progress indicator inside the chip.\nThis does _not_ disable the interactivity of the chip!" }, "attribute": "loading", "reflect": true, "defaultValue": "false" }, "progress": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Reflects the current value of a progress bar on the chip,\nvisualizing the percentage of an ongoing process.\nMust be a number between `0` and `100`." }, "attribute": "progress", "reflect": true }, "identifier": { "type": "any", "mutable": false, "complexType": { "original": "number | string", "resolved": "number | string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Identifier for the chip. Must be unique." }, "attribute": "identifier", "reflect": true, "defaultValue": "crypto.randomUUID()" }, "size": { "type": "string", "mutable": false, "complexType": { "original": "'small' | 'default'", "resolved": "\"default\" | \"small\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines the size of the chip." }, "attribute": "size", "reflect": true, "defaultValue": "'default'" }, "menuItems": { "type": "unknown", "mutable": false, "complexType": { "original": "Array<MenuItem | ListSeparator>", "resolved": "any[]", "references": { "Array": { "location": "global" }, "MenuItem": { "location": "import", "path": "../../components" }, "ListSeparator": { "location": "import", "path": "../list/list-item.types" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "When provided, the chip will render an ellipsis menu with the supplied items.\nAlso, this will hide the \"remove button\" when `removable={true}`, as\nthe remove button will automatically become the last item in the menu." }, "defaultValue": "[]" } }; } static get events() { return [{ "method": "remove", "name": "remove", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Fired when clicking on the remove button of a `removable` chip.\nThe value of `identifier` is emitted as the event detail." }, "complexType": { "original": "number | string", "resolved": "number | string", "references": {} } }, { "method": "menuItemSelected", "name": "menuItemSelected", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Emitted when a menu item is selected from the actions menu." }, "complexType": { "original": "MenuItem", "resolved": "MenuItem", "references": { "MenuItem": { "location": "import", "path": "../../components" } } } }]; } static get elementRef() { return "host"; } } //# sourceMappingURL=chip.js.map