Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move template selection into modal UI #201

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 23 additions & 26 deletions src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SplitPane from 'react-split-pane';
import { Editor } from './Editor/Editor';
import { Navigation } from './Navigation';
import { Template } from './Template';
import NewFile from './NewFile';
import { NewFileModal } from './Modals/NewFileModal';
import { VisualiserTemplate } from './Visualiser';

import { debounce } from '../helpers';
Expand Down Expand Up @@ -50,31 +50,28 @@ export const Content: React.FunctionComponent<ContentProps> = () => { // eslint-
return (
<div className="flex flex-1 flex-row relative">
<div className="flex flex-1 flex-row relative">
{newFileEnabled && <NewFile />}

{!newFileEnabled &&
<SplitPane
size={viewEnabled ? secondPaneSize : 0}
minSize={0}
maxSize={secondPaneMaxSize}
pane1Style={
navigationEnabled || editorEnabled ? undefined : { width: '0px' }
}
pane2Style={
viewEnabled ? { overflow: 'auto' } : { width: '0px' }
}
primary={viewEnabled ? 'first' : 'second'}
defaultSize={localStorageRightPaneSize}
onChange={debounce((size: string) => {
localStorage.setItem(splitPosRight, String(size));
}, 100)}
>
{navigationAndEditor}
{viewType === 'template' && <Template />}
{viewType === 'visualiser' && <VisualiserTemplate />}
</SplitPane>
}
{newFileEnabled && <NewFileModal />}
Copy link
Member

@magicmatatjahu magicmatatjahu Jan 26, 2022

Choose a reason for hiding this comment

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

All our modals has that nice enter animation (like settings on the bottom left corner) but templates hasn't and here is a problem. You always destroy and create new React node when user will open that model. I think that we should fix that by:

Suggested change
{newFileEnabled && <NewFileModal />}
<NewFileModal />

but we have to "watch" for change newFileEnabled in the NewFileModal component and change show prop to the show={newFileEnabled}.

<SplitPane
size={viewEnabled ? secondPaneSize : 0}
minSize={0}
maxSize={secondPaneMaxSize}
pane1Style={
navigationEnabled || editorEnabled ? undefined : { width: '0px' }
}
pane2Style={
viewEnabled ? { overflow: 'auto' } : { width: '0px' }
}
primary={viewEnabled ? 'first' : 'second'}
defaultSize={localStorageRightPaneSize}
onChange={debounce((size: string) => {
localStorage.setItem(splitPosRight, String(size));
}, 100)}
>
{navigationAndEditor}
{viewType === 'template' && <Template />}
{viewType === 'visualiser' && <VisualiserTemplate />}
</SplitPane>
</div>
</div>
);
};
};
4 changes: 3 additions & 1 deletion src/components/Modals/ConfirmModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface ConfirmModalProps {
cancelDisabled?: boolean;
opener?: React.ReactNode;
show?: boolean;
containerClassName? : string;
onSubmit?: () => void;
onCancel?: () => void;
}
Expand All @@ -25,6 +26,7 @@ export const ConfirmModal: React.FunctionComponent<ConfirmModalProps> = ({
onCancel = () => {
// This is intentional
},
containerClassName,
children,
}) => {
const [showModal, setShowModal] = useState(show);
Expand Down Expand Up @@ -86,7 +88,7 @@ export const ConfirmModal: React.FunctionComponent<ConfirmModalProps> = ({
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<div className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full sm:p-6">
<div className={`inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl sm:w-full sm:p-6 ${containerClassName}`}>
<div>
<div>
<Dialog.Title
Expand Down
104 changes: 104 additions & 0 deletions src/components/Modals/NewFileModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useState } from 'react';
import { BsFillCheckCircleFill } from 'react-icons/bs';
import toast from 'react-hot-toast';

import { ConfirmModal } from './index';
import examples from '../../examples';
import { EditorService } from '../../services';
import state from '../../state';

export const NewFileModal: React.FunctionComponent = () => {
const sidebarState = state.useSidebarState();
const [selectedTemplate, setSelectedTemplate] = useState({ title: '', template: '' });

const handleSubmit = () => {
EditorService.updateState({ content: selectedTemplate.template, updateModel: true });
const panels = state.sidebar.panels;
panels.merge({
newFile: false,
});

toast.success(
<div>
<span className="block text-bold">
Succesfully reused the {`"${selectedTemplate.title}"`} template.
</span>
</div>
);
};

const realLifeExamples = examples.filter((template) => template.type === 'real-example');
const templates = examples.filter((template) => template.type === 'protocol-example');

return (
<ConfirmModal containerClassName="sm:max-w-6xl" onCancel={() => sidebarState.panels.newFile.set(false)} title="AsyncAPI Templates - Start with our template examples" confirmText="Use Template" confirmDisabled={false} show={true} onSubmit={handleSubmit}>
<div className="flex content-center justify-center">
<div className="w-full overflow-auto space-y-8 ">
<div>
<span className="uppercase text-gray-800 text-sm underline font-bold">Templates</span>
<div className="grid grid-cols-3 gap-4 py-4">
{templates.map(({ title, description: Description, template }) => {
const isSelected = selectedTemplate.title === title;
const containerStyles = isSelected ? 'border-pink-500' : 'border-gray-200';
const textStyles = isSelected ? 'text-pink-600' : 'text-gray-600';

return (
<button
onClick={() => setSelectedTemplate({ title, template })}
key={title}
className={`group text-left flex flex-col cursor-pointer rounded-lg p-4 pb-6 border-2 hover:border-pink-500 ${containerStyles}`}
>
<div className="flex justify-between w-full">
<span className={`block text-md font-bold leading-0 group-hover:text-pink-600 ${textStyles} `}>{title}</span>
{isSelected && <BsFillCheckCircleFill className="w-5 h-5 text-pink-600" />}
</div>
<span className="block text-sm text-gray-500 font-light mt-1 group-hover:text-gray-900">
<Description />
</span>
</button>
);
})}
boyney123 marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
<div>
<span className="uppercase text-gray-800 text-sm underline font-bold">Real world Examples</span>
<div className="grid grid-cols-3 gap-4 py-4">
{realLifeExamples.map(({ title, description: Description, template }) => {
const isSelected = selectedTemplate.title === title;
const containerStyles = isSelected ? 'border-pink-500' : 'border-gray-200';
const textStyles = isSelected ? 'text-pink-600' : 'text-gray-600';

return (
<button
onClick={() => setSelectedTemplate({ title, template })}
key={title}
className={`group text-left flex flex-col cursor-pointer rounded-lg p-4 pb-6 border-2 border-gray-200 hover:border-pink-500 ${containerStyles}`}
>
<div className="flex justify-between w-full">
<span className={`block text-md font-bold leading-0 group-hover:text-pink-600 ${textStyles} `}>{title}</span>
{isSelected && <BsFillCheckCircleFill className="w-5 h-5 text-pink-600" />}
</div>
<span className="block text-sm text-gray-500 font-light mt-1 group-hover:text-gray-900">
<Description />
</span>
</button>
);
})}
</div>
</div>
<span className=" text-xs block text-gray-900 text-right ">
Don&apos;t see what you&apos;re looking for? <br />
<a
target="_blank"
href="https://github.com/asyncapi/studio/issues/new?assignees=&labels=enhancement&template=enhancement.md&title=Template%20Request:%20{%20template%20name%20and%20type%20}"
className="underline text-pink-500"
rel="noreferrer"
>
Request a template or add one to the list &rarr;
</a>
</span>
</div>
</div>
</ConfirmModal>
);
};
77 changes: 0 additions & 77 deletions src/components/NewFile/index.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const Sidebar: React.FunctionComponent<SidebarProps> = () => {
// newFile
{
name: 'newFile',
state: () => sidebarState.panels.newFile.get(),
state: () => false,
icon: <VscNewFile className="w-5 h-5" />,
},
];
Expand Down
6 changes: 0 additions & 6 deletions src/examples/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ export default [
template: simple,
type: templateTypes.protocol
},
{
title: 'Simple Hello World',
description: () => <>A basic example of a service that is in charge of processing user signups. Great place to start learning AsyncAPI.</>,
template: simple,
type: templateTypes.protocol
},
{
title: 'Apache Kafka',
description: () => <>A framework implementation of a software bus using stream-processing. Open Source developed by the Apache Software Foundation.</>,
Expand Down