Angular Foundation

With love, from Pinecone


Download v0.8.0 Contribute

Dependencies

This repository contains a set of native AngularJS directives based on Foundation's markup and CSS. As a result no dependency on jQuery or Foundation's JavaScript is required. The only required dependencies are:

  • AngularJS 1.3 or 1.4 (tested with 1.3.19 and 1.4.6)
  • Foundation 5 CSS (tested with version 5.5.2).

Downloading

Build files for all directives are distributed in several flavours: minified for production usage, un-minified for development, with or without templates. All the options are described and can be downloaded from here.

Installation

As soon as you've got all the files downloaded and included in your page you just need to declare a dependency on the mm.foundation module:

angular.module('myModule', ['mm.foundation']);

You can fork one of the plunkers from this page to see a working example of what is described here.

This content is straight in the template. {{group.content}}

The body of the accordion group grows to fit the contents

{{item}}
I can have markup, too! This is just some content to illustrate fancy headings.

The accordion directive builds on top of the collapse directive to provide a list of items, with collapsible bodies that are collapsed or expanded by clicking on the item's header.

We can control whether expanding an item will cause the other items to close, using the close-others attribute on accordion.

The body of each accordion group is transcluded in to the body of the collapsible element.


<div ng-controller="AccordionDemoCtrl">
  <label class="checkbox">
    <input type="checkbox" ng-model="oneAtATime">
    Open only one at a time
  </label>

  <accordion close-others="oneAtATime">
    <accordion-group heading="Static Header, initially expanded" is-open="true">
      This content is straight in the template.
    </accordion-group>
    <accordion-group heading="{{group.title}}" ng-repeat="group in groups">
      {{group.content}}
    </accordion-group>
    <accordion-group heading="Dynamic Body Content">
      <p>The body of the accordion group grows to fit the contents</p>
        <button class="button small" ng-click="addItem()">Add Item</button>
        <div ng-repeat="item in items">{{item}}</div>
    </accordion-group>
    <accordion-group is-open="isopen">
        <accordion-heading>
            I can have markup, too! <i class="right" ng-class="{'fa fa-chevron-down': isopen, 'fa fa-chevron-right': !isopen}"></i>
        </accordion-heading>
        This is just some content to illustrate fancy headings.
    </accordion-group>
  </accordion>
</div>
angular.module('foundationDemoApp').controller('AccordionDemoCtrl', function($scope) {
  $scope.oneAtATime = true;

  $scope.groups = [
    {
      title: "Dynamic Group Header - 1",
      content: "Dynamic Group Body - 1"
    },
    {
      title: "Dynamic Group Header - 2",
      content: "Dynamic Group Body - 2"
    }
  ];

  $scope.items = ['Item 1', 'Item 2', 'Item 3'];

  $scope.addItem = function() {
    var newItemNo = $scope.items.length + 1;
    $scope.items.push('Item ' + newItemNo);
  };
});
{{alert.msg}}

Alert is an AngularJS-version of Foundation's alert.

This directive can be used to generate alerts from the dynamic model data (using the ng-repeat directive);

The presence of the "close" attribute determines if a close button is displayed


<div ng-controller="AlertDemoCtrl">
  <alert ng-repeat="alert in alerts" type="alert.type" close="closeAlert($index)">{{alert.msg}}</alert>
  <button class='button' ng-click="addAlert()">Add Alert</button>
</div>
angular.module('foundationDemoApp').controller('AlertDemoCtrl', function($scope) {
  $scope.alerts = [
    { type: 'danger', msg: 'Oh snap! Change a few things up and try submitting again.' },
    { type: 'success round', msg: 'Well done! You successfully read this important alert message.' }
  ];

  $scope.addAlert = function() {
    $scope.alerts.push({msg: "Another alert!"});
  };

  $scope.closeAlert = function(index) {
    $scope.alerts.splice(index, 1);
  };

});

Single toggle

{{singleModel}}

Checkbox

{{checkModel}}

Radio

{{radioModel}}

There are 2 directives that can make a group of buttons to behave like a set of checkboxes or radio buttons.


<div ng-controller="ButtonsCtrl">
    <h4>Single toggle</h4>
    <pre>{{singleModel}}</pre>
    <button type="button" class="button" ng-model="singleModel" btn-checkbox btn-checkbox-true="1" btn-checkbox-false="0">
        Single Toggle
    </button>
    <h4>Checkbox</h4>
    <pre>{{checkModel}}</pre>
    <div class="button-group">
        <button type="button" class="button" ng-model="checkModel.left" btn-checkbox>Left</button>
        <button type="button" class="button" ng-model="checkModel.middle" btn-checkbox>Middle</button>
        <button type="button" class="button" ng-model="checkModel.right" btn-checkbox>Right</button>
    </div>
    <h4>Radio</h4>
    <pre>{{radioModel}}</pre>
    <div class="button-group">
        <button type="button" class="button" ng-model="radioModel" btn-radio="'Left'">Left</button>
        <button type="button" class="button" ng-model="radioModel" btn-radio="'Middle'">Middle</button>
        <button type="button" class="button" ng-model="radioModel" btn-radio="'Right'">Right</button>
    </div>
