mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-11-22 04:17:33 +00:00
feat: redesign of some pages
This commit is contained in:
parent
fdb4250652
commit
3ed594ab88
@ -57,8 +57,9 @@ func postGet(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var postResult []models.PostListItem
|
var postResult []models.PostListItem
|
||||||
|
var tagStrings []string
|
||||||
for _, post := range posts {
|
for _, post := range posts {
|
||||||
var tagStrings []string
|
|
||||||
for _, tag := range post.Tags {
|
for _, tag := range post.Tags {
|
||||||
tagStrings = append(tagStrings, tag.TagType.Name+":"+tag.Name)
|
tagStrings = append(tagStrings, tag.TagType.Name+":"+tag.Name)
|
||||||
}
|
}
|
||||||
@ -67,7 +68,6 @@ func postGet(c *gin.Context) {
|
|||||||
ID: post.ID,
|
ID: post.ID,
|
||||||
ImageThumbnailPath: "/data/" + post.Blob.ThumbnailFilePath,
|
ImageThumbnailPath: "/data/" + post.Blob.ThumbnailFilePath,
|
||||||
ImagePath: "/data/" + post.Blob.FilePath,
|
ImagePath: "/data/" + post.Blob.FilePath,
|
||||||
Tags: tagStrings,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, models.PostPaginationResponse{
|
c.JSON(http.StatusOK, models.PostPaginationResponse{
|
||||||
@ -75,6 +75,7 @@ func postGet(c *gin.Context) {
|
|||||||
TotalPage: totalPage,
|
TotalPage: totalPage,
|
||||||
PostCount: postPages,
|
PostCount: postPages,
|
||||||
Posts: postResult,
|
Posts: postResult,
|
||||||
|
Tags: tagStrings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +17,9 @@ type TagAutocompleteListItem struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type PostListItem struct {
|
type PostListItem struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
ImagePath string `json:"image_path"`
|
ImagePath string `json:"image_path"`
|
||||||
ImageThumbnailPath string `json:"thumbnail_path"`
|
ImageThumbnailPath string `json:"thumbnail_path"`
|
||||||
Tags []string `json:"tags"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostSimilarityListItem struct {
|
type PostSimilarityListItem struct {
|
||||||
|
@ -31,4 +31,5 @@ type PostPaginationResponse struct {
|
|||||||
TotalPage int `json:"totalPage"`
|
TotalPage int `json:"totalPage"`
|
||||||
PostCount int `json:"postCount"`
|
PostCount int `json:"postCount"`
|
||||||
Posts []PostListItem `json:"posts"`
|
Posts []PostListItem `json:"posts"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
}
|
}
|
||||||
|
12
web/app/src/AuthCheck.svelte
Normal file
12
web/app/src/AuthCheck.svelte
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script>
|
||||||
|
import { token } from "./stores.js";
|
||||||
|
|
||||||
|
let loggedIn = false;
|
||||||
|
token.subscribe((value) => {
|
||||||
|
loggedIn = value !== "";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if loggedIn == true}
|
||||||
|
<slot />
|
||||||
|
{/if}
|
90
web/app/src/EditPostPanel.svelte
Normal file
90
web/app/src/EditPostPanel.svelte
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
<script>
|
||||||
|
import Tags from "svelte-tags-input";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { getPost, postUpdate, getTagAutocomplete } from "./api.js";
|
||||||
|
|
||||||
|
export let isActive = false;
|
||||||
|
export let post;
|
||||||
|
export let onSubmit;
|
||||||
|
|
||||||
|
const toggleEditModal = () => {
|
||||||
|
isActive = !isActive;
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = {
|
||||||
|
source_url: "",
|
||||||
|
tags: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
form.source_url = post.source_url;
|
||||||
|
form.tags = post.tags;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTagChange = (value) => {
|
||||||
|
form.tags = value.detail.tags;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAutocomplete = async () => {
|
||||||
|
const list = await getTagAutocomplete();
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFormSubmit = async () => {
|
||||||
|
const response = await postUpdate(post.id, form);
|
||||||
|
toggleEditModal();
|
||||||
|
|
||||||
|
onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
getData();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form on:submit|preventDefault={onFormSubmit}>
|
||||||
|
<div class="panel is-warning">
|
||||||
|
<p class="panel-heading">Edit Post</p>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<label for="source" class="label">Source URL</label>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
id="source"
|
||||||
|
class="input"
|
||||||
|
type="url"
|
||||||
|
placeholder="Source URL"
|
||||||
|
bind:value={form.source_url}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<label for="tags" class="label">Tags</label>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="field">
|
||||||
|
<div class="control" id="tags">
|
||||||
|
<Tags
|
||||||
|
tags={form.tags}
|
||||||
|
addKeys={[9, 32]}
|
||||||
|
on:tags={onTagChange}
|
||||||
|
autoComplete={onAutocomplete}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<button class="button is-primary" type="submit">Save</button>
|
||||||
|
<button class="button" on:click|preventDefault={toggleEditModal}
|
||||||
|
>Cancel</button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -8,4 +8,4 @@
|
|||||||
let tagDisplay = tagName.split("_").join(" ");
|
let tagDisplay = tagName.split("_").join(" ");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Link class="button is-rounded is-primary is-small m-1" to="/posts?tags={tagName}">{tagDisplay}</Link>
|
<Link to="/posts?tags={tagName}">{tagDisplay}</Link>
|
||||||
|
13
web/app/src/TagLinkNumbered.svelte
Normal file
13
web/app/src/TagLinkNumbered.svelte
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<script>
|
||||||
|
import { Link } from "svelte-routing";
|
||||||
|
|
||||||
|
export let tag;
|
||||||
|
export let num;
|
||||||
|
|
||||||
|
let tagType = tag.split(":")[0] ?? "";
|
||||||
|
let tagName = tag.split(":")[1] ?? "";
|
||||||
|
let tagDisplay = tagName.split("_").join(" ");
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Link to="/posts?tags={tagName}">{tagDisplay} <span class="is-pulled-right">{num}</span></Link>
|
78
web/app/src/ViewPostPanel.svelte
Normal file
78
web/app/src/ViewPostPanel.svelte
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<script>
|
||||||
|
import AuthCheck from "./AuthCheck.svelte";
|
||||||
|
import TagLink from "./TagLink.svelte";
|
||||||
|
export let post;
|
||||||
|
export let toggleEditMenu;
|
||||||
|
export let toggleDeleteMenu;
|
||||||
|
|
||||||
|
const trimUrl = (str) => {
|
||||||
|
if (str.length > 30) {
|
||||||
|
return str.substring(0, 30) + "...";
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="panel is-primary">
|
||||||
|
<p class="panel-heading">Post</p>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<strong>Uploader:</strong>
|
||||||
|
</div>
|
||||||
|
<div class="row">{post.uploader}</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<strong>Source URL:</strong>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<a href={post.source_url}>{trimUrl(post.source_url)}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<strong>Original:</strong>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<a href={post.image_path}>Image</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<strong>Dimensions:</strong>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{post.width}x{post.height}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-block column">
|
||||||
|
<div class="row">
|
||||||
|
<p><strong>Tags:</strong></p>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="menu">
|
||||||
|
<ul class="menu-list">
|
||||||
|
{#if post.tags}
|
||||||
|
{#each post.tags as tag (tag)}
|
||||||
|
<li>
|
||||||
|
<TagLink class="" {tag} />
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<AuthCheck>
|
||||||
|
<p class="panel-block column">
|
||||||
|
<button
|
||||||
|
on:click|preventDefault={toggleEditMenu}
|
||||||
|
class="button is-primary">Edit</button
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
on:click|preventDefault={toggleDeleteMenu}
|
||||||
|
class="button is-danger">Delete</button
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</AuthCheck>
|
||||||
|
</div>
|
@ -12,5 +12,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.svelte-tags-input-matchs-parent {
|
.svelte-tags-input-matchs-parent {
|
||||||
z-index: 200;
|
z-index: 2000;
|
||||||
}
|
}
|
@ -3,6 +3,8 @@
|
|||||||
import TagLink from "../TagLink.svelte";
|
import TagLink from "../TagLink.svelte";
|
||||||
import { getPost, postCreate, postDelete } from "../api.js";
|
import { getPost, postCreate, postDelete } from "../api.js";
|
||||||
import { Link, navigate } from "svelte-routing";
|
import { Link, navigate } from "svelte-routing";
|
||||||
|
import EditPostPanel from "../EditPostPanel.svelte";
|
||||||
|
import ViewPostPanel from "../ViewPostPanel.svelte";
|
||||||
export let id;
|
export let id;
|
||||||
let post;
|
let post;
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
@ -21,70 +23,62 @@
|
|||||||
getData();
|
getData();
|
||||||
});
|
});
|
||||||
|
|
||||||
let modal_shown = false;
|
let deleteMenuShown = false;
|
||||||
|
|
||||||
const deletePost = async () => {
|
const deletePost = async () => {
|
||||||
toggleModal();
|
toggleDeleteMenu();
|
||||||
const success = await postDelete({ id });
|
const success = await postDelete({ id });
|
||||||
if (success) {
|
if (success) {
|
||||||
navigate("/posts");
|
navigate("/posts");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const toggleModal = () => {
|
const toggleDeleteMenu = () => {
|
||||||
modal_shown = !modal_shown;
|
deleteMenuShown = !deleteMenuShown;
|
||||||
|
};
|
||||||
|
|
||||||
|
let editMenuShown = false;
|
||||||
|
|
||||||
|
const toggleEditMenu = () => {
|
||||||
|
editMenuShown = !editMenuShown;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="hero is-primary">
|
|
||||||
<div class="hero-body">
|
|
||||||
{#if post}
|
|
||||||
<p class="title">
|
|
||||||
Post ID: {post.id}
|
|
||||||
</p>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{#if post}
|
{#if post}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column is-one-third box">
|
<div class="column is-one-third">
|
||||||
<div class="content">
|
{#if editMenuShown == false && deleteMenuShown == false}
|
||||||
<p>
|
<ViewPostPanel
|
||||||
<Link
|
{post}
|
||||||
class="button is-primary"
|
{toggleDeleteMenu}
|
||||||
to="/post/edit/{post.id}">Edit</Link
|
{toggleEditMenu}
|
||||||
>
|
/>
|
||||||
<button
|
{:else if editMenuShown == true}
|
||||||
on:click|preventDefault={toggleModal}
|
<EditPostPanel
|
||||||
class="button is-danger">Delete</button
|
bind:isActive={editMenuShown}
|
||||||
>
|
{post}
|
||||||
</p>
|
onSubmit={getData}
|
||||||
<p>
|
/>
|
||||||
Uploader: {post.uploader}
|
{:else if deleteMenuShown == true}
|
||||||
</p>
|
<div class="panel is-danger">
|
||||||
<p>
|
<p class="panel-heading">Delete Post</p>
|
||||||
Source URL: <a href={post.source_url}
|
<div class="panel-block">
|
||||||
>{trimUrl(post.source_url)}</a
|
Are you sure to delete post {post.id}?
|
||||||
>
|
</div>
|
||||||
</p>
|
<div class="panel-block column">
|
||||||
<p>
|
<button
|
||||||
Original: <a href={post.image_path}>Image</a>
|
on:click|preventDefault={deletePost}
|
||||||
</p>
|
class="button is-danger">Delete</button
|
||||||
<p>
|
>
|
||||||
Dimensions: {post.width}x{post.height}
|
<button
|
||||||
</p>
|
class="button"
|
||||||
<p>
|
on:click|preventDefault={toggleDeleteMenu}
|
||||||
Tags:<br />
|
>Cancel</button
|
||||||
</p>
|
>
|
||||||
<p>
|
</div>
|
||||||
{#if post.tags}
|
</div>
|
||||||
{#each post.tags as tag (tag)}
|
{/if}
|
||||||
<TagLink {tag} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="column box">
|
<div class="column box">
|
||||||
{#if post.width > 1000}
|
{#if post.width > 1000}
|
||||||
@ -105,26 +99,4 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal" class:is-active={modal_shown}>
|
|
||||||
<div class="modal-background" />
|
|
||||||
<div class="modal-content">
|
|
||||||
Are you sure to delete post {post.id}?
|
|
||||||
</div>
|
|
||||||
<div class="modal-card">
|
|
||||||
<header class="modal-card-head">
|
|
||||||
<p class="modal-card-title">Delete post?</p>
|
|
||||||
<button class="delete" aria-label="close" />
|
|
||||||
</header>
|
|
||||||
<section class="modal-card-body" />
|
|
||||||
<footer class="modal-card-foot">
|
|
||||||
<button
|
|
||||||
on:click|preventDefault={deletePost}
|
|
||||||
class="button is-danger">Delete</button
|
|
||||||
>
|
|
||||||
<button class="button" on:click|preventDefault={toggleModal}
|
|
||||||
>Cancel</button
|
|
||||||
>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -3,47 +3,47 @@
|
|||||||
import { getPostSearchTag, getTagAutocomplete } from "../api.js";
|
import { getPostSearchTag, getTagAutocomplete } from "../api.js";
|
||||||
import { Link, navigate } from "svelte-routing";
|
import { Link, navigate } from "svelte-routing";
|
||||||
import InfiniteScroll from "svelte-infinite-scroll";
|
import InfiniteScroll from "svelte-infinite-scroll";
|
||||||
import TagLink from "../TagLink.svelte";
|
import TagLinkNumbered from "../TagLinkNumbered.svelte";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
import Tags from "svelte-tags-input";
|
import Tags from "svelte-tags-input";
|
||||||
import { add_attribute } from "svelte/internal";
|
import { add_attribute } from "svelte/internal";
|
||||||
|
import { paginate } from "../simple-pagination.js";
|
||||||
|
|
||||||
export let location;
|
export let location;
|
||||||
|
|
||||||
let searchTerms = [];
|
let searchTerms = [];
|
||||||
|
|
||||||
let page = 1;
|
let page = 1;
|
||||||
|
let totalPages = 1;
|
||||||
|
let pagination = [];
|
||||||
let posts = [];
|
let posts = [];
|
||||||
let newBatch = [];
|
let tags = [];
|
||||||
|
let categorizedTags = {};
|
||||||
const splitToChunks = (array, parts) => {
|
|
||||||
let result = [];
|
|
||||||
for (let i = 0; i < parts; i++) {
|
|
||||||
let currentColumn = [];
|
|
||||||
for (let j = i; j < array.length; j += parts) {
|
|
||||||
currentColumn.push(array[j]);
|
|
||||||
}
|
|
||||||
result.push(currentColumn);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
let postChunks = [];
|
|
||||||
// split posts into 4 columns
|
|
||||||
$: {
|
|
||||||
postChunks = splitToChunks(posts, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
|
const data = await getPostSearchTag({ page, q: searchTerms.join("+") });
|
||||||
if (data.posts) {
|
if (data.posts) {
|
||||||
newBatch = data.posts;
|
posts = data.posts;
|
||||||
|
tags = data.tags;
|
||||||
|
totalPages = data.totalPage;
|
||||||
|
pagination = paginate(page, totalPages);
|
||||||
} else {
|
} else {
|
||||||
newBatch = [];
|
posts = [];
|
||||||
|
tags = [];
|
||||||
|
totalPages = 0;
|
||||||
|
pagination = paginate(page, totalPages);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
$: {
|
$: {
|
||||||
posts = [...posts, ...newBatch];
|
let catTags = tags.reduce(
|
||||||
|
(acc, o) => ((acc[o] = (acc[o] || 0) + 1), acc),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
categorizedTags = Object.entries(catTags).map(([k, v]) => ({
|
||||||
|
tag: k,
|
||||||
|
num: v,
|
||||||
|
}));
|
||||||
|
categorizedTags = categorizedTags.sort((a, b) => b.num - a.num);
|
||||||
}
|
}
|
||||||
let queryParams;
|
let queryParams;
|
||||||
|
|
||||||
@ -64,9 +64,9 @@
|
|||||||
searchTerms = [];
|
searchTerms = [];
|
||||||
}
|
}
|
||||||
posts = [];
|
posts = [];
|
||||||
|
page = 1;
|
||||||
getData();
|
getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSearch = (i) => {
|
const onSearch = (i) => {
|
||||||
if (searchTerms.length > 0) {
|
if (searchTerms.length > 0) {
|
||||||
navigate(`/posts?tags=${searchTerms.join("+")}`);
|
navigate(`/posts?tags=${searchTerms.join("+")}`);
|
||||||
@ -74,13 +74,12 @@
|
|||||||
navigate(`/posts`);
|
navigate(`/posts`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
|
||||||
|
|
||||||
<section class="hero is-primary">
|
const changePage = (i) => {
|
||||||
<div class="hero-body">
|
page = i;
|
||||||
<p class="title">Posts</p>
|
getData();
|
||||||
</div>
|
}
|
||||||
</section>
|
</script>
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@ -107,46 +106,101 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
{#each postChunks as postChunk}
|
<div class="column is-one-third">
|
||||||
<div class="column is-one-fifth">
|
<div class="panel is-primary">
|
||||||
{#each postChunk as post, i (post.id)}
|
<div class="panel-heading">Tags</div>
|
||||||
<div class="block">
|
<div class="panel-block column">
|
||||||
<div class="card">
|
<div class="menu">
|
||||||
<div class="card-image">
|
<ul class="menu-list">
|
||||||
<figure class="image">
|
{#each categorizedTags as tag (tag)}
|
||||||
<Link to="/post/{post.id}">
|
<li>
|
||||||
<img
|
<TagLinkNumbered
|
||||||
alt={post.id}
|
class=""
|
||||||
src={post.thumbnail_path}
|
tag={tag.tag}
|
||||||
/>
|
num={tag.num}
|
||||||
</Link>
|
/>
|
||||||
</figure>
|
</li>
|
||||||
</div>
|
{/each}
|
||||||
<div class="card-content">
|
</ul>
|
||||||
{#if post.tags}
|
|
||||||
{#each post.tags as tag (tag)}
|
|
||||||
<TagLink {tag} />
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<TagLink tag="tagme" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
</div>
|
||||||
|
<div class="column is-two-thirds">
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
<div class="column is-full">
|
||||||
|
<nav
|
||||||
|
class="pagination is-centered"
|
||||||
|
role="navigation"
|
||||||
|
aria-label="pagination"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={null}
|
||||||
|
on:click={changePage(page - 1)}
|
||||||
|
class="pagination-previous">Previous</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={null}
|
||||||
|
on:click={changePage(page + 1)}
|
||||||
|
class="pagination-next">Next page</a
|
||||||
|
>
|
||||||
|
<ul class="pagination-list">
|
||||||
|
{#each pagination as pageEntry}
|
||||||
|
{#if pageEntry == "..."}
|
||||||
|
<li>
|
||||||
|
<span
|
||||||
|
class="pagination-ellipsis"
|
||||||
|
>…</span
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
{:else}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href={null}
|
||||||
|
on:click={() =>
|
||||||
|
(changePage(pageEntry))}
|
||||||
|
class="pagination-link"
|
||||||
|
class:is-current={page ==
|
||||||
|
pageEntry}
|
||||||
|
aria-label="Goto page {pageEntry}"
|
||||||
|
>{pageEntry}</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="column is-full">
|
||||||
|
<div class="columns is-multiline">
|
||||||
|
{#each posts as post, i (post.id)}
|
||||||
|
<div class="column is-one-third">
|
||||||
|
<div class="block">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-image">
|
||||||
|
<figure class="image">
|
||||||
|
<Link
|
||||||
|
to="/post/{post.id}"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
alt={post.id}
|
||||||
|
src={post.thumbnail_path}
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
<div class="card-content" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<InfiniteScroll
|
|
||||||
hasMore={newBatch.length}
|
|
||||||
elementScroll={document}
|
|
||||||
on:loadMore={() => {
|
|
||||||
page++;
|
|
||||||
getData();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{#if newBatch.length == 0}
|
{#if page >= totalPages}
|
||||||
<div class="notification is-primary">
|
<div class="notification is-primary">
|
||||||
<p class="has-text-centered">End of posts</p>
|
<p class="has-text-centered">End of posts</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,12 +14,6 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<section class="hero is-primary">
|
|
||||||
<div class="hero-body">
|
|
||||||
<p class="title">Tags</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<table class="table is-fullwidth">
|
<table class="table is-fullwidth">
|
||||||
|
@ -51,12 +51,6 @@
|
|||||||
|
|
||||||
<AuthRequired />
|
<AuthRequired />
|
||||||
|
|
||||||
<section class="hero is-primary">
|
|
||||||
<div class="hero-body">
|
|
||||||
<p class="title">Upload</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form on:submit|preventDefault={onSubmit}>
|
<form on:submit|preventDefault={onSubmit}>
|
||||||
|
64
web/app/src/simple-pagination.js
Normal file
64
web/app/src/simple-pagination.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Implementation in ES6
|
||||||
|
// https://gist.github.com/kottenator/9d936eb3e4e3c3e02598
|
||||||
|
|
||||||
|
const paginate = (c, m) => {
|
||||||
|
let current = c,
|
||||||
|
last = m,
|
||||||
|
delta = 2,
|
||||||
|
left = current - delta,
|
||||||
|
right = current + delta + 1,
|
||||||
|
range = [],
|
||||||
|
rangeWithDots = [],
|
||||||
|
l;
|
||||||
|
|
||||||
|
for (let i = 1; i <= last; i++) {
|
||||||
|
if (i == 1 || i == last || i >= left && i < right) {
|
||||||
|
range.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i of range) {
|
||||||
|
if (l) {
|
||||||
|
if (i - l === 2) {
|
||||||
|
rangeWithDots.push(l + 1);
|
||||||
|
} else if (i - l !== 1) {
|
||||||
|
rangeWithDots.push('...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rangeWithDots.push(i);
|
||||||
|
l = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rangeWithDots;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { paginate };
|
||||||
|
/*
|
||||||
|
Test it:
|
||||||
|
|
||||||
|
for (let i = 1, l = 20; i <= l; i++)
|
||||||
|
console.log(`Selected page ${i}:`, pagination(i, l));
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
|
||||||
|
Selected page 1: [1, 2, 3, "...", 20]
|
||||||
|
Selected page 2: [1, 2, 3, 4, "...", 20]
|
||||||
|
Selected page 3: [1, 2, 3, 4, 5, "...", 20]
|
||||||
|
Selected page 4: [1, 2, 3, 4, 5, 6, "...", 20]
|
||||||
|
Selected page 5: [1, 2, 3, 4, 5, 6, 7, "...", 20]
|
||||||
|
Selected page 6: [1, "...", 4, 5, 6, 7, 8, "...", 20]
|
||||||
|
Selected page 7: [1, "...", 5, 6, 7, 8, 9, "...", 20]
|
||||||
|
Selected page 8: [1, "...", 6, 7, 8, 9, 10, "...", 20]
|
||||||
|
Selected page 9: [1, "...", 7, 8, 9, 10, 11, "...", 20]
|
||||||
|
Selected page 10: [1, "...", 8, 9, 10, 11, 12, "...", 20]
|
||||||
|
Selected page 11: [1, "...", 9, 10, 11, 12, 13, "...", 20]
|
||||||
|
Selected page 12: [1, "...", 10, 11, 12, 13, 14, "...", 20]
|
||||||
|
Selected page 13: [1, "...", 11, 12, 13, 14, 15, "...", 20]
|
||||||
|
Selected page 14: [1, "...", 12, 13, 14, 15, 16, "...", 20]
|
||||||
|
Selected page 15: [1, "...", 13, 14, 15, 16, 17, "...", 20]
|
||||||
|
Selected page 16: [1, "...", 14, 15, 16, 17, 18, 19, 20]
|
||||||
|
Selected page 17: [1, "...", 15, 16, 17, 18, 19, 20]
|
||||||
|
Selected page 18: [1, "...", 16, 17, 18, 19, 20]
|
||||||
|
Selected page 19: [1, "...", 17, 18, 19, 20]
|
||||||
|
Selected page 20: [1, "...", 18, 19, 20]
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user