Manager

From Frontal Wiki

Jump to: navigation, search

Contents

Introduction to the manager tag

The "manager" tag is used to control the selection of one from a number of visual sibling DocumentElement objects. As the selection changes, the manager then uses transitioners (see the "transitioner" tag) to change the display of the siblings, for example, to fade out the current selection and fade in the new selection. Some common use-cases are:

  • a slide show
  • menued sections
  • a tabbed interface
  • a form with processing, error and thank you pages

The manager tag is designed to be extremely flexible though and so these use cases are hardly exhaustive.

Also, the manager tag sends events surrounding the process of changing the selection. These events trigger several interactions as seen in the section "Adding Interactions" which make it is easy to add many types of controls such a previous and next buttons, thumbnail buttons (where a button is tied to a particular managed DocumentElement object), auto-play pause and resume controls, "1 of N"-style displays and more.

Here is an example of the manager tag (run the following code sample):

<div style="layout: stack;">
    <manager style="hide-unselected: true;" />
    <text>One</text>
    <text>Two</text>
    <text>Three</text>
</div>

In this example, we call the three text elements the managed elements of the manager tag. The "manager" tag marks the first sibling as selected (this is the default behavior and may be changed with the "initialIndex" attribute) and all the others as deselected. Note that at most one sibling may be selected at a time. Then by virtue of the "hide-unselected" style, all but the selected element are hidden. If we set this style to false then you'll see all the texts stacked on top of one another. Whether or not we want to use this style depends on the type of transitioners (discussed shortly) we will use. (We could also have hidden unselected elements using the ":not(:selected)" pseudo-class in a style sheet but this style is a bit easier to read and comes in handy.)

In this example, we can't see the other two siblings and there's no way to make them appear. We can take two approaches: make the manager automatically select one sibling after another or add a control to change the selection. First, let's make the manager auto play. Change the manager tag to this:

<manager style="hide-unselected: true; auto-play: true;" />

Now we can see the other texts though they fly by so quickly that it's not very helpful. Add these styles to control the speed of the auto playing manager: "hold-time" and "hold-time-use-secs." We can also tell the manager to go through the siblings once and then stop with the "stop-at-end" style. For example, try this manager tag:

<manager style="hide-unselected: true; auto-play: true; hold-time-use-secs: true; hold-time: 1; stop-at-end: true;" />

Let's now add manual controls to the selection process. The Frontal default style sheet provides three style classes to make adding certain controls straightforward: frButtonPrev, frButtonNext and frMarker. Let's add previous and next buttons to the managed siblings.

<style><![CDATA[
    #mgr1
        {
            hide-unselected: true;
        }
    .mgrButton:disabled
        {
            display: none;
        }
    .mgrButton
        {
            float: left;
            stop-at-end: true;
        }
]]></style>
<div style="layout: stack;">
    <manager id="mgr1" />
    <text>One</text>
    <text>Two</text>
    <text>Three</text>
</div>
<div>
    <text class="frButtonPrev mgrButton" mgrId="mgr1">previous</text>
    <text class="frButtonNext mgrButton" mgrId="mgr1">next</text>
</div>

First, notice that we've added a style sheet. This just makes things easier to read. So we've reverted the manager's styles back to their original state in which we weren't able to get to the second or third managed element. But now we've added two text tags, one with the frButtonPrev style class and the other with the frButtonNext style class. These then will make these text elements act as previous and next buttons.

The last step in adding these buttons is to associate them with a manager tag and we do that with the "mgrId" attribute. That is, on every manager control, we need to let it know what manager tag it is controlling or responding to and in this case, it is the manager tag with the id "mgr1" so we add mgrId="mgr1" to our previous and next controls.

Finally, we've added two more style sheet rulesets: one hides the button if it is disabled and the other tells the button to float left next to the other button and to not wrap from the first sibling to the last or from the last to the first. It is this style, "stop-at-end," that puts the previous button into a disabled state when the first sibling is selected and the next button into a disabled state when the last sibling is selected. Without this style, the buttons could loop through the siblings and never be disabled.

Next, let's see how the manager and its controls interact. This is an odd case, but let's make the manager auto play again. Change the manager's style sheet ruleset to this:

