Skip to content

feat(portal-framework-core): Implement widget area layout using bin packing#443

Merged
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-framework-core
Jul 31, 2025
Merged

feat(portal-framework-core): Implement widget area layout using bin packing#443
pcfreak30 merged 1 commit intodevelopfrom
libs/portal-framework-core

Conversation

@pcfreak30
Copy link
Copy Markdown
Member

@pcfreak30 pcfreak30 commented Jul 31, 2025

  • Introduces binpackingjs library for widget layout optimization.
  • Modifies WidgetArea component to calculate and apply widget positions using bin packing algorithm.
  • Adds cols, rows, and order properties to WidgetRegistration for widget sizing and ordering.
  • Updates WidgetRegistrationEntity to include the React component.

Summary by CodeRabbit

  • New Features

    • Enhanced widget layout management with automatic arrangement using a bin-packing algorithm, allowing widgets to be positioned and sized dynamically within the grid.
    • Widgets can now specify layout properties such as columns, rows, and display order for improved customization.
  • Bug Fixes

    • Improved handling of asynchronous widget loading states for a smoother user experience.
  • Chores

    • Added a new runtime dependency to support advanced layout calculations.
    • Updated widget registration to include additional widget details for richer information.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Jul 31, 2025

⚠️ No Changeset found

Latest commit: 7ec04b1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jul 31, 2025

Warning

Rate limit exceeded

