Skip to content

Instantly share code, notes, and snippets.

@Skymetal
Last active October 9, 2017 13:02
Show Gist options
  • Save Skymetal/24517689bc5e578a28caa61242013af2 to your computer and use it in GitHub Desktop.
Save Skymetal/24517689bc5e578a28caa61242013af2 to your computer and use it in GitHub Desktop.
winjs: Flyout/Dropdown

Flyout/Dropdown

HTML

#flyout-control

The flyout element that contains the repeater that will be displayed, populated with options

<!-- Flyout Control -->
<div id="flyout-control" data-win-control="WinJS.UI.Flyout">
    <div id="flyout-repeater" data-win-control="XboxJS.UI.Repeater"></div>
</div>

#flyout-invoker-template

Creates a button with a label, which when clicked will invoke the flyout with its values

<div id="flyout-invoker-template" data-win-control="WinJS.Binding.Template">
    <div data-win-control="XboxJS.UI.ItemContainer" class="flyout-invoker">
        <div class="flyout-invoker-layout-grid">
            <div class="flyout-invoker-image-container"><img data-win-bind="src: imageUrl"></div>
            <div class="flyout-invoker-label black-text white-stroke cap-first" data-win-bind="innerText: label"></div>
            <div class="flyout-invoker-selected-container black-text white-stroke cap-first" data-win-bind="innerText: selected"></div>
        </div>
    </div>
</div>

#flyout-option-template

When a flyout appears on screen, each item in the flyout should use this template

<!-- Flyout Option Item Template -->
<div id="flyout-option-template" data-win-control="WinJS.Binding.Template" class="win-hidden" data-win-options="{extractChild: 'true'}">
	<!-- extractChild: https://msdn.microsoft.com/en-us/library/windows/apps/dn301970.aspx -->
    <div data-win-control="XboxJS.UI.ItemContainer" class="flyout-option-container">
        <div class="flyout-layout-grid">
			<div class="flyout-option-image-container win-hidden"><img data-win-bind="src: flyoutOptionImage"></div>
            <div class="flyout-option-name" data-win-bind="innerText: flyoutOptionText"></div>
        </div>
    </div>
</div>

JS code

_createFlyoutInvokerControl

Sets template and data for flyout, then shows flyout anchored to the specified item

Flyout Parameters Notes
.control Flyout Control element that will be shown/hidden
.repeater Repeater HTML control inside the flyout
.templates.invoker Template to display for the button to show/invoke the flyout
.templates.option Template to display for each item in the flyout
_createFlyoutInvokerControl: function (flyout, sourceData, callback) {
    // Create a button, with sourceData
    // Wire up the invoked event for the button
    // Button Invoked displays the flyout control with the sourceData values
    // Option Invoked sets the invoker value to sourceData.selected, hides the flyout and calls the callback if provided


    // Flyout config object:
    // var flyout = {
    //     control: '#flyout-control',
    //     repeater: '#flyout-repeater',
    //     templates: {
    //         invoker: '#flyout-invoker-template',
    //         option: '#flyout-option-template'
    //     }
    // }

    // sourceData is in object that is bound to the flyoutInvoker
    //      Contains name, **imageUrl** and anything else to be data-win-bound into the invoker control
    //      **values** - Contains a list of data to populate the flyout
    //      **selected** - Contains a field for the selected parameter
    var baseErrorMessage = "Error creating flyout invoker: ";

    if(!flyout) throw new Error(baseErrorMessage + "no flyout");
    if(!flyout.control || !flyout.control.winControl) throw new Error(baseErrorMessage + "no flyout control");
    if(!flyout.repeater) throw new Error(baseErrorMessage + "no flyout repeater");
    if(!flyout.templates.invoker || !flyout.templates.invoker.winControl) throw new Error(baseErrorMessage + "no invoker template");
    if(!flyout.templates.option || !flyout.templates.option.winControl) throw new Error(baseErrorMessage + "no option template");
    if(!sourceData) throw new Error(baseErrorMessage + "no source data");
    if(!sourceData.values) throw new Error(baseErrorMessage + "no source data values");
    if(!sourceData.label) throw new Error(baseErrorMessage + "no source data label");

    // Create the button & render the invoker template
    var invokerControl = document.createElement('div');
    flyout.templates.invoker.winControl.render(sourceData, invokerControl);

    // Create an instance template by rendering the option template with invoked event to: set selected, hide, callback
    var optionTemplate = function (sourceDataValue) {
        var optionErrorMessage = "Error creating flyout option: ";
        if(!sourceDataValue) throw new Error(optionErrorMessage + "no source data value");
        if(!sourceDataValue.flyoutOptionText) throw new Error(optionErrorMessage + "no source data value flyoutOptionText");

        // Create the div & render the option template
        var optionElement = document.createElement('div');
        flyout.templates.option.winControl.render(sourceDataValue, optionElement);

        // Add invoked event to option to set selected, hide and call callback
        optionElement.addEventListener('invoked', function (event) {
            sourceData.selected = sourceDataValue.flyoutOptionText;
            flyout.control.winControl.hide();
            if (callback) callback(sourceDataValue, sourceData);
        });

        // Return the instance of the template
        return optionElement;
    };

    // Create the binding list of values for the repeater
    var bindingData = new WinJS.Binding.List(sourceData.values);

    // Create the button invoked event to show the flyout & data
    invokerControl.addEventListener('invoked', function () {
        if (!flyout.repeater.winControl) return;

        flyout.repeater.winControl = null;                                              // Resets the repeater winControl
        flyout.repeater.winControl = new XboxJS.UI.Repeater(flyout.repeater);           // Creates a new winControl in place of the old one
        flyout.repeater.winControl.template = optionTemplate;                           // Binds the template for the repeater to the provided template
        flyout.repeater.winControl.data = bindingData;                                  // Bind the data to the repeater
        flyout.control.winControl.show(invokerControl);                                 // Show the flyout
    });

    // Return button
    return invokerControl;
}

Example usage

// Define the flyout control elements
var flyout = {
    control: document.querySelector('#flyout-control'),
    repeater: document.querySelector('#flyout-repeater'),
    templates: {
        invoker: document.querySelector('#flyout-invoker-template'),
        option: document.querySelector('#flyout-option-template')
    }
}

// Define the data
var cardTypes = {
    label: "Card Type",
    imageUrl: undefined,
    selected: undefined,
    values: [
        {
            flyoutOptionImage: undefined,
            flyoutOptionText: "Visa"
        },
        {
            flyoutOptionImage: undefined,
            flyoutOptionText: "MasterCard"
        },
        {
            flyoutOptionImage: undefined,
            flyoutOptionText: "American Express"
        }
    ]
}

// Create the button, option templates and click events
_createFlyoutInvokerControl(flyout, cardTypes, function cardTypeClicked(cardType){
    console.log("User clicked on " + cardType.flyoutOptionText);
    console.log("Card Types has .selected " + cardTypes.selected);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment