lion/packages/ui/components/listbox/LionOption.js
2022-10-31 16:55:07 +01:00

149 lines
3.4 KiB
JavaScript

import { css, html, LitElement } from 'lit';
import { ChoiceInputMixin, FormRegisteringMixin } from '@lion/ui/form-core.js';
import { DisabledMixin, SlotMixin } from '@lion/ui/core.js';
/**
* @typedef {import('@lion/core').TemplateResult } TemplateResult
* @typedef {import('@lion/form-core/types/choice-group/ChoiceGroupMixinTypes').ChoiceGroupHost } ChoiceGroupHost
* @typedef {import('../types/LionOption').LionOptionHost } LionOptionHost
*/
/**
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option
* Can be a child of datalist/select, or role="listbox"
*
* Element gets state supplied externally, reflects this to attributes,
* enabling SubClassers to style based on those states
*/
export class LionOption extends DisabledMixin(
ChoiceInputMixin(FormRegisteringMixin(SlotMixin(LitElement))),
) {
/** @type {any} */
static get properties() {
return {
active: {
type: Boolean,
reflect: true,
},
};
}
static get styles() {
return [
css`
:host {
display: block;
background-color: white;
padding: 4px;
cursor: default;
}
:host([hidden]) {
display: none;
}
:host(:hover) {
background-color: #eee;
}
:host([active]) {
background-color: #ddd;
}
:host([checked]) {
background-color: #bde4ff;
}
:host([disabled]) {
color: #adadad;
}
`,
];
}
/**
* @override We want to start with a clean slate, so we omit slots inherited from FormControl
*/
// eslint-disable-next-line class-methods-use-this
get slots() {
return {};
}
constructor() {
super();
this.active = false;
/** @private */
this.__onClick = this.__onClick.bind(this);
/** @private */
this.__registerEventListeners();
}
/**
* @param {string} name
* @param {unknown} oldValue
*/
requestUpdate(name, oldValue) {
super.requestUpdate(name, oldValue);
if (name === 'active' && this.active !== oldValue) {
this.dispatchEvent(new Event('active-changed', { bubbles: true }));
}
}
/**
* @param {import('@lion/core').PropertyValues } changedProperties
*/
updated(changedProperties) {
super.updated(changedProperties);
if (changedProperties.has('checked')) {
this.setAttribute('aria-selected', `${this.checked}`);
}
if (changedProperties.has('disabled')) {
this.setAttribute('aria-disabled', `${this.disabled}`);
}
}
/**
*
* @returns {TemplateResult}
*/
render() {
return html`
<div class="choice-field__label">
<slot></slot>
</div>
`;
}
connectedCallback() {
super.connectedCallback();
this.setAttribute('role', 'option');
}
/** @private */
__registerEventListeners() {
this.addEventListener('click', this.__onClick);
}
/** @private */
__unRegisterEventListeners() {
this.removeEventListener('click', this.__onClick);
}
/** @private */
__onClick() {
if (this.disabled) {
return;
}
const parentForm = /** @type {unknown} */ (this._parentFormGroup);
this._isHandlingUserInput = true;
if (parentForm && /** @type {ChoiceGroupHost} */ (parentForm).multipleChoice) {
this.checked = !this.checked;
this.active = !this.active;
} else {
this.checked = true;
this.active = true;
}
this._isHandlingUserInput = false;
}
}