mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-24 10:21:15 +01:00
A bunch more small things
This commit is contained in:
@@ -89,7 +89,12 @@
|
||||
|
||||
.cm-editor .cm-activeLineGutter,
|
||||
.cm-editor .cm-activeLine {
|
||||
background-color: hsl(var(--color-gray-50));
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.cm-editor.cm-focused .cm-activeLineGutter,
|
||||
.cm-editor.cm-focused .cm-activeLine {
|
||||
background-color: hsl(var(--color-gray-100)/0.3);
|
||||
}
|
||||
|
||||
.cm-editor * {
|
||||
@@ -101,9 +106,9 @@
|
||||
}
|
||||
|
||||
.cm-editor .cm-selectionBackground {
|
||||
background-color: hsl(var(--color-gray-100));
|
||||
background-color: hsl(var(--color-gray-200));
|
||||
}
|
||||
|
||||
.cm-editor.cm-focused .cm-selectionBackground {
|
||||
background-color: hsl(var(--color-gray-100));
|
||||
background-color: hsl(var(--color-gray-200));
|
||||
}
|
||||
|
||||
@@ -1,14 +1,42 @@
|
||||
import useCodeMirror from '../../hooks/useCodemirror';
|
||||
import './Editor.css';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { EditorView } from 'codemirror';
|
||||
import { baseExtensions, syntaxExtension } from './extensions';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
|
||||
interface Props {
|
||||
contentType: string;
|
||||
initialValue?: string;
|
||||
value?: string;
|
||||
defaultValue?: string | null;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
export default function Editor(props: Props) {
|
||||
const { ref } = useCodeMirror(props);
|
||||
export default function Editor({ contentType, defaultValue, onChange }: Props) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const extensions = useMemo(() => {
|
||||
const ext = syntaxExtension(contentType);
|
||||
return [
|
||||
...baseExtensions,
|
||||
...(ext ? [ext] : []),
|
||||
EditorView.updateListener.of((update) => {
|
||||
if (typeof onChange === 'function') {
|
||||
onChange(update.state.doc.toString());
|
||||
}
|
||||
}),
|
||||
];
|
||||
}, [contentType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current === null) return;
|
||||
|
||||
const view = new EditorView({
|
||||
state: EditorState.create({
|
||||
doc: defaultValue ?? '',
|
||||
extensions: extensions,
|
||||
}),
|
||||
parent: ref.current,
|
||||
});
|
||||
return () => view?.destroy();
|
||||
}, [ref.current]);
|
||||
|
||||
return <div ref={ref} className="cm-wrapper" />;
|
||||
}
|
||||
|
||||
99
src-web/components/Editor/extensions.ts
Normal file
99
src-web/components/Editor/extensions.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import {
|
||||
bracketMatching,
|
||||
defaultHighlightStyle,
|
||||
foldGutter,
|
||||
foldKeymap,
|
||||
HighlightStyle,
|
||||
indentOnInput,
|
||||
LanguageSupport,
|
||||
syntaxHighlighting,
|
||||
} from '@codemirror/language';
|
||||
import {
|
||||
crosshairCursor,
|
||||
drawSelection,
|
||||
dropCursor,
|
||||
highlightActiveLine,
|
||||
highlightActiveLineGutter,
|
||||
highlightSpecialChars,
|
||||
keymap,
|
||||
lineNumbers,
|
||||
rectangularSelection,
|
||||
} from '@codemirror/view';
|
||||
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands';
|
||||
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search';
|
||||
import {
|
||||
autocompletion,
|
||||
closeBrackets,
|
||||
closeBracketsKeymap,
|
||||
completionKeymap,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { lintKeymap } from '@codemirror/lint';
|
||||
import { EditorState } from '@codemirror/state';
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { html } from '@codemirror/lang-html';
|
||||
import { tags } from '@lezer/highlight';
|
||||
|
||||
export const myHighlightStyle = HighlightStyle.define([
|
||||
{
|
||||
tag: [tags.documentMeta, tags.blockComment, tags.lineComment, tags.docComment, tags.comment],
|
||||
color: '#757b93',
|
||||
},
|
||||
{ tag: tags.name, color: '#4699de' },
|
||||
{ tag: tags.variableName, color: '#31c434' },
|
||||
{ tag: tags.bool, color: '#e864f6' },
|
||||
{ tag: tags.attributeName, color: '#8f68ff' },
|
||||
{ tag: tags.attributeValue, color: '#ff964b' },
|
||||
{ tag: [tags.keyword, tags.string], color: '#e8b045' },
|
||||
{ tag: tags.comment, color: '#cec4cc', fontStyle: 'italic' },
|
||||
]);
|
||||
|
||||
const syntaxExtensions: Record<string, LanguageSupport> = {
|
||||
'application/json': json(),
|
||||
'application/javascript': javascript(),
|
||||
'text/html': html(),
|
||||
};
|
||||
|
||||
export function syntaxExtension(contentType: string): LanguageSupport | undefined {
|
||||
return syntaxExtensions[contentType];
|
||||
}
|
||||
|
||||
export const baseExtensions = [
|
||||
lineNumbers(),
|
||||
highlightActiveLineGutter(),
|
||||
highlightSpecialChars(),
|
||||
history(),
|
||||
foldGutter({
|
||||
markerDOM: (open) => {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('fold-gutter-icon');
|
||||
el.tabIndex = -1;
|
||||
if (open) {
|
||||
el.setAttribute('data-open', '');
|
||||
}
|
||||
return el;
|
||||
},
|
||||
}),
|
||||
drawSelection(),
|
||||
dropCursor(),
|
||||
EditorState.allowMultipleSelections.of(true),
|
||||
indentOnInput(),
|
||||
syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
|
||||
bracketMatching(),
|
||||
closeBrackets(),
|
||||
autocompletion(),
|
||||
rectangularSelection(),
|
||||
crosshairCursor(),
|
||||
highlightActiveLine(),
|
||||
highlightSelectionMatches(),
|
||||
keymap.of([
|
||||
...closeBracketsKeymap,
|
||||
...defaultKeymap,
|
||||
...searchKeymap,
|
||||
...historyKeymap,
|
||||
...foldKeymap,
|
||||
...completionKeymap,
|
||||
...lintKeymap,
|
||||
]),
|
||||
syntaxHighlighting(myHighlightStyle),
|
||||
];
|
||||
Reference in New Issue
Block a user