github.com/stampzilla/stampzilla-go@v2.0.0-rc9+incompatible/nodes/stampzilla-server/web/src/routes/automation/components/SavedStatePicker.js (about) 1 import React from 'react'; 2 import Modal from 'react-modal'; 3 import classnames from 'classnames'; 4 import { connect } from 'react-redux'; 5 import makeUUID from 'uuid/v4'; 6 7 import './SavedStatePicker.scss'; 8 import Scene from './Scene'; 9 import { save } from '../../../ducks/savedstates'; 10 11 Modal.setAppElement('#app'); 12 13 const valueToText = (value, savedstates) => { 14 if (value && value.length === 36) { 15 return ( 16 <React.Fragment> 17 <small>Scene</small>{' '} 18 <strong>{savedstates.getIn([value, 'name']) || value}</strong> 19 </React.Fragment> 20 ); 21 } 22 23 if (value && value.length > 0) { 24 return ( 25 <React.Fragment> 26 <small>Delay for</small> <strong>{value}</strong> 27 </React.Fragment> 28 ); 29 } 30 31 return 'New action'; 32 }; 33 34 const valueToTab = (value) => { 35 if (value && value.length === 36) { 36 return 'state'; 37 } 38 39 if (value && value.length > 0) { 40 return 'time'; 41 } 42 43 return 'state'; 44 }; 45 46 class SavedStatePicker extends React.Component { 47 constructor(props) { 48 super(props); 49 50 const scene = props.savedstates.get(props.value); 51 this.state = { 52 tab: valueToTab(props.value), 53 value: props.value, 54 modalIsOpen: props.value === undefined, 55 scene: (scene && scene.toJS()) || {}, 56 }; 57 58 this.selectRef = React.createRef(); 59 } 60 61 componentWillReceiveProps(props) { 62 if (props.value !== this.props.value) { 63 const scene = props.savedstates.get(props.value); 64 this.setState({ 65 tab: valueToTab(props.value), 66 value: props.value, 67 scene: (scene && scene.toJS()) || {}, 68 }); 69 } 70 } 71 72 onSceneChange = () => (state) => { 73 const { scene } = this.state; 74 this.setState({ 75 scene: { ...scene, state }, 76 }); 77 }; 78 79 openModal = () => () => { 80 const { value } = this.props; 81 this.setState({ 82 modalIsOpen: true, 83 tab: valueToTab(value), 84 value, 85 }); 86 return false; 87 }; 88 89 closeModal = () => () => { 90 this.setState({ modalIsOpen: false }); 91 }; 92 93 save = () => () => { 94 const { onChange, dispatch } = this.props; 95 const { value, scene } = this.state; 96 97 if (value.length === 36) { 98 dispatch(save({ ...scene, uuid: value })); 99 } 100 101 onChange(value); 102 this.setState({ modalIsOpen: false }); 103 }; 104 105 render() { 106 const { 107 tab, modalIsOpen, value, scene, 108 } = this.state; 109 const { state: states } = scene || {}; 110 const { savedstates, options } = this.props; 111 112 return ( 113 <React.Fragment> 114 <div 115 className="btn btn-secondary btn-block" 116 onClick={this.openModal()} 117 role="button" 118 tabIndex={-1} 119 > 120 {valueToText(this.props.value, savedstates)} 121 </div> 122 <Modal 123 className="Modal__Bootstrap modal-dialog saved-state-modal" 124 closeTimeoutMS={150} 125 isOpen={modalIsOpen} 126 onRequestClose={this.closeModal()} 127 > 128 <div className="modal-content"> 129 <div style={{ display: 'flex' }}> 130 <ul 131 className="nav nav-tabs" 132 style={{ marginLeft: '-1px', flex: '1 1 0%' }} 133 > 134 <li className="nav-item"> 135 <a 136 className={classnames( 137 'nav-link', 138 tab === 'state' && 'active', 139 )} 140 role="button" 141 tabIndex="0" 142 onClick={() => this.setState({ tab: 'state' })} 143 > 144 Scene 145 </a> 146 </li> 147 {!(value && value.length === 36) && !options.hideDelay && ( 148 <li className="nav-item"> 149 <a 150 className={classnames( 151 'nav-link', 152 tab === 'time' && 'active', 153 )} 154 role="button" 155 tabIndex="0" 156 onClick={() => this.setState({ tab: 'time' })} 157 > 158 Time delay 159 </a> 160 </li> 161 )} 162 </ul> 163 <button 164 type="button" 165 className="close" 166 style={{ 167 padding: '0px 15px', 168 borderBottom: '1px solid rgb(222, 226, 230)', 169 }} 170 onClick={this.closeModal()} 171 > 172 <span aria-hidden="true">×</span> 173 <span className="sr-only">Close</span> 174 </button> 175 </div> 176 <div className="modal-body"> 177 {tab === 'time' && ( 178 <form> 179 <div className="form-group"> 180 <label htmlFor="name">Duration</label> 181 <input 182 type="text" 183 id="name" 184 className="form-control" 185 placeholder="ex. 2h15m" 186 value={value || ''} 187 onChange={event => 188 this.setState({ value: event.target.value }) 189 } 190 /> 191 <small className="form-text text-muted"> 192 ParseDuration parses a duration string. A duration string 193 is a sequence of decimal numbers, each with optional 194 fraction and a unit suffix, such as <strong>300ms</strong> 195 , <strong>1.5h</strong> or <strong>2h45m</strong>. Valid 196 time units are <strong>n</strong>, <strong>us</strong> (or{' '} 197 <strong>µs</strong>), <strong>ms</strong>, 198 <strong>s</strong>, <strong>m</strong>, <strong>h</strong> 199 . 200 </small> 201 </div> 202 </form> 203 )} 204 {tab === 'state' && (!value || value.length !== 36) && ( 205 <React.Fragment> 206 <button 207 type="button" 208 className="btn btn-success btn-block" 209 onClick={() => this.setState({ value: makeUUID() })} 210 > 211 Create a new scene 212 </button> 213 <div className="text-center p-4">or select an existing</div> 214 <div> 215 <select 216 size={10} 217 style={{ width: '100%' }} 218 ref={this.selectRef} 219 > 220 {savedstates.map(savedstate => ( 221 <option value={savedstate.get('uuid')}> 222 {savedstate.get('name')} 223 </option> 224 ))} 225 </select> 226 </div> 227 <button 228 type="button" 229 className="btn btn-secondary btn-block" 230 onClick={() => { 231 this.setState({ 232 value: this.selectRef.current.value, 233 scene: 234 savedstates 235 .get(this.selectRef.current.value) 236 .toJS() || {}, 237 }); 238 }} 239 > 240 Select 241 </button> 242 </React.Fragment> 243 )} 244 {tab === 'state' && value && value.length === 36 && ( 245 <form> 246 <div className="form-group"> 247 <label htmlFor="name">Name</label> 248 <input 249 type="text" 250 id="name" 251 className="form-control" 252 placeholder="" 253 value={(scene && scene.name) || ''} 254 onChange={event => 255 this.setState({ 256 scene: { ...scene, name: event.target.value }, 257 }) 258 } 259 /> 260 <small className="form-text text-muted"> 261 Give the state a good name so you can find it later 262 </small> 263 </div> 264 <div className="form-group"> 265 <label htmlFor="name">State</label> 266 <Scene 267 id={value} 268 onChange={this.onSceneChange()} 269 states={states} 270 /> 271 </div> 272 </form> 273 )} 274 </div> 275 <div className="modal-footer"> 276 {!value && ( 277 <button 278 type="button" 279 className="btn btn-secondary" 280 onClick={this.closeModal()} 281 > 282 Close 283 </button> 284 )} 285 {value && ( 286 <button 287 type="button" 288 className="btn btn-primary" 289 onClick={this.save()} 290 > 291 Use 292 </button> 293 )} 294 </div> 295 </div> 296 </Modal> 297 </React.Fragment> 298 ); 299 } 300 } 301 302 const mapStateToProps = state => ({ 303 devices: state.getIn(['devices', 'list']), 304 savedstates: state.getIn(['savedstates', 'list']), 305 }); 306 307 export default connect(mapStateToProps)(SavedStatePicker);