@bagelink/vue 0.0.986 → 0.0.992

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 (75) hide show
  1. package/dist/components/Btn.vue.d.ts.map +1 -1
  2. package/dist/components/Loading.vue.d.ts +16 -0
  3. package/dist/components/Loading.vue.d.ts.map +1 -0
  4. package/dist/components/form/BglField.vue.d.ts.map +1 -1
  5. package/dist/components/form/inputs/CodeEditor/CodeTypes.d.ts +197 -0
  6. package/dist/components/form/inputs/CodeEditor/CodeTypes.d.ts.map +1 -0
  7. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts +15 -0
  8. package/dist/components/form/inputs/CodeEditor/Index.vue.d.ts.map +1 -0
  9. package/dist/components/form/inputs/CodeEditor/format.d.ts +2 -0
  10. package/dist/components/form/inputs/CodeEditor/format.d.ts.map +1 -0
  11. package/dist/components/form/inputs/FileUpload.vue.d.ts.map +1 -1
  12. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts +5 -3
  13. package/dist/components/form/inputs/RichText/Toolbar.vue.d.ts.map +1 -1
  14. package/dist/components/form/inputs/RichText/components/Toolbar.vue.d.ts +14 -0
  15. package/dist/components/form/inputs/RichText/components/Toolbar.vue.d.ts.map +1 -0
  16. package/dist/components/form/inputs/RichText/components/gridBox.vue.d.ts +7 -0
  17. package/dist/components/form/inputs/RichText/components/gridBox.vue.d.ts.map +1 -0
  18. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts +86 -0
  19. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -0
  20. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts +2 -0
  21. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -0
  22. package/dist/components/form/inputs/RichText/config.d.ts +5 -0
  23. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -0
  24. package/dist/components/form/inputs/RichText/index.vue.d.ts +1 -1
  25. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  26. package/dist/components/form/inputs/RichText/utils/formatting.d.ts +7 -0
  27. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -0
  28. package/dist/components/form/inputs/RichText/utils/media.d.ts +4 -0
  29. package/dist/components/form/inputs/RichText/utils/media.d.ts.map +1 -0
  30. package/dist/components/form/inputs/RichText/utils/selection.d.ts +4 -0
  31. package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -0
  32. package/dist/components/form/inputs/RichText/utils/table.d.ts +7 -0
  33. package/dist/components/form/inputs/RichText/utils/table.d.ts.map +1 -0
  34. package/dist/components/form/inputs/index.d.ts +1 -0
  35. package/dist/components/form/inputs/index.d.ts.map +1 -1
  36. package/dist/components/index.d.ts +1 -0
  37. package/dist/components/index.d.ts.map +1 -1
  38. package/dist/components/layout/TabsNav.vue.d.ts.map +1 -1
  39. package/dist/editor-CUDRLdmS.js +4 -0
  40. package/dist/editor-Cu374vEW.cjs +4 -0
  41. package/dist/editor-a8DSbb6P.js +4 -0
  42. package/dist/editor-xBt_vIha.cjs +4 -0
  43. package/dist/index.cjs +15594 -1103
  44. package/dist/index.mjs +15595 -1104
  45. package/dist/style.css +790 -511
  46. package/package.json +6 -33
  47. package/src/components/Btn.vue +113 -136
  48. package/src/components/Loading.vue +177 -0
  49. package/src/components/form/BglField.vue +2 -0
  50. package/src/components/form/inputs/CodeEditor/CodeTypes.ts +446 -0
  51. package/src/components/form/inputs/CodeEditor/Index.vue +117 -0
  52. package/src/components/form/inputs/CodeEditor/format.ts +98 -0
  53. package/src/components/form/inputs/FileUpload.vue +2 -1
  54. package/src/components/form/inputs/RichText/components/Toolbar.vue +51 -0
  55. package/src/components/form/inputs/RichText/components/gridBox.vue +51 -0
  56. package/src/components/form/inputs/RichText/composables/useEditor.ts +215 -0
  57. package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +21 -0
  58. package/src/components/form/inputs/RichText/config.ts +73 -0
  59. package/src/components/form/inputs/RichText/editor.css +25 -0
  60. package/src/components/form/inputs/RichText/index.vue +84 -195
  61. package/src/components/form/inputs/RichText/richTextTypes.d.ts +77 -0
  62. package/src/components/form/inputs/RichText/utils/formatting.ts +98 -0
  63. package/src/components/form/inputs/RichText/utils/media.ts +42 -0
  64. package/src/components/form/inputs/RichText/utils/selection.ts +48 -0
  65. package/src/components/form/inputs/RichText/utils/table.ts +81 -0
  66. package/src/components/form/inputs/index.ts +1 -0
  67. package/src/components/index.ts +2 -2
  68. package/src/components/layout/TabsNav.vue +1 -0
  69. package/src/styles/theme.css +256 -256
  70. package/src/components/form/inputs/RichText/Toolbar.vue +0 -87
  71. package/src/components/form/inputs/RichText/formatting.ts +0 -246
  72. package/src/components/form/inputs/RichText/richtext-types.ts +0 -29
  73. package/src/components/formkit/FileUploader.vue +0 -406
  74. package/src/components/formkit/MiscFields.vue +0 -74
  75. package/src/components/formkit/Toggle.vue +0 -149
