-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.tsx
More file actions
127 lines (115 loc) · 3.55 KB
/
index.tsx
File metadata and controls
127 lines (115 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import React, { useCallback, useRef, useEffect } from 'react';
import {
_cs,
isNotDefined,
} from '@togglecorp/fujs';
import RawButton from '../RawButton';
import styles from './styles.css';
export interface ContentBaseProps {
containerClassName?: string;
title?: string;
}
export type OptionKey = string | number;
export interface GenericOptionParams<P extends ContentBaseProps, OK extends OptionKey, O> {
optionContainerClassName?: string;
contentRenderer: (props: Pick<P, Exclude<keyof P, 'containerClassName' | 'title'>>) => React.ReactNode;
contentRendererParam: (key: OK, opt: O) => P;
actionsSelector?: (props: O) => React.ReactNode;
option: O;
optionKey: OK;
onClick: (optionKey: OK, option: O) => void;
focusedKey?: { key: OK, mouse?: boolean } | undefined;
onFocus?: (options: { key: OK, mouse?: boolean }) => void;
}
function GenericOption<P extends ContentBaseProps, OK extends OptionKey, O>({
optionContainerClassName,
contentRenderer,
contentRendererParam,
actionsSelector,
option,
onClick,
onFocus,
optionKey,
focusedKey,
}: GenericOptionParams<P, OK, O>) {
const params = contentRendererParam(optionKey, option);
const {
containerClassName,
title,
...props
} = params;
const isFocused = focusedKey?.key === optionKey; // && focusedKey?.mouse;
const divRef = useRef<HTMLButtonElement>(null);
const focusedByMouse = useRef(false);
useEffect(
() => {
if (focusedKey && focusedKey.key === optionKey && !focusedKey.mouse && divRef.current) {
divRef.current.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
}
},
[optionKey, focusedKey, isFocused],
);
const handleClick = useCallback(
() => {
onClick(optionKey, option);
},
[optionKey, option, onClick],
);
const handleMouseMove = useCallback(
() => {
if (onFocus) {
onFocus({ key: optionKey, mouse: true });
}
},
[
onFocus,
optionKey,
],
);
const handleMouseLeave = useCallback(
() => {
focusedByMouse.current = false;
},
[],
);
if (isNotDefined(actionsSelector)) {
return (
<RawButton
elementRef={divRef}
className={_cs(styles.optionRenderer, containerClassName, optionContainerClassName)}
onClick={handleClick}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
title={title}
name={optionKey}
focused={isFocused}
>
{contentRenderer(props)}
</RawButton>
);
}
return (
<div
className={styles.optionContainer}
>
<RawButton
elementRef={divRef}
// FIXME: Need to use consistent intentional styling for this
className={_cs(styles.optionRenderer, containerClassName, optionContainerClassName)}
onClick={handleClick}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
title={title}
name={optionKey}
focused={isFocused}
>
{contentRenderer(props)}
</RawButton>
{actionsSelector(option)}
</div>
);
}
export default GenericOption;