@srcker/editor-vue-next 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.vscode/extensions.json +3 -0
  2. package/README.md +50 -0
  3. package/index.html +13 -0
  4. package/index.ts +11 -0
  5. package/jsconfig.json +8 -0
  6. package/package.json +69 -0
  7. package/public/favicon.ico +0 -0
  8. package/src/App.vue +9 -0
  9. package/src/App.vue.js +17 -0
  10. package/src/Button/BackgroundButton.vue +331 -0
  11. package/src/Button/BackgroundButton.vue.js +243 -0
  12. package/src/Button/BlockQuoteButton.vue +26 -0
  13. package/src/Button/BlockQuoteButton.vue.js +56 -0
  14. package/src/Button/BoldButton.vue +29 -0
  15. package/src/Button/BoldButton.vue.js +56 -0
  16. package/src/Button/BulletListButton.vue +114 -0
  17. package/src/Button/BulletListButton.vue.js +147 -0
  18. package/src/Button/CodeBlockButton.vue +28 -0
  19. package/src/Button/CodeBlockButton.vue.js +56 -0
  20. package/src/Button/CodeButton.vue +30 -0
  21. package/src/Button/CodeButton.vue.js +56 -0
  22. package/src/Button/FontSizeButton.vue +85 -0
  23. package/src/Button/FontSizeButton.vue.js +131 -0
  24. package/src/Button/FormatButton.vue +25 -0
  25. package/src/Button/FormatButton.vue.js +54 -0
  26. package/src/Button/HeadingButton.vue +103 -0
  27. package/src/Button/HeadingButton.vue.js +164 -0
  28. package/src/Button/ImageUploadButton.vue +93 -0
  29. package/src/Button/ImageUploadButton.vue.js +123 -0
  30. package/src/Button/IndentLeftButton.vue +25 -0
  31. package/src/Button/IndentLeftButton.vue.js +54 -0
  32. package/src/Button/IndentRightButton.vue +29 -0
  33. package/src/Button/IndentRightButton.vue.js +54 -0
  34. package/src/Button/ItalicButton.vue +29 -0
  35. package/src/Button/ItalicButton.vue.js +56 -0
  36. package/src/Button/LineHeightButton.vue +88 -0
  37. package/src/Button/LineHeightButton.vue.js +131 -0
  38. package/src/Button/LinkButton.vue +44 -0
  39. package/src/Button/LinkButton.vue.js +69 -0
  40. package/src/Button/OrderedListButton.vue +121 -0
  41. package/src/Button/OrderedListButton.vue.js +146 -0
  42. package/src/Button/RedoButton.vue +29 -0
  43. package/src/Button/RedoButton.vue.js +55 -0
  44. package/src/Button/StrikeButton.vue +30 -0
  45. package/src/Button/StrikeButton.vue.js +56 -0
  46. package/src/Button/SubscriptButton.vue +29 -0
  47. package/src/Button/SubscriptButton.vue.js +56 -0
  48. package/src/Button/SuperscriptButton.vue +29 -0
  49. package/src/Button/SuperscriptButton.vue.js +56 -0
  50. package/src/Button/TextAlignCenterButton.vue +26 -0
  51. package/src/Button/TextAlignCenterButton.vue.js +56 -0
  52. package/src/Button/TextAlignLeftButton.vue +26 -0
  53. package/src/Button/TextAlignLeftButton.vue.js +56 -0
  54. package/src/Button/TextAlignRightButton.vue +26 -0
  55. package/src/Button/TextAlignRightButton.vue.js +56 -0
  56. package/src/Button/TextColorButton.vue +329 -0
  57. package/src/Button/TextColorButton.vue.js +243 -0
  58. package/src/Button/ThemeButton.vue +34 -0
  59. package/src/Button/ThemeButton.vue.js +63 -0
  60. package/src/Button/UnderLineButton.vue +29 -0
  61. package/src/Button/UnderLineButton.vue.js +56 -0
  62. package/src/Button/UndoButton.vue +29 -0
  63. package/src/Button/UndoButton.vue.js +55 -0
  64. package/src/Components/IconArrow.vue +16 -0
  65. package/src/Components/IconArrow.vue.js +30 -0
  66. package/src/Components/IconCheck.vue +40 -0
  67. package/src/Components/IconCheck.vue.js +59 -0
  68. package/src/EditorToolbar.vue +150 -0
  69. package/src/EditorToolbar.vue.js +306 -0
  70. package/src/Extensions/BulletListStyle.js +19 -0
  71. package/src/Extensions/BulletListStyle.ts +24 -0
  72. package/src/Extensions/FontSize.js +27 -0
  73. package/src/Extensions/FontSize.ts +37 -0
  74. package/src/Extensions/Indent.js +58 -0
  75. package/src/Extensions/Indent.ts +73 -0
  76. package/src/Extensions/OrderedListStyle.js +19 -0
  77. package/src/Extensions/OrderedListStyle.ts +24 -0
  78. package/src/Extensions/UploadImage.js +18 -0
  79. package/src/Extensions/UploadImage.ts +22 -0
  80. package/src/Extensions/shims.d.ts +26 -0
  81. package/src/RichEditor.vue +191 -0
  82. package/src/RichEditor.vue.js +199 -0
  83. package/src/env.d.ts +7 -0
  84. package/src/index.js +6 -0
  85. package/src/index.ts +11 -0
  86. package/src/main.js +4 -0
  87. package/src/main.ts +7 -0
  88. package/src/styles/style.scss +196 -0
  89. package/src/styles/theme.css +28 -0
  90. package/src/styles/variables.css +158 -0
  91. package/src/styles/variables.scss +175 -0
  92. package/src/types.js +1 -0
  93. package/src/types.ts +7 -0
  94. package/tsconfig.json +17 -0
  95. package/vite.config.ts +29 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["Vue.volar"]
