aboutsummaryrefslogtreecommitdiffstats
path: root/packages/forms/src/select/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/forms/src/select/index.tsx')
-rw-r--r--packages/forms/src/select/index.tsx146
1 files changed, 81 insertions, 65 deletions
diff --git a/packages/forms/src/select/index.tsx b/packages/forms/src/select/index.tsx
index 0e5ded176..d7479f63e 100644
--- a/packages/forms/src/select/index.tsx
+++ b/packages/forms/src/select/index.tsx
@@ -1,13 +1,16 @@
1import { mdiArrowRightDropCircleOutline, mdiCloseCircle, mdiMagnify } from '@mdi/js'; 1import {
2 mdiArrowRightDropCircleOutline,
3 mdiCloseCircle,
4 mdiMagnify,
5} from '@mdi/js';
2import Icon from '@mdi/react'; 6import Icon from '@mdi/react';
3import { Theme } from '@meetfranz/theme';
4import classnames from 'classnames'; 7import classnames from 'classnames';
5import React, { Component, createRef } from 'react'; 8import React, { Component, createRef } from 'react';
6import injectStyle from 'react-jss'; 9import injectStyle from 'react-jss';
7 10
8import { IFormField, IWithStyle } from '../typings/generic'; 11import { IFormField, IWithStyle } from '../typings/generic';
12import { Theme } from '../../../theme';
9 13
10import { NONAME } from 'dns';
11import { Error } from '../error'; 14import { Error } from '../error';
12import { Label } from '../label'; 15import { Label } from '../label';
13import { Wrapper } from '../wrapper'; 16import { Wrapper } from '../wrapper';
@@ -43,6 +46,14 @@ interface IState {
43 options: IOptions; 46 options: IOptions;
44} 47}
45 48
49let popupTransition: string = 'none';
50let toggleTransition: string = 'none';
51
52if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) {
53 popupTransition = 'all 0.3s';
54 toggleTransition = 'transform 0.3s';
55}
56
46const styles = (theme: Theme) => ({ 57const styles = (theme: Theme) => ({
47 select: { 58 select: {
48 background: theme.selectBackground, 59 background: theme.selectBackground,
@@ -59,7 +70,7 @@ const styles = (theme: Theme) => ({
59 label: { 70 label: {
60 '& > div': { 71 '& > div': {
61 marginTop: 5, 72 marginTop: 5,
62 } 73 },
63 }, 74 },
64 popup: { 75 popup: {
65 opacity: 0, 76 opacity: 0,
@@ -67,7 +78,7 @@ const styles = (theme: Theme) => ({
67 overflowX: 'scroll', 78 overflowX: 'scroll',
68 border: theme.selectBorder, 79 border: theme.selectBorder,
69 borderTop: 0, 80 borderTop: 0,
70 transition: 'all 0.3s', 81 transition: popupTransition,
71 }, 82 },
72 open: { 83 open: {
73 opacity: 1, 84 opacity: 1,
@@ -95,7 +106,7 @@ const styles = (theme: Theme) => ({
95 toggle: { 106 toggle: {
96 marginLeft: 'auto', 107 marginLeft: 'auto',
97 fill: theme.selectToggleColor, 108 fill: theme.selectToggleColor,
98 transition: 'transform 0.3s', 109 transition: toggleTransition,
99 }, 110 },
100 toggleOpened: { 111 toggleOpened: {
101 transform: 'rotateZ(90deg)', 112 transform: 'rotateZ(90deg)',
@@ -154,9 +165,13 @@ class SelectComponent extends Component<IProps> {
154 }; 165 };
155 166
156 private componentRef = createRef<HTMLDivElement>(); 167 private componentRef = createRef<HTMLDivElement>();
168
157 private inputRef = createRef<HTMLInputElement>(); 169 private inputRef = createRef<HTMLInputElement>();
170
158 private searchInputRef = createRef<HTMLInputElement>(); 171 private searchInputRef = createRef<HTMLInputElement>();
172
159 private scrollContainerRef = createRef<HTMLDivElement>(); 173 private scrollContainerRef = createRef<HTMLDivElement>();
174
160 private activeOptionRef = createRef<HTMLDivElement>(); 175 private activeOptionRef = createRef<HTMLDivElement>();
161 176
162 private keyListener: any; 177 private keyListener: any;
@@ -169,10 +184,8 @@ class SelectComponent extends Component<IProps> {
169 } 184 }
170 } 185 }
171 186
172 componentDidUpdate(prevProps: IProps, prevState: IState) { 187 componentDidUpdate() {
173 const { 188 const { open } = this.state;
174 open,
175 } = this.state;
176 189
177 if (this.searchInputRef && this.searchInputRef.current) { 190 if (this.searchInputRef && this.searchInputRef.current) {
178 if (open) { 191 if (open) {
@@ -183,12 +196,12 @@ class SelectComponent extends Component<IProps> {
183 196
184 componentDidMount() { 197 componentDidMount() {
185 if (this.inputRef && this.inputRef.current) { 198 if (this.inputRef && this.inputRef.current) {
186 const { 199 const { data } = this.props;
187 data,
188 } = this.props;
189 200
190 if (data) { 201 if (data) {
191 Object.keys(data).map(key => this.inputRef.current!.dataset[key] = data[key]); 202 Object.keys(data).map(
203 key => (this.inputRef.current!.dataset[key] = data[key]),
204 );
192 } 205 }
193 } 206 }
194 207
@@ -196,12 +209,13 @@ class SelectComponent extends Component<IProps> {
196 } 209 }
197 210
198 componentWillMount() { 211 componentWillMount() {
199 const { 212 const { value } = this.props;
200 value,
201 } = this.props;
202 213
203 if (this.componentRef && this.componentRef.current) { 214 if (this.componentRef && this.componentRef.current) {
204 this.componentRef.current.removeEventListener('keydown', this.keyListener); 215 this.componentRef.current.removeEventListener(
216 'keydown',
217 this.keyListener,
218 );
205 } 219 }
206 220
207 if (value) { 221 if (value) {
@@ -217,13 +231,18 @@ class SelectComponent extends Component<IProps> {
217 window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); 231 window.removeEventListener('keydown', this.arrowKeysHandler.bind(this));
218 } 232 }
219 233
220 setFilter(needle: string = '') { 234 setFilter(needle = '') {
221 const { options } = this.props; 235 const { options } = this.props;
222 236
223 let filteredOptions = {}; 237 let filteredOptions = {};
224 if (needle) { 238 if (needle) {
225 Object.keys(options).map((key) => { 239 Object.keys(options).map(key => {
226 if (key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || options[key].toLocaleLowerCase().startsWith(needle.toLocaleLowerCase())) { 240 if (
241 key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) ||
242 options[key]
243 .toLocaleLowerCase()
244 .startsWith(needle.toLocaleLowerCase())
245 ) {
227 Object.assign(filteredOptions, { 246 Object.assign(filteredOptions, {
228 [`${key}`]: options[key], 247 [`${key}`]: options[key],
229 }); 248 });
@@ -241,7 +260,7 @@ class SelectComponent extends Component<IProps> {
241 } 260 }
242 261
243 select(key: string) { 262 select(key: string) {
244 this.setState((state: IState) => ({ 263 this.setState(() => ({
245 value: key, 264 value: key,
246 open: false, 265 open: false,
247 })); 266 }));
@@ -254,11 +273,7 @@ class SelectComponent extends Component<IProps> {
254 } 273 }
255 274
256 arrowKeysHandler(e: KeyboardEvent) { 275 arrowKeysHandler(e: KeyboardEvent) {
257 const { 276 const { selected, open, options } = this.state;
258 selected,
259 open,
260 options,
261 } = this.state;
262 277
263 if (!open) return; 278 if (!open) return;
264 279
@@ -271,7 +286,10 @@ class SelectComponent extends Component<IProps> {
271 this.setState((state: IState) => ({ 286 this.setState((state: IState) => ({
272 selected: state.selected - 1, 287 selected: state.selected - 1,
273 })); 288 }));
274 } else if (e.keyCode === 40 && selected < Object.keys(options!).length - 1) { 289 } else if (
290 e.keyCode === 40 &&
291 selected < Object.keys(options!).length - 1
292 ) {
275 this.setState((state: IState) => ({ 293 this.setState((state: IState) => ({
276 selected: state.selected + 1, 294 selected: state.selected + 1,
277 })); 295 }));
@@ -279,7 +297,12 @@ class SelectComponent extends Component<IProps> {
279 this.select(Object.keys(options!)[selected]); 297 this.select(Object.keys(options!)[selected]);
280 } 298 }
281 299
282 if (this.activeOptionRef && this.activeOptionRef.current && this.scrollContainerRef && this.scrollContainerRef.current) { 300 if (
301 this.activeOptionRef &&
302 this.activeOptionRef.current &&
303 this.scrollContainerRef &&
304 this.scrollContainerRef.current
305 ) {
283 const containerTopOffset = this.scrollContainerRef.current.offsetTop; 306 const containerTopOffset = this.scrollContainerRef.current.offsetTop;
284 const optionTopOffset = this.activeOptionRef.current.offsetTop; 307 const optionTopOffset = this.activeOptionRef.current.offsetTop;
285 308
@@ -289,10 +312,15 @@ class SelectComponent extends Component<IProps> {
289 } 312 }
290 } 313 }
291 314
292 switch (e.keyCode){ 315 switch (e.keyCode) {
293 case 37: case 39: case 38: case 40: // Arrow keys 316 case 37:
294 case 32: break; // Space 317 case 39:
295 default: break; // do not block other keys 318 case 38:
319 case 40: // Arrow keys
320 case 32:
321 break; // Space
322 default:
323 break; // do not block other keys
296 } 324 }
297 } 325 }
298 326
@@ -314,13 +342,7 @@ class SelectComponent extends Component<IProps> {
314 required, 342 required,
315 } = this.props; 343 } = this.props;
316 344
317 const { 345 const { open, needle, value, selected, options } = this.state;
318 open,
319 needle,
320 value,
321 selected,
322 options,
323 } = this.state;
324 346
325 let selection = ''; 347 let selection = '';
326 if (!value && defaultValue && options![defaultValue]) { 348 if (!value && defaultValue && options![defaultValue]) {
@@ -332,10 +354,7 @@ class SelectComponent extends Component<IProps> {
332 } 354 }
333 355
334 return ( 356 return (
335 <Wrapper 357 <Wrapper className={className} identifier="franz-select">
336 className={className}
337 identifier="franz-select"
338 >
339 <Label 358 <Label
340 title={label} 359 title={label}
341 showLabel={showLabel} 360 showLabel={showLabel}
@@ -352,14 +371,19 @@ class SelectComponent extends Component<IProps> {
352 > 371 >
353 <button 372 <button
354 type="button" 373 type="button"
355 className={classnames({ 374 className={classnames({
356 [`${inputClassName}`]: inputClassName, 375 [`${inputClassName}`]: inputClassName,
357 [`${classes.select}`]: true, 376 [`${classes.select}`]: true,
358 [`${classes.hasError}`]: error, 377 [`${classes.hasError}`]: error,
359 })} 378 })}
360 onClick= {!disabled ? () => this.setState((state: IState) => ({ 379 onClick={
361 open: !state.open, 380 !disabled
362 })) : () => {}} 381 ? () =>
382 this.setState((state: IState) => ({
383 open: !state.open,
384 }))
385 : () => {}
386 }
363 > 387 >
364 {selection} 388 {selection}
365 <Icon 389 <Icon
@@ -373,10 +397,7 @@ class SelectComponent extends Component<IProps> {
373 </button> 397 </button>
374 {showSearch && open && ( 398 {showSearch && open && (
375 <div className={classes.searchContainer}> 399 <div className={classes.searchContainer}>
376 <Icon 400 <Icon path={mdiMagnify} size={0.8} />
377 path={mdiMagnify}
378 size={0.8}
379 />
380 <input 401 <input
381 type="text" 402 type="text"
382 value={needle} 403 value={needle}
@@ -391,10 +412,7 @@ class SelectComponent extends Component<IProps> {
391 className={classes.clearNeedle} 412 className={classes.clearNeedle}
392 onClick={() => this.setFilter()} 413 onClick={() => this.setFilter()}
393 > 414 >
394 <Icon 415 <Icon path={mdiCloseCircle} size={0.7} />
395 path={mdiCloseCircle}
396 size={0.7}
397 />
398 </button> 416 </button>
399 )} 417 )}
400 </div> 418 </div>
@@ -406,7 +424,7 @@ class SelectComponent extends Component<IProps> {
406 })} 424 })}
407 ref={this.scrollContainerRef} 425 ref={this.scrollContainerRef}
408 > 426 >
409 {Object.keys(options!).map(((key, i) => ( 427 {Object.keys(options!).map((key, i) => (
410 <div 428 <div
411 key={key} 429 key={key}
412 onClick={() => this.select(key)} 430 onClick={() => this.select(key)}
@@ -420,7 +438,7 @@ class SelectComponent extends Component<IProps> {
420 > 438 >
421 {options![key]} 439 {options![key]}
422 </div> 440 </div>
423 )))} 441 ))}
424 </div> 442 </div>
425 </div> 443 </div>
426 <input 444 <input
@@ -434,9 +452,7 @@ class SelectComponent extends Component<IProps> {
434 ref={this.inputRef} 452 ref={this.inputRef}
435 /> 453 />
436 </Label> 454 </Label>
437 {error && ( 455 {error && <Error message={error} />}
438 <Error message={error} />
439 )}
440 </Wrapper> 456 </Wrapper>
441 ); 457 );
442 } 458 }