</div>
angular.module('foundationDemoApp').controller('ButtonsCtrl', function ($scope) {

  $scope.singleModel = 1;

  $scope.radioModel = 'Middle';

  $scope.checkModel = {
    left: false,
    middle: true,
    right: false
  };
});

Resize the window to update the content

Interchange uses media queries to dynamically load responsive content that is appropriate for different screen sizes.

You can use Interchange with different content types:

  • HTML templates: by linking a rule with html files to a div tag
  • Images: through an interchange rule to an img tag
  • Background-images through linking a rule with picture files to a div tag

Custom named queries are available via the method add of the interchangeQueriesManager factory. You just need to provide the name and media type desired.

Like the original, replace events are available when an Interchange element switch to another media query.

For more information, check out the docs for the original Interchange component.

Images courtesy of the official ZURB Foundation documentation.


<img interchange="[assets/img/space-small.jpg, (small)],
                  [assets/img/space-medium.jpg, (medium)],
                  [assets/img/space-large.jpg, (large)]"
     src="assets/img/space-small.jpg">

<p class="panel">Resize the window to update the content</p>
// No controller required

How to use

Just use the standard layout for an offcanvas page as documented in the foundation docs

As long as you include mm.foundation.offcanvas it should simply work

A lightweight directive that provides the Foundation Offcanvas component.

There are no settings. You simply need to include the foundation off canvas CSS component in your page.

The off canvas module expects the use of several nested elements with the following classes:

  • off-canvas-wrap: The most outter page wrapper.
  • inner-wrap: Second page wrapper nested directly inside off-canvas-wrap.
  • left-off-canvas-toggle: Wraps the left off canvas menu.
  • right-off-canvas-toggle: Wraps the right off canvas menu.
  • exit-off-canvas: Occludes the main page content when an off canvas menu is visible. Hides the menu when clicked.
  • off-canvas-list: Contains off canvas menu items. Hides the menu after a nested link is clicked.

See the demo page for example on how to use this and see the Foundation docs for more details.


<div ng-controller="OffCanvasDemoCtrl">
    <div class="off-canvas-wrap">
        <div class="inner-wrap">
            <nav class="tab-bar">
                <section class="left-small">
                    <a class="left-off-canvas-toggle menu-icon" ><span></span></a>
                </section>

                <section class="middle tab-bar-section">
                    <h1 class="title">OffCanvas</h1>
                </section>

                <section class="right-small">
                    <a class="right-off-canvas-toggle menu-icon" ><span></span></a>
                </section>

            </nav>

            <aside class="left-off-canvas-menu">
                <ul class="off-canvas-list">
                    <li><a href="#">Left Sidebar</a></li>
                </ul>
            </aside>
            <aside class="right-off-canvas-menu">
                <ul class="off-canvas-list">
                    <li><a href="#">Right Sidebar</a></li>
                </ul>
            </aside>
            <section class="main-section">
                <div class="small-12 columns">
                    <h1>How to use</h1>
                    <p>Just use the standard layout for an offcanvas page as documented in the <a href="http://foundation.zurb.com/docs/components/offcanvas.html">foundation docs</a></p>
                    <p>As long as you include mm.foundation.offcanvas it should simply work</p>
                </div>
            </section>

            <a class="exit-off-canvas"></a>
        </div>
    </div>
</div>
angular.module('foundationDemoApp').controller('OffCanvasDemoCtrl', function ($scope) {

});

Default

The selected page no: {{currentPage}}

Pager


Limit the maximum visible buttons

Page: {{bigCurrentPage}} / {{numPages}}

A lightweight pagination directive that is focused on ... providing pagination & will take care of visualising a pagination bar and enable / disable buttons correctly!

Pagination Settings

Settings can be provided as attributes in the <pagination> or globally configured through the paginationConfig.

  • page : Current page number. First page is 1.

  • total-items : Total number of items in all pages.

  • items-per-page (Defaults: 10) : Maximum number of items per page. A value less than one indicates all items on one page.

  • max-size (Defaults: null) : Limit number for pagination size.

  • num-pages readonly (Defaults: angular.noop) : An optional expression assigned the total number of pages to display.

  • rotate (Defaults: true) : Whether to keep current page in the middle of the visible ones.

  • on-select-page (page) (Default: null) : An optional expression called when a page is selected having the page number as argument.

  • direction-links (Default: true) : Whether to display Previous / Next buttons.

  • previous-text (Default: 'Previous') : Text for Previous button.

  • next-text (Default: 'Next') : Text for Next button.

  • boundary-links (Default: false) : Whether to display First / Last buttons.

  • first-text (Default: 'First') : Text for First button.

  • last-text (Default: 'Last') : Text for Last button.

Pager Settings

Settings can be provided as attributes in the <pager> or globally configured through the pagerConfig.
For page, total-items, items-per-page, num-pages and on-select-page (page) see pagination settings. Other settings are:

  • align (Default: true) : Whether to align each link to the sides.

  • previous-text (Default: '« Previous') : Text for Previous button.

  • next-text (Default: 'Next »') : Text for Next button.


