poniedziałek, 28 listopada 2011

Notatki o Windows 8 - odc.5

Kontynuacja kontrolek HTML5 / JS w Windows 8 - pasek postępu, Tooltip, FlipView, ListView, SemanticZoom. Szczególnie godne uwagi są kontrolki obsługujące kolekcje i związane z nimi szablony w HTML, grupowanie, źródła danych. AppBar to również bardzo istotna część interfejsu aplikacji Metro.

progress

determinate, indeterminate, indeterminate progress ring

max, value, position (read-only)

When you can't estimate how much work remains to finish a task and the task doesn't block user interaction, use an indeterminate progress bar. To create an indeterminate progress bar, create a progress element without specifying a value or max.

When you can't estimate how much work remains to finish a task and the task does block user interaction, use an indeterminate progress ring. To create an indeterminate progress ring, create a progress element without specifying a value or max and set its width and height to the same value.

<label><progress style="width: 20px; height: 20px;"></progress>Processing</label>

Does the user need to know that something is happening? For example, if app is downloading something in the background, and user didn’t initiate the download, the user doesn’t need to know about it;

Show some form of progress indication when a task can take more than 2 seconds to complete.

Can content be used to visualize progress? If so, do not show progress control. For example, when displaying images loaded from the disk, images appear on the screen one-by-one as they are loaded. Displaying progress control would provide no benefit; it would just clutter the UI.

Do show a single progress control for multiple active related tasks. If there are multiple related items on the screen that are all simultaneously performing some kind of activity, do not show multiple progress controls. Instead, show one that ends when the last task completes. For example, if the app downloads multiple photos, show a single progress control, instead of showing one for every photo.

If some time (or action) is needed to start providing determinate progress, use the indeterminate bar first, and then switch to the determinate bar. For example, if the first step of a download task is connecting to a server, you can’t estimate how long that takes. After the connection is complete, you should switch to the determinate progress bar to show the download progress. Keep progress bar in exactly the same place, and of the same size after the switch.

Inline progress with status. Show the subject (label) of the task above the bar, and status underneath. Don’t show status if it is obvious what’s happening. Once the task completes, hide the progress bar. Use status text to communicate the new state of an item.

Multiple progress bars. Use this pattern if there is a need to show progress of multiple items. The key is to align the content in a grid for at-a-glance reading of status. Show progress bar for all items – even those that are still pending. Once activity is complete for an item, remove it from the list, to reinforce the sense of overall progress to the user.

If a task was initiated from the app bar, and it blocks user’s interaction, show the progress control in the app bar. You can use pattern above (with label/status), or you can align progress bar to the top of the app bar, and use it without label/status – as longs it's clear what the task is. Disable interaction during the task by disabling controls in the the app bar, and ignoring input in the content area.

If the task is modal (blocks user interaction), and takes longer than 10 seconds, provide a way to cancel it.

Space progress updates evenly. Avoid situations where progress gets to over 80%, and then gets stuck for a long period of time. You want to speed up progress towards the end, not slow it down. Avoid drastic jumps, such as from 0% to 90%.

After setting progress to 100%, wait until the determinate progress bar finishes animating before hiding it. Depending on UI framework, you can either use a completion property/event, or just wait 1 second for animation to complete.

If your task is stopped (by a user, or an external condition), but can be resumed by the user, set the progress bar to the Paused state. Show appropriate message underneath to reinforce the visual state. Switch it to normal state once task is resumed.

If the task is stopped and can’t be resumed or has to be restarted from scratch, set the progress bar to the Error state. Replace status text (underneath the bar) with a message that tells the user what happened and how to fix the issue (if possible).

Always increment the progress value - never decrement it. If you need to reverse an action, show the progress of reversal as you would show progress of any other action.

Don’t restart progress (from 100% to 0%), unless it’s obvious to the user that a current step or task is not the last one. For example, suppose a task has two parts: downloading some data, and then processing and displaying the data. After the download is complete, reset the progress bar to 0% and begin showing the data processing progress. Avoid doing this if it’s unclear to users that there are multiple steps in a task. Instead, collapse them into a single 0-100% scale, and update status text as you move from one task to the next.

Dialog – action occurs prior to moving to the next screen. Place progress indicator just above the button area, left-aligned with the content of the dialog.

