> ## Documentation Index
> Fetch the complete documentation index at: https://docs.li.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# 选择组件布局

> 为您的组件选择模式、变体和高度

## 模式

模式允许您为用户呈现不同的工作流程。

<Note>
  在 [组件演示平台](https://playground.li.fi/) 中体验不同的模式，打开侧边栏中的 **Mode** 选项。
</Note>

```typescript theme={"system"}
type WidgetMode = 'default' | 'split' | 'custom' | 'refuel';
```

### 默认模式

**default** 模式提供标准功能，在统一视图中进行桥接和交换。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  mode: 'default', // 这是默认值
};
```

### 分离模式

**split** 模式分离了心理模型，在主页面上通过标签为桥接和交换体验提供不同的视图。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  mode: 'split',
};
```

#### 分离模式选项

对于 `mode: 'split'`，`modeOptions` 配置控制是显示"交换"和"桥接"两个标签还是单个界面：

* **默认（无选项）**：显示"桥接"和"交换"标签
* **`split: 'bridge'`**：仅显示桥接界面（无标签）
* **`split: 'swap'`**：仅显示交换界面（无标签）
* **`split: { defaultTab: 'bridge' }` 或 `split: { defaultTab: 'swap' }`**：显示两个标签，并默认选中指定的标签

```typescript theme={"system"}
// 默认 - 显示两个标签
const tabsConfig: WidgetConfig = {
  mode: 'split',
};

// 纯桥接界面
const bridgeConfig: WidgetConfig = {
  mode: 'split',
  modeOptions: {
    split: 'bridge',
  },
};

// 纯交换界面
const swapConfig: WidgetConfig = {
  mode: 'split',
  modeOptions: {
    split: 'swap',
  },
};

// 两个标签，默认选中交换
const tabsSwapDefaultConfig: WidgetConfig = {
  mode: 'split',
  modeOptions: {
    split: { defaultTab: 'swap' },
  },
};
```

<Columns cols={2}>
  <Card title="分离模式（默认）" img="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/widget-variants/split-subvariant.png?fit=max&auto=format&n=08FOM1AsMmrVbIEl&q=85&s=0605060dbe054ed4801bf1539dad5017" width="452" height="682" data-path="images/widget-variants/split-subvariant.png" />

  <Card title="分离模式（交换选项）" img="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/widget-variants/swap-subvariant.png?fit=max&auto=format&n=08FOM1AsMmrVbIEl&q=85&s=0c751e92cdb5e6b16c295c13a03cfd1c" width="452" height="682" data-path="images/widget-variants/swap-subvariant.png" />
</Columns>

### 自定义模式

**custom** 模式提供不同的外观，允许您显示自定义组件并构建全新的流程，包括 NFT 结账和存款。

```typescript theme={"system"}
type CustomMode = 'checkout' | 'deposit';
```

#### 结账流程

用于 NFT 或商品结账流程：

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  mode: 'custom',
  modeOptions: {
    custom: { type: 'checkout' },
  },
  contractCalls: [...], // 您的合约调用
  contractComponent: <YourCheckoutComponent />,
  contractTool: {
    name: 'Your Protocol',
    logoURI: 'https://your-protocol.com/logo.png',
  },
};
```

#### 存款流程

用于协议存款流程：

```typescript theme={"system"}
import { ChainType, LiFiWidget, WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  mode: 'custom',
  modeOptions: {
    custom: { type: 'deposit' },
  },
  toAddress: {
    name: 'Protocol Vault',
    address: '0x...',
    chainType: ChainType.EVM,
    logoURI: 'https://your-protocol.com/logo.png',
  },
  disabledUI: { toAddress: true },
  hiddenUI: { appearance: true, language: true },
  showSingleRoute: true,
  contractComponent: <YourDepositCard />,
  contractTool: {
    name: 'Your Protocol',
    logoURI: 'https://your-protocol.com/logo.png',
  },
};
```

请参阅完整的[存款流程示例](https://github.com/lifinance/widget/tree/main/examples/deposit-flow)。

### 加油模式

**refuel** 模式针对 Gas 加油操作进行了优化，帮助用户在目标链上获取原生代币。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  mode: 'refuel',
};
```

### ModeOptions 接口

