Add tokenfield tag editor

This commit is contained in:
Las Zenow 2021-04-30 13:47:59 +00:00
parent 6a3da59c75
commit 02f711aa97
10 changed files with 148 additions and 3 deletions

View file

@ -80,6 +80,8 @@ file with the exception of:
* css/bootstrap.bundle.min.css css/bootstrap.bundle.min.css.map js/bootstrap.bundle.min.js * css/bootstrap.bundle.min.css css/bootstrap.bundle.min.css.map js/bootstrap.bundle.min.js
js/bootstrap.bundle.min.js.map img/bootstrap-icons.svg js/bootstrap.bundle.min.js.map img/bootstrap-icons.svg
From the bootstrap framework: https://getbootstrap.com/ From the bootstrap framework: https://getbootstrap.com/
* css/tokenfield.css css/tokenfield.css.map js/tokenfield.min.js
From tokenfield: https://github.com/KaneCohen/tokenfield
* img/bright_squares.png * img/bright_squares.png
From subtlepatterns: http://subtlepatterns.com/bright-squares/ From subtlepatterns: http://subtlepatterns.com/bright-squares/
* css/FredokaOne.ttf css/PTSerif.ttf * css/FredokaOne.ttf css/PTSerif.ttf

97
css/tokenfield.css Normal file
View file