#mgr1
    {
        hide-unselected: true;
        auto-play: true;
        hold-time-use-secs: true;
        hold-time: 1;
    }

Run this in the online workspace and notice how the controls change according to the selected sibling even though they aren't causing the change. Also notice how the controls still work. This kind of coordinated behavior is very handy when adding redundant controls to a manager. Imagine the case of an image slide show where clicking the current image causes the slide show to progress to the next image. An example like this is shown in the section on the "transitioner" tag.) In such a case coordinating next and previous buttons is done automatically for you as this example shows.

Another type of manager control is a marker. In this case, the control is associated with a particular sibling. When clicked then, it causes the manager to directly select that sibling. In this example, we add markers for each of the three managed siblings. First add this ruleset to your style sheet.

.mgrMarker:selected
    {
        color: red;
    }

Now add these three text markers to the bottom of your Frontal document:

<text class="frMarker mgrMarker" mgrId="mgr1" elemNdx="1">Go to One</text>
<text class="frMarker mgrMarker" mgrId="mgr1" elemNdx="2">Go to Two</text>
<text class="frMarker mgrMarker" mgrId="mgr1" elemNdx="3">Go to Three</text>

Notice that these three tags all have the attribute "elemNdx." Since unlike the previous and next buttons, markers are associated with exactly one sibling, we need a way to specify which sibling that is. The "elemNdx" is how we do that. It's value is passed to a call to gE() run on the manager tag's parent and so it may be a numeric index as in our example, or it may be a name or an id. See Accessing Frontal Tags for Scripting for more details.

Now run the example. Here is more evidence of how a manager's controls are coordinated. All the controls and the manager work together and affect one another.

So far in this section, the transition from one sibling to another was done with a simple jump or cut. In the section on the 'transitioner' tag we will see how we can make this far more complex and interesting.

We've seen how markers can be used to create buttons to select particular managed elements via the frMarker style class. An important thing to note though is that a marker by itself is not a button but simply a passive object that receives events as the managed element it is marking is either selected or deselected. It was only via the frMarker style class what we made our exemplar markers clickable. With this style class, we applied the is-link style and an onClick interaction to the markers which together added this behavior. (See #Under the Covers of the Frontal Default Style Sheet (Advanced Topic) for exactly how this is done.)

Here then is an example showing how elements may be markers without also being buttons. Cut and paste following into the workspace. What you will see are red, green and blue squares lined up across the top. These are clickable markers like we've seen before.

Then you will see a large red square labeled "A." This is our selected, managed element. Inside of this red square, you will also see the word "Marker." This is a passive marker inside of a managed element. You will also see the Frontal Console displaying a message. This is a message generated by this marker as a result of the onSelect interaction applied to it. More on this in a bit.

And finally you will see the display "1 of 3." This is a result of using a 'text' tag as a marker in this case to display which of the managed elements is currently selected.

<style>
    .frMarker {
        width: 30px;
        height: 30px;  
        float: left;
    }
    .slides > div {
        top: 0px;
        left: 0px;
        width: 100px;
        height: 100px;
    }
</style>
<div class="frMarker" mgrId="mgr" elemNdx="A" style="background-color: red;">show A</div>
<div class="frMarker" mgrId="mgr" elemNdx="B" style="background-color: green;">show B</div>
<div class="frMarker" mgrId="mgr" elemNdx="C" style="background-color: blue;">show C</div>
<div class="slides" style="clear: both; margin-top: 10px;">
    <manager id="mgr" style="hide-unselected: true;"/>
    <div name="A" style="background-color: red;">
        A
        <div onSelect="com.frontalcode.Debugger.msg('select A.m ' + selected + ' ' + ( event.next == markedElement ));" mgrId="mgr" elemNdx="A" style="is-marker: true;">
            Marker
        </div>
    </div>
    <div name="B" style="background-color: green;">B</div>
    <div name="C" style="background-color: blue;">C</div>
</div>
<text onSelect="text = ( event.next.childIndex + 1 ) + ' of ' + event.manager.managedContainers.length;" mgrId="mgr" style="is-marker: true;" />

