import { html, css, TemplateResult, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { OmniElement } from '../core/OmniElement.js'; import '../icons/Indeterminate.icon.js'; import '../icons/Check.icon.js'; /** * Control that allows a selection to be made. * * @import * ```js * import '@capitec/omni-components/check'; * ``` * * @example * ```html * * * ``` * * @element omni-check * * Registry of all properties defined by the component. * * @slot - Content to render inside the component. * @slot indeterminate_icon - Replaces the icon for the indeterminate state * @slot check_icon - Replaces the icon for the checked state * * @fires {CustomEvent<{ old: Boolean; new: Boolean; }>} value-change - Dispatched when the control value is changed to either on or off. * * @cssprop --omni-check-label-font-color - Label font color. * @cssprop --omni-check-label-font-family - Label font family. * @cssprop --omni-check-label-font-size - Label font size. * @cssprop --omni-check-label-font-weight - Label font weight. * @cssprop --omni-check-label-spacing - Label spacing. * * @cssprop --omni-check-hint-label-font-color - Hint font color. * @cssprop --omni-check-hint-label-font-family - Hint font family. * @cssprop --omni-check-hint-label-font-size - Hint font size. * @cssprop --omni-check-hint-label-font-weight - Hint font weight. * @cssprop --omni-check-hint-label-padding-top - Hint top padding. * * @cssprop --omni-check-error-label-font-color - Error font color. * @cssprop --omni-check-error-label-font-family - Error font family. * @cssprop --omni-check-error-label-font-size - Error font size. * @cssprop --omni-check-error-label-font-weight - Error font weight. * @cssprop --omni-check-error-label-padding-top - Error top padding. * * @cssprop --omni-check-width - Width. * @cssprop --omni-check-height - Height. * @cssprop --omni-check-background-color - Background color. * * @cssprop --omni-check-border-width - Border width. * @cssprop --omni-check-border-color - Border color. * @cssprop --omni-check-border-radius - Border radius. * * @cssprop --omni-check-indicator-border-width - Indicator Border width. * @cssprop --omni-check-indicator-border-color - Indicator Border color. * @cssprop --omni-check-indicator-color - Indicator color. * @cssprop --omni-check-indicator-icon-width - Indicator icon width. * @cssprop --omni-check-indicator-icon-height - Indicator icon height. * * @cssprop --omni-check-checked-background-color - Checked Background color. * @cssprop --omni-check-indeterminate-background-color - Indeterminate Background color. * @cssprop --omni-check-disabled-background-color - Disabled Background color. * @cssprop --omni-check-disabled-border-color - Disabled border color. * * @cssprop --omni-check-hover-box-shadow - Hover box shadow. * @cssprop --omni-check-hover-background-color - Hover background color. * */ @customElement('omni-check') export class Check extends OmniElement { /** * Text label. * @attr */ @property({ type: String, reflect: true }) label?: string; /** * Data associated with the component. * @attr */ @property({ type: Object, reflect: true }) data?: object; /** * Hint message to assist the user. * @attr */ @property({ type: String, reflect: true }) hint?: string; /** * An error message to guide users to correct a mistake. * @attr */ @property({ type: String, reflect: true }) error?: string; /** * Indicator if the component is checked or not. * @attr */ @property({ type: Boolean, reflect: true }) checked?: boolean; /** * Indicator if the component is disabled. * @attr */ @property({ type: Boolean, reflect: true }) disabled?: boolean; /** * Indicator if the component is in and indeterminate state. * @attr */ @property({ type: Boolean, reflect: true }) indeterminate?: boolean; override connectedCallback(): void { super.connectedCallback(); this.tabIndex = this.disabled ? -1 : 0; this.addEventListener('click', this._click); } override focus() { this.shadowRoot?.getElementById('content')?.focus(); } _click(e: MouseEvent): void { // Ignore the event if the component is disabled. if (this.disabled) { return e.stopImmediatePropagation(); } this._toggleChecked(e); } _keyDown(e: KeyboardEvent): void { if (this.disabled) { return e.stopImmediatePropagation(); } // Intercept space and enter key events to toggle the component checked state. const keyCode = (e.code || '').toUpperCase(); if (keyCode === 'SPACE' || keyCode === 'ENTER') { // Toggle the component checked state. this._toggleChecked(e); // Prevent the key event from propagating further. return e.preventDefault(); } } _toggleChecked(e: MouseEvent | KeyboardEvent): void { const oldValue = this.checked; this.checked = !oldValue; this.dispatchEvent( new CustomEvent('value-change', { detail: { old: oldValue, new: this.checked } }) ); e.stopPropagation(); } static override get styles() { return [ super.styles, css` :host { outline: none; } /* CONTAINER STYLES */ .container { display: flex; align-items: center; } /* LABEL STYLES */ .container > .label { color: var(--omni-check-label-font-color, var(--omni-font-color)); font-family: var(--omni-check-label-font-family, var(--omni-font-family)); font-size: var(--omni-check-label-font-size, var(--omni-font-size)); font-weight: var(--omni-check-label-font-weight, var(--omni-font-weight)); margin-left: var(--omni-check-label-spacing, 8px); cursor: pointer; } .container > .label > .hint { color: var(--omni-check-hint-label-font-color, var(--omni-hint-font-color)); font-family: var(--omni-check-hint-label-font-family, var(--omni-font-family)); font-size: var(--omni-check-hint-label-font-size, 0.86em); font-weight: var(--omni-check-hint-label-font-weight, 300); padding-top: var(--omni-check-hint-label-padding-top, 4px); } .container > .label > .error { color: var(--omni-check-error-label-font-color, var(--omni-error-font-color)); font-family: var(--omni-check-error-label-font-family, var(--omni-font-family)); font-size: var(--omni-check-error-label-font-size, 0.86em); font-weight: var(--omni-check-error-label-font-weight, 300); padding-top: var(--omni-check-error-label-padding-top, 4px); } /* CHECK BOX STYLES */ .container > #content { box-sizing: border-box; cursor: pointer; display: flex; align-items: center; justify-content: center; width: var(--omni-check-width, 22px); height: var(--omni-check-height, 22px); background-color: var(--omni-check-background-color, var(--omni-background-color)); border-width: var(--omni-check-border-width, var(--omni-border-width)); border-style: solid; border-color: var(--omni-check-border-color, var(--omni-primary-color)); border-radius: var(--omni-check-border-radius, var(--omni-border-radius)); outline: 0; } .indicator { width: 100%; height: 100%; display: flex; align-items: center; align-content: center; justify-content: center; } .container.checked > #content > .indicator { border-width: var(--omni-check-indicator-border-width, 1px); border-style: solid; border-color: var(--omni-check-indicator-border-color, var(--omni-primary-color)); border-radius: var(--omni-check-border-radius, var(--omni-border-radius)); color: var(--omni-check-indicator-color, var(--omni-background-color)); display: flex; align-items: center; justify-content: center; fill: currentColor; } /* CHECKED STATE STYLES */ .container.checked > #content { background-color: var(--omni-check-checked-background-color, var(--omni-primary-color)); } /* INDETERMINATE STATE STYLES */ .container.indeterminate > #content { background-color: var(--omni-check-indeterminate-background-color, var(--omni-primary-color)); color: var(--omni-check-indicator-color, var(--omni-background-color)); } .container.indeterminate > #content > .indicator { color: var(--omni-check-indicator-color, var(--omni-background-color)); fill: currentColor; } /* HOVER STATE STYLES */ .container > #content:hover { box-shadow: var(--omni-check-hover-box-shadow, var(--omni-box-shadow)); background-color: var(--omni-check-hover-background-color, var(--omni-box-shadow-color)); } .container.checked:hover > #content { background-color: var(--omni-check-checked-background-color, var(--omni-primary-color)); } .container.checked.disabled:hover > #content { background-color: var(--omni-check-disabled-background-color, var(--omni-disabled-background-color)); } .container.indeterminate:hover > #content { background-color: var(--omni-check-indeterminate-background-color, var(--omni-primary-color)); } .container.disabled.indeterminate:hover > #content { background-color: var(--omni-check-disabled-background-color, var(--omni-disabled-background-color)); } /* DISABLED STATE STYLES */ .container.disabled > #content { cursor: default; border-color: var(--omni-check-disabled-border-color, var(--omni-disabled-background-color)); background-color: var(--omni-check-disabled-background-color, var(--omni-disabled-background-color)); } .container.disabled:hover > #content { box-shadow: none; } .container.checked.disabled > #content > .indicator { border-color: transparent; } .indicator-icon, ::slotted([slot='indeterminate_icon']), ::slotted([slot='check_icon']) { width: var(--omni-check-indicator-icon-width, 16px); height: var(--omni-check-indicator-icon-height, 16px); } ` ]; } override render(): TemplateResult { return html`
${ this.indeterminate ? html` ` : this.checked ? html` ` : nothing }
`; } } declare global { interface HTMLElementTagNameMap { 'omni-check': Check; } }