1
1
import classNames from 'classnames' ;
2
- import type { CSSProperties } from 'react' ;
3
- import React , { forwardRef , useState } from 'react' ;
2
+ import type { CSSProperties , ChangeEvent } from 'react' ;
3
+ import React , { forwardRef , useEffect , useState } from 'react' ;
4
4
5
5
import { Button , ImageSizeWarning , ImageWithLoadingPlaceholder } from '#components' ;
6
- import { Crop , Delete } from '#icons' ;
6
+ import { Crop , Delete , Edit } from '#icons' ;
7
7
8
8
import styles from './GalleryTile.module.scss' ;
9
9
@@ -28,7 +28,7 @@ export interface Props {
28
28
29
29
export const GalleryTile = forwardRef < HTMLDivElement , Props > ( function GalleryTile (
30
30
{
31
- caption,
31
+ caption : originalCaption ,
32
32
className,
33
33
clone = false ,
34
34
dragging,
@@ -48,7 +48,18 @@ export const GalleryTile = forwardRef<HTMLDivElement, Props>(function GalleryTil
48
48
} ,
49
49
ref ,
50
50
) {
51
+ // We have to use an intermediate state otherwise the input keeps
52
+ // re-rendering every time the original caption changes and moves
53
+ // the caret to the end of the input
54
+ const [ caption , setCaption ] = useState ( originalCaption ) ;
51
55
const [ isHovering , setHovering ] = useState ( false ) ;
56
+ const [ isEditingCaption , setEditingCaption ] = useState ( false ) ;
57
+
58
+ function handleCaptionChange ( event : ChangeEvent < HTMLInputElement > ) {
59
+ const text = event . currentTarget . value ;
60
+ setCaption ( text ) ;
61
+ onCaptionChange ( text ) ;
62
+ }
52
63
53
64
function handleShowOverlay ( ) {
54
65
setTimeout ( ( ) => setHovering ( true ) , 0 ) ;
@@ -58,6 +69,10 @@ export const GalleryTile = forwardRef<HTMLDivElement, Props>(function GalleryTil
58
69
setHovering ( false ) ;
59
70
}
60
71
72
+ useEffect ( ( ) => {
73
+ setCaption ( caption ) ;
74
+ } , [ caption ] ) ;
75
+
61
76
return (
62
77
< div
63
78
className = { classNames ( styles . GalleryTile , className , {
@@ -88,13 +103,29 @@ export const GalleryTile = forwardRef<HTMLDivElement, Props>(function GalleryTil
88
103
[ styles . visible ] : caption !== '' ,
89
104
} ) }
90
105
>
91
- < input
92
- type = "text"
93
- className = { styles . Input }
94
- onChange = { ( event ) => onCaptionChange ?.( event . currentTarget . value ) }
95
- value = { caption }
96
- placeholder = { isInteractive ? 'add caption' : '' }
97
- />
106
+ { isEditingCaption ? (
107
+ < input
108
+ autoFocus
109
+ type = "text"
110
+ className = { styles . Input }
111
+ onChange = { handleCaptionChange }
112
+ onBlur = { ( ) => setEditingCaption ( false ) }
113
+ value = { caption }
114
+ placeholder = "add caption"
115
+ />
116
+ ) : (
117
+ < Button
118
+ className = { classNames ( styles . Button , {
119
+ [ styles . empty ] : ! caption ,
120
+ } ) }
121
+ icon = { isInteractive ? Edit : undefined }
122
+ iconPosition = "right"
123
+ onClick = { ( ) => setEditingCaption ( true ) }
124
+ variant = "clear"
125
+ >
126
+ { caption || 'add caption' }
127
+ </ Button >
128
+ ) }
98
129
</ div >
99
130
</ div >
100
131
) }
0 commit comments