First let's look at the 'text' tag we are using as a display of the selected element. Notice that as you click the top three marker buttons, the "1 of 3" display changes. This is because we have set the is-marker style to true on this element and because we have set the "mgrId" attribute to the id of the manager we are interested in. (Notice that we have not set the "elemNdx" attribute as this is not needed in this case. "elemNdx" is only needed when we are marking a particular managed element and so interested in having the style pseudo-classes ":link," ":visited" and ":selected" maintained.) Having set these things, this element will have interactions run on it whenever there are changes to the selection of any element managed by the manager "mgr." Whether it is "A," "B" or "C" being selected, the 'text' tag will be notified.

For complete details of what the 'text' tag used as a marker is receiving in the onSelect interaction, see Adding Interactions#Manager and Transitioner Interactions. But for now, it's sufficient to know that there is an object named "event" set as a pre-defined variable and it has a member named "manager" which is the "mgr" element and it has a member named "next" which is the element being selected. With these two objects, we are able to construct our "1 of 3" display.

Next, let's look at the 'div' tag inside of managed element "A." Unlike with the 'text' tag we used for the display, here we've also set the "elemNdx" attribute. This means that this element's "selected" and "markedElement" properties will be maintained. And it is with these properties that we can discern in an onSelect interaction if the selected element is the one we are marking or not. That is, this marker will still have its onSelect interaction called every time any managed element is selected just as with the display marker so we need to do a little more work if we want to act only on the selection of the "A" element. In the example, we are showing two ways to test this: check if "selected" is true or check if "event.next" is equal to "markedElement."

One last thing to note is that as a child element of a managed element, this second marker would not automatically receive the onSelect interaction that is called on "A" only when it is selected. This is for efficiency reasons. That is why we might use the is-marker style as we have done in this example.

Implementation

The "manager" tag is a non-visual element implemented by the ContainerManager class which extends the DocumentElement class.

Properties

In addition to the DocumentElement class's properties, ContainerManager adds these:

  • paused: Whether the ContainerManageris paused or not.
  • current: The current sibling.
  • next: The next sibling during a transition otherwise null.
  • afterNext: Non-null only during a transition, the sibling selected to be shown after the next sibling.
  • managedContainers: The containers managed by this manager.

Methods

It adds these methods:

  • jumpTo: Use this method to transition to one of the manager's managed siblings. It takes the following parameters:
    • index: Either the sibling Container object itself of a parameter to a gE() call made on the manager's parent.
    • quick: Whether to transition to the sibling (false) or to go to it immediately (true). This is false by default.
  • getNext: Gets the next sibling relative to the another one. It's parameters are:
    • delta: A positive or negative number. E.g., 1 will return the next container.
    • wrap: Whether to wrap around the beginning and end of list of children when applying the delta. If false and the delta extends past the end of the children list then null is returned.
    • from: If set, then do the calculation from this container.
  • pause: For auto-playing managers, this pauses that process.
  • resume: For auto-playing managers, this resumes that process.

Interactions

The "manager" element supports many specific interactions. See the Adding Interactions section for details.

Styles

Styles particular to the manager element are:

  • wants-reset: While not particular to the manager tag, the manager's response to a reset event is singular. On reset, the manager tag will immediately set the currently selected sibling back to the initial sibling (either the first sibling or the one specified by the attribute "initialIndex" on the manager tag).
  • hide-unselected: If true then the manager tag will hide (via a call to show(false)) all but the currently selected sibling and show that one. Then, upon the start of a transition, the manager will show the next sibling. And after the transition is done, it will hide (show(false)) the formerly current sibling.
  • deep-link: Eight "query" or "path," this is again not particular to the manager element but it's behavior is. See the section on deep linking below.
  • auto-play: If true then the manager will automatically jump to the next sibling once a transition completes.
  • hold-time: The amount of time to wait when auto-play is true before jumping to the next sibling.
  • hold-time-use-secs: If true then the hold time is in seconds otherwise it is in frames. This is false by default.
  • stop-at-end: If true and auto-play is true then the manager will jump to the first sibling after reaching the last.

Attributes

Attributes particular to the manager element are:

  • initialIndex: Specify the index of the initial sibling when first showing the managed sibling or after a reset event.

Styles of Managed Elements