<div ng-controller="PaginationDemoCtrl">
    <h4>Default</h4>
    <pagination total-items="totalItems" page="currentPage"></pagination>
    <pagination boundary-links="true" total-items="totalItems" page="currentPage" class="pagination-sm" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></pagination>
    <pagination direction-links="false" boundary-links="true" total-items="totalItems" page="currentPage"></pagination>
    <pagination direction-links="false" total-items="totalItems" page="currentPage" num-pages="smallnumPages"></pagination>
    <pre>The selected page no: {{currentPage}}</pre>
    <button class="button secondary" ng-click="setPage(3)">Set current page to: 3</button>

    <hr />
    <h4>Pager</h4>
    <pager total-items="totalItems" page="currentPage"></pager>

    <hr />
    <h4>Limit the maximum visible buttons</h4>
    <pagination total-items="bigTotalItems" page="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true"></pagination>
    <pagination total-items="bigTotalItems" page="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false" num-pages="numPages"></pagination>
    <pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>
</div>
angular.module('foundationDemoApp').controller('PaginationDemoCtrl', function ($scope) {
  $scope.totalItems = 64;
  $scope.currentPage = 4;
  $scope.maxSize = 5;
  
  $scope.setPage = function (pageNo) {
    $scope.currentPage = pageNo;
  };

  $scope.bigTotalItems = 175;
  $scope.bigCurrentPage = 1;
});

Dynamic


Positional


Triggers


Other

A lightweight, extensible directive for fancy popover creation. The popover directive supports multiple placements, optional transition animation, and more.

Like the Bootstrap jQuery plugin, the popover requires the tooltip module.

The popover directives provides several optional attributes to control how it will display:

  • popover-title: A string to display as a fancy title.
  • popover-placement: Where to place it? Defaults to "top", but also accepts "bottom", "left", "right".
  • popover-animation: Should it fade in and out? Defaults to "true".
  • popover-popup-delay: For how long should the user have to have the mouse over the element before the popover shows (in milliseconds)? Defaults to 0.
  • popover-trigger: What should trigger the show of the popover? See the tooltip directive for supported values.
  • popover-append-to-body: Should the tooltip be appended to $body instead of the parent element?

The popover directives require the $position service.

The popover directive also supports various default configurations through the $tooltipProvider. See the tooltip section for more information.


<div ng-controller="PopoverDemoCtrl">
    <h4>Dynamic</h4>
    <div class="form-group">
      <label>Popup Text:</label>
      <input type="text" ng-model="dynamicPopover" class="form-control">
    </div>
    <div class="form-group">
      <label>Popup Title:</label>
      <input type="text" ng-model="dynamicPopoverTitle" class="form-control">
    </div>
    <button popover="{{dynamicPopover}}"
      popover-title="{{dynamicPopoverTitle}}" class="button">Dynamic Popover</button>
    
    <hr />
    <h4>Positional</h4>
    <button popover-placement="top" popover="On the Top!" class="button">Top</button>
    <button popover-placement="left" popover="On the Left!" class="button">Left</button>
    <button popover-placement="right" popover="On the Right!" class="button">Right</button>
    <button popover-placement="bottom" popover="On the Bottom!" class="button">Bottom</button>
    
    <hr />
    <h4>Triggers</h4>
    <p>
      <button popover="I appeared on mouse enter!" popover-trigger="mouseenter" class="button">Mouseenter</button>
    </p>
    <input type="text" value="Click me!" popover="I appeared on focus! Click away and I'll vanish..."  popover-trigger="focus" class="form-control">

    <hr />
    <h4>Other</h4>
    <button Popover-animation="true" popover="I fade in and out!" class="button">fading</button>
    <button popover="I have a title!" popover-title="The title." class="button">title</button>
</div>
angular.module('foundationDemoApp').controller('PopoverDemoCtrl', function ($scope) {
  $scope.dynamicPopover = "Hello, World!";
  $scope.dynamicPopoverTitle = "Title";
});

Static

22%
166 / 200

Dynamic

{{dynamic}} / {{max}} No animation {{dynamic}}% Object (changes type based on value) {{type}} !!! Watch out !!!

A progress bar directive that is focused on providing feedback on the progress of a workflow or action.

It supports multiple (stacked) bars into the same <progress> element or a single <progressbar> element with optional max attribute and transition animations.

Settings

<progressbar>

  • value : The current value of progress completed.

  • type (Default: null) : Style type. Possible values are 'success', 'warning' etc.

  • max (Default: 100) : A number that specifies the total value of bars that is required.

  • animate (Default: true) : Whether bars use transitions to achieve the width change. IMPORTANT: Foundation's CSS does not not apply a transition to progress bars. In order for animations to work, you'll need to apply a transition in your own CSS. See the demo for an example.


