UNPKG

remix-ide

Version:

Minimalistic browser-based Solidity IDE

442 lines (403 loc) 15.2 kB
var yo = require('yo-yo') var csjs = require('csjs-inject') var helper = require('../../lib/helper') let globalRegistry = require('../../global/registry') const EventEmitter = require('events') // Component export class VerticalIcons { constructor (name, appStore, homeProfile) { this.store = appStore this.homeProfile = homeProfile.profile this.events = new EventEmitter() this.icons = {} this.iconKind = {} this.iconStatus = {} this.name = name this.store.event.on('activate', (name) => { const api = this.store.getOne(name) if (!api.profile.icon) return if (api.profile.location === this.name) { this.addIcon(api.profile) this.listenOnStatus(api) } }) this.store.event.on('deactivate', (name) => { const api = this.store.getOne(name) if (api && this.icons[name]) { this.removeIcon(api.profile) this.stopListenOnStatus(api) } }) this.store.event.on('add', (api) => { }) this.store.event.on('remove', (api) => { }) let themeModule = globalRegistry.get('themeModule').api themeModule.events.on('themeChanged', (theme) => { this.onThemeChanged(theme.quality) }) } stopListenOnStatus (api) { if (!api.events) return let fn = this.iconStatus[api.profile.name] if (fn) { api.events.removeListener('statusChanged', fn) delete this.iconStatus[api.profile.name] } } listenOnStatus (api) { if (!api.events) return // the list of supported keys. 'none' will remove the status const keys = ['edited', 'succeed', 'none', 'loading', 'failed'] const types = ['error', 'warning', 'success', 'info', ''] const fn = (status) => { if (!types.includes(status.type) && status.type) throw new Error(`type should be ${keys.join()}`) if (!status.key) throw new Error(`status key should be defined`) if (typeof status.key === 'string' && (!keys.includes(status.key))) { throw new Error('key should contain either number or ' + keys.join()) } this.setIconStatus(api.profile.name, status) } this.iconStatus[api.profile.name] = fn api.events.on('statusChanged', this.iconStatus[api.profile.name]) } /** * Add an icon to the map * @param {ModuleProfile} profile The profile of the module */ addIcon ({kind, name, icon, displayName, tooltip}) { let title = (tooltip || displayName || name) this.icons[name] = yo` <div class="${css.icon}" onclick="${() => { this.toggle(name) }}" plugin="${name}" title="${title}"> <img class="image" src="${icon}" alt="${name}" /> </div>` this.iconKind[kind || 'other'].appendChild(this.icons[name]) } /** * resolve a classes list for @arg key * @param {Object} key * @param {Object} type */ resolveClasses (key, type) { let classes = css.status switch (key) { case 'succeed': classes += ' fas fa-check-circle text-' + type + ' ' + css.statusCheck break case 'edited': classes += ' fas fa-sync text-' + type + ' ' + css.statusCheck break case 'loading': classes += ' fas fa-spinner text-' + type + ' ' + css.statusCheck break case 'failed': classes += ' fas fa-exclamation-triangle text-' + type + ' ' + css.statusCheck break default: { classes += ' badge badge-pill badge-' + type } } return classes } /** * Set a new status for the @arg name * @param {String} name * @param {Object} status */ setIconStatus (name, status) { const el = this.icons[name] if (!el) return let statusEl = el.querySelector('span') if (statusEl) { el.removeChild(statusEl) } if (status.key === 'none') return // remove status let text = '' let key = '' if (typeof status.key === 'number') { key = status.key.toString() text = key } else key = helper.checkSpecialChars(status.key) ? '' : status.key let type = '' if (status.type === 'error') { type = 'danger' // to use with bootstrap } else type = helper.checkSpecialChars(status.type) ? '' : status.type let title = helper.checkSpecialChars(status.title) ? '' : status.title el.appendChild(yo`<span title="${title}" class="${this.resolveClasses(key, type)}" aria-hidden="true" > ${text} </span>`) el.classList.add(`${css.icon}`) } /** * Remove an icon from the map * @param {ModuleProfile} profile The profile of the module */ removeIcon ({kind, name}) { if (this.icons[name]) this.iconKind[kind || 'other'].removeChild(this.icons[name]) } /** * Remove active for the current activated icons */ removeActive () { // reset filters const images = this.view.querySelectorAll(`.image`) images.forEach(function (im) { im.style.setProperty('filter', 'invert(0.5)') }) // remove active const currentActive = this.view.querySelector(`.${css.active}`) if (currentActive) { currentActive.classList.remove(css.active) } } /** * Add active for the new activated icon * @param {string} name Name of profile of the module to activate */ addActive (name) { const themeType = globalRegistry.get('themeModule').api.currentTheme().quality const invert = themeType === 'dark' ? 1 : 0 const nextActive = this.view.querySelector(`[plugin="${name}"]`) if (nextActive) { let image = nextActive.querySelector('.image') nextActive.classList.add(css.active) image.style.setProperty('filter', `invert(${invert})`) } } /** * Set an icon as active * @param {string} name Name of profile of the module to activate */ select (name) { this.updateActivations(name) this.events.emit('showContent', name) } /** * Toggles the side panel for plugin * @param {string} name Name of profile of the module to activate */ toggle (name) { this.updateActivations(name) this.events.emit('toggleContent', name) } updateActivations (name) { this.removeActive() this.addActive(name) } onThemeChanged (themeType) { const invert = themeType === 'dark' ? 1 : 0 const active = this.view.querySelector(`.${css.active}`) if (active) { let image = active.querySelector('.image') image.style.setProperty('filter', `invert(${invert})`) } } /** * Show the home page */ showHome () { globalRegistry.get('appmanager').api.ensureActivated('home') } render () { let home = yo` <div class="${css.homeIcon}" onclick="${(e) => { this.showHome() }}" plugin="${this.homeProfile.name}" title="${this.homeProfile.displayName}" > <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="42px" height="42px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve"> <g> <path fill="#414042" d="M70.582,428.904c0.811,0,1.622,0.285,2.437,0.853c0.811,0.571,1.218,1.34,1.218,2.314 c0,2.277-1.059,3.496-3.168,3.656c-5.038,0.814-9.381,2.356-13.037,4.63c-3.655,2.276-6.663,5.117-9.016,8.528 c-2.357,3.411-4.104,7.272-5.239,11.575c-1.139,4.307-1.706,8.814-1.706,13.524v32.653c0,2.273-1.139,3.411-3.412,3.411 c-2.277,0-3.412-1.138-3.412-3.411v-74.323c0-2.273,1.135-3.411,3.412-3.411c2.273,0,3.412,1.138,3.412,3.411v15.108 c1.462-2.437,3.206-4.752,5.239-6.945c2.029-2.193,4.264-4.143,6.701-5.848c2.437-1.706,5.076-3.085,7.919-4.143 C64.771,429.433,67.658,428.904,70.582,428.904z"/> <path fill="#414042" d="M137.773,427.198c5.685,0,10.966,1.181,15.839,3.534c4.874,2.356,9.055,5.482,12.55,9.381 c3.492,3.899,6.214,8.407,8.164,13.524c1.949,5.117,2.924,10.44,2.924,15.961c0,0.976-0.366,1.79-1.097,2.438 c-0.731,0.65-1.583,0.975-2.559,0.975h-67.987c0.487,4.226,1.584,8.285,3.29,12.184c1.706,3.899,3.937,7.312,6.701,10.234 c2.761,2.925,6.008,5.281,9.748,7.067c3.735,1.789,7.877,2.681,12.428,2.681c12.021,0,21.36-4.79,28.023-14.377 c0.647-1.136,1.622-1.706,2.924-1.706c2.273,0,3.412,1.139,3.412,3.412c0,0.163-0.164,0.73-0.487,1.705 c-3.412,6.013-8.205,10.479-14.377,13.402c-6.176,2.924-12.671,4.387-19.495,4.387c-5.689,0-10.928-1.181-15.718-3.533 c-4.793-2.354-8.936-5.483-12.428-9.382c-3.495-3.899-6.214-8.407-8.163-13.524c-1.95-5.118-2.924-10.437-2.924-15.962 c0-5.521,0.975-10.844,2.924-15.961c1.949-5.117,4.668-9.625,8.163-13.524c3.492-3.898,7.634-7.024,12.428-9.381 C126.846,428.379,132.084,427.198,137.773,427.198z M169.94,466.188c-0.328-4.223-1.341-8.285-3.046-12.184 c-1.706-3.899-3.982-7.312-6.823-10.235c-2.844-2.924-6.175-5.277-9.991-7.067c-3.819-1.785-7.92-2.68-12.306-2.68 c-4.55,0-8.692,0.895-12.428,2.68c-3.739,1.79-6.987,4.144-9.748,7.067c-2.764,2.924-4.995,6.336-6.701,10.235 c-1.706,3.898-2.802,7.961-3.29,12.184H169.94z"/> <path fill="#414042" d="M304.69,427.441c5.034,0,9.504,1.018,13.402,3.047c3.899,2.033,7.189,4.672,9.87,7.92 c2.68,3.251,4.709,7.066,6.092,11.452c1.379,4.387,2.07,8.856,2.07,13.402v43.62c0,0.975-0.365,1.789-1.097,2.438 c-0.73,0.646-1.503,0.975-2.313,0.975c-2.276,0-3.412-1.14-3.412-3.412v-43.62c0-3.571-0.529-7.104-1.584-10.6 c-1.059-3.491-2.602-6.618-4.63-9.382c-2.033-2.761-4.592-4.953-7.677-6.58c-3.088-1.621-6.662-2.436-10.722-2.436 c-5.2,0-9.587,1.218-13.159,3.654c-3.574,2.438-6.457,5.566-8.65,9.382c-2.193,3.819-3.818,8.042-4.874,12.672 c-1.059,4.63-1.584,9.058-1.584,13.28v33.629c0,0.975-0.365,1.789-1.096,2.438c-0.731,0.646-1.505,0.975-2.315,0.975 c-2.276,0-3.411-1.14-3.411-3.412v-43.62c0-3.571-0.53-7.104-1.585-10.6c-1.058-3.491-2.601-6.618-4.629-9.382 c-2.034-2.761-4.592-4.953-7.677-6.58c-3.087-1.621-6.663-2.436-10.722-2.436c-5.037,0-9.344,0.895-12.915,2.68 c-3.575,1.79-6.542,4.266-8.895,7.433c-2.357,3.167-4.063,6.944-5.117,11.331c-1.059,4.386-1.584,9.1-1.584,14.134v3.899v0.243 v32.897c0,2.272-1.138,3.412-3.412,3.412c-2.276,0-3.411-1.14-3.411-3.412v-74.567c0-2.273,1.135-3.411,3.411-3.411 c2.273,0,3.412,1.138,3.412,3.411v12.428c2.924-5.197,6.861-9.382,11.819-12.55c4.954-3.167,10.517-4.752,16.692-4.752 c6.983,0,12.995,1.991,18.032,5.97c5.033,3.983,8.688,9.223,10.966,15.719c2.76-6.336,6.739-11.533,11.94-15.596 C291.125,429.475,297.38,427.441,304.69,427.441z"/> <path fill="#414042" d="M378.753,429.392c0.811,0,1.584,0.365,2.314,1.097c0.731,0.73,1.097,1.504,1.097,2.314v74.08 c0,0.814-0.365,1.584-1.097,2.315c-0.73,0.73-1.504,1.097-2.314,1.097c-0.975,0-1.79-0.366-2.438-1.097 c-0.65-0.731-0.975-1.501-0.975-2.315v-74.08c0-0.811,0.324-1.584,0.975-2.314C376.963,429.757,377.778,429.392,378.753,429.392z" /> <path fill="#414042" d="M473.34,428.66c2.273,0,3.412,1.139,3.412,3.411l-0.487,1.95l-24.368,35.334l24.368,35.577 c0.323,0.976,0.487,1.626,0.487,1.95c0,2.272-1.139,3.412-3.412,3.412c-1.302,0-2.193-0.488-2.68-1.463l-22.906-33.384 l-22.663,33.384c-0.814,0.975-1.79,1.463-2.924,1.463c-2.277,0-3.411-1.14-3.411-3.412c0-0.324,0.159-0.975,0.486-1.95 l24.369-35.577l-24.369-35.334l-0.486-1.95c0-2.272,1.134-3.411,3.411-3.411c1.134,0,2.109,0.487,2.924,1.462l22.663,33.141 l22.906-33.141C471.146,429.147,472.038,428.66,473.34,428.66z"/> </g> <g> <g> <g opacity="0.45"> <g> <polygon fill="#010101" points="150.734,196.212 255.969,344.508 255.969,258.387"/> </g> </g> <g opacity="0.8"> <g> <polygon fill="#010101" points="255.969,258.387 255.969,344.508 361.267,196.212"/> </g> </g> <g opacity="0.6"> <g> <polygon fill="#010101" points="255.969,126.781 150.733,174.611 255.969,236.818 361.204,174.611"/> </g> </g> <g opacity="0.45"> <g> <polygon fill="#010101" points="150.734,174.612 255.969,236.818 255.969,126.782 255.969,0.001"/> </g> </g> <g opacity="0.8"> <g> <polygon fill="#010101" points="255.969,0 255.969,126.781 255.969,236.818 361.204,174.611"/> </g> </g> </g> </g> </svg> </div>` this.iconKind['fileexplorer'] = yo` <div id='fileExplorerIcons'> </div> ` this.iconKind['compile'] = yo` <div id='compileIcons'> </div> ` this.iconKind['run'] = yo` <div id='runIcons'> </div> ` this.iconKind['testing'] = yo` <div id='testingIcons'> </div> ` this.iconKind['analysis'] = yo` <div id='analysisIcons'> </div> ` this.iconKind['debugging'] = yo` <div id='debuggingIcons'> </div> ` this.iconKind['other'] = yo` <div id='otherIcons'> </div> ` this.iconKind['settings'] = yo` <div id='settingsIcons'> </div> ` this.view = yo` <div class=${css.icons}> ${home} ${this.iconKind['fileexplorer']} ${this.iconKind['compile']} ${this.iconKind['run']} ${this.iconKind['testing']} ${this.iconKind['analysis']} ${this.iconKind['debugging']} ${this.iconKind['other']} ${this.iconKind['settings']} </div> ` return this.view } } const css = csjs` .homeIcon { display: block; width: 42px; height: 42px; margin-bottom: 20px; margin-left: -5px; cursor: pointer; } .homeIcon svg path { fill: var(--primary); } .homeIcon svg polygon { fill: var(--primary); } .icons { margin-left: 10px; margin-top: 15px; } .icon { cursor: pointer; margin-bottom: 12px; width: 36px; height: 36px; padding: 3px; position: relative; border-radius: 8px; } .icon img { width: 28px; height: 28px; padding: 4px; filter: invert(0.5); } .image { } .icon svg { width: 28px; height: 28px; padding: 4px; } .icon[title='Settings'] { position: absolute; bottom: 0; } .status { position: absolute; bottom: 0; right: 0; } .statusCheck { font-size: 1.2em; } .statusWithBG border-radius: 8px; background-color: var(--danger); color: var(--light); font-size: 12px; height: 15px; text-align: center; font-weight: bold; padding-left: 5px; padding-right: 5px; } `