Making a DocumentElement managed, that is, by making it a sibling of a manager tag, allows for these styles to be applied to it:

  • reset-on-select: If "true," this will dispatch a reset event on the element when it is selected by the manager.
  • reset-on-deselect: If "true," this will dispatch a reset event on the element when it is deselected by the manager.
  • reset-on-select-transition-end: If "true," this will dispatch a reset event on the element when it is selected by the manager but after the transition to it has completed.
  • reset-on-deselect-transition-end: If "true," this will dispatch a reset event on the element when it is deselected by the manager but after the transition away from it has completed.
  • reset-on-will-select: If "true," this will dispatch a reset event on the element when it is selected while another selection is in progress.

And any element may have these manager-related styles:

  • is-marker: Set to "true" and supply "mgrId" and "elemNdx" attributes to have this element receive notification of changes to some managed element. For example, a thumbnail of a particular slide in a slide show would have is-marker set to true. See #More About Markers below for more details.
  • jump-delta: Use this style with the special frButtonNext and frButtonPrev style classes defined in The Default Frontal Style Sheet to change how far a button will jump forward or backward in a list of managed elements. See #Introduction to the manager Tag for more details.

Deep Linking

Manager tags are particularly suited for deep linking. See the section Deep Linking for details.

Under the Covers of the Frontal Default Style Sheet (Advanced Topic)

We mentioned earlier that the functionality of next and previous buttons and markers was built into Frontal via the default style sheet. This might make it seem as though this functionality is quite complicated and so not very flexible. On the contrary, these style classes are quite straightforward and mainly included for convenience. Here in their entirety are the definitions of the frButtonPrev, frButtonNext and frMarker style classes:

.frButtonNext, .frButtonPrev
    {
        is-link: true;
        is-marker: true;
        jump-delta: 1;
        @onClick { var mgr = gE  ( gA ( "mgrId" ) ); mgr.jumpTo ( mgr.getNext ( gS ( "jump-delta" ), ! gS ( "stop-at-end" ) ) ); }
        @onSelect
            {
                if ( event is com.frontalcode.ContainerMgrEvent )
                    markedElement = event.manager.getNext ( gS ( "jump-delta" ), ! gS ( "stop-at-end" ), event.next );
                else
                    markedElement = gE  ( gA ( "mgrId" ) ).getNext ( gS ( "jump-delta" ), ! gS ( "stop-at-end" ) );
            }
    }
.frButtonPrev        { jump-delta: -1; }
.frMarker
    {
        is-link: true;
        is-marker: true;
        @onClick { gE ( gA ( "mgrId" ) ).jumpTo ( gA ( "elemNdx" ) ); }
    }

Hierarchy Pseudo-Classes for Menus

The 'manager' tag supports the imposition of a hierarchy on its managed elements. That is, a 'manager' tag only manages a flat list of elements - its siblings - but there are times when we want to treat these sibling as if they have parent-child relationships between them. That is the case with a sectioned site. In that case, a sibling represents a section or a sub-section or a sub-sub-section and so forth. As far as the manager is concerned, there is always jsut one sibling selected but markers may consider the related sibling to be in some enhanced version of unselected, like, "unselected but with a child selected." And transitioners can get into the act by changing their behavior depending on the relationship in the hierarchy between the current selection and the next. For example, if they are siblings in the hierarchy, the transition may be to slide them up and down. If they have different parents, the transition may be to slide them left and right.

Here is an example showing how to define a hierarchy on a number of managed elements. Below the style sheet, we first have a 'div' that contains our menu buttons. Then we have a 'div' that contains the managed elements of our sectioned site. In that second 'div', notice how on the second 'text' tag we have added the attribute "parentElemNdx" and set it to "Section One". This then defines a hierarchical relationship between this 'text' tag and that identified by the index "Section One" which is the first 'text' tag by virtue of it having its "name" attribute set to that string. (See Accessing Frontal Tags for Scripting for more details.)

Similarly, the third 'text' tag is a child of the first by virtue of having its "parentElemNdx" attribute also set to "Section One".

The fourth 'text' tag is a child of the third and so a grandchild of the first by virtue of having its "parentElemNdx" attribute set to "Section One: Sub Section Two".

The last 'text' tag is a top level element in the hierarchy just as the first is by virtue of not having a "parentElemNdx" attribute.

Run the following code sample.

