Skip to content

Commit

Permalink
Grid view (PoC)
Browse files Browse the repository at this point in the history
See #5967
  • Loading branch information
BurntimeX committed Sep 30, 2024
1 parent c42e754 commit be81740
Show file tree
Hide file tree
Showing 18 changed files with 693 additions and 2 deletions.
31 changes: 31 additions & 0 deletions com.woltlab.wcf/templates/shared_gridView.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="paginationTop">
<woltlab-core-pagination id="{$view->getID()}_topPagination" page="{$view->getPageNo()}" count="{$view->countPages()}"></woltlab-core-pagination>
</div>

<div class="section tabularBox">
<table class="table" id="{$view->getID()}_table">
<thead>
<tr>
{unsafe:$view->renderHeader()}
</td>
</thead>
<tbody>
{unsafe:$view->renderRows()}
</tbody>
</table>
</div>

<div class="paginationBottom">
<woltlab-core-pagination id="{$view->getID()}_bottomPagination" page="{$view->getPageNo()}" count="{$view->countPages()}"></woltlab-core-pagination>
</div>

<script data-relocate="true">
require(['WoltLabSuite/Core/Component/GridView'], ({ GridView }) => {
new GridView(
'{unsafe:$view->getID()|encodeJs}',
'{unsafe:$view->getClassName()|encodeJS}',
{$view->getPageNo()},
'{unsafe:$view->getBaseUrl()|encodeJS}'
);
});
</script>
21 changes: 21 additions & 0 deletions ts/WoltLabSuite/Core/Api/GridViews/GetRows.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { prepareRequest } from "WoltLabSuite/Core/Ajax/Backend";
import { ApiResult, apiResultFromError, apiResultFromValue } from "../Result";

type Response = {
template: string;
};

export async function getRows(gridViewClass: string, pageNo: number): Promise<ApiResult<Response>> {
const url = new URL(`${window.WSC_RPC_API_URL}core/gridViews/rows`);
url.searchParams.set("gridView", gridViewClass);
url.searchParams.set("pageNo", pageNo.toString());

let response: Response;
try {
response = (await prepareRequest(url).get().allowCaching().disableLoadingIndicator().fetchAsJson()) as Response;
} catch (e) {
return apiResultFromError(e);
}

return apiResultFromValue(response);
}
61 changes: 61 additions & 0 deletions ts/WoltLabSuite/Core/Component/GridView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { getRows } from "../Api/GridViews/GetRows";
import DomUtil from "../Dom/Util";

export class GridView {
readonly #gridClassName: string;
readonly #table: HTMLTableElement;
readonly #topPagination: WoltlabCorePaginationElement;
readonly #bottomPagination: WoltlabCorePaginationElement;
readonly #baseUrl: string;
#pageNo: number;

constructor(gridId: string, gridClassName: string, pageNo: number, baseUrl: string = "") {
this.#gridClassName = gridClassName;
this.#table = document.getElementById(`${gridId}_table`) as HTMLTableElement;
this.#topPagination = document.getElementById(`${gridId}_topPagination`) as WoltlabCorePaginationElement;
this.#bottomPagination = document.getElementById(`${gridId}_bottomPagination`) as WoltlabCorePaginationElement;
this.#pageNo = pageNo;
this.#baseUrl = baseUrl;

this.#initPagination();
}

#initPagination(): void {
this.#topPagination.addEventListener("switchPage", (event: CustomEvent) => {
this.#switchPage(event.detail);
});
this.#bottomPagination.addEventListener("switchPage", (event: CustomEvent) => {
this.#switchPage(event.detail);
});
}

async #switchPage(pageNo: number): Promise<void> {
this.#topPagination.page = pageNo;
this.#bottomPagination.page = pageNo;

