@bagelink/vue 0.0.974 → 0.0.980
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/Image.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/index.vue.d.ts +0 -1
- package/dist/components/form/inputs/RichText2/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
- package/dist/components/form/inputs/index.d.ts +1 -2
- package/dist/components/form/inputs/index.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/lightbox/Lightbox.vue.d.ts.map +1 -1
- package/dist/index.cjs +1012 -27332
- package/dist/index.mjs +1103 -27423
- package/dist/plugins/bagel.d.ts +1 -1
- package/dist/plugins/bagel.d.ts.map +1 -1
- package/dist/style.css +82 -286
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Image.vue +50 -6
- package/src/components/form/inputs/RichText2/Toolbar.vue +27 -20
- package/src/components/form/inputs/RichText2/index.vue +130 -178
- package/src/components/form/inputs/index.ts +2 -2
- package/src/components/index.ts +2 -1
- package/src/components/layout/Skeleton.vue +2 -2
- package/src/components/lightbox/Lightbox.vue +1 -8
- package/src/plugins/bagel.ts +1 -1
- package/src/utils/index.ts +11 -0
- package/src/components/form/inputs/RichText.vue +0 -420
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
import type { ToolbarConfig } from './richtext-types'
|
|
3
3
|
import { useModal } from '@bagelink/vue'
|
|
4
|
-
import { onMounted, watch } from 'vue'
|
|
4
|
+
import { onMounted, watch, ref } from 'vue'
|
|
5
5
|
import { useFormatting } from './formatting'
|
|
6
6
|
import Toolbar from './Toolbar.vue'
|
|
7
7
|
|
|
@@ -11,7 +11,7 @@ const emit = defineEmits(['update:modelValue'])
|
|
|
11
11
|
const modal = useModal()
|
|
12
12
|
const { applyFormatting, clearFormatting } = useFormatting(modal)
|
|
13
13
|
|
|
14
|
-
const editableContent =
|
|
14
|
+
const editableContent = ref<HTMLElement>()
|
|
15
15
|
const defaultConfig: ToolbarConfig = [
|
|
16
16
|
'formatBlock',
|
|
17
17
|
'bold',
|
|
@@ -32,81 +32,94 @@ const defaultConfig: ToolbarConfig = [
|
|
|
32
32
|
'link',
|
|
33
33
|
'image',
|
|
34
34
|
'table',
|
|
35
|
-
'blockquote',
|
|
36
|
-
'codeBlock',
|
|
37
35
|
'splitView',
|
|
38
|
-
'youtube',
|
|
39
|
-
'codeView',
|
|
40
36
|
'clear',
|
|
41
37
|
'fullScreen'
|
|
42
38
|
]
|
|
43
|
-
const config = $ref<ToolbarConfig>(props.toolbarConfig || defaultConfig)
|
|
44
|
-
let contentHtml = $ref(props.modelValue)
|
|
45
|
-
let isCodeView = $ref(false)
|
|
46
|
-
let isSplitView = $ref(false)
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
const config = ref<ToolbarConfig>(props.toolbarConfig || defaultConfig)
|
|
41
|
+
const contentHtml = ref(props.modelValue)
|
|
42
|
+
const isCodeView = ref(false)
|
|
43
|
+
const isSplitView = ref(false)
|
|
44
|
+
const selectedStyles = ref(new Set<string>())
|
|
45
|
+
|
|
46
|
+
function updateContent(source: 'html' | 'editor' = 'editor') {
|
|
47
|
+
if (source === 'editor' && !isCodeView.value) {
|
|
48
|
+
contentHtml.value = editableContent.value?.innerHTML ?? ''
|
|
51
49
|
}
|
|
52
|
-
emit('update:modelValue', contentHtml)
|
|
50
|
+
emit('update:modelValue', contentHtml.value)
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
}
|
|
53
|
+
function handleHtmlInput(e: Event) {
|
|
54
|
+
const target = e.target as HTMLTextAreaElement
|
|
55
|
+
contentHtml.value = target.value
|
|
56
|
+
if (editableContent.value) {
|
|
57
|
+
editableContent.value.innerHTML = target.value
|
|
70
58
|
}
|
|
59
|
+
updateContent('html')
|
|
71
60
|
}
|
|
72
61
|
|
|
73
|
-
|
|
62
|
+
function updateToolbarHighlight() {
|
|
63
|
+
selectedStyles.value.clear()
|
|
64
|
+
const selection = window.getSelection()
|
|
65
|
+
if (!selection?.rangeCount) return
|
|
74
66
|
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
const range = selection.getRangeAt(0)
|
|
68
|
+
const element = range.commonAncestorContainer as HTMLElement
|
|
69
|
+
const parent = element.nodeType === 3 ? element.parentElement : element
|
|
70
|
+
|
|
71
|
+
if (!parent) return
|
|
72
|
+
|
|
73
|
+
if (parent.style.fontWeight === 'bold') selectedStyles.value.add('bold')
|
|
74
|
+
if (parent.style.fontStyle === 'italic') selectedStyles.value.add('italic')
|
|
75
|
+
if (parent.style.textDecoration === 'underline') selectedStyles.value.add('underline')
|
|
76
|
+
}
|
|
77
77
|
|
|
78
78
|
function handleToolbarAction(action: string, value?: string) {
|
|
79
|
-
if (!editableContent) return
|
|
80
|
-
|
|
79
|
+
if (!editableContent.value) return
|
|
80
|
+
|
|
81
|
+
if (['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'].includes(action)) {
|
|
81
82
|
value = action.replace('align', '').toLowerCase()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
switch (action) {
|
|
86
|
+
case 'splitView':
|
|
87
|
+
isSplitView.value = !isSplitView.value
|
|
88
|
+
break
|
|
89
|
+
case 'codeView':
|
|
90
|
+
isCodeView.value = !isCodeView.value
|
|
91
|
+
break
|
|
92
|
+
case 'fullScreen':
|
|
93
|
+
toggleFullScreen()
|
|
94
|
+
break
|
|
95
|
+
case 'clear':
|
|
96
|
+
clearFormatting()
|
|
97
|
+
break
|
|
98
|
+
default:
|
|
99
|
+
applyFormatting(action, value)
|
|
100
|
+
}
|
|
87
101
|
updateContent()
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
function toggleFullScreen() {
|
|
91
105
|
const editor = document.querySelector('.rich-text-editor')
|
|
92
|
-
|
|
93
|
-
editor.classList.toggle('fullscreen-mode')
|
|
94
|
-
}
|
|
106
|
+
editor?.classList.toggle('fullscreen-mode')
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
watch(() => props.modelValue, (newValue) => {
|
|
98
|
-
if (newValue !== contentHtml) {
|
|
99
|
-
contentHtml = newValue
|
|
100
|
-
if (editableContent) {
|
|
101
|
-
editableContent.innerHTML = newValue
|
|
110
|
+
if (newValue !== contentHtml.value) {
|
|
111
|
+
contentHtml.value = newValue
|
|
112
|
+
if (editableContent.value) {
|
|
113
|
+
editableContent.value.innerHTML = newValue
|
|
102
114
|
}
|
|
103
115
|
}
|
|
104
116
|
})
|
|
105
117
|
|
|
106
118
|
onMounted(() => {
|
|
107
|
-
if (editableContent) {
|
|
108
|
-
editableContent.innerHTML = contentHtml
|
|
119
|
+
if (editableContent.value) {
|
|
120
|
+
editableContent.value.innerHTML = contentHtml.value
|
|
109
121
|
}
|
|
122
|
+
document.addEventListener('selectionchange', updateToolbarHighlight)
|
|
110
123
|
})
|
|
111
124
|
|
|
112
125
|
// Keyboard shortcuts
|
|
@@ -125,31 +138,26 @@ function handleKeyDown(event: KeyboardEvent) {
|
|
|
125
138
|
event.preventDefault()
|
|
126
139
|
handleToolbarAction('underline')
|
|
127
140
|
break
|
|
128
|
-
case 'k':
|
|
129
|
-
event.preventDefault()
|
|
130
|
-
handleToolbarAction('link')
|
|
131
|
-
break
|
|
132
|
-
case 'l':
|
|
133
|
-
event.preventDefault()
|
|
134
|
-
handleToolbarAction('orderedList')
|
|
135
|
-
break
|
|
136
|
-
default:
|
|
137
|
-
break
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
}
|
|
141
|
-
function logInput(e: InputEvent) {
|
|
142
|
-
const target = e.target as HTMLElement
|
|
143
|
-
contentHtml = target.textContent || ''
|
|
144
|
-
}
|
|
145
144
|
</script>
|
|
146
145
|
|
|
147
146
|
<template>
|
|
148
147
|
<div class="rich-text-editor rounded pt-05 px-05 pb-1">
|
|
149
|
-
<Toolbar
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
<Toolbar
|
|
149
|
+
:config="config"
|
|
150
|
+
:selected-styles="selectedStyles"
|
|
151
|
+
@action="handleToolbarAction"
|
|
152
|
+
/>
|
|
153
|
+
<div class="editor-container" :class="{ 'split-view': isSplitView }">
|
|
154
|
+
<div class="content-area radius-05 p-1">
|
|
155
|
+
<textarea
|
|
156
|
+
v-if="isCodeView"
|
|
157
|
+
v-model="contentHtml"
|
|
158
|
+
class="html-editor"
|
|
159
|
+
@input="handleHtmlInput"
|
|
160
|
+
/>
|
|
153
161
|
<div
|
|
154
162
|
v-else
|
|
155
163
|
ref="editableContent"
|
|
@@ -158,119 +166,36 @@ function logInput(e: InputEvent) {
|
|
|
158
166
|
role="textbox"
|
|
159
167
|
aria-multiline="true"
|
|
160
168
|
tabindex="0"
|
|
161
|
-
@input="updateContent"
|
|
169
|
+
@input="updateContent()"
|
|
162
170
|
@keydown="handleKeyDown"
|
|
163
171
|
/>
|
|
164
172
|
</div>
|
|
165
|
-
<
|
|
173
|
+
<div
|
|
166
174
|
v-if="isSplitView"
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/>
|
|
175
|
+
class="preview-area radius-05 p-1"
|
|
176
|
+
>
|
|
177
|
+
<pre><code v-text="contentHtml" /></pre>
|
|
178
|
+
</div>
|
|
172
179
|
</div>
|
|
173
180
|
</div>
|
|
174
181
|
</template>
|
|
175
182
|
|
|
176
183
|
<style scoped>
|
|
177
|
-
@font-face {
|
|
178
|
-
font-family: 'FontWithASyntaxHighlighter';
|
|
179
|
-
font-style: normal;
|
|
180
|
-
font-weight: 400;
|
|
181
|
-
font-stretch: 100%;
|
|
182
|
-
font-display: swap;
|
|
183
|
-
src: url(https://bagel.sfo2.cdn.digitaloceanspaces.com/Fonts/FontWithASyntaxHighlighter-Regular.woff2) format('woff2')
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
@font-palette-values --myCustomPalette {
|
|
187
|
-
font-family: 'FontWithASyntaxHighlighter';
|
|
188
|
-
override-colors:
|
|
189
|
-
0 #6191c2, /* keywords, {} */
|
|
190
|
-
1 #75975d, /* comments */
|
|
191
|
-
2 yellow, /* literals */
|
|
192
|
-
3 #a3c19a, /* numbers */
|
|
193
|
-
4 lightblue, /* functions, [] */
|
|
194
|
-
5 orange, /* js others */
|
|
195
|
-
6 black, /* not in use */
|
|
196
|
-
7 #bc8abd, /* inside quotes, css properties, few chars */
|
|
197
|
-
8 #818181 /* few chars */
|
|
198
|
-
;
|
|
199
|
-
}
|
|
200
|
-
.preview-area{
|
|
201
|
-
font-family: "FontWithASyntaxHighlighter", monospace;
|
|
202
|
-
font-palette: --myCustomPalette;
|
|
203
|
-
background: var(--bgl-code-bg);
|
|
204
|
-
color: var(--bgl-code-color)
|
|
205
|
-
|
|
206
|
-
}
|
|
207
184
|
.rich-text-editor {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
.editableContent{
|
|
213
|
-
min-height: 100%;
|
|
214
|
-
white-space: pre-wrap;
|
|
215
|
-
outline: none;
|
|
185
|
+
background: var(--input-bg);
|
|
186
|
+
border: 1px solid var(--border-color);
|
|
187
|
+
transition: all 0.3s ease;
|
|
216
188
|
}
|
|
217
|
-
.content-area{
|
|
218
|
-
background: var(--bgl-richtext-color);
|
|
219
|
-
}
|
|
220
|
-
.content-area, .preview-area{
|
|
221
|
-
min-height: 200px;
|
|
222
|
-
}
|
|
223
|
-
.fullscreen-mode {
|
|
224
|
-
position: fixed;
|
|
225
|
-
top: 0;
|
|
226
|
-
left: 0;
|
|
227
|
-
width: 100%;
|
|
228
|
-
height: 100%;
|
|
229
|
-
z-index: 9999;
|
|
230
|
-
background: var(--input-bg);
|
|
231
|
-
padding: 2rem;
|
|
232
|
-
overflow: auto;
|
|
233
|
-
border-radius: 0;
|
|
234
|
-
}
|
|
235
|
-
.fullscreen-mode .content-area{
|
|
236
|
-
height: calc(100vh - 5rem);
|
|
237
|
-
padding: 4rem !important;
|
|
238
|
-
max-width: 900px;
|
|
239
|
-
margin-inline: auto ;
|
|
240
|
-
overflow-y: auto;
|
|
241
|
-
}
|
|
242
|
-
.fullscreen-mode .toolbar{
|
|
243
|
-
max-width: 900px;
|
|
244
|
-
margin-inline: auto ;
|
|
245
|
-
/* border-bottom: 1px solid var(--border-color); */
|
|
246
|
-
/* margin-bottom: 2rem; */
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
/* [contenteditable='true'] {
|
|
250
|
-
white-space: pre-wrap;
|
|
251
|
-
word-wrap: break-word;
|
|
252
|
-
outline: none;
|
|
253
|
-
height: 100%;
|
|
254
|
-
} */
|
|
255
|
-
/*
|
|
256
189
|
|
|
257
190
|
.editor-container {
|
|
258
191
|
display: flex;
|
|
259
|
-
|
|
260
|
-
border-radius: 4px;
|
|
261
|
-
overflow: hidden;
|
|
192
|
+
gap: 1rem;
|
|
262
193
|
}
|
|
263
194
|
|
|
264
|
-
.content-area,
|
|
265
|
-
.preview-area {
|
|
195
|
+
.content-area, .preview-area {
|
|
266
196
|
flex: 1;
|
|
267
|
-
min-height:
|
|
268
|
-
|
|
269
|
-
overflow-y: auto;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.content-area:focus {
|
|
273
|
-
outline: none;
|
|
197
|
+
min-height: 200px;
|
|
198
|
+
background: var(--bgl-richtext-color);
|
|
274
199
|
}
|
|
275
200
|
|
|
276
201
|
.split-view .content-area,
|
|
@@ -278,24 +203,51 @@ function logInput(e: InputEvent) {
|
|
|
278
203
|
width: 50%;
|
|
279
204
|
}
|
|
280
205
|
|
|
206
|
+
.editableContent {
|
|
207
|
+
min-height: 100%;
|
|
208
|
+
white-space: pre-wrap;
|
|
209
|
+
outline: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.html-editor {
|
|
213
|
+
width: 100%;
|
|
214
|
+
height: 100%;
|
|
215
|
+
min-height: 200px;
|
|
216
|
+
font-family: monospace;
|
|
217
|
+
background: transparent;
|
|
218
|
+
border: none;
|
|
219
|
+
outline: none;
|
|
220
|
+
resize: none;
|
|
221
|
+
}
|
|
222
|
+
|
|
281
223
|
.preview-area {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
224
|
+
font-family: monospace;
|
|
225
|
+
white-space: pre-wrap;
|
|
226
|
+
overflow-x: auto;
|
|
227
|
+
}
|
|
285
228
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
229
|
+
.preview-area code {
|
|
230
|
+
display: block;
|
|
231
|
+
padding: 1rem;
|
|
232
|
+
}
|
|
290
233
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
234
|
+
.fullscreen-mode {
|
|
235
|
+
position: fixed;
|
|
236
|
+
top: 0;
|
|
237
|
+
left: 0;
|
|
238
|
+
width: 100vw;
|
|
239
|
+
height: 100vh;
|
|
240
|
+
z-index: 9999;
|
|
241
|
+
padding: 2rem;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.fullscreen-mode .editor-container {
|
|
245
|
+
height: calc(100vh - 8rem);
|
|
246
|
+
}
|
|
295
247
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
248
|
+
.fullscreen-mode .content-area,
|
|
249
|
+
.fullscreen-mode .preview-area {
|
|
250
|
+
height: 100%;
|
|
251
|
+
overflow-y: auto;
|
|
252
|
+
}
|
|
301
253
|
</style>
|
|
@@ -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 RichText } from './RichText.vue'
|
|
14
|
-
export { default as
|
|
13
|
+
// export { default as RichText } 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/components/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export { default as DataPreview } from './DataPreview.vue'
|
|
|
13
13
|
export { default as Dropdown } from './Dropdown.vue'
|
|
14
14
|
export { default as Flag } from './Flag.vue'
|
|
15
15
|
export * from './form'
|
|
16
|
+
export { default as Image } from './Image.vue'
|
|
16
17
|
// export * from './whatsapp';
|
|
17
18
|
export * from './layout'
|
|
18
19
|
export { default as ListItem } from './ListItem.vue'
|
|
@@ -25,8 +26,8 @@ export { default as ModalConfirm } from './ModalConfirm.vue'
|
|
|
25
26
|
export { default as ModalForm } from './ModalForm.vue'
|
|
26
27
|
export { default as NavBar } from './NavBar.vue'
|
|
27
28
|
export { default as PageTitle } from './PageTitle.vue'
|
|
28
|
-
export { default as Pill } from './Pill.vue'
|
|
29
29
|
|
|
30
|
+
export { default as Pill } from './Pill.vue'
|
|
30
31
|
export { default as RouterWrapper } from './RouterWrapper.vue'
|
|
31
32
|
export { default as TableSchema } from './TableSchema.vue'
|
|
32
33
|
export { default as Title } from './Title.vue'
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { LightboxItem } from './lightbox.types'
|
|
3
3
|
|
|
4
|
-
import { BglVideo, Btn, Icon, Zoomer } from '@bagelink/vue'
|
|
4
|
+
import { BglVideo, Btn, Icon, Zoomer, Image, normalizeURL } from '@bagelink/vue'
|
|
5
5
|
import { watch } from 'vue'
|
|
6
|
-
import Image from '../Image.vue'
|
|
7
6
|
|
|
8
7
|
let isOpen = $ref(false)
|
|
9
8
|
let currentItem = $ref<LightboxItem>()
|
|
@@ -48,12 +47,6 @@ watch(() => isOpen, (val) => {
|
|
|
48
47
|
else document.body.style.overflow = ''
|
|
49
48
|
})
|
|
50
49
|
|
|
51
|
-
function normalizeURL(url: string) {
|
|
52
|
-
if (url.startsWith('https://')) return url
|
|
53
|
-
url = url.replace(/http:\/\//, '')
|
|
54
|
-
return `//${url}`
|
|
55
|
-
}
|
|
56
|
-
|
|
57
50
|
function handleKeydown(event: KeyboardEvent) {
|
|
58
51
|
if (event.key === 'Escape') {
|
|
59
52
|
close()
|
package/src/plugins/bagel.ts
CHANGED
package/src/utils/index.ts
CHANGED
|
@@ -127,6 +127,17 @@ export function appendScript(src: string): Promise<void> {
|
|
|
127
127
|
})
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
export function normalizeURL(url: string) {
|
|
131
|
+
if (url.startsWith('https://')) return url
|
|
132
|
+
url = url.replace(/http:\/\//, '')
|
|
133
|
+
return `https://${url}`
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function normalizeDimension(value: string | number | undefined, defaultMetric = 'px'): string | undefined {
|
|
137
|
+
if (typeof value === 'number') return `${value}${defaultMetric}`
|
|
138
|
+
return value
|
|
139
|
+
}
|
|
140
|
+
|
|
130
141
|
export * as bagelFormUtils from './BagelFormUtils'
|
|
131
142
|
|
|
132
143
|
export { useLang } from './lang'
|