Skip to content

增加自定义工具栏功能和文档说明 #35

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 111 additions & 1 deletion packages/floatingToolbar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<p align="center">floating toolbar plugin for canvas-editor</p>

## usage
## 基本用法

```bash
npm i @hufe921/canvas-editor-plugin-floating-toolbar --save
Expand All @@ -15,3 +15,113 @@ import floatingToolbarPlugin from '@hufe921/canvas-editor-plugin-floating-toolba
const instance = new Editor()
instance.use(floatingToolbarPlugin)
```

## 高级用法

### 自定义工具栏

```javascript
import Editor from '@hufe921/canvas-editor'
import floatingToolbarPlugin from '@hufe921/canvas-editor-plugin-floating-toolbar'

const instance = new Editor()

// 配置选项
const options = {
// 是否显示默认工具栏项,默认为true
showDefaultItems: true,

// 自定义工具栏项
customItems: [
{
key: 'custom-ai-polish',
title: 'AI润色',
icon: '.icon-ai-polish', // CSS类图标(以.开头表示使用CSS类)
callback: editor => {
alert('AI润色功能尚未实现')
}
},
// 添加分隔符
{
isDivider: true,
key: 'divider-1' // key对分隔符是可选的
},
{
key: 'custom-text-icon',
title: '文本图标',
icon: 'T', // 文本图标(直接使用字符作为图标)
callback: editor => {
// 自定义功能
}
}
]
}

instance.use(floatingToolbarPlugin, options)
```

### 图标类型

浮动工具栏支持两种类型的图标:

1. **CSS 类图标**

- 使用`.className`格式,如:`.icon-align-left`
- 插件会自动将其解析为 CSS 类名并应用到图标元素上
- 您需要在 CSS 中定义相应的样式,例如:

```css
/* 方法1: 使用SVG文件 */
.icon-ai-polish {
background-image: url('/path/to/icons/ai-polish.svg');
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}
```

2. **文本图标**
- 直接使用文本字符作为图标,如:`T`、`B`、`I`等
- 适合简单的图标表示,会居中显示在工具栏按钮中

### 分隔符

您可以在工具栏中添加分隔符来分隔不同功能的按钮:

```javascript
{
isDivider: true,
key: 'divider-name' // 可选,用于标识分隔符
}
```

分隔符会在工具栏中显示为垂直线。

## 接口说明

### 选项接口

