@@ -41,6 +56,9 @@ const description = ref(null)
const onImageUpload = (file: File): string => {
// Upload the file to your server and return a URL
// This example url will not work bc of proxy
+
+ // If the upload fails, throw an error and it will show as
+ // a Validation Error to the user
return URL.createObjectURL(file).replace("blob:", "");
};
diff --git a/lib/assets/styles/classes.scss b/lib/assets/styles/classes.scss
index e6643be77..bbce8abf0 100644
--- a/lib/assets/styles/classes.scss
+++ b/lib/assets/styles/classes.scss
@@ -70,8 +70,6 @@
:where(input) {
box-sizing: border-box;
max-height: 40px;
- width: 24rem;
- flex-basis: 24rem;
&:not(.stylized-toggle) {
max-width: 100%;
diff --git a/lib/components/base/MarkdownEditor.vue b/lib/components/base/MarkdownEditor.vue
index 6f18f8b44..3c8042427 100644
--- a/lib/components/base/MarkdownEditor.vue
+++ b/lib/components/base/MarkdownEditor.vue
@@ -223,11 +223,19 @@
-
-
- This editor supports
- Markdown formatting.
-
+
+
+ Max length:
+ {{ props.maxLength ?? 'Unlimited' }}
+
Promise
+ placeholder?: string
+ maxLength?: number
}>(),
{
modelValue: '',
disabled: false,
headingButtons: true,
onImageUpload: undefined,
+ placeholder: undefined,
+ maxLength: undefined,
}
)
@@ -321,7 +338,6 @@ onMounted(() => {
paste: (ev, view) => {
// If the user's pasting a url, automatically convert it to a link with the selection as the text or the url itself if no selection content.
const url = ev.clipboardData?.getData('text/plain')
-
if (url) {
try {
cleanUrl(url)
@@ -337,6 +353,35 @@ onMounted(() => {
const linkMarkdown = `[${linkText}](${url})`
return markdownCommands.replaceSelection(view, linkMarkdown)
}
+ // Check if the length of the document is greater than the max length. If it is, prevent the paste.
+ if (props.maxLength && view.state.doc.length > props.maxLength) {
+ ev.preventDefault()
+ return false
+ }
+ },
+ beforeinput: (ev, view) => {
+ if (props.maxLength && view.state.doc.length > props.maxLength) {
+ ev.preventDefault()
+ // Calculate how many characters to remove from the end
+ const excessLength = view.state.doc.length - props.maxLength
+ // Dispatch transaction to remove excess characters
+ view.dispatch({
+ changes: { from: view.state.doc.length - excessLength, to: view.state.doc.length },
+ selection: { anchor: props.maxLength, head: props.maxLength }, // Place cursor at the end
+ })
+ return true
+ }
+ },
+ blur: (_, view) => {
+ if (props.maxLength && view.state.doc.length > props.maxLength) {
+ // Calculate how many characters to remove from the end
+ const excessLength = view.state.doc.length - props.maxLength
+ // Dispatch transaction to remove excess characters
+ view.dispatch({
+ changes: { from: view.state.doc.length - excessLength, to: view.state.doc.length },
+ selection: { anchor: props.maxLength, head: props.maxLength }, // Place cursor at the end
+ })
+ }
},
})
@@ -353,6 +398,7 @@ onMounted(() => {
addKeymap: false,
}),
keymap.of(historyKeymap),
+ cm_placeholder(props.placeholder || ''),
],
})
@@ -532,6 +578,9 @@ const handleImageUpload = async (files: FileList) => {
linkUrl.value = url
validateURL()
} catch (error) {
+ if (error instanceof Error) {
+ linkValidationErrorMessage.value = error.message
+ }
console.error(error)
}
}
@@ -577,7 +626,7 @@ function openVideoModal() {
}
-