diff --git a/src/plugins/index.js b/src/plugins/index.js index 44e7aad2..389beaa2 100644 --- a/src/plugins/index.js +++ b/src/plugins/index.js @@ -1,3 +1,5 @@ +import viewKanbanEditor from "./web_view_kanban/FNAbviewkanbanEditor.js"; +import viewKanbanProperties from "./web_view_kanban/FNAbviewkanban.js"; import viewCarouselProperties from "./web_view_carousel/FNAbviewcarousel.js"; import viewCarouselEditor from "./web_view_carousel/FNAbviewcarouselEditor.js"; import viewCommentProperties from "./web_view_comment/FNAbviewcomment.js"; @@ -46,6 +48,8 @@ const AllPlugins = [ viewTabEditor, viewTextProperties, viewTextEditor, + viewKanbanProperties, + viewKanbanEditor, ]; export default { @@ -54,4 +58,4 @@ export default { AB.pluginRegister(plugin); }); }, -}; +}; \ No newline at end of file diff --git a/src/plugins/web_view_kanban/Abkanbanworkspace.js b/src/plugins/web_view_kanban/Abkanbanworkspace.js new file mode 100644 index 00000000..4dadf0e7 --- /dev/null +++ b/src/plugins/web_view_kanban/Abkanbanworkspace.js @@ -0,0 +1,313 @@ +// Abkanbanworkspace.js +// +// Manages the settings for a KanBan View in the Object Workspace + +const defaultValues = { + name: "Default Kanban", + settings: { + verticalGroupingField: null, + horizontalGroupingField: null, + ownerField: null, + }, +}; + +var classABViewKanban = null; + +import UI_Class from "../../rootPages/Designer/ui_class"; + +export default function (AB, ibase) { + const UIClass = UI_Class(AB); + var L = UIClass.L(); + + const ABFieldConnect = AB.Class.ABFieldManager.fieldByKey("connectObject"); + const ABFieldList = AB.Class.ABFieldManager.fieldByKey("list"); + const ABFieldUser = AB.Class.ABFieldManager.fieldByKey("user"); + + if (!classABViewKanban) { + classABViewKanban = class ABViewKanban extends UIClass { + constructor(idBase) { + super(idBase, { + vGroupInput: "", + hGroupInput: "", + ownerInput: "", + }); + + this.on("field.added", (field) => { + this.refreshOptions(this.CurrentObject, this._view); + if (this._autoSelectInput) { + $$(this._autoSelectInput)?.setValue(field.id); + } + }); + + this._autoSelectInput = null; + } + + type() { + return "kanban"; + } + + icon() { + return "fa fa-columns"; + } + + refreshOptions(object, view, options = {}) { + let ids = this.ids; + + const initSelect = ( + $option, + attribute, + filter = (f) => f.key === ABFieldList.defaults().key, + isRequired + ) => { + if ($option == null || object == null) return; + + var options = object + .fields() + .filter(filter) + .map(({ id, label }) => ({ id, value: label })); + if (!isRequired && options.length) { + options.unshift({ + id: "none", + value: L("None"), + }); + } + $option.define("options", options); + + if (view) { + if (view.settings[attribute]) { + $option.define("value", view.settings[attribute]); + } else if (!isRequired && options[0]) { + $option.define("value", options[0].id); + } + } else if (options.filter((o) => o.id).length === 1) { + $option.define("value", options[0].id); + } + + $option.refresh(); + }; + + const verticalGroupingFieldFilter = (field) => + [ + ABFieldList.defaults().key, + ABFieldUser.defaults().key, + ].includes(field.key); + + const horizontalGroupingFieldFilter = (field) => + [ + ABFieldConnect.defaults().key, + ABFieldList.defaults().key, + ABFieldUser.defaults().key, + ].includes(field.key); + + initSelect( + options.vGroupInput || $$(ids.vGroupInput), + "verticalGroupingField", + verticalGroupingFieldFilter, + true + ); + initSelect( + options.hGroupInput || $$(ids.hGroupInput), + "horizontalGroupingField", + horizontalGroupingFieldFilter, + false + ); + initSelect( + options.ownerInput || $$(ids.ownerInput), + "ownerField", + (f) => { + return ( + f.key === ABFieldUser.defaults().key || + (f.key === ABFieldConnect.defaults().key && + f.settings.linkType == "one" && + f.settings.linkViaType == "many") + ); + }, + false + ); + } + + ui() { + let ids = this.ids; + return { + batch: "kanban", + rows: [ + { + cols: [ + { + id: ids.vGroupInput, + view: "richselect", + label: ` ${L( + "Vertical Grouping" + )}`, + placeholder: L("Select a field"), + labelWidth: 180, + name: "verticalGroupingField", + required: true, + options: [], + on: { + onChange: () => { + $$(ids.vGroupInput).validate(); + $$(ids.hGroupInput).validate(); + this.emit("changed"); + }, + }, + invalidMessage: L("Required"), + }, + { + view: "button", + css: "webix_primary", + type: "icon", + icon: "fa fa-plus", + label: "", + width: 30, + click: () => { + this._autoSelectInput = ids.vGroupInput; + this.emit( + "new.field", + ABFieldList.defaults().key + ); + }, + }, + ], + }, + { + cols: [ + { + id: ids.hGroupInput, + view: "richselect", + label: ` ${L( + "Horizontal Grouping" + )}`, + placeholder: L("Select a field"), + labelWidth: 180, + name: "horizontalGroupingField", + required: false, + options: [], + invalidMessage: L( + "Cannot be the same as vertical grouping field" + ), + validate: (value) => { + var vGroupValue = $$(ids.vGroupInput).getValue(); + return ( + !vGroupValue || !value || vGroupValue !== value + ); + }, + on: { + onChange: () => { + $$(ids.hGroupInput).validate(); + this.emit("changed"); + }, + }, + }, + { + view: "button", + css: "webix_primary", + type: "icon", + icon: "fa fa-plus", + label: "", + width: 30, + click: () => { + this._autoSelectInput = ids.hGroupInput; + this.emit( + "new.field", + ABFieldList.defaults().key + ); + }, + }, + ], + }, + { + cols: [ + { + id: ids.ownerInput, + view: "richselect", + label: ` ${L( + "Card Owner" + )}`, + placeholder: L("Select a user field"), + labelWidth: 180, + name: "ownerField", + options: [], + on: { + onChange: (newID, oldID) => { + if (newID == oldID) return; + this.emit("changed"); + }, + }, + }, + { + view: "button", + css: "webix_primary", + type: "icon", + icon: "fa fa-plus", + label: "", + width: 30, + click: () => { + this._autoSelectInput = ids.ownerInput; + this.emit( + "new.field", + ABFieldConnect.defaults().key + ); + }, + }, + ], + }, + ], + }; + } + + init(object, view) { + this.objectLoad(object); + this._view = view; + this.refreshOptions(object, view); + } + + clearValues() { + const ids = this.ids; + let idFields = [ids.vGroupInput, ids.hGroupInput, ids.ownerInput]; + idFields.forEach((id) => { + let $o = $$(id); + $o.blockEvent(); + $o.define("value", null); + $o.unblockEvent(); + $o.refresh(); + }); + } + + values() { + let ids = this.ids; + let result = {}; + result.verticalGroupingField = + $$(ids.vGroupInput).getValue() || null; + result.horizontalGroupingField = + $$(ids.hGroupInput).getValue() || null; + result.ownerField = $$(ids.ownerInput).getValue() || null; + + return result; + } + + fromSettings(data) { + for (var v in defaultValues) { + this[v] = data[v] || defaultValues[v]; + } + + this.type = this.type(); + this.key = this.type(); + } + + toSettings() { + var obj = {}; + + for (var v in defaultValues) { + obj[v] = this[v] || defaultValues[v]; + } + + obj.key = this.type(); + obj.type = this.type(); + return obj; + } + }; + } + + return new classABViewKanban(ibase); +} diff --git a/src/plugins/web_view_kanban/FNAbviewkanban.js b/src/plugins/web_view_kanban/FNAbviewkanban.js new file mode 100644 index 00000000..21214195 --- /dev/null +++ b/src/plugins/web_view_kanban/FNAbviewkanban.js @@ -0,0 +1,174 @@ +// FNAbviewkanban Properties +// A properties side import for an ABView. +// +import FABViewKanbanWorkspace from "./Abkanbanworkspace.js"; +import FPopupNewDataField from "../../rootPages/Designer/ui_work_object_workspace_popupNewDataField"; + +export default function FNAbviewkanbanProperties({ + AB, + ABViewPropertiesPlugin, + ABViewPropertyLinkPage, +}) { + const BASE_ID = "properties_abview_kanban"; + + const uiConfig = AB.UISettings.config(); + const L = AB.Label(); + + const LinkPagePropertyClass = ABViewPropertyLinkPage(AB, BASE_ID); + const ViewKanbanProperties = FABViewKanbanWorkspace(AB, `${BASE_ID}_prop`); + var PopupNewDataFieldComponent = null; + + return class ABAbviewkanbanProperties extends ABViewPropertiesPlugin { + static getPluginKey() { + return this.key; + } + + static getPluginType() { + return "properties-view"; + } + + constructor() { + super(BASE_ID, { datacollection: "" }); + + this.AB = AB; + this.linkPageComponent = new LinkPagePropertyClass(); + } + + static get key() { + return "kanban"; + } + + ui() { + let _ui = ViewKanbanProperties.ui(); + + let rows = [ + { + view: "fieldset", + label: L("Kanban Data:"), + labelWidth: uiConfig.labelWidthLarge, + body: { + type: "clean", + padding: 10, + rows: [ + { + id: this.ids.datacollection, + view: "richselect", + name: "dataviewID", + label: L("Data Source:"), + placeholder: L("Select a Datacollection"), + labelWidth: uiConfig.labelWidthLarge, + options: [], + on: { + onChange: (newv, oldv) => { + if (newv != oldv) { + this.refreshFields(newv); + this.onChange(); + } + }, + }, + }, + ], + }, + }, + ..._ui.rows, + this.linkPageComponent.ui(), + ]; + + return super.ui(rows); + } + + async init(AB) { + this.AB = AB; + + ViewKanbanProperties.on("new.field", (key) => { + let dc = this.AB.datacollectionByID( + this.CurrentView.settings.dataviewID + ); + PopupNewDataFieldComponent.objectLoad(dc.datasource); + PopupNewDataFieldComponent.resetState(); + PopupNewDataFieldComponent.show(null, key, false); + }); + ViewKanbanProperties.on("changed", () => { + this.onChange(); + }); + + PopupNewDataFieldComponent = FPopupNewDataField( + AB, + `${BASE_ID}_popupNewDataField` + ); + await PopupNewDataFieldComponent.init(AB); + PopupNewDataFieldComponent.on("save", (...params) => { + ViewKanbanProperties.emit("field.added", params[0]); + }); + + this.linkPageComponent.init(); + this.linkPageComponent.on("changed", () => { + this.onChange(); + }); + + await super.init(AB); + } + + populate(view) { + super.populate(view); + + var listDC = view.application.datacollectionsIncluded().map((d) => { + return { + id: d.id, + value: d.label, + icon: + d.sourceType == "query" ? "fa fa-filter" : "fa fa-database", + }; + }); + + let $dc = $$(this.ids.datacollection); + let dcID = view.settings.dataviewID || null; + $dc.blockEvent(); + $dc.define("options", listDC); + $dc.define("value", dcID); + $dc.unblockEvent(); + $dc.refresh(); + this.refreshFields(dcID); + + this.linkPageComponent.viewLoad(view); + this.linkPageComponent.setSettings(view.settings); + } + + refreshFields(dcID) { + let dc = this.AB.datacollectionByID(dcID); + if (!dc) { + ViewKanbanProperties.clearValues(); + return; + } + + let obj = dc.datasource; + ViewKanbanProperties.init(obj, this.CurrentView); + PopupNewDataFieldComponent?.objectLoad(obj); + } + + values() { + const values = super.values(); + + const ids = this.ids; + const $component = $$(ids.component); + + values.settings = $component.getValues(); + + let fields = ViewKanbanProperties.values(); + Object.keys(fields).forEach((f) => { + values.settings[f] = fields[f]; + }); + + const linkSettings = this.linkPageComponent.getSettings(); + for (const key in linkSettings) { + values.settings[key] = linkSettings[key]; + } + + return values; + } + + ViewClass() { + return super._ViewClass("kanban"); + } + }; +} diff --git a/src/plugins/web_view_kanban/FNAbviewkanbanEditor.js b/src/plugins/web_view_kanban/FNAbviewkanbanEditor.js new file mode 100644 index 00000000..6c628264 --- /dev/null +++ b/src/plugins/web_view_kanban/FNAbviewkanbanEditor.js @@ -0,0 +1,61 @@ +// FNAbviewkanban 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. +// +export default function FNAbviewkanbanEditor({ AB, ABViewEditorPlugin }) { + const BASE_ID = "interface_editor_viewkanban"; + + return class ABAbviewkanbanEditor extends ABViewEditorPlugin { + 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 "kanban"; + } + + constructor(view, base = BASE_ID) { + // base: {string} unique base id reference + super(view, base); + } + + ui() { + const component = this.component; + const _ui = component.ui(); + _ui.minWidth = 400; + + return { + view: "layout", + cols: [_ui, { fillspace: true }], + }; + } + + async init(AB) { + this.AB = AB; + + await super.init(AB); + this.component?.init?.(AB); + } + + detatch() { + this.component?.detatch?.(); + } + + onShow() { + this.component?.onShow?.(); + } + }; +} diff --git a/src/rootPages/Designer/editors/EditorManager.js b/src/rootPages/Designer/editors/EditorManager.js index dde8f1de..49aa3aab 100644 --- a/src/rootPages/Designer/editors/EditorManager.js +++ b/src/rootPages/Designer/editors/EditorManager.js @@ -31,7 +31,7 @@ export default function (AB) { require("./views/ABViewFormUrl"), require("./views/ABViewGantt"), require("./views/ABViewGrid"), - require("./views/ABViewKanban"), + // require("./views/ABViewKanban"), // require("./views/ABViewLabel"), // require("./views/ABViewLayout"), require("./views/ABViewMenu"), diff --git a/src/rootPages/Designer/properties/PropertyManager.js b/src/rootPages/Designer/properties/PropertyManager.js index 452280d1..6f816154 100644 --- a/src/rootPages/Designer/properties/PropertyManager.js +++ b/src/rootPages/Designer/properties/PropertyManager.js @@ -105,10 +105,10 @@ export default function (AB) { require("./views/ABViewFormTextbox"), require("./views/ABViewFormTree"), require("./views/ABViewFormUrl"), - require("./views/ABViewGantt"), + // require("./views/ABViewGantt"), require("./views/ABViewGrid"), // require("./views/ABViewImage"), - require("./views/ABViewKanban"), + // require("./views/ABViewKanban"), // require("./views/ABViewLabel"), // require("./views/ABViewLayout"), // require("./views/ABViewList"), diff --git a/src/rootPages/Designer/properties/workspaceViews/ABViewGantt.js b/src/rootPages/Designer/properties/workspaceViews/ABViewGantt.js index 66fbfbfb..bdabda8c 100644 --- a/src/rootPages/Designer/properties/workspaceViews/ABViewGantt.js +++ b/src/rootPages/Designer/properties/workspaceViews/ABViewGantt.js @@ -573,7 +573,7 @@ export default function (AB, ibase) { */ fromSettings(data) { for (const key in defaultValues) - this[v] = data[key] || defaultValues[key]; + this[key] = data[key] || defaultValues[key]; this.settings = Object.assign( {}, diff --git a/src/rootPages/Designer/ui_work_object_workspace_popupViewSettings.js b/src/rootPages/Designer/ui_work_object_workspace_popupViewSettings.js index b20fd45f..b167f6e1 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_popupViewSettings.js +++ b/src/rootPages/Designer/ui_work_object_workspace_popupViewSettings.js @@ -7,7 +7,7 @@ import UI_Class from "./ui_class"; import FormABViewGantt from "./properties/workspaceViews/ABViewGantt"; import FormABViewGrid from "./properties/workspaceViews/ABViewGrid"; -import FormABViewKanBan from "./properties/workspaceViews/ABViewKanban"; +import FormABViewKanBan from "../../plugins/web_view_kanban/Abkanbanworkspace.js"; export default function (AB, ibase, isettings) { ibase = ibase || "abd_work_object_workspace_popupAddView"; diff --git a/src/rootPages/Designer/ui_work_object_workspace_view_kanban.js b/src/rootPages/Designer/ui_work_object_workspace_view_kanban.js index f13df456..c20a36c5 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_view_kanban.js +++ b/src/rootPages/Designer/ui_work_object_workspace_view_kanban.js @@ -9,7 +9,7 @@ * */ import UI_Class from "./ui_class"; -import FViewKanbanProperties from "./properties/workspaceViews/ABViewKanban"; +import FViewKanbanProperties from "../../plugins/web_view_kanban/Abkanbanworkspace.js"; export default function (AB, ibase) { ibase = ibase || "ui_work_object_workspace_view_kanban"; diff --git a/src/rootPages/Designer/ui_work_object_workspace_workspaceviews.js b/src/rootPages/Designer/ui_work_object_workspace_workspaceviews.js index 556e56e0..da5f3758 100644 --- a/src/rootPages/Designer/ui_work_object_workspace_workspaceviews.js +++ b/src/rootPages/Designer/ui_work_object_workspace_workspaceviews.js @@ -18,7 +18,7 @@ import WorkspaceKanban from "./ui_work_object_workspace_view_kanban"; import FViewGanttProperties from "./properties/workspaceViews/ABViewGantt"; import FViewGridProperties from "./properties/workspaceViews/ABViewGrid"; -import FViewKanbanProperties from "./properties/workspaceViews/ABViewKanban"; +import FViewKanbanProperties from "../../plugins/web_view_kanban/Abkanbanworkspace.js"; export default function (AB, ibase, isettings) { ibase = ibase || "ui_work_object_workspace_workspaceviews";