mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00: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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
Reference in new issue
Block a user