Lens app source code (#119)

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen authored and GitHub committed 2020-03-15 09:52:02 +02:00
1 parent 936cbd53d4
commit 1d0815abd2
797 files changed
+73714 -215

No files matched your search

@@ -0,0 +1,2 @@
export * from './tooltip'
export * from './withTooltip'
@@ -0,0 +1,104 @@
.Tooltip {
--tooltip-bgc: #{$contentColor};
--tooltip-color: #{$textColorSecondary};
--tooltip-margin: #{$padding / 2 $padding / 3};
position: absolute;
background: var(--tooltip-bgc);
font-size: small;
font-weight: normal;
border: 1px solid $borderColor;
border-radius: $radius;
color: var(--tooltip-color);
margin: var(--tooltip-margin);
white-space: normal;
padding: .5em;
text-align: center;
transition-delay: 100ms; // delay for .Animate
pointer-events: none;
z-index: 1000;
* {
white-space: normal;
}
&:empty {
display: none;
}
&.following {
position: fixed;
}
&:not(.following) {
&.left {
left: 0;
}
&.right {
right: 0;
}
&.top {
bottom: 100%;
}
&.bottom {
top: 100%;
}
&.center {
left: 50%;
transform: translateX(-50%);
}
}
}
.TooltipContent {
&.nowrap {
white-space: nowrap;
}
&.narrow {
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
word-wrap: break-word;
text-align: left;
}
&.small {
font-size: $font-size-small;
}
&.warning {
color: $colorError;
}
&.tableView {
min-width: 200px;
> :not(:last-child) {
margin-bottom: var(--flex-gap);
}
.title {
color: $textColorAccent;
text-align: center;
}
.name {
color: $textColorAccent;
text-align: right;
flex: 0 0 35%;
}
.value {
text-align: left;
max-width: 300px;
word-break: break-word;
}
}
}
@@ -0,0 +1,151 @@
import './tooltip.scss'
import React from "react"
import { observer } from "mobx-react";
import { observable } from "mobx";
import { createPortal } from "react-dom"
import { autobind, cssNames } from "../../utils";
import { Animate } from "../animate";
export interface TooltipProps {
htmlFor: string;
className?: string;
position?: Position;
useAnimation?: boolean;
following?: boolean; // tooltip is following mouse position
style?: React.CSSProperties;
children?: React.ReactNode;
}
interface Position {
left?: boolean;
right?: boolean;
top?: boolean;
bottom?: boolean;
center?: boolean;
}
const defaultProps: Partial<TooltipProps> = {
useAnimation: true,
position: {
center: true,
bottom: true,
}
};
@observer
export class Tooltip extends React.Component<TooltipProps> {
static defaultProps = defaultProps as object;
public anchor: HTMLElement;
public elem: HTMLElement;
@observable isVisible = false;
componentDidMount() {
const { htmlFor } = this.props;
this.anchor = htmlFor ? document.getElementById(htmlFor) : this.elem.parentElement;
if (this.anchor) {
if (window.getComputedStyle(this.anchor).position === "static") {
this.anchor.style.position = "relative"
}
this.anchor.addEventListener("mouseenter", this.onMouseEnter);
this.anchor.addEventListener("mouseleave", this.onMouseLeave);
this.anchor.addEventListener("mousemove", this.onMouseMove);
}
}
componentWillUnmount() {
if (this.anchor) {
this.anchor.removeEventListener("mouseenter", this.onMouseEnter);
this.anchor.removeEventListener("mouseleave", this.onMouseLeave);
this.anchor.removeEventListener("mousemove", this.onMouseMove);
}
}
@autobind()
onMouseEnter(evt: MouseEvent) {
this.isVisible = true;
this.onMouseMove(evt);
}
@autobind()
onMouseLeave(evt: MouseEvent) {
this.isVisible = false;
}
@autobind()
onMouseMove(evt: MouseEvent) {
if (!this.props.following) {
return;
}
const offsetX = -10;
const offsetY = 10;
const { clientX, clientY } = evt;
const { innerWidth, innerHeight } = window;
const initialPos: Partial<CSSStyleDeclaration> = {
top: "auto",
left: "auto",
right: (innerWidth - clientX + offsetX) + "px",
bottom: (innerHeight - clientY + offsetY) + "px",
}
Object.assign(this.elem.style, initialPos);
// correct position if not fits to viewport
const { left, top } = this.elem.getBoundingClientRect();
if (left < 0) {
this.elem.style.left = clientX + offsetX + "px";
this.elem.style.right = "auto"
}
if (top < 0) {
this.elem.style.top = clientY + offsetY + "px";
this.elem.style.bottom = "auto"
}
}
@autobind()
bindRef(elem: HTMLElement) {
this.elem = elem;
}
render() {
const { isVisible } = this;
const { useAnimation, position, following, style, children } = this.props;
let { className } = this.props;
className = cssNames('Tooltip', position, { following }, className);
const tooltip = (
<Animate enter={isVisible} enabled={useAnimation}>
<div className={className} ref={this.bindRef} style={style}>
{children}
</div>
</Animate>
);
if (following) {
return createPortal(tooltip, document.body);
}
return tooltip;
}
}
interface TooltipContentProps {
className?: string;
narrow?: boolean; // max-width
warning?: boolean; // color
small?: boolean; // font-size
nowrap?: boolean; // white-space
tableView?: boolean;
}
export class TooltipContent extends React.Component<TooltipContentProps> {
render() {
const { className, children, ...modifiers } = this.props;
return (
<div className={cssNames("TooltipContent", className, modifiers)}>
{children}
</div>
)
}
}
@@ -0,0 +1,48 @@
// Tooltip decorator for simple composition with other components
import React, { HTMLAttributes, ReactNode } from "react"
import hoistNonReactStatics from "hoist-non-react-statics";
import { Tooltip, TooltipProps } from "./tooltip";
import { isReactNode } from "../../utils/isReactNode";
import uniqueId from "lodash/uniqueId"
export interface TooltipDecoratorProps {
tooltip?: ReactNode | Omit<TooltipProps, "htmlFor">;
}
export function withTooltip<T extends React.ComponentType<any>>(Target: T): T {
const DecoratedComponent = class extends React.Component<HTMLAttributes<any> & TooltipDecoratorProps> {
static displayName = `withTooltip(${Target.displayName || Target.name})`;
protected tooltipId = uniqueId("tooltip_target_");
render() {
const { tooltip, ...targetProps } = this.props;
if (tooltip) {
const tooltipId = targetProps.id || this.tooltipId;
const tooltipProps: TooltipProps = {
htmlFor: tooltipId,
following: true,
...(isReactNode(tooltip) ? { children: tooltip } : tooltip),
};
if (!tooltipProps.following) {
targetProps.style = {
position: "relative",
...(targetProps.style || {})
}
}
targetProps.id = tooltipId;
targetProps.children = (
<>
{targetProps.children}
<Tooltip {...tooltipProps}/>
</>
)
}
return <Target {...targetProps as any}/>;
}
}
return hoistNonReactStatics(DecoratedComponent, Target) as any;
}