## Description Issues: 1. Excess padding on right side on MainContainer and other container-like widgets. 2. End highlight not visible in deeply nested containers. 3. Modal widget takes up space on MainContainer. Causes: 1.a. Border around the MainContainer has been removed. However, the border width was still being deducted from the total width. 1.b. For parentColumnSpace calculation, CONTAINER_GRID_PADDING (= 6px) was used. However, on AutoLayout canvases, containers only account for 5px in padding, resulting in excess space of 2px on the right side. 2.a. End position highlight has negative drop zones causing it to be excluded from selection calculations. 2.b. container scrollbars are causing the drag on the canvas to not get triggered. 3.a. This happens when the modal widget is dropped in an existing flex layer. Check for `detachFromLayout` prop and move the widget to the bottom of flexLayers. Fixes # (issue) 1. https://github.com/appsmithorg/appsmith/issues/20705 2. https://github.com/appsmithorg/appsmith/issues/21311 3. https://github.com/appsmithorg/appsmith/issues/22423 4. https://github.com/appsmithorg/appsmith/issues/20111 5. https://github.com/appsmithorg/appsmith/issues/22655 Media https://user-images.githubusercontent.com/5424788/232890004-2f66b697-e84c-4625-966d-894cc63f70b7.mov ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - Manual ## Checklist: ### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag
225 lines
7.4 KiB
TypeScript
225 lines
7.4 KiB
TypeScript
import type {
|
|
MouseEventHandler,
|
|
PropsWithChildren,
|
|
ReactNode,
|
|
RefObject,
|
|
} from "react";
|
|
import React, { 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 type { WidgetStyleContainerProps } from "components/designSystems/appsmith/WidgetStyleContainer";
|
|
import WidgetStyleContainer from "components/designSystems/appsmith/WidgetStyleContainer";
|
|
import type { WidgetType } from "utils/WidgetFactory";
|
|
import { scrollCSS } from "widgets/WidgetUtils";
|
|
import { useSelector } from "react-redux";
|
|
import { getCurrentAppPositioningType } from "selectors/editorSelectors";
|
|
import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer";
|
|
import { MAIN_CONTAINER_WIDGET_ID } from "constants/WidgetConstants";
|
|
|
|
const StyledContainerComponent = styled.div<
|
|
Omit<ContainerWrapperProps, "widgetId">
|
|
>`
|
|
height: 100%;
|
|
width: 100%;
|
|
overflow: hidden;
|
|
${(props) => (!!props.dropDisabled ? `position: relative;` : ``)}
|
|
|
|
${(props) =>
|
|
props.shouldScrollContents && !props.$noScroll ? 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<HTMLDivElement>;
|
|
onClickCapture?: MouseEventHandler<HTMLDivElement>;
|
|
resizeDisabled?: boolean;
|
|
shouldScrollContents?: boolean;
|
|
backgroundColor?: string;
|
|
widgetId: string;
|
|
type: WidgetType;
|
|
dropDisabled?: boolean;
|
|
$noScroll: boolean;
|
|
}
|
|
function ContainerComponentWrapper(
|
|
props: PropsWithChildren<ContainerWrapperProps>,
|
|
) {
|
|
const containerRef: RefObject<HTMLDivElement> = useRef<HTMLDivElement>(null);
|
|
const appPositioningType = useSelector(getCurrentAppPositioningType);
|
|
|
|
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<HTMLDivElement>) => {
|
|
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<HTMLDivElement>;
|
|
|
|
fastdom.mutate(() => {
|
|
for (const elHovered of elementsHovered) {
|
|
elHovered.classList.remove("hover-styles");
|
|
}
|
|
|
|
if (!isMainContainer) {
|
|
el.classList.add("hover-styles");
|
|
}
|
|
});
|
|
}
|
|
},
|
|
[props.onClickCapture],
|
|
);
|
|
|
|
return (
|
|
<StyledContainerComponent
|
|
// Before you remove: generateClassName is used for bounding the resizables within this canvas
|
|
// getCanvasClassName is used to add a scrollable parent.
|
|
$noScroll={props.$noScroll}
|
|
backgroundColor={props.backgroundColor}
|
|
className={`${
|
|
props.shouldScrollContents ? getCanvasClassName() : ""
|
|
} ${generateClassName(props.widgetId)} container-with-scrollbar ${
|
|
appPositioningType === AppPositioningTypes.AUTO &&
|
|
props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
|
? "auto-layout"
|
|
: ""
|
|
}`}
|
|
data-widgetId={props.widgetId}
|
|
dropDisabled={props.dropDisabled}
|
|
onClick={props.onClick}
|
|
onClickCapture={props.onClickCapture}
|
|
onMouseOver={onMouseOver}
|
|
ref={containerRef}
|
|
resizeDisabled={props.resizeDisabled}
|
|
shouldScrollContents={!!props.shouldScrollContents}
|
|
tabIndex={props.shouldScrollContents ? undefined : 0}
|
|
type={props.type}
|
|
>
|
|
{props.children}
|
|
</StyledContainerComponent>
|
|
);
|
|
}
|
|
|
|
function ContainerComponent(props: ContainerComponentProps) {
|
|
if (props.detachFromLayout) {
|
|
return (
|
|
<ContainerComponentWrapper
|
|
$noScroll={!!props.noScroll}
|
|
dropDisabled={props.dropDisabled}
|
|
onClick={props.onClick}
|
|
onClickCapture={props.onClickCapture}
|
|
resizeDisabled={props.resizeDisabled}
|
|
shouldScrollContents={
|
|
props.shouldScrollContents &&
|
|
props.appPositioningType !== AppPositioningTypes.AUTO
|
|
}
|
|
type={props.type}
|
|
widgetId={props.widgetId}
|
|
>
|
|
{props.children}
|
|
</ContainerComponentWrapper>
|
|
);
|
|
}
|
|
return (
|
|
<WidgetStyleContainer
|
|
backgroundColor={props.backgroundColor}
|
|
borderColor={props.borderColor}
|
|
borderRadius={props.borderRadius}
|
|
borderWidth={props.borderWidth}
|
|
boxShadow={props.boxShadow}
|
|
className="style-container"
|
|
containerStyle={props.containerStyle}
|
|
selected={props.selected}
|
|
widgetId={props.widgetId}
|
|
>
|
|
<ContainerComponentWrapper
|
|
$noScroll={!!props.noScroll}
|
|
backgroundColor={props.backgroundColor}
|
|
dropDisabled={props.dropDisabled}
|
|
onClick={props.onClick}
|
|
onClickCapture={props.onClickCapture}
|
|
resizeDisabled={props.resizeDisabled}
|
|
shouldScrollContents={
|
|
props.shouldScrollContents &&
|
|
props.appPositioningType !== AppPositioningTypes.AUTO // Disable scrollbar on autolayout canvas as it meddles with canvas drag and highlight position.
|
|
}
|
|
type={props.type}
|
|
widgetId={props.widgetId}
|
|
>
|
|
{props.children}
|
|
</ContainerComponentWrapper>
|
|
</WidgetStyleContainer>
|
|
);
|
|
}
|
|
|
|
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<HTMLDivElement>;
|
|
onClickCapture?: MouseEventHandler<HTMLDivElement>;
|
|
backgroundColor?: string;
|
|
type: WidgetType;
|
|
noScroll?: boolean;
|
|
minHeight?: number;
|
|
useAutoLayout?: boolean;
|
|
direction?: string;
|
|
justifyContent?: string;
|
|
alignItems?: string;
|
|
dropDisabled?: boolean;
|
|
appPositioningType?: AppPositioningTypes;
|
|
}
|
|
|
|
export default ContainerComponent;
|