UNPKG

@panoramax/web-viewer

Version:

Panoramax web viewer for geolocated pictures

158 lines (145 loc) 4.57 kB
import { LitElement, html, css } from "lit"; import Basic from "../core/Basic"; import { parseSemanticsString, computeDiffTags } from "../../utils/semantics"; import { textarea } from "../styles"; import JSON5 from "json5"; /** * Semantics Editor offer an easy-to-use input for adding or editing semantics tags. * * It manipulates list of `{key: "mypanokey", value: "myvalue"}` entries through `semantics` attribute. * * @class Panoramax.components.ui.SemanticsEditor * @element pnx-semantics-editor * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/) * @fires Panoramax.components.ui.SemanticsEditor#change * @example * ```html * <!-- Basic example --> * <pnx-semantics-editor * id="editor" * semantics=${mypic.semantics} * ._t=${viewer._t} * onchange=${e => console.log(e.detail.semantics)} * /> * * <!-- You can access editor and check its validity through native web browser functions --> * <script> * const editor = document.getElementById("editor"); * console.log(editor.checkValidity()); // True if input is valid * </script> * * <!-- You can change specifically style of textarea --> * <style> * pnx-semantics-editor::part(text) { * color: blue; * } * </style> * ``` */ export default class SemanticsEditor extends LitElement { static styles = [textarea, css` textarea:invalid { border: 2px solid var(--red); background-color:var(--beige); } `]; /** * Component properties. * @memberof Panoramax.components.ui.SemanticsEditor# * @type {Object} * @property {object} [semantics] The `semantics` field of a picture or annotation feature. It is updated when field changes, but reflect the whole list of new tags (not delta needed by API). If you want delta, please use getDiff function. * @property {number} [rows=3] The amount of rows shown for textarea */ static properties = { semantics: {converter: Basic.GetJSONConverter(), reflect: true}, rows: {type: Number}, _valid: {state: true}, _t: {converter: Basic.GetJSONConverter()}, _firstSemantics: {state: true}, }; constructor() { super(); this.semantics = null; this._valid = true; this._firstSemantics = null; this.rows = 3; } /** @private */ connectedCallback() { super.connectedCallback(); this._firstSemantics = this.semantics === null ? [] : JSON5.parse(JSON5.stringify(this.semantics)); } /** * Get current delta between initial tags and user changes. * @memberof Panoramax.components.ui.SemanticsEditor# * @returns {object[]} The list of tag changes (in API format) */ getDiff() { return computeDiffTags(this._firstSemantics || [], this.semantics); } /** * Check if input is having a valid value. * @memberof Panoramax.components.ui.SemanticsEditor# * @returns {boolean} True if it's valid */ checkValidity() { return this._valid; } /** @private */ _onInput(e) { const tarea = e.target; try { this.semantics = parseSemanticsString(tarea.value) || []; this._valid = true; } catch (err) { if(err.message !== "Invalid tags") { console.error(err); } this._valid = false; } } /** @private */ _onBlur(e) { const prevValidity = e.target.checkValidity(); if(this._valid) { e.target.setCustomValidity(""); /** * Event for value change. * * Note that this event is launched only on valid input. * * @event Panoramax.components.ui.SemanticsEditor#change * @type {CustomEvent} * @property {object[]} detail.semantics The new tags list (in API semantics property format) * @property {object[]} detail.delta The delta between old and current tags (expected by API) */ this.dispatchEvent(new CustomEvent("change", { detail: { semantics: this.semantics || [], delta: computeDiffTags(this._firstSemantics || [], this.semantics), } })); } else if(prevValidity) { // Do not call again if already shows up, to fix Chrome issue e.target.setCustomValidity(this._t?.pnx.semantics_editor_error || "Invalid syntax"); e.target.reportValidity(); } } /** @private */ render() { /* eslint-disable indent */ return html` <textarea part="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="key1=value1\nprefix|key2=value2" @input=${this._onInput} @blur=${this._onBlur} rows=${this.rows} .value=${(this.semantics || []).map(s => `${s.key}=${s.value}`).join("\n")} ></textarea> `; } } customElements.define("pnx-semantics-editor", SemanticsEditor);