@bagelink/vue 1.4.139 → 1.4.141

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 (26) hide show
  1. package/dist/components/form/inputs/RichText/components/EditorToolbar.vue.d.ts.map +1 -1
  2. package/dist/components/form/inputs/RichText/composables/useCommands.d.ts.map +1 -1
  3. package/dist/components/form/inputs/RichText/composables/useEditor.d.ts.map +1 -1
  4. package/dist/components/form/inputs/RichText/composables/useEditorKeyboard.d.ts.map +1 -1
  5. package/dist/components/form/inputs/RichText/config.d.ts.map +1 -1
  6. package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
  7. package/dist/components/form/inputs/RichText/richTextTypes.d.ts +1 -1
  8. package/dist/components/form/inputs/RichText/richTextTypes.d.ts.map +1 -1
  9. package/dist/components/form/inputs/RichText/utils/commands.d.ts.map +1 -1
  10. package/dist/components/form/inputs/RichText/utils/formatting.d.ts.map +1 -1
  11. package/dist/components/form/inputs/RichText/utils/selection.d.ts.map +1 -1
  12. package/dist/index.cjs +20 -20
  13. package/dist/index.mjs +19 -19
  14. package/dist/style.css +1 -1
  15. package/package.json +1 -1
  16. package/src/components/dataTable/DataTable.vue +1 -1
  17. package/src/components/form/inputs/RichText/components/EditorToolbar.vue +11 -0
  18. package/src/components/form/inputs/RichText/composables/useCommands.ts +42 -0
  19. package/src/components/form/inputs/RichText/composables/useEditor.ts +8 -5
  20. package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +2 -128
  21. package/src/components/form/inputs/RichText/config.ts +15 -4
  22. package/src/components/form/inputs/RichText/index.vue +275 -73
  23. package/src/components/form/inputs/RichText/richTextTypes.ts +5 -0
  24. package/src/components/form/inputs/RichText/utils/commands.ts +614 -82
  25. package/src/components/form/inputs/RichText/utils/formatting.ts +17 -15
  26. package/src/components/form/inputs/RichText/utils/selection.ts +32 -11
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.4.139",
4
+ "version": "1.4.141",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Neveh Allon",
@@ -127,7 +127,7 @@ const showLoading = computed(() => loading.value)
127
127
  @click="toggleSort(field?.id || '')"
128
128
  >
129
129
  <div class="flex">
130
- {{ field.label || keyToLabel(field?.id) }}
130
+ {{ field.label || field.attrs?.label || keyToLabel(field?.id) }}
131
131
  <div class="list-arrows" :class="{ sorted: sortField === field?.id }">
132
132
  <Icon :class="{ desc: sortDirection === 'DESC' }" icon="keyboard_arrow_up" />
133
133
  </div>
