XControls

TIBET Widgets

wins

  • Fully-supported collection of a number of common application controls.
  • Powerful "widget composition model" based on markup-first authoring.
  • Easy low-code integration of data binding, keyboard handling, and more.
  • Custom controls are easy to create using inheritance and OO concepts.
  • Fully-integrated GUI Driver supporting component integration testing.

concepts

In our over twenty years working in the web one of the things we've seen consistently is a tendency to focus on a platform's widget kit as a key measure of fit for a project.

Unfortunately, we've often seen development teams discover the widgets they planned on using don't support the keyboard correctly, can't be localized, can't be integrated with their data without conversion, or don't include some key feature the application requires. Worse, the widget code itself is often unintelligible and unsupported.

While we like to think widgets are "universal" the reality is due to branding, locale, UX, and other business requirements, widgets often aren't as reusable as we like to believe.

TIBET's implementation of a "widget kit" took all of this into consideration.

Instead of building widgets in the traditional fashion where data sources, keyboard handling, event handling, etc. were tightly wired into the widget, we turned that model inside out and made it possible to define much of a widget's operation through external attributes.

TIBET's bind namespace provides data binding, the ev and on namespaces provide simple event-handling support, the tibet namespace lets you define a tag or ctrl attribute which can direct events to common types or controllers, etc. Widgets aren't unique per se; they're just custom tags, but in TIBET that defines pretty much everything you'll interact with.

In essence, TIBET "deconstructs" the typical concept of a widget and lets you create new ones out of markup, attributes, and simple OO extensions to powerful UI base classes.

xctrls: widgets

The TIBET xctrls namespace contains TIBET's baseline "widget kit", an ever-expanding collection of common components you can use in your applications.

The current list of xctrls list of widgets includes:

TP.xctrls.button
TP.xctrls.buttonitem
TP.xctrls.checkitem
TP.xctrls.codeeditor
TP.xctrls.combo
TP.xctrls.curtain
TP.xctrls.dialog
TP.xctrls.hint
TP.xctrls.item
TP.xctrls.itemgroup
TP.xctrls.itemset
TP.xctrls.label
TP.xctrls.list
TP.xctrls.notifier
TP.xctrls.pagerbar
TP.xctrls.panel
TP.xctrls.panelbox
TP.xctrls.picker
TP.xctrls.popup
TP.xctrls.propertysheet
TP.xctrls.radioitem
TP.xctrls.Searcher
TP.xctrls.select
TP.xctrls.sticky
TP.xctrls.tabbar
TP.xctrls.table
TP.xctrls.textitem
TP.xctrls.tooltip

Sample usage of most of these can be found in the ~lib/test/src/xctrls directory.

sherpa: widgets

TIBET also includes a number of widgets in the sherpa namespace which leverage xctrls as part of their implementation. We use the Sherpa application as a kind of "pipeline" of widgets we refine and migrate into the xctrls namespace.

Here's the current list of relatively "generic" widgets in the Sherpa which will likely find their way into xctrls over time:

TP.sherpa.about
TP.sherpa.breadcrumb
TP.sherpa.connector
TP.sherpa.console
TP.sherpa.consoleoutput
TP.sherpa.count
TP.sherpa.dispenser
TP.sherpa.dispenseritem
TP.sherpa.drawer
TP.sherpa.flex
TP.sherpa.inspector
TP.sherpa.inspectoritem
TP.sherpa.opener
TP.sherpa.outliner
TP.sherpa.screen
TP.sherpa.scrollbutton
TP.sherpa.splitbar
TP.sherpa.splitbox
TP.sherpa.statusbar
TP.sherpa.thumbnail
TP.sherpa.tile
TP.sherpa.toggle
TP.sherpa.toolbar

Note that there are dozens of other sherpa-namespaced widgets specific to the Sherpa application which provide plenty of example code for how to leverage xctrls and the rest of TIBET's UI functionality (binding, keyboard handing, etc).

The best way to get familiar with these is to look at the various widgets and explore the markup and code in TIBET's test directories. There are hundreds of examples to be found there, far more than we could ever document here.

Using xctrls Widgets

To use an xctrls widget just include it in your application pages. Most widgets are authored such that they render correctly without the need for coding.

Note that you don't need to fuss with namespaces. TIBET automatically ensures the xctrls namespace is available within your application and all of TIBET's orthagonal functionality for bind, on, etc. works exactly as you'd expect.

