Skip to content

Commit

Permalink
MOD-349 Contextual Uploads for MD Editor (modrinth#119)
Browse files Browse the repository at this point in the history
* Migrate DropArea to composition

* remove hardcoded button styling

* let markdown editor call for image upload

* allow for local testing in the docs

* validate url on set

* add chips to modal with correct defaults

* update docs to show example url doesn't load

* Bump version 0.6.4
  • Loading branch information
darling committed Oct 26, 2023
1 parent c056c4e commit 79bdea0
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 88 deletions.
10 changes: 8 additions & 2 deletions docs/components/drop-area.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Drop Area
<script setup>
import { ref } from "vue";

const files = ref([])
</script>

<DemoContainer>
<InfoIcon /> Click to choose a file or drag one onto this page
<DropArea accept="*" />
<DropArea accept="*" @change="files">
<InfoIcon /> Click to choose a file or drag one onto this page
</DropArea>
</DemoContainer>

```vue
Expand Down
33 changes: 30 additions & 3 deletions docs/components/markdown-editor.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Markdown Editor
<script setup>
import { ref } from "vue";
const description = ref(null)
const description2 = ref(null)

const description = ref(null);
const description2 = ref(null);
const description3 = ref(null);

const onImageUpload = (file) => {
return URL.createObjectURL(file).replace("blob:", "");
};
</script>

The Markdown editor allows for easy formatting of Markdown text whether the user is familiar with Markdown or not. It includes standard shortcuts such as `CTRL+B` for bold, `CTRL+I` for italic, and more.
Expand All @@ -21,9 +27,30 @@ const description = ref(null)
<MarkdownEditor v-model="description" />
```

## With image upload
<DemoContainer>
<MarkdownEditor v-model="description2" :on-image-upload="onImageUpload" />
</DemoContainer>

```vue
<script setup lang="ts">
import { ref } from "vue";
const description = ref(null)
// Return a URL to the image for the editor to consume
const onImageUpload = (file: File): string => {
// Upload the file to your server and return a URL
// This example url will not work bc of proxy
return URL.createObjectURL(file).replace("blob:", "");
};
</script>
<MarkdownEditor v-model="description" :on-image-upload="onImageUpload" />
```

## Without heading buttons
<DemoContainer>
<MarkdownEditor v-model="description2" :heading-buttons="false" />
<MarkdownEditor v-model="description3" :heading-buttons="false" />
</DemoContainer>

```vue
Expand Down
6 changes: 3 additions & 3 deletions docs/components/text-inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const inputText = ref(null)
type="text"
placeholder="Text input"
/>
<Button @click="() => inputText = ''">
<Button class="r-btn" @click="() => inputText = ''">
<XIcon/>
</Button>
</div>
Expand Down Expand Up @@ -65,7 +65,7 @@ const inputText = ref(null)
type="text"
placeholder="Text input"
/>
<Button @click="() => inputText = ''">
<Button class="r-btn" @click="() => inputText = ''">
<XIcon/>
</Button>
</div>
Expand All @@ -92,7 +92,7 @@ const value = ref(null)
type="text"
placeholder="Text input"
/>
<Button @click="() => inputText = ''">
<Button class="r-btn" @click="() => inputText = ''">
<XIcon/>
</Button>
</div>
Expand Down
3 changes: 2 additions & 1 deletion lib/assets/styles/defaults.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ input[type='url'],
input[type='number'],
input[type='password'],
textarea,
.input-text-inherit,
.cm-content {
border-radius: var(--radius-md);
box-sizing: border-box;
Expand Down Expand Up @@ -146,7 +147,7 @@ input[type='number'] {
opacity: 0.6;
}

.btn {
.r-btn {
@extend .transparent, .icon-only;

position: absolute;
Expand Down
117 changes: 62 additions & 55 deletions lib/components/base/DropArea.vue
Original file line number Diff line number Diff line change
@@ -1,65 +1,72 @@
<template>
<div
ref="drop_area"
class="drop-area"
@drop.stop.prevent="
(event) => {
$refs.drop_area.style.visibility = 'hidden'
if (event.dataTransfer && event.dataTransfer.files && fileAllowed) {
$emit('change', event.dataTransfer.files)
}
}
"
@dragenter.prevent="allowDrag"
@dragover.prevent="allowDrag"
@dragleave.prevent="$refs.drop_area.style.visibility = 'hidden'"
/>
<Teleport to="body">
<div
ref="dropAreaRef"
class="drop-area"
@drop.stop.prevent="handleDrop"
@dragenter.prevent="allowDrag"
@dragover.prevent="allowDrag"
@dragleave.prevent="hideDropArea"
/>
</Teleport>
<slot />
</template>

<script>
import { defineComponent } from 'vue'
<script setup lang="ts">
import { defineProps, defineEmits, ref, onMounted } from 'vue'
export default defineComponent({
props: {
accept: {
type: String,
default: '',
},
},
emits: ['change'],
data() {
return {
fileAllowed: false,
const props = withDefaults(
defineProps<{
accept: string
}>(),
{
accept: '*',
}
)
const emit = defineEmits(['change'])
const dropAreaRef = ref<HTMLDivElement>()
const fileAllowed = ref(false)
const hideDropArea = () => {
if (dropAreaRef.value) {
dropAreaRef.value.style.visibility = 'hidden'
}
}
const handleDrop = (event: DragEvent) => {
hideDropArea()
if (event.dataTransfer && event.dataTransfer.files && fileAllowed.value) {
emit('change', event.dataTransfer.files)
}
}
const allowDrag = (event: DragEvent) => {
const file = event.dataTransfer?.items[0]
if (
file &&
props.accept
.split(',')
.reduce((acc, t) => acc || file.type.startsWith(t) || file.type === t || t === '*', false)
) {
fileAllowed.value = true
event.dataTransfer.dropEffect = 'copy'
event.preventDefault()
if (dropAreaRef.value) {
dropAreaRef.value.style.visibility = 'visible'
}
},
mounted() {
document.addEventListener('dragenter', this.allowDrag)
},
methods: {
allowDrag(event) {
const file = event.dataTransfer?.items[0]
if (
file &&
this.accept
.split(',')
.reduce((acc, t) => acc || file.type.startsWith(t) || file.type === t || t === '*', false)
) {
this.fileAllowed = true
event.dataTransfer.dropEffect = 'copy'
event.preventDefault()
if (this.$refs.drop_area) {
this.$refs.drop_area.style.visibility = 'visible'
}
} else {
this.fileAllowed = false
if (this.$refs.drop_area) {
this.$refs.drop_area.style.visibility = 'hidden'
}
}
},
},
} else {
fileAllowed.value = false
hideDropArea()
}
}
onMounted(() => {
document.addEventListener('dragenter', allowDrag)
})
</script>

<style lang="scss" scoped>
.drop-area {
position: fixed;
Expand Down
Loading

0 comments on commit 79bdea0

Please sign in to comment.