update tooltip component to show on hover and add different directions
This commit is contained in:
parent
7c4e4c24ad
commit
f86bb14591
11 changed files with 181 additions and 140 deletions
|
@ -223,6 +223,7 @@ class CategoryList extends React.PureComponent<Props, State> {
|
||||||
{category &&
|
{category &&
|
||||||
category.match(/^community/i) && (
|
category.match(/^community/i) && (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
direction="bottom"
|
||||||
label={__("What's this?")}
|
label={__("What's this?")}
|
||||||
body={__(
|
body={__(
|
||||||
'Community Content is a public space where anyone can share content with the rest of the LBRY community. Bid on the names "one," "two," "three," "four" and "five" to put your content here!'
|
'Community Content is a public space where anyone can share content with the rest of the LBRY community. Bid on the names "one," "two," "three," "four" and "five" to put your content here!'
|
||||||
|
|
|
@ -2,22 +2,35 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as FeatherIcons from 'react-feather';
|
import * as FeatherIcons from 'react-feather';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
|
import Tooltip from 'component/common/tooltip';
|
||||||
|
|
||||||
const RED_COLOR = '#e2495e';
|
const RED_COLOR = '#e2495e';
|
||||||
const PURPLE_COLOR = '#8165b0';
|
const PURPLE_COLOR = '#8165b0';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
icon: string,
|
icon: string,
|
||||||
|
tooltip: ?string, // tooltip direction
|
||||||
};
|
};
|
||||||
|
|
||||||
class IconComponent extends React.PureComponent<Props> {
|
class IconComponent extends React.PureComponent<Props> {
|
||||||
// TODO: Move all icons to constants and add titles for all
|
getTooltip = (icon: string) => {
|
||||||
// Add some some sort of hover flyout with the title?
|
switch (icon) {
|
||||||
|
case icons.FEATURED:
|
||||||
|
return __('Featured content. Earn rewards for watching.');
|
||||||
|
case icons.LOCAL:
|
||||||
|
return __('This file is downloaded.');
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
render() {
|
render() {
|
||||||
const { icon } = this.props;
|
const { icon, tooltip } = this.props;
|
||||||
const Icon = FeatherIcons[icon];
|
const Icon = FeatherIcons[icon];
|
||||||
|
|
||||||
|
if (!Icon) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let color;
|
let color;
|
||||||
if (icon === icons.TRASH || icon === icons.FEATURED) {
|
if (icon === icons.TRASH || icon === icons.FEATURED) {
|
||||||
color = RED_COLOR;
|
color = RED_COLOR;
|
||||||
|
@ -30,7 +43,19 @@ class IconComponent extends React.PureComponent<Props> {
|
||||||
size = 20;
|
size = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Icon ? <Icon size={size} className="icon" color={color} /> : null;
|
let tooltipText;
|
||||||
|
if (tooltip) {
|
||||||
|
tooltipText = this.getTooltip(icon);
|
||||||
|
}
|
||||||
|
const inner = <Icon size={size} className="icon" color={color} />;
|
||||||
|
|
||||||
|
return tooltip ? (
|
||||||
|
<Tooltip icon body={tooltipText} direction={tooltip}>
|
||||||
|
{inner}
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
inner
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,41 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import Icon from 'component/common/icon';
|
|
||||||
import Button from 'component/button';
|
|
||||||
import * as icons from 'constants/icons';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
body: string,
|
body: ?string,
|
||||||
label: string,
|
label?: string,
|
||||||
|
children: ?React.Node,
|
||||||
|
icon: ?boolean,
|
||||||
|
direction: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
class ToolTip extends React.PureComponent<Props> {
|
||||||
showTooltip: boolean,
|
static defaultProps = {
|
||||||
};
|
direction: 'top',
|
||||||
|
};
|
||||||
class ToolTip extends React.PureComponent<Props, State> {
|
|
||||||
constructor(props: Props) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
showTooltip: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
(this: any).handleClick = this.handleClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick() {
|
|
||||||
const { showTooltip } = this.state;
|
|
||||||
|
|
||||||
if (!showTooltip) {
|
|
||||||
document.addEventListener('click', this.handleClick);
|
|
||||||
} else {
|
|
||||||
document.removeEventListener('click', this.handleClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
showTooltip: !showTooltip,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { label, body } = this.props;
|
const { children, label, body, icon, direction } = this.props;
|
||||||
const { showTooltip } = this.state;
|
|
||||||
|
const tooltipContent = children || label;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="tooltip">
|
<span
|
||||||
<Button button="link" className="help tooltip__link" onClick={this.handleClick}>
|
className={classnames('tooltip', {
|
||||||
{label}
|
'tooltip--label': label && !icon,
|
||||||
{showTooltip && <Icon icon={icons.CLOSE} />}
|
'tooltip--icon': icon,
|
||||||
</Button>
|
'tooltip--top': direction === 'top',
|
||||||
<div className={classnames('tooltip__body', { hidden: !showTooltip })}>{body}</div>
|
'tooltip--right': direction === 'right',
|
||||||
|
'tooltip--bottom': direction === 'bottom',
|
||||||
|
'tooltip--left': direction === 'left',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{tooltipContent}
|
||||||
|
{body && (
|
||||||
|
// body may be undefined for some icons until we add messages for them
|
||||||
|
<span className="tooltip__body">{body}</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,14 +98,16 @@ class FileCard extends React.PureComponent<Props> {
|
||||||
<div className="card__title--small">
|
<div className="card__title--small">
|
||||||
<TruncatedText lines={3}>{title}</TruncatedText>
|
<TruncatedText lines={3}>{title}</TruncatedText>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__subtitle card__subtitle--file-info">
|
<div className="card__subtitle">
|
||||||
{pending ? (
|
{pending ? (
|
||||||
<div>Pending...</div>
|
<div>Pending...</div>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<UriIndicator uri={uri} link />
|
<UriIndicator uri={uri} link />
|
||||||
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
<div>
|
||||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
||||||
|
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||||
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -158,7 +158,7 @@ class FilePage extends React.Component<Props> {
|
||||||
<h1 className="card__title card__title--file">{title}</h1>
|
<h1 className="card__title card__title--file">{title}</h1>
|
||||||
<div className="card__title-identity-icons">
|
<div className="card__title-identity-icons">
|
||||||
<FilePrice uri={normalizeURI(uri)} />
|
<FilePrice uri={normalizeURI(uri)} />
|
||||||
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
{isRewardContent && <Icon tooltip="bottom" icon={icons.FEATURED} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="card__subtitle card__subtitle--file">
|
<span className="card__subtitle card__subtitle--file">
|
||||||
|
|
|
@ -5,6 +5,8 @@ import FileTile from 'component/fileTile';
|
||||||
import FileListSearch from 'component/fileListSearch';
|
import FileListSearch from 'component/fileListSearch';
|
||||||
import ToolTip from 'component/common/tooltip';
|
import ToolTip from 'component/common/tooltip';
|
||||||
import Page from 'component/page';
|
import Page from 'component/page';
|
||||||
|
import Icon from 'component/common/icon';
|
||||||
|
import * as icons from 'constants/icons';
|
||||||
|
|
||||||
const MODAL_ANIMATION_TIME = 250;
|
const MODAL_ANIMATION_TIME = 250;
|
||||||
|
|
||||||
|
@ -50,10 +52,11 @@ class SearchPage extends React.PureComponent<Props> {
|
||||||
<div className="file-list__header">
|
<div className="file-list__header">
|
||||||
{__('Exact URL')}
|
{__('Exact URL')}
|
||||||
<ToolTip
|
<ToolTip
|
||||||
label="?"
|
icon
|
||||||
body={__('This is the resolution of a LBRY URL and not controlled by LBRY Inc.')}
|
body={__('This is the resolution of a LBRY URL and not controlled by LBRY Inc.')}
|
||||||
className="tooltip--header"
|
>
|
||||||
/>
|
<Icon icon={icons.HELP} />
|
||||||
|
</ToolTip>
|
||||||
</div>
|
</div>
|
||||||
<FileTile fullWidth uri={normalizeURI(query)} showUri />
|
<FileTile fullWidth uri={normalizeURI(query)} showUri />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -122,12 +122,11 @@ input::placeholder {
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
min-width: 100px;
|
min-width: 60px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: var(--input-select-bg-color);
|
background-color: var(--input-select-bg-color);
|
||||||
font: normal 12px/30px 'metropolis-medium';
|
font: normal 12px/30px 'metropolis-medium';
|
||||||
text-indent: 12px;
|
|
||||||
color: var(--input-select-color);
|
color: var(--input-select-color);
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
|
|
|
@ -161,9 +161,8 @@ $large-breakpoint: 1760px;
|
||||||
--modal-btn-bg-color: var(--btn-bg-alt);
|
--modal-btn-bg-color: var(--btn-bg-alt);
|
||||||
|
|
||||||
// /* Tooltip */
|
// /* Tooltip */
|
||||||
--tooltip-width: 300px;
|
--tooltip-bg: #555;
|
||||||
--tooltip-bg: var(--color-bg);
|
--tooltip-color: var(--color-white);
|
||||||
--tooltip-color: var(--text-color);
|
|
||||||
|
|
||||||
/* Scrollbar */
|
/* Scrollbar */
|
||||||
--scrollbar-radius: 10px;
|
--scrollbar-radius: 10px;
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
.card--small {
|
.card--small {
|
||||||
width: var(--card-small-width);
|
width: var(--card-small-width);
|
||||||
overflow-x: hidden;
|
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
|
||||||
.card__media {
|
.card__media {
|
||||||
|
@ -86,8 +85,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
.credit-amount,
|
.credit-amount,
|
||||||
.icon {
|
.icon {
|
||||||
margin-top: $spacing-vertical * 1/3;
|
margin: $spacing-vertical * 1/3;
|
||||||
margin-left: $spacing-vertical * 2/3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +124,11 @@
|
||||||
color: var(--card-text-color);
|
color: var(--card-text-color);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
margin-left: $spacing-vertical * 1/3;
|
margin-top: $spacing-vertical * 1/6;
|
||||||
|
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
margin: 0 $spacing-vertical * 1/3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,11 +136,6 @@
|
||||||
padding-top: $spacing-vertical * 1/3;
|
padding-top: $spacing-vertical * 1/3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card__subtitle--file-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__subtitle--block {
|
.card__subtitle--block {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -243,12 +240,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.card-row is used on the discover/subscriptions page
|
.card-row is used on the discover page
|
||||||
It is a list of cards that extend past the right edge of the screen
|
It is a list of cards that extend past the right edge of the screen
|
||||||
There are left/right arrows to scroll the cards and view hidden content
|
There are left/right arrows to scroll the cards and view hidden content
|
||||||
*/
|
*/
|
||||||
.card-row {
|
.card-row {
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: var(--card-small-width);
|
min-width: var(--card-small-width);
|
||||||
|
@ -289,6 +285,18 @@
|
||||||
padding-top: $spacing-vertical * 2/3;
|
padding-top: $spacing-vertical * 2/3;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
overflow-y: visible;
|
||||||
|
// 31 px to handle to padding between cards
|
||||||
|
width: calc((100% / 4) - 31px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:not(:first-of-type) {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.card:first-of-type {
|
.card:first-of-type {
|
||||||
margin-left: $spacing-width;
|
margin-left: $spacing-width;
|
||||||
}
|
}
|
||||||
|
@ -327,27 +335,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-row__scrollhouse {
|
|
||||||
padding-top: $spacing-vertical * 2/3;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.card {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
overflow: visible;
|
|
||||||
// 31 px to handle to padding between cards
|
|
||||||
width: calc((100% / 4) - 31px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:not(:first-of-type) {
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:last-of-type {
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__success-msg {
|
.card__success-msg {
|
||||||
border-left: 2px solid var(--success-msg-border);
|
border-left: 2px solid var(--success-msg-border);
|
||||||
color: var(--success-msg-color);
|
color: var(--success-msg-color);
|
||||||
|
|
|
@ -1,30 +1,97 @@
|
||||||
@import '../mixin/link.scss';
|
|
||||||
|
|
||||||
.tooltip {
|
.tooltip {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0 $spacing-vertical / 3;
|
display: inline-block;
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip__body {
|
// When there is a label for the tooltip and not just using a button or icon
|
||||||
|
.tooltip.tooltip--label {
|
||||||
|
font-size: 12px;
|
||||||
|
padding-left: $spacing-vertical * 1/3;
|
||||||
|
|
||||||
|
.tooltip__body {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip--icon {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip text */
|
||||||
|
.tooltip .tooltip__body {
|
||||||
|
background-color: var(--tooltip-bg);
|
||||||
|
font-family: 'metropolis-medium';
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--tooltip-color);
|
||||||
|
border-radius: 8px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
left: 50%;
|
width: 200px;
|
||||||
margin-left: calc(var(--tooltip-width) * -1 / 2);
|
text-align: center;
|
||||||
white-space: normal;
|
white-space: pre-wrap;
|
||||||
box-sizing: border-box;
|
padding: $spacing-vertical * 1/3;
|
||||||
padding: $spacing-vertical / 2;
|
visibility: hidden;
|
||||||
width: var(--tooltip-width);
|
|
||||||
color: var(--tooltip-color);
|
|
||||||
background-color: var(--tooltip-bg);
|
|
||||||
font-size: calc(var(--font-size) * 7 / 8);
|
|
||||||
line-height: var(--font-line-height);
|
|
||||||
box-shadow: var(--box-shadow-layer);
|
|
||||||
border-radius: var(--card-radius);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip__link {
|
.tooltip .tooltip__body::after {
|
||||||
font-size: calc(var(--font-size) * 3 / 4);
|
content: ' ';
|
||||||
margin-left: var(--button-padding);
|
width: 0;
|
||||||
vertical-align: middle;
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip--top .tooltip__body {
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -100px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-color: var(--tooltip-bg) transparent transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip--right .tooltip__body {
|
||||||
|
margin-top: -5px;
|
||||||
|
margin-left: 10px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
top: 17px;
|
||||||
|
right: 100%; /* To the left of the tooltip */
|
||||||
|
margin-top: -5px;
|
||||||
|
border-color: transparent var(--tooltip-bg) transparent transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip--bottom .tooltip__body {
|
||||||
|
top: 90%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -100px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-color: transparent transparent var(--tooltip-bg) transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip.tooltip--left .tooltip__body {
|
||||||
|
top: -5px;
|
||||||
|
right: 105%;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
top: 17px;
|
||||||
|
left: 100%;
|
||||||
|
margin-top: -5px;
|
||||||
|
border-color: transparent transparent transparent var(--tooltip-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltip__body {
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
@mixin text-link($color: var(--color-primary), $hover-opacity: 0.7) {
|
|
||||||
.icon {
|
|
||||||
&:first-child {
|
|
||||||
padding-right: 5px;
|
|
||||||
}
|
|
||||||
&:last-child:not(:only-child) {
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.no-underline) {
|
|
||||||
text-decoration: underline;
|
|
||||||
.icon {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
opacity: $hover-opacity;
|
|
||||||
transition: opacity var(--transition-duration) var(--transition-type);
|
|
||||||
text-decoration: underline;
|
|
||||||
.icon {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
color: $color;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
Loading…
Reference in a new issue