mimic native nested scroll behaviour (#3325)
This commit is contained in:
@@ -112,7 +112,7 @@ function BlockCodeEditor({
|
||||
<div className="h-full flex-1 overflow-y-hidden">
|
||||
<CodeEditor
|
||||
key="static"
|
||||
className="nopan nowheel h-full overflow-y-scroll"
|
||||
className="nopan h-full overflow-y-scroll"
|
||||
language="python"
|
||||
value={script}
|
||||
lineWrap={false}
|
||||
|
||||
@@ -3,8 +3,11 @@ import { json } from "@codemirror/lang-json";
|
||||
import { python } from "@codemirror/lang-python";
|
||||
import { html } from "@codemirror/lang-html";
|
||||
import { tokyoNightStorm } from "@uiw/codemirror-theme-tokyo-night-storm";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { cn } from "@/util/utils";
|
||||
|
||||
import "./code-mirror-overrides.css";
|
||||
|
||||
function getLanguageExtension(language: "python" | "json" | "html") {
|
||||
switch (language) {
|
||||
case "python":
|
||||
@@ -30,8 +33,8 @@ type Props = {
|
||||
};
|
||||
|
||||
const fullHeightExtension = EditorView.theme({
|
||||
"&": { height: "100%" }, // the root
|
||||
".cm-scroller": { flex: 1 }, // makes the scrollable area expand
|
||||
"&": { height: "100%" },
|
||||
".cm-scroller": { flex: 1 },
|
||||
});
|
||||
|
||||
function CodeEditor({
|
||||
@@ -46,17 +49,58 @@ function CodeEditor({
|
||||
fontSize = 12,
|
||||
fullHeight = false,
|
||||
}: Props) {
|
||||
const viewRef = useRef<EditorView | null>(null);
|
||||
|
||||
const extensions = language
|
||||
? [getLanguageExtension(language), lineWrap ? EditorView.lineWrapping : []]
|
||||
: [lineWrap ? EditorView.lineWrapping : []];
|
||||
|
||||
const style: React.CSSProperties = { fontSize };
|
||||
|
||||
if (fullHeight) {
|
||||
extensions.push(fullHeightExtension);
|
||||
style.height = "100%";
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const view = viewRef.current;
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = view.scrollDOM; // this is the .cm-scroller element
|
||||
|
||||
const onWheel = (e: WheelEvent) => {
|
||||
if (e.ctrlKey || e.metaKey) return;
|
||||
|
||||
const factor =
|
||||
e.deltaMode === 1 ? 16 : e.deltaMode === 2 ? el.clientHeight : 1;
|
||||
const dy = e.deltaY * factor;
|
||||
const dx = e.deltaX * factor;
|
||||
|
||||
const top = el.scrollTop;
|
||||
const left = el.scrollLeft;
|
||||
const maxY = el.scrollHeight - el.clientHeight;
|
||||
const maxX = el.scrollWidth - el.clientWidth;
|
||||
|
||||
const atTop = top <= 0;
|
||||
const atBottom = top >= maxY - 1;
|
||||
const atLeft = left <= 0;
|
||||
const atRight = left >= maxX - 1;
|
||||
|
||||
const verticalWouldScroll = (dy < 0 && !atTop) || (dy > 0 && !atBottom);
|
||||
const horizontalWouldScroll = (dx < 0 && !atLeft) || (dx > 0 && !atRight);
|
||||
|
||||
if (verticalWouldScroll || horizontalWouldScroll) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
el.addEventListener("wheel", onWheel, { passive: true, capture: true });
|
||||
|
||||
return () => el.removeEventListener("wheel", onWheel, { capture: true });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [viewRef.current]);
|
||||
|
||||
return (
|
||||
<CodeMirror
|
||||
value={value}
|
||||
@@ -68,6 +112,12 @@ function CodeEditor({
|
||||
readOnly={readOnly}
|
||||
className={cn("cursor-auto", className)}
|
||||
style={style}
|
||||
onCreateEditor={(view) => {
|
||||
viewRef.current = view;
|
||||
}}
|
||||
onUpdate={(viewUpdate) => {
|
||||
if (!viewRef.current) viewRef.current = viewUpdate.view;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.cm-editor {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
overflow: auto !important;
|
||||
}
|
||||
Reference in New Issue
Block a user