@@ -0,0 +1,51 @@
1
+ <script lang="ts" setup>
2
+ import type { ToolbarConfig, ToolbarConfigOption, ToolbarOption } from '../richTextTypes'
3
+ import { Btn, Dropdown } from '@bagelink/vue'
4
+ import { defaultToolbarConfig, toolbarOptions } from '../config'
5
+ import GridBox from './gridBox.vue'
6
+
7
+ const { config = defaultToolbarConfig, selectedStyles } = defineProps<{
8
+ config?: ToolbarConfig
9
+ selectedStyles: Set<string>
10
+ }>()
11
+ const emit = defineEmits(['action'])
12
+
13
+ const configToOption = (action: ToolbarConfigOption) => toolbarOptions.find(option => option.name === action) as ToolbarOption
14
+
15
+ function runAction(name: ToolbarConfigOption, value?: string) {
16
+ emit('action', name, value)
17
+ }
18
+ </script>
19
+
20
+ <template>
21
+ <div class="toolbar flex gap-025 pb-05 flex-wrap" role="toolbar">
22
+ <template v-for="(action, index) in config.map(configToOption).filter(Boolean)" :key="index">
23
+ <Dropdown v-if="action.name === 'insertTable'" placement="bottom-start" thin flat icon="table">
24
+ <template #default="{ hide }">
25
+ <GridBox
26
+ :gridSize="5" @select="$event => {
27
+ runAction('insertTable', $event);
28
+ ($event.target as any)?.blur();
29
+ hide()
30
+ }"
31
+ />
32
+ </template>
33
+ </Dropdown>
34
+ <Btn
35
+ v-if="action.name !== 'separator'" v-tooltip="action.label" :icon="action.icon" thin flat
36
+ :aria-label="action.name" :class="[action.class, { active: selectedStyles.has(action.name) }]"
37
+ @click="runAction(action.name)"
38
+ />
39
+ <span v-else-if="action.name === 'separator'" :key="`separator-${index}`" class="opacity-2 mb-025">|</span>
40
+ </template>
41
+ </div>
42
+ </template>
43
+
44
+ <style scoped>
45
+ .toolbar :deep(.active) {
46
+ background: var(--bgl-primary);
47
+ color: white;
48
+ }
49
+ </style>
50
+
51
+ <style scoped></style>
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ const emit = defineEmits(['select'])
3
+ const fb = 1
4
+ const base = 5
5
+ const hoveredRow = $ref(fb)
6
+ const hoveredCol = $ref(fb)
7
+
8
+ const rowSize = $computed(() => {
9
+ const enlarge = Math.min(hoveredRow + 1, 20)
10
+ return hoveredRow > (base - 1) ? enlarge : base
11
+ })
12
+ const colSize = $computed(() => {
13
+ const enlarge = Math.min(hoveredCol + 1, 20)
14
+ return hoveredCol > (base - 1) ? enlarge : base
15
+ })
16
+ </script>
17
+
18
+ <template>
19
+ <div class="grid grid-wrap p-05">
20
+ <div
21
+ v-for="row in rowSize" :key="`row-${row}`" class="flex" @mouseout="hoveredRow = fb; hoveredCol = fb"
22
+ @focusout="hoveredRow = fb; hoveredCol = fb"
23
+ >
24
+ <div
25
+ v-for="col in colSize" :key="`col-${col}`" role="button" tabindex="0" aria-label="Insert Table"
26
+ :style="{ width: '20px', height: '20px' }" class="flex pointer gap-025 tableBoxSelect"
27
+ :class="{ borderSelect: hoveredRow >= row && hoveredCol >= col }"
28
+ @mousemove="hoveredRow = row; hoveredCol = col" @click="emit('select', `${row}x${col}`)"
29
+ />
30
+ </div>
31
+ <p class="txt-center txt-12 color-gray">
32
+ {{ hoveredRow }}x{{ hoveredCol }}
33
+ </p>
34
+ </div>
35
+ </template>
36
+
37
+ <style scoped>
38
+ .tableBoxSelect::after {
39
+ content: '';
40
+ display: block;
41
+ width: calc(100% - 4px);
42
+ height: calc(100% - 4px);
43
+ border: 1px solid var(--bgl-gray);
44
+ background: var(--bgl-gray-light);
45
+
46
+ }
47
+ .borderSelect::after {
48
+ border-color: var(--bgl-primary-tint);
49
+ background: var(--bgl-primary-light);
50
+ }
51
+ </style>
@@ -0,0 +1,215 @@
1
+ import type { EditorState, Modal } from '../richTextTypes'
2
+ import { reactive } from 'vue'
3
+ import { formatting } from '../utils/formatting'
4
+ import { insertImage, insertLink } from '../utils/media'
5
+ import { isStyleActive } from '../utils/selection'
6
+ import { addRow, deleteRow, mergeCells, splitCell, insertTable } from '../utils/table'
7
+
8
+ export function useEditor() {
9
+ const state = reactive<
10
+ EditorState
11
+ >({
12
+ isFullscreen: false,
13
+ hasInit: false,
14
+ isCodeView: false,
15
+ isSplitView: false,
16
+ selectedStyles: new Set<string>(),
17
+ rangeCount: 0,
18
+ content: '',
19
+ })
20
+
21
+ const updateListStyles = (styles: Set<string>) => {
22
+ if (state.selection?.rangeCount) {
23
+ const node = state.selection.getRangeAt(0).commonAncestorContainer
24
+ const parentElement = node.nodeType === 3 ? node.parentElement : node
25
+ if (parentElement) {
26
+ const list = (parentElement as Element).closest('ul, ol')
27
+ if (list) {
28
+ styles.add(
29
+ list.tagName.toLowerCase() === 'ul'
30
+ ? 'unorderedList'
31
+ : 'orderedList'
32
+ )
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ const updateActiveStyles = () => {
39
+ if (!state.doc) return
40
+ const styles = new Set<string>()
41
+ const styleTypes = [
42
+ 'bold',
43
+ 'italic',
44
+ 'underline',
45
+ 'h1',
46
+ 'h2',
47
+ 'h3',
48
+ 'h4',
49
+ 'h5',
50
+ 'h6',
51
+ ]
52
+
53
+ styleTypes.forEach((style) => {
54
+ if (state.doc && isStyleActive(style, state.doc)) {
55
+ styles.add(style)
56
+ }
57
+ })
58
+
59
+ updateListStyles(styles)
60
+ state.selectedStyles = styles
61
+ }
62
+
63
+ const updateSelection = () => {
64
+ if (!state.doc) return
65
+ state.selection = state.doc.getSelection()
66
+ if (!state.selection) return
67
+
68
+ try {
69
+ if (!state.doc.body.contains(state.selection.anchorNode)) {
70
+ state.doc.body.focus()
71
+ return
72
+ }
73
+
74
+ state.rangeCount = state.selection.rangeCount
75
+ if (!state.rangeCount) {
76
+ const range = state.doc.createRange()
77
+ range.selectNodeContents(state.doc.body)
78
+ range.collapse(false)
79
+ state.selection.removeAllRanges()
80
+ state.selection.addRange(range)
81
+ }
82
+ state.range = state.selection.getRangeAt(0).cloneRange()
83
+ updateActiveStyles()
84
+ } catch (e) {
85
+ console.warn('Selection error:', e)
86
+ state.selection = null
87
+ state.range = null
88
+ state.rangeCount = 0
89
+ state.selectedStyles = new Set()
90
+ }
91
+ }
92
+
93
+ const format = () => formatting(state)
94
+
95
+ const updateContent = (type?: string) => {
96
+ if (!state.doc) return
97
+ if (type === 'html') {
98
+ state.doc.body.innerHTML = state.content
99
+ } else {
100
+ state.content = state.doc.body.innerHTML
101
+ }
102
+ updateActiveStyles()
103
+ }
104
+
105
+ const handleToolbarAction = (action: string, value?: string) => {
106
+ if (!state.doc) return
107
+ if (action === 'fullScreen') {
108
+ state.isFullscreen = !state.isFullscreen
109
+ return
110
+ }
111
+ if (action === 'splitView') {
112
+ state.isSplitView = !state.isSplitView
113
+ return
114
+ }
115
+ if (action === 'codeView') {
116
+ state.isCodeView = !state.isCodeView
117
+ return
118
+ }
119
+
120
+ const applyFormatting = (command: string, value?: string) => {
121
+ const { selection, range } = state
122
+ if (!state.doc || !state.modal || !selection || !range) return
123
+
124
+ // Check if there is a selection or just a caret position
125
+ const isCaret = selection.isCollapsed
126
+
127
+ if (!state.doc.body.contains(selection.anchorNode)) {
128
+ state.doc.body.focus()
129
+ }
130
+ console.log(command)
131
+ switch (command) {
132
+ case 'insertTable': {
133
+ const [rows, cols] = value?.split('x').map(Number) || [3, 3]
134
+ insertTable(rows, cols, state)
135
+ break
136
+ }
137
+ case 'mergeCells':
138
+ if (isCaret) return
139
+ mergeCells(range, state.doc)
140
+ break
141
+ case 'splitCells':
142
+ if (isCaret) return
143
+ splitCell(range, state.doc)
144
+ break
145
+ case 'addRowBefore':
146
+ case 'addRowAfter':
147
+ if (isCaret) return
148
+ addRow(
149
+ command === 'addRowBefore' ? 'before' : 'after',
150
+ range,
151
+ state.doc
152
+ )
153
+ break
154
+ case 'deleteRow':
155
+ if (isCaret) return
156
+ deleteRow(range)
157
+ break
158
+ case 'bold':
159
+ case 'italic':
160
+ case 'underline':
161
+ format().text(command)
162
+ break
163
+ case 'orderedList':
164
+ case 'unorderedList':
165
+ format().list(command)
166
+ break
167
+ case 'image':
168
+ insertImage(state.modal, state.doc, range)
169
+ break
170
+ // case 'youtube':
171
+ // if (isCaret) return
172
+ // insertImage(state.modal, state.doc, range)
173
+ // break
174
+ case 'link':
175
+ if (isCaret) return
176
+ insertLink(state.modal, state.doc, range)
177
+ break
178
+ default:
179
+ if (/^h[1-6]$/.test(command)) {
180
+ format().block(command, value || command)
181
+ }
182
+ }
183
+ }
184
+ state.doc.body.focus()
185
+ applyFormatting(action, value)
186
+ updateContent()
187
+ updateActiveStyles()
188
+ }
189
+ const setupEventListeners = () => {
190
+ if (!state.doc) return
191
+ state.doc.addEventListener('selectionchange', () => { updateSelection() })
192
+ state.doc.addEventListener('input', () => { updateContent() })
193
+ state.doc.addEventListener('mouseup', () => { updateSelection() })
194
+ state.doc.addEventListener('keyup', (e) => {
195
+ if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) {
196
+ updateSelection()
197
+ }
198
+ })
199
+ }
200
+
201
+ const init = (doc: Document, modal: Modal) => {
202
+ state.doc = doc
203
+ state.modal = modal
204
+ setupEventListeners()
205
+ updateContent('html')
206
+ state.hasInit = true
207
+ }
208
+
209
+ return {
210
+ state,
211
+ init,
212
+ handleToolbarAction,
213
+ updateContent,
214
+ }
215
+ }
@@ -0,0 +1,21 @@
1
+
2
+ export function useEditorKeyboard(doc: Document, handleToolbarAction: (action: string, value?: string) => void): void {
3
+ doc.addEventListener('keydown', (e) => {
4
+ if (e.ctrlKey || e.metaKey) {
5
+ switch (e.key) {
6
+ case 'b':
7
+ e.preventDefault()
8
+ handleToolbarAction('bold')
9
+ break
10
+ case 'i':
11
+ e.preventDefault()
12
+ handleToolbarAction('italic')
13
+ break
14
+ case 'u':
15
+ e.preventDefault()
16
+ handleToolbarAction('underline')
17
+ break
18
+ }
19
+ }
20
+ })
21
+ }
@@ -0,0 +1,73 @@
1
+ import type { ToolbarConfig, ToolbarOption } from './richTextTypes'
2
+
3
+ export const tableTools: ToolbarConfig = [
4
+ 'mergeCells',
5
+ 'splitCells',
6
+ 'addRowBefore',
7
+ 'addRowAfter',
8
+ 'deleteRow'
9
+ ]
10
+
11
+ export const defaultToolbarConfig: ToolbarConfig = [
12
+ 'h2',
13
+ 'h3',
14
+ 'h4',
15
+ 'h5',
16
+ 'h6',
17
+ 'separator',
18
+ 'bold',
19
+ 'italic',
20
+ 'underline',
21
+ 'separator',
22
+ 'orderedList',
23
+ 'unorderedList',
24
+ 'separator',
25
+ 'link',
26
+ 'image',
27
+ 'youtube',
28
+ 'separator',
29
+ 'splitView',
30
+ 'clear',
31
+ 'insertTable',
32
+ // ...tableTools,
33
+ 'fullScreen',
34
+ ]
35
+
36
+ export const toolbarOptions: ToolbarOption[] = [
37
+ { name: 'h2', label: 'h2', icon: 'format_h2' },
38
+ { name: 'h3', label: 'h3', icon: 'format_h3' },
39
+ { name: 'h4', label: 'h4', icon: 'format_h4' },
40
+ { name: 'h5', label: 'h5', icon: 'format_h5' },
41
+ { name: 'h6', label: 'h6', icon: 'format_h6' },
42
+ { name: 'bold', label: 'Bold', icon: 'format_bold' },
43
+ { name: 'italic', label: 'Italic', icon: 'format_italic' },
44
+ { name: 'underline', label: 'Underline', icon: 'format_underlined' },
45
+ { name: 'orderedList', label: 'Ordered List', icon: 'format_list_numbered' },
46
+ { name: 'unorderedList', label: 'Unordered List', icon: 'format_list_bulleted' },
47
+ { name: 'link', label: 'Link', icon: 'add_link' },
48
+ { name: 'image', label: 'Image', icon: 'add_photo_alternate' },
49
+ { name: 'youtube', label: 'YouTube', icon: 'youtube_activity' },
50
+ { name: 'splitView', label: 'Split View', icon: 'code' },
51
+ { name: 'clear', label: 'Clear Formatting', icon: 'format_clear' },
52
+ { name: 'alignLeft', label: 'Align Left', icon: 'format_align_left' },
53
+ { name: 'alignCenter', label: 'Align Center', icon: 'format_align_center' },
54
+ { name: 'alignRight', label: 'Align Right', icon: 'format_align_right' },
55
+ { name: 'alignJustify', label: 'Align Justify', icon: 'format_align_justify' },
56
+ { name: 'indent', label: 'Indent', icon: 'format_indent_increase' },
57
+ { name: 'outdent', label: 'Outdent', icon: 'format_indent_decrease' },
58
+ { name: 'fontColor', label: 'Font Color', icon: 'format_color_text' },
59
+ { name: 'bgColor', label: 'Background Color', icon: 'format_color_fill' },
60
+ { name: 'insertTable', label: 'Insert Table', icon: 'table' },
61
+ { name: 'deleteTable', label: 'Delete Table', icon: 'table_rows' },
62
+ { name: 'insertRowAbove', label: 'Insert Row Above', icon: 'table_rows' },
63
+ { name: 'insertRowBelow', label: 'Insert Row Below', icon: 'table_rows' },
64
+ { name: 'deleteRow', label: 'Delete Row', icon: 'table_rows' },
65
+ { name: 'insertColumnLeft', label: 'Insert Column Left', icon: 'add_column_left' },
66
+ { name: 'insertColumnRight', label: 'Insert Column Right', icon: 'add_column_right' },
67
+ { name: 'deleteColumn', label: 'Delete Column', icon: 'view_column' },
68
+ { name: 'separator' },
69
+ { name: 'undo', label: 'Undo', icon: 'undo' },
70
+ { name: 'redo', label: 'Redo', icon: 'redo' },
71
+ { name: 'separator' },
72
+ { name: 'fullScreen', label: 'Full Screen', icon: 'fullscreen', class: 'ms-auto' },
73
+ ]
@@ -0,0 +1,25 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 8px;
4
+ min-height: 200px;
5
+ font-family: sans-serif;
6
+ color: inherit;
7
+ background: transparent;
8
+ }
9
+
10
+ table {
11
+ border-collapse: collapse;
12
+ margin-bottom: 1rem;
13
+ }
14
+
15
+ th,
16
+ td {
17
+ padding: 1rem;
18
+ text-align: left;
19
+ border: 1px solid #2a2a2a;
20
+ line-height: 1.5;
21
+ }
22
+
23
+ th {
24
+ background-color: #f4f4f4;
25
+ }