@@ -14,25 +14,39 @@ import { Box, IconButton, type SxProps } from '@mui/material';
1414import SaveIcon from '@mui/icons-material/Save' ;
1515import CloseIcon from '@mui/icons-material/Close' ;
1616
17- import {
18- InPlaceEditorContext ,
19- type InPlaceEditorValue ,
20- type InPlaceEditorAction ,
21- } from './InPlaceEditorContext' ;
2217import { TextInput } from '../TextInput' ;
2318import { TextField } from '../../field' ;
2419
20+ export type InPlaceEditorAction =
21+ | { type : 'edit' }
22+ | { type : 'save' ; values : any }
23+ | { type : 'cancel' }
24+ | { type : 'success' }
25+ | { type : 'error' ; error : any } ;
26+
27+ export type InPlaceEditorValue =
28+ | { state : 'editing' }
29+ | { state : 'saving' ; values : any }
30+ | { state : 'reading' } ;
31+
2532export interface InPlaceEditorProps {
2633 source ?: string ;
2734 mutationMode ?: 'optimistic' | 'pessimistic' | 'undoable' ;
2835 cancelOnBlur ?: boolean ;
2936 notifyOnSuccess ?: boolean ;
37+ resource ?: string ;
3038 showButtons ?: boolean ;
3139 children ?: React . ReactNode ;
3240 editor ?: React . ReactNode ;
3341 sx ?: SxProps ;
3442}
3543
44+ /**
45+ * Renders a value, and on click it turns into an editable field.
46+ *
47+ * The editable field is rendered inside a Form component, so InPlaceEditor
48+ * cannot be used inside another Form component.
49+ */
3650export const InPlaceEditor = ( props : InPlaceEditorProps ) => {
3751 const {
3852 source,
@@ -97,10 +111,10 @@ export const InPlaceEditor = (props: InPlaceEditorProps) => {
97111 ) ;
98112
99113 const record = useRecordContext ( ) ;
100- const resource = useResourceContext ( ) ;
114+ const resource = useResourceContext ( props ) ;
101115 const notify = useNotify ( ) ;
102- const [ update ] = useUpdate ( ) ;
103116 const translate = useTranslate ( ) ;
117+ const [ update ] = useUpdate ( ) ;
104118
105119 const handleSave = async values => {
106120 if ( ! record ) {
@@ -151,6 +165,12 @@ export const InPlaceEditor = (props: InPlaceEditorProps) => {
151165 const handleCancel = ( ) => {
152166 dispatch ( { type : 'cancel' } ) ;
153167 } ;
168+ const handleKeyDown = ( event : React . KeyboardEvent ) => {
169+ if ( event . key === 'Escape' ) {
170+ dispatch ( { type : 'cancel' } ) ;
171+ }
172+ } ;
173+
154174 const handleBlur = ( event : React . FocusEvent ) => {
155175 if ( event . relatedTarget ) {
156176 return ;
@@ -165,9 +185,9 @@ export const InPlaceEditor = (props: InPlaceEditorProps) => {
165185 }
166186 } ;
167187
168- return (
169- < InPlaceEditorContext . Provider value = { { state , dispatch } } >
170- { state . state === 'reading' ? (
188+ switch ( state . state ) {
189+ case 'reading' :
190+ return (
171191 < Box
172192 onClick = { handleEdit }
173193 sx = { {
@@ -179,14 +199,12 @@ export const InPlaceEditor = (props: InPlaceEditorProps) => {
179199 >
180200 { children }
181201 </ Box >
182- ) : state . state === 'editing' ? (
202+ ) ;
203+ case 'editing' :
204+ return (
183205 < Form onSubmit = { handleSave } >
184206 < Box
185- onKeyDown = { event => {
186- if ( event . key === 'Escape' ) {
187- handleCancel ( ) ;
188- }
189- } }
207+ onKeyDown = { handleKeyDown }
190208 onBlur = { handleBlur }
191209 sx = { {
192210 display : 'flex' ,
@@ -222,13 +240,16 @@ export const InPlaceEditor = (props: InPlaceEditorProps) => {
222240 ) }
223241 </ Box >
224242 </ Form >
225- ) : state . state === 'saving' ? (
243+ ) ;
244+ case 'saving' :
245+ // set a custom record context with the new values
246+ // to avoid flickering
247+ return (
226248 < RecordContextProvider value = { state . values } >
227249 < Box sx = { { opacity : 0.5 } } > { children } </ Box >
228250 </ RecordContextProvider >
229- ) : (
230- ''
231- ) }
232- </ InPlaceEditorContext . Provider >
233- ) ;
251+ ) ;
252+ default :
253+ throw new Error ( 'Unhandled state' ) ;
254+ }
234255} ;
0 commit comments