Last active
July 25, 2025 19:46
-
-
Save thinkphp/88a6c1ea088dc76f359cf9053384bf1f to your computer and use it in GitHub Desktop.
fab web component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Contact FAB Web Component Demo</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 20px; | |
| font-family: Arial, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Usage of the web component --> | |
| <contact-fab | |
| whatsapp="+40123456789" | |
| email="[email protected]" | |
| phone="+40123456789"> | |
| </contact-fab> | |
| <script> | |
| class ContactFab extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| this.isOpen = false; | |
| // Get attributes or set defaults | |
| this.whatsappNumber = this.getAttribute('whatsapp') || '+1234567890'; | |
| this.emailAddress = this.getAttribute('email') || '[email protected]'; | |
| this.phoneNumber = this.getAttribute('phone') || '+1234567890'; | |
| this.render(); | |
| this.attachEventListeners(); | |
| } | |
| render() { | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| :host { | |
| position: fixed; | |
| bottom: 30px; | |
| right: 30px; | |
| z-index: 1000; | |
| font-family: Arial, sans-serif; | |
| } | |
| .fab-main { | |
| width: 60px; | |
| height: 60px; | |
| background: linear-gradient(45deg, #25D366, #128C7E); | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: 0 4px 20px rgba(37, 211, 102, 0.4); | |
| transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); | |
| border: none; | |
| outline: none; | |
| } | |
| .fab-main:hover { | |
| transform: scale(1.1); | |
| box-shadow: 0 6px 25px rgba(37, 211, 102, 0.6); | |
| } | |
| .fab-main.active { | |
| transform: rotate(45deg); | |
| background: linear-gradient(45deg, #ff4757, #ff3742); | |
| box-shadow: 0 6px 25px rgba(255, 71, 87, 0.6); | |
| } | |
| .fab-icon { | |
| width: 24px; | |
| height: 24px; | |
| fill: white; | |
| transition: transform 0.3s ease; | |
| } | |
| .fab-main.active .fab-icon { | |
| transform: rotate(-45deg); | |
| } | |
| .fab-options { | |
| position: absolute; | |
| bottom: 80px; | |
| right: 0; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| opacity: 0; | |
| visibility: hidden; | |
| transform: translateY(20px); | |
| transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); | |
| } | |
| .fab-options.active { | |
| opacity: 1; | |
| visibility: visible; | |
| transform: translateY(0); | |
| } | |
| .fab-option { | |
| width: 50px; | |
| height: 50px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| text-decoration: none; | |
| color: white; | |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); | |
| transition: all 0.3s ease; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .fab-option::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(45deg, rgba(255,255,255,0.1), rgba(255,255,255,0.3)); | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| } | |
| .fab-option:hover::before { | |
| opacity: 1; | |
| } | |
| .fab-option:hover { | |
| transform: scale(1.1); | |
| box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .whatsapp { | |
| background: linear-gradient(45deg, #25D366, #128C7E); | |
| animation-delay: 0.1s; | |
| } | |
| .email { | |
| background: linear-gradient(45deg, #EA4335, #D33B2C); | |
| animation-delay: 0.2s; | |
| } | |
| .phone { | |
| background: linear-gradient(45deg, #4285F4, #1a73e8); | |
| animation-delay: 0.3s; | |
| } | |
| .fab-option-icon { | |
| width: 20px; | |
| height: 20px; | |
| fill: white; | |
| } | |
| .fab-tooltip { | |
| position: absolute; | |
| right: 60px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| background: rgba(0, 0, 0, 0.8); | |
| color: white; | |
| padding: 8px 12px; | |
| border-radius: 20px; | |
| font-size: 14px; | |
| white-space: nowrap; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: all 0.3s ease; | |
| pointer-events: none; | |
| } | |
| .fab-tooltip::after { | |
| content: ''; | |
| position: absolute; | |
| left: 100%; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| border: 6px solid transparent; | |
| border-left-color: rgba(0, 0, 0, 0.8); | |
| } | |
| .fab-option:hover .fab-tooltip { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-10px); } | |
| } | |
| .fab-options.active .fab-option { | |
| animation: bounce 0.6s ease forwards; | |
| } | |
| /* Mobile responsiveness */ | |
| @media (max-width: 768px) { | |
| :host { | |
| bottom: 20px; | |
| right: 20px; | |
| } | |
| .fab-main { | |
| width: 55px; | |
| height: 55px; | |
| } | |
| .fab-option { | |
| width: 45px; | |
| height: 45px; | |
| } | |
| .fab-tooltip { | |
| font-size: 12px; | |
| padding: 6px 10px; | |
| } | |
| } | |
| </style> | |
| <div class="contact-fab"> | |
| <button class="fab-main" id="fabMain"> | |
| <svg class="fab-icon" viewBox="0 0 24 24"> | |
| <path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" /> | |
| </svg> | |
| </button> | |
| <div class="fab-options" id="fabOptions"> | |
| <a href="https://wa.me/${this.whatsappNumber.replace(/\D/g, '')}" class="fab-option whatsapp" target="_blank"> | |
| <svg class="fab-option-icon" viewBox="0 0 24 24"> | |
| <path d="M17.472,14.382c-0.297-0.149-1.758-0.867-2.03-0.967c-0.273-0.099-0.471-0.148-0.67,0.15c-0.197,0.297-0.767,0.966-0.94,1.164c-0.173,0.199-0.347,0.223-0.644,0.075c-0.297-0.15-1.255-0.463-2.39-1.475c-0.883-0.788-1.48-1.761-1.653-2.059c-0.173-0.297-0.018-0.458,0.13-0.606c0.134-0.133,0.297-0.347,0.446-0.52C9.87,9.97,9.919,9.846,10.019,9.65c0.099-0.198,0.05-0.371-0.025-0.52C9.919,8.981,9.325,7.515,9.078,6.92c-0.241-0.58-0.487-0.5-0.669-0.51c-0.173-0.008-0.371-0.01-0.57-0.01c-0.198,0-0.52,0.074-0.792,0.372c-0.272,0.297-1.04,1.016-1.04,2.479c0,1.462,1.065,2.875,1.213,3.074c0.149,0.198,2.096,3.2,5.077,4.487c0.709,0.306,1.262,0.489,1.694,0.625c0.712,0.227,1.36,0.195,1.871,0.118c0.571-0.085,1.758-0.719,2.006-1.413c0.248-0.694,0.248-1.289,0.173-1.413C17.944,14.653,17.769,14.531,17.472,14.382z M12.057,21.785h-0.008c-1.805,0-3.573-0.488-5.126-1.411l-0.368-0.218l-3.815,1.002l1.018-3.721l-0.238-0.382C2.438,15.25,1.898,13.688,1.898,12.057c0-5.561,4.526-10.086,10.091-10.086c2.693,0,5.225,1.05,7.127,2.953c1.902,1.903,2.951,4.435,2.951,7.128C22.067,17.613,17.541,22.14,12.057,21.785z"/> | |
| </svg> | |
| <div class="fab-tooltip">WhatsApp</div> | |
| </a> | |
| <a href="mailto:${this.emailAddress}" class="fab-option email"> | |
| <svg class="fab-option-icon" viewBox="0 0 24 24"> | |
| <path d="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z" /> | |
| </svg> | |
| <div class="fab-tooltip">Email Us</div> | |
| </a> | |
| <a href="tel:${this.phoneNumber}" class="fab-option phone"> | |
| <svg class="fab-option-icon" viewBox="0 0 24 24"> | |
| <path d="M6.62,10.79C8.06,13.62 10.38,15.94 13.21,17.38L15.41,15.18C15.69,14.9 16.08,14.82 16.43,14.93C17.55,15.3 18.75,15.5 20,15.5A1,1 0 0,1 21,16.5V20A1,1 0 0,1 20,21A17,17 0 0,1 3,4A1,1 0 0,1 4,3H7.5A1,1 0 0,1 8.5,4C8.5,5.25 8.7,6.45 9.07,7.57C9.18,7.92 9.1,8.31 8.82,8.59L6.62,10.79Z" /> | |
| </svg> | |
| <div class="fab-tooltip">Call Us</div> | |
| </a> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| attachEventListeners() { | |
| const fabMain = this.shadowRoot.getElementById('fabMain'); | |
| const fabOptions = this.shadowRoot.getElementById('fabOptions'); | |
| fabMain.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| this.toggleFab(); | |
| }); | |
| // Close FAB when clicking outside | |
| document.addEventListener('click', (e) => { | |
| if (!e.composedPath().includes(this)) { | |
| this.closeFab(); | |
| } | |
| }); | |
| } | |
| toggleFab() { | |
| this.isOpen = !this.isOpen; | |
| const fabMain = this.shadowRoot.getElementById('fabMain'); | |
| const fabOptions = this.shadowRoot.getElementById('fabOptions'); | |
| if (this.isOpen) { | |
| fabMain.classList.add('active'); | |
| fabOptions.classList.add('active'); | |
| } else { | |
| fabMain.classList.remove('active'); | |
| fabOptions.classList.remove('active'); | |
| } | |
| } | |
| closeFab() { | |
| if (this.isOpen) { | |
| this.isOpen = false; | |
| const fabMain = this.shadowRoot.getElementById('fabMain'); | |
| const fabOptions = this.shadowRoot.getElementById('fabOptions'); | |
| fabMain.classList.remove('active'); | |
| fabOptions.classList.remove('active'); | |
| } | |
| } | |
| // Lifecycle method - called when attributes change | |
| static get observedAttributes() { | |
| return ['whatsapp', 'email', 'phone']; | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (oldValue !== newValue) { | |
| this[name === 'whatsapp' ? 'whatsappNumber' : name === 'email' ? 'emailAddress' : 'phoneNumber'] = newValue; | |
| this.render(); | |
| this.attachEventListeners(); | |
| } | |
| } | |
| } | |
| customElements.define('contact-fab', ContactFab); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment