import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
import {FormControlController} from "../../internal/form";
import {property, query} from 'lit/decorators.js';
import ZincElement from '../../internal/zinc-element';
import ZnSelect from "../select";
import type {ZincFormControl} from '../../internal/zinc-element';
import type {ZnSelectEvent} from "../../events/zn-select";
import styles from './linked-select.scss';
interface linkedSelectOption {
[key: string]: string;
}
interface linkedSelectOptions {
[key: string]: linkedSelectOption[];
}
/**
* @summary Short summary of the component's intended use.
* @documentation https://zinc.style/components/linked-select
* @status experimental
* @since 1.0
*
* @dependency zn-example
*
* @event zn-event-name - Emitted as an example.
*
* @slot - The default slot.
* @slot example - An example slot.
*
* @csspart base - The component's base wrapper.
*
* @cssproperty --example - An example CSS custom property.
*/
export default class ZnLinkedSelect extends ZincElement implements ZincFormControl {
static styles: CSSResultGroup = unsafeCSS(styles);
@property() name: string = "";
@property() value: string;
@property({type: Boolean, reflect: true}) checked = false;
@property({type: Array}) options: linkedSelectOptions;
@property({attribute: 'linked-select'}) linkedSelect: string = "";
@property({attribute: 'cache-key'}) cacheKey: string = "";
@property() label: string = "";
@query('zn-select') input: ZnSelect;
private linkedSelectElement: HTMLSelectElement | ZnSelect;
private readonly formControlController = new FormControlController(this, {
value: (input) => {
const selectElement = this.input;
if (selectElement) {
return selectElement.value;
}
return input.value;
}
});
get displayLabel() {
return (this.input as ZnSelect).displayLabel;
}
get validity() {
return this.input?.validity;
}
get validationMessage() {
return this.input.validationMessage;
}
connectedCallback() {
super.connectedCallback();
let level = 0;
let currentElement: Element | null = this.parentElement;
while (level < 10 && currentElement && currentElement.tagName !== 'DOCUMENT') {
const element = currentElement.querySelector(`[id="${this.linkedSelect}"]`);
if (element instanceof ZnSelect) {
this.linkedSelectElement = element as ZnSelect;
break;
}
currentElement = currentElement.parentElement;
level++;
}
if (!this.linkedSelectElement) {
throw new Error(`Linked select element with name ${this.linkedSelect} not found`);
}
}
protected firstUpdated(_changedProperties: PropertyValues) {
this.linkedSelectElement?.addEventListener('zn-change', this.handleLinkedSelectChange);
this.input.addEventListener('zn-change', this.handleChange);
this.formControlController.updateValidity();
super.firstUpdated(_changedProperties);
}
disconnectedCallback() {
this.linkedSelectElement?.removeEventListener('zn-change', this.handleLinkedSelectChange);
super.disconnectedCallback();
}
checkValidity(): boolean {
return this.input.checkValidity();
}
getForm(): HTMLFormElement | null {
return this.formControlController.getForm();
}
reportValidity(): boolean {
return this.input.reportValidity();
}
setCustomValidity(message: string): void {
this.input.setCustomValidity(message);
this.formControlController.updateValidity();
}
public handleLinkedSelectChange = () => {
this.value = "";
this.requestUpdate();
this.formControlController.updateValidity();
};
public handleChange(e: Event) {
this.value = (e.target as HTMLSelectElement).value;
this.formControlController.updateValidity();
}
handleSelectChange = (e: ZnSelectEvent) => {
this.value = (e.target as ZnSelect).value as string;
}
render() {
let selected = this.linkedSelectElement?.value as string;
if (!selected && this.options) {
selected = Object.keys(this.options)[0];
}
const options: linkedSelectOption[] = selected ? this.options[selected] : [];
return html`
${options && Object.entries(options).map(([key, value]) => html`
${value}
`)}
`;
}
}