App window - left-aligned controls. If a control that starts the modal action is aligned to the left, place progress ring with text to the right of it. Alternatively, you can place it underneath the control.

Outside of context – flyout. If the control can’t be displayed in context, place it underneath the content it’s associated with (if present). Use flyout if activity can proceed in the background if user dismisses flyout by tapping outside of it.

Multiple items. Place progress control with status text underneath the title of the item. If error occurs, replace it with error text

Flyout. Place indeterminate progress bar on top of the flyout, spanning its whole width. Placement flush with the border minimizes visual distraction of the animation, while still communicating ongoing activity. Do not use flyout title (it will prevent you from being able to place progress bar at the very top).

App window. Place indeterminate progress bar at the very top of the app window, spanning the whole area that displays associated content.

Treat partially modal tasks as non-modal. Some tasks block interaction until some progress has been made, and then user can start interacting with the app again. For example, when the user performs a search query, interaction would be blocked until the first result is displayed. Treat tasks such as these as non-modal and use the indeterminate progress bar style – if modal state lasts less than 2 seconds. If modal state can last more than 2 seconds, use indeterminate progress ring for the modal phase of the task, and indeterminate progress bar for the non-modal.

Only show progress for non-modal tasks that the user is interested in.If a task is non-modal (the user can continue interacting with the app), show the indeterminate progress bar only if user needs to know that task is happening. For example, if app downloads something in the background, and the user didn't initiate the download, the user doesn’t need to know about it. On the other hand, if the user initiated an update or sync, show the activity, since user is likely to be interested in its completion, even though app is fully usable in the meantime.

Don't use a progress control when you want to surface background activities that don't block user activity. For these situations, you can use text and an ellipsis.

Use this pattern when your app is performing tasks that don't have to be visible all the time, but there still has to be a way for users to see the status.

When using determinate progress bar, don’t show progress percentage in the status string. The control already provides that information.

If you use status text in conjunction with progress control, do not use ellipsis as they duplicate the message that the visual indicator already gives.

Tooltip

The contents of the tooltip can be text or an image but is not interactive.

You can use the tooltip to show the item under the finger during touchdown, so that users know where they are touching.

Do not use a tooltip on a button to show the same text of the button while the button is long enough to make the :active state visible during touch down.

Do not put interactive controls inside the tooltip.

Do not use images that look interactive.

FlipView

The FlipView is a Windows Library for JavaScript control that lets you flip through a collection of items, one at a time. It's great for displaying a gallery of images. The FlipView can accept data from several types of sources. You can enter markup directly or bind to a local or web-based data source.

Windows Library for JavaScript provides several types of ready-to-use data sources: there's ArrayDataSource for accessing arrays and JSON data, IteratorDataSource for accessing objects that implement the IIterator interface, and StorageDataSource for accessing files and directories. You can also connect to a custom data source by using an ListDataSource and implementing your own IListDataAdapter.

<div id="basicFlipView" data-win-control="WinJS.UI.FlipView>

    <img alt="Banana" src="images/banana.jpg" />

    <img alt="Mint" src="images/mint.jpg" />

    <img alt="Orange" src="images/orange.jpg" />

</div>

Binding to JSON data

var myData = new WinJS.UI.ArrayDataSource([

        { title: "Banana", description: "Banana Frozen Yogurt", picture: "images/banana.jpg" },

        { title: "Orange", description: "Orange Sherbet", picture: "images/orange.jpg" },

        { title: "Vanilla", description: "Vanilla Ice Cream", picture: "images/vanilla.jpg" },

        { title: "Mint", description: "Mint Gelato", picture: "images/mint.jpg" },

        { title: "Strawberry", description: "Strawberry Sorbet", picture: "images/strawberry.jpg" }

    ]);

Item template:

data-win-bind="propertyName: dataFieldName"

data-win-bind="property1Name: dataField1Name, property2Name: dataField2Name"

1.  <div id="itemTemplate" data-win-control="WinJS.Binding.Template" >

2.          <div>

3.              <!-- Displays the "title" field. -->

4.              <div data-win-bind="innerText: title">

5.              </div>

6.   

7.              <!-- Displays the "picture" field. -->

8.              <img  data-win-bind="alt: title; src: picture" />

