refactor: minor clean up of editor (#43513)
This commit is contained in:
committed by
GitHub
parent
bfb70e422e
commit
222fc3f255
@ -35,10 +35,6 @@
|
|||||||
color: var(--secondary-background);
|
color: var(--secondary-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* [widgetid='my.overlay.widget'] {
|
|
||||||
padding: 10px;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.editor-upper-jaw,
|
.editor-upper-jaw,
|
||||||
.editor-lower-jaw {
|
.editor-lower-jaw {
|
||||||
padding: 15px 15px 15px 0px;
|
padding: 15px 15px 15px 0px;
|
||||||
|
@ -74,16 +74,16 @@ interface EditorProps {
|
|||||||
interface EditorProperties {
|
interface EditorProperties {
|
||||||
editor?: editor.IStandaloneCodeEditor;
|
editor?: editor.IStandaloneCodeEditor;
|
||||||
model?: editor.ITextModel;
|
model?: editor.ITextModel;
|
||||||
viewZoneId: string;
|
descriptionZoneId: string;
|
||||||
startEditDecId: string;
|
startEditDecId: string;
|
||||||
endEditDecId: string;
|
endEditDecId: string;
|
||||||
insideEditDecId: string;
|
insideEditDecId: string;
|
||||||
viewZoneHeight: number;
|
descriptionZoneHeight: number;
|
||||||
outputZoneHeight: number;
|
outputZoneHeight: number;
|
||||||
outputZoneId: string;
|
outputZoneId: string;
|
||||||
descriptionNode?: HTMLDivElement;
|
descriptionNode?: HTMLDivElement;
|
||||||
outputNode?: HTMLDivElement;
|
outputNode?: HTMLDivElement;
|
||||||
overlayWidget?: editor.IOverlayWidget;
|
descriptionWidget?: editor.IOverlayWidget;
|
||||||
outputWidget?: editor.IOverlayWidget;
|
outputWidget?: editor.IOverlayWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,11 +174,11 @@ const toLastLine = (range: RangeType) => {
|
|||||||
|
|
||||||
// TODO: properly initialise data with values not null
|
// TODO: properly initialise data with values not null
|
||||||
const initialData: EditorProperties = {
|
const initialData: EditorProperties = {
|
||||||
viewZoneId: '',
|
descriptionZoneId: '',
|
||||||
startEditDecId: '',
|
startEditDecId: '',
|
||||||
endEditDecId: '',
|
endEditDecId: '',
|
||||||
insideEditDecId: '',
|
insideEditDecId: '',
|
||||||
viewZoneHeight: 0,
|
descriptionZoneHeight: 0,
|
||||||
outputZoneId: '',
|
outputZoneId: '',
|
||||||
outputZoneHeight: 0
|
outputZoneHeight: 0
|
||||||
};
|
};
|
||||||
@ -207,8 +207,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
// automatically be first, but if there's jsx and js (for some reason) it
|
// automatically be first, but if there's jsx and js (for some reason) it
|
||||||
// will be [jsx, js].
|
// will be [jsx, js].
|
||||||
|
|
||||||
// NOTE: the ARIA state is controlled by fileKey, so changes to it must
|
|
||||||
// trigger a re-render. Hence state:
|
|
||||||
const options: editor.IStandaloneEditorConstructionOptions = {
|
const options: editor.IStandaloneEditorConstructionOptions = {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
scrollBeyondLastLine: false,
|
scrollBeyondLastLine: false,
|
||||||
@ -400,99 +398,89 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const domNode = createDescription(editor);
|
const descriptionNode = createDescription(editor);
|
||||||
|
|
||||||
const outputNode = createOutputNode(editor);
|
const outputNode = createOutputNode(editor);
|
||||||
|
|
||||||
const overlayWidget = createWidget(
|
const descriptionWidget = createWidget(
|
||||||
'my.overlay.widget',
|
'description.widget',
|
||||||
domNode,
|
descriptionNode,
|
||||||
getViewZoneTop
|
getDescriptionZoneTop
|
||||||
);
|
);
|
||||||
data.overlayWidget = overlayWidget;
|
data.descriptionWidget = descriptionWidget;
|
||||||
const outputWidget = createWidget(
|
const outputWidget = createWidget(
|
||||||
'my.output.widget',
|
'output.widget',
|
||||||
outputNode,
|
outputNode,
|
||||||
getOutputZoneTop
|
getOutputZoneTop
|
||||||
);
|
);
|
||||||
data.outputWidget = outputWidget;
|
data.outputWidget = outputWidget;
|
||||||
|
|
||||||
editor.addOverlayWidget(overlayWidget);
|
editor.addOverlayWidget(descriptionWidget);
|
||||||
|
|
||||||
// TODO: order of insertion into the DOM probably matters, revisit once
|
// TODO: order of insertion into the DOM probably matters, revisit once
|
||||||
// the tabs have been fixed!
|
// the tabs have been fixed!
|
||||||
|
|
||||||
editor.addOverlayWidget(outputWidget);
|
editor.addOverlayWidget(outputWidget);
|
||||||
|
|
||||||
editor.changeViewZones(viewZoneCallback);
|
editor.changeViewZones(descriptionZoneCallback);
|
||||||
editor.changeViewZones(outputZoneCallback);
|
editor.changeViewZones(outputZoneCallback);
|
||||||
|
|
||||||
editor.onDidScrollChange(() => {
|
editor.onDidScrollChange(() => {
|
||||||
editor.layoutOverlayWidget(overlayWidget);
|
editor.layoutOverlayWidget(descriptionWidget);
|
||||||
editor.layoutOverlayWidget(outputWidget);
|
editor.layoutOverlayWidget(outputWidget);
|
||||||
});
|
});
|
||||||
showEditableRegion(editableBoundaries);
|
showEditableRegion(editableBoundaries);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const viewZoneCallback = (changeAccessor: editor.IViewZoneChangeAccessor) => {
|
const descriptionZoneCallback = (
|
||||||
|
changeAccessor: editor.IViewZoneChangeAccessor
|
||||||
|
) => {
|
||||||
const editor = data.editor;
|
const editor = data.editor;
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
// TODO: is there any point creating this here? I know it's cached, but
|
|
||||||
// would it not be better just sourced from the overlayWidget?
|
|
||||||
const domNode = createDescription(editor);
|
const domNode = createDescription(editor);
|
||||||
|
|
||||||
// make sure the overlayWidget has resized before using it to set the height
|
// make sure the overlayWidget has resized before using it to set the height
|
||||||
|
|
||||||
domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
||||||
|
|
||||||
// TODO: set via onComputedHeight?
|
data.descriptionZoneHeight = domNode.offsetHeight;
|
||||||
data.viewZoneHeight = domNode.offsetHeight;
|
|
||||||
|
|
||||||
const background = document.createElement('div');
|
|
||||||
// background.style.background = 'lightgreen';
|
|
||||||
|
|
||||||
// We have to wait for the viewZone to finish rendering before adjusting the
|
// We have to wait for the viewZone to finish rendering before adjusting the
|
||||||
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If
|
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If
|
||||||
// not the editor may report the wrong value for position of the lines.
|
// not the editor may report the wrong value for position of the lines.
|
||||||
const viewZone = {
|
const viewZone = {
|
||||||
afterLineNumber: getLineAfterViewZone() - 1,
|
afterLineNumber: getLineAfterDescriptionZone() - 1,
|
||||||
heightInPx: domNode.offsetHeight,
|
heightInPx: domNode.offsetHeight,
|
||||||
domNode: background,
|
domNode: document.createElement('div'),
|
||||||
onComputedHeight: () =>
|
onComputedHeight: () =>
|
||||||
data.overlayWidget && editor.layoutOverlayWidget(data.overlayWidget)
|
data.descriptionWidget &&
|
||||||
|
editor.layoutOverlayWidget(data.descriptionWidget)
|
||||||
};
|
};
|
||||||
|
|
||||||
data.viewZoneId = changeAccessor.addZone(viewZone);
|
data.descriptionZoneId = changeAccessor.addZone(viewZone);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this is basically the same as viewZoneCallback, so DRY them out.
|
|
||||||
const outputZoneCallback = (
|
const outputZoneCallback = (
|
||||||
changeAccessor: editor.IViewZoneChangeAccessor
|
changeAccessor: editor.IViewZoneChangeAccessor
|
||||||
) => {
|
) => {
|
||||||
const editor = data.editor;
|
const editor = data.editor;
|
||||||
if (!editor) return;
|
if (!editor) return;
|
||||||
// TODO: is there any point creating this here? I know it's cached, but
|
|
||||||
// would it not be better just sourced from the overlayWidget?
|
|
||||||
const outputNode = createOutputNode(editor);
|
const outputNode = createOutputNode(editor);
|
||||||
|
|
||||||
// make sure the overlayWidget has resized before using it to set the height
|
// make sure the overlayWidget has resized before using it to set the height
|
||||||
|
|
||||||
outputNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
outputNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
||||||
|
|
||||||
// TODO: set via onComputedHeight?
|
|
||||||
data.outputZoneHeight = outputNode.offsetHeight;
|
data.outputZoneHeight = outputNode.offsetHeight;
|
||||||
|
|
||||||
const background = document.createElement('div');
|
|
||||||
// background.style.background = 'lightpink';
|
|
||||||
|
|
||||||
// We have to wait for the viewZone to finish rendering before adjusting the
|
// We have to wait for the viewZone to finish rendering before adjusting the
|
||||||
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If
|
// position of the overlayWidget (i.e. trigger it via onComputedHeight). If
|
||||||
// not the editor may report the wrong value for position of the lines.
|
// not the editor may report the wrong value for position of the lines.
|
||||||
const viewZone = {
|
const viewZone = {
|
||||||
afterLineNumber: getLineAfterEditableRegion() - 1,
|
afterLineNumber: getLineAfterEditableRegion() - 1,
|
||||||
heightInPx: outputNode.offsetHeight,
|
heightInPx: outputNode.offsetHeight,
|
||||||
domNode: background,
|
domNode: document.createElement('div'),
|
||||||
onComputedHeight: () =>
|
onComputedHeight: () =>
|
||||||
data.outputWidget && editor.layoutOverlayWidget(data.outputWidget)
|
data.outputWidget && editor.layoutOverlayWidget(data.outputWidget)
|
||||||
};
|
};
|
||||||
@ -505,7 +493,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
const { description, title } = props;
|
const { description, title } = props;
|
||||||
const jawHeading = document.createElement('h3');
|
const jawHeading = document.createElement('h3');
|
||||||
jawHeading.innerText = title;
|
jawHeading.innerText = title;
|
||||||
// TODO: var was used here. Should it?
|
|
||||||
const domNode = document.createElement('div');
|
const domNode = document.createElement('div');
|
||||||
const desc = document.createElement('div');
|
const desc = document.createElement('div');
|
||||||
const descContainer = document.createElement('div');
|
const descContainer = document.createElement('div');
|
||||||
@ -515,8 +502,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
descContainer.appendChild(jawHeading);
|
descContainer.appendChild(jawHeading);
|
||||||
descContainer.appendChild(desc);
|
descContainer.appendChild(desc);
|
||||||
desc.innerHTML = description;
|
desc.innerHTML = description;
|
||||||
// desc.style.background = 'white';
|
|
||||||
// domNode.style.background = 'lightgreen';
|
|
||||||
// TODO: the solution is probably just to use an overlay that's forced to
|
// TODO: the solution is probably just to use an overlay that's forced to
|
||||||
// follow the decorations.
|
// follow the decorations.
|
||||||
// TODO: this is enough for Firefox, but Chrome needs more before the
|
// TODO: this is enough for Firefox, but Chrome needs more before the
|
||||||
@ -526,12 +511,10 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
domNode.style.zIndex = '10';
|
domNode.style.zIndex = '10';
|
||||||
|
|
||||||
domNode.setAttribute('aria-hidden', 'true');
|
domNode.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
// domNode.style.background = 'lightYellow';
|
|
||||||
domNode.style.left = `${editor.getLayoutInfo().contentLeft}px`;
|
domNode.style.left = `${editor.getLayoutInfo().contentLeft}px`;
|
||||||
domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
domNode.style.width = `${editor.getLayoutInfo().contentWidth}px`;
|
||||||
|
|
||||||
domNode.style.top = getViewZoneTop();
|
domNode.style.top = getDescriptionZoneTop();
|
||||||
data.descriptionNode = domNode;
|
data.descriptionNode = domNode;
|
||||||
return domNode;
|
return domNode;
|
||||||
}
|
}
|
||||||
@ -672,14 +655,14 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: this is where the view zone *should* be, not necessarily were it
|
// NOTE: this is where the view zone *should* be, not necessarily were it
|
||||||
// currently is. (see getLineAfterViewZone)
|
// currently is. (see getLineAfterDescriptionZone)
|
||||||
// TODO: DRY this and getOutputZoneTop out.
|
// TODO: DRY this and getOutputZoneTop out.
|
||||||
function getViewZoneTop() {
|
function getDescriptionZoneTop() {
|
||||||
const editor = data.editor;
|
const editor = data.editor;
|
||||||
const heightDelta = data.viewZoneHeight;
|
const heightDelta = data.descriptionZoneHeight;
|
||||||
if (editor) {
|
if (editor) {
|
||||||
const top = `${
|
const top = `${
|
||||||
editor.getTopForLineNumber(getLineAfterViewZone()) -
|
editor.getTopForLineNumber(getLineAfterDescriptionZone()) -
|
||||||
heightDelta -
|
heightDelta -
|
||||||
editor.getScrollTop()
|
editor.getScrollTop()
|
||||||
}px`;
|
}px`;
|
||||||
@ -705,9 +688,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
|
|
||||||
// It's not possible to directly access the current view zone so we track
|
// It's not possible to directly access the current view zone so we track
|
||||||
// the region it should cover instead.
|
// the region it should cover instead.
|
||||||
// TODO: DRY
|
function getLineAfterDescriptionZone() {
|
||||||
function getLineAfterViewZone() {
|
|
||||||
// TODO: abstract away the data, ids etc.
|
|
||||||
const range = data.model?.getDecorationRange(data.startEditDecId);
|
const range = data.model?.getDecorationRange(data.startEditDecId);
|
||||||
// if the first decoration is missing, this implies the region reaches the
|
// if the first decoration is missing, this implies the region reaches the
|
||||||
// start of the editor.
|
// start of the editor.
|
||||||
@ -731,7 +712,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
return monacoRef.current?.Range.lift(iRange);
|
return monacoRef.current?.Range.lift(iRange);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: TESTS!
|
|
||||||
// Make 100% sure this is inclusive.
|
// Make 100% sure this is inclusive.
|
||||||
const getLinesBetweenRanges = (
|
const getLinesBetweenRanges = (
|
||||||
firstRange: RangeType,
|
firstRange: RangeType,
|
||||||
@ -785,7 +765,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: do this once after _monaco has been created.
|
|
||||||
const getStartOfEditor = () =>
|
const getStartOfEditor = () =>
|
||||||
monacoRef.current?.Range.lift({
|
monacoRef.current?.Range.lift({
|
||||||
startLineNumber: 1,
|
startLineNumber: 1,
|
||||||
@ -869,7 +848,6 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
return newLines.map(({ range }) => range);
|
return newLines.map(({ range }) => range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO refactor this mess
|
|
||||||
// TODO this listener needs to be replaced on reset.
|
// TODO this listener needs to be replaced on reset.
|
||||||
model.onDidChangeContent(e => {
|
model.onDidChangeContent(e => {
|
||||||
// TODO: it would be nice if undoing could remove the warning, but
|
// TODO: it would be nice if undoing could remove the warning, but
|
||||||
@ -893,7 +871,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
if (e.isUndoing) {
|
if (e.isUndoing) {
|
||||||
// TODO: can we be more targeted? Only update when they could get out of
|
// TODO: can we be more targeted? Only update when they could get out of
|
||||||
// sync
|
// sync
|
||||||
updateViewZone();
|
updateDescriptionZone();
|
||||||
updateOutputZone();
|
updateOutputZone();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -922,7 +900,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
// have changed if a line has been added or removed
|
// have changed if a line has been added or removed
|
||||||
const handleDescriptionZoneChange = () => {
|
const handleDescriptionZoneChange = () => {
|
||||||
if (newLineRanges.length > 0 || deletedLine > 0) {
|
if (newLineRanges.length > 0 || deletedLine > 0) {
|
||||||
updateViewZone();
|
updateDescriptionZone();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1117,7 +1095,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
const editor = data.editor;
|
const editor = data.editor;
|
||||||
editor?.layout();
|
editor?.layout();
|
||||||
if (data.startEditDecId) {
|
if (data.startEditDecId) {
|
||||||
updateViewZone();
|
updateDescriptionZone();
|
||||||
updateOutputZone();
|
updateOutputZone();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
@ -1132,11 +1110,11 @@ const Editor = (props: EditorProps): JSX.Element => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateViewZone() {
|
function updateDescriptionZone() {
|
||||||
const editor = data.editor;
|
const editor = data.editor;
|
||||||
editor?.changeViewZones(changeAccessor => {
|
editor?.changeViewZones(changeAccessor => {
|
||||||
changeAccessor.removeZone(data.viewZoneId);
|
changeAccessor.removeZone(data.descriptionZoneId);
|
||||||
viewZoneCallback(changeAccessor);
|
descriptionZoneCallback(changeAccessor);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user