<div ng-controller="ProgressDemoCtrl">
    <style>
      .progress .meter {
        -webkit-transition: width 1s ease-out;
        transition: width 1s ease-out;
      }
    </style>

    <h3>Static</h3>
    <div class="row">
        <div class="col-sm-4"><progressbar value="55"></progressbar></div>
        <div class="col-sm-4"><progressbar value="22" type="warning">22%</progressbar></div>
        <div class="col-sm-4"><progressbar max="200" value="166" type="alert"><i>166 / 200</i></progressbar></div>
    </div>

    <hr />
    <h3>Dynamic <button class="button small" type="button" ng-click="random()">Randomize</button></h3>
    <progressbar max="max" value="dynamic"><span style="color:black; white-space:nowrap;">{{dynamic}} / {{max}}</span></progressbar>

    <small><em>No animation</em></small>
    <progressbar animate="false" value="dynamic" type="success"><b>{{dynamic}}%</b></progressbar>

    <small><em>Object (changes type based on value)</em></small>
    <progressbar class="progress-striped active" value="dynamic" type="{{type}}">{{type}} <i ng-show="showWarning">!!! Watch out !!!</i></progressbar>

</div>
angular.module('foundationDemoApp').controller('ProgressDemoCtrl', function ($scope) {
  
  $scope.max = 200;

  $scope.random = function() {
    var value = Math.floor((Math.random() * 100) + 1);
    var type;

    if (value < 25) {
      type = 'success';
    } else if (value < 50) {
      type = 'info';
    } else if (value < 75) {
      type = 'warning';
    } else {
      type = 'alert';
    }

    $scope.showWarning = (type === 'alert' || type === 'warning');

    $scope.dynamic = value;
    $scope.type = type;
  };
  $scope.random();
});

Default

{{percent}}%
Rate: {{rate}} - Readonly is: {{isReadonly}} - Hovering over: {{overStar || "none"}}

Custom icons

(Rate: {{x}})
(Rate: {{y}})

Rating directive that will take care of visualising a star rating bar.

