mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-28 03:50:10 +00:00
Compare commits
2 Commits
a0db685af2
...
47ce8a9ec4
Author | SHA1 | Date | |
---|---|---|---|
47ce8a9ec4 | |||
2d83718f81 |
@ -104,7 +104,7 @@ func (app *appContext) GetActivities(gc *gin.Context) {
|
|||||||
query = badgerhold.Where("Type").In(activityTypes...)
|
query = badgerhold.Where("Type").In(activityTypes...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Ascending {
|
if !req.Ascending {
|
||||||
query = query.Reverse()
|
query = query.Reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +125,7 @@ func (app *appContext) GetActivities(gc *gin.Context) {
|
|||||||
|
|
||||||
resp := GetActivitiesRespDTO{
|
resp := GetActivitiesRespDTO{
|
||||||
Activities: make([]ActivityDTO, len(results)),
|
Activities: make([]ActivityDTO, len(results)),
|
||||||
|
LastPage: len(results) != req.Limit,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, act := range results {
|
for i, act := range results {
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
color: rgba(0, 0, 0, 0) !important;
|
color: rgba(0, 0, 0, 0) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loader.rel {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.loader .dot {
|
.loader .dot {
|
||||||
--diameter: 0.5rem;
|
--diameter: 0.5rem;
|
||||||
--radius: calc(var(--diameter) / 2);
|
--radius: calc(var(--diameter) / 2);
|
||||||
@ -15,6 +19,12 @@
|
|||||||
left: calc(50% - var(--radius));
|
left: calc(50% - var(--radius));
|
||||||
animation: osc 1s cubic-bezier(.72,.16,.31,.97) infinite;
|
animation: osc 1s cubic-bezier(.72,.16,.31,.97) infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loader.rel .dot {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.loader.loader-sm .dot {
|
.loader.loader-sm .dot {
|
||||||
--deviation: 10%;
|
--deviation: 10%;
|
||||||
}
|
}
|
||||||
|
@ -732,34 +732,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<button class="button ~neutral @low ml-2" id="activity-sort-direction">{{ .strings.sortDirection }}</button>
|
||||||
<input type="search" class="field ~neutral @low input search ml-2 mr-2" id="activity-search" placeholder="{{ .strings.search }}">
|
<input type="search" class="field ~neutral @low input search ml-2 mr-2" id="activity-search" placeholder="{{ .strings.search }}">
|
||||||
<span class="button ~neutral @low center ml-[-2.64rem] rounded-s-none activity-search-clear" aria-label="{{ .strings.clearSearch }}" text="{{ .strings.clearSearch }}"><i class="ri-close-line"></i></span>
|
<span class="button ~neutral @low center ml-[-2.64rem] rounded-s-none activity-search-clear" aria-label="{{ .strings.clearSearch }}" text="{{ .strings.clearSearch }}"><i class="ri-close-line"></i></span>
|
||||||
|
<button class="button ~info @low ml-2" id="activity-refresh" aria-label="{{ .strings.refresh }}" disabled><i class="ri-refresh-line"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="supra py-1 sm hidden" id="activity-search-options-header">{{ .strings.searchOptions }}</div>
|
<div class="supra py-1 sm hidden" id="activity-search-options-header">{{ .strings.searchOptions }}</div>
|
||||||
<div class="row -mx-2 mb-2">
|
<div class="row -mx-2 mb-2">
|
||||||
<button type="button" class="button ~neutral @low center mx-2 hidden"><span id="activity-sort-by-field"></span> <i class="ri-close-line ml-2 text-2xl"></i></button>
|
<button type="button" class="button ~neutral @low center mx-2 hidden"><span id="activity-sort-by-field"></span> <i class="ri-close-line ml-2 text-2xl"></i></button>
|
||||||
<span id="activity-filter-area"></span>
|
<span id="activity-filter-area"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col md:flex-row gap-3">
|
<div class="my-2">
|
||||||
<div class="card @low dark:~d_neutral col max-w-[20%]">
|
<div id="activity-card-list"></div>
|
||||||
<div class="flex-expand">
|
<div id="activity-loader"></div>
|
||||||
<input type="search" class="field ~neutral @low input settings-section-button justify-between mb-2" id="settings-search" placeholder="{{ .strings.search }}">
|
<div class="unfocused h-[100%] my-3" id="activity-not-found">
|
||||||
<button class="button ~neutral @low center -ml-10 rounded-s-none mb-2 settings-search-clear" aria-label="{{ .strings.clearSearch }}" text="{{ .strings.clearSearch }}"><i class="ri-close-line"></i></button>
|
<div class="flex flex-col h-[100%] justify-center items-center">
|
||||||
|
<span class="text-2xl font-medium italic mb-3">{{ .strings.noResultsFound }}</span>
|
||||||
|
<button class="button ~neutral @low activity-search-clear">
|
||||||
|
<span class="mr-2">{{ .strings.clearSearch }}</span><i class="ri-close-line"></i>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<aside class="aside sm ~urge dark:~d_info mb-2 @low" id="settings-message">Note: <span class="badge ~critical">*</span> indicates a required field, <span class="badge ~info dark:~d_warning">R</span> indicates changes require a restart.</aside>
|
|
||||||
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-about"><span class="flex">{{ .strings.aboutProgram }} <i class="ri-information-line ml-2"></i></span></span>
|
|
||||||
<span class="button ~neutral @low settings-section-button justify-between mb-2" id="setting-profiles"><span class="flex">{{ .strings.userProfiles }} <i class="ri-user-line ml-2"></i></span></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral @low col">
|
<div class="flex justify-center">
|
||||||
<div id="activity-card-list"></div>
|
<button class="button my-2 ~neutral @low" id="activity-load-more">{{ .strings.loadMore }}</button>
|
||||||
<div class="unfocused h-[100%] my-3" id="activity-not-found">
|
|
||||||
<div class="flex flex-col h-[100%] justify-center items-center">
|
|
||||||
<span class="text-2xl font-medium italic mb-3">{{ .strings.noResultsFound }}</span>
|
|
||||||
<button class="button ~neutral @low activity-search-clear">
|
|
||||||
<span class="mr-2">{{ .strings.clearSearch }}</span><i class="ri-close-line"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -121,6 +121,7 @@
|
|||||||
"accessJFA": "Access jfa-go",
|
"accessJFA": "Access jfa-go",
|
||||||
"accessJFASettings": "Cannot be changed as either \"Admin Only\" or \"Allow All\" has been set in Settings > General.",
|
"accessJFASettings": "Cannot be changed as either \"Admin Only\" or \"Allow All\" has been set in Settings > General.",
|
||||||
"sortingBy": "Sorting By",
|
"sortingBy": "Sorting By",
|
||||||
|
"sortDirection": "Sort Direction",
|
||||||
"filters": "Filters",
|
"filters": "Filters",
|
||||||
"clickToRemoveFilter": "Click to remove this filter.",
|
"clickToRemoveFilter": "Click to remove this filter.",
|
||||||
"clearSearch": "Clear search",
|
"clearSearch": "Clear search",
|
||||||
@ -156,7 +157,7 @@
|
|||||||
"title": "Title",
|
"title": "Title",
|
||||||
"usersMentioned": "User mentioned",
|
"usersMentioned": "User mentioned",
|
||||||
"actor": "Actor",
|
"actor": "Actor",
|
||||||
"actorDescription": "The thing that caused this action. <hr class=\"sep\"> \"user\"/\"admin\"/\"daemon\" or a username.",
|
"actorDescription": "The thing that caused this action. \"user\"/\"admin\"/\"daemon\" or a username.",
|
||||||
"accountCreationFilter": "Account Creation",
|
"accountCreationFilter": "Account Creation",
|
||||||
"accountDeletionFilter": "Account Deletion",
|
"accountDeletionFilter": "Account Deletion",
|
||||||
"accountDisabledFilter": "Account Disabled",
|
"accountDisabledFilter": "Account Disabled",
|
||||||
@ -166,7 +167,9 @@
|
|||||||
"passwordChangeFilter": "Password Changed",
|
"passwordChangeFilter": "Password Changed",
|
||||||
"passwordResetFilter": "Password Reset",
|
"passwordResetFilter": "Password Reset",
|
||||||
"inviteCreatedFilter": "Invite Created",
|
"inviteCreatedFilter": "Invite Created",
|
||||||
"inviteDeletedFilter": "Invite Deleted/Expired"
|
"inviteDeletedFilter": "Invite Deleted/Expired",
|
||||||
|
"loadMore": "Load More",
|
||||||
|
"noMoreResults": "No more results."
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"changedEmailAddress": "Changed email address of {n}.",
|
"changedEmailAddress": "Changed email address of {n}.",
|
||||||
|
@ -453,4 +453,5 @@ type GetActivitiesDTO struct {
|
|||||||
|
|
||||||
type GetActivitiesRespDTO struct {
|
type GetActivitiesRespDTO struct {
|
||||||
Activities []ActivityDTO `json:"activities"`
|
Activities []ActivityDTO `json:"activities"`
|
||||||
|
LastPage bool `json:"last_page"`
|
||||||
}
|
}
|
||||||
|
@ -1808,7 +1808,7 @@ export class accountsList {
|
|||||||
queries: this._queries,
|
queries: this._queries,
|
||||||
setVisibility: this.setVisibility,
|
setVisibility: this.setVisibility,
|
||||||
clearSearchButtonSelector: ".accounts-search-clear",
|
clearSearchButtonSelector: ".accounts-search-clear",
|
||||||
onSearchCallback: () => {
|
onSearchCallback: (_0: number, _1: boolean) => {
|
||||||
this._checkCheckCount();
|
this._checkCheckCount();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { _post, _delete, toDateString } from "../modules/common.js";
|
import { _post, _delete, toDateString, addLoader, removeLoader } from "../modules/common.js";
|
||||||
import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modules/search.js";
|
import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modules/search.js";
|
||||||
|
|
||||||
export interface activity {
|
export interface activity {
|
||||||
@ -31,7 +31,7 @@ var activityTypeMoods = {
|
|||||||
|
|
||||||
export var activityReload = new CustomEvent("activity-reload");
|
export var activityReload = new CustomEvent("activity-reload");
|
||||||
|
|
||||||
export class Activity implements activity, SearchableItem { // FIXME: Add "implements"
|
export class Activity implements activity, SearchableItem {
|
||||||
private _card: HTMLElement;
|
private _card: HTMLElement;
|
||||||
private _title: HTMLElement;
|
private _title: HTMLElement;
|
||||||
private _time: HTMLElement;
|
private _time: HTMLElement;
|
||||||
@ -173,14 +173,6 @@ export class Activity implements activity, SearchableItem { // FIXME: Add "imple
|
|||||||
|
|
||||||
this._title.innerHTML = innerHTML.replace("{invite}", this._renderInvText());
|
this._title.innerHTML = innerHTML.replace("{invite}", this._renderInvText());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*} else if (this.source_type == "admin") {
|
|
||||||
// FIXME: Handle contactLinked/Unlinked, creation/deletion, enable/disable, createInvite/deleteInvite
|
|
||||||
} else if (this.source_type == "anon") {
|
|
||||||
this._referrer.innerHTML = ``;
|
|
||||||
} else if (this.source_type == "daemon") {
|
|
||||||
// FIXME: Handle deleteInvite, disabled, deletion
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get time(): number { return this._timeUnix; }
|
get time(): number { return this._timeUnix; }
|
||||||
@ -289,7 +281,6 @@ export class Activity implements activity, SearchableItem { // FIXME: Add "imple
|
|||||||
}
|
}
|
||||||
|
|
||||||
update = (act: activity) => {
|
update = (act: activity) => {
|
||||||
// FIXME
|
|
||||||
this._act = act;
|
this._act = act;
|
||||||
this.source_type = act.source_type;
|
this.source_type = act.source_type;
|
||||||
this.invite_code = act.invite_code;
|
this.invite_code = act.invite_code;
|
||||||
@ -312,6 +303,7 @@ export class Activity implements activity, SearchableItem { // FIXME: Add "imple
|
|||||||
|
|
||||||
interface ActivitiesDTO {
|
interface ActivitiesDTO {
|
||||||
activities: activity[];
|
activities: activity[];
|
||||||
|
last_page: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class activityList {
|
export class activityList {
|
||||||
@ -323,7 +315,16 @@ export class activityList {
|
|||||||
private _sortingByButton = document.getElementById("activity-sort-by-field") as HTMLButtonElement;
|
private _sortingByButton = document.getElementById("activity-sort-by-field") as HTMLButtonElement;
|
||||||
private _notFoundPanel = document.getElementById("activity-not-found");
|
private _notFoundPanel = document.getElementById("activity-not-found");
|
||||||
private _searchBox = document.getElementById("activity-search") as HTMLInputElement;
|
private _searchBox = document.getElementById("activity-search") as HTMLInputElement;
|
||||||
|
private _sortDirection = document.getElementById("activity-sort-direction") as HTMLButtonElement;
|
||||||
|
private _loader = document.getElementById("activity-loader");
|
||||||
|
private _loadMoreButton = document.getElementById("activity-load-more") as HTMLButtonElement;
|
||||||
|
private _refreshButton = document.getElementById("activity-refresh") as HTMLButtonElement;
|
||||||
private _search: Search;
|
private _search: Search;
|
||||||
|
private _ascending: boolean;
|
||||||
|
private _hasLoaded: boolean;
|
||||||
|
private _lastLoad: number;
|
||||||
|
private _page: number = 0;
|
||||||
|
private _lastPage: boolean;
|
||||||
|
|
||||||
|
|
||||||
setVisibility = (activities: string[], visible: boolean) => {
|
setVisibility = (activities: string[], visible: boolean) => {
|
||||||
@ -338,12 +339,70 @@ export class activityList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reload = () => {
|
reload = () => {
|
||||||
|
this._lastLoad = Date.now();
|
||||||
|
this._lastPage = false;
|
||||||
|
// this._page = 0;
|
||||||
|
let limit = 10;
|
||||||
|
if (this._page != 0) {
|
||||||
|
limit *= this._page+1;
|
||||||
|
};
|
||||||
|
|
||||||
let send = {
|
let send = {
|
||||||
"type": [],
|
"type": [],
|
||||||
"limit": 60,
|
"limit": limit,
|
||||||
"page": 0,
|
"page": 0,
|
||||||
"ascending": false
|
"ascending": this.ascending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_post("/activity", send, (req: XMLHttpRequest) => {
|
||||||
|
if (req.readyState != 4) return;
|
||||||
|
if (req.status != 200) {
|
||||||
|
window.notifications.customError("loadActivitiesError", window.lang.notif("errorLoadActivities"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._hasLoaded = true;
|
||||||
|
// Allow refreshes every 15s
|
||||||
|
this._refreshButton.disabled = true;
|
||||||
|
setTimeout(() => this._refreshButton.disabled = false, 15000);
|
||||||
|
|
||||||
|
let resp = req.response as ActivitiesDTO;
|
||||||
|
// FIXME: Don't destroy everything each reload!
|
||||||
|
this._activities = {};
|
||||||
|
this._ordering = [];
|
||||||
|
|
||||||
|
for (let act of resp.activities) {
|
||||||
|
this._activities[act.id] = new Activity(act);
|
||||||
|
this._ordering.push(act.id);
|
||||||
|
}
|
||||||
|
this._search.items = this._activities;
|
||||||
|
this._search.ordering = this._ordering;
|
||||||
|
|
||||||
|
if (this._search.inSearch) {
|
||||||
|
this._search.onSearchBoxChange(true);
|
||||||
|
} else {
|
||||||
|
this.setVisibility(this._ordering, true);
|
||||||
|
this._notFoundPanel.classList.add("unfocused");
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMore = () => {
|
||||||
|
this._lastLoad = Date.now();
|
||||||
|
this._loadMoreButton.disabled = true;
|
||||||
|
const timeout = setTimeout(() => this._loadMoreButton.disabled = false, 1000);
|
||||||
|
this._page += 1;
|
||||||
|
|
||||||
|
let send = {
|
||||||
|
"type": [],
|
||||||
|
"limit": 10,
|
||||||
|
"page": this._page,
|
||||||
|
"ascending": this._ascending
|
||||||
|
};
|
||||||
|
|
||||||
|
// this._activityList.classList.add("unfocused");
|
||||||
|
// addLoader(this._loader, false, true);
|
||||||
|
|
||||||
_post("/activity", send, (req: XMLHttpRequest) => {
|
_post("/activity", send, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState != 4) return;
|
if (req.readyState != 4) return;
|
||||||
if (req.status != 200) {
|
if (req.status != 200) {
|
||||||
@ -352,32 +411,32 @@ export class activityList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let resp = req.response as ActivitiesDTO;
|
let resp = req.response as ActivitiesDTO;
|
||||||
// FIXME: Don't destroy everything each reload!
|
|
||||||
this._activities = {};
|
this._lastPage = resp.last_page;
|
||||||
|
if (this._lastPage) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
this._loadMoreButton.disabled = true;
|
||||||
|
this._loadMoreButton.textContent = window.lang.strings("noMoreResults");
|
||||||
|
}
|
||||||
|
|
||||||
for (let act of resp.activities) {
|
for (let act of resp.activities) {
|
||||||
this._activities[act.id] = new Activity(act);
|
this._activities[act.id] = new Activity(act);
|
||||||
this._activityList.appendChild(this._activities[act.id].asElement());
|
this._ordering.push(act.id);
|
||||||
}
|
}
|
||||||
this._search.items = this._activities;
|
// this._search.items = this._activities;
|
||||||
// FIXME: Actually implement sorting
|
// this._search.ordering = this._ordering;
|
||||||
this._ordering = Object.keys(this._activities);
|
|
||||||
this._search.ordering = this._ordering;
|
|
||||||
|
|
||||||
if (this._search.inSearch) {
|
if (this._search.inSearch) {
|
||||||
const results = this._search.search(this._searchBox.value);
|
this._search.onSearchBoxChange(true);
|
||||||
this.setVisibility(results, true);
|
|
||||||
if (results.length == 0) {
|
|
||||||
this._notFoundPanel.classList.remove("unfocused");
|
|
||||||
} else {
|
|
||||||
this._notFoundPanel.classList.add("unfocused");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
this.setVisibility(this._ordering, true);
|
this.setVisibility(this._ordering, true);
|
||||||
this._notFoundPanel.classList.add("unfocused");
|
this._notFoundPanel.classList.add("unfocused");
|
||||||
}
|
}
|
||||||
|
// removeLoader(this._loader);
|
||||||
|
// this._activityList.classList.remove("unfocused");
|
||||||
}, true);
|
}, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _queries: { [field: string]: QueryType } = {
|
private _queries: { [field: string]: QueryType } = {
|
||||||
"id": {
|
"id": {
|
||||||
name: window.lang.strings("activityID"),
|
name: window.lang.strings("activityID"),
|
||||||
@ -494,6 +553,27 @@ export class activityList {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
get ascending(): boolean { return this._ascending; }
|
||||||
|
set ascending(v: boolean) {
|
||||||
|
this._ascending = v;
|
||||||
|
this._sortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="ri-arrow-${v ? "up" : "down"}-s-line ml-2"></i>`;
|
||||||
|
if (this._hasLoaded) {
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detectScroll = () => {
|
||||||
|
// console.log(window.innerHeight + document.documentElement.scrollTop, document.scrollingElement.scrollHeight);
|
||||||
|
if (Math.abs(window.innerHeight + document.documentElement.scrollTop - document.scrollingElement.scrollHeight) < 50) {
|
||||||
|
// window.notifications.customSuccess("scroll", "Reached bottom.");
|
||||||
|
// Wait 1s between loads
|
||||||
|
if (this._lastLoad + 1000 > Date.now()) return;
|
||||||
|
this.loadMore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _prevResultCount = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._activityList = document.getElementById("activity-card-list");
|
this._activityList = document.getElementById("activity-card-list");
|
||||||
document.addEventListener("activity-reload", this.reload);
|
document.addEventListener("activity-reload", this.reload);
|
||||||
@ -508,9 +588,24 @@ export class activityList {
|
|||||||
queries: this._queries,
|
queries: this._queries,
|
||||||
setVisibility: this.setVisibility,
|
setVisibility: this.setVisibility,
|
||||||
filterList: document.getElementById("activity-filter-list"),
|
filterList: document.getElementById("activity-filter-list"),
|
||||||
onSearchCallback: () => {}
|
onSearchCallback: (visibleCount: number, newItems: boolean) => {
|
||||||
|
|
||||||
|
if (visibleCount < 10) {
|
||||||
|
if (!newItems || this._prevResultCount != visibleCount || (visibleCount == 0 && !this._lastPage)) this.loadMore();
|
||||||
|
}
|
||||||
|
this._prevResultCount = visibleCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this._search = new Search(conf);
|
this._search = new Search(conf);
|
||||||
this._search.generateFilterList();
|
this._search.generateFilterList();
|
||||||
|
|
||||||
|
this._hasLoaded = false;
|
||||||
|
this.ascending = false;
|
||||||
|
this._sortDirection.addEventListener("click", () => this.ascending = !this.ascending);
|
||||||
|
|
||||||
|
this._loadMoreButton.onclick = this.loadMore;
|
||||||
|
this._refreshButton.onclick = this.reload;
|
||||||
|
|
||||||
|
window.onscroll = this.detectScroll;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,9 +199,10 @@ export function toggleLoader(el: HTMLElement, small: boolean = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addLoader(el: HTMLElement, small: boolean = true) {
|
export function addLoader(el: HTMLElement, small: boolean = true, relative: boolean = false) {
|
||||||
if (!el.classList.contains("loader")) {
|
if (!el.classList.contains("loader")) {
|
||||||
el.classList.add("loader");
|
el.classList.add("loader");
|
||||||
|
if (relative) el.classList.add("rel");
|
||||||
if (small) { el.classList.add("loader-sm"); }
|
if (small) { el.classList.add("loader-sm"); }
|
||||||
const dot = document.createElement("span") as HTMLSpanElement;
|
const dot = document.createElement("span") as HTMLSpanElement;
|
||||||
dot.classList.add("dot")
|
dot.classList.add("dot")
|
||||||
@ -213,6 +214,7 @@ export function removeLoader(el: HTMLElement, small: boolean = true) {
|
|||||||
if (el.classList.contains("loader")) {
|
if (el.classList.contains("loader")) {
|
||||||
el.classList.remove("loader");
|
el.classList.remove("loader");
|
||||||
el.classList.remove("loader-sm");
|
el.classList.remove("loader-sm");
|
||||||
|
el.classList.remove("rel");
|
||||||
const dot = el.querySelector("span.dot");
|
const dot = el.querySelector("span.dot");
|
||||||
if (dot) { dot.remove(); }
|
if (dot) { dot.remove(); }
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ export interface SearchConfiguration {
|
|||||||
search: HTMLInputElement;
|
search: HTMLInputElement;
|
||||||
queries: { [field: string]: QueryType };
|
queries: { [field: string]: QueryType };
|
||||||
setVisibility: (items: string[], visible: boolean) => void;
|
setVisibility: (items: string[], visible: boolean) => void;
|
||||||
onSearchCallback: () => void;
|
onSearchCallback: (visibleCount: number, newItems: boolean) => void;
|
||||||
|
loadMore?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchableItem {
|
export interface SearchableItem {
|
||||||
@ -267,7 +268,7 @@ export class Search {
|
|||||||
get ordering(): string[] { return this._ordering; }
|
get ordering(): string[] { return this._ordering; }
|
||||||
set ordering(v: string[]) { this._ordering = v; }
|
set ordering(v: string[]) { this._ordering = v; }
|
||||||
|
|
||||||
onSearchBoxChange = () => {
|
onSearchBoxChange = (newItems: boolean = false) => {
|
||||||
const query = this._c.search.value;
|
const query = this._c.search.value;
|
||||||
if (!query) {
|
if (!query) {
|
||||||
this.inSearch = false;
|
this.inSearch = false;
|
||||||
@ -276,7 +277,7 @@ export class Search {
|
|||||||
}
|
}
|
||||||
const results = this.search(query);
|
const results = this.search(query);
|
||||||
this._c.setVisibility(results, true);
|
this._c.setVisibility(results, true);
|
||||||
this._c.onSearchCallback();
|
this._c.onSearchCallback(results.length, newItems);
|
||||||
this.showHideSearchOptionsHeader();
|
this.showHideSearchOptionsHeader();
|
||||||
if (results.length == 0) {
|
if (results.length == 0) {
|
||||||
this._c.notFoundPanel.classList.remove("unfocused");
|
this._c.notFoundPanel.classList.remove("unfocused");
|
||||||
@ -294,6 +295,8 @@ export class Search {
|
|||||||
this._c.search.setSelectionRange(newPos, newPos);
|
this._c.search.setSelectionRange(newPos, newPos);
|
||||||
this._c.search.oninput(null as any);
|
this._c.search.oninput(null as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
generateFilterList = () => {
|
generateFilterList = () => {
|
||||||
// Generate filter buttons
|
// Generate filter buttons
|
||||||
@ -372,7 +375,7 @@ export class Search {
|
|||||||
constructor(c: SearchConfiguration) {
|
constructor(c: SearchConfiguration) {
|
||||||
this._c = c;
|
this._c = c;
|
||||||
|
|
||||||
this._c.search.oninput = this.onSearchBoxChange;
|
this._c.search.oninput = () => this.onSearchBoxChange();
|
||||||
|
|
||||||
const clearSearchButtons = Array.from(document.querySelectorAll(this._c.clearSearchButtonSelector)) as Array<HTMLSpanElement>;
|
const clearSearchButtons = Array.from(document.querySelectorAll(this._c.clearSearchButtonSelector)) as Array<HTMLSpanElement>;
|
||||||
for (let b of clearSearchButtons) {
|
for (let b of clearSearchButtons) {
|
||||||
|
Loading…
Reference in New Issue
Block a user