Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Enhance React Native Layout Management: Native Layout Manager for Padding and Calculations! 💪 #48655

Closed
wants to merge 2 commits into from

Conversation

krishpranav
Copy link

@krishpranav krishpranav commented Jan 14, 2025

PR Description:

This PR introduces a Native Layout Manager that integrates directly with the Yoga engine for efficient layout and padding management in React Native. The module allows developers to dynamically set padding, fetch layout information, and perform layout calculations from JavaScript, providing more control over component layouts and optimizations for UI rendering. ✨

Closes #48527

Changelog:

[General] [Added] - React Native Layout Management

Key Features:

  1. Set Padding: Developers can now apply padding values to layout components dynamically.
  2. Get Layout: Easily retrieve the calculated layout (width, height, etc.) of a view.
  3. Calculate Layout: Perform accurate layout calculations based on the width and height of a view.

Code Example:

import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import NativeLayoutManager from './private/specs/modules/NativeLayoutManager'; 

const TestLayoutComponent = () => {
  const [layout, setLayout] = useState(null);
  const [padding, setPadding] = useState({ top: 0, right: 0, bottom: 0, left: 0 });

  const handleSetPadding = () => {
    NativeLayoutManager.setPadding(1, padding.top, padding.right, padding.bottom, padding.left);
    console.log(`Padding set to: ${JSON.stringify(padding)}`);
  };

  const handleGetLayout = () => {
    NativeLayoutManager.getLayout(1).then((layoutData) => {
      setLayout(layoutData);
      console.log('Layout data received:', layoutData);
    });
  };

  const handleCalculateLayout = () => {
    NativeLayoutManager.calculateLayout(1, 300, 500).then((calculatedLayout) => {
      console.log('Calculated layout:', calculatedLayout);
    });
  };

  useEffect(() => {
    handleGetLayout();
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Testing Native Layout Manager</Text>
      <View style={[styles.testBox, { paddingTop: padding.top, paddingRight: padding.right, paddingBottom: padding.bottom, paddingLeft: padding.left }]}>
        <Text style={styles.boxText}>Test Box with Padding</Text>
      </View>
      <Button title="Set Padding" onPress={handleSetPadding} />
      <Button title="Get Layout" onPress={handleGetLayout} />
      <Button title="Calculate Layout" onPress={handleCalculateLayout} />
      {layout && <Text style={styles.layoutText}>Layout: {JSON.stringify(layout)}</Text>}
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 20 },
  title: { fontSize: 18, marginBottom: 20 },
  testBox: { backgroundColor: '#f0f0f0', width: 200, height: 200, justifyContent: 'center', alignItems: 'center' },
  boxText: { fontSize: 14, color: '#333' },
  layoutText: { marginTop: 20, fontSize: 16, color: '#333' },
});

export default TestLayoutComponent;

Expected Output Logs:

  • Log when setting padding:

    Padding set to: {"top":10,"right":20,"bottom":10,"left":20}
    
  • Log when getting layout:

    Layout data received: { width: 200, height: 200, top: 0, left: 0 }
    
  • Log when calculating layout:

    Calculated layout: { width: 300, height: 500 }
    

Another Example: Dynamic Layout Manager with Multiple Components

import React, { useState } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import NativeLayoutManager from './private/specs/modules/NativeLayoutManager'; // Adjust path if needed