@@ -30,6 +30,17 @@ function runAction(name: ToolbarConfigOption, value?: string) {
30
30
  />
31
31
  </template>
32
32
  </Dropdown>
33
+ <Btn
34
+ v-else-if="action.name === 'textDirection'"
35
+ v-tooltip="action.label"
36
+ :icon="selectedStyles.has('textDirection') ? 'format_textdirection_l_to_r' : 'format_textdirection_r_to_l'"
37
+ thin flat
38
+ :aria-label="action.name"
39
+ :class="[action.class, { active: selectedStyles.has('textDirection') }]"
40
+ class=""
41
+ tabindex="-1"
42
+ @click="runAction(action.name)"
43
+ />
33
44
  <Btn
34
45
  v-else-if="action.name !== 'separator'" v-tooltip="action.label" :icon="action.icon" thin flat
35
46
  :aria-label="action.name" :class="[action.class, { active: selectedStyles.has(action.name) }]"
@@ -1,10 +1,46 @@
1
1
  import type { EditorState } from '../richTextTypes'
2
2
  import { createCommandExecutor, createCommandRegistry } from '../utils/commands'
3
+ import { isStyleActive } from '../utils/selection'
3
4
 
4
5
  export function useCommands(state: EditorState, debug?: { logCommand: (command: string, value?: string) => void }) {
5
6
  const commands = createCommandRegistry(state)
6
7
  const executor = createCommandExecutor(state, commands)
7
8
 
9
+ // Function to immediately update styles
10
+ const updateStylesImmediately = () => {
11
+ if (!state.doc) return
12
+
13
+ const styles = new Set<string>()
14
+ const styleTypes = [
15
+ 'bold',
16
+ 'italic',
17
+ 'underline',
18
+ 'h1',
19
+ 'h2',
20
+ 'h3',
21
+ 'h4',
22
+ 'h5',
23
+ 'h6',
24
+ 'blockquote',
25
+ 'p',
26
+ 'orderedList',
27
+ 'unorderedList',
28
+ 'alignLeft',
29
+ 'alignCenter',
30
+ 'alignRight',
31
+ 'alignJustify',
32
+ 'textDirection'
33
+ ]
34
+
35
+ styleTypes.forEach((style) => {
36
+ if (isStyleActive(style, state.doc!)) {
37
+ styles.add(style)
38
+ }
39
+ })
40
+
41
+ state.selectedStyles = styles
42
+ }
43
+
8
44
  return {
9
45
  execute: (command: string, value?: string) => {
10
46
  if (!state.doc) {
@@ -48,6 +84,12 @@ export function useCommands(state: EditorState, debug?: { logCommand: (command:
48
84
  // Execute the command
49
85
  try {
50
86
  executor.execute(command, value)
87
+
88
+ // Update styles immediately after command execution for all commands except view state
89
+ const viewCommands = ['splitView', 'codeView', 'fullScreen']
90
+ if (!viewCommands.includes(command)) {
91
+ updateStylesImmediately()
92
+ }
51
93
  } catch (e) {
52
94
  console.error('[useCommands] Error during command execution:', e)
53
95
  }
@@ -72,7 +72,12 @@ export function useEditor() {
72
72
  'blockquote',
73
73
  'p',
74
74
  'orderedList',
75
- 'unorderedList'
75
+ 'unorderedList',
76
+ 'alignLeft',
77
+ 'alignCenter',
78
+ 'alignRight',
79
+ 'alignJustify',
80
+ 'textDirection'
76
81
  ]
77
82
  styleTypes.forEach((style) => {
78
83
  if (state.doc && isStyleActive(style, state.doc)) {
@@ -151,10 +156,8 @@ export function useEditor() {
151
156
  state.range = newSelection.getRangeAt(0).cloneRange()
152
157
  }
153
158
 
154
- // Update styles less frequently
155
- requestAnimationFrame(() => {
156
- updateState.styles()
157
- })
159
+ // Update styles immediately for better responsiveness
160
+ updateState.styles()
158
161
  }
159
162
  } catch (e) {
160
163
  state.selection = null
@@ -31,105 +31,8 @@ const shortcuts: KeyboardShortcut[] = [
31
31
  export function useEditorKeyboard(doc: Document, executor: CommandExecutor): void {
32
32
  // Handle keyboard shortcuts
33
33
  doc.addEventListener('keydown', (e) => {
34
- // Handle Enter key in lists and empty editor
35
- if (e.key === 'Enter' && !e.shiftKey) {
36
- const selection = doc.getSelection()
37
- if (!selection || !selection.rangeCount) return
38
-
39
- const range = selection.getRangeAt(0)
40
- const container = range.commonAncestorContainer
41
- const listItem = (container.nodeType === 3 ? container.parentElement : container as Element)?.closest('li')
42
-
43
- if (listItem) {
44
- // If we're at the end of a list item
45
- if (range.collapsed && isAtEndOfNode(listItem, range)) {
46
- // If the list item is empty, break out of the list
47
- if (isNodeEmpty(listItem)) {
48
- e.preventDefault()
49
- // Create a new paragraph after the list
50
- const list = listItem.parentElement
51
- if (!list) return
52
-
53
- // Remove the empty list item
54
- listItem.remove()
55
-
56
- // If the list is now empty, remove it
57
- if (!list.querySelector('li')) {
58
- const p = doc.createElement('p')
59
- p.innerHTML = ''
60
- list.parentNode?.replaceChild(p, list)
61
-
62
- // Set cursor in the new paragraph
63
- range.selectNodeContents(p)
64
- range.collapse(true)
65
- selection.removeAllRanges()
66
- selection.addRange(range)
67
- }
68
- } else {
69
- // Create a new list item
70
- e.preventDefault()
71
- const newLi = doc.createElement('li')
72
- newLi.innerHTML = '' // Start with empty list item
73
- listItem.insertAdjacentElement('afterend', newLi)
74
-
75
- // Move cursor to new list item
76
- range.selectNodeContents(newLi)
77
- range.collapse(true)
78
- selection.removeAllRanges()
79
- selection.addRange(range)
80
- }
81
- }
82
- } else {
83
- // Handle Enter in regular content - create new paragraph
84
- const blockElement = (container.nodeType === 3 ? container.parentElement : container as Element)?.closest('p,h1,h2,h3,h4,h5,h6,blockquote,div')
85
-
86
- if (blockElement && range.collapsed) {
87
- // If we're at the end of a block element, create a new paragraph
88
- if (isAtEndOfNode(blockElement, range)) {
89
- e.preventDefault()
90
- const newP = doc.createElement('p')
91
- newP.innerHTML = ''
92
- blockElement.insertAdjacentElement('afterend', newP)
93
-
94
- // Move cursor to new paragraph
95
- range.selectNodeContents(newP)
96
- range.collapse(true)
97
- selection.removeAllRanges()
98
- selection.addRange(range)
99
- }
100
- } else if (!blockElement && doc.body.textContent?.trim()) {
101
- // If we're typing directly in the body and press Enter, wrap in paragraphs
102
- e.preventDefault()
103
-
104
- // Split content at cursor position
105
- const textContent = doc.body.textContent || ''
106
- const cursorPos = range.startOffset
107
- const beforeText = textContent.substring(0, cursorPos).trim()
108
- const afterText = textContent.substring(cursorPos).trim()
109
-
110
- // Clear body
111
- doc.body.innerHTML = ''
112
-
113
- // Create first paragraph with before text
114
- if (beforeText) {
115
- const p1 = doc.createElement('p')
116
- p1.textContent = beforeText
117
- doc.body.appendChild(p1)
118
- }
119
-
120
- // Create second paragraph for after text
121
- const p2 = doc.createElement('p')
122
- p2.textContent = afterText
123
- doc.body.appendChild(p2)
124
-
125
- // Set cursor at beginning of second paragraph
126
- range.selectNodeContents(p2)
127
- range.collapse(true)
128
- selection.removeAllRanges()
129
- selection.addRange(range)
130
- }
131
- }
132
- }
34
+ // Remove Enter key handling from here - it's handled in setupAutoWrapping in index.vue
35
+ // This was causing conflicts with the main Enter key handler
133
36
 
134
37
  // Handle other keyboard shortcuts
135
38
  if (!e.ctrlKey && !e.metaKey) return
@@ -150,32 +53,3 @@ export function useEditorKeyboard(doc: Document, executor: CommandExecutor): voi
150
53
  }
151
54
  })
152
55
  }
153
-
154
- // Helper function to check if we're at the end of a node
155
- function isAtEndOfNode(node: Node, range: Range): boolean {
156
- if (node.nodeType === 3) { // Text node
157
- return range.startOffset === (node as Text).length
158
- }
159
-
160
- const { lastChild } = node
161
- if (!lastChild) return true
162
-
163
- if (lastChild.nodeType === 3) { // Text node
164
- return range.startContainer === lastChild && range.startOffset === lastChild.textContent?.length
165
- }
166
-
167
- return range.startContainer === node && range.startOffset === node.childNodes.length
168
- }
169
-
170
- // Helper function to check if a node is empty (contains only whitespace or <br> or &nbsp;)
171
- function isNodeEmpty(node: Node): boolean {
172
- const text = node.textContent?.replace(/\s/g, '') || '' // Remove non-breaking spaces and whitespace
173
- if (text) return false
174
-
175
- // Check for <br> tags
176
- const brElements = (node as Element).getElementsByTagName('br')
177
- if (brElements.length === 0) return true
178
-
179
- // If there's only one <br> and it's the only content (besides potential &nbsp;), consider it empty
180
- return brElements.length === 1 && node.childNodes.length <= 2 // Allow for &nbsp; + <br>
181
- }
@@ -9,10 +9,10 @@ export const tableTools: ToolbarConfig = [
9
9
  'insertColumnLeft',
10
10
  'insertColumnRight',
11
11
  'deleteColumn',
12
- 'alignLeft',
13
- 'alignCenter',
14
- 'alignRight',
15
- 'alignJustify',
12
+ 'tableAlignLeft',
13
+ 'tableAlignCenter',
14
+ 'tableAlignRight',
15
+ 'tableAlignJustify',
16
16
  'deleteTable'
17
17
  ]
18
18
 
@@ -33,6 +33,12 @@ export const basicToolbarConfig: ToolbarConfig = [
33
33
  'italic',
34
34
  'underline',
35
35
  'separator',
36
+ 'alignLeft',
37
+ 'alignCenter',
38
+ 'alignRight',
39
+ 'alignJustify',
40
+ 'textDirection',
41
+ 'separator',
36
42
  'link',
37
43
  'image',
38
44
  'embed',
@@ -100,6 +106,10 @@ export const toolbarOptions: ToolbarOption[] = [
100
106
  { name: 'alignCenter', label: 'Align Center', icon: 'format_align_center' },
101
107
  { name: 'alignRight', label: 'Align Right', icon: 'format_align_right' },
102
108
  { name: 'alignJustify', label: 'Align Justify', icon: 'format_align_justify' },
109
+ { name: 'tableAlignLeft', label: 'Table Align Left', icon: 'format_align_left' },
110
+ { name: 'tableAlignCenter', label: 'Table Align Center', icon: 'format_align_center' },
111
+ { name: 'tableAlignRight', label: 'Table Align Right', icon: 'format_align_right' },
112
+ { name: 'tableAlignJustify', label: 'Table Align Justify', icon: 'format_align_justify' },
103
113
  { name: 'indent', label: 'Indent', icon: 'format_indent_increase' },
104
114
  { name: 'outdent', label: 'Outdent', icon: 'format_indent_decrease' },
105
115
  { name: 'fontColor', label: 'Font Color', icon: 'format_color_text' },
@@ -119,4 +129,5 @@ export const toolbarOptions: ToolbarOption[] = [
119
129
  { name: 'redo', label: 'Redo', icon: 'redo' },
120
130
  { name: 'separator' },
121
131
  { name: 'fullScreen', label: 'Full Screen', icon: 'fullscreen', class: 'ms-auto' },
132
+ { name: 'textDirection', label: 'Text Direction (RTL/LTR)', icon: 'format_textdirection_r_to_l' },
122
133
  ]