Merge branch 'feature/orchestration-using-applicationId' into 'release'

Use applicationId and pageId to render pages

The builder and viewer are orchestrated using `applicationId` and `pageId`

- Fixes #256 The application builder and application viewer both now work based on the `applicationId` and `pageId` in the URL.
- Fixes #177 There are placeholders and loading indications, now, in the application viewer.

See merge request theappsmith/internal-tools-client!151
This commit is contained in:
Hetu Nandu 2019-11-22 14:02:55 +00:00
commit a0a2cd46ac
54 changed files with 1550 additions and 1141 deletions

View File

@ -1,6 +1,6 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ["react", "@typescript-eslint", "prettier"],
plugins: ["react", "@typescript-eslint", "prettier", "react-hooks"],
extends: [
"plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
"plugin:@typescript-eslint/recommended",

View File

@ -24,7 +24,7 @@
"@types/react": "^16.8.2",
"@types/react-dom": "^16.8.0",
"@types/react-redux": "^7.0.1",
"@types/react-router-dom": "^4.3.5",
"@types/react-router-dom": "^5.1.2",
"@types/styled-components": "^4.1.8",
"@uppy/core": "^1.5.1",
"@uppy/file-input": "^1.3.1",
@ -65,8 +65,8 @@
"react-netlify-identity": "^0.1.9",
"react-redux": "^6.0.0",
"react-rnd": "^10.1.1",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-router": "^5.1.2",
"react-router-dom": "^5.1.2",
"react-scripts": "^3.1.1",
"react-select": "^3.0.8",
"redux": "^4.0.1",

View File

@ -1,7 +1,6 @@
import { ReduxActionTypes } from "../constants/ReduxActionConstants";
export type EditorConfigIdsType = {
propertyPaneConfigsId?: string;
widgetCardsPaneId?: string;
widgetConfigsId?: string;
};

View File

@ -1,8 +1,14 @@
import {
ReduxActionTypes,
ReduxActionWithoutPayload,
} from "../constants/ReduxActionConstants";
ReduxAction,
InitializeEditorPayload,
} from "constants/ReduxActionConstants";
export const initEditor = (): ReduxActionWithoutPayload => ({
export const initEditor = (
applicationId: string,
): ReduxAction<InitializeEditorPayload> => ({
type: ReduxActionTypes.INITIALIZE_EDITOR,
payload: {
applicationId,
},
});

View File

@ -7,21 +7,34 @@ import {
UpdateCanvasPayload,
SavePagePayload,
SavePageSuccessPayload,
FetchPageListPayload,
} from "../constants/ReduxActionConstants";
import { ContainerWidgetProps } from "../widgets/ContainerWidget";
export const fetchPageList = () => ({
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
});
export const fetchPageList = (
applicationId: string,
): ReduxAction<FetchPageListPayload> => {
return {
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
payload: {
applicationId,
},
};
};
export const fetchPage = (pageId: string): ReduxAction<FetchPageRequest> => {
return {
type: ReduxActionTypes.FETCH_PAGE,
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: {
pageId: pageId,
},
};
};
export const fetchPageSuccess = () => {
return {
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
};
};
export const addWidget = (
pageId: string,

View File

@ -11,7 +11,6 @@ export interface FetchPageRequest {
export interface FetchPublishedPageRequest {
pageId: string;
layoutId: string;
}
export interface SavePageRequest {
@ -70,8 +69,8 @@ class PageApi extends Api {
return `v1/layouts/${layoutId}/pages/${pageId}`;
};
static getPublishedPageURL = (pageId: string, layoutId: string) => {
return `v1/layouts/${layoutId}/pages/${pageId}/view`;
static getPublishedPageURL = (pageId: string) => {
return `v1/pages/${pageId}/view`;
};
static fetchPage(
@ -97,9 +96,7 @@ class PageApi extends Api {
static fetchPublishedPage(
pageRequest: FetchPublishedPageRequest,
): AxiosPromise<FetchPublishedPageResponse> {
return Api.get(
PageApi.getPublishedPageURL(pageRequest.pageId, pageRequest.layoutId),
);
return Api.get(PageApi.getPublishedPageURL(pageRequest.pageId));
}
static createPage(
@ -108,8 +105,10 @@ class PageApi extends Api {
return Api.post(PageApi.url, createPageRequest);
}
static fetchPageList(): AxiosPromise<FetchPageListResponse> {
return Api.get(PageApi.url);
static fetchPageList(
applicationId: string,
): AxiosPromise<FetchPageListResponse> {
return Api.get(PageApi.url + "/application/" + applicationId);
}
}

View File

@ -1,3 +1,3 @@
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 6.001C10 6.3 9.695 6.515 9.695 6.515L1.134 11.818C0.51 12.227 0 11.924 0 11.149V0.852C0 0.0749997 0.51 -0.226 1.135 0.182L9.696 5.487C9.695 5.487 10 5.702 10 6.001Z" fill="#A3B3BF"/>
<path d="M10 6.00102C10 6.30001 9.695 6.51502 9.695 6.51502L1.134 11.818C0.51 12.227 0 11.924 0 11.149V0.852015C0 0.0750149 0.51 -0.225985 1.135 0.182015L9.696 5.48702C9.695 5.48702 10 5.70202 10 6.00102Z" fill="#A3B3BF"/>
</svg>

Before

Width:  |  Height:  |  Size: 299 B

After

Width:  |  Height:  |  Size: 326 B

View File

@ -0,0 +1,9 @@
import styled from "styled-components";
export default styled.div`
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
`;

View File

@ -38,7 +38,7 @@ ContainerComponent.defaultProps = {
backgroundColor: "white",
};
type ContainerStyle = "border" | "card" | "rounded-border" | "none";
export type ContainerStyle = "border" | "card" | "rounded-border" | "none";
export interface ContainerComponentProps extends ComponentProps {
containerStyle?: ContainerStyle;

View File

@ -29,6 +29,7 @@ type ContextDropdownProps = {
toggle: {
type: "icon" | "button";
icon?: ControlIconName;
iconSize?: number;
text?: string;
placeholder?: string;
};
@ -37,7 +38,10 @@ type ContextDropdownProps = {
export const ContextDropdown = (props: ContextDropdownProps) => {
let trigger: ReactNode;
if (props.toggle.type === "icon" && props.toggle.icon)
trigger = ControlIcons[props.toggle.icon]();
trigger = ControlIcons[props.toggle.icon]({
width: props.toggle.iconSize,
height: props.toggle.iconSize,
});
if (props.toggle.type === "button" && props.toggle.text)
trigger = <Button text={props.toggle.text} />;

View File

@ -0,0 +1,37 @@
import React from "react";
import { Text, ProgressBar } from "@blueprintjs/core";
import styled from "styled-components";
const PageLoaderWrapper = styled.div`
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
background: white;
& > div {
width: 100px;
height: 100px;
text-align: center;
}
`;
type PageLoaderProps = {
value?: number;
};
export const PageLoader = (props: PageLoaderProps) => {
return (
<PageLoaderWrapper>
<div>
<ProgressBar value={props.value ? props.value / 100 : undefined} />
<Text>Loading</Text>
</div>
</PageLoaderWrapper>
);
};
export default PageLoader;

View File

@ -5,31 +5,27 @@ import {
API_EDITOR_URL,
BUILDER_URL,
API_EDITOR_ID_URL,
} from "../../constants/routes";
import WidgetSidebar from "../../pages/Editor/WidgetSidebar";
import ApiSidebar from "../../pages/Editor/ApiSidebar";
} from "constants/routes";
import WidgetSidebar from "pages/Editor/WidgetSidebar";
import ApiSidebar from "pages/Editor/ApiSidebar";
const SidebarWrapper = styled.div`
background-color: ${props => props.theme.colors.paneBG};
padding: 5px 10px;
color: ${props => props.theme.colors.textOnDarkBG};
overflow-y: scroll;
overflow-y: auto;
`;
class Sidebar extends React.Component {
render() {
return (
<React.Fragment>
<SidebarWrapper>
<Switch>
<Route exact path={BUILDER_URL} component={WidgetSidebar} />
<Route exact path={API_EDITOR_URL} component={ApiSidebar} />
<Route exact path={API_EDITOR_ID_URL()} component={ApiSidebar} />
</Switch>
</SidebarWrapper>
</React.Fragment>
);
}
}
export const Sidebar = () => {
return (
<SidebarWrapper>
<Switch>
<Route exact path={BUILDER_URL} component={WidgetSidebar} />
<Route exact path={API_EDITOR_URL()} component={ApiSidebar} />
<Route exact path={API_EDITOR_ID_URL()} component={ApiSidebar} />
</Switch>
</SidebarWrapper>
);
};
export default Sidebar;

View File

@ -64,7 +64,4 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(DatasourcesField);
export default connect(mapStateToProps, mapDispatchToProps)(DatasourcesField);

View File

@ -29,21 +29,3 @@ export interface APIHeaders {
export interface APIRequest {
requestId?: string;
}
export const getEditorConfigs = () => {
if (process.env.NODE_ENV === "development") {
return {
currentPageId: "5dcab609312463000408b92b",
currentLayoutId: "5dcab609312463000408b92a",
currentPageName: "page1",
propertyPaneConfigsId: "5d8a04195cf8050004db6e30",
};
} else {
return {
currentPageId: "5dcab609312463000408b92b",
currentLayoutId: "5dcab609312463000408b92a",
currentPageName: "page1",
propertyPaneConfigsId: "5d8a04195cf8050004db6e30",
};
}
};

View File

@ -33,4 +33,4 @@ export const Colors: Record<string, string> = {
CADET_BLUE: "#A3B3BF",
};
export type Color = (typeof Colors)[keyof typeof Colors];
export type Color = typeof Colors[keyof typeof Colors];

View File

@ -3,7 +3,7 @@ import { Colors, Color } from "./Colors";
import * as FontFamilies from "./Fonts";
import _ from "lodash";
export type FontFamily = (typeof FontFamilies)[keyof typeof FontFamilies];
export type FontFamily = typeof FontFamilies[keyof typeof FontFamilies];
const {
default: styled,

View File

@ -9,7 +9,8 @@ export const ReduxActionTypes: { [key: string]: string } = {
UPDATE_CANVAS: "UPDATE_CANVAS",
FETCH_CANVAS: "FETCH_CANVAS",
CLEAR_CANVAS: "CLEAR_CANVAS",
FETCH_PAGE: "FETCH_PAGE",
FETCH_PAGE_INIT: "FETCH_PAGE_INIT",
FETCH_PAGE_SUCCESS: "FETCH_PAGE_SUCCESS",
DROP_WIDGET_CANVAS: "DROP_WIDGET_CANVAS",
REMOVE_WIDGET_CANVAS: "REMOVE_WIDGET_CANVAS",
LOAD_WIDGET_PANE: "LOAD_WIDGET_PANE",
@ -83,10 +84,10 @@ export const ReduxActionTypes: { [key: string]: string } = {
HIDE_PROPERTY_PANE: "HIDE_PROPERTY_PANE",
};
export type ReduxActionType = (typeof ReduxActionTypes)[keyof typeof ReduxActionTypes];
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];
export const ReduxActionErrorTypes: { [key: string]: string } = {
INIT_EDITOR_ERROR: "INIT_EDITOR_ERROR",
INITIALIZE_EDITOR_ERROR: "INITIALIZE_EDITOR_ERROR",
API_ERROR: "API_ERROR",
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
WIDGET_MOVE_ERROR: "WIDGET_MOVE_ERROR",
@ -117,7 +118,7 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
CREATE_APPLICATION_ERROR: "CREATE_APPLICATION_ERROR",
};
export type ReduxActionErrorType = (typeof ReduxActionErrorTypes)[keyof typeof ReduxActionErrorTypes];
export type ReduxActionErrorType = typeof ReduxActionErrorTypes[keyof typeof ReduxActionErrorTypes];
export interface ReduxAction<T> {
type: ReduxActionType | ReduxActionErrorType;
@ -149,7 +150,6 @@ export interface ShowPropertyPanePayload {
export type PageListPayload = Array<{
pageName: string;
pageId: string;
layoutId: string;
}>;
export type ApplicationPayload = {
@ -175,3 +175,9 @@ export interface LoadWidgetSidebarPayload {
export type SavePagePayload = {};
export type SavePageErrorPayload = {};
export type SavePageSuccessPayload = {};
export type InitializeEditorPayload = {
applicationId: string;
};
export type FetchPageListPayload = InitializeEditorPayload;

View File

@ -14,5 +14,5 @@ export type ValidationResponse = {
parsed: any;
};
export type ValidationType = (typeof VALIDATION_TYPES)[keyof typeof VALIDATION_TYPES];
export type ValidationType = typeof VALIDATION_TYPES[keyof typeof VALIDATION_TYPES];
export type Validator = (value: any) => ValidationResponse;

View File

@ -1,26 +1,62 @@
import { MenuIcons } from "../icons/MenuIcons";
export const BASE_URL = "/";
export const LOGIN_URL = "/login";
export const BUILDER_URL = "/builder";
export const API_EDITOR_URL = `${BUILDER_URL}/api`;
export const API_EDITOR_ID_URL = (id = ":id") => `${API_EDITOR_URL}/${id}`;
export const APP_VIEW_URL = `/view/pages/:pageId`;
export const APPLICATIONS_URL = `/applications`;
export const BUILDER_URL = "/applications/:applicationId/pages/:pageId/edit";
// TODO(abhinav): We probably need a utils/routes file for such functions.
export const getApplicationBuilderURL = (applicationId: string) =>
`${BUILDER_URL}/${applicationId}`;
export type BuilderRouteParams = {
applicationId: string;
pageId: string;
};
export type AppViewerRouteParams = {
applicationId?: string;
pageId?: string;
};
export type APIEditorRouteParams = {
applicationId: string;
pageId: string;
apiId?: string;
};
export const BUILDER_BASE_URL = (applicationId = ":applicationId"): string =>
`/applications/${applicationId}`;
export const BUILDER_PAGE_URL = (
applicationId?: string,
pageId?: string,
): string => {
if (!pageId) return APPLICATIONS_URL;
return `${BUILDER_BASE_URL(applicationId)}/pages/${pageId}/edit`;
};
export const API_EDITOR_URL = (
applicationId = ":applicationId",
pageId = ":pageId",
): string => `${BUILDER_PAGE_URL(applicationId, pageId)}/api`;
export const API_EDITOR_ID_URL = (
applicationId = ":applicationId",
pageId = ":pageId",
apiId = ":apiId",
): string => `${API_EDITOR_URL(applicationId, pageId)}/${apiId}`;
export const APP_VIEW_URL = `/applications/:applicationId`;
export const getApplicationViewerURL = (
applicationId: string,
pageId?: string,
) => `/view/application/${applicationId}/pages/${pageId}`;
applicationId = ":applicationId",
): string => `/applications/${applicationId}`;
export const getApplicationViewerPageURL = (
applicationId = ":applicationId",
pageId = ":pageId",
): string => `/applications/${applicationId}/pages/${pageId}`;
export const EDITOR_ROUTES = [
{
icon: MenuIcons.WIDGETS_ICON,
path: BUILDER_URL,
path: BUILDER_PAGE_URL,
title: "Widgets",
exact: true,
},

View File

@ -49,7 +49,11 @@ ReactDOM.render(
<Route exact path={BASE_URL} component={App} />
<ProtectedRoute path={BUILDER_URL} component={Editor} />
<ProtectedRoute path={APP_VIEW_URL} component={AppViewer} />
<ProtectedRoute path={APPLICATIONS_URL} component={Applications} />
<ProtectedRoute
exact
path={APPLICATIONS_URL}
component={Applications}
/>
<Route exact path={LOGIN_URL} component={LoginPage} />
<Route component={PageNotFound} />
</Switch>

View File

@ -0,0 +1,95 @@
import React, { Component } from "react";
import { RouteComponentProps, Link } from "react-router-dom";
import { connect } from "react-redux";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import {
getIsFetchingPage,
getCurrentPageLayoutDSL,
} from "selectors/appViewSelectors";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import { WidgetProps } from "widgets/BaseWidget";
import { AppViewerRouteParams } from "constants/routes";
import { AppState } from "reducers";
import { theme } from "constants/DefaultTheme";
import { NonIdealState, Icon, Spinner } from "@blueprintjs/core";
import Centered from "components/designSystems/appsmith/CenteredWrapper";
import AppPage from "./AppPage";
type AppViewerPageContainerProps = {
isFetchingPage: boolean;
dsl?: ContainerWidgetProps<WidgetProps>;
fetchPage: (pageId: string) => void;
} & RouteComponentProps<AppViewerRouteParams>;
class AppViewerPageContainer extends Component<AppViewerPageContainerProps> {
componentDidMount() {
const { pageId } = this.props.match.params;
if (pageId) {
this.props.fetchPage(pageId);
}
}
componentDidUpdate(previously: AppViewerPageContainerProps) {
const { pageId } = this.props.match.params;
if (
pageId &&
previously.location.pathname !== this.props.location.pathname
) {
this.props.fetchPage(pageId);
}
}
render() {
const pageNotFound = (
<Centered>
<NonIdealState
icon={
<Icon
iconSize={theme.fontSizes[9]}
icon="page-layout"
color={theme.colors.primary}
/>
}
title="This page seems to be blank"
description={
<p>
Please add widgets to this page in the
<Link to="/builder"> Appsmith Editor</Link>
</p>
}
/>
</Centered>
);
const pageLoading = (
<Centered>
<Spinner />
</Centered>
);
if (this.props.isFetchingPage) {
return pageLoading;
} else if (!this.props.isFetchingPage && !this.props.dsl) {
return pageNotFound;
} else if (!this.props.isFetchingPage && this.props.dsl) {
return <AppPage dsl={this.props.dsl} />;
}
}
}
const mapStateToProps = (state: AppState) => ({
isFetchingPage: getIsFetchingPage(state),
dsl: getCurrentPageLayoutDSL(state),
});
const mapDispatchToProps = (dispatch: any) => ({
fetchPage: (pageId: string) =>
dispatch({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
payload: {
pageId,
},
}),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(AppViewerPageContainer);

View File

@ -1,32 +1,31 @@
import React, { Component } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import { withRouter, RouteComponentProps } from "react-router";
import { Switch, Route } from "react-router-dom";
import { AppState } from "../../reducers";
import {
AppViewerRouteParams,
getApplicationViewerPageURL,
} from "constants/routes";
import {
ReduxActionTypes,
PageListPayload,
} from "../../constants/ReduxActionConstants";
import {
getCurrentRoutePageId,
getCurrentPageLayoutDSL,
getPageList,
getIsFetchingPage,
getCurrentDSLPageId,
} from "../../selectors/appViewSelectors";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import { WidgetProps } from "../../widgets/BaseWidget";
import { executeAction } from "../../actions/widgetActions";
import { ActionPayload } from "../../constants/ActionConstants";
import AppPage from "./AppPage";
import { Spinner, NonIdealState, Icon } from "@blueprintjs/core";
import { Link } from "react-router-dom";
import { theme } from "../../constants/DefaultTheme";
import SideNav, { SideNavItem } from "./viewer/SideNav";
import AppViewerHeader from "./viewer/AppViewerHeader";
import { updateWidgetProperty } from "../../actions/controlActions";
import { RenderModes } from "../../constants/WidgetConstants";
import { EditorContext } from "components/editorComponents/EditorContextProvider";
import AppViewerPageContainer from "./AppViewerPageContainer";
import AppViewerSideNavWrapper from "./viewer/AppViewerSideNavWrapper";
const AppViewWrapper = styled.div`
margin-top: ${props => props.theme.headerHeight};
@ -39,57 +38,13 @@ const AppViewerBody = styled.section`
justify-content: flex-start;
height: calc(100vh - ${props => props.theme.headerHeight});
`;
const Centered = styled.div`
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
`;
const SideNavWrapper = styled.div`
background: ${props => props.theme.colors.paneBG};
& button.sidenav-toggle,
& button.sidenav-toggle:hover,
& button.sidenav-toggle:active {
background: ${props => props.theme.colors.paneBG};
outline: none;
border: none;
border-radius: 0;
}
& ul {
background: ${props => props.theme.colors.paneBG};
color: ${props => props.theme.colors.textOnDarkBG};
padding: 0;
height: 100%;
width: 100%;
& li {
padding: 0;
}
& li div.bp3-menu-item {
width: 100%;
font-size: ${props => props.theme.fontSizes[3]}px;
&.bp3-intent-primary {
background: ${props => props.theme.sideNav.activeItemBGColor};
}
& div {
line-height: ${props => props.theme.lineHeights[6]}px;
}
}
}
`;
export type AppViewerProps = {
currentRoutePageId?: string;
currentDSLPageId?: string;
currentLayoutId?: string;
fetchPageWidgets: Function;
pages?: PageListPayload;
dsl?: ContainerWidgetProps<WidgetProps>;
initializeAppViewer: Function;
isFetching: boolean;
match: any;
location: any;
history: any;
executeAction: (actionPayloads?: ActionPayload[]) => void;
updateWidgetProperty: (
widgetId: string,
@ -98,25 +53,15 @@ export type AppViewerProps = {
) => void;
};
class AppViewer extends Component<AppViewerProps> {
handlePageSelect = (item: SideNavItem) => {
this.props.fetchPageWidgets(item.id);
this.props.history.push(`/view/pages/${item.id}`);
};
class AppViewer extends Component<
AppViewerProps & RouteComponentProps<AppViewerRouteParams>
> {
componentDidMount() {
this.props.initializeAppViewer(this.props.currentRoutePageId);
}
componentDidUpdate(prevProps: AppViewerProps) {
if (
prevProps.currentRoutePageId !== this.props.currentRoutePageId &&
this.props.currentDSLPageId !== this.props.currentRoutePageId
) {
this.props.fetchPageWidgets(this.props.currentRoutePageId);
const { applicationId } = this.props.match.params;
if (this.props.match.params.applicationId) {
this.props.initializeAppViewer(applicationId);
}
}
public render() {
const items: SideNavItem[] | undefined =
this.props.pages &&
@ -124,13 +69,12 @@ class AppViewer extends Component<AppViewerProps> {
text: page.pageName,
id: page.pageId,
icon: "page-layout", //TODO: get the icon from page.
path: getApplicationViewerPageURL(
this.props.match.params.applicationId,
page.pageId,
),
}));
const currentPage =
this.props.pages &&
this.props.pages.find(
page => page.pageId === this.props.currentRoutePageId,
);
return (
<EditorContext.Provider
value={{
@ -141,47 +85,20 @@ class AppViewer extends Component<AppViewerProps> {
<AppViewWrapper>
<AppViewerHeader />
<AppViewerBody>
{items && (
<SideNavWrapper>
<SideNav
items={items}
onSelect={this.handlePageSelect}
iconSize={24}
active={
currentPage && {
text: currentPage.pageName,
id: currentPage.pageId,
}
}
/>
</SideNavWrapper>
)}
{this.props.isFetching && (
<Centered>
<Spinner />
</Centered>
)}
{!this.props.isFetching && !this.props.dsl && items && (
<Centered>
<NonIdealState
icon={
<Icon
iconSize={theme.fontSizes[9]}
icon="page-layout"
color={theme.colors.primary}
/>
}
title="This page seems to be blank"
description={
<p>
Please add widgets to this page in the
<Link to="/builder"> Appsmith Editor</Link>
</p>
}
/>
</Centered>
)}
{this.props.dsl && <AppPage dsl={this.props.dsl} />}
<AppViewerSideNavWrapper>
<SideNav
items={items}
iconSize={24}
active={this.props.currentDSLPageId}
/>
</AppViewerSideNavWrapper>
<Switch>
<Route
path={getApplicationViewerPageURL()}
exact
component={AppViewerPageContainer}
/>
</Switch>
</AppViewerBody>
</AppViewWrapper>
</EditorContext.Provider>
@ -189,10 +106,8 @@ class AppViewer extends Component<AppViewerProps> {
}
}
const mapStateToProps = (state: AppState, props: AppViewerProps) => ({
currentRoutePageId: getCurrentRoutePageId(state, props),
const mapStateToProps = (state: AppState) => ({
currentDSLPageId: getCurrentDSLPageId(state),
dsl: getCurrentPageLayoutDSL(state),
pages: getPageList(state),
isFetching: getIsFetchingPage(state),
});
@ -213,24 +128,13 @@ const mapDispatchToProps = (dispatch: any) => ({
RenderModes.PAGE,
),
),
fetchPageWidgets: (pageId: string) => {
dispatch({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
payload: {
pageId,
},
});
},
initializeAppViewer: (pageId: string) =>
initializeAppViewer: (applicationId: string) =>
dispatch({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
payload: { pageId },
payload: { applicationId },
}),
});
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(AppViewer),
connect(mapStateToProps, mapDispatchToProps)(AppViewer),
);

View File

@ -0,0 +1,32 @@
import styled from "styled-components";
export default styled.div`
background: ${props => props.theme.colors.paneBG};
& button.sidenav-toggle,
& button.sidenav-toggle:hover,
& button.sidenav-toggle:active {
background: ${props => props.theme.colors.paneBG};
outline: none;
border: none;
border-radius: 0;
}
& ul {
background: ${props => props.theme.colors.paneBG};
color: ${props => props.theme.colors.textOnDarkBG};
padding: 0;
height: 100%;
width: 100%;
& li {
padding: 0;
}
& li div.bp3-menu-item {
width: 100%;
font-size: ${props => props.theme.fontSizes[3]}px;
&.bp3-intent-primary {
background: ${props => props.theme.sideNav.activeItemBGColor};
}
& div {
line-height: ${props => props.theme.lineHeights[6]}px;
}
}
}
`;

View File

@ -1,17 +1,25 @@
import React, { useState } from "react";
import styled from "styled-components";
import { Menu, MenuItem, IconName, Button, Icon } from "@blueprintjs/core";
import {
Menu,
MenuItem,
IconName,
Button,
Icon,
Classes,
} from "@blueprintjs/core";
import { Link } from "react-router-dom";
export type SideNavItem = {
id: string;
icon?: string;
text: string;
path: string;
};
type SideNavProps = {
items: SideNavItem[];
active?: SideNavItem;
onSelect: Function;
items?: SideNavItem[];
active?: string;
headeroffset?: number;
iconSize?: number;
};
@ -44,6 +52,7 @@ const SideNavWrapper = styled.div<{
flex-grow: 0;
display: inline;
width: ${props => (props.open ? 100 : 0)}px;
color: ${props => props.theme.sideNav.fontColor};
}
& > span {
margin-right: ${props => (props.open ? props.theme.spaces[3] : 0)}px;
@ -70,23 +79,29 @@ const ToggleButton = styled(Button)<{
export const SideNav = (props: SideNavProps) => {
const [open, setopen] = useState(true);
const select = (item: SideNavItem) => () => {
props.onSelect(item);
};
const renderItems = (items: SideNavItem[]) => {
const renderItems = (sideNavItems?: SideNavItem[]) => {
let items = sideNavItems;
if (!items) {
items = [
{ id: "0", text: "", path: "" },
{ id: "1", text: "", path: "" },
{ id: "2", text: "", path: "" },
];
}
return items.map(item => {
const icon = (
<Icon iconSize={props.iconSize} icon={item.icon as IconName} />
);
return (
<MenuItem
icon={icon}
active={props.active && item.id === props.active.id}
key={item.id}
onClick={select(item)}
text={open ? item.text : undefined}
tagName="div"
/>
<Link to={item.path} key={item.id}>
<MenuItem
className={!sideNavItems ? Classes.SKELETON : undefined}
icon={icon}
active={props.active === item.id}
text={open ? item.text : undefined}
tagName="div"
/>
</Link>
);
});
};

View File

@ -1,5 +1,10 @@
import React from "react";
import styled from "styled-components";
import { Link } from "react-router-dom";
import {
getApplicationViewerPageURL,
BUILDER_PAGE_URL,
} from "constants/routes";
import { Card, Tooltip, Classes } from "@blueprintjs/core";
import { ApplicationPayload } from "constants/ReduxActionConstants";
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
@ -107,8 +112,9 @@ const Control = styled.button<{ fixed?: boolean }>`
const APPLICATION_CONTROL_FONTSIZE_INDEX = 6;
const viewControlIcon = ControlIcons.VIEW_CONTROL({
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
width: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX - 1],
height: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX - 1],
color: theme.colors.secondary,
});
type ApplicationCardProps = {
@ -150,33 +156,49 @@ export const ApplicationCard = (props: ApplicationCardProps) => {
intent: "danger",
},
];
const viewApplicationURL = getApplicationViewerPageURL(
props.application.id,
props.application.defaultPageId,
);
const editApplicationURL = BUILDER_PAGE_URL(
props.application.id,
props.application.defaultPageId,
);
return (
<Wrapper key={props.application.id}>
<ApplicationTitle
className={props.loading ? Classes.SKELETON : undefined}
>
<span>{props.application.name}</span>
<Control className="control">
<Tooltip content="View Application" hoverOpenDelay={500}>
{viewControlIcon}
</Tooltip>
</Control>
<Link to={viewApplicationURL}>
<Control className="control">
<Tooltip content="View Application" hoverOpenDelay={500}>
{viewControlIcon}
</Tooltip>
</Control>
</Link>
<ContextDropdown
options={moreActionItems}
toggle={{ type: "icon", icon: "MORE_VERTICAL_CONTROL" }}
toggle={{
type: "icon",
icon: "MORE_VERTICAL_CONTROL",
iconSize: theme.fontSizes[APPLICATION_CONTROL_FONTSIZE_INDEX],
}}
className="more"
/>
</ApplicationTitle>
<ApplicationImage className="image-container">
<Control className="control">
<Tooltip content="Edit Application" hoverOpenDelay={500}>
<BaseButton
icon="edit"
text="Edit"
styleName="primary"
filled={true}
/>
</Tooltip>
<Link to={editApplicationURL}>
<Tooltip content="Edit Application" hoverOpenDelay={500}>
<BaseButton
icon="edit"
text="Edit"
styleName="primary"
filled={true}
/>
</Tooltip>
</Link>
</Control>
</ApplicationImage>
</Wrapper>

View File

@ -1,7 +1,6 @@
import React, { Component } from "react";
import styled from "styled-components";
import { connect } from "react-redux";
import { withRouter } from "react-router";
import { AppState } from "../../reducers";
import {
getApplicationList,
@ -54,7 +53,6 @@ type ApplicationProps = {
isCreatingApplication: boolean;
isFetchingApplications: boolean;
createApplicationError?: string;
history: any;
};
class Applications extends Component<ApplicationProps> {
@ -86,16 +84,20 @@ class Applications extends Component<ApplicationProps> {
/>
<SectionDivider />
<ApplicationCardsWrapper>
{applicationList.map((application: ApplicationPayload) => (
<ApplicationCard
key={application.id}
loading={this.props.isFetchingApplications}
application={application}
share={noop}
duplicate={noop}
delete={noop}
/>
))}
{applicationList.map((application: ApplicationPayload) => {
return (
application.pageCount > 0 && (
<ApplicationCard
key={application.id}
loading={this.props.isFetchingApplications}
application={application}
share={noop}
duplicate={noop}
delete={noop}
/>
)
);
})}
</ApplicationCardsWrapper>
</ApplicationsBody>
</ApplicationsPageWrapper>
@ -123,9 +125,4 @@ const mapDispatchToProps = (dispatch: any) => ({
},
});
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(Applications),
);
export default connect(mapStateToProps, mapDispatchToProps)(Applications);

View File

@ -35,7 +35,7 @@ interface ReduxActionProps {
type Props = ReduxActionProps &
ReduxStateProps &
RouteComponentProps<{ id: string }>;
RouteComponentProps<{ apiId: string; applicationId: string; pageId: string }>;
const EmptyStateContainer = styled.div`
display: flex;
@ -47,21 +47,26 @@ const EmptyStateContainer = styled.div`
class ApiEditor extends React.Component<Props> {
componentDidMount(): void {
const currentId = this.props.match.params.id;
if (!currentId) return;
const currentApiId = this.props.match.params.apiId;
const currentApplicationId = this.props.match.params.applicationId;
const currentPageId = this.props.match.params.pageId;
if (!currentApiId) return;
if (!this.props.actions.data.length) {
this.props.history.push(API_EDITOR_URL);
this.props.history.push(
API_EDITOR_URL(currentApplicationId, currentPageId),
);
return;
}
const data = this.props.actions.data.filter(
action => action.id === currentId,
action => action.id === currentApiId,
)[0];
this.props.initialize(API_EDITOR_FORM_NAME, data);
}
componentDidUpdate(prevProps: Readonly<Props>): void {
const currentId = this.props.match.params.id;
if (currentId && currentId !== prevProps.match.params.id) {
const currentId = this.props.match.params.apiId;
if (currentId && currentId !== prevProps.match.params.apiId) {
const data = this.props.actions.data.filter(
action => action.id === currentId,
)[0];
@ -90,7 +95,7 @@ class ApiEditor extends React.Component<Props> {
this.props.submitForm(API_EDITOR_FORM_NAME);
};
handleDeleteClick = () => {
this.props.deleteAction(this.props.match.params.id);
this.props.deleteAction(this.props.match.params.apiId);
};
handleRunClick = () => {
this.props.runAction();
@ -100,12 +105,12 @@ class ApiEditor extends React.Component<Props> {
const {
apiPane: { isSaving, isRunning, isDeleting },
match: {
params: { id },
params: { apiId },
},
} = this.props;
return (
<React.Fragment>
{id ? (
{apiId ? (
<ApiEditorForm
isSaving={isSaving}
isRunning={isRunning}
@ -142,7 +147,4 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
destroy: (formName: string) => dispatch(destroy(formName)),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ApiEditor);
export default connect(mapStateToProps, mapDispatchToProps)(ApiEditor);

View File

@ -4,7 +4,11 @@ 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";
import {
API_EDITOR_ID_URL,
API_EDITOR_URL,
APIEditorRouteParams,
} from "../../constants/routes";
import { BaseButton } from "../../components/designSystems/blueprint/ButtonComponent";
import { FormIcons } from "../../icons/FormIcons";
import { Spinner } from "@blueprintjs/core";
@ -12,6 +16,7 @@ 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";
import { RestAction } from "api/ActionAPI";
import Fuse from "fuse.js";
const LoadingContainer = styled.div`
@ -134,7 +139,7 @@ interface ReduxDispatchProps {
type Props = ReduxStateProps &
ReduxDispatchProps &
RouteComponentProps<{ id: string }>;
RouteComponentProps<APIEditorRouteParams>;
type State = {
isCreating: boolean;
name: string;
@ -160,7 +165,7 @@ class ApiSidebar extends React.Component<Props, State> {
}
componentDidUpdate(prevProps: Readonly<Props>): void {
if (!prevProps.match.params.id && this.props.match.params.id) {
if (!prevProps.match.params.apiId && this.props.match.params.apiId) {
this.setState({
isCreating: false,
name: "",
@ -170,7 +175,9 @@ class ApiSidebar extends React.Component<Props, State> {
handleCreateNew = () => {
const { history } = this.props;
history.push(API_EDITOR_URL);
const { pageId, applicationId } = this.props.match.params;
history.push(API_EDITOR_URL(applicationId, pageId));
this.setState({
isCreating: true,
name: "",
@ -202,6 +209,7 @@ class ApiSidebar extends React.Component<Props, State> {
};
render() {
const { applicationId, pageId } = this.props.match.params;
const {
apiPane,
history,
@ -209,9 +217,9 @@ class ApiSidebar extends React.Component<Props, State> {
actions: { data },
} = this.props;
const { isCreating, search, name } = this.state;
const activeActionId = match.params.id;
const activeActionId = match.params.apiId;
const fuse = new Fuse(data, fuseOptions);
const actions = search ? fuse.search(search) : data;
const actions: RestAction[] = search ? fuse.search(search) : data;
return (
<React.Fragment>
{apiPane.isFetching ? (
@ -232,7 +240,11 @@ class ApiSidebar extends React.Component<Props, State> {
{actions.map(action => (
<ApiItem
key={action.id}
onClick={() => history.push(API_EDITOR_ID_URL(action.id))}
onClick={() =>
history.push(
API_EDITOR_ID_URL(applicationId, pageId, action.id),
)
}
isSelected={activeActionId === action.id}
>
{action.actionConfiguration ? (
@ -291,7 +303,4 @@ const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
createAction: (name: string) => dispatch(createActionRequest({ name })),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ApiSidebar);
export default connect(mapStateToProps, mapDispatchToProps)(ApiSidebar);

View File

@ -39,11 +39,11 @@ const StretchedBreadCrumb = styled(Breadcrumbs)`
type EditorHeaderProps = {
isSaving?: boolean;
pageName: string;
pageName?: string;
onPublish: React.FormEventHandler;
onCreatePage: (name: string) => void;
pages?: PageListPayload;
currentPageId: string;
currentPageId?: string;
switchToPage: (selectedPage: string) => void;
isPublishing: boolean;
};

View File

@ -197,7 +197,4 @@ export interface PropertyPaneFunctions {
hidePropertyPane: () => void;
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(PropertyPane);
export default connect(mapStateToProps, mapDispatchToProps)(PropertyPane);

View File

@ -1,8 +1,9 @@
import React from "react";
import { useParams } from "react-router-dom";
import styled from "styled-components";
import SidebarComponent from "../../components/editorComponents/Sidebar";
import NavBarItem from "../../components/editorComponents/NavBarItem";
import { EDITOR_ROUTES } from "../../constants/routes";
import { EDITOR_ROUTES, BuilderRouteParams } from "../../constants/routes";
const Wrapper = styled.div`
display: grid;
@ -23,11 +24,16 @@ const EditorSidebar = styled(SidebarComponent)`
`;
const Sidebar = () => {
const params = useParams<BuilderRouteParams>();
return (
<Wrapper>
<NavBar>
{EDITOR_ROUTES.map(config => (
<NavBarItem key={config.path} {...config} />
<NavBarItem
key={config.title}
{...config}
path={config.path(params.applicationId, params.pageId)}
/>
))}
</NavBar>
<EditorSidebar />

View File

@ -1,16 +1,23 @@
import React from "react";
import React, { useEffect, ReactNode } from "react";
import { connect } from "react-redux";
import { useParams } from "react-router-dom";
import styled from "styled-components";
import Canvas from "./Canvas";
import PropertyPane from "./PropertyPane";
import { AppState } from "../../reducers";
import { WidgetProps } from "../../widgets/BaseWidget";
import { savePage } from "../../actions/pageActions";
import { getDenormalizedDSL } from "../../selectors/editorSelectors";
import {
getDenormalizedDSL,
getIsFetchingPage,
getCurrentPageId,
} from "selectors/editorSelectors";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
import { BuilderRouteParams } from "constants/routes";
import Centered from "components/designSystems/appsmith/CenteredWrapper";
import EditorContextProvider from "components/editorComponents/EditorContextProvider";
import { Spinner } from "@blueprintjs/core";
const EditorWrapper = styled.div`
display: flex;
@ -38,31 +45,56 @@ const CanvasContainer = styled.section`
`;
type EditorProps = {
dsl: ContainerWidgetProps<WidgetProps> | any;
dsl?: ContainerWidgetProps<WidgetProps>;
savePageLayout: Function;
showPropertyPane: (
widgetId?: string,
node?: HTMLDivElement,
toggle?: boolean,
) => void;
fetchPage: (pageId: string) => void;
currentPageId?: string;
isFetchingPage: boolean;
};
const WidgetsEditor = (props: EditorProps) => (
<EditorContextProvider>
<EditorWrapper>
<CanvasContainer>
{props.dsl && (
<Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />
)}
</CanvasContainer>
<PropertyPane />
</EditorWrapper>
</EditorContextProvider>
);
const WidgetsEditor = (props: EditorProps) => {
const params = useParams<BuilderRouteParams>();
const { pageId } = params;
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
if (pageId !== props.currentPageId) {
props.fetchPage(pageId);
}
}, [pageId]);
const pageLoading = (
<Centered>
<Spinner />
</Centered>
);
let node: ReactNode;
if (props.isFetchingPage) {
node = pageLoading;
}
if (!props.isFetchingPage && props.dsl) {
node = <Canvas dsl={props.dsl} showPropertyPane={props.showPropertyPane} />;
}
return (
<EditorContextProvider>
<EditorWrapper>
<CanvasContainer>{node}</CanvasContainer>
<PropertyPane />
</EditorWrapper>
</EditorContextProvider>
);
};
const mapStateToProps = (state: AppState) => {
return {
dsl: getDenormalizedDSL(state),
isFetchingPage: getIsFetchingPage(state),
currentPageId: getCurrentPageId(state),
};
};
@ -83,10 +115,12 @@ const mapDispatchToProps = (dispatch: any) => {
payload: { widgetId, node, toggle },
});
},
fetchPage: (pageId: string) =>
dispatch({
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: { pageId },
}),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(WidgetsEditor);
export default connect(mapStateToProps, mapDispatchToProps)(WidgetsEditor);

View File

@ -1,5 +1,12 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";
import { withRouter, RouteComponentProps } from "react-router-dom";
import {
BuilderRouteParams,
getApplicationViewerPageURL,
BUILDER_PAGE_URL,
} from "constants/routes";
import { AppState } from "../../reducers";
import EditorHeader from "./EditorHeader";
import MainContainer from "./MainContainer";
@ -11,20 +18,23 @@ import {
getIsPublishingApplication,
getPublishingError,
getIsPageSaving,
} from "../../selectors/editorSelectors";
getIsEditorLoading,
getLoadingError,
getPublishedTime,
} from "selectors/editorSelectors";
import {
ReduxActionTypes,
PageListPayload,
} from "../../constants/ReduxActionConstants";
} from "constants/ReduxActionConstants";
import { Dialog, Classes, AnchorButton } from "@blueprintjs/core";
import { initEditor } from "../../actions/initActions";
import { initEditor } from "actions/initActions";
type EditorProps = {
currentPageName: string;
currentPageName?: string;
isSaving: boolean;
currentApplicationId?: string;
currentLayoutId: string;
currentPageId: string;
currentLayoutId?: string;
currentPageId?: string;
publishApplication: Function;
previewPage: Function;
initEditor: Function;
@ -32,8 +42,12 @@ type EditorProps = {
pages: PageListPayload;
switchPage: (pageId: string) => void;
isPublishing: boolean;
isEditorLoading: boolean;
editorLoadingError: boolean;
errorPublishing: boolean;
};
publishedTime: string | boolean;
isPageSwitching: boolean;
} & RouteComponentProps<BuilderRouteParams>;
class Editor extends Component<EditorProps> {
public state = {
@ -41,20 +55,37 @@ class Editor extends Component<EditorProps> {
};
componentDidMount() {
this.props.initEditor();
}
componentDidUpdate(currently: EditorProps) {
const previously = this.props;
if (
!currently.isPublishing &&
previously.isPublishing &&
!currently.errorPublishing
) {
this.setState({
isDialogOpen: true,
});
if (this.props.match.params.applicationId) {
this.props.initEditor(this.props.match.params.applicationId);
}
}
componentDidUpdate(previously: EditorProps) {
// const currently = this.props;
// if (currently.publishedTime !== previously.publishedTime) {
// this.setState({
// isDialogOpen: true,
// });
// }
// if (
// currently.currentPageId &&
// previously.currentPageId !== currently.currentPageId &&
// currently.currentApplicationId
// ) {
// this.props.history.replace(
// BUILDER_PAGE_URL(
// currently.currentApplicationId,
// currently.currentPageId,
// ),
// );
// }
// if (
// previously.match.params.pageId !== currently.match.params.pageId &&
// currently.currentPageId !== currently.match.params.pageId
// ) {
// this.props.switchPage(currently.match.params.pageId);
// }
}
handleDialogClose = () => {
this.setState({
isDialogOpen: false,
@ -68,7 +99,17 @@ class Editor extends Component<EditorProps> {
handleCreatePage = (pageName: string) => {
this.props.createPage(this.props.currentApplicationId, pageName);
};
redirectToPage = (pageId: string) => {
if (this.props.currentApplicationId) {
this.props.history.push(
BUILDER_PAGE_URL(this.props.currentApplicationId, pageId),
);
}
};
public render() {
if (!this.props.match.params.applicationId) {
return <Redirect to="/applications" />;
}
return (
<div>
<EditorHeader
@ -78,7 +119,7 @@ class Editor extends Component<EditorProps> {
onCreatePage={this.handleCreatePage}
pages={this.props.pages}
currentPageId={this.props.currentPageId}
switchToPage={this.props.switchPage}
switchToPage={this.redirectToPage}
isPublishing={this.props.isPublishing}
/>
<MainContainer />
@ -101,7 +142,10 @@ class Editor extends Component<EditorProps> {
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<AnchorButton
target="_blank"
href={`/view/pages/${this.props.currentPageId}`}
href={getApplicationViewerPageURL(
this.props.currentApplicationId,
this.props.currentPageId,
)}
text="View Application"
/>
</div>
@ -121,11 +165,14 @@ const mapStateToProps = (state: AppState) => ({
pages: getPageList(state),
errorPublishing: getPublishingError(state),
isPublishing: getIsPublishingApplication(state),
isEditorLoading: getIsEditorLoading(state),
editorLoadingError: getLoadingError(state),
publishedTime: getPublishedTime(state),
});
const mapDispatchToProps = (dispatch: any) => {
return {
initEditor: () => dispatch(initEditor()),
initEditor: (applicationId: string) => dispatch(initEditor(applicationId)),
publishApplication: (applicationId: string) => {
dispatch({
type: ReduxActionTypes.PUBLISH_APPLICATION_INIT,
@ -154,7 +201,7 @@ const mapDispatchToProps = (dispatch: any) => {
},
switchPage: (pageId: string) => {
dispatch({
type: ReduxActionTypes.FETCH_PAGE,
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: {
pageId,
},
@ -163,7 +210,4 @@ const mapDispatchToProps = (dispatch: any) => {
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Editor);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Editor));

View File

@ -9,7 +9,10 @@ import ApiEditor from "./ApiEditor";
import {
API_EDITOR_ID_URL,
API_EDITOR_URL,
BUILDER_URL,
BUILDER_PAGE_URL,
BUILDER_BASE_URL,
BuilderRouteParams,
APIEditorRouteParams,
} from "../../constants/routes";
import styled from "styled-components";
@ -39,27 +42,39 @@ interface RouterState {
isVisible: boolean;
}
class EditorsRouter extends React.Component<RouteComponentProps, RouterState> {
constructor(props: RouteComponentProps) {
class EditorsRouter extends React.Component<
RouteComponentProps<BuilderRouteParams>,
RouterState
> {
constructor(props: RouteComponentProps<APIEditorRouteParams>) {
super(props);
const { applicationId, pageId } = this.props.match.params;
this.state = {
isVisible: this.props.location.pathname !== BUILDER_URL,
isVisible:
this.props.location.pathname !== BUILDER_BASE_URL(applicationId) &&
this.props.location.pathname !==
BUILDER_PAGE_URL(applicationId, pageId),
};
}
componentDidUpdate(prevProps: Readonly<RouteComponentProps>): void {
if (this.props.location.pathname !== prevProps.location.pathname) {
const { applicationId, pageId } = this.props.match.params;
this.setState({
isVisible: this.props.location.pathname !== BUILDER_URL,
isVisible:
this.props.location.pathname !== BUILDER_BASE_URL(applicationId) &&
this.props.location.pathname !==
BUILDER_PAGE_URL(applicationId, pageId),
});
}
}
handleClose = (e: React.MouseEvent) => {
e.stopPropagation();
const { applicationId, pageId } = this.props.match.params;
this.setState({
isVisible: false,
});
this.props.history.replace(BUILDER_URL);
this.props.history.replace(BUILDER_PAGE_URL(applicationId, pageId));
};
preventClose = (e: React.MouseEvent) => {
@ -74,8 +89,8 @@ class EditorsRouter extends React.Component<RouteComponentProps, RouterState> {
onClick={this.preventClose}
>
<Switch>
<Route exact path={API_EDITOR_URL} component={ApiEditor} />
<Route path={API_EDITOR_ID_URL()} component={ApiEditor} />
<Route exact path={API_EDITOR_URL()} component={ApiEditor} />
<Route exact path={API_EDITOR_ID_URL()} component={ApiEditor} />
</Switch>
</DrawerWrapper>
</Wrapper>

View File

@ -1,18 +1,32 @@
import * as React from "react";
import React from "react";
import styled from "styled-components";
import { NonIdealState, Button, Card, Elevation } from "@blueprintjs/core";
import { RouterProps } from "react-router";
const NotFoundPageWrapper = styled.div`
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
`;
const Title = styled.div`
font-size: ${props => props.theme.fontSizes[10]}px;
text-align: center;
`;
class PageNotFound extends React.PureComponent<RouterProps> {
public render() {
return (
<div style={{ textAlign: "center" }}>
<NotFoundPageWrapper>
<Card elevation={Elevation.TWO}>
<Title>
<span role="img" aria-label="Page Not Found">
🙊
</span>
</Title>
<NonIdealState
icon={"search"}
title="Page not found"
description={
"The page you were looking for does not appear to exist"
"We didn't mean for you to reach this page. Let's find your way back to building awesome applications."
}
action={
<Button
@ -25,7 +39,7 @@ class PageNotFound extends React.PureComponent<RouterProps> {
}
/>
</Card>
</div>
</NotFoundPageWrapper>
);
}
}

View File

@ -10,6 +10,7 @@ const ProtectedRoute = ({
}: {
path: string;
component: React.ReactType;
exact?: boolean;
}) => {
return (
<Route

View File

@ -7,6 +7,7 @@ import actionsReducer from "./actionsReducer";
import propertyPaneConfigReducer from "./propertyPaneConfigReducer";
import datasourceReducer from "./datasourceReducer";
import bindingsReducer from "./bindingsReducer";
import pageListReducer from "./pageListReducer";
const entityReducer = combineReducers({
canvasWidgets: canvasWidgetsReducer,
@ -17,5 +18,7 @@ const entityReducer = combineReducers({
propertyConfig: propertyPaneConfigReducer,
datasources: datasourceReducer,
nameBindings: bindingsReducer,
pageList: pageListReducer,
});
export default entityReducer;

View File

@ -0,0 +1,36 @@
import { createReducer } from "utils/AppsmithUtils";
import {
ReduxAction,
ReduxActionTypes,
PageListPayload,
} from "constants/ReduxActionConstants";
const initialState: PageListReduxState = {
pages: [],
};
const pageListReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
state: PageListReduxState,
action: ReduxAction<{ pages: PageListPayload; applicationId: string }>,
) => {
return {
...state,
...action.payload,
};
},
[ReduxActionTypes.CREATE_PAGE_SUCCESS]: (
state: PageListReduxState,
action: ReduxAction<{ pageName: string; pageId: string; layoutId: string }>,
) => {
state.pages.push(action.payload);
return { ...state };
},
});
export interface PageListReduxState {
pages: PageListPayload;
applicationId?: string;
}
export default pageListReducer;

View File

@ -16,6 +16,7 @@ import { DatasourceDataState } from "./entityReducers/datasourceReducer";
import { AppViewReduxState } from "./uiReducers/appViewReducer";
import { ApplicationsReduxState } from "./uiReducers/applicationsReducer";
import { BindingsDataState } from "./entityReducers/bindingsReducer";
import { PageListReduxState } from "./entityReducers/pageListReducer";
import { ApiPaneReduxState } from "./uiReducers/apiPaneReducer";
const appReducer = combineReducers({
@ -45,6 +46,7 @@ export interface AppState {
widgetConfig: WidgetConfigReducerState;
datasources: DatasourceDataState;
nameBindings: BindingsDataState;
pageList: PageListReduxState;
};
}

View File

@ -9,16 +9,14 @@ import {
const initialState: AppViewReduxState = {
isFetchingPage: false,
loading: false,
pages: [],
pageWidgetId: "0",
};
const appViewReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
state: AppViewReduxState,
action: ReduxAction<PageListPayload>,
) => {
return { ...state, pages: action.payload };
[ReduxActionTypes.INITIALIZE_PAGE_VIEWER]: (state: AppViewReduxState) => {
return { ...state, loading: true };
},
[ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => {
return { ...state, dsl: undefined, isFetchingPage: true };
@ -30,16 +28,13 @@ const appViewReducer = createReducer(initialState, {
state: AppViewReduxState,
action: ReduxAction<{
pageId: string;
layoutId: string;
dsl: ContainerWidgetProps<WidgetProps>;
pageWidgetId: string;
}>,
) => {
return {
pages: state.pages,
dsl: action.payload.dsl,
currentPageId: action.payload.pageId,
currentLayoutId: action.payload.layoutId,
isFetchingPage: false,
pageWidgetId: action.payload.pageWidgetId,
};
@ -50,6 +45,7 @@ export interface AppViewReduxState {
dsl?: ContainerWidgetProps<WidgetProps>;
isFetchingPage: boolean;
currentPageId?: string;
loading: boolean;
currentLayoutId?: string;
pages: PageListPayload;
pageWidgetId: string;

View File

@ -1,28 +1,69 @@
import { createReducer } from "../../utils/AppsmithUtils";
import { getEditorConfigs } from "../../constants/ApiConstants";
import { ReduxActionTypes } from "../../constants/ReduxActionConstants";
import {
ReduxActionTypes,
ReduxActionErrorTypes,
} from "../../constants/ReduxActionConstants";
import { WidgetProps } from "../../widgets/BaseWidget";
import { ContainerWidgetProps } from "../../widgets/ContainerWidget";
import moment from "moment";
import {
ReduxAction,
UpdateCanvasPayload,
PageListPayload,
} from "../../constants/ReduxActionConstants";
} from "constants/ReduxActionConstants";
const editorConfigs = getEditorConfigs();
const initialState: EditorReduxState = {
pageWidgetId: "0",
...editorConfigs,
pages: [],
loadingStates: {
publishing: false,
publishingError: false,
published: false,
saving: false,
savingError: false,
loading: false,
loadingError: false,
pageSwitchingError: false,
isPageSwitching: false,
},
};
const editorReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_PAGE_INIT]: (state: EditorReduxState) => ({
...state,
loadingStates: {
...state.loadingStates,
isPageSwitching: true,
},
}),
[ReduxActionTypes.FETCH_PAGE_SUCCESS]: (state: EditorReduxState) => ({
...state,
loadingStates: {
...state.loadingStates,
isPageSwitching: false,
},
}),
[ReduxActionErrorTypes.FETCH_PAGE_ERROR]: (state: EditorReduxState) => ({
...state,
loadingStates: {
...state.loadingStates,
isPageSwitching: false,
},
}),
[ReduxActionTypes.INIT_EDITOR]: (state: EditorReduxState) => {
state.loadingStates.loading = true;
state.loadingStates.loadingError = false;
return { ...state };
},
[ReduxActionTypes.INIT_EDITOR_SUCCESS]: (state: EditorReduxState) => {
state.loadingStates.loading = false;
state.loadingStates.loadingError = false;
return { ...state };
},
[ReduxActionErrorTypes.INITIALIZE_EDITOR_ERROR]: (
state: EditorReduxState,
) => {
state.loadingStates.loading = false;
state.loadingStates.loadingError = true;
return { ...state };
},
[ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => {
state.loadingStates.publishing = true;
state.loadingStates.publishingError = false;
@ -36,19 +77,7 @@ const editorReducer = createReducer(initialState, {
[ReduxActionTypes.PUBLISH_APPLICATION_SUCCESS]: (state: EditorReduxState) => {
state.loadingStates.publishing = false;
state.loadingStates.publishingError = false;
return { ...state };
},
[ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS]: (
state: EditorReduxState,
action: ReduxAction<PageListPayload>,
) => {
return { ...state, pages: action.payload };
},
[ReduxActionTypes.CREATE_PAGE_SUCCESS]: (
state: EditorReduxState,
action: ReduxAction<{ pageName: string; pageId: string; layoutId: string }>,
) => {
state.pages.push(action.payload);
state.loadingStates.published = moment().format();
return { ...state };
},
[ReduxActionTypes.SAVE_PAGE_INIT]: (state: EditorReduxState) => {
@ -86,18 +115,20 @@ const editorReducer = createReducer(initialState, {
export interface EditorReduxState {
dsl?: ContainerWidgetProps<WidgetProps>;
pageWidgetId: string;
currentPageId: string;
currentLayoutId: string;
currentPageName: string;
propertyPaneConfigsId: string;
currentApplicationId?: string;
pages: PageListPayload;
pageWidgetId?: string;
currentPageId?: string;
currentLayoutId?: string;
currentPageName?: string;
loadingStates: {
saving: boolean;
savingError: boolean;
publishing: boolean;
published: string | boolean;
publishingError: boolean;
loading: boolean;
loadingError: boolean;
isPageSwitching: boolean;
pageSwitchingError: boolean;
};
}

View File

@ -237,9 +237,7 @@ export function* createActionSaga(actionPayload: ReduxAction<RestAction>) {
export function* fetchActionsSaga() {
try {
const response: GenericApiResponse<
RestAction[]
> = yield ActionAPI.fetchActions();
const response: GenericApiResponse<RestAction[]> = yield ActionAPI.fetchActions();
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
@ -281,9 +279,9 @@ export function* updateActionSaga(
export function* deleteActionSaga(actionPayload: ReduxAction<{ id: string }>) {
try {
const id = actionPayload.payload.id;
const response: GenericApiResponse<
RestAction
> = yield ActionAPI.deleteAction(id);
const response: GenericApiResponse<RestAction> = yield ActionAPI.deleteAction(
id,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
AppToaster.show({

View File

@ -3,15 +3,17 @@ import {
ReduxActionErrorTypes,
ReduxAction,
ApplicationPayload,
} from "../constants/ReduxActionConstants";
} from "constants/ReduxActionConstants";
import ApplicationApi, {
PublishApplicationResponse,
PublishApplicationRequest,
FetchApplicationsResponse,
CreateApplicationRequest,
CreateApplicationResponse,
ApplicationResponsePayload,
ApplicationPagePayload,
} from "../api/ApplicationApi";
} from "api/ApplicationApi";
import { getDefaultPageId } from "./SagaUtils";
import { call, put, takeLatest, all, select } from "redux-saga/effects";
import { validateResponse } from "./ErrorSagas";
@ -42,19 +44,6 @@ export function* publishApplicationSaga(
}
}
const getDefaultPageId = (
pages?: ApplicationPagePayload[],
): string | undefined => {
let defaultPage: ApplicationPagePayload | undefined = undefined;
if (pages) {
pages.find(page => page.isDefault);
if (!defaultPage) {
defaultPage = pages[0];
}
}
return defaultPage ? defaultPage.id : undefined;
};
export function* fetchApplicationListSaga() {
try {
const response: FetchApplicationsResponse = yield call(
@ -63,7 +52,11 @@ export function* fetchApplicationListSaga() {
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const applicationListPayload: ApplicationPayload[] = response.data.map(
application => ({
(
application: ApplicationResponsePayload & {
pages: ApplicationPagePayload[];
},
) => ({
name: application.name,
organizationId: application.organizationId,
id: application.id,

View File

@ -15,9 +15,7 @@ import { validateResponse } from "./ErrorSagas";
function* fetchDatasourcesSaga() {
try {
const response: GenericApiResponse<
Datasource[]
> = yield DatasourcesApi.fetchDatasources();
const response: GenericApiResponse<Datasource[]> = yield DatasourcesApi.fetchDatasources();
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
@ -37,9 +35,9 @@ function* createDatasourceSaga(
actionPayload: ReduxAction<CreateDatasourceConfig>,
) {
try {
const response: GenericApiResponse<
Datasource
> = yield DatasourcesApi.createDatasource(actionPayload.payload);
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
actionPayload.payload,
);
if (response.responseMeta.success) {
yield put({
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,

View File

@ -1,56 +1,52 @@
import { all, select, put, takeLatest, take } from "redux-saga/effects";
import { all, put, takeLatest, take } from "redux-saga/effects";
import {
ReduxAction,
ReduxActionTypes,
} from "../constants/ReduxActionConstants";
import { getCurrentPageId } from "../selectors/editorSelectors";
InitializeEditorPayload,
} from "constants/ReduxActionConstants";
import { fetchEditorConfigs } from "../actions/configsActions";
import { fetchPage, fetchPageList } from "../actions/pageActions";
import { fetchPageList } from "../actions/pageActions";
import { fetchActions } from "../actions/actionActions";
import { fetchDatasources } from "../actions/datasourcesActions";
import { initBindingMapListener } from "../actions/bindingActions";
function* initializeEditorSaga() {
// Step 1: Start getting all the data needed by the app
const currentPageId = yield select(getCurrentPageId);
function* initializeEditorSaga(
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
) {
const { applicationId } = initializeEditorAction.payload;
// Step 1: Start getting all the data needed by the
yield all([
put(initBindingMapListener()),
put(fetchPageList()),
put(fetchPageList(applicationId)),
put(fetchEditorConfigs()),
put(fetchPage(currentPageId)),
put(initBindingMapListener()),
put(fetchActions()),
put(fetchDatasources()),
]);
// Step 2: Wait for all data to be in the state
yield all([
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
take(ReduxActionTypes.UPDATE_CANVAS),
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
]);
// Step 3: Create the success;
// Step 6: Notify UI that the editor is ready to go
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
});
}
export function* initializeAppViewerSaga(
action: ReduxAction<{ pageId: string }>,
action: ReduxAction<{ pageId: string; applicationId: string }>,
) {
const { applicationId } = action.payload;
yield put(initBindingMapListener());
yield all([put(fetchPageList()), put(fetchActions())]);
yield all([put(fetchPageList(applicationId)), put(fetchActions())]);
yield all([
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
]);
yield put({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
payload: action.payload,
});
yield take(ReduxActionTypes.UPDATE_CANVAS);
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
});
}
export default function* watchInitSagas() {

View File

@ -5,8 +5,13 @@ import {
ReduxAction,
UpdateCanvasPayload,
PageListPayload,
FetchPageListPayload,
} from "../constants/ReduxActionConstants";
import { updateCanvas, savePageSuccess } from "../actions/pageActions";
import {
updateCanvas,
savePageSuccess,
fetchPageSuccess,
} from "../actions/pageActions";
import PageApi, {
FetchPageResponse,
SavePageResponse,
@ -26,7 +31,6 @@ import {
takeEvery,
all,
} from "redux-saga/effects";
import { getPageLayoutId } from "./selectors";
import { extractCurrentDSL } from "../utils/WidgetPropsUtils";
import { getEditorConfigs, getWidgets } from "./selectors";
@ -34,19 +38,27 @@ import { validateResponse } from "./ErrorSagas";
import { RenderModes } from "constants/WidgetConstants";
import { UpdateWidgetPropertyPayload } from "actions/controlActions";
export function* fetchPageListSaga() {
export function* fetchPageListSaga(
fetchPageListAction: ReduxAction<FetchPageListPayload>,
) {
try {
const response: FetchPageListResponse = yield call(PageApi.fetchPageList);
const { applicationId } = fetchPageListAction.payload;
const response: FetchPageListResponse = yield call(
PageApi.fetchPageList,
applicationId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const pageList: PageListPayload = response.data.map(page => ({
const pages: PageListPayload = response.data.map(page => ({
pageName: page.name,
pageId: page.id,
layoutId: page.layouts[0].id,
}));
yield put({
type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
payload: pageList,
payload: {
pages,
applicationId,
},
});
return;
}
@ -76,21 +88,6 @@ const getCanvasWidgetsPayload = (
};
};
// TODO(abhinav): Make this similar for both Render Modes
const getAppViewWidgetsPayload = (pageResponse: any) => {
const normalizedResponse = CanvasWidgetsNormalizer.normalize(
pageResponse.data.dsl,
);
return {
pageWidgetId: normalizedResponse.result,
currentPageName: pageResponse.data.name,
currentPageId: pageResponse.data.id,
widgets: normalizedResponse.entities.canvasWidgets,
currentLayoutId: pageResponse.data.id,
currentApplicationId: pageResponse.data.applicationId,
};
};
export function* fetchPageSaga(
pageRequestAction: ReduxAction<FetchPageRequest>,
) {
@ -104,6 +101,7 @@ export function* fetchPageSaga(
if (isValidResponse) {
const canvasWidgetsPayload = getCanvasWidgetsPayload(fetchPageResponse);
yield put(updateCanvas(canvasWidgetsPayload));
yield put(fetchPageSuccess());
}
} catch (error) {
yield put({
@ -120,10 +118,8 @@ export function* fetchPublishedPageSaga(
) {
try {
const { pageId } = pageRequestAction.payload;
const layoutId: string = yield select(getPageLayoutId, pageId);
const request: FetchPublishedPageRequest = {
pageId,
layoutId,
};
const response: FetchPublishedPageResponse = yield call(
PageApi.fetchPublishedPage,
@ -131,12 +127,12 @@ export function* fetchPublishedPageSaga(
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const canvasWidgetsPayload = getAppViewWidgetsPayload(response);
const canvasWidgetsPayload = getCanvasWidgetsPayload(response);
yield put({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
payload: {
dsl: response.data.dsl,
layoutId: response.data.id,
dsl: response.data.layouts[0].dsl,
layoutId: response.data.layouts[0].id,
pageId: request.pageId,
pageWidgetId: canvasWidgetsPayload.pageWidgetId,
},
@ -248,7 +244,7 @@ export function* createPageSaga(
export default function* pageSagas() {
yield all([
takeLatest(ReduxActionTypes.FETCH_PAGE, fetchPageSaga),
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
takeLatest(
ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
fetchPublishedPageSaga,

View File

@ -0,0 +1,13 @@
import { ApplicationPagePayload } from "api/ApplicationApi";
export const getDefaultPageId = (
pages?: ApplicationPagePayload[],
): string | undefined => {
let defaultPage: ApplicationPagePayload | undefined = undefined;
if (pages) {
pages.find(page => page.isDefault);
if (!defaultPage) {
defaultPage = pages[0];
}
}
return defaultPage ? defaultPage.id : undefined;
};

View File

@ -14,12 +14,14 @@ export const getWidget = (state: AppState, widgetId: string): WidgetProps => {
export const getEditorConfigs = (
state: AppState,
): { pageId: string; layoutId: string } => {
): { pageId: string; layoutId: string } | undefined => {
const { currentLayoutId, currentPageId } = state.ui.editor;
return {
pageId: currentPageId,
layoutId: currentLayoutId,
};
return currentLayoutId && currentPageId
? {
pageId: currentPageId,
layoutId: currentLayoutId,
}
: undefined;
};
export const getDefaultWidgetConfig = (
@ -33,11 +35,20 @@ export const getDefaultWidgetConfig = (
return widgetConfig;
};
export const getPageLayoutId = (state: AppState, pageId: string): string => {
const pages = state.ui.appView.pages;
// export const getPageLayoutId = (state: AppState, pageId: string): string => {
// const { pages } = state.entities.pageList;
// const page = pages.find(page => page.pageId === pageId);
// if (!page) {
// throw Error("Page not found");
// }
// return page.layoutId;
// };
//TODO(abhinav): The api will return a default flag in the future. use that
// Also, check out `sagaUtils.ts` this has a getDefaultPageId. when the flag is available
// remove that and use this.
export const getDefaultPageId = (state: AppState, pageId?: string): string => {
const { pages } = state.entities.pageList;
const page = pages.find(page => page.pageId === pageId);
if (!page) {
throw Error("Page not found");
}
return page.layoutId;
return page ? page.pageId : pages[0].pageId;
};

View File

@ -1,7 +1,7 @@
import { createSelector } from "reselect";
import { AppState, DataTree } from "../reducers";
import { AppViewReduxState } from "../reducers/uiReducers/appViewReducer";
import { AppViewerProps } from "../pages/AppViewer";
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
import { getDataTree } from "./entitiesSelector";
import createCachedSelector from "re-reselect";
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
@ -9,13 +9,10 @@ import { getValidatedDynamicProps } from "./editorSelectors";
import { CanvasWidgetsReduxState } from "../reducers/entityReducers/canvasWidgetsReducer";
const getAppViewState = (state: AppState) => state.ui.appView;
export const getCurrentLayoutId = (state: AppState, props: AppViewerProps) =>
state.ui.appView.currentLayoutId || props.match.params.layoutId;
export const getCurrentPageId = (state: AppState, props: AppViewerProps) =>
state.ui.appView.currentPageId || props.match.params.pageId;
export const getCurrentRoutePageId = (state: AppState, props: AppViewerProps) =>
props.match.params.pageId;
const getPageListState = (state: AppState): PageListReduxState =>
state.entities.pageList;
export const getCurrentPageId = (state: AppState) =>
state.ui.appView.currentPageId;
// For the viewer, this does not need to be wrapped in createCachedSelector, as it will not change in subsequent renders.
// export const getCurrentPageLayoutDSL = createSelector(
@ -26,8 +23,9 @@ export const getCurrentRoutePageId = (state: AppState, props: AppViewerProps) =>
// );
export const getPageList = createSelector(
getAppViewState,
(view: AppViewReduxState) => (view.pages.length > 0 ? view.pages : undefined),
getPageListState,
(pageList: PageListReduxState) =>
pageList.pages.length > 0 ? pageList.pages : undefined,
);
export const getIsFetchingPage = createSelector(

View File

@ -13,6 +13,7 @@ import {
FlattenedWidgetProps,
CanvasWidgetsReduxState,
} from "reducers/entityReducers/canvasWidgetsReducer";
import { PageListReduxState } from "reducers/entityReducers/pageListReducer";
import { OccupiedSpace } from "constants/editorConstants";
import { WidgetTypes } from "constants/WidgetConstants";
@ -20,18 +21,33 @@ import { WidgetTypes } from "constants/WidgetConstants";
const getEditorState = (state: AppState) => state.ui.editor;
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
const getWidgetSideBar = (state: AppState) => state.ui.widgetSidebar;
const getPageListState = (state: AppState) => state.entities.pageList;
const getWidgets = (state: AppState): CanvasWidgetsReduxState =>
state.entities.canvasWidgets;
export const getPageList = createSelector(
export const getIsEditorLoading = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.pages,
(editor: EditorReduxState) => editor.loadingStates.loading,
);
export const getIsFetchingPage = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.loadingStates.isPageSwitching,
);
export const getPropertyPaneConfigsId = createSelector(
export const getPublishedTime = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.propertyPaneConfigsId,
(editor: EditorReduxState) => editor.loadingStates.published,
);
export const getLoadingError = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.loadingStates.loadingError,
);
export const getPageList = createSelector(
getPageListState,
(pageList: PageListReduxState) => pageList.pages,
);
export const getCurrentPageId = createSelector(
@ -46,7 +62,7 @@ export const getCurrentLayoutId = createSelector(
export const getPageWidgetId = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.pageWidgetId,
(editor: EditorReduxState) => editor.pageWidgetId || "0",
);
export const getCurrentPageName = createSelector(
@ -55,8 +71,8 @@ export const getCurrentPageName = createSelector(
);
export const getCurrentApplicationId = createSelector(
getEditorState,
(editor: EditorReduxState) => editor.currentApplicationId,
getPageListState,
(pageList: PageListReduxState) => pageList.applicationId,
);
export const getIsPageSaving = createSelector(

View File

@ -35,8 +35,8 @@ const defaultDSL = {
parentColumnSpace: 1,
parentRowSpace: 1,
renderMode: "CANVAS",
rightColumn: 1300,
snapColumns: 16,
rightColumn: 1200,
snapColumns: 24,
snapRows: 32,
topRow: 0,
type: "CONTAINER_WIDGET",
@ -47,8 +47,6 @@ export const extractCurrentDSL = (
fetchPageResponse: FetchPageResponse,
): ContainerWidgetProps<WidgetProps> => {
const currentDSL = fetchPageResponse.data.layouts[0].dsl || defaultDSL;
currentDSL.rightColumn = 1200;
currentDSL.snapColumns = 24;
return currentDSL;
};

View File

@ -278,6 +278,6 @@ export const WidgetOperations = {
DELETE: "DELETE",
};
export type WidgetOperation = (typeof WidgetOperations)[keyof typeof WidgetOperations];
export type WidgetOperation = typeof WidgetOperations[keyof typeof WidgetOperations];
export default BaseWidget;

View File

@ -1,7 +1,9 @@
import React from "react";
import _ from "lodash";
import ContainerComponent from "../components/designSystems/appsmith/ContainerComponent";
import ContainerComponent, {
ContainerStyle,
} from "components/designSystems/appsmith/ContainerComponent";
import { ContainerOrientation, WidgetType } from "constants/WidgetConstants";
import WidgetFactory from "utils/WidgetFactory";
import { Color } from "constants/Colors";
@ -107,6 +109,7 @@ export interface ContainerWidgetProps<T extends WidgetProps>
snapRows?: number;
orientation?: ContainerOrientation;
backgroundColor?: Color;
containerStyle?: ContainerStyle;
}
export default ContainerWidget;

File diff suppressed because it is too large Load Diff