@ -0,0 +1,97 @@
.tokenfield {
position: relative; }
.tokenfield:before, .tokenfield:after {
content: " ";
display: table; }
.tokenfield:after {
clear: both; }
.tokenfield.tokenfield-mode-tokens {
display: block;
width: 100%;
min-height: 34px;
padding: 6px 12px 0;
font-size: 14px;
line-height: 1.42857;
color: #555555;
background-color: #fff;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
-o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; }
.tokenfield.tokenfield-mode-tokens:focus {
border-color: #66afe9;
outline: 0;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); }
.tokenfield.tokenfield-mode-tokens::-moz-placeholder {
color: #999;
opacity: 1; }
.tokenfield.tokenfield-mode-tokens:-ms-input-placeholder {
color: #999; }
.tokenfield.tokenfield-mode-tokens::-webkit-input-placeholder {
color: #999; }
.tokenfield.tokenfield-mode-tokens::-ms-expand {
border: 0;
background-color: transparent; }
.tokenfield.tokenfield-mode-tokens[disabled], .tokenfield.tokenfield-mode-tokens[readonly],
fieldset[disabled] .tokenfield.tokenfield-mode-tokens {
background-color: #eeeeee;
opacity: 1; }
.tokenfield.tokenfield-mode-tokens[disabled],
fieldset[disabled] .tokenfield.tokenfield-mode-tokens {
cursor: not-allowed; }
.tokenfield.tokenfield-mode-tokens .focused {
box-shadow: 0 0 0 1px #337ab7 inset; }
.tokenfield.tokenfield-mode-tokens .selected {
background: rgba(0, 0, 0, 0.1); }
.tokenfield .tokenfield-set > ul {
margin: 0;
padding: 0;
list-style-type: none; }
.tokenfield .tokenfield-set > ul > li {
float: left;
margin-right: 5px;
margin-bottom: 5px;
padding: 0 5px;
border-radius: 4px;
line-height: 1.5;
cursor: pointer;
color: rgba(0, 0, 0, 0.6);
background: rgba(0, 0, 0, 0.08); }
.tokenfield .tokenfield-set > ul > li:hover {
color: black;
background: rgba(0, 0, 0, 0.16); }
.tokenfield .tokenfield-set > ul > li .item-remove {
display: inline-block;
font-weight: bold;
font-size: 0.9285em;
cursor: pointer;
color: rgba(0, 0, 0, 0.4); }
.tokenfield .tokenfield-input {
margin-bottom: 5px;
border: none;
outline: none;
float: left; }
.tokenfield .tokenfield-suggest {
position: absolute;
left: -1px;
top: 100%;
width: 100%;
z-index: 10;
overflow: auto;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-sizing: content-box; }
.tokenfield .tokenfield-suggest > ul {
margin: 0;
padding: 0;
list-style: none; }
.tokenfield .tokenfield-suggest > ul > li {
padding: 6px 10px;
cursor: pointer; }
/*# sourceMappingURL=tokenfield.css.map */

11
css/tokenfield.css.map Normal file
View file

@ -0,0 +1,11 @@
{
"version": 3,
"file": "tokenfield.css",
"sources": [
"../lib/scss/tokenfield.scss",
"../lib/scss/mixins.scss",
"../lib/scss/variables.scss"
],
"names": [],
"mappings": "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"
}

1
js/tokenfield.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -59,6 +59,7 @@ type editData struct {
S Status S Status
Book database.Book Book database.Book
SubmissionID string SubmissionID string
Tags []string
} }
func editHandler(h handler) { func editHandler(h handler) {
@ -78,6 +79,10 @@ func editHandler(h handler) {
data.Book = book data.Book = book
data.S = GetStatus(h) data.S = GetStatus(h)
data.SubmissionID = submissionID data.SubmissionID = submissionID
data.Tags, err = h.db.GetTags()
if err != nil {
log.Error("Error getting tags: ", err)
}
author := "" author := ""
if len(book.Authors) > 0 { if len(book.Authors) > 0 {
author = " by " + book.Authors[0] author = " by " + book.Authors[0]

View file

@ -33,6 +33,7 @@ type DB interface {
IncDownloads(ID string) error IncDownloads(ID string) error
GetDownloadCounter(ID string) (int, error) GetDownloadCounter(ID string) (int, error)
GetFrontPage() FrontPage GetFrontPage() FrontPage
GetTags() ([]string, error)
AddSubmission(submission Submission, userName string) (id int, err error) AddSubmission(submission Submission, userName string) (id int, err error)
UpdateSubmission(id int, status string, book *Book) error UpdateSubmission(id int, status string, book *Book) error
UpdateSubmissionByBook(bookID string, status string, book *Book) error UpdateSubmissionByBook(bookID string, status string, book *Book) error

View file

@ -109,6 +109,10 @@ func (db *roDB) GetFrontPage() FrontPage {
return db.db.GetFrontPage() return db.db.GetFrontPage()
} }
func (db *roDB) GetTags() ([]string, error) {
return db.db.GetTags()
}
func (db *roDB) AddSubmission(submission Submission, userName string) (id int, err error) { func (db *roDB) AddSubmission(submission Submission, userName string) (id int, err error) {
return 0, errors.New("RO database") return 0, errors.New("RO database")
} }

View file

@ -96,6 +96,10 @@ func (db *pgDB) frontPageUpdater() {
} }
} }
func (db *pgDB) GetTags() ([]string, error) {
return db.frontPage.Tags, nil
}
func (db *pgDB) getVisitedBooks(num int) (books []Book, err error) { func (db *pgDB) getVisitedBooks(num int) (books []Book, err error) {
err = db.sql.Model(&books). err = db.sql.Model(&books).
Column("book.*"). Column("book.*").

View file

@ -1,7 +1,5 @@
{{template "header.html" .S}} {{template "header.html" .S}}
<!-- TODO: tokenfield -->
{{$submissionID := .SubmissionID}} {{$submissionID := .SubmissionID}}
{{with .Book}} {{with .Book}}
<div class="row"> <div class="row">
@ -108,6 +106,27 @@
</div> </div>
{{end}} {{end}}
<!-- TODO: tokenfield --> <script src="/js/tokenfield.min.js"></script>
<script>
var tf = new Tokenfield({
el: document.querySelector('#tags'),
items: [
{{range $i, $tag := .Tags}}
{id: 1000 + {{$i}}, name: '{{$tag}}'},
{{end}}
],
setItems: [
{{range $i, $tag := .Book.Tags}}
{id: {{$i}}, name: '{{$tag}}'},
{{end}}
],
delimiters: [","],
newItems: true
});
tf.on('change', function() {
const tag_list = tf.getItems().map((i) => i.name).join();
document.querySelector('#tags').value = tag_list;
});
</script>
{{template "footer.html" .S}} {{template "footer.html" .S}}

View file

@ -7,6 +7,7 @@
<link href="/img/favicon.ico" rel="icon" type="image/x-icon" /> <link href="/img/favicon.ico" rel="icon" type="image/x-icon" />
<link href="/css/bootstrap.min.css" rel="stylesheet"> <link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/custom.css" rel="stylesheet"> <link href="/css/custom.css" rel="stylesheet">
<link href="/css/tokenfield.css" rel="stylesheet">
<link rel="alternate" type="application/rss+xml" title="{{.Title}}" href="/search/?fmt=rss&q={{.Search}}" /> <link rel="alternate" type="application/rss+xml" title="{{.Title}}" href="/search/?fmt=rss&q={{.Search}}" />
<title>{{.Title}}</title> <title>{{.Title}}</title>