【element-tiptap】如何把分隔线改造成下拉框的形式?

当前的分隔线只有细横线这一种形式
在这里插入图片描述
但是咱们可以看一下wps中的分隔线,花里胡哨的
这些在wps里都需要使用快捷键打出来,真没找到菜单在哪里
在这里插入图片描述
那么这篇文章咱们就来看一下如何改造分隔线组件,改造成下拉框的形式,并且把咱们想要的分隔线都放进去
分隔线扩展是这个 HorizontalRule
src/extensions/horizontal-rule.ts

1、创建下拉框组件

项目中有好几个下拉框组件,首先,咱们需要仿照它们,创建分隔线的下拉框组件
仿照上一篇文章研究的 FontFamilyDropdown.vue,先大致写一下,后面再详细补充

<template><el-dropdown placement="bottom" trigger="click" @command="insertHorizontalRule"><command-button :enable-tooltip="enableTooltip" tooltip="插入分隔线" icon="horizontal-rule" /><template #dropdown><el-dropdown-menu><el-dropdown-item v-for="rule in horizontalRules" :key="rule.value" :command="rule.value">{{ rule.label }}</el-dropdown-item></el-dropdown-menu></template></el-dropdown>
</template>
<script lang="ts">
import {defineComponent, inject} from 'vue';
import {Editor, getMarkAttributes} from '@tiptap/vue-3';
import {ElDropdown, ElDropdownMenu, ElDropdownItem} from 'element-plus';
import CommandButton from './CommandButton.vue';export default defineComponent({name: 'FontFamilyDropdown',components: {ElDropdown,ElDropdownMenu,ElDropdownItem,CommandButton,},props: {editor: {type: Editor,required: true,},},setup() {const t = inject('t');const enableTooltip = inject('enableTooltip', true);const isCodeViewMode = inject('isCodeViewMode', false);return {t, enableTooltip, isCodeViewMode};},computed: {horizontalRules() {return [{ label: '细线', value: '---' },{ label: '粗线', value: '___' },{ label: '星号线', value: '***' },];},},methods: {insertHorizontalRule(rule: string) {this.editor.commands.setHorizontalRule();},},
});
</script>

2、在扩展中应用分隔线下拉框组件

src/extensions/horizontal-rule.ts