9.   

10.             <!-- Displays the "description" field.  -->

11.             <div data-win-bind="innerText: description">

12.             </div>

13.         </div>

14.</div>

 

   <div id="dataFlipView" data-win-control="WinJS.UI.FlipView"

        data-win-options="{dataSource: myData,  itemRenderer: itemTemplate}">

    </div>

ListView

The ListView is a Windows Library for JavaScript control that displays data in a customizable list or grid.

Windows Library for JavaScript provides several types of ready-to-use data sources: there's ArrayDataSource for accessing arrays and JSON data, GroupDataSource for creating a grouped list, GroupedItemDataSource for use with the SemanticZoom control, IteratorDataSource for accessing objects that implement the IIterator interface, and StorageDataSource for accessing files and directories. You can also connect to a custom data source by using an ListDataSource and implementing your own IListDataAdapter.

WinJS.UI.processAll()

            .then(function () {

 

                var basicListView = WinJS.UI.getControl(

                    document.getElementById("basicListView"));

 

                basicListView.dataSource = myDataSource;

            });

You can style an item template like you would any other HTML element by assign CSS classes or using CSS selectors. When the ListView renders each item, the elements in the template become child elements of the ListView, so you can use the ID of the ListView as part of the CSS selector.

    <div id="itemTemplate" data-win-control="WinJS.Binding.Template" >

        <div class="itemContainer">

 

            <!-- Displays the "picture" field.  -->

            <img class="itemPicture"  data-win-bind="alt: title; src: picture"  />

 

            <!-- Displays the "title" field. -->

            <div class="itemTitle" data-win-bind="innerText: title">

            </div>

 

            <!-- Displays the "description" field.  -->

            <div class="itemDescription" data-win-bind="innerText: description">

            </div>

 

        </div>

    </div>

   

    <div id="basicListView" data-win-control="WinJS.UI.ListView"

        data-win-options="{dataSource: myData,  itemRenderer: itemTemplate}">

    </div>

CSS:

#basicListView .itemPicture

{

    float: left;

    margin: 10px;

}

 

#basicListView .itemTitle

{

    font-weight: bold;

}

 

#basicListView .itemDescription

{

    font-style: italic;

}

ListView CSS classes: win-listView, win-item, win-inTransit (being dragged)

.win-listView .win-item {

    left-margin: 100px;

    width: 50px;

    height: 50px;

}

.win-listView .win-item.win-hover {

    background-color: red;

}

Layouts

data-win-options="{itemRenderer: itemTemplate, layout: {type: WinJS.UI.ListLayout}}"

data-win-options="{itemRenderer: itemTemplate, layout: {type: WinJS.UI.GridLayout}}"

Selection

selectionMode: none, single, multi

data-win-options="{dataSource: myData,  itemRenderer: itemTemplate, selectionMode : "single"}"

myListView.selectionMode = WinJS.UI.SelectionMode.none;

Grouping data

You can use a GroupDataSource to group items in your ListView. To supporting grouping, the ListView must use the grid layout.

image

To create a GroupDataSource, you define a function that groups your data. This function will be called for each item in the data source. Your function must take an IListItem as a parameter and return an IListItem whose key property is the group to switch the item belongs.

     // Groups the items by the first letter of the 'title' property.

function simpleGroupBy(item) {

    var firstLetter = item.data.title.toUpperCase().charAt(0);

    return {

        key: firstLetter,

        data: {

            title: firstLetter

        }

    };

 }

var myGDS = new WinJS.UI.GroupDataSource(myDataSource, simpleGroupBy);

groupedListView.dataSource = myDataSource;

groupedListView.groupDataSource = myGDs;

<div id="headerTemplate" data-win-control="WinJS.Binding.Template">

    <div>

        <div data-win-bind="innerText: title">

        </div>

    </div>

</div>

 

<div id="groupedListView" data-win-control="WinJS.UI.ListView"

    data-win-options="{itemRenderer: itemTemplate, groupRenderer:headerTemplate, layout: {type: WinJS.UI.GridLayout}}"> 

</div>

Sort data

