npm package: @hadss/react_native_folderstack
Description: 这是一个旨在解决 React Native多设备适配问题的三方库，专为不同设备类型（包括折叠屏、平板、手机等）提供了便捷的支持。该库包含的接口和开箱即用的组件，使开发者能够轻松应对各种设备的布局适配需求。
Latest version: 1.0.0-rc.2

# @hadss/react_native_folderstack

## 介绍

这是一个旨在解决 React Native多设备适配问题的三方库，专为不同设备类型（包括折叠屏、平板、手机等）提供了便捷的支持。该库包含的接口和开箱即用的组件，使开发者能够轻松应对各种设备的布局适配需求。

目前该库提供的组件如下：

1. 折叠屏相关组件：基于ArkUI折叠屏相关接口开发的RN获取折叠屏信息的接口，包括折叠屏状态、是否是折叠屏、折叠屏避让区域信息及监听折叠屏变化等能力。
2. 自适应显隐容器：对标ArkUI的自适应显隐布局开发的容器，使开发者能够轻松的完成自适应显隐布局能力的开发。
3. 侧边栏组件：对标ArkUI的SideBarContainer组件开发的RN侧边栏组件，使开发者能够轻松的完成侧边栏相关布局能力的开发。
4. 自适应分栏组件：参考ArkUI的Navigation组件开发的RN分栏组件，使开发者能够轻松的完成分栏及路由跳转能力的开发。
5. 栅格组件：对标ArkUI的GridRow/GridCol开发的RN栅格组件，提供基础的栅格布局能力，使开发能够直接使用ArkUI栅格布局能力。

## 工程目录

```
.
├─harmony
│  ├─folder_stack.har                             // Turbo Modules接口har包
│  └─folder_stack
│     └─src/main/ets
│        ├─FolderStackModule.ets                  // Turbo Modules接口ArkTS实现
│        ├─FolderStackModulePackage.ets           // FolderStackModulesFactory
│        ├─FolderStackModuleSpec.ts               // Turbo Modules接口，提供折叠屏相关接口
│        └─core
│           └─FoldConfig.ts   
├─src
│  ├─index.ts                                // RN模块导出
│  ├─component
│  │  ├─FoldSplitContainer                   // 折叠屏高阶组件FoldSplitContainer
|  |  └─FoldStack                   		     // 折叠屏高阶组件FoldStack
│  ├─turbo
│  |   └─FolderStackModule.ts                // Turbo Modules接口
│  └─utils
│     ├─FoldUtils                            // 封装折叠屏相关接口
│     └─ThrottleUtils                        //  节流（throttle）函数的 TypeScript 实现，用于限制某个函数的执行频率，确保它在指定的时间间隔内最多执行一次。
```


## 安装与使用

进入到工程目录并输入以下命令：

<!-- tabs:start -->

**npm**

```bash
npm install @hadss/react_native_folderstack
```

**yarn**

```bash
yarn add @hadss/react_native_folderstack
```

<!-- tabs:end -->

下面的代码展示了这个库的基本使用场景：

