Skip to content

Commit

Permalink
Add sorting function
Browse files Browse the repository at this point in the history
  • Loading branch information
BurntimeX committed Sep 9, 2024
1 parent e2252d2 commit 9835c34
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 22 deletions.
4 changes: 3 additions & 1 deletion com.woltlab.wcf/templates/shared_gridView.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
'{unsafe:$view->getID()|encodeJs}',
'{unsafe:$view->getClassName()|encodeJS}',
{$view->getPageNo()},
'{unsafe:$view->getBaseUrl()|encodeJS}'
'{unsafe:$view->getBaseUrl()|encodeJS}',
'{unsafe:$view->getSortField()|encodeJS}',
'{unsafe:$view->getSortOrder()|encodeJS}'
);
});
</script>
9 changes: 8 additions & 1 deletion ts/WoltLabSuite/Core/Api/GridViews/GetRows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,17 @@ type Response = {
template: string;
};

export async function getRows(gridViewClass: string, pageNo: number): Promise<ApiResult<Response>> {
export async function getRows(
gridViewClass: string,
pageNo: number,
sortField: string = "",
sortOrder: string = "ASC",
): 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());
url.searchParams.set("sortField", sortField);
url.searchParams.set("sortOrder", sortOrder);

let response: Response;
try {
Expand Down
72 changes: 65 additions & 7 deletions ts/WoltLabSuite/Core/Component/GridView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,87 @@ export class GridView {
readonly #bottomPagination: WoltlabCorePaginationElement;
readonly #baseUrl: string;
#pageNo: number;
#sortField: string;
#sortOrder: string;

constructor(gridId: string, gridClassName: string, pageNo: number, baseUrl: string = "") {
constructor(
gridId: string,
gridClassName: string,
pageNo: number,
baseUrl: string = "",
sortField = "",
sortOrder = "ASC",
) {
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.#sortField = sortField;
this.#sortOrder = sortOrder;

this.#initPagination();
this.#initSorting();
}

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

async #switchPage(pageNo: number): Promise<void> {
#initSorting(): void {
this.#table.querySelectorAll<HTMLTableCellElement>('th[data-sortable="1"]').forEach((element) => {
const link = document.createElement("a");
link.role = "button";
link.addEventListener("click", () => {
this.#sort(element.dataset.id!);
});

link.textContent = element.textContent;
element.innerHTML = "";
element.append(link);
});

this.#renderActiveSorting();
}

#sort(sortField: string): void {
if (this.#sortField == sortField && this.#sortOrder == "ASC") {
this.#sortOrder = "DESC";
} else {
this.#sortField = sortField;
this.#sortOrder = "ASC";
}

this.#loadRows();

Check failure on line 68 in ts/WoltLabSuite/Core/Component/GridView.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
this.#renderActiveSorting();
}

#renderActiveSorting(): void {
this.#table.querySelectorAll<HTMLTableCellElement>('th[data-sortable="1"]').forEach((element) => {
element.classList.remove("active", "ASC", "DESC");

if (element.dataset.id == this.#sortField) {
element.classList.add("active", this.#sortOrder);
}
});
}

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

const response = await getRows(this.#gridClassName, pageNo);
this.#pageNo = pageNo;

this.#loadRows();

Check failure on line 87 in ts/WoltLabSuite/Core/Component/GridView.ts

View workflow job for this annotation

GitHub Actions / eslint

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}

async #loadRows(): Promise<void> {
const response = await getRows(this.#gridClassName, this.#pageNo, this.#sortField, this.#sortOrder);
DomUtil.setInnerHtml(this.#table.querySelector("tbody")!, response.unwrap().template);
this.#updateQueryString();
}
Expand All @@ -50,12 +104,16 @@ export class GridView {
if (this.#pageNo > 1) {
parameters.push(["pageNo", this.#pageNo.toString()]);
}
if (this.#sortField) {
parameters.push(["sortField", this.#sortField]);
parameters.push(["sortOrder", this.#sortOrder]);
}

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

window.history.pushState({ name: "gridView" }, document.title, url.toString());
window.history.pushState({}, document.title, url.toString());
}
}

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

56 changes: 50 additions & 6 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.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __invoke(ServerRequestInterface $request, array $variables): Res
$parameters = Helper::mapApiParameters($request, GetRowsParameters::class);

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

$view = new $parameters->gridView($parameters->pageNo);
Expand All @@ -31,6 +31,13 @@ public function __invoke(ServerRequestInterface $request, array $variables): Res
throw new PermissionDeniedException();
}

if ($parameters->sortField) {
$view->setSortField($parameters->sortField);
}
if ($parameters->sortOrder) {
$view->setSortOrder($parameters->sortOrder);
}

return new JsonResponse([
'template' => $view->renderRows(),
]);
Expand All @@ -43,6 +50,8 @@ final class GetRowsParameters
public function __construct(
/** @var non-empty-string */
public readonly string $gridView,
public readonly int $pageNo
public readonly int $pageNo,
public readonly string $sortField,
public readonly string $sortOrder
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ abstract class AbstractGridView
private array $columns = [];
private int $rowsPerPage = 2;
private string $baseUrl = '';
private string $sortField = '';
private string $sortOrder = 'ASC';

public function __construct(private readonly int $pageNo = 1)
{
Expand Down Expand Up @@ -51,7 +53,11 @@ public function renderHeader(): string

foreach ($this->getColumns() as $column) {
$header .= <<<EOT
<th class="{$column->getClasses()}">{$column->getLabel()}</th>
<th
class="{$column->getClasses()}"
data-id="{$column->getID()}"
data-sortable="{$column->isSortable()}"
>{$column->getLabel()}</th>
EOT;
}

Expand Down Expand Up @@ -127,4 +133,40 @@ public function getBaseUrl(): string
{
return $this->baseUrl;
}

/**
* @return GridViewColumn[]
*/
public function getSortableColumns(): array
{
return \array_filter($this->getColumns(), fn($column) => $column->isSortable());
}

public function setSortField(string $sortField): void
{
if (!\in_array($sortField, \array_map(fn($column) => $column->getID(), $this->getSortableColumns()))) {
throw new \InvalidArgumentException("Invalid value '{$sortField}' as sort field given.");
}

$this->sortField = $sortField;
}

public function setSortOrder(string $sortOrder): void
{
if ($sortOrder !== 'ASC' && $sortOrder !== 'DESC') {
throw new \InvalidArgumentException("Invalid value '{$sortOrder}' as sort order given.");
}

$this->sortOrder = $sortOrder;
}

public function getSortField(): string
{
return $this->sortField;
}

public function getSortOrder(): string
{
return $this->sortOrder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@ final class GridViewColumn
* @var IColumnRenderer[]
*/
private array $renderer = [];

private string $label = '';

private static DefaultColumnRenderer $defaultRenderer;
private bool $sortable = false;

private function __construct(private readonly string $id) {}

Expand Down Expand Up @@ -72,6 +71,13 @@ public function label(string $languageItem): static
return $this;
}

public function sortable(bool $sortable = true): static
{
$this->sortable = $sortable;

return $this;
}

/**
* @return IColumnRenderer[]
*/
Expand All @@ -90,6 +96,11 @@ public function getLabel(): string
return $this->label;
}

public function isSortable(): bool
{
return $this->sortable;
}

private static function getDefaultRenderer(): DefaultColumnRenderer
{
if (!isset(self::$defaultRenderer)) {
Expand Down
Loading

0 comments on commit 9835c34

Please sign in to comment.