const response = await getRows(this.#gridClassName, pageNo);
this.#pageNo = pageNo;
DomUtil.setInnerHtml(this.#table.querySelector("tbody")!, response.unwrap().template);
this.#updateQueryString();
}

#updateQueryString(): void {
if (!this.#baseUrl) {
return;
}

const url = new URL(this.#baseUrl);

const parameters: [string, string][] = [];
if (this.#pageNo > 1) {
parameters.push(["pageNo", this.#pageNo.toString()]);
}

if (parameters.length > 0) {
url.search += url.search !== "" ? "&" : "?";
url.search += new URLSearchParams(parameters).toString();
}

window.history.pushState({ name: "gridView" }, document.title, url.toString());
}
}
6 changes: 4 additions & 2 deletions wcfsetup/install/files/acp/templates/userRankList.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@
{/hascontent}

{if $objects|count}
<div class="section tabularBox">
{unsafe:$view->render()}

{*<div class="section tabularBox">
<table class="table jsObjectActionContainer" data-object-action-class-name="wcf\data\user\rank\UserRankAction">
<thead>
<tr>
Expand Down Expand Up @@ -85,7 +87,7 @@
{/foreach}
</tbody>
</table>
</div>
</div>*}

<footer class="contentFooter">
{hascontent}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions wcfsetup/install/files/js/WoltLabSuite/Core/Component/GridView.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions wcfsetup/install/files/lib/acp/page/UserRankListPage.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use wcf\data\user\rank\I18nUserRankList;
use wcf\page\SortablePage;
use wcf\system\request\LinkHandler;
use wcf\system\view\grid\UserRankGridView;
use wcf\system\WCF;

/**
* Lists available user ranks.
Expand Down Expand Up @@ -58,4 +61,16 @@ protected function initObjectList()
LEFT JOIN wcf' . WCF_N . '_user_group user_group
ON user_group.groupID = user_rank.groupID';
}

public function assignVariables()
{
parent::assignVariables();

$view = new UserRankGridView(isset($_GET['pageNo']) ? \intval($_GET['pageNo']) : 1);
$view->setBaseUrl(LinkHandler::getInstance()->getControllerLink(self::class));

WCF::getTPL()->assign([
'view' => $view,
]);
}
}
1 change: 1 addition & 0 deletions wcfsetup/install/files/lib/bootstrap/com.woltlab.wcf.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ static function (\wcf\event\endpoint\ControllerCollecting $event) {
$event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponse);
$event->register(new \wcf\system\endpoint\controller\core\comments\responses\RenderResponses);
$event->register(new \wcf\system\endpoint\controller\core\comments\responses\UpdateResponse);
$event->register(new \wcf\system\endpoint\controller\core\gridViews\GetRows);
$event->register(new \wcf\system\endpoint\controller\core\messages\GetMentionSuggestions);
$event->register(new \wcf\system\endpoint\controller\core\sessions\DeleteSession);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace wcf\system\endpoint\controller\core\gridViews;

use Laminas\Diactoros\Response\JsonResponse;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use wcf\http\Helper;
use wcf\system\endpoint\GetRequest;
use wcf\system\endpoint\IController;
use wcf\system\exception\PermissionDeniedException;
use wcf\system\exception\UserInputException;
use wcf\system\view\grid\AbstractGridView;

#[GetRequest('/core/gridViews/rows')]
final class GetRows implements IController
{
#[\Override]
public function __invoke(ServerRequestInterface $request, array $variables): ResponseInterface
{
$parameters = Helper::mapApiParameters($request, GetRowsParameters::class);

if (!\is_subclass_of($parameters->gridView, AbstractGridView::class)) {
throw new UserInputException('gridView', $parameters->gridView);
}

$view = new $parameters->gridView($parameters->pageNo);
\assert($view instanceof AbstractGridView);

if (!$view->isAccessible()) {
throw new PermissionDeniedException();
}

return new JsonResponse([
'template' => $view->renderRows(),
]);
}
}

/** @internal */
final class GetRowsParameters
{
public function __construct(
/** @var non-empty-string */
public readonly string $gridView,
public readonly int $pageNo
) {}
}
Loading

0 comments on commit be81740

Please sign in to comment.