import { customElement, DeesElement, type TemplateResult, html, property, css, cssManager, state, } from '@design.estate/dees-element'; import { DeesAppuiSecondarymenu, DeesIcon } from '@design.estate/dees-catalog'; import type { ISecondaryMenuGroup } from '../../elements/interfaces/secondarymenu.js'; import { demo } from './eco-view-scan.demo.js'; // Ensure components are registered DeesAppuiSecondarymenu; DeesIcon; declare global { interface HTMLElementTagNameMap { 'eco-view-scan': EcoViewScan; } } // Types export type TScanFormat = 'pdf' | 'jpeg' | 'png' | 'tiff'; export type TScanColorMode = 'color' | 'grayscale' | 'blackwhite'; export type TScanSource = 'flatbed' | 'adf' | 'adf-duplex'; export type TScanPanel = 'scan' | 'history' | 'settings'; export interface IScanSettings { format: TScanFormat; resolution: number; colorMode: TScanColorMode; source: TScanSource; } export interface IScannedDocument { id: string; timestamp: Date; format: TScanFormat; data: string; // base64 thumbnail?: string; size: number; name?: string; } export interface IScannerInfo { id: string; name: string; address: string; status: 'online' | 'offline' | 'busy' | 'error'; capabilities?: { resolutions: number[]; formats: TScanFormat[]; colorModes: TScanColorMode[]; sources: TScanSource[]; }; } export interface IDataProviderInfo { id: string; name: string; icon?: string; } @customElement('eco-view-scan') export class EcoViewScan extends DeesElement { public static demo = demo; public static demoGroup = 'Views'; public static styles = [ cssManager.defaultStyles, css` :host { display: block; width: 100%; height: 100%; background: ${cssManager.bdTheme('#f5f5f7', 'hsl(240 6% 10%)')}; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 98%)')}; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; } .scan-container { display: flex; height: 100%; } dees-appui-secondarymenu { flex-shrink: 0; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 8%)')}; border-right: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 15%)')}; } .content { flex: 1; overflow-y: auto; padding: 32px 48px; display: flex; flex-direction: column; } .panel-header { margin-bottom: 24px; display: flex; align-items: center; justify-content: space-between; } .panel-header-left { display: flex; flex-direction: column; gap: 4px; } .panel-title { font-size: 28px; font-weight: 600; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 98%)')}; } .panel-description { font-size: 14px; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')}; } .scanner-selector { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; } .scanner-selector label { font-size: 14px; font-weight: 500; color: ${cssManager.bdTheme('hsl(0 0% 40%)', 'hsl(0 0% 70%)')}; } .scanner-selector select { flex: 1; max-width: 300px; padding: 10px 14px; font-size: 14px; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(240 5% 25%)')}; border-radius: 8px; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 15%)')}; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 95%)')}; cursor: pointer; } .scanner-selector select:focus { outline: none; border-color: hsl(217 91% 60%); } .preview-area { flex: 1; min-height: 300px; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 12%)')}; border: 2px dashed ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(240 5% 25%)')}; border-radius: 12px; display: flex; align-items: center; justify-content: center; overflow: hidden; margin-bottom: 24px; } .preview-area.has-image { border-style: solid; } .preview-placeholder { display: flex; flex-direction: column; align-items: center; gap: 12px; color: ${cssManager.bdTheme('hsl(0 0% 60%)', 'hsl(0 0% 50%)')}; } .preview-placeholder dees-icon { opacity: 0.5; } .preview-image { max-width: 100%; max-height: 100%; object-fit: contain; } .action-bar { display: flex; gap: 12px; margin-bottom: 32px; } .action-button { display: flex; align-items: center; gap: 8px; padding: 12px 24px; font-size: 14px; font-weight: 500; border: none; border-radius: 8px; cursor: pointer; transition: all 0.15s ease; } .action-button.primary { background: hsl(217 91% 60%); color: white; } .action-button.primary:hover:not(:disabled) { background: hsl(217 91% 55%); } .action-button.secondary { background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(240 5% 20%)')}; color: ${cssManager.bdTheme('hsl(0 0% 30%)', 'hsl(0 0% 90%)')}; } .action-button.secondary:hover:not(:disabled) { background: ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 25%)')}; } .action-button:disabled { opacity: 0.5; cursor: not-allowed; } .action-button.scanning dees-icon { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .dropdown-container { position: relative; } .dropdown-menu { position: absolute; bottom: 100%; left: 0; margin-bottom: 4px; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 15%)')}; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 25%)')}; border-radius: 8px; box-shadow: 0 4px 16px ${cssManager.bdTheme('rgba(0,0,0,0.1)', 'rgba(0,0,0,0.3)')}; min-width: 200px; z-index: 100; overflow: hidden; } .dropdown-item { display: flex; align-items: center; gap: 10px; padding: 10px 14px; font-size: 14px; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; cursor: pointer; transition: background 0.1s ease; } .dropdown-item:hover { background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(240 5% 20%)')}; } .dropdown-item dees-icon { opacity: 0.7; } .settings-section { background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 12%)')}; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 18%)')}; border-radius: 12px; padding: 20px; margin-bottom: 20px; } .settings-section-title { font-size: 16px; font-weight: 600; margin-bottom: 16px; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; } .setting-row { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid ${cssManager.bdTheme('hsl(0 0% 92%)', 'hsl(240 5% 18%)')}; } .setting-row:last-child { border-bottom: none; } .setting-label { font-size: 14px; color: ${cssManager.bdTheme('hsl(0 0% 30%)', 'hsl(0 0% 80%)')}; } .setting-control select { padding: 8px 12px; font-size: 14px; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 85%)', 'hsl(240 5% 25%)')}; border-radius: 6px; background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 15%)')}; color: ${cssManager.bdTheme('hsl(0 0% 10%)', 'hsl(0 0% 95%)')}; cursor: pointer; } .history-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; } .history-item { background: ${cssManager.bdTheme('#ffffff', 'hsl(240 6% 12%)')}; border: 1px solid ${cssManager.bdTheme('hsl(0 0% 90%)', 'hsl(240 5% 18%)')}; border-radius: 8px; overflow: hidden; cursor: pointer; transition: all 0.15s ease; } .history-item:hover { border-color: hsl(217 91% 60%); transform: translateY(-2px); box-shadow: 0 4px 12px ${cssManager.bdTheme('rgba(0,0,0,0.1)', 'rgba(0,0,0,0.3)')}; } .history-thumbnail { width: 100%; aspect-ratio: 1; background: ${cssManager.bdTheme('hsl(0 0% 95%)', 'hsl(240 5% 18%)')}; display: flex; align-items: center; justify-content: center; } .history-thumbnail img { width: 100%; height: 100%; object-fit: cover; } .history-info { padding: 10px; } .history-name { font-size: 13px; font-weight: 500; color: ${cssManager.bdTheme('hsl(0 0% 20%)', 'hsl(0 0% 90%)')}; margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .history-date { font-size: 12px; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')}; } .empty-state { text-align: center; padding: 60px 20px; color: ${cssManager.bdTheme('hsl(0 0% 50%)', 'hsl(0 0% 60%)')}; } .empty-state dees-icon { margin-bottom: 16px; opacity: 0.4; } .empty-state p { font-size: 14px; margin: 0; } `, ]; @property({ type: Array }) accessor scanners: IScannerInfo[] = []; @property({ type: Array }) accessor providers: IDataProviderInfo[] = []; @state() accessor activePanel: TScanPanel = 'scan'; @state() accessor selectedScannerId: string | null = null; @state() accessor isScanning = false; @state() accessor currentPreview: string | null = null; @state() accessor currentDocument: IScannedDocument | null = null; @state() accessor scanHistory: IScannedDocument[] = []; @state() accessor settings: IScanSettings = { format: 'pdf', resolution: 300, colorMode: 'color', source: 'flatbed', }; @state() accessor showSendToMenu = false; private get selectedScanner(): IScannerInfo | null { return this.scanners.find(s => s.id === this.selectedScannerId) || null; } private get availableResolutions(): number[] { return this.selectedScanner?.capabilities?.resolutions || [150, 300, 600]; } private get availableFormats(): TScanFormat[] { return this.selectedScanner?.capabilities?.formats || ['pdf', 'jpeg', 'png']; } private get availableColorModes(): TScanColorMode[] { return this.selectedScanner?.capabilities?.colorModes || ['color', 'grayscale']; } private get availableSources(): TScanSource[] { return this.selectedScanner?.capabilities?.sources || ['flatbed']; } public render(): TemplateResult { return html`
Select a scanner and start scanning
View and manage your scanned documents
No scans yet. Start scanning to see your documents here.
Configure default scan options