@@ -5,6 +5,7 @@ import {Data, State} from 'slate'
5
5
import { Editor } from 'slate-react'
6
6
import FullscreenDialog from 'part:@sanity/components/dialogs/fullscreen?'
7
7
import ScrollContainer from 'part:@sanity/components/utilities/scroll-container'
8
+ import ActivateOnFocus from 'part:@sanity/components/utilities/activate-on-focus'
8
9
import { uniqueId } from 'lodash'
9
10
10
11
import FormField from 'part:@sanity/components/formfields/default'
@@ -36,7 +37,9 @@ export default class BlockEditor extends React.Component {
36
37
37
38
state = {
38
39
fullscreen : false ,
39
- toolbarStyle : { }
40
+ toolbarStyle : { } ,
41
+ preventScroll : false ,
42
+ editorHasFocus : false
40
43
}
41
44
42
45
_inputId = uniqueId ( 'SlateBlockEditor' )
@@ -58,13 +61,30 @@ export default class BlockEditor extends React.Component {
58
61
componentDidMount ( ) {
59
62
window . addEventListener ( 'keydown' , this . handleKeyDown )
60
63
// this._inputContainer.addEventListener('mousewheel', this.handleInputScroll)
64
+ this . checkScrollHeight ( )
61
65
}
62
66
63
67
componentWillUnmount ( ) {
64
68
window . removeEventListener ( 'keydown' , this . handleKeyDown )
65
69
// this._inputContainer.removeEventListener('mousewheel', this.handleInputScroll)
66
70
}
67
71
72
+ checkScrollHeight = ( ) => {
73
+ if ( ! this . _inputContainer || ! this . _editorWrapper ) {
74
+ return
75
+ }
76
+
77
+ const inputHeight = this . _inputContainer . offsetHeight
78
+ const contentHeight = this . _editorWrapper . offsetHeight
79
+
80
+ if ( contentHeight > inputHeight + 100 ) {
81
+ this . setState ( {
82
+ preventScroll : true
83
+ } )
84
+ }
85
+
86
+ }
87
+
68
88
handleNodePatch = event => this . props . onNodePatch ( event )
69
89
70
90
handleInsertBlock = item => {
@@ -260,6 +280,19 @@ export default class BlockEditor extends React.Component {
260
280
this . editor . focus ( )
261
281
}
262
282
283
+ handleEditorFocus = event => {
284
+ this . setState ( {
285
+ editorHasFocus : true
286
+ } )
287
+ this . focus ( )
288
+ }
289
+
290
+ handleEditorBlur = event => {
291
+ this . setState ( {
292
+ editorHasFocus : false
293
+ } )
294
+ }
295
+
263
296
handleInputScroll = event => {
264
297
// Prevents the parent container to scroll when user tries
265
298
// to scroll to the top/bottom of the block editor with momentum scroll or
@@ -294,9 +327,13 @@ export default class BlockEditor extends React.Component {
294
327
this . _inputContainer = element
295
328
}
296
329
330
+ setEditorWrapper = element => {
331
+ this . _editorWrapper = element
332
+ }
333
+
297
334
renderBlockEditor ( ) {
298
335
const { value, onChange} = this . props
299
- const { fullscreen, toolbarStyle} = this . state
336
+ const { fullscreen, toolbarStyle, preventScroll , editorHasFocus } = this . state
300
337
301
338
return (
302
339
< div
@@ -318,31 +355,38 @@ export default class BlockEditor extends React.Component {
318
355
decorators = { this . getActiveDecorators ( ) }
319
356
style = { toolbarStyle }
320
357
/>
321
- < div
322
- className = { styles . inputContainer }
323
- id = { this . _inputId }
324
- onClick = { this . handleEditorContainerClick }
325
- ref = { this . setInputContainerElement }
326
- onWheel = { this . handleInputScroll }
358
+ < ActivateOnFocus
359
+ isActive = { editorHasFocus || fullscreen || ! preventScroll }
360
+ message = "Click to edit"
327
361
>
328
- < div >
329
- < Editor
330
- ref = { this . refEditor }
331
- className = { styles . input }
332
- onChange = { onChange }
333
- placeholder = ""
334
- state = { value }
335
- blockEditor = { this }
336
- plugins = { this . slatePlugins }
337
- schema = { this . slateSchema }
338
- />
339
- < div
340
- ref = { this . refBlockDragMarker }
341
- style = { { display : 'none' } }
342
- className = { styles . blockDragMarker }
343
- />
362
+ < div
363
+ className = { styles . inputContainer }
364
+ id = { this . _inputId }
365
+ onClick = { this . handleEditorContainerClick }
366
+ ref = { this . setInputContainerElement }
367
+ onWheel = { this . handleInputScroll }
368
+ >
369
+ < div ref = { this . setEditorWrapper } >
370
+ < Editor
371
+ ref = { this . refEditor }
372
+ className = { styles . input }
373
+ onChange = { onChange }
374
+ placeholder = ""
375
+ state = { value }
376
+ blockEditor = { this }
377
+ plugins = { this . slatePlugins }
378
+ schema = { this . slateSchema }
379
+ onFocus = { this . handleEditorFocus }
380
+ onBlur = { this . handleEditorBlur }
381
+ />
382
+ < div
383
+ ref = { this . refBlockDragMarker }
384
+ style = { { display : 'none' } }
385
+ className = { styles . blockDragMarker }
386
+ />
387
+ </ div >
344
388
</ div >
345
- </ div >
389
+ </ ActivateOnFocus >
346
390
</ div >
347
391
)
348
392
}
@@ -372,22 +416,23 @@ export default class BlockEditor extends React.Component {
372
416
const { type, level} = this . props
373
417
const { fullscreen} = this . state
374
418
const blockEditor = this . renderBlockEditor ( )
419
+
375
420
return (
376
421
< FormField
377
422
label = { type . title }
378
423
description = { type . description }
379
424
labelFor = { this . _inputId }
380
425
level = { level }
381
426
>
382
- < button tabIndex = { 0 } className = { styles . focusSkipper } onClick = { ( ) => this . focus ( ) } > Jump to editor</ button >
427
+ < button tabIndex = { 0 } className = { styles . focusSkipper } onClick = { ( ) => this . handleEditorFocus ( ) } > Jump to editor</ button >
383
428
{
384
429
fullscreen ? (
385
430
< FullscreenDialog isOpen onClose = { this . handleFullScreenClose } >
386
431
< ScrollContainer className = { styles . portal } onScroll = { this . handleFullScreenScroll } >
387
432
{ blockEditor }
388
433
</ ScrollContainer >
389
434
</ FullscreenDialog >
390
- ) : blockEditor
435
+ ) : blockEditor
391
436
}
392
437
</ FormField >
393
438
)
0 commit comments