PromucFlow_constructor/app/client/src/components/ads/Button.tsx
Pranav Kanade f414285201
[Feature] Unified New Nav (#5558)
* temp commit

* using onsubmit to continue using action on form

* added recaptcha site key to env example file

* moved the recaptcha lib loading logic to signup page

* removed unnecessary edit

* handle the case where the recaptcha token is not provided as env var

* added proper env var config for client

* recaptcha config for ansible

* recaptcha config for heroku

* recaptcha config for k8s

* updated app.json

* fixed the typos

* added more description for env vars

* removed api key

* minor typo fix

* added new integration button

* updated the add int default link

* added active and create new tabs

* added the empty components to tabs. will control the section manually.

* added proper grid for integrations page

* added vertical tabs

* Added secondary tabs to integrations page

* added separate page for new apis

* classname changes

* added new components for active queries, new queries etc.

* added a separate component for data source list

* adding screen component conditionally, to be showing upon user's choice

* 1. Added grid styling to datasource home
2. Added connect buttons to em

* fixed data source security banner

* updated the styling for new api page

* added tertiary menu for active integrations

* updated styling for active connections

* updated collapse component to work properly

* added show more option to active data sources

* Slash commands feature init commit

* Added more commands

* Introduced JSX to render custom commands

* Merge conflict fix

* Spacing changes

* removed apis/db tabs and replaced em with integrations tab

* removed the unnecessary + integrations btn

* Added slash commands button

* Adjust styles for better ui

* Ordered the action entries under integrations

* Added new datasource command

* updated the getURL with proper params

* updated the link of create datasource btn

* updated the back btn link from data source editor

* Show connect data cta in property pane

* Styling fixes

* Fix margin

* added scrollable content to create new

* added on click scroll to create new page

* fixed a bug, creating new datasource twice

* added new action creator for integrations.

* Minor changes to add new bindings command.
Changed ui behaviour of / button

* UI style change

* updated the query editor to match the over all theme

* updated the query editor tabs

* Added the run btn to empty response screens

* minor fix

* updated the bg color of api type drop down

* updated the url being visited after delete api/query

* removed log

* Insert binding command UI change

* More UI changes

* removed unnecessary junk from integrations editor index

* clean up, removed unnecessary files

* removed useless routes

* for debugger only checking if integrations editor

* Removed all the links for api/query home pages

* Move command actions to a saga
Added support to binding the data back to the widget when are new API is created from widget

* Added reverse binding for DB queries

* Show / button only on hover

* not routing to integrations on create query/api

* Hide actions from suggestions in action pages

* removed the query/datasource/api home pages

* Changes widget.data to widget in slash commands

* Show dependencies in property pane

* Fix warning

* fixed scrolling issue

* will show a list of queries and apis for action picker

* showing icons for each action under integrations

* Fix dropdown not showing up

* Minor refactoring.
Changed commands

* added a way to list data sources in action creators

* Update query page url

* cam show icons for datasources

* Removed unused code

* Feature/slash commands (#5002)

* Slash commands feature init commit

* Added more commands

* Introduced JSX to render custom commands

* Merge conflict fix

* Spacing changes

* Added slash commands button

* Adjust styles for better ui

* Added new datasource command

* Minor changes to add new bindings command.
Changed ui behaviour of / button

* UI style change

* Insert binding command UI change

* More UI changes

* Move command actions to a saga
Added support to binding the data back to the widget when are new API is created from widget

* Added reverse binding for DB queries

* Show / button only on hover

* Hide actions from suggestions in action pages

* Changes widget.data to widget in slash commands

* Minor refactoring.
Changed commands

* Removed unused code

* remove more unusued code

* Added support to generate new api from a datasource in quick commands

* Code correction to use types

* Refactored commands code

* Minor bug fixes

* Remove new integrations command for actions.
Fixed autocomplete not showing up

* Changes to prevent autocomplete trigger for navigation commands

* Prevent hinter execution when show hint is open already.

* Show hinter on focus

* Update text to be called in the omnibar

* updated the copy for empty active datasources

* Update url

* Fix text decoration

* updated the redirection for back btns

* Use themes

* Add cypress test

* fixed back btn nav

* fetching form configs for datasources

* a callback fixed

* Fix slash command not executed on click (#5540)

* Replace the value if not a string else append

* Log commands menu events

* updated mock data base navigation

* updated mock data base navigation

* updated the close editors and back buttons

* All back btns from editors will go back to data sources and back from data source will go back to canvas

* fixed bg colors

* minor styled updates

* removed margin from header of generic datasource

* warnings fixes

* If user is already on the location not redirecting em

* when editing, will check if the coming from data source and redirect accordingly

* updated redirection for newly created api/queries

* updated back btn for newly created datasources

* back for new curl goes to data sources

* Revert "[Fix] revert new nav (#5533)"

This reverts commit 1647815d

* remaining original reverted chagnes

* fixed the width of incoming/outgoing entity bar in property pane

* removing residue from resolved merge conflicts

* Fix widget icons not visible in dropdown menu

* minor fix to use proper integration URL

* updated the URLs for unified datasources

* converted back and close to btns from banners

* on accessing data source from sidebar, it'll always go to view mode

* updated the edit path for saas editors

* Added saved state for google sheet

* on google sheet delete redirecting to create new

* minor fix

* fixed the redirection call on saving a datasource

* removed save and test cmd as it wasn't needed

* Removing test cases to be fixed by Arun

* commenting more tests to be fixed by Arun

* updated call api cy command

* Fix extra margin issue

* fixed the update datasource saga

* fixed video spec

* Revert "commenting more tests to be fixed by Arun"

This reverts commit 42087a95ad77107401a1619e4c2d4c541a81d6c3.

* Revert "Removing test cases to be fixed by Arun"

This reverts commit f6fad67e558d22045114a90409428ef9b737478f.

* fixed the entity explorer query datasource spec

* cautious fix

* update widget locators

* fixed leave org test

* fixes for FormWidgets

* updated the image spec

* Use memo

* Fix debugger url checks

* for copy and delete widget pointing directly to svgs

* Fix entity text

* Fix styling and show tooltip for property pane dependencies

* removed the unnecessary callback

* added a separate saga to to redirect to new integrations using onSuccess

* Bug Fixes - New nav (#5629)

* will show scrollbar only on hover

* made mock data cards clickable

* fixed the grid view

* fixed the cursor position when clicking on / btn

* updated the hint for `/` command

* binding prompt will close on focus change

* hiding / command for api body

* hiding / command for query pane

* Added 2 new icons

* Fix cursor position on selecting a binding and clicking on the slash menu button

* trying out fix to copyWidget cy command

* removing zero width space characters from the property pane text

Co-authored-by: arunvjn <arun@appsmith.com>
Co-authored-by: Akash N <akash@codemonk.in>
Co-authored-by: arunvjn <32433245+arunvjn@users.noreply.github.com>
Co-authored-by: Rishabh Saxena <rishabh.robben@gmail.com>
2021-07-07 09:16:16 +05:30

455 lines
13 KiB
TypeScript

import React from "react";
import {
CommonComponentProps,
hexToRgba,
ThemeProp,
Classes,
Variant,
} from "./common";
import styled, { css } from "styled-components";
import Icon, { IconName, IconSize } from "./Icon";
import Spinner from "./Spinner";
import { mediumButton, smallButton, largeButton } from "constants/DefaultTheme";
export enum Category {
primary = "primary",
secondary = "secondary",
tertiary = "tertiary",
}
export enum Size {
xxs = "xxs",
xs = "xs",
small = "small",
medium = "medium",
large = "large",
}
type stateStyleType = {
bgColorPrimary: string;
borderColorPrimary: string;
txtColorPrimary: string;
bgColorSecondary: string;
borderColorSecondary: string;
txtColorSecondary: string;
bgColorTertiary: string;
borderColorTertiary: string;
txtColorTertiary: string;
};
type BtnColorType = {
bgColor: string;
txtColor: string;
border: string;
};
type BtnFontType = {
buttonFont: any;
padding: string;
height: number;
};
type ButtonProps = CommonComponentProps & {
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
text?: string;
category?: Category;
variant?: Variant;
className?: string;
icon?: IconName;
size?: Size;
fill?: boolean;
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: undefined,
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,
stateArg: string,
): stateStyleType => {
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 {
...styles,
...stylesByState[state],
};
};
const btnColorStyles = (
props: ThemeProp & ButtonProps,
state: string,
): BtnColorType => {
let bgColor = "",
txtColor = "",
border = "";
switch (props.category) {
case Category.primary:
bgColor = stateStyles(props, state).bgColorPrimary;
txtColor = stateStyles(props, state).txtColorPrimary;
border = `2px solid ${stateStyles(props, state).borderColorPrimary}`;
break;
case Category.secondary:
bgColor = stateStyles(props, state).bgColorSecondary;
txtColor = stateStyles(props, state).txtColorSecondary;
border = `2px solid ${stateStyles(props, state).borderColorSecondary}`;
break;
case Category.tertiary:
bgColor = stateStyles(props, state).bgColorTertiary;
txtColor = stateStyles(props, state).txtColorTertiary;
border = `2px solid ${stateStyles(props, state).borderColorTertiary}`;
break;
}
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 => {
const padding = getPaddingBySize(props);
const height = getHeightBySize(props);
const buttonFont = getBtnFontBySize(props);
return { buttonFont, padding, height };
};
const ButtonStyles = css<ThemeProp & ButtonProps>`
width: ${(props) => (props.fill ? "100%" : "auto")};
height: ${(props) => btnFontStyles(props).height}px;
border: none;
text-decoration: none;
outline: none;
text-transform: uppercase;
background-color: ${(props) => btnColorStyles(props, "main").bgColor};
color: ${(props) => btnColorStyles(props, "main").txtColor};
border: ${(props) => btnColorStyles(props, "main").border};
border-radius: 0;
${(props) => btnFontStyles(props).buttonFont};
padding: ${(props) => btnFontStyles(props).padding};
.${Classes.ICON} {
margin-right: ${(props) =>
props.text && props.icon ? `${props.theme.spaces[2] - 1}px` : `0`};
path {
fill: ${(props) => btnColorStyles(props, "main").txtColor};
}
}
&:hover {
text-decoration: none;
background-color: ${(props) => btnColorStyles(props, "hover").bgColor};
color: ${(props) => btnColorStyles(props, "hover").txtColor};
border: ${(props) => btnColorStyles(props, "hover").border};
cursor: ${(props) =>
props.isLoading || props.disabled ? `not-allowed` : `pointer`};
.${Classes.ICON} {
margin-right: ${(props) =>
props.text && props.icon ? `${props.theme.spaces[2] - 1}px` : `0`};
path {
fill: ${(props) => btnColorStyles(props, "hover").txtColor};
}
}
}
font-style: normal;
&:active {
background-color: ${(props) => btnColorStyles(props, "active").bgColor};
color: ${(props) => btnColorStyles(props, "active").txtColor};
border: ${(props) => btnColorStyles(props, "active").border};
cursor: ${(props) =>
props.isLoading || props.disabled ? `not-allowed` : `pointer`};
.${Classes.ICON} {
path {
fill: ${(props) => btnColorStyles(props, "active").txtColor};
}
}
}
display: flex;
align-items: center;
justify-content: center;
position: relative;
.${Classes.SPINNER} {
position: absolute;
left: 0;
right: 0;
margin-left: auto;
margin-right: auto;
}
`;
const StyledButton = styled("button")`
${ButtonStyles}
`;
const StyledLinkButton = styled("a")`
${ButtonStyles}
`;
export const VisibilityWrapper = styled.div`
visibility: hidden;
`;
const IconSizeProp = (size?: Size) => {
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;
};
function TextLoadingState({ text }: { text?: string }) {
return <VisibilityWrapper>{text}</VisibilityWrapper>;
}
function IconLoadingState({ icon, size }: { size?: Size; icon?: IconName }) {
return <Icon invisible name={icon} size={IconSizeProp(size)} />;
}
const getIconContent = (props: ButtonProps) =>
props.icon ? (
props.isLoading ? (
<IconLoadingState {...props} />
) : (
<Icon name={props.icon} size={IconSizeProp(props.size)} />
)
) : null;
const getTextContent = (props: ButtonProps) =>
props.text ? (
props.isLoading ? (
<TextLoadingState text={props.text} />
) : (
props.text
)
) : null;
const getButtonContent = (props: ButtonProps) => (
<>
{getIconContent(props)}
<span>{getTextContent(props)}</span>
{props.isLoading ? <Spinner size={IconSizeProp(props.size)} /> : null}
</>
);
function ButtonComponent(props: ButtonProps) {
return (
<StyledButton
className={props.className}
data-cy={props.cypressSelector}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledButton>
);
}
function LinkButtonComponent(props: ButtonProps) {
return (
<StyledLinkButton
className={props.className}
data-cy={props.cypressSelector}
href={props.href}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledLinkButton>
);
}
function Button(props: ButtonProps) {
return props.tag === "button" ? (
<ButtonComponent {...props} />
) : (
<LinkButtonComponent {...props} />
);
}
export default Button;
Button.defaultProps = defaultProps;