UNPKG

@conectate/ct-list

Version:
211 lines (206 loc) 6.23 kB
import { __decorate } from "tslib"; import "@conectate/ct-icon"; import { CtLit, customElement, property } from "@conectate/ct-lit"; import { css } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; import { html, literal } from "lit/static-html.js"; /** * A customizable list item component that can display icons, text, and additional content. * * This component can be used standalone or within menu/list structures and provides * a flexible layout with slots for prefix, main content, and suffix elements. * It can act as a button or as a link depending on the properties provided. * * Features: * - Display text with optional icons * - Can act as a navigation link when href/link is provided * - Auto-closes parent menus when clicked (configurable) * - Customizable appearance with CSS variables * - Responsive hover and active states * * @element ct-list-item * * @slot prefix - Content placed before the main content (left side in LTR layouts) * @slot - Default slot for main content * @slot suffix - Content placed after the main content (right side in LTR layouts) * * @cssprop --ct-list-item--white-space - Controls text wrapping (default: nowrap) * @cssprop --ct-icon-size - Size of the icon displayed (default: 21px) * @cssprop --color-outline - Color of the bottom border (default: transparent) * @cssprop --color-primary - Color used for outline when focused * * @example * ```html * <!-- Basic usage with text --> * <ct-list-item text="Settings"></ct-list-item> * * <!-- With icon --> * <ct-list-item icon="settings" text="Settings"></ct-list-item> * * <!-- As a link --> * <ct-list-item icon="home" text="Home" href="/home"></ct-list-item> * * <!-- With custom content --> * <ct-list-item> * <span slot="prefix">🔔</span> * Custom content here * <span slot="suffix">3</span> * </ct-list-item> * ``` */ let CtListItem = class CtListItem extends CtLit { constructor() { super(...arguments); /** * Custom SVG content for the icon (alternative to using the icon property) */ this.svg = ""; /** * Text content to display in the list item */ this.text = ""; /** * When true, clicking this item won't close parent menus * Useful for items that trigger actions that should keep the menu open */ this.keepOpen = false; /** * When true, hides the bottom border * Typically used for the last item or for custom styling */ this.hideoutline = false; /** * When true, hides the bottom border * Typically used for the last item or for custom styling */ this.role = "menuitem"; } static { this.styles = [ css ` :host { --ct-icon-size: 21px; } :host, a { display: flex; flex: 1; outline: none; cursor: pointer; color: inherit; text-decoration: none; } button, .btn { cursor: pointer; appearance: none; background: none; border: none; display: inline-flex; width: 100%; align-items: center; white-space: var(--ct-list-item--white-space, nowrap); font-family: inherit; font-size: inherit; font-weight: inherit; text-align: start; padding: 0; flex: 1; line-height: 1.75; color: inherit; outline-color: var(--color-primary); outline-offset: -5px; transition: background 0.2s ease-in-out; } :host(:hover) { background: #7c7c7c36; } .text { display: flex; flex-wrap: nowrap; flex: 1; border-bottom: 1px solid var(--color-outline, transparent); } .text span { padding: var(--ct-list-item--padding, 8px 16px 8px 0); flex: 1; } :host(:last-child) .text, :host([hideoutline]) .text { border-bottom: none; } ct-icon { margin: 0 16px; width: 21px; height: 21px; min-width: 21px; min-height: 21px; } .space { margin-right: 16px; } ` ]; } /** * Renders the list item as either a button or a link based on properties * @returns Rendered template */ render() { let tag = this.role == "button" ? literal `button` : literal `div`; let button = html `<${tag} class="btn" @click=${this.closeMenu} aria-label=${this.text} role=${this.role}> <slot name="prefix"></slot> ${this.icon || this.svg ? html `<ct-icon class="space" svg=${this.svg} icon=${ifDefined(this.icon ? this.icon : undefined)}></ct-icon>` : html `<div class="space"></div>`} <div class="text"> <span>${this.text}<slot></slot></span> <slot name="suffix"></slot> </div> </${tag}>`; let href = this.href || this.link; if (href) return html `<a href="${href}" target="${ifDefined(this.target)}"> ${button}</a> `; return button; } /** * Closes the parent menu when this item is clicked * Respects the keepOpen property * @param e Click event */ closeMenu(e) { let menu = this.closest("ct-button-menu") || this.closest("md-menu"); if (menu && !this.keepOpen) { this.blur(); // @ts-ignore menu.open = false; } } }; __decorate([ property({ type: String }) ], CtListItem.prototype, "svg", void 0); __decorate([ property({ type: String }) ], CtListItem.prototype, "icon", void 0); __decorate([ property({ type: String }) ], CtListItem.prototype, "text", void 0); __decorate([ property({ type: String }) ], CtListItem.prototype, "link", void 0); __decorate([ property({ type: String }) ], CtListItem.prototype, "href", void 0); __decorate([ property({ type: String }) ], CtListItem.prototype, "target", void 0); __decorate([ property({ type: Boolean, reflect: true, attribute: "keep-open" }) ], CtListItem.prototype, "keepOpen", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], CtListItem.prototype, "hideoutline", void 0); __decorate([ property({ type: String, reflect: true }) ], CtListItem.prototype, "role", void 0); CtListItem = __decorate([ customElement("ct-list-item") ], CtListItem); export { CtListItem };