web: port library page to clientside, router performance improvements

This commit is contained in:
Jens Langhammer 2020-11-30 12:33:09 +01:00
parent 775d80de6d
commit 1193608631
14 changed files with 287 additions and 387 deletions

View File

@ -1,6 +1,7 @@
"""Application API Views"""
from django.db.models import QuerySet
from rest_framework.decorators import action
from rest_framework.fields import SerializerMethodField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
@ -16,6 +17,12 @@ from passbook.policies.engine import PolicyEngine
class ApplicationSerializer(ModelSerializer):
"""Application Serializer"""
launch_url = SerializerMethodField()
def get_launch_url(self, instance: Application) -> str:
"""Get generated launch URL"""
return instance.get_launch_url() or ""
class Meta:
model = Application
@ -24,6 +31,7 @@ class ApplicationSerializer(ModelSerializer):
"name",
"slug",
"provider",
"launch_url",
"meta_launch_url",
"meta_icon",
"meta_description",
@ -47,11 +55,8 @@ class ApplicationViewSet(ModelViewSet):
queryset = backend().filter_queryset(self.request, queryset, self)
return queryset
def list(self, request: Request, *args, **kwargs) -> Response:
def list(self, request: Request) -> Response:
"""Custom list method that checks Policy based access instead of guardian"""
if request.user.is_superuser:
# pylint: disable=no-member
return super().list(request, *args, **kwargs)
queryset = self._filter_queryset_for_list(self.get_queryset())
self.paginate_queryset(queryset)
allowed_applications = []

View File

@ -97,14 +97,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -167,14 +162,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -315,14 +305,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -442,14 +427,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -588,14 +568,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -655,14 +630,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -747,14 +717,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -914,14 +879,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1041,14 +1001,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1168,14 +1123,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1295,14 +1245,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1421,14 +1366,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1548,14 +1488,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1675,14 +1610,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1770,14 +1700,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -1897,14 +1822,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2024,14 +1944,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2151,14 +2066,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2278,14 +2188,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2405,14 +2310,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2532,14 +2432,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2659,14 +2554,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2786,14 +2676,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2856,14 +2741,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -2983,14 +2863,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3110,14 +2985,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3237,14 +3107,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3306,14 +3171,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3432,14 +3292,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3558,14 +3413,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3716,14 +3566,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3786,14 +3631,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -3913,14 +3753,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4040,14 +3875,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4167,14 +3997,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4237,14 +4062,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4364,14 +4184,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4491,14 +4306,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4618,14 +4428,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4745,14 +4550,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4872,14 +4672,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -4939,14 +4734,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5126,14 +4916,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5253,14 +5038,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5380,14 +5160,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5507,14 +5282,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5634,14 +5404,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5761,14 +5526,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -5888,14 +5648,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -6015,14 +5770,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -6142,14 +5892,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -6269,14 +6014,9 @@ paths:
description: A search term.
required: false
type: string
- name: limit
- name: page
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
description: A page number within the paginated result set.
required: false
type: integer
responses:
@ -7640,6 +7380,18 @@ definitions:
title: Branding title
type: string
readOnly: true
error_reporting_enabled:
title: Error reporting enabled
type: string
readOnly: true
error_reporting_environment:
title: Error reporting environment
type: string
readOnly: true
error_reporting_send_pii:
title: Error reporting send pii
type: string
readOnly: true
Message:
description: Serialize Django Message into DRF Object
type: object