### FoldSplitContainer使用示例
```jsx
import { Fold, ExpandedRegionLayoutOptions, ExtraRegionPosition, HoverModeRegionLayoutOptions, FoldSplitContainer } from "@hadss/react_native_folderstack";
import React, { useState, useEffect } from "react";
import { View, Text, Button, StyleSheet } from "react-native";

const TestSample = () => {
    const [foldCreaseRegion, setFoldCreaseRegion] = useState<number[]>([]);
    const [foldStatus, setFoldStatus] = useState('');
    const [isDeviceFoldable, setIsDeviceFoldable] = useState(false);
  
    useEffect(() => {
      const checkFoldable = () => {
        // 判断当前设备是否可折叠
        const foldable = Fold.isFoldable();
        setIsDeviceFoldable(foldable);
        if (foldable) {
          // 获取折痕区域
          setFoldCreaseRegion(Fold?.getFoldCreaseRegion()==null ?[]:Fold?.getFoldCreaseRegion()[0]);
          
          // 获取当前折叠屏状态
          setFoldStatus(Fold.getFoldStatus());
        }
      };
  
      checkFoldable();
  
      // 监听折叠屏状态变化
      Fold.addFoldListener((foldStatus) => {
        console.log('折叠屏状态发生变化', foldStatus);
      });
  
      return () => {
        // 销毁监听折叠屏状态变化
        Fold.removeFoldListener();
      };
    }, []);
  
    const handleGetFoldStatus = () => {
      if (isDeviceFoldable) {
        // 获取当前折叠屏状态
        setFoldStatus(Fold.getFoldStatus());
      }
    };
    
    // FoldSplitContainer 主要区域
    const primaryRender = () => (
      <View style={{ flex: 1 }}>
        <Text>Is Device Foldable: {isDeviceFoldable ? 'Yes' : 'No'}</Text>
  
        {isDeviceFoldable && (
          <>
            <Text>Fold Crease Region: {JSON.stringify(foldCreaseRegion)}</Text>
            <Button title="Get Fold Status" onPress={handleGetFoldStatus} />
            {foldStatus && <Text>Fold Status: {foldStatus}</Text>}
          </>
        )}
      </View>
    );
  
    // FoldSplitContainer 次要区域
    const secondRender = () => (
      <View style={{ flex: 1, backgroundColor: 'blue' }}>
        <Text style={{ fontSize: 20 }}>secondRender</Text>
      </View>
    );
  
    // FoldSplitContainer 扩展区域
    const extraRender = () => (
      <View style={{ flex: 1, backgroundColor: 'red' }}>
        <Text style={{ fontSize: 20 }}>extraRender</Text>
      </View>
    );
  
    // 折叠屏展开态配置
    const expandedLayoutOptions: ExpandedRegionLayoutOptions = {
      isExtraRegionPerpendicular: false,
      extraRegionPosition: ExtraRegionPosition.BOTTOM,
    };
  
    // 折叠屏悬停态配置
    const hoverModeRegionLayoutOptions: HoverModeRegionLayoutOptions = {
      showExtraRegion: true,
    };
  
    return (
      <View style={styles.container}>
        <FoldSplitContainer
          primary={primaryRender()}
          secondary={secondRender()}
          extra={extraRender()}
          expandedLayoutOptions={expandedLayoutOptions}
          hoverModeLayoutOptions={hoverModeRegionLayoutOptions}
        />
      </View>
    );
  };
  
  const styles = StyleSheet.create({
    container: {
      flex: 1,
    },
  });
  
  export default TestSample;
```

### **FoldStack使用示例**

```jsx
import React from 'react';
import {
  View,
  Text,
  StyleSheet,
} from 'react-native';
import { FolderStack } from '@hadss/react_native_folderstack';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'rgb(39, 135, 217)'
  },
  videoContainer: {
    width: '100%',
    height: '100%',
    justifyContent: 'center',
    backgroundColor: 'rgb(100, 135, 217)'
  },
  videoText: {
    fontSize: 25,
    color: 'white',
    textAlign: 'center'
  },
  controlBar: {
    height: '100%',
    width: '100%',
  },
  controlText: {
    fontSize: 25,
    textAlign: 'center',
    width: '100%',
    height: 50
  }
});


const FolderStackDemo = () => {
  return (
    <View style={[styles.videoContainer]}>
      <FolderStack
        upperItems={["upperitemsId"]}
        enableAnimation={true}
        autoHalfFold={true}
        alignContent='End'
        onFolderStateChange={(foldStatus) => {
          console.log(`onFolderStateChange params is ${JSON.stringify(foldStatus)}`)
        }
        }
        onHoverStatusChange={(HoverStatus) => {
          console.log(`onHoverStatusChange params is ${JSON.stringify(HoverStatus)}`)
        }
        }
      >
        {/* 上半屏视频区域 */}
        <View style={[styles.videoContainer, { backgroundColor: 'rgb(0, 74, 175)' }]} id="upperitemsId">
          <Text style={styles.videoText}>video zone</Text>
        </View>

        {/* 下半屏操作区域 */}
        <View style={[styles.controlBar, { justifyContent: 'flex-start' }]} id="2">
          <Text style={[styles.controlText, { backgroundColor: 'rgb(213, 213, 213)' }]}>video title</Text>
        </View>

        <View style={[styles.controlBar, { justifyContent: 'flex-end', zIndex: 5 }]} id="3">
          <Text style={[styles.controlText, { backgroundColor: 'rgb(213, 213, 213)' }]}>video bar</Text>
        </View>
      </FolderStack>
    </View>
  );
};

export default FolderStackDemo;


```

## Link

> 本库依赖以下三方库，请查看对应文档：

> [@react-native-oh-tpl/react-native-orientation](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/react-native-orientation.md)


目前OpenHarmony暂不支持AutoLink，所以Link步骤需要手动配置。

首先需要使用DevEco Studio打开项目里的OpenHarmony工程，在工程根目录的`oh-package.json5`添加overrides字段：

```json
{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}
```

### 引入原生端代码

目前有两种方法：

