UNPKG

@limetech/lime-elements

Version:
446 lines (445 loc) • 17.8 kB
import { h, Host, } from "@stencil/core"; import { isItem } from "../action-bar/is-item"; import { getIconName } from "../icon/get-icon-props"; import { getMouseEventHandlers } from "../../util/3d-tilt-hover-effect"; /** * Card is a component that displays content about a single topic, * in a structured way. It can contain a header, and some supporting media such * as an image or an icon, a body of text, or optional actions. * * @exampleComponent limel-example-card-basic * @exampleComponent limel-example-card-image * @exampleComponent limel-example-card-actions * @exampleComponent limel-example-card-clickable * @exampleComponent limel-example-card-3d-effect * @exampleComponent limel-example-card-selected * @exampleComponent limel-example-card-orientation * @exampleComponent limel-example-card-slot * @exampleComponent limel-example-card-styling * @exampleComponent limel-example-card-scrollable-shadow */ export class Card { constructor() { /** * Actions to display in the card, * to provide the user with options to interact with the content. */ this.actions = []; /** * When true, improve the accessibility of the component and hints the user * that the card can be interacted width. */ this.clickable = false; /** * The orientation of the card, * specially useful when the card has an image. */ this.orientation = 'portrait'; /** * When `true`, the card displays a visual selected state (highlighted border shadow). * Should be used together with `clickable` to let users toggle selection. */ this.selected = false; /** * When `true`, the card displays a glow effect on hover, * following the 3D tilt hover effect. */ this.show3dEffect = true; this.canScrollUp = false; this.canScrollDown = false; this.setMarkdownElement = (element) => { var _a; if (element === this.markdownElement) { return; } if (this.markdownElement) { (_a = this.markdownResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); this.markdownElement.removeEventListener('scroll', this.checkIfScrollable); } this.markdownElement = element; if (!this.markdownElement) { return; } this.markdownResizeObserver = new ResizeObserver(this.checkIfScrollable); this.markdownResizeObserver.observe(this.markdownElement); this.markdownElement.addEventListener('scroll', this.checkIfScrollable, { passive: true }); this.checkIfScrollable(); }; this.checkIfScrollable = () => { if (!this.markdownElement) { return; } const scrollHeight = this.markdownElement.scrollHeight; const clientHeight = this.markdownElement.clientHeight; const scrollTop = this.markdownElement.scrollTop; const isScrollable = scrollHeight > clientHeight; // Use a 2px tolerance to handle sub-pixel rounding issues const isScrolledToBottom = scrollTop + clientHeight >= scrollHeight - 2; const shouldShowBottomShadow = isScrollable && !isScrolledToBottom; const isScrolledToTop = scrollTop <= 1; const shouldShowTopShadow = isScrollable && !isScrolledToTop; if (this.canScrollDown !== shouldShowBottomShadow) { this.canScrollDown = shouldShowBottomShadow; } if (this.canScrollUp !== shouldShowTopShadow) { this.canScrollUp = shouldShowTopShadow; } }; this.handleActionSelect = (event) => { event.stopPropagation(); if (isItem(event.detail)) { this.actionSelected.emit(event.detail); } }; this.handleKeyDown = (event) => { if (event.target !== event.currentTarget) { return; } if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.host.click(); } }; } componentWillLoad() { const { handleMouseEnter, handleMouseLeave } = getMouseEventHandlers(this.host); this.handleMouseEnter = handleMouseEnter; this.handleMouseLeave = handleMouseLeave; } disconnectedCallback() { var _a, _b; (_a = this.markdownResizeObserver) === null || _a === void 0 ? void 0 : _a.disconnect(); (_b = this.markdownElement) === null || _b === void 0 ? void 0 : _b.removeEventListener('scroll', this.checkIfScrollable); } componentDidLoad() { this.setMarkdownElement(this.markdownElement); } render() { return (h(Host, { key: 'fbb3c4a47fe819511f56bf250b52a5effdf64f15', onMouseEnter: this.show3dEffect ? this.handleMouseEnter : undefined, onMouseLeave: this.show3dEffect ? this.handleMouseLeave : undefined }, h("section", { key: '735df6ada728adabd822698395a280742a49cee2', tabindex: this.clickable ? 0 : undefined, role: this.clickable ? 'button' : undefined, "aria-pressed": this.clickable ? String(this.selected) : undefined, onKeyDown: this.clickable ? this.handleKeyDown : undefined }, this.renderImage(), h("div", { key: '2382833712ee8e01bbd702b30b10ea6d3a0f7dcb', class: "body" }, this.renderHeader(), this.renderSlot(), this.renderValue(), this.renderActionBar()), this.show3dEffect && h("limel-3d-hover-effect-glow", { key: 'd13545dc6829b59bce0d870dfdeed409f2608529' })))); } renderImage() { var _a; if (!((_a = this.image) === null || _a === void 0 ? void 0 : _a.src)) { return; } return (h("div", { class: "image-wrapper" }, h("img", { src: this.image.src, alt: this.image.alt, loading: "lazy" }))); } renderHeader() { if (!this.heading && !this.subheading && !this.icon) { return; } return (h("header", null, this.renderIcon(), h("div", { class: "headings" }, this.renderHeading(), this.renderSubheading()))); } renderIcon() { var _a; const icon = getIconName(this.icon); const color = typeof this.icon === 'string' ? undefined : (_a = this.icon) === null || _a === void 0 ? void 0 : _a.color; if (!icon) { return; } return (h("limel-icon", { style: { color: `${color}`, }, badge: true, name: icon })); } renderHeading() { if (!this.heading) { return; } return h("h1", { class: "title" }, this.heading); } renderSubheading() { if (!this.subheading) { return; } return h("h2", null, this.subheading); } renderSlot() { return h("slot", { name: "component" }); } renderValue() { if (!this.value) { return; } return (h("div", { class: { 'markdown-wrapper': true, 'can-scroll-up': this.canScrollUp, 'can-scroll-down': this.canScrollDown, } }, h("limel-markdown", { class: "body-text", ref: this.setMarkdownElement, value: this.value }))); } renderActionBar() { if (this.actions.length === 0) { return; } return (h("limel-action-bar", { actions: this.actions, onItemSelected: this.handleActionSelect })); } static get is() { return "limel-card"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["card.scss"] }; } static get styleUrls() { return { "$": ["card.css"] }; } static get properties() { return { "heading": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Heading of the card,\nto provide a short title about the context." }, "getter": false, "setter": false, "reflect": true, "attribute": "heading" }, "subheading": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Subheading of the card\nto provide a short description of the context." }, "getter": false, "setter": false, "reflect": true, "attribute": "subheading" }, "image": { "type": "unknown", "mutable": false, "complexType": { "original": "Image", "resolved": "Image", "references": { "Image": { "location": "import", "path": "../../global/shared-types/image.types", "id": "src/global/shared-types/image.types.ts::Image", "referenceLocation": "Image" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "A hero image to display in the card,\nto enrich the content with visual information." }, "getter": false, "setter": false }, "icon": { "type": "string", "mutable": false, "complexType": { "original": "string | Icon", "resolved": "Icon | string", "references": { "Icon": { "location": "import", "path": "../../global/shared-types/icon.types", "id": "src/global/shared-types/icon.types.ts::Icon", "referenceLocation": "Icon" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "An icon, to display along with the heading and subheading." }, "getter": false, "setter": false, "reflect": true, "attribute": "icon" }, "value": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "The content of the card.\nSupports markdown, to provide a rich text experience." }, "getter": false, "setter": false, "reflect": false, "attribute": "value" }, "actions": { "type": "unknown", "mutable": false, "complexType": { "original": "Array<ActionBarItem | ListSeparator>", "resolved": "(ListSeparator | ActionBarItem)[]", "references": { "Array": { "location": "global", "id": "global::Array" }, "ActionBarItem": { "location": "import", "path": "../action-bar/action-bar.types", "id": "src/components/action-bar/action-bar.types.ts::ActionBarItem", "referenceLocation": "ActionBarItem" }, "ListSeparator": { "location": "import", "path": "../../global/shared-types/separator.types", "id": "src/global/shared-types/separator.types.ts::ListSeparator", "referenceLocation": "ListSeparator" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Actions to display in the card,\nto provide the user with options to interact with the content." }, "getter": false, "setter": false, "defaultValue": "[]" }, "clickable": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When true, improve the accessibility of the component and hints the user\nthat the card can be interacted width." }, "getter": false, "setter": false, "reflect": true, "attribute": "clickable", "defaultValue": "false" }, "orientation": { "type": "string", "mutable": false, "complexType": { "original": "'landscape' | 'portrait'", "resolved": "\"landscape\" | \"portrait\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "The orientation of the card,\nspecially useful when the card has an image." }, "getter": false, "setter": false, "reflect": true, "attribute": "orientation", "defaultValue": "'portrait'" }, "selected": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the card displays a visual selected state (highlighted border shadow).\nShould be used together with `clickable` to let users toggle selection." }, "getter": false, "setter": false, "reflect": true, "attribute": "selected", "defaultValue": "false" }, "show3dEffect": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "When `true`, the card displays a glow effect on hover,\nfollowing the 3D tilt hover effect." }, "getter": false, "setter": false, "reflect": true, "attribute": "show-3d-effect", "defaultValue": "true" } }; } static get states() { return { "canScrollUp": {}, "canScrollDown": {} }; } static get events() { return [{ "method": "actionSelected", "name": "actionSelected", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Fired when a action bar item has been clicked." }, "complexType": { "original": "ActionBarItem", "resolved": "ActionBarItemOnlyIcon<any> | ActionBarItemWithLabel<any>", "references": { "ActionBarItem": { "location": "import", "path": "../action-bar/action-bar.types", "id": "src/components/action-bar/action-bar.types.ts::ActionBarItem", "referenceLocation": "ActionBarItem" } } } }]; } static get elementRef() { return "host"; } }