Skip to content

Commit

Permalink
Feat: Autoplay pages in fullscreen (#35981)
Browse files Browse the repository at this point in the history
* feat: add autoplay redux boilerplate

WIP auto-play settings

* feat: add page cycle settings

* feat: add cycle toggle hotkey

* chore: add tooltip text to settings icon

* settings layout

* fix: handle invalid input for custom interval

* chore: address nit
  • Loading branch information
w33ble authored May 21, 2019
1 parent 60088eb commit 917ece2
Show file tree
Hide file tree
Showing 15 changed files with 384 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,39 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React, { Fragment, Component } from 'react';
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import {
EuiFlexGroup,
EuiFlexGrid,
EuiFlexItem,
EuiFormRow,
EuiButton,
EuiLink,
EuiFieldText,
EuiSpacer,
EuiHorizontalRule,
EuiDescriptionList,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
EuiFormLabel,
EuiText,
EuiButtonIcon,
EuiToolTip,
} from '@elastic/eui';
import { timeDurationString } from '../../../lib/time_duration';
import { RefreshControl } from '../refresh_control';
import { CustomInterval } from './custom_interval';

const ListGroup = ({ children }) => <ul style={{ listStyle: 'none', margin: 0 }}>{[children]}</ul>;

export class AutoRefreshControls extends Component {
static propTypes = {
refreshInterval: PropTypes.number,
setRefresh: PropTypes.func.isRequired,
disableInterval: PropTypes.func.isRequired,
};
export const AutoRefreshControls = ({ refreshInterval, setRefresh, disableInterval }) => {
const RefreshItem = ({ duration, label }) => (
<li>
<EuiLink onClick={() => setRefresh(duration)}>{label}</EuiLink>
</li>
);

refreshInput = null;

render() {
const { refreshInterval, setRefresh, disableInterval } = this.props;

const RefreshItem = ({ duration, label }) => (
<li>
<EuiLink onClick={() => setRefresh(duration)}>{label}</EuiLink>
</li>
);

return (
<div>
return (
<EuiFlexGroup direction="column" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiFlexGroup alignItems="center" justifyContent="spaceAround" gutterSize="xs">
<EuiFlexItem>
<EuiDescriptionList textStyle="reverse">
Expand All @@ -55,11 +45,6 @@ export class AutoRefreshControls extends Component {
{refreshInterval > 0 ? (
<Fragment>
<span>Every {timeDurationString(refreshInterval)}</span>
<div>
<EuiLink size="s" onClick={disableInterval}>
Disable auto-refresh
</EuiLink>
</div>
</Fragment>
) : (
<span>Manually</span>
Expand All @@ -68,7 +53,22 @@ export class AutoRefreshControls extends Component {
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<RefreshControl />
<EuiFlexGroup justifyContent="flexEnd" gutterSize="xs">
{refreshInterval > 0 ? (
<EuiFlexItem grow={false}>
<EuiToolTip position="bottom" content="Disable auto-refresh">
<EuiButtonIcon
iconType="cross"
onClick={disableInterval}
aria-label="Disable auto-refresh"
/>
</EuiToolTip>
</EuiFlexItem>
) : null}
<EuiFlexItem grow={false}>
<RefreshControl />
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>

Expand Down Expand Up @@ -100,35 +100,17 @@ export class AutoRefreshControls extends Component {
</EuiFlexItem>
</EuiFlexGrid>
</EuiText>
</EuiFlexItem>

<EuiSpacer size="m" />
<EuiFlexItem grow={false}>
<CustomInterval onSubmit={value => setRefresh(value)} />
</EuiFlexItem>
</EuiFlexGroup>
);
};

<form
onSubmit={ev => {
ev.preventDefault();
setRefresh(this.refreshInput.value);
}}
>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<EuiFormRow
label="Set a custom interval"
helpText="Use shorthand notation, like 30s, 10m, or 1h"
compressed
>
<EuiFieldText inputRef={i => (this.refreshInput = i)} />
</EuiFormRow>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFormRow label="&nbsp;">
<EuiButton size="s" type="submit" style={{ minWidth: 'auto' }}>
Set
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</form>
</div>
);
}
}
AutoRefreshControls.propTypes = {
refreshInterval: PropTypes.number,
setRefresh: PropTypes.func.isRequired,
disableInterval: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,29 @@

import React from 'react';
import PropTypes from 'prop-types';
import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiToolTip } from '@elastic/eui';
import { Popover } from '../../popover';
import { AutoRefreshControls } from './auto_refresh_controls';
import { KioskControls } from './kiosk_controls';

const getRefreshInterval = (val = '') => {
// if it's a number, just use it directly
if (!isNaN(Number(val))) {
return val;
}

// if it's a string, try to parse out the shorthand duration value
const match = String(val).match(/^([0-9]{1,})([hmsd])$/);

// TODO: do something better with improper input, like show an error...
if (!match) {
return;
}

switch (match[2]) {
case 's':
return match[1] * 1000;
case 'm':
return match[1] * 1000 * 60;
case 'h':
return match[1] * 1000 * 60 * 60;
case 'd':
return match[1] * 1000 * 60 * 60 * 24;
}
};

export const ControlSettings = ({ setRefreshInterval, refreshInterval }) => {
const setRefresh = val => setRefreshInterval(getRefreshInterval(val));
export const ControlSettings = ({
setRefreshInterval,
refreshInterval,
autoplayEnabled,
autoplayInterval,
enableAutoplay,
setAutoplayInterval,
}) => {
const setRefresh = val => setRefreshInterval(val);

const disableInterval = () => {
setRefresh(0);
};

const popoverButton = handleClick => (
<EuiButtonIcon iconType="gear" aria-label="Control settings" onClick={handleClick} />
<EuiToolTip position="bottom" content="Control settings">
<EuiButtonIcon iconType="gear" aria-label="Control settings" onClick={handleClick} />
</EuiToolTip>
);

return (
Expand All @@ -54,19 +38,21 @@ export const ControlSettings = ({ setRefreshInterval, refreshInterval }) => {
anchorPosition="rightUp"
panelClassName="canvasControlSettings__popover"
>
{({ closePopover }) => (
{() => (
<EuiFlexGroup>
<EuiFlexItem>
<AutoRefreshControls
refreshInterval={refreshInterval}
setRefresh={val => {
setRefresh(val);
closePopover();
}}
disableInterval={() => {
disableInterval();
closePopover();
}}
setRefresh={val => setRefresh(val)}
disableInterval={() => disableInterval()}
/>
</EuiFlexItem>
<EuiFlexItem>
<KioskControls
autoplayEnabled={autoplayEnabled}
autoplayInterval={autoplayInterval}
onSetInterval={setAutoplayInterval}
onSetEnabled={enableAutoplay}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand All @@ -78,4 +64,8 @@ export const ControlSettings = ({ setRefreshInterval, refreshInterval }) => {
ControlSettings.propTypes = {
refreshInterval: PropTypes.number,
setRefreshInterval: PropTypes.func.isRequired,
autoplayEnabled: PropTypes.bool,
autoplayInterval: PropTypes.number,
enableAutoplay: PropTypes.func.isRequired,
setAutoplayInterval: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.canvasControlSettings__popover {
width: 300px;
width: 600px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiButton, EuiFieldText } from '@elastic/eui';

const getRefreshInterval = (val = '') => {
// if it's a number, there is no interval, return undefined
if (!isNaN(Number(val))) {
return;
}

// if it's a string, try to parse out the shorthand duration value
const match = String(val).match(/^([0-9]{1,})([hmsd])$/);

// if it's invalid, there is no interval, return undefined
if (!match) {
return;
}

switch (match[2]) {
case 's':
return match[1] * 1000;
case 'm':
return match[1] * 1000 * 60;
case 'h':
return match[1] * 1000 * 60 * 60;
case 'd':
return match[1] * 1000 * 60 * 60 * 24;
}
};

export const CustomInterval = ({ gutterSize, buttonSize, onSubmit, defaultValue }) => {
const [customInterval, setCustomInterval] = useState(defaultValue);
const refreshInterval = getRefreshInterval(customInterval);
const isInvalid = Boolean(customInterval.length && !refreshInterval);

const handleChange = ev => setCustomInterval(ev.target.value);

return (
<form
onSubmit={ev => {
ev.preventDefault();
onSubmit(refreshInterval);
}}
>
<EuiFlexGroup gutterSize={gutterSize}>
<EuiFlexItem>
<EuiFormRow
label="Set a custom interval"
helpText="Use shorthand notation, like 30s, 10m, or 1h"
compressed
>
<EuiFieldText isInvalid={isInvalid} value={customInterval} onChange={handleChange} />
</EuiFormRow>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiFormRow label="&nbsp;">
<EuiButton
disabled={isInvalid}
size={buttonSize}
type="submit"
style={{ minWidth: 'auto' }}
>
Set
</EuiButton>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</form>
);
};

CustomInterval.propTypes = {
buttonSize: PropTypes.string,
gutterSize: PropTypes.string,
defaultValue: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
};

CustomInterval.defaultProps = {
buttonSize: 's',
gutterSize: 's',
defaultValue: '',
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,28 @@
*/

import { connect } from 'react-redux';
import { setRefreshInterval } from '../../../state/actions/workpad';
import { getRefreshInterval } from '../../../state/selectors/workpad';
import {
setRefreshInterval,
enableAutoplay,
setAutoplayInterval,
} from '../../../state/actions/workpad';
import { getRefreshInterval, getAutoplay } from '../../../state/selectors/workpad';
import { ControlSettings as Component } from './control_settings';

const mapStateToProps = state => ({
refreshInterval: getRefreshInterval(state),
});
const mapStateToProps = state => {
const { enabled, interval } = getAutoplay(state);

return {
refreshInterval: getRefreshInterval(state),
autoplayEnabled: enabled,
autoplayInterval: interval,
};
};

const mapDispatchToProps = {
setRefreshInterval,
enableAutoplay,
setAutoplayInterval,
};

export const ControlSettings = connect(
Expand Down
Loading

0 comments on commit 917ece2

Please sign in to comment.