1. **通过har包引入（在IDE完善相关功能后该方法会被遗弃，目前首选此方法）。**

    >**说明：**
    >har包位于三方库安装路径的`harmony`文件夹下。

    a. 打开`entry/oh-package.json5`，添加以下依赖：

    ```json
    "dependencies": {
        "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
        "@hadss/react_native_folderstack": "file:../../node_modules/@hadss/react_native_folderstack/harmony/folder_stack.har",
      }
    ```

    b. 配置CMakeLists和引入RNOHGeneratedPackage：

    打开`entry/src/main/cpp/CMakeLists.txt`，添加：

    ```diff
    project(rnapp)
    cmake_minimum_required(VERSION 3.4.1)
    set(CMAKE_SKIP_BUILD_RPATH TRUE)
    set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
    set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
    
    set(RNOH_CPP_DIR "${OH_MODULE_DIR}/@rnoh/react-native-openharmony/src/main/cpp")
    set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
    set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
    set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
    add_compile_definitions(WITH_HITRACE_SYSTRACE)
    set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
    + file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")
    
    add_subdirectory("${RNOH_CPP_DIR}" ./rn)
    
    add_library(rnoh_app SHARED
    +    ${GENERATED_CPP_FILES}
        "./PackageProvider.cpp"
        "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
    )
    
    target_link_libraries(rnoh_app PUBLIC rnoh)
    ```

    c. 打开`entry/src/main/cpp/PackageProvider.cpp`，添加：

    ```diff
    #include "RNOH/PackageProvider.h"
    + #include "generated/RNOHGeneratedPackage.h"
    
    using namespace rnoh;
    
    std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx) {
        return {
    +        std::make_shared<RNOHGeneratedPackage>(ctx)
        };
    }
    ```

    d. 在ArkTs侧引入FolderStackModulePackage：

    打开`entry/src/main/ets/RNPackagesFactory.ts`，添加：

    ```diff
      ...
    + import { FolderStackModulePackage } from '@hadss/react_native_folderstack/ts';
    
    export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
      return [
    +   new FolderStackModulePackage(ctx),
      ];
    }
    ```

    e. 运行：

    点击右上角的`sync`按钮

    或者在终端执行：

    ```bash
    cd entry
    ohpm install
    ```
    然后编译、运行即可。
> **说明**
> 若项目启动时报错：can not find record '&@rnoh/react-native-openharmony/generated/ts&X.X.X'。需在entry\oh_modules@rnoh\react-native-openharmony\ts.ts文件中添加export * from './generated/ts'，并删除.cxx文件夹、build文件夹，然后执行sync操作同步代码。

