github.com/resonatecoop/id@v1.1.0-43/frontend/src/components/forms/profileSwitcher.js (about) 1 const Component = require('choo/component') 2 const compare = require('nanocomponent/compare') 3 const html = require('choo/html') 4 const icon = require('@resonate/icon-element') 5 const morph = require('nanomorph') 6 const imagePlaceholder = require('@resonate/svg-image-placeholder') 7 const NIL_UUID = require('../../lib/nil') 8 9 // ProfileSwitcher component class 10 // [Profile switcher for Artists and Labels only... multiple tabs, initially one only, scrolling if necessary... If Label, label tab shown first in different colour, followed by artists on label ] 11 class ProfileSwitcher extends Component { 12 /*** 13 * Create profile switcher component 14 * @param {String} id - The profile switcher component id (unique) 15 * @param {Number} state - The choo app state 16 * @param {Function} emit - Emit event on choo app 17 */ 18 constructor (id, state, emit) { 19 super(id) 20 21 this.emit = emit 22 this.state = state 23 24 this.local = state.components[id] = {} 25 26 this.handleKeyPress = this.handleKeyPress.bind(this) 27 this.updateSelection = this.updateSelection.bind(this) 28 } 29 30 /*** 31 * Create profile switcher component element 32 * @param {Object} props - The profile switcher component props 33 * @param {String} props.value - Selected value 34 * @returns {HTMLElement} 35 */ 36 createElement (props = {}) { 37 this.local.value = props.value 38 this.local.usergroups = props.usergroups || [] 39 this.onChangeCallback = typeof props.onChangeCallback === 'function' 40 ? props.onChangeCallback 41 : this.onChangeCallback 42 this.local.items = this.local.usergroups.map((item) => { 43 return { 44 value: item.id, 45 name: item.displayName, 46 banner: item.banner, 47 avatar: item.avatar 48 } 49 }) 50 51 return html` 52 <div class="mb5"> 53 ${this.renderItems()} 54 </div> 55 ` 56 } 57 58 renderItems () { 59 const length = this.local.items.length 60 61 const attrs = { 62 method: 'POST', 63 onsubmit: (e) => { 64 e.preventDefault() 65 66 this.onChangeCallback() 67 }, 68 novalidate: 'novalidate', 69 action: '/' 70 } 71 72 return html` 73 <form ${attrs}> 74 <div class="items overflow-x-auto overflow-y-hidden bg-light-gray bw bb b--mid-gray"> 75 <div class="cf flex${length <= 3 ? ' justify-center' : ''}"> 76 ${this.local.items.map((item, index) => { 77 const { value, name, avatar } = item 78 79 const id = 'usergroup-item-' + index 80 const checked = value === this.local.value 81 82 // input attrs 83 const attrs = { 84 onchange: this.updateSelection, 85 id: id, 86 tabindex: -1, 87 name: 'usergroup', 88 type: 'radio', 89 checked: checked, 90 value: value 91 } 92 93 // label attrs 94 const attrs2 = { 95 class: 'flex flex-column fw4', 96 style: 'outline:solid 1px var(--near-black);outline-offset:0px', 97 tabindex: '0', 98 title: 'Select profile', 99 onkeypress: this.handleKeyPress, 100 for: id 101 } 102 103 const src = avatar !== NIL_UUID 104 ? `https://${process.env.STATIC_HOSTNAME}/images/${avatar}-x300.jpg` 105 : imagePlaceholder(400, 400) 106 107 // item background attrs 108 const attrs3 = { 109 class: 'flex items-end pb2 aspect-ratio--object z-1', 110 style: `background: url(${src}) center center / cover no-repeat;` 111 } 112 113 return html` 114 <div class="fl flex flex-column justify-center flex-shrink-0 w4 pt2 pb4 ph3"> 115 <input ${attrs}> 116 <label ${attrs2}> 117 <div class="aspect-ratio aspect-ratio--1x1"> 118 <div ${attrs3}> 119 <div class="flex flex-shrink-0 justify-center items-center ml2"> 120 ${icon('circle', { size: 'sm', class: 'fill-transparent' })} 121 </div> 122 <span class="absolute truncate w-100 f5 bottom-0${checked ? ' b' : ''}" style="transform:translateY(100%)">${name}</span> 123 </div> 124 </div> 125 </label> 126 </div> 127 ` 128 })} 129 <div class="fl flex justify-center items-center flex-shrink-0 w4"> 130 <button type="submit" title="Create new profile" class="bg-white ba b--mid-gray br-pill w3 h3 mb3 grow"> 131 <div class="flex items-center justify-center"> 132 ${icon('add', { size: 'sm' })} 133 </div> 134 </button> 135 </div> 136 </div> 137 </div> 138 </form> 139 ` 140 } 141 142 updateSelection (e) { 143 const val = e.target.value 144 this.local.value = val 145 morph(this.element.querySelector('.items'), this.renderItems()) 146 this.onChangeCallback(this.local.value) 147 } 148 149 handleKeyPress (e) { 150 if (e.keyCode === 13) { 151 e.preventDefault() 152 e.target.control.checked = !e.target.control.checked 153 const val = e.target.control.value 154 this.local.value = val 155 morph(this.element.querySelector('.items'), this.renderItems()) 156 this.onChangeCallback(this.local.value) 157 } 158 } 159 160 handleSubmit (e) { 161 e.preventDefault() 162 163 this.onChangeCallback(this.local.value) 164 } 165 166 onSubmit () {} 167 168 update (props) { 169 return compare(this.local.usergroups, props.usergroups) || 170 this.local.value !== props.value 171 } 172 } 173 174 module.exports = ProfileSwitcher