Final fixes

This commit is contained in:
HarelM
2025-09-17 00:12:14 +03:00
parent b70b6f16b7
commit 176e1e4ae8
7 changed files with 61 additions and 178 deletions
+1
View File
@@ -11,6 +11,7 @@ export type BlockProps = PropsWithChildren & {
onChange?(...args: unknown[]): unknown
fieldSpec?: object
wideMode?: boolean
error?: {message: string}
};
type BlockState = {
+4 -2
View File
@@ -3,7 +3,8 @@ import { TbMathFunction } from "react-icons/tb";
import { PiListPlusBold } from "react-icons/pi";
import {isEqual} from "lodash";
import {type ExpressionSpecification, type LegacyFilterSpecification} from "maplibre-gl";
import {latest, migrate, convertFilter} from "@maplibre/maplibre-gl-style-spec";
import {migrate, convertFilter} from "@maplibre/maplibre-gl-style-spec";
import latest from "@maplibre/maplibre-gl-style-spec/dist/latest.json";
import {combiningFilterOps} from "../libs/filterops";
import InputSelect from "./InputSelect";
@@ -96,7 +97,7 @@ type FilterEditorInternalProps = {
properties?: {[key:string]: any}
filter?: any[]
errors?: MappedLayerErrors
onChange(value: LegacyFilterSpecification | ExpressionSpecification): unknown
onChange(value: LegacyFilterSpecification | ExpressionSpecification): void
} & WithTranslation;
type FilterEditorState = {
@@ -293,6 +294,7 @@ class FilterEditorInternal extends React.Component<FilterEditorInternalProps, Fi
this.props.onChange(defaultFilter);
}}
fieldName="filter"
fieldSpec={fieldSpec as any}
value={filter}
errors={errors}
onChange={this.props.onChange}
+23 -65
View File
@@ -11,15 +11,10 @@ import type { StylePropertySpecification } from "maplibre-gl";
export type InputJsonProps = {
value: object
maxHeight?: number
lineNumbers?: boolean
lineWrapping?: boolean
gutters?: string[]
className?: string
onChange(object: object): void
onFocus?(...args: unknown[]): unknown
onBlur?(...args: unknown[]): unknown
onJSONValid?(): void
onJSONInvalid?(_err: Error): void
lintType: "layer" | "style" | "expression" | "json"
spec?: StylePropertySpecification | undefined
};
@@ -32,16 +27,11 @@ type InputJsonState = {
class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJsonState> {
static defaultProps = {
lineNumbers: true,
lineWrapping: false,
gutters: ["CodeMirror-lint-markers"],
onFocus: () => {},
onBlur: () => {},
onJSONInvalid: () => {},
onJSONValid: () => {},
};
_keyEvent: string;
_doc: EditorView | undefined;
_view: EditorView | undefined;
_el: HTMLDivElement | null = null;
_cancelNextChange: boolean = false;
@@ -59,37 +49,15 @@ class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJso
}
componentDidMount () {
this._doc = createEditor(
this._el!,
this.getPrettyJson(this.props.value),
this.props.lintType || "layer",
(value:string) => this.props.onChange(value ? JSON.parse(value) : {}),
this.props.spec
);
/*CodeMirror(this._el!, {
value: this.props.getValue!(this.props.layer),
mode: this.props.mode || {
name: "mgl",
},
lineWrapping: this.props.lineWrapping,
tabSize: 2,
theme: "maputnik",
viewportMargin: Infinity,
lineNumbers: this.props.lineNumbers,
lint: this.props.lint || {
context: "layer"
},
matchBrackets: true,
gutters: this.props.gutters,
scrollbarStyle: "null",
this._view = createEditor({
parent: this._el!,
value: this.getPrettyJson(this.props.value),
lintType: this.props.lintType || "layer",
onChange: (value:string) => this.onChange(value),
onFocus: () => this.onFocus(),
onBlur: () => this.onBlur(),
spec: this.props.spec
});
this._doc.on("change", this.onChange);
this._doc.on("focus", this.onFocus);
this._doc.on("blur", this.onBlur);
*/
this._doc.dispatch();
}
onPointerDown = () => {
@@ -100,7 +68,6 @@ class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJso
if (this.props.onFocus) this.props.onFocus();
this.setState({
isEditing: true,
showMessage: (this._keyEvent === "keyboard"),
});
};
@@ -109,35 +76,31 @@ class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJso
if (this.props.onBlur) this.props.onBlur();
this.setState({
isEditing: false,
showMessage: false,
});
};
componentWillUnMount () {
//this._doc!.off("change", this.onChange);
//this._doc!.off("focus", this.onFocus);
//this._doc!.off("blur", this.onBlur);
}
componentDidUpdate(_prevProps: InputJsonProps) {
//if (!this.state.isEditing && prevProps.layer !== this.props.layer) {
// this._cancelNextChange = true;
// this._doc!.setValue(
// this.props.getValue!(this.props.layer),
// );
//}
componentDidUpdate(prevProps: InputJsonProps) {
if (!this.state.isEditing && prevProps.value !== this.props.value) {
this._cancelNextChange = true;
this._view!.dispatch({
changes: {
from: 0,
to: this._view!.state.doc.length,
insert: this.getPrettyJson(this.props.value)
}
});
}
}
onChange = (_e: unknown) => {
/*
if (this._cancelNextChange) {
this._cancelNextChange = false;
this.setState({
prevValue: this._doc!.getValue(),
prevValue: this._view!.state.doc.toString(),
});
return;
}
const newCode = this._doc!.getValue();
const newCode = this._view!.state.doc.toString();
if (this.state.prevValue !== newCode) {
let parsedLayer, err;
@@ -148,19 +111,14 @@ class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJso
console.warn(_err);
}
if (err && this.props.onJSONInvalid) {
this.props.onJSONInvalid();
}
else {
if (!err) {
if (this.props.onChange) this.props.onChange(parsedLayer);
if (this.props.onJSONValid) this.props.onJSONValid();
}
}
this.setState({
prevValue: newCode,
});
*/
};
render() {
+7 -1
View File
@@ -12,6 +12,7 @@ import { type MappedLayerErrors } from "../libs/definitions";
type ExpressionPropertyInternalProps = {
fieldName: string
fieldType?: string
fieldSpec?: StylePropertySpecification
value?: any
errors?: MappedLayerErrors
@@ -64,12 +65,17 @@ class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInter
</InputButton>
</>
);
let error = undefined;
if (this.props.errors) {
const fieldKey = this.props.fieldType ? this.props.fieldType + "." + this.props.fieldName : this.props.fieldName;
error = this.props.errors[fieldKey];
}
return <Block
fieldSpec={this.props.fieldSpec}
label={t(labelFromFieldName(this.props.fieldName))}
action={deleteStopBtn}
wideMode={true}
error={error}
>
<FieldJson
lintType="expression"
+26 -9
View File
@@ -1,5 +1,6 @@
import { basicSetup } from "codemirror";
import { EditorView } from "@codemirror/view";
import { EditorState, Compartment } from "@codemirror/state";
import { json, jsonParseLinter } from "@codemirror/lang-json";
import { linter, lintGutter, type Diagnostic } from "@codemirror/lint";
import { oneDark } from "@codemirror/theme-one-dark";
@@ -150,15 +151,17 @@ function createMaplibreExpressionLinter(spec: StylePropertySpecification) {
};
}
export function createEditor(
export function createEditor(props: {
parent: HTMLElement,
value: string,
lintType: LintType,
onChange: (value: string) => void,
onFocus: () => void,
onBlur: () => void,
spec?: StylePropertySpecification,
): EditorView {
}): EditorView {
let specificLinter: (view: EditorView) => Diagnostic[] = () => [];
switch (lintType) {
switch (props.lintType) {
case "style":
specificLinter = createMaplibreStyleLinter();
break;
@@ -166,26 +169,40 @@ export function createEditor(
specificLinter = createMaplibreLayerLinter();
break;
case "expression":
if (!spec) {
if (!props.spec) {
throw new Error("spec is required for expression mode");
}
specificLinter = createMaplibreExpressionLinter(spec);
specificLinter = createMaplibreExpressionLinter(props.spec);
break;
case "json":
specificLinter = () => [];
break;
}
return new EditorView({
doc: value,
doc: props.value,
extensions: [
basicSetup,
json(),
oneDark,
oneDark,
new Compartment().of(EditorState.tabSize.of(2)),
EditorView.theme({
"&": {
fontSize: "9pt"
}
}),
EditorView.updateListener.of((update) => {
if (update.docChanged) {
const doc = update.state.doc;
const value = doc.toString();
onChange(value);
props.onChange(value);
}
if (update.focusChanged) {
if (update.view.hasFocus) {
props.onFocus();
} else {
props.onBlur();
}
}
}),
lintGutter(),
@@ -197,6 +214,6 @@ export function createEditor(
return specificLinter(view);
})
],
parent,
parent: props.parent,
});
}
-100
View File
@@ -1,100 +0,0 @@
@use "vars";
.CodeMirror-lint-tooltip {
z-index: 2000 !important;
}
.codemirror-container {
max-width: 100%;
position: relative;
overflow: auto;
}
.cm-s-maputnik.CodeMirror {
height: 100%;
font-size: 12px;
background: transparent;
}
.cm-s-maputnik.CodeMirror, .cm-s-maputnik .CodeMirror-gutters {
color: #8e8e8e;
border: none;
}
.cm-s-maputnik .CodeMirror-gutters {
background: #212328;
}
.cm-s-maputnik .CodeMirror-cursor {
border-left: solid thin #f0f0f0 !important;
}
.cm-s-maputnik.CodeMirror-focused div.CodeMirror-selected {
background: rgba(255, 255, 255, 0.10);
}
.cm-s-maputnik .CodeMirror-line::selection,
.cm-s-maputnik .CodeMirror-line > span::selection,
.cm-s-maputnik .CodeMirror-line > span > span::selection {
background: rgba(255, 255, 255, 0.10);
}
.cm-s-maputnik .CodeMirror-line::-moz-selection,
.cm-s-maputnik .CodeMirror-line > span::-moz-selection,
.cm-s-maputnik .CodeMirror-line > span > span::-moz-selection {
background: rgba(255, 255, 255, 0.10);
}
.cm-s-maputnik span.cm-string, .cm-s-maputnik span.cm-string-2 {
color: #8f9d6a;
}
.cm-s-maputnik span.cm-number { color: #91675f; }
.cm-s-maputnik span.cm-property { color: #b8a077; }
.cm-s-maputnik .CodeMirror-activeline-background {
background: rgba(255,255,255,0.1);
}
.cm-s-maputnik .CodeMirror-matchingbracket {
background: hsla(223, 12%, 35%, 1);
color: vars.$color-white !important;
}
.cm-s-maputnik .CodeMirror-nonmatchingbracket {
background-color: #bb0000;
color: white !important;
}
@keyframes JSONEditor__animation-fade {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
.JSONEditor__message {
position: absolute;
right: 0;
font-size: 0.85em;
z-index: 99999;
padding: 0.3em 0.5em;
background: hsla(0, 0%, 0%, 0.3);
color: vars.$color-lowgray;
border-bottom-left-radius: 2px;
transition: opacity 320ms ease;
opacity: 0;
pointer-events: none;
&--on {
opacity: 1;
animation: 320ms ease 0s JSONEditor__animation-fade;
animation-delay: 2000ms;
animation-fill-mode: forwards;
}
kbd {
font-family: monospace;
}
}
-1
View File
@@ -15,7 +15,6 @@
@use 'zoomproperty';
@use 'popup';
@use 'map';
@use 'codemirror';
@use 'react-collapse';
.maputnik-layout {