Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions AppBuilder/ABFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import Multilingual from "../resources/Multilingual.js";
import Network from "../resources/Network.js";
// Network: our interface for communicating to our server

import LocalPlugins from "./platform/plugins/included/index.js";

import Storage from "../resources/Storage.js";
// Storage: manages our interface for local storage

Expand Down Expand Up @@ -877,11 +875,15 @@ class ABFactory extends ABFactoryCore {
this._plugins.push(p);
}

pluginLocalLoad() {
// This is a placeholder for a local plugin load.
// The platform version of this method will load the plugins from
// /platform/plugins/local/
return LocalPlugins.load(this);
async pluginLocalLoad() {
// Load included plugins when available (e.g. platform/plugins/included/).
// Optional so unit tests and CI can run when that path is not present.
try {
const LocalPlugins = await import("./platform/plugins/included/index.js");
return LocalPlugins.default.load(this);
} catch (e) {
if (e?.code !== "MODULE_NOT_FOUND") throw e;
}
}

//
Expand Down
2 changes: 1 addition & 1 deletion AppBuilder/core
Submodule core updated from 6120ef to 6a6a72
3 changes: 2 additions & 1 deletion AppBuilder/platform/plugins/included/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import viewList from "./view_list/FNAbviewlist.js";
import viewTab from "./view_tab/FNAbviewtab.js";
import viewDetail from "./view_detail/FNAbviewdetail.js";
import viewLabel from "./view_label/FNAbviewlabel.js";

import viewText from "./view_text/FNAbviewtext.js";
import viewImage from "./view_image/FNAbviewimage.js";
import viewDataSelect from "./view_data-select/FNAbviewdataselect.js";
Expand All @@ -11,6 +11,7 @@ import viewCarousel from "./view_carousel/FNAbviewcarousel.js";
const AllPlugins = [
viewTab,
viewList,
viewDetail,
viewText,
viewLabel,
viewImage,
Expand Down
140 changes: 140 additions & 0 deletions AppBuilder/platform/plugins/included/view_detail/FNAbviewdetail.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import FNAbviewdetailComponent from "./FNAbviewdetailComponent.js";

// Detail view plugin: replaces the original ABViewDetail / ABViewDetailCore.
// All logic from both Core and platform is contained in this file.
export default function FNAbviewdetail({
ABViewContainer,
ABViewContainerComponent,
ABViewComponentPlugin,
}) {
const ABViewDetailComponent = FNAbviewdetailComponent({
ABViewContainerComponent,
ABViewComponentPlugin,
});

const ABViewDetailDefaults = {
key: "detail",
icon: "file-text-o",
labelKey: "Detail(plugin)",
};

const ABViewDetailPropertyComponentDefaults = {
dataviewID: null,
showLabel: true,
labelPosition: "left",
labelWidth: 120,
height: 0,
};

return class ABViewDetailPlugin extends ABViewContainer {
/**
* @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 ?? ABViewDetailDefaults
);
}

static getPluginType() {
return "view";
}

static getPluginKey() {
return this.common().key;
}

static common() {
return ABViewDetailDefaults;
}

static defaultValues() {
return ABViewDetailPropertyComponentDefaults;
}

/**
* @method fromValues()
* Initialize this object with the given set of values.
* @param {obj} values
*/
fromValues(values) {
super.fromValues(values);

this.settings.labelPosition =
this.settings.labelPosition ||
ABViewDetailPropertyComponentDefaults.labelPosition;

this.settings.showLabel = JSON.parse(
this.settings.showLabel != null
? this.settings.showLabel
: ABViewDetailPropertyComponentDefaults.showLabel
);

this.settings.labelWidth = parseInt(
this.settings.labelWidth ||
ABViewDetailPropertyComponentDefaults.labelWidth
);
this.settings.height = parseInt(
this.settings.height ??
ABViewDetailPropertyComponentDefaults.height
);
}

/**
* @method componentList
* Return the list of components available on this view to display in the editor.
*/
componentList() {
const viewsToAllow = ["label", "text"];
const allComponents = this.application.viewAll();
return allComponents.filter((c) =>
viewsToAllow.includes(c.common().key)
);
}

addFieldToDetail(field, yPosition) {
if (field == null) return;

const newView = field
.detailComponent()
.newInstance(this.application, this);
if (newView == null) return;

newView.settings = newView.settings ?? {};
newView.settings.fieldId = field.id;
newView.settings.labelWidth =
this.settings.labelWidth ||
ABViewDetailPropertyComponentDefaults.labelWidth;
newView.settings.alias = field.alias;
newView.position.y = yPosition;

this._views.push(newView);
return newView;
}

/**
* @method component()
* Return a UI component based upon this view.
* @return {obj} UI component
*/
component() {
return new ABViewDetailComponent(this);
}

warningsEval() {
super.warningsEval();

const DC = this.datacollection;
if (!DC) {
this.warningsMessage(
`can't resolve it's datacollection[${this.settings.dataviewID}]`
);
}
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
export default function FNAbviewdetailComponent({
ABViewContainerComponent,
ABViewComponentPlugin,
}) {
const ContainerComponent =
ABViewContainerComponent?.default ?? ABViewContainerComponent;
const Base = ContainerComponent ?? ABViewComponentPlugin;
if (!Base) {
return class ABAbviewdetailComponent {};
}

return class ABAbviewdetailComponent extends Base {
constructor(baseView, idBase, ids) {
super(
baseView,
idBase || `ABViewDetail_${baseView.id}`,
Object.assign({ detail: "" }, ids)
);
this.idBase = idBase || `ABViewDetail_${baseView.id}`;
}

ui() {
if (!ContainerComponent) {
return this._uiDataviewFallback();
}
const _ui = super.ui();
return {
type: "form",
id: this.ids.component,
borderless: true,
rows: [{ body: _ui }],
};
}

_uiDataviewFallback() {
const settings = this.settings;
const _uiDetail = {
id: this.ids.detail,
view: "dataview",
type: { width: 1000, height: 30 },
template: (item) => (item ? JSON.stringify(item) : ""),
};
if (settings.height !== 0) _uiDetail.height = settings.height;
else _uiDetail.autoHeight = true;
const _ui = super.ui([_uiDetail]);
delete _ui.type;
return _ui;
}

onShow() {
const baseView = this.view;
try {
const dataCy = `Detail ${baseView.name?.split(".")[0]} ${baseView.id}`;
$$(this.ids.component)?.$view?.setAttribute("data-cy", dataCy);
} catch (e) {
console.warn("Problem setting data-cy", e);
}

const dv = this.datacollection;
if (dv) {
const currData = dv.getCursor();
if (currData) this.displayData(currData);

["changeCursor", "cursorStale", "collectionEmpty"].forEach((key) => {
this.eventAdd({
emitter: dv,
eventName: key,
listener: (...p) => this.displayData(...p),
});
});
this.eventAdd({
emitter: dv,
eventName: "create",
listener: (createdRow) => {
if (dv.getCursor()?.id === createdRow.id)
this.displayData(createdRow);
},
});
this.eventAdd({
emitter: dv,
eventName: "update",
listener: (updatedRow) => {
if (dv.getCursor()?.id === updatedRow.id)
this.displayData(updatedRow);
},
});
}

super.onShow?.();
}

displayData(rowData = {}) {
if (!ContainerComponent) return;
if (rowData == null && this.datacollection)
rowData = this.datacollection.getCursor() ?? {};

const views = (this.view.views() || []).sort((a, b) => {
if (!a?.field?.() || !b?.field?.()) return 0;
if (a.field().key === "formula" && b.field().key === "calculate")
return -1;
if (a.field().key === "calculate" && b.field().key === "formula")
return 1;
return 0;
});

views.forEach((f) => {
let val;
if (f.field) {
const field = f.field();
if (!field) return;

switch (field.key) {
case "connectObject":
val = field.pullRelationValues(rowData);
break;
case "list":
val = rowData?.[field.columnName];
if (!val || (Array.isArray(val) && val.length === 0)) {
val = "";
break;
}
if (field.settings.isMultiple === 0) {
let myVal = "";
(field.settings.options || []).forEach((opt) => {
if (opt.id === val) myVal = opt.text;
});
if (field.settings.hasColors) {
let hasCustomColor = "";
(field.settings.options || []).forEach((h) => {
if (h.text === myVal) {
hasCustomColor = "hascustomcolor";
}
});
const hex = (field.settings.options || []).find(
(o) => o.text === myVal
)?.hex ?? "#66666";
myVal = `<span class="webix_multicombo_value ${hasCustomColor}" style="background-color: ${hex} !important;"><span>${myVal}</span></span>`;
}
val = myVal;
} else {
const items = val.map((value) => {
let myVal = "";
(field.settings.options || []).forEach((opt) => {
if (opt.id === value.id) myVal = opt.text;
});
const optionHex =
field.settings.hasColors && value.hex
? `background: ${value.hex};`
: "";
const hasCustomColor =
field.settings.hasColors && value.hex
? "hascustomcolor"
: "";
return `<span class="webix_multicombo_value ${hasCustomColor}" style="${optionHex}" optvalue="${value.id}"><span>${myVal}</span></span>`;
});
val = items.join("");
}
break;
case "user":
val = field.pullRelationValues(rowData);
break;
case "file":
val = rowData?.[field.columnName] ?? "";
break;
case "formula":
val = rowData ? field.format(rowData, false) : "";
break;
default:
val = field.format(rowData);
}
}

const vComponent = f.component(this.idBase);
vComponent?.setValue?.(val);
vComponent?.displayText?.(rowData);
});
}
};
}
20 changes: 0 additions & 20 deletions test/AppBuilder/platform/views/ABViewDetail.test.js

This file was deleted.

Loading