import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; import PresentationUploaderContainer from '/imports/ui/components/presentation/presentation-uploader/container'; import { withModalMounter } from '/imports/ui/components/modal/service'; import _ from 'lodash'; import { Session } from 'meteor/session'; import cx from 'classnames'; import Button from '/imports/ui/components/button/component'; import Checkbox from '/imports/ui/components/checkbox/component'; import LiveResult from './live-result/component'; import { styles } from './styles.scss'; import DragAndDrop from './dragAndDrop/component'; const intlMessages = defineMessages({ pollPaneTitle: { id: 'app.poll.pollPaneTitle', description: 'heading label for the poll menu', }, closeLabel: { id: 'app.poll.closeLabel', description: 'label for poll pane close button', }, hidePollDesc: { id: 'app.poll.hidePollDesc', description: 'aria label description for hide poll button', }, quickPollInstruction: { id: 'app.poll.quickPollInstruction', description: 'instructions for using pre configured polls', }, activePollInstruction: { id: 'app.poll.activePollInstruction', description: 'instructions displayed when a poll is active', }, dragDropPollInstruction: { id: 'app.poll.dragDropPollInstruction', description: 'instructions for upload poll options via drag and drop', }, ariaInputCount: { id: 'app.poll.ariaInputCount', description: 'aria label for custom poll input field', }, customPlaceholder: { id: 'app.poll.customPlaceholder', description: 'custom poll input field placeholder text', }, noPresentationSelected: { id: 'app.poll.noPresentationSelected', description: 'no presentation label', }, clickHereToSelect: { id: 'app.poll.clickHereToSelect', description: 'open uploader modal button label', }, questionErr: { id: 'app.poll.questionErr', description: 'question text area error label', }, optionErr: { id: 'app.poll.optionErr', description: 'poll input error label', }, tf: { id: 'app.poll.tf', description: 'label for true / false poll', }, a4: { id: 'app.poll.a4', description: 'label for A / B / C / D poll', }, delete: { id: 'app.poll.optionDelete.label', description: '', }, pollPanelDesc: { id: 'app.poll.panel.desc', description: '', }, questionLabel: { id: 'app.poll.question.label', description: '', }, userResponse: { id: 'app.poll.userResponse.label', description: '', }, responseChoices: { id: 'app.poll.responseChoices.label', description: '', }, typedResponseDesc: { id: 'app.poll.typedResponse.desc', description: '', }, responseTypesLabel: { id: 'app.poll.responseTypes.label', description: '', }, addOptionLabel: { id: 'app.poll.addItem.label', description: '', }, startPollLabel: { id: 'app.poll.start.label', description: '', }, questionTitle: { id: 'app.poll.question.title', description: '', }, true: { id: 'app.poll.answer.true', description: '', }, false: { id: 'app.poll.answer.false', description: '', }, a: { id: 'app.poll.answer.a', description: '', }, b: { id: 'app.poll.answer.b', description: '', }, c: { id: 'app.poll.answer.c', description: '', }, d: { id: 'app.poll.answer.d', description: '', }, yna: { id: 'app.poll.yna', description: '', }, yes: { id: 'app.poll.y', description: '', }, no: { id: 'app.poll.n', description: '', }, abstention: { id: 'app.poll.abstention', description: '', }, enableMultipleResponseLabel: { id: 'app.poll.enableMultipleResponseLabel', description: 'label for checkbox to enable multiple choice', }, }); const CHAT_ENABLED = Meteor.settings.public.chat.enabled; const MAX_CUSTOM_FIELDS = Meteor.settings.public.poll.max_custom; const MAX_INPUT_CHARS = 45; const FILE_DRAG_AND_DROP_ENABLED = Meteor.settings.public.poll.allowDragAndDropFile; const validateInput = (i) => { let _input = i; if (/^\s/.test(_input)) _input = ''; return _input; }; class Poll extends Component { constructor(props) { super(props); this.state = { isPolling: false, question: '', optList: [], error: null, isMultipleResponse: false, }; this.handleBackClick = this.handleBackClick.bind(this); this.handleAddOption = this.handleAddOption.bind(this); this.handleRemoveOption = this.handleRemoveOption.bind(this); this.handleTextareaChange = this.handleTextareaChange.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.toggleIsMultipleResponse = this.toggleIsMultipleResponse.bind(this); } componentDidMount() { const { props } = this.hideBtn; const { className } = props; const hideBtn = document.getElementsByClassName(`${className}`); if (hideBtn[0]) hideBtn[0].focus(); } componentDidUpdate() { const { amIPresenter } = this.props; if (Session.equals('resetPollPanel', true)) { this.handleBackClick(); } if (!amIPresenter) { Session.set('openPanel', 'userlist'); Session.set('forcePollOpen', false); } } handleBackClick() { const { stopPoll } = this.props; this.setState({ isPolling: false, error: null, }, () => { stopPoll(); Session.set('resetPollPanel', false); document.activeElement.blur(); }); } handleInputChange(index, event) { this.handleInputTextChange(index, event.target.value); } handleInputTextChange(index, text) { const { optList } = this.state; // This regex will replace any instance of 2 or more consecutive white spaces // with a single white space character. const option = text.replace(/\s{2,}/g, ' ').trim(); if (index < optList.length) optList[index].val = option === '' ? '' : option; this.setState({ optList }); } handleInputChange(e, index) { const { optList, type, error } = this.state; const list = [...optList]; const validatedVal = validateInput(e.target.value).replace(/\s{2,}/g, ' '); const clearError = validatedVal.length > 0 && type !== 'RP'; list[index] = { val: validatedVal }; this.setState({ optList: list, error: clearError ? null : error }); } toggleIsMultipleResponse() { const { isMultipleResponse } = this.state; return this.setState({ isMultipleResponse: !isMultipleResponse }); } handleTextareaChange(e) { const { type, error } = this.state; const validatedQuestion = validateInput(e.target.value); const clearError = validatedQuestion.length > 0 && type === 'RP'; this.setState({ question: validateInput(e.target.value), error: clearError ? null : error }); } pushToCustomPollValues(text) { const lines = text.split('\n'); for (let i = 0; i < MAX_CUSTOM_FIELDS; i += 1) { let line = ''; if (i < lines.length) { line = lines[i]; line = line.length > MAX_INPUT_CHARS ? line.substring(0, MAX_INPUT_CHARS) : line; } this.handleInputTextChange(i, line); } } handlePollValuesText(text) { if (text && text.length > 0) { this.pushToCustomPollValues(text); } } handleRemoveOption(index) { const { optList } = this.state; const list = [...optList]; list.splice(index, 1); this.setState({ optList: list }); } handleAddOption() { const { optList } = this.state; this.setState({ optList: [...optList, { val: '' }] }); } checkPollType() { const { type, optList } = this.state; let _type = type; let pollString = ''; let defaultMatch = null; let isDefault = null; switch (_type) { case 'A-': pollString = optList.map(x => x.val).sort().join(''); defaultMatch = pollString.match(/^(ABCDEFG)|(ABCDEF)|(ABCDE)|(ABCD)|(ABC)|(AB)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; _type = isDefault ? `${_type}${defaultMatch[0].length}` : 'custom'; break; case 'TF': pollString = optList.map(x => x.val).join(''); defaultMatch = pollString.match(/^(TRUEFALSE)|(FALSETRUE)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; if (!isDefault) _type = 'custom'; break; case 'YNA': pollString = optList.map(x => x.val).join(''); defaultMatch = pollString.match(/^(YesNoAbstention)$/gi); isDefault = defaultMatch && pollString.length === defaultMatch[0].length; if (!isDefault) _type = 'custom'; break; default: break; } return _type; } renderInputs() { const { intl } = this.props; const { optList, type, error } = this.state; let hasVal = false; return optList.map((o, i) => { if (o.val.length > 0) hasVal = true; const pollOptionKey = `poll-option-${i}`; return (
this.handleInputChange(e, i)} maxLength={MAX_INPUT_CHARS} /> { i > 1 ? (