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,18 @@
.Dialog {
@include custom-scrollbar;
position: fixed;
overflow: auto;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: $unit * 5;
z-index: $zIndex-dialog;
overscroll-behavior: none; // prevent swiping with touch-pad on MacOSX
&.modal {
background: transparentize(#222, .5);
}
}
@@ -0,0 +1,145 @@
import "./dialog.scss";
import * as React from "react";
import { createPortal, findDOMNode } from "react-dom";
import { disposeOnUnmount, observer } from "mobx-react";
import { reaction } from "mobx";
import { Animate } from "../animate";
import { cssNames, noop, stopPropagation } from "../../utils";
import { navigation } from "../../navigation";
export interface DialogProps {
className?: string;
isOpen?: boolean;
open?: () => void;
close?: () => void;
onOpen?: () => void;
onClose?: () => void;
modal?: boolean;
pinned?: boolean;
animated?: boolean;
}
interface DialogState {
isOpen: boolean;
}
// fixme: handle animation end props.onClose() (await props.close()?)
@observer
export class Dialog extends React.PureComponent<DialogProps, DialogState> {
private contentElem: HTMLElement;
static defaultProps: DialogProps = {
isOpen: false,
open: noop,
close: noop,
onOpen: noop,
onClose: noop,
modal: true,
animated: true,
pinned: false,
};
@disposeOnUnmount
closeOnNavigate = reaction(() => navigation.getPath(), () => this.close())
public state: DialogState = {
isOpen: this.props.isOpen,
}
get elem() {
return findDOMNode(this) as HTMLElement;
}
get isOpen() {
return this.state.isOpen;
}
componentDidMount() {
if (this.isOpen) this.onOpen();
}
componentDidUpdate(prevProps: DialogProps) {
const { isOpen } = this.props;
if (isOpen !== prevProps.isOpen) {
this.toggle(isOpen);
}
}
componentWillUnmount() {
if (this.isOpen) this.onClose();
}
toggle(isOpen: boolean) {
if (isOpen) this.open();
else this.close();
}
open() {
requestAnimationFrame(this.onOpen); // wait for render(), bind close-event to this.elem
this.setState({ isOpen: true });
this.props.open();
}
close() {
this.onClose(); // must be first to get access to dialog's content from outside
this.setState({ isOpen: false });
this.props.close();
}
onOpen = () => {
this.props.onOpen();
if (!this.props.pinned) {
if (this.elem) this.elem.addEventListener('click', this.onClickOutside);
window.addEventListener('keydown', this.onEscapeKey);
}
}
onClose = () => {
this.props.onClose();
if (!this.props.pinned) {
if (this.elem) this.elem.removeEventListener('click', this.onClickOutside);
window.removeEventListener('keydown', this.onEscapeKey);
}
}
onEscapeKey = (evt: KeyboardEvent) => {
const escapeKey = evt.code === "Escape";
if (escapeKey) {
this.close();
evt.stopPropagation();
}
}
onClickOutside = (evt: MouseEvent) => {
const target = evt.target as HTMLElement;
if (!this.contentElem.contains(target)) {
this.close();
evt.stopPropagation();
}
}
render() {
const { modal, animated, pinned } = this.props;
let { className } = this.props;
className = cssNames("Dialog flex center", className, { modal, pinned });
let dialog = (
<div className={className} onClick={stopPropagation}>
<div className="box" ref={e => this.contentElem = e}>
{this.props.children}
</div>
</div>
);
if (animated) {
dialog = (
<Animate enter={this.isOpen} name="opacity-scale">
{dialog}
</Animate>
);
}
else if (!this.isOpen) {
return null;
}
return createPortal(dialog, document.body);
}
}
@@ -0,0 +1 @@
export * from './dialog'
@@ -0,0 +1,15 @@
.LogsDialog {
.Wizard {
--wizard-width: 70vw;
}
.WizardStep {
.step-content {
padding: var(--wizard-spacing);
}
code {
max-height: 50vh;
}
}
}
@@ -0,0 +1,52 @@
import "./logs-dialog.scss";
import * as React from "react";
import { t, Trans } from "@lingui/macro";
import { Dialog, DialogProps } from "../dialog";
import { Wizard, WizardStep } from "../wizard";
import { copyToClipboard } from "../../utils";
import { Notifications } from "../notifications";
import { Button } from "../button";
import { Icon } from "../icon";
import { _i18n } from "../../i18n";
interface Props extends DialogProps {
title: string;
logs: string;
}
export class LogsDialog extends React.Component<Props> {
public logsElem: HTMLElement;
copyToClipboard = () => {
if (copyToClipboard(this.logsElem)) {
Notifications.ok(_i18n._(t`Logs copied to clipboard.`))
}
}
render() {
const { title, logs, ...dialogProps } = this.props;
const header = <h5>{title}</h5>
const customButtons = (
<div className="buttons flex gaps align-center justify-space-between">
<Button plain onClick={this.copyToClipboard}>
<Icon material="assignment"/> <Trans>Copy to clipboard</Trans>
</Button>
<Button plain onClick={dialogProps.close}>
<Trans>Close</Trans>
</Button>
</div>
)
return (
<Dialog {...dialogProps} className="LogsDialog">
<Wizard header={header} done={dialogProps.close}>
<WizardStep scrollable={false} customButtons={customButtons}>
<code ref={e => this.logsElem = e}>
{logs || <Trans>There are no logs available.</Trans>}
</code>
</WizardStep>
</Wizard>
</Dialog>
)
}
}