It uses Font Awesome icons (http://fontawesome.io/) by default.

Settings

<rating>

  • value : The current rate.

  • max (Defaults: 5) : Changes the number of icons.

  • readonly (Defaults: false) : Prevent user's interaction.

  • on-hover(value) : An optional expression called when user's mouse is over a particular icon.

  • on-leave() : An optional expression called when user's mouse leaves the control altogether.

  • state-on (Defaults: null) : A variable used in template to specify the state (class, src, etc) for selected icons.

  • state-off (Defaults: null) : A variable used in template to specify the state for unselected icons.

  • rating-states (Defaults: null) : An array of objects defining properties for all icons. In default template, stateOn & stateOff property is used to specify the icon's class.


<div ng-controller="RatingDemoCtrl">
    <h4>Default</h4>
    <rating value="rate" max="max" readonly="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null"></rating>
    <span class="label" ng-class="{'label-warning': percent<30, 'label-info': percent>=30 && percent<70, 'label-success': percent>=70}" ng-show="overStar && !isReadonly">{{percent}}%</span>

    <pre style="margin:15px 0;">Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>

    <button class="button small alert" ng-click="rate = 0" ng-disabled="isReadonly">Clear</button>
    <button class="button small" ng-click="isReadonly = ! isReadonly">Toggle Readonly</button>
    <hr />

    <h4>Custom icons</h4>
    <div ng-init="x = 5">
      <rating value="x" max="15" state-on="'fa-check-circle'" state-off="'fa-check-circle-o'"></rating>
      <b>(<i>Rate:</i> {{x}})</b>
    </div>
    <div ng-init="y = 2">
      <rating value="y" rating-states="ratingStates"></rating>
      <b>(<i>Rate:</i> {{y}})</b>
    </div>
</div>
angular.module('foundationDemoApp').controller('RatingDemoCtrl', function ($scope) {
  $scope.rate = 7;
  $scope.max = 10;
  $scope.isReadonly = false;

  $scope.hoveringOver = function(value) {
    $scope.overStar = value;
    $scope.percent = 100 * (value / $scope.max);
  };

  $scope.ratingStates = [
    {stateOn: 'fa-check-circle', stateOff: 'fa-check-circle-o'},
    {stateOn: 'fa-star', stateOff: 'fa-start-o'},
    {stateOn: 'fa-heart', stateOff: 'fa-ban'},
    {stateOn: 'fa-heart'},
    {stateOff: 'fa-power-off'}
  ];
});

Select a tab by setting active binding to true:


Static content {{tab.content}} Alert! I've got an HTML heading, and a select callback. Pretty cool!
Vertical content 1 Vertical content 2

Foundation version of the tabs directive.

Settings

<tabset>

  • vertical (Defaults: false) : Whether tabs appear vertically stacked.

  • open-on-load (Defaults: true) : Whether to open an active tab automatically.

<tab>

  • heading or <tab-heading> : Heading text or HTML markup.

  • active (Defaults: false) : Whether tab is currently selected.

  • select() (Defaults: null) : An optional expression called when tab is activated.


<div ng-controller="TabsDemoCtrl">
  <p>Select a tab by setting active binding to true:</p>
  <p>
    <button class="button small" ng-click="tabs[0].active = true">Select second tab</button>
    <button class="button small" ng-click="tabs[1].active = true">Select third tab</button>
  </p>
  <hr />

  <tabset>
    <tab heading="Static title">Static content</tab>
    <tab ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active">
      {{tab.content}}
    </tab>
    <tab select="alertMe()">
      <tab-heading>
        <i class="fa fa-bell"></i> Alert!
      </tab-heading>
      I've got an HTML heading, and a select callback. Pretty cool!
    </tab>
  </tabset>

  <hr />

  <tabset vertical="true" type="navType">
    <tab heading="Vertical 1">Vertical content 1</tab>
    <tab heading="Vertical 2">Vertical content 2</tab>
  </tabset>

  <hr />
</div>
angular.module('foundationDemoApp').controller('TabsDemoCtrl', function ($scope) {
  $scope.tabs = [
    { title:"Dynamic Title 1", content:"Dynamic content 1" },
    { title:"Dynamic Title 2", content:"Dynamic content 2" }
  ];

  $scope.alertMe = function() {
    setTimeout(function() {
      alert("You've selected the alert tab!");
    });
  };
});

Pellentesque {{dynamicTooltipText}}, sit amet venenatis urna cursus eget nunc scelerisque viverra mauris, in aliquam. Tincidunt lobortis feugiat vivamus at left eget arcu dictum varius duis at consectetur lorem. Vitae elementum curabitur right nunc sed velit dignissim sodales ut eu sem integer vitae. Turpis egestas bottom pharetra convallis posuere morbi leo urna, fading at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus delayed turpis massa tincidunt dui ut.

I can even contain HTML. Check me out!

A lightweight, extensible directive for fancy tooltip creation. The tooltip directive supports multiple placements, optional transition animation, and more.

There are two versions of the tooltip: tooltip and tooltip-html-unsafe. The former takes text only and will escape any HTML provided. The latter takes whatever HTML is provided and displays it in a tooltip; it called "unsafe" because the HTML is not sanitized. The user is responsible for ensuring the content is safe to put into the DOM!

The tooltip directives provide several optional attributes to control how they will display:

  • tooltip-placement: Where to place it? Defaults to "top", but also accepts "bottom", "left", "right".
  • tooltip-animation: Should it fade in and out? Defaults to "true".
  • tooltip-popup-delay: For how long should the user have to have the mouse over the element before the tooltip shows (in milliseconds)? Defaults to 0.
  • tooltip-trigger: What should trigger a show of the tooltip?
  • tooltip-append-to-body: Should the tooltip be appended to $body instead of the parent element?

The tooltip directives require the $position service.

Triggers

The following show triggers are supported out of the box, along with their provided hide triggers:

  • mouseenter: mouseleave
  • click: click
  • focus: blur

For any non-supported value, the trigger will be used to both show and hide the tooltip.

$tooltipProvider

Through the $tooltipProvider, you can change the way tooltips and popovers behave by default; the attributes above always take precedence. The following methods are available:

  • setTriggers( obj ): Extends the default trigger mappings mentioned above with mappings of your own. E.g. { 'openTrigger': 'closeTrigger' }.
  • options( obj ): Provide a set of defaults for certain tooltip and popover attributes. Currently supports 'placement', 'animation', 'popupDelay', and appendToBody. Here are the defaults:

    placement: 'top',
    animation: true,
    popupDelay: 0,
    appendToBody: false
    

<div ng-controller="TooltipDemoCtrl">
    <div class="form-group">
      <label>Dynamic Tooltip Text</label>
      <input type="text" ng-model="dynamicTooltipText" class="form-control">
    </div>
    <div class="form-group">
      <label>Dynamic Tooltip Popup Text</label>
      <input type="text" ng-model="dynamicTooltip" class="form-control">
    </div>
    <p>
      Pellentesque <a href="#" class="has-tip" tooltip="{{dynamicTooltip}}">{{dynamicTooltipText}}</a>,
      sit amet venenatis urna cursus eget nunc scelerisque viverra mauris, in
      aliquam. Tincidunt lobortis feugiat vivamus at 
      <a href="#" class="has-tip" tooltip-placement="left" tooltip="On the Left!">left</a> eget
      arcu dictum varius duis at consectetur lorem. Vitae elementum curabitur
      <a href="#" class="has-tip" tooltip-placement="right" tooltip="On the Right!">right</a> 
      nunc sed velit dignissim sodales ut eu sem integer vitae. Turpis egestas 
      <a href="#" class="has-tip" tooltip-placement="bottom" tooltip="On the Bottom!">bottom</a> 
      pharetra convallis posuere morbi leo urna, 
      <a href="#" class="has-tip" tooltip-animation="false" tooltip="I don't fade. :-(">fading</a>
      at elementum eu, facilisis sed odio morbi quis commodo odio. In cursus
      <a href="#" class="has-tip" tooltip-popup-delay='1000' tooltip='appears with delay'>delayed</a> turpis massa tincidunt dui ut.
    </p>

    <p>
      I can even contain HTML. <a href="#" class="has-tip" tooltip-html-unsafe="{{htmlTooltip}}">Check me out!</a>
    </p>

    <form role="form">
      <div class="form-group">
        <label>Or use custom triggers, like focus: </label>
        <input type="text" value="Click me!" tooltip="See? Now click away..."  tooltip-trigger="focus" tooltip-placement="right" class="form-control" />
      </div>
    </form>
</div>
angular.module('foundationDemoApp').controller('TooltipDemoCtrl', function ($scope) {
  $scope.dynamicTooltip = "Hello, World!";
  $scope.dynamicTooltipText = "dynamic";
  $scope.htmlTooltip = "I've been made <b>bold</b>!";
});

A directive that provides the Foundation Top Bar component.

The directive has virtually identical behavior to Foundation Top Bar. The markup however, is slightly different. The top bar consist of a root top-bar element with nested top-bar-section elements that encapsulate menus. The title-area list is also a direct descendant of the root element. The mobile menu toggle is created by adding the toggle-top-bar attribute to a li element inside the title area. Applying menu-icon class to this element will trigger the icon as determined by the Foundation CSS. li elements that contain nested menus need to have the has-dropdown attribute. The nested list itself should have the top-bar-dropdown attribute.

The following settings can be applied as attributes to the top-bar element:

  • sticky-class: Class name that will trigger sticky behavior.
  • custom-back-text: Set this to false and it will pull the top level link name as the back text.
  • back-text: Define what you want your custom back text to be if custom-back-text is set.
  • is-hover: Toggle drop down menus on hover.
  • mobile-show-parent-link: will copy parent links into dropdowns for mobile navigation.
  • scrolltop: jump to top when sticky nav menu toggle is clicked

See the demo page for example on how to use this and visit the Foundation docs for more details.


<div ng-controller="TopBarDemoCtrl">
  <top-bar>
    <ul class="title-area">
      <li class="name">
        <h1><a href="#">My Site</a></h1>
      </li>
      <li toggle-top-bar class="menu-icon"><a href="#">Menu</a></li>
    </ul>

    <top-bar-section>
      <!-- Right Nav Section -->
      <ul class="right">
        <li class="active"><a href="#">Active</a></li>
        <li has-dropdown>
          <a href="#">Dropdown</a>
          <ul top-bar-dropdown>
            <li><a href="#">First link in dropdown</a></li>
          </ul>
        </li>
      </ul>

      <!-- Left Nav Section -->
      <ul class="left">
        <li><a href="#">Left</a></li>
      </ul>
    </top-bar-section>
  </top-bar>
</div>
angular.module('foundationDemoApp').controller('TopBarDemoCtrl', function ($scope) {

});
First tour step points here
Second tour step points here
Third tour step points here

A $tour service to give users of your website or app a tour when they visit. It works in conjunction with a step-text directive. This is an angular-focused refactoring of the Foundation Joyride component.

The step directive requires the tooltip module and supports multiple placements, optional transition animation, and more.

Required attributes are:

  • step-text: The text to show in the tour popup when the tour is focused on the element.
  • step-index: The order in which the tour should focus on the element strting with 1.

The step-text directive provides several optional attributes to control how it will display:

  • step-title: A string to display as a fancy title.
  • step-placement: Where to place it? Defaults to "top", but also accepts "bottom", "left", "right".
  • step-animation: Should it fade in and out? Defaults to "true".
  • step-popup-delay: For how long should the user have to have the mouse over the element before the step shows (in milliseconds)? Defaults to 0.
  • step-trigger: What should trigger the show of the step? See the tooltip directive for supported values.
  • step-append-to-body: Should the tooltip be appended to $body instead of the parent element?

The step directive requires the $position service.

The step directive also supports various default configurations through the $tooltipProvider. See the tooltip section for more information.


<div ng-controller="TourDemoCtrl">
  <button ng-click="startTour()">Start tour</button>
  <div class="panel"
    step-text="Hello, this is the first step of the tour."
    step-index="1"
    step-placement="bottom">
    First tour step points here
  </div>
  <div class="panel"
    step-text="This is another step of the tour."
    step-index="2"
    step-placement="right">
    Second tour step points here
  </div>
  <div class="panel"
    step-text="Last step. Finally."
    step-index="3">
    Third tour step points here
  </div>
</div>
angular.module('foundationDemoApp').controller('TourDemoCtrl', function ($scope, $tour) {
  $scope.startTour = $tour.start;
});

Static arrays

Model: {{selected | json}}

Asynchronous results

Model: {{asyncSelected | json}}

Custom templates for results

Model: {{customSelected | json}}

Typeahead is a AngularJS version of Bootstrap v2's typeahead plugin. This directive can be used to quickly create elegant typeaheads with any form text input.

It is very well integrated into AngularJS as it uses a subset of the select directive syntax, which is very flexible. Supported expressions are:

  • label for value in sourceArray
  • select as label for value in sourceArray

The sourceArray expression can use a special $viewValue variable that corresponds to the value entered inside the input.

This directive works with promises, meaning you can retrieve matches using the $http service with minimal effort.

The typeahead directives provide several attributes:

  • ng-model : Assignable angular expression to data-bind to

  • typeahead : Comprehension Angular expression (see select directive)

  • typeahead-editable (Defaults: true) : Should it restrict model values to the ones selected from the popup only ?

  • typeahead-input-formatter (Defaults: undefined) : Format the ng-model result after selection

  • typeahead-loading (Defaults: angular.noop) : Binding to a variable that indicates if matches are being retrieved asynchronously

  • typeahead-min-length (Defaults: 1) : Minimal no of characters that needs to be entered before typeahead kicks-in

  • typeahead-on-select (Defaults: null) : A callback executed when a match is selected

  • typeahead-template-url : Set custom item template

  • typeahead-wait-ms (Defaults: 0) : Minimal wait time after last character typed before typeahead kicks-in


<script type="text/ng-template" id="customTemplate.html">
  <a>
      <img ng-src="http://upload.wikimedia.org/wikipedia/commons/thumb/{{match.model.flag}}" width="16">
      <span bind-html-unsafe="match.label | typeaheadHighlight:query"></span>
  </a>
</script>
<div class='container-fluid' ng-controller="TypeaheadCtrl">

    <h4>Static arrays</h4>
    <pre>Model: {{selected | json}}</pre>
    <input type="text" ng-model="selected" typeahead="state for state in states | filter:$viewValue | limitTo:8" class="form-control">

    <h4>Asynchronous results</h4>
    <pre>Model: {{asyncSelected | json}}</pre>
    <input type="text" ng-model="asyncSelected" placeholder="Locations loaded via $http" typeahead="address for address in getLocation($viewValue)" typeahead-loading="loadingLocations" class="form-control">
    <i ng-show="loadingLocations" class="glyphicon glyphicon-refresh"></i>

    <h4>Custom templates for results</h4>
    <pre>Model: {{customSelected | json}}</pre>
    <input type="text" ng-model="customSelected" placeholder="Custom template" typeahead="state as state.name for state in statesWithFlags | filter:{name:$viewValue}" typeahead-template-url="customTemplate.html" class="form-control">
</div>
angular.module('foundationDemoApp').controller('TypeaheadCtrl', function($scope, $http) {

  $scope.selected = undefined;
  $scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
  // Any function returning a promise object can be used to load values asynchronously
  $scope.getLocation = function(val) {
    return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
      params: {
        address: val,
        sensor: false
      }
    }).then(function(res){
      var addresses = [];
      angular.forEach(res.data.results, function(item){
        addresses.push(item.formatted_address);
      });
      return addresses;
    });
  };

  $scope.statesWithFlags = [{"name":"Alabama","flag":"5/5c/Flag_of_Alabama.svg/45px-Flag_of_Alabama.svg.png"},{"name":"Alaska","flag":"e/e6/Flag_of_Alaska.svg/43px-Flag_of_Alaska.svg.png"},{"name":"Arizona","flag":"9/9d/Flag_of_Arizona.svg/45px-Flag_of_Arizona.svg.png"},{"name":"Arkansas","flag":"9/9d/Flag_of_Arkansas.svg/45px-Flag_of_Arkansas.svg.png"},{"name":"California","flag":"0/01/Flag_of_California.svg/45px-Flag_of_California.svg.png"},{"name":"Colorado","flag":"4/46/Flag_of_Colorado.svg/45px-Flag_of_Colorado.svg.png"},{"name":"Connecticut","flag":"9/96/Flag_of_Connecticut.svg/39px-Flag_of_Connecticut.svg.png"},{"name":"Delaware","flag":"c/c6/Flag_of_Delaware.svg/45px-Flag_of_Delaware.svg.png"},{"name":"Florida","flag":"f/f7/Flag_of_Florida.svg/45px-Flag_of_Florida.svg.png"},{"name":"Georgia","flag":"5/54/Flag_of_Georgia_%28U.S._state%29.svg/46px-Flag_of_Georgia_%28U.S._state%29.svg.png"},{"name":"Hawaii","flag":"e/ef/Flag_of_Hawaii.svg/46px-Flag_of_Hawaii.svg.png"},{"name":"Idaho","flag":"a/a4/Flag_of_Idaho.svg/38px-Flag_of_Idaho.svg.png"},{"name":"Illinois","flag":"0/01/Flag_of_Illinois.svg/46px-Flag_of_Illinois.svg.png"},{"name":"Indiana","flag":"a/ac/Flag_of_Indiana.svg/45px-Flag_of_Indiana.svg.png"},{"name":"Iowa","flag":"a/aa/Flag_of_Iowa.svg/44px-Flag_of_Iowa.svg.png"},{"name":"Kansas","flag":"d/da/Flag_of_Kansas.svg/46px-Flag_of_Kansas.svg.png"},{"name":"Kentucky","flag":"8/8d/Flag_of_Kentucky.svg/46px-Flag_of_Kentucky.svg.png"},{"name":"Louisiana","flag":"e/e0/Flag_of_Louisiana.svg/46px-Flag_of_Louisiana.svg.png"},{"name":"Maine","flag":"3/35/Flag_of_Maine.svg/45px-Flag_of_Maine.svg.png"},{"name":"Maryland","flag":"a/a0/Flag_of_Maryland.svg/45px-Flag_of_Maryland.svg.png"},{"name":"Massachusetts","flag":"f/f2/Flag_of_Massachusetts.svg/46px-Flag_of_Massachusetts.svg.png"},{"name":"Michigan","flag":"b/b5/Flag_of_Michigan.svg/45px-Flag_of_Michigan.svg.png"},{"name":"Minnesota","flag":"b/b9/Flag_of_Minnesota.svg/46px-Flag_of_Minnesota.svg.png"},{"name":"Mississippi","flag":"4/42/Flag_of_Mississippi.svg/45px-Flag_of_Mississippi.svg.png"},{"name":"Missouri","flag":"5/5a/Flag_of_Missouri.svg/46px-Flag_of_Missouri.svg.png"},{"name":"Montana","flag":"c/cb/Flag_of_Montana.svg/45px-Flag_of_Montana.svg.png"},{"name":"Nebraska","flag":"4/4d/Flag_of_Nebraska.svg/46px-Flag_of_Nebraska.svg.png"},{"name":"Nevada","flag":"f/f1/Flag_of_Nevada.svg/45px-Flag_of_Nevada.svg.png"},{"name":"New Hampshire","flag":"2/28/Flag_of_New_Hampshire.svg/45px-Flag_of_New_Hampshire.svg.png"},{"name":"New Jersey","flag":"9/92/Flag_of_New_Jersey.svg/45px-Flag_of_New_Jersey.svg.png"},{"name":"New Mexico","flag":"c/c3/Flag_of_New_Mexico.svg/45px-Flag_of_New_Mexico.svg.png"},{"name":"New York","flag":"1/1a/Flag_of_New_York.svg/46px-Flag_of_New_York.svg.png"},{"name":"North Carolina","flag":"b/bb/Flag_of_North_Carolina.svg/45px-Flag_of_North_Carolina.svg.png"},{"name":"North Dakota","flag":"e/ee/Flag_of_North_Dakota.svg/38px-Flag_of_North_Dakota.svg.png"},{"name":"Ohio","flag":"4/4c/Flag_of_Ohio.svg/46px-Flag_of_Ohio.svg.png"},{"name":"Oklahoma","flag":"6/6e/Flag_of_Oklahoma.svg/45px-Flag_of_Oklahoma.svg.png"},{"name":"Oregon","flag":"b/b9/Flag_of_Oregon.svg/46px-Flag_of_Oregon.svg.png"},{"name":"Pennsylvania","flag":"f/f7/Flag_of_Pennsylvania.svg/45px-Flag_of_Pennsylvania.svg.png"},{"name":"Rhode Island","flag":"f/f3/Flag_of_Rhode_Island.svg/32px-Flag_of_Rhode_Island.svg.png"},{"name":"South Carolina","flag":"6/69/Flag_of_South_Carolina.svg/45px-Flag_of_South_Carolina.svg.png"},{"name":"South Dakota","flag":"1/1a/Flag_of_South_Dakota.svg/46px-Flag_of_South_Dakota.svg.png"},{"name":"Tennessee","flag":"9/9e/Flag_of_Tennessee.svg/46px-Flag_of_Tennessee.svg.png"},{"name":"Texas","flag":"f/f7/Flag_of_Texas.svg/45px-Flag_of_Texas.svg.png"},{"name":"Utah","flag":"f/f6/Flag_of_Utah.svg/45px-Flag_of_Utah.svg.png"},{"name":"Vermont","flag":"4/49/Flag_of_Vermont.svg/46px-Flag_of_Vermont.svg.png"},{"name":"Virginia","flag":"4/47/Flag_of_Virginia.svg/44px-Flag_of_Virginia.svg.png"},{"name":"Washington","flag":"5/54/Flag_of_Washington.svg/46px-Flag_of_Washington.svg.png"},{"name":"West Virginia","flag":"2/22/Flag_of_West_Virginia.svg/46px-Flag_of_West_Virginia.svg.png"},{"name":"Wisconsin","flag":"2/22/Flag_of_Wisconsin.svg/45px-Flag_of_Wisconsin.svg.png"},{"name":"Wyoming","flag":"b/bc/Flag_of_Wyoming.svg/43px-Flag_of_Wyoming.svg.png"}];
});

.fade {
  opacity: 0;
  -webkit-transition: opacity .15s linear;
          transition: opacity .15s linear;
}
.fade.in {
  opacity: 1;
}

.reveal-modal.fade {
  -webkit-transition: -webkit-transform .3s ease-out;
     -moz-transition:    -moz-transform .3s ease-out;
       -o-transition:      -o-transform .3s ease-out;
          transition:         transform .3s ease-out;
  -webkit-transform: translate(0, -25%);
      -ms-transform: translate(0, -25%);
          transform: translate(0, -25%);
}
.reveal-modal.in {
  -webkit-transform: translate(0, 0);
      -ms-transform: translate(0, 0);
          transform: translate(0, 0);
}

.reveal-modal-bg.fade {
  filter: alpha(opacity=0);
  opacity: 0;
}
.reveal-modal-bg.in {
  filter: alpha(opacity=50);
  opacity: .5;
}