Below we make use of an xctrls:button along with an xctrls:label and an xctrls:hint. This simple example combines a bit of data access, data binding, and event triggering without any need to drop into JavaScript. The button, label, and hint (tooltip) automatically work:

<hello:app id="app">
<tibet:service href="/tibet.json" id="urn:tibet:tibet_json"
    on:attach="UIActivate"/>
<div bind:scope="urn:tibet:tibet_json">
    <div bind:scope="$.project">
        <xctrls:button on:click="ClickMe">
        <xctrls:label>[[name]] world!</xctrls:label>
        <xctrls:hint>Click me ;)</xctrls:hint>
        </xctrls:button>
    </div>
</div>
</hello:app>

To get the button's click action handled we do need to write a little code. The simplest thing we can do is add a handler to the hello:app tag which encloses the page:

APP.hello.app.Inst.defineHandler('ClickMe', function(aSignal) {
    APP.info('click me button was clicked');
});

By taking advantage of TIBET's signal responder chain the simple handler above will log using TIBET's Logging layer any time a 'ClickMe' signal reaches the app tag.

The majority of development in TIBET follows this pattern.

Most app functionality can be encapsulated in reusable custom tags you augment using tibet, bind, on, ev, and other attributes to provide data and adjust how signals flow to and from the tag. Coding is limited to event handlers you define on either reusable controllers or other tags/types in TIBET's signal responder chain. What code is written stays organized.

Widget Construction

Let's take a quick look at how a simple widget, TP.xctrls.button, was built.

We'll take a look at a few of the common things all widgets have to consider: their base type, what signals they manage internally, and how they interact with their child elements.

Base Type

The first thing in creating any widget is deciding on a proper supertype. A number of TIBET's widgets use an xhtml template to provide their UI framing. The xctrls:button is no different, so we inherit from TIBET's TP.xctrls.TemplatedTag, an xctrls-specific subtype of TIBET's common TP.tag.TemplatedTag type that mixes in xctrls behavior as a set of traits.

TP.xctrls.TemplatedTag.defineSubtype('xctrls:button');

As an aside, the complexity of UI hierarchies makes them particularly well-suited to leveraging traits as a form of multiple inheritance. TIBET has exceptional support for this approach.

If you look through the xctrls namespace you'll see most widgets inherit from a common supertype you'll come to recognize pretty quickly (a computed or templated supertype of some kind) which mixes in xctrls traits.

For example, TP.xctrls.TemplatedTag is constructed as follows:

TP.tag.TemplatedTag.defineSubtype('xctrls.TemplatedTag');
TP.xctrls.TemplatedTag.addTraits(TP.xctrls.Element);

This pattern, coincidentally, shows up in most extension namespaces…inheritance from a typical base type and then mixing in of traits specific to the target namespace.

Opaque Signals

With our base type defined we next want to tell TIBET which events, signals actually, should be captured and/or bubbled at the widget level.

One of the most important characteristics of a "widget" is how it creates an opaque view of what's below it in terms of specific DOM. When you work with a "table widget" for example you want the events to come from the table, not from a random text node in a random td.

For an xctrls:button we want signals related to being active and enabled to be managed by the button so we list those as opaqueBubblingSignalNames.

TP.xctrls.button.Type.defineAttribute('opaqueBubblingSignalNames',
    TP.ac('TP.sig.UIActivate', 'TP.sig.UIDeactivate',
        'TP.sig.UIDisabled', 'TP.sig.UIEnabled'));

As you might expect, there's also a opaqueCapturingSignalNames property. (Yes, TIBET property names can be verbose :)).

Child Nodes

Finally, we want our button to properly serialize any child elements such as xctrls:label, xctrls:value, and xctrls:hint. These elements give all xctrls widgets a way to set a label, value, and hint respectively:

TP.xctrls.button.Inst.defineMethod('getDescendantsForSerialization',
function() {
    var selectedDescendants;

    selectedDescendants = this.get('./xctrls:label|./xctrls:value|./xctrls:hint');
    selectedDescendants = TP.expand(selectedDescendants);

    return selectedDescendants;
});

That's the entire implementation at the JavaScript level. Let's take a look at the template:

<xctrls:button
        id="{{$SOURCE.(@id)}}">
    {{$SOURCE.(./xctrls:label|./xctrls:value|./xctrls:hint)}}
</xctrls:button>

