"Fossies" - the Fresh Open Source Software Archive 
Member "jitsi-meet-7329/react/features/polls/components/web/PollCreate.tsx" (9 Jun 2023, 8899 Bytes) of package /linux/misc/jitsi-meet-7329.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TSX (TypeScript with React) source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
1 import React, { useCallback, useEffect, useRef, useState } from 'react';
2 import { makeStyles } from 'tss-react/mui';
3
4 import { withPixelLineHeight } from '../../../base/styles/functions.web';
5 import Button from '../../../base/ui/components/web/Button';
6 import Input from '../../../base/ui/components/web/Input';
7 import { BUTTON_TYPES } from '../../../base/ui/constants.web';
8 import { ANSWERS_LIMIT, CHAR_LIMIT } from '../../constants';
9 import AbstractPollCreate, { AbstractProps } from '../AbstractPollCreate';
10
11 const useStyles = makeStyles()(theme => {
12 return {
13 container: {
14 height: '100%',
15 position: 'relative'
16 },
17 createContainer: {
18 padding: '0 24px',
19 height: 'calc(100% - 88px)',
20 overflowY: 'auto'
21 },
22 header: {
23 ...withPixelLineHeight(theme.typography.heading6),
24 color: theme.palette.text01,
25 margin: '24px 0 16px'
26 },
27 questionContainer: {
28 paddingBottom: '24px',
29 borderBottom: `1px solid ${theme.palette.ui03}`
30 },
31 answerList: {
32 listStyleType: 'none',
33 margin: 0,
34 padding: 0
35 },
36 answer: {
37 marginBottom: '24px'
38 },
39 removeOption: {
40 ...withPixelLineHeight(theme.typography.bodyShortRegular),
41 color: theme.palette.link01,
42 marginTop: '8px',
43 border: 0,
44 background: 'transparent'
45 },
46 addButtonContainer: {
47 display: 'flex'
48 },
49 footer: {
50 position: 'absolute',
51 bottom: 0,
52 display: 'flex',
53 justifyContent: 'flex-end',
54 padding: '24px',
55 width: '100%',
56 boxSizing: 'border-box'
57 },
58 buttonMargin: {
59 marginRight: theme.spacing(3)
60 }
61 };
62 });
63
64 const PollCreate = ({
65 addAnswer,
66 answers,
67 isSubmitDisabled,
68 onSubmit,
69 question,
70 removeAnswer,
71 setAnswer,
72 setCreateMode,
73 setQuestion,
74 t
75 }: AbstractProps) => {
76 const { classes } = useStyles();
77
78 /*
79 * This ref stores the Array of answer input fields, allowing us to focus on them.
80 * This array is maintained by registerfieldRef and the useEffect below.
81 */
82 const answerInputs = useRef<Array<HTMLInputElement>>([]);
83 const registerFieldRef = useCallback((i, r) => {
84 if (r === null) {
85 return;
86 }
87 answerInputs.current[i] = r;
88 }, [ answerInputs ]);
89
90 useEffect(() => {
91 answerInputs.current = answerInputs.current.slice(0, answers.length);
92 }, [ answers ]);
93
94 /*
95 * This state allows us to requestFocus asynchronously, without having to worry
96 * about whether a newly created input field has been rendered yet or not.
97 */
98 const [ lastFocus, requestFocus ] = useState<number | null>(null);
99
100 useEffect(() => {
101 if (lastFocus === null) {
102 return;
103 }
104 const input = answerInputs.current[lastFocus];
105
106 if (input === undefined) {
107 return;
108 }
109 input.focus();
110 }, [ lastFocus ]);
111
112 const checkModifiers = useCallback(ev => {
113 // Composition events used to add accents to characters
114 // despite their absence from standard US keyboards,
115 // to build up logograms of many Asian languages
116 // from their base components or categories and so on.
117 if (ev.isComposing || ev.keyCode === 229) {
118 // keyCode 229 means that user pressed some button,
119 // but input method is still processing that.
120 // This is a standard behavior for some input methods
121 // like entering japanese or сhinese hieroglyphs.
122 return true;
123 }
124
125 // Because this isn't done automatically on MacOS
126 if (ev.key === 'Enter' && ev.metaKey) {
127 ev.preventDefault();
128 onSubmit();
129
130 return;
131 }
132 if (ev.ctrlKey || ev.metaKey || ev.altKey || ev.shiftKey) {
133 return;
134 }
135 }, []);
136
137 const onQuestionKeyDown = useCallback(ev => {
138 if (checkModifiers(ev)) {
139 return;
140 }
141
142 if (ev.key === 'Enter') {
143 requestFocus(0);
144 ev.preventDefault();
145 }
146 }, []);
147
148 // Called on keypress in answer fields
149 const onAnswerKeyDown = useCallback((i, ev) => {
150 if (checkModifiers(ev)) {
151 return;
152 }
153
154 if (ev.key === 'Enter') {
155 // We add a new option input
156 // only if we are on the last option input
157 if (i === answers.length - 1) {
158 addAnswer(i + 1);
159 }
160 requestFocus(i + 1);
161 ev.preventDefault();
162 } else if (ev.key === 'Backspace' && ev.target.value === '' && answers.length > 1) {
163 removeAnswer(i);
164 requestFocus(i > 0 ? i - 1 : 0);
165 ev.preventDefault();
166 } else if (ev.key === 'ArrowDown') {
167 if (i === answers.length - 1) {
168 addAnswer();
169 }
170 requestFocus(i + 1);
171 ev.preventDefault();
172 } else if (ev.key === 'ArrowUp') {
173 if (i === 0) {
174 addAnswer(0);
175 requestFocus(0);
176 } else {
177 requestFocus(i - 1);
178 }
179 ev.preventDefault();
180 }
181 }, [ answers, addAnswer, removeAnswer, requestFocus ]);
182
183 /* eslint-disable react/jsx-no-bind */
184 return (<form
185 className = { classes.container }
186 onSubmit = { onSubmit }>
187 <div className = { classes.createContainer }>
188 <div className = { classes.header }>
189 { t('polls.create.create') }
190 </div>
191 <div className = { classes.questionContainer }>
192 <Input
193 autoFocus = { true }
194 label = { t('polls.create.pollQuestion') }
195 maxLength = { CHAR_LIMIT }
196 onChange = { setQuestion }
197 onKeyPress = { onQuestionKeyDown }
198 placeholder = { t('polls.create.questionPlaceholder') }
199 textarea = { true }
200 value = { question } />
201 </div>
202 <ol className = { classes.answerList }>
203 {answers.map((answer: any, i: number) =>
204 (<li
205 className = { classes.answer }
206 key = { i }>
207 <Input
208 label = { t('polls.create.pollOption', { index: i + 1 }) }
209 maxLength = { CHAR_LIMIT }
210 onChange = { val => setAnswer(i, val) }
211 onKeyPress = { ev => onAnswerKeyDown(i, ev) }
212 placeholder = { t('polls.create.answerPlaceholder', { index: i + 1 }) }
213 ref = { r => registerFieldRef(i, r) }
214 textarea = { true }
215 value = { answer } />
216
217 { answers.length > 2
218 && <button
219 className = { classes.removeOption }
220 onClick = { () => removeAnswer(i) }
221 type = 'button'>
222 { t('polls.create.removeOption') }
223 </button>}
224 </li>)
225 )}
226 </ol>
227 <div className = { classes.addButtonContainer }>
228 <Button
229 accessibilityLabel = { t('polls.create.addOption') }
230 disabled = { answers.length >= ANSWERS_LIMIT }
231 labelKey = { 'polls.create.addOption' }
232 onClick = { () => {
233 addAnswer();
234 requestFocus(answers.length);
235 } }
236 type = { BUTTON_TYPES.SECONDARY } />
237 </div>
238 </div>
239 <div className = { classes.footer }>
240 <Button
241 accessibilityLabel = { t('polls.create.cancel') }
242 className = { classes.buttonMargin }
243 labelKey = { 'polls.create.cancel' }
244 onClick = { () => setCreateMode(false) }
245 type = { BUTTON_TYPES.SECONDARY } />
246 <Button
247 accessibilityLabel = { t('polls.create.send') }
248 disabled = { isSubmitDisabled }
249 isSubmit = { true }
250 labelKey = { 'polls.create.send' } />
251 </div>
252 </form>);
253 };
254
255 /*
256 * We apply AbstractPollCreate to fill in the AbstractProps common
257 * to both the web and native implementations.
258 */
259 // eslint-disable-next-line new-cap
260 export default AbstractPollCreate(PollCreate);