2
web/dist/main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
import { DefaultClient } from "./client";
import { DefaultClient, PBResponse } from "./client";
export class Application {
pk?: string;
@ -6,6 +6,7 @@ export class Application {
slug?: string;
provider?: number;
launch_url?: string;
meta_launch_url?: string;
meta_icon?: string;
meta_description?: string;
@ -15,4 +16,8 @@ export class Application {
static get(slug: string): Promise<Application> {
return DefaultClient.fetch<Application>(["core", "applications", slug]);
}
static list(filter?: { [key: string]: string }): Promise<PBResponse<Application>> {
return DefaultClient.fetch<PBResponse<Application>>(["core", "applications"], filter);
}
}

View File

@ -47,8 +47,8 @@ export interface PBPagination {
end_index: number;
}
export interface PBResponse {
export interface PBResponse<T> {
pagination: PBPagination;
results: Array<any>;
results: Array<T>;
}

View File

@ -18,7 +18,7 @@ export interface SidebarItem {
export const SIDEBAR_ITEMS: SidebarItem[] = [
{
name: "Library",
path: ["/-/overview/"],
path: ["/library/"],
},
{
name: "Monitor",

View File

@ -3,13 +3,13 @@ import { until } from "lit-html/directives/until.js";
import { PBResponse } from "../../api/client";
import { COMMON_STYLES } from "../../common/styles";
export abstract class Table extends LitElement {
abstract apiEndpoint(page: number): Promise<PBResponse>;
export abstract class Table<T> extends LitElement {
abstract apiEndpoint(page: number): Promise<PBResponse<T>>;
abstract columns(): Array<string>;
abstract row(item: any): Array<string>;
@property()
data?: PBResponse;
data?: PBResponse<T>;
@property()
page: number = 1;
@ -25,6 +25,27 @@ export abstract class Table extends LitElement {
});
}
private renderLoading(): TemplateResult {
return html`<tr role="row">
<td role="cell" colspan="25">
<div class="pf-l-bullseye">
<div class="pf-c-empty-state pf-m-sm">
<div class="pf-c-empty-state__content">
<div class="pf-c-empty-state__icon">
<span class="pf-c-spinner" role="progressbar">
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
<h2 class="pf-c-title pf-m-lg">Loading</h2>
</div>
</div>
</div>
</td>
</tr>`;
}
private renderRows(): TemplateResult[] | undefined {
if (!this.data) {
return;
@ -43,7 +64,6 @@ export abstract class Table extends LitElement {
render() {
if (!this.data) {
this.fetch();
return;
}
return html`<div class="pf-c-toolbar">
<div class="pf-c-toolbar__content">
@ -73,7 +93,7 @@ export abstract class Table extends LitElement {
</tr>
</thead>
<tbody role="rowgroup">
${this.renderRows()}
${this.data ? this.renderRows() : this.renderLoading()}
</tbody>
</table>
<div class="pf-c-pagination pf-m-bottom">

View File

@ -5,7 +5,7 @@ import { COMMON_STYLES } from "../../common/styles";
@customElement("pb-table-pagination")
export class TablePagination extends LitElement {
@property()
table?: Table;
table?: Table<any>;
static get styles() {
return [COMMON_STYLES];

View File

@ -15,6 +15,7 @@ import "./elements/sidebar/SidebarUser";
import "./elements/Tabs";
import "./elements/table/TablePagination";
import "./pages/applications/ApplicationViewPage";
import "./pages/applications/LibraryPage";
import "./pages/FlowShellCard";
import "./pages/RouterOutlet";
import "./pages/SiteShell";

View File

@ -49,8 +49,9 @@ export class Route {
export const SLUG_REGEX = "[-a-zA-Z0-9_]+";
export const ROUTES: Route[] = [
// Prevent infinite Shell loops
new Route(new RegExp(`^/$`)).redirect("/-/overview/"),
new Route(new RegExp(`^#.*`)).redirect("/-/overview/"),
new Route(new RegExp(`^/$`)).redirect("/library/"),
new Route(new RegExp(`^#.*`)).redirect("/library/"),
new Route(new RegExp(`^/library/$`), html`<pb-library></pb-library>`),
new Route(new RegExp(`^/applications/$`), html`<h1>test</h1>`),
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})/$`)).then((args) => {
return html`<pb-application-view .args=${args}></pb-application-view>`;
@ -114,7 +115,7 @@ export class RouterOutlet extends LitElement {
return;
}
let matchedRoute: RouteMatch | null = null;
ROUTES.forEach((route) => {
ROUTES.some((route) => {
console.debug(`passbook/router: matching ${activeUrl} against ${route.url}`);
const match = route.url.exec(activeUrl);
if (match != null) {
@ -122,7 +123,7 @@ export class RouterOutlet extends LitElement {
matchedRoute.arguments = match;
matchedRoute.fullUrl = activeUrl;
console.debug(`passbook/router: found match ${matchedRoute}`);
return;
return true;
}
});
if (!matchedRoute) {

View File

@ -5,12 +5,12 @@ import { COMMON_STYLES } from "../../common/styles";
import { Table } from "../../elements/table/Table";
@customElement("pb-bound-policies-list")
export class BoundPoliciesList extends Table {
export class BoundPoliciesList extends Table<any> {
@property()
target?: string;
apiEndpoint(page: number): Promise<PBResponse> {
return DefaultClient.fetch<PBResponse>(["policies", "bindings"], {
apiEndpoint(page: number): Promise<PBResponse<any>> {
return DefaultClient.fetch<PBResponse<any>>(["policies", "bindings"], {
target: this.target!,
ordering: "order",
page: page,

View File

@ -0,0 +1,108 @@
import { css, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { Application } from "../../api/application";
import { DefaultClient, PBResponse } from "../../api/client";
import { COMMON_STYLES } from "../../common/styles";
import { truncate } from "../../utils";
@customElement("pb-library")
export class ApplicationViewPage extends LitElement {
@property()
apps?: PBResponse<Application>;
static get styles() {
return COMMON_STYLES.concat(
css`
img.pf-icon {
max-height: 24px;
}
`
);
}
firstUpdated() {
Application.list().then((r) => (this.apps = r));
}
renderEmptyState() {
return html` <div class="pf-c-empty-state pf-m-full-height">
<div class="pf-c-empty-state__content">
<i class="fas fa-cubes pf-c-empty-state__icon" aria-hidden="true"></i>
<h1 class="pf-c-title pf-m-lg">{% trans 'No Applications available.' %}</h1>
<div class="pf-c-empty-state__body">
{% trans "Either no applications are defined, or you don't have access to any."
%}
</div>
{% if perms.passbook_core.add_application %}
<a
href="{% url 'passbook_admin:application-create' %}"
class="pf-c-button pf-m-primary"
type="button"
>
{% trans 'Create Application' %}
</a>
{% endif %}
</div>
</div>`;
}
renderApp(app: Application): TemplateResult {
return html` <a href="${app.launch_url}" class="pf-c-card pf-m-hoverable pf-m-compact">
<div class="pf-c-card__header">
${app.meta_icon
? html`<img
class="app-icon pf-c-avatar"
src="${app.meta_icon}"
alt="Application Icon"
/>`
: html`<i class="pf-icon pf-icon-arrow"></i>`}
</div>
<div class="pf-c-card__title">
<p id="card-1-check-label">${app.name}</p>
<div class="pf-c-content">
<small>${app.meta_publisher}</small>
</div>
</div>
<div class="pf-c-card__body">${truncate(app.meta_description, 35)}</div>
</a>`;
}
renderLoading() {
return html`<div class="pf-c-empty-state pf-m-full-height">
<div class="pf-c-empty-state__content">
<div class="pf-l-bullseye">
<div class="pf-l-bullseye__item">
<span
class="pf-c-spinner pf-m-xl"
role="progressbar"
aria-valuetext="Loading..."
>
<span class="pf-c-spinner__clipper"></span>
<span class="pf-c-spinner__lead-ball"></span>
<span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
</div>
</div>
</div>`;
}
render() {
return html`<main role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
<section class="pf-c-page__main-section pf-m-light">
<div class="pf-c-content">
<h1>
<i class="pf-icon pf-icon-applications"></i>
Applications
</h1>
</div>
</section>
${this.apps
? html`<section class="pf-c-page__main-section">
<div class="pf-l-gallery pf-m-gutter">
${this.apps.results.map((app) => this.renderApp(app))}
</div>
</section>`
: this.renderLoading()}
</main>`;
}
}

View File

@ -20,3 +20,11 @@ export function convertToSlug(text: string): string {
.replace(/ /g, "-")
.replace(/[^\w-]+/g, "");
}
export function truncate(input?: string, max = 10): string {
input = input || "";
const array = input.trim().split(" ");
const ellipsis = array.length > max ? "..." : "";
return array.slice(0, max).join(" ") + ellipsis;
}