@bagelink/vue 0.0.978 → 0.0.982

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 (28) hide show
  1. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts +12 -0
  2. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts.map +1 -0
  3. package/dist/components/form/inputs/RichText/formatting.d.ts +11 -0
  4. package/dist/components/form/inputs/RichText/formatting.d.ts.map +1 -0
  5. package/dist/components/form/inputs/RichText/index.vue.d.ts +14 -0
  6. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -0
  7. package/dist/components/form/inputs/RichText/richtext-types.d.ts +3 -0
  8. package/dist/components/form/inputs/RichText/richtext-types.d.ts.map +1 -0
  9. package/dist/components/form/inputs/RichText2/Toolbar.vue.d.ts.map +1 -1
  10. package/dist/components/form/inputs/RichText2/index.vue.d.ts +0 -1
  11. package/dist/components/form/inputs/RichText2/index.vue.d.ts.map +1 -1
  12. package/dist/components/form/inputs/TelInput.vue.d.ts +2 -2
  13. package/dist/components/form/inputs/index.d.ts +1 -2
  14. package/dist/components/form/inputs/index.d.ts.map +1 -1
  15. package/dist/index.cjs +953 -27313
  16. package/dist/index.mjs +1044 -27404
  17. package/dist/plugins/bagel.d.ts +1 -1
  18. package/dist/plugins/bagel.d.ts.map +1 -1
  19. package/dist/style.css +53 -264
  20. package/package.json +1 -10
  21. package/src/components/form/inputs/{RichText2 → RichText}/Toolbar.vue +27 -20
  22. package/src/components/form/inputs/RichText/index.vue +253 -0
  23. package/src/components/form/inputs/index.ts +1 -2
  24. package/src/plugins/bagel.ts +1 -1
  25. package/src/components/form/inputs/RichText.vue +0 -420
  26. package/src/components/form/inputs/RichText2/index.vue +0 -301
  27. /package/src/components/form/inputs/{RichText2 → RichText}/formatting.ts +0 -0
  28. /package/src/components/form/inputs/{RichText2 → RichText}/richtext-types.ts +0 -0
