Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

CSS-only tree view. (#753)

* Make tree-view work with CSS only

Changed the file list tree-view to use recursive templating instead of
an external function, and improved it so that it works with only CSS.
Striped lines won't work though.

* Remove inline-block from folder label

It breaks with the text-overflow: ellipsis.

* Rename makeFolderData to makeTreeViewData
Cette révision appartient à :
Ramon Dantas 2017-05-25 22:53:18 -03:00 révisé par ewhal
Parent f22d11b35d
révision 8fbdeed9f5
7 fichiers modifiés avec 89 ajouts et 194 suppressions

Voir le fichier

@ -549,16 +549,31 @@ td.tr-le { color: #E84C4C; }
width: 24px;
}
/* Filelist */
#filelist-control {
.filelist-control {
cursor: pointer;
}
#filelist-control::before {
.filelist-control::before {
content: "\25B6 ";
}
#filelist-control[data-filelist-open="true"]::before {
input#show-filelist:checked ~ .filelist-control::before {
content: "\25BC ";
}
input#show-filelist {
display: none;
}
#filelist {
display: none;
}
input#show-filelist:checked ~ #filelist {
display: block;
}
#filelist tr {
background: none; /* Striped lines will look really ugly due to how it's drawn */
}
.tr-filelist {
--nest-level: 0;
}
@ -580,10 +595,18 @@ td.tr-le { color: #E84C4C; }
}
/* Filesize column */
.tr-filelist td:nth-child(2) {
width: 30%;
text-align: center;
}
/* Input that show/hides each folder */
input.filelist-checkbox {
display: none;
}
input.filelist-checkbox:checked + table.table-filelist {
display: none;
}
.tr-folder {
.tr-folder label {
cursor: pointer;
}
/* The folder or file icon */
@ -594,6 +617,7 @@ td.tr-le { color: #E84C4C; }
background-size: 24px;
width: 24px;
height: 24px;
vertical-align: middle;
}
.tr-file td:first-child::before {
content: " ";
@ -602,6 +626,7 @@ td.tr-le { color: #E84C4C; }
background-size: 24px;
width: 24px;
height: 24px;
vertical-align: middle;
}
#torrent-name {

Voir le fichier

@ -60,5 +60,3 @@ td.tr-le { color: #cc6666; }
margin-bottom: -.5em;
}
.tr-folder td:first-child::before { filter: invert(100%); -webkit-filter: invert(100%); }
.tr-file td:first-child::before { filter: invert(100%); -webkit-filter: invert(100%); }

Voir le fichier

@ -20,14 +20,6 @@ type captchaData struct {
T languages.TemplateTfunc
}
// Will be reused later.
func fileSizeFunc(filesize int64, T languages.TemplateTfunc) template.HTML {
if filesize == 0 {
return T("unknown")
}
return template.HTML(util.FormatFilesize(filesize))
}
// FuncMap : Functions accessible in templates by {{ $.Function }}
var FuncMap = template.FuncMap{
"inc": func(i int) int {
@ -189,21 +181,24 @@ var FuncMap = template.FuncMap{
}
return ""
},
"fileSize": fileSizeFunc,
"fileSize": func(filesize int64, T languages.TemplateTfunc) template.HTML {
if filesize == 0 {
return T("unknown")
}
return template.HTML(util.FormatFilesize(filesize))
},
"makeCaptchaData": func(captchaID string, T languages.TemplateTfunc) captchaData {
return captchaData{captchaID, T}
},
"DefaultUserSettings": func(s string) bool {
return config.DefaultUserSettings[s]
},
"MakeFolderTreeView": func(f *filelist.FileListFolder, folderFmt string, fileFmt string, data interface{}) template.HTML {
out, err := f.MakeFolderTreeView(folderFmt, fileFmt, map[string]interface{}{
// Add the functions needed for the tree view here.
"fileSize": fileSizeFunc,
}, data)
if err != nil {
return template.HTML("Error while making tree view")
}
return out
"makeTreeViewData": func(f *filelist.FileListFolder, nestLevel int, T languages.TemplateTfunc, identifierChain string) interface{} {
return struct{
Folder *filelist.FileListFolder
NestLevel int
T languages.TemplateTfunc
IdentifierChain string
}{ f, nestLevel, T, identifierChain }
},
}

Voir le fichier

@ -45,7 +45,7 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) {
return
}
b := torrent.ToJSON()
folder := filelist.FileListToFolder(torrent.FileList)
folder := filelist.FileListToFolder(torrent.FileList, "root")
captchaID := ""
if userPermission.NeedsCaptcha(user) {
captchaID = captcha.GetID()

Voir le fichier

@ -1,5 +1,30 @@
{{define "title"}}{{.Torrent.Name}}{{end}}
{{define "contclass"}}cont-view {{if eq .Torrent.Status 2}}remake{{end}} {{if eq .Torrent.Status 3}}trusted{{end}} {{if eq .Torrent.Status 4}}aplus{{end}}{{end}}
{{ define "make_treeview" }}
{{ range $index, $folder := .Folder.Folders }}
{{ $folderId := (print $.IdentifierChain "_" $index) }}
<tr class="tr-filelist tr-folder" style="--nest-level: {{ $.NestLevel }}">
<td><label for="contents_{{$folderId}}">{{$folder.FolderName}}</label></td>
<td>{{ fileSize $folder.TotalSize $.T }}</td>
</tr>
<tr>
<td colspan="2">
<input id="contents_{{$folderId}}" type="checkbox" class="filelist-checkbox">
<table class="table-filelist">
{{ template "make_treeview" (makeTreeViewData $folder (inc $.NestLevel) $.T $folderId) }}
</table>
</td>
</tr>
{{ end }}
{{ range .Folder.Files }}
<tr class="tr-filelist tr-file" style="--nest-level: {{ $.NestLevel }}">
<td>{{.Filename}}</td>
<td>{{fileSize .Filesize $.T}}</td>
</tr>
{{ end }}
{{ end }}
{{define "content"}}
<div style="text-align: left;" class="box">
{{with .Torrent}}
@ -67,31 +92,10 @@
{{else}}
<p>No description provided!</p>
{{end}}
<p class="torrent-hr" id="filelist-control" onclick="javascript:toggleFilelist()" data-filelist-open="true">{{call $.T "files"}}</p>
<input type="checkbox" id="show-filelist">
<label class="torrent-hr filelist-control" for="show-filelist">{{call $.T "files"}}</label>
{{ if gt (len .FileList) 0 }}
{{/* how do i concat lol */}}
{{ $folderFormat := `<tr id="{{.Identifier}}" class='childs-of-{{.ParentIdentifier}} tr-filelist tr-folder' onclick="javascript:toggleFolder(this)" data-filelist-open="true" style="--nest-level: {{.NestLevel}}"><td>{{.FolderName}}</td><td>{{ fileSize .TotalSize .Data.T }}</td></tr>` }}
{{ $fileFormat := `<tr class='childs-of-{{.ParentIdentifier}} tr-filelist tr-file' style="--nest-level: {{.NestLevel}}"><td>{{.Filename}}</td><td>{{ fileSize .Filesize .Data.T }}</td>` }}
<script>
function toggleFilelist() {
var control = document.getElementById("filelist-control")
var filelist = document.getElementById("filelist")
filelist.hidden = !filelist.hidden
control.setAttribute("data-filelist-open", filelist.hidden ? "false" : "true")
}
function toggleFolder(folderNode) {
var isOpen = folderNode.getAttribute("data-filelist-open") == "true" ? true : false
var rows = document.querySelectorAll("*[class^='childs-of-" + folderNode.id + "']")
for (var i = 0; i < rows.length; i++) {
// If it's open (true), will hide, if not, will show.
rows[i].hidden = isOpen
}
folderNode.setAttribute("data-filelist-open", !isOpen ? "true" : "false")
}
</script>
<div class="torrent-info-box" id="filelist">
<table>
<thead>
@ -99,12 +103,10 @@
<th>{{call $.T "size"}}</th>
</thead>
<tbody>
{{ MakeFolderTreeView $.RootFolder $folderFormat $fileFormat $ }}
</tbody>
</table>
</div>
{{/* Make filelist hidden by default with JS, but still visible without it */}}
<script>toggleFilelist()</script>
{{ template "make_treeview" (makeTreeViewData $.RootFolder 0 $.T "root") }}
</tbody>
</table>
</div>
{{ else }}
<p>No files found? That doesn't even make sense!</p>
{{end}}

Voir le fichier

@ -1,23 +1,22 @@
package filelist
import (
"bytes"
"github.com/NyaaPantsu/nyaa/model"
"github.com/bradfitz/slice"
"html/template"
"strconv"
"strings"
)
type FileListFolder struct {
Folders map[string]*FileListFolder
Files []model.File
Folders []*FileListFolder
Files []model.File
FolderName string
}
func FileListToFolder(fileList []model.File) (out *FileListFolder) {
func FileListToFolder(fileList []model.File, folderName string) (out *FileListFolder) {
out = &FileListFolder{
Folders: make(map[string]*FileListFolder),
Files: make([]model.File, 0),
Folders: make([]*FileListFolder, 0),
Files: make([]model.File, 0),
FolderName: folderName,
}
pathsToFolders := make(map[string][]model.File)
@ -40,9 +39,16 @@ func FileListToFolder(fileList []model.File) (out *FileListFolder) {
}
for folderName, folderFiles := range pathsToFolders {
out.Folders[folderName] = FileListToFolder(folderFiles)
out.Folders = append(out.Folders, FileListToFolder(folderFiles, folderName))
}
// Do some sorting
slice.Sort(out.Folders, func(i, j int) bool {
return strings.ToLower(out.Folders[i].FolderName) < strings.ToLower(out.Folders[j].FolderName)
})
slice.Sort(out.Files, func(i, j int) bool {
return strings.ToLower(out.Files[i].Filename()) < strings.ToLower(out.Files[i].Filename())
})
return
}
@ -58,86 +64,3 @@ func (f *FileListFolder) TotalSize() (out int64) {
return
}
type folderFormatData struct {
Data interface{}
FolderName string
TotalSize int64
NestLevel uint
ParentIdentifier string
Identifier string
}
type fileFormatData struct {
Data interface{}
Filename string
Filesize int64
NestLevel uint
ParentIdentifier string
}
func execTemplateToHTML(tmpl *template.Template, data interface{}) (out template.HTML, err error) {
var buf bytes.Buffer
err = tmpl.Execute(&buf, data)
if err != nil {
return
}
out = template.HTML(buf.String())
return
}
func (f *FileListFolder) makeFolderTreeView(folderTmpl *template.Template, fileTmpl *template.Template, nestLevel uint, identifier string, data interface{}) (output template.HTML, err error) {
output = template.HTML("")
var tmp template.HTML
var folderNames []string // need this for sorting
for folderName, _ := range f.Folders {
folderNames = append(folderNames, folderName)
}
slice.Sort(folderNames, func(i, j int) bool {
return strings.ToLower(folderNames[i]) < strings.ToLower(folderNames[j])
})
for i, folderName := range folderNames {
folder := f.Folders[folderName]
childIdentifier := identifier + "_d" + strconv.Itoa(i)
// To the folder, our identifier is their parent identifier, and our child identifier is their own identifier.
tmp, err = execTemplateToHTML(folderTmpl, folderFormatData{data, folderName, folder.TotalSize(), nestLevel, identifier, childIdentifier})
if err != nil {
return
}
output += tmp
tmp, err = folder.makeFolderTreeView(folderTmpl, fileTmpl, nestLevel+1, childIdentifier, data)
if err != nil {
return
}
output += tmp
}
slice.Sort(f.Files, func(i, j int) bool {
return strings.ToLower(f.Files[i].Filename()) < strings.ToLower(f.Files[j].Filename())
})
for _, file := range f.Files {
tmp, err = execTemplateToHTML(fileTmpl, fileFormatData{data, file.Filename(), file.Filesize, nestLevel, identifier})
if err != nil {
return
}
output += tmp
}
return
}
func (f *FileListFolder) MakeFolderTreeView(folderFormat string, fileFormat string, funcMap template.FuncMap, data interface{}) (output template.HTML, err error) {
folderTmpl, err := template.New("folderTemplate").Funcs(funcMap).Parse(folderFormat)
if err != nil {
return
}
fileTmpl, err := template.New("fileTemplate").Funcs(funcMap).Parse(fileFormat)
if err != nil {
return
}
output, err = f.makeFolderTreeView(folderTmpl, fileTmpl, 0, "root", data)
return
}

Voir le fichier

@ -1,48 +0,0 @@
package filelist
import (
"github.com/NyaaPantsu/nyaa/model"
"html/template"
"testing"
)
func makeDummyFile(path ...string) (file model.File) {
file.SetPath(path)
return
}
func dashes(n uint) (out string) {
var i uint
for i = 0; i < n; i++ {
out += "-"
}
return
}
func TestFilelist(T *testing.T) {
files := []model.File{
makeDummyFile("A", "B", "C.txt"),
makeDummyFile("A", "C", "C.txt"),
makeDummyFile("B.txt"),
}
expected := "A\n" +
"-B\n" +
"--C.txt\n" +
"-C\n" +
"--C.txt\n" +
"B.txt\n"
filelist := FileListToFolder(files)
out, err := filelist.MakeFolderTreeView("{{dashes .NestLevel}}{{.FolderName}}\n", "{{dashes .NestLevel}}{{.Filename}}\n", map[string]interface{}{
"dashes": dashes,
}, nil)
if err != nil {
T.Fatalf("%v", err)
return
}
if out != template.HTML(expected) {
T.Fatalf("Error: expected %s, got %s", expected, out)
return
}
}