import { LitElement, html, css } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import { FileType } from '../../types/file-selector'; import { I18nService } from '../../utils/i18n'; /** * Toolbar Component * Provides file selection controls and type filter * * @fires select-all - Dispatched when select all button is clicked * @fires deselect-all - Dispatched when deselect all button is clicked * @fires type-filter - Dispatched when type filter changes * * @example * ```html * * * ``` */ @customElement('file-toolbar') export class Toolbar extends LitElement { /** * Number of selected files */ @property({ type: Number }) selectedCount = 0; /** * Total number of files */ @property({ type: Number }) totalCount = 0; /** * Current type filter */ @property({ type: String }) typeFilter: FileType | 'all' = 'all'; /** * Whether controls are disabled */ @property({ type: Boolean }) disabled = false; /** * Computed: checkbox state * Returns 'all', 'none', or 'partial' * @private */ private get checkboxState(): 'all' | 'none' | 'partial' { if (this.selectedCount === 0) return 'none'; if (this.selectedCount === this.totalCount) return 'all'; return 'partial'; } /** * Handle checkbox click - toggles between select all and deselect all * @private */ private handleCheckboxClick(): void { if (this.checkboxState === 'all') { // Currently all selected, deselect all this.dispatchEvent( new CustomEvent('deselect-all', { bubbles: true, composed: true, }) ); } else { // Currently none or partial selected, select all this.dispatchEvent( new CustomEvent('select-all', { bubbles: true, composed: true, }) ); } } /** * Handle type filter change * @private */ private handleTypeFilterChange(e: Event): void { const select = e.target as HTMLSelectElement; const type = select.value as FileType | 'all'; this.dispatchEvent( new CustomEvent('type-filter', { detail: { type }, bubbles: true, composed: true, }) ); } render() { const state = this.checkboxState; return html`
${I18nService.t('selected_files_count', [String(this.selectedCount), String(this.totalCount)])}
`; } static styles = css` :host { display: block; } .toolbar { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: #fafafa; border-bottom: 1px solid #f0f0f0; gap: 16px; } .toolbar-left { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; } .toolbar-right { display: flex; align-items: center; flex-shrink: 0; } .checkbox-label { display: flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; transition: all 0.2s; } .checkbox-label.disabled { cursor: not-allowed; opacity: 0.6; } .checkbox { width: 18px; height: 18px; border: 2px solid #d9d9d9; border-radius: 4px; display: flex; align-items: center; justify-content: center; background: #fff; transition: all 0.2s; flex-shrink: 0; position: relative; } .checkbox-label:not(.disabled):hover .checkbox { border-color: #1890ff; } .checkbox.all, .checkbox.partial { border-color: #1890ff; background: #1890ff; color: #fff; } .checkbox.none { background: #fff; color: transparent; } /* Ensure partial state dash is visible even if SVG fails */ .checkbox.partial::before { content: ''; position: absolute; width: 10px; height: 2px; background: currentColor; border-radius: 1px; } .checkbox svg { width: 16px; height: 16px; position: relative; z-index: 1; } .checkbox-text { font-size: 14px; color: #262626; white-space: nowrap; } .checkbox-label.disabled .checkbox-text { color: #bfbfbf; } .filter-group { display: flex; align-items: center; gap: 8px; } .filter-label { font-size: 14px; color: #595959; white-space: nowrap; } .type-filter { padding: 6px 12px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; color: #262626; background: #fff; cursor: pointer; transition: all 0.2s; min-width: 120px; } .type-filter:hover:not(:disabled) { border-color: #1890ff; } .type-filter:focus { outline: none; border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); } .type-filter:disabled { color: #bfbfbf; cursor: not-allowed; opacity: 0.6; } .selected-count { font-size: 14px; color: #595959; white-space: nowrap; } @media (max-width: 768px) { .toolbar { flex-direction: column; align-items: stretch; } .toolbar-left { flex-wrap: wrap; } .toolbar-right { justify-content: center; } } `; } declare global { interface HTMLElementTagNameMap { 'toolbar': Toolbar; } }