@@ -28,7 +28,7 @@ export interface BagelOptions {
28
28
  availableLangs?: string[]
29
29
  defaultLang?: string
30
30
  language?: string
31
- host: string
31
+ host?: string
32
32
 
33
33
  onError?: (err: Error) => void
34
34
 
@@ -1,420 +0,0 @@
1
- <script setup lang="ts">
2
- import type { MaterialIcons } from '@bagelink/vue'
3
- import {
4
- FileUpload as $el,
5
- BglVideo,
6
- Btn,
7
- bagelFormUtils,
8
- useModal,
9
- } from '@bagelink/vue'
10
- import Image from '@tiptap/extension-image'
11
- import Table from '@tiptap/extension-table'
12
- import TableCell from '@tiptap/extension-table-cell'
13
- import TableHeader from '@tiptap/extension-table-header'
14
- import TableRow from '@tiptap/extension-table-row'
15
- import YouTube from '@tiptap/extension-youtube'
16
- import StarterKit from '@tiptap/starter-kit'
17
- import { Editor, EditorContent } from '@tiptap/vue-3'
18
- import { onBeforeUnmount, onMounted, watch } from 'vue'
19
-
20
- const props = defineProps<{ modelValue: string }>()
21
- const emit = defineEmits(['update:modelValue'])
22
- // import $el from './FileUploadURL.vue';
23
- const { txtField } = bagelFormUtils
24
- let editor = $ref<Editor>()
25
-
26
- function focus() {
27
- if (!editor) throw new Error('editor is not defined')
28
- return editor.chain().focus()
29
- }
30
- const { showModalForm } = useModal()
31
-
32
- Image.configure({
33
- inline: true,
34
- allowBase64: true,
35
- })
36
-
37
- function addImage() {
38
- showModalForm({
39
- title: 'Add Image',
40
- schema: [
41
- txtField('title', 'Title'),
42
- { $el, id: 'src', attrs: { bindkey: 'url' } },
43
- txtField('alt', 'Alt Text'),
44
- ],
45
- onSubmit: imgObj => focus().setImage(imgObj).run(),
46
- })
47
- }
48
-
49
- const config: {
50
- name: string
51
- command: any
52
- icon: MaterialIcons
53
- option?: { [key: string]: any }
54
- disabled?: () => boolean
55
- }[] = [
56
- {
57
- name: 'Bold',
58
- command: () => focus().toggleBold().run(),
59
- icon: 'format_bold',
60
- disabled: () => !editor?.can().chain().focus().toggleBold().run(),
61
- },
62
- {
63
- name: 'Italic',
64
- command: () => focus().toggleItalic().run(),
65
- icon: 'format_italic',
66
- disabled: () => !editor?.can().chain().focus().toggleItalic().run(),
67
- },
68
- {
69
- name: 'Strike',
70
- command: () => focus().toggleStrike().run(),
71
- icon: 'format_strikethrough',
72
- disabled: () => !editor?.can().chain().focus().toggleStrike().run(),
73
- },
74
- {
75
- name: 'Code Text',
76
- command: () => focus().toggleCode().run(),
77
- icon: 'code',
78
- },
79
- {
80
- name: 'Image',
81
- command: addImage,
82
- icon: 'image',
83
- },
84
- {
85
- name: 'YouTube',
86
- command: () => {
87
- showModalForm({
88
- title: 'Add YouTube Video',
89
- schema: [
90
- txtField('src', 'YouTube URL'),
91
- {
92
- $el: BglVideo,
93
- attrs: { src: (_: any, { src }: { src: string }) => src || '' },
94
- },
95
- ],
96
- onSubmit: videoObj => editor?.commands.setYoutubeVideo(videoObj),
97
- })
98
- },
99
- icon: 'youtube_activity',
100
- },
101
- {
102
- name: 'Clear',
103
- command: () => focus().clearNodes().run(),
104
- icon: 'format_clear',
105
- },
106
- {
107
- name: 'Paragraph',
108
- command: () => focus().setParagraph().run(),
109
- icon: 'format_paragraph',
110
- },
111
- {
112
- name: 'Heading',
113
- command: () => focus().toggleHeading({ level: 1 }).run(),
114
- icon: 'format_h1',
115
- option: { level: 1 },
116
- },
117
- {
118
- name: 'Heading',
119
- command: () => focus().toggleHeading({ level: 2 }).run(),
120
- icon: 'format_h2',
121
- option: { level: 2 },
122
- },
123
- {
124
- name: 'Heading',
125
- command: () => focus().toggleHeading({ level: 3 }).run(),
126
- icon: 'format_h3',
127
- option: { level: 3 },
128
- },
129
- {
130
- name: 'Heading',
131
- command: () => focus().toggleHeading({ level: 4 }).run(),
132
- icon: 'format_h4',
133
- option: { level: 4 },
134
- },
135
- {
136
- name: 'Heading',
137
- command: () => focus().toggleHeading({ level: 5 }).run(),
138
- icon: 'format_h5',
139
- option: { level: 5 },
140
- },
141
- {
142
- name: 'Heading',
143
- command: () => focus().toggleHeading({ level: 6 }).run(),
144
- icon: 'format_h6',
145
- option: { level: 6 },
146
- },
147
- {
148
- name: 'Bullet List',
149
- command: () => focus().toggleBulletList().run(),
150
- icon: 'format_list_bulleted',
151
- },
152
- {
153
- name: 'Ordered List',
154
- command: () => focus().toggleOrderedList().run(),
155
- icon: 'format_list_numbered',
156
- },
157
- {
158
- name: 'Code Block',
159
- command: () => focus().toggleCodeBlock().run(),
160
- icon: 'code_blocks',
161
- },
162
- {
163
- name: 'Blockquote',
164
- command: () => focus().toggleBlockquote().run(),
165
- icon: 'format_quote',
166
- },
167
- {
168
- name: 'Horizontal Rule',
169
- command: () => focus().setHorizontalRule().run(),
170
- icon: 'horizontal_rule',
171
- },
172
- {
173
- name: 'Hard Break',
174
- command: () => focus().setHardBreak().run(),
175
- icon: 'keyboard_return',
176
- },
177
- {
178
- name: 'Table',
179
- command: () => editor
180
- ?.chain()
181
- .focus()
182
- .insertTable({ rows: 3, cols: 3, withHeaderRow: true })
183
- .run(),
184
- icon: 'table_chart',
185
- },
186
- {
187
- name: 'Undo',
188
- command: () => focus().undo().run(),
189
- icon: 'undo',
190
- disabled: () => !editor?.can().chain().focus().undo().run(),
191
- },
192
- {
193
- name: 'Redo',
194
- command: () => focus().redo().run(),
195
- icon: 'redo',
196
- disabled: () => !editor?.can().chain().focus().redo().run(),
197
- },
198
- ]
199
-
200
- function initEditor() {
201
- editor = new Editor({
202
- extensions: [
203
- StarterKit,
204
- Image,
205
- YouTube,
206
- Table,
207
- TableRow,
208
- TableCell,
209
- TableHeader,
210
- ],
211
- content: props.modelValue,
212
- onUpdate: ({ editor }) => { emit('update:modelValue', editor.getHTML()) },
213
- })
214
- }
215
-
216
- watch(
217
- () => props.modelValue,
218
- (value) => {
219
- if (editor?.getHTML() === value) return
220
- editor?.commands.setContent(value, false)
221
- },
222
- { immediate: true },
223
- )
224
-
225
- onMounted(initEditor)
226
-
227
- onBeforeUnmount(() => editor?.destroy())
228
- </script>
229
-
230
- <template>
231
- <div class="RichText">
232
- <div v-if="editor" class="RichText-tools">
233
- <Btn
234
- v-for="item in config"
235
- :key="item.name"
236
- v-tooltip="item.name"
237
- tabindex="-1"
238
- :flat="!editor.isActive(item.name, item.option)"
239
- thin
240
- :icon="item.icon"
241
- :disabled="item.disabled?.()"
242
- @click="item.command"
243
- />
244
- </div>
245
- <EditorContent :editor="editor" />
246
- </div>
247
- </template>
248
-
249
- <style>
250
- /* Basic editor styles */
251
- .tiptap > * + * {
252
- margin-top: 0.75em;
253
- }
254
-
255
- .tiptap ul,
256
- .tiptap ol {
257
- padding: 0 1rem;
258
- }
259
-
260
- .tiptap h1,
261
- .tiptap h2 {
262
- line-height: 1;
263
- }
264
-
265
- .tiptap h3,
266
- .tiptap h4,
267
- .tiptap h5,
268
- .tiptap h6 {
269
- line-height: 1.1;
270
- }
271
-
272
- .tiptap pre {
273
- background: #0d0d0d;
274
- color: var(--bgl-light-text);
275
- font-family: 'JetBrainsMono', monospace;
276
- padding: 0.75rem 1rem;
277
- border-radius: 0.5rem;
278
- margin: 0;
279
- }
280
-
281
- .tiptap pre code {
282
- color: inherit;
283
- padding: 0;
284
- background: none;
285
- font-size: 0.8rem;
286
- }
287
-
288
- .tiptap img {
289
- max-width: 100%;
290
- height: auto;
291
- }
292
-
293
- .tiptap blockquote {
294
- padding-inline-start: 0.5rem;
295
- border-inline-start: 2px solid var(--border-color);
296
- margin: 0.5rem
297
- }
298
-
299
- .tiptap hr {
300
- border: none;
301
- border-top: 2px solid var(--border-color);
302
- margin: 1rem 0;
303
- }
304
-
305
- .tiptap {
306
- min-height: 200px;
307
- background: var(--input-bg);
308
- overflow: auto;
309
- max-height: 500px;
310
- padding: 0.7rem;
311
- color: var(--input-color);
312
- border-radius: var(--input-border-radius);
313
- font-size: var(--input-font-size);
314
- line-height: 1.5;
315
- }
316
- .tiptap.ProseMirror-focused {
317
- outline: none;
318
- box-shadow: inset 0 0 10px #00000012;
319
- }
320
- .RichText {
321
- margin-bottom: 0.5rem;
322
- }
323
- .RichText-tools {
324
- background: var(--input-bg);
325
- margin-bottom: 0.125rem;
326
- border-radius: var(--input-border-radius);
327
- }
328
-
329
- .tiptap {
330
- table {
331
- border-collapse: collapse;
332
- table-layout: fixed;
333
- width: 100%;
334
- margin: 0;
335
- overflow: hidden;
336
-
337
- td,
338
- th {
339
- min-width: 1em;
340
- border: 2px solid var(--border-color);
341
- padding: 3px 5px;
342
- vertical-align: top;
343
- box-sizing: border-box;
344
- position: relative;
345
-
346
- > * {
347
- margin-bottom: 0;
348
- margin-top: 0;
349
- }
350
- }
351
-
352
- th {
353
- font-weight: bold;
354
- text-align: start;
355
- background-color: var(--bgl-gray-80);
356
- }
357
-
358
- .selectedCell:after {
359
- z-index: 2;
360
- position: absolute;
361
- content: '';
362
- left: 0;
363
- right: 0;
364
- top: 0;
365
- bottom: 0;
366
- background: var(--bgl-gray-20);
367
- pointer-events: none;
368
- }
369
- .selectedCell p::selection{
370
- color: var(--bgl-popup-text)
371
- }
372
-
373
- .column-resize-handle {
374
- position: absolute;
375
- inset-inline-start: -2px;
376
- top: 0;
377
- bottom: -2px;
378
- width: 4px;
379
- background-color: #adf;
380
- pointer-events: none;
381
- }
382
-
383
- p {
384
- margin: 0;
385
- }
386
- }
387
- }
388
-
389
- .tableWrapper {
390
- padding: 1rem 0;
391
- overflow-x: auto;
392
- }
393
-
394
- .resize-cursor {
395
- cursor: ew-resize;
396
- cursor: col-resize;
397
- }
398
- .RichText-tools [disabled]{
399
- color: var(--bgl-popup-text) !important;
400
- opacity: 0.3;
401
- }
402
- .RichText iframe{
403
- width: 100%;
404
- max-width: 700px;
405
- margin: 0 auto;
406
- height: auto;
407
- display: block;
408
- border-radius: var(--input-border-radius);
409
- border: none;
410
- aspect-ratio: 16/9;
411
- }
412
- .RichText img{
413
- width: 100%;
414
- max-width: 700px;
415
- margin: 0 auto;
416
- height: auto;
417
- display: block;
418
- border-radius: var(--input-border-radius);
419
- }
420
- </style>
@@ -1,301 +0,0 @@
1
- <script lang="ts" setup>
2
- import type { ToolbarConfig } from './richtext-types'
3
- import { useModal } from '@bagelink/vue'
4
- import { onMounted, watch } from 'vue'
5
- import { useFormatting } from './formatting'
6
- import Toolbar from './Toolbar.vue'
7
-
8
- const props = defineProps<{ modelValue: string, toolbarConfig?: ToolbarConfig }>()
9
- const emit = defineEmits(['update:modelValue'])
10
-
11
- const modal = useModal()
12
- const { applyFormatting, clearFormatting } = useFormatting(modal)
13
-
14
- const editableContent = $ref<HTMLElement | undefined>()
15
- const defaultConfig: ToolbarConfig = [
16
- 'formatBlock',
17
- 'bold',
18
- 'italic',
19
- 'underline',
20
- 'fontSize',
21
- 'fontFamily',
22
- 'textColor',
23
- 'backgroundColor',
24
- 'alignLeft',
25
- 'alignCenter',
26
- 'alignRight',
27
- 'alignJustify',
28
- 'orderedList',
29
- 'unorderedList',
30
- 'indent',
31
- 'outdent',
32
- 'link',
33
- 'image',
34
- 'table',
35
- 'blockquote',
36
- 'codeBlock',
37
- 'splitView',
38
- 'youtube',
39
- 'codeView',
40
- 'clear',
41
- 'fullScreen'
42
- ]
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
-
48
- function updateContent() {
49
- if (!isCodeView) {
50
- contentHtml = editableContent?.innerHTML ?? ''
51
- }
52
- emit('update:modelValue', contentHtml)
53
- }
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
- export type ToolbarAction = ToolbarConfig[number]
77
-
78
- function handleToolbarAction(action: string, value?: string) {
79
- if (!editableContent) return
80
- if (['alignLeft', 'alignCenter', 'alignRight', 'alignJustify'].includes(action))
81
- value = action.replace('align', '').toLowerCase()
82
- if (action === 'splitView') isSplitView = !isSplitView
83
- else if (action === 'codeView') isCodeView = !isCodeView
84
- else if (action === 'fullScreen') toggleFullScreen()
85
- else if (action === 'clear') clearFormatting()
86
- else applyFormatting(action, value)
87
- updateContent()
88
- }
89
-
90
- function toggleFullScreen() {
91
- const editor = document.querySelector('.rich-text-editor')
92
- if (editor) {
93
- editor.classList.toggle('fullscreen-mode')
94
- }
95
- }
96
-
97
- watch(() => props.modelValue, (newValue) => {
98
- if (newValue !== contentHtml) {
99
- contentHtml = newValue
100
- if (editableContent) {
101
- editableContent.innerHTML = newValue
102
- }
103
- }
104
- })
105
-
106
- onMounted(() => {
107
- if (editableContent) {
108
- editableContent.innerHTML = contentHtml
109
- }
110
- })
111
-
112
- // Keyboard shortcuts
113
- function handleKeyDown(event: KeyboardEvent) {
114
- if (event.ctrlKey || event.metaKey) {
115
- switch (event.key.toLowerCase()) {
116
- case 'b':
117
- event.preventDefault()
118
- handleToolbarAction('bold')
119
- break
120
- case 'i':
121
- event.preventDefault()
122
- handleToolbarAction('italic')
123
- break
124
- case 'u':
125
- event.preventDefault()
126
- handleToolbarAction('underline')
127
- 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
- }
139
- }
140
- }
141
- function logInput(e: InputEvent) {
142
- const target = e.target as HTMLElement
143
- contentHtml = target.textContent || ''
144
- }
145
- </script>
146
-
147
- <template>
148
- <div class="rich-text-editor rounded pt-05 px-05 pb-1">
149
- <Toolbar :config @action="handleToolbarAction" />
150
- <div class="editor-container flex flex-stretch gap-1 m_column">
151
- <div class="content-area radius-05 p-1 w-100 grid">
152
- <textarea v-if="isCodeView" v-model="contentHtml" @input="updateContent" />
153
- <div
154
- v-else
155
- ref="editableContent"
156
- contenteditable="true"
157
- class="editableContent"
158
- role="textbox"
159
- aria-multiline="true"
160
- tabindex="0"
161
- @input="updateContent"
162
- @keydown="handleKeyDown"
163
- />
164
- </div>
165
- <code
166
- v-if="isSplitView"
167
- contenteditable="true"
168
- class="preview-area w-100 radius-05 p-1"
169
- @input="(e) => logInput(e as InputEvent)"
170
- v-text="contentHtml"
171
- />
172
- </div>
173
- </div>
174
- </template>
175
-
176
- <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
- .rich-text-editor {
208
- background: var(--input-bg);
209
- margin-bottom: 0.5rem;
210
- width: 100%;
211
- }
212
- .editableContent{
213
- min-height: 100%;
214
- white-space: pre-wrap;
215
- outline: none;
216
- }
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
-
257
- .editor-container {
258
- display: flex;
259
- border: 1px solid #ccc;
260
- border-radius: 4px;
261
- overflow: hidden;
262
- }
263
-
264
- .content-area,
265
- .preview-area {
266
- flex: 1;
267
- min-height: 300px;
268
- padding: 10px;
269
- overflow-y: auto;
270
- }
271
-
272
- .content-area:focus {
273
- outline: none;
274
- }
275
-
276
- .split-view .content-area,
277
- .split-view .preview-area {
278
- width: 50%;
279
- }
280
-
281
- .preview-area {
282
- background-color: #f9f9f9;
283
- border-left: 1px solid #ccc;
284
- } */
285
-
286
- /* @media (max-width: 768px) {
287
- .split-view {
288
- flex-direction: column;
289
- }
290
-
291
- .split-view .content-area,
292
- .split-view .preview-area {
293
- width: 100%;
294
- }
295
-
296
- .preview-area {
297
- border-left: none;
298
- border-top: 1px solid #ccc;
299
- }
300
- } */
301
- </style>