As we can see, the button's template is fairly direct. A button retains its XML form as xctrls:button (it doesn't become a "normal button"), it pulls in any id from the originally authored markup, and it pulls in any child label/value/hint elements.

Some of the root properties available to a tag template include:

'APP', APP,
'TP', TP,
'$FOCUS', focusFunc,
'$REQUEST', aRequest,
'$SELECTION', selectionFunc,
'$SOURCE', TP.wrap(source),
'$TAG', TP.wrap(parentNode),
'$TARGET', aRequest.at('target'),
'$*', selectionFunc,            //  Alias for $SELECTION
'$@', focusFunc                 //  Alias for $FOCUS

When dealing with repeated content a few additional properties are defined:

'$_', wrappedVal,
'$INPUT', repeatSource,
'$INDEX', index,
'$FIRST', index === 1,
'$MIDDLE', index > 1 && index < last,
'$LAST', index !== last,
'$EVEN', index % 2 === 0,
'$ODD', index % 2 !== 0,
'$#', index);

Other Widget Features

While our xctrls:button walkthrough gives us a way to review some basics, a good example that covers much of what a widget might need to do is xctrls:dialog.

Multiple Templates

Like xctrls:button, xctrls:dialog is a templated tag. But it includes 4 separate templates in it's tag bundle: default, alert, confirm, and prompt templates to be precise:

  system_alert.xhtml
  system_confirm.xhtml
  system_prompt.xhtml
  TP.xctrls.dialog.xhtml

Based on code and how the dialog is being constructed it can choose to render any of these. There's no requirement that a widget use only a single template.

Access Paths

When you are dealing with markup it can be helpful to have an easy way to access elements using paths rather than hard-coded DOM lookups that might cause maintenance issues.

TIBET provides an easy way to define an attribute name, like body in the example below, and assign it to a query path. Here we map body to a path and tell it we want to collapse the query result into a single node:

TP.xctrls.dialog.Inst.defineAttribute('body',
    TP.cpc('> *[tibet|pelem="body"]', TP.hc('shouldCollapse', true)));

With the above definition in place the dialog can issue a get('body') call and the result will be the desired element. If we need to change the markup implementation we change only the path and template, none of the code has to change.

See TIBET Paths for more on access paths and queries.

Keyboard Sequences

In addition, xctrls:dialog takes advantage of another TIBET feature that lets you quickly define what keyboard shortcuts apply to a tag and what signals those keyboard actions should trigger. The file TP.xctrls.dialog.keybindings.js contains the following line:

TP.xctrls.dialog.registerKeybinding('DOM_Esc_Up', 'TP.sig.DialogCancel');

The line above tells TIBET to register DOM_Esc_Up such that any time that key is pressed while the dialog is active it should signal TP.sig.DialogCancel.

The xctrls:dialog responds to signals of that type by hiding the dialog. So with a simple line of code we get keyboard-independent event registration for key handing.

All you need to do to register keyboard sequences is to create a keybindings file using the same naming convention and place your registrations in it, then make sure the widget's manifest loads that file after it loads the widget.

Widget "Bundles"

When you create any custom tag in TIBET the result is a directory that typically contains 4-5 files: the widget source code, the widget test code, the widget template, the widget style sheet, and a widget "manifest".

The manifest for a widget is an XML file with TIBET-specific tags that define all the components that make up that widget, when they should load, what order should be used, whether there are specific configuration changes between things like development and production, etc.

For our dialog/keybinding example here's the scripts section:

<config id="scripts">
    <!-- tag-related source file references here -->
    <script src="TP.xctrls.dialog.js"/>
    <script src="TP.xctrls.dialog.keybindings.js"/>
</config>

As you can imagine, this approach lets you arrange your source code, bring in separate helper scripts, etc. in any way that makes sense. TIBET will automatically roll up and package the content of this block when you build.

cookbook

Look for "manpages" for the individual widgets to appear shortly. In the meantime see the various references in the 'code' section for sample usage in TIBET's test and Sherpa codebases.

code

The xctrls codebase resides largely in ~lib/src/xctrls. Top-level types are kept in that directory. Individual xctrls widgets are kept in TIBET 'bundle' subdirectories.

The TIBET Sherpa makes extensive use of xctrls components.

There are dozens of Sherpa widgets in ~lib/src/tibet/tools/sherpa. The Sherpa codebase also provides you with example code on how to create your own namespaced widget/tag set.