Getting started
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.5 (tested with 1.5.10)
- Foundation 6 CSS (tested with version 6.4.3).
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.
Accordion (mm.foundation.accordion)
The body of the accordion group grows to fit the contents
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!
</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 (mm.foundation.alert)
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: 'alert', 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({type: 'alert', msg: "Another alert!"});
};
$scope.closeAlert = function(index) {
$scope.alerts.splice(index, 1);
};
});
Buttons (mm.foundation.buttons)
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
};
});
Drilldown Menu (mm.foundation.drilldownMenu)
Auto `Wrapper` and `Back` Links
Manual `Wrapper` and `Back` Links
API Usage
Other API functions
A directive that provides the Foundation Drilldown Menu component.
The directive relies on the same markup as the original Foundation Drilldown Menu. It implements some, but not all of the features of the Foundation Drilldown Menu.
See the demo page for example on how to use this and visit the Foundation docs for more details.
Supported Features:
- Automatically add a wrapper div around the menu, if not already provided in the html.
- Automatically add back entries to the top of submenus, if not already provided in the html.
- Wrapper div sized to the min-height required to show the largest sub-menu.
- Automatically resizes as the viewport size changes.
- WARNING: show/hide via
ng-show
or similar DOES NOT cause a resize. See notes below.
- API exposed via
drilldown-menu-api
two-way binding. - Events
emit
-ed on the $scope (i.e. upwards towards parent scopes).
API
The API for programmatic control of the drilldown menu is exposed to the parent
scope via the drilldown-menu-api
two-way binding. See the 3rd example html and
associated js for an example.
The available methods are:
resizeMenu()
to trigger a resize.show(element)
to show the submenu under the given angular.element.hide(element)
to hide the submenu under the given angular.element.hideAll()
to collapse all submenus.
Events
The controller emit-s
events on the $scope (i.e. upwards towards the parent scope). The available events are similarly named to the Foundation for Sites equivalents.
'resize.mm.foundation.drilldownMenu'
when the menu resizes. The event listener function format isfunction(event, menuElement)
:event
- Standard Angularevent
objectmenuElement
- angular.element for the top level UL element of the menu.
'open.mm.foundation.drilldownMenu'
when a submenu is opened. The event listener function format isfunction(event, menuElement, submenuElement)
:event
- Standard Angularevent
objectmenuElement
- angular.element for the top level UL element of the menu.submenuElement
- angular.element for the UL element at the top of the submenu that has just been opened.
'hide.mm.foundation.drilldownMenu'
when a submenu is closed. The event listener function format isfunction(event, menuElement, submenuElement)
as above.
Usage Notes
Be careful when dynamically showing and hiding this component. The component only resizes when created, when the viewport size changes, or when the resize() API is triggered. If it is hidden via css when created it will initialse with 0 height and width. If it is then shown (using ng-show
or similar), it will remain as 0 height and width and thus not appear to be visible!
This can be resolved by using the API to resize as part of the reveal.
Alternatively, use ng-if
instead of ng-show
. This re-creates the component when showing, and
thus the size is set correctly as part of the initialisation.
NOT implemented:
The following features of the Foundation Drilldown Menu are NOT implemented:
- back entries at the bottom of submenus.
- templates for auto-added back entries.
autoHeight
.ScrollTop
.- Handling of keyboard events (up/down/etc.)
- Automatic addition of aria attributes.
- Any of the plugin options.
scrollme
andclosed
events._scrollTop
and_destroy
methods.
<div ng-controller="DrillDownDemoCtrl">
<div class="callout">
<h4>Auto `Wrapper` and `Back` Links</h4>
<ul class="drilldown menu" drilldown-menu>
<li>
<a>Item 1</a>
<ul class="menu">
<li><a href="#">Item 1A</a></li>
<li>
<a href="#">Item 1B</a>
<ul class="menu">
<li><a href="#">Item 1B i make this really long so that it will overflow a line</a></li>
<li><a href="#">Item 1B ii</a></li>
<li>
<a href="#">Item 1B iii - this one is also extended to overflow a line</a>
<ul class="menu">
<li><a href="#">Item 1B iii alpha</a></li>
<li><a href="#">Item 1B iii omega</a></li>
</ul>
</li>
<li>
<a href="#">Item 1B iv</a>
<ul class="menu">
<li><a href="#">Item 1B iv alpha</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">Item 1C</a></li>
</ul>
</li>
<li>
<a href="#">Search Engines</a>
<ul class="menu">
<li><a href="https://www.google.com/">Google</a></li>
<li><a href="https://www.bing.com/">Bing</a></li>
<li><a href="https://www.duckduckgo.com/">DuckDuckGo</a></li>
</ul>
</li>
<li><a href="http://foundation.zurb.com/">Foundation</a></li>
<li><a href="#">Item 4</a></li>
</ul>
</div>
<div class="callout">
<!-- Manual Wrapper and Back Links -->
<h4>Manual `Wrapper` and `Back` Links</h4>
<!-- Manually added wrapper MUST be `div` with class `is-drilldown` -->
<div class="is-drilldown">
<ul class="drilldown menu" drilldown-menu>
<li>
<a>Item 1</a>
<ul class="menu">
<!-- Manual back link MUST be `li` with class `.js-drilldown-back` -->
<li class="js-drilldown-back"><a href="#">Manual Back To Top</a></li>
<li><a href="#">Item 1A</a></li>
<li>
<a href="#">Item 1B</a>
<ul class="menu">
<li class="js-drilldown-back"><a href="#">Manual Back To Level 1</a></li>
<li><a href="#">Item 1B i</a></li>
<li><a href="#">Item 1B ii</a></li>
</ul>
</li>
<li><a href="#">Item 1C</a></li>
</ul>
</li>
<li><a href="#">Item 2</a></li>
</ul>
</div>
</div>
<div class="callout" id="drilldown-api">
<!-- API usage -->
<h4>API Usage</h4>
<alert ng-repeat="alert in vm.alerts" type="alert.type" close="vm.closeAlert($index)">
{{alert.msg}}
</alert>
<div>
<button class="button" ng-click="vm.showHideMenu()">Show / Hide the Menu</button>
<button class="button" ng-click="vm.ddmApi.resizeMenu()" ng-show="vm.haveShown">Resize</button>
</div>
<div class="callout ng-hide" ng-show="vm.shouldShow">
<ul class="drilldown menu" id="drilldown-api-menu" drilldown-menu drilldown-menu-api="vm.ddmApi">
<li>
<a href="#">Item 1</a>
<ul class="menu" id="drilldown-api-submenu-1">
<li><a href="#">Item 1A</a></li>
<li>
<a href="#">Item 1B</a>
<ul class="menu">
<li><a href="#">Item 1B i</a></li>
<li><a href="#">Item 1B ii</a></li>
</ul>
</li>
<li><a href="#">Item 1C</a></li>
</ul>
</li>
<li><a href="#">Item 2</a></li>
</ul>
<div ng-show="vm.haveResized">
<h5>Other API functions</h5>
<button class="button" ng-click="vm.showSubmenu1()">Open Submenu 1</button>
<button class="button" ng-click="vm.hideSubmenu1()">Close Submenu 1</button>
<button class="button" ng-click="vm.ddmApi.hideAll()">Close All Submenus</button>
</div>
</div>
</div>
</div>
angular.module('foundationDemoApp')
.controller('DrillDownDemoCtrl', function($scope, $element, $timeout, $log) {
'ngInject';
const vm = {
ddmApi: null,
shouldShow: false,
haveShown: false,
haveResized: false,
alerts: [],
showHideMenu: showHideMenu,
showSubmenu1: showSubmenu1,
hideSubmenu1: hideSubmenu1,
closeAlert: closeAlert,
};
$scope.vm = vm;
/**
* Initial alert used as part of the API Usage demo
*/
vm.alerts = [{
type: 'alert',
msg: 'The menu below is hidden with css. Press the button to show it (using ng-show).',
}];
/**
* Event handlers for the menu events
*/
$scope.$on('resize.mm.foundation.drilldownMenu', onMenuResized);
$scope.$on('open.mm.foundation.drilldownMenu', onSubmenuOpen);
$scope.$on('hide.mm.foundation.drilldownMenu', onSubmenuClosed);
/**
* Sets the variable to show or hide the menu block
*/
function showHideMenu() {
vm.shouldShow = !vm.shouldShow;
if (!vm.haveShown) {
closeAlert(0);
addAlert(
'alert',
'The menu is now "shown", but because it was hidden initially it will start with ' +
'"max-width: 0px" and doesn\'t appear! Press the resize button to trigger ' +
'a resize via the API'
);
vm.haveShown = true;
}
}
/**
* Get the UL for submenu 1.
* NOTE: You MUST use the UL for the submenu to show/hide, NOT the parent LI!
*
* @returns {angular.element} - The element for submenu 1 in the API section
*/
function getSubmenu1() {
return angular.element($element[0].querySelector('ul#drilldown-api-submenu-1'));
}
/**
* Function to use the API to open a specific menu item
*/
function showSubmenu1() {
//
// Find the item
//
var element = getSubmenu1();
//
// Use the API to open it
//
vm.ddmApi.show(element);
}
/**
* Function to use the API to close a specific menu item
*/
function hideSubmenu1() {
//
// Find the item
//
var element = getSubmenu1();
//
// Use the API to open it
//
vm.ddmApi.hide(element);
}
/**
* Handler for the resized event from the menu
*
* @param {Object} event - Angular `event` object (see $rootScope.$on)
* @param {angular.element} menuElement - The root element of the menu
*/
function onMenuResized(event, menuElement) {
if (menuElement.attr('id') !== 'drilldown-api-menu') {
return; // One of the other menus
}
if (vm.haveShown && !vm.haveResized) {
closeAlert(0);
addAlert(
'success',
'Congratulations! The menu should now be visible!'
);
addAlert(
'alert',
'The same 0-width problem will happen again if the menu is hidden then ' +
'resized (via the API or via a viewport size change). ' +
'You MUST trigger a resize via the API after showing it again if this happens.'
);
vm.haveResized = true;
}
}
/**
* Example handler for the submenu open event
*
* @param {Object} event - Angular `event` object (see $rootScope.$on)
* @param {angular.element} menuElement - The root element of the menu
* @param {angular.element} submenuElement - The submenu element that has just opened
*/
function onSubmenuOpen(event, menuElement, submenuElement) {
if (submenuElement.attr('id') === 'drilldown-api-submenu-1') {
$log.log('Submenu 1 has been opened!');
}
}
/**
* Example handler for the submenu close event
*
* @param {Object} event - Angular `event` object (see $rootScope.$on)
* @param {angular.element} menuElement - The root element of the menu
* @param {angular.element} submenuElement - The submenu element that has just closed
*/
function onSubmenuClosed(event, menuElement, submenuElement) {
if (submenuElement.attr('id') === 'drilldown-api-submenu-1') {
$log.log('Submenu 1 has been closed!');
}
}
/**
* Closes the specific alert
*
* @param {number} index - The index of the alert to close
*/
function closeAlert(index) {
vm.alerts.splice(index, 1);
}
/**
* Adds a new alert programmatically.
* Used of the explanations of the drilldown menu API and functionality.
*
* @param {string} type - the type of alert
* @param {string} msg - the alert message
*/
function addAlert(type, msg) {
vm.alerts.push({ type: type, msg: msg });
}
});
Dropdown Menu (mm.foundation.dropdownMenu)
Horisontal menu
Vertical menu
A directive that provides the Foundation Dropdown Menu component.
The directive relies on the same markup as the original Foundation Dropdown Menu.
The following settings can be applied as attributes to the root element with the dropdown-menu
attribute:
disable-hover
: A mouse hover event will no open the root menu.disable-click-open
: Click events will not open menus, only hover (not implemented yet).closing-time
: Amount of time to delay closing a submenu on a mouseleave event. Default is 500ms
See the demo page for example on how to use this and visit the Foundation docs for more details.
<div ng-controller="TopBarDemoCtrl">
<!-- Horisontal -->
<h2>Horisontal menu</h2>
<ul class="dropdown menu" dropdown-menu>
<li>
<a tabindex="0">Item 1</a>
<ul class="menu">
<li><a href="#">Item 1A</a></li>
<li>
<a href="#">Item 1B</a>
<ul class="menu">
<li><a href="#">Item 1B i</a></li>
<li><a href="#">Item 1B ii</a></li>
<li>
<a href="#">Item 1B iii</a>
<ul class="menu">
<li><a href="#">Item 1B iii alpha</a></li>
<li><a href="#">Item 1B iii omega</a></li>
</ul>
</li>
<li>
<a href="#">Item 1B iv</a>
<ul class="menu">
<li><a href="#">Item 1B iv alpha</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">Item 1C</a></li>
</ul>
</li>
<li>
<a href="#">Item 2</a>
<ul class="menu">
<li><a href="#">Item 2A</a></li>
<li><a href="#">Item 2B</a></li>
</ul>
</li>
<li><a href="#">Item 3</a></li>
<li><a href="#">Item 4</a></li>
</ul>
<!-- Vertical -->
<h2>Vertical menu</h2>
<ul class="vertical dropdown menu mm-foundation-6-demo-dropdown-menu" dropdown-menu>
<li>
<a href="#" tabindex="0">Item 1</a>
<ul class="menu">
<li><a href="#">Item 1A Loooong</a></li>
<li><a href="#">Item 1B</a></li>
</ul>
</li>
<li>
<a href="#">Item 2</a>
<ul class="menu">
<li><a href="#">Item 2A</a></li>
<li><a href="#">Item 2B</a></li>
</ul>
</li>
<li>
<a href="#">Item 3</a>
<ul class="menu">
<li><a href="#">Item 3A</a></li>
<li><a href="#">Item 3B</a></li>
</ul>
</li>
<li>
<a href="#">Item 1</a>
<ul class="menu">
<li><a href="#">Item 1A Loooong</a></li>
<li>
<a href="#"> Item 1 sub</a>
<ul class="menu">
<li><a href="#">Item 1 subA</a></li>
<li><a href="#">Item 1 subB</a></li>
<li>
<a href="#"> Item 1 sub</a>
<ul class="menu">
<li><a href="#">Item 1 subA</a></li>
<li><a href="#">Item 1 subB</a></li>
</ul>
</li>
<li>
<a href="#"> Item 1 sub</a>
<ul class="menu">
<li><a href="#">Item 1 subA</a></li>
<li><a href="#">Item 1 subB</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#">Item 1B</a></li>
</ul>
</li>
</ul>
</div>
angular.module('foundationDemoApp').controller('TopBarDemoCtrl', function ($scope) {
});
Dropdown Toggle (mm.foundation.dropdownToggle)
DropdownToggle is a simple directive which will toggle a dropdown link on click. Simply put it on the toggler-element, and it will find the target element and toggle it when the element matching the value of the dropdown-toggle
attribute is clicked.
The following settings can be applied as attributes to the root element:
close-on-click
: Allows a click on the body to close the dropdown.pane-align
: Determines how the dropdown-pane should be aligned from the toggle.toggle-on-hover
: Toggles the dropdown-pane when hovering over the toggle instead of on click.pane-offset
: Sets the pane's vertical distance from the toggle.
The scope of the pane exposes a $close()
function that allows you to close the drop down programatically.
<div ng-controller="DropdownCtrl">
<dropdown-toggle>
<toggle><p><a>Click me for a dropdown-toggle, yo!</a></p></toggle>
<pane>
<ul class="menu vertical">
<li ng-repeat="choice in items">
<a>{{choice}}</a>
</li>
<li>
<a ng-click="$close()">Close</a>
</li>
</ul>
</pane>
</dropdown-toggle>
<dropdown-toggle close-on-click="true" pane-align="right">
<toggle>
<a class="dropdown button large">dropdown-toggles can also have links!</a>
</toggle>
<pane>
<ul class="menu vertical">
<li ng-repeat="(label, url) in linkItems">
<a href="{{url}}" target="_blank">{{label}}</a>
</li>
</ul>
</pane>
</dropdown-toggle>
<div class="button-group">
<a class="button">Primary Action</a>
<dropdown-toggle close-on-click="true" pane-align="center">
<toggle>
<a class="dropdown button arrow-only">
<span class="show-for-sr">Show menu</span>
</a>
</toggle>
<pane>
<ul class="menu vertical">
<li ng-repeat="choice in items">
<a>{{choice}}</a>
</li>
</ul>
</pane>
</dropdown-toggle>
</div>
<dropdown-toggle toggle-on-hover="true" pane-offset="-2">
<toggle>
<a class="dropdown button large">Hover over me!</a>
</toggle>
<pane>
<ul class="menu vertical">
<li ng-repeat="choice in items">
<a>{{choice}}</a>
</li>
</ul>
</pane>
</dropdown-toggle>
</div>
angular.module('foundationDemoApp').controller('DropdownCtrl', function($scope) {
$scope.items = [
"The first choice!",
"And another choice for you.",
"but wait! A third!"
];
$scope.linkItems = {
"Google": "http://google.com",
"AltaVista": "http://altavista.com"
};
});
Modal (mm.foundation.modal)
$modal
is a service to quickly create AngularJS-powered modal windows. It is the equivalent of the Foundation Reveal component.
Creating custom modals is straightforward: create a partial view, its controller and reference them when using the service.
The $modal
service has only one method: open(options)
where available options are like follows:
templateUrl
- a path to a template representing modal's contenttemplate
- inline template representing the modal's contentscope
- a scope instance to be used for the modal's content (actually the$modal
service is going to create a child scope of a provided scope). Defaults to$rootScope
component
- a string reference to the component to be rendered as the modal content.
It supports these bindings:close
- A method that can be used to close a modal, passing a result. The result must be passed in this format:{$value: myResult}
dismiss
- A method that can be used to dismiss a modal, passing a result. The result must be passed in this format:{$value: myRejectedResult}
modalInstance
- The modal instance. This is the same as$modalInstance
injected when usingcontroller
.resolve
- An object containing the resolved values passed in via theresolve
option.
controller
- a controller for a modal instance - it can initialize scope used by modal. Accepts the "controller-as" syntax, and can be injected with$modalInstance
controllerAs
- an alternative to the aforementioned "controller-as" syntax. This is intended for the case of when thecontroller
option is assigned an anonymous or local function and thus the "controller-as" syntax couldn't be applied.resolve
- members that will be resolved and passed to the controller as locals; it is equivalent of theresolve
property for AngularJS routesbackdrop
- controls presence of a backdrop. Allowed values: true (default), false (no backdrop),'static'
- backdrop is present but modal window is not closed when clicking outside of the modal window.keyboard
- indicates whether the dialog should be closable by hitting the ESC key, defaults to truewindowClass
- additional CSS class(es) to be added to a modal window templatesize
- controls the size of the modal. Allowed values:tiny
,small
,large
, andfull
.closeOnClick
- determines whether clicking the backdrop closes the modal. Defaults totrue
.
The open
method returns a modal instance, an object with the following properties:
close(result)
- a method that can be used to close a modal, passing a resultdismiss(reason)
- a method that can be used to dismiss a modal, passing a reasonreposition()
- recalculates and positions the top position of the modalresult
- a promise that is resolved when a modal is closed and rejected when a modal is dismissedopened
- a promise that is resolved when a modal gets opened after downloading content's template and resolving all variables
In addition the scope associated with modal's content is augmented with 2 methods:
$close(result)
- an alias for$modalInstance.close()
$dismiss(reason)
- an alias for$modalInstance.dismiss()
Those methods make it easy to close a modal window without a need to create a dedicated controller
<div ng-controller="ModalDemoCtrl">
<script type="text/ng-template" id="myModalContent.html">
<h3>I'm a modal!</h3>
<ul>
<li ng-repeat="item in items track by $index">
<a ng-click="selected.item = item">{{ item }}</a>
</li>
</ul>
<p>Selected: <b>{{ selected.item }}</b></p>
<button class="button" ng-click="openNested()">Open nested</button>
<button class="button" ng-click="ok()">OK</button>
<button ng-click="cancel()" class="close-button" aria-label="Close reveal" type="button">
<span aria-hidden="true">×</span>
</button>
</script>
<button class="button" ng-click="open()">Open me!</button>
<button class="button" ng-click="open('', false)">No backdrop</button>
<button class="button" ng-click="open('', true, 40)">Overflow</button>
<button class="button" ng-click="open('tiny')">Tiny</button>
<button class="button" ng-click="open('small')">Small</button>
<button class="button" ng-click="open('large')">Large</button>
<button class="button" ng-click="open('full')">Fullscreen</button>
<button class="button" ng-click="open('', true, 3, false)">Close via backdrop click disabled</button>
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
</div>
angular.module('foundationDemoApp').controller('ModalDemoCtrl', function($scope, $modal, $log) {
$scope.open = open;
function open(size, backdrop, itemCount, closeOnClick) {
$scope.items = [];
var count = itemCount || 3;
for(var i = 0; i < count; i++){
$scope.items.push('item ' + i);
}
var params = {
templateUrl: 'myModalContent.html',
resolve: {
items: function() {
return $scope.items;
},
},
controller: function($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0],
};
$scope.reposition = function() {
$modalInstance.reposition();
};
$scope.ok = function() {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.openNested = function() {
open();
};
}
};
if(angular.isDefined(closeOnClick)){
params.closeOnClick = closeOnClick;
}
if(angular.isDefined(size)){
params.size = size;
}
if(angular.isDefined(backdrop)){
params.backdrop = backdrop;
}
var modalInstance = $modal.open(params);
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
});
Offcanvas (mm.foundation.offcanvas)
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-wrapper
: The most outter page wrapper.inner-wrapper
: Second page wrapper nested directly inside off-canvas-wrapper.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-wrapper">
<div class="off-canvas-wrapper-inner">
<div class="title-bar">
<div class="title-bar-left">
<a class="left-off-canvas-toggle menu-icon" ><span></span></a>
<span class="title-bar-title">Foundation</span>
</div>
<div class="title-bar-right">
<a class="right-off-canvas-toggle menu-icon" ><span></span></a>
</div>
</div>
<div class="off-canvas position-left">
<ul class="mobile-ofc vertical menu">
<li>
<a href="#">Link 1</a>
<ul class="submenu menu vertical">
<li><a href="#">Sublink 1</a></li>
<li><a href="#">Sublink 2</a></li>
<li><a href="#">Sublink 3</a></li>
<li><a href="#">Sublink 4</a></li>
</ul>
</li>
<li>
<a href="#">Link 2</a>
<ul class="submenu menu vertical">
<li><a href="#">Sublink 5</a></li>
<li><a href="#">Sublink 6</a></li>
<li><a href="#">Sublink 7</a></li>
</ul>
</li>
<li><a href="#" class="button">Button 1</a></li>
</ul>
</div>
<div class="off-canvas position-right">
<ul class="mobile-ofc vertical menu">
<li>
<a href="#">Link 1</a>
<ul class="submenu menu vertical">
<li><a href="#">Sublink 1</a></li>
<li><a href="#">Sublink 2</a></li>
<li><a href="#">Sublink 3</a></li>
<li><a href="#">Sublink 4</a></li>
</ul>
</li>
<li>
<a href="#">Link 2</a>
<ul class="submenu menu vertical">
<li><a href="#">Sublink 5</a></li>
<li><a href="#">Sublink 6</a></li>
<li><a href="#">Sublink 7</a></li>
</ul>
</li>
<li><a href="#" class="button">Button 1</a></li>
</ul>
</div>
<div class="off-canvas-content">
<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>
</div>
</div>
</div>
</div>
angular.module('foundationDemoApp').controller('OffCanvasDemoCtrl', function ($scope) {
});
Orbit (mm.foundation.orbit)
Beta version of the orbit directive.
<div ng-controller="OrbitDemoCtrl">
<div class="orbit">
<div class="orbit-wrapper">
<div class="orbit-controls">
<span class="orbit-previous"><</span>
<span class="orbit-next">></span>
</div>
<ul class="orbit-container" style="height: auto">
<li class="orbit-slide is-active">
<div style="background-color: #20b2aa; padding: 2rem 4rem; color: #fefefe;">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</li>
<li class="orbit-slide">
<div style="background-color: #663399; padding: 2rem 4rem; color: #fefefe;">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</li>
<li class="orbit-slide">
<div style="background-color: #b8860b; padding: 2rem 4rem; color: #fefefe;">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</li>
<li class="orbit-slide">
<div style="background-color: #16b80b; padding: 2rem 4rem; color: #fefefe;">
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</div>
</li>
</ul>
</div>
<nav class="orbit-bullets"></nav>
</div>
</div>
angular.module('foundationDemoApp').controller('OrbitDemoCtrl', function ($scope) {
});
Pagination (mm.foundation.pagination)
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="‹" next-text="›" first-text="«" last-text="»"></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;
});
Progressbar (mm.foundation.progressbar)
Static
Dynamic
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 atransition
to progress bars. In order for animations to work, you'll need to apply atransition
in your own CSS. See the demo for an example.
<div ng-controller="ProgressDemoCtrl">
<h3>Static</h3>
<div class="row">
<div class="columns small-4"><progressbar value="55"></progressbar></div>
<div class="columns small-4"><progressbar value="22" type="warning">22%</progressbar></div>
<div class="columns small-4"><progressbar max="200" value="166" type="alert">166 / 200</progressbar></div>
</div>
<hr />
<h3>Dynamic <button class="button small" type="button" ng-click="random()">Randomize</button></h3>
<progressbar max="max" value="dynamic">{{dynamic}} / {{max}}</progressbar>
<small><em>No animation</em></small>
<progressbar animate="false" value="dynamic" type="success">{{dynamic}}%</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();
});
Rating (mm.foundation.rating)
Default
Rate: {{rate}} - Readonly is: {{isReadonly}} - Hovering over: {{overStar || "none"}}
Custom icons
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 class="mm-foundation-rating-demo-pre">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'}
];
});
Tabs (mm.foundation.tabs)
Select a tab by setting active binding to true:
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!");
});
};
});
Tooltip (mm.foundation.tooltip)
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, 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, 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-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? Supports a space separated list of event namestooltip-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', 'popupDelay', andappendToBody
. Here are the defaults:placement: 'top', 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, 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>!";
});
Animations
Animations are supported via ngAnimate. This can be used in conjuction with Zurb's MotionUI library.
$motion-ui-classes: (
chain: true,
prefix: 'ng-',
active: '-active',
);
@import 'motion-ui';
.tooltip {
@include mui-fade(
$state: in,
$duration: 0.25s,
$delay: 0s
);
}
.tooltip {
@include mui-fade(
$state: out,
$from: 1,
$to: 0,
$duration: 0.25s,
$delay: 0s
);
}
.reveal-overlay {
@include mui-fade($state: in);
}
.reveal-overlay {
@include mui-fade(
$state: out,
$from: 1,
$to: 0
);
}
.reveal {
@include mui-slide(
$state: in, // Specify in or out
$direction: down, // Can be top, right, bottom, or left
$fade: true, // If true, the element fades simultaneously
$duration: 0.25s,
$timing: cubic-bezier,
$delay: 0s
);
}
.reveal {
@include mui-zoom(
$state: out,
$from: 1,
$to: 0,
$duration: 0.25s
);
}