From c72833b53bbfea8f022555fb5533ef2ec7c706df Mon Sep 17 00:00:00 2001 From: Rishabh Saxena Date: Thu, 4 Feb 2021 11:21:16 +0530 Subject: [PATCH 1/9] Add an option to set reset the input widget's value on submit (#2837) --- .../mockResponses/PropertyPaneConfigResponse.tsx | 8 ++++++++ app/client/src/widgets/InputWidget.tsx | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx index d757a50071..5b916cc376 100644 --- a/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx +++ b/app/client/src/mockResponses/PropertyPaneConfigResponse.tsx @@ -711,6 +711,14 @@ const PropertyPaneConfigResponse: PropertyPaneConfigsResponse["data"] = { controlType: "SWITCH", isJSConvertible: true, }, + { + id: "4.2.0", + helpText: "Clears the input value after submit", + propertyName: "resetOnSubmit", + label: "Reset on submit", + controlType: "SWITCH", + isJSConvertible: true, + }, ], }, { diff --git a/app/client/src/widgets/InputWidget.tsx b/app/client/src/widgets/InputWidget.tsx index 90427ccd2e..4388ec1122 100644 --- a/app/client/src/widgets/InputWidget.tsx +++ b/app/client/src/widgets/InputWidget.tsx @@ -4,7 +4,7 @@ import { WidgetType } from "constants/WidgetConstants"; import InputComponent, { InputComponentProps, } from "components/designSystems/blueprint/InputComponent"; -import { EventType } from "constants/ActionConstants"; +import { EventType, ExecutionResult } from "constants/ActionConstants"; import { WidgetPropertyValidationType, BASE_WIDGET_VALIDATION, @@ -39,6 +39,7 @@ class InputWidget extends BaseWidget { // onTextChanged: VALIDATION_TYPES.ACTION_SELECTOR, isRequired: VALIDATION_TYPES.BOOLEAN, isValid: VALIDATION_TYPES.BOOLEAN, + resetOnSubmit: VALIDATION_TYPES.BOOLEAN, }; } static getTriggerPropertyMap(): TriggerPropertiesMap { @@ -137,6 +138,17 @@ class InputWidget extends BaseWidget { this.props.updateWidgetMetaProperty("isFocused", focusState); }; + onSubmitSuccess = (result: ExecutionResult) => { + if (result.success && this.props.resetOnSubmit) { + this.props.updateWidgetMetaProperty("text", "", { + dynamicString: this.props.onTextChanged, + event: { + type: EventType.ON_TEXT_CHANGE, + }, + }); + } + }; + handleKeyDown = ( e: | React.KeyboardEvent @@ -148,6 +160,7 @@ class InputWidget extends BaseWidget { dynamicString: this.props.onSubmit, event: { type: EventType.ON_SUBMIT, + callback: this.onSubmitSuccess, }, }); } From 32b641c118aa62f143b1d02f18394a2d4d0ed12c Mon Sep 17 00:00:00 2001 From: Rishabh Saxena Date: Thu, 4 Feb 2021 12:32:36 +0530 Subject: [PATCH 2/9] Implement new designs for the editor and viewer's navbar (#2750) --- app/client/.storybook/configs/contexts.js | 40 +- .../Smoke_TestSuite/Pages/Page_Load_Spec.js | 6 + app/client/src/actions/applicationActions.ts | 6 + .../src/assets/icons/ads/arrow-left.svg | 3 + .../src/assets/icons/ads/chevron_left.svg | 3 + .../src/assets/icons/ads/chevron_right.svg | 3 + app/client/src/assets/icons/ads/fork.svg | 1 + .../assets/images/appsmith_logo_square.png | Bin 0 -> 18049 bytes app/client/src/components/ads/AppIcon.tsx | 5 + app/client/src/components/ads/Button.tsx | 480 ++++++++++-------- app/client/src/components/ads/Icon.tsx | 22 +- app/client/src/components/ads/Menu.tsx | 3 + app/client/src/components/ads/Text.tsx | 2 +- app/client/src/components/ads/Tooltip.tsx | 17 +- .../appsmith/header/DeployLinkButton.tsx | 83 +-- app/client/src/constants/DefaultTheme.tsx | 57 ++- .../src/constants/ReduxActionConstants.tsx | 1 + app/client/src/constants/messages.ts | 2 +- app/client/src/constants/routes.ts | 1 + app/client/src/pages/AppViewer/index.tsx | 17 +- .../AppViewer/viewer/AppViewerHeader.tsx | 223 +++----- .../src/pages/AppViewer/viewer/PageTabs.tsx | 177 +++++++ .../AppViewer/viewer/PageTabsContainer.tsx | 150 ++++++ .../src/pages/Editor/APIEditor/Form.tsx | 2 +- .../pages/Editor/DataSourceEditor/DBForm.tsx | 2 +- .../src/pages/Editor/EditableAppName.tsx | 70 +++ app/client/src/pages/Editor/EditorHeader.tsx | 162 +++--- app/client/src/pages/Editor/MainContainer.tsx | 2 +- .../src/pages/Editor/QueryEditor/Form.tsx | 2 +- .../Editor/QueryEditor/QueryHomeScreen.tsx | 2 +- app/client/src/pages/Editor/WidgetsEditor.tsx | 2 +- app/client/src/pages/Editor/index.tsx | 2 +- app/client/src/pages/Editor/routes.tsx | 2 +- .../src/pages/common/ProfileDropdown.tsx | 12 +- app/client/src/pages/common/ProfileImage.tsx | 3 + .../uiReducers/applicationsReducer.tsx | 3 + app/client/src/sagas/ApplicationSagas.tsx | 3 + app/client/src/selectors/themeSelectors.tsx | 17 +- app/client/src/utils/hooks/useThrottledRAF.ts | 38 ++ 39 files changed, 1079 insertions(+), 547 deletions(-) create mode 100644 app/client/src/assets/icons/ads/arrow-left.svg create mode 100644 app/client/src/assets/icons/ads/chevron_left.svg create mode 100644 app/client/src/assets/icons/ads/chevron_right.svg create mode 100644 app/client/src/assets/icons/ads/fork.svg create mode 100644 app/client/src/assets/images/appsmith_logo_square.png create mode 100644 app/client/src/pages/AppViewer/viewer/PageTabs.tsx create mode 100644 app/client/src/pages/AppViewer/viewer/PageTabsContainer.tsx create mode 100644 app/client/src/pages/Editor/EditableAppName.tsx create mode 100644 app/client/src/utils/hooks/useThrottledRAF.ts diff --git a/app/client/.storybook/configs/contexts.js b/app/client/.storybook/configs/contexts.js index 5f6a61ddce..00c5acb548 100644 --- a/app/client/.storybook/configs/contexts.js +++ b/app/client/.storybook/configs/contexts.js @@ -1,10 +1,36 @@ import { ThemeProvider, theme } from "../../src/constants/DefaultTheme"; +import { light, dark } from "constants/DefaultTheme"; export const contexts = [ - { - icon: "box", - title: "Themes", - components: [ThemeProvider], - params: [{ name: "default", props: { theme: theme } }], - }, -]; \ No newline at end of file + { + icon: "box", + title: "Themes", + components: [ThemeProvider], + params: [ + { + name: "lightTheme", + props: { + theme: { + ...theme, + colors: { + ...theme.colors, + ...light, + }, + }, + }, + }, + { + name: "darkTheme", + props: { + theme: { + ...theme, + colors: { + ...theme.colors, + ...dark, + }, + }, + }, + }, + ], + }, +]; diff --git a/app/client/cypress/integration/Smoke_TestSuite/Pages/Page_Load_Spec.js b/app/client/cypress/integration/Smoke_TestSuite/Pages/Page_Load_Spec.js index 583fbe9fcd..d8b75a5f9f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/Pages/Page_Load_Spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/Pages/Page_Load_Spec.js @@ -21,6 +21,8 @@ describe("Page Load tests", () => { cy.get(".t--page-switch-tab") .contains("Page2") .parent() + .parent() + .parent() .should("have.class", "is-active"); // Assert active page DSL cy.get(commonlocators.headingTextStyle).should( @@ -33,6 +35,8 @@ describe("Page Load tests", () => { cy.get(".t--page-switch-tab") .contains("Page2") .parent() + .parent() + .parent() .should("have.class", "is-active"); // Assert active page DSL cy.get(commonlocators.headingTextStyle).should( @@ -47,6 +51,8 @@ describe("Page Load tests", () => { cy.get(".t--page-switch-tab") .contains("Page1") .parent() + .parent() + .parent() .should("have.class", "is-active"); // Assert active page DSL cy.get(commonlocators.headingTextStyle).should( diff --git a/app/client/src/actions/applicationActions.ts b/app/client/src/actions/applicationActions.ts index e6f8948c18..83458a9ff0 100644 --- a/app/client/src/actions/applicationActions.ts +++ b/app/client/src/actions/applicationActions.ts @@ -69,3 +69,9 @@ export const getAllApplications = () => { type: ReduxActionTypes.GET_ALL_APPLICATION_INIT, }; }; + +export const resetCurrentApplication = () => { + return { + type: ReduxActionTypes.RESET_CURRENT_APPLICATION, + }; +}; diff --git a/app/client/src/assets/icons/ads/arrow-left.svg b/app/client/src/assets/icons/ads/arrow-left.svg new file mode 100644 index 0000000000..016b212df5 --- /dev/null +++ b/app/client/src/assets/icons/ads/arrow-left.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/ads/chevron_left.svg b/app/client/src/assets/icons/ads/chevron_left.svg new file mode 100644 index 0000000000..2dfca379fe --- /dev/null +++ b/app/client/src/assets/icons/ads/chevron_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/ads/chevron_right.svg b/app/client/src/assets/icons/ads/chevron_right.svg new file mode 100644 index 0000000000..2ee5421219 --- /dev/null +++ b/app/client/src/assets/icons/ads/chevron_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/ads/fork.svg b/app/client/src/assets/icons/ads/fork.svg new file mode 100644 index 0000000000..c2f9c94ecc --- /dev/null +++ b/app/client/src/assets/icons/ads/fork.svg @@ -0,0 +1 @@ +fork \ No newline at end of file diff --git a/app/client/src/assets/images/appsmith_logo_square.png b/app/client/src/assets/images/appsmith_logo_square.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ff69ce8ba8f5b82ecc0ed85f4552269800603d GIT binary patch literal 18049 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 { height = 50; padding = 50; break; + default: + width = 20; + height = 20; + padding = 5; + break; } return { width, height, padding }; }; diff --git a/app/client/src/components/ads/Button.tsx b/app/client/src/components/ads/Button.tsx index c49e083978..aa07d126ae 100644 --- a/app/client/src/components/ads/Button.tsx +++ b/app/client/src/components/ads/Button.tsx @@ -18,6 +18,8 @@ export enum Category { } export enum Size { + xxs = "xxs", + xs = "xs", small = "small", medium = "medium", large = "large", @@ -59,128 +61,148 @@ type ButtonProps = CommonComponentProps & { href?: string; tag?: "a" | "button"; type?: "submit" | "reset" | "button"; + target?: string; +}; + +const defaultProps = { + category: Category.primary, + variant: Variant.info, + size: Size.small, + isLoading: false, + disabled: false, + fill: false, + tag: "a", +}; + +const getDisabledStyles = (props: ThemeProp & ButtonProps) => { + const variant = props.variant || defaultProps.variant; + const category = props.category || defaultProps.category; + + const stylesByCategory = { + [Category.primary]: { + txtColorPrimary: props.theme.colors.button.disabledText, + bgColorPrimary: props.theme.colors[variant].darkest, + borderColorPrimary: props.theme.colors[variant].darkest, + }, + [Category.secondary]: { + txtColorSecondary: props.theme.colors.button.disabledText, + bgColorSecondary: props.theme.colors[variant].darkest, + borderColorSecondary: props.theme.colors[variant].darker, + }, + [Category.tertiary]: { + txtColorTertiary: props.theme.colors.button.disabledText, + bgColorTertiary: props.theme.colors.tertiary.darker, + borderColorTertiary: props.theme.colors.tertiary.dark, + }, + }; + + return stylesByCategory[category]; +}; + +const getMainStateStyles = (props: ThemeProp & ButtonProps) => { + const variant = props.variant || defaultProps.variant; + const category = props.category || defaultProps.category; + + const stylesByCategory = { + [Category.primary]: { + bgColorPrimary: props.theme.colors[variant].main, + borderColorPrimary: props.theme.colors[variant].main, + txtColorPrimary: "#fff", + }, + [Category.secondary]: { + borderColorSecondary: props.theme.colors[variant].main, + txtColorSecondary: props.theme.colors[variant].main, + bgColorSecondary: "transparent", + }, + [Category.tertiary]: { + bgColorTertiary: "transparent", + borderColorTertiary: props.theme.colors.tertiary.main, + txtColorTertiary: props.theme.colors.tertiary.main, + }, + }; + + return stylesByCategory[category]; +}; + +const getHoverStateStyles = (props: ThemeProp & ButtonProps) => { + const variant = props.variant || defaultProps.variant; + const category = props.category || defaultProps.category; + + const stylesByCategory = { + [Category.primary]: { + bgColorPrimary: props.theme.colors[variant].dark, + borderColorPrimary: props.theme.colors[variant].dark, + txtColorPrimary: "#fff", + }, + [Category.secondary]: { + bgColorSecondary: hexToRgba(props.theme.colors[variant].main, 0.1), + txtColorSecondary: props.theme.colors[variant].main, + borderColorSecondary: props.theme.colors[variant].main, + }, + [Category.tertiary]: { + bgColorTertiary: hexToRgba(props.theme.colors.tertiary.main, 0.1), + borderColorTertiary: props.theme.colors.tertiary.main, + txtColorTertiary: props.theme.colors.tertiary.main, + }, + }; + + return stylesByCategory[category]; +}; + +const getActiveStateStyles = (props: ThemeProp & ButtonProps) => { + const variant = props.variant || defaultProps.variant; + const category = props.category || defaultProps.category; + + const stylesByCategory = { + [Category.primary]: { + bgColorPrimary: props.theme.colors[variant].dark, + borderColorPrimary: props.theme.colors[variant].main, + txtColorPrimary: "#fff", + }, + [Category.secondary]: { + bgColorSecondary: hexToRgba(props.theme.colors[variant].main, 0.1), + txtColorSecondary: props.theme.colors[variant].light, + borderColorSecondary: props.theme.colors[variant].light, + }, + [Category.tertiary]: { + bgColorTertiary: hexToRgba(props.theme.colors.tertiary.main, 0.1), + borderColorTertiary: props.theme.colors.tertiary.light, + txtColorTertiary: props.theme.colors.tertiary.light, + }, + }; + + return stylesByCategory[category]; }; const stateStyles = ( props: ThemeProp & ButtonProps, - state: string, + stateArg: string, ): stateStyleType => { - let bgColorPrimary, - borderColorPrimary, - txtColorPrimary, - bgColorSecondary, - borderColorSecondary, - txtColorSecondary, - bgColorTertiary, - borderColorTertiary, - txtColorTertiary; - - if (props.isLoading || props.disabled) { - switch (props.category) { - case Category.primary: - if (props.variant) { - bgColorPrimary = props.theme.colors[props.variant].darkest; - borderColorPrimary = props.theme.colors[props.variant].darkest; - } - txtColorPrimary = props.theme.colors.button.disabledText; - break; - case Category.secondary: - if (props.variant) { - bgColorSecondary = props.theme.colors[props.variant].darkest; - borderColorSecondary = props.theme.colors[props.variant].darker; - } - txtColorSecondary = props.theme.colors.button.disabledText; - break; - case Category.tertiary: - bgColorTertiary = props.theme.colors.tertiary.darker; - borderColorTertiary = props.theme.colors.tertiary.dark; - txtColorTertiary = props.theme.colors.button.disabledText; - break; - } - } else if (state === "main") { - switch (props.category) { - case Category.primary: - if (props.variant) { - bgColorPrimary = props.theme.colors[props.variant].main; - borderColorPrimary = props.theme.colors[props.variant].main; - } - txtColorPrimary = "#fff"; - break; - case Category.secondary: - if (props.variant) { - borderColorSecondary = props.theme.colors[props.variant].main; - txtColorSecondary = props.theme.colors[props.variant].main; - } - bgColorSecondary = "transparent"; - break; - case Category.tertiary: - bgColorTertiary = "transparent"; - borderColorTertiary = props.theme.colors.tertiary.main; - txtColorTertiary = props.theme.colors.tertiary.main; - break; - } - } else if (state === "hover") { - switch (props.category) { - case Category.primary: - if (props.variant) { - bgColorPrimary = props.theme.colors[props.variant].dark; - borderColorPrimary = props.theme.colors[props.variant].dark; - } - txtColorPrimary = "#fff"; - break; - case Category.secondary: - if (props.variant) { - bgColorSecondary = hexToRgba( - props.theme.colors[props.variant].main, - 0.1, - ); - txtColorSecondary = props.theme.colors[props.variant].main; - borderColorSecondary = props.theme.colors[props.variant].main; - } - break; - case Category.tertiary: - bgColorTertiary = hexToRgba(props.theme.colors.tertiary.main, 0.1); - borderColorTertiary = props.theme.colors.tertiary.main; - txtColorTertiary = props.theme.colors.tertiary.main; - break; - } - } else if (state === "active") { - switch (props.category) { - case Category.primary: - if (props.variant) { - bgColorPrimary = props.theme.colors[props.variant].dark; - borderColorPrimary = props.theme.colors[props.variant].main; - } - txtColorPrimary = "#fff"; - break; - case Category.secondary: - if (props.variant) { - bgColorSecondary = hexToRgba( - props.theme.colors[props.variant].main, - 0.1, - ); - txtColorSecondary = props.theme.colors[props.variant].light; - borderColorSecondary = props.theme.colors[props.variant].light; - } - break; - case Category.tertiary: - bgColorTertiary = hexToRgba(props.theme.colors.tertiary.main, 0.1); - borderColorTertiary = props.theme.colors.tertiary.light; - txtColorTertiary = props.theme.colors.tertiary.light; - break; - } - } + const styles = { + bgColorPrimary: "", + borderColorPrimary: "", + txtColorPrimary: "", + bgColorSecondary: "", + borderColorSecondary: "", + txtColorSecondary: "", + bgColorTertiary: "", + borderColorTertiary: "", + txtColorTertiary: "", + }; + const state = + props.isLoading || props.disabled + ? "disabled" + : (stateArg as keyof typeof stylesByState); + const stylesByState = { + disabled: getDisabledStyles(props), + main: getMainStateStyles(props), + hover: getHoverStateStyles(props), + active: getActiveStateStyles(props), + }; return { - bgColorPrimary, - borderColorPrimary, - txtColorPrimary, - bgColorSecondary, - borderColorSecondary, - txtColorSecondary, - bgColorTertiary, - borderColorTertiary, - txtColorTertiary, + ...styles, + ...stylesByState[state], }; }; @@ -211,36 +233,61 @@ const btnColorStyles = ( return { bgColor, txtColor, border }; }; +const getPaddingBySize = (props: ThemeProp & ButtonProps) => { + const paddingBySize = { + [Size.small]: `0px ${props.theme.spaces[3]}px`, + [Size.medium]: `0px ${props.theme.spaces[7]}px`, + [Size.large]: `0px ${props.theme.spaces[12] - 4}px`, + }; + const paddingBySizeForJustIcon = { + [Size.small]: `0px ${props.theme.spaces[1]}px`, + [Size.medium]: `0px ${props.theme.spaces[2]}px`, + [Size.large]: `0px ${props.theme.spaces[3]}px`, + }; + + const isIconOnly = !props.text && props.icon; + const paddingConfig = isIconOnly ? paddingBySizeForJustIcon : paddingBySize; + + const iSizeInConfig = + Object.keys(paddingConfig).indexOf(props.size || "") !== -1; + const size: any = props.size && iSizeInConfig ? props.size : Size.small; + + return paddingConfig[size as keyof typeof paddingConfig]; +}; + +const getHeightBySize = (props: ThemeProp & ButtonProps) => { + const heightBySize = { + [Size.small]: 20, + [Size.medium]: 30, + [Size.large]: 38, + }; + + const iSizeInConfig = + Object.keys(heightBySize).indexOf(props.size || "") !== -1; + const size: any = props.size && iSizeInConfig ? props.size : Size.small; + + return heightBySize[size as keyof typeof heightBySize]; +}; + +const getBtnFontBySize = (props: ThemeProp & ButtonProps) => { + const fontBySize = { + [Size.small]: smallButton, + [Size.medium]: mediumButton, + [Size.large]: largeButton, + }; + + const iSizeInConfig = + Object.keys(fontBySize).indexOf(props.size || "") !== -1; + const size: any = props.size && iSizeInConfig ? props.size : Size.small; + + return fontBySize[size as keyof typeof fontBySize]; +}; + const btnFontStyles = (props: ThemeProp & ButtonProps): BtnFontType => { - let buttonFont, - padding = "", - height = 0; - switch (props.size) { - case Size.small: - buttonFont = smallButton; - height = 20; - padding = - !props.text && props.icon - ? `0px ${props.theme.spaces[1]}px` - : `0px ${props.theme.spaces[3]}px`; - break; - case Size.medium: - buttonFont = mediumButton; - height = 30; - padding = - !props.text && props.icon - ? `0px ${props.theme.spaces[2]}px` - : `0px ${props.theme.spaces[7]}px`; - break; - case Size.large: - buttonFont = largeButton; - height = 38; - padding = - !props.text && props.icon - ? `0px ${props.theme.spaces[3]}px` - : `0px ${props.theme.spaces[12] - 4}px`; - break; - } + const padding = getPaddingBySize(props); + const height = getHeightBySize(props); + const buttonFont = getBtnFontBySize(props); + return { buttonFont, padding, height }; }; @@ -318,78 +365,85 @@ export const VisibilityWrapper = styled.div` `; const IconSizeProp = (size?: Size) => { - if (size === Size.small) { - return IconSize.SMALL; - } else if (size === Size.medium) { - return IconSize.MEDIUM; - } else if (size === Size.large) { - return IconSize.LARGE; - } else { - return IconSize.SMALL; - } + const sizeMapping = { + [Size.xxs]: IconSize.XXS, + [Size.xs]: IconSize.XS, + [Size.small]: IconSize.SMALL, + [Size.medium]: IconSize.MEDIUM, + [Size.large]: IconSize.LARGE, + }; + + return size ? sizeMapping[size] : IconSize.SMALL; }; -Button.defaultProps = { - category: Category.primary, - variant: Variant.info, - size: Size.small, - isLoading: false, - disabled: false, - fill: false, - tag: "a", -}; +const TextLoadingState = ({ text }: { text?: string }) => ( + {text} +); -function Button(props: ButtonProps) { - const IconLoadingState = ( - +const IconLoadingState = ({ size, icon }: { size?: Size; icon?: IconName }) => ( + +); + +const getIconContent = (props: ButtonProps) => + props.icon ? ( + props.isLoading ? ( + + ) : ( + + ) + ) : null; + +const getTextContent = (props: ButtonProps) => + props.text ? ( + props.isLoading ? ( + + ) : ( + props.text + ) + ) : null; + +const getButtonContent = (props: ButtonProps) => ( + <> + {getIconContent(props)} + {getTextContent(props)} + {props.isLoading ? : null} + +); + +const ButtonComponent = (props: ButtonProps) => ( + ) => + props.onClick && props.onClick(e) + } + > + {getButtonContent(props)} + +); + +const LinkButtonComponent = (props: ButtonProps) => ( + ) => + props.onClick && props.onClick(e) + } + > + {getButtonContent(props)} + +); + +const Button = (props: ButtonProps) => + props.tag === "button" ? ( + + ) : ( + ); - const TextLoadingState = {props.text}; - - const buttonContent = ( - <> - {props.icon ? ( - props.isLoading ? ( - IconLoadingState - ) : ( - - ) - ) : null} - - {props.text ? (props.isLoading ? TextLoadingState : props.text) : null} - - {props.isLoading ? : null} - - ); - - if (props.tag === "button") { - return ( - ) => - props.onClick && props.onClick(e) - } - > - {buttonContent} - - ); - } else { - return ( - ) => - props.onClick && props.onClick(e) - } - > - {buttonContent} - - ); - } -} - export default Button; + +Button.defaultProps = defaultProps; diff --git a/app/client/src/components/ads/Icon.tsx b/app/client/src/components/ads/Icon.tsx index 34e880b2c0..c82b3d69a3 100644 --- a/app/client/src/components/ads/Icon.tsx +++ b/app/client/src/components/ads/Icon.tsx @@ -21,6 +21,10 @@ import { ReactComponent as ContextMenuIcon } from "assets/icons/ads/context-menu import { ReactComponent as DuplicateIcon } from "assets/icons/ads/duplicate.svg"; import { ReactComponent as LogoutIcon } from "assets/icons/ads/logout.svg"; import { ReactComponent as ManageIcon } from "assets/icons/ads/manage.svg"; +import { ReactComponent as ArrowLeft } from "assets/icons/ads/arrow-left.svg"; +import { ReactComponent as Fork } from "assets/icons/ads/fork.svg"; +import { ReactComponent as ChevronLeft } from "assets/icons/ads/chevron_left.svg"; +import { ReactComponent as ChevronRight } from "assets/icons/ads/chevron_right.svg"; import styled from "styled-components"; import { CommonComponentProps, Classes } from "./common"; import { noop } from "lodash"; @@ -95,11 +99,15 @@ export const IconCollection = [ "duplicate", "logout", "manage", + "arrow-left", + "fork", + "chevron-left", + "chevron-right", ] as const; export type IconName = typeof IconCollection[number]; -const IconWrapper = styled.span` +export const IconWrapper = styled.span` &:focus { outline: none; } @@ -208,6 +216,18 @@ const Icon = forwardRef( case "warning": returnIcon = ; break; + case "arrow-left": + returnIcon = ; + break; + case "fork": + returnIcon = ; + break; + case "chevron-left": + returnIcon = ; + break; + case "chevron-right": + returnIcon = ; + break; default: returnIcon = null; break; diff --git a/app/client/src/components/ads/Menu.tsx b/app/client/src/components/ads/Menu.tsx index 5748732665..116dcfed15 100644 --- a/app/client/src/components/ads/Menu.tsx +++ b/app/client/src/components/ads/Menu.tsx @@ -3,6 +3,7 @@ import { CommonComponentProps } from "./common"; import styled from "styled-components"; import { Popover } from "@blueprintjs/core/lib/esm/components/popover/popover"; import { Position } from "@blueprintjs/core/lib/esm/common/position"; +import { PopperModifiers } from "@blueprintjs/core"; type MenuProps = CommonComponentProps & { children?: ReactNode[]; @@ -10,6 +11,7 @@ type MenuProps = CommonComponentProps & { position?: Position; onOpening?: (node: HTMLElement) => void; onClosing?: (node: HTMLElement) => void; + modifiers?: PopperModifiers; }; const MenuWrapper = styled.div` @@ -33,6 +35,7 @@ const Menu = (props: MenuProps) => { portalClassName={props.className} data-cy={props.cypressSelector} disabled={props.disabled} + modifiers={props.modifiers} > {props.target} diff --git a/app/client/src/components/ads/Text.tsx b/app/client/src/components/ads/Text.tsx index 969b2ad208..c2a58a9a06 100644 --- a/app/client/src/components/ads/Text.tsx +++ b/app/client/src/components/ads/Text.tsx @@ -70,7 +70,7 @@ const Text = styled.span.attrs((props: TextProps) => ({ letter-spacing: ${(props) => props.theme.typography[props.type].letterSpacing}px; color: ${(props) => - props.highlight ? props.theme.colors.text.hightlight : typeSelector(props)}; + props.highlight ? props.theme.colors.text.highlight : typeSelector(props)}; text-transform: ${(props) => (props.case ? props.case : "none")}; `; diff --git a/app/client/src/components/ads/Tooltip.tsx b/app/client/src/components/ads/Tooltip.tsx index f40b3c2a02..9b9d849af4 100644 --- a/app/client/src/components/ads/Tooltip.tsx +++ b/app/client/src/components/ads/Tooltip.tsx @@ -1,7 +1,7 @@ import React from "react"; import { CommonComponentProps } from "./common"; import styled from "styled-components"; -import { Position, Tooltip, Classes } from "@blueprintjs/core"; +import { Position, Tooltip, Classes, PopperBoundary } from "@blueprintjs/core"; import { Classes as CsClasses } from "./common"; type Variant = "dark" | "light"; @@ -12,6 +12,8 @@ type TooltipProps = CommonComponentProps & { children: JSX.Element; variant?: Variant; maxWidth?: number; + usePortal?: boolean; + boundary?: PopperBoundary; }; const TooltipWrapper = styled.div<{ variant?: Variant; maxWidth?: number }>` @@ -24,6 +26,16 @@ const TooltipWrapper = styled.div<{ variant?: Variant; maxWidth?: number }>` : props.theme.colors.tooltip.lightBg}; } div.${Classes.POPOVER_ARROW} { + path { + fill: ${(props) => + props.variant === "dark" + ? props.theme.colors.tooltip.darkBg + : props.theme.colors.tooltip.lightBg}; + stroke: ${(props) => + props.variant === "dark" + ? props.theme.colors.tooltip.darkBg + : props.theme.colors.tooltip.lightBg}; + } display: block; } .${Classes.TOOLTIP} { @@ -52,7 +64,8 @@ const TooltipComponent = (props: TooltipProps) => { {props.children} diff --git a/app/client/src/components/designSystems/appsmith/header/DeployLinkButton.tsx b/app/client/src/components/designSystems/appsmith/header/DeployLinkButton.tsx index 711a42bbd6..7fd9615df8 100644 --- a/app/client/src/components/designSystems/appsmith/header/DeployLinkButton.tsx +++ b/app/client/src/components/designSystems/appsmith/header/DeployLinkButton.tsx @@ -1,34 +1,14 @@ import React, { ReactNode, useState } from "react"; -import styled from "styled-components"; -import { Icon, Popover, PopoverPosition, Tooltip } from "@blueprintjs/core"; -import copy from "copy-to-clipboard"; -import { PopoverInteractionKind } from "@blueprintjs/core/lib/esm/components/popover/popover"; - -const IconContainer = styled.div` - cursor: pointer; - margin: 0 10px; - border: 1px solid #bcccd9; - border-radius: 50%; - min-width: 32px; - max-width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - flex-grow: 1; - svg { - transform: rotate(45deg); - } -`; +import styled, { withTheme } from "styled-components"; +import { Icon, Popover, PopoverPosition } from "@blueprintjs/core"; +import { Theme } from "constants/DefaultTheme"; const DeployLinkDialog = styled.div` display: flex; align-items: center; - background-color: #fff; padding: 10px; - width: 336px; - height: 62px; - color: #2e3d49; + background-color: ${(props) => + props.theme.colors.header.deployToolTipBackground}; flex-direction: row; `; @@ -36,17 +16,15 @@ const DeployLink = styled.a` display: flex; cursor: pointer; text-decoration: none; - padding-right: 10px; - color: #2e3d49; + color: ${(props) => props.theme.colors.header.deployToolTipText}; :hover { - text-decoration: none; - color: #2e3d49; + text-decoration: underline; + color: ${(props) => props.theme.colors.header.deployToolTipText}; } `; const DeployUrl = styled.div` flex: 1; - width: 222px; font-size: 12px; white-space: nowrap; overflow: hidden; @@ -57,65 +35,40 @@ const DeployUrl = styled.div` type Props = { trigger: ReactNode; link: string; + theme: Theme; }; -export const DeployLinkButton = (props: Props) => { +export const DeployLinkButton = withTheme((props: Props) => { const [isOpen, setIsOpen] = useState(false); - const [isCopied, setIsCopied] = useState(false); - const link = window.location.origin + props.link; const onClose = () => { setIsOpen(false); }; - const copyToClipboard = () => { - copy(link); - setIsCopied(true); - setTimeout(() => { - setIsCopied(false); - }, 3000); - }; - return ( - - - - - - - {link} - - + Current deployed version + } canEscapeKeyClose={false} onClose={onClose} isOpen={isOpen} - position={PopoverPosition.BOTTOM} + position={PopoverPosition.BOTTOM_RIGHT} >
setIsOpen(true)}>{props.trigger}
); -}; +}); export default DeployLinkButton; diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 2e1b047cd7..c3a8596c96 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -41,6 +41,15 @@ export enum Skin { DARK, } +export const hideScrollbar = css` + scrollbar-width: none; + -ms-overflow-style: none; + &::-webkit-scrollbar { + display: none; + -webkit-appearance: none; + } +`; + export const scrollbarDark = css` scrollbar-color: ${(props) => props.theme.colors.paneCard} ${(props) => props.theme.colors.paneBG}; @@ -285,6 +294,7 @@ export type Theme = { }; propertyPane: PropertyPaneTheme; headerHeight: string; + smallHeaderHeight: string; homePage: any; sidebarWidth: string; canvasPadding: string; @@ -511,7 +521,7 @@ type ColorType = { text: { normal: ShadeColor; heading: ShadeColor; - hightlight: ShadeColor; + highlight: ShadeColor; }; icon: { normal: ShadeColor; @@ -742,6 +752,19 @@ type ColorType = { floatingBtn: any; auth: any; formMessage: Record>; + header: { + separator: string; + appName: ShadeColor; + background: string; + deployToolTipBackground: string; + deployToolTipText: ShadeColor; + shareBtnHighlight: string; + shareBtn: string; + tabsHorizontalSeparator: string; + tabText: string; + activeTabBorderBottom: string; + activeTabText: string; + }; }; const auth: any = { @@ -772,6 +795,19 @@ const formMessage = { }; export const dark: ColorType = { + header: { + separator: darkShades[4], + appName: darkShades[7], + background: darkShades[2], + deployToolTipBackground: lightShades[10], + deployToolTipText: darkShades[7], + shareBtnHighlight: "#F86A2B", + shareBtn: "#fff", + tabsHorizontalSeparator: "#EFEFEF", + tabText: "#6F6D6D", + activeTabBorderBottom: "#FF6D2D", + activeTabText: "#000", + }, button: { disabledText: darkShades[6], }, @@ -820,7 +856,7 @@ export const dark: ColorType = { text: { normal: darkShades[6], heading: darkShades[7], - hightlight: darkShades[9], + highlight: darkShades[9], }, icon: { normal: darkShades[6], @@ -1058,6 +1094,19 @@ export const dark: ColorType = { }; export const light: ColorType = { + header: { + separator: "#E0DEDE", + appName: lightShades[8], + background: lightShades[0], + deployToolTipText: lightShades[8], + deployToolTipBackground: "#FFF", + shareBtnHighlight: "#F86A2B", + shareBtn: "#4B4848", + tabsHorizontalSeparator: "#EFEFEF", + tabText: "#6F6D6D", + activeTabBorderBottom: "#FF6D2D", + activeTabText: "#000", + }, button: { disabledText: lightShades[6], }, @@ -1106,7 +1155,7 @@ export const light: ColorType = { text: { normal: lightShades[8], heading: lightShades[9], - hightlight: lightShades[11], + highlight: lightShades[11], }, icon: { normal: lightShades[4], @@ -1560,6 +1609,7 @@ export const theme: Theme = { }, }, headerHeight: "48px", + smallHeaderHeight: "35px", canvasPadding: "20px 0 200px 0", sideNav: { maxWidth: 220, @@ -1670,7 +1720,6 @@ export const theme: Theme = { export const scrollbarLight = css<{ backgroundColor?: Color }>` scrollbar-color: ${(props) => props.theme.colors.paneText} - scrollbar-width: thin; &::-webkit-scrollbar { width: 4px; diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index c7d41a158c..ea0c4e91b7 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -320,6 +320,7 @@ export const ReduxActionTypes: { [key: string]: string } = { FETCH_RELEASES_SUCCESS: "FETCH_RELEASES_SUCCESS", RESET_UNREAD_RELEASES_COUNT: "RESET_UNREAD_RELEASES_COUNT", SET_LOADING_ENTITIES: "SET_LOADING_ENTITIES", + RESET_CURRENT_APPLICATION: "RESET_CURRENT_APPLICATION", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/constants/messages.ts b/app/client/src/constants/messages.ts index d8dd5a19d4..01964f6296 100644 --- a/app/client/src/constants/messages.ts +++ b/app/client/src/constants/messages.ts @@ -145,7 +145,7 @@ export const AUTOFIT_COLUMN = "Autofit column"; export const TIMEZONE = "Timezone"; export const ENABLE_TIME = "Enable Time"; -export const EDIT_APP = "Edit App"; +export const EDIT_APP = "Edit"; export const FORK_APP = "Fork App"; export const SIGN_IN = "Sign In"; diff --git a/app/client/src/constants/routes.ts b/app/client/src/constants/routes.ts index c0429d61c7..ff4a8d7d11 100644 --- a/app/client/src/constants/routes.ts +++ b/app/client/src/constants/routes.ts @@ -6,6 +6,7 @@ export const APPLICATIONS_URL = `/applications`; export const BUILDER_URL = "/applications/:applicationId/pages/:pageId/edit"; export const USER_AUTH_URL = "/user"; export const USERS_URL = "/users"; +export const VIEWER_URL_REGEX = /applications\/.*?\/pages\/.*/; export type BuilderRouteParams = { applicationId: string; diff --git a/app/client/src/pages/AppViewer/index.tsx b/app/client/src/pages/AppViewer/index.tsx index 5aee53c7db..76cd70b0be 100644 --- a/app/client/src/pages/AppViewer/index.tsx +++ b/app/client/src/pages/AppViewer/index.tsx @@ -9,7 +9,10 @@ import { BuilderRouteParams, getApplicationViewerPageURL, } from "constants/routes"; -import { ReduxActionTypes } from "constants/ReduxActionConstants"; +import { + PageListPayload, + ReduxActionTypes, +} from "constants/ReduxActionConstants"; import { getIsInitialized } from "selectors/appViewSelectors"; import { executeAction } from "actions/widgetActions"; import { ExecuteActionPayload } from "constants/ActionConstants"; @@ -24,15 +27,19 @@ import { import { editorInitializer } from "utils/EditorUtils"; import * as Sentry from "@sentry/react"; import log from "loglevel"; +import { getPageList } from "selectors/editorSelectors"; const SentryRoute = Sentry.withSentryRouting(Route); -const AppViewerBody = styled.section` +const AppViewerBody = styled.section<{ hasPages: boolean }>` display: flex; flex-direction: row; align-items: stretch; justify-content: flex-start; - height: calc(100vh - ${(props) => props.theme.headerHeight}); + height: calc( + 100vh - + ${(props) => (!props.hasPages ? props.theme.smallHeaderHeight : "72px")} + ); `; export type AppViewerProps = { @@ -51,6 +58,7 @@ export type AppViewerProps = { propertyValue: any, ) => void; resetChildrenMetaProperty: (widgetId: string) => void; + pages: PageListPayload; } & RouteComponentProps; class AppViewer extends Component< @@ -85,7 +93,7 @@ class AppViewer extends Component< resetChildrenMetaProperty: this.props.resetChildrenMetaProperty, }} > - + 1}> {isInitialized && this.state.registered && ( ({ isInitialized: getIsInitialized(state), + pages: getPageList(state), }); const mapDispatchToProps = (dispatch: any) => ({ diff --git a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx index 35daeb5ecf..d909898952 100644 --- a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx +++ b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx @@ -1,10 +1,9 @@ -import React, { useRef, useEffect, useState } from "react"; -import { Link, NavLink, useLocation } from "react-router-dom"; +import React from "react"; +import { Link, useLocation } from "react-router-dom"; import { Helmet } from "react-helmet"; import styled from "styled-components"; import StyledHeader from "components/designSystems/appsmith/StyledHeader"; -import AppsmithLogo from "assets/images/appsmith_logo_white.png"; -import Button from "components/editorComponents/Button"; +import AppsmithLogo from "assets/images/appsmith_logo.png"; import { EDIT_APP, FORK_APP, SIGN_IN } from "constants/messages"; import { isPermitted, @@ -17,7 +16,6 @@ import { import { APPLICATIONS_URL, AUTH_LOGIN_URL, - getApplicationViewerPageURL, SIGN_UP_URL, } from "constants/routes"; import { connect } from "react-redux"; @@ -27,27 +25,53 @@ import { getPageList } from "selectors/editorSelectors"; import { FormDialogComponent } from "components/editorComponents/form/FormDialogComponent"; import AppInviteUsersForm from "pages/organization/AppInviteUsersForm"; import { getCurrentOrgId } from "selectors/organizationSelectors"; -import { HeaderIcons } from "icons/HeaderIcons"; -import { Colors } from "constants/Colors"; + import { getCurrentUser } from "selectors/usersSelectors"; import { ANONYMOUS_USERNAME, User } from "constants/userConstants"; -import { isEllipsisActive } from "utils/helpers"; -import TooltipComponent from "components/ads/Tooltip"; import Text, { TextType } from "components/ads/Text"; import { Classes } from "components/ads/common"; +import { getTypographyByKey } from "constants/DefaultTheme"; +import { IconWrapper } from "components/ads/Icon"; +import Button, { Size } from "components/ads/Button"; +import ProfileDropdown from "pages/common/ProfileDropdown"; +import { Profile } from "pages/common/ProfileImage"; +import PageTabsContainer from "./PageTabsContainer"; const HeaderWrapper = styled(StyledHeader)<{ hasPages: boolean }>` - background: ${Colors.BALTIC_SEA}; - height: ${(props) => (props.hasPages ? "90px" : "48px")}; + box-shadow: unset; + height: unset; + padding: 0; + background-color: ${(props) => props.theme.colors.header.background}; color: white; flex-direction: column; - box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.05); .${Classes.TEXT} { max-width: 194px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - color: #d4d4d4; + ${(props) => getTypographyByKey(props, "h4")} + color: ${(props) => props.theme.colors.header.appName}; + } + + & .header__application-share-btn { + background-color: ${(props) => props.theme.colors.header.background}; + border-color: ${(props) => props.theme.colors.header.background}; + color: ${(props) => props.theme.colors.header.shareBtn}; + ${IconWrapper} path { + fill: ${(props) => props.theme.colors.header.shareBtn}; + } + } + + & .header__application-share-btn:hover { + color: ${(props) => props.theme.colors.header.shareBtnHighlight}; + ${IconWrapper} path { + fill: ${(props) => props.theme.colors.header.shareBtnHighlight}; + } + } + + & ${Profile} { + width: 24px; + height: 24px; } `; @@ -57,6 +81,7 @@ const HeaderRow = styled.div<{ justify: string }>` flex: 1; flex-direction: row; justify-content: ${(props) => props.justify}; + height: ${(props) => `calc(${props.theme.smallHeaderHeight})`}; `; const HeaderSection = styled.div<{ justify: string }>` @@ -67,66 +92,26 @@ const HeaderSection = styled.div<{ justify: string }>` `; const AppsmithLogoImg = styled.img` + padding-left: ${(props) => props.theme.spaces[7]}px; max-width: 110px; `; -const BackToEditorButton = styled(Button)` - max-width: 200px; - height: 32px; - margin: 5px 10px; +const Cta = styled(Button)` + ${(props) => getTypographyByKey(props, "btnLarge")} + height: 100%; `; -const ForkButton = styled(Button)` - max-width: 200px; - height: 32px; - margin: 5px 10px; +const ForkButton = styled(Cta)` svg { transform: rotate(-90deg); } `; -const ShareButton = styled(Button)` - height: 32px; - margin: 5px 10px; - color: white !important; -`; - -const PageTab = styled(NavLink)` +const HeaderRightItemContainer = styled.div` display: flex; - height: 30px; - max-width: 170px; - margin-right: 1px; - align-self: flex-end; - cursor: pointer; align-items: center; - justify-content: center; - text-decoration: none; - background-color: rgb(49, 48, 51); - padding: 0px 10px; - && span { - font-weight: 500; - font-size: 12px; - line-height: 20px; - letter-spacing: 0.04em; - color: #fff; - max-width: 150px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - &&&:hover { - text-decoration: none; - background-color: #fff; - span { - color: #2e3d49; - } - } - &&&.is-active { - background-color: white; - span { - color: #2e3d49; - } - } + margin-right: ${(props) => props.theme.spaces[7]}px; + height: 100%; `; type AppViewerHeaderProps = { @@ -137,34 +122,17 @@ type AppViewerHeaderProps = { currentUser?: User; }; -const PageTabName: React.FunctionComponent<{ name: string }> = ({ name }) => { - const tabNameRef = useRef(null); - const [ellipsisActive, setEllipsisActive] = useState(false); - const tabNameText = {name}; - - useEffect(() => { - if (isEllipsisActive(tabNameRef?.current)) { - setEllipsisActive(true); - } - }, [tabNameRef]); - - return ellipsisActive ? ( - - {tabNameText} - - ) : ( - <>{tabNameText} - ); -}; - export const AppViewerHeader = (props: AppViewerHeaderProps) => { const { currentApplicationDetails, pages, currentOrgId, currentUser } = props; const isExampleApp = currentApplicationDetails?.appIsExample; const userPermissions = currentApplicationDetails?.userPermissions ?? []; const permissionRequired = PERMISSION_TYPE.MANAGE_APPLICATION; const canEdit = isPermitted(userPermissions, permissionRequired); - const queryParams = new URLSearchParams(useLocation().search); - const hideHeader = !!queryParams.get("embed"); + const { search } = useLocation(); + const queryParams = new URLSearchParams(search); + const isEmbed = queryParams.get("embed"); + const hideHeader = !!isEmbed; + const HtmlTitle = () => { if (!currentApplicationDetails?.name) return null; return ( @@ -174,16 +142,6 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { ); }; if (hideHeader) return ; - // Mark default page as first page - const appPages = pages; - if (appPages.length > 1) { - appPages.forEach(function(item, i) { - if (item.isDefault) { - appPages.splice(i, 1); - appPages.unshift(item); - } - }); - } const forkAppUrl = `${window.location.origin}${SIGN_UP_URL}?appId=${currentApplicationDetails?.id}`; const loginAppUrl = `${window.location.origin}${AUTH_LOGIN_URL}?appId=${currentApplicationDetails?.id}`; @@ -192,14 +150,11 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { if (props.url && canEdit) { CTA = ( - ); } else if (isExampleApp) { @@ -207,26 +162,15 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { ); } else if ( currentApplicationDetails?.isPublic && currentUser?.username === ANONYMOUS_USERNAME ) { - CTA = ( - - ); + CTA = ; } return ( @@ -234,7 +178,7 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { - + @@ -248,19 +192,11 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { <> - } +