From b8976921f103682a3ab34e1b657f686673fa9462 Mon Sep 17 00:00:00 2001 From: Wirachot Date: Tue, 3 Mar 2026 12:14:55 +0700 Subject: [PATCH] migrate layout --- AppBuilder/platform/plugins/included/index.js | 2 + .../included/view_layout/FNAbviewlayout.js | 122 ++++++++++++++++++ .../view_layout/FNAbviewlayoutComponent.js | 76 +++++++++++ 3 files changed, 200 insertions(+) create mode 100644 AppBuilder/platform/plugins/included/view_layout/FNAbviewlayout.js create mode 100644 AppBuilder/platform/plugins/included/view_layout/FNAbviewlayoutComponent.js diff --git a/AppBuilder/platform/plugins/included/index.js b/AppBuilder/platform/plugins/included/index.js index 2f45b217..4b3c00e2 100644 --- a/AppBuilder/platform/plugins/included/index.js +++ b/AppBuilder/platform/plugins/included/index.js @@ -4,6 +4,7 @@ import viewText from "./view_text/FNAbviewtext.js"; import viewImage from "./view_image/FNAbviewimage.js"; import viewDataSelect from "./view_data-select/FNAbviewdataselect.js"; import viewPdfImporter from "./view_pdfImporter/FNAbviewpdfimporter.js"; +import viewLayout from "./view_layout/FNAbviewlayout.js"; const AllPlugins = [ viewTab, @@ -12,6 +13,7 @@ const AllPlugins = [ viewImage, viewDataSelect, viewPdfImporter, + viewLayout, ]; export default { diff --git a/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayout.js b/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayout.js new file mode 100644 index 00000000..c9ef3234 --- /dev/null +++ b/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayout.js @@ -0,0 +1,122 @@ +import FNAbviewlayoutComponent from "./FNAbviewlayoutComponent.js"; + +// FNAbviewlayout Web +// A web side import for an ABView. +// +export default function FNAbviewlayout({ + /*AB,*/ + ABViewWidgetPlugin, + ABViewComponentPlugin, + ABViewContainer, +}) { + const ABAbviewlayoutComponent = FNAbviewlayoutComponent({ + ABViewComponentPlugin, + }); + + const PropertyComponentDefaults = { + label: "", + numColumns: 1, // The number of columns for this layout + }; + + const ABViewDefaults = { + key: "layout", // {string} unique key for this view + icon: "columns", // {string} fa-[icon] reference for this view + labelKey: "Layout", // {string} the multilingual label key for the class label + }; + + class ABViewLayoutCore extends ABViewWidgetPlugin { + /** + * @param {obj} values key=>value hash of ABView values + * @param {ABApplication} application the application object this view is under + * @param {ABView} parent the ABView this view is a child of. (can be null) + */ + constructor(values, application, parent, defaultValues) { + super(values, application, parent, defaultValues || ABViewDefaults); + } + + static common() { + return ABViewDefaults; + } + + static defaultValues() { + return PropertyComponentDefaults; + } + + /** + * @method addColumn + * method to actually add a new ABView as one of our columns. + * This is called by the static .addView() method. + */ + addColumn() { + this._views.push( + this.application.viewNew( + { + key: ABViewContainer.common().key, + }, + this + ) + ); + } + + /** + * @method componentList + * return the list of components available on this view to display in the editor. + * @param {bool} isEdited is this component currently in the Interface Editor + * @return {array} of ABView objects. + */ + componentList(isEdited) { + if (isEdited) { + // if the layout component is being edited in the editor (isEdited == true) + // then we return []; + return []; + } else { + // the layout view doesn't care what components are offered, it get's + // the list from it's parent view. + // ## NOTE: layout views should not be root views. + if (this.parent) { + return this.parent.componentList(false); + } else { + return []; + } + } + } + + /** + * @property datacollection + * return data source + * NOTE: this view doesn't track a DataCollection. + * @return {ABDataCollection} + */ + get datacollection() { + return null; + } + } + + return class ABViewLayout extends ABViewLayoutCore { + /** + * @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 ABAbviewlayoutComponent(this, parentId); + } + + warningsEval() { + super.warningsEval(); + + if (this._views.length == 0) { + this.warningsMessage("has no columns set."); + } + } + }; +} diff --git a/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayoutComponent.js b/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayoutComponent.js new file mode 100644 index 00000000..4932118f --- /dev/null +++ b/AppBuilder/platform/plugins/included/view_layout/FNAbviewlayoutComponent.js @@ -0,0 +1,76 @@ +export default function FNAbviewlayoutComponent({ + /*AB,*/ + ABViewComponentPlugin, +}) { + return class ABAbviewlayoutComponent extends ABViewComponentPlugin { + constructor(baseView, idBase, ids) { + super(baseView, idBase || `ABViewLayout_${baseView.id}`, ids); + + const viewComponents = this.viewComponents ?? {}; // { viewId: viewComponent, ..., viewIdn: viewComponent } + + baseView.views().forEach((v) => { + viewComponents[v.id] = v.component(); + }); + + this.viewComponents = viewComponents; + } + + ui() { + const viewComponents = this.viewComponents; + const uiComponents = Object.keys(viewComponents) + .map((vId) => viewComponents[vId].ui()) + .filter((ui) => ui); + + if (uiComponents.length == 0) { + uiComponents.push({}); + uiComponents.push({ + view: "label", + label: this.label("no content"), + }); + uiComponents.push({}); + } + + const _ui = super.ui([ + { + view: "layout", + cols: uiComponents, + }, + ]); + + delete _ui.type; + + return _ui; + } + + async init(AB, accessLevel) { + await super.init(AB); + + const baseView = this.view; + + // make sure each of our child views get .init() called + baseView.views().forEach((v) => { + const component = this.viewComponents[v.id]; + + // initial sub-component + component?.init(AB, accessLevel); + + // Trigger 'changePage' event to parent + baseView.eventAdd({ + emitter: v, + eventName: "changePage", + listener: (pageId) => { + baseView.changePage(pageId); + }, + }); + }); + } + + onShow() { + // calll .onShow in child components + this.view.views().forEach((v) => { + const component = this.viewComponents[v.id]; + component?.onShow(); + }); + } + }; +}