@bagelink/vue 0.0.863 → 0.0.869
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.
- package/dist/components/Carousel.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText2/Toolbar.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText2/formatting.d.ts +6 -0
- package/dist/components/form/inputs/RichText2/formatting.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText2/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText2/richtext-types.d.ts +1 -1
- package/dist/components/form/inputs/RichText2/richtext-types.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/index.d.ts +2 -2
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/index.cjs +141 -77
- package/dist/index.mjs +141 -77
- package/dist/style.css +29 -22
- package/package.json +1 -1
- package/src/components/Carousel.vue +8 -6
- package/src/components/form/inputs/RichText.zip +0 -0
- package/src/components/form/inputs/RichText2/Toolbar.vue +10 -15
- package/src/components/form/inputs/RichText2/formatting.ts +69 -25
- package/src/components/form/inputs/RichText2/index.vue +46 -30
- package/src/components/form/inputs/RichText2/richtext-types.ts +1 -0
- package/src/components/form/inputs/index.ts +2 -2
- package/src/styles/layout.css +4 -0
- package/src/styles/mobilLayout.css +4 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
let modal: any
|
|
2
2
|
|
|
3
3
|
export function applyFormatting(command: string, value: string = '') {
|
|
4
4
|
const selection = window.getSelection()
|
|
@@ -7,6 +7,9 @@ export function applyFormatting(command: string, value: string = '') {
|
|
|
7
7
|
const span = document.createElement('span')
|
|
8
8
|
|
|
9
9
|
switch (command) {
|
|
10
|
+
case 'formatBlock':
|
|
11
|
+
formatBlock(value)
|
|
12
|
+
break
|
|
10
13
|
case 'bold':
|
|
11
14
|
span.style.fontWeight = 'bold'
|
|
12
15
|
break
|
|
@@ -73,7 +76,7 @@ export function applyFormatting(command: string, value: string = '') {
|
|
|
73
76
|
case 'decreaseFontSize':
|
|
74
77
|
decreaseFontSize()
|
|
75
78
|
break
|
|
76
|
-
case '
|
|
79
|
+
case 'link':
|
|
77
80
|
insertLink()
|
|
78
81
|
break
|
|
79
82
|
case 'image':
|
|
@@ -91,6 +94,16 @@ export function applyFormatting(command: string, value: string = '') {
|
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
function formatBlock(value: string) {
|
|
98
|
+
if (!value) return
|
|
99
|
+
const selection = window.getSelection()
|
|
100
|
+
const range = selection?.getRangeAt(0)
|
|
101
|
+
if (!range) return
|
|
102
|
+
range.selectNodeContents(range.startContainer)
|
|
103
|
+
const el = document.createElement(value)
|
|
104
|
+
range.surroundContents(el)
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
function increaseFontSize() {
|
|
95
108
|
const selection = window.getSelection()
|
|
96
109
|
if (selection && selection.rangeCount > 0) {
|
|
@@ -104,7 +117,15 @@ function increaseFontSize() {
|
|
|
104
117
|
}
|
|
105
118
|
|
|
106
119
|
function decreaseFontSize() {
|
|
107
|
-
|
|
120
|
+
const selection = window.getSelection()
|
|
121
|
+
if (selection && selection.rangeCount > 0) {
|
|
122
|
+
const range = selection.getRangeAt(0)
|
|
123
|
+
const span = document.createElement('span')
|
|
124
|
+
const currentFontSize = Number.parseInt((range.startContainer.parentNode as HTMLElement).style.fontSize || '16', 10)
|
|
125
|
+
const newFontSize = currentFontSize - 2
|
|
126
|
+
span.style.fontSize = `${newFontSize}px`
|
|
127
|
+
range.surroundContents(span)
|
|
128
|
+
}
|
|
108
129
|
}
|
|
109
130
|
|
|
110
131
|
function removeFormatting() {
|
|
@@ -142,36 +163,40 @@ function insertList(type: string) {
|
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
export function insertLink() {
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
166
|
+
const selection = window.getSelection()
|
|
167
|
+
if (!selection || selection.rangeCount < 1) return
|
|
168
|
+
const range = selection.getRangeAt(0)
|
|
169
|
+
modal.showModalForm({ title: 'Insert Link', schema: [
|
|
170
|
+
{ id: 'url', $el: 'text', label: 'URL' },
|
|
171
|
+
{ id: 'openInNewTab', $el: 'check', label: 'Open in new tab' },
|
|
172
|
+
], onSubmit: (data: any) => {
|
|
173
|
+
const { url, openInNewTab } = data
|
|
174
|
+
if (url) {
|
|
150
175
|
const anchor = document.createElement('a')
|
|
151
176
|
anchor.href = url
|
|
152
177
|
range.surroundContents(anchor)
|
|
178
|
+
if (openInNewTab) anchor.target = '_blank'
|
|
153
179
|
}
|
|
154
|
-
}
|
|
180
|
+
} })
|
|
155
181
|
}
|
|
156
182
|
|
|
157
183
|
export function insertImage() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
showModalForm({ title: 'Upload Image', schema: [
|
|
162
|
-
|
|
184
|
+
const selection = window.getSelection()
|
|
185
|
+
if (!selection || selection.rangeCount < 1) return
|
|
186
|
+
const range = selection.getRangeAt(0)
|
|
187
|
+
modal.showModalForm({ title: 'Upload Image', schema: [
|
|
188
|
+
{ id: 'src', $el: 'file', attrs: { bindkey: 'url' } },
|
|
189
|
+
{ id: 'alt', $el: 'text', label: 'Alt Text' },
|
|
190
|
+
], onSubmit: (_data: any) => {
|
|
191
|
+
const { src } = _data
|
|
192
|
+
if (src) {
|
|
193
|
+
const img = document.createElement('img')
|
|
194
|
+
img.src = src
|
|
195
|
+
img.alt = _data.alt
|
|
196
|
+
range.deleteContents()
|
|
197
|
+
range.insertNode(img)
|
|
198
|
+
}
|
|
163
199
|
} })
|
|
164
|
-
// const url = prompt('Enter the image URL:')
|
|
165
|
-
// if (url) {
|
|
166
|
-
// const selection = window.getSelection()
|
|
167
|
-
// if (selection && selection.rangeCount > 0) {
|
|
168
|
-
// const range = selection.getRangeAt(0)
|
|
169
|
-
// const img = document.createElement('img')
|
|
170
|
-
// img.src = url
|
|
171
|
-
// range.deleteContents()
|
|
172
|
-
// range.insertNode(img)
|
|
173
|
-
// }
|
|
174
|
-
// }
|
|
175
200
|
}
|
|
176
201
|
|
|
177
202
|
export function createTable() {
|
|
@@ -200,3 +225,22 @@ export function createTable() {
|
|
|
200
225
|
}
|
|
201
226
|
}
|
|
202
227
|
}
|
|
228
|
+
|
|
229
|
+
function clearFormatting() {
|
|
230
|
+
const selection = window.getSelection()
|
|
231
|
+
const range = selection?.getRangeAt(0)
|
|
232
|
+
if (!range) return
|
|
233
|
+
range.selectNodeContents(range.startContainer)
|
|
234
|
+
const contents = range.extractContents()
|
|
235
|
+
const text = contents.textContent
|
|
236
|
+
if (text !== null && text !== '') {
|
|
237
|
+
range.deleteContents()
|
|
238
|
+
const textNode = document.createTextNode(`${text}`)
|
|
239
|
+
setTimeout(() => { range.insertNode(textNode) }, 1)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function useFormatting(modalInstance?: any) {
|
|
244
|
+
modal = modalInstance
|
|
245
|
+
return { applyFormatting, clearFormatting }
|
|
246
|
+
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { ToolbarConfig } from './richtext-types'
|
|
3
|
+
import { useModal } from '@bagelink/vue'
|
|
3
4
|
import { onMounted, watch } from 'vue'
|
|
4
|
-
import {
|
|
5
|
+
import { useFormatting } from './formatting'
|
|
5
6
|
import Toolbar from './Toolbar.vue'
|
|
6
7
|
|
|
7
8
|
const props = defineProps<{ modelValue: string, toolbarConfig?: ToolbarConfig }>()
|
|
8
9
|
const emit = defineEmits(['update:modelValue'])
|
|
9
10
|
|
|
11
|
+
const modal = useModal()
|
|
12
|
+
const { applyFormatting, clearFormatting } = useFormatting(modal)
|
|
13
|
+
|
|
10
14
|
const editableContent = $ref<HTMLElement | undefined>()
|
|
11
15
|
const defaultConfig: ToolbarConfig = [
|
|
16
|
+
'formatBlock',
|
|
12
17
|
'bold',
|
|
13
18
|
'italic',
|
|
14
19
|
'underline',
|
|
@@ -47,36 +52,37 @@ function updateContent() {
|
|
|
47
52
|
emit('update:modelValue', contentHtml)
|
|
48
53
|
}
|
|
49
54
|
|
|
55
|
+
function updateToolbarHighlight() {
|
|
56
|
+
if (document.getSelection()) {
|
|
57
|
+
const selection = document.getSelection()
|
|
58
|
+
const range = selection?.rangeCount ? selection.getRangeAt(0) : null
|
|
59
|
+
const container = range?.commonAncestorContainer as HTMLElement
|
|
60
|
+
if (container) {
|
|
61
|
+
let currentElement = container.nodeType === 3 ? container.parentElement : container
|
|
62
|
+
while (currentElement && currentElement !== editableContent) {
|
|
63
|
+
if (['H1', 'H2', 'H3'].includes(currentElement.tagName)) {
|
|
64
|
+
// emit('action', { type: currentElement.tagName.toLowerCase() }); // Emit the tag to highlight button
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
currentElement = currentElement.parentElement
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
document.addEventListener('selectionchange', updateToolbarHighlight)
|
|
74
|
+
|
|
75
|
+
type ToolbarAction = 'link' | 'image' | 'table' | 'youtube' | 'fontSize' | 'fontFamily' | 'textColor' | 'backgroundColor' | 'bold' | 'italic' | 'underline' | 'alignLeft' | 'alignCenter' | 'alignRight' | 'alignJustify' | 'orderedList' | 'unorderedList' | 'indent' | 'outdent' | 'blockquote' | 'codeBlock' | 'splitView' | 'codeView' | 'clear' | 'fullScreen'
|
|
76
|
+
|
|
50
77
|
function handleToolbarAction(action: string, value?: string) {
|
|
51
78
|
if (!editableContent) return
|
|
52
79
|
if (['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'].includes(action))
|
|
53
80
|
value = action.replace('align', '').toLowerCase()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
applyFormatting('insertUnorderedList')
|
|
60
|
-
break
|
|
61
|
-
case 'blockquote':
|
|
62
|
-
applyFormatting('formatBlock', '<blockquote>')
|
|
63
|
-
break
|
|
64
|
-
case 'codeBlock':
|
|
65
|
-
applyFormatting('formatBlock', '<pre>')
|
|
66
|
-
break
|
|
67
|
-
case 'splitView':
|
|
68
|
-
isSplitView = !isSplitView
|
|
69
|
-
break
|
|
70
|
-
case 'codeView':
|
|
71
|
-
isCodeView = !isCodeView
|
|
72
|
-
break
|
|
73
|
-
case 'fullScreen':
|
|
74
|
-
toggleFullScreen()
|
|
75
|
-
break
|
|
76
|
-
default:
|
|
77
|
-
applyFormatting(action, value)
|
|
78
|
-
break
|
|
79
|
-
}
|
|
81
|
+
if (action === 'splitView') isSplitView = !isSplitView
|
|
82
|
+
else if (action === 'codeView') isCodeView = !isCodeView
|
|
83
|
+
else if (action === 'fullScreen') toggleFullScreen()
|
|
84
|
+
else if (action === 'clear') clearFormatting()
|
|
85
|
+
else applyFormatting(action, value)
|
|
80
86
|
updateContent()
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -131,13 +137,17 @@ function handleKeyDown(event: KeyboardEvent) {
|
|
|
131
137
|
}
|
|
132
138
|
}
|
|
133
139
|
}
|
|
140
|
+
function logInput(e: InputEvent) {
|
|
141
|
+
const target = e.target as HTMLElement
|
|
142
|
+
contentHtml = target.textContent || ''
|
|
143
|
+
}
|
|
134
144
|
</script>
|
|
135
145
|
|
|
136
146
|
<template>
|
|
137
|
-
<div class="rich-text-editor rounded pt-05 px-
|
|
147
|
+
<div class="rich-text-editor rounded pt-05 px-05 pb-1">
|
|
138
148
|
<Toolbar :config @action="handleToolbarAction" />
|
|
139
149
|
<div class="editor-container flex flex-stretch gap-1 m_column">
|
|
140
|
-
<div class="content-area radius-05 p-1
|
|
150
|
+
<div class="content-area radius-05 p-1 w-100 grid">
|
|
141
151
|
<textarea v-if="isCodeView" v-model="contentHtml" @input="updateContent" />
|
|
142
152
|
<div
|
|
143
153
|
v-else
|
|
@@ -151,7 +161,13 @@ function handleKeyDown(event: KeyboardEvent) {
|
|
|
151
161
|
@keydown="handleKeyDown"
|
|
152
162
|
/>
|
|
153
163
|
</div>
|
|
154
|
-
<code
|
|
164
|
+
<code
|
|
165
|
+
v-if="isSplitView"
|
|
166
|
+
contenteditable="true"
|
|
167
|
+
class="preview-area w-100 radius-05 p-1"
|
|
168
|
+
@input="(e) => logInput(e as InputEvent)"
|
|
169
|
+
v-text="contentHtml"
|
|
170
|
+
/>
|
|
155
171
|
</div>
|
|
156
172
|
</div>
|
|
157
173
|
</template>
|
|
@@ -10,8 +10,8 @@ export { default as PasswordInput } from './PasswordInput.vue'
|
|
|
10
10
|
export { default as RadioGroup } from './RadioGroup.vue'
|
|
11
11
|
export { default as RadioPillsInput } from './RadioPillsInput.vue'
|
|
12
12
|
export { default as RangeInput } from './RangeInput.vue'
|
|
13
|
-
export { default as
|
|
14
|
-
export { default as
|
|
13
|
+
export { default as RichText2 } from './RichText.vue'
|
|
14
|
+
export { default as RichText } from './RichText2/index.vue'
|
|
15
15
|
export { default as SelectInput } from './SelectInput.vue'
|
|
16
16
|
export { default as SignaturePad } from './SignaturePad.vue'
|
|
17
17
|
export { default as TableField } from './TableField.vue'
|
package/src/styles/layout.css
CHANGED