import React, { MouseEventHandler, PropsWithChildren, ReactNode, RefObject, useCallback, useEffect, useRef, } from "react"; import styled from "styled-components"; import tinycolor from "tinycolor2"; import fastdom from "fastdom"; import { generateClassName, getCanvasClassName } from "utils/generators"; import WidgetStyleContainer, { WidgetStyleContainerProps, } from "components/designSystems/appsmith/WidgetStyleContainer"; import { WidgetType } from "utils/WidgetFactory"; import { scrollCSS } from "widgets/WidgetUtils"; const StyledContainerComponent = styled.div< Omit >` height: 100%; width: 100%; overflow: hidden; ${(props) => (!!props.dropDisabled ? `position: relative;` : ``)} ${(props) => (props.shouldScrollContents ? scrollCSS : ``)} opacity: ${(props) => (props.resizeDisabled ? "0.8" : "1")}; background: ${(props) => props.backgroundColor}; &:hover { background-color: ${(props) => { return props.onClickCapture && props.backgroundColor ? tinycolor(props.backgroundColor) .darken(5) .toString() : props.backgroundColor; }}; z-index: ${(props) => (props.onClickCapture ? "2" : "1")}; cursor: ${(props) => (props.onClickCapture ? "pointer" : "inherit")}; } `; interface ContainerWrapperProps { onClick?: MouseEventHandler; onClickCapture?: MouseEventHandler; resizeDisabled?: boolean; shouldScrollContents?: boolean; backgroundColor?: string; widgetId: string; type: WidgetType; dropDisabled?: boolean; } function ContainerComponentWrapper( props: PropsWithChildren, ) { const containerRef: RefObject = useRef(null); useEffect(() => { if (!props.shouldScrollContents) { const supportsNativeSmoothScroll = "scrollBehavior" in document.documentElement.style; fastdom.mutate(() => { if (supportsNativeSmoothScroll) { containerRef.current?.scrollTo({ top: 0, behavior: "smooth" }); } else { if (containerRef.current) { containerRef.current.scrollTop = 0; } } }); } }, [props.shouldScrollContents]); /** * This is for all the container widgets that have the onClickCapture method. * The mouse over event makes sure to add the class `hover-styles` so that a * darker shade of the background color takes effect to induce the hover effect. * * Why not use the :hover css selector? * For cases like List widget, it can have inner list widgets; so there can be * containers inside containers. When the inner container is hovered, the parent container's * :hover selector is also triggered making the outer and inner container both having this * hover effect. */ const onMouseOver = useCallback( (e: React.MouseEvent) => { const el = e.currentTarget; const widgetType = el.getAttribute("type"); const widgetId = el.dataset.widgetid; const isMainContainer = widgetId === "0"; if ( (widgetType === "CONTAINER_WIDGET" && props.onClickCapture) || isMainContainer ) { const elementsHovered = document.getElementsByClassName( "hover-styles", ) as HTMLCollectionOf; fastdom.mutate(() => { for (const elHovered of elementsHovered) { elHovered.classList.remove("hover-styles"); } if (!isMainContainer) { el.classList.add("hover-styles"); } }); } }, [props.onClickCapture], ); return ( {props.children} ); } function ContainerComponent(props: ContainerComponentProps) { if (props.detachFromLayout) { return ( {props.children} ); } return ( {props.children} ); } export type ContainerStyle = "border" | "card" | "rounded-border" | "none"; export interface ContainerComponentProps extends WidgetStyleContainerProps { children?: ReactNode; shouldScrollContents?: boolean; resizeDisabled?: boolean; selected?: boolean; focused?: boolean; detachFromLayout?: boolean; onClick?: MouseEventHandler; onClickCapture?: MouseEventHandler; backgroundColor?: string; type: WidgetType; noScroll?: boolean; dropDisabled?: boolean; } export default ContainerComponent;