editable.ts 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. import { Compartment, EditorState, TransactionSpec } from '@codemirror/state'
  2. import { EditorView } from '@codemirror/view'
  3. const readOnlyConf = new Compartment()
  4. // Make the editor focusable even when contenteditable="false" (read-only mode)
  5. // This allows keyboard shortcuts like Cmd+F to work in read-only mode
  6. const focusableReadOnly = EditorView.contentAttributes.of({ tabindex: '0' })
  7. // Hide the blinking cursor in read-only mode
  8. const hideCursor = EditorView.theme({
  9. '&.cm-editor .cm-cursorLayer': {
  10. display: 'none',
  11. },
  12. })
  13. const readOnlyAttributes = [
  14. EditorState.readOnly.of(true),
  15. EditorView.editable.of(false),
  16. focusableReadOnly,
  17. hideCursor,
  18. ]
  19. const editableAttributes = [
  20. EditorState.readOnly.of(false),
  21. EditorView.editable.of(true),
  22. ]
  23. /**
  24. * A custom extension which determines whether the content is editable, by setting the value of the EditorState.readOnly and EditorView.editable facets.
  25. * Commands and extensions read the EditorState.readOnly facet to decide whether they should be applied.
  26. * EditorView.editable determines whether the DOM can be focused, by changing the value of the contenteditable attribute.
  27. * We add tabindex="0" in read-only mode to ensure the editor remains focusable for keyboard shortcuts.
  28. */
  29. export const editable = () => {
  30. return [readOnlyConf.of(readOnlyAttributes)]
  31. }
  32. export const setEditable = (value = true): TransactionSpec => {
  33. return {
  34. effects: [
  35. readOnlyConf.reconfigure(value ? editableAttributes : readOnlyAttributes),
  36. ],
  37. }
  38. }