diff --git a/src/plugins/index.js b/src/plugins/index.js index 02b8afe..44e7aad 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -4,6 +4,10 @@ import viewCommentProperties from "./web_view_comment/FNAbviewcomment.js"; import viewCommentEditor from "./web_view_comment/FNAbviewcommentEditor.js"; import viewDataSelectProperties from "./web_view_data-select/FNAbviewdataselect.js"; import viewDataSelectEditor from "./web_view_data-select/FNAbviewdataselectEditor.js"; +import viewDataviewProperties from "./web_view_dataview/FNAbviewdataview.js"; +import viewDataviewEditor from "./web_view_dataview/FNAbviewdataviewEditor.js"; +import viewDetailProperties from "./web_view_detail/FNAbviewdetail.js"; +import viewDetailEditor from "./web_view_detail/FNAbviewdetailEditor.js"; import viewImageProperties from "./web_view_image/FNAbviewimage.js"; import viewImageEditor from "./web_view_image/FNAbviewimageEditor.js"; import viewLabelProperties from "./web_view_label/FNAbviewLabel.js"; @@ -25,6 +29,10 @@ const AllPlugins = [ viewCommentEditor, viewDataSelectProperties, viewDataSelectEditor, + viewDataviewProperties, + viewDataviewEditor, + viewDetailProperties, + viewDetailEditor, viewImageProperties, viewImageEditor, viewLabelProperties, diff --git a/src/plugins/web_view_dataview/FNAbviewdataview.js b/src/plugins/web_view_dataview/FNAbviewdataview.js new file mode 100644 index 0000000..e3ec26d --- /dev/null +++ b/src/plugins/web_view_dataview/FNAbviewdataview.js @@ -0,0 +1,135 @@ +// FNAbviewdataview Properties +// A properties side import for an ABView. +// + +import FABViewDetail from "../web_view_detail/FNAbviewdetail"; +import ABViewPropertyLinkPage from "../../rootPages/Designer/properties/views/viewProperties/ABViewPropertyLinkPage"; + +export default function FNAbviewdataviewProperties({ + AB, + // ABViewPropertiesPlugin, + // ABUIPlugin, +}) { + const base = "properties_abview_dataview"; + + const ABViewDetail = FABViewDetail({ AB }); + // NOTE: this is another plugin, so pass in { AB } + + const LinkPageProperty = ABViewPropertyLinkPage(AB, base); + const uiConfig = AB.Config.uiSettings(); + const L = ABViewDetail.L(); + + let ABViewDataviewPropertyComponentDefaults = {}; + + return class ABAbviewdataviewProperties extends ABViewDetail { + static getPluginKey() { + return this.key; + } + + static getPluginType() { + return "properties-view"; + // properties-view : will display in the properties panel of the ABDesigner + } + + constructor() { + super(base, { + // Put our ids here + xCount: "", + }); + + this.AB = AB; + ABViewDataviewPropertyComponentDefaults = + this.AB.ClassManager.viewClass("dataview").defaultValues(); + + this.linkPageComponent = new LinkPageProperty(AB, base); + } + + static get key() { + return "dataview"; + } + + ui() { + const ids = this.ids; + + return super.ui([ + { + id: ids.xCount, + view: "counter", + name: "xCount", + min: 1, // we cannot have 0 columns per row so lets not accept it + label: L("Items in a row"), + labelWidth: uiConfig.labelWidthLarge, + step: 1, + on: { + onChange: () => { + this.onChange(); + }, + }, + }, + this.linkPageComponent.ui(), + ]); + } + + init() { + super.init(this.AB); + + this.linkPageComponent.init(); + this.linkPageComponent.on("changed", () => { + this.onChange(); + }); + } + + populate(view) { + super.populate(view); + if (!view) return; + + const ids = this.ids; + + $$(ids.xCount).setValue( + view.settings.xCount || + ABViewDataviewPropertyComponentDefaults.xCount + ); + + this.linkPageComponent.viewLoad(view); + this.linkPageComponent.setSettings(view.settings); + } + + defaultValues() { + let values = {}; + const ViewClass = this.ViewClass(); + if (ViewClass) { + values = ViewClass.defaultValues(); + } + return values; + } + + /** + * @method values + * return the values for this form. + * @return {obj} + */ + values() { + const ids = this.ids; + let vals = super.values(); + + vals.settings = vals.settings ?? {}; + vals.settings.xCount = $$(ids.xCount).getValue(); + + let linkSettings = this.linkPageComponent.getSettings(); + for (let key in linkSettings) { + vals.settings[key] = linkSettings[key]; + } + + return vals; + } + + /** + * @method FieldClass() + * A method to return the proper ABViewXXX Definition. + * NOTE: Must be overwritten by the Child Class + */ + ViewClass() { + return super._ViewClass("dataview"); + } + }; +} diff --git a/src/plugins/web_view_dataview/FNAbviewdataviewEditor.js b/src/plugins/web_view_dataview/FNAbviewdataviewEditor.js new file mode 100644 index 0000000..1e91344 --- /dev/null +++ b/src/plugins/web_view_dataview/FNAbviewdataviewEditor.js @@ -0,0 +1,62 @@ +// FNAbviewdataview Editor +// An Editor wrapper for the ABView Component. +// The Editor is displayed in the ABDesigner as a view is worked on. +// The Editor allows a widget to be moved and placed on the canvas. +// +import FABViewContainer from "../../rootPages/Designer/editors/views/ABViewContainer"; + +export default function FNAbviewdataviewEditor({ AB /*ABViewEditorPlugin*/ }) { + const ABViewContainer = FABViewContainer(AB); + // var L = UIClass.L(); + // var L = ABViewContainer.L(); + + return class ABAbviewdataviewEditor extends ABViewContainer { + static getPluginKey() { + return this.key; + } + + /** + * @method getPluginType + * return the plugin type for this editor. + * plugin types are how our ClassManager knows how to store + * the plugin. + * @return {string} plugin type + */ + static getPluginType() { + return "editor-view"; + // editor-view : will display in the editor panel of the ABDesigner + } + + static get key() { + return "dataview"; + } + + constructor(view, base = "interface_editor_viewdataview") { + // base: {string} unique base id reference + + super(view, base); + + // this.component = this.view.component(); + } + + ui() { + let _ui = super.ui(); + _ui.rows[0].cellHeight = 75; + return _ui; + } + + init(AB) { + this.AB = AB; + return super.init(AB); + } + + detatch() { + this.component?.detatch?.(); + } + + onShow() { + super.onShow(); + this.component?.onShow?.(); + } + }; +} diff --git a/src/plugins/web_view_detail/FNAbviewdetail.js b/src/plugins/web_view_detail/FNAbviewdetail.js new file mode 100644 index 0000000..fc6be29 --- /dev/null +++ b/src/plugins/web_view_detail/FNAbviewdetail.js @@ -0,0 +1,374 @@ +// FNAbviewdetail Properties +// A properties side import for an ABView. +// + +import FABViewContainer from "../../rootPages/Designer/properties/views/ABViewContainer"; + +export default function FNAbviewdetailProperties({ AB }) { + const ABViewContainer = FABViewContainer(AB); + const uiConfig = AB.Config.uiSettings(); + const L = ABViewContainer.L(); + + let ABViewDetailPropertyComponentDefaults = {}; + + return class ABAbviewdetailProperties extends ABViewContainer { + static getPluginKey() { + return this.key; + } + + static getPluginType() { + return "properties-view"; + // properties-view : will display in the properties panel of the ABDesigner + } + + constructor(base, ids = {}) { + super( + base, + Object.assign( + { + // Put our ids here + datacollection: "", + fields: "", + showLabel: "", + labelPosition: "", + labelWidth: "", + height: "", + }, + ids + ) + ); + + this.AB = AB; + ABViewDetailPropertyComponentDefaults = + this.AB.ClassManager.viewClass("detail").defaultValues(); + } + + static get key() { + return "detail"; + } + + ui(elements) { + const ids = this.ids; + + let _ui = [ + { + id: ids.datacollection, + name: "datacollection", + view: "richselect", + label: L("Data Source"), + labelWidth: uiConfig.labelWidthLarge, + skipAutoSave: true, + on: { + onChange: (newId, oldId) => { + this.selectSource(newId, oldId); + }, + }, + }, + { + id: ids.fields, + name: "fields", + view: "list", + select: false, + minHeight: 200, + template: this.listTemplate, + type: { + markCheckbox: function (item) { + return ( + "" + ); + }, + }, + onClick: { + check: (...params) => this.check(...params), + }, + }, + { + id: ids.showLabel, + name: "showLabel", + view: "checkbox", + label: L("Display Label"), + labelWidth: uiConfig.labelWidthLarge, + }, + { + id: ids.labelPosition, + name: "labelPosition", + view: "richselect", + label: L("Label Position"), + labelWidth: uiConfig.labelWidthLarge, + options: [ + { + id: "left", + value: L("Left"), + }, + { + id: "top", + value: L("Top"), + }, + ], + on: { + onChange: () => { + this.onChange(); + }, + }, + }, + { + id: ids.labelWidth, + name: "labelWidth", + view: "counter", + label: L("Label Width"), + labelWidth: uiConfig.labelWidthLarge, + on: { + onChange: () => { + this.onChange(); + }, + }, + }, + { + id: ids.height, + name: "height", + view: "counter", + label: L("Height:"), + labelWidth: uiConfig.labelWidthLarge, + on: { + onChange: () => { + this.onChange(); + }, + }, + }, + ]; + + // Union arrays: ._ui + .elements + (elements ?? []).forEach((elem) => { + _ui.push(elem); + }); + + return super.ui(_ui); + } + + populate(view) { + super.populate(view); + if (!view) return; + + const ids = this.ids; + + const datacollectionId = view.settings?.dataviewID; + const SourceSelector = $$(ids.datacollection); + + // Pull data collections to options + const dcOptions = view.application + .datacollectionsIncluded() + .filter((dc) => { + const obj = dc.datasource; + return obj && !obj.isImported; + }) + .map((d) => { + let entry = { id: d.id, value: d.label }; + if (d.sourceType == "query") { + entry.icon = "fa fa-filter"; + } else { + entry.icon = "fa fa-database"; + } + return entry; + }); + SourceSelector.define("options", dcOptions); + SourceSelector.define("value", datacollectionId); + SourceSelector.refresh(); + + this.propertyUpdateFieldOptions(datacollectionId, view); + + $$(ids.showLabel).setValue( + view.settings.showLabel ?? + ABViewDetailPropertyComponentDefaults.showLabel + ); + $$(ids.labelPosition).setValue( + view.settings.labelPosition ?? + ABViewDetailPropertyComponentDefaults.labelPosition + ); + $$(ids.labelWidth).setValue( + parseInt(view.settings.labelWidth) ?? + ABViewDetailPropertyComponentDefaults.labelWidth + ); + $$(ids.height).setValue( + view.settings.height >= 0 + ? view.settings.height + : ABViewDetailPropertyComponentDefaults.height + ); + + // update properties when a field component is deleted + view?.views().forEach((v) => { + if (v instanceof this.AB.Class.ABViewDetailItem) + v.once("destroyed", () => this.populate(view)); + }); + } + + defaultValues() { + let values = {}; + const ViewClass = this.ViewClass(); + if (ViewClass) { + values = ViewClass.defaultValues(); + } + return values; + } + + /** + * @method values + * return the values for this form. + * @return {obj} + */ + values() { + const ids = this.ids; + let vals = super.values(); + + vals.settings = vals.settings ?? {}; + vals.settings.dataviewID = $$(ids.datacollection).getValue(); + vals.settings.showLabel = $$(ids.showLabel).getValue(); + vals.settings.labelPosition = $$(ids.labelPosition).getValue(); + vals.settings.labelWidth = $$(ids.labelWidth).getValue(); + vals.settings.height = $$(ids.height).getValue(); + + return vals; + } + + /** + * @method FieldClass() + * A method to return the proper ABViewXXX Definition. + * NOTE: Must be overwritten by the Child Class + */ + ViewClass() { + return super._ViewClass("detail"); + } + + /** + * @method propertyUpdateFieldOptions + * Populate fields of object to select list in property + * + * @param {string} dcId - id of ABDatacollection + */ + propertyUpdateFieldOptions(dcId, view) { + const ids = this.ids; + const datacollection = this.AB.datacollectionByID(dcId); + const object = datacollection?.datasource; + + // Pull field list + const fieldOptions = object?.fields().map((f) => { + f.selected = + view?.views((com) => f.id == com.settings.fieldId).length > 0; + + return f; + }); + + $$(ids.fields).clearAll(); + $$(ids.fields).parse(fieldOptions ?? []); + } + + async selectSource(dcId) { + const ids = this.ids; + // _logic.busy(); + + let currView = this.CurrentView; + + currView.settings.dataviewID = dcId; + + // clear sub views + let viewsToRemove = currView._views; + currView._views = []; + + // remove all old field components + let allRemoves = []; + viewsToRemove.forEach((v) => { + allRemoves.push(v.destroy()); + }); + await Promise.all(allRemoves); + + // refresh UI + // Update field options in property + this.propertyUpdateFieldOptions(dcId); + + // add all fields to editor by default + // if (currView._views.length < 1) { + let saveTasks = []; + let fields = $$(ids.fields).find({}); + fields.reverse(); + fields.forEach((f, index) => { + if (!f.selected) { + const yPosition = fields.length - index - 1; + + // Add new form field + const newFieldView = currView.addFieldToDetail(f, yPosition); + if (newFieldView) { + newFieldView.once("destroyed", () => this.populate(currView)); + + // // Call save API + saveTasks.push(newFieldView.save()); + } + + // update item to UI list + f.selected = 1; + $$(ids.fields).updateItem(f.id, f); + } + }); + + await Promise.all(saveTasks); + // } + + // Saving + await currView.save(); + + // Finally + const detailView = currView.parentDetailComponent(); + detailView?.emit("properties.updated", currView); + + // _logic.ready(); + this.onChange(); + } + + listTemplate(field, common) { + return `${common.markCheckbox(field)} ${field.label}`; + } + + check(e, fieldId) { + const ids = this.ids; + const currView = this.CurrentView; + const detailView = currView.parentDetailComponent(); + + // update UI list + const item = $$(ids.fields).getItem(fieldId); + item.selected = item.selected ? 0 : 1; + $$(ids.fields).updateItem(fieldId, item); + + const doneFn = () => { + // refresh UI + currView.emit("properties.updated", currView); + this.onChange(); + }; + + // add a field to the form + if (item.selected) { + const fieldView = currView.addFieldToDetail(item); + if (fieldView) { + fieldView.save().then(() => { + fieldView.once("destroyed", () => this.populate(currView)); + currView.viewInsert(fieldView).then(() => { + doneFn(); + }); + }); + } + } + // remove field in the form + else { + const fieldView = detailView.views( + (c) => c.settings.fieldId == fieldId + )[0]; + + if (fieldView) { + fieldView.destroy(); + currView.viewRemove(fieldView).then(() => { + doneFn(); + }); + } + } + } + }; +} diff --git a/src/plugins/web_view_detail/FNAbviewdetailEditor.js b/src/plugins/web_view_detail/FNAbviewdetailEditor.js new file mode 100644 index 0000000..1620d9f --- /dev/null +++ b/src/plugins/web_view_detail/FNAbviewdetailEditor.js @@ -0,0 +1,62 @@ +// FNAbviewdetail Editor +// An Editor wrapper for the ABView Component. +// The Editor is displayed in the ABDesigner as a view is worked on. +// The Editor allows a widget to be moved and placed on the canvas. +// +import FABViewContainer from "../../rootPages/Designer/editors/views/ABViewContainer"; + +export default function FNAbviewdetailEditor({ AB /*ABViewEditorPlugin*/ }) { + const ABViewContainer = FABViewContainer(AB); + // var L = UIClass.L(); + // var L = ABViewContainer.L(); + + return class ABAbviewdetailEditor extends ABViewContainer { + static getPluginKey() { + return this.key; + } + + /** + * @method getPluginType + * return the plugin type for this editor. + * plugin types are how our ClassManager knows how to store + * the plugin. + * @return {string} plugin type + */ + static getPluginType() { + return "editor-view"; + // editor-view : will display in the editor panel of the ABDesigner + } + + static get key() { + return "detail"; + } + + constructor(view, base = "interface_editor_viewdetail") { + // base: {string} unique base id reference + + super(view, base); + + // this.component = this.view.component(); + } + + ui() { + let _ui = super.ui(); + _ui.rows[0].cellHeight = 75; + return _ui; + } + + init(AB) { + this.AB = AB; + return super.init(AB); + } + + detatch() { + this.component?.detatch?.(); + } + + onShow() { + super.onShow(); + this.component?.onShow?.(); + } + }; +} diff --git a/src/rootPages/Designer/editors/EditorManager.js b/src/rootPages/Designer/editors/EditorManager.js index c81903b..dde8f1d 100644 --- a/src/rootPages/Designer/editors/EditorManager.js +++ b/src/rootPages/Designer/editors/EditorManager.js @@ -24,8 +24,8 @@ export default function (AB) { require("./views/ABViewCSVExporter"), require("./views/ABViewCSVImporter"), // require("./views/ABViewDataSelect"), - require("./views/ABViewDataview"), - require("./views/ABViewDetail"), + // require("./views/ABViewDataview"), + // require("./views/ABViewDetail"), require("./views/ABViewDocxBuilder"), require("./views/ABViewForm"), require("./views/ABViewFormUrl"), diff --git a/src/rootPages/Designer/editors/views/ABViewContainer.js b/src/rootPages/Designer/editors/views/ABViewContainer.js index 7ff5bb6..82d4ec5 100644 --- a/src/rootPages/Designer/editors/views/ABViewContainer.js +++ b/src/rootPages/Designer/editors/views/ABViewContainer.js @@ -63,8 +63,7 @@ export default function (AB) { ui() { let key = ABViewContainerEditor.key; - let Defaults = - AB.ClassManager.viewClass(key).defaultValues(); + let Defaults = AB.ClassManager.viewClass(key).defaultValues(); return { _dashboardID: this.ids.component, rows: [ diff --git a/src/rootPages/Designer/properties/PropertyManager.js b/src/rootPages/Designer/properties/PropertyManager.js index 2a0bee7..452280d 100644 --- a/src/rootPages/Designer/properties/PropertyManager.js +++ b/src/rootPages/Designer/properties/PropertyManager.js @@ -83,8 +83,8 @@ export default function (AB) { require("./views/ABViewCSVImporter"), require("./views/ABViewDataFilter"), // require("./views/ABViewDataSelect"), - require("./views/ABViewDataview"), - require("./views/ABViewDetail"), + // require("./views/ABViewDataview"), + // require("./views/ABViewDetail"), require("./views/ABViewDetailCheckbox"), require("./views/ABViewDetailCustom"), require("./views/ABViewDetailImage"),