diff --git a/app/client/src/assets/icons/control/border-radius-circle.svg b/app/client/src/assets/icons/control/border-radius-circle.svg new file mode 100755 index 0000000000..0683922aca --- /dev/null +++ b/app/client/src/assets/icons/control/border-radius-circle.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/border-radius-rounded.svg b/app/client/src/assets/icons/control/border-radius-rounded.svg new file mode 100755 index 0000000000..6457a8a822 --- /dev/null +++ b/app/client/src/assets/icons/control/border-radius-rounded.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/border-radius-sharp.svg b/app/client/src/assets/icons/control/border-radius-sharp.svg new file mode 100755 index 0000000000..3cbbd67cba --- /dev/null +++ b/app/client/src/assets/icons/control/border-radius-sharp.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/box-shadow-none.svg b/app/client/src/assets/icons/control/box-shadow-none.svg new file mode 100755 index 0000000000..341d9a49c8 --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-none.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/assets/icons/control/box-shadow-variant1.svg b/app/client/src/assets/icons/control/box-shadow-variant1.svg new file mode 100755 index 0000000000..6aa7157246 --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-variant1.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/app/client/src/assets/icons/control/box-shadow-variant2.svg b/app/client/src/assets/icons/control/box-shadow-variant2.svg new file mode 100755 index 0000000000..8fa744e9a3 --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-variant2.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/app/client/src/assets/icons/control/box-shadow-variant3.svg b/app/client/src/assets/icons/control/box-shadow-variant3.svg new file mode 100755 index 0000000000..6db02bac83 --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-variant3.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/app/client/src/assets/icons/control/box-shadow-variant4.svg b/app/client/src/assets/icons/control/box-shadow-variant4.svg new file mode 100755 index 0000000000..de2d7f492a --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-variant4.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/app/client/src/assets/icons/control/box-shadow-variant5.svg b/app/client/src/assets/icons/control/box-shadow-variant5.svg new file mode 100755 index 0000000000..7f1aebca1e --- /dev/null +++ b/app/client/src/assets/icons/control/box-shadow-variant5.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/app/client/src/assets/icons/widget/icon-button.svg b/app/client/src/assets/icons/widget/icon-button.svg new file mode 100755 index 0000000000..60f87aa313 --- /dev/null +++ b/app/client/src/assets/icons/widget/icon-button.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/components/designSystems/appsmith/IconButtonComponent.tsx b/app/client/src/components/designSystems/appsmith/IconButtonComponent.tsx new file mode 100644 index 0000000000..47c05cef9c --- /dev/null +++ b/app/client/src/components/designSystems/appsmith/IconButtonComponent.tsx @@ -0,0 +1,255 @@ +import * as React from "react"; +import { useMemo } from "react"; +import styled from "styled-components"; +import { Button } from "@blueprintjs/core"; +import { IconName } from "@blueprintjs/icons"; + +import { ComponentProps } from "components/designSystems/appsmith/BaseComponent"; +import { ThemeProp } from "components/ads/common"; +import { WIDGET_PADDING } from "constants/WidgetConstants"; + +const IconButtonContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +`; + +export interface ButtonStyleProps { + borderRadius?: ButtonBorderRadius; + boxShadow?: ButtonBoxShadow; + boxShadowColor?: string; + buttonStyle?: ButtonStyle; + buttonVariant?: ButtonVariant; + dimension: number; +} + +const StyledButton = styled(Button)` + background-image: none !important; + height: ${({ dimension }) => `${dimension}px`}; + width: ${({ dimension }) => `${dimension}px`}; + ${({ buttonStyle, buttonVariant, theme }) => ` + &:enabled { + background: ${ + buttonStyle === ButtonStyleTypes.WARNING + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.warning.solid.bgColor + : "none" + : buttonStyle === ButtonStyleTypes.DANGER + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.danger.solid.bgColor + : "none" + : buttonStyle === ButtonStyleTypes.INFO + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.info.solid.bgColor + : "none" + : buttonStyle === ButtonStyleTypes.SECONDARY + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.secondary.solid.bgColor + : "none" + : buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.primary.solid.bgColor + : "none" + } !important; + } + + &:hover:enabled, &:active:enabled { + background: ${ + buttonStyle === ButtonStyleTypes.WARNING + ? buttonVariant === ButtonVariantTypes.OUTLINE + ? theme.colors.button.warning.outline.hoverColor + : buttonVariant === ButtonVariantTypes.GHOST + ? theme.colors.button.warning.ghost.hoverColor + : theme.colors.button.warning.solid.hoverColor + : buttonStyle === ButtonStyleTypes.DANGER + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.danger.solid.hoverColor + : theme.colors.button.danger.outline.hoverColor + : buttonStyle === ButtonStyleTypes.INFO + ? buttonVariant === ButtonVariantTypes.SOLID + ? theme.colors.button.info.solid.hoverColor + : theme.colors.button.info.outline.hoverColor + : buttonStyle === ButtonStyleTypes.SECONDARY + ? buttonVariant === ButtonVariantTypes.OUTLINE + ? theme.colors.button.secondary.outline.hoverColor + : buttonVariant === ButtonVariantTypes.GHOST + ? theme.colors.button.secondary.ghost.hoverColor + : theme.colors.button.secondary.solid.hoverColor + : buttonVariant === ButtonVariantTypes.OUTLINE + ? theme.colors.button.primary.outline.hoverColor + : buttonVariant === ButtonVariantTypes.GHOST + ? theme.colors.button.primary.ghost.hoverColor + : theme.colors.button.primary.solid.hoverColor + } !important; + } + + &:disabled { + background-color: ${theme.colors.button.disabled.bgColor} !important; + color: ${theme.colors.button.disabled.textColor} !important; + } + + border: ${ + buttonVariant === ButtonVariantTypes.OUTLINE + ? buttonStyle === ButtonStyleTypes.WARNING + ? `1px solid ${theme.colors.button.warning.outline.borderColor}` + : buttonStyle === ButtonStyleTypes.DANGER + ? `1px solid ${theme.colors.button.danger.outline.borderColor}` + : buttonStyle === ButtonStyleTypes.INFO + ? `1px solid ${theme.colors.button.info.outline.borderColor}` + : buttonStyle === ButtonStyleTypes.SECONDARY + ? `1px solid ${theme.colors.button.secondary.outline.borderColor}` + : `1px solid ${theme.colors.button.primary.outline.borderColor}` + : "none" + } !important; + + & > span { + height: 100%; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + + color: ${ + buttonVariant === ButtonVariantTypes.SOLID + ? `${theme.colors.button.primary.solid.textColor}` + : buttonStyle === ButtonStyleTypes.WARNING + ? `${theme.colors.button.warning.outline.textColor}` + : buttonStyle === ButtonStyleTypes.DANGER + ? `${theme.colors.button.danger.outline.textColor}` + : buttonStyle === ButtonStyleTypes.INFO + ? `${theme.colors.button.info.outline.textColor}` + : buttonStyle === ButtonStyleTypes.SECONDARY + ? `${theme.colors.button.secondary.outline.textColor}` + : `${theme.colors.button.primary.outline.textColor}` + } !important; + } + + & > span > svg { + height: 60%; + width: 60%; + min-height: 16px; + min-width: 16px; + } + `} + + + border-radius: ${({ borderRadius }) => + borderRadius === ButtonBorderRadiusTypes.CIRCLE + ? "50%" + : borderRadius === ButtonBorderRadiusTypes.ROUNDED + ? "10px" + : 0}; + + box-shadow: ${({ boxShadow, boxShadowColor, theme }) => + boxShadow === ButtonBoxShadowTypes.VARIANT1 + ? `0px 0px 4px 3px ${boxShadowColor || + theme.colors.button.boxShadow.default.variant1}` + : boxShadow === ButtonBoxShadowTypes.VARIANT2 + ? `3px 3px 4px ${boxShadowColor || + theme.colors.button.boxShadow.default.variant2}` + : boxShadow === ButtonBoxShadowTypes.VARIANT3 + ? `0px 1px 3px ${boxShadowColor || + theme.colors.button.boxShadow.default.variant3}` + : boxShadow === ButtonBoxShadowTypes.VARIANT4 + ? `2px 2px 0px ${boxShadowColor || + theme.colors.button.boxShadow.default.variant4}` + : boxShadow === ButtonBoxShadowTypes.VARIANT5 + ? `-2px -2px 0px ${boxShadowColor || + theme.colors.button.boxShadow.default.variant5}` + : "none"} !important; +`; + +export enum ButtonStyleTypes { + PRIMARY = "PRIMARY", + WARNING = "WARNING", + DANGER = "DANGER", + INFO = "INFO", + SECONDARY = "SECONDARY", +} +export type ButtonStyle = keyof typeof ButtonStyleTypes; + +export enum ButtonVariantTypes { + SOLID = "SOLID", + OUTLINE = "OUTLINE", + GHOST = "GHOST", +} +export type ButtonVariant = keyof typeof ButtonVariantTypes; + +export enum ButtonBorderRadiusTypes { + SHARP = "SHARP", + ROUNDED = "ROUNDED", + CIRCLE = "CIRCLE", +} +export type ButtonBorderRadius = keyof typeof ButtonBorderRadiusTypes; + +export enum ButtonBoxShadowTypes { + NONE = "NONE", + VARIANT1 = "VARIANT1", + VARIANT2 = "VARIANT2", + VARIANT3 = "VARIANT3", + VARIANT4 = "VARIANT4", + VARIANT5 = "VARIANT5", +} +export type ButtonBoxShadow = keyof typeof ButtonBoxShadowTypes; + +export interface IconButtonComponentProps extends ComponentProps { + iconName?: IconName; + buttonStyle: ButtonStyle; + buttonVariant: ButtonVariant; + borderRadius: ButtonBorderRadius; + boxShadow: ButtonBoxShadow; + boxShadowColor: string; + isDisabled: boolean; + isVisible: boolean; + onClick: () => void; + height: number; + width: number; +} + +function IconButtonComponent(props: IconButtonComponentProps) { + const { + borderRadius, + boxShadow, + boxShadowColor, + buttonStyle, + buttonVariant, + height, + isDisabled, + onClick, + width, + } = props; + + /** + * returns the dimension to be used for widget + * whatever is the minimum between width and height, + * we will use that for the dimension of the widget + */ + const dimension = useMemo(() => { + console.log({ width, height }); + if (width > height) { + return height - WIDGET_PADDING * 2; + } + + return width - WIDGET_PADDING * 2; + }, [width, height]); + + return ( + + + + ); +} + +export default IconButtonComponent; diff --git a/app/client/src/components/propertyControls/BorderRadiusOptionsControl.tsx b/app/client/src/components/propertyControls/BorderRadiusOptionsControl.tsx new file mode 100644 index 0000000000..9f1d369fed --- /dev/null +++ b/app/client/src/components/propertyControls/BorderRadiusOptionsControl.tsx @@ -0,0 +1,88 @@ +import * as React from "react"; +import styled from "styled-components"; +import { Button, ButtonGroup, IButtonProps } from "@blueprintjs/core"; + +import BaseControl, { ControlProps } from "./BaseControl"; +import { ControlIcons } from "icons/ControlIcons"; +import { ThemeProp } from "components/ads/common"; +import { + ButtonBorderRadius, + ButtonBorderRadiusTypes, +} from "components/designSystems/appsmith/IconButtonComponent"; + +const StyledButtonGroup = styled(ButtonGroup)` + height: 33px; +`; + +const StyledButton = styled(Button)` + border: ${(props) => + props.active ? `1px solid #6A86CE` : `1px solid #A9A7A7`}; + border-radius: 0; + box-shadow: none !important; + background-image: none !important; + background-color: #ffffff !important; + & > div { + display: flex; + } + &.bp3-active { + box-shadow: none !important; + background-color: #ffffff !important; + } + &:hover { + background-color: #ffffff !important; + } +`; + +export interface BorderRadiusOptionsControlProps extends ControlProps { + propertyValue: ButtonBorderRadius | undefined; + onChange: (borderRaidus: ButtonBorderRadius) => void; +} + +class BorderRadiusOptionsControl extends BaseControl< + BorderRadiusOptionsControlProps +> { + constructor(props: BorderRadiusOptionsControlProps) { + super(props); + } + + static getControlType() { + return "BORDER_RADIUS_OPTIONS"; + } + + public render() { + const { propertyValue } = this.props; + + return ( + + } + large + onClick={() => this.toggleOption(ButtonBorderRadiusTypes.SHARP)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBorderRadiusTypes.ROUNDED)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBorderRadiusTypes.CIRCLE)} + /> + + ); + } + + private toggleOption = (option: ButtonBorderRadius) => { + this.updateProperty(this.props.propertyName, option); + }; +} + +export default BorderRadiusOptionsControl; diff --git a/app/client/src/components/propertyControls/BoxShadowOptionsControl.tsx b/app/client/src/components/propertyControls/BoxShadowOptionsControl.tsx new file mode 100644 index 0000000000..d86d8a8a71 --- /dev/null +++ b/app/client/src/components/propertyControls/BoxShadowOptionsControl.tsx @@ -0,0 +1,141 @@ +import * as React from "react"; +import styled from "styled-components"; +import { Button, ButtonGroup, IButtonProps } from "@blueprintjs/core"; + +import BaseControl, { ControlProps } from "./BaseControl"; +import { ControlIcons } from "icons/ControlIcons"; +import { ThemeProp } from "components/ads/common"; +import { + ButtonBoxShadow, + ButtonBoxShadowTypes, +} from "components/designSystems/appsmith/IconButtonComponent"; + +const StyledButtonGroup = styled(ButtonGroup)` + display: grid !important; + grid-template-columns: repeat(3, 1fr); + gap: 10px; + height: 100%; +`; + +const StyledButton = styled(Button)` + margin-right: 0 !important; + border: ${(props) => + props.active ? `1px solid #6A86CE` : `1px solid #E0DEDE`}; + border-radius: 0; + box-shadow: none !important; + background-image: none; + background-color: #ffffff !important; + & > div { + display: flex; + } + &.bp3-active { + box-shadow: none !important; + background-color: #ffffff !important; + } + &:hover { + background-color: #ffffff !important; + } +`; + +export interface BoxShadowOptionsControlProps extends ControlProps { + propertyValue: ButtonBoxShadow | undefined; +} + +class BoxShadowOptionsControl extends BaseControl< + BoxShadowOptionsControlProps +> { + constructor(props: BoxShadowOptionsControlProps) { + super(props); + } + + static getControlType() { + return "BOX_SHADOW_OPTIONS"; + } + + public render() { + const { propertyValue } = this.props; + + return ( + + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.NONE)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.VARIANT1)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.VARIANT2)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.VARIANT3)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.VARIANT4)} + /> + + } + large + onClick={() => this.toggleOption(ButtonBoxShadowTypes.VARIANT5)} + /> + + ); + } + + private toggleOption = (option: ButtonBoxShadow) => { + this.updateProperty(this.props.propertyName, option); + }; +} + +export default BoxShadowOptionsControl; diff --git a/app/client/src/components/propertyControls/IconSelectControl.tsx b/app/client/src/components/propertyControls/IconSelectControl.tsx index 9f09b45a42..286f293305 100644 --- a/app/client/src/components/propertyControls/IconSelectControl.tsx +++ b/app/client/src/components/propertyControls/IconSelectControl.tsx @@ -24,7 +24,6 @@ const StyledButton = styled(Button)` border: none !important; border-radius: 0; background-color: #ffffff !important; - > span.bp3-icon-caret-down { color: rgb(169, 167, 167); } @@ -38,7 +37,6 @@ const StyledMenu = styled(Menu)` max-height: 170px !important; padding-left: 5px !important; padding-right: 5px !important; - &::-webkit-scrollbar { width: 8px; background-color: #eeeeee; @@ -54,13 +52,11 @@ const StyledMenuItem = styled(MenuItem)` flex-direction: column; align-items: center; padding: 13px 5px; - &:active, &:hover, &.bp3-active { background-color: #eeeeee !important; } - > span.bp3-icon { margin-right: 0; color: #939090 !important; diff --git a/app/client/src/components/propertyControls/index.ts b/app/client/src/components/propertyControls/index.ts index 0f2c0952a4..c0a034b05b 100644 --- a/app/client/src/components/propertyControls/index.ts +++ b/app/client/src/components/propertyControls/index.ts @@ -45,6 +45,8 @@ import MultiSwitchControl, { import MenuItemsControl from "./MenuItemsControl"; import IconSelectControl from "./IconSelectControl"; import IconAlignControl from "./IconAlignControl"; +import BorderRadiusOptionsControl from "./BorderRadiusOptionsControl"; +import BoxShadowOptionsControl from "./BoxShadowOptionsControl"; export const PropertyControls = { InputTextControl, @@ -69,6 +71,8 @@ export const PropertyControls = { MenuItemsControl, IconSelectControl, IconAlignControl, + BorderRadiusOptionsControl, + BoxShadowOptionsControl, }; export type PropertyControlPropsType = diff --git a/app/client/src/constants/Colors.tsx b/app/client/src/constants/Colors.tsx index 7ab029adaf..2b783af53c 100644 --- a/app/client/src/constants/Colors.tsx +++ b/app/client/src/constants/Colors.tsx @@ -87,6 +87,29 @@ export const Colors = { OPAQ_BLUE: "rgba(106, 134, 206, 0.1)", RATE_ACTIVE: "#FFCB45", RATE_INACTIVE: "#F2F2F2", + + ICON_BUTTON_WARNING_SOLID: "#FEB811", + ICON_BUTTON_WARNING_SOLID_HOVER: "#EFA903", + ICON_BUTTON_WARNING_OUTLINE_HOVER: "#FFFAE9", + ICON_BUTTON_WARNING_GHOST_HOVER: "#FBEED0", + + ICON_BUTTON_DANGER_SOLID: "#F22B2B", + ICON_BUTTON_DANGER_SOLID_HOVER: "#B90707", + ICON_BUTTON_DANGER_NO_SOLID_HOVER: "#FDE4E4", + + ICON_BUTTON_INFO_SOLID: "#6698FF", + ICON_BUTTON_INFO_SOLID_HOVER: "#1A65FF", + ICON_BUTTON_INFO_NO_SOLID_HOVER: "#CEDCFF", + + ICON_BUTTON_PRIMARY_SOLID_HOVER: "#00693B", + ICON_BUTTON_PRIMARY_OUTLINE_HOVER: "#D9FDED", + ICON_BUTTON_PRIMARY_GHOST_HOVER: "#CBF4E2", + + BOX_SHADOW_DEFAULT_VARIANT1: "rgba(0, 0, 0, 0.25)", + BOX_SHADOW_DEFAULT_VARIANT2: "rgba(0, 0, 0, 0.25)", + BOX_SHADOW_DEFAULT_VARIANT3: "rgba(0, 0, 0, 0.5)", + BOX_SHADOW_DEFAULT_VARIANT4: "rgba(0, 0, 0, 0.25)", + BOX_SHADOW_DEFAULT_VARIANT5: "rgba(0, 0, 0, 0.25)", SELECT_DISABLED: "#ced9e080", }; export type Color = typeof Colors[keyof typeof Colors]; diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 735cba2e18..02fec91873 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -571,6 +571,134 @@ type ColorType = { overlayColor: string; button: { disabledText: ShadeColor; + boxShadow: { + default: { + variant1: Color; + variant2: Color; + variant3: Color; + variant4: Color; + variant5: Color; + }; + }; + disabled: { + bgColor: Color; + textColor: Color; + }; + /** + * PRIMARY style + */ + primary: { + solid: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + outline: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + ghost: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor?: Color; + }; + }; + /** + * WARNING style + */ + warning: { + solid: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + outline: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + ghost: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor?: Color; + }; + }; + /** + * DANGER style + */ + danger: { + solid: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + outline: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + ghost: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor?: Color; + }; + }; + /** + * INFO style + */ + info: { + solid: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + outline: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + ghost: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor?: Color; + }; + }; + /** + * SECONDARY style + */ + secondary: { + solid: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + outline: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor: Color; + }; + ghost: { + bgColor?: Color; + borderColor?: Color; + hoverColor: Color; + textColor?: Color; + }; + }; }; tertiary: buttonVariant; info: buttonVariant; @@ -1296,6 +1424,94 @@ export const dark: ColorType = { }, button: { disabledText: darkShades[6], + boxShadow: { + default: { + variant1: Colors.BOX_SHADOW_DEFAULT_VARIANT1, + variant2: Colors.BOX_SHADOW_DEFAULT_VARIANT2, + variant3: Colors.BOX_SHADOW_DEFAULT_VARIANT3, + variant4: Colors.BOX_SHADOW_DEFAULT_VARIANT4, + variant5: Colors.BOX_SHADOW_DEFAULT_VARIANT5, + }, + }, + disabled: { + bgColor: Colors.DARK_GRAY, + textColor: Colors.WHITE, + }, + primary: { + solid: { + bgColor: Colors.GREEN, + hoverColor: Colors.ICON_BUTTON_PRIMARY_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.GREEN, + hoverColor: Colors.ICON_BUTTON_PRIMARY_OUTLINE_HOVER, + textColor: Colors.GREEN, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_PRIMARY_GHOST_HOVER, + }, + }, + warning: { + solid: { + bgColor: Colors.ICON_BUTTON_WARNING_SOLID, + hoverColor: Colors.ICON_BUTTON_WARNING_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_WARNING_SOLID, + hoverColor: Colors.ICON_BUTTON_WARNING_OUTLINE_HOVER, + textColor: Colors.ICON_BUTTON_WARNING_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_WARNING_GHOST_HOVER, + }, + }, + danger: { + solid: { + bgColor: Colors.ICON_BUTTON_DANGER_SOLID, + hoverColor: Colors.ICON_BUTTON_DANGER_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_DANGER_SOLID, + hoverColor: Colors.ICON_BUTTON_DANGER_NO_SOLID_HOVER, + textColor: Colors.ICON_BUTTON_DANGER_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_DANGER_NO_SOLID_HOVER, + }, + }, + info: { + solid: { + bgColor: Colors.ICON_BUTTON_INFO_SOLID, + hoverColor: Colors.ICON_BUTTON_INFO_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_INFO_SOLID, + hoverColor: Colors.ICON_BUTTON_INFO_NO_SOLID_HOVER, + textColor: Colors.ICON_BUTTON_INFO_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_INFO_NO_SOLID_HOVER, + }, + }, + secondary: { + solid: { + bgColor: Colors.GRAY, + hoverColor: Colors.CHARCOAL, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.GRAY, + hoverColor: Colors.Gallery, + textColor: Colors.GRAY, + }, + ghost: { + hoverColor: Colors.MERCURY, + }, + }, }, tertiary: { main: "#D4D4D4", @@ -1771,6 +1987,94 @@ export const light: ColorType = { }, button: { disabledText: lightShades[6], + boxShadow: { + default: { + variant1: Colors.BOX_SHADOW_DEFAULT_VARIANT1, + variant2: Colors.BOX_SHADOW_DEFAULT_VARIANT2, + variant3: Colors.BOX_SHADOW_DEFAULT_VARIANT3, + variant4: Colors.BOX_SHADOW_DEFAULT_VARIANT4, + variant5: Colors.BOX_SHADOW_DEFAULT_VARIANT5, + }, + }, + disabled: { + bgColor: Colors.DARK_GRAY, + textColor: Colors.WHITE, + }, + primary: { + solid: { + bgColor: Colors.GREEN, + hoverColor: Colors.ICON_BUTTON_PRIMARY_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.GREEN, + hoverColor: Colors.ICON_BUTTON_PRIMARY_OUTLINE_HOVER, + textColor: Colors.GREEN, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_PRIMARY_GHOST_HOVER, + }, + }, + warning: { + solid: { + bgColor: Colors.ICON_BUTTON_WARNING_SOLID, + hoverColor: Colors.ICON_BUTTON_WARNING_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_WARNING_SOLID, + hoverColor: Colors.ICON_BUTTON_WARNING_OUTLINE_HOVER, + textColor: Colors.ICON_BUTTON_WARNING_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_WARNING_GHOST_HOVER, + }, + }, + danger: { + solid: { + bgColor: Colors.ICON_BUTTON_DANGER_SOLID, + hoverColor: Colors.ICON_BUTTON_DANGER_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_DANGER_SOLID, + hoverColor: Colors.ICON_BUTTON_DANGER_NO_SOLID_HOVER, + textColor: Colors.ICON_BUTTON_DANGER_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_DANGER_NO_SOLID_HOVER, + }, + }, + info: { + solid: { + bgColor: Colors.ICON_BUTTON_INFO_SOLID, + hoverColor: Colors.ICON_BUTTON_INFO_SOLID_HOVER, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.ICON_BUTTON_INFO_SOLID, + hoverColor: Colors.ICON_BUTTON_INFO_NO_SOLID_HOVER, + textColor: Colors.ICON_BUTTON_INFO_SOLID, + }, + ghost: { + hoverColor: Colors.ICON_BUTTON_INFO_NO_SOLID_HOVER, + }, + }, + secondary: { + solid: { + bgColor: Colors.GRAY, + hoverColor: Colors.CHARCOAL, + textColor: Colors.WHITE, + }, + outline: { + borderColor: Colors.GRAY, + hoverColor: Colors.Gallery, + textColor: Colors.GRAY, + }, + ghost: { + hoverColor: Colors.MERCURY, + }, + }, }, tertiary: { main: "#716E6E", diff --git a/app/client/src/constants/HelpConstants.ts b/app/client/src/constants/HelpConstants.ts index 4c8d820754..e9bb2bed17 100644 --- a/app/client/src/constants/HelpConstants.ts +++ b/app/client/src/constants/HelpConstants.ts @@ -135,6 +135,10 @@ export const HelpMap = { path: "/widget-reference/menu-button", searchKey: "Menu Button", }, + ICON_BUTTON_WIDGET: { + path: "/widget-reference/icon-button", + searchKey: "Icon Button", + }, }; export const HelpBaseURL = "https://docs.appsmith.com"; diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index d4e552acc8..eadc95421c 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -32,6 +32,7 @@ export enum WidgetTypes { IFRAME_WIDGET = "IFRAME_WIDGET", DIVIDER_WIDGET = "DIVIDER_WIDGET", MENU_BUTTON_WIDGET = "MENU_BUTTON_WIDGET", + ICON_BUTTON_WIDGET = "ICON_BUTTON_WIDGET", } export type WidgetType = keyof typeof WidgetTypes; diff --git a/app/client/src/icons/ControlIcons.tsx b/app/client/src/icons/ControlIcons.tsx index 7dd974aee9..2b4a0d01bf 100644 --- a/app/client/src/icons/ControlIcons.tsx +++ b/app/client/src/icons/ControlIcons.tsx @@ -50,6 +50,15 @@ import { ReactComponent as DividerCapAllIcon } from "assets/icons/control/divide import { ReactComponent as TrendingFlat } from "assets/icons/ads/trending-flat.svg"; import { ReactComponent as AlignLeftIcon } from "assets/icons/control/align_left.svg"; import { ReactComponent as AlignRightIcon } from "assets/icons/control/align_right.svg"; +import { ReactComponent as BorderRadiusSharpIcon } from "assets/icons/control/border-radius-sharp.svg"; +import { ReactComponent as BorderRadiusRoundedIcon } from "assets/icons/control/border-radius-rounded.svg"; +import { ReactComponent as BorderRadiusCircleIcon } from "assets/icons/control/border-radius-circle.svg"; +import { ReactComponent as BoxShadowNoneIcon } from "assets/icons/control/box-shadow-none.svg"; +import { ReactComponent as BoxShadowVariant1Icon } from "assets/icons/control/box-shadow-variant1.svg"; +import { ReactComponent as BoxShadowVariant2Icon } from "assets/icons/control/box-shadow-variant2.svg"; +import { ReactComponent as BoxShadowVariant3Icon } from "assets/icons/control/box-shadow-variant3.svg"; +import { ReactComponent as BoxShadowVariant4Icon } from "assets/icons/control/box-shadow-variant4.svg"; +import { ReactComponent as BoxShadowVariant5Icon } from "assets/icons/control/box-shadow-variant5.svg"; import PlayIcon from "assets/icons/control/play-icon.png"; /* eslint-disable react/display-name */ @@ -316,6 +325,51 @@ export const ControlIcons: { ), + BORDER_RADIUS_SHARP: (props: IconProps) => ( + + + + ), + BORDER_RADIUS_ROUNDED: (props: IconProps) => ( + + + + ), + BORDER_RADIUS_CIRCLE: (props: IconProps) => ( + + + + ), + BOX_SHADOW_NONE: (props: IconProps) => ( + + + + ), + BOX_SHADOW_VARIANT1: (props: IconProps) => ( + + + + ), + BOX_SHADOW_VARIANT2: (props: IconProps) => ( + + + + ), + BOX_SHADOW_VARIANT3: (props: IconProps) => ( + + + + ), + BOX_SHADOW_VARIANT4: (props: IconProps) => ( + + + + ), + BOX_SHADOW_VARIANT5: (props: IconProps) => ( + + + + ), }; export type ControlIconName = keyof typeof ControlIcons; diff --git a/app/client/src/icons/WidgetIcons.tsx b/app/client/src/icons/WidgetIcons.tsx index edc10b14a5..daadae1486 100644 --- a/app/client/src/icons/WidgetIcons.tsx +++ b/app/client/src/icons/WidgetIcons.tsx @@ -27,6 +27,7 @@ import { ReactComponent as RatingIcon } from "assets/icons/widget/rating.svg"; import { ReactComponent as EmbedIcon } from "assets/icons/widget/embed.svg"; import { ReactComponent as DividerIcon } from "assets/icons/widget/divider.svg"; import { ReactComponent as MenuButtonIcon } from "assets/icons/widget/menu-button.svg"; +import { ReactComponent as IconButtonIcon } from "assets/icons/widget/icon-button.svg"; /* eslint-disable react/display-name */ @@ -173,6 +174,11 @@ export const WidgetIcons: { ), + ICON_BUTTON_WIDGET: (props: IconProps) => ( + + + + ), }; export type WidgetIcon = typeof WidgetIcons[keyof typeof WidgetIcons]; diff --git a/app/client/src/mockResponses/WidgetConfigResponse.tsx b/app/client/src/mockResponses/WidgetConfigResponse.tsx index a0198c3b54..07ffca4c65 100644 --- a/app/client/src/mockResponses/WidgetConfigResponse.tsx +++ b/app/client/src/mockResponses/WidgetConfigResponse.tsx @@ -1,3 +1,5 @@ +import { IconNames } from "@blueprintjs/icons"; + import { WidgetConfigReducerState } from "reducers/entityReducers/widgetConfigReducer"; import { WidgetProps } from "widgets/BaseWidget"; import moment from "moment-timezone"; @@ -9,6 +11,13 @@ import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReduc import { getDynamicBindings } from "utils/DynamicBindingUtils"; import { Colors } from "constants/Colors"; import FileDataTypes from "widgets/FileDataTypes"; +import { + ButtonBorderRadiusTypes, + ButtonBoxShadowTypes, + ButtonStyleTypes, + ButtonVariantTypes, +} from "components/designSystems/appsmith/IconButtonComponent"; + /* ********************************{Grid Density Migration}********************************* */ @@ -1244,6 +1253,19 @@ const WidgetConfigResponse: WidgetConfigReducerState = { columns: 4 * GRID_DENSITY_MIGRATION_V1, widgetName: "MenuButton", }, + [WidgetTypes.ICON_BUTTON_WIDGET]: { + iconName: IconNames.PLUS, + borderRadius: ButtonBorderRadiusTypes.CIRCLE, + boxShadow: ButtonBoxShadowTypes.NONE, + buttonStyle: ButtonStyleTypes.PRIMARY, + buttonVariant: ButtonVariantTypes.SOLID, + isDisabled: false, + isVisible: true, + rows: 1 * GRID_DENSITY_MIGRATION_V1, + columns: 1 * GRID_DENSITY_MIGRATION_V1, + widgetName: "IconButton", + version: 1, + }, }, configVersion: 1, }; diff --git a/app/client/src/mockResponses/WidgetSidebarResponse.tsx b/app/client/src/mockResponses/WidgetSidebarResponse.tsx index 9dd925c8d2..a4b5e0941a 100644 --- a/app/client/src/mockResponses/WidgetSidebarResponse.tsx +++ b/app/client/src/mockResponses/WidgetSidebarResponse.tsx @@ -130,6 +130,11 @@ const WidgetSidebarResponse: WidgetCardProps[] = [ widgetCardName: "Menu Button", key: generateReactKey(), }, + { + type: "ICON_BUTTON_WIDGET", + widgetCardName: "Icon Button", + key: generateReactKey(), + }, ]; export default WidgetSidebarResponse; diff --git a/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx b/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx index 594b26ede5..074afe53c6 100644 --- a/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx +++ b/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx @@ -34,6 +34,7 @@ import { DividerWidgetProps } from "widgets/DividerWidget"; import { RateWidgetProps } from "widgets/RateWidget"; import { IframeWidgetProps } from "widgets/IframeWidget"; import { MenuButtonWidgetProps } from "widgets/MenuButtonWidget"; +import { IconButtonWidgetProps } from "widgets/IconButtonWidget"; const initialState: WidgetConfigReducerState = WidgetConfigResponse; @@ -92,6 +93,7 @@ export interface WidgetConfigReducerState { RATE_WIDGET: Partial & WidgetConfigProps; IFRAME_WIDGET: Partial & WidgetConfigProps; MENU_BUTTON_WIDGET: Partial & WidgetConfigProps; + ICON_BUTTON_WIDGET: Partial & WidgetConfigProps; }; configVersion: number; } diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index f2c56bcb88..2b5aa30836 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -123,6 +123,10 @@ import MenuButtonWidget, { MenuButtonWidgetProps, ProfiledMenuButtonWidget, } from "widgets/MenuButtonWidget"; +import IconButtonWidget, { + IconButtonWidgetProps, + ProfiledIconButtonWidget, +} from "widgets/IconButtonWidget"; export default class WidgetBuilderRegistry { static registerWidgetBuilders() { @@ -538,5 +542,18 @@ export default class WidgetBuilderRegistry { MenuButtonWidget.getMetaPropertiesMap(), MenuButtonWidget.getPropertyPaneConfig(), ); + + WidgetFactory.registerWidgetBuilder( + WidgetTypes.ICON_BUTTON_WIDGET, + { + buildWidget(widgetData: IconButtonWidgetProps): JSX.Element { + return ; + }, + }, + IconButtonWidget.getDerivedPropertiesMap(), + IconButtonWidget.getDefaultPropertiesMap(), + IconButtonWidget.getMetaPropertiesMap(), + IconButtonWidget.getPropertyPaneConfig(), + ); } } diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts index cd15ce6bc0..c61457f913 100644 --- a/app/client/src/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts @@ -287,6 +287,12 @@ export const entityDefinitions = { isVisible: isVisible, label: "string", }, + ICON_BUTTON_WIDGET: { + "!doc": + "Icon button widget is just an icon, along with all other button properties.", + "!url": "https://docs.appsmith.com/widget-reference/icon-button", + isVisible: isVisible, + }, }; export const GLOBAL_DEFS = { diff --git a/app/client/src/widgets/IconButtonWidget.tsx b/app/client/src/widgets/IconButtonWidget.tsx new file mode 100644 index 0000000000..643dbca427 --- /dev/null +++ b/app/client/src/widgets/IconButtonWidget.tsx @@ -0,0 +1,236 @@ +import * as React from "react"; +import * as Sentry from "@sentry/react"; +import { IconName } from "@blueprintjs/icons"; + +import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; +import { WidgetType, WidgetTypes } from "constants/WidgetConstants"; +import { ValidationTypes } from "constants/WidgetValidation"; +import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; + +import IconButtonComponent, { + ButtonBorderRadius, + ButtonBoxShadow, + ButtonStyle, + ButtonVariant, +} from "components/designSystems/appsmith/IconButtonComponent"; + +export interface IconButtonWidgetProps extends WidgetProps { + iconName?: IconName; + buttonStyle: ButtonStyle; + buttonVariant: ButtonVariant; + borderRadius: ButtonBorderRadius; + boxShadow: ButtonBoxShadow; + boxShadowColor: string; + isDisabled: boolean; + isVisible: boolean; + onClick?: string; +} + +class IconButtonWidget extends BaseWidget { + static getPropertyPaneConfig() { + return [ + { + sectionName: "General", + children: [ + { + propertyName: "iconName", + label: "Icon", + helpText: "Sets the icon to be used for the icon button", + controlType: "ICON_SELECT", + isBindProperty: false, + isTriggerProperty: false, + validation: { type: ValidationTypes.TEXT }, + }, + { + propertyName: "buttonStyle", + label: "Button Style", + controlType: "DROP_DOWN", + helpText: "Sets the style of the icon button", + options: [ + { + label: "Primary", + value: "PRIMARY", + }, + { + label: "Warning", + value: "WARNING", + }, + { + label: "Danger", + value: "DANGER", + }, + { + label: "Info", + value: "INFO", + }, + { + label: "Secondary", + value: "SECONDARY", + }, + ], + isBindProperty: false, + isTriggerProperty: false, + }, + { + propertyName: "buttonVariant", + label: "Button Variant", + controlType: "DROP_DOWN", + helpText: "Sets the variant of the icon button", + options: [ + { + label: "Solid", + value: "SOLID", + }, + { + label: "Outline", + value: "OUTLINE", + }, + { + label: "Ghost", + value: "GHOST", + }, + ], + isBindProperty: false, + isTriggerProperty: false, + }, + { + propertyName: "borderRadius", + label: "Border Radius", + helpText: + "Rounds the corners of the icon button's outer border edge", + controlType: "BORDER_RADIUS_OPTIONS", + isBindProperty: false, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: ["CIRCLE", "SHARP", "ROUNDED"], + }, + }, + }, + { + propertyName: "boxShadow", + label: "Box Shadow", + helpText: + "Enables you to cast a drop shadow from the frame of the widget", + controlType: "BOX_SHADOW_OPTIONS", + isBindProperty: false, + isTriggerProperty: false, + validation: { + type: ValidationTypes.TEXT, + params: { + allowedValues: [ + "NONE", + "VARIANT1", + "VARIANT2", + "VARIANT3", + "VARIANT4", + "VARIANT5", + ], + }, + }, + }, + { + propertyName: "boxShadowColor", + helpText: "Sets the shadow color of the widget", + label: "Shadow Color", + controlType: "COLOR_PICKER", + isBindProperty: false, + isTriggerProperty: false, + }, + { + propertyName: "isDisabled", + helpText: "Disables input to the widget", + label: "Disabled", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + { + propertyName: "isVisible", + helpText: "Controls the visibility of the widget", + label: "Visible", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + validation: { type: ValidationTypes.BOOLEAN }, + }, + ], + }, + { + sectionName: "Actions", + children: [ + { + helpText: "Triggers an action when the button is clicked", + propertyName: "onClick", + label: "onClick", + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + }, + ], + }, + ]; + } + + getPageView() { + const { + borderRadius, + boxShadow, + boxShadowColor, + buttonStyle, + buttonVariant, + iconName, + isDisabled, + isVisible, + widgetId, + } = this.props; + + return ( + + ); + } + + getWidgetType(): WidgetType { + return WidgetTypes.ICON_BUTTON_WIDGET; + } + + handleClick = () => { + const { onClick } = this.props; + + if (onClick) { + super.executeAction({ + triggerPropertyName: "onClick", + dynamicString: onClick, + event: { + type: EventType.ON_CLICK, + }, + }); + } + }; +} + +export default IconButtonWidget; +export const ProfiledIconButtonWidget = Sentry.withProfiler(IconButtonWidget);