import {classMap} from "lit/directives/class-map.js"; import {type CSSResultGroup, html, nothing, type PropertyValues, unsafeCSS} from 'lit'; import {deepQuerySelectorAll} from "../../utilities/query"; import {HasSlotController} from "../../internal/slot"; import {ifDefined} from "lit/directives/if-defined.js"; import {property, query, state} from 'lit/decorators.js'; import ZincElement from "../../internal/zinc-element"; import ZnDialog from "../dialog"; import styles from './confirm.scss'; /** * @summary Short summary of the component's intended use. * @documentation https://zinc.style/components/confirm-modal * @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 ZnConfirm extends ZincElement { static styles: CSSResultGroup = unsafeCSS(styles); static dependencies = { 'zn-dialog': ZnDialog }; private readonly hasSlotController = new HasSlotController(this, '[default]', 'footer'); /** The dialog's theme variant. */ @property({reflect: true}) variant: 'default' | 'warning' | 'announcement' = 'default'; /** The dialog's size. */ @property({reflect: true}) size: 'small' | 'medium' | 'large' = 'medium'; /** The dialogs type, which will determine the icon and color. */ @property() type: 'warning' | 'error' | 'success' | 'info' = 'warning'; /** * Indicated whether of not the dialog is open. You can toggle this attribute to show and hide the dialog, or you can * use the `show()` and `hide()` methods and this attribute will reflect the dialog's state. */ @property({type: Boolean, reflect: true}) open = false; @property() caption: string = ''; @property() action: string = ''; @property() content: string = ''; @property() confirmText: string = "Confirm"; @property() cancelText: string = "Cancel"; @property({type: Boolean, attribute: 'hide-icon'}) hideIcon: boolean = false; /** * Show a loading state when the dialog is submitted. */ @property({type: Boolean, attribute: 'show-loading'}) showLoading: boolean = false; /** * The dialog's trigger element. This is used to open the dialog when clicked. If you do not provide a trigger, you * will need to manually open the dialog using the `show()` method. */ @property({reflect: true}) trigger: string; /** The Dialogs announcement text. */ @property() announcement: string = ''; /** The Dialogs footer text. */ @property({attribute: 'footer-text'}) footerText: string = ''; @query('zn-dialog') dialog: ZnDialog; /** Internal loading state used when showLoading is enabled */ @state() private loading: boolean = false; protected firstUpdated(_changedProperties: PropertyValues) { super.firstUpdated(_changedProperties); if (this.open) { this.dialog.show(); } } connectedCallback() { super.connectedCallback(); if (this.hasSlotController.test('trigger')) { const trigger = this.hasSlotController.getSlot('trigger'); trigger?.addEventListener('click', this.show); } else if (this.trigger) { const triggers = deepQuerySelectorAll('#' + this.trigger, this.parentElement as Element, ''); triggers.forEach((el: HTMLButtonElement) => { el.addEventListener('click', this.show); }); } } updateTriggers() { const triggers = deepQuerySelectorAll('#' + this.trigger, this.parentElement as Element, ''); triggers.forEach((el: HTMLButtonElement) => { // if already has a click listener, remove it el.removeEventListener('click', this.show); el.addEventListener('click', this.show); }); } show = (event: Event | undefined = undefined) => { const trigger = event?.target as HTMLButtonElement if (trigger?.disabled) return; this.loading = false; this.dialog.show(); } hide() { this.loading = false; this.dialog.hide(); } render() { const src = { 'warning': 'priority_high', 'error': 'priority_high', 'success': 'check', 'info': 'help' }; return html` ${this.announcement} ${!this.hideIcon ? html` ` : ''}
${!this.loading ? html` ${this.content ? html`${this.content}` : ''} ` : html` Loading...`}
${this.cancelText} ${this.confirmText} ${this.footerText}
`; } submitDialog() { // get the form from the slot let form = this.querySelector('form'); if (!form && this.action) { form = document.createElement('form') as HTMLFormElement; form.action = this.action; form.method = 'POST'; // copy data-attributes from the confirm element to the form Array.from(this.attributes).forEach(attr => { if (attr.name.startsWith('data-')) { form?.setAttribute(attr.name, attr.value); } }); this.querySelectorAll('input').forEach((el: HTMLInputElement) => { form?.appendChild(el.cloneNode() as Node); }) this.appendChild(form); } if (form && form.reportValidity()) { document.dispatchEvent(new CustomEvent('zn-register-element', { detail: {element: form} })) form.requestSubmit(); if (!this.showLoading) { this.hide(); } else { this.loading = true; this.dialog.closer.disabled = true; } } } }