Merge branch 'next' of github.com:coralproject/talk into ui-components

This commit is contained in:
Belen Curcio
2018-08-08 08:31:08 -03:00
102 changed files with 2617 additions and 588 deletions
@@ -15,6 +15,9 @@ import * as styles from "./BaseButton.css";
interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** If set renders an anchor tag instead */
anchor?: boolean;
href?: string;
target?: string;
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
@@ -11,6 +11,10 @@ import * as styles from "./Button.css";
// This should extend from BaseButton instead but we can't because of this bug
// TODO: add bug link.
interface InnerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
/** If set renders an anchor tag instead */
anchor?: boolean;
href?: string;
target?: string;
/**
* This prop can be used to add custom classnames.
* It is handled by the `withStyles `HOC.
@@ -19,8 +19,11 @@ clicks outside the component.
Click the blue background. It should trigger an alert. Nothing should happen if you click the button.
<Playground>
<div style={{background: 'blue', padding: '10px'}}>
<ClickOutside onClickOutside={() => alert('You clicked outside!')}>
<div id="outside" style={{background: 'blue', padding: '10px'}}>
<ClickOutside onClickOutside={e => {
if (e.srcElement.id === "outside") {
alert('You clicked outside!');
}}}>
<Button variant="filled">Push Me</Button>
</ClickOutside>
</div>
@@ -10,8 +10,8 @@ export type ClickFarAwayRegister = (
callback: ClickFarAwayCallback
) => ClickFarAwayUnlistenCallback;
interface Props {
onClickOutside: () => void;
export interface ClickOutsideProps {
onClickOutside: (e?: MouseEvent) => void;
/**
* A way to listen for clicks that are e.g. outside of the
@@ -22,7 +22,7 @@ interface Props {
children: React.ReactNode;
}
export class ClickOutside extends React.Component<Props> {
export class ClickOutside extends React.Component<ClickOutsideProps> {
public domNode: Element | null = null;
private unlisten?: ClickFarAwayUnlistenCallback;
@@ -30,7 +30,7 @@ export class ClickOutside extends React.Component<Props> {
const { onClickOutside } = this.props;
if (!e || !this.domNode!.contains(e.target as HTMLInputElement)) {
// tslint:disable-next-line:no-unused-expression
onClickOutside && onClickOutside();
onClickOutside && onClickOutside(e);
}
};
@@ -65,7 +65,9 @@ export class ClickOutside extends React.Component<Props> {
}
}
const ClickOutsideWithContext: StatelessComponent<Props> = props => (
const ClickOutsideWithContext: StatelessComponent<
ClickOutsideProps
> = props => (
<UIContext.Consumer>
{({ registerClickFarAway }) => (
<ClickOutside {...props} registerClickFarAway={registerClickFarAway} />
@@ -1 +1 @@
export { default as ClickOutside, ClickFarAwayRegister } from "./ClickOutside";
export { default, ClickFarAwayRegister } from "./ClickOutside";
+6 -6
View File
@@ -37,18 +37,18 @@
}
.sm {
font-size: 14px;
width: 14px;
}
.md {
font-size: 18px;
width: 18px;
}
.md {
.lg {
font-size: 24px;
width: 24px;
}
.lg {
.xl {
font-size: 36px;
width: 36px;
}
.xl {
font-size: 48px;
width: 48px;
}
@@ -0,0 +1,21 @@
.root {
background: var(--palette-common-white);
border: 1px solid var(--palette-grey-lighter);
box-sizing: border-box;
box-shadow: var(--elevation-main);
border-radius: var(--round-corners);
padding: calc(0.5 * var(--spacing-unit));
}
.top {
margin: calc(0.5 * var(--spacing-unit)) 0;
}
.left {
margin: 0 calc(0.5 * var(--spacing-unit));
}
.right {
margin: 0 calc(0.5 * var(--spacing-unit));
}
.bottom {
margin: calc(0.5 * var(--spacing-unit)) 0;
}
@@ -0,0 +1,49 @@
---
name: Popover
menu: UI Kit
---
import { Playground } from 'docz'
import Popover from './Popover'
import Button from '../Button'
import Flex from '../Flex'
import Typography from '../Typography'
import ButtonIcon from '../Button/ButtonIcon'
# Popover
`Popover` renders a popover dialog attached to another `Element`.
## Basic usage
<Playground>
<Popover
body={<Typography>This is the body</Typography>}
>
{({ toggleVisibility, forwardRef }) => (
<Button onClick={toggleVisibility} forwardRef={forwardRef} variant="filled" color="primary">
Click me!
</Button>
)}
</Popover>
</Playground>
#### Example with `placement=top`
<Playground>
<Popover
placement="top"
body={({ toggleVisibility }) => (
<Flex itemGutter="half">
<Typography>This is the body</Typography>
<Button onClick={toggleVisibility} size="small">
<ButtonIcon>close</ButtonIcon>
</Button>
</Flex>
)}
>
{({ toggleVisibility, forwardRef }) => (
<Button onClick={toggleVisibility} forwardRef={forwardRef} variant="filled" color="primary">
Click me!
</Button>
)}
</Popover>
</Playground>
@@ -0,0 +1,147 @@
import cn from "classnames";
import React from "react";
import {
Manager,
Popper,
PopperArrowProps,
Reference,
RefHandler,
} from "react-popper";
import AriaInfo from "../AriaInfo";
import * as styles from "./Popover.css";
type Placement =
| "top-start"
| "top"
| "top-end"
| "right-start"
| "right"
| "right-end"
| "bottom-end"
| "bottom"
| "bottom-start"
| "left-end"
| "left"
| "left-start";
interface BodyRenderProps {
toggleVisibility: () => void;
visible: boolean;
}
interface ChildrenRenderProps {
toggleVisibility: () => void;
forwardRef?: RefHandler;
visible: boolean;
}
interface PopoverProps {
body: (props: BodyRenderProps) => React.ReactNode | React.ReactElement<any>;
children: (props: ChildrenRenderProps) => React.ReactNode;
description: string;
id: string;
onClose?: () => void;
className?: string;
placement?: Placement;
}
interface State {
visible: false;
}
class Popover extends React.Component<PopoverProps> {
public static defaultProps = {
placement: "top",
};
public state: State = {
visible: false,
};
public toggleVisibility = () => {
this.setState((state: State) => ({
visible: !state.visible,
}));
};
public close = () => {
this.setState((state: State) => ({
visible: false,
}));
};
public handleEsc = (e: KeyboardEvent) => {
if (e.key === "Escape") {
e.preventDefault();
this.close();
}
};
public componentDidMount() {
document.addEventListener("keydown", this.handleEsc, true);
}
public componentWillUnmount() {
document.removeEventListener("keydown", this.handleEsc, true);
}
public render() {
const {
id,
body,
children,
description,
className,
placement,
} = this.props;
const { visible } = this.state;
const popoverClassName = cn(styles.root, className, {
[styles.top]: placement!.startsWith("top"),
[styles.left]: placement!.startsWith("left"),
[styles.right]: placement!.startsWith("right"),
[styles.bottom]: placement!.startsWith("bottom"),
});
return (
<Manager>
<Reference>
{(props: PopperArrowProps) =>
children({
forwardRef: props.ref,
toggleVisibility: this.toggleVisibility,
visible: this.state.visible,
})
}
</Reference>
<Popper placement={placement} eventsEnabled positionFixed={false}>
{(props: PopperArrowProps) => (
<div
id={id}
role="popup"
aria-labelledby={`${id}-ariainfo`}
aria-hidden={!visible}
>
<AriaInfo id={`${id}-ariainfo`}>{description}</AriaInfo>
{visible && (
<div
style={props.style}
className={popoverClassName}
ref={props.ref}
>
{typeof body === "function"
? body({
toggleVisibility: this.toggleVisibility,
visible: this.state.visible,
})
: body}
</div>
)}
</div>
)}
</Popper>
</Manager>
);
}
}
export default Popover;
@@ -0,0 +1 @@
export { default } from "./Popover";
@@ -0,0 +1,44 @@
---
name: Popup
menu: UI Kit
---
import { Playground } from "docz"
import Popup from "./Popup"
import Button from "../Button"
import Flex from "../Flex"
import Container from "react-with-state-props"
# Popup
Declaratively control a popup.
## Basic usage
<Playground>
<Container
state={{ open: false, focus: false }}
render={props => (
<Flex itemGutter>
<Popup
href={"/static/js/src-core-client-ui-components-popup-popup.js"}
title="Coral Project"
features="menubar=0,resizable=0,width=500,height=550,top=200,left=500"
open={props.open}
focus={props.focus}
onFocus={() => props.setFocus(true)}
onBlur={() => props.setFocus(false)}
onClose={() => props.setOpen(false)}
/>
<Button onClick={() => props.setOpen(true)} variant="filled" color="primary" disabled={props.open}>
Open Popup
</Button>
<Button onClick={() => props.setOpen(false)} variant="outlined" disabled={!props.open}>
Close Popup
</Button>
<Button onClick={() => props.setFocus(true)} variant="outlined" disabled={!props.open}>
Focus Popup
</Button>
</Flex>
)}/>
</Playground>
@@ -0,0 +1,174 @@
import { Component } from "react";
interface PopupProps {
open?: boolean;
focus?: boolean;
onFocus?: (e: FocusEvent) => void;
onBlur?: (e: FocusEvent) => void;
onLoad?: (e: Event) => void;
onUnload?: (e: Event) => void;
onClose?: () => void;
href: string;
features?: string;
title?: string;
}
export default class Popup extends Component<PopupProps> {
private ref: Window | null = null;
private detectCloseInterval: any = null;
private resetCallbackInterval: any = null;
constructor(props: PopupProps) {
super(props);
if (props.open) {
this.openWindow(props);
}
}
private openWindow(props = this.props) {
this.ref = window.open(props.href, props.title, props.features);
this.setCallbacks();
// For some reasons IE needs a timeout before setting the callbacks...
setTimeout(() => this.setCallbacks(), 1000);
}
private setCallbacks() {
this.ref!.onload = e => {
if (this.detectCloseInterval) {
clearInterval(this.detectCloseInterval);
}
this.onLoad(e);
};
this.ref!.onfocus = e => {
this.onFocus(e);
};
this.ref!.onblur = e => {
this.onBlur(e);
};
// Use `onunload` instead of `onbeforeunload` which is not supported in iOS
// Safari.
this.ref!.onunload = e => {
this.onUnload(e);
if (this.resetCallbackInterval) {
clearInterval(this.resetCallbackInterval);
}
this.resetCallbackInterval = setInterval(() => {
try {
if (this.ref && this.ref.onload === null) {
if (this.resetCallbackInterval) {
clearInterval(this.resetCallbackInterval);
}
this.resetCallbackInterval = null;
this.setCallbacks();
}
} catch (err) {
// We could be getting a security exception here if the login page
// gets redirected to another domain to authenticate.
}
}, 50);
if (this.detectCloseInterval) {
clearInterval(this.detectCloseInterval);
}
this.detectCloseInterval = setInterval(() => {
if (!this.ref || this.ref.closed) {
if (this.detectCloseInterval) {
clearInterval(this.detectCloseInterval);
}
this.detectCloseInterval = null;
this.onClose();
}
}, 50);
};
}
private closeWindow() {
if (this.ref) {
if (!this.ref.closed) {
this.ref.close();
}
this.ref = null;
}
}
private focusWindow() {
if (this.ref && !this.ref.closed) {
this.ref.focus();
}
}
private blurWindow() {
if (this.ref && !this.ref.closed) {
this.ref.blur();
}
}
private onLoad = (e: Event) => {
if (this.props.onLoad) {
this.props.onLoad(e);
}
};
private onUnload = (e: Event) => {
if (this.props.onUnload) {
this.props.onUnload(e);
}
};
private onClose = () => {
if (this.props.onClose) {
this.props.onClose();
}
};
private onFocus = (e: FocusEvent) => {
if (this.props.onFocus) {
this.props.onFocus(e);
}
};
private onBlur = (e: FocusEvent) => {
if (this.props.onBlur) {
this.props.onBlur(e);
}
};
public componentWillReceiveProps(nextProps: PopupProps) {
if (nextProps.open && !this.ref) {
this.openWindow(nextProps);
}
if (this.props.open && !nextProps.open) {
this.closeWindow();
}
if (!this.props.focus && nextProps.focus) {
this.focusWindow();
}
if (this.props.focus && !nextProps.focus) {
this.blurWindow();
}
if (this.props.href !== nextProps.href) {
this.ref!.location.href = nextProps.href;
}
}
public componentWillUnmount() {
this.closeWindow();
}
public render() {
return null;
}
}
@@ -0,0 +1 @@
export { default } from "./Popup";
@@ -1 +1,2 @@
export * from "./TextField";
export { default } from "./TextField";
+4
View File
@@ -1,6 +1,8 @@
export { default as BaseButton } from "./BaseButton";
export { default as Button } from "./Button";
export { default as ButtonIcon } from "./Button/ButtonIcon";
export { default as Typography } from "./Typography";
export { default as Popover } from "./Popover";
export { default as RelativeTime } from "./RelativeTime";
export { default as UIContext, UIContextProps } from "./UIContext";
export { default as Flex } from "./Flex";
@@ -10,3 +12,5 @@ export { default as ValidationMessage } from "./ValidationMessage";
export { default as InputLabel } from "./InputLabel";
export { default as TextField } from "./TextField";
export { default as CallOut } from "./CallOut";
export { default as ClickOutside } from "./ClickOutside";
export { default as Popup } from "./Popup";