var myData = [

{ title: "Banana", description: "Banana Frozen Yogurt", picture: "images/60banana.png" },

{ title: "Banana", description: "Banana Ice Cream", picture: "images/60banana.png" },

{ title: "Banana", description: "Banana Frozen Custard", picture: "images/60banana.png" },

{ title: "Orange", description: "Orange Sherbet", picture: "images/60orange.png" },

{ title: "Vanilla", description: "Vanilla Ice Cream", picture: "images/60vanilla.png" },

{ title: "Mint", description: "Mint Gelato", picture: "images/60mint.png" },

{ title: "Strawberry", description: "Strawberry Sorbet", picture: "images/60strawberry.png" }

];

 

var myDataSource = new WinJS.UI.ArrayDataSource(

    myData.sort(function (a, b) {

        return a.title.toUpperCase().charCodeAt(0) - b.title.toUpperCase().charCodeAt(0);

        })

    );

Item templates for list layouts

Item templates for grid layouts

To use the template, copy the HTML and one of the two sets of CSS styles into your project.

SemanticZoom

The SemanticZoom control enables the user to zoom between two different views of the same content.

image

You can use the SemanticZoom control with any control that implements the IZoomable interface.

To use a SemanticZoom, you need three data sources: one for the underlying data, a GroupDataSource, and a GroupedItemDataSource.

Create a GroupedItemDataSource. Use the same underlying data source and grouping function that you used to create the GroupDataSource.

var myGroupedItemDataSource = new WinJS.UI.GroupedItemDataSource(myDataSource, simpleGroupBy);

The SemanticZoom control uses two ListView controls: one to supply the zoomed-in view and one for the zoomed-out view.

<div id="semanticZoomDiv" data-win-control="WinJS.UI.SemanticZoom"> 

    <!-- The zoomed-in view. -->

    <div id="zoomedInListView"

        data-win-control="WinJS.UI.ListView"

        data-win-options="{itemRenderer: mediumListIconTextTemplate, groupRenderer:headerTemplate, selectionMode: 'none',  crossSlide: 'none' }">

    </div>

 

    <!-- The zoomed-out view. -->

    <div id="zoomedOutListView"

        data-win-control="WinJS.UI.ListView"

        data-win-options="{itemRenderer: semanticZoomTemplate, selectionMode: 'none', crossSlide: 'none' }">

    </div>

</div>

zoomedInListView.dataSource = myGroupedItemDataSource;

        zoomedInListView.groupDataSource = myGroupDataSource;

        zoomedInListView.refresh();

zoomedOutListView.dataSource = myGroupDataSource;

        zoomedOutListView.GroupDataSource = myGroupDataSource;

Using:

Zoom out

  • Touch: Pinch out
  • Keyboard: Ctrl + Minus sign, Enter, Space
  • Mouse: Ctrl + Rotate the mouse wheel backward

Zoom in

  • Touch: Pinch in, tap
  • Keyboard: Ctrl + Plus sign, Enter, Space
  • Mouse: Ctrl + Rotate the mouse wheel forward

Custom data source

You can create your own custom data source that accesses any other type of data, such as an XML file or a web service. This topic shows you how to implement a custom data source that accesses a web service. It uses XHR to connect to the bing image search service and displays the result in a ListView.

Use the WinJS.Namespace.define function to define a namespace for your custom data source.

Define a function that creates your data source. This function should have parameters for any data you need to connect to your data source. The function should create a custom IListDataAdapter, and it should return a ListDataSource object.

createBingImageSearchDataSource: function (appId, query) {         

 

        var bingImageSearchDataAdapater = {

 

        };

 

        return new WinJS.UI.ListDataSource(bingImageSearchDataAdapater);

    }

The data (from xhr from Bing) processing function must return an array of IListItem objects ( one IListItem for each data item). Each IListItem must have a key property that contains an identifier for that item and a data property that contains the item's data: { key: key1, data : { field1: value, field2: value, ... }}

The next step is to implement the IListDataAdapter interface. Regardless of what type of custom data source you connect to, this step is required. Although you can fully implement IListDataAdapter so that it supports read/write access and change notifications, you create a simple, read-only IListDataAdapter just by implementing the itemsFromIndex and getCount methods.

Create and return a new Promise. When you create a Promise, you pass the constructor a function to call, the init function, when the Promise is fulfilled. This function takes two parameters: complete, which is called when the Promise is successfully fulfilled, and error, which called if there was an error.

