Skip to content
38 changes: 35 additions & 3 deletions src/components/App/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@
*
* SPDX-License-Identifier: EPL-2.0 OR W3C-20150513
********************************************************************************/
import React, { useState, useEffect, useCallback } from "react";
import React, { useState, useEffect, useCallback, useContext } from "react";
import InfoIconWrapper from "../base/InfoIconWrapper";
import TextField from "../base/TextField";
import { isValidUrl } from "../../utils/strings";
import EdiTDorContext from "../../context/ediTDorContext";
import Dropdown from "../base/Dropdown";

export interface SettingsData {
northboundUrl: string;
southboundUrl: string;
pathToValue: string;
jsonIndentation: 2 | 4;
}

export interface SettingsErrors {
Expand All @@ -39,11 +42,13 @@ const Settings: React.FC<SettingsProps> = ({
northboundUrl: "",
southboundUrl: "",
pathToValue: "/",
jsonIndentation: 2,
},
onChange,
hideTitle = false,
className = "",
}) => {
const context = useContext(EdiTDorContext);
const [data, setData] = useState<SettingsData>(initialData);
const [errors, setErrors] = useState<SettingsErrors>({
northboundUrl: "",
Expand All @@ -52,8 +57,11 @@ const Settings: React.FC<SettingsProps> = ({
});

useEffect(() => {
setData(initialData);
}, [initialData]);
setData((prev) => ({
...prev,
jsonIndentation: context.jsonIndentation,
}));
}, [context.jsonIndentation]);

useEffect(() => {
if (onChange) {
Expand Down Expand Up @@ -127,8 +135,32 @@ const Settings: React.FC<SettingsProps> = ({
[]
);

const handleJsonIndentationChange = useCallback(
(e: React.ChangeEvent<HTMLSelectElement>) => {
const parsed = Number(e.target.value);
const value: 2 | 4 = parsed === 4 ? 4 : 2;
setData((prev) => ({ ...prev, jsonIndentation: value }));
context.updateJsonIndentation(value);
},
[context]
);

return (
<div className={className}>
<div className="my-4 rounded-md bg-black bg-opacity-80 p-2">
{!hideTitle && <h1 className="font-bold">JSON Editor</h1>}
<div className="px-4">
<Dropdown
id="json-indentation-select"
label="Space indentation"
value={String(data.jsonIndentation)}
onChange={handleJsonIndentationChange}
options={["2", "4"]}
className="w-full"
/>
</div>
</div>

<div className="rounded-md bg-black bg-opacity-80 p-2">
{!hideTitle && (
<h1 className="font-bold">Third Party Service Configuration</h1>
Expand Down
53 changes: 38 additions & 15 deletions src/components/Editor/JsonEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ import { IValidationMessage } from "../../types/context";
type SchemaMapMessage = Map<string, Record<string, unknown>>;

// List of all Options can be found here: https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html
const editorOptions: editor.IStandaloneEditorConstructionOptions = {
selectOnLineNumbers: true,
automaticLayout: true,
lineDecorationsWidth: 20,
tabSize: 2,
insertSpaces: true,
};

// delay function that executes the callback once it hasn't been called for
// at least x ms.
Expand All @@ -54,6 +47,7 @@ interface JsonSchemaEntry {
const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
const context = useContext(ediTDorContext);

const jsonIndentation = context.jsonIndentation ?? 2;
const [schemas] = useState<JsonSchemaEntry[]>([]);
const [proxy, setProxy] = useState<any>(undefined);
const editorInstance = useRef<editor.IStandaloneCodeEditor | null>(null);
Expand Down Expand Up @@ -85,6 +79,8 @@ const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
);

useEffect(() => {
if (!proxy) return;

const updateMonacoSchemas = (schemaMap: SchemaMapMessage) => {
proxy.splice(0, proxy.length);

Expand Down Expand Up @@ -165,10 +161,8 @@ const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
setProxy(proxy);
};

const onChange: OnChange = async (editorText: string | undefined) => {
if (!editorText) {
return;
}
const onChange: OnChange = (editorText: string | undefined) => {
if (!editorText) return;
let validate: IValidationMessage = {
report: {
json: null,
Expand Down Expand Up @@ -199,15 +193,19 @@ const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
},
customMessage: "",
};

try {
JSON.parse(editorText);
context.updateOfflineTD(editorText);

if (editorText !== context.offlineTD) {
context.updateOfflineTD(editorText);
}

setLocalTextState(editorText);
context.updateValidationMessage(validate);

delay(messageWorkers, editorText, 500);
} catch (error) {
let message: string =
"Invalid JSON: " +
(error instanceof Error ? error.message : String(error));
validate.report.json = "failed";
context.updateValidationMessage(validate);
setLocalTextState(editorText);
Expand Down Expand Up @@ -242,6 +240,19 @@ const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
}
}, [context.linkedTd, context.offlineTD]);

useEffect(() => {
try {
const parsed = JSON.parse(context.offlineTD);
const formatted = JSON.stringify(parsed, null, jsonIndentation);

if (formatted !== context.offlineTD) {
context.updateOfflineTD(formatted);
}
} catch {
// ignore invalid JSON
}
}, [jsonIndentation, context.offlineTD]);

const changeLinkedTd = async () => {
let href = (document.getElementById("linkedTd") as HTMLSelectElement).value;
changeBetweenTd(context, href);
Expand All @@ -255,6 +266,18 @@ const JsonEditor: React.FC<JsonEditorProps> = ({ editorRef }) => {
});
};

const editorOptions = useMemo(
() => ({
selectOnLineNumbers: true,
automaticLayout: true,
lineDecorationsWidth: 20,
tabSize: jsonIndentation,
insertSpaces: true,
detectIndentation: false,
}),
[jsonIndentation]
);

return (
<>
<div className="h-[5%] bg-[#1e1e1e]">
Expand Down
4 changes: 4 additions & 0 deletions src/components/base/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ interface IDropdownProps {
id: string;
label: string;
options: string[];
value?: string;
onChange?: (event: React.ChangeEvent<HTMLSelectElement>) => void;
className?: string;
}

Expand All @@ -33,6 +35,8 @@ const Dropdown: React.FC<IDropdownProps> = (props) => {
<select
className="block w-full appearance-none rounded border-2 border-gray-600 bg-gray-600 px-4 py-3 pr-8 leading-tight text-white focus:border-blue-500 focus:outline-none"
id={props.id}
value={props.value}
onChange={props.onChange}
>
{props.options.map((e) => {
return <option key={e}>{e}</option>;
Expand Down
11 changes: 11 additions & 0 deletions src/context/GlobalState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const UPDATE_VALIDATION_MESSAGE = "UPDATE_VALIDATION_MESSAGE";
export const UPDATE_NORTHBOUND_CONNECTION = "UPDATE_NORTHBOUND_CONNECTION";
export const UPDATE_CONTRIBUTE_CATALOG = "UPDATE_CONTRIBUTE_CATALOG";
export const UPDATE_BACKGROUND_TM = "UPDATE_BACKGROUND_TM";
export const UPDATE_JSON_INDENTATION = "UPDATE_JSON_INDENTATION";

interface IGlobalStateProps {
children: ReactNode;
Expand Down Expand Up @@ -93,6 +94,7 @@ const GlobalState: React.FC<IGlobalStateProps> = ({ children }) => {
nameRepository: "",
dynamicValues: {},
},
jsonIndentation: 2,
});

const updateOfflineTD = (offlineTD: string) => {
Expand Down Expand Up @@ -177,6 +179,13 @@ const GlobalState: React.FC<IGlobalStateProps> = ({ children }) => {
});
};

const updateJsonIndentation = (value: 2 | 4) => {
dispatch({
type: UPDATE_JSON_INDENTATION,
value,
});
};

return (
<EdiTDorContext.Provider
value={{
Expand All @@ -190,6 +199,8 @@ const GlobalState: React.FC<IGlobalStateProps> = ({ children }) => {
validationMessage: editdorState.validationMessage,
northboundConnection: editdorState.northboundConnection,
contributeCatalog: editdorState.contributeCatalog,
jsonIndentation: editdorState.jsonIndentation,
updateJsonIndentation,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateJsonIndentation should be in the last line

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateJsonIndentation should be in the last line

updateOfflineTD,
updateIsModified,
setFileHandle,
Expand Down
12 changes: 12 additions & 0 deletions src/context/editorReducers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
UPDATE_VALIDATION_MESSAGE,
UPDATE_NORTHBOUND_CONNECTION,
UPDATE_CONTRIBUTE_CATALOG,
UPDATE_JSON_INDENTATION,
} from "./GlobalState";
import type {
ThingDescription,
Expand Down Expand Up @@ -70,6 +71,8 @@ export const editdorReducer = (
return updateNorthboundConnection(action.northboundConnection, state);
case UPDATE_CONTRIBUTE_CATALOG:
return updateContributeCatalog(action.contributeCatalog, state);
case UPDATE_JSON_INDENTATION:
return updateJsonIndentationReducer(action.value, state);
default:
return state;
}
Expand Down Expand Up @@ -365,3 +368,12 @@ const updateContributeCatalog = (
): EditorState => {
return { ...state, contributeCatalog };
};
const updateJsonIndentationReducer = (
jsonIndentation: 2 | 4,
state: EditorState
): EditorState => {
return {
...state,
jsonIndentation,
};
};
6 changes: 5 additions & 1 deletion src/types/context.d.ts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an increase of typescritp errors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an increase of typescript errors

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ declare global {

northboundConnection: INorthboundConnection;
contributeCatalog: IContributeCatalog;
jsonIndentation: 2 | 4;
updateJsonIndentation: (value: 2 | 4) => void;

// Callback functions
updateOfflineTD: (td: string) => void;
Expand Down Expand Up @@ -141,7 +143,8 @@ declare global {
| {
type: "UPDATE_CONTRIBUTE_CATALOG";
contributeCatalog: IContributeCatalog;
};
}
| { type: "UPDATE_JSON_INDENTATION"; value: 2 | 4 };

declare type Validation = "VALID" | "INVALID" | "VALIDATING" | null;
declare type ActiveSection =
Expand All @@ -157,6 +160,7 @@ declare global {
northboundUrl: string;
southboundUrl: string;
pathToValue: string;
jsonIndentation: 2 | 4;
}

// Define the shape of the state
Expand Down