1
- import { GriffelStyle , Input , InputOnChangeData , InputProps , Label , Link , makeStyles , mergeClasses , Textarea , TextareaProps } from '@fluentui/react-components' ;
2
- import { isFunction , isNotEmptyArray , isNullOrEmptyString , isNullOrNaN , isNullOrUndefined , isNumber } from '@kwiz/common' ;
3
- import React from 'react' ;
1
+ import { GriffelStyle , Input , InputOnChangeData , InputProps , Label , Link , makeStyles , mergeClasses , Textarea , TextareaOnChangeData , TextareaProps } from '@fluentui/react-components' ;
2
+ import { isFunction , isNotEmptyArray , isNullOrEmptyString , isNullOrNaN , isNullOrUndefined , isNumber , pasteTextAtCursor , stopEvent } from '@kwiz/common' ;
3
+ import React , { useCallback , useEffect } from 'react' ;
4
+ import { useEffectOnlyOnMount , useRefWithState } from '../helpers' ;
4
5
import { useKWIZFluentContext } from '../helpers/context-internal' ;
5
6
import { useCommonStyles } from '../styles/styles' ;
6
7
import { Horizontal } from './horizontal' ;
@@ -10,7 +11,9 @@ import { Vertical } from './vertical';
10
11
11
12
12
13
interface IProps extends InputProps {
14
+ /** fire on enter */
13
15
onOK ?: ( ) => void ;
16
+ /** fire on escape */
14
17
onCancel ?: ( ) => void ;
15
18
tokens ?: { title : string ; value : string ; replace ?: boolean ; } [ ] ;
16
19
tokenMenuLabel ?: string ;
@@ -20,8 +23,8 @@ export const InputEx: React.FunctionComponent<React.PropsWithChildren<IProps>> =
20
23
const input = < Input appearance = { ctx . inputAppearance } { ...props }
21
24
onKeyDown = { isFunction ( props . onOK ) || isFunction ( props . onCancel )
22
25
? e => {
23
- if ( isFunction ( props . onOK ) && e . key === "Enter" ) props . onOK ( ) ;
24
- else if ( isFunction ( props . onCancel ) && e . key === "Escape" ) props . onCancel ( ) ;
26
+ if ( e . key === "Enter" ) props . onOK ?. ( ) ;
27
+ if ( e . key === "Escape" ) props . onCancel ?. ( ) ;
25
28
}
26
29
: undefined
27
30
}
@@ -69,27 +72,60 @@ const useStyles = makeStyles({
69
72
70
73
interface IPropsTextArea extends TextareaProps {
71
74
fullSize ?: boolean ;
75
+ /** recalc the height to grow to show all text */
72
76
growNoShrink ?: boolean ;
77
+ allowTab ?: boolean ;
78
+ /** fire on enter */
79
+ onOK ?: ( ) => void ;
80
+ /** fire on escape */
81
+ onCancel ?: ( ) => void ;
82
+ onValueChange ?: ( e : React . ChangeEvent < HTMLTextAreaElement > | React . KeyboardEvent < HTMLTextAreaElement > , d : {
83
+ value : string ;
84
+ elm : HTMLTextAreaElement ;
85
+ } ) => void ;
73
86
}
74
87
export const TextAreaEx : React . FunctionComponent < React . PropsWithChildren < IPropsTextArea > > = ( props ) => {
75
88
const cssNames = useStyles ( ) ;
76
89
let css : string [ ] = [ ] ;
77
90
78
91
if ( props . fullSize ) css . push ( cssNames . fullSizeTextArea ) ;
79
- const textAreaRef = React . useRef < HTMLTextAreaElement > ( null ) ;
92
+ const textAreaRef = useRefWithState < HTMLTextAreaElement > ( null ) ;
80
93
const recalcHeight = React . useCallback ( ( ) => {
81
- if ( textAreaRef . current && props . growNoShrink ) {
82
- if ( textAreaRef . current . scrollHeight > textAreaRef . current . clientHeight )
83
- textAreaRef . current . style . minHeight = textAreaRef . current . scrollHeight + 'px' ;
94
+ if ( textAreaRef . ref . current && props . growNoShrink ) {
95
+ if ( textAreaRef . ref . current . scrollHeight > textAreaRef . ref . current . clientHeight )
96
+ textAreaRef . ref . current . style . minHeight = textAreaRef . ref . current . scrollHeight + 'px' ;
84
97
}
85
- } , [ textAreaRef ] ) ;
98
+ } , useEffectOnlyOnMount ) ;
99
+
100
+ useEffect ( ( ) => { recalcHeight ( ) ; } , [ textAreaRef . value ] ) ;
101
+
102
+ const onChange = useCallback ( ( e : React . ChangeEvent < HTMLTextAreaElement > | React . KeyboardEvent < HTMLTextAreaElement > , d : TextareaOnChangeData ) => {
103
+ props . onValueChange ?.( e , { value : d . value , elm : textAreaRef . ref . current } ) ;
104
+ recalcHeight ( ) ;
105
+ } , [ props . onChange ] ) ;
106
+
107
+ const needOnKeyDown = props . allowTab || isFunction ( props . onOK ) || isFunction ( props . onCancel ) ;
86
108
87
109
let style : React . CSSProperties = { height : '100%' , ...props . style } ;
110
+
88
111
return (
89
- < Textarea ref = { textAreaRef } className = { mergeClasses ( ...css ) } { ...props } style = { style } onChange = { ( e , d ) => {
90
- if ( props . onChange ) props . onChange ( e , d ) ;
91
- recalcHeight ( ) ;
92
- } } />
112
+ < Textarea ref = { textAreaRef . set } className = { mergeClasses ( ...css ) } { ...props } style = { style }
113
+ onKeyDown = { needOnKeyDown ? ( e ) => {
114
+ if ( props . allowTab && e . key === "Tab" ) {
115
+ stopEvent ( e ) ;
116
+ const textArea = e . target as HTMLTextAreaElement ;
117
+ pasteTextAtCursor ( textArea , "\t" ) ;
118
+ onChange ( e , { value : textArea . value } ) ;
119
+ return ;
120
+ }
121
+ if ( e . key === "Enter" ) props . onOK ?.( ) ;
122
+ if ( e . key === "Escape" ) props . onCancel ?.( ) ;
123
+ props . onKeyDown ?.( e ) ;
124
+ } : props . onKeyDown }
125
+ onChange = { ( e , d ) => {
126
+ props . onChange ?.( e , d ) ;
127
+ onChange ( e , d ) ;
128
+ } } />
93
129
) ;
94
130
}
95
131
0 commit comments