Widget: Creating custom widget classes
WidgetPostion
, WidgetPositionExt
, WidgetStack
and WidgetStdMod
extensions to build custom versions of the Widget
class, using Base.build
.
Widget
withWidgetStdMod
Widget
withWidgetPosition, WidgetStack
Widget
withWidgetPosition, WidgetStack, WidgetPositionExt
Creating Custom Widget Classes
The Base
class provides a build
method which can be used to create custom versions of classes which derive from Base
by adding extension classes to them.
Widget currently ships with four such extensions: WidgetPosition
, WidgetStack
, WidgetPositionExt
and WidgetStdMod
.
These extensions are used to build the basic Overlay
widget, but can also be used individually, to create custom versions of the base Widget
class.
Widget with WidgetStdMod support
Adding the WidgetStdMod
extension to Widget, creates a statically positioned Widget, with support for standard module format sections - header, body and footer, which maybe useful in portal type use cases, where the positioning/stacking capabilities which come bundled with Overlay are not required.
To create a custom class, we use Base.build
, which is described in detail on the documention page for Base.
We pass in Widget
as the main class we want to add extensions to, and WidgetStdMod
as the extension we'd like added to the main class:
var StandardModule = Y.Base.build("standardModule", Y.Widget, [Y.WidgetStdMod]); // Render from Markup var stdmod = new StandardModule({ contentBox: "#widget1", width:"12em", height:"12em" }); stdmod.render();
var StandardModule = Y.Base.build("standardModule", Y.Widget, [Y.WidgetStdMod]); // Render from Markup var stdmod = new StandardModule({ contentBox: "#widget1", width:"12em", height:"12em" }); stdmod.render();
Base.build
will:
- Create a new class which extends
Widget
- Aggregate known
Base
andWidget
fields, such asATTRS
andHTML_PARSER
fromWidgetStdMod
on the new class - Augment prototype methods from
WidgetStdMod
onto the new class prototype
The first argument to build is the NAME
of the new class we are creating, just like the NAME
we define when extending the Widget class directly.
Note that the Widget
class is unchanged, allowing you to still create Widget
instances without any standard module support, along with StandardModule
instances which have standard module support.
Testing It Out
The example attempts to set content on an instance of the newly created StandardModule
class, using the setStdModContent
method which is added by the extension (which would otherwise not exist on the Widget instance).
var contentInput = Y.one("#content"); var sectionInput = Y.one("#section"); // This should work, since the StandardModule widget has settable // header/body/footer sections Y.on("click", function(e) { var content = contentInput.get("value"); var section = sectionInput.get("value"); stdmod.setStdModContent(section, content); }, "#setContent");
var contentInput = Y.one("#content"); var sectionInput = Y.one("#section"); // This should work, since the StandardModule widget has settable // header/body/footer sections Y.on("click", function(e) { var content = contentInput.get("value"); var section = sectionInput.get("value"); stdmod.setStdModContent(section, content); }, "#setContent");
To verify that no unrequested features are added, we also attempt to move the instance using the move
method, which is not part of the base Widget class, and would be added by the WidgetPosition
extension. This verifies that the other example classes we'll create, which do create new classes which use WidgetPosition
, have not modified the base Widget class.
// This shoud fail, since the StandardModule widget is not positionable Y.on("click", function(e) { try { stdmod.move([0,0]); } catch (e) { alert("move() is " + typeof stdmod.move + ", stdmod.hasImpl(Y.WidgetPosition) : " + stdmod.hasImpl(Y.WidgetPosition)); } }, "#tryMove");
// This shoud fail, since the StandardModule widget is not positionable Y.on("click", function(e) { try { stdmod.move([0,0]); } catch (e) { alert("move() is " + typeof stdmod.move + ", stdmod.hasImpl(Y.WidgetPosition) : " + stdmod.hasImpl(Y.WidgetPosition)); } }, "#tryMove");
Note that Base.build
adds a hasImpl
method to the built class, which allows you to query whether or not it has a particular extension applied.
CSS Considerations
We need to define the CSS which goes with this new StandardModule
class we have created. The only rule really required out of the box is the rule which handles visibility (yui-standardmodule-hidden
). The "standardmodule" used in the class name comes from the NAME
property we set up for the new class, and is used to prefix all state related classes added to the widgets bounding box.
Since the StandardModule
class is not positionable, we use display:none
to define the hidden
state.
/* Visibility - How to handle visibility for this new widget */ .yui-standardmodule-hidden { display:none; }
/* Visibility - How to handle visibility for this new widget */ .yui-standardmodule-hidden { display:none; }
The other "yui-standardmodule" rules are only used to create the required look/feel for this particular example, and do not impact the StandardModule widget's functionality.
Widget with WidgetPosition and WidgetStack support
As with StandardModule
, we use Base.build
to create the new Positionable
widget class. This time we add WidgetPosition
and WidgetStack
support to the base Widget
class to create a basic XY positionable widget, with shimming and z-index support.
var Positionable = Y.Base.build("positionable", Y.Widget, [Y.WidgetPosition, Y.WidgetStack]); // Render from markup var positionable = new Positionable({ contentBox: "#widget2", width:"10em", height:"10em", zIndex:1 }); positionable.render("#widget2-example"); var xy = Y.one("#widget2-example > p").getXY(); positionable.move(xy[0], xy[1]);
var Positionable = Y.Base.build("positionable", Y.Widget, [Y.WidgetPosition, Y.WidgetStack]); // Render from markup var positionable = new Positionable({ contentBox: "#widget2", width:"10em", height:"10em", zIndex:1 }); positionable.render("#widget2-example"); var xy = Y.one("#widget2-example > p").getXY(); positionable.move(xy[0], xy[1]);
We don't add WidgetPositionExt
or WidgetStdMod
support, so the widget doesn't have extended positioning support (align, center) or standard module support. Hence we position it manually using the move
method which the WidgetPosition
extension provides.
Testing It Out
We should now be able to invoke the move
method on an instance of the newly created Positionable
class:
// This should work, since Positionable has basic XY Positioning support Y.on("click", function(e) { var x = parseInt(xInput.get("value")); var y = parseInt(yInput.get("value")); positionable.move(x,y); }, "#move");
// This should work, since Positionable has basic XY Positioning support Y.on("click", function(e) { var x = parseInt(xInput.get("value")); var y = parseInt(yInput.get("value")); positionable.move(x,y); }, "#move");
And, as with the StandardModule
class, we should not be allowed to invoke any methods from an extension which we didn't request:
// This should fail, since Positionable does not have Standard Module sections Y.on("click", function(e) { try { positionable.setStdModContent("header", "new content"); } catch (e) { alert("setStdModContent() is " + typeof positionable.setStdModContent + ", positionable.hasImpl(Y.WidgetStdMod) : " + positionable.hasImpl(Y.WidgetStdMod)); } }, "#tryContent");
// This should fail, since Positionable does not have Standard Module sections Y.on("click", function(e) { try { positionable.setStdModContent("header", "new content"); } catch (e) { alert("setStdModContent() is " + typeof positionable.setStdModContent + ", positionable.hasImpl(Y.WidgetStdMod) : " + positionable.hasImpl(Y.WidgetStdMod)); } }, "#tryContent");
CSS Considerations
Since now we have a positionable widget, with z-index support, we set the widget to be absolutely positioned by default, and control it's hidden state using visibility
as opposed to display
/* Define absolute positioning as the default for positionable widgets */ .yui-positionable { position:absolute; } /* In order to be able to position the widget when hidden, we define hidden to use visibility, as opposed to display */ .yui-positionable-hidden { visibility:hidden; }
/* Define absolute positioning as the default for positionable widgets */ .yui-positionable { position:absolute; } /* In order to be able to position the widget when hidden, we define hidden to use visibility, as opposed to display */ .yui-positionable-hidden { visibility:hidden; }
Widget with WidgetPosition, WidgetStack and WidgetPositionExt support
Lastly, we'll attempt to create a new widget class, which, in addition to basic positioning and stacking support, also has extended positioning support, allowing us to align it with other elements on the page.
Again, we use Base.build
to create our new Alignable
widget class, by combining WidgetPosition
, WidgetStack
and WidgetPositionExt
with the base widget class:
var Alignable = Y.Base.build("alignable", Y.Widget, [Y.WidgetPosition, Y.WidgetPositionExt, Y.WidgetStack]); var alignable = new Alignable({ width:"13em", align : { node: "#widget3-example", points: ["cc", "cc"] }, zIndex:1 }); alignable.get("contentBox").set("innerHTML", '<strong>Alignable Widget</strong><div id="alignment"><p>#widget3-example</p> \ <p>[center, center]</p></div>'); alignable.render("#widget3-example");
var Alignable = Y.Base.build("alignable", Y.Widget, [Y.WidgetPosition, Y.WidgetPositionExt, Y.WidgetStack]); var alignable = new Alignable({ width:"13em", align : { node: "#widget3-example", points: ["cc", "cc"] }, zIndex:1 }); alignable.get("contentBox").set("innerHTML", '<strong>Alignable Widget</strong><div id="alignment"><p>#widget3-example</p> \ <p>[center, center]</p></div>'); alignable.render("#widget3-example");
Testing It Out
We'll attempt to align an instance of the Alignable
class, using some of the additional attributes which WidgetPositionExt
adds to the base Widget
class: align
and centered
:
// Align left-center egde of widget to // right-center edge of the node with id "widget3-example" alignable.set("align", {node:"#widget3-example", points:["lc", "rc"]}); // Align top-right corner of widget to // bottom-right corner of the node with id "widget3-example" alignable.set("align", {node:"#widget3-example", points:["tr", "br"]}); // Center the widget in the node with id "widget3-example" alignable.set("centered", "widget3-example"); // Align the right-center edge of the widget to // the right center edge of the viewport (since a node is not provided to 'align') alignable.set("align", {points:["rc", "rc"]}); // Center the widget in the viewport (wince a node is not provided to 'centered') alignable.set("centered", true); // Return the node to it's original alignment // (centered in the node with id "widget3-example") // NOTE: centered is a shortcut for align : { points:["cc", "cc"] } alignable.set("align", {node:"#widget3-example", points:["cc", "cc"]});
// Align left-center egde of widget to // right-center edge of the node with id "widget3-example" alignable.set("align", {node:"#widget3-example", points:["lc", "rc"]}); // Align top-right corner of widget to // bottom-right corner of the node with id "widget3-example" alignable.set("align", {node:"#widget3-example", points:["tr", "br"]}); // Center the widget in the node with id "widget3-example" alignable.set("centered", "widget3-example"); // Align the right-center edge of the widget to // the right center edge of the viewport (since a node is not provided to 'align') alignable.set("align", {points:["rc", "rc"]}); // Center the widget in the viewport (wince a node is not provided to 'centered') alignable.set("centered", true); // Return the node to it's original alignment // (centered in the node with id "widget3-example") // NOTE: centered is a shortcut for align : { points:["cc", "cc"] } alignable.set("align", {node:"#widget3-example", points:["cc", "cc"]});
CSS Considerations
The Alignable
widget class, has the same core CSS rules as the Positionable
class, to define how it is positioned and how it is hidden:
/* Define absolute positioning as the default for alignable widgets */ .yui-alignable { position:absolute; } /* In order to be able to position the widget when hidden, we define hidden to use visibility, as opposed to display */ .yui-alignable-hidden { visibility:hidden; }
/* Define absolute positioning as the default for alignable widgets */ .yui-alignable { position:absolute; } /* In order to be able to position the widget when hidden, we define hidden to use visibility, as opposed to display */ .yui-alignable-hidden { visibility:hidden; }