import shoelace from "../Styles/shoelace"; import styles from "./Et2FileItem.styles"; import {customElement} from "lit/decorators/custom-element.js"; import {Et2Widget} from "../Et2Widget/Et2Widget"; import {html, LitElement, nothing} from "lit"; import {HasSlotController} from "../Et2Widget/slot"; import {property} from "lit/decorators/property.js"; import {classMap} from "lit/directives/class-map.js"; import {waitForEvent} from "../Et2Widget/event"; import {ifDefined} from "lit/directives/if-defined.js"; /** * @summary Displays a single (uploaded) file with file information, upload status, etc. * * * @dependency sl-format-bytes * @dependency sl-progress-bar * @dependency sl-icon * * @slot - File name * @slot image - The file's image (mimetype icon, status icon, etc) * @slot close-button - Close button * @event load - Emitted when file is loaded * * @csspart base - Component internal wrapper */ @customElement("et2-file-item") export class Et2FileItem extends Et2Widget(LitElement) { static get styles() { return [ shoelace, super.styles, styles ]; } /** Draws the item in a loading state. */ @property({type: Boolean, reflect: true}) loading = false; /** The current progress, 0 to 100. Only used if loading property is true. */ @property({type: Number, reflect: true}) progress : number; /** The item's theme variant. */ @property({reflect: true}) variant : "default" | "primary" | "success" | "neutral" | "warning" | "danger" = "default"; /** Different ways of displaying the item. Large for a few files, small is like a tag, list is for several files */ @property({reflect: true}) display : "large" | "small" | "list" = "large"; /** Makes the item closable (removable). */ @property({type: Boolean, reflect: true}) closable = false; /** A unique value to store in the item. This can be used as a way to identify items. */ @property() value = ""; /** The size of the file in bytes as a read-only 64-bit integer. */ @property({type: Number, reflect: true}) size : number; /** The file's thumbnail image */ @property({type: String}) image : string = ""; /** Indicates whether the file item is hidden. */ @property({type: Boolean, reflect: true}) hidden = false; private readonly hasSlotController = new HasSlotController(this, "image", "suffix"); private get base() : HTMLElement {return this.shadowRoot?.querySelector('[part~="base"]');} updated(changedProperties : Map) { if(changedProperties.has('hidden')) { this.handleHiddenChange(); } } /* Hides the file item */ async hide() { if(this.hidden) { return undefined; } this.hidden = true; this.requestUpdate("hidden"); return waitForEvent(this, 'sl-after-hide'); } async error(message?) { this.variant = "danger"; if(message) { this.innerHTML += "
" + message; } this.requestUpdate("variant"); } handleCloseClick() { this.hide(); } async handleHiddenChange() { if(!this.hidden) { // Show this.dispatchEvent(new Event('sl-show', {bubbles: true})); // TODO: Animation? this.base.hidden = false; this.dispatchEvent(new Event('sl-after-show', {bubbles: true})); } else { // Hide this.dispatchEvent(new Event('sl-hide', {bubbles: true})); // TODO: Animation? this.base.hidden = true; this.dispatchEvent(new Event('sl-after-hide', {bubbles: true})); } } handleTriggerKeyUp(event : KeyboardEvent) { // Prevent space from triggering a click event in Firefox if(event.key === "\xA0 ") { event.preventDefault(); } } render() { const progressBar = html`${this.loading ? html` ` : nothing}`; return html`
${this.image ? html` ` : nothing} ${this.display == "large" ? progressBar : nothing} ${this.size ? html` ` : ""} ${this.display != "large" ? html`${progressBar}` : nothing} ${this.closable ? html` ` : ""}
`; } }