Add zoom tooll and styles
This commit is contained in:
parent
75967a0aac
commit
7c082c608a
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
@ -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.",
|
||||
|
Loading…
Reference in New Issue
Block a user