custom tooltips

This commit is contained in:
Antelle 2016-01-10 23:58:21 +03:00
parent 0977595712
commit c26406940c
10 changed files with 203 additions and 5 deletions

View File

@ -1,6 +1,7 @@
'use strict';
var Backbone = require('backbone');
var Backbone = require('backbone'),
Tip = require('../util/tip');
_.extend(Backbone.View.prototype, {
hide: function() {
@ -48,6 +49,7 @@ _.extend(Backbone.View.prototype, {
this.$el.replaceWith(el);
}
this.setElement(el);
Tip.createTips(el);
},
_parentRemove: Backbone.View.prototype.remove,

112
app/scripts/util/tip.js Normal file
View File

@ -0,0 +1,112 @@
'use strict';
var Tip = function(el) {
this.el = el;
this.title = el.attr('title');
this.tipEl = null;
this.showTimeout = null;
this.hideTimeout = null;
this.init();
};
Tip.prototype.init = function() {
this.el.removeAttr('title');
this.el.mouseenter(this.mouseenter.bind(this)).mouseleave(this.mouseleave.bind(this));
};
Tip.prototype.mouseenter = function() {
var that = this;
if (this.showTimeout) {
return;
}
this.showTimeout = setTimeout(function() {
that.showTimeout = null;
if (that.tipEl) {
that.tipEl.remove();
if (that.hideTimeout) {
clearTimeout(that.hideTimeout);
that.hideTimeout = null;
}
}
var tipEl = that.tipEl = $('<div></div>').addClass('tip').appendTo('body').html(that.title);
var rect = that.el[0].getBoundingClientRect(),
tipRect = that.tipEl[0].getBoundingClientRect();
var placement = that.el.attr('tip-placement') || that.getAutoPlacement(rect, tipRect);
tipEl.addClass('tip--' + placement);
var top, left;
var offset = 10;
switch (placement) {
case 'top':
top = rect.top - tipRect.height - offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'bottom':
top = rect.bottom + offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'left':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.left - tipRect.width - offset;
break;
case 'right':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.right + offset;
break;
}
tipEl.css({ top: top, left: left });
}, 200);
};
Tip.prototype.mouseleave = function() {
var that = this;
if (this.tipEl) {
that.tipEl.addClass('tip--hide');
this.hideTimeout = setTimeout(function () {
that.hideTimeout = null;
if (that.tipEl) {
that.tipEl.remove();
that.tipEl = null;
}
}, 500);
}
if (this.showTimeout) {
clearTimeout(this.showTimeout);
this.showTimeout = null;
}
};
Tip.prototype.getAutoPlacement = function(rect, tipRect) {
var padding = 20;
var bodyRect = document.body.getBoundingClientRect();
var canShowToBottom = bodyRect.bottom - rect.bottom > padding + tipRect.height,
canShowToHalfRight = bodyRect.right - rect.right > padding + tipRect.width / 2,
canShowToRight = bodyRect.right - rect.right > padding + tipRect.width,
canShowToHalfLeft = rect.left > padding + tipRect.width / 2,
canShowToLeft = rect.left > padding + tipRect.width;
if (canShowToBottom) {
if (canShowToLeft && !canShowToHalfRight) {
return 'left';
} else if (canShowToRight && !canShowToHalfLeft) {
return 'right';
} else {
return 'bottom';
}
}
if (canShowToLeft && !canShowToHalfRight) {
return 'left';
} else if (canShowToRight && !canShowToHalfLeft) {
return 'right';
} else {
return 'top';
}
};
Tip.createTips = function(container) {
container.find('[title]').each(function(ix, el) {
if (!el._tip) {
el._tip = new Tip($(el));
}
});
};
module.exports = Tip;

View File

@ -19,6 +19,7 @@ var Backbone = require('backbone'),
CopyPaste = require('../../comp/copy-paste'),
Format = require('../../util/format'),
Locale = require('../../util/locale'),
Tip = require('../../util/tip'),
FileSaver = require('filesaver'),
baron = require('baron'),
kdbxweb = require('kdbxweb');
@ -80,10 +81,12 @@ var DetailsView = Backbone.View.extend({
}
if (this.model instanceof GroupModel) {
this.$el.html(this.groupTemplate());
Tip.createTips(this.$el);
return;
}
var model = $.extend({ deleted: this.appModel.filter.trash }, this.model);
this.$el.html(this.template(model));
Tip.createTips(this.$el);
this.setSelectedColor(this.model.color);
this.addFieldViews();
this.scroll = baron({

View File

@ -4,6 +4,7 @@ var Backbone = require('backbone'),
Keys = require('../const/keys'),
KeyHandler = require('../comp/key-handler'),
GeneratorView = require('./generator-view'),
Tip = require('../util/tip'),
UpdateModel = require('../models/update-model');
var FooterView = Backbone.View.extend({
@ -36,6 +37,7 @@ var FooterView = Backbone.View.extend({
files: this.model.files,
updateAvailable: ['ready', 'found'].indexOf(UpdateModel.instance.get('updateStatus')) >= 0
}));
Tip.createTips(this.$el);
return this;
},

View File

@ -3,6 +3,7 @@
var Backbone = require('backbone'),
Scrollable = require('../mixins/scrollable'),
IconSelectView = require('./icon-select-view'),
Tip = require('../util/tip'),
baron = require('baron');
var GrpView = Backbone.View.extend({
@ -30,6 +31,7 @@ var GrpView = Backbone.View.extend({
enableSearching: this.model.get('enableSearching') !== false,
readonly: this.model.get('top')
}));
Tip.createTips(this.$el);
if (!this.model.get('title')) {
this.$el.find('#grp__field-title').focus();
}

View File

@ -44,6 +44,8 @@ $base-duration: 150ms;
$base-timing: ease;
$slow-transition-in: $base-duration*2 ease-in;
$slow-transition-out: $base-duration ease-out;
$tip-transition-in: 500ms $ease-in-expo;
$tip-transition-out: $slow-transition-out;
// Math
$sqrt2: 1.41;

View File

@ -0,0 +1,73 @@
.tip {
position: absolute;
padding: $base-padding;
border-radius: $base-border-radius;
white-space: nowrap;
z-index: $z-index-no-modal;
pointer-events: none;
animation: tip $tip-transition-in;
@include dropdown();
&--hide.tip, &--hide.tip:before, &--hide.tip:after {
transition: all $tip-transition-out;
transition-property: color, border-color, background-color, box-shadow;
color: transparent;
background-color: transparent;
border-color: transparent !important;
box-shadow: none;
}
&:before, &:after {
animation: tip $tip-transition-in;
content: " ";
width: 0;
height: 0;
}
$arrow-size-small: 10px 8px;
$arrow-size-large: 12px 9px;
&.tip--bottom:after {
@include position(absolute, - nth($arrow-size-small, 2) null null 50%);
@include transform(translate(-50%, 0));
@include th { @include triangle($arrow-size-small, background-color(), up); }
}
&.tip--top:after {
@include position(absolute, 100% null null 50%);
@include transform(translate(-50%, 0));
@include th { @include triangle($arrow-size-small, background-color(), down); }
}
&.tip--left:after {
@include position(absolute, 50% null null 100%);
@include transform(translate(0, -50%));
@include th { @include triangle($arrow-size-small, background-color(), right); }
}
&.tip--right:after {
@include position(absolute, 50% null null (- nth($arrow-size-small, 2)));
@include transform(translate(0, -50%));
@include th { @include triangle($arrow-size-small, background-color(), left); }
}
&.tip--bottom:before {
@include position(absolute, - nth($arrow-size-large, 2) null null 50%);
@include transform(translate(-50%, 0));
@include th { @include triangle($arrow-size-large, light-border-color(), up); }
}
&.tip--top:before {
@include position(absolute, 100% null null 50%);
@include transform(translate(-50%, 0));
@include th { @include triangle($arrow-size-large, light-border-color(), down); }
}
&.tip--left:before {
@include position(absolute, 50% null null 100%);
@include transform(translate(0, -50%));
@include th { @include triangle($arrow-size-large, light-border-color(), right); }
}
&.tip--right:before {
@include position(absolute, 50% null null (- nth($arrow-size-large, 2)));
@include transform(translate(0, -50%));
@include th { @include triangle($arrow-size-large, light-border-color(), left); }
}
}
@keyframes tip {
from { color: transparent; background-color: transparent; border-color: transparent; box-shadow: none; }
}

View File

@ -18,6 +18,7 @@
@import "common/icon-select";
@import "common/modal";
@import "common/scroll";
@import "common/tip";
@import "areas/app";
@import "areas/details";

View File

@ -3,7 +3,7 @@
<i class="fa fa-chevron-left"></i> {{res 'detBackToList'}}
</div>
<div class="details__header">
<i class="details__header-color fa fa-bookmark-o" title="{{res 'detSetIconColor'}}">
<i class="details__header-color fa fa-bookmark-o" title="{{res 'detSetIconColor'}}" tip-placement="left">
<span class="details__colors-popup">
<span class="details__colors-popup-item yellow-color fa fa-bookmark-o" data-color="yellow"></span>
<span class="details__colors-popup-item green-color fa fa-bookmark-o" data-color="green"></span>
@ -31,9 +31,9 @@
</div>
<div class="details__buttons">
{{#if deleted~}}
<i class="details__buttons-trash-del fa fa-minus-circle" title="{{res 'detDelEntryPerm'}}"></i>
<i class="details__buttons-trash-del fa fa-minus-circle" title="{{res 'detDelEntryPerm'}}" tip-placement="top"></i>
{{~else~}}
<i class="details__buttons-trash fa fa-trash-o" title="{{res 'detDelEntry'}}"></i>
<i class="details__buttons-trash fa fa-trash-o" title="{{res 'detDelEntry'}}" tip-placement="top"></i>
{{~/if~}}
<div class="details__attachments">
{{#each attachments as |attachment ix|}}

View File

@ -20,6 +20,7 @@
</div>
{{/if}}
{{#if editable}}<i class="menu__item-edit fa fa-cog"></i>{{/if}}
{{#ifeq filterKey 'trash'}}<i class="menu__item-empty-trash fa fa-minus-circle" title="{{res 'menuEmptyTrash'}}"></i>{{/ifeq}}
{{#ifeq filterKey 'trash'}}<i class="menu__item-empty-trash fa fa-minus-circle" title="{{res 'menuEmptyTrash'}}"
tip-placement="right"></i>{{/ifeq}}
</div>
</div>