mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-08-10 09:23:25 +00:00
Restructure Sass files and folders for proper application Sass development
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
<div ng-controller="AppCtrl">
|
||||
|
||||
<md-toolbar class="md-accent">
|
||||
<h1 class="md-toolbar-tools">
|
||||
<span flex>Awesome Md App</span>
|
||||
<md-button class="md-fab md-accent" aria-label="refresh">
|
||||
<md-tooltip>
|
||||
Refresh
|
||||
</md-tooltip>
|
||||
<md-icon icon="/img/icons/ic_refresh_24px.svg">
|
||||
</md-icon>
|
||||
</md-button>
|
||||
</h1>
|
||||
</md-toolbar>
|
||||
<md-content class="md-padding">
|
||||
|
||||
<p>
|
||||
The tooltip is visible when the button is hovered, focused, or touched.
|
||||
</p>
|
||||
|
||||
<md-button class="md-fab md-fab-top-left left" aria-label="Insert Drive">
|
||||
<md-icon icon="/img/icons/ic_insert_drive_file_24px.svg"></md-icon>
|
||||
<md-tooltip md-visible="demo.showTooltip">
|
||||
Insert Drive
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
<md-button class="md-fab md-fab-top-right right" aria-label="Photos">
|
||||
<md-icon icon="/img/icons/ic_photo_24px.svg"></md-icon>
|
||||
<md-tooltip>
|
||||
Photos
|
||||
</md-tooltip>
|
||||
</md-button>
|
||||
|
||||
<br/><br/><br/><br/><br/><br/>
|
||||
|
||||
<p>
|
||||
<div>
|
||||
Additionally, the Tooltip's `md-visible` attribute can use data-binding to programmatically
|
||||
show/hide itself. Toggle the checkbox below...
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<md-checkbox ng-model="demo.showTooltip">
|
||||
Insert Drive
|
||||
</md-checkbox>
|
||||
|
||||
</md-content>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
angular.module('tooltipDemo1', ['ngMaterial'])
|
||||
|
||||
.controller('AppCtrl', function($scope) {
|
||||
$scope.demo = {};
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
md-toolbar .md-toolbar-tools .md-button,
|
||||
md-toolbar .md-toolbar-tools .md-button:hover {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
transform: none;
|
||||
-webkit-transform: none;
|
||||
}
|
||||
|
||||
|
||||
.left {
|
||||
top:70px !important;
|
||||
left:56px !important;
|
||||
}
|
||||
|
||||
.right {
|
||||
top:70px !important;
|
||||
right:56px !important;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
md-tooltip.md-THEME_NAME-theme {
|
||||
color: '{{background-A100}}';
|
||||
.md-background {
|
||||
background-color: '{{foreground-2}}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name material.components.tooltip
|
||||
*/
|
||||
angular.module('material.components.tooltip', [
|
||||
'material.core'
|
||||
])
|
||||
.directive('mdTooltip', MdTooltipDirective);
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name mdTooltip
|
||||
* @module material.components.tooltip
|
||||
* @description
|
||||
* Tooltips are used to describe elements that are interactive and primarily graphical (not textual).
|
||||
*
|
||||
* Place a `<md-tooltip>` as a child of the element it describes.
|
||||
*
|
||||
* A tooltip will activate when the user focuses, hovers over, or touches the parent.
|
||||
*
|
||||
* @usage
|
||||
* <hljs lang="html">
|
||||
* <md-icon icon="/img/icons/ic_play_arrow_24px.svg">
|
||||
* <md-tooltip>
|
||||
* Play Music
|
||||
* </md-tooltip>
|
||||
* </md-icon>
|
||||
* </hljs>
|
||||
*
|
||||
* @param {expression=} md-visible Boolean bound to whether the tooltip is
|
||||
* currently visible.
|
||||
*/
|
||||
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement) {
|
||||
|
||||
var TOOLTIP_SHOW_DELAY = 400;
|
||||
var TOOLTIP_WINDOW_EDGE_SPACE = 8;
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
template:
|
||||
'<div class="md-background"></div>' +
|
||||
'<div class="md-content" ng-transclude></div>',
|
||||
scope: {
|
||||
visible: '=?mdVisible'
|
||||
},
|
||||
link: postLink
|
||||
};
|
||||
|
||||
function postLink(scope, element, attr, contentCtrl) {
|
||||
$mdTheming(element);
|
||||
var parent = element.parent();
|
||||
|
||||
// Look for the nearest parent md-content, stopping at the rootElement.
|
||||
var current = element.parent()[0];
|
||||
while (current && current !== $rootElement[0] && current !== document.body) {
|
||||
if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
|
||||
current = current.parentNode;
|
||||
}
|
||||
var tooltipParent = angular.element(current || document.body);
|
||||
|
||||
// We will re-attach tooltip when visible
|
||||
element.detach();
|
||||
element.attr('role', 'tooltip');
|
||||
element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
|
||||
|
||||
parent.on('focus mouseenter touchstart', function() {
|
||||
setVisible(true);
|
||||
});
|
||||
parent.on('blur mouseleave touchend touchcancel', function() {
|
||||
// Don't hide the tooltip if the parent is still focused.
|
||||
if ($document[0].activeElement === parent[0]) return;
|
||||
setVisible(false);
|
||||
});
|
||||
|
||||
scope.$watch('visible', function(isVisible) {
|
||||
if (isVisible) showTooltip();
|
||||
else hideTooltip();
|
||||
});
|
||||
|
||||
var debouncedOnResize = $$rAF.debounce(function windowResize() {
|
||||
// Reposition on resize
|
||||
if (scope.visible) positionTooltip();
|
||||
});
|
||||
angular.element($window).on('resize', debouncedOnResize);
|
||||
|
||||
// Be sure to completely cleanup the element on destroy
|
||||
scope.$on('$destroy', function() {
|
||||
scope.visible = false;
|
||||
element.remove();
|
||||
angular.element($window).off('resize', debouncedOnResize);
|
||||
});
|
||||
|
||||
// *******
|
||||
// Methods
|
||||
// *******
|
||||
|
||||
// If setting visible to true, debounce to TOOLTIP_SHOW_DELAY ms
|
||||
// If setting visible to false and no timeout is active, instantly hide the tooltip.
|
||||
function setVisible(value) {
|
||||
setVisible.value = !!value;
|
||||
|
||||
if (!setVisible.queued) {
|
||||
if (value) {
|
||||
setVisible.queued = true;
|
||||
$timeout(function() {
|
||||
scope.visible = setVisible.value;
|
||||
setVisible.queued = false;
|
||||
}, TOOLTIP_SHOW_DELAY);
|
||||
|
||||
} else {
|
||||
$timeout(function() { scope.visible = false; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showTooltip() {
|
||||
// Insert the element before positioning it, so we can get position
|
||||
// (tooltip is hidden by default)
|
||||
element.removeClass('md-hide');
|
||||
parent.attr('aria-describedby', element.attr('id'));
|
||||
tooltipParent.append(element);
|
||||
|
||||
// Wait until the element has been in the dom for two frames before
|
||||
// fading it in.
|
||||
// Additionally, we position the tooltip twice to avoid positioning bugs
|
||||
positionTooltip();
|
||||
$$rAF(function() {
|
||||
|
||||
$$rAF(function() {
|
||||
positionTooltip();
|
||||
if (!scope.visible) return;
|
||||
element.addClass('md-show');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
element.removeClass('md-show').addClass('md-hide');
|
||||
parent.removeAttr('aria-describedby');
|
||||
$timeout(function() {
|
||||
if (scope.visible) return;
|
||||
element.detach();
|
||||
}, 200, false);
|
||||
}
|
||||
|
||||
function positionTooltip() {
|
||||
var tipRect = $mdUtil.elementRect(element, tooltipParent);
|
||||
var parentRect = $mdUtil.elementRect(parent, tooltipParent);
|
||||
|
||||
// Default to bottom position if possible
|
||||
var tipDirection = 'bottom';
|
||||
var newPosition = {
|
||||
left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
|
||||
top: parentRect.top + parentRect.height
|
||||
};
|
||||
|
||||
// If element bleeds over left/right of the window, place it on the edge of the window.
|
||||
newPosition.left = Math.min(
|
||||
newPosition.left,
|
||||
tooltipParent.prop('scrollWidth') - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE
|
||||
);
|
||||
newPosition.left = Math.max(newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE);
|
||||
|
||||
// If element bleeds over the bottom of the window, place it above the parent.
|
||||
if (newPosition.top + tipRect.height > tooltipParent.prop('scrollHeight')) {
|
||||
newPosition.top = parentRect.top - tipRect.height;
|
||||
tipDirection = 'top';
|
||||
}
|
||||
|
||||
element.css({top: newPosition.top + 'px', left: newPosition.left + 'px'});
|
||||
// Tell the CSS the size of this tooltip, as a multiple of 32.
|
||||
element.attr('width-32', Math.ceil(tipRect.width / 32));
|
||||
element.attr('md-direction', tipDirection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
})();
|
||||
@@ -0,0 +1,96 @@
|
||||
@keyframes tooltipBackgroundShow {
|
||||
0% {
|
||||
transform: scale(0.2);
|
||||
opacity: 0.25;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes tooltipBackgroundHide {
|
||||
0% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
md-tooltip {
|
||||
position: absolute;
|
||||
font-size: 14px;
|
||||
z-index: $z-index-tooltip;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
border-radius: 4px;
|
||||
|
||||
&[md-direction="bottom"] {
|
||||
transform: translate3d(0, -30%, 0);
|
||||
margin-top: 8px;
|
||||
}
|
||||
&[md-direction="top"] {
|
||||
transform: translate3d(0, 30%, 0);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.md-background {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
margin-left: -128px;
|
||||
margin-top: -128px;
|
||||
border-radius: 256px;
|
||||
|
||||
opacity: 0.25;
|
||||
transform: scale(0.2);
|
||||
}
|
||||
|
||||
.md-content {
|
||||
max-width: 240px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
padding: 8px;
|
||||
background: transparent;
|
||||
opacity: 0.3;
|
||||
transition: inherit;
|
||||
}
|
||||
|
||||
&.md-hide {
|
||||
transition: $swift-ease-in;
|
||||
}
|
||||
|
||||
&.md-show {
|
||||
transition: $swift-ease-out;
|
||||
pointer-events: auto;
|
||||
transform: translate3d(0,0,0);
|
||||
|
||||
.md-background {
|
||||
transform: scale(1.0);
|
||||
opacity: 1.0;
|
||||
animation: tooltipBackgroundShow linear;
|
||||
}
|
||||
.md-content {
|
||||
opacity: 0.99;
|
||||
}
|
||||
}
|
||||
&.md-hide .md-background {
|
||||
transform: scale(1.0);
|
||||
opacity: 0;
|
||||
animation: tooltipBackgroundHide 0.2s linear;
|
||||
}
|
||||
|
||||
/**
|
||||
* Depending on the tooltip's size as a multiple of 32 (set by JS),
|
||||
* change the background's animation duration.
|
||||
* The larger the tooltip, the less time the background should take to ripple outwards.
|
||||
*/
|
||||
@for $i from 1 through 8 {
|
||||
&[width-32="#{$i}"].md-show .md-background {
|
||||
$duration: 1000 - $i * 100;
|
||||
animation-duration: #{$duration}ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
describe('<md-tooltip> directive', function() {
|
||||
|
||||
beforeEach(module('material.components.tooltip', function($provide) {
|
||||
$provide.value('$$rAF', mockRaf);
|
||||
|
||||
// fake synchronous version of rAF
|
||||
function mockRaf(cb) { cb(); }
|
||||
mockRaf.debounce = function(cb) {
|
||||
var context = this, args = arguments;
|
||||
return function() {
|
||||
cb.apply(context, args);
|
||||
};
|
||||
};
|
||||
findTooltip().remove();
|
||||
}));
|
||||
|
||||
function findTooltip() {
|
||||
return angular.element(document.body).find('md-tooltip');
|
||||
}
|
||||
|
||||
it('should show and hide when visible is set', inject(function($compile, $rootScope, $timeout) {
|
||||
var element = $compile('<md-button>' +
|
||||
'Hello' +
|
||||
'<md-tooltip md-visible="isVisible">Tooltip</md-tooltip>' +
|
||||
'</md-button>')($rootScope);
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(findTooltip().length).toBe(0);
|
||||
|
||||
$rootScope.$apply('isVisible = true');
|
||||
expect(findTooltip().length).toBe(1);
|
||||
expect(findTooltip().hasClass('md-show')).toBe(true);
|
||||
expect(findTooltip().hasClass('md-hide')).toBe(false);
|
||||
|
||||
$rootScope.$apply('isVisible = false');
|
||||
expect(findTooltip().hasClass('md-hide')).toBe(true);
|
||||
expect(findTooltip().hasClass('md-show')).toBe(false);
|
||||
$timeout.flush();
|
||||
expect(findTooltip().length).toBe(0);
|
||||
}));
|
||||
|
||||
it('should describe parent', inject(function($compile, $rootScope, $timeout) {
|
||||
var element = $compile('<md-button>' +
|
||||
'Hello' +
|
||||
'<md-tooltip md-visible="isVisible">Tooltip</md-tooltip>' +
|
||||
'</md-button>')($rootScope);
|
||||
|
||||
$rootScope.$apply('isVisible = true');
|
||||
|
||||
expect(element.attr('aria-describedby')).toEqual(findTooltip().attr('id'));
|
||||
|
||||
$rootScope.$apply('isVisible = false');
|
||||
expect(element.attr('aria-describedby')).toBeFalsy();
|
||||
|
||||
}));
|
||||
|
||||
it('should set visible on mouseenter and mouseleave', inject(function($compile, $rootScope, $timeout) {
|
||||
var element = $compile('<md-button>' +
|
||||
'Hello' +
|
||||
'<md-tooltip md-visible="isVisible">Tooltip</md-tooltip>' +
|
||||
'</md-button>')($rootScope);
|
||||
|
||||
$rootScope.$apply();
|
||||
|
||||
element.triggerHandler('mouseenter');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(true);
|
||||
|
||||
element.triggerHandler('mouseleave');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(false);
|
||||
}));
|
||||
|
||||
it('should set visible on focus and blur', inject(function($compile, $rootScope, $timeout) {
|
||||
var element = $compile('<md-button>' +
|
||||
'Hello' +
|
||||
'<md-tooltip md-visible="isVisible">Tooltip</md-tooltip>' +
|
||||
'</md-button>')($rootScope);
|
||||
|
||||
$rootScope.$apply();
|
||||
|
||||
element.triggerHandler('focus');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(true);
|
||||
|
||||
element.triggerHandler('blur');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(false);
|
||||
}));
|
||||
|
||||
it('should set visible on touchstart and touchend', inject(function($compile, $rootScope, $timeout) {
|
||||
var element = $compile('<md-button>' +
|
||||
'Hello' +
|
||||
'<md-tooltip md-visible="isVisible">Tooltip</md-tooltip>' +
|
||||
'</md-button>')($rootScope);
|
||||
|
||||
$rootScope.$apply();
|
||||
|
||||
element.triggerHandler('touchstart');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(true);
|
||||
|
||||
element.triggerHandler('touchend');
|
||||
$timeout.flush();
|
||||
expect($rootScope.isVisible).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user