<style><![CDATA[
*
    {
        style-tween-ease: fl.transitions.easing.Regular.easeOut;
        style-tween-duration: 6;
        style-tween-use-secs: false;
    }
 
.section
    {
        width: 470px;
        height: 220px;
        padding: 40px;
        color: white;
        font-size: 24;
    }
 
.sectionButtons
    {
        float: left;
        width: 112px;
        padding: 4px;
        border-width: 1px;
        background-color: orange;
        margin-right: 4px;
    }
 
.menuEntry
    {
         width: auto;
         height: 0px;
         alpha: 0;
     }
.menuEntry:link { color: aliceblue; }
.menuEntry:visited { color: darkblue; }
.menuEntry:focus, .menuEntry:hover { color: royalblue; underline: true; }
.menuEntry:active { color: red; }
.menuEntry:selected { color: black; underline: false; }
.menuEntry:selected, .menuEntry:siblingSelected, .menuEntry:parentSelected(1), .menuEntry:childSelected, .menuEntry:nephewSelected { alpha: 1; height: 18px; }
]]></style>
 
<!-- Menu buttons -->
<div style="width: 100%;">
    <div class="sectionButtons">
        <text class="frMarker menuEntry" mgrId="mgr" elemNdx="Section One">Section 1</text>
        <text class="frMarker menuEntry" mgrId="mgr" elemNdx="Section One: Sub Section One" style="padding-left: 4px;">Section 1.1</text>
        <text class="frMarker menuEntry" mgrId="mgr" elemNdx="Section One: Sub Section Two" style="padding-left: 4px;">Section 1.2</text>
        <text class="frMarker menuEntry" mgrId="mgr" elemNdx="Section One: Sub Section Two: Sub Sub Section One" style="padding-left: 8px;">Section 1.2.1</text>
    </div>
    <div class="sectionButtons">
        <text class="frMarker menuEntry" mgrId="mgr" elemNdx="Section Three">Section 3</text>
    </div>
</div>
 
<!-- Site sections -->
<div style="layout: stack; left: 0px; top: 100px; width: 550px; height: 300px; overflow: hidden;">
    <manager id="mgr" style="deep-link: path; hide-unselected: true; tween-container-init: true;">
        <transitioner custom="com.frontalcode.transitions.TweenTransition" target="current" property="top" start="0" finish="300" duration="15" />
        <transitioner custom="com.frontalcode.transitions.TweenTransition" target="next" property="top" start="-300" finish="0" duration="15" />
    </manager>
    <text name="Section One" class="section" style="background-color: blue;"><![CDATA[Section One]]></text>
    <text name="Section One: Sub Section One" class="section" style="background-color: orange;" parentElemNdx="Section One">
        <![CDATA[Section One: Sub Section One]]>
    </text>
    <text name="Section One: Sub Section Two" class="section" style="background-color: teal;" parentElemNdx="Section One">
        <![CDATA[Section One: Sub Section Two]]>
    </text>
    <text name="Section One: Sub Section Two: Sub Sub Section One" class="section" style="background-color: purple;" parentElemNdx="Section One: Sub Section Two">
        <![CDATA[Section One: Sub Section Two: Sub Sub Section One]]>
    </text>
    <text name="Section Three" class="section" style="background-color: green;"><![CDATA[Section Three]]></text>
</div>

As you click the menu buttons, the menu reacts to show or hide certain buttons. We'll look at that more closely but first note that the managed siblings are behaving like normal - simply transitioning from the current sibling to the next unaffected by the imposed hierarchy.

Back to the menu buttons then, these are behaving quite complexly. When "Section 3" is clicked for example, that button goes black and loses its rollover state. When "Section 1" is clicked, it does the same but also exposes the buttons for "Section 1.1" and "Section 1.2". And when "section 1.2" is clicked, it exposes the button "Section 1.2.1".

All of these behaviors are implemented via the style sheet but to do this we have to take advantage of some custom pseudo-classes supported by Frontal. These pseudo-classes are:

  • siblingSelected: Set to true if a sibling of the element in the imposed hierarchy is selected. (An element is not considered its own sibling.)
  • parentSelected: Set to a 1 if the element's parent is selected. Set to 2 if its grandparent is selected. And so forth. That is, this pseudo-class is set to the distance between the element and the selected element in the hierarchy. If the selected element is not an ancestor of this element then this pseudo-class is set to zero.
  • childSelected: This is the inversion of parentSelected. If one of the element's children is selected then this pseudo-class is set to 1. If a grandchild is selected then it is set to 2. And so on until the selected element is found in this element's descendants. If the selected element is not in this element's descendants then this pseudo-class is set to zero.
  • nephewSelected: A sibling of an element that has childSelected set to a non-zero value will have its nephewSelected pseudo-class also set to the same value.

