Files
squoosh/src/client/lazy-app/Compress/Flyout/index.tsx
2020-12-09 02:09:24 -05:00

85 lines
2.1 KiB
TypeScript

import { h, cloneElement, Component, VNode, createRef, ComponentChildren, ComponentProps } from "preact";
import { ClickOutsideDetector } from "../ClickOutsideDetector";
import * as style from './style.css';
import 'add-css:./style.css';
type Anchor = 'left' | 'right' | 'top' | 'bottom';
interface Props extends ComponentProps<'aside'> {
showing?: boolean;
direction?: 'up' | 'down';
anchor?: Anchor | Anchor[];
toggle?: VNode;
children?: ComponentChildren;
}
interface State {
showing: boolean;
}
export default class Flyout extends Component<Props, State> {
state = {
showing: this.props.showing === true
};
private menu = createRef<HTMLElement>();
private hide = () => {
this.setState({ showing: false });
};
private toggle = () => {
this.setState({ showing: !this.state.showing });
};
componentWillReceiveProps({ showing }: Props) {
if (showing !== this.props.showing) {
this.setState({ showing });
}
}
componentDidUpdate(prevProps: Props, prevState: State) {
if (this.state.showing && !prevState.showing) {
const menu = this.menu.current;
if (menu) {
let toFocus = menu.firstElementChild;
for (let child of menu.children) {
if (child.hasAttribute('autofocus')) {
toFocus = child;
break;
}
}
// @ts-ignore-next
if (toFocus) toFocus.focus();
}
}
}
render({ direction, anchor, toggle, children, ...props }: Props, { showing }: State) {
const toggleProps = {
flyoutOpen: showing,
onClick: this.toggle
};
const anchorText = Array.isArray(anchor) ? anchor.join(' ') : anchor;
return (
<span class={style.wrap} data-flyout-open={showing ? '' : undefined}>
<ClickOutsideDetector onClick={this.hide}>
{toggle && cloneElement(toggle, toggleProps)}
<aside
{...props}
ref={this.menu}
hidden={!showing}
data-anchor={anchorText}
data-direction={direction}
>
{children}
</aside>
</ClickOutsideDetector>
</span>
);
}
}