-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CodeEditor/UrlDrilldown] Add fitToContent
support, autoresize the url template editor
#175561
Changes from all commits
1b136e4
bd8687b
d7fe921
4cbb49e
8ac02d1
2036a23
eb20083
007a6ee
df4c2ca
40585dc
43892c1
d559d70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,7 +7,6 @@ | |
*/ | ||
|
||
import React, { useState, useRef, useCallback, useMemo, useEffect, KeyboardEvent } from 'react'; | ||
import { useResizeDetector } from 'react-resize-detector'; | ||
import ReactMonacoEditor, { | ||
type MonacoEditorProps as ReactMonacoEditorProps, | ||
} from 'react-monaco-editor'; | ||
|
@@ -140,6 +139,15 @@ export interface CodeEditorProps { | |
* Alternate text to display, when an attempt is made to edit read only content. (Defaults to "Cannot edit in read-only editor") | ||
*/ | ||
readOnlyMessage?: string; | ||
|
||
/** | ||
* Enables the editor to grow vertically to fit its content. | ||
* This option overrides the `height` option. | ||
*/ | ||
fitToContent?: { | ||
minLines?: number; | ||
maxLines?: number; | ||
}; | ||
} | ||
|
||
export const CodeEditor: React.FC<CodeEditorProps> = ({ | ||
|
@@ -168,6 +176,7 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
readOnlyMessage = i18n.translate('sharedUXPackages.codeEditor.readOnlyMessage', { | ||
defaultMessage: 'Cannot edit in read-only editor', | ||
}), | ||
fitToContent, | ||
}) => { | ||
const { colorMode, euiTheme } = useEuiTheme(); | ||
const useDarkTheme = useDarkThemeProp ?? colorMode === 'DARK'; | ||
|
@@ -189,29 +198,18 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
|
||
const isReadOnly = options?.readOnly ?? false; | ||
|
||
const _editor = useRef<monaco.editor.IStandaloneCodeEditor | null>(null); | ||
const [_editor, setEditor] = useState<monaco.editor.IStandaloneCodeEditor | null>(null); | ||
const _placeholderWidget = useRef<PlaceholderWidget | null>(null); | ||
const isSuggestionMenuOpen = useRef(false); | ||
const editorHint = useRef<HTMLDivElement>(null); | ||
const textboxMutationObserver = useRef<MutationObserver | null>(null); | ||
|
||
const [isHintActive, setIsHintActive] = useState(true); | ||
|
||
const _updateDimensions = useCallback(() => { | ||
_editor.current?.layout(); | ||
}, []); | ||
|
||
useResizeDetector({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seemed to be unused. The resize detection is working where it is needed using built in |
||
handleWidth: true, | ||
handleHeight: true, | ||
onResize: _updateDimensions, | ||
refreshMode: 'debounce', | ||
}); | ||
|
||
const startEditing = useCallback(() => { | ||
setIsHintActive(false); | ||
_editor.current?.focus(); | ||
}, []); | ||
_editor?.focus(); | ||
}, [_editor]); | ||
|
||
const stopEditing = useCallback(() => { | ||
setIsHintActive(true); | ||
|
@@ -391,8 +389,6 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
|
||
remeasureFonts(); | ||
|
||
_editor.current = editor; | ||
|
||
const textbox = editor.getDomNode()?.getElementsByTagName('textarea')[0]; | ||
if (textbox) { | ||
// Make sure the textarea is not directly accessible with TAB | ||
|
@@ -435,6 +431,7 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
} | ||
|
||
editorDidMount?.(editor); | ||
setEditor(editor); | ||
}, | ||
[editorDidMount, onBlurMonaco, onKeydownMonaco, readOnlyMessage] | ||
); | ||
|
@@ -454,16 +451,18 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
}, []); | ||
|
||
useEffect(() => { | ||
if (placeholder && !value && _editor.current) { | ||
if (placeholder && !value && _editor) { | ||
// Mounts editor inside constructor | ||
_placeholderWidget.current = new PlaceholderWidget(placeholder, euiTheme, _editor.current); | ||
_placeholderWidget.current = new PlaceholderWidget(placeholder, euiTheme, _editor); | ||
} | ||
|
||
return () => { | ||
_placeholderWidget.current?.dispose(); | ||
_placeholderWidget.current = null; | ||
}; | ||
}, [placeholder, value, euiTheme]); | ||
}, [placeholder, value, euiTheme, _editor]); | ||
|
||
useFitToContent({ editor: _editor, fitToContent, isFullScreen }); | ||
|
||
const { CopyButton } = useCopy({ isCopyable, value }); | ||
|
||
|
@@ -512,7 +511,7 @@ export const CodeEditor: React.FC<CodeEditorProps> = ({ | |
value={value} | ||
onChange={onChange} | ||
width={isFullScreen ? '100vw' : width} | ||
height={isFullScreen ? '100vh' : height} | ||
height={isFullScreen ? '100vh' : fitToContent ? undefined : height} | ||
editorWillMount={_editorWillMount} | ||
editorDidMount={_editorDidMount} | ||
editorWillUnmount={_editorWillUnmount} | ||
|
@@ -640,3 +639,40 @@ const useCopy = ({ isCopyable, value }: { isCopyable: boolean; value: string }) | |
|
||
return { showCopyButton, CopyButton }; | ||
}; | ||
|
||
const useFitToContent = ({ | ||
editor, | ||
fitToContent, | ||
isFullScreen, | ||
}: { | ||
editor: monaco.editor.IStandaloneCodeEditor | null; | ||
isFullScreen: boolean; | ||
fitToContent?: { minLines?: number; maxLines?: number }; | ||
}) => { | ||
const isFitToContent = !!fitToContent; | ||
const minLines = fitToContent?.minLines; | ||
const maxLines = fitToContent?.maxLines; | ||
useEffect(() => { | ||
if (!editor) return; | ||
if (isFullScreen) return; | ||
if (!isFitToContent) return; | ||
|
||
const updateHeight = () => { | ||
const contentHeight = editor.getContentHeight(); | ||
const lineHeight = editor.getOption(monaco.editor.EditorOption.lineHeight); | ||
const minHeight = (minLines ?? 1) * lineHeight; | ||
let maxHeight = maxLines ? maxLines * lineHeight : contentHeight; | ||
maxHeight = Math.max(minHeight, maxHeight); | ||
editor.layout({ | ||
height: Math.min(maxHeight, Math.max(minHeight, contentHeight)), | ||
width: editor.getLayoutInfo().width, | ||
}); | ||
}; | ||
updateHeight(); | ||
const disposable = editor.onDidContentSizeChange(updateHeight); | ||
return () => { | ||
disposable.dispose(); | ||
editor.layout(); // reset the layout that was controlled by the fitToContent | ||
}; | ||
}, [editor, isFitToContent, minLines, maxLines, isFullScreen]); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,7 +106,9 @@ export const MockedMonacoEditor = ({ | |
className?: string; | ||
['data-test-subj']?: string; | ||
}) => { | ||
editorWillMount?.(monaco); | ||
useComponentWillMount(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixing willMount in the mock because now it re-renders twice due to |
||
editorWillMount?.(monaco); | ||
}); | ||
|
||
useEffect(() => { | ||
editorDidMount?.( | ||
|
@@ -133,3 +135,11 @@ export const MockedMonacoEditor = ({ | |
</div> | ||
); | ||
}; | ||
|
||
const useComponentWillMount = (cb: Function) => { | ||
const willMount = React.useRef(true); | ||
|
||
if (willMount.current) cb(); | ||
|
||
willMount.current = false; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
.urlTemplateEditor__container { | ||
.monaco-editor .lines-content.monaco-editor-background { | ||
margin: $euiSizeS; | ||
margin: 0 $euiSizeS; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the vertical margin here broke the height calculation. Moved it to |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
had to move
editor
to setState so that I could properly register the callback that would react to props changes