diff --git a/css/iui.css b/css/iui.css index d4fa756..9a8b921 100644 --- a/css/iui.css +++ b/css/iui.css @@ -1935,7 +1935,7 @@ _:-moz-tree-row(hover), html[dir='rtl'] .autocomplete-menu, html[dir='rtl'] .sel } .input > input { - width: 100%; + width: calc(100% - 10px); } .input > input, .input > textarea, .select, .textbox { diff --git a/src/Router/Router.js b/src/Router/Router.js index b473825..742268a 100644 --- a/src/Router/Router.js +++ b/src/Router/Router.js @@ -115,6 +115,11 @@ export default IUI.module( async navigate(url, data, target, state, dataToQuery = true) { + if (this.isLoading || target?.isLoading){ + console.warn("Router in loading mode."); + return; + } + if (url == null) throw new Error("URL not specified."); diff --git a/src/Router/Target.js b/src/Router/Target.js index c267801..d624b11 100644 --- a/src/Router/Target.js +++ b/src/Router/Target.js @@ -7,6 +7,7 @@ export default IUI.module(class Target extends IUIElement { $messageElement; $progressElement; + $loadingMode = false; constructor(properties) { super(IUI.extend(properties, { cssClass: 'target' })); @@ -16,12 +17,18 @@ export default IUI.module(class Target extends IUIElement { } setLoading(value) { + this.$loadingMode = value; + if (value) this.$loadingElement.classList.add(this.cssClass + "-loading-visible"); else this.$loadingElement.classList.remove(this.cssClass + "-loading-visible"); } + get isLoading() + { + return this.$loadingMode; + } async setMessage(message) { await this.$messageElement.setData({ message }); diff --git a/src/UI/Radio.js b/src/UI/Radio.js index 7445e3f..05c35c8 100644 --- a/src/UI/Radio.js +++ b/src/UI/Radio.js @@ -13,6 +13,8 @@ export default IUI.module(class Radio extends IUIElement { this._register("select"); this._register("input"); this._register("add"); + + //this.selected = null; } _checkValidity() { @@ -51,6 +53,46 @@ export default IUI.module(class Radio extends IUIElement { return this.hasAttribute("add"); } + _selectIndex(index){ + let found = null; + + for(let i = 0; i < this.repeat.children.length; i++) + { + let el = this.repeat.children[i]; + + if (i == index) + { + found = el.data; + el.classList.add(this.cssClass + "-selected"); + } + else { + el.classList.remove(this.cssClass + "-selected"); + } + } + + return found; + } + + _select(value){ + + let found = false; + + for(let i = 0; i < this.repeat.children.length; i++) + { + let el = this.repeat.children[i]; + + if (el.data == value) + { + found = true; + el.classList.add(this.cssClass + "-selected"); + } + else { + el.classList.remove(this.cssClass + "-selected"); + } + } + return false; + } + async create() { this.isAuto = this.hasAttribute("auto"); @@ -68,10 +110,16 @@ export default IUI.module(class Radio extends IUIElement { this.repeat = new Repeat(); this.repeat.cssClass = this.cssClass + "-repeat"; - + if (this.hasAttribute("menu")) { + let menuData = this.getAttribute("menu"); + this.repeat.setAttribute(":data", menuData);// "d[1]"); + } + + this.repeat.setAttribute("i-skip-data", "skip"); + this.repeat.on("click", async (e) => { - - if (e.target !== self.repeat) + + if (e.target == self.repeat) return; for(let i = 0; i < self.repeat.children.length; i++) @@ -80,10 +128,21 @@ export default IUI.module(class Radio extends IUIElement { if (el.contains(e.target)) { + el.classList.add(self.cssClass + "-selected"); - await self.setData(el.data); - self._emit("input", { value: el.data }); + + try { + self._emit("input", { value: el.data }); + //self._emit("select", { value: el.data }); + self._skipQuery = true; + await self.setData(el.data); + self._skipQuery = false; + //self.selected = el.data; + } + catch { + + } } else { el.classList.remove(self.cssClass + "-selected"); @@ -91,8 +150,11 @@ export default IUI.module(class Radio extends IUIElement { } }); + let html = this.innerHTML; + // get repeated item - this.repeat.innerHTML = this.innerHTML; + this.repeat.innerHTML = html; + this.innerHTML = ""; this.appendChild(this.repeat); @@ -119,7 +181,6 @@ export default IUI.module(class Radio extends IUIElement { await IUI.created(this.repeat); } - await this._query(); } get disabled() { @@ -153,32 +214,32 @@ export default IUI.module(class Radio extends IUIElement { if (res instanceof Promise) res = await res; } - + await this.repeat.setData(res); - if (this.repeat.data.length == 0) - await self.setData(null); + //if (this.repeat.data.length == 0) + // await self.setData(null); } async setData(value, radix) { - // this.label.innerHTML = ""; - await super.setData(value, radix); - try { - //let text = this.formatter(value); - // this.label.innerHTML = text == null ? "" : text; + if (!this._skipQuery) { + await this._query(); + + if (!this._select(value)) + super.setData(this._selectIndex(0), radix); + } + + try { this._emit("select", { value }); } catch (ex) { - //console.log(ex); - this._emit("select", { value }); - } - //this._checkValidity(); + } if (this._checkValidity() && this.isAuto) diff --git a/src/UI/Search.js b/src/UI/Search.js new file mode 100644 index 0000000..8b1fd6d --- /dev/null +++ b/src/UI/Search.js @@ -0,0 +1,353 @@ +import { IUI, iui } from '../Core/IUI.js'; +import IUIElement from '../Core/IUIElement.js'; +import Menu from '../UI/Menu.js'; +import Layout from '../Data/Layout.js'; +import Repeat from '../Data/Repeat.js'; + +export default IUI.module(class Select extends IUIElement { + constructor() { + super({ + visible: false, + searchlist: false, + query: (x) => null, + }); + + this._register("select"); + this._register("input"); + this._register("add"); + } + + disconnectedCallback() { + //console.log("Select removed", this); + if (!this.searchlist && this.menu) + app.removeChild(this.menu); + + } + + connectedCallback(){ + super.connectedCallback(); + if (!this.searchlist && this.menu) + app.appendChild(this.menu); + } + + + _checkValidity() { + + if (this.validate != null) { + try { + let valid = this.validate.apply(this); + if (!valid) { + this.setAttribute("invalid", ""); + this.classList.add(this.cssClass + "-invalid"); + return false; + } + else { + this.removeAttribute("invalid"); + this.classList.remove(this.cssClass + "-invalid"); + return true; + } + } + catch (ex) { + console.log("Validation Error", ex); + return false; + } + } + + return true; + } + + + async create() { + + this.isAuto = this.hasAttribute("auto"); + this.field = this.getAttribute("field"); + + + if (this.field != null) + { + this.setAttribute(":data", `d['${this.field}']`) + this.setAttribute(":revert", `d['${this.field}'] = this.data`); + } + + + let self = this; + + + this.repeat = new Repeat(); + this.repeat.cssClass = this.cssClass + "-menu-repeat"; + + + if (this.hasAttribute("menu")) { + let menuData = this.getAttribute("menu"); + this.repeat.setAttribute(":data", menuData); + } + + if (this.hasAttribute("footer")) { + let footer = this.getAttribute("footer"); + this.footer = document.createElement("div"); + this.footer.className = this.cssClass + "-footer"; + this.footer.innerHTML = footer;// "${d[0]}"; + } + + this.menu = new Menu({ cssClass: this.cssClass + "-menu", "target-class": "" }); + + this.menu.on("click", async (e) => { + + if (e.target != self.textbox && e.target != self.footer && e.target !== self.menu && e.target != self.repeat) { + await self.setData(e.target.data); + + self._emit("input", { value: e.target.data }); + + self.hide(); + } + }).on("visible", x=> { if (!x.visible) self.hide()}); + + + this.textbox = document.createElement("input"); + this.textbox.type = "search"; + this.textbox.className = this.cssClass + "-textbox"; + + if (this.placeholder) + this.textbox.placeholder = this.placeholder; + + this.textbox.addEventListener("keyup", function (e) { + if (e.keyCode != 13) { + self._query(0, self.textbox.value); + } + }); + + this.textbox.addEventListener("search", function (e) { + // console.log(e); + }); + + this.appendChild(this.textbox); + + + // get collection + let layout = Layout.get(this, "div", true, true); + + + if (layout != null && layout.label != undefined && layout.menu != undefined) { + this.label = layout.label.node; + this.repeat.appendChild(layout.menu.node); + } + else if (layout != null && layout.null != null) + { + this.label = layout.null.node; + this.repeat.appendChild(layout.null.node.cloneNode(true)); + } + else + { + this.label = document.createElement("div"); + this.label.innerHTML = this.innerHTML; + this.repeat.innerHTML = this.innerHTML; + } + + // clear everything else + //this.innerHTML = ""; + + this.label.className = this.cssClass + "-label"; + + this.appendChild(this.label); + + this.label.addEventListener("click", function (e) { + self.show(); + }); + + this.menu.appendChild(this.repeat); + if (this.footer != null) + this.menu.appendChild(this.footer); + + + if (this.searchlist) + this.appendChild(this.menu); + else + { + app.appendChild(this.menu); + if (app.loaded) + { + + + await IUI.create(this.menu); + + IUI.bind(this.menu, false, "menu", this.__i_bindings?.scope, false); + // update referencing + this.__i_bindings?.scope?.refs?._build(); + + await IUI.created(this.menu); + + /////console.log("Append", this.menu); + //await this.menu.create(); + + //IUI.bind(this.menu, false, "menu"); + //await IUI.create(this.menu); + + //await await IUI.create(e); + + } + } + + this.addEventListener("click", function (e) { + if (e.target == self.textbox) + self.show(); + }); + } + + get disabled() { + return this.hasAttribute("disabled"); + } + + set disabled(value) { + if (this._autocomplete) { + this.textbox.disabled = value; + } + + if (value) { + this.setAttribute("disabled", value); + } + else { + this.removeAttribute("disabled"); + } + + + } + /* + set(item) { + + if (this.autocomplete != undefined) { + if (item != null) + this.textbox.value = this.layout.text.formatter ? this.layout.text.formatter(item[this.layout.text.field], item) : item[this.layout.text.field]; + else + this.textbox.value = ""; + } else { + if (item != null) + this.label.innerHTML = this.layout.text.formatter ? this.layout.text.formatter(item[this.layout.text.field], item) : item[this.layout.text.field]; + else + this.label.innerHTML = ""; + } + + this.selected = item; + this._emit("select", item); + } + */ + + show() { + this.setVisible(true); + //this.textbox.focus(); + } + + hide() { + this.setVisible(false); + //this.textbox.focus(); + } + + clear() { + if (this.autocomplete !== undefined) + this.textbox.value = ""; + //else + // this.label.innerHTML = ""; + + //this.menu.clear(); + this.response.start = 0; + this.selected = null; + } + + async _query() { + + + if (this._autocomplete) + if (this.disabled) + return; + + let self = this; + let text = this._autocomplete ? this.textbox.value : null; + let res; + + if (this.query instanceof Array) { + res = this.query; + } + else if (this.query instanceof Function) { + res = this.query(0, text) + if (res instanceof Promise) + res = await res; + } + + + + //if (res[1].length == 0) + // await self.setData(null); + + await this.menu.setData(res); + + if (this.repeat.data.length == 0) + await self.setData(null); + } + + + async setData(value, radix) { + + // this.label.innerHTML = ""; + + await super.setData(value, radix); + + try { + //let text = this.formatter(value); + // this.label.innerHTML = text == null ? "" : text; + + this._emit("select", { value }); + } + catch (ex) { + //console.log(ex); + this._emit("select", { value }); + } + + //this._checkValidity(); + + + if (this._checkValidity() && this.isAuto) + this.revert(); + + } + + + setVisible(visible) { + + if (visible == this.visible) + return; + + //console.log("SLCT: SetVisible", visible); + + if (visible) { + this._query(0); + + // show menu + var rect = this.getBoundingClientRect(); + this.menu.style.width = (this.clientWidth - this._computeMenuOuterWidth()) + "px"; + this.menu.style.paddingTop = rect.height + "px"; + this.menu.setVisible(true, rect.left, rect.top);//, this.menu); + this.visible = true; + + this.classList.add(this.cssClass + "-visible"); + + if (this._autocomplete) + setTimeout(() => { + this.textbox.focus(); + }, 100); + + } + else { + this.visible = false; + this.classList.remove(this.cssClass + "-visible"); + + this.menu.hide(); + } + + //this.textbox.focus(); + + } + + _computeMenuOuterWidth() { + return this.menu.offsetWidth - this.menu.clientWidth; + } + +}); \ No newline at end of file