@pcfreak30 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 56 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 60a7864 and 7ec04b1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/portal-framework-core/package.json (2 hunks)
  • libs/portal-framework-core/src/api/framework.ts (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/plugin.ts (2 hunks)

Walkthrough

The changes introduce bin-packing-based layout management to the WidgetArea component, utilizing the new binpackingjs dependency. Widget registration types are extended to include sizing and ordering properties, and internal data structures are updated to support layout metadata. Supporting type definitions are refactored, and the getWidgetRegistrations method now returns richer registration objects.

Changes

Cohort / File(s) Change Summary
Package & Dependency Update
libs/portal-framework-core/package.json
Changed the dev script from tsup --watch to tsdown --watch. Added binpackingjs as a runtime dependency.
Widget Area Layout & Logic
libs/portal-framework-core/src/components/WidgetArea.tsx
Enhanced the WidgetArea component to use a bin-packing algorithm for widget layout, introduced grid-based rendering, and added support for widget sizing (cols, rows) and ordering (order). Added exported constants for default widget dimensions.
Widget Registration Retrieval
libs/portal-framework-core/src/api/framework.ts
Modified getWidgetRegistrations to return full registration objects (spread all properties) along with pluginId instead of only componentName and pluginId.
Type Definitions Refactor
libs/portal-framework-core/src/types/plugin.ts
Removed the ModuleMapping interface, reformatted the PluginInitStatus union type, extended WidgetRegistration with optional cols, rows, and order properties, updated WidgetRegistrationInfo to extend WidgetRegistration and include pluginId, and added a new WidgetRegistrationEntity interface with a required component property of type React.FC.

Sequence Diagram(s)

sequenceDiagram
    participant UI as User Interface
    participant WidgetArea as WidgetArea Component
    participant Framework as Framework API
    participant BinPacking as binpackingjs

    UI->>WidgetArea: Render WidgetArea
    WidgetArea->>Framework: getWidgetRegistrations(area)
    Framework-->>WidgetArea: List of widget registrations (with layout info)
    WidgetArea->>BinPacking: Pack widgets (sizes from registration)
    BinPacking-->>WidgetArea: Positions (x, y) for each widget
    WidgetArea->>UI: Render widgets in grid layout with computed positions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit hopped through grids anew,
With widgets packed in tidy view.
Binpacking magic, columns aligned,
Types extended, layouts refined.
Scripts and types now clean and neat—
This dashboard’s never missed a beat!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch libs/portal-framework-core

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
libs/portal-framework-core/src/types/plugin.ts (1)

61-64: Consider adding validation constraints for layout properties.

The optional layout properties cols, rows, and order are well-designed for the bin packing feature. Consider adding JSDoc comments or validation constraints to specify valid ranges (e.g., positive integers for cols/rows).

 export interface WidgetRegistration {
   area: string;
   componentName: string;
+  /** Number of grid columns the widget should span (positive integer) */
   cols?: number;
+  /** Number of grid rows the widget should span (positive integer) */
   rows?: number;
+  /** Display order within the widget area (higher values appear later) */
   order?: number;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3dc91b and 19ef254.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/portal-framework-core/package.json (2 hunks)
  • libs/portal-framework-core/src/api/framework.ts (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/plugin.ts (2 hunks)
🔇 Additional comments (11)
libs/portal-framework-core/package.json (2)

21-21: Verify if the script change is related to the bin packing feature.

The change from tsup --watch to tsdown --watch appears unrelated to the bin packing implementation. Ensure this change is intentional and not accidentally included in this PR.


33-33: Verify binpackingjs Version & Security

  • Confirmed that 3.0.2 is the latest published version on npm.

  • Audit couldn’t run without a lockfile—please generate one and re-run security checks:

    npm i --package-lock-only
    npm audit --audit-level=moderate
  • Address or upgrade any moderate- or high-severity advisories before merging.

libs/portal-framework-core/src/types/plugin.ts (2)

34-38: LGTM: Improved readability.

The multi-line formatting of PluginInitStatus improves code readability without changing functionality.


66-72: LGTM: Well-structured interface separation.

The separation of concerns between WidgetRegistrationInfo (data transfer) and WidgetRegistrationEntity (runtime with React component) is well-designed and supports the new layout system effectively.

libs/portal-framework-core/src/api/framework.ts (1)

108-108: LGTM: Enhanced widget registration data.

Spreading the entire registration object (...reg) provides the WidgetArea component with necessary layout properties (cols, rows, order) while maintaining backward compatibility. This change effectively supports the bin packing layout system.

libs/portal-framework-core/src/components/WidgetArea.tsx (6)

2-2: LGTM: Appropriate library import.

The binpackingjs import provides the necessary bin packing functionality for the layout system.


11-12: LGTM: Sensible default constants.

The default widget dimensions of 1x1 provide reasonable fallback values for widgets without specified sizes.


20-29: LGTM: Well-structured state types.

The widget state type correctly includes both the React component and layout properties needed for the bin packing system.


38-52: LGTM: Correct widget registration mapping.

The transformation from widget registrations to entities with components is implemented correctly, preserving all registration properties while adding the component loader.


145-168: LGTM: Correct CSS grid implementation.

The CSS grid implementation correctly uses dynamic template columns/rows and proper grid positioning. The styling with padding, borders, and rounded corners provides good visual separation.


157-161: Bin packing coordinates are 0-based; CSS grid conversion is correct

Verified that binpackingjs’s heuristics assign box.x and box.y starting at 0. Since CSS Grid uses 1-based indices, widget.x + 1 and widget.y + 1 correctly map those 0-based coordinates. No changes needed.

@pcfreak30 pcfreak30 force-pushed the libs/portal-framework-core branch from 19ef254 to 0e39067 Compare July 31, 2025 10:51
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)

58-90: The bridge wrapper implementation still has the previously identified issues.

The same problems highlighted in the previous review persist:

  1. Fragile promise type checking using "then" property
  2. Bridge loader result is not used to render the actual component
  3. Loading state management is still basic
🧹 Nitpick comments (3)
libs/portal-framework-core/src/components/WidgetArea.tsx (3)

110-112: Remove unused totalWidth and totalHeight calculations.

Lines 110-112 calculate totalWidth and totalHeight but these values are never used in the improved bin sizing logic that follows.

-    // Calculate total width and height
-    const totalWidth = widgetSizes.reduce((sum, size) => sum + size.width, 0);
-    const totalHeight = widgetSizes.reduce((sum, size) => sum + size.height, 0);
-

114-127: Consider making aspect ratio configurable or use container-based dimensions.

While the area-based approach with golden ratio is an improvement over the previous flawed logic, the fixed golden ratio (1.618) may not be optimal for all widget layouts. Consider making this configurable or basing dimensions on the actual container size.

-    // Define aspect ratio (width / height)
-    const aspectRatio = 1.618; // Golden Ratio
+    // Define aspect ratio (width / height) - could be made configurable
+    const aspectRatio = 1.618; // Golden Ratio - consider making this a prop

174-174: Remove duplicate layoutSize setting.

Line 174 sets layoutSize again after it was already set in the error handling logic above (lines 157/159). This is redundant.

-    setLayoutSize([binWidth, binHeight]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19ef254 and 0e39067.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/portal-framework-core/package.json (2 hunks)
  • libs/portal-framework-core/src/api/framework.ts (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/plugin.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/portal-framework-core/package.json
  • libs/portal-framework-core/src/api/framework.ts
  • libs/portal-framework-core/src/types/plugin.ts
🔇 Additional comments (6)
libs/portal-framework-core/src/components/WidgetArea.tsx (6)

2-2: LGTM: Clean imports and sensible defaults.

The bin packing imports and WidgetRegistrationEntity type import are appropriate. The default widget dimensions provide good fallback values.

Also applies to: 9-12


20-29: LGTM: Well-structured state for layout management.

The enhanced widget state type properly includes layout properties (cols, rows, x, y) needed for bin packing positioning, and the layoutSize state effectively tracks grid dimensions.


38-52: LGTM: Proper widget loading implementation.

The transformation to WidgetRegistrationEntity correctly preserves widget metadata while adding the dynamically loaded component using createRemoteComponentLoader.


93-101: LGTM: Proper widget filtering and ordering.

The filtering ensures only valid React components are included, and the immutable sorting by order property provides controlled widget arrangement.


141-160: LGTM: Excellent error handling for packing failures.

The fallback logic for unpacked widgets is well-implemented, providing a simple grid layout when bin packing fails and properly adjusting the layout size.


177-201: LGTM: Effective CSS Grid implementation for widget layout.

The dynamic grid template with calculated dimensions and proper widget positioning using grid coordinates creates a clean, responsive layout. The styling provides good visual separation between widgets.

@pcfreak30 pcfreak30 force-pushed the libs/portal-framework-core branch from 0e39067 to 7180bf1 Compare July 31, 2025 10:58
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e39067 and 7180bf1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/portal-framework-core/package.json (2 hunks)
  • libs/portal-framework-core/src/api/framework.ts (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/plugin.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • libs/portal-framework-core/package.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • libs/portal-framework-core/src/api/framework.ts
  • libs/portal-framework-core/src/types/plugin.ts
🔇 Additional comments (6)
libs/portal-framework-core/src/components/WidgetArea.tsx (6)

2-2: LGTM! Well-structured imports and constants.

The addition of binpackingjs imports and WidgetRegistrationEntity type import are appropriate for the new functionality. The exported constants provide sensible defaults, and using the golden ratio for aspect ratio is a good design choice.

Also applies to: 9-9, 11-14


18-18: LGTM! Well-designed interface and state changes.

The addition of the aspectRatio prop with a sensible default and the enhanced widget state structure to include layout properties (cols, rows, x, y) properly support the bin packing functionality. The layoutSize state addition is appropriate for tracking grid dimensions.

Also applies to: 21-35


101-118: LGTM! Robust filtering and sorting implementation.

The filtering logic properly validates React components, and the immutable sorting by order property is correctly implemented. The widget size collection with default fallbacks is well-handled.


120-143: Excellent improvement to bin sizing logic!

The bin sizing calculation has been significantly improved from previous implementations. Using total area with aspect ratio to determine bin dimensions is mathematically sound and will produce much better layouts than summing widget dimensions. This properly addresses the critical bin sizing issue from past reviews.


180-192: LGTM! Proper widget layout state management.

The transformation of bin packing results into widget state is correctly implemented, preserving all layout information (x, y, cols, rows) needed for rendering.


194-219: LGTM! Excellent CSS Grid implementation.

The rendering logic properly uses CSS Grid with dynamic template dimensions and correctly positions widgets using their computed coordinates. The 1-indexed grid positioning is correctly handled, and the styling provides appropriate visual separation.

@pcfreak30 pcfreak30 force-pushed the libs/portal-framework-core branch from 7180bf1 to 60a7864 Compare July 31, 2025 11:03
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🔭 Outside diff range comments (1)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)

203-218: Missing error boundary for widget rendering.

Widget components loaded dynamically could throw errors during rendering, potentially crashing the entire widget area.

+    // Add error boundary wrapper component
+    const WidgetErrorBoundary: React.FC<{ children: React.ReactNode; widgetIndex: number }> = ({ 
+      children, 
+      widgetIndex 
+    }) => {
+      const [hasError, setHasError] = useState(false);
+      
+      useEffect(() => {
+        const errorHandler = (error: ErrorEvent) => {
+          console.error(`Widget ${widgetIndex} error:`, error);
+          setHasError(true);
+        };
+        
+        window.addEventListener('error', errorHandler);
+        return () => window.removeEventListener('error', errorHandler);
+      }, [widgetIndex]);
+      
+      if (hasError) {
+        return <div className="text-red-500">Widget failed to load</div>;
+      }
+      
+      return <>{children}</>;
+    };
+
       {widgets.map((widget, index) => {
         const Widget = widget.component;
         return (
           <div
             key={index}
             style={{
               gridColumnStart: widget.x + 1,
               gridRowStart: widget.y + 1,
               gridColumnEnd: `span ${widget.cols || 1}`,
               gridRowEnd: `span ${widget.rows || 1}`,
             }}
             className="p-4 border rounded-md">
-            <Widget />
+            <WidgetErrorBoundary widgetIndex={index}>
+              <Widget />
+            </WidgetErrorBoundary>
           </div>
         );
       })}
♻️ Duplicate comments (1)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)

80-80: Still using incorrect component resolution from bridge result.

Based on the past review comment, the bridge loader result should be accessed via result.component rather than using result directly, but this hasn't been fixed.

-                  setLoadedComponent(() => result);
+                  setLoadedComponent(() => result.component);
🧹 Nitpick comments (1)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)

114-118: Potential data mutation in widget size mapping.

The current code directly references the widget object in the mapping, which could lead to unintended mutations during bin packing operations.

-    const widgetSizes = sortedWidgets.map((widget) => ({
-      width: widget.cols || DEFAULT_WIDGET_WIDTH,
-      height: widget.rows || DEFAULT_WIDGET_HEIGHT,
-      widget,
-    }));
+    const widgetSizes = sortedWidgets.map((widget) => ({
+      width: widget.cols || DEFAULT_WIDGET_WIDTH,
+      height: widget.rows || DEFAULT_WIDGET_HEIGHT,
+      widget: { ...widget }, // Create a shallow copy to prevent mutations
+    }));
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7180bf1 and 60a7864.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/portal-framework-core/package.json (2 hunks)
  • libs/portal-framework-core/src/api/framework.ts (1 hunks)
  • libs/portal-framework-core/src/components/WidgetArea.tsx (2 hunks)
  • libs/portal-framework-core/src/types/plugin.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/portal-framework-core/src/api/framework.ts
  • libs/portal-framework-core/package.json
  • libs/portal-framework-core/src/types/plugin.ts
🔇 Additional comments (1)
libs/portal-framework-core/src/components/WidgetArea.tsx (1)

208-213: Grid positioning mapping is correct—no changes needed.

binpackingjs uses 0-based x/y coordinates, and CSS Grid is 1-based, so adding +1 correctly aligns widgets.

@pcfreak30 pcfreak30 force-pushed the libs/portal-framework-core branch from 60a7864 to 96e46c3 Compare July 31, 2025 11:11
…acking

- Introduces binpackingjs library for widget layout optimization.
- Modifies WidgetArea component to calculate and apply widget positions using bin packing algorithm.
- Adds cols, rows, and order properties to WidgetRegistration for widget sizing and ordering.
- Updates WidgetRegistrationEntity to include the React component.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant