diff --git a/app/client/src/layoutSystems/anvil/common/AnvilFlexComponent.tsx b/app/client/src/layoutSystems/anvil/common/AnvilFlexComponent.tsx
index e62650358e..e74af5aa01 100644
--- a/app/client/src/layoutSystems/anvil/common/AnvilFlexComponent.tsx
+++ b/app/client/src/layoutSystems/anvil/common/AnvilFlexComponent.tsx
@@ -68,6 +68,7 @@ export const AnvilFlexComponent = forwardRef(
flexBasis: isFillWidget ? "0%" : "auto",
padding: "spacing-1",
alignItems: "center",
+ width: "max-content",
};
if (widgetSize) {
const { maxHeight, maxWidth, minHeight, minWidth } = widgetSize;
diff --git a/app/client/src/layoutSystems/anvil/integrations/layoutSelectors.ts b/app/client/src/layoutSystems/anvil/integrations/layoutSelectors.ts
new file mode 100644
index 0000000000..c4ad61945b
--- /dev/null
+++ b/app/client/src/layoutSystems/anvil/integrations/layoutSelectors.ts
@@ -0,0 +1,36 @@
+import { getLayoutElementPositions } from "layoutSystems/common/selectors";
+import type {
+ LayoutElementPosition,
+ LayoutElementPositions,
+} from "layoutSystems/common/types";
+import { createSelector } from "reselect";
+import { AlignmentIndexMap } from "../utils/constants";
+import { FlexLayerAlignment } from "layoutSystems/common/utils/constants";
+
+export const ALIGNMENT_WIDTH_THRESHOLD = 0.95;
+
+export function shouldOverrideAlignmentStyle(layoutId: string) {
+ return createSelector(
+ getLayoutElementPositions,
+ (positions: LayoutElementPositions): boolean => {
+ if (!layoutId || !positions || !positions[layoutId]) return false;
+
+ // If positions don't exist for start alignment, return false as this layout is not aligned.
+ if (!positions[`${layoutId}-0`]) return false;
+
+ const layoutPosition: LayoutElementPosition = positions[layoutId];
+ const threshold = layoutPosition.width * ALIGNMENT_WIDTH_THRESHOLD;
+
+ // return true if width of any alignment exceeds the limit.
+ return [
+ FlexLayerAlignment.Start,
+ FlexLayerAlignment.Center,
+ FlexLayerAlignment.End,
+ ].some((each: FlexLayerAlignment) => {
+ const alignmentPosition: LayoutElementPosition =
+ positions[`${layoutId}-${AlignmentIndexMap[each]}`];
+ return alignmentPosition.width >= threshold;
+ });
+ },
+ );
+}
diff --git a/app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/AlignedWidgetRowComp.tsx b/app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/AlignedWidgetRowComp.tsx
new file mode 100644
index 0000000000..a4e9905e38
--- /dev/null
+++ b/app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/AlignedWidgetRowComp.tsx
@@ -0,0 +1,194 @@
+import React, { useEffect, useMemo, useState } from "react";
+import {
+ LayoutComponentTypes,
+ type LayoutComponentProps,
+ type WidgetLayoutProps,
+} from "layoutSystems/anvil/utils/anvilTypes";
+import {
+ AlignmentIndexMap,
+ MOBILE_BREAKPOINT,
+} from "layoutSystems/anvil/utils/constants";
+import { FlexLayerAlignment } from "layoutSystems/common/utils/constants";
+import { renderWidgets } from "layoutSystems/anvil/utils/layouts/renderUtils";
+import { FlexLayout, type FlexLayoutProps } from "../FlexLayout";
+import { isFillWidgetPresentInList } from "layoutSystems/anvil/utils/layouts/widgetUtils";
+import { getAnvilLayoutDOMId } from "layoutSystems/common/utils/LayoutElementPositionsObserver/utils";
+import {
+ ALIGNMENT_WIDTH_THRESHOLD,
+ shouldOverrideAlignmentStyle,
+} from "layoutSystems/anvil/integrations/layoutSelectors";
+import { useSelector } from "react-redux";
+import { RenderModes } from "constants/WidgetConstants";
+
+/**
+ * If AlignedRow hasFillWidget:
+ * then render all children directly within the AlignedRow (row / flex-start / wrap);
+ * no need for alignments.
+ *
+ * Else:
+ * render children in 3 alignments: start, center and end.
+ * Each alignment has following characteristics:
+ * 1. Mobile viewport:
+ * - flex-wrap: wrap.
+ * - flex-basis: auto.
+ * ~ This ensures the alignment takes up as much space as needed by the children.
+ * ~ It can stretch to the full width of the viewport.
+ * ~ or collapse completely if there is no content.
+ *
+ * 2. Larger view ports:
+ * - flex-wrap: nowrap.
+ * - flex-basis: 0%.
+ * ~ This ensures that alignments share the total space equally, until possible.
+ * ~ Soon as the content in any alignment needs more space, it will wrap to the next line
+ * thanks to flex wrap in the parent layout.
+ */
+const AlignedWidgetRowComp = (props: LayoutComponentProps) => {
+ const { canvasId, layout, layoutId, renderMode } = props;
+ // Whether default alignment styles should be overridden, when renderMode = Canvas.
+ const shouldOverrideStyle: boolean = useSelector(
+ shouldOverrideAlignmentStyle(layoutId),
+ );
+
+ // check if layout renders a Fill widget.
+ const hasFillWidget: boolean = isFillWidgetPresentInList(
+ layout as WidgetLayoutProps[],
+ );
+
+ const [isAnyAlignmentOverflowing, setIsAnyAlignmentOverflowing] =
+ useState(false);
+
+ useEffect(() => {
+ // getBoundingClientRect is an expensive operation and should only be used when renderMode = Page,
+ // because layout positions are not available in that case.
+ if (hasFillWidget || renderMode !== RenderModes.PAGE) return;
+ const parentLayoutId = getAnvilLayoutDOMId(canvasId, layoutId);
+ const parentLayout = document.getElementById(parentLayoutId);
+ if (parentLayout) {
+ const parentLayoutWidth = parentLayout.getBoundingClientRect().width;
+
+ // Use requestAnimationFrame to ensure calculation is done after rendering
+ requestAnimationFrame(() => {
+ const isOverflowing = [
+ FlexLayerAlignment.Start,
+ FlexLayerAlignment.Center,
+ FlexLayerAlignment.End,
+ ].some((each: FlexLayerAlignment) => {
+ const alignmentId = `${parentLayoutId}-${AlignmentIndexMap[each]}`;
+ const alignment = document.getElementById(alignmentId);
+ if (!alignment) return false;
+ const alignmentWidth = alignment.getBoundingClientRect().width;
+ // return true if width of any alignment exceeds the limit.
+ return (
+ alignmentWidth >= parentLayoutWidth * ALIGNMENT_WIDTH_THRESHOLD
+ );
+ });
+ setIsAnyAlignmentOverflowing(isOverflowing);
+ });
+ }
+ }, [hasFillWidget, layout.length, renderMode]);
+
+ useEffect(() => {
+ if (hasFillWidget || renderMode === RenderModes.PAGE) return;
+ setIsAnyAlignmentOverflowing(shouldOverrideStyle);
+ }, [hasFillWidget, renderMode, shouldOverrideStyle]);
+
+ const commonProps: Omit<
+ FlexLayoutProps,
+ "children" | "layoutId" | "layoutIndex"
+ > = useMemo(() => {
+ return {
+ alignSelf: "stretch",
+ canvasId,
+ direction: "row",
+ flexBasis: isAnyAlignmentOverflowing
+ ? { base: "auto" }
+ : { base: "auto", [`${MOBILE_BREAKPOINT}px`]: "0%" },
+ flexGrow: 1,
+ flexShrink: 1,
+ layoutType: LayoutComponentTypes.WIDGET_ROW,
+ parentDropTarget: props.parentDropTarget,
+ renderMode: props.renderMode,
+ wrap: isAnyAlignmentOverflowing
+ ? { base: "wrap" }
+ : { base: "wrap", [`${MOBILE_BREAKPOINT}px`]: "nowrap" },
+ className: props.className,
+ };
+ }, [isAnyAlignmentOverflowing]);
+
+ // If a Fill widget exists, then render the child widgets together.
+ if (hasFillWidget) {
+ return <>{renderWidgets(props)}>;
+ }
+
+ /**
+ * else render the child widgets separately
+ * in their respective alignments.
+ */
+ const startChildren: WidgetLayoutProps[] = (
+ layout as WidgetLayoutProps[]
+ ).filter(
+ (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.Start,
+ );
+ const centerChildren: WidgetLayoutProps[] = (
+ layout as WidgetLayoutProps[]
+ ).filter(
+ (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.Center,
+ );
+ const endChildren: WidgetLayoutProps[] = (
+ layout as WidgetLayoutProps[]
+ ).filter(
+ (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.End,
+ );
+
+ // TODO: After positionObserver integration,
+ // check if use of FlexLayout is causing performance or other issues.
+ // WDS Flex can be used as a replacement.
+ return (
+ <>
+
+ {renderWidgets({
+ ...props,
+ layout: startChildren,
+ })}
+
+
+ {renderWidgets(
+ {
+ ...props,
+ layout: centerChildren,
+ },
+ startChildren?.length,
+ )}
+
+
+ {renderWidgets(
+ {
+ ...props,
+ layout: endChildren,
+ },
+ startChildren?.length + centerChildren?.length,
+ )}
+
+ >
+ );
+};
+
+export default AlignedWidgetRowComp;
diff --git a/app/client/src/layoutSystems/anvil/layoutComponents/components/AlignedWidgetRow.tsx b/app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/index.tsx
similarity index 75%
rename from app/client/src/layoutSystems/anvil/layoutComponents/components/AlignedWidgetRow.tsx
rename to app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/index.tsx
index f1e9713019..50abb014c2 100644
--- a/app/client/src/layoutSystems/anvil/layoutComponents/components/AlignedWidgetRow.tsx
+++ b/app/client/src/layoutSystems/anvil/layoutComponents/components/alignedWidgetRow/index.tsx
@@ -1,11 +1,12 @@
-import BaseLayoutComponent from "../BaseLayoutComponent";
+import React from "react";
+import BaseLayoutComponent from "../../BaseLayoutComponent";
import {
type DeriveHighlightsFn,
LayoutComponentTypes,
} from "layoutSystems/anvil/utils/anvilTypes";
-import type { FlexLayoutProps } from "./FlexLayout";
+import type { FlexLayoutProps } from "../FlexLayout";
import { deriveAlignedRowHighlights } from "layoutSystems/anvil/utils/layouts/highlights/alignedRowHighlights";
-import { renderWidgetsInAlignedRow } from "layoutSystems/anvil/utils/layouts/renderUtils";
+import AlignedWidgetRowComp from "./AlignedWidgetRowComp";
class AlignedWidgetRow extends BaseLayoutComponent {
static type: LayoutComponentTypes = LayoutComponentTypes.ALIGNED_WIDGET_ROW;
@@ -13,7 +14,7 @@ class AlignedWidgetRow extends BaseLayoutComponent {
static deriveHighlights: DeriveHighlightsFn = deriveAlignedRowHighlights;
renderChildWidgets(): React.ReactNode {
- return renderWidgetsInAlignedRow(this.props);
+ return ;
}
static rendersWidgets: boolean = true;
diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/highlights/alignedRowHighlights.test.ts b/app/client/src/layoutSystems/anvil/utils/layouts/highlights/alignedRowHighlights.test.ts
index cb3711d848..6f8c08fb3d 100644
--- a/app/client/src/layoutSystems/anvil/utils/layouts/highlights/alignedRowHighlights.test.ts
+++ b/app/client/src/layoutSystems/anvil/utils/layouts/highlights/alignedRowHighlights.test.ts
@@ -11,7 +11,7 @@ import {
} from "layoutSystems/common/utils/constants";
import { HIGHLIGHT_SIZE } from "../../constants";
import LayoutFactory from "layoutSystems/anvil/layoutComponents/LayoutFactory";
-import AlignedWidgetRow from "layoutSystems/anvil/layoutComponents/components/AlignedWidgetRow";
+import AlignedWidgetRow from "layoutSystems/anvil/layoutComponents/components/alignedWidgetRow";
import type { BaseWidgetProps } from "widgets/BaseWidgetHOC/withBaseWidgetHOC";
import { mockButtonProps } from "mocks/widgetProps/button";
import { getAlignmentLayoutId } from "../layoutUtils";
diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/layoutUtils.ts b/app/client/src/layoutSystems/anvil/utils/layouts/layoutUtils.ts
index 7c2df6f0fd..71658d8dd3 100644
--- a/app/client/src/layoutSystems/anvil/utils/layouts/layoutUtils.ts
+++ b/app/client/src/layoutSystems/anvil/utils/layouts/layoutUtils.ts
@@ -9,7 +9,7 @@ import type { FlexLayerAlignment } from "layoutSystems/common/utils/constants";
import { AlignmentIndexMap } from "../constants";
import AlignedLayoutColumn from "layoutSystems/anvil/layoutComponents/components/AlignedLayoutColumn";
import AlignedWidgetColumn from "layoutSystems/anvil/layoutComponents/components/AlignedWidgetColumn";
-import AlignedWidgetRow from "layoutSystems/anvil/layoutComponents/components/AlignedWidgetRow";
+import AlignedWidgetRow from "layoutSystems/anvil/layoutComponents/components/alignedWidgetRow";
import LayoutColumn from "layoutSystems/anvil/layoutComponents/components/LayoutColumn";
import LayoutRow from "layoutSystems/anvil/layoutComponents/components/LayoutRow";
import WidgetColumn from "layoutSystems/anvil/layoutComponents/components/WidgetColumn";
diff --git a/app/client/src/layoutSystems/anvil/utils/layouts/renderUtils.tsx b/app/client/src/layoutSystems/anvil/utils/layouts/renderUtils.tsx
index bba5a3c9ea..77adea5a24 100644
--- a/app/client/src/layoutSystems/anvil/utils/layouts/renderUtils.tsx
+++ b/app/client/src/layoutSystems/anvil/utils/layouts/renderUtils.tsx
@@ -1,19 +1,11 @@
import React, { type ReactNode } from "react";
import LayoutFactory from "layoutSystems/anvil/layoutComponents/LayoutFactory";
-import {
- LayoutComponentTypes,
- type LayoutComponentProps,
- type LayoutProps,
- type WidgetLayoutProps,
+import type {
+ LayoutComponentProps,
+ LayoutProps,
+ WidgetLayoutProps,
} from "../anvilTypes";
import { type RenderMode, RenderModes } from "constants/WidgetConstants";
-import { isFillWidgetPresentInList } from "./widgetUtils";
-import { AlignmentIndexMap, MOBILE_BREAKPOINT } from "../constants";
-import {
- FlexLayout,
- type FlexLayoutProps,
-} from "layoutSystems/anvil/layoutComponents/components/FlexLayout";
-import { FlexLayerAlignment } from "layoutSystems/common/utils/constants";
import type BaseLayoutComponent from "layoutSystems/anvil/layoutComponents/BaseLayoutComponent";
import { WidgetRenderer } from "layoutSystems/anvil/layoutComponents/WidgetRenderer";
@@ -78,125 +70,3 @@ export function renderLayouts(
);
});
}
-
-/**
- * If AlignedRow hasFillWidget:
- * then render all children directly within the AlignedRow (row / flex-start / wrap);
- * no need for alignments.
- *
- * Else:
- * render children in 3 alignments: start, center and end.
- * Each alignment has following characteristics:
- * 1. Mobile viewport:
- * - flex-wrap: wrap.
- * - flex-basis: auto.
- * ~ This ensures the alignment takes up as much space as needed by the children.
- * ~ It can stretch to the full width of the viewport.
- * ~ or collapse completely if there is no content.
- *
- * 2. Larger view ports:
- * - flex-wrap: nowrap.
- * - flex-basis: 0%.
- * ~ This ensures that alignments share the total space equally, until possible.
- * ~ Soon as the content in any alignment needs more space, it will wrap to the next line
- * thanks to flex wrap in the parent layout.
- */
-export function renderWidgetsInAlignedRow(
- props: LayoutComponentProps,
-): React.ReactNode {
- const { canvasId, layout, layoutId } = props;
- // check if layout renders a Fill widget.
- const hasFillWidget: boolean = isFillWidgetPresentInList(
- layout as WidgetLayoutProps[],
- );
-
- // If a Fill widget exists, then render the child widgets together.
- if (hasFillWidget) {
- return renderWidgets(props);
- }
-
- /**
- * else render the child widgets separately
- * in their respective alignments.
- */
- const commonProps: Omit<
- FlexLayoutProps,
- "children" | "layoutId" | "layoutIndex"
- > = {
- alignSelf: "stretch",
- canvasId,
- direction: "row",
- flexBasis: { base: "auto", [`${MOBILE_BREAKPOINT}px`]: "0%" },
- flexGrow: 1,
- flexShrink: 1,
- layoutType: LayoutComponentTypes.WIDGET_ROW,
- parentDropTarget: props.parentDropTarget,
- renderMode: props.renderMode,
- wrap: { base: "wrap", [`${MOBILE_BREAKPOINT}px`]: "nowrap" },
- className: props.className,
- };
-
- const startChildren: WidgetLayoutProps[] = (
- layout as WidgetLayoutProps[]
- ).filter(
- (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.Start,
- );
- const centerChildren: WidgetLayoutProps[] = (
- layout as WidgetLayoutProps[]
- ).filter(
- (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.Center,
- );
- const endChildren: WidgetLayoutProps[] = (
- layout as WidgetLayoutProps[]
- ).filter(
- (each: WidgetLayoutProps) => each.alignment === FlexLayerAlignment.End,
- );
-
- // TODO: After positionObserver integration,
- // check if use of FlexLayout is causing performance or other issues.
- // WDS Flex can be used as a replacement.
- return [
-
- {renderWidgets({
- ...props,
- layout: startChildren,
- })}
- ,
-
- {renderWidgets(
- {
- ...props,
- layout: centerChildren,
- },
- startChildren?.length,
- )}
- ,
-
- {renderWidgets(
- {
- ...props,
- layout: endChildren,
- },
- startChildren?.length + centerChildren?.length,
- )}
- ,
- ];
-}