mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-10 11:13:48 +02:00
Prevent sidebar re-render on every keypress (#152)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { defaultKeymap, historyField } from '@codemirror/commands';
|
||||
import { foldState, forceParsing } from '@codemirror/language';
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { forceParsing } from '@codemirror/language';
|
||||
import { Compartment, EditorState, type Extension } from '@codemirror/state';
|
||||
import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view';
|
||||
import type { EnvironmentVariable } from '@yaakapp-internal/models';
|
||||
@@ -8,12 +8,12 @@ import classNames from 'classnames';
|
||||
import { EditorView } from 'codemirror';
|
||||
import type { MutableRefObject, ReactNode } from 'react';
|
||||
import {
|
||||
useEffect,
|
||||
Children,
|
||||
cloneElement,
|
||||
forwardRef,
|
||||
isValidElement,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useMemo,
|
||||
useRef,
|
||||
@@ -31,7 +31,7 @@ import { HStack } from '../Stacks';
|
||||
import './Editor.css';
|
||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
||||
import type { GenericCompletionConfig } from './genericCompletion';
|
||||
import { singleLineExt } from './singleLine';
|
||||
import { singleLineExtensions } from './singleLine';
|
||||
|
||||
export interface EditorProps {
|
||||
id?: string;
|
||||
@@ -122,7 +122,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
// Use ref so we can update the handler without re-initializing the editor
|
||||
const handleChange = useRef<EditorProps['onChange']>(onChange);
|
||||
useEffect(() => {
|
||||
handleChange.current = onChange ? onChange : onChange;
|
||||
handleChange.current = onChange;
|
||||
}, [onChange]);
|
||||
|
||||
// Use ref so we can update the handler without re-initializing the editor
|
||||
@@ -304,36 +304,35 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
onClickMissingVariable,
|
||||
onClickPathParameter,
|
||||
});
|
||||
const extensions = [
|
||||
languageCompartment.of(langExt),
|
||||
placeholderCompartment.current.of(
|
||||
placeholderExt(placeholderElFromText(placeholder ?? '')),
|
||||
),
|
||||
wrapLinesCompartment.current.of(wrapLines ? [EditorView.lineWrapping] : []),
|
||||
...getExtensions({
|
||||
container,
|
||||
readOnly,
|
||||
singleLine,
|
||||
hideGutter,
|
||||
stateKey,
|
||||
onChange: handleChange,
|
||||
onPaste: handlePaste,
|
||||
onPasteOverwrite: handlePasteOverwrite,
|
||||
onFocus: handleFocus,
|
||||
onBlur: handleBlur,
|
||||
onKeyDown: handleKeyDown,
|
||||
}),
|
||||
...(extraExtensions ?? []),
|
||||
];
|
||||
|
||||
const cachedJsonState = getCachedEditorState(stateKey);
|
||||
const state = cachedJsonState
|
||||
? EditorState.fromJSON(
|
||||
cachedJsonState,
|
||||
{ extensions },
|
||||
{ fold: foldState, history: historyField },
|
||||
)
|
||||
: EditorState.create({ doc: `${defaultValue ?? ''}`, extensions });
|
||||
const cachedJsonState = getCachedEditorState(defaultValue ?? '', stateKey);
|
||||
|
||||
const state =
|
||||
cachedJsonState ??
|
||||
EditorState.create({
|
||||
doc: `${defaultValue ?? ''}`,
|
||||
extensions: [
|
||||
languageCompartment.of(langExt),
|
||||
placeholderCompartment.current.of(
|
||||
placeholderExt(placeholderElFromText(placeholder ?? '')),
|
||||
),
|
||||
wrapLinesCompartment.current.of(wrapLines ? [EditorView.lineWrapping] : []),
|
||||
...getExtensions({
|
||||
container,
|
||||
readOnly,
|
||||
singleLine,
|
||||
hideGutter,
|
||||
stateKey,
|
||||
onChange: handleChange,
|
||||
onPaste: handlePaste,
|
||||
onPasteOverwrite: handlePasteOverwrite,
|
||||
onFocus: handleFocus,
|
||||
onBlur: handleBlur,
|
||||
onKeyDown: handleKeyDown,
|
||||
}),
|
||||
...(extraExtensions ?? []),
|
||||
],
|
||||
});
|
||||
|
||||
const view = new EditorView({ state, parent: container });
|
||||
|
||||
@@ -515,7 +514,7 @@ function getExtensions({
|
||||
}),
|
||||
tooltips({ parent }),
|
||||
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
|
||||
...(singleLine ? [singleLineExt()] : []),
|
||||
...(singleLine ? [singleLineExtensions()] : []),
|
||||
...(!singleLine ? [multiLineExtensions({ hideGutter })] : []),
|
||||
...(readOnly
|
||||
? [EditorState.readOnly.of(true), EditorView.contentAttributes.of({ tabindex: '-1' })]
|
||||
@@ -525,12 +524,17 @@ function getExtensions({
|
||||
// Things that must be last //
|
||||
// ------------------------ //
|
||||
|
||||
// Fire onChange event
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (onChange && update.docChanged) {
|
||||
onChange.current?.(update.state.doc.toString());
|
||||
saveCachedEditorState(stateKey, update.state);
|
||||
}
|
||||
}),
|
||||
|
||||
// Cache editor state
|
||||
EditorView.updateListener.of((update) => {
|
||||
saveCachedEditorState(stateKey, update.state);
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -540,20 +544,25 @@ const placeholderElFromText = (text: string) => {
|
||||
return el;
|
||||
};
|
||||
|
||||
function saveCachedEditorState(stateKey: string | null, state: EditorState | null) {
|
||||
if (!stateKey || state == null) return;
|
||||
const stateJson = state.toJSON({ history: historyField, folds: foldState });
|
||||
sessionStorage.setItem(stateKey, JSON.stringify(stateJson));
|
||||
}
|
||||
|
||||
function getCachedEditorState(stateKey: string | null) {
|
||||
if (stateKey == null) return;
|
||||
const serializedState = stateKey ? sessionStorage.getItem(stateKey) : null;
|
||||
if (serializedState == null) return;
|
||||
try {
|
||||
return JSON.parse(serializedState);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
return null;
|
||||
declare global {
|
||||
interface Window {
|
||||
editorStates: Record<string, EditorState>;
|
||||
}
|
||||
}
|
||||
window.editorStates = window.editorStates ?? {};
|
||||
|
||||
function saveCachedEditorState(stateKey: string | null, state: EditorState | null) {
|
||||
if (!stateKey || state == null) return;
|
||||
window.editorStates[stateKey] = state;
|
||||
}
|
||||
|
||||
function getCachedEditorState(doc: string, stateKey: string | null) {
|
||||
if (stateKey == null) return;
|
||||
|
||||
const state = window.editorStates[stateKey] ?? null;
|
||||
if (state == null) return null;
|
||||
if (state.doc.toString() !== doc) return null;
|
||||
|
||||
console.log('CACHED STATE', stateKey, state);
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Transaction, TransactionSpec } from '@codemirror/state';
|
||||
import type {Extension, Transaction, TransactionSpec} from '@codemirror/state';
|
||||
import { EditorSelection, EditorState } from '@codemirror/state';
|
||||
|
||||
export function singleLineExt() {
|
||||
export function singleLineExtensions(): Extension {
|
||||
return EditorState.transactionFilter.of(
|
||||
(tr: Transaction): TransactionSpec | TransactionSpec[] => {
|
||||
if (!tr.isUserEvent('input')) return tr;
|
||||
|
||||
Reference in New Issue
Block a user