mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 11:25:29 -04:00
Rewrite tagging UI with suggestions (#146)
This commit is contained in:
parent
5abf7032a5
commit
1065b430e3
58
public/css/tags.less
Normal file
58
public/css/tags.less
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@light-gray: #e5e5e5;
|
||||||
|
@gray: #666666;
|
||||||
|
@black: #141414;
|
||||||
|
@blue: rgb(30, 135, 240);
|
||||||
|
@white1: rgba(255, 255, 255, .1);
|
||||||
|
@white2: rgba(255, 255, 255, .2);
|
||||||
|
@white7: rgba(255, 255, 255, .7);
|
||||||
|
|
||||||
|
.select2-container--default {
|
||||||
|
.select2-selection--multiple {
|
||||||
|
border: 1px solid @light-gray;
|
||||||
|
.select2-selection__choice,
|
||||||
|
.select2-selection__choice__remove,
|
||||||
|
.select2-selection__choice__remove:hover
|
||||||
|
{
|
||||||
|
background-color: @blue;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.select2-dropdown {
|
||||||
|
.select2-results__option--highlighted.select2-results__option--selectable {
|
||||||
|
background-color: @blue;
|
||||||
|
}
|
||||||
|
.select2-results__option--selected:not(.select2-results__option--highlighted) {
|
||||||
|
background-color: @light-gray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uk-light {
|
||||||
|
.select2-container--default {
|
||||||
|
.select2-selection {
|
||||||
|
background-color: @white1;
|
||||||
|
}
|
||||||
|
.select2-selection--multiple {
|
||||||
|
border: 1px solid @white2;
|
||||||
|
.select2-selection__choice,
|
||||||
|
.select2-selection__choice__remove,
|
||||||
|
.select2-selection__choice__remove:hover
|
||||||
|
{
|
||||||
|
background-color: white;
|
||||||
|
color: @gray;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.select2-search__field {
|
||||||
|
color: @white7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.select2-dropdown {
|
||||||
|
background-color: @black;
|
||||||
|
.select2-results__option--selected:not(.select2-results__option--highlighted) {
|
||||||
|
background-color: @white2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -255,48 +255,64 @@ const bulkProgress = (action, el) => {
|
|||||||
|
|
||||||
const tagsComponent = () => {
|
const tagsComponent = () => {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
newTag: '',
|
|
||||||
inputShown: false,
|
|
||||||
tid: $('.upload-field').attr('data-title-id'),
|
tid: $('.upload-field').attr('data-title-id'),
|
||||||
|
loading: true,
|
||||||
|
|
||||||
load(admin) {
|
load(admin) {
|
||||||
this.isAdmin = admin;
|
this.isAdmin = admin;
|
||||||
|
|
||||||
|
$('.tag-select').select2({
|
||||||
|
tags: true,
|
||||||
|
placeholder: 'Tag the title',
|
||||||
|
templateSelection(state) {
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.setAttribute('href', `${base_url}tags/${encodeURIComponent(state.text)}`);
|
||||||
|
a.setAttribute('class', 'uk-link-reset');
|
||||||
|
a.onclick = event => {
|
||||||
|
event.stopPropagation();
|
||||||
|
};
|
||||||
|
a.innerText = state.text;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.request(`${base_url}api/tags`, 'GET', (data) => {
|
||||||
|
const allTags = data.tags;
|
||||||
const url = `${base_url}api/tags/${this.tid}`;
|
const url = `${base_url}api/tags/${this.tid}`;
|
||||||
this.request(url, 'GET', (data) => {
|
this.request(url, 'GET', data => {
|
||||||
this.tags = data.tags;
|
this.tags = data.tags;
|
||||||
|
allTags.forEach(t => {
|
||||||
|
const op = new Option(t, t, false, this.tags.indexOf(t) >= 0);
|
||||||
|
$('.tag-select').append(op);
|
||||||
|
});
|
||||||
|
$('.tag-select').on('select2:select', e => {
|
||||||
|
this.onAdd(e);
|
||||||
|
});
|
||||||
|
$('.tag-select').on('select2:unselect', e => {
|
||||||
|
this.onDelete(e);
|
||||||
|
});
|
||||||
|
$('.tag-select').on('change', () => {
|
||||||
|
this.onChange();
|
||||||
|
});
|
||||||
|
$('.tag-select').trigger('change');
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
add() {
|
onChange() {
|
||||||
const tag = this.newTag.trim();
|
this.tags = $('.tag-select').select2('data').map(o => o.text);
|
||||||
|
},
|
||||||
|
onAdd(event) {
|
||||||
|
const tag = event.params.data.text;
|
||||||
const url = `${base_url}api/admin/tags/${this.tid}/${encodeURIComponent(tag)}`;
|
const url = `${base_url}api/admin/tags/${this.tid}/${encodeURIComponent(tag)}`;
|
||||||
this.request(url, 'PUT', () => {
|
this.request(url, 'PUT');
|
||||||
this.tags.push(tag);
|
|
||||||
this.newTag = '';
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
keydown(event) {
|
onDelete(event) {
|
||||||
if (event.key === 'Enter')
|
const tag = event.params.data.text;
|
||||||
this.add()
|
|
||||||
},
|
|
||||||
rm(event) {
|
|
||||||
const tag = event.currentTarget.id.split('-')[0];
|
|
||||||
const url = `${base_url}api/admin/tags/${this.tid}/${encodeURIComponent(tag)}`;
|
const url = `${base_url}api/admin/tags/${this.tid}/${encodeURIComponent(tag)}`;
|
||||||
this.request(url, 'DELETE', () => {
|
this.request(url, 'DELETE');
|
||||||
const idx = this.tags.indexOf(tag);
|
|
||||||
if (idx < 0) return;
|
|
||||||
this.tags.splice(idx, 1);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
toggleInput(nextTick) {
|
|
||||||
this.inputShown = !this.inputShown;
|
|
||||||
if (this.inputShown) {
|
|
||||||
nextTick(() => {
|
|
||||||
$('#tag-input').get(0).focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
request(url, method, cb) {
|
request(url, method, cb) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@ -305,9 +321,9 @@ const tagsComponent = () => {
|
|||||||
dataType: 'json'
|
dataType: 'json'
|
||||||
})
|
})
|
||||||
.done(data => {
|
.done(data => {
|
||||||
if (data.success)
|
if (data.success) {
|
||||||
cb(data);
|
if (cb) cb(data);
|
||||||
else {
|
} else {
|
||||||
alert('danger', data.error);
|
alert('danger', data.error);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -713,6 +713,24 @@ struct APIRouter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Koa.describe "Returns all tags"
|
||||||
|
Koa.response 200, ref: "$tagsResult"
|
||||||
|
get "/api/tags" do |env|
|
||||||
|
begin
|
||||||
|
tags = Storage.default.list_tags
|
||||||
|
send_json env, {
|
||||||
|
"success" => true,
|
||||||
|
"tags" => tags,
|
||||||
|
}.to_json
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
send_json env, {
|
||||||
|
"success" => false,
|
||||||
|
"error" => e.message,
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Koa.describe "Adds a new tag to a title"
|
Koa.describe "Adds a new tag to a title"
|
||||||
Koa.path "tid", desc: "A title ID"
|
Koa.path "tid", desc: "A title ID"
|
||||||
Koa.response 200, ref: "$result"
|
Koa.response 200, ref: "$result"
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
<div class="uk-margin" x-data="tagsComponent()" x-cloak x-init="load(<%= is_admin %>)">
|
|
||||||
<p class="uk-text-meta" @selectstart.prevent>
|
|
||||||
<span style="position:relative; bottom:3px; margin-right:5px;">Tags: </span>
|
|
||||||
<template x-for="tag in tags" :key="tag">
|
|
||||||
<span class="uk-label uk-label-primary" style="padding:2px 5px; margin:0 5px 5px 5px; text-transform:none;">
|
|
||||||
<a class="uk-link-reset" x-show="isAdmin" @click="rm($event)" :id="`${tag}-rm`"><span uk-icon="close" style="margin-right: 5px; position: relative; bottom: 1.5px;"></span></a><a class="uk-link-reset" x-text="tag" :href="`<%= base_url %>tags/${encodeURIComponent(tag)}`"></a>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
<a class="uk-link-reset" style="position:relative; bottom:3px;" :uk-icon="inputShown ? 'close' : 'plus'" @click="toggleInput($nextTick)" x-show="isAdmin"></a>
|
|
||||||
</p>
|
|
||||||
<input id="tag-input" class="uk-input" type="text" placeholder="Type in a new tag and hit enter" x-model="newTag" @keydown="keydown($event)" x-show="inputShown">
|
|
||||||
</div>
|
|
@ -34,7 +34,10 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<p class="uk-text-meta"><%= title.content_label %> found</p>
|
<p class="uk-text-meta"><%= title.content_label %> found</p>
|
||||||
|
|
||||||
<%= render_component "tags" %>
|
<div class="uk-margin" x-data="tagsComponent()" x-cloak x-init="load(<%= is_admin %>)" x-show="!loading">
|
||||||
|
<select class="tag-select" multiple="multiple" style="width:100%">
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="uk-grid-small" uk-grid>
|
<div class="uk-grid-small" uk-grid>
|
||||||
<div class="uk-margin-bottom uk-width-3-4@s">
|
<div class="uk-margin-bottom uk-width-3-4@s">
|
||||||
@ -121,6 +124,9 @@
|
|||||||
|
|
||||||
<% content_for "script" do %>
|
<% content_for "script" do %>
|
||||||
<%= render_component "dots-scripts" %>
|
<%= render_component "dots-scripts" %>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
<link href="/css/tags.css" rel="stylesheet" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-beta.1/dist/js/select2.min.js"></script>
|
||||||
<script src="<%= base_url %>js/alert.js"></script>
|
<script src="<%= base_url %>js/alert.js"></script>
|
||||||
<script src="<%= base_url %>js/title.js"></script>
|
<script src="<%= base_url %>js/title.js"></script>
|
||||||
<script src="<%= base_url %>js/search.js"></script>
|
<script src="<%= base_url %>js/search.js"></script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user