diff --git a/packages/ui/src/OpenApiEditor.tsx b/packages/ui/src/OpenApiEditor.tsx
index f7c42c9..ab14d06 100644
--- a/packages/ui/src/OpenApiEditor.tsx
+++ b/packages/ui/src/OpenApiEditor.tsx
@@ -263,7 +263,7 @@ function Editor() {
switch (selectedNode.type) {
case "validation":
case "root":
- return undefined;
+ return ;
case "path":
return ;
case "datatype":
@@ -296,7 +296,8 @@ function Editor() {
switch (true) {
case selectedNode.type === "validation":
return ;
- case view === "designer":
+ case view === "design":
+ case view === "visualize":
switch (selectedNode.type) {
case "root":
return actorRef ? (
diff --git a/packages/ui/src/OpenApiEditorMachine.ts b/packages/ui/src/OpenApiEditorMachine.ts
index bb9dbd0..2acdce1 100644
--- a/packages/ui/src/OpenApiEditorMachine.ts
+++ b/packages/ui/src/OpenApiEditorMachine.ts
@@ -17,11 +17,12 @@ import { CodeEditorMachine } from "./codeEditor/CodeEditorMachine.ts";
import { PathDesignerMachine } from "./pathDesigner/PathDesignerMachine.ts";
import { DataTypeDesignerMachine } from "./dataTypeDesigner/DataTypeDesignerMachine.ts";
import { ResponseDesignerMachine } from "./responseDesigner/ResponseDesignerMachine.ts";
+import { EditorToolbarView } from "./components/EditorToolbar.tsx";
type Context = EditorModel & {
navigationFilter: string;
selectedNode: SelectedNode | { type: "validation" };
- view: "designer" | "code";
+ view: Exclude;
actorRef?:
| ActorRefFrom
| ActorRefFrom
@@ -41,6 +42,21 @@ type Events =
| {
readonly type: "SELECT_DOCUMENT_ROOT_DESIGNER";
}
+ | {
+ readonly type: "SELECT_PATH_VISUALIZER";
+ readonly path: string;
+ readonly nodePath: string;
+ }
+ | {
+ readonly type: "SELECT_DATA_TYPE_VISUALIZER";
+ readonly name: string;
+ readonly nodePath: string;
+ }
+ | {
+ readonly type: "SELECT_RESPONSE_VISUALIZER";
+ readonly name: string;
+ readonly nodePath: string;
+ }
| {
readonly type: "SELECT_PATH_DESIGNER";
readonly path: string;
@@ -56,6 +72,9 @@ type Events =
readonly name: string;
readonly nodePath: string;
}
+ | {
+ readonly type: "SELECT_DOCUMENT_ROOT_VISUALIZER";
+ }
| {
readonly type: "SELECT_DOCUMENT_ROOT_CODE";
}
@@ -107,6 +126,9 @@ type Events =
| {
readonly type: "REDO";
}
+ | {
+ readonly type: "GO_TO_VISUALIZER_VIEW";
+ }
| {
readonly type: "GO_TO_DESIGNER_VIEW";
}
@@ -162,7 +184,7 @@ export const OpenApiEditorMachine = setup({
selectedNode: {
type: "root",
},
- view: "designer",
+ view: "visualize",
},
initial: "viewChanged",
states: {
@@ -184,12 +206,15 @@ export const OpenApiEditorMachine = setup({
return undefined;
}
switch (context.view) {
- case "designer":
+ case "design":
+ case "visualize":
switch (context.selectedNode.type) {
case "root":
+ console.log("SPAWN", context.view);
return spawn("documentRootDesigner", {
input: {
parentRef: self,
+ editable: context.view === "design",
},
});
case "path":
@@ -328,13 +353,53 @@ export const OpenApiEditorMachine = setup({
target: ".debouncing",
actions: assign({ navigationFilter: ({ event }) => event.filter }),
},
+ SELECT_DOCUMENT_ROOT_VISUALIZER: {
+ target: ".viewChanged",
+ actions: assign({
+ selectedNode: { type: "root" },
+ view: "visualize",
+ }),
+ },
SELECT_DOCUMENT_ROOT_DESIGNER: {
target: ".viewChanged",
actions: assign({
selectedNode: { type: "root" },
- view: "designer",
+ view: "design",
}),
},
+ SELECT_PATH_VISUALIZER: {
+ target: ".viewChanged",
+ actions: assign(({ event }) => ({
+ selectedNode: {
+ type: "path",
+ path: event.path,
+ nodePath: event.nodePath,
+ },
+ view: "visualize",
+ })),
+ },
+ SELECT_DATA_TYPE_VISUALIZER: {
+ target: ".viewChanged",
+ actions: assign(({ event }) => ({
+ selectedNode: {
+ type: "datatype",
+ name: event.name,
+ nodePath: event.nodePath,
+ },
+ view: "visualize",
+ })),
+ },
+ SELECT_RESPONSE_VISUALIZER: {
+ target: ".viewChanged",
+ actions: assign(({ event }) => ({
+ selectedNode: {
+ type: "response",
+ name: event.name,
+ nodePath: event.nodePath,
+ },
+ view: "visualize",
+ })),
+ },
SELECT_PATH_DESIGNER: {
target: ".viewChanged",
actions: assign(({ event }) => ({
@@ -343,7 +408,7 @@ export const OpenApiEditorMachine = setup({
path: event.path,
nodePath: event.nodePath,
},
- view: "designer",
+ view: "design",
})),
},
SELECT_DATA_TYPE_DESIGNER: {
@@ -354,7 +419,7 @@ export const OpenApiEditorMachine = setup({
name: event.name,
nodePath: event.nodePath,
},
- view: "designer",
+ view: "design",
})),
},
SELECT_RESPONSE_DESIGNER: {
@@ -365,7 +430,7 @@ export const OpenApiEditorMachine = setup({
name: event.name,
nodePath: event.nodePath,
},
- view: "designer",
+ view: "design",
})),
},
SELECT_DOCUMENT_ROOT_CODE: {
@@ -416,13 +481,19 @@ export const OpenApiEditorMachine = setup({
GO_TO_DESIGNER_VIEW: {
target: ".viewChanged",
actions: assign({
- view: "code",
+ view: "design",
}),
},
GO_TO_CODE_VIEW: {
target: ".viewChanged",
actions: assign({
- view: "designer",
+ view: "code",
+ }),
+ },
+ GO_TO_VISUALIZER_VIEW: {
+ target: ".viewChanged",
+ actions: assign({
+ view: "visualize",
}),
},
UNDO: ".undoing",
diff --git a/packages/ui/src/components/EditorSidebar.tsx b/packages/ui/src/components/EditorSidebar.tsx
index bd7b6f2..eb707ca 100644
--- a/packages/ui/src/components/EditorSidebar.tsx
+++ b/packages/ui/src/components/EditorSidebar.tsx
@@ -11,7 +11,7 @@ import { NavigationDataTypes } from "./NavigationDataTypes.tsx";
export function EditorSidebar() {
const {
isFiltering,
- isDesignerView,
+ view,
paths,
responses,
dataTypes,
@@ -19,7 +19,7 @@ export function EditorSidebar() {
selectedNode,
} = OpenApiEditorMachineContext.useSelector((state) => ({
isFiltering: state.matches("debouncing") || state.matches("filtering"),
- isDesignerView: state.context.view === "designer",
+ view: state.context.view,
paths: state.context.navigation.paths,
responses: state.context.navigation.responses,
dataTypes: state.context.navigation.dataTypes,
@@ -62,15 +62,23 @@ export function EditorSidebar() {
+ onClick={(p) => {
+ const type = (() => {
+ switch (view) {
+ case "visualize":
+ return "SELECT_PATH_VISUALIZER";
+ case "design":
+ return "SELECT_PATH_DESIGNER";
+ case "code":
+ return "SELECT_PATH_CODE";
+ }
+ })();
actorRef.send({
- type: isDesignerView
- ? "SELECT_PATH_DESIGNER"
- : "SELECT_PATH_CODE",
+ type,
path: p.path,
nodePath: p.nodePath,
- })
- }
+ });
+ }}
isActive={(p) =>
"path" in selectedNode && p.path === selectedNode?.path
}
@@ -80,15 +88,23 @@ export function EditorSidebar() {
+ onClick={(dt) => {
+ const type = (() => {
+ switch (view) {
+ case "visualize":
+ return "SELECT_DATA_TYPE_VISUALIZER";
+ case "design":
+ return "SELECT_DATA_TYPE_DESIGNER";
+ case "code":
+ return "SELECT_DATA_TYPE_CODE";
+ }
+ })();
actorRef.send({
- type: isDesignerView
- ? "SELECT_DATA_TYPE_DESIGNER"
- : "SELECT_DATA_TYPE_CODE",
+ type,
name: dt.name,
nodePath: dt.nodePath,
- })
- }
+ });
+ }}
isActive={(p) =>
"name" in selectedNode && p.name === selectedNode?.name
}
@@ -98,15 +114,23 @@ export function EditorSidebar() {
+ onClick={(r) => {
+ const type = (() => {
+ switch (view) {
+ case "visualize":
+ return "SELECT_RESPONSE_VISUALIZER";
+ case "design":
+ return "SELECT_RESPONSE_DESIGNER";
+ case "code":
+ return "SELECT_RESPONSE_CODE";
+ }
+ })();
actorRef.send({
- type: isDesignerView
- ? "SELECT_RESPONSE_DESIGNER"
- : "SELECT_RESPONSE_CODE",
+ type,
name: r.name,
nodePath: r.nodePath,
- })
- }
+ });
+ }}
isActive={(p) =>
"name" in selectedNode && p.name === selectedNode?.name
}
diff --git a/packages/ui/src/components/EditorToolbar.tsx b/packages/ui/src/components/EditorToolbar.tsx
index 43bd387..117f991 100644
--- a/packages/ui/src/components/EditorToolbar.tsx
+++ b/packages/ui/src/components/EditorToolbar.tsx
@@ -22,14 +22,14 @@ import {
} from "@patternfly/react-icons";
import { ReactNode } from "react";
-type View = "designer" | "code" | "no-code";
+export type EditorToolbarView = "design" | "code" | "visualize" | "hidden";
export type EditorToolbarProps = {
title: ReactNode;
label?: ReactNode;
- view: View;
+ view: EditorToolbarView;
canGoBack: boolean;
onBack: () => void;
- onViewChange: (view: View) => void;
+ onViewChange: (view: EditorToolbarView) => void;
};
export function EditorToolbar({
title,
@@ -56,75 +56,84 @@ export function EditorToolbar({
return (
- {canGoBack && (
-
- }
- onClick={onBack}
- variant={"plain"}
- />
-
- )}
- {label && {label}}
-
-
- {title}
-
-
- {low + medium + high > 0 && (
- <>
-
-
-
-
+
+ {canGoBack && (
+
+ }
+ onClick={onBack}
+ variant={"plain"}
+ />
- >
- )}
-
+ )}
+ {label && {label}}
+
+
+ {title}
+
+
+ {low + medium + high > 0 && (
+ <>
+
+
+
+
+
+ >
+ )}
+
+
- {view !== "no-code" && (
+ {view !== "hidden" && (
<>
-
{
+ onViewChange("visualize");
+ }}
+ />
+ {
- onViewChange("designer");
+ onViewChange("design");
}}
/>
{
diff --git a/packages/ui/src/components/InlineEdit.tsx b/packages/ui/src/components/InlineEdit.tsx
index 5f7f0ad..3461327 100644
--- a/packages/ui/src/components/InlineEdit.tsx
+++ b/packages/ui/src/components/InlineEdit.tsx
@@ -9,12 +9,7 @@ import {
InputGroupItem,
TextInput,
} from "@patternfly/react-core";
-import {
- CheckIcon,
- ExclamationCircleIcon,
- PencilAltIcon,
- TimesIcon,
-} from "@patternfly/react-icons";
+import { CheckIcon, ExclamationCircleIcon } from "@patternfly/react-icons";
import {
FormEventHandler,
FunctionComponent,
@@ -36,27 +31,19 @@ interface IInlineEdit {
validator?: (value: string) => ValidationResult;
onChange?: (value: string) => void;
onClick?: () => void;
- disabled?: boolean;
+ editing?: boolean;
+ autoFocus?: boolean;
}
export const InlineEdit: FunctionComponent = (props) => {
const [localValue, setLocalValue] = useState(props.value ?? "");
- const [isReadOnly, setIsReadOnly] = useState(true);
-
- const focusTextInput = useCallback((element: HTMLInputElement) => {
- element?.focus();
- }, []);
+ const [isReadOnly, setIsReadOnly] = useState(false);
const [validationResult, setValidationResult] = useState({
status: "default",
errMessages: [],
});
- const onEdit: MouseEventHandler = useCallback((event) => {
- setIsReadOnly(false);
- event.stopPropagation();
- }, []);
-
const onChange = useCallback(
(_event: unknown, value: string) => {
setLocalValue(value);
@@ -113,25 +100,20 @@ export const InlineEdit: FunctionComponent = (props) => {
[saveValue]
);
- const onCancel: MouseEventHandler = useCallback(
- (event) => {
- cancelValue();
- event.stopPropagation();
- },
- [cancelValue]
- );
-
const noop: FormEventHandler = useCallback((event) => {
event.preventDefault();
}, []);
useEffect(() => {
setLocalValue(props.value ?? "");
+ setIsReadOnly(false);
}, [props.value]);
+ const isChanged = props.value !== localValue;
+
return (
<>
- {isReadOnly || props.disabled ? (
+ {!props.editing ? (
<>
= (props) => {
)}
-
- {!props.disabled && (
- } />
- )}
>
) : (