PromucFlow_constructor/app/client/src/pages/Editor/ApiSidebar.tsx

298 lines
7.2 KiB
TypeScript
Raw Normal View History

2019-10-18 08:16:26 +00:00
import React from "react";
2019-10-21 15:12:45 +00:00
import { connect } from "react-redux";
import { RouteComponentProps } from "react-router";
import styled from "styled-components";
import { AppState } from "../../reducers";
import { ActionDataState } from "../../reducers/entityReducers/actionsReducer";
import { API_EDITOR_ID_URL, API_EDITOR_URL } from "../../constants/routes";
2019-11-05 05:09:50 +00:00
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
import { FormIcons } from "../../icons/FormIcons";
import { Spinner } from "@blueprintjs/core";
2019-11-13 07:34:59 +00:00
import { ApiPaneReduxState } from "../../reducers/uiReducers/apiPaneReducer";
import { BaseTextInput } from "../../components/designSystems/appsmith/TextInputComponent";
import { TICK } from "@blueprintjs/icons/lib/esm/generated/iconNames";
import { createActionRequest } from "../../actions/actionActions";
2019-11-14 09:01:23 +00:00
import Fuse from "fuse.js";
2019-11-13 11:34:11 +00:00
const LoadingContainer = styled.div`
height: 50%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
`;
const ApiSidebarWrapper = styled.div`
2019-11-13 11:34:11 +00:00
margin-top: 10px;
height: 100%;
width: 100%;
flex-direction: column;
`;
2019-11-14 09:01:23 +00:00
const SearchBar = styled(BaseTextInput)`
margin-bottom: 10px;
input {
background-color: #23292e;
border: none;
color: ${props => props.theme.colors.textOnDarkBG}
:focus {
background-color: #23292e;
}
}
.bp3-icon {
background-color: #23292e;
}
`;
const ApiItemsWrapper = styled.div`
flex: 1;
2019-11-13 11:34:11 +00:00
margin-bottom: 15px;
`;
2019-10-21 15:12:45 +00:00
const ApiItem = styled.div<{ isSelected: boolean }>`
height: 32px;
2019-10-21 15:12:45 +00:00
width: 100%;
2019-11-13 11:34:11 +00:00
padding: 8px 12px;
2019-10-21 15:12:45 +00:00
border-radius: 4px;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
margin-bottom: 2px;
2019-10-21 15:12:45 +00:00
background-color: ${props =>
props.isSelected ? props.theme.colors.paneCard : props.theme.colors.paneBG}
:hover {
background-color: ${props => props.theme.colors.paneCard};
}
`;
2019-11-14 09:01:23 +00:00
const HTTPMethod = styled.span<{ method?: string }>`
2019-10-21 15:12:45 +00:00
flex: 1;
font-size: 12px;
2019-10-21 15:12:45 +00:00
color: ${props => {
switch (props.method) {
case "GET":
return "#29CCA3";
case "POST":
return "#F7C75B";
case "PUT":
return "#30A5E0";
case "DELETE":
return "#CE4257";
default:
return "#333";
}
}};
`;
const ActionName = styled.span`
flex: 3;
padding: 0 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
const CreateNewButton = styled(BaseButton)`
&& {
border: none;
color: ${props => props.theme.colors.textOnDarkBG};
height: 32px;
2019-11-13 11:34:11 +00:00
text-align: left;
justify-content: flex-start;
&:hover {
color: ${props => props.theme.colors.paneBG};
svg {
path {
fill: ${props => props.theme.colors.paneBG};
}
}
}
svg {
2019-11-13 11:34:11 +00:00
margin-top: 4px;
height: 14px;
width: 14px;
}
}
2019-10-21 15:12:45 +00:00
`;
2019-11-13 07:34:59 +00:00
const CreateApiWrapper = styled.div`
display: grid;
2019-11-13 11:34:11 +00:00
grid-template-columns: 6fr 1fr;
2019-11-13 07:34:59 +00:00
grid-gap: 5px;
2019-11-13 11:34:11 +00:00
height: 32px;
2019-11-13 07:34:59 +00:00
`;
2019-10-21 15:12:45 +00:00
interface ReduxStateProps {
actions: ActionDataState;
2019-11-13 07:34:59 +00:00
apiPane: ApiPaneReduxState;
2019-10-21 15:12:45 +00:00
}
2019-11-13 07:34:59 +00:00
interface ReduxDispatchProps {
createAction: (name: string) => void;
}
type Props = ReduxStateProps &
ReduxDispatchProps &
RouteComponentProps<{ id: string }>;
type State = {
isCreating: boolean;
name: string;
2019-11-14 09:01:23 +00:00
search: string;
};
const fuseOptions = {
shouldSort: true,
threshold: 0.1,
location: 0,
minMatchCharLength: 3,
keys: ["name"],
2019-11-13 07:34:59 +00:00
};
class ApiSidebar extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
isCreating: false,
name: "",
2019-11-14 09:01:23 +00:00
search: "",
2019-11-13 07:34:59 +00:00
};
}
componentDidUpdate(prevProps: Readonly<Props>): void {
if (!prevProps.match.params.id && this.props.match.params.id) {
this.setState({
isCreating: false,
name: "",
});
}
}
2019-10-21 15:12:45 +00:00
handleCreateNew = () => {
const { history } = this.props;
history.push(API_EDITOR_URL);
2019-11-13 07:34:59 +00:00
this.setState({
isCreating: true,
name: "",
});
};
saveAction = () => {
if (this.state.name) {
this.props.createAction(this.state.name);
} else {
this.setState({
isCreating: false,
});
}
};
handleNameChange = (e: React.ChangeEvent<{ value: string }>) => {
const value = e.target.value;
this.setState({
name: value,
});
};
2019-11-14 09:01:23 +00:00
handleSearchChange = (e: React.ChangeEvent<{ value: string }>) => {
const value = e.target.value;
this.setState({
search: value,
});
};
2019-10-18 08:16:26 +00:00
render() {
2019-11-14 09:01:23 +00:00
const {
apiPane,
history,
match,
actions: { data },
} = this.props;
const { isCreating, search, name } = this.state;
2019-10-21 15:12:45 +00:00
const activeActionId = match.params.id;
2019-11-14 09:01:23 +00:00
const fuse = new Fuse(data, fuseOptions);
const actions = search ? fuse.search(search) : data;
2019-10-21 15:12:45 +00:00
return (
2019-11-13 11:34:11 +00:00
<React.Fragment>
{apiPane.isFetching ? (
<LoadingContainer>
<Spinner size={30} />
</LoadingContainer>
2019-11-13 07:34:59 +00:00
) : (
2019-11-13 11:34:11 +00:00
<ApiSidebarWrapper>
<ApiItemsWrapper>
2019-11-14 09:01:23 +00:00
<SearchBar
icon="search"
input={{
value: search,
onChange: this.handleSearchChange,
}}
placeholderMessage="Search"
/>
{actions.map(action => (
2019-11-13 11:34:11 +00:00
<ApiItem
key={action.id}
onClick={() => history.push(API_EDITOR_ID_URL(action.id))}
isSelected={activeActionId === action.id}
>
2019-11-14 09:01:23 +00:00
{action.actionConfiguration ? (
<HTTPMethod method={action.actionConfiguration.httpMethod}>
{action.actionConfiguration.httpMethod}
</HTTPMethod>
) : (
<HTTPMethod />
)}
2019-11-13 11:34:11 +00:00
<ActionName>{action.name}</ActionName>
</ApiItem>
))}
</ApiItemsWrapper>
{isCreating ? (
<CreateApiWrapper>
<BaseTextInput
placeholderMessage="API name"
input={{
2019-11-14 09:01:23 +00:00
value: name,
2019-11-13 11:34:11 +00:00
onChange: this.handleNameChange,
}}
/>
<BaseButton
icon={TICK}
styleName="primary"
text=""
onClick={this.saveAction}
filled
2019-11-14 09:01:23 +00:00
loading={apiPane.isSaving}
2019-11-13 11:34:11 +00:00
/>
</CreateApiWrapper>
) : (
<React.Fragment>
{!apiPane.isFetching && (
<CreateNewButton
text="Create new API"
icon={FormIcons.ADD_NEW_ICON()}
onClick={this.handleCreateNew}
/>
)}
</React.Fragment>
)}
</ApiSidebarWrapper>
2019-11-13 07:34:59 +00:00
)}
2019-11-13 11:34:11 +00:00
</React.Fragment>
2019-10-21 15:12:45 +00:00
);
2019-10-18 08:16:26 +00:00
}
}
2019-10-21 15:12:45 +00:00
const mapStateToProps = (state: AppState): ReduxStateProps => ({
actions: state.entities.actions,
2019-11-13 07:34:59 +00:00
apiPane: state.ui.apiPane,
});
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
2019-11-14 13:35:39 +00:00
createAction: (name: string) => dispatch(createActionRequest({ name })),
2019-10-21 15:12:45 +00:00
});
2019-11-13 07:34:59 +00:00
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ApiSidebar);