Now that we know we have these pseudo-classes available, how do we use them? In our example, all of the menu buttons are defined with 'text' tags in the first 'div' tag. Each of these 'text' tags has two style classes: "frMarker" and "menuEntry". "frMarker" is used as we've seen in previous examples to turn the 'text' tag into a button to select a particular managed element. But it also works to turn the button into a marker and so it will have its styles set as if it were the targetted managed element. That is, by adding "frMarker," these 'text' tags will have the pseudo-classes "selected", "siblingSelected", "parentSelected", "childSelected" and "nephewSelected" maintained on them. We could have also achieve this by applying the is-marker style to them but then they would not be cickable buttons - just passive markers.

Since we know these hierarchical pseudo-classes will be applied to menu buttons, the following style sheet snippet should begin to make more sense. These are the rulesets that define the behavior of the menu buttons:

<style><![CDATA[
.menuEntry
    {
         width: auto;
         height: 0px;
         alpha: 0;
     }
.menuEntry:link { color: aliceblue; }
.menuEntry:visited { color: darkblue; }
.menuEntry:focus, .menuEntry:hover { color: royalblue; underline: true; }
.menuEntry:active { color: red; }
.menuEntry:selected { color: black; underline: false; }
.menuEntry:selected, .menuEntry:siblingSelected, .menuEntry:parentSelected(1), .menuEntry:childSelected, .menuEntry:nephewSelected
    {
        alpha: 1;
        height: 18px;
    }
]]></style>

The ".menuEntry" ruleset is just setting the default button state to be invisible with 0 height.

The rulesets ".menuEntry:link", ".menuEntry:visited", ".menuEntry:focus", ".menuEntry:hover", ".menuEntry:active" and ".menuEntry:selected" use standard pseudo-classes we've seen before for links. The first two, ".menuEntry:link" and ".menuEntry:visited", are responsible for showing unvisited links in very light blue and visited links in dark blue.

The next two, ".menuEntry:focus" and ".menuEntry:hover", set the color to royal blue on roll over or when the entry is tabbed to.

".menuEntry:active" sets the color of the text when the mouse is clicked. Click and hold a button to see it turn red.

The ruleset ".menuEntry:selected" is what sets the text to black when the corresponding sibling is selected.

Then the remaining ruleset is what determines when a button is visible or not. The selectors are described here:

  • .menuEntry:selected: Matches the element when it is selected.
  • .menuEntry:siblingSelected: Matches when the element has a sibling in the imposed hierarchy that is selected. This is why if "Section 1.1" is selected, then "Section 1.2" is displayed.
  • .menuEntry:parentSelected(1): Matches when the element's immediate parent in the imposed hierarchy is selected. This is why "Section 1.1" and "Section 1.2" are exposed when "Section 1" is selected. It is also why "Section 1.2.1" is exposed when "Section 1.2" is selected.
  • .menuEntry:childSelected: Matches whenever the pseudo-class childSelected is true which is whenever it is non-zero and so is whenever any descendant is selected. This is why when "Section 1.2.1" is selected then so is "Section 1", its grandparent.
  • .menuEntry:nephewSelected: Matches whenever a sibling has a non-zero vale for its childSelected pseudo-class. This is why "Section 1.1" is exposed when "Section 1.2.1" is selected.

With these custom pseudo-classes then, a complex menu control can be created for a set of managed elements that have a hierarchy imposed on them. These pseudo-classes may also serve as an example of how Frontal may be extended to support rich states for its elements that can be used to drive various styles.

For completeness we'll mention that the orange wrapper around the section buttons is added by the two divs with the style class "sectionButtons" that wrap each set of section buttons. Since their height is taken from the height of their contents, they shrink and expand as the buttons are hidden and shown.

Personal tools
Get Adobe Flash player