@bagelink/vue 1.5.17 → 1.5.20
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/bin/experimentalGenTypedRoutes.ts +15 -15
- package/bin/generateFormSchema.ts +12 -12
- package/bin/utils.ts +4 -4
- package/dist/components/Dropdown.vue.d.ts.map +1 -1
- package/dist/components/calendar/CalendarPopover.vue.d.ts.map +1 -1
- package/dist/components/form/BagelForm.vue.d.ts.map +1 -1
- package/dist/components/form/FieldArray.vue.d.ts +5 -4
- package/dist/components/form/FieldArray.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/CheckInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/RichText/index.vue.d.ts +1 -0
- package/dist/components/form/inputs/RichText/index.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/SelectInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/ToggleInput.vue.d.ts.map +1 -1
- package/dist/components/form/inputs/Upload/UploadInput.vue.d.ts.map +1 -1
- package/dist/composables/useFormField.d.ts.map +1 -1
- package/dist/composables/useSchemaField.d.ts.map +1 -1
- package/dist/index.cjs +17 -14
- package/dist/index.mjs +511 -422
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/AccordionItem.vue +2 -2
- package/src/components/AddToCalendar.vue +1 -1
- package/src/components/BglVideo.vue +8 -8
- package/src/components/Btn.vue +9 -9
- package/src/components/Card.vue +4 -4
- package/src/components/Carousel.vue +44 -44
- package/src/components/DataPreview.vue +1 -1
- package/src/components/DragOver.vue +6 -6
- package/src/components/Dropdown.vue +14 -14
- package/src/components/Flag.vue +3 -3
- package/src/components/Icon/Icon.vue +13 -13
- package/src/components/Image.vue +4 -4
- package/src/components/ImportData.vue +79 -79
- package/src/components/ListItem.vue +7 -7
- package/src/components/MapEmbed/Index.vue +6 -6
- package/src/components/Modal.vue +10 -10
- package/src/components/ModalForm.vue +4 -4
- package/src/components/NavBar.vue +2 -2
- package/src/components/Pagination.vue +9 -9
- package/src/components/Pill.vue +1 -1
- package/src/components/Rating.vue +2 -2
- package/src/components/Slider.vue +77 -77
- package/src/components/Spreadsheet/Index.vue +34 -34
- package/src/components/Spreadsheet/SpreadsheetTable.vue +3 -3
- package/src/components/Zoomer.vue +28 -28
- package/src/components/analytics/BarChart.vue +6 -6
- package/src/components/analytics/KpiCard.vue +2 -2
- package/src/components/analytics/LineChart.vue +14 -14
- package/src/components/analytics/PieChart.vue +11 -11
- package/src/components/calendar/CalendarPopover.vue +1 -1
- package/src/components/calendar/Index.vue +1 -1
- package/src/components/calendar/views/AgendaView.vue +2 -2
- package/src/components/calendar/views/DayView.vue +6 -6
- package/src/components/calendar/views/MonthView.vue +2 -2
- package/src/components/calendar/views/WeekView.vue +18 -18
- package/src/components/dataTable/DataTable.vue +3 -3
- package/src/components/dataTable/useSorting.ts +1 -1
- package/src/components/dataTable/useTableData.ts +15 -15
- package/src/components/dataTable/useTableSelection.ts +8 -8
- package/src/components/dataTable/useTableVirtualization.ts +1 -1
- package/src/components/draggable/useDraggable.ts +42 -42
- package/src/components/form/BagelForm.vue +66 -23
- package/src/components/form/BglMultiStepForm.vue +18 -18
- package/src/components/form/FieldArray.vue +177 -67
- package/src/components/form/inputs/CheckInput.vue +2 -1
- package/src/components/form/inputs/CodeEditor/Index.vue +1 -1
- package/src/components/form/inputs/CodeEditor/format.ts +7 -7
- package/src/components/form/inputs/CodeEditor/useHighlight.ts +6 -6
- package/src/components/form/inputs/DateInput.vue +6 -6
- package/src/components/form/inputs/DatePicker.vue +19 -19
- package/src/components/form/inputs/EmailInput.vue +14 -14
- package/src/components/form/inputs/NumberInput.vue +6 -6
- package/src/components/form/inputs/OTP.vue +3 -3
- package/src/components/form/inputs/RadioGroup.vue +1 -1
- package/src/components/form/inputs/RadioPillsInput.vue +8 -8
- package/src/components/form/inputs/RichText/components/EditorToolbar.vue +10 -10
- package/src/components/form/inputs/RichText/components/TableGridSelector.vue +1 -1
- package/src/components/form/inputs/RichText/composables/useCommands.ts +1 -1
- package/src/components/form/inputs/RichText/composables/useEditor.ts +12 -12
- package/src/components/form/inputs/RichText/composables/useEditorKeyboard.ts +1 -1
- package/src/components/form/inputs/RichText/index.vue +143 -134
- package/src/components/form/inputs/RichText/utils/commands.ts +69 -69
- package/src/components/form/inputs/RichText/utils/debug.ts +1 -1
- package/src/components/form/inputs/RichText/utils/formatting.ts +39 -39
- package/src/components/form/inputs/RichText/utils/media.ts +6 -6
- package/src/components/form/inputs/RichText/utils/selection.ts +28 -28
- package/src/components/form/inputs/RichText/utils/table.ts +19 -19
- package/src/components/form/inputs/SelectBtn.vue +1 -1
- package/src/components/form/inputs/SelectInput.vue +50 -26
- package/src/components/form/inputs/SignaturePad.vue +15 -15
- package/src/components/form/inputs/TableField.vue +1 -1
- package/src/components/form/inputs/TelInput.vue +6 -6
- package/src/components/form/inputs/TextInput.vue +5 -5
- package/src/components/form/inputs/ToggleInput.vue +2 -1
- package/src/components/form/inputs/Upload/UploadInput.vue +155 -102
- package/src/components/form/inputs/Upload/upload.ts +1 -1
- package/src/components/form/inputs/Upload/useFileUpload.ts +6 -6
- package/src/components/form/useBagelFormState.ts +5 -5
- package/src/components/layout/AppContent.vue +1 -1
- package/src/components/layout/AppLayout.vue +1 -1
- package/src/components/layout/Layout.vue +4 -4
- package/src/components/layout/TabbedLayout.vue +1 -1
- package/src/components/layout/Tabs.vue +2 -2
- package/src/components/layout/TabsNav.vue +7 -7
- package/src/components/lightbox/Lightbox.vue +8 -8
- package/src/components/lightbox/index.ts +8 -8
- package/src/composables/index.ts +2 -2
- package/src/composables/useAddToCalendar.ts +13 -13
- package/src/composables/useDevice.ts +2 -2
- package/src/composables/useExcel.ts +6 -6
- package/src/composables/useFormField.ts +5 -9
- package/src/composables/usePolling.ts +8 -8
- package/src/composables/useSchemaField.ts +53 -38
- package/src/composables/useTheme.ts +9 -9
- package/src/composables/useValidateFieldValue.ts +2 -2
- package/src/directives/pattern.ts +25 -25
- package/src/directives/ripple.ts +4 -4
- package/src/directives/vResize.ts +6 -6
- package/src/plugins/bagel.ts +4 -4
- package/src/plugins/useModal.ts +3 -3
- package/src/styles/layout.css +7 -1
- package/src/utils/BagelFormUtils.ts +7 -7
- package/src/utils/calendar/Helpers.ts +8 -8
- package/src/utils/calendar/dateUtils.ts +22 -22
- package/src/utils/calendar/time.ts +25 -25
- package/src/utils/calendar/week.ts +25 -25
- package/src/utils/elementUtils.ts +27 -27
- package/src/utils/index.ts +22 -22
- package/src/utils/sizeParsing.ts +2 -2
- package/src/utils/strings.ts +5 -5
- package/src/utils/tapDetector.ts +11 -11
- package/src/utils/useSearch.ts +29 -29
- package/vite.config.ts +1 -1
|
@@ -41,6 +41,8 @@ const props = defineProps<{
|
|
|
41
41
|
autofocus?: boolean
|
|
42
42
|
// Text color
|
|
43
43
|
textColor?: string
|
|
44
|
+
// Font size
|
|
45
|
+
fontSize?: number | string
|
|
44
46
|
}>()
|
|
45
47
|
|
|
46
48
|
const emit = defineEmits(['update:modelValue'])
|
|
@@ -59,7 +61,7 @@ const currentInputColor = computed(() => {
|
|
|
59
61
|
// Force reactivity by using forceUpdate
|
|
60
62
|
void forceUpdate.value
|
|
61
63
|
|
|
62
|
-
if (typeof document
|
|
64
|
+
if ('undefined' !== typeof document) {
|
|
63
65
|
const computedStyle = getComputedStyle(document.documentElement)
|
|
64
66
|
const color = computedStyle.getPropertyValue('--input-color').trim()
|
|
65
67
|
console.log('🎨 currentInputColor computed:', color, 'forceUpdate:', forceUpdate.value)
|
|
@@ -70,7 +72,7 @@ const currentInputColor = computed(() => {
|
|
|
70
72
|
|
|
71
73
|
const currentTextColor = computed(() => {
|
|
72
74
|
const inputColor = currentInputColor.value
|
|
73
|
-
const textColor = props.textColor || (inputColor && inputColor.length
|
|
75
|
+
const textColor = props.textColor || (inputColor && 0 < inputColor.length && '#000' !== inputColor ? inputColor : 'inherit')
|
|
74
76
|
console.log('🎨 currentTextColor computed:', textColor, 'from inputColor:', inputColor)
|
|
75
77
|
return textColor
|
|
76
78
|
})
|
|
@@ -79,15 +81,15 @@ const currentTextColor = computed(() => {
|
|
|
79
81
|
let themeObserver: MutationObserver | null = null
|
|
80
82
|
|
|
81
83
|
onMounted(() => {
|
|
82
|
-
if (typeof document
|
|
84
|
+
if ('undefined' !== typeof document) {
|
|
83
85
|
themeObserver = new MutationObserver((mutations) => {
|
|
84
86
|
let shouldUpdate = false
|
|
85
87
|
mutations.forEach((mutation) => {
|
|
86
|
-
if (mutation.type
|
|
88
|
+
if ('attributes' === mutation.type) {
|
|
87
89
|
const { attributeName } = mutation
|
|
88
|
-
if (
|
|
89
|
-
||
|
|
90
|
-
||
|
|
90
|
+
if ('data-theme' === attributeName
|
|
91
|
+
|| 'class' === attributeName
|
|
92
|
+
|| 'style' === attributeName) {
|
|
91
93
|
shouldUpdate = true
|
|
92
94
|
}
|
|
93
95
|
}
|
|
@@ -127,7 +129,7 @@ onBeforeUnmount(() => {
|
|
|
127
129
|
})
|
|
128
130
|
|
|
129
131
|
// Global testing functions
|
|
130
|
-
if (typeof window
|
|
132
|
+
if ('undefined' !== typeof window) {
|
|
131
133
|
(window as any).testRichTextTheme = () => {
|
|
132
134
|
console.log('🧪 Testing RichText theme:', {
|
|
133
135
|
currentInputColor: currentInputColor.value,
|
|
@@ -150,13 +152,13 @@ if (typeof window !== 'undefined') {
|
|
|
150
152
|
|
|
151
153
|
// Computed properties for UI control
|
|
152
154
|
const shouldShowToolbar = computed(() => {
|
|
153
|
-
if (props.hideToolbar) return false
|
|
154
|
-
if (props.basic) return false
|
|
155
|
+
if (props.hideToolbar) {return false}
|
|
156
|
+
if (props.basic) {return false}
|
|
155
157
|
return true
|
|
156
158
|
})
|
|
157
159
|
|
|
158
160
|
const shouldShowInlineToolbar = computed(() => {
|
|
159
|
-
if (props.hideInlineToolbar) return false
|
|
161
|
+
if (props.hideInlineToolbar) {return false}
|
|
160
162
|
return true
|
|
161
163
|
})
|
|
162
164
|
|
|
@@ -335,7 +337,7 @@ const tablePreviewHtml = computed(() => {
|
|
|
335
337
|
border: ${form.borderWidth}px solid ${form.borderColor};
|
|
336
338
|
text-align: ${form.alignment};
|
|
337
339
|
${form.fixedLayout ? `width: ${100 / form.cols}%;` : ''}
|
|
338
|
-
${form.alternateRows && i % 2
|
|
340
|
+
${form.alternateRows && 1 === i % 2
|
|
339
341
|
? `background-color: ${form.alternateRowBgColor}; color: ${form.alternateRowTextColor};`
|
|
340
342
|
: `background-color: ${form.cellBgColor}; color: ${form.cellTextColor};`}
|
|
341
343
|
`
|
|
@@ -397,7 +399,7 @@ function detectAndSetDirection() {
|
|
|
397
399
|
// Function to get current direction based on content
|
|
398
400
|
function getCurrentDirection() {
|
|
399
401
|
const { doc } = editor.state
|
|
400
|
-
if (!doc?.body) return 'ltr'
|
|
402
|
+
if (!doc?.body) {return 'ltr'}
|
|
401
403
|
|
|
402
404
|
const allText = doc.body.textContent || ''
|
|
403
405
|
const firstChars = allText.trim().substring(0, 10)
|
|
@@ -408,7 +410,7 @@ function getCurrentDirection() {
|
|
|
408
410
|
|
|
409
411
|
// Helper function to update content with history tracking
|
|
410
412
|
function updateContentWithHistory(doc: Document, skipHistory = false) {
|
|
411
|
-
if (!doc) return
|
|
413
|
+
if (!doc) {return}
|
|
412
414
|
|
|
413
415
|
const newContent = doc.body.innerHTML
|
|
414
416
|
if (newContent !== editor.state.content) {
|
|
@@ -418,7 +420,7 @@ function updateContentWithHistory(doc: Document, skipHistory = false) {
|
|
|
418
420
|
// Clear redo stack when new content is added
|
|
419
421
|
editor.state.redoStack = []
|
|
420
422
|
// Limit undo stack size to prevent memory issues
|
|
421
|
-
if (editor.state.undoStack.length
|
|
423
|
+
if (50 < editor.state.undoStack.length) {
|
|
422
424
|
editor.state.undoStack.shift()
|
|
423
425
|
}
|
|
424
426
|
}
|
|
@@ -470,10 +472,10 @@ function showInlineToolbarForSelection() {
|
|
|
470
472
|
}
|
|
471
473
|
|
|
472
474
|
const { doc } = editor.state
|
|
473
|
-
if (!doc) return
|
|
475
|
+
if (!doc) {return}
|
|
474
476
|
|
|
475
477
|
const selection = doc.getSelection()
|
|
476
|
-
if (!selection || selection.rangeCount
|
|
478
|
+
if (!selection || 0 === selection.rangeCount) {
|
|
477
479
|
hideInlineToolbar()
|
|
478
480
|
return
|
|
479
481
|
}
|
|
@@ -527,14 +529,14 @@ function hideInlineToolbar() {
|
|
|
527
529
|
|
|
528
530
|
// Function to run inline toolbar action
|
|
529
531
|
function runInlineAction(actionName: string) {
|
|
530
|
-
if (!inlineToolbarSelection.value) return
|
|
532
|
+
if (!inlineToolbarSelection.value) {return}
|
|
531
533
|
|
|
532
534
|
const { doc } = editor.state
|
|
533
|
-
if (!doc) return
|
|
535
|
+
if (!doc) {return}
|
|
534
536
|
|
|
535
537
|
// Get the stored range
|
|
536
538
|
const storedRange = (inlineToolbarSelection.value as any)._storedRange as Range
|
|
537
|
-
if (!storedRange) return
|
|
539
|
+
if (!storedRange) {return}
|
|
538
540
|
|
|
539
541
|
// Restore selection using the stored range
|
|
540
542
|
const selection = doc.getSelection()
|
|
@@ -549,7 +551,7 @@ function runInlineAction(actionName: string) {
|
|
|
549
551
|
// Keep the toolbar visible but update the stored selection
|
|
550
552
|
setTimeout(() => {
|
|
551
553
|
const newSelection = doc.getSelection()
|
|
552
|
-
if (newSelection && newSelection.rangeCount
|
|
554
|
+
if (newSelection && 0 < newSelection.rangeCount) {
|
|
553
555
|
const newRange = newSelection.getRangeAt(0)
|
|
554
556
|
if (!newRange.collapsed) {
|
|
555
557
|
; (inlineToolbarSelection.value as any)._storedRange = newRange.cloneRange()
|
|
@@ -570,7 +572,7 @@ function showTooltipMessage(message: string, x?: number, y?: number) {
|
|
|
570
572
|
// If coordinates not provided, try to get cursor position
|
|
571
573
|
if (x === undefined || y === undefined) {
|
|
572
574
|
const selection = editor.state.doc?.getSelection()
|
|
573
|
-
if (selection && selection.rangeCount
|
|
575
|
+
if (selection && 0 < selection.rangeCount) {
|
|
574
576
|
const range = selection.getRangeAt(0)
|
|
575
577
|
const rect = range.getBoundingClientRect()
|
|
576
578
|
// Get iframe offset
|
|
@@ -606,20 +608,20 @@ function openLinkModal(selection: Selection, range: Range, existingLink: HTMLAnc
|
|
|
606
608
|
pendingLinkData = { selection, range, existingLink }
|
|
607
609
|
linkForm.value = {
|
|
608
610
|
url: existingLink?.href || '',
|
|
609
|
-
openInNewTab: existingLink?.target
|
|
611
|
+
openInNewTab: '_self' !== existingLink?.target
|
|
610
612
|
}
|
|
611
613
|
showLinkModal.value = true
|
|
612
614
|
}
|
|
613
615
|
|
|
614
616
|
// Function to submit link
|
|
615
617
|
function submitLink() {
|
|
616
|
-
if (!pendingLinkData || !linkForm.value.url) return
|
|
618
|
+
if (!pendingLinkData || !linkForm.value.url) {return}
|
|
617
619
|
|
|
618
620
|
const { selection, range, existingLink } = pendingLinkData
|
|
619
621
|
const { url, openInNewTab } = linkForm.value
|
|
620
622
|
const { doc } = editor.state
|
|
621
623
|
|
|
622
|
-
if (!doc) return
|
|
624
|
+
if (!doc) {return}
|
|
623
625
|
|
|
624
626
|
if (existingLink) {
|
|
625
627
|
// Update existing link
|
|
@@ -666,7 +668,7 @@ function submitLink() {
|
|
|
666
668
|
|
|
667
669
|
// Function to visit link
|
|
668
670
|
function visitLink() {
|
|
669
|
-
if (!linkForm.value.url || !isValidUrl(linkForm.value.url)) return
|
|
671
|
+
if (!linkForm.value.url || !isValidUrl(linkForm.value.url)) {return}
|
|
670
672
|
|
|
671
673
|
let url = linkForm.value.url.trim()
|
|
672
674
|
|
|
@@ -684,7 +686,7 @@ function visitLink() {
|
|
|
684
686
|
|
|
685
687
|
// Function to validate URL
|
|
686
688
|
function isValidUrl(url: string): boolean {
|
|
687
|
-
if (!url || url.trim()
|
|
689
|
+
if (!url || '' === url.trim()) {return false}
|
|
688
690
|
|
|
689
691
|
try {
|
|
690
692
|
URL.canParse(url)
|
|
@@ -703,12 +705,12 @@ function isValidUrl(url: string): boolean {
|
|
|
703
705
|
// Function to open image modal
|
|
704
706
|
function openImageModal(existingImage: HTMLElement | null = null) {
|
|
705
707
|
const { doc } = editor.state
|
|
706
|
-
if (!doc) return
|
|
708
|
+
if (!doc) {return}
|
|
707
709
|
|
|
708
710
|
// Get current selection for new images
|
|
709
711
|
if (!existingImage) {
|
|
710
712
|
const selection = doc.getSelection()
|
|
711
|
-
if (!selection || !selection.rangeCount) return
|
|
713
|
+
if (!selection || !selection.rangeCount) {return}
|
|
712
714
|
const range = selection.getRangeAt(0)
|
|
713
715
|
pendingImageData = { selection, range, existingImage: null }
|
|
714
716
|
} else {
|
|
@@ -752,10 +754,10 @@ function openImageModal(existingImage: HTMLElement | null = null) {
|
|
|
752
754
|
|
|
753
755
|
// Function to submit image
|
|
754
756
|
function submitImage() {
|
|
755
|
-
if (!imageForm.value.src || !pendingImageData) return
|
|
757
|
+
if (!imageForm.value.src || !pendingImageData) {return}
|
|
756
758
|
|
|
757
759
|
const { doc } = editor.state
|
|
758
|
-
if (!doc) return
|
|
760
|
+
if (!doc) {return}
|
|
759
761
|
|
|
760
762
|
const { existingImage } = pendingImageData
|
|
761
763
|
|
|
@@ -778,7 +780,7 @@ function submitImage() {
|
|
|
778
780
|
|
|
779
781
|
if (imageForm.value.height) {
|
|
780
782
|
img.setAttribute('data-height', imageForm.value.height)
|
|
781
|
-
if (imageForm.value.height.includes('%') || imageForm.value.height.includes('px') || imageForm.value.height.includes('vh') || imageForm.value.height.includes('rem') || imageForm.value.height.includes('em') || imageForm.value.height
|
|
783
|
+
if (imageForm.value.height.includes('%') || imageForm.value.height.includes('px') || imageForm.value.height.includes('vh') || imageForm.value.height.includes('rem') || imageForm.value.height.includes('em') || 'auto' === imageForm.value.height) {
|
|
782
784
|
img.style.height = imageForm.value.height
|
|
783
785
|
} else {
|
|
784
786
|
img.style.height = `${imageForm.value.height}px`
|
|
@@ -809,7 +811,7 @@ function submitImage() {
|
|
|
809
811
|
// Create or update figure/image - always wrap in figure
|
|
810
812
|
let elementToUpdate: HTMLElement
|
|
811
813
|
if (existingImage) {
|
|
812
|
-
if (existingImage.tagName.toLowerCase()
|
|
814
|
+
if ('img' === existingImage.tagName.toLowerCase()) {
|
|
813
815
|
// Converting standalone image to figure (always)
|
|
814
816
|
const figure = doc.createElement('figure')
|
|
815
817
|
figure.className = 'image-figure'
|
|
@@ -874,12 +876,12 @@ function submitImage() {
|
|
|
874
876
|
// Function to open embed modal
|
|
875
877
|
function openEmbedModal(existingEmbed: HTMLElement | null = null) {
|
|
876
878
|
const { doc } = editor.state
|
|
877
|
-
if (!doc) return
|
|
879
|
+
if (!doc) {return}
|
|
878
880
|
|
|
879
881
|
// Get current selection for new embeds
|
|
880
882
|
if (!existingEmbed) {
|
|
881
883
|
const selection = doc.getSelection()
|
|
882
|
-
if (!selection || !selection.rangeCount) return
|
|
884
|
+
if (!selection || !selection.rangeCount) {return}
|
|
883
885
|
const range = selection.getRangeAt(0)
|
|
884
886
|
pendingEmbedData = { selection, range, existingEmbed: null }
|
|
885
887
|
} else {
|
|
@@ -933,10 +935,10 @@ function extractEmbedUrl(input: string): string {
|
|
|
933
935
|
|
|
934
936
|
// Function to submit embed
|
|
935
937
|
function submitEmbed() {
|
|
936
|
-
if (!embedForm.value.src || !pendingEmbedData) return
|
|
938
|
+
if (!embedForm.value.src || !pendingEmbedData) {return}
|
|
937
939
|
|
|
938
940
|
const { doc } = editor.state
|
|
939
|
-
if (!doc) return
|
|
941
|
+
if (!doc) {return}
|
|
940
942
|
|
|
941
943
|
const { existingEmbed } = pendingEmbedData
|
|
942
944
|
|
|
@@ -1013,12 +1015,12 @@ function submitEmbed() {
|
|
|
1013
1015
|
// Function to open video modal
|
|
1014
1016
|
function openVideoModal(existingVideo: HTMLElement | null = null) {
|
|
1015
1017
|
const { doc } = editor.state
|
|
1016
|
-
if (!doc) return
|
|
1018
|
+
if (!doc) {return}
|
|
1017
1019
|
|
|
1018
1020
|
// Get current selection for new videos
|
|
1019
1021
|
if (!existingVideo) {
|
|
1020
1022
|
const selection = doc.getSelection()
|
|
1021
|
-
if (!selection || !selection.rangeCount) return
|
|
1023
|
+
if (!selection || !selection.rangeCount) {return}
|
|
1022
1024
|
const range = selection.getRangeAt(0)
|
|
1023
1025
|
pendingVideoData = { selection, range, existingVideo: null }
|
|
1024
1026
|
} else {
|
|
@@ -1033,14 +1035,14 @@ function openVideoModal(existingVideo: HTMLElement | null = null) {
|
|
|
1033
1035
|
if (existingVideo) {
|
|
1034
1036
|
// Populate form with existing video data
|
|
1035
1037
|
const container = existingVideo.querySelector('.video-container')
|
|
1036
|
-
const isCustom = container?.getAttribute('data-custom-aspect-ratio')
|
|
1038
|
+
const isCustom = 'true' === container?.getAttribute('data-custom-aspect-ratio')
|
|
1037
1039
|
videoForm.value = {
|
|
1038
1040
|
src: container?.getAttribute('data-video-src') || '',
|
|
1039
1041
|
width: container?.getAttribute('data-width') || '',
|
|
1040
|
-
autoplay: container?.getAttribute('data-autoplay')
|
|
1041
|
-
mute: container?.getAttribute('data-mute')
|
|
1042
|
-
controls: container?.getAttribute('data-controls')
|
|
1043
|
-
loop: container?.getAttribute('data-loop')
|
|
1042
|
+
autoplay: 'true' === container?.getAttribute('data-autoplay'),
|
|
1043
|
+
mute: 'true' === container?.getAttribute('data-mute'),
|
|
1044
|
+
controls: 'true' === container?.getAttribute('data-controls'),
|
|
1045
|
+
loop: 'true' === container?.getAttribute('data-loop'),
|
|
1044
1046
|
aspectRatio: isCustom ? 'custom' : (container?.getAttribute('data-aspect-ratio') || '16:9'),
|
|
1045
1047
|
customWidth: container?.getAttribute('data-custom-width') || '',
|
|
1046
1048
|
customHeight: container?.getAttribute('data-custom-height') || '',
|
|
@@ -1069,7 +1071,7 @@ function openVideoModal(existingVideo: HTMLElement | null = null) {
|
|
|
1069
1071
|
|
|
1070
1072
|
// Function to validate video URL
|
|
1071
1073
|
function isValidVideoUrl(url: string): boolean {
|
|
1072
|
-
if (!url || url.trim()
|
|
1074
|
+
if (!url || '' === url.trim()) {return false}
|
|
1073
1075
|
|
|
1074
1076
|
try {
|
|
1075
1077
|
// Check for iframe tags
|
|
@@ -1104,23 +1106,23 @@ function isValidVideoUrl(url: string): boolean {
|
|
|
1104
1106
|
}
|
|
1105
1107
|
}
|
|
1106
1108
|
function submitVideo() {
|
|
1107
|
-
if (!videoForm.value.src || !pendingVideoData) return
|
|
1109
|
+
if (!videoForm.value.src || !pendingVideoData) {return}
|
|
1108
1110
|
|
|
1109
1111
|
const { doc } = editor.state
|
|
1110
|
-
if (!doc) return
|
|
1112
|
+
if (!doc) {return}
|
|
1111
1113
|
|
|
1112
1114
|
const { existingVideo } = pendingVideoData
|
|
1113
1115
|
|
|
1114
1116
|
// Calculate aspect ratio
|
|
1115
1117
|
let { aspectRatio } = videoForm.value
|
|
1116
|
-
const isCustom = videoForm.value.aspectRatio
|
|
1118
|
+
const isCustom = 'custom' === videoForm.value.aspectRatio
|
|
1117
1119
|
if (isCustom && videoForm.value.customWidth && videoForm.value.customHeight) {
|
|
1118
1120
|
aspectRatio = `${videoForm.value.customWidth}:${videoForm.value.customHeight}`
|
|
1119
1121
|
}
|
|
1120
1122
|
|
|
1121
1123
|
// Auto-adjust aspect ratio for YouTube Shorts
|
|
1122
1124
|
const isYoutubeShort = videoForm.value.src.includes('youtube.com/shorts/')
|
|
1123
|
-
if (isYoutubeShort &&
|
|
1125
|
+
if (isYoutubeShort && '16:9' === aspectRatio) {
|
|
1124
1126
|
aspectRatio = '9:16'
|
|
1125
1127
|
}
|
|
1126
1128
|
|
|
@@ -1168,8 +1170,8 @@ function submitVideo() {
|
|
|
1168
1170
|
const description = doc.createElement('div')
|
|
1169
1171
|
description.className = 'video-placeholder-description'
|
|
1170
1172
|
let platformText = 'video'
|
|
1171
|
-
if (isYoutube) platformText = 'YouTube video'
|
|
1172
|
-
else if (isVimeo) platformText = 'Vimeo video'
|
|
1173
|
+
if (isYoutube) {platformText = 'YouTube video'}
|
|
1174
|
+
else if (isVimeo) {platformText = 'Vimeo video'}
|
|
1173
1175
|
description.textContent = platformText
|
|
1174
1176
|
|
|
1175
1177
|
// Try to get YouTube thumbnail if possible
|
|
@@ -1258,7 +1260,7 @@ function submitVideo() {
|
|
|
1258
1260
|
|
|
1259
1261
|
// Function to delete video
|
|
1260
1262
|
function deleteVideo() {
|
|
1261
|
-
if (!pendingVideoData?.existingVideo) return
|
|
1263
|
+
if (!pendingVideoData?.existingVideo) {return}
|
|
1262
1264
|
|
|
1263
1265
|
const { existingVideo } = pendingVideoData
|
|
1264
1266
|
existingVideo.remove()
|
|
@@ -1277,9 +1279,9 @@ function detectTableAlignment(table: HTMLTableElement): 'left' | 'center' | 'rig
|
|
|
1277
1279
|
|
|
1278
1280
|
console.log('Table margins:', { marginLeft, marginRight })
|
|
1279
1281
|
|
|
1280
|
-
if (
|
|
1282
|
+
if ('auto' === marginLeft && 'auto' === marginRight) {
|
|
1281
1283
|
return 'center'
|
|
1282
|
-
}
|
|
1284
|
+
}if (marginLeft === 'auto' && marginRight === '0') {
|
|
1283
1285
|
return 'right'
|
|
1284
1286
|
} else {
|
|
1285
1287
|
return 'left'
|
|
@@ -1288,12 +1290,12 @@ function detectTableAlignment(table: HTMLTableElement): 'left' | 'center' | 'rig
|
|
|
1288
1290
|
|
|
1289
1291
|
function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
1290
1292
|
const { doc } = editor.state
|
|
1291
|
-
if (!doc) return
|
|
1293
|
+
if (!doc) {return}
|
|
1292
1294
|
|
|
1293
1295
|
// Get current selection for new tables
|
|
1294
1296
|
if (!existingTable) {
|
|
1295
1297
|
const selection = doc.getSelection()
|
|
1296
|
-
if (!selection || !selection.rangeCount) return
|
|
1298
|
+
if (!selection || !selection.rangeCount) {return}
|
|
1297
1299
|
const range = selection.getRangeAt(0)
|
|
1298
1300
|
pendingTableData = { selection, range, existingTable: null }
|
|
1299
1301
|
} else {
|
|
@@ -1315,11 +1317,11 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1315
1317
|
let alternateRowBg = '#f9f9f9'
|
|
1316
1318
|
let alternateRowTextColor = '#333333'
|
|
1317
1319
|
|
|
1318
|
-
if (tbody && tbody.rows.length
|
|
1320
|
+
if (tbody && 2 <= tbody.rows.length) {
|
|
1319
1321
|
const firstRow = tbody.rows[0]
|
|
1320
1322
|
const secondRow = tbody.rows[1]
|
|
1321
1323
|
|
|
1322
|
-
if (firstRow.cells.length
|
|
1324
|
+
if (0 < firstRow.cells.length && 0 < secondRow.cells.length) {
|
|
1323
1325
|
const firstCell = firstRow.cells[0] as HTMLElement
|
|
1324
1326
|
const secondCell = secondRow.cells[0] as HTMLElement
|
|
1325
1327
|
|
|
@@ -1337,7 +1339,7 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1337
1339
|
|
|
1338
1340
|
tableForm.value = {
|
|
1339
1341
|
rows: tbody ? tbody.rows.length : 3,
|
|
1340
|
-
cols: tbody && tbody.rows.length
|
|
1342
|
+
cols: tbody && 0 < tbody.rows.length ? tbody.rows[0].cells.length : 3,
|
|
1341
1343
|
width: Number.parseInt(existingTable.style.width) || 100,
|
|
1342
1344
|
borderWidth: Number.parseInt(existingTable.style.borderWidth) || 1,
|
|
1343
1345
|
borderColor: existingTable.style.borderColor || '#dddddd',
|
|
@@ -1350,7 +1352,7 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1350
1352
|
alternateRows: hasAlternatingRows,
|
|
1351
1353
|
alternateRowBgColor: alternateRowBg,
|
|
1352
1354
|
alternateRowTextColor,
|
|
1353
|
-
fixedLayout: existingTable.style.tableLayout
|
|
1355
|
+
fixedLayout: 'fixed' === existingTable.style.tableLayout || true, // ברירת מחדל true
|
|
1354
1356
|
alignment: detectTableAlignment(existingTable),
|
|
1355
1357
|
direction: existingTable.dir || 'ltr'
|
|
1356
1358
|
}
|
|
@@ -1380,10 +1382,10 @@ function openTableEditor(existingTable: HTMLTableElement | null = null) {
|
|
|
1380
1382
|
|
|
1381
1383
|
// Function to submit table changes
|
|
1382
1384
|
function submitTable() {
|
|
1383
|
-
if (!pendingTableData) return
|
|
1385
|
+
if (!pendingTableData) {return}
|
|
1384
1386
|
|
|
1385
1387
|
const { doc } = editor.state
|
|
1386
|
-
if (!doc) return
|
|
1388
|
+
if (!doc) {return}
|
|
1387
1389
|
|
|
1388
1390
|
if (pendingTableData.existingTable) {
|
|
1389
1391
|
// Update existing table instead of creating new one
|
|
@@ -1443,7 +1445,7 @@ function submitTable() {
|
|
|
1443
1445
|
const cells = row.querySelectorAll('td')
|
|
1444
1446
|
cells.forEach((cell) => {
|
|
1445
1447
|
const cellEl = cell as HTMLElement
|
|
1446
|
-
if (tableForm.value.alternateRows && i % 2
|
|
1448
|
+
if (tableForm.value.alternateRows && 1 === i % 2) {
|
|
1447
1449
|
// Alternate rows: use special colors
|
|
1448
1450
|
cellEl.style.backgroundColor = tableForm.value.alternateRowBgColor
|
|
1449
1451
|
cellEl.style.color = tableForm.value.alternateRowTextColor
|
|
@@ -1468,7 +1470,7 @@ function submitTable() {
|
|
|
1468
1470
|
|
|
1469
1471
|
// Function to create new table (moved from submitTable)
|
|
1470
1472
|
function createNewTable(doc: Document) {
|
|
1471
|
-
if (!pendingTableData) return
|
|
1473
|
+
if (!pendingTableData) {return}
|
|
1472
1474
|
|
|
1473
1475
|
// Create new table with current settings
|
|
1474
1476
|
const table = doc.createElement('table')
|
|
@@ -1488,10 +1490,10 @@ function createNewTable(doc: Document) {
|
|
|
1488
1490
|
// Don't set textAlign on table - let it inherit from document direction
|
|
1489
1491
|
// table.style.textAlign = tableForm.value.alignment
|
|
1490
1492
|
|
|
1491
|
-
if (tableForm.value.alignment
|
|
1493
|
+
if ('center' === tableForm.value.alignment) {
|
|
1492
1494
|
table.style.marginLeft = 'auto'
|
|
1493
1495
|
table.style.marginRight = 'auto'
|
|
1494
|
-
} else if (tableForm.value.alignment
|
|
1496
|
+
} else if ('right' === tableForm.value.alignment) {
|
|
1495
1497
|
table.style.marginLeft = 'auto'
|
|
1496
1498
|
table.style.marginRight = '0'
|
|
1497
1499
|
} else {
|
|
@@ -1544,7 +1546,7 @@ function createNewTable(doc: Document) {
|
|
|
1544
1546
|
}
|
|
1545
1547
|
|
|
1546
1548
|
// Apply background and text colors
|
|
1547
|
-
if (tableForm.value.alternateRows && i % 2
|
|
1549
|
+
if (tableForm.value.alternateRows && 1 === i % 2) {
|
|
1548
1550
|
// Alternate rows: use special colors
|
|
1549
1551
|
cell.style.backgroundColor = tableForm.value.alternateRowBgColor
|
|
1550
1552
|
cell.style.color = tableForm.value.alternateRowTextColor
|
|
@@ -1588,7 +1590,7 @@ function createNewTable(doc: Document) {
|
|
|
1588
1590
|
|
|
1589
1591
|
// Function to delete table
|
|
1590
1592
|
function deleteTable() {
|
|
1591
|
-
if (!pendingTableData?.existingTable) return
|
|
1593
|
+
if (!pendingTableData?.existingTable) {return}
|
|
1592
1594
|
|
|
1593
1595
|
const { existingTable } = pendingTableData
|
|
1594
1596
|
existingTable.remove()
|
|
@@ -1617,7 +1619,7 @@ function applyDefaultTableSettings(table: HTMLTableElement) {
|
|
|
1617
1619
|
const headers = table.querySelectorAll('th')
|
|
1618
1620
|
const cells = table.querySelectorAll('td')
|
|
1619
1621
|
|
|
1620
|
-
if (headers.length
|
|
1622
|
+
if (0 < headers.length) {
|
|
1621
1623
|
const colWidth = `${100 / headers.length}%`
|
|
1622
1624
|
headers.forEach((th) => {
|
|
1623
1625
|
if (!(th as HTMLElement).style.width) {
|
|
@@ -1626,7 +1628,7 @@ function applyDefaultTableSettings(table: HTMLTableElement) {
|
|
|
1626
1628
|
})
|
|
1627
1629
|
}
|
|
1628
1630
|
|
|
1629
|
-
if (cells.length
|
|
1631
|
+
if (0 < cells.length && 0 === headers.length) {
|
|
1630
1632
|
// If no headers, use first row to determine column count
|
|
1631
1633
|
const firstRow = table.querySelector('tr')
|
|
1632
1634
|
if (firstRow) {
|
|
@@ -1656,7 +1658,7 @@ function applyDefaultTableSettings(table: HTMLTableElement) {
|
|
|
1656
1658
|
|
|
1657
1659
|
// Function to delete image
|
|
1658
1660
|
function deleteImage() {
|
|
1659
|
-
if (!pendingImageData?.existingImage) return
|
|
1661
|
+
if (!pendingImageData?.existingImage) {return}
|
|
1660
1662
|
|
|
1661
1663
|
const { existingImage } = pendingImageData
|
|
1662
1664
|
existingImage.remove()
|
|
@@ -1668,7 +1670,7 @@ function deleteImage() {
|
|
|
1668
1670
|
|
|
1669
1671
|
// Function to delete embed
|
|
1670
1672
|
function deleteEmbed() {
|
|
1671
|
-
if (!pendingEmbedData?.existingEmbed) return
|
|
1673
|
+
if (!pendingEmbedData?.existingEmbed) {return}
|
|
1672
1674
|
|
|
1673
1675
|
const { existingEmbed } = pendingEmbedData
|
|
1674
1676
|
existingEmbed.remove()
|
|
@@ -1691,19 +1693,19 @@ function deleteEmbed() {
|
|
|
1691
1693
|
// Table manipulation functions
|
|
1692
1694
|
function mergeCellRight() {
|
|
1693
1695
|
const { doc } = editor.state
|
|
1694
|
-
if (!doc || !contextMenuCell.value) return
|
|
1696
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1695
1697
|
|
|
1696
1698
|
const cell = contextMenuCell.value
|
|
1697
1699
|
const row = cell.parentElement as HTMLTableRowElement
|
|
1698
1700
|
const cellIndex = Array.from(row.cells).indexOf(cell)
|
|
1699
1701
|
const table = cell.closest('table') as HTMLTableElement
|
|
1700
|
-
const isRTL = table?.dir
|
|
1702
|
+
const isRTL = 'rtl' === table?.dir
|
|
1701
1703
|
|
|
1702
1704
|
// In RTL, "right" means the previous cell, in LTR it means the next cell
|
|
1703
1705
|
const targetIndex = isRTL ? cellIndex - 1 : cellIndex + 1
|
|
1704
1706
|
const targetCell = row.cells[targetIndex]
|
|
1705
1707
|
|
|
1706
|
-
if (targetCell &&
|
|
1708
|
+
if (targetCell && 0 <= targetIndex && targetIndex < row.cells.length) {
|
|
1707
1709
|
// Combine content
|
|
1708
1710
|
if (targetCell.innerHTML.trim()) {
|
|
1709
1711
|
cell.innerHTML += ` ${targetCell.innerHTML}`
|
|
@@ -1723,7 +1725,7 @@ function mergeCellRight() {
|
|
|
1723
1725
|
|
|
1724
1726
|
function mergeCellDown() {
|
|
1725
1727
|
const { doc } = editor.state
|
|
1726
|
-
if (!doc || !contextMenuCell.value) return
|
|
1728
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1727
1729
|
|
|
1728
1730
|
const cell = contextMenuCell.value
|
|
1729
1731
|
const table = cell.closest('table') as HTMLTableElement
|
|
@@ -1759,15 +1761,15 @@ function mergeCellDown() {
|
|
|
1759
1761
|
|
|
1760
1762
|
function splitCell() {
|
|
1761
1763
|
const { doc } = editor.state
|
|
1762
|
-
if (!doc || !contextMenuCell.value) return
|
|
1764
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1763
1765
|
|
|
1764
1766
|
const cell = contextMenuCell.value
|
|
1765
1767
|
const table = cell.closest('table') as HTMLTableElement
|
|
1766
|
-
const isRTL = table?.dir
|
|
1768
|
+
const isRTL = 'rtl' === table?.dir
|
|
1767
1769
|
const colspan = Number.parseInt(cell.getAttribute('colspan') || '1')
|
|
1768
1770
|
const rowspan = Number.parseInt(cell.getAttribute('rowspan') || '1')
|
|
1769
1771
|
|
|
1770
|
-
if (
|
|
1772
|
+
if (1 < colspan) {
|
|
1771
1773
|
// Split horizontally
|
|
1772
1774
|
cell.setAttribute('colspan', '1')
|
|
1773
1775
|
const row = cell.parentElement as HTMLTableRowElement
|
|
@@ -1788,7 +1790,7 @@ function splitCell() {
|
|
|
1788
1790
|
}
|
|
1789
1791
|
}
|
|
1790
1792
|
}
|
|
1791
|
-
} else if (
|
|
1793
|
+
} else if (1 < rowspan) {
|
|
1792
1794
|
// Split vertically
|
|
1793
1795
|
cell.setAttribute('rowspan', '1')
|
|
1794
1796
|
const row = cell.parentElement as HTMLTableRowElement
|
|
@@ -1820,7 +1822,7 @@ function splitCell() {
|
|
|
1820
1822
|
|
|
1821
1823
|
function insertRowAbove() {
|
|
1822
1824
|
const { doc } = editor.state
|
|
1823
|
-
if (!doc || !contextMenuCell.value) return
|
|
1825
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1824
1826
|
|
|
1825
1827
|
const cell = contextMenuCell.value
|
|
1826
1828
|
const row = cell.parentElement as HTMLTableRowElement
|
|
@@ -1847,7 +1849,7 @@ function insertRowAbove() {
|
|
|
1847
1849
|
|
|
1848
1850
|
function insertRowBelow() {
|
|
1849
1851
|
const { doc } = editor.state
|
|
1850
|
-
if (!doc || !contextMenuCell.value) return
|
|
1852
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1851
1853
|
|
|
1852
1854
|
const cell = contextMenuCell.value
|
|
1853
1855
|
const row = cell.parentElement as HTMLTableRowElement
|
|
@@ -1874,7 +1876,7 @@ function insertRowBelow() {
|
|
|
1874
1876
|
|
|
1875
1877
|
function deleteRow() {
|
|
1876
1878
|
const { doc } = editor.state
|
|
1877
|
-
if (!doc || !contextMenuCell.value) return
|
|
1879
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1878
1880
|
|
|
1879
1881
|
const cell = contextMenuCell.value
|
|
1880
1882
|
const row = cell.parentElement as HTMLTableRowElement
|
|
@@ -1884,7 +1886,7 @@ function deleteRow() {
|
|
|
1884
1886
|
const tbody = table.querySelector('tbody') || table
|
|
1885
1887
|
const rows = tbody.querySelectorAll('tr')
|
|
1886
1888
|
|
|
1887
|
-
if (rows.length
|
|
1889
|
+
if (1 >= rows.length) {
|
|
1888
1890
|
alert('Cannot delete the last row')
|
|
1889
1891
|
return
|
|
1890
1892
|
}
|
|
@@ -1895,7 +1897,7 @@ function deleteRow() {
|
|
|
1895
1897
|
|
|
1896
1898
|
function insertColumnLeft() {
|
|
1897
1899
|
const { doc } = editor.state
|
|
1898
|
-
if (!doc || !contextMenuCell.value) return
|
|
1900
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1899
1901
|
|
|
1900
1902
|
const cell = contextMenuCell.value
|
|
1901
1903
|
const table = cell.closest('table') as HTMLTableElement
|
|
@@ -1904,7 +1906,7 @@ function insertColumnLeft() {
|
|
|
1904
1906
|
// Add new cell to each row at the same index
|
|
1905
1907
|
const rows = table.querySelectorAll('tr')
|
|
1906
1908
|
rows.forEach((row) => {
|
|
1907
|
-
const newCell = doc.createElement(row.parentElement?.tagName
|
|
1909
|
+
const newCell = doc.createElement('THEAD' === row.parentElement?.tagName ? 'th' : 'td')
|
|
1908
1910
|
newCell.innerHTML = ' '
|
|
1909
1911
|
newCell.style.padding = '8px'
|
|
1910
1912
|
newCell.style.border = '1px solid #ddd'
|
|
@@ -1921,7 +1923,7 @@ function insertColumnLeft() {
|
|
|
1921
1923
|
|
|
1922
1924
|
function insertColumnRight() {
|
|
1923
1925
|
const { doc } = editor.state
|
|
1924
|
-
if (!doc || !contextMenuCell.value) return
|
|
1926
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1925
1927
|
|
|
1926
1928
|
const cell = contextMenuCell.value
|
|
1927
1929
|
const table = cell.closest('table') as HTMLTableElement
|
|
@@ -1930,7 +1932,7 @@ function insertColumnRight() {
|
|
|
1930
1932
|
// Add new cell to each row after the current index
|
|
1931
1933
|
const rows = table.querySelectorAll('tr')
|
|
1932
1934
|
rows.forEach((row) => {
|
|
1933
|
-
const newCell = doc.createElement(row.parentElement?.tagName
|
|
1935
|
+
const newCell = doc.createElement('THEAD' === row.parentElement?.tagName ? 'th' : 'td')
|
|
1934
1936
|
newCell.innerHTML = ' '
|
|
1935
1937
|
newCell.style.padding = '8px'
|
|
1936
1938
|
newCell.style.border = '1px solid #ddd'
|
|
@@ -1948,7 +1950,7 @@ function insertColumnRight() {
|
|
|
1948
1950
|
|
|
1949
1951
|
function deleteColumn() {
|
|
1950
1952
|
const { doc } = editor.state
|
|
1951
|
-
if (!doc || !contextMenuCell.value) return
|
|
1953
|
+
if (!doc || !contextMenuCell.value) {return}
|
|
1952
1954
|
|
|
1953
1955
|
const cell = contextMenuCell.value
|
|
1954
1956
|
const table = cell.closest('table') as HTMLTableElement
|
|
@@ -1956,7 +1958,7 @@ function deleteColumn() {
|
|
|
1956
1958
|
|
|
1957
1959
|
// Check if this is the only column
|
|
1958
1960
|
const firstRow = table.querySelector('tr')
|
|
1959
|
-
if (firstRow && firstRow.cells.length
|
|
1961
|
+
if (firstRow && 1 >= firstRow.cells.length) {
|
|
1960
1962
|
alert('Cannot delete the last column')
|
|
1961
1963
|
return
|
|
1962
1964
|
}
|
|
@@ -1974,18 +1976,18 @@ function deleteColumn() {
|
|
|
1974
1976
|
|
|
1975
1977
|
// Table context menu functions
|
|
1976
1978
|
const canMergeRight = computed(() => {
|
|
1977
|
-
if (!contextMenuCell.value) return false
|
|
1979
|
+
if (!contextMenuCell.value) {return false}
|
|
1978
1980
|
const cell = contextMenuCell.value
|
|
1979
1981
|
const row = cell.parentElement as HTMLTableRowElement
|
|
1980
1982
|
const cellIndex = Array.from(row.cells).indexOf(cell)
|
|
1981
1983
|
const table = cell.closest('table') as HTMLTableElement
|
|
1982
|
-
const isRTL = table?.dir
|
|
1984
|
+
const isRTL = 'rtl' === table?.dir
|
|
1983
1985
|
const targetIndex = isRTL ? cellIndex - 1 : cellIndex + 1
|
|
1984
|
-
return
|
|
1986
|
+
return 0 <= targetIndex && targetIndex < row.cells.length
|
|
1985
1987
|
})
|
|
1986
1988
|
|
|
1987
1989
|
const canMergeDown = computed(() => {
|
|
1988
|
-
if (!contextMenuCell.value) return false
|
|
1990
|
+
if (!contextMenuCell.value) {return false}
|
|
1989
1991
|
const cell = contextMenuCell.value
|
|
1990
1992
|
const table = cell.closest('table') as HTMLTableElement
|
|
1991
1993
|
const rows = Array.from(table.rows)
|
|
@@ -1995,11 +1997,11 @@ const canMergeDown = computed(() => {
|
|
|
1995
1997
|
})
|
|
1996
1998
|
|
|
1997
1999
|
const canSplit = computed(() => {
|
|
1998
|
-
if (!contextMenuCell.value) return false
|
|
2000
|
+
if (!contextMenuCell.value) {return false}
|
|
1999
2001
|
const cell = contextMenuCell.value
|
|
2000
2002
|
const colspan = Number.parseInt(cell.getAttribute('colspan') || '1')
|
|
2001
2003
|
const rowspan = Number.parseInt(cell.getAttribute('rowspan') || '1')
|
|
2002
|
-
return colspan
|
|
2004
|
+
return 1 < colspan || 1 < rowspan
|
|
2003
2005
|
})
|
|
2004
2006
|
|
|
2005
2007
|
function handleTableContextMenu(event: MouseEvent) {
|
|
@@ -2031,9 +2033,9 @@ const hasRTL = $computed(() => /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.test(
|
|
|
2031
2033
|
|
|
2032
2034
|
// Computed property to handle height prop
|
|
2033
2035
|
const editorHeight = $computed(() => {
|
|
2034
|
-
if (typeof props.height
|
|
2036
|
+
if ('number' === typeof props.height) {
|
|
2035
2037
|
return `${props.height}px`
|
|
2036
|
-
}
|
|
2038
|
+
}if (typeof props.height === 'string') {
|
|
2037
2039
|
return props.height
|
|
2038
2040
|
}
|
|
2039
2041
|
return '240px' // default height
|
|
@@ -2101,16 +2103,16 @@ function setupTableEditButtons(doc: Document) {
|
|
|
2101
2103
|
if (table.style.marginLeft && table.style.marginRight) {
|
|
2102
2104
|
// Detect current alignment
|
|
2103
2105
|
let alignment = 'left'
|
|
2104
|
-
if (table.style.marginLeft
|
|
2106
|
+
if ('auto' === table.style.marginLeft && 'auto' === table.style.marginRight) {
|
|
2105
2107
|
alignment = 'center'
|
|
2106
|
-
} else if (table.style.marginLeft
|
|
2108
|
+
} else if ('auto' === table.style.marginLeft) {
|
|
2107
2109
|
alignment = 'right'
|
|
2108
2110
|
}
|
|
2109
2111
|
|
|
2110
2112
|
// Apply flex alignment to wrapper
|
|
2111
|
-
if (
|
|
2113
|
+
if ('center' === alignment) {
|
|
2112
2114
|
wrapper.style.alignItems = 'center'
|
|
2113
|
-
} else if (
|
|
2115
|
+
} else if ('right' === alignment) {
|
|
2114
2116
|
wrapper.style.alignItems = 'flex-end'
|
|
2115
2117
|
} else {
|
|
2116
2118
|
wrapper.style.alignItems = 'flex-start'
|
|
@@ -2188,14 +2190,14 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2188
2190
|
}
|
|
2189
2191
|
|
|
2190
2192
|
// If body is completely empty, add a paragraph and return
|
|
2191
|
-
if (!doc.body.innerHTML.trim() || doc.body.innerHTML
|
|
2193
|
+
if (!doc.body.innerHTML.trim() || '' === doc.body.innerHTML) {
|
|
2192
2194
|
const direction = getCurrentDirection()
|
|
2193
2195
|
doc.body.innerHTML = `<p dir="${direction}"><br></p>`
|
|
2194
2196
|
return
|
|
2195
2197
|
}
|
|
2196
2198
|
|
|
2197
2199
|
// Mark body as being normalized to prevent recursive processing
|
|
2198
|
-
if (doc.body.dataset.normalizing
|
|
2200
|
+
if ('true' === doc.body.dataset.normalizing) {
|
|
2199
2201
|
return
|
|
2200
2202
|
}
|
|
2201
2203
|
doc.body.dataset.normalizing = 'true'
|
|
@@ -2211,7 +2213,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2211
2213
|
const parent = node.parentNode as HTMLElement
|
|
2212
2214
|
return (parent === doc.body
|
|
2213
2215
|
&& node.textContent?.trim()
|
|
2214
|
-
&& node.textContent.trim().length
|
|
2216
|
+
&& 0 < node.textContent.trim().length)
|
|
2215
2217
|
? NodeFilter.FILTER_ACCEPT
|
|
2216
2218
|
: NodeFilter.FILTER_REJECT
|
|
2217
2219
|
}
|
|
@@ -2260,13 +2262,13 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2260
2262
|
const paras = Array.from(doc.body.querySelectorAll('p'))
|
|
2261
2263
|
for (let i = 0; i < paras.length; i++) {
|
|
2262
2264
|
const current = paras[i]
|
|
2263
|
-
const isEmpty = !current.textContent?.trim() ||
|
|
2265
|
+
const isEmpty = !current.textContent?.trim() || '<br>' === current.innerHTML
|
|
2264
2266
|
|
|
2265
2267
|
if (isEmpty) {
|
|
2266
2268
|
// Only remove if we have consecutive empty paragraphs at the end
|
|
2267
2269
|
const isLastPara = i === paras.length - 1
|
|
2268
|
-
const prevPara =
|
|
2269
|
-
const isPrevEmpty = prevPara && (!prevPara.textContent?.trim() ||
|
|
2270
|
+
const prevPara = 0 < i ? paras[i - 1] : null
|
|
2271
|
+
const isPrevEmpty = prevPara && (!prevPara.textContent?.trim() || '<br>' === prevPara.innerHTML)
|
|
2270
2272
|
|
|
2271
2273
|
// Keep at least one empty paragraph for cursor placement
|
|
2272
2274
|
if (isPrevEmpty && !isLastPara) {
|
|
@@ -2289,7 +2291,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2289
2291
|
detectAndSetDirection()
|
|
2290
2292
|
|
|
2291
2293
|
// Handle complete content deletion immediately
|
|
2292
|
-
if (!doc.body.innerHTML.trim() || doc.body.innerHTML
|
|
2294
|
+
if (!doc.body.innerHTML.trim() || '' === doc.body.innerHTML) {
|
|
2293
2295
|
if (props.placeholder) {
|
|
2294
2296
|
addPlaceholder()
|
|
2295
2297
|
} else {
|
|
@@ -2316,9 +2318,9 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2316
2318
|
|
|
2317
2319
|
// Handle Enter key
|
|
2318
2320
|
doc.addEventListener('keydown', (e) => {
|
|
2319
|
-
if (e.key
|
|
2321
|
+
if ('Enter' === e.key) {
|
|
2320
2322
|
const selection = doc.getSelection()
|
|
2321
|
-
if (!selection || !selection.rangeCount) return
|
|
2323
|
+
if (!selection || !selection.rangeCount) {return}
|
|
2322
2324
|
|
|
2323
2325
|
const range = selection.getRangeAt(0)
|
|
2324
2326
|
|
|
@@ -2344,13 +2346,13 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2344
2346
|
}
|
|
2345
2347
|
paragraph = (paragraph as Element).closest('p,h1,h2,h3,h4,h5,h6') as HTMLElement
|
|
2346
2348
|
|
|
2347
|
-
if (!paragraph) return
|
|
2349
|
+
if (!paragraph) {return}
|
|
2348
2350
|
|
|
2349
2351
|
// Check if current paragraph is empty and previous paragraph is also empty
|
|
2350
2352
|
const currentIsEmpty = !paragraph.textContent?.trim()
|
|
2351
2353
|
const prevSibling = (paragraph as Element).previousElementSibling as HTMLElement
|
|
2352
2354
|
const prevIsEmpty = prevSibling
|
|
2353
|
-
&& (prevSibling.tagName
|
|
2355
|
+
&& ('P' === prevSibling.tagName)
|
|
2354
2356
|
&& !prevSibling.textContent?.trim()
|
|
2355
2357
|
|
|
2356
2358
|
// If both current and previous paragraphs are empty, don't create another empty paragraph
|
|
@@ -2440,7 +2442,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2440
2442
|
}) // Add a MutationObserver to catch structural changes that might create loose text
|
|
2441
2443
|
const observer = new MutationObserver((mutations) => {
|
|
2442
2444
|
for (const mutation of mutations) {
|
|
2443
|
-
if (mutation.type
|
|
2445
|
+
if ('childList' === mutation.type) {
|
|
2444
2446
|
// Check if any loose text nodes were added to body
|
|
2445
2447
|
Array.from(mutation.addedNodes).forEach((addedNode) => {
|
|
2446
2448
|
if (addedNode.nodeType === Node.TEXT_NODE
|
|
@@ -2456,7 +2458,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2456
2458
|
// Check if a table was added
|
|
2457
2459
|
else if (addedNode.nodeType === Node.ELEMENT_NODE) {
|
|
2458
2460
|
const element = addedNode as HTMLElement
|
|
2459
|
-
if (element.tagName
|
|
2461
|
+
if ('TABLE' === element.tagName) {
|
|
2460
2462
|
console.log('MutationObserver detected new table:', element)
|
|
2461
2463
|
setTimeout(() => {
|
|
2462
2464
|
console.log('Applying default table settings...')
|
|
@@ -2474,7 +2476,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2474
2476
|
}
|
|
2475
2477
|
|
|
2476
2478
|
// Also apply direction to list items if it's a list
|
|
2477
|
-
if (
|
|
2479
|
+
if ('UL' === element.tagName || 'OL' === element.tagName) {
|
|
2478
2480
|
const listItems = element.querySelectorAll('li')
|
|
2479
2481
|
listItems.forEach((li) => {
|
|
2480
2482
|
if (!li.dir) {
|
|
@@ -2541,7 +2543,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2541
2543
|
|
|
2542
2544
|
// Get current selection
|
|
2543
2545
|
const selection = doc.getSelection()
|
|
2544
|
-
if (!selection) return
|
|
2546
|
+
if (!selection) {return}
|
|
2545
2547
|
|
|
2546
2548
|
// Create a range that selects the entire link
|
|
2547
2549
|
const range = doc.createRange()
|
|
@@ -2551,7 +2553,7 @@ function setupAutoWrapping(doc: Document) {
|
|
|
2551
2553
|
|
|
2552
2554
|
// Populate the form with existing link data
|
|
2553
2555
|
linkForm.value.url = link.href
|
|
2554
|
-
linkForm.value.openInNewTab = link.target
|
|
2556
|
+
linkForm.value.openInNewTab = '_blank' === link.target
|
|
2555
2557
|
|
|
2556
2558
|
// Store the link data for editing
|
|
2557
2559
|
pendingLinkData = {
|
|
@@ -2720,7 +2722,7 @@ async function initEditor() {
|
|
|
2720
2722
|
|
|
2721
2723
|
// Write the complete HTML document to the iframe
|
|
2722
2724
|
const doc = iframe.value.contentDocument || iframe.value.contentWindow?.document
|
|
2723
|
-
if (!doc) return
|
|
2725
|
+
if (!doc) {return}
|
|
2724
2726
|
|
|
2725
2727
|
// First write the content
|
|
2726
2728
|
doc.open()
|
|
@@ -2749,7 +2751,7 @@ async function initEditor() {
|
|
|
2749
2751
|
}
|
|
2750
2752
|
|
|
2751
2753
|
// Also apply direction to list items
|
|
2752
|
-
if (
|
|
2754
|
+
if ('UL' === htmlElement.tagName || 'OL' === htmlElement.tagName) {
|
|
2753
2755
|
const listItems = htmlElement.querySelectorAll('li')
|
|
2754
2756
|
listItems.forEach((li) => {
|
|
2755
2757
|
const listItem = li as HTMLElement
|
|
@@ -2773,7 +2775,7 @@ async function initEditor() {
|
|
|
2773
2775
|
// Clear all formatting by removing styles and replacing with plain text
|
|
2774
2776
|
const elements = doc.body.querySelectorAll('*')
|
|
2775
2777
|
elements.forEach((el) => {
|
|
2776
|
-
if (
|
|
2778
|
+
if ('P' !== el.tagName && 'BR' !== el.tagName) {
|
|
2777
2779
|
const textContent = el.textContent || ''
|
|
2778
2780
|
if (textContent.trim()) {
|
|
2779
2781
|
const p = doc.createElement('p')
|
|
@@ -2782,10 +2784,10 @@ async function initEditor() {
|
|
|
2782
2784
|
} else {
|
|
2783
2785
|
el.remove()
|
|
2784
2786
|
}
|
|
2785
|
-
} else if (el.tagName
|
|
2787
|
+
} else if ('P' === el.tagName) {
|
|
2786
2788
|
// Remove all attributes from paragraphs
|
|
2787
2789
|
Array.from(el.attributes).forEach((attr) => {
|
|
2788
|
-
if (attr.name
|
|
2790
|
+
if ('dir' !== attr.name) {
|
|
2789
2791
|
el.removeAttribute(attr.name)
|
|
2790
2792
|
}
|
|
2791
2793
|
})
|
|
@@ -2809,7 +2811,7 @@ async function initEditor() {
|
|
|
2809
2811
|
doc.addEventListener('click', (e) => {
|
|
2810
2812
|
setTimeout(() => {
|
|
2811
2813
|
const selection = doc.getSelection()
|
|
2812
|
-
if (!selection || selection.rangeCount
|
|
2814
|
+
if (!selection || 0 === selection.rangeCount || selection.getRangeAt(0).collapsed) {
|
|
2813
2815
|
hideInlineToolbar()
|
|
2814
2816
|
}
|
|
2815
2817
|
}, 10)
|
|
@@ -2820,7 +2822,7 @@ async function initEditor() {
|
|
|
2820
2822
|
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Escape'].includes(e.key)) {
|
|
2821
2823
|
setTimeout(() => {
|
|
2822
2824
|
const selection = doc.getSelection()
|
|
2823
|
-
if (!selection || selection.rangeCount
|
|
2825
|
+
if (!selection || 0 === selection.rangeCount || selection.getRangeAt(0).collapsed) {
|
|
2824
2826
|
hideInlineToolbar()
|
|
2825
2827
|
}
|
|
2826
2828
|
}, 10)
|
|
@@ -2914,7 +2916,7 @@ async function initEditor() {
|
|
|
2914
2916
|
}
|
|
2915
2917
|
|
|
2916
2918
|
// Only focus if autofocus is explicitly set to true
|
|
2917
|
-
if (props.autofocus
|
|
2919
|
+
if (true === props.autofocus) {
|
|
2918
2920
|
doc.body.focus()
|
|
2919
2921
|
}
|
|
2920
2922
|
|
|
@@ -2940,7 +2942,7 @@ function updateIframeColors() {
|
|
|
2940
2942
|
hasInitialized: hasInitialized.value
|
|
2941
2943
|
})
|
|
2942
2944
|
|
|
2943
|
-
if (!iframe.value?.contentDocument || iframe.value.contentDocument
|
|
2945
|
+
if (!iframe.value?.contentDocument || null === iframe.value.contentDocument) {
|
|
2944
2946
|
console.warn('❌ No iframe or contentDocument')
|
|
2945
2947
|
return
|
|
2946
2948
|
}
|
|
@@ -2955,17 +2957,22 @@ function updateIframeColors() {
|
|
|
2955
2957
|
console.log('✅ Created new style element')
|
|
2956
2958
|
}
|
|
2957
2959
|
|
|
2960
|
+
const fontSize = props.fontSize ? ('number' === typeof props.fontSize ? `${props.fontSize}px` : props.fontSize) : '16px'
|
|
2961
|
+
|
|
2958
2962
|
const css = `
|
|
2959
2963
|
:root {
|
|
2960
2964
|
--input-color: ${currentInputColor.value || '#000'};
|
|
2965
|
+
--richtext-font-size: ${fontSize};
|
|
2961
2966
|
}
|
|
2962
2967
|
body {
|
|
2963
2968
|
color: ${currentTextColor.value} !important;
|
|
2969
|
+
font-size: ${fontSize} !important;
|
|
2964
2970
|
}
|
|
2965
2971
|
|
|
2966
|
-
/* Ensure all text elements inherit the color */
|
|
2972
|
+
/* Ensure all text elements inherit the color and font size */
|
|
2967
2973
|
p, h1, h2, h3, h4, h5, h6, div, span, li, td, th {
|
|
2968
2974
|
color: inherit !important;
|
|
2975
|
+
font-size: inherit !important;
|
|
2969
2976
|
}
|
|
2970
2977
|
`
|
|
2971
2978
|
|
|
@@ -2994,7 +3001,7 @@ watch([currentInputColor, currentTextColor], (newValues, oldValues) => {
|
|
|
2994
3001
|
watch(() => props.modelValue, (newValue, oldValue) => {
|
|
2995
3002
|
if (newValue !== editor.state.content) {
|
|
2996
3003
|
// Only reset if content change is significant (not just minor edits)
|
|
2997
|
-
if (!oldValue || Math.abs(newValue.length - oldValue.length)
|
|
3004
|
+
if (!oldValue || 50 < Math.abs(newValue.length - oldValue.length)) {
|
|
2998
3005
|
hasInitialized.value = false
|
|
2999
3006
|
// For external changes, update content directly but then push to history
|
|
3000
3007
|
editor.state.content = newValue
|
|
@@ -3029,10 +3036,10 @@ watch(() => editor.state.content, (newValue) => {
|
|
|
3029
3036
|
watch(() => tableForm.value.alignment, (newAlignment) => {
|
|
3030
3037
|
if (pendingTableData?.existingTable) {
|
|
3031
3038
|
const table = pendingTableData.existingTable
|
|
3032
|
-
if (
|
|
3039
|
+
if ('center' === newAlignment) {
|
|
3033
3040
|
table.style.marginLeft = 'auto'
|
|
3034
3041
|
table.style.marginRight = 'auto'
|
|
3035
|
-
} else if (
|
|
3042
|
+
} else if ('right' === newAlignment) {
|
|
3036
3043
|
table.style.marginLeft = 'auto'
|
|
3037
3044
|
table.style.marginRight = '0'
|
|
3038
3045
|
} else {
|
|
@@ -3053,13 +3060,13 @@ function handleGlobalClick() {
|
|
|
3053
3060
|
}
|
|
3054
3061
|
|
|
3055
3062
|
// Add event listener when component mounts
|
|
3056
|
-
if (typeof window
|
|
3063
|
+
if ('undefined' !== typeof window) {
|
|
3057
3064
|
window.addEventListener('click', handleGlobalClick)
|
|
3058
3065
|
}
|
|
3059
3066
|
|
|
3060
3067
|
// Cleanup event listener
|
|
3061
3068
|
onBeforeUnmount(() => {
|
|
3062
|
-
if (typeof window
|
|
3069
|
+
if ('undefined' !== typeof window) {
|
|
3063
3070
|
window.removeEventListener('click', handleGlobalClick)
|
|
3064
3071
|
}
|
|
3065
3072
|
})
|
|
@@ -3571,7 +3578,6 @@ defineExpose({
|
|
|
3571
3578
|
min-width: calc(var(--input-height) * 3);
|
|
3572
3579
|
width: 100%;
|
|
3573
3580
|
}
|
|
3574
|
-
|
|
3575
3581
|
.rich-text-editor--basic .content-area:hover {
|
|
3576
3582
|
outline-color: rgba(0, 0, 0, 0.05);
|
|
3577
3583
|
box-shadow: inset 0 0 8px #00000018;
|
|
@@ -3582,6 +3588,7 @@ defineExpose({
|
|
|
3582
3588
|
.content-area span,
|
|
3583
3589
|
.content-area li {
|
|
3584
3590
|
line-height: 1.65;
|
|
3591
|
+
font-size: var(--richtext-font-size, 16px);
|
|
3585
3592
|
}
|
|
3586
3593
|
</style>
|
|
3587
3594
|
|
|
@@ -3611,6 +3618,7 @@ defineExpose({
|
|
|
3611
3618
|
.preview-area {
|
|
3612
3619
|
flex: 1;
|
|
3613
3620
|
background: var(--bgl-richtext-color);
|
|
3621
|
+
font-size: var(--richtext-font-size, 16px);
|
|
3614
3622
|
}
|
|
3615
3623
|
|
|
3616
3624
|
.split-view {
|
|
@@ -3624,6 +3632,7 @@ defineExpose({
|
|
|
3624
3632
|
border: none;
|
|
3625
3633
|
outline: none;
|
|
3626
3634
|
background: transparent;
|
|
3635
|
+
font-size: var(--richtext-font-size, 16px);
|
|
3627
3636
|
}
|
|
3628
3637
|
|
|
3629
3638
|
.html-editor {
|