2017-06-08 05:25:47 +08:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
2017-04-19 08:54:51 +08:00
|
|
|
import cx from 'classnames';
|
2017-09-22 07:52:43 +08:00
|
|
|
import { HEXToINTColor, INTToHEXColor } from '/imports/utils/hexInt';
|
2017-08-03 09:24:38 +08:00
|
|
|
import styles from './styles.scss';
|
2017-08-25 07:47:20 +08:00
|
|
|
import WhiteboardToolbarItem from './whiteboard-toolbar-item/component';
|
2017-02-23 08:10:30 +08:00
|
|
|
|
2017-09-21 05:05:17 +08:00
|
|
|
const TRANSITION_DURATION = '0.4s';
|
2017-09-22 07:44:32 +08:00
|
|
|
const TOOLBAR_CONFIG = Meteor.settings.public.whiteboard.toolbar;
|
|
|
|
const ANNOTATION_COLORS = TOOLBAR_CONFIG.colors;
|
|
|
|
const THICKNESS_RADIUSES = TOOLBAR_CONFIG.thickness;
|
|
|
|
const FONT_SIZES = TOOLBAR_CONFIG.font_sizes;
|
|
|
|
const ANNOTATION_TOOLS = TOOLBAR_CONFIG.tools;
|
2017-09-21 05:05:17 +08:00
|
|
|
|
2017-04-19 08:54:51 +08:00
|
|
|
export default class WhiteboardToolbar extends Component {
|
2017-08-03 09:24:38 +08:00
|
|
|
|
2017-09-22 07:52:43 +08:00
|
|
|
constructor() {
|
|
|
|
super();
|
2017-04-19 08:54:51 +08:00
|
|
|
|
|
|
|
this.state = {
|
2017-08-03 09:24:38 +08:00
|
|
|
// a variable to control which list is currently open
|
2017-04-19 08:54:51 +08:00
|
|
|
currentSubmenuOpen: '',
|
2017-05-03 08:05:41 +08:00
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// variables to keep current selected draw settings
|
2017-05-03 08:05:41 +08:00
|
|
|
annotationSelected: {
|
|
|
|
icon: 'hand',
|
2017-09-22 07:44:32 +08:00
|
|
|
name: 'hand',
|
2017-05-03 08:05:41 +08:00
|
|
|
},
|
2017-09-06 06:55:18 +08:00
|
|
|
thicknessSelected: 4,
|
2017-04-19 08:54:51 +08:00
|
|
|
colorSelected: '#000000',
|
2017-08-31 07:12:03 +08:00
|
|
|
fontSizeSelected: 20,
|
2017-05-03 08:05:41 +08:00
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// keeping the previous color and the thickness icon's radius selected for svg animation
|
2017-06-03 08:41:39 +08:00
|
|
|
prevColorSelected: '#000000',
|
2017-06-17 10:32:41 +08:00
|
|
|
prevIconRadius: 4,
|
2017-06-03 08:41:39 +08:00
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// lists of tools/thickness/colors are not direct children of main toolbar buttons
|
|
|
|
// and we want the list to close when onBlur fires at the main toolbar button
|
|
|
|
// (click anywhere on the screen) thus we have to control the blur manually by disabling it
|
|
|
|
// when you hover over the buttons in the list and enabling when the mouse leaves the list
|
2017-04-19 08:54:51 +08:00
|
|
|
onBlurEnabled: true,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.displaySubMenu = this.displaySubMenu.bind(this);
|
|
|
|
this.closeSubMenu = this.closeSubMenu.bind(this);
|
|
|
|
this.handleUndo = this.handleUndo.bind(this);
|
2017-04-22 02:01:52 +08:00
|
|
|
this.handleClearAll = this.handleClearAll.bind(this);
|
2017-08-03 09:24:38 +08:00
|
|
|
this.handleSwitchWhiteboardMode = this.handleSwitchWhiteboardMode.bind(this);
|
2017-04-19 08:54:51 +08:00
|
|
|
this.handleAnnotationChange = this.handleAnnotationChange.bind(this);
|
|
|
|
this.handleThicknessChange = this.handleThicknessChange.bind(this);
|
2017-06-03 07:46:02 +08:00
|
|
|
this.handleFontSizeChange = this.handleFontSizeChange.bind(this);
|
2017-04-19 08:54:51 +08:00
|
|
|
this.handleColorChange = this.handleColorChange.bind(this);
|
2017-09-21 05:05:17 +08:00
|
|
|
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
|
|
|
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
2017-08-25 07:47:20 +08:00
|
|
|
this.renderAnnotationList = this.renderAnnotationList.bind(this);
|
|
|
|
this.renderFontSizeList = this.renderFontSizeList.bind(this);
|
|
|
|
this.renderThicknessList = this.renderThicknessList.bind(this);
|
|
|
|
this.renderColorList = this.renderColorList.bind(this);
|
2017-04-19 08:54:51 +08:00
|
|
|
}
|
|
|
|
|
2017-05-03 08:05:41 +08:00
|
|
|
componentWillMount() {
|
2017-09-06 06:55:18 +08:00
|
|
|
const drawSettings = this.props.actions.getCurrentDrawSettings();
|
|
|
|
// if there are saved drawSettings in the session storage
|
|
|
|
// - retrieve them and update toolbar values
|
|
|
|
if (drawSettings) {
|
|
|
|
this.setToolbarValues(drawSettings);
|
|
|
|
// no drawSettings in the sessionStorage - setting default values
|
|
|
|
} else {
|
|
|
|
// setting default drawing settings if they haven't been set previously
|
|
|
|
const { annotationSelected, thicknessSelected, colorSelected, fontSizeSelected } = this.state;
|
|
|
|
this.props.actions.setInitialWhiteboardToolbarValues(
|
2017-09-22 07:44:32 +08:00
|
|
|
annotationSelected.name,
|
|
|
|
thicknessSelected * 2,
|
2017-09-22 07:52:43 +08:00
|
|
|
HEXToINTColor(colorSelected),
|
2017-09-06 06:55:18 +08:00
|
|
|
fontSizeSelected,
|
|
|
|
{
|
|
|
|
textShapeValue: '',
|
|
|
|
textShapeActiveId: '',
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2017-05-03 08:05:41 +08:00
|
|
|
}
|
|
|
|
|
2017-04-19 08:54:51 +08:00
|
|
|
componentDidMount() {
|
2017-08-03 09:24:38 +08:00
|
|
|
// to let the whiteboard know that the presentation area's size has changed
|
2017-04-19 08:54:51 +08:00
|
|
|
window.dispatchEvent(new Event('resize'));
|
2017-06-07 07:47:31 +08:00
|
|
|
|
2017-09-22 07:44:32 +08:00
|
|
|
if (this.state.annotationSelected.name !== 'text') {
|
2017-08-03 09:24:38 +08:00
|
|
|
// trigger initial animation on the thickness circle, otherwise it stays at 0
|
2017-09-06 06:55:18 +08:00
|
|
|
this.thicknessListIconColor.beginElement();
|
2017-08-19 10:47:31 +08:00
|
|
|
this.thicknessListIconRadius.beginElement();
|
2017-09-06 06:55:18 +08:00
|
|
|
this.colorListIconColor.beginElement();
|
|
|
|
} else {
|
|
|
|
this.colorListIconColor.beginElement();
|
2017-06-07 07:47:31 +08:00
|
|
|
}
|
2017-04-19 08:54:51 +08:00
|
|
|
}
|
|
|
|
|
2017-06-03 08:41:39 +08:00
|
|
|
componentDidUpdate(prevProps, prevState) {
|
2017-06-08 05:40:15 +08:00
|
|
|
// if color or thickness were changed
|
|
|
|
// we might need to trigger svg animation for Color and Thickness icons
|
|
|
|
this.animateSvgIcons(prevState);
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
componentWillUnmount() {
|
|
|
|
// to let the whiteboard know that the presentation area's size has changed
|
|
|
|
window.dispatchEvent(new Event('resize'));
|
|
|
|
}
|
|
|
|
|
2017-09-06 06:55:18 +08:00
|
|
|
setToolbarValues(drawSettings) {
|
|
|
|
// divide by 2, since we need the radius for the thickness icon
|
|
|
|
const thicknessSelected = drawSettings.whiteboardAnnotationThickness / 2;
|
|
|
|
const fontSizeSelected = drawSettings.textFontSize;
|
2017-09-22 07:52:43 +08:00
|
|
|
const colorSelected = INTToHEXColor(drawSettings.whiteboardAnnotationColor);
|
2017-09-06 06:55:18 +08:00
|
|
|
|
|
|
|
let annotationSelected = {};
|
|
|
|
for (let i = 0; i < this.props.annotations.length; i += 1) {
|
2017-09-22 07:44:32 +08:00
|
|
|
if (drawSettings.whiteboardAnnotationTool === this.props.annotations[i].name) {
|
2017-09-06 06:55:18 +08:00
|
|
|
annotationSelected = this.props.annotations[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
colorSelected,
|
|
|
|
fontSizeSelected,
|
|
|
|
thicknessSelected,
|
|
|
|
annotationSelected,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-06-08 05:40:15 +08:00
|
|
|
animateSvgIcons(prevState) {
|
2017-06-17 10:32:41 +08:00
|
|
|
/* Animation for the svg icons that we use for thickness (circle) and color (rectangle)
|
|
|
|
* has to be triggered manually
|
|
|
|
* we have 4 main cases:
|
|
|
|
* 1. Color change -
|
|
|
|
a) Text tool is selected, Font-Size icon substitutes the thickness icon,
|
2017-09-06 09:36:15 +08:00
|
|
|
thus we need to trigger the color change just for the color icon
|
2017-06-17 10:32:41 +08:00
|
|
|
b) Any other tool than Text tool is selected - trigger color change for both icons
|
|
|
|
* 2. Thickness change - trigger radius for the thickness icon
|
|
|
|
* 3. Switch from the Text tool to any other - trigger color and radius for thickness
|
|
|
|
* 4. Trigger initial animation for the icons
|
|
|
|
*/
|
|
|
|
|
|
|
|
// 1st case
|
2017-08-03 09:24:38 +08:00
|
|
|
if (this.state.colorSelected !== prevState.colorSelected) {
|
2017-06-17 10:32:41 +08:00
|
|
|
// 1st case b)
|
2017-09-22 07:44:32 +08:00
|
|
|
if (this.state.annotationSelected.name !== 'text') {
|
2017-08-19 10:47:31 +08:00
|
|
|
this.thicknessListIconColor.beginElement();
|
2017-06-08 05:40:15 +08:00
|
|
|
}
|
2017-09-06 09:36:15 +08:00
|
|
|
// 1st case a)
|
|
|
|
this.colorListIconColor.beginElement();
|
2017-06-17 10:32:41 +08:00
|
|
|
// 2nd case
|
2017-09-06 06:55:18 +08:00
|
|
|
} else if (this.state.thicknessSelected !== prevState.thicknessSelected) {
|
2017-08-19 10:47:31 +08:00
|
|
|
this.thicknessListIconRadius.beginElement();
|
2017-08-03 09:24:38 +08:00
|
|
|
// 3rd case
|
2017-09-22 07:44:32 +08:00
|
|
|
} else if (this.state.annotationSelected.name !== 'text' &&
|
|
|
|
prevState.annotationSelected.name === 'text') {
|
2017-08-19 10:47:31 +08:00
|
|
|
this.thicknessListIconRadius.beginElement();
|
|
|
|
this.thicknessListIconColor.beginElement();
|
2017-06-17 10:32:41 +08:00
|
|
|
}
|
|
|
|
|
2017-09-06 06:55:18 +08:00
|
|
|
// 4th case, initial animation is triggered in componentDidMount
|
2017-06-03 08:41:39 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// open a submenu
|
2017-04-19 08:54:51 +08:00
|
|
|
displaySubMenu(listName) {
|
|
|
|
this.setState({
|
2017-08-03 09:24:38 +08:00
|
|
|
currentSubmenuOpen: this.state.currentSubmenuOpen === listName ? '' : listName,
|
2017-04-19 08:54:51 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// close a current submenu (fires onBlur only, when you click anywhere on the screen)
|
2017-04-19 08:54:51 +08:00
|
|
|
closeSubMenu() {
|
2017-06-17 10:32:41 +08:00
|
|
|
// a separate case for the active text shape
|
2017-09-22 07:44:32 +08:00
|
|
|
if (this.state.annotationSelected.name === 'text' && this.props.textShapeActiveId !== '') {
|
2017-06-17 10:32:41 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
if (this.state.onBlurEnabled) {
|
2017-04-19 08:54:51 +08:00
|
|
|
this.setState({
|
|
|
|
currentSubmenuOpen: undefined,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// undo annotation
|
2017-04-19 08:54:51 +08:00
|
|
|
handleUndo() {
|
2017-06-17 10:32:41 +08:00
|
|
|
this.props.actions.undoAnnotation(this.props.whiteboardId);
|
2017-04-19 08:54:51 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// clear all annotations
|
2017-04-22 02:01:52 +08:00
|
|
|
handleClearAll() {
|
2017-06-17 10:32:41 +08:00
|
|
|
this.props.actions.clearWhiteboard(this.props.whiteboardId);
|
2017-04-19 08:54:51 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
handleSwitchWhiteboardMode() {
|
|
|
|
this.props.actions.changeWhiteboardMode(!this.props.multiUser);
|
|
|
|
}
|
2017-05-03 08:05:41 +08:00
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// changes a current selected annotation both in the state and in the session
|
|
|
|
// and closes the annotation list
|
|
|
|
handleAnnotationChange(annotation) {
|
2017-06-17 10:32:41 +08:00
|
|
|
const obj = {
|
2017-04-19 08:54:51 +08:00
|
|
|
annotationSelected: annotation,
|
|
|
|
onBlurEnabled: true,
|
|
|
|
currentSubmenuOpen: '',
|
2017-06-17 10:32:41 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// to animate thickness icon properly when you switch the tool back from Text
|
2017-09-22 07:44:32 +08:00
|
|
|
if (annotation.name === 'text') {
|
2017-06-17 10:32:41 +08:00
|
|
|
obj.prevIconRadius = 0;
|
|
|
|
}
|
|
|
|
|
2017-09-22 07:44:32 +08:00
|
|
|
this.props.actions.setTool(annotation.name);
|
2017-06-17 10:32:41 +08:00
|
|
|
this.setState(obj);
|
2017-04-19 08:54:51 +08:00
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// changes a current selected thickness both in the state and in the session
|
|
|
|
// and closes the thickness list
|
2017-09-06 06:55:18 +08:00
|
|
|
handleThicknessChange(thicknessValue) {
|
|
|
|
// thicknessValue * 2 since this is radius, we need to double it
|
|
|
|
this.props.actions.setThickness(thicknessValue * 2);
|
2017-05-03 08:05:41 +08:00
|
|
|
|
2017-04-19 08:54:51 +08:00
|
|
|
this.setState({
|
2017-09-06 06:55:18 +08:00
|
|
|
prevIconRadius: this.state.thicknessSelected,
|
|
|
|
thicknessSelected: thicknessValue,
|
2017-04-19 08:54:51 +08:00
|
|
|
onBlurEnabled: true,
|
|
|
|
currentSubmenuOpen: '',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-09-06 06:55:18 +08:00
|
|
|
handleFontSizeChange(fontSizeValue) {
|
|
|
|
this.props.actions.setFontSize(fontSizeValue);
|
2017-06-03 07:46:02 +08:00
|
|
|
|
|
|
|
this.setState({
|
2017-09-06 06:55:18 +08:00
|
|
|
fontSizeSelected: fontSizeValue,
|
2017-06-03 07:46:02 +08:00
|
|
|
onBlurEnabled: true,
|
|
|
|
currentSubmenuOpen: '',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// changes a current selected color both in the state and in the session
|
|
|
|
// and closes the color list
|
2017-04-19 08:54:51 +08:00
|
|
|
handleColorChange(color) {
|
2017-09-22 07:52:43 +08:00
|
|
|
this.props.actions.setColor(HEXToINTColor(color));
|
2017-06-17 10:32:41 +08:00
|
|
|
|
2017-04-19 08:54:51 +08:00
|
|
|
this.setState({
|
2017-06-03 08:41:39 +08:00
|
|
|
prevColorSelected: this.state.colorSelected,
|
2017-04-19 08:54:51 +08:00
|
|
|
colorSelected: color,
|
|
|
|
onBlurEnabled: true,
|
|
|
|
currentSubmenuOpen: '',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// disabling onBlur flag when mouse is over the items in the lists
|
2017-09-21 05:05:17 +08:00
|
|
|
handleMouseEnter() {
|
2017-04-19 08:54:51 +08:00
|
|
|
this.setState({
|
|
|
|
onBlurEnabled: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
// enabling the onBlur flag when the mouse leaving the lists
|
2017-09-21 05:05:17 +08:00
|
|
|
handleMouseLeave() {
|
2017-04-19 08:54:51 +08:00
|
|
|
this.setState({
|
|
|
|
onBlurEnabled: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
renderAnnotationList() {
|
2017-08-03 09:24:38 +08:00
|
|
|
const { annotations } = this.props;
|
2017-04-19 08:54:51 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={cx(styles.annotationList, styles.toolbarList)}>
|
2017-08-03 09:24:38 +08:00
|
|
|
{ annotations ? annotations.map(annotation =>
|
2017-08-25 07:47:20 +08:00
|
|
|
(
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Annotation'}
|
|
|
|
icon={annotation.icon}
|
|
|
|
onItemClick={this.handleAnnotationChange}
|
|
|
|
objectToReturn={annotation}
|
2017-09-22 07:44:32 +08:00
|
|
|
className={cx(styles.toolbarListButton, this.state.annotationSelected.name === annotation.name ? styles.selectedListButton : '')}
|
2017-09-21 05:05:17 +08:00
|
|
|
onMouseEnter={this.handleMouseEnter}
|
|
|
|
onMouseLeave={this.handleMouseLeave}
|
2017-09-22 07:44:32 +08:00
|
|
|
key={annotation.name}
|
2017-08-25 07:47:20 +08:00
|
|
|
/>
|
|
|
|
),
|
2017-04-19 08:54:51 +08:00
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-06-03 07:46:02 +08:00
|
|
|
renderFontSizeList() {
|
2017-08-03 09:24:38 +08:00
|
|
|
const { fontSizes } = this.props;
|
2017-06-03 07:46:02 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={cx(styles.fontSizeList, styles.toolbarList)}>
|
2017-09-06 06:55:18 +08:00
|
|
|
{fontSizes ? fontSizes.map(fontSizeValue =>
|
2017-08-25 07:47:20 +08:00
|
|
|
(
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Font Size'}
|
|
|
|
customIcon={
|
2017-09-06 06:55:18 +08:00
|
|
|
<p className={styles.textThickness} style={{ fontSize: fontSizeValue }}>
|
2017-08-25 07:47:20 +08:00
|
|
|
Aa
|
|
|
|
</p>
|
|
|
|
}
|
|
|
|
onItemClick={this.handleFontSizeChange}
|
2017-09-06 06:55:18 +08:00
|
|
|
objectToReturn={fontSizeValue}
|
|
|
|
className={cx(styles.toolbarListButton, styles.fontSizeListButton, this.state.fontSizeSelected === fontSizeValue ? styles.selectedListButton : '')}
|
2017-09-21 05:05:17 +08:00
|
|
|
onMouseEnter={this.handleMouseEnter}
|
|
|
|
onMouseLeave={this.handleMouseLeave}
|
2017-09-06 06:55:18 +08:00
|
|
|
key={fontSizeValue}
|
2017-08-25 07:47:20 +08:00
|
|
|
/>
|
|
|
|
),
|
2017-06-03 07:46:02 +08:00
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-04-19 08:54:51 +08:00
|
|
|
renderThicknessList() {
|
2017-08-03 09:24:38 +08:00
|
|
|
const { thicknessRadiuses } = this.props;
|
2017-04-19 08:54:51 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={cx(styles.thicknessList, styles.toolbarList)}>
|
2017-09-06 06:55:18 +08:00
|
|
|
{thicknessRadiuses ? thicknessRadiuses.map(thicknessRadius =>
|
2017-08-25 07:47:20 +08:00
|
|
|
(
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Radius'}
|
|
|
|
customIcon={
|
|
|
|
<svg className={styles.customSvgIcon}>
|
2017-09-06 06:55:18 +08:00
|
|
|
<circle cx="50%" cy="50%" r={thicknessRadius} fill="#F3F6F9" />
|
2017-08-25 07:47:20 +08:00
|
|
|
</svg>
|
|
|
|
}
|
|
|
|
onItemClick={this.handleThicknessChange}
|
2017-09-06 06:55:18 +08:00
|
|
|
objectToReturn={thicknessRadius}
|
|
|
|
className={cx(styles.toolbarListButton, this.state.thicknessSelected === thicknessRadius ? styles.selectedListButton : '')}
|
2017-09-21 05:05:17 +08:00
|
|
|
onMouseEnter={this.handleMouseEnter}
|
|
|
|
onMouseLeave={this.handleMouseLeave}
|
2017-09-06 06:55:18 +08:00
|
|
|
key={thicknessRadius}
|
2017-08-25 07:47:20 +08:00
|
|
|
/>
|
|
|
|
),
|
2017-04-19 08:54:51 +08:00
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderColorList() {
|
2017-08-03 09:24:38 +08:00
|
|
|
const { colors } = this.props;
|
2017-04-19 08:54:51 +08:00
|
|
|
|
|
|
|
return (
|
|
|
|
<div className={cx(styles.colorList, styles.toolbarList)}>
|
2017-08-03 09:24:38 +08:00
|
|
|
{colors ? colors.map(color =>
|
2017-08-25 07:47:20 +08:00
|
|
|
(
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Color'}
|
|
|
|
customIcon={
|
|
|
|
<svg className={styles.customSvgIcon}>
|
|
|
|
<rect x="20%" y="20%" width="60%" height="60%" fill={color} />
|
|
|
|
</svg>
|
|
|
|
}
|
|
|
|
onItemClick={this.handleColorChange}
|
|
|
|
objectToReturn={color}
|
|
|
|
className={cx(styles.toolbarListButton, this.state.colorSelected === color ? styles.selectedListButton : '')}
|
2017-09-21 05:05:17 +08:00
|
|
|
onMouseEnter={this.handleMouseEnter}
|
|
|
|
onMouseLeave={this.handleMouseLeave}
|
2017-08-25 07:47:20 +08:00
|
|
|
key={color}
|
|
|
|
/>
|
|
|
|
),
|
2017-04-19 08:54:51 +08:00
|
|
|
) : null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
2017-08-03 09:24:38 +08:00
|
|
|
<div className={styles.toolbarContainer} style={{ height: this.props.height }}>
|
2017-04-19 08:54:51 +08:00
|
|
|
<div className={styles.toolbarWrapper}>
|
2017-08-25 07:47:20 +08:00
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Tools'}
|
|
|
|
icon={this.state.annotationSelected.icon}
|
|
|
|
onItemClick={this.displaySubMenu}
|
|
|
|
objectToReturn={'annotationList'}
|
|
|
|
onBlur={this.closeSubMenu}
|
|
|
|
className={cx(styles.toolbarButton, this.state.currentSubmenuOpen === 'annotationList' ? '' : styles.notActive)}
|
|
|
|
renderSubMenu={this.state.currentSubmenuOpen === 'annotationList' ? this.renderAnnotationList : null}
|
|
|
|
/>
|
2017-09-22 07:44:32 +08:00
|
|
|
{this.state.annotationSelected.name === 'text' ?
|
2017-08-25 07:47:20 +08:00
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Font Size List'}
|
|
|
|
customIcon={
|
|
|
|
<p
|
|
|
|
className={styles.textThickness}
|
|
|
|
style={{
|
|
|
|
fontSize: this.state.fontSizeSelected,
|
|
|
|
color: this.state.colorSelected,
|
2017-09-21 05:05:17 +08:00
|
|
|
WebkitTransition: `color ${TRANSITION_DURATION}, font-size ${TRANSITION_DURATION}`, /* Safari */
|
|
|
|
transition: `color ${TRANSITION_DURATION}, font-size ${TRANSITION_DURATION}`,
|
2017-08-25 07:47:20 +08:00
|
|
|
}}
|
|
|
|
>
|
|
|
|
Aa
|
|
|
|
</p>
|
|
|
|
}
|
|
|
|
onItemClick={this.displaySubMenu}
|
|
|
|
objectToReturn={'fontSizeList'}
|
2017-04-19 08:54:51 +08:00
|
|
|
onBlur={this.closeSubMenu}
|
2017-08-25 07:47:20 +08:00
|
|
|
className={cx(styles.toolbarButton, this.state.currentSubmenuOpen === 'fontSizeList' ? '' : styles.notActive)}
|
|
|
|
renderSubMenu={this.state.currentSubmenuOpen === 'fontSizeList' ? this.renderFontSizeList : null}
|
2017-04-19 08:54:51 +08:00
|
|
|
/>
|
2017-06-03 07:46:02 +08:00
|
|
|
:
|
2017-08-25 07:47:20 +08:00
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Thickness List'}
|
|
|
|
onItemClick={this.displaySubMenu}
|
|
|
|
objectToReturn={'thicknessList'}
|
2017-06-03 07:46:02 +08:00
|
|
|
onBlur={this.closeSubMenu}
|
2017-08-03 09:24:38 +08:00
|
|
|
className={cx(styles.toolbarButton, this.state.currentSubmenuOpen === 'thicknessList' ? '' : styles.notActive)}
|
2017-08-25 07:47:20 +08:00
|
|
|
renderSubMenu={this.state.currentSubmenuOpen === 'thicknessList' ? this.renderThicknessList : null}
|
2017-06-03 07:46:02 +08:00
|
|
|
customIcon={
|
|
|
|
<svg className={styles.customSvgIcon} shapeRendering="geometricPrecision">
|
2017-06-03 08:41:39 +08:00
|
|
|
<circle
|
|
|
|
shapeRendering="geometricPrecision"
|
|
|
|
cx="50%"
|
|
|
|
cy="50%"
|
|
|
|
stroke="black"
|
|
|
|
strokeWidth="1"
|
|
|
|
>
|
|
|
|
<animate
|
2017-06-07 07:47:31 +08:00
|
|
|
ref={(ref) => { this.thicknessListIconColor = ref; }}
|
2017-06-03 08:41:39 +08:00
|
|
|
attributeName="fill"
|
|
|
|
attributeType="XML"
|
|
|
|
from={this.state.prevColorSelected}
|
|
|
|
to={this.state.colorSelected}
|
|
|
|
begin={'indefinite'}
|
2017-09-21 05:05:17 +08:00
|
|
|
dur={TRANSITION_DURATION}
|
2017-06-03 08:41:39 +08:00
|
|
|
repeatCount="0"
|
|
|
|
fill="freeze"
|
|
|
|
/>
|
|
|
|
<animate
|
2017-06-07 07:47:31 +08:00
|
|
|
ref={(ref) => { this.thicknessListIconRadius = ref; }}
|
2017-06-03 08:41:39 +08:00
|
|
|
attributeName="r"
|
|
|
|
attributeType="XML"
|
|
|
|
from={this.state.prevIconRadius}
|
2017-09-06 06:55:18 +08:00
|
|
|
to={this.state.thicknessSelected}
|
2017-06-03 08:41:39 +08:00
|
|
|
begin={'indefinite'}
|
2017-09-21 05:05:17 +08:00
|
|
|
dur={TRANSITION_DURATION}
|
2017-06-03 08:41:39 +08:00
|
|
|
repeatCount="0"
|
|
|
|
fill="freeze"
|
|
|
|
/>
|
|
|
|
</circle>
|
2017-06-03 07:46:02 +08:00
|
|
|
</svg>
|
2017-08-25 07:47:20 +08:00
|
|
|
}
|
2017-06-03 07:46:02 +08:00
|
|
|
/>
|
|
|
|
}
|
2017-08-25 07:47:20 +08:00
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Color List'}
|
|
|
|
onItemClick={this.displaySubMenu}
|
|
|
|
objectToReturn={'colorList'}
|
|
|
|
onBlur={this.closeSubMenu}
|
|
|
|
className={cx(styles.toolbarButton, this.state.currentSubmenuOpen === 'colorList' ? '' : styles.notActive)}
|
|
|
|
renderSubMenu={this.state.currentSubmenuOpen === 'colorList' ? this.renderColorList : null}
|
|
|
|
customIcon={
|
|
|
|
<svg className={styles.customSvgIcon}>
|
|
|
|
<rect x="25%" y="25%" width="50%" height="50%" stroke="black" strokeWidth="1">
|
|
|
|
<animate
|
|
|
|
ref={(ref) => { this.colorListIconColor = ref; }}
|
|
|
|
attributeName="fill"
|
|
|
|
attributeType="XML"
|
|
|
|
from={this.state.prevColorSelected}
|
|
|
|
to={this.state.colorSelected}
|
|
|
|
begin={'indefinite'}
|
2017-09-21 05:05:17 +08:00
|
|
|
dur={TRANSITION_DURATION}
|
2017-08-25 07:47:20 +08:00
|
|
|
repeatCount="0"
|
|
|
|
fill="freeze"
|
|
|
|
/>
|
|
|
|
</rect>
|
|
|
|
</svg>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Undo Annotation'}
|
|
|
|
icon={'undo'}
|
|
|
|
onItemClick={this.handleUndo}
|
|
|
|
className={cx(styles.toolbarButton, styles.notActive)}
|
|
|
|
/>
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={'Clear All Annotations'}
|
|
|
|
icon={'circle_close'}
|
|
|
|
onItemClick={this.handleClearAll}
|
|
|
|
className={cx(styles.toolbarButton, styles.notActive)}
|
|
|
|
/>
|
|
|
|
{this.props.isPresenter ?
|
|
|
|
<WhiteboardToolbarItem
|
|
|
|
label={this.props.multiUser ? 'Turn multi-user mode off' : 'Tuen multi-user mode on'}
|
|
|
|
icon={this.props.multiUser ? 'multi_whiteboard' : 'whiteboard'}
|
|
|
|
onItemClick={this.handleSwitchWhiteboardMode}
|
2017-04-19 08:54:51 +08:00
|
|
|
className={cx(styles.toolbarButton, styles.notActive)}
|
|
|
|
/>
|
2017-08-03 09:24:38 +08:00
|
|
|
: null}
|
2017-04-19 08:54:51 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
WhiteboardToolbar.defaultProps = {
|
2017-09-22 07:44:32 +08:00
|
|
|
colors: ANNOTATION_COLORS,
|
|
|
|
thicknessRadiuses: THICKNESS_RADIUSES,
|
|
|
|
fontSizes: FONT_SIZES,
|
|
|
|
annotations: ANNOTATION_TOOLS,
|
2017-04-19 08:54:51 +08:00
|
|
|
};
|
|
|
|
|
2017-08-03 09:24:38 +08:00
|
|
|
WhiteboardToolbar.propTypes = {
|
|
|
|
// defines a current mode of the whiteboard, multi/single user
|
|
|
|
multiUser: PropTypes.bool.isRequired,
|
|
|
|
|
|
|
|
// defines whether a current user is a presenter or not
|
|
|
|
isPresenter: PropTypes.bool.isRequired,
|
|
|
|
|
|
|
|
// defines an object of available actions
|
|
|
|
actions: PropTypes.objectOf(PropTypes.func).isRequired,
|
|
|
|
|
|
|
|
// defines the id of the active text shape (if any)
|
|
|
|
// for the separate onBlur case in the closeSubMenu()
|
|
|
|
textShapeActiveId: PropTypes.string.isRequired,
|
|
|
|
|
|
|
|
// defines a current whiteboard id
|
|
|
|
whiteboardId: PropTypes.string.isRequired,
|
|
|
|
|
|
|
|
// defines an array of icons for the toolbar as well as their corresponding session values
|
|
|
|
annotations: PropTypes.arrayOf(PropTypes.object).isRequired,
|
|
|
|
|
|
|
|
// defines an array of font-sizes for the Font-size submenu of the text shape
|
2017-09-06 06:55:18 +08:00
|
|
|
fontSizes: PropTypes.arrayOf(PropTypes.number).isRequired,
|
2017-08-03 09:24:38 +08:00
|
|
|
|
|
|
|
// defines an array of colors for the toolbar (color submenu)
|
|
|
|
colors: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
|
|
|
|
|
|
// defines an array of thickness values for the toolbar and their corresponding session values
|
2017-09-06 06:55:18 +08:00
|
|
|
thicknessRadiuses: PropTypes.arrayOf(PropTypes.number).isRequired,
|
2017-08-03 09:24:38 +08:00
|
|
|
|
|
|
|
// defines the physical height of the whiteboard
|
|
|
|
height: PropTypes.number.isRequired,
|
|
|
|
};
|