import { LitElement, html } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; import { customElement, property } from 'lit/decorators.js'; import Worker from './sketcher.worker.ts'; import style from './index.css'; const worker = new Worker(); const SUPPORTED_EXTENSIONS = ['.fa', '.fasta', '.fna', '.gz', '.fq', '.fastq']; @customElement('mgnify-sourmash-component') export class MGnifySourmash extends LitElement { @property({ type: Boolean, reflect: true }) directory = false; @property({ type: Boolean }) show_directory_checkbox = false; @property({ type: Boolean }) show_signatures = false; // KmerMinHash parameters @property({ type: Number }) num = 0; @property({ type: Number }) ksize = 21; @property({ type: Boolean }) is_protein = false; @property({ type: Boolean }) dayhoff = false; @property({ type: Boolean }) hp = false; @property({ type: Number }) seed = 42; @property({ type: Number }) scaled = 1000; @property({ type: Boolean }) track_abundance = false; selectedFiles: Array = null; progress: { [filename: string]: number; } = {}; signatures: { [filename: string]: string; } = {}; errors: { [filename: string]: string; } = {}; static styles = [style]; constructor() { super(); worker.addEventListener('message', (event) => { switch (event?.data?.type) { case 'progress:read': this.progress[event.data.filename] = event.data.progress; this.requestUpdate(); break; case 'signature:error': this.errors[event.data.filename] = event.data.error; this.dispatchEvent( new CustomEvent('sketchedError', { bubbles: true, detail: { filename: event.data.filename, error: event.data.error, }, }) ); this.requestUpdate(); break; case 'signature:generated': this.signatures[event.data.filename] = event.data.signature; this.progress[event.data.filename] = 100; this.dispatchEvent( new CustomEvent('sketched', { bubbles: true, detail: { filename: event.data.filename, signature: event.data.signature, }, }) ); if (this.haveCompletedAllSignatures()) { this.dispatchEvent( new CustomEvent('sketchedall', { bubbles: true, detail: { signatures: this.signatures, errors: this.errors, }, }) ); } this.requestUpdate(); break; default: break; } }); } private haveCompletedAllSignatures() { return Object.keys(this.progress).every( (key: string) => key in this.signatures || key in this.errors ); } setChecked(event: MouseEvent) { this.directory = (event.target as HTMLInputElement).checked; } clear() { this.selectedFiles = null; this.progress = {}; this.signatures = {}; this.errors = {}; ( this.renderRoot.querySelector('#sourmash-selector') as HTMLInputElement ).value = null; this.requestUpdate(); } renderSelectedFiles() { if ((this.selectedFiles?.length || 0) < 1) return ''; return html`

Selected Files:

`; } render() { let label = this.directory ? 'Choose a directory...' : 'Choose Files...'; if (this.selectedFiles?.length) label = `${this.selectedFiles?.length} Files Selected`; return html`
${this.show_directory_checkbox ? html`
` : ''} ${this.renderSelectedFiles()}
`; } handleFileChanges(event: InputEvent) { event.preventDefault(); this.selectedFiles = Array.from( (event.currentTarget as HTMLInputElement).files ).filter((file: File) => { for (const ext of SUPPORTED_EXTENSIONS) { if (file.name.endsWith(ext)) { return true; } } return false; }); worker.postMessage({ files: this.selectedFiles, options: { num: this.num, ksize: this.ksize, is_protein: this.is_protein, dayhoff: this.dayhoff, hp: this.hp, seed: this.seed, scaled: this.scaled, track_abundance: this.track_abundance, }, }); this.dispatchEvent( new CustomEvent('change', { bubbles: true, detail: { selectedFiles: this.selectedFiles, }, }) ); this.requestUpdate(); } } declare global { interface HTMLElementTagNameMap { 'mgnify-sourmash-component': MGnifySourmash; } }