Cookie Consent by FreePrivacyPolicy.com TIBET
TIBET Logo

TIBET

UX

UX Base Tags


NOTE: we're migrating the xctrls namespace to a new ux namespace.

The xctrls namespace is DEPRECATED and UNSUPPORTED.

Stay tuned for significant updates as we complete our ux migration.

Wins

  • Powerful tag 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.

Contents

Concepts

Cookbook

Code

Concepts

One of the things we've seen consistently during our 25-year tenure in the web is a tendency to focus on a platform's widget kit as a key measure of project fit.

Ironically, off-the-shelf widgets rarely determine project success.

All too often 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.

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 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 "deconstructs" the typical concept of a widget and lets you create new tags out of markup, attributes, and simple OO extensions to powerful UI base classes.

Orthagonal namespace'd attributes provide much of TIBET's UX power.

TIBET's bind namespace provides data binding, the ev and on namespaces integrate event-handling, the tibet namespace lets you direct events to common types or controllers, and more.

Using ux: Tags

To use a ux tag just include it in your application pages. Most tags are authored such that they render correctly without the need for code.

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

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.

Tag Construction

Let's take a quick look at how a simple tag, app:button, might be built.

We'll take a look at a few of the common things all tags 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 tag is deciding on a proper supertype.

A number of TIBET's tags use an xhtml template to provide UI framing rather than relying on JavaScript. This allows the rendered aspects of the tag to be checked for well-formedness and validated against XML Schema if desired.

Our app:button is simple to handle via markup, so we inherit from TIBET's TP.ux.TemplatedTag, an ux-specific subtype of TIBET's common TP.tag.TemplatedTag type that mixes in ux behavior as a set of traits.

We can create our new tag with a simple command line:

tibet tag app:button --supertype=TP.ux.TemplatedTag

(As an aside, the complexity and cross-cutting behavior 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 ux namespace you'll see most tags inherit from a common supertype you'll come to recognize pretty quickly (a computed or templated supertype of some kind) which mixes in ux traits.

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

TP.tag.TemplatedTag.defineSubtype('TP.ux.TemplatedTag');

TP.ux.TemplatedTag.addTraitTypes(TP.ux.Element);

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

Tag Templating

As mentioned earlier, our app:button will render based on an XHTML template.

In our case we want the button to bring over any child nodes it might wrap when authored so our template resembles:

<app:button id="{{$SOURCE.(@name)}}" class="{{$SOURCE.(@class)}}">
    {{$SOURCE.(./node())}}
</app:button>

As we can see, the button's template is fairly direct.

Our button retains its XML form as app:button (it doesn't become button), it pulls in any id or class attribute from the originally authored DOM ($SOURCE), and it pulls in any child 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);

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 our app:button we want signals related to being active and enabled to be managed by the button so we list those as opaqueBubblingSignalNames.

APP.app.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.

Access Paths

When you are dealing with markup it can be helpful to have an easy way to access elements using named queries (what we call access 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.

Below we map body to a CSS query path and tell it we want to collapse the query result into a single node:

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

With the above definition in place the tag's code can invoke get('body') 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 that uses the body element has to change.

See TIBET Paths for more on access paths and queries.

Keyboard Sequences

TIBET UX tags can also take 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 tag's manifest loads that file after it loads the tag.

Tag "Bundles"

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

The manifest for a TIBET tag is an XML file with TIBET-specific tags that define all the components that make up that tag, 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 TIBET's built-in UX tags to appear shortly. In the meantime see the various references in the 'code' section for sample usage in TIBET's test and Lama 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.

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

The early TIBET Lama prototype makes extensive use of xctrls components.

The "deconstructed" Lama is being assembled from our growing ux tag set. Look for a number of examples of how to leverage ux widgets as that work progresses.