---
title: "添加链接弹出框 UI 组件"
description: "使用此 Tiptap UI 组件在弹出框元素中选择链接选项。更多内容请参考我们的文档"
canonical_url: "https://tiptap.zhcndoc.com/ui-components/components/link-popover"
---

# 添加链接弹出框 UI 组件

使用此 Tiptap UI 组件在弹出框元素中选择链接选项。更多内容请参考我们的文档

用于创建和编辑链接的弹出框。

> **Interactive demo:** [link popover](https://template.tiptap.dev/preview/tiptap-ui/link-popover)

## 安装

您可以通过 Tiptap CLI（适用于 Vite 或 Next.js）添加组件：

```bash
npx @tiptap/cli@latest add link-popover
```

### 手动集成

对于 Vite 或 Next.js 以外的框架，请从 [开源仓库](https://github.com/ueberdosis/tiptap-ui-components/tree/main/apps/web/src/components/tiptap-ui/link-popover) 手动添加组件。

### 导入样式

此组件需要您导入我们的样式，这些样式已添加到 `styles/keyframe-animation.scss` 和 `styles/_variables.scss` 中。

## 使用

### `<LinkPopover />`

一个为 Tiptap 编辑器提供完整链接编辑界面的预制 React 组件，带有弹出框。用户友好的弹出界面支持键盘快捷键和灵活的自定义选项，可轻松添加、编辑和删除链接。

#### 使用范例

```tsx
import { EditorContent, EditorContext, useEditor } from '@tiptap/react'
import { StarterKit } from '@tiptap/starter-kit'
import { Link } from '@/components/tiptap-extension/link-extension'
import { LinkPopover } from '@/components/tiptap-ui/link-popover'

import '@/components/tiptap-node/paragraph-node/paragraph-node.scss'

export default function MyEditor() {
  const editor = useEditor({
    immediatelyRender: false,
    extensions: [StarterKit, Link.configure({ openOnClick: false })],
    content: `
        <p>点击按钮打开链接弹出框。</p>
        <p><a href="https://www.tiptap.dev">Tiptap</a></p>
        `,
  })

  return (
    <EditorContext.Provider value={{ editor }}>
      <LinkPopover
        editor={editor}
        hideWhenUnavailable={true}
        autoOpenOnLinkActive={true}
        onSetLink={() => console.log('Link set!')}
        onOpenChange={(isOpen) => console.log('Popover opened:', isOpen)}
      />

      <EditorContent editor={editor} role="presentation" />
    </EditorContext.Provider>
  )
}
```

#### 参数说明

| 名称                   | 类型                        | 默认值   | 描述             |
| -------------------- | ------------------------- | ----- | -------------- |
| editor               | Editor \| null            | null  | Tiptap 编辑器实例   |
| hideWhenUnavailable  | boolean                   | false | 链接功能不可用时是否隐藏按钮 |
| onSetLink            | () => void                | -     | 成功设置链接后的回调     |
| onOpenChange         | (isOpen: boolean) => void | -     | 弹出框打开状态改变时的回调  |
| autoOpenOnLinkActive | boolean                   | true  | 链接激活时是否自动打开弹出框 |

### `<LinkButton />`

一个独立的链接按钮组件，用以触发链接相关功能。

#### 使用范例

```tsx
<LinkButton onClick={handleClick} aria-label="添加链接">
  自定义链接内容
</LinkButton>
```

### `<LinkContent />`

一个独立组件，渲染链接编辑界面，但不包含弹出框包装。

#### 使用范例

```tsx
<LinkContent editor={editor} />
```

## Hooks

### `useLinkPopover()`

自定义钩子，可构建自定义链接界面，全面控制渲染及行为。

#### 使用范例

```tsx
function MyLinkButton() {
  const { isVisible, canSet, isActive, url, setUrl, setLink, removeLink, label, Icon } =
    useLinkPopover({
      editor: myEditor,
      hideWhenUnavailable: true,
      onSetLink: () => console.log('Link set!'),
    })

  if (!isVisible) return null

  return (
    <div>
      <button onClick={setLink} disabled={!canSet} aria-label={label} aria-pressed={isActive}>
        <Icon />
        {label}
      </button>
      <input
        type="url"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
        placeholder="请输入 URL..."
      />
      <button onClick={removeLink}>移除</button>
    </div>
  )
}
```

#### 参数说明

| 名称                  | 类型             | 默认值   | 描述            |
| ------------------- | -------------- | ----- | ------------- |
| editor              | Editor \| null | -     | Tiptap 编辑器实例  |
| hideWhenUnavailable | boolean        | false | 链接不能应用时是否隐藏功能 |
| onSetLink           | () => void     | -     | 设置链接后的回调      |

#### 返回值说明

| Name         | Type                                                   | Description                                       |
| ------------ | ------------------------------------------------------ | ------------------------------------------------- |
| `isVisible`  | `boolean`                                              | Whether the link functionality should be rendered |
| `canSet`     | `boolean`                                              | If a link can be set in the current context       |
| `isActive`   | `boolean`                                              | If a link is currently active/selected            |
| `url`        | `string`                                               | Current URL value for the link                    |
| `setUrl`     | `React.Dispatch<React.SetStateAction<string \| null>>` | Function to update the URL state                  |
| `setLink`    | `() => void`                                           | Function to apply the link in the editor          |
| `removeLink` | `() => void`                                           | Function to remove the link from the editor       |
| `label`      | `string`                                               | Accessible label text for the button              |
| `Icon`       | `React.FC`                                             | Icon component for the link button                |

### `useLinkHandler()`

专注于处理链接操作的钩子，不包含 UI 状态管理。

#### 使用范例

```tsx
function MyCustomLinkInterface() {
  const { url, setUrl, setLink, removeLink } = useLinkHandler({
    editor: myEditor,
    onSetLink: () => console.log('链接已应用！'),
  })

  return (
    <div>
      <input
        value={url}
        onChange={(e) => setUrl(e.target.value)}
        onKeyDown={(e) => e.key === 'Enter' && setLink()}
      />
      <button onClick={setLink}>应用</button>
      <button onClick={removeLink}>移除</button>
    </div>
  )
}
```

#### 参数说明

| Name        | Type             | Default     | Description                         |
| ----------- | ---------------- | ----------- | ----------------------------------- |
| `editor`    | `Editor \| null` | `undefined` | The Tiptap editor instance          |
| `onSetLink` | `() => void`     | `undefined` | Callback fired after setting a link |

#### 返回值说明

| Name         | Type                                                   | Description                                 |
| ------------ | ------------------------------------------------------ | ------------------------------------------- |
| `url`        | `string`                                               | Current URL value for the link              |
| `setUrl`     | `React.Dispatch<React.SetStateAction<string \| null>>` | Function to update the URL state            |
| `setLink`    | `() => void`                                           | Function to apply the link in the editor    |
| `removeLink` | `() => void`                                           | Function to remove the link from the editor |

## 工具函数

### `canSetLink(editor)`

判断当前编辑器状态是否允许设置链接。

```tsx
import { canSetLink } from '@/components/tiptap-ui/link-popover'

const canSet = canSetLink(editor)
if (canSet) {
  console.log('当前选区可应用链接')
}
```

### `isLinkActive(editor)`

判断编辑器中是否当前处于链接激活状态。

```tsx
import { isLinkActive } from '@/components/tiptap-ui/link-popover'

const isActive = isLinkActive(editor)
if (isActive) {
  console.log('当前已选中链接')
}
```

## 键盘快捷键

链接弹出框支持以下键盘操作：

- **Enter**：应用当前输入框中 URL 作为链接（当焦点在 URL 输入框时）
- **Escape**：关闭弹出框（标准弹出框行为）

## 需求

### 依赖

- `@tiptap/react` - Tiptap React 核心集成
- `@tiptap/extension-link` - 链接功能扩展

### 使用到的参考组件

- `use-tiptap-editor`（钩子）
- `use-mobile`（钩子）
- `use-link-popover`（钩子）
- `button`（原语）
- `popover`（原语）
- `card`（原语）
- `input`（原语）
- `separator`（原语）
- `tiptap-utils`（库）
- `corner-down-left-icon`（图标）
- `external-link-icon`（图标）
- `link-icon`（图标）
- `trash-icon`（图标）
