From bce16319c3675b076ee74916db05593afc2f34f7 Mon Sep 17 00:00:00 2001 From: aednikanov Date: Tue, 2 Jul 2024 15:52:08 +0300 Subject: [PATCH] implemented complex types except object --- .../Fields/ConditionalField.tsx | 96 +++++++++++++++++++ .../ParserOpenRPC/InteractiveBox/index.tsx | 37 +++---- .../InteractiveBox/styles.module.css | 35 ++++--- .../templates/ArrayFieldTemplate.tsx | 16 ++-- .../templates/BaseInputTemplate.tsx | 30 +++--- .../InteractiveBox/widgets/SelectWidget.tsx | 43 +++++++++ .../ParserOpenRPC/Tooltip/Tooltip.module.css | 1 - src/css/custom.css | 5 + 8 files changed, 205 insertions(+), 58 deletions(-) create mode 100644 src/components/ParserOpenRPC/InteractiveBox/Fields/ConditionalField.tsx create mode 100644 src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget.tsx diff --git a/src/components/ParserOpenRPC/InteractiveBox/Fields/ConditionalField.tsx b/src/components/ParserOpenRPC/InteractiveBox/Fields/ConditionalField.tsx new file mode 100644 index 0000000000..477d21c5ff --- /dev/null +++ b/src/components/ParserOpenRPC/InteractiveBox/Fields/ConditionalField.tsx @@ -0,0 +1,96 @@ +import React, { useContext, useEffect, useState } from "react"; +import { FieldTemplateProps } from "@rjsf/utils"; +import { BaseInputTemplate } from "@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate"; +import { SelectWidget } from "@site/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget"; +import styles from "@site/src/components/ParserOpenRPC/InteractiveBox/styles.module.css"; +import { ParserOpenRPCContext } from "@site/src/components/ParserOpenRPC"; +import clsx from "clsx"; + +export const ConditionalField = (props: FieldTemplateProps) => { + const [isOpened, setIsOpened] = useState(false); + const [selectedTypeSchema, setSelectedTypeSchema] = useState(null); + const [isEditView, setIsEditView] = useState(false); + const { setIsDrawerContentFixed, setDrawerLabel } = useContext(ParserOpenRPCContext); + const { formData, schema, name, formContext, onChange } = props; + const { setIsComplexTypeView, isComplexTypeView } = formContext; + const listItems = schema?.anyOf ? schema?.anyOf : schema?.oneOf; + const checkForNullTypeSchema = (type) => type === "null"; + const showComplexTypeView = () => { + setDrawerLabel(name); + setIsDrawerContentFixed(true); + setIsEditView(true); + setIsComplexTypeView(true); + } + const onDropdownOptionClick = (e) => { + const selectedSchema = listItems.find(({ title }) => title === e.target.innerText); + const isNullTypeSchema = checkForNullTypeSchema(selectedSchema?.type); + if (isNullTypeSchema) { + onChange(null); + } else { + setSelectedTypeSchema(listItems.find(({ title }) => title === e.target.innerText)); + showComplexTypeView(); + } + setIsOpened(false); + } + const selectWidgetProps = { + ...props, + schema: selectedTypeSchema, + label: name, + value: formData, + ...(selectedTypeSchema?.enum && { + options:{ + enumOptions: selectedTypeSchema?.enum.map(item => ({ label: item, value: item })) + } + }) + } + const baseInputProps = { + ...props, + schema: selectedTypeSchema + } + + useEffect(() => { + if(!isComplexTypeView) { + setIsEditView(false); + setSelectedTypeSchema(null); + } + }, [isComplexTypeView]) + + return listItems?.length > 0 ? ( + <> +
+
+ +
+
+
+ {formData === undefined ? "" : String(formData)} + + { setIsOpened(!isOpened); }}> + {schema?.anyOf ? "anyOf" : "oneOf"} + + + +
    + {listItems?.map(({ title }, index) => ( +
  • + {String(title)} +
  • + ))} +
+
+
+
+
+ {isComplexTypeView && isEditView && selectedTypeSchema && selectedTypeSchema.type !== "null" ? +
+ {selectedTypeSchema?.enum ? : } +
+ : null + } + + ) : null; +} diff --git a/src/components/ParserOpenRPC/InteractiveBox/index.tsx b/src/components/ParserOpenRPC/InteractiveBox/index.tsx index e3dfb14047..16b197a0b7 100644 --- a/src/components/ParserOpenRPC/InteractiveBox/index.tsx +++ b/src/components/ParserOpenRPC/InteractiveBox/index.tsx @@ -1,17 +1,19 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import Form from "@rjsf/core"; import clsx from "clsx"; -import { RJSFSchema, UiSchema, RegistryWidgetsType } from "@rjsf/utils"; +import {RJSFSchema, UiSchema, RegistryWidgetsType, RegistryFieldsType} from "@rjsf/utils"; import validator from "@rjsf/validator-ajv8"; import $RefParser from "@apidevtools/json-schema-ref-parser"; import { MethodExample, MethodParam, SchemaComponents } from "@site/src/components/ParserOpenRPC/interfaces"; import styles from "./styles.module.css"; import global from "../global.module.css"; import { BaseInputTemplate } from "@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate"; +import { ArrayFieldTemplate } from "@site/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate"; +import { ConditionalField } from "@site/src/components/ParserOpenRPC/InteractiveBox/fields/ConditionalField"; import { DropdownWidget } from "@site/src/components/ParserOpenRPC/InteractiveBox/widgets/DropdownWidget"; +import { SelectWidget } from "@site/src/components/ParserOpenRPC/InteractiveBox/widgets/SelectWidget"; import { Tooltip } from "@site/src/components/ParserOpenRPC/Tooltip"; import { useColorMode } from "@docusaurus/theme-common"; -import { ArrayFieldTemplate } from "@site/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate"; import { ParserOpenRPCContext } from "@site/src/components/ParserOpenRPC"; interface InteractiveBoxProps { @@ -45,10 +47,20 @@ export default function InteractiveBox({ params, components, examples, onParamCh }, "ui:widget": "checkbox", }; + const templates = { + BaseInputTemplate, + ArrayFieldTemplate, + FieldErrorTemplate: () => null, + ErrorListTemplate: () => null, + } const widgets: RegistryWidgetsType = { CheckboxWidget: DropdownWidget, + SelectWidget: SelectWidget, + }; + const fields: RegistryFieldsType = { + AnyOfField: ConditionalField, + OneOfField: ConditionalField, }; - const log = (type) => console.log.bind(console, type); const handleResetForm = (e) => { e.preventDefault(); setDefaultFormData(defaultExampleFormData); @@ -81,6 +93,7 @@ export default function InteractiveBox({ params, components, examples, onParamCh const onChangeHandler = (data) => { onParamChange(data); + setDefaultFormData(data); }; const closeComplexTypeView = () => { @@ -107,7 +120,7 @@ export default function InteractiveBox({ params, components, examples, onParamCh } const handleCancelClick = () => { - if (drawerLabel && drawerLabel !== null) { + if (drawerLabel) { const upData = cloneAndSetNullIfExists(defaultFormData, drawerLabel); setDefaultFormData(upData) } @@ -131,22 +144,12 @@ export default function InteractiveBox({ params, components, examples, onParamCh validator={validator} liveValidate noHtml5Validate - onChange={(data) => { - console.log('changed', data); - onChangeHandler(data.formData); - setDefaultFormData(data.formData); - }} - onSubmit={() => {log("submitted");}} - onError={log("errors")} - templates={{ - BaseInputTemplate, - ArrayFieldTemplate, - FieldErrorTemplate: () => null, - ErrorListTemplate: () => null, - }} + onChange={(data) => { onChangeHandler(data.formData); }} + templates={templates} uiSchema={uiSchema} widgets={widgets} ref={formRef} + fields={fields} >
diff --git a/src/components/ParserOpenRPC/InteractiveBox/styles.module.css b/src/components/ParserOpenRPC/InteractiveBox/styles.module.css index c0c441b258..c8429fa123 100644 --- a/src/components/ParserOpenRPC/InteractiveBox/styles.module.css +++ b/src/components/ParserOpenRPC/InteractiveBox/styles.module.css @@ -1,9 +1,6 @@ .tableHeadingRow { display: flex; border-top: 1px solid rgba(132, 140, 150, 1); - /*position: fixed;*/ - /*z-index: 2;*/ - /*width: 100%;*/ } .tableHeadingColumn { width: 50%; @@ -29,10 +26,14 @@ height: 100%; padding: 12px 16px 12px 12px; border-left: 4px solid transparent; + line-height: 28px; } .tableColumnParamFocused { border-left: 4px solid rgba(16, 152, 252, 1); } +.tableColumnParamError { + border-left: 4px solid rgba(224, 100, 112, 1); +} .tableColumn:first-child { border-right: 1px solid rgba(132, 140, 150, 1); } @@ -43,9 +44,10 @@ justify-content: space-between; align-items: center; position: relative; + font-size: 14px; } .tableValueRowPadding { - padding: 12px 56px 12px 16px; + padding: 12px 72px 12px 16px; } .tableColumnType { display: flex; @@ -59,6 +61,12 @@ line-height: 24px; font-size: 14px; } +.tableLabelIconError { + width: 11px; + height: 11px; + margin-left: 8px; + background: url("/img/icons/error-icon.svg") no-repeat 50% 50%; +} .tableColumnIcon { position: absolute; right: 13px; @@ -68,9 +76,6 @@ .tableColumnIcon:hover { cursor: pointer; } -.tableColumnIconError { - background: url("/img/icons/error-icon.svg") no-repeat 50% 50%; -} .tableColumnIconRemove { background: url("/img/icons/remove-icon.svg") no-repeat 50% 50%; } @@ -119,7 +124,7 @@ .dropdownList { position: absolute; right: 0; - opacity: 1; + visibility: visible; list-style: none; border-radius: 10px; padding: 0; @@ -127,10 +132,10 @@ top: 35px; overflow: hidden; border: 1px solid rgba(132, 140, 150, 1); - transition: 0.4s opacity; + z-index: 2; } .dropdownListClosed { - opacity: 0; + visibility: hidden; } .dropdownList li + li { margin-top: 0; @@ -255,14 +260,12 @@ width: 100%; border-radius: 0; } - .arrayParentRow { position: relative; width: 100%; height: 100%; padding: 12px 72px 12px 16px; } - .arrayColumnType { position: absolute; top: 0; @@ -275,13 +278,13 @@ font-size: 14px; cursor: pointer; } - .arrayFormDataWrap { + display: block; + max-width: 395px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } - .addItemBtnWrap { min-height: 52px; display: flex; @@ -289,7 +292,6 @@ padding: 12px; border-bottom: 1px solid rgba(132, 140, 150, 1); } - .addItemBtn { display: flex; align-items: center; @@ -302,7 +304,6 @@ line-height: 1; cursor: pointer; } - .addItemIcon { display: inline-flex; align-items: center; @@ -316,7 +317,6 @@ line-height: 1; margin-right: 10px; } - .arrayItemIcon { position: absolute; top: 50%; @@ -326,7 +326,6 @@ line-height: 1; font-weight: bold; } - .arrayItemRowWrap { position: relative; padding-left: 40px; diff --git a/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx b/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx index c421848d9d..ab04d823cf 100644 --- a/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx +++ b/src/components/ParserOpenRPC/InteractiveBox/templates/ArrayFieldTemplate.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from "react"; +import React, { useContext, useState } from "react"; import { useCollapsible, Collapsible } from "@docusaurus/theme-common"; import { ArrayFieldTemplateProps } from "@rjsf/utils"; import { BaseInputTemplate } from "@site/src/components/ParserOpenRPC/InteractiveBox/templates/BaseInputTemplate"; @@ -6,8 +6,8 @@ import styles from "@site/src/components/ParserOpenRPC/InteractiveBox/styles.mod import clsx from "clsx"; import { ParserOpenRPCContext } from "@site/src/components/ParserOpenRPC"; -export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { - const { items, canAdd, onAddClick, title, schema, formData, formContext } = props; +export const ArrayFieldTemplate = ({ items, canAdd, onAddClick, title, schema, formData, formContext }: ArrayFieldTemplateProps) => { + const [isComplexArrayEditView, setIsComplexArrayEditView] = useState(false); const { setIsDrawerContentFixed, setDrawerLabel } = useContext(ParserOpenRPCContext); const { collapsed, toggleCollapsed } = useCollapsible({ initialState: true }); const itemsType = schema?.items?.type; @@ -17,6 +17,7 @@ export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { onAddClick(); setDrawerLabel(title); setIsDrawerContentFixed(true); + setIsComplexArrayEditView(true); setIsComplexTypeView(true); } @@ -48,7 +49,7 @@ export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => {
- {isComplexTypeView && !isSimpleArray ? + {isComplexTypeView && isComplexArrayEditView && !isSimpleArray ?
{items.map(({ children, index, onDropIndexClick, hasRemove }) => (
@@ -73,16 +74,17 @@ export const ArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { <> {items.map((el, i) => { - const props = { + const baseInputTemplateProps = { ...el.children.props, - isArray: true + isArray: true, + value: formData, } const { index, hasRemove, onDropIndexClick, schema } = el; const isNumber = schema.type === "number" || schema.type === "integer"; return (
{i+1} - + {hasRemove && ( { + schema, + id, + name, + value = "", + disabled, + onChange, + rawErrors, + hideError, + required, + formContext, + isArray + }: ExtendedInputProps) => { const isNumber = schema.type === "number" || schema.type === "integer"; const [isFocused, setIsFocused] = useState(false); const [inputValue, setInputValue] = useState(isNumber ? 0 : ""); @@ -52,8 +52,9 @@ export const BaseInputTemplate = ({
{!isArray && (
-
)} @@ -73,7 +74,6 @@ export const BaseInputTemplate = ({ /> {schema.type} - {hasErrors && !isNumber ? : null} {isNumber ? ( <> { + const [isOpened, setIsOpened] = useState(false); + const emptyValue = value === undefined || !options?.enumOptions.some(({ label }) => label === value); + + return ( +
+
+ +
+
+
+ {emptyValue ? "" : String(value)} + + { setIsOpened(!isOpened); }}> + {schema?.enum ? 'enum' : schema?.type} + + + +
    + {options?.enumOptions?.map(({ label, value }, index) => ( +
  • { + onChange(value); + setIsOpened(false); + }} + > + {String(label)} +
  • + ))} +
+
+
+
+
+ ); +}; diff --git a/src/components/ParserOpenRPC/Tooltip/Tooltip.module.css b/src/components/ParserOpenRPC/Tooltip/Tooltip.module.css index 5f0553c5c4..f899638b91 100644 --- a/src/components/ParserOpenRPC/Tooltip/Tooltip.module.css +++ b/src/components/ParserOpenRPC/Tooltip/Tooltip.module.css @@ -1,6 +1,5 @@ .tooltipContainer { max-width: 180px; - max-height: 60px; font-size: 16px; } .tooltipContainer:first-letter { diff --git a/src/css/custom.css b/src/css/custom.css index 649d55d980..87800888b0 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -209,6 +209,10 @@ pre { max-width: 500px; } +form { + position: relative; +} + fieldset { border: none; padding: 0; @@ -217,6 +221,7 @@ fieldset { fieldset#root { position: relative; + padding-bottom: 10px; } button:hover {