Hi,
in this tutorial, I will show you how to inherit a more complex widget. For this tutorial I asume that you have read basic widget tutorial before.
In this example, I want to extend the DropDown widget. The DropDown widget does exactly what the name says, it creates a drop-down list of items. If the widget is near the bottom of your screen, this creates the problem that the drop-down list leaves the screen at the bottom and you can no longer select an item from that list. I will extend the DropDown widget with the option to drop up a list or center a list to screen middle so that it always uses the maximum space available.
As a starting point, we follow the exact same steps as we did in the basic tutorial. This gives us an exact copy of the widget with all its functionality. The function that we would like to modify is DropDownBoxView and is stored in original widget folder under â/lib/view/DropDownBoxView.jsâ. This funtion implements the list box that pops out when you click the widget. Remember that we can only derive complete widgets but we can not reference any files that are inside a brease widget folder.
We need to copy the file DropDownBoxView.js and all its dependencies into our widget folder. In total we need to copy these 5 files from the original widget into our own widget folder.
The files that we just copied use dependencies that do no longer work when the files are not in their original location. The references point into the widget brease framework that we can not reach from our widget library. This means that we need to modify these references. Our first goal is to fix these references and make the code work as it is.
We start with the file DropDownBoxView.js The start of the file looks like this
'use strict';
define([
'widgets/brease/common/libs/redux/view/ListView/ListView',
'widgets/brease/common/libs/redux/view/ItemView/ItemView',
'widgets/brease/DropDownBox/libs/reducer/DropDownBoxActions',
'brease'
], function (ListView, ItemView, DropDownBoxActions, { appView, bodyEl, enum: { Enum }, core: { Utils }, events: { BreaseEvent, EventDispatcher }, controller: { popUpManager } }) {
/**
* @class widgets.brease.DropDownBox.libs.view.DropDownBoxView
*/
The function references several items from âwidgets/brease/common/libsâ. Unfortunately, the API does not allow access to all components in this path. You will see later on that there are more references to the common path. Instead of copying one file at a time, we take the complete âlibsâ folder and make a copy in our widget library in our own âcommonâ folder. This has the advantage that we can use this common folder in the future without picking every single file again.
Keep in mind that this common folder could change in the future with another mappView version at which point you have to repeat the process again.
The result should look like this:
âŚ
What we do next is something that I have tried with the drop-down box, and it worked fine, but I can not guarantee that this does not have any side effects that I do not know about.
The common folder that we just copied has tons of references to the brease framework that we need to modify. Right click on your âcommon/libsâ folder and select âFind in folderâ
Enter the following text and hit the replace all button
Repeat the same with this text and hit the replace all button
With a little bit of luck, everything in libs should now point to our common library. We can now go ahead and modify the references in the DropDownBoxView.js file from âbreaseâ to brXtended" to point to our library.
'use strict';
define([
'widgets/brXtended/common/libs/redux/view/ListView/ListView',
'widgets/brXtended/common/libs/redux/view/ItemView/ItemView',
'widgets/brXtended/DropDownBox/libs/reducer/DropDownBoxActions',
'brease'
], function (ListView, ItemView, DropDownBoxActions, { appView, bodyEl, enum: { Enum }, core: { Utils }, events: { BreaseEvent, EventDispatcher }, controller: { popUpManager } }) {
We repeat the same process with all other files in our widget libs folder.
Wherever something points to âwidgets/brease/common/libsâ we change it to âwidgets/brXtended/common/libsâ
This is a good time to do a final test before we modify something to make sure that the widget still works as expected. After that we can modify the code.
I will not explain every line that I have modified, just the parts that are important to modify an derived widget function. The first file we change is the Config.js We want to introduce a new parameter called âlistPositionExtâ. This new parameter has the following options:
- If 0, list position extension is off.
- If 1, the list box will reverse the pop-up direction. For ex. listPosition=ârightâ from bottom upwards.
- If 2, the list box will always try to center the list box to the screen.
Since we derive from an existing widget, including the configuration, we can pretty much delete everything from the original config file except the return value. We then add our own parameter and the file should look like this. Donât forget to include the new parameter in the return structure!
'use strict';
define(['brease'], function ({ enum: { Enum } }) {
/**
* @class widgets.brXtended.DropDownBox.config.Config
* @extends core.javascript.Object
* @override widgets.brXtended.DropDownBox
*/
/**
* @cfg {Integer} listPositionExt=0
* @iatStudioExposed
* @iatCategory Extended
* If 0, list position extension is off.
* If 1, the list box will reverse the pop-up direction. For ex. listPosition='right' from bottom upwards.
* If 2, the list box will always try to center the list box to the screen.
*/
return {
selectedIndex: 0,
selectedValue: '',
itemHeight: 40,
imageAlign: Enum.ImageAlign.left,
imagePath: '',
ellipsis: false,
wordWrap: false,
multiLine: false,
fitHeight2Items: true,
dataProvider: [],
listPosition: Enum.Position.right,
listPositionExt: 0,
listWidth: 150,
maxVisibleEntries: 4,
cropToParent: Enum.CropToParent.none,
displaySettings: Enum.DropDownDisplaySettings.default,
tabIndex: 0
};
});
The next file we modify is the main file DropDownBox.js Here we have to add all references that we will use later to replace the DropDownBoxView. This is something that you might do at the end when you know what references you really used in this file.
'use strict';
define([
'brease',
'widgets',
'./libs/config/Config',
'./libs/view/DropDownBoxView/DropDownBoxView',
'./libs/config/InitState',
'./libs/reducer/DropDownBoxReducer',
'widgets/brXtended/common/libs/external/redux',
], function (
brease,
widgets,
Config,
DropDownBoxView,
InitState,
DropDownBoxReducer,
Redux,
)
{
We then add our standard code with the reference to the brease widget and point to our modified config file.
{
/**
* @class widgets.brXtended.DropDownBox
*
* @mixins widgets.brease.common.DragDropProperties.libs.DroppablePropertiesEvents
*
* DropDownBox
* @extends widgets.brease.DropDownBox
*
* @iatMeta studio:visible
* true
* @iatMeta category:Category
* Selector
* @iatMeta description:short
* DropDownliste von Texten
* @iatMeta description:de
* Zeigt eine DropDownliste, aus welcher der Benutzer Elemente auswählen kann
* @iatMeta description:en
* Displays a drop-down list where the user can select items
*/
var defaultSettings = Config
The next code block is pretty standard, so I skip over that.
We have to modify the init function so that the widget uses our DropDownBoxView and not the one that comes with the derived widget. To be honest, this was a lot of trial and error with the original init function, so I am not going to pretend that I fully understand everything that is going on here. I used the original init function and found the line where the DropDownBoxView is created.
this.dropDownBoxView = new DropDownBoxView(this.store, this.el, this);
I worked my way back and left everything that is needed to create the DropDownBoxView which is basically this:
// Calculate init state
var initState = InitState.calculateInitState(this.settings, this.isEnabled(), this.isVisible());
// Add additional property to init state
initState.items.listSettings.listPositionExt = this.settings.listPositionExt;
// Create store
this.store = Redux.createStore(DropDownBoxReducer, initState);
The next part is important because here we remove the original DropDownBoxView from the derived widget
// Remove inherited view
window.removeEventListener('addEventListener', this.focusHandler.onRender.bind(this.focusHandler));
this.dropDownBoxView.dispose();
this.submitQueue.clear();
and replace it with our reference.
// Create View
this.dropDownBoxView = new DropDownBoxView(this.store, this.el, this);
// Subscribe master view to the store
this.store.subscribe(this.dropDownBoxView.render.bind(this.dropDownBoxView));
if (brease.config.isKeyboardOperationEnabled()) {
// Update focus after a render of the view
this.dropDownBoxView.addEventListener('ViewRendered', this.focusHandler.onRender.bind(this.focusHandler));
}
The code for removing the DropDownBoxView was stolen from the dispose function from the original widget ![]()
Now that we injected our own DropDownBoxView we can finally make the code changes to modify the behavior of the DropDownBox list. Again, I am not listing all the changes, that is something you can easily do with a compare between the original and modified version. The important change is in the function âgetLocationâ. All I really do is modifing the top position of the list box.
// Calculate top for left, right and center
if (listPositionExt === listPositionExtDropUp) {
location.top = targetRect.bottom - listRect.height * scaleFactor;
} else if (listPositionExt === listPositionExtCenter) {
location.top = (pageHeight - listRect.height * scaleFactor) / 2;
// Limit list box top to parent top
if (location.top < 0) {
location.top = 0;
}
// If top of list box is lower than button top move it up to button top
if (location.top > targetRect.top) {
location.top = targetRect.top;
}
// If bottom of list box is higher than button bottom move it down
if (location.top + listRect.height * scaleFactor < targetRect.bottom) {
location.top = targetRect.bottom - listRect.height * scaleFactor;
}
} else {
location.top = targetRect.top;
}
And with this change you have a brand new DropDown, DropUp and DropCenter box. I will add the complete code to this post as reference.
Stephan
brXtended.zip (1.0 MB)




