diff --git a/AppBuilder/core b/AppBuilder/core index a740ae92..f74c2b72 160000 --- a/AppBuilder/core +++ b/AppBuilder/core @@ -1 +1 @@ -Subproject commit a740ae923955823569f86421f4bc29b107f4fbc3 +Subproject commit f74c2b72a15d9f6b3b7a9cbb64f9af683c980e85 diff --git a/AppBuilder/platform/plugins/included/index.js b/AppBuilder/platform/plugins/included/index.js index cdc4f0a9..b7b4ddda 100644 --- a/AppBuilder/platform/plugins/included/index.js +++ b/AppBuilder/platform/plugins/included/index.js @@ -12,6 +12,7 @@ import viewLabel from "./view_label/FNAbviewlabel.js"; import viewLayout from "./view_layout/FNAbviewlayout.js"; import viewList from "./view_list/FNAbviewlist.js"; import viewPdfImporter from "./view_pdfImporter/FNAbviewpdfimporter.js"; +import viewPivot from "./view_pivot/FNABViewPivot.js"; import viewTab from "./view_tab/FNAbviewtab.js"; import viewText from "./view_text/FNAbviewtext.js"; @@ -30,6 +31,7 @@ const AllPlugins = [ viewLayout, viewList, viewPdfImporter, + viewPivot, viewTab, viewText, ]; diff --git a/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivot.js b/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivot.js new file mode 100644 index 00000000..b196b7ec --- /dev/null +++ b/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivot.js @@ -0,0 +1,154 @@ +import FNABViewPivotComponent from "./FNABViewPivotComponent.js"; + +// FNAbviewpivot Web +// A web side import for an ABView. +// +export default function FNABViewPivot({ + /*AB,*/ + ABViewWidgetPlugin, + ABViewComponentPlugin, + ABViewContainer, +}) { + const ABAbviewpivotComponent = FNABViewPivotComponent({ + AB, + ABViewComponentPlugin, + }); + + const ABViewPivotPropertyComponentDefaults = { + dataviewID: null, + removeMissed: 0, + totalColumn: 0, + separateLabel: 0, + min: 0, + max: 0, + height: 0, + }; + + const ABViewDefaults = { + key: "pivot", // {string} unique key for this view + icon: "cube", // {string} fa-[icon] reference for this view + labelKey: "Pivot", // {string} the multilingual label key for the class label + }; + + class ABViewPivotCore extends ABViewWidgetPlugin { + constructor(values, application, parent, defaultValues) { + super(values, application, parent, defaultValues || ABViewDefaults); + } + + static common() { + return ABViewDefaults; + } + + static defaultValues() { + return ABViewPivotPropertyComponentDefaults; + } + + /// + /// Instance Methods + /// + + /** + * @method fromValues() + * + * initialze this object with the given set of values. + * @param {obj} values + */ + fromValues(values) { + super.fromValues(values); + + // Convert to boolean + this.settings.removeMissed = JSON.parse( + this.settings.removeMissed || + ABViewPivotPropertyComponentDefaults.removeMissed + ); + this.settings.totalColumn = JSON.parse( + this.settings.totalColumn || + ABViewPivotPropertyComponentDefaults.totalColumn + ); + this.settings.separateLabel = JSON.parse( + this.settings.separateLabel || + ABViewPivotPropertyComponentDefaults.separateLabel + ); + this.settings.min = JSON.parse( + this.settings.min || ABViewPivotPropertyComponentDefaults.min + ); + this.settings.max = JSON.parse( + this.settings.max || ABViewPivotPropertyComponentDefaults.max + ); + + if ( + this.settings.structure && + typeof this.settings.structure == "string" + ) + this.settings.structure = JSON.parse(this.settings.structure); + + // "0" -> 0 + this.settings.height = parseInt( + this.settings.height || ABViewPivotPropertyComponentDefaults.height + ); + } + + /** + * @method toObj() + * + * properly compile the current state of this ABViewLabel instance + * into the values needed for saving. + * + * @return {json} + */ + toObj() { + var obj = super.toObj(); + + obj.views = []; + obj.settings = obj.settings || {}; + + if (this.settings.structure) + obj.settings.structure = JSON.stringify(this.settings.structure); + + return obj; + } + + /** + * @method componentList + * return the list of components available on this view to display in the editor. + */ + componentList() { + return []; + } + } + + return class ABViewPivot extends ABViewPivotCore { + /** + * @method getPluginKey + * return the plugin key for this view. + * @return {string} plugin key + */ + static getPluginKey() { + return this.common().key; + } + + /** + * @method component() + * return a UI component based upon this view. + * @return {obj} UI component + */ + component(parentId) { + return new ABAbviewpivotComponent(this, parentId); + } + + constructor(values, application, parent, defaultValues) { + super(values, application, parent, defaultValues); + } + + warningsEval() { + super.warningsEval(); + + let DC = this.datacollection; + if (!DC) { + this.warningsMessage( + `can't resolve it's datacollection[${this.settings.dataviewID}]` + ); + } + } + }; +} diff --git a/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivotComponent.js b/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivotComponent.js new file mode 100644 index 00000000..0b9dd693 --- /dev/null +++ b/AppBuilder/platform/plugins/included/view_pivot/FNABViewPivotComponent.js @@ -0,0 +1,193 @@ +export default function FNAbviewpivotComponent({ + AB, + ABViewComponentPlugin, +}) { + return class ABAbviewpivotComponent extends ABViewComponentPlugin { + constructor(baseView, idBase, ids) { + super( + baseView, + idBase || `ABViewPivot_${baseView.id}`, + Object.assign({ pivot: "" }, ids) + ); + + // refresh the widget by id. + this._handler_refreshPivot = () => { + const ids = this.ids; + $$(ids.pivot)?.refresh?.(); + }; + } + + async init(AB) { + await super.init(AB); + + const dc = this.datacollection; + if (!dc) return; + + dc.removeListener("initializedData", this._handler_refreshPivot); + dc.on("initializedData", this._handler_refreshPivot); + + dc.removeListener("loadData", this._handler_refreshPivot); + dc.on("loadData", this._handler_refreshPivot); + } + + /** + * Remove DC listeners + */ + detatch() { + const dc = this.datacollection; + if (!dc) return; + + dc.removeListener("initializedData", this._handler_refreshPivot); + dc.removeListener("loadData", this._handler_refreshPivot); + } + + ui() { + const ids = this.ids; + const ABFieldCalculate = AB.Class.ABFieldManager.fieldByKey("calculate"); + const ABFieldNumber = AB.Class.ABFieldManager.fieldByKey("number"); + const ABFieldFormula = AB.Class.ABFieldManager.fieldByKey("formula"); + + const self = this; + const settings = this.settings; + + const uiPivot = { + id: ids.pivot, + view: "pivot", + readonly: true, + removeMissed: settings.removeMissed, + totalColumn: settings.totalColumn, + separateLabel: settings.separateLabel, + min: settings.min, + max: settings.max, + height: settings.height, + fields: this._getFields(), + format: (value) => { + const decimalPlaces = settings.decimalPlaces ?? 2; + + return value && value != "0" + ? parseFloat(value).toFixed(decimalPlaces || 0) + : value; + }, + override: new Map([ + [ + pivot.services.Backend, + class MyBackend extends pivot.services.Backend { + async data() { + const dc = self.datacollection; + if (!dc) return webix.promise.resolve([]); + + const object = dc.datasource; + if (!object) return webix.promise.resolve([]); + + await dc.waitReady(); + + const data = dc.getData(); + const dataMapped = data.map((d) => { + const result = {}; + + object.fields().forEach((f) => { + if ( + f instanceof ABFieldCalculate || + f instanceof ABFieldFormula || + f instanceof ABFieldNumber + ) + result[f.columnName] = d[f.columnName]; + else result[f.columnName] = f.format(d); + }); + + return result; + }); + + return webix.promise.resolve(dataMapped); + } + }, + ], + [ + pivot.views.table, + class CustomTable extends pivot.views.table { + /** + * Webix Pivot UpdateTable uses `if (data.totalColumn)`; loadError() + * returns totalColumn: [] which is truthy with header: [], causing + * data.header[last].id to throw. Strip totalColumn when header empty. + */ + UpdateTable(data) { + if ( + data && + !data.$ready && + data.totalColumn && + !data.header?.length + ) { + data = { ...data }; + delete data.totalColumn; + } + return super.UpdateTable(data); + } + + CellFormat(value) { + const decimalPlaces = settings.decimalPlaces ?? 2; + if (!value) value = value === 0 ? "0" : ""; + return value + ? parseFloat(value).toFixed(decimalPlaces) + : value; + } + }, + ], + ]), + }; + + if (settings.structure) uiPivot.structure = settings.structure; + + const _ui = super.ui([uiPivot]); + delete _ui.type; + + return _ui; + } + + _getFields() { + const dc = this.datacollection; + if (!dc) return []; + + const object = dc.datasource; + if (!object) return []; + + const fields = object.fields().map((f) => { + let fieldType = "text"; + + switch (f.key) { + case "calculate": + case "formula": + case "number": + fieldType = "number"; + break; + case "date": + case "datetime": + fieldType = "date"; + break; + } + + return { + id: f.columnName, + value: f.label, + type: fieldType, + }; + }); + + return fields; + } + + async onShow() { + const ids = this.ids; + super.onShow(); + + const dc = this.datacollection; + if (!dc) return; + + const object = dc.datasource; + if (!object) return; + + await dc.waitReady(); + + $$(ids.pivot)?.refresh?.(); + } + }; +} diff --git a/AppBuilder/platform/views/ABViewPivot.js b/AppBuilder/platform/views/ABViewPivot.js deleted file mode 100644 index e18cec95..00000000 --- a/AppBuilder/platform/views/ABViewPivot.js +++ /dev/null @@ -1,30 +0,0 @@ -const ABViewPivotCore = require("../../core/views/ABViewPivotCore"); -const ABViewPivotComponent = require("./viewComponent/ABViewPivotComponent"); - -let L = (...params) => AB.Multilingual.label(...params); - -module.exports = class ABViewPivot extends ABViewPivotCore { - constructor(values, application, parent, defaultValues) { - super(values, application, parent, defaultValues); - } - - /** - * @method component() - * return a UI component based upon this view. - * @return {obj} UI component - */ - component() { - return new ABViewPivotComponent(this); - } - - warningsEval() { - super.warningsEval(); - - let DC = this.datacollection; - if (!DC) { - this.warningsMessage( - `can't resolve it's datacollection[${this.settings.dataviewID}]` - ); - } - } -}; diff --git a/AppBuilder/platform/views/viewComponent/ABViewPivotComponent.js b/AppBuilder/platform/views/viewComponent/ABViewPivotComponent.js deleted file mode 100644 index 3506be81..00000000 --- a/AppBuilder/platform/views/viewComponent/ABViewPivotComponent.js +++ /dev/null @@ -1,128 +0,0 @@ -const ABViewComponent = require("./ABViewComponent").default; -const ABFieldCalculate = require("../../dataFields/ABFieldCalculate"); -const ABFieldFormula = require("../../dataFields/ABFieldFormula"); -const ABFieldNumber = require("../../dataFields/ABFieldNumber"); -/* global pivot */ -module.exports = class ABViewPivotComponent extends ABViewComponent { - constructor(baseView, idBase, ids) { - super( - baseView, - idBase || `ABViewPivot_${baseView.id}`, - Object.assign({ pivot: "" }, ids) - ); - } - - ui() { - const self = this; - const settings = this.settings; - const uiPivot = { - id: this.ids.pivot, - view: "pivot", - readonly: true, - removeMissed: settings.removeMissed, - totalColumn: settings.totalColumn, - separateLabel: settings.separateLabel, - min: settings.min, - max: settings.max, - height: settings.height, - fields: this._getFields(), - format: (value) => { - const decimalPlaces = settings.decimalPlaces ?? 2; - - return value && value != "0" - ? parseFloat(value).toFixed(decimalPlaces || 0) - : value; - }, - override: new Map([ - [ - pivot.services.Backend, - class MyBackend extends pivot.services.Backend { - async data() { - const dc = self.datacollection; - if (!dc) return webix.promise.resolve([]); - - const object = dc.datasource; - if (!object) return webix.promise.resolve([]); - - switch (dc.dataStatus) { - case dc.dataStatusFlag.notInitial: - await dc.loadData(); - break; - } - - const data = dc.getData(); - const dataMapped = data.map((d) => { - const result = {}; - - object.fields().forEach((f) => { - if ( - f instanceof ABFieldCalculate || - f instanceof ABFieldFormula || - f instanceof ABFieldNumber - ) - result[f.columnName] = d[f.columnName]; - else result[f.columnName] = f.format(d); - }); - - return result; - }); - - return webix.promise.resolve(dataMapped); - } - }, - ], - [ - pivot.views.table, - class CustomTable extends pivot.views.table { - CellFormat(value) { - const decimalPlaces = settings.decimalPlaces ?? 2; - if (!value) value = value === 0 ? "0" : ""; - return value - ? parseFloat(value).toFixed(decimalPlaces) - : value; - } - }, - ], - ]), - }; - - if (settings.structure) uiPivot.structure = settings.structure; - - const _ui = super.ui([uiPivot]); - delete _ui.type; - - return _ui; - } - - _getFields() { - const dc = this.datacollection; - if (!dc) return []; - - const object = dc.datasource; - if (!object) return []; - - const fields = object.fields().map((f) => { - let fieldType = "text"; - - switch (f.key) { - case "calculate": - case "formula": - case "number": - fieldType = "number"; - break; - case "date": - case "datetime": - fieldType = "date"; - break; - } - - return { - id: f.columnName, - value: f.label, - type: fieldType, - }; - }); - - return fields; - } -};