1import {css, html, LitElement} from 'lit'; 2import {customElement, property} from 'lit/decorators.js'; 3 4import {Notifiable, SimulationInfo, simulationState,} from './device-observer.js'; 5 6@customElement('ns-cube-sprite') 7export class CubeSprite extends LitElement implements Notifiable { 8 /** 9 * the yaw value in orientation for ns-cube-sprite 10 * unit: deg 11 */ 12 @property({type: Number}) yaw = -15; 13 14 /** 15 * the pitch value in orientation for ns-cube-sprite 16 * unit: deg 17 */ 18 @property({type: Number}) pitch = -15; 19 20 /** 21 * the roll value in orientation for ns-cube-sprite 22 * unit: deg 23 */ 24 @property({type: Number}) roll = 0; 25 26 /** 27 * the z value in position for ns-cube-sprite 28 * unit: cm 29 */ 30 @property({type: Number}) posZ = 0; 31 32 /** 33 * the css value for color 34 */ 35 @property({type: css, attribute: 'color'}) color = css`red`; 36 37 /** 38 * the css value for size 39 */ 40 @property({type: css, attribute: 'size'}) size = css`30px`; 41 42 /** 43 * A Boolean property; if set true, the user would 44 * be able to control the cube's pitch, yaw, and roll 45 * with the info panel. 46 */ 47 @property({type: Boolean}) controls = false; 48 49 /** 50 * A Boolean property; if set true, the box is selected 51 * therefore the outline gets dotted. 52 */ 53 @property({type: Boolean}) highlighted = false; 54 55 connectedCallback() { 56 super.connectedCallback(); // eslint-disable-line 57 simulationState.registerObserver(this); 58 window.addEventListener('orientationEvent', this.handleOrientationEvent); 59 } 60 61 disconnectedCallback() { 62 window.removeEventListener('orientationEvent', this.handleOrientationEvent); 63 simulationState.removeObserver(this); 64 super.disconnectedCallback(); // eslint-disable-line 65 } 66 67 onNotify(data: SimulationInfo) { 68 this.highlighted = data.selectedId === this.id; 69 for (const device of data.devices) { 70 if (device.name === this.id) { 71 this.posZ = device.position.z * 100; 72 return; 73 } 74 } 75 } 76 77 static styles = css` 78 :host { 79 /** all sizes are relative to font-size **/ 80 display: block; 81 min-height: 1.5em; 82 min-width: 1.5em; 83 width: 1em; 84 /* overflow: hidden; */ 85 transform-origin: center; 86 transform-style: preserve-3d; 87 transform: translateZ(calc(var(--posZ) * 1px)); 88 cursor: move; 89 } 90 91 .cube { 92 transform-style: preserve-3d; 93 transform: rotateX(calc(var(--yaw) * 1deg)) 94 rotateY(calc(var(--pitch) * 1deg)) rotateZ(calc(var(--roll) * 1deg)); 95 position: absolute; 96 left: 0.25em; 97 bottom: 0.25em; 98 width: 1em; 99 height: 1em; 100 } 101 .cube > div { 102 position: absolute; 103 background-color: var(--color); 104 width: 100%; 105 height: 100%; 106 box-shadow: 0 0 0.25em #000 inset; 107 } 108 .cube > div:nth-child(1) { 109 transform: translateZ(0.5em); 110 } 111 .cube > div:nth-child(2) { 112 transform: rotateY(180deg) translateZ(0.5em); 113 } 114 .cube > div:nth-child(3) { 115 right: 0; 116 width: 1em; 117 transform: rotateY(90deg) translateZ(0.5em); 118 } 119 .cube > div:nth-child(4) { 120 width: 1em; 121 transform: rotateY(270deg) translateZ(0.5em); 122 } 123 .cube > div:nth-child(5) { 124 bottom: -0.5em; 125 height: 1em; 126 transform: rotateX(90deg); 127 box-shadow: 0 0 0.25em #000 inset, 0 0 0.25em #000; 128 } 129 .cube div:nth-child(6) { 130 height: 1em; 131 transform: translateY(-0.5em) rotateX(90deg); 132 overflow: hidden; 133 } 134 135 .line { 136 position: absolute; 137 border-bottom: 5px dashed; 138 width: calc(var(--posZ) * 1px); 139 top: 50%; 140 left: 50%; 141 transform: rotateY(90deg) rotateX(90deg); 142 transform-origin: left; 143 } 144 145 .base { 146 position: absolute; 147 border: 5px solid; 148 border-radius: 50%; 149 background-color: black; 150 height: 5px; 151 width: 5px; 152 top: 50%; 153 left: 50%; 154 transform: translate3d(-50%, -50%, calc(var(--posZ) * -1px)); 155 } 156 `; 157 158 private handleOrientationEvent = (e: Event) => { 159 const {detail} = e as CustomEvent; 160 if (detail.name === this.id && this.controls) { 161 if (detail.type === 'yaw') { 162 this.yaw = detail.value; 163 } else if (detail.type === 'pitch') { 164 this.pitch = detail.value; 165 } else { 166 this.roll = detail.value; 167 } 168 } 169 }; 170 171 render() { 172 // TODO(b/255635486): Make cube easily scalable with user input size 173 return html` 174 <style> 175 :host { 176 font-size: ${this.size}; 177 --color: ${this.color}; 178 --yaw: ${this.yaw}; 179 --pitch: ${this.pitch}; 180 --roll: ${this.roll}; 181 --posZ: ${this.controls ? this.posZ : 0}; 182 } 183 .cube > div { 184 outline: ${this.highlighted && this.controls ? css`dashed` : css``}; 185 } 186 </style> 187 <div class="cube"> 188 <div></div> 189 <div></div> 190 <div></div> 191 <div></div> 192 <div></div> 193 <div></div> 194 </div> 195 ${ 196 this.controls ? html` 197 <div class="line"></div> 198 <div class="base"></div> 199 ` : 200 html``} 201 `; 202 } 203} 204