change categories to tags in frontend
This commit is contained in:
parent
92ed17efa5
commit
5be099c61c
16 changed files with 196 additions and 112 deletions
|
|
@ -20,9 +20,11 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- TODO: autocomplete category -->
|
||||
<td><label>Category</label></td>
|
||||
<td><input type="text" v-model="category" placeholder="Plants" /></td>
|
||||
<!-- TODO: autocomplete tags -->
|
||||
<td><label>Tags</label></td>
|
||||
<td>
|
||||
<tags-input v-model="tags" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -36,14 +38,16 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { Image } from '../../common/GameCommon'
|
||||
import { Image, Tag } from '../../common/GameCommon'
|
||||
|
||||
import ResponsiveImage from './ResponsiveImage.vue'
|
||||
import TagsInput from './TagsInput.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'edit-image-dialog',
|
||||
components: {
|
||||
ResponsiveImage,
|
||||
TagsInput,
|
||||
},
|
||||
props: {
|
||||
image: {
|
||||
|
|
@ -58,19 +62,19 @@ export default defineComponent({
|
|||
data () {
|
||||
return {
|
||||
title: '',
|
||||
category: '',
|
||||
tags: [] as string[],
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.title = this.image.title
|
||||
this.category = this.image.categories.length > 0 ? this.image.categories[0].title : ''
|
||||
this.tags = this.image.tags.map((t: Tag) => t.title)
|
||||
},
|
||||
methods: {
|
||||
saveImage () {
|
||||
this.$emit('saveClick', {
|
||||
id: this.image.id,
|
||||
title: this.title,
|
||||
category: this.category,
|
||||
tags: this.tags,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,12 +16,8 @@ gallery", if possible!
|
|||
<div v-else>
|
||||
<label class="upload">
|
||||
<input type="file" style="display: none" @change="preview" accept="image/*" />
|
||||
<span class="btn">{{label || 'Upload File'}}</span>
|
||||
<span class="btn">Upload File</span>
|
||||
</label>
|
||||
|
||||
|
||||
<!-- TODO: drop area for image -->
|
||||
<!-- <upload class="upload" @uploaded="mediaImgUploaded($event)" accept="image/*" label="Upload an image" /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -37,9 +33,11 @@ gallery", if possible!
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- TODO: autocomplete category -->
|
||||
<td><label>Category</label></td>
|
||||
<td><input type="text" v-model="category" placeholder="Plants" /></td>
|
||||
<!-- TODO: autocomplete tags -->
|
||||
<td><label>Tags</label></td>
|
||||
<td>
|
||||
<tags-input v-model="tags" />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
|
@ -55,14 +53,14 @@ gallery", if possible!
|
|||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import Upload from './Upload.vue'
|
||||
import ResponsiveImage from './ResponsiveImage.vue'
|
||||
import TagsInput from './TagsInput.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'new-image-dialog',
|
||||
components: {
|
||||
Upload,
|
||||
ResponsiveImage,
|
||||
TagsInput,
|
||||
},
|
||||
emits: {
|
||||
bgclick: null,
|
||||
|
|
@ -74,7 +72,7 @@ export default defineComponent({
|
|||
previewUrl: '',
|
||||
file: null as File|null,
|
||||
title: '',
|
||||
category: '',
|
||||
tags: [] as string[],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -103,14 +101,14 @@ export default defineComponent({
|
|||
this.$emit('postToGalleryClick', {
|
||||
file: this.file,
|
||||
title: this.title,
|
||||
category: this.category,
|
||||
tags: this.tags,
|
||||
})
|
||||
},
|
||||
setupGameClick () {
|
||||
this.$emit('setupGameClick', {
|
||||
file: this.file,
|
||||
title: this.title,
|
||||
category: this.category,
|
||||
tags: this.tags,
|
||||
})
|
||||
},
|
||||
},
|
||||
|
|
|
|||
60
src/frontend/components/TagsInput.vue
Normal file
60
src/frontend/components/TagsInput.vue
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div>
|
||||
<input class="input" type="text" v-model="input" placeholder="Plants, People" @keydown.enter="add" @keyup="onKeyUp" />
|
||||
<span v-for="(tag,idx) in values" :key="idx" class="bit" @click="rm(tag)">{{tag}} ✖</span>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'tags-input',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array as PropType<Array<string>>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: {
|
||||
'update:modelValue': null,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
input: '',
|
||||
values: [] as Array<string>,
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.values = this.modelValue
|
||||
},
|
||||
methods: {
|
||||
onKeyUp (ev: KeyboardEvent) {
|
||||
if (ev.key === ',') {
|
||||
this.add()
|
||||
ev.stopPropagation()
|
||||
return false
|
||||
}
|
||||
},
|
||||
add () {
|
||||
const newval = this.input.replace(/,/g, '').trim()
|
||||
if (!newval) {
|
||||
return
|
||||
}
|
||||
if (!this.values.includes(newval)) {
|
||||
this.values.push(newval)
|
||||
}
|
||||
this.input = ''
|
||||
this.$emit('update:modelValue', this.values)
|
||||
},
|
||||
rm (val: string) {
|
||||
this.values = this.values.filter(v => v !== val)
|
||||
this.$emit('update:modelValue', this.values)
|
||||
},
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.input {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
--link-color: #808db0;
|
||||
--link-hover-color: #c5cfeb;
|
||||
--highlight-color: #dd7e7e;
|
||||
--positive-color: #64a756;
|
||||
--input-bg-color: #262523;
|
||||
--bg-color: rgba(0,0,0,.7);
|
||||
}
|
||||
|
|
@ -212,6 +213,18 @@ kbd {
|
|||
color: var(--main-darker-color);
|
||||
}
|
||||
|
||||
.bit {
|
||||
background: rgb(59, 55, 55);
|
||||
border-radius: .5em;
|
||||
padding: .25em .5em;
|
||||
display: inline-block;
|
||||
margin: 0 .25em .25em 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bit.on {
|
||||
color: var(--positive-color);
|
||||
}
|
||||
|
||||
.upload-image-teaser {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ in jigsawpuzzles.io
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label v-if="categories.length > 0">
|
||||
Category:
|
||||
<select v-model="filters.category" @change="filtersChanged">
|
||||
<label v-if="tags.length > 0">
|
||||
Tags:
|
||||
<span class="bit" v-for="(t,idx) in tags" :key="idx" @click="toggleTag(t)" :class="{on: filters.tags.includes(t.slug)}">{{t.title}}</span>
|
||||
<!-- <select v-model="filters.tags" @change="filtersChanged">
|
||||
<option value="">All</option>
|
||||
<option v-for="(c, idx) in categories" :key="idx" :value="c.slug">{{c.title}}</option>
|
||||
</select>
|
||||
<option v-for="(c, idx) in tags" :key="idx" :value="c.slug">{{c.title}}</option>
|
||||
</select> -->
|
||||
</label>
|
||||
<label>
|
||||
Sort by:
|
||||
|
|
@ -44,7 +45,7 @@ import ImageLibrary from './../components/ImageLibrary.vue'
|
|||
import NewImageDialog from './../components/NewImageDialog.vue'
|
||||
import EditImageDialog from './../components/EditImageDialog.vue'
|
||||
import NewGameDialog from './../components/NewGameDialog.vue'
|
||||
import { GameSettings, Image } from '../../common/GameCommon'
|
||||
import { GameSettings, Image, Tag } from '../../common/GameCommon'
|
||||
import Util from '../../common/Util'
|
||||
|
||||
export default defineComponent({
|
||||
|
|
@ -58,10 +59,10 @@ export default defineComponent({
|
|||
return {
|
||||
filters: {
|
||||
sort: 'date_desc',
|
||||
category: '',
|
||||
tags: [] as string[],
|
||||
},
|
||||
images: [],
|
||||
categories: [],
|
||||
tags: [],
|
||||
|
||||
image: {
|
||||
id: 0,
|
||||
|
|
@ -69,7 +70,7 @@ export default defineComponent({
|
|||
file: '',
|
||||
url: '',
|
||||
title: '',
|
||||
categories: [],
|
||||
tags: [],
|
||||
created: 0,
|
||||
} as Image,
|
||||
|
||||
|
|
@ -80,11 +81,19 @@ export default defineComponent({
|
|||
await this.loadImages()
|
||||
},
|
||||
methods: {
|
||||
toggleTag (t: Tag) {
|
||||
if (this.filters.tags.includes(t.slug)) {
|
||||
this.filters.tags = this.filters.tags.filter(slug => slug !== t.slug)
|
||||
} else {
|
||||
this.filters.tags.push(t.slug)
|
||||
}
|
||||
this.filtersChanged()
|
||||
},
|
||||
async loadImages () {
|
||||
const res = await fetch(`/api/newgame-data${Util.asQueryArgs(this.filters)}`)
|
||||
const json = await res.json()
|
||||
this.images = json.images
|
||||
this.categories = json.categories
|
||||
this.tags = json.tags
|
||||
},
|
||||
async filtersChanged () {
|
||||
await this.loadImages()
|
||||
|
|
@ -101,7 +110,7 @@ export default defineComponent({
|
|||
const formData = new FormData();
|
||||
formData.append('file', data.file, data.file.name);
|
||||
formData.append('title', data.title)
|
||||
formData.append('category', data.category)
|
||||
formData.append('tags', data.tags)
|
||||
|
||||
const res = await fetch('/api/upload', {
|
||||
method: 'post',
|
||||
|
|
@ -119,7 +128,7 @@ export default defineComponent({
|
|||
body: JSON.stringify({
|
||||
id: data.id,
|
||||
title: data.title,
|
||||
category: data.category,
|
||||
tags: data.tags,
|
||||
}),
|
||||
})
|
||||
return await res.json()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue