Tweaks for Knossos Integration (#122)
* placeholder * max length & placeholder * remove default placeholder * remove scoped css * allow for throwing in the upload process * explicit import of info * fix aggressive card input selection
This commit is contained in:
parent
79bdea0441
commit
544111846c
@ -3,6 +3,7 @@
|
||||
import { ref } from "vue";
|
||||
|
||||
const description = ref(null);
|
||||
const description1 = ref(null);
|
||||
const description2 = ref(null);
|
||||
const description3 = ref(null);
|
||||
|
||||
@ -27,6 +28,20 @@ const description = ref(null)
|
||||
<MarkdownEditor v-model="description" />
|
||||
```
|
||||
|
||||
## With options
|
||||
<DemoContainer>
|
||||
<MarkdownEditor v-model="description1" placeholder="Enter a description" max-length="30" />
|
||||
</DemoContainer>
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
const description = ref(null)
|
||||
</script>
|
||||
|
||||
<MarkdownEditor v-model="description" placeholder="Enter a description" max-length="30" />
|
||||
```
|
||||
|
||||
## With image upload
|
||||
<DemoContainer>
|
||||
<MarkdownEditor v-model="description2" :on-image-upload="onImageUpload" />
|
||||
@ -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:", "");
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -70,8 +70,6 @@
|
||||
:where(input) {
|
||||
box-sizing: border-box;
|
||||
max-height: 40px;
|
||||
width: 24rem;
|
||||
flex-basis: 24rem;
|
||||
|
||||
&:not(.stylized-toggle) {
|
||||
max-width: 100%;
|
||||
|
||||
@ -223,11 +223,19 @@
|
||||
</div>
|
||||
<div ref="editorRef" :class="{ hide: previewMode }" />
|
||||
<div v-if="!previewMode" class="info-blurb">
|
||||
<InfoIcon />
|
||||
<span>
|
||||
This editor supports
|
||||
<a href="https://docs.modrinth.com/docs/markdown" target="_blank">Markdown formatting</a>.
|
||||
</span>
|
||||
<div class="info-blurb">
|
||||
<InfoIcon />
|
||||
<span
|
||||
>This editor supports
|
||||
<a class="link" href="https://docs.modrinth.com/docs/markdown" target="_blank"
|
||||
>Markdown formatting</a
|
||||
>.</span
|
||||
>
|
||||
</div>
|
||||
<div :class="{ hide: !props.maxLength }" class="max-length-label">
|
||||
<span>Max length: </span>
|
||||
<span>{{ props.maxLength ?? 'Unlimited' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="previewMode"
|
||||
@ -242,7 +250,7 @@
|
||||
import { type Component, computed, ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import { EditorView, keymap } from '@codemirror/view'
|
||||
import { EditorView, keymap, placeholder as cm_placeholder } from '@codemirror/view'
|
||||
import { markdown } from '@codemirror/lang-markdown'
|
||||
import { indentWithTab, historyKeymap, history } from '@codemirror/commands'
|
||||
|
||||
@ -268,6 +276,7 @@ import {
|
||||
Toggle,
|
||||
FileInput,
|
||||
UploadIcon,
|
||||
InfoIcon,
|
||||
Chips,
|
||||
} from '@/components'
|
||||
import { markdownCommands, modrinthMarkdownEditorKeymap } from '@/helpers/codemirror'
|
||||
@ -278,13 +287,21 @@ const props = withDefaults(
|
||||
modelValue: string
|
||||
disabled: boolean
|
||||
headingButtons: boolean
|
||||
/**
|
||||
* @param file The file to upload
|
||||
* @throws If the file is invalid or the upload fails
|
||||
*/
|
||||
onImageUpload?: (file: File) => Promise<string>
|
||||
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() {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss">
|
||||
.display-options {
|
||||
margin-bottom: var(--gap-sm);
|
||||
}
|
||||
@ -586,7 +635,6 @@ function openVideoModal() {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
overflow: hidden;
|
||||
justify-content: space-between;
|
||||
margin-bottom: var(--gap-sm);
|
||||
gap: var(--gap-xs);
|
||||
@ -635,6 +683,7 @@ function openVideoModal() {
|
||||
.info-blurb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--gap-xs);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user