@conectate/ct-list
Version:
Wrapper from LitElement
211 lines (206 loc) • 6.23 kB
JavaScript
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" =${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 };