<!DOCTYPE html> <template id="myuw-profile-template"> <style> :host([hidden]) { display: none; } #myuw-profile-login { font-family: var( --myuw-profile-font, var(--myuw-font, 'Montserrat', 'Roboto', Arial, sans-serif) ); text-transform: uppercase; text-decoration: none; color: var( --myuw-profile-login-color, var(--myuw-primary-color, white) ); padding: 10px 13px; font-weight: bold; letter-spacing: 0px; font-size: 14px; position: relative; display: inline-block; transition: background .25s cubic-bezier(0.0, 0.0, 0.2, 1); } #myuw-profile-login.hidden { display: none; } #myuw-profile-circle { display: inline-block; outline: none; border-radius: 50%; height: 42px; width: 42px; min-width: initial; margin-left: 8px; padding: 4px; text-transform: uppercase; text-align: center; background: transparent; border-color: transparent; transition: background .25s cubic-bezier(0.0, 0.0, 0.2, 1); } #myuw-profile-circle:hover, #myuw-profile-login:hover { cursor: pointer; background: rgba(0,0,0,0.12); } #myuw-profile-circle-initial { padding: 0; margin: 0; font-weight: 500; font-size: 18px; font-family: var( --myuw-profile-font, var(--myuw-font, 'Montserrat', 'Roboto', Arial, sans-serif) ); background-color: var( --myuw-profile-background-color, #fb686d); user-select: none; color: white; text-transform: capitalize; border-radius: 50%; display: block; margin: 0; overflow: hidden; position: relative; height: 33px; width: 33px; line-height: 33px; font-size: 18px; font-weight: 400; } #myuw-profile-wrapper { position: relative; display: inline-block; } #myuw-profile-wrapper.hidden { display: none; } #myuw-profile-nav { position: absolute; top: 45px; right: 0; min-width: 320px; color: black; list-style: none; margin: 0; padding: 0; font-size: 14px; z-index: 101; transform-origin: top right; transform: scale(0); opacity: 0; visibility: hidden; transition: visibility 0s, opacity .25s cubic-bezier(0.0, 0.0, 0.2, 1), transform .25s cubic-bezier(0.0, 0.0, 0.2, 1); -webkit-box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); -moz-box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12); } #myuw-profile-nav.open-right { left: 0; right: inherit; transform-origin: top left; } #myuw-profile-nav.visible { transform: scale(1); opacity: 1; visibility: visible; } #myuw-profile-nav p { padding: 0; margin: 0; } #myuw-profile-nav a, #myuw-profile-nav p, ::slotted(a), ::slotted(p){ transition: all .3s ease; position: relative; font-size: 15px; font-family: var( --myuw-profile-font, var(--myuw-font, 'Montserrat', 'Roboto', Arial, sans-serif) ); padding: 3px 16px; color: rgba(0,0,0,0.87); text-decoration: none; background-color: #f5f5f5; border-bottom: 1px solid #e5e5e5; user-select: none; display: flex; -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; flex-direction: row; min-height: 48px; height: 48px; -webkit-align-content: center; align-content: center; -webkit-align-items: center; align-items: center; -webkit-box-pack: start; -webkit-justify-content: flex-start; justify-content: flex-start; } #myuw-profile-nav a:first-child, #myuw-profile-nav p:first-child { font-weight: 600; text-transform: capitalize; background-color: rgb(255,255,255); border-bottom: none; } #myuw-profile-nav a:first-child:hover, #myuw-profile-nav p:first-child:hover { background-color: rgb(255,255,255); } #myuw-profile-nav a:hover, #myuw-profile-nav p:hover, ::slotted(a:hover), ::slotted(p:hover) { background-color: #ececec; } </style> <a href="#" id="myuw-profile-login" class="hidden">Login</a> <div id="myuw-profile-wrapper" class="hidden"> <button id="myuw-profile-circle" aria-label="show profile menu"> <p id="myuw-profile-circle-initial">B</p> </button> <div id="myuw-profile-nav" class="aria-hidden"> <p id="myuw-profile-nav-user">Bucky</p> <slot name="nav-item"></slot> <a href="#">Settings</a> <a id="myuw-profile-logout" href="#">Logout</a> </div> </div> </template> <script> class MyUWProfile extends HTMLElement { constructor() { super(); this['open-right'] = false; // Create a shadowroot for this element this.attachShadow({mode: 'open'}); // Append the custom HTML to the shadowroot this.shadowRoot.appendChild(MyUWProfile.template.content.cloneNode(true)); } static get observedAttributes() { return [ 'login-url', 'logout-url', 'open-right', 'background-color' ]; } attributeChangedCallback(name, oldValue, newValue){ // Update the attribute internally this[name] = newValue; // Update the component with new att value this.updateAttribute(name); } connectedCallback(){ // Get all attributes this['login-url'] = this.getAttribute('login-url'); this['logout-url'] = this.getAttribute('logout-url'); this['session-endpoint'] = this.getAttribute('session-endpoint'); this['background-color'] = this.getAttribute('background-color'); this['user'] = false; if(this.getAttribute('open-right') !== null){ this['open-right'] = true; } // If the session endpoint is set, fetch session info if(this['session-endpoint']){ fetch(this['session-endpoint']) .then(res => { // Check if the request was valid if(res.status === 200){ res.json() .then( data => { // If data.person is not set, return. if(!data.person){ return; } // Set user data to the component this.user = data.person; this.componentReady(); }) } else { this.componentReady(); } }) .catch( e => { console.log(e); // Show login button if we couldn't get session info this.componentReady(); } ); } else { throw Error('No session endpoint has been defiend. Please set the "session-endpoint" attribute URL into the myuw-profile element!'); } /* Add an on click event to the window. This allows us to close the menu if the user clicks anywhere but on the menu. */ window.addEventListener('click', e => { let nav = this.shadowRoot.getElementById('myuw-profile-nav'); if (nav.classList.contains('visible')) { nav.classList.remove('visible') } }); /* Add an on click event to the profile nav menu. We need to do this in order to stop the propagation of click events on the menu specifically. If a user clicks on the nav menu, the window on click event will not fire, and it will not close the nav menu */ this.shadowRoot.getElementById('myuw-profile-nav').addEventListener('click', e => { e.stopPropagation(); }); /* Add an on click event to profile bubble We need to make sure that we stop propagation on this event or else the window on click will always fire and the menu will never open. */ this.shadowRoot.getElementById('myuw-profile-circle').addEventListener('click', e => { e.stopPropagation(); this.shadowRoot.getElementById('myuw-profile-nav').classList.toggle('visible'); }); // Update the component to use the new attributes this.updateAttribute('login-url'); this.updateAttribute('logout-url'); this.updateAttribute('session-endpoint'); this.updateAttribute('open-right'); this.updateAttribute('background-color'); } // Update the component with attribute values updateAttribute(att) { switch(att){ case 'login-url': this.shadowRoot.getElementById('myuw-profile-login').setAttribute('href', this['login-url']); break; case 'logout-url': this.shadowRoot.getElementById('myuw-profile-logout').setAttribute('href', this['logout-url']); break; case 'open-right': if(this['open-right']){ this.shadowRoot.getElementById('myuw-profile-nav').classList.add('open-right'); } break; case 'background-color': this.shadowRoot.getElementById('myuw-profile-circle-initial').style.backgroundColor = this['background-color']; break; } } /* Function to run after the session endpoint has been hit and the component has all the data that it needs to render. If user data was returned from the endpoint, the profile bubble will show. If not, the login button will show. */ componentReady() { if(this.user){ // Add user's name to first menu item this.shadowRoot.getElementById('myuw-profile-nav-user').innerHTML = this.user.firstName; // Change the letter in the profile circle this.shadowRoot.getElementById('myuw-profile-circle-initial').innerHTML = this.user.firstName.substring(0,1); // Show the profile bubble this.showProfileBubble(); } else { if (this['login-url'] !== null) { this.showLoginButton(); } else { this.hidden = true; } } } showLoginButton() { // Show Login Button this.shadowRoot.getElementById('myuw-profile-login').classList.remove('hidden'); // Hide profile Circle this.shadowRoot.getElementById('myuw-profile-wrapper').classList.add('hidden'); } showProfileBubble() { // Hide login button this.shadowRoot.getElementById('myuw-profile-login').classList.add('hidden'); // Show Profile circle this.shadowRoot.getElementById('myuw-profile-wrapper').classList.remove('hidden'); } } MyUWProfile.template = (document._currentScript || document.currentScript).ownerDocument.getElementById('myuw-profile-template'); window.customElements.define('myuw-profile', MyUWProfile); </script>