Add zoom tooll and styles

This commit is contained in:
Tainan Felipe 2018-08-22 14:49:33 -03:00
parent 75967a0aac
commit 7c082c608a
5 changed files with 322 additions and 56 deletions

View File

@ -4,6 +4,11 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import Button from '/imports/ui/components/button/component';
import { styles } from './styles.scss';
import ZoomTool from './zoom-tool/component';
const STEP = 5;
const HUNDRED_PERCENT = 100;
const MAX_PERCENT = 400;
const intlMessages = defineMessages({
previousSlideLabel: {
@ -125,7 +130,8 @@ class PresentationToolbar extends Component {
this.state = { sliderValue: 100 };
this.handleValuesChange = this.handleValuesChange.bind(this);
this.handleSkipToSlideChange = this.handleSkipToSlideChange.bind(this);
this.handleZoom = this.handleZoom.bind(this);
this.change = this.change.bind(this);
this.setInt = 0;
}
handleSkipToSlideChange(event) {
@ -134,10 +140,10 @@ class PresentationToolbar extends Component {
}
handleValuesChange(event) {
this.setState({ sliderValue: event.target.value }, () => this.handleZoom(this.state.sliderValue));
}
handleZoom(value) {
this.props.actions.zoomSlideHandler(value);
this.setState(
{ sliderValue: event.target.value },
() => this.handleZoom(this.state.sliderValue),
);
}
fitToWidthClickHandler() {
@ -151,6 +157,11 @@ class PresentationToolbar extends Component {
fitToScreenValue: 'not_implemented_yet',
});
}
change(value) {
this.props.zoomChanger(value);
}
renderSkipSlideOpts(numberOfSlides) {
// Fill drop down menu with all the slides in presentation
const { intl } = this.props;
@ -176,51 +187,67 @@ class PresentationToolbar extends Component {
numberOfSlides,
actions,
intl,
zoom,
} = this.props;
return (
<div id="presentationToolbarWrapper" className={styles.presentationToolbarWrapper}>
{PresentationToolbar.renderAriaLabelsDescs()}
<Button
role="button"
aria-labelledby="prevSlideLabel"
aria-describedby="prevSlideDesc"
disabled={!(currentSlideNum > 1)}
color="default"
icon="left_arrow"
size="md"
onClick={actions.previousSlideHandler}
label={intl.formatMessage(intlMessages.previousSlideLabel)}
hideLabel
className={styles.prevSlide}
/>
<select
// <select> has an implicit role of listbox, no need to define role="listbox" explicitly
id="skipSlide"
aria-labelledby="skipSlideLabel"
aria-describedby="skipSlideDesc"
aria-live="polite"
aria-relevant="all"
value={currentSlideNum}
onChange={actions.skipToSlideHandler}
className={styles.skipSlideSelect}
>
{this.renderSkipSlideOpts(numberOfSlides)}
</select>
<Button
role="button"
aria-labelledby="nextSlideLabel"
aria-describedby="nextSlideDesc"
disabled={!(currentSlideNum < numberOfSlides)}
color="default"
icon="right_arrow"
size="md"
onClick={actions.nextSlideHandler}
label={intl.formatMessage(intlMessages.nextSlideLabel)}
hideLabel
className={styles.skipSlide}
/>
<div className={styles.zoomMinMax}> 100% </div>
{
<span className={styles.presentationControls}>
<Button
role="button"
aria-labelledby="prevSlideLabel"
aria-describedby="prevSlideDesc"
disabled={!(currentSlideNum > 1)}
color="default"
icon="left_arrow"
size="md"
onClick={actions.previousSlideHandler}
label={intl.formatMessage(intlMessages.previousSlideLabel)}
hideLabel
className={styles.prevSlide}
/>
<select
// <select> has an implicit role of listbox, no need to define role="listbox" explicitly
id="skipSlide"
aria-labelledby="skipSlideLabel"
aria-describedby="skipSlideDesc"
aria-live="polite"
aria-relevant="all"
value={currentSlideNum}
onChange={actions.skipToSlideHandler}
className={styles.skipSlideSelect}
>
{this.renderSkipSlideOpts(numberOfSlides)}
</select>
<Button
role="button"
aria-labelledby="nextSlideLabel"
aria-describedby="nextSlideDesc"
disabled={!(currentSlideNum < numberOfSlides)}
color="default"
icon="right_arrow"
size="md"
onClick={actions.nextSlideHandler}
label={intl.formatMessage(intlMessages.nextSlideLabel)}
hideLabel
className={styles.skipSlide}
/>
</span>
}
{
<span className={styles.zoomWrapper}>
<ZoomTool
value={zoom}
change={this.change}
minBound={HUNDRED_PERCENT}
maxBound={MAX_PERCENT}
step={STEP}
/>
</span>
}
{/* <div className={styles.zoomMinMax}> 100% </div>
<input
role="slider"
aria-labelledby="zoomLabel"
@ -231,13 +258,13 @@ class PresentationToolbar extends Component {
value={this.state.sliderValue}
step="5"
type="range"
min="25"
max="100"
min="100"
max="400"
onChange={this.handleValuesChange}
onInput={this.handleValuesChange}
className={styles.zoomSlider}
/>
<div className={styles.zoomMinMax}> 400% </div>
<div className={styles.zoomMinMax}> 400% </div> */}
{/* Fit to width button

View File

@ -1,10 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withTracker } from 'meteor/react-meteor-data';
import PresentationService from '/imports/ui/components/presentation/service';
import PresentationToolbarService from './service';
import PresentationToolbar from './component';
import PresentationService from '/imports/ui/components/presentation/service';
const PresentationToolbarContainer = (props) => {
const {
@ -12,15 +12,21 @@ const PresentationToolbarContainer = (props) => {
userIsPresenter,
numberOfSlides,
actions,
zoom,
zoomChanger,
} = props;
if (userIsPresenter) {
// Only show controls if user is presenter
return (
<PresentationToolbar
currentSlideNum={currentSlideNum}
numberOfSlides={numberOfSlides}
actions={actions}
{...{
currentSlideNum,
numberOfSlides,
actions,
zoom,
zoomChanger,
}}
/>
);
}
@ -38,6 +44,8 @@ export default withTracker((params) => {
return {
userIsPresenter: PresentationService.isPresenter(podId),
numberOfSlides,
zoom: params.zoom,
zoomChanger: params.zoomChanger,
actions: {
nextSlideHandler: () =>
PresentationToolbarService.nextSlide(params.currentSlideNum, numberOfSlides, podId),
@ -54,6 +62,8 @@ export default withTracker((params) => {
PresentationToolbarContainer.propTypes = {
// Number of current slide being displayed
currentSlideNum: PropTypes.number.isRequired,
zoom: PropTypes.number.isRequired,
zoomChanger: PropTypes.func.isRequired,
// Is the user a presenter
userIsPresenter: PropTypes.bool.isRequired,

View File

@ -3,19 +3,28 @@
$controls-color: $color-gray !default;
$controls-background: $color-white !default;
$toolbar-button-border-radius: 5px;
.presentationToolbarWrapper,
.presentationControls,
.zoomWrapper {
order: 2;
display: flex;
flex-direction: row;
align-items: center;
margin-left: .5rem;
margin-right: .5rem;
border-radius: $toolbar-button-border-radius;
}
.presentationControls,
.zoomWrapper {
box-shadow: 0 0 10px -2px rgba(0, 0, 0, .25);
}
.presentationToolbarWrapper {
position: absolute;
bottom: .8rem;
box-shadow: 0 0 10px -2px rgba(0, 0, 0, .25);
align-self: center;
justify-content: center;
z-index: 1;
@ -25,6 +34,7 @@ $controls-background: $color-white !default;
transform-origin: bottom;
}
button,
select,
> div {
@ -34,7 +44,6 @@ $controls-background: $color-white !default;
border-bottom: 0;
border-left: 0;
border-radius: 0;
box-shadow: none;
height: 2.25rem;
box-shadow: none !important;
border: 0;
@ -59,10 +68,12 @@ $controls-background: $color-white !default;
}
.zoomWrapper {
border-radius: 0 5px 5px 0;
// border-radius: 0 5px 5px 0;
justify-content: space-between;
// flex-direction: column;
width: 11.5%;
min-width: 175px;
background-color: $color-white;
}
.zoomWrapperNoBorder {
@ -88,9 +99,16 @@ $controls-background: $color-white !default;
.zoomSlider {
width: 50%;
direction: rtl;
}
.zoomMinMax {
font-weight: normal;
}
.zoomPercentageDisplay {
width: 100%;
height: calc(100% - 100px);
text-align: center;
color: black;
border-left: $border-size solid $color-gray-lighter !important;
border-right: $border-size solid $color-gray-lighter !important;
}

View File

@ -0,0 +1,206 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import { styles } from '../styles.scss';
const DELAY_MILLISECONDS = 500;
const STEP_TIME = 100;
export default class ZoomTool extends Component {
static renderAriaLabelsDescs() {
return (
<div hidden >
{/* Previous Slide button aria */}
<div id="zoomInLabel">
<FormattedMessage
id="app.presentation.presentationToolbar.zoomInLabel"
description="Aria label for when switching to previous slide"
defaultMessage="Previous slide"
/>
</div>
<div id="zoomInDesc">
<FormattedMessage
id="app.presentation.presentationToolbar.zoomInDesc"
description="Aria description for when switching to previous slide"
defaultMessage="Change the presentation to the previous slide"
/>
</div>
{/* Next Slide button aria */}
<div id="zoomOutLabel">
<FormattedMessage
id="app.presentation.presentationToolbar.zoomOutLabel"
description="Aria label for when switching to next slide"
defaultMessage="Next slide"
/>
</div>
<div id="zoomOutDesc">
<FormattedMessage
id="app.presentation.presentationToolbar.zoomOutDesc"
description="Aria description for when switching to next slide"
defaultMessage="Change the presentation to the next slide"
/>
</div>
{/* Skip Slide drop down aria */}
<div id="zoomIndicator">
<FormattedMessage
id="app.presentation.presentationToolbar.zoomIndicator"
description="Aria label for when switching to a specific slide"
defaultMessage="Skip slide"
/>
</div>
</div>
);
}
constructor(props) {
super(props);
this.increment = this.increment.bind(this);
this.decrement = this.decrement.bind(this);
this.mouseDownHandler = this.mouseDownHandler.bind(this);
this.mouseUpHandler = this.mouseUpHandler.bind(this);
this.execInterval = this.execInterval.bind(this);
this.onChanger = this.onChanger.bind(this);
this.setInt = 0;
this.state = {
value: props.value,
mouseHolding: false,
};
}
componentDidUpdate() {
const isDifferent = this.props.value !== this.state.value;
if (isDifferent) this.onChanger(this.props.value);
}
onChanger(value) {
const {
maxBound,
minBound,
change,
} = this.props;
let newValue = value;
const isDifferent = newValue !== this.state.value;
if (newValue <= minBound) {
newValue = minBound;
} else if (newValue >= maxBound) {
newValue = maxBound;
}
const propsIsDifferente = this.props.value !== newValue;
if (isDifferent && propsIsDifferente) {
this.setState({ value: newValue }, () => {
change(newValue);
});
}
if (isDifferent && !propsIsDifferente) this.setState({ value: newValue });
}
increment() {
const {
step,
} = this.props;
const increaseZoom = this.state.value + step;
this.onChanger(increaseZoom);
}
decrement() {
const {
step,
} = this.props;
const decreaseZoom = this.state.value - step;
this.onChanger(decreaseZoom);
}
execInterval(inc) {
const exec = inc ? this.increment : this.decrement;
const interval = () => {
clearInterval(this.setInt);
this.setInt = setInterval(exec, STEP_TIME);
};
setTimeout(() => {
if (this.state.mouseHolding) {
interval();
}
}, DELAY_MILLISECONDS);
}
mouseDownHandler(bool) {
this.setState({
...this.state,
mouseHolding: true,
}, () => {
this.execInterval(bool);
});
}
mouseUpHandler() {
this.setState({
...this.state,
mouseHolding: false,
}, () => clearInterval(this.setInt));
}
render() {
const {
value,
minBound,
maxBound,
} = this.props;
return (
[
ZoomTool.renderAriaLabelsDescs(),
(<Button
key="zoom-tool-1"
aria-labelledby="zoomInLabel"
aria-describedby="zoomInDesc"
role="button"
label="-"
icon="minus"
onClick={() => this.decrement()}
disabled={(value <= minBound)}
onMouseDown={() => this.mouseDownHandler(false)}
onMouseUp={this.mouseUpHandler}
onMouseLeave={this.mouseUpHandler}
className={styles.prevSlide}
hideLabel
/>),
(
<span
key="zoom-tool-2"
aria-labelledby="prevSlideLabel"
aria-describedby={this.state.value}
className={styles.zoomPercentageDisplay}
>
{`${this.state.value}%`}
</span>
),
(<Button
key="zoom-tool-3"
aria-labelledby="zoomOutLabel"
aria-describedby="zoomOutDesc"
role="button"
label="+"
icon="plus"
onClick={() => this.increment()}
disabled={(value >= maxBound)}
onMouseDown={() => this.mouseDownHandler(true)}
onMouseUp={this.mouseUpHandler}
onMouseLeave={this.mouseUpHandler}
className={styles.skipSlide}
hideLabel
/>),
]
);
}
}
const propTypes = {
value: PropTypes.number.isRequired,
change: PropTypes.func.isRequired,
minBound: PropTypes.number.isRequired,
maxBound: PropTypes.number.isRequired,
step: PropTypes.number.isRequired,
};
ZoomTool.propTypes = propTypes;

View File

@ -57,6 +57,11 @@
"app.presentation.presentationToolbar.fitScreenDesc": "Display the whole slide",
"app.presentation.presentationToolbar.zoomLabel": "Zoom",
"app.presentation.presentationToolbar.zoomDesc": "Change the zoom level of the presentation",
"app.presentation.presentationToolbar.zoomInLabel": "Zoom in",
"app.presentation.presentationToolbar.zoomInDesc": "Zoom in the presentation",
"app.presentation.presentationToolbar.zoomOutLabel": "Zoom out",
"app.presentation.presentationToolbar.zoomOutDesc": "Zoom out of the presentation",
"app.presentation.presentationToolbar.zoomIndicator": "Show the zoom percentage",
"app.presentation.presentationToolbar.goToSlide": "Slide {0}",
"app.presentationUploder.title": "Presentation",
"app.presentationUploder.message": "As a presenter in BigBlueButton, you have the ability of uploading any office document or PDF file. We recommend for the best results, to please upload a PDF file.",