Skip to content

Commit

Permalink
refactor: switch to the nice-modal-react modal management (#518)
Browse files Browse the repository at this point in the history
  • Loading branch information
magicmatatjahu authored Dec 1, 2022
1 parent 35b19bc commit 4f35efb
Show file tree
Hide file tree
Showing 34 changed files with 30,661 additions and 483 deletions.
30,446 changes: 30,365 additions & 81 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@asyncapi/parser": "^2.0.0-next-major.10",
"@asyncapi/react-component": "^1.0.0-next.44",
"@asyncapi/specs": "^4.0.1",
"@ebay/nice-modal-react": "^1.2.8",
"@headlessui/react": "^1.7.4",
"@hookstate/core": "^4.0.0-rc21",
"@monaco-editor/react": "^4.4.6",
Expand Down
39 changes: 27 additions & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import React, { useEffect } from 'react';
import { useEffect } from 'react';
import { show } from '@ebay/nice-modal-react';

import AsyncAPIStudio from './studio';
import { AsyncAPIStudio } from './studio';
import { RedirectedModal } from './components/Modals';

import state from './state';

const App: React.FunctionComponent = () => {
import type { FunctionComponent } from 'react';

async function onInit(editorLoaded: boolean, redirectedFrom: string | false) {
if (!editorLoaded) {
return;
}

const preloader = document.getElementById('preloader');
if (preloader) {
preloader.classList.add('loaded');
}

if (typeof redirectedFrom === 'string') {
show(RedirectedModal);
}
}

export const App: FunctionComponent = () => {
const appState = state.useAppState();
const editorState = state.useEditorState();

const redirectedFrom = appState.redirectedFrom.get();
const editorLoaded = editorState.editorLoaded.get();

useEffect(() => {
if (editorLoaded === true) {
const preloader = document.getElementById('preloader');
if (preloader) {
preloader.classList.add('loaded');
}
}
}, [editorLoaded]); // eslint-disable-line
onInit(editorLoaded, redirectedFrom);
}, [editorLoaded, redirectedFrom]); // eslint-disable-line

return (
<AsyncAPIStudio />
);
};

export default App;
3 changes: 0 additions & 3 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import SplitPane from './SplitPane';
import { Editor } from './Editor/Editor';
import { Navigation } from './Navigation';
import { Template } from './Template';
import { NewFileModal, RedirectedModal } from './Modals';
import { VisualiserTemplate } from './Visualiser';

import { debounce } from '../helpers';
Expand Down Expand Up @@ -49,8 +48,6 @@ export const Content: FunctionComponent<ContentProps> = () => { // eslint-disabl
return (
<div className="flex flex-1 flex-row relative">
<div className="flex flex-1 flex-row relative">
<NewFileModal />
<RedirectedModal />
<SplitPane
size={viewEnabled ? secondPaneSize : 0}
minSize={0}
Expand Down
66 changes: 56 additions & 10 deletions src/components/Editor/EditorDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import toast from 'react-hot-toast';
import { show } from '@ebay/nice-modal-react';
import { FaEllipsisH } from 'react-icons/fa';
import { hasErrorDiagnostic } from '@asyncapi/parser/cjs/utils';

import {
ConvertModal,
GeneratorModal,
ImportBase64Modal,
ImportURLModal,
ImportBase64Modal,
GeneratorModal,
ConvertModal,
} from '../Modals';
import { Dropdown } from '../common';

Expand All @@ -24,6 +25,17 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
const language = editorState.language.get();
const hasParserErrors = hasErrorDiagnostic(parserState.diagnostics.get());

const importUrlButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Import from URL"
onClick={() => show(ImportURLModal)}
>
Import from URL
</button>
);

const importFileButton = (
<label
className="block px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 cursor-pointer"
Expand Down Expand Up @@ -56,10 +68,33 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
</label>
);

const saveFileButton = (
const importBase64Button = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Import from Base64"
onClick={() => show(ImportBase64Modal)}
>
Import from Base64
</button>
);

const generateButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
title="Generate code/docs"
disabled={hasParserErrors}
onClick={() => show(GeneratorModal)}
>
Generate code/docs
</button>
);

const saveFileButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
title={`Save as ${language === 'yaml' ? 'YAML' : 'JSON'}`}
onClick={() => {
toast.promise(
Expand Down Expand Up @@ -94,7 +129,7 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
const convertLangAndSaveButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
title={`Convert and save as ${
language === 'yaml' ? 'JSON' : 'YAML'
}`}
Expand Down Expand Up @@ -131,7 +166,7 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
const convertLangButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
title={`Convert to ${language === 'yaml' ? 'JSON' : 'YAML'}`}
onClick={() => {
toast.promise(
Expand Down Expand Up @@ -163,6 +198,17 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
</button>
);

const convertButton = (
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Convert AsyncAPI document"
onClick={() => show(ConvertModal)}
>
Convert document
</button>
);

return (
<Dropdown
opener={<FaEllipsisH />}
Expand All @@ -171,18 +217,18 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
<ul className="bg-gray-800 text-md text-white">
<div className="border-b border-gray-700">
<li className="hover:bg-gray-900">
<ImportURLModal />
{importUrlButton}
</li>
<li className="hover:bg-gray-900">
{importFileButton}
</li>
<li className="hover:bg-gray-900">
<ImportBase64Modal />
{importBase64Button}
</li>
</div>
<div className="border-b border-gray-700">
<li className="hover:bg-gray-900">
<GeneratorModal />
{generateButton}
</li>
</div>
<div className="border-b border-gray-700">
Expand All @@ -198,7 +244,7 @@ export const EditorDropdown: React.FunctionComponent<EditorDropdownProps> = () =
{convertLangButton}
</li>
<li className="hover:bg-gray-900">
<ConvertModal />
{convertButton}
</li>
</div>
</ul>
Expand Down
5 changes: 2 additions & 3 deletions src/components/Editor/MonacoWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@ import * as monacoAPI from 'monaco-editor/esm/vs/editor/editor.api';
import { debounce } from '../../helpers';
import { useServices } from '../../services';
import state from '../../state';
import { useSettingsState } from '../../state/index.state';

export type MonacoWrapperProps = MonacoEditorProps

export const MonacoWrapper: React.FunctionComponent<MonacoWrapperProps> = ({
...props
}) => {
const { editorSvc, specificationSvc } = useServices();
const { autoSaving, savingDelay } = useSettingsState(state => state.editor);
const editorState = state.useEditorState();
const settingsState = state.useSettingsState();
const autoSaving = settingsState.editor.autoSaving.get();
const savingDelay = settingsState.editor.savingDelay.get();

async function handleEditorDidMount(
editor: monacoAPI.editor.IStandaloneCodeEditor,
Expand Down
52 changes: 18 additions & 34 deletions src/components/Modals/ConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -1,78 +1,65 @@
import React, { Fragment, useEffect, useRef, useState, useImperativeHandle, forwardRef } from 'react';
import { Fragment, useRef, useCallback } from 'react';
import { Dialog, Transition } from '@headlessui/react';
import { useModal } from '@ebay/nice-modal-react';

export interface ConfirmModalHandle {
close(): void;
}
import type { ReactNode, FunctionComponent, PropsWithChildren } from 'react';

interface ConfirmModalProps {
title: React.ReactNode;
description?: React.ReactNode;
confirmText?: React.ReactNode;
cancelText?: React.ReactNode;
title: ReactNode;
description?: ReactNode;
confirmText?: ReactNode;
cancelText?: ReactNode;
confirmDisabled?: boolean;
cancelDisabled?: boolean;
opener?: React.ReactNode;
show?: boolean;
containerClassName? : string;
closeAfterSumbit?: boolean;
onSubmit?: () => void;
onCancel?: () => void;
}

const ConfirmModalSans: React.ForwardRefRenderFunction<ConfirmModalHandle, React.PropsWithChildren<ConfirmModalProps>> = ({
export const ConfirmModal: FunctionComponent<PropsWithChildren<ConfirmModalProps>> = ({
title,
description,
confirmText = 'Save',
cancelText = 'Cancel',
confirmDisabled = true,
cancelDisabled = false,
opener,
show = false,
closeAfterSumbit = true,
onSubmit,
onCancel = () => {
// This is intentional
},
containerClassName,
children,
}, modalRef) => {
const [showModal, setShowModal] = useState(show);

}) => {
const modal = useModal();
const cancelButtonRef = useRef(null);

useImperativeHandle(modalRef, () => ({
close() {
setShowModal(false);
},
}));

useEffect(() => {
setShowModal(show);
}, [show]);

const handleOnSubmit = () => {
onSubmit && onSubmit();
if (closeAfterSumbit) {
setShowModal(false);
modal.hide();
}
};

const handleOnCancel = () => {
onCancel();
setShowModal(false);
modal.hide();
};

const handleAfterLeave = useCallback(() => {
modal.remove();
}, []);

return (
<>
{opener && <div onClick={() => setShowModal(true)}>{opener}</div>}
<Transition.Root show={showModal} as={Fragment}>
<Transition.Root show={modal.visible} as={Fragment} afterLeave={handleAfterLeave}>
<Dialog
as="div"
static
className="fixed z-50 inset-0 overflow-y-auto"
initialFocus={cancelButtonRef}
open={showModal}
open={modal.visible}
onClose={handleOnCancel}
>
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
Expand Down Expand Up @@ -150,6 +137,3 @@ const ConfirmModalSans: React.ForwardRefRenderFunction<ConfirmModalHandle, React
</>
);
};

const ConfirmModal = forwardRef(ConfirmModalSans);
export { ConfirmModal };
27 changes: 11 additions & 16 deletions src/components/Modals/ConvertModal.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import React, { useState } from 'react';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { create } from '@ebay/nice-modal-react';

import { ConfirmModal } from './index';

import { useServices } from '../../services';
import state from '../../state';

export const ConvertModal: React.FunctionComponent = () => {
const [version, setVersion] = useState('');
import type { SpecVersions } from '../../types';

export const ConvertModal = create(() => {
const { editorSvc, specificationSvc } = useServices();
const latestVersion = specificationSvc.getLastVersion();

const [version, setVersion] = useState(latestVersion);
const parserState = state.useParserState();
const actualVersion = parserState.parsedSpec.get()?.version();
const latestVersion = specificationSvc.getLastVersion();
const allowedVersions = Object.keys(specificationSvc.getSpecs());
actualVersion && (allowedVersions.splice(0, allowedVersions.indexOf(actualVersion) + 1));
const reservedAllowedVersions = [...allowedVersions].reverse();
Expand Down Expand Up @@ -41,15 +45,6 @@ export const ConvertModal: React.FunctionComponent = () => {
title={`Convert AsyncAPI ${actualVersion} document`}
confirmText="Convert"
confirmDisabled={!version || allowedVersions.length === 0}
opener={
<button
type="button"
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
title="Convert AsyncAPI document"
>
Convert document
</button>
}
onSubmit={onSubmit}
>
<div>
Expand All @@ -64,7 +59,7 @@ export const ConvertModal: React.FunctionComponent = () => {
<select
name="asyncapi-version"
className="shadow-sm focus:ring-pink-500 focus:border-pink-500 w-1/2 block w-full sm:text-sm border-gray-300 rounded-md py-2 px-1 text-gray-700 border-pink-300 border-2"
onChange={e => setVersion(e.target.value)}
onChange={e => setVersion(e.target.value as SpecVersions)}
value={version}
>
<option value="">Please Select</option>
Expand All @@ -81,4 +76,4 @@ export const ConvertModal: React.FunctionComponent = () => {
</div>
</ConfirmModal>
);
};
});
Loading

0 comments on commit 4f35efb

Please sign in to comment.