```typescript theme={"system"}
type SplitMode = 'bridge' | 'swap'
type SplitModeOptions = {
  defaultTab: SplitMode
}

type CustomMode = 'checkout' | 'deposit'

interface ModeOptions {
  // 'split' 模式的选项
  // 传入字符串表示单一模式（无标签），传入对象表示带默认标签的标签视图
  split?: SplitMode | SplitModeOptions
  
  // 'custom' 模式的选项
  custom?: { type: CustomMode }
}
```

***

## 变体

变体提供了一种方法来优化组件的呈现样式，以适应您应用程序中的可用空间。

<Note>
  在 [组件演示平台](https://playground.li.fi/) 中体验不同的变体，打开侧边栏中的 **Variant** 选项。
</Note>

```typescript theme={"system"}
type WidgetVariant = 'compact' | 'wide' | 'drawer';
```

### 紧凑型变体

当您页面空间有限或处理较小屏幕尺寸时，紧凑型变体是一个很好的选择。它具有您在紧凑视图中进行桥接和交换所需的一切，并允许您将组件集成到 Web 应用程序页面的任何位置。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  variant: 'compact', // 这是默认值
};
```

<img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/widget-variants/compact_variant.png?fit=max&auto=format&n=08FOM1AsMmrVbIEl&q=85&s=cc9934ab188eb5d86e68080eb6fddaa9" alt="紧凑型变体" width="906" height="1422" data-path="images/widget-variants/compact_variant.png" />

### 宽型变体

宽型变体允许您利用更大的页面和屏幕尺寸，在您可能有更多可用屏幕空间的地方使用。它提供更全面的可用路由概览，在侧边栏中显示，带有流畅的动画。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  variant: 'wide',
};
```

<img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/widget-variants/wide_variant.gif?s=fc1152216b270848bb119a5a7631a865" alt="宽型变体" width="814" height="646" data-path="images/widget-variants/wide_variant.gif" />

#### 链侧边栏

`wide` 变体默认显示链侧边栏。要隐藏它，请在 `hiddenUI` 中将 `chainSidebar` 设置为 `true`：

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  variant: 'wide',
  hiddenUI: {
    chainSidebar: true,
  },
};
```

### 抽屉型变体

抽屉型变体允许您根据用户交互显示或隐藏组件。它可以很好地适应页面的侧面，具有与紧凑型变体相同的布局。

```typescript theme={"system"}
const widgetConfig: WidgetConfig = {
  variant: 'drawer',
};
```

<img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/widget-variants/drawer_variant.gif?s=8c9cbd2fbbc68d37d5b2affbb8e9c526" alt="抽屉型变体" width="768" height="1177" data-path="images/widget-variants/drawer_variant.gif" />

#### 控制抽屉

抽屉没有预构建的按钮来打开和关闭它。要控制抽屉，请创建并分配一个 `ref` 给组件：

```typescript theme={"system"}
import { useRef } from 'react';
import { LiFiWidget, WidgetDrawer, WidgetConfig } from '@lifi/widget';

export const WidgetPage = () => {
  const drawerRef = useRef<WidgetDrawer>(null);

  const toggleWidget = () => {
    drawerRef.current?.toggleDrawer();
  };

  const openWidget = () => {
    drawerRef.current?.openDrawer();
  };

  const closeWidget = () => {
    drawerRef.current?.closeDrawer();
  };

  const isWidgetOpen = () => {
    return drawerRef.current?.isOpen();
  };

  return (
    <div>
      <button onClick={toggleWidget}>切换组件</button>
      <button onClick={openWidget}>打开组件</button>
      <button onClick={closeWidget}>关闭组件</button>
      <LiFiWidget
        ref={drawerRef}
        config={{
          variant: 'drawer',
        }}
        integrator="drawer-example"
      />
    </div>
  );
};
```

#### WidgetDrawer 接口

```typescript theme={"system"}
interface WidgetDrawer {
  isOpen(): boolean      // 检查抽屉是否打开
  toggleDrawer(): void   // 切换抽屉打开/关闭
  openDrawer(): void     // 打开抽屉
  closeDrawer(): void    // 关闭抽屉
}
```

#### 受控抽屉

您还可以使用 `open` 和 `onClose` 属性从外部控制抽屉状态：

```typescript theme={"system"}
import { useState } from 'react';
import { LiFiWidget, WidgetConfig } from '@lifi/widget';