const DynamicLayoutManager = () => {
  const [padding, setPadding] = useState({
    view1: { top: 10, right: 20, bottom: 10, left: 20 },
    view2: { top: 15, right: 30, bottom: 15, left: 30 },
  });

  const [layoutData, setLayoutData] = useState(null);

  const handleSetPaddingForView = (viewId, paddingValues) => {
    NativeLayoutManager.setPadding(
      viewId,
      paddingValues.top,
      paddingValues.right,
      paddingValues.bottom,
      paddingValues.left
    );
    console.log(`Padding set for View ${viewId}: ${JSON.stringify(paddingValues)}`);
  };

  const handleGetLayoutForView = (viewId) => {
    NativeLayoutManager.getLayout(viewId).then((data) => {
      setLayoutData((prevData) => ({
        ...prevData,
        [viewId]: data,
      }));
      console.log(`Layout data for View ${viewId}:`, data);
    });
  };

  const handleCalculateLayoutForView = (viewId, width, height) => {
    NativeLayoutManager.calculateLayout(viewId, width, height).then((calculatedLayout) => {
      console.log(`Calculated layout for View ${viewId}:`, calculatedLayout);
    });
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Dynamic Layout Manager</Text>

      <View style={[styles.viewBox, { paddingTop: padding.view1.top, paddingRight: padding.view1.right, paddingBottom: padding.view1.bottom, paddingLeft: padding.view1.left }]}>
        <Text style={styles.boxText}>View 1 with Padding</Text>
      </View>

      <View style={[styles.viewBox, { paddingTop: padding.view2.top, paddingRight: padding.view2.right, paddingBottom: padding.view2.bottom, paddingLeft: padding.view2.left }]}>
        <Text style={styles.boxText}>View 2 with Padding</Text>
      </View>

      <Button
        title="Set Padding for View 1"
        onPress={() => handleSetPaddingForView(1, padding.view1)}
      />
      <Button
        title="Set Padding for View 2"
        onPress={() => handleSetPaddingForView(2, padding.view2)}
      />

      <Button title="Get Layout for View 1" onPress={() => handleGetLayoutForView(1)} />
      <Button title="Get Layout for View 2" onPress={() => handleGetLayoutForView(2)} />

      <Button
        title="Calculate Layout for View 1"
        onPress={() => handleCalculateLayoutForView(1, 300, 500)}
      />
      <Button
        title="Calculate Layout for View 2"
        onPress={() => handleCalculateLayoutForView(2, 250, 400)}
      />

      <View style={styles.layoutInfoContainer}>
        {layoutData && (
          <>
            <Text>Layout for View 1: {JSON.stringify(layoutData.view1)}</Text>
            <Text>Layout for View 2: {JSON.stringify(layoutData.view2)}</Text>
          </>
        )}
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 18,
    marginBottom: 20,
  },
  viewBox: {
    backgroundColor: '#f0f0f0',
    width: 200,
    height: 200,
    justifyContent: 'center',
    alignItems: 'center',
    marginBottom: 20,
  },
  boxText: {
    fontSize: 14,
    color: '#333',
  },
  layoutInfoContainer: {
    marginTop: 20,
  },
});

export default DynamicLayoutManager;

Explanation:

  • Multiple Views with Different Padding:

    • View 1 and View 2 are each assigned different padding values, which can be dynamically set using the buttons.
  • Set Padding for Individual Views:

    • Each View has a button to apply its respective padding, controlled by the handleSetPaddingForView function.
  • Get Layout Information for Views:

    • The layout information of each view (like width, height, and position) can be fetched via handleGetLayoutForView and displayed in the UI.
  • Calculate Layout:

    • Layout calculations can be performed by pressing the "Calculate Layout" buttons for both views, with different width and height values passed in for each.

Expected Logs:

When setting padding:

  • Padding set for View 1: {"top":10,"right":20,"bottom":10,"left":20}
  • Padding set for View 2: {"top":15,"right":30,"bottom":15,"left":30}

When getting layout:

  • Layout data for View 1: { width: 200, height: 200, top: 0, left: 0 }
  • Layout data for View 2: { width: 200, height: 200, top: 0, left: 0 }

When calculating layout:

  • Calculated layout for View 1: { width: 300, height: 500 }
  • Calculated layout for View 2: { width: 250, height: 400 }

Why This Feature is Useful:

  • Flexibility: With this feature, padding and layout calculations can be controlled directly from JavaScript, enabling dynamic UI adjustments based on runtime data. 🧩
  • Optimized Layout: By using the Yoga engine's layout calculations, this feature ensures that layouts are computed efficiently and accurately for a responsive design. 📐
  • Ease of Use: The integration with the React Native environment ensures that developers can access layout management directly, without needing to interact with native code. 🛠️

Testing Results:

  1. Padding Set Functionality: ✅ The setPadding function allows setting dynamic padding values, which are applied immediately to the layout.
  2. Layout Retrieval: ✅ The getLayout function accurately returns the current layout of a component, with details such as width and height.
  3. Layout Calculation: ✅ The calculateLayout function ensures that layout calculations are correctly performed based on provided dimensions (width and height).

Conclusion:

This addition significantly enhances React Native's layout management system by giving developers more power over their UI, enabling dynamic padding adjustments, layout information retrieval, and accurate layout calculations. By leveraging Yoga's engine for layout calculations, this feature ensures consistent and performant rendering. 💥

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jan 14, 2025
@react-native-bot
Copy link
Collaborator

react-native-bot commented Jan 14, 2025

Warnings
⚠️ 📋 Missing Test Plan - Can you add a Test Plan? To do so, add a "## Test Plan" section to your PR description. A Test Plan lets us know how these changes were tested.

Generated by 🚫 dangerJS against 1df9ea1

@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jan 14, 2025
@javache
Copy link
Member

javache commented Jan 14, 2025

Is this AI generated? React Native uses a declarative layout system, so this is not the right direction.

Please consider discussing proposals first in https://github.com/react-native-community/discussions-and-proposals before putting up a PR.

@javache javache closed this Jan 14, 2025
@krishpranav
Copy link
Author

No, this is not AI generated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

flexWrap wrapping unexpectedly with alignItems: 'flex-end'
4 participants