From c7862b733d80e404ae8d5ce93678b2b850c8dbba Mon Sep 17 00:00:00 2001 From: Raruto Date: Fri, 9 Feb 2024 09:13:38 +0100 Subject: [PATCH 001/377] Refactor `Panel` and `Component` classes --- src/app/api.js | 1 - src/app/core/g3w-barstack.js | 174 +++ src/app/core/g3w-component.js | 429 +++++++ src/app/core/g3w-panel.js | 79 ++ src/app/g3w-ol/controls/streetviewcontrol.js | 21 +- src/app/gui/catalog/vue/catalog.js | 59 +- src/app/gui/changemapmenu/changemapmenu.js | 22 +- src/app/gui/component/component.js | 410 +----- src/app/gui/form/vue/form.js | 95 +- src/app/gui/map/vue/map.js | 50 +- src/app/gui/metadata/metadataservice.js | 99 -- .../metadata/vue/components/layer/layer.js | 18 - .../vue/components/project/project.js | 18 - src/app/gui/metadata/vue/metadata.js | 43 +- src/app/gui/panel.js | 79 +- src/app/gui/print/printservice.js | 12 +- src/app/gui/print/vue/print.js | 46 +- src/app/gui/print/vue/printpage.js | 29 - src/app/gui/projectsmenu/menu.js | 28 - src/app/gui/projectsmenu/projectsmenu.js | 37 +- .../gui/querybuilder/querybuilderuifactory.js | 36 +- .../vue/panel/querybuilderpanel.js | 14 - src/app/gui/querybuilder/vue/querybuilder.js | 5 - src/app/gui/queryresults/vue/queryresults.js | 44 +- src/app/gui/relations/relationsservice.js | 77 -- src/app/gui/relations/vue/relationspage.js | 42 +- src/app/gui/search/service.js | 80 -- src/app/gui/search/vue/panel/searchpanel.js | 33 +- src/app/gui/search/vue/panel/searchservice.js | 994 +-------------- src/app/gui/search/vue/search.js | 36 +- .../spatialbookmarks/vue/spatialbookmarks.js | 32 +- src/app/gui/streetview/vue/streetview.js | 25 - src/app/gui/table/tableservice.js | 786 ------------ src/app/gui/table/vue/table.js | 73 -- src/app/gui/tools/vue/tools.js | 37 +- src/app/gui/utils/barstack.js | 172 +-- src/app/gui/utils/utils.js | 3 - src/app/gui/viewport/contentsviewer.js | 163 +-- src/app/gui/wms/vue/wms.js | 443 +------ src/components/CatalogLayerContextMenu.vue | 846 ++++++++++++- .../g3w-catalog.js} | 81 +- src/components/g3w-changemapmenu.js | 19 + src/components/g3w-contentsviewer.js | 146 +++ src/components/g3w-form.js | 56 + src/components/g3w-map.js | 41 + src/components/g3w-metadata.js | 79 ++ src/components/g3w-print.js | 35 + src/components/g3w-projectsmenu.js | 38 + src/components/g3w-querybuilder.js | 30 + src/components/g3w-queryresults.js | 38 + src/components/g3w-relationspage.js | 108 ++ src/components/g3w-search.js | 1107 +++++++++++++++++ src/components/g3w-spatialbookmarks.js | 23 + .../service.js => components/g3w-tools.js} | 67 +- src/components/g3w-wms.js | 437 +++++++ src/services/floatbar.js | 7 +- src/services/sidebar.js | 6 +- 57 files changed, 3922 insertions(+), 4086 deletions(-) create mode 100644 src/app/core/g3w-barstack.js create mode 100644 src/app/core/g3w-component.js create mode 100644 src/app/core/g3w-panel.js delete mode 100644 src/app/gui/metadata/metadataservice.js delete mode 100644 src/app/gui/metadata/vue/components/layer/layer.js delete mode 100644 src/app/gui/metadata/vue/components/project/project.js delete mode 100644 src/app/gui/print/vue/printpage.js delete mode 100644 src/app/gui/projectsmenu/menu.js delete mode 100644 src/app/gui/querybuilder/vue/panel/querybuilderpanel.js delete mode 100644 src/app/gui/querybuilder/vue/querybuilder.js delete mode 100644 src/app/gui/relations/relationsservice.js delete mode 100644 src/app/gui/search/service.js delete mode 100644 src/app/gui/streetview/vue/streetview.js delete mode 100644 src/app/gui/table/tableservice.js delete mode 100644 src/app/gui/table/vue/table.js delete mode 100644 src/app/gui/utils/utils.js rename src/{app/gui/catalog/catalogservice.js => components/g3w-catalog.js} (67%) create mode 100644 src/components/g3w-changemapmenu.js create mode 100644 src/components/g3w-contentsviewer.js create mode 100644 src/components/g3w-form.js create mode 100644 src/components/g3w-map.js create mode 100644 src/components/g3w-metadata.js create mode 100644 src/components/g3w-print.js create mode 100644 src/components/g3w-projectsmenu.js create mode 100644 src/components/g3w-querybuilder.js create mode 100644 src/components/g3w-queryresults.js create mode 100644 src/components/g3w-relationspage.js create mode 100644 src/components/g3w-search.js create mode 100644 src/components/g3w-spatialbookmarks.js rename src/{app/gui/tools/service.js => components/g3w-tools.js} (69%) create mode 100644 src/components/g3w-wms.js diff --git a/src/app/api.js b/src/app/api.js index 5ca8ab6d6..7c5b101df 100644 --- a/src/app/api.js +++ b/src/app/api.js @@ -165,7 +165,6 @@ const QueryResultsComponent = require('gui/queryresults/vue/queryresults'); const FormComponent = require('gui/form/vue/form'); const FormService = require('gui/form/formservice'); const InputsComponents = require('gui/inputs/inputs'); -const ChartsFactory = require('gui/charts/chartsfactory'); const Fields = require('gui/fields/fields'); const SearchPanelService = require('gui/search/vue/panel/searchservice'); diff --git a/src/app/core/g3w-barstack.js b/src/app/core/g3w-barstack.js new file mode 100644 index 000000000..6ea00acf3 --- /dev/null +++ b/src/app/core/g3w-barstack.js @@ -0,0 +1,174 @@ +/** + * @file + * @since 3.10.0 + */ + +import Panel from 'core/g3w-panel'; +import Component from 'core/g3w-component'; + +const { resolve, inherit } = require('utils'); +const G3WObject = require('core/g3wobject'); + +/** + * Barstack Class - used to mount panels stack on top of each parent + * + * ORIGINAL SOURCE src/app/gui/utils/barstack.js@v3.9.3 + * + */ +export function BarStack() { + this._parent = null; + // barstack state. It store the panels array + this.state = { + contentsdata: [] + } +} + +inherit(BarStack, G3WObject); + +const proto = BarStack.prototype; + +// push componenet on top of parent +proto.push = function(content, options={}) { + // parent identify the DOM element where insert (append o meno) the component/panel + this._parent = options.parent; + // call barstack mount method + return this._mount(content, options); +}; + +// remove last component from stack +proto.pop = function() { + const d = $.Deferred(); + if (this.state.contentsdata.length) { + const content = this.state.contentsdata.slice(-1)[0].content; + this._unmount(content).then(() => { + const content = this.state.contentsdata.pop(); + d.resolve(content) + }) + } else d.resolve(); + return d.promise(); +}; + +// clear all stack +proto.clear = function() { + const d = $.Deferred(); + if (this.state.contentsdata.length) { + let unmountRequests = []; + this.state.contentsdata.forEach((data) => { + unmountRequests.push(this._unmount(data.content)); + }); + $.when(unmountRequests).then(() => { + this.state.contentsdata.splice(0, this.state.contentsdata.length); + d.resolve(); + }); + } + else d.resolve(); + return d.promise(); +}; + +proto.getContentData = function() { + return this.state.contentsdata +}; + +proto.getCurrentContentData = function() { + return this.state.contentsdata[this.state.contentsdata.length - 1]; +}; + +proto.getPreviousContentData = function() { + return this.state.contentsdata[this.state.contentsdata.length - 2]; +}; + +// mount component +proto._mount = function(content, options) { + // check the type of content: + // JQuery type + if (content instanceof jQuery) return this._setJqueryContent(content); + //String + else if (_.isString(content)) { + let jqueryEl = $(content); + if (!jqueryEl.length) jqueryEl = $('
'+content+'
'); + return this._setJqueryContent(jqueryEl); + } + // Vue + else if (content.mount && typeof content.mount == 'function') { + this._checkDuplicateVueContent(content); // if already exist it removed before based on id + return this._setVueContent(content,options) + } + // DOM + else return this._setDOMContent(content); +}; + +// JQuery append jQuery component +proto._setJqueryContent = function(content, options) { + $(this._parent).append(content); + this.state.contentsdata.push({ + content, + options + }); + return resolve(); +}; + +//Append DOM element +proto._setDOMContent = function(content, options) { + this._parent.appendChild(content); + this.state.contentsdata.push({ + content, + options + }); + return resolve(); +}; + +// Mount component to parent +proto._setVueContent = function(content, options={}) { + const d = $.Deferred(); + const append = options.append || false; + content.mount(this._parent, append) + .then(() => { + $(this._parent).localize(); + // Insert the content into the array with the following attributes: + // content: component object + // options: es. title, perc etc ... + this.state.contentsdata.push({ + content, + options + }); + d.resolve(content); + }); + return d.promise(); +}; + +// Check duplicate Vue Content +proto._checkDuplicateVueContent = function(content) { + let idxToRemove = null; + const id = content.getId(); + this.state.contentsdata.forEach((data, idx) => { + if (data.content.getId && (data.content.getId() == id)) idxToRemove = idx; + }); + if (!_.isNull(idxToRemove)) { + const data = this.state.contentsdata[idxToRemove]; + data.content.unmount() + .then(() => this.state.contentsdata.splice(idxToRemove,1)); + } +}; + +// unmount component +proto._unmount = function(content) { + const d = $.Deferred(); + if (content instanceof Component || content instanceof Panel) { + content.unmount() + .then(() => d.resolve()); + } + else { + $(this._parent).empty(); + d.resolve(); + } + return d.promise(); +}; + +proto.forEach = function(cbk) { + this.state.contentsdata.forEach(data => cbk(data.content)); +}; + +// Get lenght / numbero of element stored in stack +proto.getLength = function() { + return this.state.contentsdata.length; +}; \ No newline at end of file diff --git a/src/app/core/g3w-component.js b/src/app/core/g3w-component.js new file mode 100644 index 000000000..0b885f616 --- /dev/null +++ b/src/app/core/g3w-component.js @@ -0,0 +1,429 @@ +/** + * @file + * @since 3.10.0 + */ + +const { + base, + inherit, + merge, + noop, + capitalize_first_letter, + resolve +} = require('utils'); +const G3WObject = require('core/g3wobject'); + +/** @deprecated */ +const _cloneDeep = require('lodash.clonedeep'); + +/** + * Component class + * + * ORIGINAL SOURCE src/app/gui/component/component.js@v3.9.3 + * + * @param { Object} options + * @param { number } options.id + * @param { string } options.title + * @param { boolean } options.visible + * @param { boolean } options.open + * @param { boolean } options.resizable + * @param { null | unknown } options.info + * @param { boolean } options.loading + * @param { boolean } options.disabled + * @param { boolean } options.closewhenshowviewportcontent + * @param options.events + * @param options.internalComponent since 3.10.0 + * @param options.service since 3.10.0 + */ +const Component = function(options = {}) { + + const { + id = Math.random() * 1000, + title = '', + visible = true, + open = false, + resizable = false, + info = null, + loading = false, + disabled = false, + closewhenshowviewportcontent = true, + } = options; + + this._firstLayout = true; + + /** internal VUE component */ + this.internalComponent = options.internalComponent || null; + + /** @type { Array } */ + this._components = []; + + /** @type { string } */ + this.id = id; + + /** @type { string } */ + this.title = title; + + this.state = { + visible, + open, + resizable, + info, + loading, + disabled, + closewhenshowviewportcontent, + sizes: { + width:0, + height:0 + } + }; + + this.setters = { + + setOpen(bool) { + this.state.open = bool; + this._setOpen(bool); + }, + + setVisible(bool) { + this.state.visible = bool; + this._setVisible(bool); + }, + + setLoading(bool=false) { + this.state.loading = bool; + }, + + setDisabled(bool=false) { + this.state.disabled = bool; + }, + + reload() { + this._reload(); + }, + + }; + + if (options.service) { + this.setService(options.service); + } + + if (options.internalComponent && this._service) { + this.setInternalComponent(options.internalComponent); + } + + merge(this, options); + + base(this); + + // add events options + this.events = options.events; + + if (this.events) { + this.handleEventsComponent(); + } + +}; + +inherit(Component, G3WObject); + +const proto = Component.prototype; + +/** + * @param { Object } options + * @param { Array } options.components + * @param { Object } options.service + * @param { Function } options.service.init + * @param options.vueComponentObject + * @param options.template + * @param options.propsData + */ +proto.init = function(options = {}) { + this.vueComponent = this.createVueComponent(options.vueComponentObject); + this._components = options.components || []; + + this.setService(options.service || noop); + + if (this._service.init) { + this._service.init(options); + } + + if (options.template) { + this.setInternalComponentTemplate(options.template); + } + + this.setInternalComponent = function() { + this.internalComponent = new (Vue.extend(this.vueComponent))({ + service: this._service, + template: options.template, + propsData: options.propsData + }); + this.internalComponent.state = this.getService().state; + }; + + this.setInternalComponent(); + + return this; +}; + +proto.getId = function() { + return this.id; +}; + +proto.setId = function(id) { + this.id = id; +}; + +proto.getOpen = function() { + return this.state.open; +}; + +proto.closeWhenViewportContentIsOpen = function() { + return this.getOpen() && this.state.closewhenshowviewportcontent; +}; + +proto.getVisible = function() { + return this.state.visible; +}; + +proto.getTitle = function() { + return this.state.title; +}; + +proto.setTitle = function(title) { + this.state.title = title; +}; + +proto.getService = function() { + return this._service; +}; + +proto.setService = function(service) { + this._service = service; +}; + +proto.handleEventsComponent = function() { + if (this.events.open) { + const { when = "after", cb = () => {} } = this.events.open; + this[`on${when}`]('setOpen', bool => cb(bool)); + } +}; + +proto.insertComponentAt = function(index, Component) { + this._components.splice(index, 0, Component); +}; + +proto.removeCompomentAt = function(index) { + this._components.splice(index, 1); +}; + +proto.addComponent = function(Component) { + this._components.push(Component); +}; + +proto.popComponent = function() { + return this._components.pop(); +}; + +proto.removeComponent = function(Component) { + this._components.forEach((c, i) => { + if (c === Component) { + this.splice(i, 1); + return false; + } + }) +}; + +proto.setComponents = function(components) { + this._components = Array.isArray(components) ? components: []; +}; + +proto.exendComponents = function(components) { + _.merge(this._components, components); +}; + +proto.getInternalComponent = function() { + return this.internalComponent; +}; + +proto.setInternalComponent = function(internalComponent, options={}) { + this.internalComponent = !internalComponent && this.internalComponentClass ? new this.internalComponentClass : internalComponent; + (options.events || []).forEach(e => this.internalComponent.$on(e.name, data => e.handler && e.handler(data) || this[`set${capitalize_first_letter(e.name)}`](data))); + if (this._service) { + this.internalComponent.state = this._service.state; + } +}; + +proto.createVueComponent = function (vueObjOptions) { + return _cloneDeep(vueObjOptions); +}; + +proto.addInternalComponentData = function(data) { + _.merge(this.internalComponent, data) +}; + +proto.overwriteServiceMethod = function(methodName, method) { + this._service[methodName] = method; +}; + +proto.overwriteServiceMethods = function(methodsOptions) { + Object.entries(methodsOptions).forEach(([methodName, method]) => this.overwriteServiceMethod(methodName, method)) +}; + +proto.extendService = function(serviceOptions) { + if (this._service) { + merge(this._service, serviceOptions); + } +}; + +proto.extendInternalComponent = function(internalComponentOptions) { + if(!this.vueComponent) { + this.vueComponent = internalComponentOptions; + return; + } + Object + .entries(internalComponentOptions) + .forEach(([key, value]) => { + switch (key) { + case 'methods': this.extendInternalComponentMethods(value); break; + case 'components': this.extendInternalComponentComponents(value); break; + case 'computed': merge(this.vueComponent[key], value); break; + case 'data': merge(this.vueComponent[key], value); break; + } + }); +}; + +proto.extendInternalComponentComponents = function(components) { + if (components) { + merge(this.vueComponent.components, components); + } +}; + +proto.extendComponents = function(components) { + this.extendInternalComponentComponents(components); +}; + +proto.addComponent = function(component) { + if (component) { + this.vueComponent.components[component.key] = component.value; + } +}; + +/** @TODO check if unusued (invalid call to "forEach.forEach") */ +proto.extendInternalComponentMethods = function(methods) { + if (methods) { + Object.entries(methods).forEach.forEach(([key, value]) => (!(value instanceof Function)) && delete methods[key]); + merge(this.vueComponent.methods, methods); + } +}; + +proto.extendInternalComponentComputed = function(computed) { + if (computed) { + Object.entries(computed).forEach(([key, value]) => (!(value instanceof Function)) && delete computed[key]); + merge(this.vueComponent.computed, computed); + } +}; + +proto.setInternalComponentTemplate = function(template) { + if (template) { + this.vueComponent.template = template; + } +}; + +proto.getInternalTemplate = function() { + return this.vueComponent.template; +}; + +proto.destroy = function() {}; + +proto.click = function() {}; + +// hook function to show componet +proto.show = function() {}; + +proto._setOpen = function(bool) {}; + +proto._setVisible = function() {}; + +proto._reload = function() {}; + +/** + * @param { Element | 'string' } parent DOM element + * @param { boolean } append + * + * @returns jquery promise + * + * @fires internalComponent~ready + * @fires mount + */ +proto.mount = function(parent, append) { + const d = $.Deferred(); + + if (!this.internalComponent) { + this.setInternalComponent(); + } + + if (append) { + $(parent).append(this.internalComponent.$mount().$el); + } + + if (!append){ + this.internalComponent.$mount(parent); + } + + this.internalComponent.$nextTick(() => { + $(parent).localize(); + this.emit('ready'); + d.resolve(true); + }); + + // emit mount event + this.emit('mount'); + + return d.promise(); +}; + +/** + * @returns jquery promise + * + * @fires unmount + */ +proto.unmount = function() { + if (!this.internalComponent) { + return resolve(); + } + if (this.state.resizable) { + this.internalComponent.$off('resize-component', this.internalComponent.layout); + } + this.state.open = false; + this.internalComponent.$destroy(true); // destroy vue component + $(this.internalComponent.$el).remove(); // remove dom element + this.internalComponent = null; // set internal componet to null (for GC) + this.emit('unmount'); // emit unmount event + return resolve(); +}; + +/** + * @returns { Element } DOM element + */ +proto.ismount = function() { + return this.internalComponent && this.internalComponent.$el; +}; + +/** + * @param { number } width + * @param { number } height + * + * @listens internalComponent~resize-component + * @fires internalComponent~resize-component + * @fires layout + */ +proto.layout = function(width, height) { + if (this.state.resizable && this._firstLayout) { + this.internalComponent.$on('resize-component', this.internalComponent.layout); + this._firstLayout = false; + } + this.internalComponent.$nextTick(() => { this.internalComponent.$emit('resize-component', { width, height }); }); + this.emit('layout'); +}; + +export default Component; \ No newline at end of file diff --git a/src/app/core/g3w-panel.js b/src/app/core/g3w-panel.js new file mode 100644 index 000000000..6b5e1939a --- /dev/null +++ b/src/app/core/g3w-panel.js @@ -0,0 +1,79 @@ +/** + * @file + * @since 3.10.0 + */ + +import GUI from 'services/gui'; + +const { inherit, resolve } = require('utils'); +const G3WObject = require('core/g3wobject'); + +/** + * ORIGINAL SOURCE: src/app/gui/panel.js@v3.9.3 + */ +export default function Panel(options = {}) { + this.id = options.id || null; + this.title = options.title || ''; + this.internalPanel = options.panel || null; + this.service = options.service; +}; + +inherit(Panel, G3WObject); + +const proto = Panel.prototype; + +proto.getId = function(){ + return this.id; +}; + +proto.getTitle = function(){ + return this.title; +}; + +proto.getService = function(){ + return this.service; +}; + +proto.setService = function(service) { + this.service = service; +}; + +proto.getInternalPanel = function() { + return this.internalPanel; +}; + +proto.setInternalPanel = function(internalPanel) { + this.internalPanel = internalPanel; +}; + +proto.show = function() { + GUI.showPanel(this); +}; + +proto.close = function(){ + GUI.closePanel(); +}; + +proto.mount = function(parent) { + const panel = this.internalPanel; + const iCinstance = panel.$mount(); + $(parent).append(iCinstance.$el); + iCinstance.$nextTick(() => { + $(parent).localize(); + panel.onShow && panel.onShow(); + }); + return resolve(true); +}; + +proto.unmount = function() { + const panel = this.internalPanel; + const d = $.Deferred(); + panel.$destroy(true); + $(panel.$el).remove(); + panel.onClose && panel.onClose(); + this.internalComponent = null; + d.resolve(); + return d.promise(); +}; + +proto.onResize = function(parentWidth,parentHeight){}; \ No newline at end of file diff --git a/src/app/g3w-ol/controls/streetviewcontrol.js b/src/app/g3w-ol/controls/streetviewcontrol.js index 731d54376..4ac027999 100644 --- a/src/app/g3w-ol/controls/streetviewcontrol.js +++ b/src/app/g3w-ol/controls/streetviewcontrol.js @@ -1,9 +1,10 @@ -import ApplicationState from 'store/application-state'; -import GUI from 'services/gui'; -import { mergeOptions } from 'utils/mergeOptions'; +import ApplicationState from 'store/application-state'; +import GUI from 'services/gui'; +import { mergeOptions } from 'utils/mergeOptions'; +import * as vueComp from 'components/StreetView.vue'; +import Component from 'core/g3w-component'; const { XHR } = require('utils'); -const StreetViewComponent = require('gui/streetview/vue/streetview'); const InteractionControl = require('g3w-ol/controls/interactioncontrol'); const PickCoordinatesInteraction = require('g3w-ol/interactions/pickcoordinatesinteraction'); @@ -147,14 +148,12 @@ proto.showStreetView = function(coordinate){ const [lng, lat] = ol.proj.transform(coordinate, this._map.getView().getProjection().getCode(), 'EPSG:4326'); if (this.key) { GUI.setContent({ - content: new StreetViewComponent({ - keyError: this.keyError - }), - title: 'StreetView' + title: 'StreetView', + content: new Component({ internalComponent: (Vue.extend(vueComp))({ keyError: this.keyError }) }), }); - !this.keyError && this.setPosition({ - lng, lat - }) + if (!this.keyError) { + this.setPosition({ lng, lat }); + } } else { this._streetViewFeature.setGeometry( new ol.geom.Point(coordinate) diff --git a/src/app/gui/catalog/vue/catalog.js b/src/app/gui/catalog/vue/catalog.js index 3b4177b01..d27c5ab10 100755 --- a/src/app/gui/catalog/vue/catalog.js +++ b/src/app/gui/catalog/vue/catalog.js @@ -1,54 +1,7 @@ -import * as catalogComponentOptions from 'components/Catalog.vue'; -import * as catalogLayersGroupComponentOptions from 'components/CatalogLayersGroup.vue'; -import * as catalogTristateTreeComponentOptions from 'components/CatalogTristateTree.vue'; -import * as catalogLayersLegendComponentOptions from 'components/CatalogLayersLegend.vue'; -import * as catalogLayersLegendItemsComponentOptions from 'components/CatalogLayersLegendItems.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -import ComponentsRegistry from 'store/components'; -import GUI from 'services/gui'; - - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); -const Service = require('gui/catalog/catalogservice'); - -const InternalComponent = Vue.extend(catalogComponentOptions); - -Vue.component('g3w-catalog', catalogComponentOptions); -Vue.component('layers-group', catalogLayersGroupComponentOptions); -Vue.component('tristate-tree', catalogTristateTreeComponentOptions); -Vue.component('layerslegend', catalogLayersLegendComponentOptions); -Vue.component('layerslegend-items', catalogLayersLegendItemsComponentOptions); - -function CatalogComponent(options={}) { - options.resizable = true; - base(this, options); - const {legend} = options.config; - this.title = "catalog"; - this.mapComponentId = options.mapcomponentid; - const service = options.service || new Service; - this.setService(service); - this.setInternalComponent(new InternalComponent({ - service, - legend - })); - this.internalComponent.state = this.getService().state; - let listenToMapVisibility = map => { - const mapService = map.getService(); - this.state.visible = !mapService.state.hidden; - mapService.onafter('setHidden', hidden => { - this.state.visible = !mapService.state.hidden; - this.state.expanded = true; - }) - }; - if (this.mapComponentId) { - const map = GUI.getComponent(this.mapComponentId); - !map && ComponentsRegistry.on('componentregistered', component => - (component.getId() === this.mapComponentId) && listenToMapVisibility(component)) - || listenToMapVisibility(map); - } -} - -inherit(CatalogComponent, Component); - -module.exports = CatalogComponent; +import vueComp from 'components/g3w-catalog'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/changemapmenu/changemapmenu.js b/src/app/gui/changemapmenu/changemapmenu.js index f17e3ebb8..4ba1b2e19 100644 --- a/src/app/gui/changemapmenu/changemapmenu.js +++ b/src/app/gui/changemapmenu/changemapmenu.js @@ -1,23 +1,7 @@ /** * @file - * @since 3.8.0 + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 */ -import * as vueComponentOptions from 'components/ChangeMapMenu.vue'; - -const { base, inherit, merge } = require('utils'); -const Component = require('gui/component/component'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -function ChangeMapMenuComponent(options={}) { - options.id = 'changemapmenu'; - base(this, options); - this.state.visible = true; - merge(this, options); - this.internalComponent = new InternalComponent(); -} -inherit(ChangeMapMenuComponent, Component); - -module.exports = ChangeMapMenuComponent; - +import vueComp from 'components/g3w-changemapmenu'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/component/component.js b/src/app/gui/component/component.js index c2f6ab21b..d4561527e 100644 --- a/src/app/gui/component/component.js +++ b/src/app/gui/component/component.js @@ -1,409 +1,7 @@ -const { - base, - inherit, - merge, - noop, - capitalize_first_letter, - resolve -} = require('utils'); -const G3WObject = require('core/g3wobject'); - -/** @deprecated */ -const _cloneDeep = require('lodash.clonedeep'); - -/** - * Component class - * - * @param { Object} options - * @param { number } options.id - * @param { string } options.title - * @param { boolean } options.visible - * @param { boolean } options.open - * @param { boolean } options.resizable - * @param { null | unknown } options.info - * @param { boolean } options.loading - * @param { boolean } options.disabled - * @param { boolean } options.closewhenshowviewportcontent - * @param options.events - */ -const Component = function(options = {}) { - - const { - id = Math.random() * 1000, - title = '', - visible = true, - open = false, - resizable = false, - info = null, - loading = false, - disabled = false, - closewhenshowviewportcontent = true, - } = options; - - this._firstLayout = true; - - /** internal VUE component */ - this.internalComponent = null; - - /** @type { Array } */ - this._components = []; - - /** @type { string } */ - this.id = id; - - /** @type { string } */ - this.title = title; - - this.state = { - visible, - open, - resizable, - info, - loading, - disabled, - closewhenshowviewportcontent, - sizes: { - width:0, - height:0 - } - }; - - this.setters = { - - setOpen(bool) { - this.state.open = bool; - this._setOpen(bool); - }, - - setVisible(bool) { - this.state.visible = bool; - this._setVisible(bool); - }, - - setLoading(bool=false) { - this.state.loading = bool; - }, - - setDisabled(bool=false) { - this.state.disabled = bool; - }, - - reload() { - this._reload(); - }, - - }; - - merge(this, options); - - base(this); - - // add events options - this.events = options.events; - - if (this.events) { - this.handleEventsComponent(); - } - -}; - -inherit(Component, G3WObject); - -const proto = Component.prototype; - -/** - * @param { Object } options - * @param { Array } options.components - * @param { Object } options.service - * @param { Function } options.service.init - * @param options.vueComponentObject - * @param options.template - * @param options.propsData - */ -proto.init = function(options = {}) { - this.vueComponent = this.createVueComponent(options.vueComponentObject); - this._components = options.components || []; - - this.setService(options.service || noop); - - if (this._service.init) { - this._service.init(options); - } - - if (options.template) { - this.setInternalComponentTemplate(options.template); - } - - this.setInternalComponent = function() { - this.internalComponent = new (Vue.extend(this.vueComponent))({ - service: this._service, - template: options.template, - propsData: options.propsData - }); - this.internalComponent.state = this.getService().state; - }; - - this.setInternalComponent(); - - return this; -}; - -proto.getId = function() { - return this.id; -}; - -proto.setId = function(id) { - this.id = id; -}; - -proto.getOpen = function() { - return this.state.open; -}; - -proto.closeWhenViewportContentIsOpen = function() { - return this.getOpen() && this.state.closewhenshowviewportcontent; -}; - -proto.getVisible = function() { - return this.state.visible; -}; - -proto.getTitle = function() { - return this.state.title; -}; - -proto.setTitle = function(title) { - this.state.title = title; -}; - -proto.getService = function() { - return this._service; -}; - -proto.setService = function(service) { - this._service = service; -}; - -proto.handleEventsComponent = function() { - if (this.events.open) { - const { when = "after", cb = () => {} } = this.events.open; - this[`on${when}`]('setOpen', bool => cb(bool)); - } -}; - -proto.insertComponentAt = function(index, Component) { - this._components.splice(index, 0, Component); -}; - -proto.removeCompomentAt = function(index) { - this._components.splice(index, 1); -}; - -proto.addComponent = function(Component) { - this._components.push(Component); -}; - -proto.popComponent = function() { - return this._components.pop(); -}; - -proto.removeComponent = function(Component) { - this._components.forEach((c, i) => { - if (c === Component) { - this.splice(i, 1); - return false; - } - }) -}; - -proto.setComponents = function(components) { - this._components = Array.isArray(components) ? components: []; -}; - -proto.exendComponents = function(components) { - _.merge(this._components, components); -}; - -proto.getInternalComponent = function() { - return this.internalComponent; -}; - -proto.setInternalComponent = function(internalComponent, options={}) { - this.internalComponent = !internalComponent && this.internalComponentClass ? new this.internalComponentClass : internalComponent; - (options.events || []).forEach(e => this.internalComponent.$on(e.name, data => e.handler && e.handler(data) || this[`set${capitalize_first_letter(e.name)}`](data))) -}; - -proto.createVueComponent = function (vueObjOptions) { - return _cloneDeep(vueObjOptions); -}; - -proto.addInternalComponentData = function(data) { - _.merge(this.internalComponent, data) -}; - -proto.overwriteServiceMethod = function(methodName, method) { - this._service[methodName] = method; -}; - -proto.overwriteServiceMethods = function(methodsOptions) { - Object.entries(methodsOptions).forEach(([methodName, method]) => this.overwriteServiceMethod(methodName, method)) -}; - -proto.extendService = function(serviceOptions) { - if (this._service) { - merge(this._service, serviceOptions); - } -}; - -proto.extendInternalComponent = function(internalComponentOptions) { - if(!this.vueComponent) { - this.vueComponent = internalComponentOptions; - return; - } - Object - .entries(internalComponentOptions) - .forEach(([key, value]) => { - switch (key) { - case 'methods': this.extendInternalComponentMethods(value); break; - case 'components': this.extendInternalComponentComponents(value); break; - case 'computed': merge(this.vueComponent[key], value); break; - case 'data': merge(this.vueComponent[key], value); break; - } - }); -}; - -proto.extendInternalComponentComponents = function(components) { - if (components) { - merge(this.vueComponent.components, components); - } -}; - -proto.extendComponents = function(components) { - this.extendInternalComponentComponents(components); -}; - -proto.addComponent = function(component) { - if (component) { - this.vueComponent.components[component.key] = component.value; - } -}; - -/** @TODO check if unusued (invalid call to "forEach.forEach") */ -proto.extendInternalComponentMethods = function(methods) { - if (methods) { - Object.entries(methods).forEach.forEach(([key, value]) => (!(value instanceof Function)) && delete methods[key]); - merge(this.vueComponent.methods, methods); - } -}; - -proto.extendInternalComponentComputed = function(computed) { - if (computed) { - Object.entries(computed).forEach(([key, value]) => (!(value instanceof Function)) && delete computed[key]); - merge(this.vueComponent.computed, computed); - } -}; - -proto.setInternalComponentTemplate = function(template) { - if (template) { - this.vueComponent.template = template; - } -}; - -proto.getInternalTemplate = function() { - return this.vueComponent.template; -}; - -proto.destroy = function() {}; - -proto.click = function() {}; - -// hook function to show componet -proto.show = function() {}; - -proto._setOpen = function(bool) {}; - -proto._setVisible = function() {}; - -proto._reload = function() {}; - -/** - * @param { Element | 'string' } parent DOM element - * @param { boolean } append - * - * @returns jquery promise - * - * @fires internalComponent~ready - * @fires mount - */ -proto.mount = function(parent, append) { - const d = $.Deferred(); - - if (!this.internalComponent) { - this.setInternalComponent(); - } - - if (append) { - $(parent).append(this.internalComponent.$mount().$el); - } - - if (!append){ - this.internalComponent.$mount(parent); - } - - this.internalComponent.$nextTick(() => { - $(parent).localize(); - this.emit('ready'); - d.resolve(true); - }); - - // emit mount event - this.emit('mount'); - - return d.promise(); -}; - -/** - * @returns jquery promise - * - * @fires unmount - */ -proto.unmount = function() { - if (!this.internalComponent) { - return resolve(); - } - if (this.state.resizable) { - this.internalComponent.$off('resize-component', this.internalComponent.layout); - } - this.state.open = false; - this.internalComponent.$destroy(true); // destroy vue component - $(this.internalComponent.$el).remove(); // remove dom element - this.internalComponent = null; // set internal componet to null (for GC) - this.emit('unmount'); // emit unmount event - return resolve(); -}; - -/** - * @returns { Element } DOM element - */ -proto.ismount = function() { - return this.internalComponent && this.internalComponent.$el; -}; - /** - * @param { number } width - * @param { number } height - * - * @listens internalComponent~resize-component - * @fires internalComponent~resize-component - * @fires layout + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 */ -proto.layout = function(width, height) { - if (this.state.resizable && this._firstLayout) { - this.internalComponent.$on('resize-component', this.internalComponent.layout); - this._firstLayout = false; - } - this.internalComponent.$nextTick(() => { this.internalComponent.$emit('resize-component', { width, height }); }); - this.emit('layout'); -}; -module.exports = Component; \ No newline at end of file +import vueComp from 'core/g3w-component'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/form/vue/form.js b/src/app/gui/form/vue/form.js index 863ed5257..aac480630 100644 --- a/src/app/gui/form/vue/form.js +++ b/src/app/gui/form/vue/form.js @@ -1,90 +1,7 @@ -import * as vueComponentOptions from 'components/Form.vue'; -import BodyFormComponent from 'components/FormBody.vue'; -import GUI from 'services/gui'; - -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); -const Service = require('gui/form/formservice'); - -function FormComponent(options = {}) { - const {id='form', name, title, headerComponent} = options; - base(this, options); - options.service = options.service ? new options.service : new Service; - options.vueComponentObject = options.vueComponentObject || vueComponentOptions; - //set element of the form - const components = options.components || [ - { - id, - title, - name, - root: true, - component: BodyFormComponent, - headerComponent - } - ]; - options.perc = options.layer.getFormPercentage() !== null ? options.layer.getFormPercentage() : options.perc; - // initialize component - this.init(options); - this.getService().addComponents(components); - this.getService().setComponent(components[0].component); - /** - * Used to add component to form body - * @param component - */ - this.addBodyFormComponent = function({component, where='after'}={}){ - this.getInternalComponent().body.components[where].push(component); - }; - - this.addBodyFormComponents = function({components=[], where="after"}={}){ - components.forEach(component => this.addBodyFormComponent({ - component, - where - })) - }; - - this.addFormComponents = function(components = []) { - this.getService().addComponents(components); - }; - - this.addFormComponent = function(component) { - component && this.getService().addComponent(component) - }; - // some utilities methods - this.addDependecyComponents = function(components) { - this.getService().addDependecyComponents(components) - }; - this.addComponentBeforeBody = function(Component) { - //this.getService().addedComponentTo('body'); - //this.insertComponentAt(1, Component); - }; - - this.addComponentAfterBody = function(Component) { - //this.getService().addedComponentTo('body'); - //this.insertComponentAt(2, Component) - }; - - this.addComponentBeforeFooter = function() { - //TODO - }; - - this.addComponentAfterFooter = function(Component) { - //TODO - }; - // overwrite father mount method. - this.mount = function(parent, append) { - return base(this, 'mount', parent, append) - .then(() => { - // set modal window to true - GUI.setModal(true); - }); - }; - - this.layout = function() { - this.internalComponent.reloadLayout(); - }; -} - -inherit(FormComponent, Component); - -module.exports = FormComponent; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-form'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/map/vue/map.js b/src/app/gui/map/vue/map.js index 51219164e..25afd4001 100755 --- a/src/app/gui/map/vue/map.js +++ b/src/app/gui/map/vue/map.js @@ -1,45 +1,7 @@ -import * as vueComponentOptions from 'components/Map.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { base, merge, inherit } = require('utils'); -const Component = require('gui/component/component'); -const { MapService } = require('gui/map/mapservice'); - -// interanl registration -const InternalComponent = Vue.extend(vueComponentOptions); - -Vue.component('g3w-map', vueComponentOptions); - -function MapComponent(options = {}) { - base(this, options); - this.id = "map-component"; - this.title = "Map Component"; - const target = options.target || "map"; - const maps_container = options.maps_container || "g3w-maps"; - options.target = target; - options.maps_container = maps_container; - const service = new MapService(options); - this.setService(service); - merge(this, options); - this.internalComponent = new InternalComponent({ - service, - target, - maps_container - }); - /** - * add Vue get cookie method - * - */ - service.getCookie = this.internalComponent.$cookie.get; -} - -inherit(MapComponent, Component); - -const proto = MapComponent.prototype; - -proto.layout = function(width, height) { - $(`#${this.target}`).height(height); - $(`#${this.target}`).width(width); - this._service.layout({width, height}); -}; - -module.exports = MapComponent; \ No newline at end of file +import vueComp from 'components/g3w-map'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/metadata/metadataservice.js b/src/app/gui/metadata/metadataservice.js deleted file mode 100644 index ecfbe7941..000000000 --- a/src/app/gui/metadata/metadataservice.js +++ /dev/null @@ -1,99 +0,0 @@ -import ProjectsRegistry from 'store/projects'; -import GUI from 'services/gui'; - -const { inherit } = require('utils'); -const G3WObject = require('core/g3wobject'); -const ProjectMetadataComponent = require('gui/metadata/vue/components/project/project'); - -const METADATAGROUPS = { - general: [ - 'title', - 'name', - 'description', - 'abstract', - 'keywords', - 'fees', - 'accessconstraints', - 'contactinformation', - 'wms_url' - ], - spatial: [ - 'crs', - 'extent' - ], - layers: [ - 'layers' - ] -}; - -function MetadataService() { - this.content = null; - this.show = false; - this.state = { - name: '', - groups: {} - }; - this._buildProjectGroupMetadata(); -} - -inherit(MetadataService, G3WObject); - -const proto = MetadataService.prototype; - -proto._buildProjectGroupMetadata = function() { - const project = ProjectsRegistry.getCurrentProject().getState(); - this.state.name = project.title; - const groups = {}; - Object.entries(METADATAGROUPS).forEach(([groupName, value]) => { - groups[groupName] = {}; - value.forEach(field => { - const fieldValue = project.metadata && project.metadata[field] ? project.metadata[field] : project[field]; - if (!!fieldValue) { - groups[groupName][field] = { - label: ['sdk','metadata','groups', groupName, 'fields', field].join('.'), - value: fieldValue - } - } - }) - }); - this.state.groups = groups; -}; - -proto.getProjectMetadata = function() { - return this.state; -}; - -proto.getLayersMetadata = function() { - return this.state.groups.layers; -}; - -proto.getLayerMetadata = function(id) { - const layerMetadata = this.state.groups.layers.filter(layer => layer.id === id); - return layerMetadata[0]; -}; - -proto.showMetadata = function(bool) { - this.show = bool; - if (this.show) { - this.content = new ProjectMetadataComponent({ - state: this.getProjectMetadata(), - service: this - }); - GUI.setContent({ - content: this.content, - title: "sdk.metadata.title", - perc: 100 - }); - this.show = true; - } else GUI.closeContent() - -}; - -proto.reload = function() { - this.emit('reload'); - this._buildProjectGroupMetadata(); -}; - - - -module.exports = MetadataService; diff --git a/src/app/gui/metadata/vue/components/layer/layer.js b/src/app/gui/metadata/vue/components/layer/layer.js deleted file mode 100644 index b5f8d2011..000000000 --- a/src/app/gui/metadata/vue/components/layer/layer.js +++ /dev/null @@ -1,18 +0,0 @@ -import Layer from 'components/MetadataLayer.vue'; - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); - -function LayerComponent({state = {}, service} = {}) { - base(this); - const vueComponent = Vue.extend(Layer); - this.setService(service); - this.internalComponent = new vueComponent({ - state - }); - this.layout = function() {}; -} - -inherit(LayerComponent, Component); - -module.exports = LayerComponent; diff --git a/src/app/gui/metadata/vue/components/project/project.js b/src/app/gui/metadata/vue/components/project/project.js deleted file mode 100644 index 9c46ddc3b..000000000 --- a/src/app/gui/metadata/vue/components/project/project.js +++ /dev/null @@ -1,18 +0,0 @@ -import ProjectCatalog from 'components/MetadataProject.vue' - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); - -function ProjectMetadataComponent({state = {}, service} = {}) { - base(this); - const vueComponent = Vue.extend(ProjectCatalog); - this.setService(service); - this.internalComponent = new vueComponent({ - state - }); - this.layout = function() {}; -} - -inherit(ProjectMetadataComponent, Component); - -module.exports = ProjectMetadataComponent; diff --git a/src/app/gui/metadata/vue/metadata.js b/src/app/gui/metadata/vue/metadata.js index 882e74b37..48a434c32 100644 --- a/src/app/gui/metadata/vue/metadata.js +++ b/src/app/gui/metadata/vue/metadata.js @@ -1,38 +1,7 @@ -import * as vueComponentOptions from 'components/Metadata.vue'; - -import GUI from 'services/gui'; - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); -const MetadataService = require('gui/metadata/metadataservice'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -const MetadataComponent = function(options = {}) { - base(this, options); - this.title = "sdk.metadata.title"; - const service = options.service || new MetadataService(options); - this.setService(service); - this._service.on('reload', () => { - this.setOpen(false); - }); - this.setInternalComponent = function () { - this.internalComponent = new InternalComponent({ - service: service - }); - this.internalComponent.state = service.state; - return this.internalComponent; - }; - this._setOpen = function(bool) { - this._service.showMetadata(bool); - }; - GUI.on('closecontent', ()=>{ - this.state.open = false; - }) -}; - -inherit(MetadataComponent, Component); - -module.exports = MetadataComponent; - +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-metadata'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/panel.js b/src/app/gui/panel.js index e9bf731ab..1df57eae0 100644 --- a/src/app/gui/panel.js +++ b/src/app/gui/panel.js @@ -1,74 +1,7 @@ -import GUI from 'services/gui'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { inherit, resolve: resolvedValue } = require('utils'); -const G3WObject = require('core/g3wobject'); - -const Panel = function(options={}) { - this.id = options.id || null; - this.title = options.title || ''; - this.internalPanel = options.panel || null; - this.service = options.service; -}; - -inherit(Panel, G3WObject); - -const proto = Panel.prototype; - -proto.getId = function(){ - return this.id; -}; - -proto.getTitle = function(){ - return this.title; -}; - -proto.getService = function(){ - return this.service; -}; - -proto.setService = function(service) { - this.service = service; -}; - -proto.getInternalPanel = function() { - return this.internalPanel; -}; - -proto.setInternalPanel = function(internalPanel) { - this.internalPanel = internalPanel; -}; - -proto.show = function() { - GUI.showPanel(this); -}; - -proto.close = function(){ - GUI.closePanel(); -}; - -proto.mount = function(parent) { - const panel = this.internalPanel; - const iCinstance = panel.$mount(); - $(parent).append(iCinstance.$el); - iCinstance.$nextTick(() => { - $(parent).localize(); - panel.onShow && panel.onShow(); - }); - return resolvedValue(true); -}; - -proto.unmount = function() { - const panel = this.internalPanel; - const d = $.Deferred(); - panel.$destroy(true); - $(panel.$el).remove(); - panel.onClose && panel.onClose(); - this.internalComponent = null; - d.resolve(); - return d.promise(); -}; - -proto.onResize = function(parentWidth,parentHeight){}; - - -module.exports = Panel; +import vueComp from 'core/g3w-panel'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/print/printservice.js b/src/app/gui/print/printservice.js index 65c2d58f3..5a03f0589 100644 --- a/src/app/gui/print/printservice.js +++ b/src/app/gui/print/printservice.js @@ -10,7 +10,7 @@ import GUI from 'services/gui'; import { getScaleFromResolution } from 'utils/getScaleFromResolution'; import { getResolutionFromScale } from 'utils/getResolutionFromScale'; import { getMetersFromDegrees } from 'utils/getMetersFromDegrees'; - +import * as vueComp from 'components/PrintPage.vue'; const { base, @@ -20,7 +20,7 @@ const { } = require('utils'); const { t } = require('core/i18n/i18n.service'); const G3WObject = require('core/g3wobject'); -const PrintPage = require('gui/print/vue/printpage'); +const Component = require('gui/component/component'); /* http://localhost/fcgi-bin/qgis_mapserver/qgis_mapserv.fcgi @@ -402,9 +402,13 @@ proto.print = function() { } else { this.state.output.url = null; this.state.output.layers = true; - this._page = new PrintPage({ - service: this + + this._page = new Component({ + service: this, + internalComponent: new (Vue.extend(vueComp))({ service: this }), }); + this._page.internalComponent.state = this.state.output; + this._page.unmount = () => { this._page.getService().setPrintAreaAfterCloseContent(); return Component.prototype.unmount.call(this._page); }; GUI.setContent({ content: this._page, diff --git a/src/app/gui/print/vue/print.js b/src/app/gui/print/vue/print.js index 4983f0f2d..8bff1039a 100644 --- a/src/app/gui/print/vue/print.js +++ b/src/app/gui/print/vue/print.js @@ -1,41 +1,7 @@ -import * as vueComponentOptions from 'components/Print.vue'; - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); -const { PrintComponentService } = require('gui/print/printservice'); - -function PrintComponent(options={}) { - base(this, options); - this.title = "print"; - this.vueComponent = vueComponentOptions; - this.internalComponent = null; - const service = options.service || new PrintComponentService; - this.setService(service); - // init service - this._service.init(); - this.setInternalComponent = function() { - const InternalComponent = Vue.extend(this.vueComponent); - this.internalComponent = new InternalComponent({ - service - }); - this.state.visible = service.state.visible; - this.internalComponent.state = service.state; - return this.internalComponent; - }; - - this._reload = function() { - const service = this.getService(); - service.reload(); - this.state.visible = service.state.visible; - }; - - this._setOpen = function(bool) { - this._service.showPrintArea(bool); - }; -} - -inherit(PrintComponent, Component); - -module.exports = PrintComponent; - +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-print'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/print/vue/printpage.js b/src/app/gui/print/vue/printpage.js deleted file mode 100644 index 9be7a1144..000000000 --- a/src/app/gui/print/vue/printpage.js +++ /dev/null @@ -1,29 +0,0 @@ -import * as vueComponentOptions from 'components/PrintPage.vue'; - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -const PrintPage = function(options={}) { - base(this); - const service = options.service; - this.setService(service); - const internalComponent = new InternalComponent({ - service - }); - this.setInternalComponent(internalComponent); - this.internalComponent.state = service.state.output; - - this.unmount = function() { - this.getService().setPrintAreaAfterCloseContent(); - return base(this, 'unmount') - } -}; - -inherit(PrintPage, Component); - - -module.exports = PrintPage; - - diff --git a/src/app/gui/projectsmenu/menu.js b/src/app/gui/projectsmenu/menu.js deleted file mode 100644 index 1190f7537..000000000 --- a/src/app/gui/projectsmenu/menu.js +++ /dev/null @@ -1,28 +0,0 @@ -import * as vueComponentOptions from 'components/ProjectsMenu.vue'; - -const { base, inherit, merge } = require('utils'); -const Component = require('gui/component/component'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -function MenuComponent(options={}){ - base(this,options); - this.title = options.title || "menu"; - this.state.visible = true; - this.state.menuitems = options.menuitems; - const host = options.host; - merge(this, options); - this.internalComponent = new InternalComponent({ - service: this, - host - }); - this.internalComponent.state = this.state; -} -inherit(MenuComponent, Component); - -const proto = MenuComponent.prototype; - -proto.trigger = function(item) {}; - -module.exports = MenuComponent; - diff --git a/src/app/gui/projectsmenu/projectsmenu.js b/src/app/gui/projectsmenu/projectsmenu.js index c96d8fb3a..f55d8715c 100644 --- a/src/app/gui/projectsmenu/projectsmenu.js +++ b/src/app/gui/projectsmenu/projectsmenu.js @@ -1,32 +1,7 @@ -import ProjectsRegistry from 'store/projects'; -import ApplicationService from 'services/application'; - -const { base, inherit } = require('utils'); -const MenuComponent = require('gui/projectsmenu/menu'); - -function ProjectsMenuComponent(options={}) { - options.id = 'projectsmenu'; - base(this, options); - this.state.menuitems = []; - const host = options.host; - const projects = options.projects || ProjectsRegistry.getListableProjects(); - this.state.menuitems = projects.map(project => ({ - title: project.title, - description: project.description, - thumbnail: project.thumbnail, - gid: project.gid, - cbk: options.cbk || function(options={}) { - const {gid} = options; - return ApplicationService.changeProject({ - gid, - host - }) - } - })); -} - -inherit(ProjectsMenuComponent, MenuComponent); - -module.exports = ProjectsMenuComponent; - +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-projectsmenu'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/querybuilder/querybuilderuifactory.js b/src/app/gui/querybuilder/querybuilderuifactory.js index 08ed75380..b225f3fd5 100644 --- a/src/app/gui/querybuilder/querybuilderuifactory.js +++ b/src/app/gui/querybuilder/querybuilderuifactory.js @@ -1,31 +1,7 @@ -const QueryBuilder = require('gui/querybuilder/vue/querybuilder'); -const QueryBuilderPanel = require('gui/querybuilder/vue/panel/querybuilderpanel'); - -const QuerybuilderUIfactory = { - type: null, - show({type='sidebar', options}={}){ - let QueryBuilderInstance; - this.type = this.type === null ? type : this.type; - if (this.type==='modal') { - QueryBuilderInstance = new QueryBuilder({ - options - }); - const queryBuilderDom = QueryBuilderInstance.$mount().$el; - GUI.showModalDialog({ - title: 'Query Builder', - message: queryBuilderDom, - className: "modal-background-dark " - }) - } else { - const panel = new QueryBuilderPanel({ - options - }); - QueryBuilderInstance = panel.getInternalPanel(); - panel.show(); - } - return QueryBuilderInstance; - } -}; - -module.exports = QuerybuilderUIfactory; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-querybuilder'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/querybuilder/vue/panel/querybuilderpanel.js b/src/app/gui/querybuilder/vue/panel/querybuilderpanel.js deleted file mode 100644 index b164c8eb7..000000000 --- a/src/app/gui/querybuilder/vue/panel/querybuilderpanel.js +++ /dev/null @@ -1,14 +0,0 @@ -const { inherit, base } = require('utils'); -const Panel = require('gui/panel'); -const QueryBuilder = require('gui/querybuilder/vue/querybuilder'); - -function QueryBuilderPanel(options={}) { - options.title = 'Query Builder'; - base(this, options); - const internalPanel = new QueryBuilder(options); - this.setInternalPanel(internalPanel); -} - -inherit(QueryBuilderPanel, Panel); - -module.exports = QueryBuilderPanel; diff --git a/src/app/gui/querybuilder/vue/querybuilder.js b/src/app/gui/querybuilder/vue/querybuilder.js deleted file mode 100644 index d7d1e0cb4..000000000 --- a/src/app/gui/querybuilder/vue/querybuilder.js +++ /dev/null @@ -1,5 +0,0 @@ -import * as vueComponentOptions from 'components/QueryBuilder.vue'; - -const QueryBuilder = Vue.extend(vueComponentOptions); - -module.exports = QueryBuilder; diff --git a/src/app/gui/queryresults/vue/queryresults.js b/src/app/gui/queryresults/vue/queryresults.js index a246181d7..379f81920 100644 --- a/src/app/gui/queryresults/vue/queryresults.js +++ b/src/app/gui/queryresults/vue/queryresults.js @@ -1,39 +1,7 @@ -import * as vueComponentOptions from 'components/QueryResults.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); -const QueryResultsService = require('gui/queryresults/queryresultsservice'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -function QueryResultsComponent(options={}) { - base(this, options); - this.id = "queryresults"; - this.title = "Query Results"; - this._service = new QueryResultsService(); - this.setInternalComponent = function() { - this.internalComponent = new InternalComponent({ - queryResultsService: this._service - }); - this.internalComponent.querytitle = this._service.state.querytitle; - }; - - this.getElement = function() { - if (this.internalComponent) return this.internalComponent.$el; - }; - - this._service.onafter('setLayersData', async () => { - !this.internalComponent && this.setInternalComponent(); - await this.internalComponent.$nextTick(); - }); - - this.layout = function(width,height) {}; - this.unmount = function() { - this.getService().closeComponent(); - return base(this, 'unmount') - } -} - -inherit(QueryResultsComponent, Component); - -module.exports = QueryResultsComponent; +import vueComp from 'components/g3w-queryresults'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/relations/relationsservice.js b/src/app/gui/relations/relationsservice.js deleted file mode 100644 index d62586e0c..000000000 --- a/src/app/gui/relations/relationsservice.js +++ /dev/null @@ -1,77 +0,0 @@ -import { G3W_FID } from 'app/constant'; -import RelationsService from 'services/relations'; -import ApplicationService from 'services/application'; -import GUI from 'services/gui'; - -const { inherit, base } = require('utils'); -const G3WObject = require('core/g3wobject'); - -function RelationsComponentService(options={}) { - this.state = {}; - this._options = {}; - base(this); -} - -inherit(RelationsComponentService, G3WObject); - -const proto = RelationsComponentService.prototype; - -proto.getRelations = function(options={}) { - this._options = options; - return RelationsService.getRelations(options); -}; - -proto.getRelationsNM = async function({nmRelation, features}){ - return await RelationsService.getRelationsNM({ - nmRelation, - features - }) -}; - -proto.saveRelations = async function(type){ - this._options.type = type; - const caller_download_id = ApplicationService.setDownload(true); - try { - await RelationsService.save(this._options) - } catch(err){ - GUI.showUserMessage({ - type: 'alert', - message: err || "info.server_error", - closable: true - }) - } - ApplicationService.setDownload(false, caller_download_id); -}; - -proto.buildRelationTable = function(relations=[], id) { - const layer = ApplicationService.getCurrentProject().getLayerById(id); - const headers = layer.getTableHeaders(); - let columns = null; - let rows = []; - let rows_fid = []; - let fields; - if (relations.length) { - const attributes = Object.keys(relations[0].attributes); - columns = headers.filter(header => attributes.indexOf(header.name) !==-1); - rows = relations.map(relation => { - rows_fid.push(relation.attributes[G3W_FID]); - return columns.map(column => { - return relation.attributes[column.name] - }) - }); - fields = columns; - columns = columns.map(column => column.label); - } - return { - columns, - rows, - rows_fid, - features: relations, - fields, - formStructure : layer.getLayerEditingFormStructure(), - rowFormStructure: null, - layerId: layer.getId() - } -}; - -module.exports = RelationsComponentService; diff --git a/src/app/gui/relations/vue/relationspage.js b/src/app/gui/relations/vue/relationspage.js index 30dc94a73..3aa54cb4e 100644 --- a/src/app/gui/relations/vue/relationspage.js +++ b/src/app/gui/relations/vue/relationspage.js @@ -1,37 +1,7 @@ -import * as vueComponentOptions from 'components/RelationsPage.vue'; - -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); -const Service = require('gui/relations/relationsservice'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -const RelationsPage = function(options={}) { - base(this, options); - const service = options.service || new Service(); - const {layer, relation=null, relations=[], feature=null, table=null, chartRelationIds=[], nmRelation, currentview="relations"} = options; - this.setService(service); - const internalComponent = new InternalComponent({ - previousview: currentview, - service, - relations, - relation, - nmRelation, - chartRelationIds, - feature, - currentview, - layer, - table - }); - this.setInternalComponent(internalComponent); - internalComponent.state = service.state; - this.layout = function() { - internalComponent.reloadLayout(); - }; -}; - -inherit(RelationsPage, Component); - -module.exports = RelationsPage; - +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ +import vueComp from 'components/g3w-relationspage'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/search/service.js b/src/app/gui/search/service.js deleted file mode 100644 index eff5c459b..000000000 --- a/src/app/gui/search/service.js +++ /dev/null @@ -1,80 +0,0 @@ -import QueryBuilderService from 'services/querybuilder'; -import ProjectsRegistry from 'store/projects'; - -const { base, inherit } = require('utils'); -const G3WObject = require('core/g3wobject'); -const SearchPanel = require('gui/search/vue/panel/searchpanel'); - -function Service() { - base(this); - const currentProjectState = ProjectsRegistry.getCurrentProject().state; - this.title = currentProjectState.search_title || "search"; - this.init = function(searchesObject) { - this.state.searches = searchesObject || currentProjectState.search; - }; - this.state = { - searches: [], - searchtools: [], - querybuildersearches: QueryBuilderService.getCurrentProjectItems() - }; -} - -inherit(Service, G3WObject); - -const proto = Service.prototype; - -proto.removeItem = function({type, index}={}){ - switch(type) { - case 'querybuilder': - this.state.querybuildersearches.splice(index, 1); - break; - } -}; - -proto.getTitle = function() { - return this.title; -}; - -proto.showPanel = function(config={}) { - const panel = new SearchPanel(config); - panel.show(); - return panel; -}; - -proto.cleanSearchPanels = function() { - this.state.panels = {}; -}; - -proto.stop = function(){ - const d = $.Deferred(); - d.resolve(); - return d.promise(); -}; - -proto.addTool = function(searchTool) { - this.state.searchtools.push(searchTool); -}; - -proto.addTools = function(searchTools) { - for (const searchTool of searchTools) { - this.addTool(searchTool); - } -}; - -proto.addQueryBuilderSearch = function(querybuildersearch){ - this.state.querybuildersearches.push(querybuildersearch); -}; - -proto.removeTool = function(searchTool) {}; - -proto.removeTools = function() { - this.state.searchtools.splice(0) -}; - -proto.reload = function() { - this.state.searches = ProjectsRegistry.getCurrentProject().state.search; - this.state.querybuildersearches = QueryBuilderService.getCurrentProjectItems(); -}; - - -module.exports = Service; diff --git a/src/app/gui/search/vue/panel/searchpanel.js b/src/app/gui/search/vue/panel/searchpanel.js index 976b1a13a..196e67cf9 100644 --- a/src/app/gui/search/vue/panel/searchpanel.js +++ b/src/app/gui/search/vue/panel/searchpanel.js @@ -1,28 +1,7 @@ -import * as vueComponentOptions from 'components/SearchPanel.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { base, inherit, uniqueId } = require('utils'); -const Panel = require('gui/panel'); -const Service = require('gui/search/vue/panel/searchservice'); - -const SearchPanelComponent = Vue.extend(vueComponentOptions); - -function SearchPanel(options = {}) { - const service = options.service || new Service(options); - this.setService(service); - this.id = uniqueId(); - this.title = 'search'; - const SearchPanel = options.component || SearchPanelComponent; - const internalPanel = new SearchPanel({ - service - }); - this.setInternalPanel(internalPanel); - this.unmount = function() { - return base(this, 'unmount').then(() => { - service.clear() - }) - } -} - -inherit(SearchPanel, Panel); - -module.exports = SearchPanel; +import { SearchPanel } from 'components/g3w-search'; +module.exports = SearchPanel; \ No newline at end of file diff --git a/src/app/gui/search/vue/panel/searchservice.js b/src/app/gui/search/vue/panel/searchservice.js index 28285f031..5c6352726 100644 --- a/src/app/gui/search/vue/panel/searchservice.js +++ b/src/app/gui/search/vue/panel/searchservice.js @@ -1,993 +1,7 @@ -import { SEARCH_ALLVALUE as ALLVALUE } from 'app/constant'; -import CatalogLayersStoresRegistry from 'store/catalog-layers'; -import DataRouterService from 'services/data'; -import ProjectsRegistry from 'store/projects'; -import GUI from 'services/gui'; - -const { - base, - inherit, - toRawType, - getUniqueDomId, - createFilterFormInputs, - createSingleFieldParameter, - isEmptyObject, - sortAlphabeticallyArray, - sortNumericArray -} = require('utils'); - -const G3WObject = require('core/g3wobject'); - -const NONVALIDVALUES = [null, undefined, ALLVALUE]; - -function SearchService(config = {}) { - - this.debounces = { - run: { - fnc: (...args) => { - const [width, heigth] = this.mapService.getMap().getSize(); - if (!GUI.isMobile() || !(width === 0 || heigth === 0)) { - this._run(...args); - return; - } - GUI.hideSidebar(); - setTimeout(() => { this._run(...args); }, 600); - } - } - }; - - base(this); - - /** - * reactivity data - */ - this.state = { - title: null, - forminputs: [], - loading: {}, - searching: false, - }; - - /** - * @FIXME add description - */ - this.config = config; - - const { options = {} } = this.config; - const layerid = options.querylayerid || options.layerid || null; - - /** - * @FIXME add description - */ - this.inputdependance = {}; - - /** - * @FIXME add description - */ - this.inputdependencies = {}; - - /** - * @FIXME add description - */ - this.cachedependencies = {}; - - /** - * @FIXME add description - */ - this.project = ProjectsRegistry.getCurrentProject(); - - /** - * @FIXME add description - */ - this.mapService = GUI.getService('map'); - - /** - * @FIXME add description - */ - this.searchLayer = null; - - /** - * @FIXME add description - */ - this.filter = null; - - /** - * @FIXME add description - */ - this.inputs = []; - - /** - * @FIXME add description - */ - this.state.title = config.name; - - /** - * @FIXME add description - */ - this.search_endpoint = config.search_endpoint; - - /** - * @FIXME add description - */ - this.url = options.queryurl; - - /** - * @FIXME add description - */ - this.filter = options.filter; - - /** - * @type { 'search' | 'search_1n' } - */ - this.type = this.config.type || 'search'; - - /** - * @FIXME add description - */ - this.return = options.return || 'data'; - - /** - * @FIXME add description - */ - this.show = 'data' === this.return && 'search' === this.type; - - /** - * @FIXME add description - */ - this.searchLayer = CatalogLayersStoresRegistry.getLayerById(layerid); - - /** - * Store layers that will be searchable for that search form. - * First one is layer owner of the search setted on admin. - */ - this.searchLayers = [ layerid, ...(options.otherquerylayerids || []) ].map(id => CatalogLayersStoresRegistry.getLayerById(id)); - - /** - * Create the form search structure - */ - this.createInputsFormFromFilter({ filter: (options.filter || []) }); - -} - -inherit(SearchService, G3WObject); - -const proto = SearchService.prototype; - -/** - * @TODO slim down and refactor - * - * Create right search structure for search form - * - * @param { Object } opts - * @param { Array } opts.filter input - * - * @returns { Promise } form input - */ -proto.createInputsFormFromFilter = async function({ - filter = [], -} = {}) { - for (let i = 0; i <= filter.length - 1; i++) { - - const input = { - label: filter[i].label, - attribute: filter[i].attribute, - type: filter[i].input.type || 'textfield', - options: { ...filter[i].input.options }, - value: null, - operator: filter[i].op, - logicop: i === (filter.length - 1) ? null : filter[i].logicop, - id: filter[i].id || getUniqueDomId(), - loading: false, - widget: null, - }; - - // check if it has a dependence - const dependance_strict = undefined !== input.options.dependance_strict ? input.options.dependance_strict : false; - const dependance = undefined !== input.options.dependance ? input.options.dependance : false; - const isInputSelectType = ['selectfield', 'autocompletefield'].includes(input.type); - input.options.values = undefined !== input.options.values ? input.options.values : []; - const { values } = input.options; - - let promise; - - //In case of select input - if ('selectfield' === input.type) { - // ensure setting values options to empty array when undefined - input.loading = true; - - promise = new Promise((resolve, reject) => { - - // in case of dependence load right now - if (dependance && dependance_strict) { - input.loading = false; - return resolve(); - } - - // not strictly dependence - if (!dependance_strict) { - this - .getValuesFromField(input) - .then(_values => { values.splice(0, values.length, ...this.valuesToKeysValues(_values)); }) - .catch((err) => { console.warn(err); values.length = 0 }) - .finally(() => { input.loading = false; resolve(); }) - } - }); - - promise.then(() => { - values[values.length && ALLVALUE !== values[0].value ? 'unshift' : 'push']({ value:ALLVALUE }); - input.value = ALLVALUE; - }); - - } - - // there is a dependence - if (isInputSelectType && dependance) { - this.inputdependance[input.attribute] = dependance; // set dependence of input - this.state.loading[dependance] = false; - input.options.disabled = dependance_strict; // disabled for BACKCOMP - this.setInputDependencies({ master: dependance, slave: input }); // set dependence between input - - } - - // set widget type for fill dependency - if (isInputSelectType && dependance && values.length > 0) { - input.widget = 'valuemap'; - input.options._values = [...values]; - } - - //Set input widget - if (isInputSelectType && dependance && !values.length && input.options.layer_id) { - input.widget = 'valuerelation'; - } - - // add form inputs to list of search input - this.state.forminputs.push(input); - } -}; - -/** - * Get return type - */ -proto.getReturnType = function() { - return this.return; -}; - -/** - * Set return type - */ -proto.setReturnType = function(returnType='data') { - this.return = returnType; - this.show = ('data' === returnType); -}; - -/** - * @param field - * - * @returns {*} - */ -proto.getAutoFieldDependeciesParamField = function(field) { - const fieldDependency = this.getCurrentFieldDependance(field); - if (fieldDependency) { - const [field, value] = Object.entries(fieldDependency)[0]; - return this.createFieldsDependenciesAutocompleteParameter({field, value}) - } -}; - -/** - * @param { Object } opts - * @param opts.fields - * @param opts.field - * @param opts.value - * - * @returns { string | undefined | * } - */ -proto.createFieldsDependenciesAutocompleteParameter = function({ - fields = [], - field, - value, -} = {}) { - const dependendency = this.getCurrentFieldDependance(field); - - if (undefined !== value) { - fields.push(createSingleFieldParameter({ field, value, operator: this.getFilterInputFromField(field).op })); - } - if (!dependendency) { - return fields.length && fields.join() || undefined; - } - const [dfield, dvalue] = Object.entries(dependendency)[0]; - // In case of some input dependeny are not filled - if (undefined !== dvalue) { - // need to set to lower case for api purpose - const { op, logicop } = this.getFilterInputFromField(dfield); - fields.unshift(`${dfield}|${op.toLowerCase()}|${encodeURI(dvalue)}|` + (fields.length ? logicop.toLowerCase() : '')); - } - return this.createFieldsDependenciesAutocompleteParameter({ fields, dfield }); -}; - -/** - * Request to server value for a specific select field - * - * @param field form input - * - * @returns { Promise<[]> } - */ -proto.getValuesFromField = async function(field) { - //if defined layer_id dependence - if (field.options.layer_id) { - //array of unique values - const uniqueValues = await this.getUniqueValuesFromField({ field: field.attribute }); - return this.getValueRelationValues( - field, - // filter - createFilterFormInputs({ - layer: CatalogLayersStoresRegistry.getLayerById(field.options.layer_id), - search_endpoint: this.getSearchEndPoint(), - inputs: [{value: uniqueValues, attribute: field.options.value, logicop: "OR", operator: "eq" }] - }) - ); - } - - // Relation reference - if (field.options.relation_reference) { - try { - //call filter data with fformatter - const response = await this.searchLayer.getFilterData({ fformatter: field.attribute }); - //check response - if (response && response.result && response.data) { - field.options.values = response.data.map(([value, key]) => ({ key, value })); - } - } catch(err) { - throw Error(err); - } - } - - if (field.options.values.length > 0) { - return this.getValueMapValues(field); - } - - return this.getUniqueValuesFromField({ field: field.attribute }) -}; - -/** - * @param field - * @param filter - * - * @returns { Promise<[]> } - */ -proto.getValueRelationValues = async function(field, filter) { - try { - const { data = [] } = await DataRouterService.getData('search:features', { - inputs:{ - layer: CatalogLayersStoresRegistry.getLayerById(field.options.layer_id), - search_endpoint: this.getSearchEndPoint(), - filter, - ordering: field.options.key - }, - outputs: false - }); - const values = []; - (data && data[0] && data[0].features || []) - .forEach(feature => { - values.push({ key: feature.get(field.options.key), value: feature.get(field.options.value) }) - }); - return values; - } catch(err) { - return []; - } -}; - -/** - * Return mapped values - * - * @param field - * - * @returns { Promise<*> } - */ -proto.getValueMapValues = async function(field) { - return field.options.values.filter(value => ALLVALUE !== value); -}; - - -/** - * @param layers - * @param options.field - * @param options.suggest - * @param options.unique - * @param options.fformatter since 3.9.0 - * @param options.ordering - * - * @returns { Promise<*> } - * - * @since 3.8.0 - */ -proto.getLayersFilterData = async function(layers, options = {}) { - const { - field, - suggest, - unique, - ordering, - fformatter, - } = options; - // get unique value from each layers - const promisesData = await Promise - .allSettled(layers.map(layer => layer.getFilterData({ - field, - suggest, - unique, - ordering, - fformatter, - }))); - - const data = Array.from( - promisesData - .filter(({status}) => 'fulfilled' === status) - .reduce((accumulator, { value = [] }) => new Set([...accumulator, ...value]), []) - ) - //check if is not empty array - switch (data.length && typeof data[0]) { - case 'string': return sortAlphabeticallyArray(data); - case 'number': return sortNumericArray(data); - default: return data; - } -}; - -/** - * Get unique values from field - * - * @param { Object } options - * @param options.field - * @param options.value - * @param options.unique - * - * @returns { Promise<[]> } - */ -proto.getUniqueValuesFromField = async function({field, value, output}) { - let data = []; - try { - data = await this.getLayersFilterData( - (1 === this.searchLayers.length ? [this.searchLayer] : this.searchLayers), { - field: this.getAutoFieldDependeciesParamField(field), - suggest: value !== undefined ? `${field}|${value}` : undefined, - unique: field, - ordering: field - }); - - if ('autocomplete' === output) { - data = data.map(value => ({ id:value, text:value })); - } - } catch(e) { console.warn(e); } - - return data; -}; - /** - * Perform search - * - * @param { Object } opts - * @param opts.filter - * @param opts.search_endpoint - * @param opts.queryUrl - * @param opts.feature_count - * @param opts.show - * - * @returns { Promise } + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 */ -proto.doSearch = async function({ - filter, - search_endpoint = this.getSearchEndPoint(), - queryUrl = this.url, - feature_count = 10000, - show = this.show, -} = {}) { - - //get or create request filter - filter = filter || this.createFilter(); - - //set searching to true - this.state.searching = true; - - let data; - - try { - data = await DataRouterService.getData('search:features', { - inputs:{ - layer: this.searchLayers, - search_endpoint, - filter, - queryUrl, - formatter: 1, - feature_count, - raw: ('search' === this.return) // in order to get raw response - }, - outputs: show && { title: this.state.title } - }); - // not show (request internal. No output data are show) - if (!show) { - const parsed = ('search_1n' === this.type) - ? await parse_search_1n(data, { - search_endpoint, - feature_count, - relation_id: this.config.options.search_1n_relationid, - output_title: this.state.title - }) - : parse_search_by_returnType(data, this.return); - data = parsed ? parsed : data; - } else if (this.project.state.autozoom_query && data && 1 === data.data.length) { - this.mapService.zoomToFeatures(data.data[0].features); // auto zoom_query - } - } catch(e) { - console.warn(e); - } - - //set searchin false - this.state.searching = false; - - return data; -}; - -/** - * Filter input by NONVALIDVALUES - * - * @returns { Array } - */ -proto.filterValidFormInputs = function() { - return this.state - .forminputs - .filter(input => -1 === NONVALIDVALUES.indexOf(input.value) && '' !== input.value.toString().trim()); -}; - -/** - * @returns { string | * } - */ -proto.getSearchEndPoint = function() { - return this.search_endpoint || this.searchLayer.getSearchEndPoint() -}; - -/** - * type wms, vector (for vector api) - */ -proto.createFilter = function(search_endpoint = this.getSearchEndPoint()) { - return createFilterFormInputs({ layer: this.searchLayers, inputs: this.filterValidFormInputs(), search_endpoint }); -}; - -/** - * @private - */ -proto._run = function() { - this.doSearch(); -}; - -/** - * Called on search input change - * - * @param { Object } opts - * @param opts.id - * @param opts.value - */ -proto.changeInput = function({id, value} = {}) { - this.state.forminputs.find(input => id == input.id).value = value; -}; - -/** - * @param { Object } opts - * @param opts.filter - * - * @returns { Object } - */ -proto.createQueryFilterFromConfig = function({ filter }) { - let queryFilter; - for (const operator in filter) { - queryFilter = create_boolean_filter(operator, filter[operator]); - } - return queryFilter; -}; - -/** - * @param field - * - * @returns {*} - */ -proto.getFilterInputFromField = function(field) { - return this.filter.find(input => input.attribute === field); -}; - -/** - * @param field - * - * @returns { * | null } - * - * @private - */ -proto._getExpressionOperatorFromInput = function(field) { - const dependanceCascadeField = this.getFilterInputFromField(field); - return dependanceCascadeField ? dependanceCascadeField.op : null; -}; - -proto._getCascadeDependanciesFilter = function(field, dependencies = []) { - const dependance = this.getFilterInputFromField(field).input.options.dependance; - if (dependance) { - dependencies.unshift(dependance); - this._getCascadeDependanciesFilter(dependance, dependencies) - } - return dependencies; -}; - -/** - * Check if a field has a dependance - * - * @param field - * - * @returns { Object } - */ -proto.getCurrentFieldDependance = function(field) { - const dependance = this.inputdependance[field]; - return dependance ? ({ - [dependance]: - (this.cachedependencies[dependance] && ALLVALUE !== this.cachedependencies[dependance]._currentValue) - ? this.cachedependencies[dependance]._currentValue // dependance as value - : undefined // undefined = so it no add on list o field dependance - }) : dependance; -}; - -/** - * Check the current value of dependance - */ -proto.getDependanceCurrentValue = function(field) { - return this.inputdependance[field] ? - this.cachedependencies[this.inputdependance[field]]._currentValue : - this.state.forminputs.find(forminput => forminput.attribute === field).value; -}; - -/** - * @TODO slim down and refactor - * - * Fill all dependencies inputs based on value - */ -proto.fillDependencyInputs = function({field, subscribers=[], value=ALLVALUE}={}) { - const isRoot = this.inputdependance[field] === undefined; - //check id inpute father is valid to search on subscribers - const invalidValue = value===ALLVALUE || value === null || value === undefined || value.toString().trim() === ''; - return new Promise((resolve, reject) => { - //loop over dependencies fields inputs - subscribers.forEach(subscribe => { - // in case of autocomplete reset values to empty array - if (subscribe.type === 'autocompletefield') { - subscribe.options.values.splice(0); - } else { - //set starting all values - if (subscribe.options._allvalues === undefined) { - subscribe.options._allvalues = [...subscribe.options.values]; - } - //case of father is set an empty invalid value (all value example) - if (invalidValue) { - //subscribe has to set all valaues - subscribe.options.values.splice(0); - setTimeout(()=>subscribe.options.values = [...subscribe.options._allvalues]); - } else { - subscribe.options.values.splice(1); - } //otherwise has to get first __ALL_VALUE - } - subscribe.value = subscribe.type !== 'selectfield' ? ALLVALUE : null; - }); - // check if cache field values are set - this.cachedependencies[field] = this.cachedependencies[field] || {}; - this.cachedependencies[field]._currentValue = value; - const notAutocompleteSubscribers = subscribers.filter(subscribe => subscribe.type !== 'autocompletefield'); - if (value && value !== ALLVALUE) { - let isCached; - let rootValues; - if (isRoot) { - const cachedValue = this.cachedependencies[field] && this.cachedependencies[field][value]; - isCached = cachedValue !== undefined; - rootValues = isCached && cachedValue; - } else { - const dependenceCurrentValue = this.getDependanceCurrentValue(field); - const cachedValue = this.cachedependencies[field] - && this.cachedependencies[field][dependenceCurrentValue] - && this.cachedependencies[field][dependenceCurrentValue][value]; - isCached = cachedValue !== undefined; - rootValues = isCached && cachedValue; - } - if (isCached) { - for (let i = 0; i < subscribers.length; i++) { - const subscribe = subscribers[i]; - const values = rootValues[subscribe.attribute]; - if (values && values.length) { - for (let i = 0; i < values.length; i++) { - subscribe.options.values.push(values[i]); - } - } - // set disabled false to dependence field - subscribe.options.disabled = false; - resolve() - } - } else { - this.state.loading[field] = true; - if (isRoot) { - this.cachedependencies[field][value] = this.cachedependencies[field][value] || {}; - } else { - const dependenceValue = this.getDependanceCurrentValue(field); - this.cachedependencies[field][dependenceValue] = this.cachedependencies[field][dependenceValue] || {}; - this.cachedependencies[field][dependenceValue][value] = this.cachedependencies[field][dependenceValue][value] || {} - } - // exclude autocomplete subscribers - if (notAutocompleteSubscribers.length > 0) { - const fieldParams = this.createFieldsDependenciesAutocompleteParameter({ - field, - value - }); - //need to set undefined because if - // it has a subscribe input with valuerelations widget needs to extract the value of the field to get - // filter data from relation layer - this.searchLayer.getFilterData({ - field: fieldParams, - formatter: 0 //v3.0 need to force to use raw value with formatter 0 parameter - }) - .then(async data => { - const parentData = data.data[0].features || []; - for (let i = 0; i < notAutocompleteSubscribers.length; i++) { - const subscribe = notAutocompleteSubscribers[i]; - const { attribute, widget } = subscribe; - const uniqueValues = new Set(); - // case value map - if (widget === 'valuemap') { - let values = [...subscribe.options._values]; - parentData.forEach(feature => { - const value = feature.get(attribute); - if (value) { - // need to covert to string - // because input values are string - uniqueValues.add(`${value}`); - } - }); - const data = [...uniqueValues]; - values = values.filter(({key}) => data.indexOf(key) !== -1); - values.forEach(value => subscribe.options.values.push(value)); - } else if (widget === 'valuerelation') { - parentData.forEach(feature => { - const value = feature.get(attribute); - value && uniqueValues.add(value); - }); - if (uniqueValues.size > 0) { - const filter = createSingleFieldParameter({ - layer: CatalogLayersStoresRegistry.getLayerById(subscribe.options.layer_id), - search_endpoint: this.getSearchEndPoint(), - field: subscribe.options.value, //v3.8.x has subscribe.options.key - value: [...uniqueValues] - }); - try { - const values = await this.getValueRelationValues(subscribe, filter); - values.forEach(value => subscribe.options.values.push(value)); - } catch(err) {console.log(err)} - } - } else { - parentData.forEach(feature => { - const value = feature.get(attribute); - value && uniqueValues.add(value); - }); - this.valuesToKeysValues([...uniqueValues].sort()).forEach(value => subscribe.options.values.push(value)); - } - if (isRoot) { - this.cachedependencies[field][value][subscribe.attribute] = subscribe.options.values.slice(1); - } else { - const dependenceValue = this.getDependanceCurrentValue(field); - this.cachedependencies[field][dependenceValue][value][subscribe.attribute] = subscribe.options.values.slice(1); - } - subscribe.options.disabled = false; - } - }) - .catch(error => reject(error)) - .finally(() => { - this.state.loading[field] = false; - resolve(); - }) - } else { - //set disable - subscribers.forEach(subscribe => { - if (subscribe.options.dependance_strict) { - subscribe.options.disabled = false; - } - }); - this.state.loading[field] = false; - resolve(); - } - } - } else { - subscribers - .forEach(subscribe => subscribe.options.disabled = subscribe.options.dependance_strict); - resolve(); - } - }) -}; - -/** - * @param field - * - * @returns { Array | * } - */ -proto.getDependencies = function(field) { - return this.inputdependencies[field] || []; -}; - -/** - * @param { Object } opts - * @param opts.master - * @param opts.slave - */ -proto.setInputDependencies = function({ master, slave }={}) { - this.inputdependencies[master] = (undefined !== this.inputdependencies[master] ? this.inputdependencies[master] : []); - this.inputdependencies[master].push(slave); -}; - -/** - * set key value for select - */ -proto.valuesToKeysValues = function(values) { - return values.length ? - ('Object' !== toRawType(values[0]) ? values.map(value => ({ key: value, value })) : values) : - values; -}; - -/** - * @param { Object } opts - * @param opts.ogcService - * @param opts.filter - * - * @returns {{infoFormat: *, crs: *, serverType, layers: [], url: *} & {filter: {}, ogcService: string}} - */ -proto.createQueryFilterObject = function({ - ogcService = 'wms', - filter = {}, -} = {}) { - return Object.assign(this.getInfoFromLayer(ogcService), { ogcService, filter }); -}; - -/** - * @param ogcService - * - * @returns {{infoFormat: *, crs: *, serverType, layers: [], url: *}} - */ -proto.getInfoFromLayer = function(ogcService) { - return { - url: ('wfs' === ogcService ? this.searchLayer.getProject().getWmsUrl() : this.searchLayer.getQueryUrl()), - layers: [], - infoFormat: this.searchLayer.getInfoFormat(ogcService), - crs: this.searchLayer.getCrs(), - serverType: this.searchLayer.getServerType() - }; -}; - -/** - * @param layer - */ -proto.setSearchLayer = function(layer) { - this.searchLayer = layer; -}; - -/** - * @returns { null | * } - */ -proto.getSearchLayer = function() { - return this.searchLayer; -}; - -/** - * - */ -proto.clear = function() { - this.state = null; -}; - -function create_boolean_filter(operator, inputs = []) { - const boolean = { [operator]: [] }; - inputs - .forEach((input) => { - for (const operator in input) { - if (Array.isArray(input[operator])) { - create_boolean_filter(operator, input[operator]); // recursion step. - break; - } - } - boolean[operator].push({ - [input.op] : { - [input.attribute]: null - }}); - }); - return boolean; -} - -/** - * Search father layer id based on result of child layer - */ -async function parse_search_1n(data, options) { - const { search_endpoint, feature_count, relation_id, output_title } = options; - - const { features = [] } = data.data[0] || {}; - - const project = ProjectsRegistry.getCurrentProject(); - - // check if it has features on result - if (!features.length) { - //show empty result output - DataRouterService.showEmptyOutputs(); - return []; - } - - //get relation - const relation = project.getRelationById(relation_id); - - //if exist relation - if (relation) { - - const inputs = []; //store inputs - - //extract properties from relation object - const { - referencedLayer, //father layer id - fieldRef: {referencingField, referencedField}} = relation; // child and father relation fields - - //Number of relation fields - const rFLength = referencingField.length; - - //Just one field - if (1 === rFLength) { - const uniqueValues = new Set(); - //loop trough feature child layer - features.forEach(feature => { - const value = feature.get(referencingField[0]); - if (!uniqueValues.has(value)) { - uniqueValues.add(value); - } - }) - inputs.push({ attribute: referencedField[0], logicop: "OR", operator: "eq", value: Array.from(uniqueValues) }) - } else { - const uniqueValues = []; - features.forEach(feature => { - const values = referencingField.map(rF => feature.get(rF)); - if (!uniqueValues.find((v) => { - return v.reduce((accumulator, value, index) => { - return accumulator && values[index] === value; - }, true); - })) { - uniqueValues.push(values); - inputs.push({ attribute: referencedField, logicop: "OR", operator: "eq", value: values }) - } - }) - } - - const layer = project.getLayerById(referencedLayer); - - - data = await DataRouterService.getData('search:features', { - inputs: { - layer, - search_endpoint, - filter: createFilterFormInputs({ layer, search_endpoint, inputs }), - formatter: 1, - feature_count - }, - outputs: { - title: output_title - } - }); - - } - return data; -} - -function parse_search_by_returnType(data, returnType) { - if ('search' === returnType) { - GUI.closeContent(); - // in case of api get first response on array - data = data.data[0].data; - if (isEmptyObject(data)) { - DataRouterService.showCustomOutputDataPromise(Promise.resolve({})); - } else { - const SearchPanel = require('gui/search/vue/panel/searchpanel'); - (new SearchPanel(data)).show(); - } - } - return data; -} -module.exports = SearchService; +import { SearchService } from 'components/g3w-search'; +module.exports = SearchService; \ No newline at end of file diff --git a/src/app/gui/search/vue/search.js b/src/app/gui/search/vue/search.js index f00e3ab06..b1633f624 100755 --- a/src/app/gui/search/vue/search.js +++ b/src/app/gui/search/vue/search.js @@ -1,31 +1,7 @@ -import * as vueComponentOptions from 'components/Search.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); -const Service = require('gui/search/service'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -function SearchComponent(options={}){ - base(this, options); - this.id = "search"; - this._service = options.service || new Service(); - this._service.init(); - this.title = this._service.getTitle(); - this.internalComponent = new InternalComponent({ - service: this._service - }); - this.internalComponent.state = this._service.state; - this.state.visible = true; - this._reload = function() { - this._service.reload(); - }; - this.unmount = function() { - this._searches_searchtools.$destroy(); - return base(this, 'unmount'); - } -} - -inherit(SearchComponent, Component); - -module.exports = SearchComponent; +import { SearchComponent } from 'components/g3w-search'; +module.exports = SearchComponent; \ No newline at end of file diff --git a/src/app/gui/spatialbookmarks/vue/spatialbookmarks.js b/src/app/gui/spatialbookmarks/vue/spatialbookmarks.js index dcf59dc99..43e7dd1ae 100644 --- a/src/app/gui/spatialbookmarks/vue/spatialbookmarks.js +++ b/src/app/gui/spatialbookmarks/vue/spatialbookmarks.js @@ -1,33 +1,7 @@ /** * @file - * @since v3.8 + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 */ - -import * as vueComponentOptions from 'components/SpatialBookMarks.vue'; - -import GUI from 'services/gui'; - -const { inherit, base } = require('utils'); -const Component = require('gui/component/component'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -const SpatialBookMarksComponent = function(options = {}) { - base(this, options); - this.title = "sdk.spatialbookmarks.title"; - this.setInternalComponent = function () { - this.internalComponent = new InternalComponent(); - return this.internalComponent; - }; - - GUI.on('closecontent', () => { - this.state.open = false; - }); - -}; - -inherit(SpatialBookMarksComponent, Component); - -module.exports = SpatialBookMarksComponent; - +import vueComp from 'components/g3w-spatialbookmarks'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/streetview/vue/streetview.js b/src/app/gui/streetview/vue/streetview.js deleted file mode 100644 index 3595c0cfe..000000000 --- a/src/app/gui/streetview/vue/streetview.js +++ /dev/null @@ -1,25 +0,0 @@ -import * as vueComponentOptions from 'components/StreetView.vue'; - -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -const StreetViewComponent = function(options={}) { - base(this); - const {keyError} = options; - const internalComponent = new InternalComponent({ - keyError - }); - this.setInternalComponent(internalComponent); - this.unmount = function() { - return base(this, 'unmount'); - } -}; - -inherit(StreetViewComponent, Component); - - -module.exports = StreetViewComponent; - - diff --git a/src/app/gui/table/tableservice.js b/src/app/gui/table/tableservice.js deleted file mode 100644 index 5f0be0836..000000000 --- a/src/app/gui/table/tableservice.js +++ /dev/null @@ -1,786 +0,0 @@ -import CatalogLayersStoresRegistry from 'store/catalog-layers'; -import DataRouterService from 'services/data'; -import GUI from 'services/gui'; -import { coordinatesToGeometry } from 'utils/coordinatesToGeometry'; - -const { inherit, noop } = require('utils'); -const G3WObject = require('core/g3wobject'); -const { t } = require('core/i18n/i18n.service'); -const { SELECTION_STATE } = require('core/layers/layer'); - -const PAGELENGTHS = [10, 25, 50]; - -/** - * Create a unique feature key - */ -function _createFeatureKey(values) { - return values.join('__'); -} - -/** - * TableService Class - * - * @param options.layer - * @param options.formatter - * - * @constructor - */ -const TableService = function(options = {}) { - - /** - * Number of pages - */ - this.currentPage = 0; - - /** - * @FIXME add description - */ - this.layer = options.layer; - - /** - * @FIXME add description - */ - this.formatter = options.formatter; - - /** - * @FIXME add description - */ - this.allfeaturesnumber = undefined; - - /** - * @FIXME add description - */ - this.nopaginationsfilter = []; - - /** - * @FIXME add description - */ - this.selectedfeaturesfid = this.layer.getSelectionFids(); - - /** - * Whether layer has geometry - */ - this.geolayer = this.layer.isGeoLayer(); - - /** - * @FIXME add description - */ - this.relationsGeometry = this._relationsGeometry(); - - /** - * @FIXME add description - */ - this.projection = this.geolayer ? this.layer.getProjection() : null; - - /** - * @FIXME add description - */ - this.mapService = GUI.getService('map'); - - /** - * @FIXME add description - */ - this.getAll = false; - - /** - * @FIXME add description - */ - this.paginationfilter = false; - - /** - * @FIXME add description - */ - this.mapBBoxEventHandlerKey = { - key: null, - cb: null - }; - - - // bind context on event listeners - this.clearAllSelection = this.clearAllSelection.bind(this); - this.filterChangeHandler = this.filterChangeHandler.bind(this); - this.onGUIContent = this.onGUIContent.bind(this); - - /** - * @FIXME add description - */ - this.state = { - pageLengths: PAGELENGTHS, - pageLength: this.layer.getAttributeTablePageLength() || PAGELENGTHS[0], - features: [], - title: this.layer.getTitle(), - headers: this.getHeaders(), - geometry: true, - loading: false, - allfeatures: 0, - pagination: !this.getAll, - selectAll: false, - nofilteredrow: false, - tools: { - geolayer: { - show: this.geolayer, - active: false, - in_bbox: undefined, - }, - show: false, - filter: this.layer.state.filter - } - }; - - /** - * Pagination filter features - */ - this._async = { - state: false, - fnc: noop - }; - - GUI.onbefore('setContent', this.onGUIContent); - this.layer.on('unselectionall', this.clearAllSelection); - this.layer.on('filtertokenchange', this.filterChangeHandler); -}; - -inherit(TableService, G3WObject); - -const proto = TableService.prototype; - -/** - * @since 3.9.0 - */ -proto._relationsGeometry = function() { - - // layer has geometry - if (this.geolayer) { - return []; - } - - const relations = []; - - this.layer - .getRelations() - .getArray() - .forEach(relation => { - const layer = CatalogLayersStoresRegistry.getLayerById(relation.getFather()); // get project layer - if ( - this.layer.getId() !== relation.getFather() && // current layer is not child layer of relation - layer.isGeoLayer() // relation layer has geometry - ) { - relations.push({ - layer, - father_fields: relation.getFatherField(), // NB: since g3w-admin@v3.7.0 this is an Array value. - fields: relation.getChildField(), // NB: since g3w-admin@v3.7.0 this is an Array value. - features: {}, - }) - } - }); - - return relations; -}; - -/** - * @since 3.9.0 - */ -proto.clearAllSelection = function() { - this.state.features.forEach(feature => feature.selected = false); - this.state.tools.show = false; - this.state.selectAll = false; -}; - -/** - * @since 3.9.0 - * - * @param { Object } opts - * @param { string } opts.type - * - * @fires redraw when `opts.type` in_bbox filter (or not select all) - */ -proto.filterChangeHandler = async function ({ type } = {}) { - this.allfeaturesnumber = undefined; - if (type === 'in_bbox' || !this.selectedfeaturesfid.has(SELECTION_STATE.ALL)) { - this.emit('redraw', this.state.pagination ? [] : await this.reloadData()); - } -}; - -/** - * @since 3.9.0 - */ -proto.onGUIContent = function(options) { - this._async.state = (100 === options.perc); -}; - -proto.toggleFilterToken = async function() { - await this.layer.toggleFilterToken(); -}; - -/** - * first value = `null` for DataTable purpose (used to add a custom input selector) - */ -proto.getHeaders = function() { - return [null, ...this.layer.getTableHeaders()]; -}; - -/** - * DataTable pagination - */ -proto.setDataForDataTable = function() { - const data = []; - this.state.features.forEach(feature => { - const attributes = feature.attributes ? feature.attributes : feature.properties; - const values = [null]; - this.state.headers.forEach(header => { - if (header) { - header.value = attributes[header.name]; - values.push(header.value); - // header.label = undefined; // removes label. - } - }); - data.push(values) - }); - return data; -}; - -proto.addRemoveSelectedFeature = function(feature) { - feature.selected = !feature.selected; - - const selected = this.selectedfeaturesfid; - const filter = this.nopaginationsfilter; - const count = this.allfeaturesnumber; - - const select_all = this.state.selectAll; - const has_pagination = this.state.pagination; - const features = this.state.features; - const is_active = this.state.tools && this.state.tools.filter && this.state.tools.filter.active - - const is_exclude = !select_all && selected.has(SELECTION_STATE.EXCLUDE); - const is_default = !select_all && !is_exclude; - - /** @FIXME add description */ - if (select_all) { - this.state.selectAll = false; - } - - /** @FIXME add description */ - if (select_all) { - this.layer.excludeSelectionFid(feature.id, has_pagination); - } - - /** @FIXME add description */ - if (is_exclude || is_default) { - this.layer[feature.selected ? 'includeSelectionFid' : 'excludeSelectionFid'](feature.id); - } - - /** @FIXME add description */ - if (!is_active && ( (is_exclude && 1 === selected.size) || (is_default && selected.size === count)) ) { - this.layer.setSelectionFidsAll(); - } - - /** @FIXME add description */ - if (is_exclude && 1 !== selected.size && selected.size === features.length + 1) { - this.layer.clearSelectionFids(); - } - - /** @FIXME add description */ - this.state.tools.show = selected.size > 0; - - /** @FIXME add description */ - if ( - (is_exclude && 1 === selected.size) || - (is_default && selected.size === count) || - (!has_pagination && filter.length && filter.length === features.filter(f => f.selected).length) - ) { - this.state.selectAll = true; - } - -}; - -proto.createFeatureForSelection = function(feature) { - return { - attributes: feature.attributes ? feature.attributes : feature.properties, - geometry: this._returnGeometry(feature), - } -}; - -proto.getAllFeatures = function(params) { - GUI.setLoadingContent(true); - return new Promise((resolve, reject) => { - this.layer - .getDataTable(params || {}) - .then(data => { - const is_valid = this.geolayer && data.features; - - if (is_valid && !params) { - const loaded_features = this.state.features.map(f => f.id); - data.features.forEach(f => { - if (-1 === loaded_features.indexOf(f.id) && f.geometry) { - this.layer.addOlSelectionFeature({ - id: f.id, - feature: this.createFeatureForSelection(f), - }); - } - }); - this.getAll = true; - } - - if (is_valid) { - resolve(data.features); - } - }) - .fail(() => reject()) - .always(() => GUI.setLoadingContent(false)); - }); -}; - -proto.switchSelection = async function() { - const has_pagination = this.state.pagination; - const filter = this.nopaginationsfilter; - const filtered = !has_pagination && filter.length ? [] : undefined; - let selected = false; - - // pagination - if (has_pagination) { - this.state.features.forEach(f => { - f.selected = !f.selected; - selected = f.selected; - }); - } - - if (has_pagination && !this.getAll) { - await this.getAllFeatures(); - } - - this.state.selectAll = (has_pagination && this.paginationfilter) ? selected : this.state.selectAll; - - // filtered - if (!has_pagination && filter.length) { - this.state.features.forEach((f, i) => { - if (-1 !== filter.indexOf(i)) { - filtered.push(f); - } - f.selected = !f.selected; - this.layer[f.selected ? 'includeSelectionFid' : 'excludeSelectionFid' ](f.id); - selected = selected || f.selected; - }); - this.state.tools.show = selected; - } - - // no filter - if (!has_pagination && !filter.length) { - this.state.features.forEach(f => { f.selected = !f.selected; }); - } - - if (has_pagination || !filter.length) { - this.layer.invertSelectionFids(); - } - - if (!has_pagination) { - this.checkSelectAll(filtered); - } - - if (has_pagination || !filter.length) { - this.state.tools.show = this.selectedfeaturesfid.size > 0; - } - -}; - -proto.clearLayerSelection = function() { - this.layer.clearSelectionFids(); -}; - -/** - * Called when a selected feature is checked - * - * @returns {Promise} - */ -proto.selectAllFeatures = async function() { - - // set inverse of selectAll - this.state.selectAll = !this.state.selectAll; - - const has_pagination = this.state.pagination; - const filter = this.nopaginationsfilter; - let selected = false; - - // filtered - if (!has_pagination && filter.length) { - this.state.features.forEach((f, i) =>{ - if (-1 !== filter.indexOf(i)) { - f.selected = this.state.selectAll; - this.layer[f.selected ? 'includeSelectionFid': 'excludeSelectionFid'](f.id); - selected = selected || f.selected; - } - }); - this.state.tools.show = selected; - } - - // no filter - if (!has_pagination && !filter.length) { - this.state.tools.show = this.state.selectAll; - this.layer[this.state.selectAll ? 'setSelectionFidsAll': 'clearSelectionFids'](); - this.state.features.forEach(f => f.selected = this.state.selectAll); - } - - // filtered pagination - if (has_pagination && this.paginationfilter && this.state.featurescount >= this.state.allfeatures) { - this.state.features.forEach(f => { - f.selected = this.state.selectAll; - this.layer[f.selected ? 'includeSelectionFid': 'excludeSelectionFid'](f.id); - }); - } - - if (has_pagination && this.paginationfilter && this.state.featurescount < this.state.allfeatures) { - const features = await this.getAllFeatures({ - search: this.paginationParams.search, - ordering: this.paginationParams.ordering, - formatter: this.paginationParams.formatter, - in_bbox: this.paginationParams.in_bbox, - }); - features.forEach(f => { - if (!this.getAll && this.geolayer && f.geometry) { - this.layer.addOlSelectionFeature({ - id: f.id, - feature: this.createFeatureForSelection(f) - }); - } - this.layer[this.state.selectAll ? 'includeSelectionFid' : 'excludeSelectionFid'](f.id); - }) - } - - if (has_pagination) { - this.state.features.forEach(f => f.selected = this.state.selectAll); - } - - if (has_pagination && !this.paginationfilter && !this.getAll) { - await this.getAllFeatures(); - } - - if (has_pagination && !this.paginationfilter) { - this.layer[this.state.selectAll ? 'setSelectionFidsAll': 'clearSelectionFids'](); - } - - if (has_pagination) { - this.state.tools.show = this.state.selectAll || this.selectedfeaturesfid.size > 0; - } - -}; - -/** - * Set filtered features - * - * @param index features index - */ -proto.setFilteredFeature = function(index) { - const filter = this.nopaginationsfilter = index; - if (0 === index.length || index.length === this.allfeaturesnumber) { - this.checkSelectAll(); - } else { - this.checkSelectAll(filter.map(i => this.state.features[i])); - } -}; - -proto.setAttributeTablePageLength = function(length) { - this.layer.setAttributeTablePageLength(length); -}; - -/** - * Get DataTable layer - * - * @param data.start - * @param data.order - * @param data.length - * @param data.columns - * @param data.search - * @param data.firstCall - * - * @returns {Promise<{{ data: [], recordsTotal: number, recordsFiltered: number }}>} - */ -proto.getData = function({ - start = 0, - order = [], - length = this.state.pageLength, - columns = [], - search = { value: null }, - firstCall = false -} = {}) { - - // reset features before load - GUI.setLoadingContent(true); - - this.setAttributeTablePageLength(length); - - return new Promise((resolve, reject) => { - - // skip when .. - if (!this.state.headers.length) { - resolve({ - data: [], - recordsTotal: 0, - recordsFiltered: 0 - }); - return; - } - - let searchText = search.value && search.value.length > 0 ? search.value : null; - - this.state.features.splice(0); - - if (!order.length) { - order.push({ - column: 1, - dir: 'asc', - }); - } - - const ordering = ('asc' === order[0].dir ? '' : '-') + this.state.headers[order[0].column].name; - - this.currentPage = (start === 0 || (this.state.pagination && this.state.tools.filter.active)) ? 1 : (start/length) + 1; - - const in_bbox = this.state.tools.geolayer.in_bbox; - - const field = this.state.pagination - ? columns.filter(c => c.search && c.search.value).map(c => `${c.name}|ilike|${c.search.value}|and`).join(',') - : undefined; - - this.paginationParams = { - field: field || undefined, - page: this.currentPage, - page_size: length, - search: searchText, - in_bbox, - formatter: this.formatter, - ordering - }; - - this.layer - .getDataTable( - this.state.pagination - ? this.paginationParams - : ({ ordering, in_bbox, formatter: this.formatter }) - ) - .then(data => { - const { features = [] } = data; - - this.state.allfeatures = data.count || this.state.features.length; - this.state.featurescount = features.length; - this.allfeaturesnumber = (undefined === this.allfeaturesnumber ? data.count : this.allfeaturesnumber); - this.paginationfilter = (data.count !== this.allfeaturesnumber); - this.state.pagination = firstCall - ? this.state.tools.filter.active || features.length < this.allfeaturesnumber - : this.state.pagination; - - this.addFeatures(features); - - resolve({ - data: this.setDataForDataTable(), - recordsFiltered: data.count, - recordsTotal: data.count - }); - }) - .fail(err => { GUI.notify.error(t("info.server_error")); reject(err); }) - .always(() => { GUI.setLoadingContent(false); }) - }); -}; - -proto.setInBBoxParam = function() { - const { geolayer } = this.state.tools; - geolayer.in_bbox = geolayer.active ? this.mapService.getMapBBOX().join(',') : undefined; -}; - -proto.resetMapBBoxEventHandlerKey = function() { - const listener = this.mapBBoxEventHandlerKey; - ol.Observable.unByKey(listener.key); - listener.key = null; - listener.cb = null; -}; - -proto.getDataFromBBOX = async function() { - const { geolayer } = this.state.tools; - - geolayer.active = !geolayer.active; - - const is_active = geolayer.active; - const listener = this.mapBBoxEventHandlerKey; - - if (is_active && this.state.pagination) { - listener.cb = () => { - this.setInBBoxParam(); - this.emit('ajax-reload'); - }; - } - - if (is_active && !this.state.pagination) { - listener.cb = async () => { - this.setInBBoxParam(); - this.filterChangeHandler({ type: 'in_bbox' }); - }; - } - - if (is_active) { - listener.key = this.mapService.getMap().on('moveend', listener.cb); - } - - if (listener.cb) { - listener.cb(); - } - - if (!is_active) { - this.resetMapBBoxEventHandlerKey(); - } -}; - -proto.addFeature = function(feature) { - const tableFeature = { - id: feature.id, - selected: this.layer.hasSelectionFid(feature.id), - attributes: feature.attributes || feature.properties, - geometry: this.geolayer && feature.geometry || undefined - }; - - const has_geom = this.geolayer && feature.geometry; - const selection = has_geom && this.layer.getOlSelectionFeature(feature.id); - - if (has_geom && !selection) { - this.layer.addOlSelectionFeature({ - id: feature.id, - feature: this.createFeatureForSelection(feature) - }); - } - - this.state.features.push(tableFeature); -}; - -proto.checkSelectAll = function(features = this.state.features) { - this.state.selectAll = ( - this.selectedfeaturesfid.has(SELECTION_STATE.ALL) || - (features.length && features.reduce((selectAll, f) => selectAll && f.selected, true)) - ); -}; - -proto.addFeatures = function(features=[]) { - features.forEach(f => this.addFeature(f)); - this.state.tools.show = this.layer.getFilterActive() || this.selectedfeaturesfid.size > 0; - this.checkSelectAll(); -}; - -proto.reloadData = async function(pagination=false) { - this.state.features.splice(0); - this.state.pagination = pagination; - const { data = [] } = await this.getData(); - return data; -}; - -proto._setLayout = function() { - //TODO -}; - -proto._returnGeometry = function(feature) { - if (feature.attributes) return feature.geometry; - if (feature.geometry) return coordinatesToGeometry(feature.geometry.type, feature.geometry.coordinates); -}; - -proto.zoomAndHighLightFeature = function(feature, zoom = true) { - // async highlight - if (feature.geometry && this._async.state) { - this._async.fnc = this.mapService.highlightGeometry.bind(mapService, feature.geometry, { zoom }); - } - // sync highlight - if (feature.geometry && !this._async.state) { - this.mapService.highlightGeometry(feature.geometry , { zoom }); - } -}; - -/** - * Zoom to eventually features relation - */ -proto.zoomAndHighLightGeometryRelationFeatures = async function(feature, zoom = true) { - - // skip when there are no relation features geometry - if (!this.relationsGeometry.length > 0) { - return; - } - - const features = []; - const promises = []; - const field_values = []; // check if add or not - - this.relationsGeometry - .forEach(({ - layer, - father_fields, - fields, - features - }) => { - const values = fields.map(f => feature.attributes[f]); - - field_values.push(values); - - let promise; - - if (zoom && undefined === features[k]) { - promise = DataRouterService - .getData('search:features', { - inputs: { - layer, - formatter: 1, - search_endpoint: 'api', - filter: ( - father_fields - .reduce((filter, field, index) => { - filter = `${filter}${index > 0 ? '|AND,' : ''}${field}|eq|${encodeURIComponent(values[index])}` - return filter; - }, '') - ), - }, - outputs: false, // just a request not show on result - }); - } - - promises.push(promise); - }); - - (await Promise.allSettled(promises)) - .forEach(({ - status, - value - }, index) => { - if ('fulfilled' === status) { - - const relation = this.relationsGeometry[index]; - const k = _createFeatureKey(field_values[index]); - const data = value && value.data[0]; - - if (undefined === relation.features[k]) { - relation.features[k] = data && data.features || []; - } - - relation.features[k].forEach(f => features.push(f)); - - } - }); - - if (zoom) { - this.mapService.zoomToFeatures(features, { highlight: true }); - } else { - this.mapService.highlightFeatures(features); - } - -}; - -proto.clear = function() { - this.layer.off('unselectionall', this.clearAllSelection); - this.layer.off('filtertokenchange', this.filterChangeHandler); - - this.resetMapBBoxEventHandlerKey(); - - this.allfeaturesnumber = null; - this.mapService = null; - - if (this._async.state) { - setTimeout(() => { - this._async.fnc(); - this._async.state = false; - this._async.fnc = noop; - }); - } -}; - -module.exports = TableService; diff --git a/src/app/gui/table/vue/table.js b/src/app/gui/table/vue/table.js deleted file mode 100644 index a361b1802..000000000 --- a/src/app/gui/table/vue/table.js +++ /dev/null @@ -1,73 +0,0 @@ -import Table from 'components/Table.vue'; -import GUI from 'services/gui'; - -const { t } = require('core/i18n/i18n.service'); -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); -const TableService = require('gui/table/tableservice'); - -const InternalComponent = Vue.extend(Table); - -const TableComponent = function(options = {}) { - base(this); - this.id = "openattributetable"; - const {layer, formatter} = options; - const service = options.service || new TableService({ - layer, - formatter - }); - - this.setService(service); - const internalComponent = new InternalComponent({ - service - }); - - this.setInternalComponent(internalComponent); - internalComponent.state = service.state; - - service.on('redraw', ()=>{ - this.layout(); - }); - - this.unmount = function() { - return base(this, 'unmount') - }; - - this.layout = function() { - internalComponent.reloadLayout(); - }; -}; - -inherit(TableComponent, Component); - -const proto = TableComponent.prototype; - -// overwrite show method -proto.show = function(options = {}) { - const service = this.getService(); - // close all sidebar open component - GUI.closeOpenSideBarComponent(); - service.getData({firstCall: true}) - .then(() => { - GUI.showContent({ - content: this, - perc: 50, - split: GUI.isMobile() ? 'h': 'v', - push: false, - title: options.title - }); - }) - .catch(err => GUI.notify.error(t("info.server_error"))) - .finally(() => this.emit('show')); -}; - -proto.unmount = function() { - return base(this, 'unmount').then(() => { - this._service.clear(); - }) -}; - - -module.exports = TableComponent; - - diff --git a/src/app/gui/tools/vue/tools.js b/src/app/gui/tools/vue/tools.js index a3292173c..affc2253c 100644 --- a/src/app/gui/tools/vue/tools.js +++ b/src/app/gui/tools/vue/tools.js @@ -1,32 +1,7 @@ -import * as vueComponentOptions from 'components/Tools.vue'; -import GUI from 'services/gui'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { base, inherit } = require('utils'); -const Component = require('gui/component/component'); -const ToolsService = require('gui/tools/service'); - -const InternalComponent = Vue.extend(vueComponentOptions); - -function ToolsComponent(options={}) { - base(this, options); - this._service = new ToolsService(options); - this.title = "tools"; - - const internalComponent = new InternalComponent({ - toolsService: this._service - }); - - internalComponent.state = this._service.state; - this.setInternalComponent(internalComponent, { - events: [{name: 'visible'}] - }); - - this._setOpen = function(bool=false) { - this.internalComponent.state.open = bool; - bool && GUI.closeContent(); - } -} - -inherit(ToolsComponent, Component); - -module.exports = ToolsComponent; +import vueComp from 'components/g3w-tools'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/utils/barstack.js b/src/app/gui/utils/barstack.js index 6f557421d..0b00feee4 100644 --- a/src/app/gui/utils/barstack.js +++ b/src/app/gui/utils/barstack.js @@ -1,167 +1,7 @@ -const { resolve, inherit } = require('utils'); -const G3WObject = require('core/g3wobject'); -const Component = require('gui/component/component'); -const Panel = require('gui/panel'); +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -//Barstack Class -// It used to mount panels stack -// on top of each parent -function BarStack() { - this._parent = null; - // barstack state. It store the panels array - this.state = { - contentsdata: [] - } -} - -inherit(BarStack, G3WObject); - -const proto = BarStack.prototype; - -// push componenet on top of parent -proto.push = function(content, options={}) { - // parent identify the DOM element where insert (append o meno) the component/panel - this._parent = options.parent; - // call barstack mount method - return this._mount(content, options); -}; - -// remove last component from stack -proto.pop = function() { - const d = $.Deferred(); - if (this.state.contentsdata.length) { - const content = this.state.contentsdata.slice(-1)[0].content; - this._unmount(content).then(() => { - const content = this.state.contentsdata.pop(); - d.resolve(content) - }) - } else d.resolve(); - return d.promise(); -}; - -// clear all stack -proto.clear = function() { - const d = $.Deferred(); - if (this.state.contentsdata.length) { - let unmountRequests = []; - this.state.contentsdata.forEach((data) => { - unmountRequests.push(this._unmount(data.content)); - }); - $.when(unmountRequests).then(() => { - this.state.contentsdata.splice(0, this.state.contentsdata.length); - d.resolve(); - }); - } - else d.resolve(); - return d.promise(); -}; - -proto.getContentData = function() { - return this.state.contentsdata -}; - -proto.getCurrentContentData = function() { - return this.state.contentsdata[this.state.contentsdata.length - 1]; -}; - -proto.getPreviousContentData = function() { - return this.state.contentsdata[this.state.contentsdata.length - 2]; -}; - -// mount component -proto._mount = function(content, options) { - // check the type of content: - // JQuery type - if (content instanceof jQuery) return this._setJqueryContent(content); - //String - else if (_.isString(content)) { - let jqueryEl = $(content); - if (!jqueryEl.length) jqueryEl = $('
'+content+'
'); - return this._setJqueryContent(jqueryEl); - } - // Vue - else if (content.mount && typeof content.mount == 'function') { - this._checkDuplicateVueContent(content); // if already exist it removed before based on id - return this._setVueContent(content,options) - } - // DOM - else return this._setDOMContent(content); -}; - -// JQuery append jQuery component -proto._setJqueryContent = function(content, options) { - $(this._parent).append(content); - this.state.contentsdata.push({ - content, - options - }); - return resolve(); -}; - -//Append DOM element -proto._setDOMContent = function(content, options) { - this._parent.appendChild(content); - this.state.contentsdata.push({ - content, - options - }); - return resolve(); -}; - -// Mount component to parent -proto._setVueContent = function(content, options={}) { - const d = $.Deferred(); - const append = options.append || false; - content.mount(this._parent, append) - .then(() => { - $(this._parent).localize(); - // Insert the content into the array with the following attributes: - // content: component object - // options: es. title, perc etc ... - this.state.contentsdata.push({ - content, - options - }); - d.resolve(content); - }); - return d.promise(); -}; - -// Check duplicate Vue Content -proto._checkDuplicateVueContent = function(content) { - let idxToRemove = null; - const id = content.getId(); - this.state.contentsdata.forEach((data, idx) => { - if (data.content.getId && (data.content.getId() == id)) idxToRemove = idx; - }); - if (!_.isNull(idxToRemove)) { - const data = this.state.contentsdata[idxToRemove]; - data.content.unmount() - .then(() => this.state.contentsdata.splice(idxToRemove,1)); - } -}; - -// unmount component -proto._unmount = function(content) { - const d = $.Deferred(); - if (content instanceof Component || content instanceof Panel) { - content.unmount() - .then(() => d.resolve()); - } - else { - $(this._parent).empty(); - d.resolve(); - } - return d.promise(); -}; - -proto.forEach = function(cbk) { - this.state.contentsdata.forEach(data => cbk(data.content)); -}; - -// Get lenght / numbero of element stored in stack -proto.getLength = function() { - return this.state.contentsdata.length; -}; - -module.exports = BarStack; +import vueComp from 'core/g3w-barstack'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/utils/utils.js b/src/app/gui/utils/utils.js deleted file mode 100644 index 33cb7fdfa..000000000 --- a/src/app/gui/utils/utils.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - barstack: require('./barstack') -}; \ No newline at end of file diff --git a/src/app/gui/viewport/contentsviewer.js b/src/app/gui/viewport/contentsviewer.js index 666a96c60..8c75b71bc 100644 --- a/src/app/gui/viewport/contentsviewer.js +++ b/src/app/gui/viewport/contentsviewer.js @@ -1,158 +1,7 @@ -import * as vueComponentOptions from 'components/ViewportContentsViewer.vue'; +/** + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 + */ -const { base, inherit } = require('utils'); -const { barstack: Stack } = require('gui/utils/utils'); -const Component = require('gui/component/component'); - -// Internal Component (VUE) of the content of the viewport -const InternalComponent = Vue.extend(vueComponentOptions); - -function ContentsViewerComponent(options={}) { - base(this, options); - this.stack = new Stack(); - this.setService(this); - this.title = "contents"; - this.contentsdata = this.stack.state.contentsdata; - this.state.visible = true; - const internalComponent = new InternalComponent({ - service: this - }); - this.setInternalComponent(internalComponent); - this.internalComponent.state = this.state; -} - -inherit(ContentsViewerComponent, Component); - -const proto = ContentsViewerComponent.prototype; - -proto.setContent = function(options={}) { - const {push=false, content, crumb} = options; - const d = $.Deferred(); - // clean the stack every time, sure to have just one component. - // Use barstack because it handle the logic og mounting component on DOM - if (push) { - this.addContent(content,options) - .then(() => d.resolve(options)); - } else { - // clear stack - this.clearContents() - .then(() => { - this.addContent(content, options) - .then(() => d.resolve(options)); - }) - } - - this.setOpen(true); - return d.promise(); -}; - -proto.addContent = function(content, options={}) { - const d = $.Deferred(); - // parent element is the internal element - options.parent = this.internalComponent.$el; - options.append = true; - const promise = this.stack.push(content, options); - promise.then(() => { - // get stack content - this.contentsdata = this.stack.state.contentsdata; - // update the visibility of the others components - this.updateContentVisibility(); - d.resolve(); - }); - return d.promise(); -}; - -// remove content from stack -proto.removeContent = function() { - this.setOpen(false); - return this.clearContents(); -}; - -// used by viewport.js -proto.popContent = function() { - return this.stack.pop() - .then(() => { - // update the content of contentsdata only after stack is updated - this.contentsdata = this.stack.state.contentsdata; - this.updateContentVisibility(); - }); -}; - -// get component through class -proto.getComponentByClass = function(componentClass) { - let component; - const contentdata = this.stack.getContentData(); - contentdata.forEach(content => { - if (content.content instanceof componentClass) { - component = content.content; - return false - } - }); - return component -}; - -// get component by component id -proto.getComponentById = function(id) { - let component; - const contentdata = this.stack.getContentData(); - contentdata.forEach(content => { - if (content.content.id == id) { - component = content.content; - return false - } - }); - return component -}; - -proto.getContentData = function() { - return this.stack.getContentData(); -}; - -// get current contentdata -proto.getCurrentContentData = function(){ - return this.stack.getCurrentContentData(); -}; - -// get previuos contentdata -proto.getPreviousContentData = function() { - return this.stack.getPreviousContentData(); -}; - -// update visibility of the components of content -proto.updateContentVisibility = function() { - // hide each elements but not the last one - const contentsEls = $(this.internalComponent.$el).children(); - contentsEls.hide(); - contentsEls.last().show(); -}; - -// stack clear because if we want the contentComponente stack -// it has to be empty stack -proto.clearContents = function() { - return this.stack.clear().then(() => this.contentsdata = this.stack.state.contentsdata); -}; - -// Set layout of the content each time -// Parameters are: height and with of the parent content -proto.layout = function(parentWidth, parentHeight) { - const el = $(this.internalComponent.$el); - //run the callback only after that vue state is updated - Vue.nextTick(() => { - const contentsdata = this.stack.state.contentsdata; - // el.parent() is div g3w-view-content - const height = el.parent().height() - - el.siblings('.close-panel-block').outerHeight(true) - - el.siblings('.content_breadcrumb').outerHeight(true) - 10; // margin 10 from bottom - el.height(height); - el.children().first().height(height); - contentsdata.forEach(data => { - //check each componentstored in stack - if (typeof data.content.layout == 'function') { - //call function layout of each component that are stored into the stack - data.content.layout(parentWidth + 0.5, height); - } - }) - }) -}; - -module.exports = ContentsViewerComponent; +import vueComp from 'components/g3w-contentsviewer'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/app/gui/wms/vue/wms.js b/src/app/gui/wms/vue/wms.js index 71835c77c..23aae09f4 100644 --- a/src/app/gui/wms/vue/wms.js +++ b/src/app/gui/wms/vue/wms.js @@ -1,442 +1,7 @@ -import * as vueComponentOptions from 'components/WMS.vue'; -import * as vuePanelComponentOptions from 'components/WMSLayersPanel.vue'; -import { LOCALSTORAGE_EXTERNALWMS_ITEM } from 'app/constant'; -import DataRouterService from 'services/data'; -import ProjectsRegistry from 'store/projects'; -import ApplicationService from 'services/application'; -import GUI from 'services/gui'; -import { base, inherit, uniqueId } from 'utils'; - -const Panel = require('gui/panel'); -const Component = require('gui/component/component'); -const InternalComponent = Vue.extend(vueComponentOptions); -const WMSLayersPanelComponent = Vue.extend(vuePanelComponentOptions); - /** - * ORIGINAL SOURCE: src/app/gui/wms/vue/panel/wmslayerspanel.js@3.8.15 + * @file + * @deprecated do not remove prior to https://github.com/g3w-suite/g3w-client/pull/451 */ -function WmsLayersPanel(options = {}) { - const { service, config } = options; - this.setService(service); - this.id = uniqueId(); - this.title = 'sidebar.wms.panel.title'; - const internal = new WMSLayersPanelComponent({ service, config }); - this.setInternalPanel(internal); - this.unmount = function() { return base(this, 'unmount').then(() => service.clear()); }; -} - -inherit(WmsLayersPanel, Panel); - -/** - * ORIGINAL SOURCE: src/app/gui/wms/service.js@3.8.15 - */ -class Service { - - constructor(options = {}) { - - const { - wmsurls = [] - } = options; - - /** - * Current project id used to store data or get data to current project - */ - this.projectId = ProjectsRegistry.getCurrentProject().getId(); // - - /** - * @FIXME add description - */ - this.panel; - - /** - * @FIXME add description - */ - this.state = { - adminwmsurls: wmsurls, - localwmsurls: [] // array of object {id, url} - }; - - GUI.isReady() - .then(() => { - GUI.getService('map') - .isReady() - .then(async () => { this.state.localwmsurls = await this.loadClientWmsUrls(); }); - }); - - ProjectsRegistry.onafter('setCurrentProject', async (project) => { - this.projectId = project.getId(); - this.state.adminwmsurls = project.wmsurls || []; - }); - - } - - /** - * Getting Wms Urls from local browser storage - */ - async loadClientWmsUrls() { - let data = this.getLocalWMSData(); - - if (undefined === data) { - data = { - urls: [], // unique url for wms - wms: {}, // bject contain url as key and array of layers bind to url - }; - this.updateLocalWMSData(data); - } - - await GUI.isReady(); - - setTimeout(() => { - const map = GUI.getService('map'); - - map.on('remove-external-layer', name => this.deleteWms(name)); - - map.on('change-layer-position-map', ({ id: name, position } = {}) => this.changeLayerData(name, { key: 'position', value: position })); - map.on('change-layer-opacity', ({ id: name, opacity } = {}) => this.changeLayerData(name, { key: 'opacity', value: opacity })); - map.on('change-layer-visibility', ({ id: name, visible } = {}) => this.changeLayerData(name, { key: 'visible', value: visible })); - - // load eventually data - Object.keys(data.wms).forEach(url => { data.wms[url].forEach(config => { this.loadWMSLayerToMap({ url, ...config }) }); }); - }); - - return data.urls; - } - - /** - * Change config of storage layer options as position, opacity - * - * @param { Object } opts - * @param { string } opts.name - * @param opts.config - */ - changeLayerData(name, attribute = {}) { - const data = this.getLocalWMSData(); - Object - .keys(data.wms) - .find((wmsurl) => { - const index = data.wms[wmsurl].findIndex(config => config.name == name); - if (-1 !== index) { - data.wms[wmsurl][index][attribute.key] = attribute.value; - return true; - } - }); - - this.updateLocalWMSData(data); - } - - /** - * Create a common status object - * - * @param { Object } request - * @param request.error - * @param request.added - * - * @returns {{ error, status: string }} - */ - getRequestStatusObject({ - error = false, - added = false, - } = {}) { - return { error, added }; - } - - /** - * Add new WMS url - * - * @param { Object } wms - * @param { string } wms.id - * @param { string } wms.url - * - * @returns {*} - */ - async addNewUrl({ - id, - url, - } = {}) { - const found = this.state.localwmsurls.find(({ id: localid, url: localurl }) => localurl == url || localid == id); - const status = this.getRequestStatusObject({ added: !!found }); - - // skip when url already added - if (found) { - return status; - } - - try { - const response = await this.getWMSLayers(url); - // skip on invalid response - if (!response.result) { - throw 'invalid response'; - } - const data = this.getLocalWMSData(); - this.state.localwmsurls.push({ id, url }); - data.urls = this.state.localwmsurls; - this.updateLocalWMSData(data); - response.wmsurl = url; - this.showWmsLayersPanel(response); - } catch(err) { - console.warn(err); - status.error = true; - } - - return status; - } - - /** - * Delete WMS by name - * - * @param name - */ - deleteWms(name) { - const data = this.getLocalWMSData(); - Object - .keys(data.wms) - .find(wmsurl => { - const index = data.wms[wmsurl].findIndex(config => config.name == name); - - // skip when .. - if (-1 === index) { - return; - } - - /** @TODO add description */ - data.wms[wmsurl].splice(index, 1); - - /** @TODO add description */ - if (0 == data.wms[wmsurl].length) { - delete data.wms[wmsurl]; - } - - return true; - }); - this.updateLocalWMSData(data); - } - - /** - * @param { Object } opts - * @param opts.name - * @param opts.layers - * - * @returns { boolean } WMS is already added (by `name` or `layer` with a specific url) - */ - checkIfWMSAlreadyAdded({ - url, - layers=[], - } = {}) { - const data = this.getLocalWMSData(); - - // wms url is not already added - if (!data.wms[url]) { - return false; - } - - // check if wms layer is already added (by name) - return data.wms[url].some( - ({ layers: addedLayers }) => addedLayers.length === layers.length - ? layers.every((name) => addedLayers.includes(name)) - : undefined - ); - } - - /** - * Delete url from local storage - * @param id - */ - deleteWmsUrl(id) { - this.state.localwmsurls = this.state.localwmsurls .filter(({ id: localid }) => id !== localid); - const data = this.getLocalWMSData(); - data.urls = this.state.localwmsurls; - this.updateLocalWMSData(data); - } - - /** - * Load data from server and show wms layer panel - * - * @param url - * - * @returns { Promise<{ added: boolean, error: boolean }> } - */ - async loadWMSDataAndShowWmsLayersPanel(url) { - const status = this.getRequestStatusObject(); - try { - const response = await this.getWMSLayers(url); - status.error = !response.result; - if (response.result) { - response.wmsurl = url; - this.showWmsLayersPanel(response); - } - } catch(err) { - console.warn(err); - status.error = true; - } - return status; - } - - /** - * show add wms layers to wms panel - * @param config - * @returns {WmsLayersPanel} - */ - showWmsLayersPanel(config={}) { - this.panel = new WmsLayersPanel({ service: this, config }); - this.panel.show(); - return this.panel; - } - - /** - * Get data of wms url from server - * - * @param { string } url - * - * @returns { Promise<{ - * result: boolean, - * info_formats: [], - * layers: [], - * map_formats: [], - * methods: [], - * abstract: null, - * title: null, - * }> } - */ - async getWMSLayers(url) { - // base schema of response - let response = { - result: false, - layers: [], - info_formats: [], // @deprecated since 3.9.0 (inside methods) - abstract: null, - methods: [], // @since 3.9.0 - map_formats: [], // @deprecated since 3.9.0 (inside methods) - title: null - }; - try { - response = await DataRouterService.getData('ows:wmsCapabilities', { inputs: { url }, outputs: false }); - } catch(err) { - console.warn(err); - } - return response; - } - - /** - * Load wms to map - * - * @param { Object } wms - * @param { string } wms.url - * @param { string } wms.name - * @param wms.epsg - * @param wms.position - * @param wms.opacity - * @param wms.visible - * @param wms.layers - * - * @returns {*} - */ - loadWMSLayerToMap({ - url, - name, - epsg, - position, - opacity, - visible = true, - layers = [], - } = {}) { - return GUI.getService('map').addExternalWMSLayer({ url, name, layers, epsg, position, visible, opacity }); - } - - /** - * Check if a layer is already added to map - * - * @param { Object } wms - * @param { string } wms.url - * @param { string } wms.name - * @param wms.epsg - * @param wms.position - * @param wms.methods - * @param wms.layers - * - * @returns { Promise } - */ - async addWMSlayer({ - url, - epsg, - position, - name = `wms_${uniqueId()}`, - layers = [], - opacity = 1, - visible = true, - } = {}) { - const data = this.getLocalWMSData(); - const config = { - url, - name, - layers, - epsg, - position, - visible, - opacity - }; - - if (undefined === data.wms[url]) { - data.wms[url] = [config]; - } else { - data.wms[url].push(config); - } - - this.updateLocalWMSData(data); - - try { - await this.loadWMSLayerToMap(config); - } catch(err) { - console.warn(err); - GUI.getService('map').removeExternalLayer(name); - this.deleteWms(name); - setTimeout(() => { GUI.showUserMessage({ type: 'warning', message: 'sidebar.wms.layer_add_error' }) }); - } - - this.panel.close(); - } - - /** - * Get local storage wms data based on current projectId - * - * @returns {*} - */ - getLocalWMSData() { - const item = ApplicationService.getLocalItem(LOCALSTORAGE_EXTERNALWMS_ITEM); - return item && item[this.projectId]; - } - - /** - * Update local storage data based on changes - * - * @param data - */ - updateLocalWMSData(data) { - const alldata = ApplicationService.getLocalItem(LOCALSTORAGE_EXTERNALWMS_ITEM) || {}; - alldata[this.projectId] = data; - ApplicationService.setLocalItem({ id: LOCALSTORAGE_EXTERNALWMS_ITEM, data: alldata }); - } - - clear() { - this.panel = null; - } - -} - -function WmsComponent(options={}) { - base(this, options); - this._service = new Service(options); - this.title = "WMS"; - const internal = new InternalComponent({ service: this._service }); - internal.state = this._service.state; - this.setInternalComponent(internal); - this._setOpen = function(bool = false) { - this.internalComponent.state.open = bool; - if (bool) { - GUI.closeContent(); - } - }; -} - -inherit(WmsComponent, Component); -module.exports = WmsComponent; +import vueComp from 'components/g3w-wms'; +module.exports = vueComp; \ No newline at end of file diff --git a/src/components/CatalogLayerContextMenu.vue b/src/components/CatalogLayerContextMenu.vue index c38bf7508..3e9bb198d 100644 --- a/src/components/CatalogLayerContextMenu.vue +++ b/src/components/CatalogLayerContextMenu.vue @@ -523,23 +523,32 @@ import LayerOpacityPicker from "components/LayerOpacityPicker.vue"; - import { CatalogEventBus as VM } from 'app/eventbus'; + import { CatalogEventBus as VM } from 'app/eventbus'; import CatalogLayersStoresRegistry from 'store/catalog-layers'; - import ApplicationService from 'services/application'; - import GUI from 'services/gui'; - - const { t } = require('core/i18n/i18n.service'); - const shpwrite = require('shp-write'); - const TableComponent = require('gui/table/vue/table'); - const { downloadFile } = require('utils'); - - - const OFFSETMENU = { - top: 50, - left: 15 - }; + import ApplicationService from 'services/application'; + import GUI from 'services/gui'; + import Table from 'components/Table.vue'; + import Component from 'core/g3w-component'; + import DataRouterService from 'services/data'; + import { coordinatesToGeometry } from 'utils/coordinatesToGeometry'; + + const { t } = require('core/i18n/i18n.service'); + const shpwrite = require('shp-write'); + const { downloadFile, inherit, noop } = require('utils'); + const G3WObject = require('core/g3wobject'); + const { SELECTION_STATE } = require('core/layers/layer'); + + const PAGELENGTHS = [10, 25, 50]; + const OFFSETMENU = { top: 50, left: 15 }; + + /** + * Create a unique feature key + */ + function _createFeatureKey(values) { + return values.join('__'); + } - export default { + const SFC = { name: 'Cataloglayermenu', props: { @@ -923,15 +932,46 @@ GUI.closeContent(); const layer = CatalogLayersStoresRegistry.getLayerById(layerId); this.layerMenu.loading.data_table = true; - const tableContent = new TableComponent({ layer, formatter: 1 }); - tableContent.on('show', () => { + + // table content + const comp = new Component({ + id: 'openattributetable', + service: new TableService({ layer, formatter: 1 }), + internalComponent: new (Vue.extend(Table))({ service: comp._service }), + }); + + comp.layout = () => { comp.internalComponent.reloadLayout(); }; + comp._service.on('redraw', ()=> { comp.layout(); }); + comp.on('unmount', () => { comp._service.clear(); }) + + // overwrite show method + comp.show = (opts = {}) => { + // close all sidebar open component + GUI.closeOpenSideBarComponent(); + comp._service + .getData({ firstCall: true }) + .then(() => { + GUI.showContent({ + content: comp, + perc: 50, + split: GUI.isMobile() ? 'h': 'v', + push: false, + title: opts.title + }); + }) + .catch(e => GUI.notify.error(t("info.server_error"))) + .finally(() => comp.emit('show')); + }; + + comp.on('show', () => { if (this.isMobile()) { GUI.hideSidebar(); } this.layerMenu.loading.data_table = false; this._hideMenu(); }); - tableContent.show({ title: layer.getName() }); + + comp.show({ title: layer.getName() }); }, startEditing() { @@ -1172,6 +1212,776 @@ }, }; + + /** + * TableService Class + * + * ORIGINAL SOURCE: src/app/gui/table/tableservice.js@v3.9.3 + * + * @param options.layer + * @param options.formatter + * + * @constructor + */ + function TableService(options = {}) { + + /** + * Number of pages + */ + this.currentPage = 0; + + /** + * @FIXME add description + */ + this.layer = options.layer; + + /** + * @FIXME add description + */ + this.formatter = options.formatter; + + /** + * @FIXME add description + */ + this.allfeaturesnumber = undefined; + + /** + * @FIXME add description + */ + this.nopaginationsfilter = []; + + /** + * @FIXME add description + */ + this.selectedfeaturesfid = this.layer.getSelectionFids(); + + /** + * Whether layer has geometry + */ + this.geolayer = this.layer.isGeoLayer(); + + /** + * @FIXME add description + */ + this.relationsGeometry = this._relationsGeometry(); + + /** + * @FIXME add description + */ + this.projection = this.geolayer ? this.layer.getProjection() : null; + + /** + * @FIXME add description + */ + this.mapService = GUI.getService('map'); + + /** + * @FIXME add description + */ + this.getAll = false; + + /** + * @FIXME add description + */ + this.paginationfilter = false; + + /** + * @FIXME add description + */ + this.mapBBoxEventHandlerKey = { + key: null, + cb: null + }; + + + // bind context on event listeners + this.clearAllSelection = this.clearAllSelection.bind(this); + this.filterChangeHandler = this.filterChangeHandler.bind(this); + this.onGUIContent = this.onGUIContent.bind(this); + + /** + * @FIXME add description + */ + this.state = { + pageLengths: PAGELENGTHS, + pageLength: this.layer.getAttributeTablePageLength() || PAGELENGTHS[0], + features: [], + title: this.layer.getTitle(), + headers: this.getHeaders(), + geometry: true, + loading: false, + allfeatures: 0, + pagination: !this.getAll, + selectAll: false, + nofilteredrow: false, + tools: { + geolayer: { + show: this.geolayer, + active: false, + in_bbox: undefined, + }, + show: false, + filter: this.layer.state.filter + } + }; + + /** + * Pagination filter features + */ + this._async = { + state: false, + fnc: noop + }; + + GUI.onbefore('setContent', this.onGUIContent); + this.layer.on('unselectionall', this.clearAllSelection); + this.layer.on('filtertokenchange', this.filterChangeHandler); + }; + + inherit(TableService, G3WObject); + + const proto = TableService.prototype; + + /** + * @since 3.9.0 + */ + proto._relationsGeometry = function() { + + // layer has geometry + if (this.geolayer) { + return []; + } + + const relations = []; + + this.layer + .getRelations() + .getArray() + .forEach(relation => { + const layer = CatalogLayersStoresRegistry.getLayerById(relation.getFather()); // get project layer + if ( + this.layer.getId() !== relation.getFather() && // current layer is not child layer of relation + layer.isGeoLayer() // relation layer has geometry + ) { + relations.push({ + layer, + father_fields: relation.getFatherField(), // NB: since g3w-admin@v3.7.0 this is an Array value. + fields: relation.getChildField(), // NB: since g3w-admin@v3.7.0 this is an Array value. + features: {}, + }) + } + }); + + return relations; + }; + + /** + * @since 3.9.0 + */ + proto.clearAllSelection = function() { + this.state.features.forEach(feature => feature.selected = false); + this.state.tools.show = false; + this.state.selectAll = false; + }; + + /** + * @since 3.9.0 + * + * @param { Object } opts + * @param { string } opts.type + * + * @fires redraw when `opts.type` in_bbox filter (or not select all) + */ + proto.filterChangeHandler = async function ({ type } = {}) { + this.allfeaturesnumber = undefined; + if (type === 'in_bbox' || !this.selectedfeaturesfid.has(SELECTION_STATE.ALL)) { + this.emit('redraw', this.state.pagination ? [] : await this.reloadData()); + } + }; + + /** + * @since 3.9.0 + */ + proto.onGUIContent = function(options) { + this._async.state = (100 === options.perc); + }; + + proto.toggleFilterToken = async function() { + await this.layer.toggleFilterToken(); + }; + + /** + * first value = `null` for DataTable purpose (used to add a custom input selector) + */ + proto.getHeaders = function() { + return [null, ...this.layer.getTableHeaders()]; + }; + + /** + * DataTable pagination + */ + proto.setDataForDataTable = function() { + const data = []; + this.state.features.forEach(feature => { + const attributes = feature.attributes ? feature.attributes : feature.properties; + const values = [null]; + this.state.headers.forEach(header => { + if (header) { + header.value = attributes[header.name]; + values.push(header.value); + // header.label = undefined; // removes label. + } + }); + data.push(values) + }); + return data; + }; + + proto.addRemoveSelectedFeature = function(feature) { + feature.selected = !feature.selected; + + const selected = this.selectedfeaturesfid; + const filter = this.nopaginationsfilter; + const count = this.allfeaturesnumber; + + const select_all = this.state.selectAll; + const has_pagination = this.state.pagination; + const features = this.state.features; + const is_active = this.state.tools && this.state.tools.filter && this.state.tools.filter.active + + const is_exclude = !select_all && selected.has(SELECTION_STATE.EXCLUDE); + const is_default = !select_all && !is_exclude; + + /** @FIXME add description */ + if (select_all) { + this.state.selectAll = false; + } + + /** @FIXME add description */ + if (select_all) { + this.layer.excludeSelectionFid(feature.id, has_pagination); + } + + /** @FIXME add description */ + if (is_exclude || is_default) { + this.layer[feature.selected ? 'includeSelectionFid' : 'excludeSelectionFid'](feature.id); + } + + /** @FIXME add description */ + if (!is_active && ( (is_exclude && 1 === selected.size) || (is_default && selected.size === count)) ) { + this.layer.setSelectionFidsAll(); + } + + /** @FIXME add description */ + if (is_exclude && 1 !== selected.size && selected.size === features.length + 1) { + this.layer.clearSelectionFids(); + } + + /** @FIXME add description */ + this.state.tools.show = selected.size > 0; + + /** @FIXME add description */ + if ( + (is_exclude && 1 === selected.size) || + (is_default && selected.size === count) || + (!has_pagination && filter.length && filter.length === features.filter(f => f.selected).length) + ) { + this.state.selectAll = true; + } + + }; + + proto.createFeatureForSelection = function(feature) { + return { + attributes: feature.attributes ? feature.attributes : feature.properties, + geometry: this._returnGeometry(feature), + } + }; + + proto.getAllFeatures = function(params) { + GUI.setLoadingContent(true); + return new Promise((resolve, reject) => { + this.layer + .getDataTable(params || {}) + .then(data => { + const is_valid = this.geolayer && data.features; + + if (is_valid && !params) { + const loaded_features = this.state.features.map(f => f.id); + data.features.forEach(f => { + if (-1 === loaded_features.indexOf(f.id) && f.geometry) { + this.layer.addOlSelectionFeature({ + id: f.id, + feature: this.createFeatureForSelection(f), + }); + } + }); + this.getAll = true; + } + + if (is_valid) { + resolve(data.features); + } + }) + .fail(() => reject()) + .always(() => GUI.setLoadingContent(false)); + }); + }; + + proto.switchSelection = async function() { + const has_pagination = this.state.pagination; + const filter = this.nopaginationsfilter; + const filtered = !has_pagination && filter.length ? [] : undefined; + let selected = false; + + // pagination + if (has_pagination) { + this.state.features.forEach(f => { + f.selected = !f.selected; + selected = f.selected; + }); + } + + if (has_pagination && !this.getAll) { + await this.getAllFeatures(); + } + + this.state.selectAll = (has_pagination && this.paginationfilter) ? selected : this.state.selectAll; + + // filtered + if (!has_pagination && filter.length) { + this.state.features.forEach((f, i) => { + if (-1 !== filter.indexOf(i)) { + filtered.push(f); + } + f.selected = !f.selected; + this.layer[f.selected ? 'includeSelectionFid' : 'excludeSelectionFid' ](f.id); + selected = selected || f.selected; + }); + this.state.tools.show = selected; + } + + // no filter + if (!has_pagination && !filter.length) { + this.state.features.forEach(f => { f.selected = !f.selected; }); + } + + if (has_pagination || !filter.length) { + this.layer.invertSelectionFids(); + } + + if (!has_pagination) { + this.checkSelectAll(filtered); + } + + if (has_pagination || !filter.length) { + this.state.tools.show = this.selectedfeaturesfid.size > 0; + } + + }; + + proto.clearLayerSelection = function() { + this.layer.clearSelectionFids(); + }; + + /** + * Called when a selected feature is checked + * + * @returns {Promise} + */ + proto.selectAllFeatures = async function() { + + // set inverse of selectAll + this.state.selectAll = !this.state.selectAll; + + const has_pagination = this.state.pagination; + const filter = this.nopaginationsfilter; + let selected = false; + + // filtered + if (!has_pagination && filter.length) { + this.state.features.forEach((f, i) =>{ + if (-1 !== filter.indexOf(i)) { + f.selected = this.state.selectAll; + this.layer[f.selected ? 'includeSelectionFid': 'excludeSelectionFid'](f.id); + selected = selected || f.selected; + } + }); + this.state.tools.show = selected; + } + + // no filter + if (!has_pagination && !filter.length) { + this.state.tools.show = this.state.selectAll; + this.layer[this.state.selectAll ? 'setSelectionFidsAll': 'clearSelectionFids'](); + this.state.features.forEach(f => f.selected = this.state.selectAll); + } + + // filtered pagination + if (has_pagination && this.paginationfilter && this.state.featurescount >= this.state.allfeatures) { + this.state.features.forEach(f => { + f.selected = this.state.selectAll; + this.layer[f.selected ? 'includeSelectionFid': 'excludeSelectionFid'](f.id); + }); + } + + if (has_pagination && this.paginationfilter && this.state.featurescount < this.state.allfeatures) { + const features = await this.getAllFeatures({ + search: this.paginationParams.search, + ordering: this.paginationParams.ordering, + formatter: this.paginationParams.formatter, + in_bbox: this.paginationParams.in_bbox, + }); + features.forEach(f => { + if (!this.getAll && this.geolayer && f.geometry) { + this.layer.addOlSelectionFeature({ + id: f.id, + feature: this.createFeatureForSelection(f) + }); + } + this.layer[this.state.selectAll ? 'includeSelectionFid' : 'excludeSelectionFid'](f.id); + }) + } + + if (has_pagination) { + this.state.features.forEach(f => f.selected = this.state.selectAll); + } + + if (has_pagination && !this.paginationfilter && !this.getAll) { + await this.getAllFeatures(); + } + + if (has_pagination && !this.paginationfilter) { + this.layer[this.state.selectAll ? 'setSelectionFidsAll': 'clearSelectionFids'](); + } + + if (has_pagination) { + this.state.tools.show = this.state.selectAll || this.selectedfeaturesfid.size > 0; + } + + }; + + /** + * Set filtered features + * + * @param index features index + */ + proto.setFilteredFeature = function(index) { + const filter = this.nopaginationsfilter = index; + if (0 === index.length || index.length === this.allfeaturesnumber) { + this.checkSelectAll(); + } else { + this.checkSelectAll(filter.map(i => this.state.features[i])); + } + }; + + proto.setAttributeTablePageLength = function(length) { + this.layer.setAttributeTablePageLength(length); + }; + + /** + * Get DataTable layer + * + * @param data.start + * @param data.order + * @param data.length + * @param data.columns + * @param data.search + * @param data.firstCall + * + * @returns {Promise<{{ data: [], recordsTotal: number, recordsFiltered: number }}>} + */ + proto.getData = function({ + start = 0, + order = [], + length = this.state.pageLength, + columns = [], + search = { value: null }, + firstCall = false + } = {}) { + + // reset features before load + GUI.setLoadingContent(true); + + this.setAttributeTablePageLength(length); + + return new Promise((resolve, reject) => { + + // skip when .. + if (!this.state.headers.length) { + resolve({ + data: [], + recordsTotal: 0, + recordsFiltered: 0 + }); + return; + } + + let searchText = search.value && search.value.length > 0 ? search.value : null; + + this.state.features.splice(0); + + if (!order.length) { + order.push({ + column: 1, + dir: 'asc', + }); + } + + const ordering = ('asc' === order[0].dir ? '' : '-') + this.state.headers[order[0].column].name; + + this.currentPage = (start === 0 || (this.state.pagination && this.state.tools.filter.active)) ? 1 : (start/length) + 1; + + const in_bbox = this.state.tools.geolayer.in_bbox; + + const field = this.state.pagination + ? columns.filter(c => c.search && c.search.value).map(c => `${c.name}|ilike|${c.search.value}|and`).join(',') + : undefined; + + this.paginationParams = { + field: field || undefined, + page: this.currentPage, + page_size: length, + search: searchText, + in_bbox, + formatter: this.formatter, + ordering + }; + + this.layer + .getDataTable( + this.state.pagination + ? this.paginationParams + : ({ ordering, in_bbox, formatter: this.formatter }) + ) + .then(data => { + const { features = [] } = data; + + this.state.allfeatures = data.count || this.state.features.length; + this.state.featurescount = features.length; + this.allfeaturesnumber = (undefined === this.allfeaturesnumber ? data.count : this.allfeaturesnumber); + this.paginationfilter = (data.count !== this.allfeaturesnumber); + this.state.pagination = firstCall + ? this.state.tools.filter.active || features.length < this.allfeaturesnumber + : this.state.pagination; + + this.addFeatures(features); + + resolve({ + data: this.setDataForDataTable(), + recordsFiltered: data.count, + recordsTotal: data.count + }); + }) + .fail(err => { GUI.notify.error(t("info.server_error")); reject(err); }) + .always(() => { GUI.setLoadingContent(false); }) + }); + }; + + proto.setInBBoxParam = function() { + const { geolayer } = this.state.tools; + geolayer.in_bbox = geolayer.active ? this.mapService.getMapBBOX().join(',') : undefined; + }; + + proto.resetMapBBoxEventHandlerKey = function() { + const listener = this.mapBBoxEventHandlerKey; + ol.Observable.unByKey(listener.key); + listener.key = null; + listener.cb = null; + }; + + proto.getDataFromBBOX = async function() { + const { geolayer } = this.state.tools; + + geolayer.active = !geolayer.active; + + const is_active = geolayer.active; + const listener = this.mapBBoxEventHandlerKey; + + if (is_active && this.state.pagination) { + listener.cb = () => { + this.setInBBoxParam(); + this.emit('ajax-reload'); + }; + } + + if (is_active && !this.state.pagination) { + listener.cb = async () => { + this.setInBBoxParam(); + this.filterChangeHandler({ type: 'in_bbox' }); + }; + } + + if (is_active) { + listener.key = this.mapService.getMap().on('moveend', listener.cb); + } + + if (listener.cb) { + listener.cb(); + } + + if (!is_active) { + this.resetMapBBoxEventHandlerKey(); + } + }; + + proto.addFeature = function(feature) { + const tableFeature = { + id: feature.id, + selected: this.layer.hasSelectionFid(feature.id), + attributes: feature.attributes || feature.properties, + geometry: this.geolayer && feature.geometry || undefined + }; + + const has_geom = this.geolayer && feature.geometry; + const selection = has_geom && this.layer.getOlSelectionFeature(feature.id); + + if (has_geom && !selection) { + this.layer.addOlSelectionFeature({ + id: feature.id, + feature: this.createFeatureForSelection(feature) + }); + } + + this.state.features.push(tableFeature); + }; + + proto.checkSelectAll = function(features = this.state.features) { + this.state.selectAll = ( + this.selectedfeaturesfid.has(SELECTION_STATE.ALL) || + (features.length && features.reduce((selectAll, f) => selectAll && f.selected, true)) + ); + }; + + proto.addFeatures = function(features=[]) { + features.forEach(f => this.addFeature(f)); + this.state.tools.show = this.layer.getFilterActive() || this.selectedfeaturesfid.size > 0; + this.checkSelectAll(); + }; + + proto.reloadData = async function(pagination=false) { + this.state.features.splice(0); + this.state.pagination = pagination; + const { data = [] } = await this.getData(); + return data; + }; + + proto._setLayout = function() { + //TODO + }; + + proto._returnGeometry = function(feature) { + if (feature.attributes) return feature.geometry; + if (feature.geometry) return coordinatesToGeometry(feature.geometry.type, feature.geometry.coordinates); + }; + + proto.zoomAndHighLightFeature = function(feature, zoom = true) { + // async highlight + if (feature.geometry && this._async.state) { + this._async.fnc = this.mapService.highlightGeometry.bind(mapService, feature.geometry, { zoom }); + } + // sync highlight + if (feature.geometry && !this._async.state) { + this.mapService.highlightGeometry(feature.geometry , { zoom }); + } + }; + + /** + * Zoom to eventually features relation + */ + proto.zoomAndHighLightGeometryRelationFeatures = async function(feature, zoom = true) { + + // skip when there are no relation features geometry + if (!this.relationsGeometry.length > 0) { + return; + } + + const features = []; + const promises = []; + const field_values = []; // check if add or not + + this.relationsGeometry + .forEach(({ + layer, + father_fields, + fields, + features + }) => { + const values = fields.map(f => feature.attributes[f]); + + field_values.push(values); + + let promise; + + if (zoom && undefined === features[k]) { + promise = DataRouterService + .getData('search:features', { + inputs: { + layer, + formatter: 1, + search_endpoint: 'api', + filter: ( + father_fields + .reduce((filter, field, index) => { + filter = `${filter}${index > 0 ? '|AND,' : ''}${field}|eq|${encodeURIComponent(values[index])}` + return filter; + }, '') + ), + }, + outputs: false, // just a request not show on result + }); + } + + promises.push(promise); + }); + + (await Promise.allSettled(promises)) + .forEach(({ + status, + value + }, index) => { + if ('fulfilled' === status) { + + const relation = this.relationsGeometry[index]; + const k = _createFeatureKey(field_values[index]); + const data = value && value.data[0]; + + if (undefined === relation.features[k]) { + relation.features[k] = data && data.features || []; + } + + relation.features[k].forEach(f => features.push(f)); + + } + }); + + if (zoom) { + this.mapService.zoomToFeatures(features, { highlight: true }); + } else { + this.mapService.highlightFeatures(features); + } + + }; + + proto.clear = function() { + this.layer.off('unselectionall', this.clearAllSelection); + this.layer.off('filtertokenchange', this.filterChangeHandler); + + this.resetMapBBoxEventHandlerKey(); + + this.allfeaturesnumber = null; + this.mapService = null; + + if (this._async.state) { + setTimeout(() => { + this._async.fnc(); + this._async.state = false; + this._async.fnc = noop; + }); + } + }; + + export default SFC; \ No newline at end of file diff --git a/src/components/TableSelectRow.vue b/src/components/TableSelectRow.vue deleted file mode 100644 index fa5037cad..000000000 --- a/src/components/TableSelectRow.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/index.js b/src/components/index.js index 73d76c798..23b9d3912 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -104,9 +104,6 @@ import SpatialBookMarkItem from './SpatialBookMarkItem.vue'; import SpatialBookMarks from './SpatialBookMarks.vue'; import StreetView from './StreetView.vue'; import Table from './Table.vue'; -import TableBody from './TableBody.vue'; -import TableLink from './TableLink.vue'; -import TableSelectRow from './TableSelectRow.vue'; import TableToolbar from './TableToolbar.vue'; import Tool from './Tool.vue'; import Tools from './Tools.vue'; @@ -228,9 +225,6 @@ export { SpatialBookMarks, StreetView, Table, - TableBody, - TableLink, - TableSelectRow, TableToolbar, Tool, Tools, From a6674f9f8c03a0772723bc39ea9915147c22e88d Mon Sep 17 00:00:00 2001 From: Raruto Date: Fri, 23 Feb 2024 14:40:01 +0100 Subject: [PATCH 019/377] spacing --- src/components/Table.vue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Table.vue b/src/components/Table.vue index af144c291..74bfb5b14 100644 --- a/src/components/Table.vue +++ b/src/components/Table.vue @@ -21,7 +21,8 @@ style = "height: 25px; min-width: 40px; padding: 2px;" class = "form-control column-search" @keyup = "changeColumn($event, i)" - :placeholder = "header.name"/> + :placeholder = "header.name" + /> @@ -32,7 +33,8 @@ id = "attribute_table_select_all_rows" :checked = "state.selectAll" class = "magic-checkbox" - :disabled = "state.nofilteredrow || state.features.length === 0"> + :disabled = "state.nofilteredrow || state.features.length === 0" + >