import type { Editor } from '@tiptap/core';
import TiptapHorizontalRule from '@tiptap/extension-horizontal-rule';
import HorizontalRuleDropdown from '@/components/MenuCommands/HorizontalRuleDropdown.vue';const HorizontalRule = TiptapHorizontalRule.extend({addOptions() {return {// 保留父扩展的所有选项...this.parent?.(),button({ editor, t }: { editor: Editor; t: (...args: any[]) => string }) {return {component: HorizontalRuleDropdown,componentProps: {editor,},};},};},
});export default HorizontalRule;

此时模样已经出来了
在这里插入图片描述
点击菜单选项都能够插入
看一下此时插入的分隔线,其实是一个 <hr/> 标签
在这里插入图片描述
显然这个方法满足不了我们的需求,因为 editor.commands.setHorizontalRule() 这个方法不允许传递参数,给元素增加类名或者其他属性。那我们只能重新写一个函数

3、探索插入的实现

向文档中插入节点,有一个方法是 editor.commands.insertContentAt
可以看一下这个函数的定义
node_modules/@tiptap/core/dist/commands/insertContentAt.d.ts

insertContentAt: (/*** 插入内容的位置。*/position: number | Range, /*** 要插入的 ProseMirror 内容。*/value: Content, /*** 可选的选项*/options?: {/*** 解析内容的选项。*/parseOptions?: ParseOptions;/*** 插入内容后是否更新选区。*/updateSelection?: boolean;/*** 插入内容后是否应用输入规则。*/applyInputRules?: boolean;/*** 插入内容后是否应用粘贴规则。*/applyPasteRules?: boolean;/*** 内容无效时是否抛出错误。*/errorOnInvalidContent?: boolean;}) => ReturnType;

可选的选项咱们可以先不管,先看下前两个参数

  • position
    可以传数字或者 Range,传数字表示索引,传 Range 就是在固定位置插入
  • value: Content
    具体内容
export type Content = HTMLContent | JSONContent | JSONContent[] | null;

HTMLContentstring 类型,直接这样写就行

'<h1>Example</h1>'

JSONContent 的定义如下

export type JSONContent = {type?: string;attrs?: Record<string, any>;content?: JSONContent[];marks?: {type: string;attrs?: Record<string, any>;[key: string]: any;}[];text?: string;[key: string]: any;
};

JSONContenttype 属性,我原以为会有一个常量的列表,但是我太天真了,找了半天没找到。但是经过我的实验,可以确定的是,如果你想往文档里插入一个 div,那么你注定会失败,例如我想用下面代码插入一个 div 标签:

editor.commands.insertContentAt(selection.from,
{type: 'div',text: 'dsadsa'
})

结果执行完了之后,长这样:
在这里插入图片描述
合理猜测,这个 type 属性,只允许在编辑器中定义好的节点类型。哭唧唧
但是我们还有有路可以走,比如,探索一下其他的节点是怎么插入的,然后模仿并且超越。

不如就来看一下图片是怎么插入的!
一个图片插入功能,其实需要好几个文件来支撑

  • src/utils/image.ts 文件,这个文件定义加载和缓存图片的方法、以及提供了图像显示方式的枚举,可以认为一些基础方法、枚举值都在这个文件夹的 ts 文件中定义

  • src/extensions/image.ts,这个文件是用来扩展 tiptap 图像节点的,并且集成了插入图片的组件,提供了自定义图像属性的方法和渲染HTML的方法

  • src/components/ExtensionViews/ImageView.vue,是图像展示的组件,用于渲染和交互式调整图像
    在这个文件中,我们可以看到,实际的插入的图像内容是被一个标签 node-view-wrapper 包裹起来的,所以咱们待会构建分割符组件的时候也要用这个标签把我们实际要插入的内容包裹起来
    在这里插入图片描述

  • src/components/MenuCommands/Image/ImageDisplayCommandButton.vue,是一个弹出菜单修改图像显示方式的组件
    在这里插入图片描述

  • src/components/MenuCommands/Image/InsertImageCommandButton.vue,插入图像的按钮,下拉框有两个选项
    在这里插入图片描述

  • src/components/MenuCommands/Image/EditImageCommandButton.vue,编辑图像的组件
    在这里插入图片描述

  • src/components/MenuCommands/Image/RemoveImageCommandButton.vue 删除图像的组件,其实就一个小按钮
    在这里插入图片描述

好吧,万幸,插入分割线功能没有这么的复杂,在插入之后就不需要修改了。那我们来梳理一下我们需要创建几个文件来插入分割线。

1、src/utils/horizontal-rule.ts 定义分割线类型和html之间的对应关系
2、src/extensions/horizontal-rule.ts 调用 tiptap 的API增加扩展项
3、src/components/MenuCommands/HorizontalRuleDropdown.vue 定义下拉菜单,用来选择分割线的类型
4、src/components/ExtensionViews/HorizontalRuleView.vue 定义插入分割线渲染出来的组件

接下来,咱们就挨个文件看,我这里主要仿照两个组件,一个是图片相关的,一个是Iframe相关的

4、src/utils/horizontal-rule.ts

这里定义为数组,在下拉框中,我们需要直接展示出来分割线,但是点击分割线的时候,需要把分割线的类型取出来,给 setHorizontalRule 方法;但是当插入的时候,又要根据分割线的类型去找对应的分割线的html。所以说,分割线的类型,其实也可以叫做唯一标识,与分割线的html之间是需要双向转换的。使用对象的话,反向查找就会有一些不方便,所以直接定义成数组。下面的html是经过我测试的,大家在开发的时候可以先写一些简单的测试数据,我的数据效果是这样子的:
在这里插入图片描述

export const horizontalRules = [{borderStyle: 'solid',html: `<hr style="border: none; border-top: 1px solid black;">`},{borderStyle: 'dotted',html: `<hr style="border: none; border-top: 1px dotted black;">`},{borderStyle: 'dashed',html: `<hr style="border: none; border-top: 1px dashed black;">`},{borderStyle: 'double',html: `<hr style="border: none; height: 6px; border-top: 1px solid black; border-bottom: 3px solid black;">`},{borderStyle: 'triple',html: `<div style="display: flex; flex-direction: column; gap: 2px;"><hr style="border: none; border-top: 1px solid black; margin: 0;"><hr style="border: none; border-top: 2px solid black; margin: 0;"><hr style="border: none; border-top: 1px solid black; margin: 0;"></div>`},
];export default horizontalRules;

5、src/extensions/horizontal-rule.ts

还是来看 src/extensions/horizontal-rule.ts 文件,参考 src/extensions/image.ts 文件
① 分割线需要一个属性表示分割线的类型,那么就需要 addAttributes 方法,直接仿照图片扩展里面的代码写就行

addAttributes() {return {...this.parent?.(),'border-style': {parseHTML: (element) => {const borderStyle = element.getAttribute('borderStyle');return borderStyle;},renderHTML: (attributes) => {return {'border-style': attributes['border-style'],};},},};
},

② 需要 addNodeView 为扩展添加节点视图

addNodeView() {return VueNodeViewRenderer(HorizontalRuleView);
},

③ 需要 parseHTMLrenderHTML 用来解析和渲染HTML

// 为扩展添加解析HTML
parseHTML() {return [{tag: 'div',},];
},
// 为扩展添加渲染HTML
renderHTML({ HTMLAttributes }) {return ['div',HTMLAttributes];
},

④ 添加命令。由于tiptap提供的 setHorizontalRule 方法满足不了需求,所以我们需要重写一下这个方法

addCommands() {return {setHorizontalRule:(options) =>({ commands }) => {return commands.insertContent({type: this.name,attrs: {'border-style': options.borderStyle,},});},};
},

完整代码

import type { Editor } from '@tiptap/core';
import TiptapHorizontalRule from '@tiptap/extension-horizontal-rule';
import HorizontalRuleDropdown from '@/components/MenuCommands/HorizontalRuleDropdown.vue';
import { mergeAttributes, VueNodeViewRenderer } from '@tiptap/vue-3';
import HorizontalRuleView from '@/components/ExtensionViews/HorizontalRuleView.vue';const HorizontalRule = TiptapHorizontalRule.extend({// 返回的数据,第一个是继承的父级的属性// 后面的是自己的属性addAttributes() {return {...this.parent?.(),'border-style': {parseHTML: (element) => {const borderStyle = element.getAttribute('borderStyle');return borderStyle;},renderHTML: (attributes) => {return {'border-style': attributes['border-style'],};},},};},// 为扩展添加选项addOptions() {return {// 保留父扩展的所有选项...this.parent?.(),button({ editor, t }: { editor: Editor; t: (...args: any[]) => string }) {return {component: HorizontalRuleDropdown,componentProps: {editor,},};},};},// 为扩展添加节点视图addNodeView() {return VueNodeViewRenderer(HorizontalRuleView);},// 为扩展添加解析HTMLparseHTML() {return [{tag: 'div',},];},// 为扩展添加渲染HTMLrenderHTML({ HTMLAttributes }) {return ['div',HTMLAttributes];},// 为扩展添加命令addCommands() {return {setHorizontalRule:(options) =>({ commands }) => {return commands.insertContent({type: this.name,attrs: {'border-style': options.borderStyle,},});},};},
});export default HorizontalRule;

6、src/components/MenuCommands/HorizontalRuleDropdown.vue

这个方法咱们已经实现的很成熟了,首先数据在这里我们不需要再定义一遍了,需要从咱们刚定义的文件中引入

import horizontalRules from '@/utils/horizontal-rule';

在 setup 函数中返回 horizontalRules:

return { t, enableTooltip, isCodeViewMode, horizontalRules };

循环的模版代码要修改一下,因为我们现在使用的是数组了

<el-dropdown-item v-for="rule in horizontalRules" :key="rule.borderStyle" :command="rule.borderStyle"><div contenteditable="false" class="horizontal-rule-item" v-html="rule.html"></div>
</el-dropdown-item>

要把 borderStyle 传到 setHorizontalRule 命令里面

insertHorizontalRule(borderStyle: string) {this.editor.commands.setHorizontalRule({ borderStyle });
},    

7、src/components/ExtensionViews/HorizontalRuleView.vue

这个文件就是插入分割线的时候实际插入的内容,模版需要使用 node-view-wrapper 标签包裹,数据也从我们定义的常量文件中获取

<template><node-view-wrapper as="div" class="horizontal-rule"><divclass="horizontal-rule__line" v-html="getHorizontalRuleHtml(borderType)"></div></node-view-wrapper>
</template><script lang="ts">
import { defineComponent } from 'vue';
import { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3';
import horizontalRules from '@/utils/horizontal-rule';export default defineComponent({name: 'HorizontalRuleView',components: {NodeViewWrapper,},props: nodeViewProps,computed: {borderType(): string {return this.node!.attrs['border-style'];},},methods: {getHorizontalRuleHtml(borderStyle: string): string {const rule = horizontalRules.find(rule => rule.borderStyle === borderStyle);return rule ? rule.html : '';},},
});
</script><style scoped>
.horizontal-rule__line {width: 100%;
}
</style>

8、看看效果
首先,点击下拉框按钮,弹出菜单
在这里插入图片描述
然后,点击菜单项,就会插入分割线
在这里插入图片描述
边距之类的样式可以自己在调整调整 耶耶耶耶耶
通过这篇文章,也掌握了tiptap大致的扩展节点的方法就是需要那么几个目录文件
1、src/utils/xx.ts 定义常量
2、src/extensions/xx.ts 定义扩展,可以创建新的节点,也可以继承、重写已有的节点
3、src/compoents/MenuCommands/xx.vue 定义菜单项
4、src/components/ExtensionViews/xx.vue 定义实际插入的内容,需要使用node-view-wrapper 标签包裹

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/57425.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何调试浏览器中的内存泄漏?

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介⭐ 如何调试浏览器中的内存泄漏&#xff1f;1. 什么是内存泄漏&#xff1f;2. 调试内存泄漏的工具3. 如何使用 Memory 面板进行内存调试3.1 获取内存快照&#xff08;Heap Snapshot&#xff09;获取内存快照的步骤&#xff1a;快照…

【二刷hot100】day 4

终于有时间刷刷力扣&#xff0c;求实习中。。。。 目录 1.最大子数组和 2.合并区间 3.轮转数组 4.除自身以外数组的乘积 1.最大子数组和 class Solution {public int maxSubArray(int[] nums) {//就是说可以转换为计算左边的最大值&#xff0c;加上中间的值&#xff0c…

1.6,unity动画Animator屏蔽某个部位,动画组合

动画组合 一边跑一边攻击 using System.Collections; using System.Collections.Generic; using UnityEngine;public class One : MonoBehaviour {private Animator anim;// Start is called before the first frame updatevoid Start(){anim GetComponent<Animator>();…

PPT自动化:Python如何修改PPT文字和样式!

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 使用 Python 修改 PPT 文本内容📝 遍历所有幻灯片和文本框📝 设置和修改文本样式📝 复制和保留文本样式⚓️ 相关链接 ⚓️📖 介绍 📖 在日常工作中,PPT 的文字内容和样式修改似乎是一项永无止境的…

渗透测试实战—教育攻防演练中突破网络隔离

免责声明&#xff1a;文章来源于真实渗透测试&#xff0c;已获得授权&#xff0c;且关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本…

基准线markLine的值超过坐标轴范围导致markline不显示

解决问题&#xff1a;动态设置yAxis的max值&#xff08;解决基准线不在y轴范围&#xff09; yAxis: [{name: 单位&#xff1a;千,...yAxis,nameTextStyle:{...yAxis.nameTextStyle,padding: [0,26,0,24]},paddingLeft:24,paddingRight:26},{name: 单位&#xff1a;百分比,...yA…

基金好书入门阅读笔记《基金作战笔记:从投基新手到配置高手的进阶之路》笔记3

公募基金的分类方式按投资范围分 80%以上资产投资于股票的&#xff0c;叫股票基金&#xff1b;80%以上资产投资于债券的&#xff0c;叫债券基金&#xff1b;80% 以上资产投资于其他基金的&#xff0c;叫FOF; 80%以上资产投资于货币市场的&#xff0c;叫货币基金&#xff1b;以上…

建库建表练习

目录 根据以下需求完成图书管理系统数据库及表设计&#xff0c;并建库建表&#xff0c;并截图创建表的详细信息(desc 表名),不用添加数据 1. 用户表: 字段: 姓名&#xff0c;用户名&#xff0c;密码&#xff0c;电话&#xff0c;住址&#xff0c;专业及年级 2. 图书表: 字段: 图…

大数据新视界 -- 大数据大厂之 AI 驱动的大数据分析:智能决策的新引擎

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

Flux.all 使用说明书

all public final Mono<Boolean> all(Predicate<? super T> predicate)Emit a single boolean true if all values of this sequence match the Predicate. 如果该序列中的所有值都匹配给定的谓词&#xff08;Predicate&#xff09;&#xff0c;则发出一个布尔值…

大模型之三十二-语音合成TTS(coqui) 之二 fine-tune

在 大模型之三十-语音合成TTS(coqui)[shichaog CSDN]中提到了xttsv2的fine-tune。 数据情况&#xff1a; 我是从bilibili up主小Lin说提取了一些视频&#xff0c;然后进行了重新的fine-tune。 训练结果 如下图所示&#xff0c;上面波形幅度较大的是xttsv2原始模型的结果&am…

tauri打包失败Error failed to bundle project: error running light.exe解决办法

Running light to produce D:\a\PakePlus\PakePlus\src-tauri\target\release\bundle\msi\快手_0.0.1_x64_en-US.msi Error failed to bundle project: error running light.exe ELIFECYCLE  Command failed with exit code 1. Error: Command failed with exit code 1: pnpm …

Python进阶--海龟绘图turtle库

目录 1. 画笔运动命令 2. 画笔控制命令 3. 设置画布大小 1. 画笔运动命令 2. 画笔控制命令 turtle.pensize()&#xff1a;设置画笔的宽度&#xff1b; turtle.pencolor(‘red’)&#xff1a;没有参数传入&#xff0c;返回当前画笔颜色&#xff0c;传入参数设置画笔颜色。 t…

Linux:线程及其控制

我们已经学了线程的创建&#xff0c;现在要学习线程的控制 线程等待 我们来先写一个没有线程等待的代码&#xff1a; pthcon.c: #include<stdio.h> #include<pthread.h> void* gopthread(void* arg){while(1){printf("pthread is running\n");sleep(1…

你了解的spring框架有哪些

列举一些重要的Spring模块&#xff1f; Spring Core&#xff1a; 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IOC 依赖注入功能。**Spring Aspects ** &#xff1a; 该模块为与AspectJ的集成提供支持。Spring AOP &#xff1a;提供了面向方面的编程实现。…

【翻译】Qt Designer自定义控件简介

原文链接&#xff1a;Using Custom Widgets with Qt Widgets Designer Qt Designer 可以通过其可扩展的插件机制显示自定义控件&#xff0c;允许用户和第三方扩展定义控件的范围。或者&#xff0c;也可以使用现有的控件作为提供类似 API 的控件类的占位符。 处理自定义控件 尽…

app端文章列表查询-详细教程(上)

app端文章列表查询 一、数据库方面 有关文章的表垂直拆分成了三张表&#xff1a;文章基本信息表&#xff08;字段有文章id、文章作者、文章标题、发布时间等&#xff09;、文章配置表&#xff08;字段有文章id、文章是否可评论、文章可转发、是否已下架、是否已删除等&#x…

STM32CubeIDE(Eclipse)Post-build steps添加带参.exe实现全流程(2):带参调用.exe的几种方法

0 工具准备 STM32CubeIDE工程 带参.exe1 前言 使用STM32CubeIDE编译生成了二进制镜像文件后&#xff0c;有时为了防止镜像被恶意修改&#xff0c;可以通过添加校验和来对整个镜像进行保护&#xff0c;实现手段就是在STM32CubeIDE工程Post-build steps中调用一些外部程序来为镜…

docker配置mysql8报错 ERROR 2002 (HY000)

通过docker启动的mysql&#xff0c;发现navicat无法连接&#xff0c;后来进入容器内部也是无法连接&#xff0c;产生以下错误 root9f3b90339a14:/var/run/mysqld# mysql -u root -p Enter password: ERROR 2002 (HY000): Cant connect to local MySQL server through socket …

【C++、数据结构】二叉排序树(二叉查找树、二叉搜索树)(图解+完整代码)

目录 [⚽1.什么是二叉排序树] [&#x1f3d0;2.构建二叉排序树] [&#x1f3c0;3.二叉排序树的查找操作] [&#x1f94e;4.二叉排序树的删除] [&#x1f3b1;5.完整代码] ⚽1.什么是二叉排序树 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是…