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';
/**
* Control to select a single value from a group of values.
*
* @import
* ```js
* import '@capitec/omni-components/radio';
* ```
*
* @example
* ```html
*
*
* ```
*
* @element omni-radio
*
* Registry of all properties defined by the component.
*
* @slot - Content to render inside the component.
*
* @fires {CustomEvent<{ old: Boolean; new: Boolean; }>} value-change - Dispatched when the control value is changed to either on or off.
*
* @cssprop --omni-radio-width - Width.
* @cssprop --omni-radio-height - Height.
* @cssprop --omni-radio-padding - Padding.
*
* @cssprop --omni-radio-label-font-color - Label Font Color.
* @cssprop --omni-radio-label-font-family - Label Font Family.
* @cssprop --omni-radio-label-font-size - Label Font Size.
* @cssprop --omni-radio-label-font-weight - Label Font Weight.
* @cssprop --omni-radio-label-line-height - Label Line Height.
* @cssprop --omni-radio-label-spacing - Label Spacing.
*
* @cssprop --omni-input-hint-label-font-color - Hint Font Color.
* @cssprop --omni-input-hint-label-font-family - Hint Font Family.
* @cssprop --omni-input-hint-label-font-size - Hint Font Size.
* @cssprop --omni-input-hint-label-font-weight - Hint Font Weight.
*
* @cssprop --omni-input-error-label-font-color - Error Font Color.
* @cssprop --omni-input-error-label-font-family - Error Font Family.
* @cssprop --omni-input-error-label-font-size - Error Font Size.
* @cssprop --omni-input-error-label-font-weight - Error Font Weight.
*
* @cssprop --omni-radio-background-color - Background Color.
* @cssprop --omni-radio-border-width - Border Width.
* @cssprop --omni-radio-border-style - Border Style.
* @cssprop --omni-radio-border-color - Border Color.
* @cssprop --omni-radio-border-radius - Border Radius.
*
* @cssprop --omni-radio-indicator-border-width - Indicator Border Width.
* @cssprop --omni-radio-indicator-border-color - Indicator Border Color.
* @cssprop --omni-radio-border-radius - Indicator Border Radius.
* @cssprop --omni-radio-indicator-color - Indicator Color.
*
* @cssprop --omni-radio-checked-background-color - Checked Background color.
*
* @cssprop --omni-radio-hover-box-shadow - Hover Box Shadow.
* @cssprop --omni-radio-hover-background-color - Hover Background Color.
*
* @cssprop --omni-radio-disabled-border-color - Disabled Border Color.
* @cssprop --omni-radio-disabled-background-color - Disabled Background Color.
*
*/
@customElement('omni-radio')
export class Radio 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;
/**
* A 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;
override focus() {
this.shadowRoot?.getElementById('content')?.focus();
}
_click(event: MouseEvent): void {
// Ignore the event if the component is disabled.
if (this.disabled) {
return event.stopImmediatePropagation();
}
// Toggle the component checked state.
this._toggleChecked();
}
_keyDown(event: KeyboardEvent): void {
// Ignore the event if the component is disabled.
if (this.disabled) {
return event.stopImmediatePropagation();
}
// Intercept space and enter key events to toggle the component checked state.
const keyCode = (event.code || '').toUpperCase();
if (keyCode === 'SPACE' || keyCode === 'ENTER') {
// Toggle the component checked state.
this._toggleChecked();
// Prevent the key event from propagating further.
return event.preventDefault();
}
}
_toggleChecked(): void {
const oldValue = this.checked;
this.checked = !oldValue;
this.dispatchEvent(
new CustomEvent('value-change', {
detail: {
old: oldValue,
new: this.checked
}
})
);
}
static override get styles() {
return [
super.styles,
css`
:host {
--omni-radio-width: 24px;
--omni-radio-height: 24px;
--omni-radio-padding: 2px;
}
/* CONTAINER STYLES */
.container {
display: flex;
align-items: center;
}
.container > .label {
color: var(--omni-radio-label-font-color, var(--omni-font-color));
font-family: var(--omni-radio-label-font-family, var(--omni-font-family));
font-size: var(--omni-radio-label-font-size, var(--omni-font-size));
font-weight: var(--omni-radio-label-font-weight, var(--omni-font-weight));
line-height: var(--omni-radio-label-line-height, 20px);
margin-left: var(--omni-radio-label-spacing, 8px);
cursor: default;
}
.container > .label > .hint {
color: var(--omni-input-hint-label-font-color, var(--omni-hint-font-color));
font-family: var(--omni-input-hint-label-font-family, var(--omni-font-family));
font-size: var(--omni-input-hint-label-font-size, 0.86em);
font-weight: var(--omni-input-hint-label-font-weight, 300);
padding-top: 4px;
}
.container > .label > .error {
color: var(--omni-input-error-label-font-color, var(--omni-error-font-color));
font-family: var(--omni-input-error-label-font-family, var(--omni-font-family));
font-size: var(--omni-input-error-label-font-size, 0.86em);
font-weight: var(--omni-input-error-label-font-weight, 300);
padding-top: 4px;
}
/* RADIO BUTTON STYLES */
.container > #content {
box-sizing: border-box;
cursor: pointer;
display: flex;
align-items: center;
align-self: flex-start;
justify-content: center;
min-width: var(--omni-radio-width, 24px);
min-height: var(--omni-radio-height, 24px);
max-width: var(--omni-radio-width, 24px);
max-height: var(--omni-radio-height, 24px);
margin: var(--omni-radio-padding, 2px);
background-color: var(--omni-radio-background-color, var(--omni-background-color));
border-width: var(--omni-radio-border-width, var(--omni-border-width));
border-style: var(--omni-radio-border-style, solid);
border-color: var(--omni-radio-border-color, var(--omni-primary-color));
border-radius: var(--omni-radio-border-radius, 50%);
outline: 0;
}
.container.checked > #content > .indicator {
width: calc(var(--omni-radio-width) - var(--omni-radio-padding) * 2);
height: calc(var(--omni-radio-height) - var(--omni-radio-padding) * 2);
border-width: var(--omni-radio-indicator-border-width, var(--omni-border-width));
border-style: solid;
border-color: var(--omni-radio-indicator-border-color, var(--omni-background-color));
border-radius: var(--omni-radio-border-radius, 50%);
color: var(--omni-radio-indicator-color, var(--omni-background-color));
}
/* CHECKED STATE STYLES */
.container.checked > #content {
background-color: var(--omni-radio-checked-background-color, var(--omni-primary-color));
}
/* HOVER STATE STYLES */
.container > #content:hover {
box-shadow: var(--omni-radio-hover-box-shadow, var(--omni-box-shadow));
background-color: var(--omni-radio-hover-background-color, var(--omni-box-shadow-color));
}
.container.checked:hover > #content {
background-color: var(--omni-radio-checked-background-color, var(--omni-primary-color));
}
/* DISABLED STATE STYLES */
.container.disabled > #content {
cursor: default;
border-color: var(--omni-radio-disabled-border-color, var(--omni-disabled-border-color));
background-color: var(--omni-radio-disabled-background-color, var(--omni-disabled-background-color));
}
.container.disabled.checked > #content {
background-color: var(--omni-radio-disabled-background-color, var(--omni-disabled-background-color));
}
.container.disabled:hover > #content {
box-shadow: none;
}
:host(:not([disabled])) .container > #label {
cursor: pointer;
}
`
];
}
override render(): TemplateResult {
return html`
${this.checked ? html`
` : nothing}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
'omni-radio': Radio;
}
}