3
+ }
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Rich Editor
2
+
3
+ 一个基于 Tiptap 的富文本编辑器,具有丰富的文本编辑功能。
4
+
5
+ ## 功能特性
6
+
7
+ - 基础文本格式化:加粗、斜体、下划线、删除线
8
+ - 标题设置
9
+ - 字体大小调整
10
+ - 颜色设置(文字颜色、背景高亮)
11
+ - 行高和缩进控制
12
+ - 对齐方式
13
+ - 列表(有序和无序)及样式
14
+ - 引用块
15
+ - 链接
16
+ - 待办事项
17
+ - 代码块
18
+ - 分割线
19
+ - **图片上传功能**
20
+
21
+ ## 快速开始
22
+
23
+ 安装依赖:
24
+
25
+ ```bash
26
+ npm install
27
+ ```
28
+
29
+ 启动开发服务:
30
+
31
+ ```bash
32
+ npm run dev
33
+ ```
34
+
35
+ 构建生产版本:
36
+
37
+ ```bash
38
+ npm run build
39
+ ```
40
+
41
+ ## 技术栈
42
+
43
+ - Vue 3
44
+ - Tiptap
45
+ - IconPark 图标库
46
+ - SCSS
47
+
48
+ ## 图片上传
49
+
50
+ 编辑器支持通过工具栏按钮上传图片,支持常见的图片格式(JPG、PNG、GIF、WebP),最大支持5MB的文件。
package/index.html ADDED
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html lang="">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <link rel="icon" href="/favicon.ico">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Vite App</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.js"></script>
12
+ </body>
13
+ </html>
package/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 组件统一导出
3
+ */
4
+ import RichEditor from './RichEditor.vue';
5
+ export { RichEditor }
6
+
7
+ export default {
8
+ install(app: any) {
9
+ app.use(RichEditor)
10
+ }
11
+ }
package/jsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "@/*": ["./src/*"]
5
+ }
6
+ },
7
+ "exclude": ["node_modules", "dist"]
8
+ }
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@srcker/editor-vue-next",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "engines": {
6
+ "node": "^20.19.0 || >=22.12.0"
7
+ },
8
+ "scripts": {
9
+ "dev": "vite",
10
+ "build": "vue-tsc -b && vite build",
11
+ "preview": "vite preview"
12
+ },
13
+ "dependencies": {
14
+ "@tiptap/extension-blockquote": "^3.16.0",
15
+ "@tiptap/extension-bold": "^3.16.0",
16
+ "@tiptap/extension-bullet-list": "^3.16.0",
17
+ "@tiptap/extension-character-count": "^3.16.0",
18
+ "@tiptap/extension-code": "^3.16.0",
19
+ "@tiptap/extension-code-block": "^3.16.0",
20
+ "@tiptap/extension-color": "^3.16.0",
21
+ "@tiptap/extension-document": "^3.16.0",
22
+ "@tiptap/extension-drag-handle": "^3.16.0",
23
+ "@tiptap/extension-dropcursor": "^3.16.0",
24
+ "@tiptap/extension-floating-menu": "^3.16.0",
25
+ "@tiptap/extension-focus": "^3.16.0",
26
+ "@tiptap/extension-gapcursor": "^3.16.0",
27
+ "@tiptap/extension-hard-break": "^3.16.0",
28
+ "@tiptap/extension-heading": "^3.16.0",
29
+ "@tiptap/extension-highlight": "^3.16.0",
30
+ "@tiptap/extension-history": "^3.16.0",
31
+ "@tiptap/extension-horizontal-rule": "^3.16.0",
32
+ "@tiptap/extension-image": "^3.16.0",
33
+ "@tiptap/extension-italic": "^3.16.0",
34
+ "@tiptap/extension-link": "^3.16.0",
35
+ "@tiptap/extension-ordered-list": "^3.16.0",
36
+ "@tiptap/extension-paragraph": "^3.16.0",
37
+ "@tiptap/extension-placeholder": "^3.16.0",
38
+ "@tiptap/extension-strike": "^3.16.0",
39
+ "@tiptap/extension-subscript": "^3.16.0",
40
+ "@tiptap/extension-superscript": "^3.16.0",
41
+ "@tiptap/extension-table": "^3.16.0",
42
+ "@tiptap/extension-table-cell": "^3.16.0",
43
+ "@tiptap/extension-table-header": "^3.16.0",
44
+ "@tiptap/extension-table-row": "^3.16.0",
45
+ "@tiptap/extension-task-item": "^3.16.0",
46
+ "@tiptap/extension-task-list": "^3.16.0",
47
+ "@tiptap/extension-text": "^3.16.0",
48
+ "@tiptap/extension-text-align": "^3.16.0",
49
+ "@tiptap/extension-text-style": "^3.16.0",
50
+ "@tiptap/extension-underline": "^3.16.0",
51
+ "@tiptap/pm": "^3.16.0",
52
+ "@tiptap/starter-kit": "^3.16.0",
53
+ "@tiptap/vue-3": "^3.16.0",
54
+ "vue": "^3.5.22"
55
+ },
56
+ "peerDependencies": {
57
+ "vue": "^3.5.22"
58
+ },
59
+ "devDependencies": {
60
+ "@vitejs/plugin-vue": "^6.0.1",
61
+ "sass-embedded": "^1.93.3",
62
+ "vite-plugin-vue-devtools": "^8.0.3",
63
+ "@types/node": "^24.10.1",
64
+ "@vue/tsconfig": "^0.8.1",
65
+ "typescript": "~5.9.3",
66
+ "vite": "^7.2.4",
67
+ "vue-tsc": "^3.1.4"
68
+ }
69
+ }
Binary file
package/src/App.vue ADDED
@@ -0,0 +1,9 @@
1
+ <script setup>
2
+ import RichEditor from './RichEditor.vue';
3
+ </script>
4
+
5
+ <template>
6
+ <RichEditor theme="dark" />
7
+ </template>
8
+
9
+ <style scoped></style>
package/src/App.vue.js ADDED
@@ -0,0 +1,17 @@
1
+ import RichEditor from './RichEditor.vue';
2
+ const __VLS_ctx = {};
3
+ let __VLS_components;
4
+ let __VLS_intrinsics;
5
+ let __VLS_directives;
6
+ const __VLS_0 = RichEditor;
7
+ // @ts-ignore
8
+ const __VLS_1 = __VLS_asFunctionalComponent1(__VLS_0, new __VLS_0({
9
+ theme: "dark",
10
+ }));
11
+ const __VLS_2 = __VLS_1({
12
+ theme: "dark",
13
+ }, ...__VLS_functionalComponentArgsRest(__VLS_1));
14
+ var __VLS_5 = {};
15
+ var __VLS_3;
16
+ const __VLS_export = (await import('vue')).defineComponent({});
17
+ export default {};
@@ -0,0 +1,331 @@
1
+ <template>
2
+ <div
3
+ class="color-button"
4
+ ref="wrapperRef"
5
+ :class="{ 'open': isOpen }"
6
+ @click="isOpen = !isOpen"
7
+ title="背景颜色">
8
+
9
+ <div class="trigger" >
10
+ <div class="icon">
11
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
12
+ <path fill="currentColor" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M296 64c6.9 0 13.5 2.7 18.3 7.6L440.4 197.7c4.9 4.9 7.6 11.5 7.6 18.3s-2.7 13.5-7.6 18.3L386.7 288 65.3 288c1.3-3.9 3.4-7.4 6.3-10.3l96.4-96.4 33.4 33.4c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L213.3 136 277.7 71.6c4.9-4.9 11.5-7.6 18.3-7.6zM122.7 136L26.3 232.4C9.5 249.3 0 272.1 0 296s9.5 46.7 26.3 63.6L152.4 485.7C169.3 502.5 192.1 512 216 512s46.7-9.5 63.6-26.3L485.7 279.6C502.5 262.7 512 239.9 512 216s-9.5-46.7-26.3-63.6L359.6 26.3C342.7 9.5 319.9 0 296 0s-46.7 9.5-63.6 26.3L168 90.7 118.6 41.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L122.7 136z"/>
13
+ </svg>
14
+ </div>
15
+ <div class="bar" :style="{ backgroundColor: getColor || '#000' }"></div>
16
+ </div>
17
+
18
+ <div class="dropdown" v-if="isOpen">
19
+ <div class="grid">
20
+ <div
21
+ v-for="color in colorPalette"
22
+ :key="color"
23
+ class="item"
24
+ :style="{ backgroundColor: color }"
25
+ @click="setColor(color)"
26
+ :title="color">
27
+ <IconCheck v-if="editor.isActive('textStyle', { color })" :size="10" color="#fff" />
28
+ </div>
29
+ </div>
30
+
31
+
32
+ <div class="footer">
33
+ <label>自定义颜色</label>
34
+ <div class="input">
35
+ <input
36
+ type="color"
37
+ @input="editor.chain().focus().setBackgroundColor($event.target.value).run()"
38
+ :value="getColor"/>
39
+ </div>
40
+
41
+ <div class="button" @click="editor.chain().focus().unsetBackgroundColor().run();">清除</div>
42
+ </div>
43
+ </div>
44
+
45
+ </div>
46
+ </template>
47
+
48
+
49
+ <script setup>
50
+
51
+ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
52
+ import IconCheck from '../components/IconCheck.vue';
53
+
54
+ const isOpen = ref(false)
55
+ const wrapperRef = ref(null)
56
+
57
+ const colorPalette = [
58
+ // 黑白灰
59
+ '#000000',
60
+ '#262626',
61
+ '#595959',
62
+ '#8C8C8C',
63
+ '#BFBFBF',
64
+ '#D9D9D9',
65
+ '#E9E9E9',
66
+ '#F5F5F5',
67
+ '#FAFAFA',
68
+ '#FFFFFF',
69
+
70
+ // 基础色
71
+ '#F5222D',
72
+ '#FA541C',
73
+ '#FA8C16',
74
+ '#FADB14',
75
+ '#52C41A',
76
+ '#13C2C2',
77
+ '#1890FF',
78
+ '#2F54EB',
79
+ '#722ED1',
80
+ '#EB2F96',
81
+
82
+ // 浅色系
83
+ '#FFE8E6',
84
+ '#FFECE0',
85
+ '#FFEF D1'.replace(' ', ''),
86
+ '#FCFCCA',
87
+ '#E4F7D2',
88
+ '#D3F5F0',
89
+ '#D4EEFC',
90
+ '#DEE8FC',
91
+ '#EFE1FA',
92
+ '#FAE1EB',
93
+
94
+ // 中色系
95
+ '#FFA39E',
96
+ '#FFBB96',
97
+ '#FFD591',
98
+ '#FFFB8F',
99
+ '#B7EB8F',
100
+ '#87E8DE',
101
+ '#91D5FF',
102
+ '#ADC6FF',
103
+ '#D3ADF7',
104
+ '#FFADD2',
105
+
106
+ // 深色系
107
+ '#FF4D4F',
108
+ '#FF7A45',
109
+ '#FFA940',
110
+ '#FFEC3D',
111
+ '#73D13D',
112
+ '#36CFC9',
113
+ '#40A9FF',
114
+ '#597EF7',
115
+ '#9254DE',
116
+ '#F759AB',
117
+
118
+ // 暗色系
119
+ '#CF1322',
120
+ '#D4380D',
121
+ '#D46B08',
122
+ '#D4B106',
123
+ '#389E0D',
124
+ '#08979C',
125
+ '#096DD9',
126
+ '#1D39C4',
127
+ '#531DAB',
128
+ '#C41D7F',
129
+
130
+ // 极深色
131
+ '#820014',
132
+ '#871400',
133
+ '#873800',
134
+ '#614700',
135
+ '#135200',
136
+ '#00474F',
137
+ '#003A8C',
138
+ '#061178',
139
+ '#22075E',
140
+ '#780650'
141
+ ];
142
+
143
+
144
+ const setColor = (color) => {
145
+ props.editor.chain().focus().setBackgroundColor(color).run();
146
+ isOpen.value = false
147
+ }
148
+
149
+
150
+
151
+ const getColor = computed(() => {
152
+ if (!props.editor) return '#fff'
153
+ const color = props.editor.getAttributes('textStyle').backgroundColor
154
+ return color || '#000'
155
+ })
156
+
157
+
158
+
159
+ const props = defineProps({
160
+ editor: {
161
+ type: Object,
162
+ required: true
163
+ }
164
+ })
165
+
166
+ const handleClickOutside = (e) => {
167
+ if (!wrapperRef.value) return
168
+
169
+ if (!wrapperRef.value.contains(e.target)) {
170
+ isOpen.value = false
171
+ }
172
+ }
173
+
174
+ onMounted(() => {
175
+ document.addEventListener('mousedown', handleClickOutside)
176
+ })
177
+
178
+ onBeforeUnmount(() => {
179
+ document.removeEventListener('mousedown', handleClickOutside)
180
+ })
181
+
182
+ </script>
183
+
184
+
185
+
186
+ <style lang="scss" scoped>
187
+
188
+ .color-button {
189
+ position: relative;
190
+ display: inline-flex;
191
+ align-items: center;
192
+
193
+ .trigger {
194
+ height: 25px;
195
+ padding: 3px 8px;
196
+ display: flex;
197
+ align-items: center;
198
+ justify-content: space-between;
199
+ cursor: pointer;
200
+ border-radius: 6px;
201
+ font-size: 14px;
202
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
203
+
204
+ color: var(--color);
205
+ background: var(--background);
206
+
207
+ &:hover,
208
+ &.is-open {
209
+ color: var(--hover-color);
210
+ background: var(--hover-background);
211
+ }
212
+
213
+ .icon{
214
+ display: inline-flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ svg{
218
+ height: 14px;
219
+ }
220
+ }
221
+
222
+ .bar{
223
+ position: absolute;
224
+ bottom: 5px;
225
+ width: 15px;
226
+ height: 3px;
227
+ left: 50%;
228
+ transform: translateX(-50%);
229
+ border: 1px solid rgba($color: #fff, $alpha: 0.5);
230
+ }
231
+
232
+ .label {
233
+ margin: 0 5px;
234
+ display: inline-flex;
235
+ align-items: center;
236
+ white-space: nowrap;
237
+ overflow: hidden;
238
+ text-overflow: ellipsis;
239
+ }
240
+ .arrow {
241
+ font-size: 14px;
242
+ color: #666;
243
+ display: inline-flex;
244
+ align-items: center;
245
+ }
246
+ }
247
+
248
+ .dropdown {
249
+ position: absolute;
250
+ top: 100%;
251
+ left: 0;
252
+ margin-top: 5px;
253
+ background: #1f1f1f;
254
+ box-shadow: 0 0px 15px rgba(0,0,0,0.05);
255
+ border-radius: 10px;
256
+ z-index: 1000;
257
+ overflow-y: auto;
258
+ padding: 15px;
259
+ transition: all 0.3s cubic-bezier(0.46, 0.03, 0.52, 0.96);
260
+
261
+ .grid{
262
+ display: grid;
263
+ grid-template-columns: repeat(10, 1fr);
264
+ gap: 5px;
265
+ width: 300px;
266
+ .item{
267
+ width: 20px;
268
+ height: 20px;
269
+ padding: 2px;
270
+ border-radius: 50%;
271
+ border: 1px solid transparent;
272
+ cursor: pointer;
273
+ display: flex;
274
+ align-items: center;
275
+ justify-content: center;
276
+ transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
277
+ &:hover {
278
+ border-color: rgba($color: #fff, $alpha: 0.5);
279
+ z-index: 1;
280
+ }
281
+
282
+ .icon{
283
+ display: inline-flex;
284
+ align-items: center;
285
+ color: rgba($color: #fff, $alpha: 1);
286
+ }
287
+ }
288
+ }
289
+
290
+ .footer{
291
+ margin-top: 20px;
292
+ display: flex;
293
+ align-items: center;
294
+
295
+ label{
296
+ font-size: 14px;
297
+ color: #fff;
298
+ }
299
+
300
+ .input{
301
+ flex: 1;
302
+ padding-left: 15px;
303
+
304
+ input{
305
+ padding: 0;
306
+ block-size: 20px;
307
+ border: none;
308
+ inline-size: 40px;
309
+ }
310
+ }
311
+
312
+ .button{
313
+ display: flex;
314
+ align-items: center;
315
+ padding: 5px 15px;
316
+ border-radius: 12px;
317
+ color: rgba($color: #fff, $alpha: 0.5);
318
+ background: rgba($color: #fff, $alpha: 0.1);
319
+ font-size: 14px;
320
+ justify-content: center;
321
+
322
+ &:hover {
323
+ cursor: pointer;
324
+ background: rgba($color: #000000, $alpha: 0.2);
325
+ color: rgba($color: #fff, $alpha: 0.8);
326
+ }
327
+ }
328
+ }
329
+ }
330
+ }
331
+ </style>