```typescript
// 工具栏配置选项
interface IFloatingToolbarOptions {
customItems?: IToolbarItem[] // 自定义工具栏项
showDefaultItems?: boolean // 是否显示默认工具栏项
}

// 分隔符项
interface IToolbarDivider {
isDivider: true
key?: string // 可选的标识符
}

// 常规工具栏项
interface IToolbarAction {
key: string // 唯一标识
title?: string // 悬停提示
icon?: string // 图标(CSS类名或自定义文本)
callback: (editor: Editor) => void // 点击回调
}

// 工具栏项可以是常规项或分隔符
type IToolbarItem = IToolbarDivider | IToolbarAction
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
104 changes: 88 additions & 16 deletions packages/floatingToolbar/src/floatingToolbar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import Pickr from '@simonwep/pickr'
import Editor from '@hufe921/canvas-editor'
import './style/index.scss'
import { ToolbarType } from './enum'
import { IToolbarRegister } from './interface'
import { IFloatingToolbarOptions, IToolbarRegister } from './interface'
import { PLUGIN_PREFIX } from './constant'

interface IRangeStyle {
type: string | null
bold: boolean
italic: boolean
underline: boolean
strikeout: boolean
}

function createPickerToolbar(
container: HTMLDivElement,
toolbarType: ToolbarType,
Expand Down Expand Up @@ -61,16 +69,18 @@ function createPickerToolbar(
})
}

// 工具栏列表
const toolbarRegisterList: IToolbarRegister[] = [
// 默认工具栏列表
const defaultToolbarRegisterList: IToolbarRegister[] = [
{
key: ToolbarType.SIZE_ADD,
title: '增大字号',
callback(editor) {
editor.command.executeSizeAdd()
}
},
{
key: ToolbarType.SIZE_MINUS,
title: '减小字号',
callback(editor) {
editor.command.executeSizeMinus()
}
Expand All @@ -80,24 +90,28 @@ const toolbarRegisterList: IToolbarRegister[] = [
},
{
key: ToolbarType.BOLD,
title: '粗体',
callback(editor) {
editor.command.executeBold()
}
},
{
key: ToolbarType.ITALIC,
title: '斜体',
callback(editor) {
editor.command.executeItalic()
}
},
{
key: ToolbarType.UNDERLINE,
title: '下划线',
callback(editor) {
editor.command.executeUnderline()
}
},
{
key: ToolbarType.STRIKEOUT,
title: '删除线',
callback(editor) {
editor.command.executeStrikeout()
}
Expand All @@ -106,13 +120,17 @@ const toolbarRegisterList: IToolbarRegister[] = [
isDivider: true
},
{
key: ToolbarType.COLOR,
title: '文字颜色',
render(container, editor) {
createPickerToolbar(container, ToolbarType.COLOR, color => {
editor.command.executeColor(color)
})
}
},
{
key: ToolbarType.HIGHLIGHT,
title: '高亮颜色',
render(container, editor) {
createPickerToolbar(container, ToolbarType.HIGHLIGHT, color => {
editor.command.executeHighlight(color)
Expand All @@ -121,28 +139,79 @@ const toolbarRegisterList: IToolbarRegister[] = [
}
]

function createToolbar(editor: Editor): HTMLDivElement {
function createToolbar(
editor: Editor,
options?: IFloatingToolbarOptions
): HTMLDivElement {
const toolbarContainer = document.createElement('div')
toolbarContainer.classList.add(`${PLUGIN_PREFIX}-floating-toolbar`)
for (const toolbar of toolbarRegisterList) {

let toolbarItems: IToolbarRegister[] = []

// 决定是否显示默认工具栏
if (!options || options.showDefaultItems !== false) {
toolbarItems = [...defaultToolbarRegisterList]
}

// 添加自定义工具栏项
if (options?.customItems?.length) {
const customRegisterItems: IToolbarRegister[] = options.customItems.map(
item => {
// 处理分隔符情况
if (item.isDivider) {
return { isDivider: true }
}

return {
key: item.key,
title: item.title,
icon: item.icon,
callback: item.callback
}
}
)

toolbarItems = [...toolbarItems, ...customRegisterItems]
}

// 创建工具栏项
for (const toolbar of toolbarItems) {
if (toolbar.render) {
toolbar.render(toolbarContainer, editor)
} else if (toolbar.isDivider) {
const divider = document.createElement('div')
divider.classList.add(`${PLUGIN_PREFIX}-divider`)
toolbarContainer.append(divider)
} else {
const { key, callback } = toolbar
const toolbarItem = document.createElement('div')
toolbarItem.classList.add(`${PLUGIN_PREFIX}-${key}`)
const icon = document.createElement('i')
toolbarItem.append(icon)
toolbarItem.onclick = () => {
callback?.(editor)
const { key, callback, title, icon } = toolbar

if (key && callback) {
const toolbarItem = document.createElement('div')
toolbarItem.classList.add(`${PLUGIN_PREFIX}-${key}`)

if (title) {
toolbarItem.setAttribute('title', title)
}

const iconElement = document.createElement('i')
if (icon) {
if (icon.startsWith('.') || icon.startsWith('#')) {
iconElement.className = icon.substring(1)
} else {
iconElement.textContent = icon
}
}

toolbarItem.append(iconElement)
toolbarItem.onclick = () => {
callback(editor)
}

toolbarContainer.append(toolbarItem)
}
toolbarContainer.append(toolbarItem)
}
}

return toolbarContainer
}

Expand All @@ -156,14 +225,17 @@ function toggleToolbarItemActive(toolbarItem: HTMLDivElement, active: boolean) {
: toolbarItem.classList.remove('active')
}

export default function floatingToolbarPlugin(editor: Editor) {
export default function floatingToolbarPlugin(
editor: Editor,
options?: IFloatingToolbarOptions
) {
// 创建工具栏
const toolbarContainer = createToolbar(editor)
const toolbarContainer = createToolbar(editor, options)
const editorContainer = editor.command.getContainer()
editorContainer.append(toolbarContainer)

// 监听选区样式变化
editor.eventBus.on('rangeStyleChange', rangeStyle => {
editor.eventBus.on('rangeStyleChange', (rangeStyle: IRangeStyle) => {
if (rangeStyle.type === null) {
toggleToolbarVisible(toolbarContainer, false)
return
Expand Down
22 changes: 22 additions & 0 deletions packages/floatingToolbar/src/floatingToolbar/interface/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
import Editor from '@hufe921/canvas-editor'

export interface IToolbarDivider {
isDivider: true
key?: string
}

export interface IToolbarAction {
key: string
title?: string
icon?: string // CSS类名或自定义图标字符串
isDivider?: false
callback: (editor: Editor) => void
}

export type IToolbarItem = IToolbarDivider | IToolbarAction

export interface IToolbarRegister {
key?: string
isDivider?: boolean
title?: string
icon?: string
render?: (container: HTMLDivElement, editor: Editor) => void
callback?: (editor: Editor) => void
}

export interface IFloatingToolbarOptions {
customItems?: IToolbarItem[]
showDefaultItems?: boolean
}
Loading