Lens restructure (#540)

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman authored and GitHub committed 2020-06-30 14:35:16 +03:00
1 parent e810eab39f
commit b7974827d2
786 files changed
+14567 -24066

No files matched your search

@@ -0,0 +1,19 @@
import React from "react";
import { DrawerItem, DrawerItemProps } from "./drawer-item";
import { Badge } from "../badge";
interface Props extends DrawerItemProps {
labels: string[];
}
export function DrawerItemLabels(props: Props) {
const { labels, ...itemProps } = props;
if (!labels || !labels.length) {
return null;
}
return (
<DrawerItem {...itemProps} labelsOnly>
{labels.map(label => <Badge key={label} label={label} title={label}/>)}
</DrawerItem>
)
}
@@ -0,0 +1,68 @@
.DrawerItem {
display: grid;
grid-template-columns: minmax(30%, min-content) auto;
border-bottom: 1px solid $borderFaintColor;
padding: $padding 0;
> .name {
padding-right: $padding;
overflow: hidden;
text-overflow: ellipsis;
color: $drawerItemNameColor;
}
> .value {
color: $drawerItemValueColor;
word-break: break-word;
max-width: 100%;
min-width: 0;
&:empty:after {
content: '—'
}
p {
margin-bottom: $margin;
&:last-child {
margin-bottom: 0;
}
}
a {
color: $primary;
}
}
&.labelsOnly {
$spacing: $margin / 2;
> .value {
margin: -$spacing;
&:empty {
margin: 0;
}
.Badge {
float: left;
margin: $spacing;
&.disabled {
opacity: 0.5;
}
}
}
}
.DrawerItem {
&:last-of-type {
padding-bottom: 0;
border-bottom: none;
}
> .name {
font-weight: $font-weight-bold;
}
}
}
@@ -0,0 +1,26 @@
import "./drawer-item.scss";
import React from "react";
import { cssNames } from "../../utils";
export interface DrawerItemProps extends React.HTMLAttributes<any> {
name: React.ReactNode;
className?: string;
title?: string;
labelsOnly?: boolean;
hidden?: boolean;
}
export class DrawerItem extends React.Component<DrawerItemProps> {
render() {
const { name, title, labelsOnly, children, hidden, ...elemProps } = this.props
let { className } = this.props;
if (hidden) return null
className = cssNames("DrawerItem", className, { labelsOnly });
return (
<div {...elemProps} className={className} title={title}>
<span className="name">{name}</span>
<span className="value">{children}</span>
</div>
)
}
}
@@ -0,0 +1,22 @@
.DrawerParamToggler {
.param-label {
flex-grow: 2
}
.param-link {
cursor: pointer;
.param-link-text {
color: $lensBlue;
text-decoration: underline;
}
}
.param-content {
&.open {
display: block;
}
display: none;
}
}
@@ -0,0 +1,40 @@
import "./drawer-param-toggler.scss";
import React from "react";
import { t } from "@lingui/macro";
import { Icon } from "../icon";
import { cssNames } from "../../utils";
import { _i18n } from "../../i18n";
interface Props {
label: string | number;
}
interface State {
open?: boolean;
}
export class DrawerParamToggler extends React.Component<Props, State> {
public state: State = {}
toggle = () => {
this.setState({ open: !this.state.open })
}
render() {
const { label, children } = this.props
const { open } = this.state
const icon = `arrow_drop_${open ? "up" : "down"}`
const link = open ? _i18n._(t`Hide`) : _i18n._(t`Show`)
return (
<div className="DrawerParamToggler">
<div className="flex gaps align-center">
<div className="param-label">{label}</div>
<div className="param-link" onClick={this.toggle}>
<span className="param-link-text">{link}</span>
<Icon material={icon}/>
</div>
</div>
<div className={cssNames("param-content", { open })}>{children}</div>
</div>
)
}
}
@@ -0,0 +1,5 @@
.DrawerTitle {
padding: $padding * 1.5 $padding * 3;
margin: $margin * 3 (-$margin * 3);
background: $drawerSubtitleBackground;
}
@@ -0,0 +1,19 @@
import "./drawer-title.scss";
import React from "react";
import { cssNames } from "../../utils";
interface Props {
className?: string;
title?: React.ReactNode;
}
export class DrawerTitle extends React.Component<Props> {
render() {
const { title, children, className } = this.props
return (
<div className={cssNames("DrawerTitle", className)}>
{title || children}
</div>
)
}
}
@@ -0,0 +1,90 @@
.Drawer {
--size: 50%;
--full-size: 75%;
--spacing: #{$padding * 3};
position: absolute;
background: $contentColor;
box-shadow: 0 0 $unit * 2 $boxShadow;
z-index: $zIndex-drawer;
&.light {
.drawer-content {
@include custom-scrollbar(dark);
}
}
&.left {
left: 0;
}
&.right {
right: 0;
}
&.top {
top: 0;
}
&.bottom {
bottom: 0;
}
&.left,
&.right {
top: 0;
width: var(--size);
height: 100%;
}
&.top,
&.bottom {
left: 0;
width: 100%;
height: var(--size);
}
.drawer-wrapper {
height: 100%;
min-height: 100%;
}
.drawer-title {
background: $lensBlue;
color: $drawerTitleText;
padding: $padding $padding * 2.5;
word-break: break-all;
position: relative;
min-height: 54px;
.drawer-title-text {
font-weight: bold;
flex-grow: 2;
padding-right: $padding;
}
.MenuActions.toolbar .Icon {
color: $drawerTitleText;
}
}
.drawer-content {
@include custom-scrollbar;
> *:not(.Spinner) {
padding: var(--spacing);
}
.Table .TableHead {
background-color: $contentColor;
border-bottom: 1px solid $borderFaintColor;
}
}
@include media("<=900px") {
&.left, &.right {
width: var(--full-size)
}
}
}
+125
View File
@@ -0,0 +1,125 @@
import "./drawer.scss";
import React from "react";
import { createPortal } from "react-dom";
import { cssNames, noop } from "../../utils";
import { Icon } from "../icon";
import { Animate, AnimateName } from "../animate";
import { browserHistory } from "../../navigation";
import { themeStore } from "../../theme.store";
export interface DrawerProps {
open: boolean;
title: React.ReactNode;
size?: string; // e.g. 50%, 500px, etc.
usePortal?: boolean;
className?: string | object;
contentClass?: string | object;
position?: "top" | "left" | "right" | "bottom";
animation?: AnimateName;
onClose?: () => void;
toolbar?: React.ReactNode;
}
const defaultProps: Partial<DrawerProps> = {
position: "right",
animation: "slide-right",
usePortal: false,
onClose: noop,
};
export class Drawer extends React.Component<DrawerProps> {
static defaultProps = defaultProps as object;
private mouseDownTarget: HTMLElement
private contentElem: HTMLElement
private scrollElem: HTMLElement
private scrollPos = new Map<string, number>();
private stopListenLocation = browserHistory.listen(() => {
this.restoreScrollPos();
});
componentDidMount() {
window.addEventListener("mousedown", this.onMouseDown)
window.addEventListener("click", this.onClickOutside)
window.addEventListener("keydown", this.onEscapeKey)
}
componentWillUnmount() {
this.stopListenLocation();
window.removeEventListener("mousedown", this.onMouseDown)
window.removeEventListener("click", this.onClickOutside)
window.removeEventListener("keydown", this.onEscapeKey)
}
saveScrollPos = () => {
if (!this.scrollElem) return;
const key = browserHistory.location.key;
this.scrollPos.set(key, this.scrollElem.scrollTop);
}
restoreScrollPos = () => {
if (!this.scrollElem) return;
const key = browserHistory.location.key;
this.scrollElem.scrollTop = this.scrollPos.get(key) || 0;
}
onEscapeKey = (evt: KeyboardEvent) => {
if (!this.props.open) {
return;
}
if (evt.code === "Escape") {
this.close();
}
}
onClickOutside = (evt: MouseEvent) => {
const { contentElem, mouseDownTarget, close, props: { open } } = this
if (!open || evt.defaultPrevented || contentElem.contains(mouseDownTarget)) {
return;
}
const clickedElem = evt.target as HTMLElement;
const isOutsideAnyDrawer = !clickedElem.closest('.Drawer');
if (isOutsideAnyDrawer) {
close()
}
this.mouseDownTarget = null
}
onMouseDown = (evt: MouseEvent) => {
if (this.props.open) {
this.mouseDownTarget = evt.target as HTMLElement
}
}
close = () => {
const { open, onClose } = this.props
if (open) onClose();
}
render() {
const { open, position, title, animation, children, toolbar, size, usePortal } = this.props
let { className, contentClass } = this.props;
className = cssNames("Drawer", className, position, themeStore.activeTheme.type);
contentClass = cssNames("drawer-content flex column box grow", contentClass);
const style = size ? { "--size": size } as React.CSSProperties : undefined;
const drawer = (
<Animate name={animation} enter={open}>
<div className={className} style={style} ref={e => this.contentElem = e}>
<div className="drawer-wrapper flex column">
<div className="drawer-title flex align-center">
<div className="drawer-title-text">{title}</div>
{toolbar}
<Icon material="close" onClick={this.close}/>
</div>
<div className={contentClass} onScroll={this.saveScrollPos} ref={e => this.scrollElem = e}>
{children}
</div>
</div>
</div>
</Animate>
)
return usePortal ? createPortal(drawer, document.body) : drawer;
}
}
+5
View File
@@ -0,0 +1,5 @@
export * from "./drawer"
export * from "./drawer-title"
export * from "./drawer-item"
export * from "./drawer-item-labels"
export * from "./drawer-param-toggler"