Skip to content

Instantly share code, notes, and snippets.

@bjones1
Last active July 7, 2023 15:51
Show Gist options
  • Save bjones1/6eb4381cb2c9b687caab7c1a8edf7381 to your computer and use it in GitHub Desktop.
Save bjones1/6eb4381cb2c9b687caab7c1a8edf7381 to your computer and use it in GitHub Desktop.
CodeMirror problem with focusin handlers
<!DOCTYPE html>
<html>
<head>
<script src="codemirror-test.js" type="module"></script>
</head>
<body></body>
</html>
// To bundle: node_modules\.bin\esbuild src/codemirror-test.mts --bundle --outdir=./static/bundled --sourcemap --format=esm
import { basicSetup } from "codemirror";
import {
Decoration,
DecorationSet,
EditorView,
ViewPlugin,
ViewUpdate,
WidgetType,
} from "@codemirror/view";
import {
EditorState,
EditorSelection,
StateField,
Transaction,
} from "@codemirror/state";
// Define a simple widget which contains a contenteditable div.
class EditableDivWidget extends WidgetType {
constructor(readonly contents: string) {
super();
}
toDOM() {
let wrap = document.createElement("div");
wrap.className = "editable-div";
wrap.innerHTML = `<div contenteditable style='font-family: auto; white-space: normal; outline-width: 0px;'>${this.contents}</div>`;
return wrap;
}
// We want to handle all events for this widget.
ignoreEvent(event: Event) {
if (event.type === "focusin") {
return false;
} else {
return true;
}
}
}
// Define a state field to contain a DecorationSet of EditableDivWidgets.
const editableDivField = StateField.define<DecorationSet>({
create(state: EditorState) {
return Decoration.none;
},
update(editableDivs: DecorationSet, tr: Transaction) {
return editableDivs.map(tr.changes);
},
provide: (field: StateField<DecorationSet>) =>
EditorView.decorations.from(field),
fromJSON: (json: any, state: EditorState) =>
Decoration.set(
json.map(([from, to, contents]: [number, number, string]) =>
Decoration.replace({
widget: new EditableDivWidget(contents),
block: true,
}).range(from, to)
)
),
});
// Define a simple view plugin to forward events correctly.
const testPlugin = ViewPlugin.fromClass(class {}, {
eventHandlers: {
focusin: (e: Event, view) => {
console.log(e.type);
console.log(e.target);
if ((e.target as HTMLElement).closest(".editable-div") !== null) {
console.log(
"Success! Handling focusin for contenteditable div."
);
return false;
} else {
console.log("Not our contentedtiable div.");
return false;
}
},
},
});
// Follow the standard steps to create the CodeMirror editor. Use fromJSON to create
// a pre-loaded setup with our widget already embedded.
let state = EditorState.fromJSON(
{
doc: "\nChecking on focusin routing...",
selection: EditorSelection.single(0).toJSON(),
editable_div: [[0, 0, "Testing"]],
},
{
extensions: [testPlugin, basicSetup],
},
{
editable_div: editableDivField,
}
);
let view = new EditorView({
state,
parent: document.body,
});
{
"name": "codemirror-focusin",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "run-script-os",
"build:win32": "node_modules\\.bin\\esbuild codemirror-test.mts --bundle --outdir=. --sourcemap --format=esm",
"build:default": "node_modules/.bin/esbuild codemirror-test.mts --bundle --outdir=. --sourcemap --format=esm",
"serve": "npx http-server"
},
"devDependencies": {
"esbuild": "^0.18",
"run-script-os": "^1",
"typescript": "^4"
},
"dependencies": {
"@codemirror/view": "=6.14.0",
"codemirror": "^6"
}
}
// <details>
// <summary>License</summary>
// <p>Copyright (C) 2022 Bryan A. Jones.</p>
// <p>This file is part of the CodeChat Editor.</p>
// <p>The CodeChat Editor is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.</p>
// <p>The CodeChat Editor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.</p>
// <p>You should have received a copy of the GNU General Public License
// along with the CodeChat Editor. If not, see <a
// href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.
// </p>
// </details>
// <h1>.tsconfig.json - TypeScript configuration</h1>
{
"compilerOptions": {
"allowJs": true,
// <p>Required by ESBuild per their <a
// href="https://esbuild.github.io/content-types/#es-module-interop">docs</a>;
// see also <a
// href="https://www.typescriptlang.org/tsconfig#esModuleInterop">the
// TypeScript docs</a>.</p>
"esModuleInterop": true,
// <p>Required by ESBuild per their <a
// href="https://esbuild.github.io/content-types/#isolated-modules">docs</a>;
// see also <a
// href="https://www.typescriptlang.org/tsconfig#isolatedModules">the
// TypeScript docs</a>.</p>
"isolatedModules": true,
"module": "ES2022",
"moduleResolution": "nodenext",
"outDir": "out",
"rootDir": "src",
"strict": true,
"target": "ES2022"
},
"exclude": ["node_modules", "static"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment