Files
Umbraco-CMS/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/input-language-picker/input-language-picker.element.ts
2023-04-04 14:13:03 +02:00

190 lines
5.0 KiB
TypeScript

import { css, html } from 'lit';
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
import { customElement, property, state } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { FormControlMixin } from '@umbraco-ui/uui-base/lib/mixins';
import { UmbLanguageRepository } from '../../../settings/languages/repository/language.repository';
import {
UmbModalContext,
UMB_MODAL_CONTEXT_TOKEN,
UMB_CONFIRM_MODAL,
UMB_LANGUAGE_PICKER_MODAL,
} from '@umbraco-cms/backoffice/modal';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/events';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import type { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@customElement('umb-input-language-picker')
export class UmbInputLanguagePickerElement extends FormControlMixin(UmbLitElement) {
static styles = [
UUITextStyles,
css`
#add-button {
width: 100%;
}
`,
];
/**
* This is a minimum amount of selected items in this input.
* @type {number}
* @attr
* @default undefined
*/
@property({ type: Number })
min?: number;
/**
* Min validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
minMessage = 'This field need more items';
/**
* This is a maximum amount of selected items in this input.
* @type {number}
* @attr
* @default undefined
*/
@property({ type: Number })
max?: number;
/**
* Max validation message.
* @type {boolean}
* @attr
* @default
*/
@property({ type: String, attribute: 'min-message' })
maxMessage = 'This field exceeds the allowed amount of items';
@property({ type: Object, attribute: false })
public filter: (language: LanguageResponseModel) => boolean = () => true;
private _selectedIsoCodes: Array<string> = [];
public get selectedIsoCodes(): Array<string> {
return this._selectedIsoCodes;
}
public set selectedIsoCodes(isoCodes: Array<string>) {
this._selectedIsoCodes = isoCodes;
super.value = isoCodes.join(',');
this._observePickedItems();
}
@property()
public set value(isoCodesString: string) {
if (isoCodesString !== this._value) {
this.selectedIsoCodes = isoCodesString.split(/[ ,]+/);
}
}
@state()
private _items?: Array<LanguageResponseModel>;
private _modalContext?: UmbModalContext;
private _repository = new UmbLanguageRepository(this);
private _pickedItemsObserver?: UmbObserverController<LanguageResponseModel[]>;
constructor() {
super();
this.addValidator(
'rangeUnderflow',
() => this.minMessage,
() => !!this.min && this._selectedIsoCodes.length < this.min
);
this.addValidator(
'rangeOverflow',
() => this.maxMessage,
() => !!this.max && this._selectedIsoCodes.length > this.max
);
this.consumeContext(UMB_MODAL_CONTEXT_TOKEN, (instance) => {
this._modalContext = instance;
});
}
protected getFormElement() {
return undefined;
}
private async _observePickedItems() {
this._pickedItemsObserver?.destroy();
if (!this._repository) return;
const { asObservable } = await this._repository.requestItems(this._selectedIsoCodes);
this._pickedItemsObserver = this.observe(asObservable(), (items) => {
this._items = items;
});
}
private _openPicker() {
const modalHandler = this._modalContext?.open(UMB_LANGUAGE_PICKER_MODAL, {
multiple: this.max === 1 ? false : true,
selection: [...this._selectedIsoCodes],
filter: this.filter,
});
modalHandler?.onSubmit().then(({ selection }) => {
this._setSelection(selection);
});
}
private _removeItem(item: LanguageResponseModel) {
const modalHandler = this._modalContext?.open(UMB_CONFIRM_MODAL, {
color: 'danger',
headline: `Remove ${item.name}?`,
content: 'Are you sure you want to remove this item',
confirmLabel: 'Remove',
});
modalHandler?.onSubmit().then(() => {
const newSelection = this._selectedIsoCodes.filter((value) => value !== item.isoCode);
this._setSelection(newSelection);
});
}
private _setSelection(newSelection: Array<string>) {
this.selectedIsoCodes = newSelection;
this.dispatchEvent(new UmbChangeEvent());
}
render() {
return html`
${this._items?.map((item) => this._renderItem(item))}
<uui-button
id="add-button"
look="placeholder"
@click=${this._openPicker}
label="open"
?disabled="${this._selectedIsoCodes.length === this.max}"
>Add</uui-button
>
`;
}
private _renderItem(item: LanguageResponseModel) {
return html`
<!-- TODO: add language ref element -->
<uui-ref-node name=${ifDefined(item.name === null ? undefined : item.name)} detail=${ifDefined(item.isoCode)}>
<uui-action-bar slot="actions">
<uui-button @click=${() => this._removeItem(item)} label="Remove ${item.name}">Remove</uui-button>
</uui-action-bar>
</uui-ref-node>
`;
}
}
export default UmbInputLanguagePickerElement;
declare global {
interface HTMLElementTagNameMap {
'umb-input-language-picker': UmbInputLanguagePickerElement;
}
}