Spaces:
Running
Running
| class FloatingControls extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.hasFile = false; | |
| } | |
| connectedCallback() { | |
| this.attachShadow({ mode: 'open' }); | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| display: block; | |
| position: fixed; | |
| bottom: 24px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 100; | |
| width: auto; | |
| max-width: 90vw; | |
| } | |
| .controls-container { | |
| background: rgba(255, 255, 255, 0.95); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| border: 1px solid rgba(226, 232, 240, 0.8); | |
| border-radius: 16px; | |
| padding: 12px 16px; | |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.05); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| justify-content: center; | |
| transition: all 0.3s ease; | |
| } | |
| .controls-container.disabled { | |
| opacity: 0.5; | |
| pointer-events: none; | |
| } | |
| .btn-group { | |
| display: flex; | |
| gap: 4px; | |
| padding: 0 8px; | |
| border-right: 1px solid #e2e8f0; | |
| } | |
| .btn-group:last-child { | |
| border-right: none; | |
| } | |
| button { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| padding: 8px 14px; | |
| border: none; | |
| border-radius: 10px; | |
| font-size: 0.875rem; | |
| font-weight: 500; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| font-family: 'Sarabun', sans-serif; | |
| white-space: nowrap; | |
| } | |
| button:hover { | |
| transform: translateY(-1px); | |
| } | |
| button:active { | |
| transform: translateY(0); | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #2563eb, #3b82f6); | |
| color: white; | |
| box-shadow: 0 4px 6px -1px rgba(37, 99, 235, 0.2); | |
| } | |
| .btn-primary:hover { | |
| box-shadow: 0 8px 12px -2px rgba(37, 99, 235, 0.3); | |
| } | |
| .btn-secondary { | |
| background: #f1f5f9; | |
| color: #475569; | |
| border: 1px solid #e2e8f0; | |
| } | |
| .btn-secondary:hover { | |
| background: #e2e8f0; | |
| color: #1e293b; | |
| } | |
| .btn-success { | |
| background: linear-gradient(135deg, #059669, #10b981); | |
| color: white; | |
| box-shadow: 0 4px 6px -1px rgba(5, 150, 105, 0.2); | |
| } | |
| .btn-success:hover { | |
| box-shadow: 0 8px 12px -2px rgba(5, 150, 105, 0.3); | |
| } | |
| .btn-export { | |
| background: #fffbeb; | |
| color: #d97706; | |
| border: 1px solid #fcd34d; | |
| } | |
| .btn-export:hover { | |
| background: #fef3c7; | |
| } | |
| .separator { | |
| width: 1px; | |
| height: 24px; | |
| background: #e2e8f0; | |
| margin: 0 4px; | |
| } | |
| .icon { | |
| width: 16px; | |
| height: 16px; | |
| } | |
| @media (max-width: 640px) { | |
| .controls-container { | |
| padding: 8px 12px; | |
| gap: 4px; | |
| } | |
| button { | |
| padding: 6px 10px; | |
| font-size: 0.75rem; | |
| } | |
| button span { | |
| display: none; | |
| } | |
| .icon { | |
| width: 18px; | |
| height: 18px; | |
| } | |
| } | |
| @keyframes slide-up { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px) translateX(-50%); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0) translateX(-50%); | |
| } | |
| } | |
| :host { | |
| animation: slide-up 0.4s ease-out; | |
| } | |
| </style> | |
| <div class="controls-container" id="controls-container"> | |
| <div class="btn-group"> | |
| <button class="btn-primary" id="analyze-btn" title="วิเคราะห์โครงสร้าง PDF"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> | |
| <polyline points="14 2 14 8 20 8"></polyline> | |
| <line x1="16" y1="13" x2="8" y2="13"></line> | |
| <line x1="16" y1="17" x2="8" y2="17"></line> | |
| <polyline points="10 9 9 9 8 9"></polyline> | |
| </svg> | |
| <span>วิเคราะห์ PDF</span> | |
| </button> | |
| <button class="btn-success" id="ocr-btn" title="OCR ภาษาไทย"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path> | |
| <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path> | |
| </svg> | |
| <span>OCR ไทย</span> | |
| </button> | |
| </div> | |
| <div class="btn-group"> | |
| <button class="btn-secondary" id="copy-btn" title="คัดลอกทั้งหมด"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect> | |
| <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path> | |
| </svg> | |
| <span>คัดลอก</span> | |
| </button> | |
| </div> | |
| <div class="btn-group"> | |
| <button class="btn-export" id="export-json-btn" title="Export JSON"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <polyline points="4 17 10 11 4 5"></polyline> | |
| <line x1="12" y1="19" x2="20" y2="19"></line> | |
| </svg> | |
| <span>JSON</span> | |
| </button> | |
| <button class="btn-export" id="export-csv-btn" title="Export CSV"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> | |
| <polyline points="14 2 14 8 20 8"></polyline> | |
| <line x1="16" y1="13" x2="8" y2="13"></line> | |
| <line x1="16" y1="17" x2="8" y2="17"></line> | |
| <polyline points="10 9 9 9 8 9"></polyline> | |
| </svg> | |
| <span>CSV</span> | |
| </button> | |
| <button class="btn-export" id="export-excel-btn" title="Export Excel"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path> | |
| <polyline points="14 2 14 8 20 8"></polyline> | |
| <path d="M8 13h8"></path> | |
| <path d="M8 17h8"></path> | |
| <path d="M9 9h6"></path> | |
| </svg> | |
| <span>Excel</span> | |
| </button> | |
| <button class="btn-export" id="export-html-btn" title="Export HTML+CSS+JS"> | |
| <svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| <path d="m13 2-2 2.5h3zm-2 2.5L9 7h3zm-2 2.5L6 9.5h3zm-2 2.5L3 12h3zm8-5L11 7h3zm-2 2.5L9 9.5h3zm-2 2.5L7 12h3z"></path> | |
| <path d="M14 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2z"></path> | |
| </svg> | |
| <span>HTML</span> | |
| </button> | |
| </div> | |
| </div> | |
| `; | |
| this.setupEventListeners(); | |
| this.updateState(); | |
| } | |
| setupEventListeners() { | |
| // Listen for events from script.js to know when file is loaded | |
| document.addEventListener('file-selected', () => { | |
| this.hasFile = true; | |
| this.updateState(); | |
| }); | |
| document.addEventListener('reset-app', () => { | |
| this.hasFile = false; | |
| this.updateState(); | |
| }); | |
| // Button click handlers | |
| this.shadowRoot.getElementById('analyze-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('analyze-pdf', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('ocr-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('ocr-thai', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('copy-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('copy-all', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('export-json-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('export-json', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('export-csv-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('export-csv', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('export-excel-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('export-excel', { bubbles: true, composed: true })); | |
| }); | |
| this.shadowRoot.getElementById('export-html-btn').addEventListener('click', () => { | |
| this.dispatchEvent(new CustomEvent('export-html', { bubbles: true, composed: true })); | |
| }); | |
| } | |
| updateState() { | |
| const container = this.shadowRoot.getElementById('controls-container'); | |
| if (this.hasFile) { | |
| container.classList.remove('disabled'); | |
| } else { | |
| container.classList.add('disabled'); | |
| } | |
| } | |
| // Make the controls always visible by removing the disabled class | |
| showControls() { | |
| const container = this.shadowRoot.getElementById('controls-container'); | |
| container.classList.remove('disabled'); | |
| } | |
| } | |
| customElements.define('floating-controls', FloatingControls); |