export const WidgetPage = () => {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>打开组件</button>
      <LiFiWidget
        open={isOpen}
        onClose={() => setIsOpen(false)}
        config={{
          variant: 'drawer',
        }}
        integrator="controlled-drawer-example"
      />
    </div>
  );
};
```

***

## 高度

以下是配置组件高度的几种推荐方式：**默认、限制最大高度、限制高度和全高**。

<Note>
  在 [组件演示平台](https://playground.li.fi/) 中体验不同的高度配置，打开侧边栏中的 **Height** 选项。
</Note>

我们建议为紧凑型和宽型变体使用默认、限制最大高度或限制高度。全高仅推荐用于紧凑型。抽屉型变体使用容器/视口的全高。

### 默认

默认情况下，组件在内容较少的页面上自适应内容高度，在包含长列表的页面上最高为 `686` 像素（`maxHeight`）。这不需要任何配置更改。

### 限制最大高度

组件会根据内容扩展和收缩，但不会超过指定的最大高度。超出的页面内容可以滚动。

在 `theme.container` 上将 `maxHeight` 设置为数字（像素）。值必须大于 `686`（默认值）。

```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  theme: {
    container: {
      maxHeight: 820,
    },
  },
};
```

<Frame caption="页面高度可能在不同页面间变化">
  <img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/page_height.gif?s=b36f1dd34ccfdf56d0c61bbe672a958b" width="532" height="864" data-path="images/page_height.gif" />
</Frame>

### 限制高度

所有页面都占据指定的精确高度，组件保持一致的大小。超出的页面内容可以滚动。

在 `theme.container` 上将 `height` 设置为数字（像素）。值必须大于 `686`（默认值）。

```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  theme: {
    container: {
      height: 900,
    },
  },
};
```

<Frame caption="页面高度在不同页面间保持一致">
  <img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/consistent_height.gif?s=01e00b209d8227ace987238203faf528" width="524" height="850" data-path="images/consistent_height.gif" />
</Frame>

<Note>
  不要同时使用 `height` 和 `maxHeight`，它们代表组件中不同的布局方式。
</Note>

### 全高

推荐用于移动端和屏幕空间有限、组件作为主要内容的场景。组件会填充其所在 HTML 元素的全部高度，并将滚动委托给页面。

<Frame caption="适用于较小的屏幕">
  <img src="https://mintcdn.com/lifi/08FOM1AsMmrVbIEl/images/small_screens.png?fit=max&auto=format&n=08FOM1AsMmrVbIEl&q=85&s=9b1022996f87f3f14da66422473ce7f2" width="812" height="1604" data-path="images/small_screens.png" />
</Frame>

```typescript theme={"system"}
import { WidgetConfig } from '@lifi/widget';

const widgetConfig: WidgetConfig = {
  variant: 'compact',
  theme: {
    container: {
      display: 'flex',
      height: '100%',
    },
    header: {
      position: 'fixed',
      top: 0,
    },
  },
};
```

配置说明：

* **variant: 'compact'**：必需；紧凑型专为较小屏幕设计。
* **theme.container**：`display: 'flex'` 和 `height: '100%'` 使组件填充其容器。
* **theme.header**（可选）：设置 `position: 'fixed'` 和 `top` 值使页眉固定。调整 `top` 以适应组件上方的元素（例如 60px 的导航栏对应 `top: 60`）。如果不设置，页眉会随页面内容滚动。

全高模式将尺寸控制委托给父容器，因此您页面的 HTML 和 CSS 必须正确处理。

* 可能需要更新视口 meta 标签以使页面正确呈现

  * 例如 `<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">`

* 组件演示平台使用 `min-height: 100dvh`，高于视口的页面保持可滚动，较矮的页面则使用 flex 填满屏幕。

* 在页面的根元素/body 上添加 `overscroll-behavior: none;` 以防止不需要的滚动行为。

<Note>
  请考虑组件与您网站导航、页眉和页脚的相对位置。在 [组件演示平台](https://playground.li.fi/) 中预览页眉和/或页脚的布局效果，打开侧边栏中的 **Developer Controls** 并切换 'Show mock header' 和/或 'Show mock footer'。
</Note>
