blob: c41e10d1eded89e3b365351824e6f98096cca001 [file] [log] [blame]
/**
* @module particles-config-sk
* @description <h2><code>particles-config-sk</code></h2>
*
* <p>
* A dialog for configuring how to render a Particles JSON file.
* </p>
*
* <p>
* The form of the 'state' property looks like a serialized UploadRequest:
* </p>
* <pre>
* {
* filename: 'foo.json',
* json: {},
* }
* <pre>
*
* @evt particles-json-selected - This event is generated when the user presses Go.
* The updated state, width, and height is available in the event detail.
* There is also an indication if the particles file was changed.
*
* @evt cancelled - This event is generated when the user presses Cancel.
*
*/
import 'elements-sk/styles/buttons'
import { errorMessage } from 'elements-sk/errorMessage'
import { html, render } from 'lit-html'
import { $$ } from 'common-sk/modules/dom'
const DEFAULT_SIZE = 600;
const cancelButton = (ele) => ele._hasCancel() ? html`<button id=cancel @click=${ele._cancel}>Cancel</button>` : '';
const template = (ele) => html`
<label class=file>Particles file to upload
<input type=file name=file id=file @change=${ele._onFileChange}/>
</label>
<div class="filename ${ele._state.filename ? '' : 'empty'}">
${ele._state.filename ? ele._state.filename : 'No file selected.'}
</div>
<label class=number>
<input type=number id=width .value=${ele._width} required /> Width (px)
</label>
<label class=number>
<input type=number id=height .value=${ele._height} required /> Height (px)
</label>
<div id=dialog-buttons>
${cancelButton(ele)}
<button class=action ?disabled=${ele._readyToGo()} @click=${ele._go}>Go</button>
</div>
`;
class ParticlesConfigSk extends HTMLElement {
constructor() {
super();
this._state = {
filename: '',
json: null
};
this._width = DEFAULT_SIZE;
this._height = DEFAULT_SIZE;
this._fileChanged = false;
this._starting_state = Object.assign({}, this._state);
}
connectedCallback() {
this._render();
this.addEventListener('input', this._inputEvent);
}
disconnectedCallback() {
this.removeEventListener('input', this._inputEvent);
}
/** @prop height {Number} Selected height for animation. */
get height() { return this._height; }
set height(val) {
this._height= +val;
this._render();
}
/** @prop state {string} Object that describes the state of the config dialog. */
get state() { return this._state; }
set state(val) {
this._state = Object.assign({}, val);
this._starting_state = Object.assign({}, this._state);
this._render();
}
/** @prop width {Number} Selected width for animation. */
get width() { return this._width; }
set width(val) {
this._width = +val;
this._render();
}
_hasCancel() {
return !!this._starting_state.json;
}
_readyToGo() {
return !this._state.json;
}
_onFileChange(e) {
this._fileChanged = true;
let reader = new FileReader();
reader.addEventListener('load', () => {
let parsed = {};
try {
parsed = JSON.parse(reader.result);
}
catch(error) {
errorMessage(`Not a valid JSON file: ${error}`);
return;
}
this._state.json = parsed;
this._state.filename = e.target.files[0].name;
this._width = parsed.w || DEFAULT_SIZE;
this._height = parsed.h || DEFAULT_SIZE;
this._render();
});
reader.addEventListener('error', () => {
errorMessage('Failed to load.');
});
reader.readAsText(e.target.files[0]);
}
_updateState() {
this._width = +$$('#width', this).value;
this._height = +$$('#height', this).value;
}
_go() {
this._updateState();
this.dispatchEvent(new CustomEvent('particles-json-selected', { detail: {
'state' : this._state,
'fileChanged': this._fileChanged,
'width' : this._width,
'height': this._height,
}, bubbles: true }));
}
_cancel() {
this.dispatchEvent(new CustomEvent('cancelled', { bubbles: true }));
}
_inputEvent() {
this._updateState();
this._render();
}
_render() {
render(template(this), this, {eventContext: this});
}
};
window.customElements.define('particles-config-sk', ParticlesConfigSk);