return new WinJS.Promise(function (complete, error) {

If the data was processed successfully, create an object that implements IFetchResult and add your data to it. Pass this object to the complete function.

data = bingImageSearchExample.createBingImageSearchDataSource(devkey, searchTerm);

listview.dataSource = data;

AppBar

The app bar presents navigation, commands, and tools to users. The app bar appears when users swipe a finger from the top or bottom edge of the screen or when you open it programmatically. It covers the top or bottom content of the app and can be dismissed by the user with an edge swipe, or by interacting with the app.

<div id="AppBar1" data-win-control="WinJS.UI.AppBar" aria-label="Command Bar" data-win-options="{position:'bottom',transient:true,autoHide:0,lightDismiss:true}">

    <div class="win-right">

        // App bar button using a font glyph for the icon.

        <button onclick="doBack()" class="win-command">

            <span class="win-commandicon win-large">&#xE0D5</span>

            <span class="win-label">Back</span>

        </button>

        // Separator between buttons.

        <hr />

        // App bar button using a custom PNG sprite for the icon.

        <button onclick="doWeekView()" class="win-command">

            <span style="background-image:url('weekview_sprite.png')" class="win-spritestates win-commandicon win-large"></span>

            <span class="win-label">WeekView</span>

        </button>

    </div>

</div>

You should place app bars containing commands at the bottom of the screen. If your app has a view where the content is full-screen with no on-canvas navigation, then you can also place a back button and title in an app bar at the top of the screen. If you have a few commands that are necessary for a user to complete an experience, such as buying a product, then place those commands on the canvas instead of in an app bar.

Choosing the right dismissal mode

  • Use manual mode when the user needs to control dismissal. If your app offers features that require interaction with non-selectable content, such as painting on a canvas, set the app bar to manual mode by turning off light dismiss. In this mode, app interaction does not dismiss the bar. The user is in complete control of when the commands show or hide.
  • Use persistent mode when the user needs the app bar visible all the time. If you have more than a few commands that are necessary for a user to complete an experience, put them on an app bar in persistent mode instead of the canvas. Set the app bar to persistent mode by turning off transience and light dismiss. In this mode, the bar is always visible and cannot be dismissed by the user.

Commands

  • Generally, you should place on the right side of the app any commands that are available and relevant all the time. If you have more than a few commands and can divide your commands into two logical groups, left-align one group and right-align the other. For example, if your commands can be split into general commands (such as Home or View) which are always present, and contextual commands (such as Crop and Rotate) which only show up when content is selected, then place the general commands on the left side of the app bar and place the contextual commands on the right.
  • If you have commands that can only be performed when content is selected, show your bar programmatically when users select content. Set the bar to persistent mode so that the bar remains visible while users act on their content. If you have commands that can be performed whether content is selected or not, keep those commands on the bar if you have enough room. When the content is deselected, hide the bar and set it back to Light Dismiss mode.
  • If you are unable to fit all of your commands in the app bar as separate buttons, group commands together and place those commands in menus that are opened from app bar buttons. Use logical groupings for the commands, such as placing Reply, Reply All, and Forward into a Respond menu. Don't create a menu like "More" or "Advanced" for unrelated, miscellaneous commands. These types of generic commands tend to make an app feel more complicated, and only a small subset of users explore these menus. If you find yourself needing an overflow and there aren't any logical groupings available, consider simplifying your app.
  • A limited number of commands can fit on the app bar when your app is snapped. You can either group commands together into menus or programmatically unsnap the app to provide additional commands.
  • Be aware that the app bar covers scrollbars when an app has a horizontally scrolling area that appears at the bottom of the app. The user may need to click in the app to dismiss the app bar in order to use the scrollbar, or they can use a mouse wheel to scroll. If your app bar is in persistent mode, such as when content is selected, reduce the height of your scrolling area so that the scrollbar is flush with the top edge of the app bar.
  • Don't place commands that are essential for the user to complete their task on a hidden app bar. If the user feels that they’re opening the bar frequently just to accomplish their task, then you should consider direct manipulation, semantic zoom, or on-canvas commands.
  • Place cut, copy, and paste commands in a context menu rather than in an app bar.

c.d.n