2. **直接链接源码。**

    如需使用直接链接源码，请参考[直接链接源码说明](https://gitee.com/react-native-oh-library/usage-docs/blob/master/zh-cn/link-source-code.md)。

##  API

>**说明：**
>"Platform"列表示支持的平台，All表示支持所有平台。

### 折叠屏适配

**折叠屏接口**

| Name                | Description                                                  | Type     | Platform  |
| ------------------- | ------------------------------------------------------------ | -------- | --------- |
| getFoldCreaseRegion | 获取可折叠设备屏幕的折痕区域。                               | function | OpenHarmony |
| getFoldStatus       | 获取当前可折叠设备的状态（例如：展开、半折叠或折叠）。       | function | OpenHarmony |
| isFoldable          | 判断设备是否可折叠。如果为 `true`，设备是可折叠的；否则是不可折叠设备。 | function | OpenHarmony |
| addFoldListener     | 监听折叠设备的折叠状态变化。                                 | function | OpenHarmony |
| removeFoldListener  | 移除已添加的折叠状态变化监听。                               | function | OpenHarmony |

**FoldSplitContainer组件**

| Name                   | Description                                    | Type                         | Platform  |
| :--------------------- | :--------------------------------------------- | :--------------------------- | :-------- |
| primary                | 主要区域回调函数。                             | function                     | OpenHarmony |
| secondary              | 次要区域回调函数。                             | function                     | OpenHarmony |
| extra                  | 扩展区域回调函数，不传入的情况，没有对应区域。 | function                     | OpenHarmony |
| expandedLayoutOptions  | 展开态布局信息。                               | ExpandedRegionLayoutOptions  | OpenHarmony |
| hoverModeLayoutOptions | 悬停态布局信息。                               | HoverModeRegionLayoutOptions | OpenHarmony |
| foldedLayoutOptions    | 折叠态布局信息。                               | FoldedRegionLayoutOptions    | OpenHarmony |
| onHoverStatusChange    | 折叠屏进入或退出悬停模式时触发的回调函数。     | onHoverStatusChangeHandler   | OpenHarmony |

**FoldStack组件**

| Name                   | Description                                    | Type                         | Platform  |
| :--------------------- | :--------------------------------------------- | :--------------------------- | :-------- |
| upperItems             | 主要区域回调函数。                             | string[]                     | OpenHarmony |
| enableAnimation        | 动画是否开启。                                 | boolean                     | OpenHarmony |
| alignContent           | 布局形式。                                    | Alignment                     | OpenHarmony |
| autoHalfFold           | 自动旋转开关。                                 | boolean                      | OpenHarmony |
| onFolderStateChange    | 折叠屏状态变化触发的回调函数。                | (event: { foldStatus: FoldStatus }) => void | OpenHarmony |
| onHoverStatusChange    | 折叠屏进入或退出悬停模式时触发的回调函数。     | onHoverStatusChangeHandler   | OpenHarmony |

**ExpandedRegionLayoutOptions**

| Name                       | Description                                                  | Type                | Platform  |
| :------------------------- | :----------------------------------------------------------- | :------------------ | --------- |
| isExtraRegionPerpendicular | 扩展区域是否从上到下贯穿整个组件，当且仅当extra有效时此字段才生效。默认值：true。 | boolean             | OpenHarmony |
| verticalSplitRatio         | 主要区域与次要区域之间的高度比例。默认值：PresetSplitRatio.LAYOUT_1V1。 | number              | OpenHarmony |
| horizontalSplitRatio       | 主要区域与扩展区域之间的宽度比例，当且仅当extra有效时此字段才生效。默认值：PresetSplitRatio.LAYOUT_3V2。 | number              | OpenHarmony |
| extraRegionPosition        | 扩展区域的位置信息，当且仅当isExtraRegionPerpendicular = false有效时此字段才生效。默认值：ExtraRegionPosition.top。 | ExtraRegionPosition | OpenHarmony |

**HoverModeRegionLayoutOptions**

| Name                 | Description                                                  | Type                | Platform  |
| :------------------- | :----------------------------------------------------------- | :------------------ | :-------- |
| showExtraRegion      | 可折叠屏幕在半折叠状态下是否显示扩展区域。默认值：false。    | boolean             | OpenHarmony |
| horizontalSplitRatio | 主要区域与扩展区域之间的宽度比例，当且仅当extra有效时此字段才生效。默认值：PresetSplitRatio.LAYOUT_3V2。 | number              | OpenHarmony |
| extraRegionPosition  | 扩展区域的位置信息，当且仅当showExtraRegion时此字段才生效。默认值：ExtraRegionPosition.top。 | ExtraRegionPosition | OpenHarmony |

**FoldedRegionLayoutOptions**

| Name               | Description                                                  | Type   | Platform  |
| :----------------- | :----------------------------------------------------------- | :----- | :-------- |
| verticalSplitRatio | 主要区域与次要区域之间的高度比例。默认值：PresetSplitRatio.LAYOUT_1V1。 | number | OpenHarmony |

**onHoverStatusChangeHandler**

| Name     | Description                                | Type                              | Platform  |
| :------- | :----------------------------------------- | :-------------------------------- | :-------- |
| callback | 折叠屏进入或退出悬停模式时触发的回调函数。 | (status: HoverModeStatus) => void | OpenHarmony |

**HoverModeStatus**

| Name        | Description             | Type       | Platform  |
| :---------- | :---------------------- | :--------- | :-------- |
| foldStatus  | 设备的折叠状态。        | FoldStatus  | OpenHarmony |
| isHoverMode | app当前是否处于悬停态。 | boolean     | OpenHarmony |
| appRotation | AppRotation。          | AppRotation | OpenHarmony |

**AppRotation**

| Name   | Description              | Value |
| :----- | :----------------------- | :---- |
| ROTATION_0   | 应用方向为0度。      |  0   |
| ROTATION_90  | 应用方向为90度。     |  1   |
| ROTATION_180 | 应用方向为180度。    |  2   |
| ROTATION_270 | 应用方向为270度。    |  3   |

**ExtraRegionPosition**

| Name   | Description              | Value |
| :----- | :----------------------- | :---- |
| top    | 扩展区域在组件上半区域。 | 1     |
| bottom | 扩展区域在组件下半区域。 | 2     |

**PresetSplitRatio**

| Name       | Description | Value |
| :--------- | :---------- | :---- |
| LAYOUT_1V1 | 1:1比例。   | 1/1   |
| LAYOUT_3V2 | 3:2比例。   | 3/2   |
| LAYOUT_2V3 | 2:3比例。   | 2/3   |

## 约束与限制
系统与设备：本示例仅支持在标准系统的华为手机上运行。
开发环境：需要DevEco Studio 5.0.1 Release或更高版本。
功能与SDK版本对应关系：
双折叠的悬停适配：需要SDK API 12及以上版本。
三折叠的悬停适配：需要SDK API 15及以上版本。