static: add RouterOutlet, render sidebar clientside

This commit is contained in:
Jens Langhammer 2020-11-22 00:06:25 +01:00
parent a8669ffe40
commit 7dac6841fb
9 changed files with 274 additions and 226 deletions

View File

@ -14,161 +14,10 @@
{% block page_content %} {% block page_content %}
<pb-admin-sidebar class="pf-c-page__sidebar"> <pb-admin-sidebar class="pf-c-page__sidebar">
<div class="pf-c-page__sidebar-body">
<nav class="pf-c-nav" id="page-default-nav-example-primary-nav" aria-label="Global">
<ul class="pf-c-nav__list">
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:overview' %}"
class="pf-c-nav__link">
{% trans 'Overview' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:applications' %}"
class="pf-c-nav__link">
{% trans 'Applications' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:sources' %}"
class="pf-c-nav__link">
{% trans 'Sources' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:providers' %}"
class="pf-c-nav__link">
{% trans 'Providers' %}
</a>
</li>
<li class="pf-c-nav__item pf-m-expanded">
<a href="" class="pf-c-nav__link" aria-expanded="true">{% trans 'Outposts' %}
<span class="pf-c-nav__toggle">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a>
<section class="pf-c-nav__subnav">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:outposts' %}"
class="pf-c-nav__link">
{% trans 'Outposts' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:outpost-service-connections' %}"
class="pf-c-nav__link">
{% trans 'Service Connections' %}
</a>
</li>
</ul>
</section>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:property-mappings' %}"
class="pf-c-nav__link">
{% trans 'Property Mappings' %}
</a>
</li>
<li class="pf-c-nav__item pf-m-expanded">
<a href="" class="pf-c-nav__link" aria-expanded="true">{% trans 'Flows' %}
<span class="pf-c-nav__toggle">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a>
<section class="pf-c-nav__subnav">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:flows' %}"
class="pf-c-nav__link">
{% trans 'Flows' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:stage-bindings' %}"
class="pf-c-nav__link">
{% trans 'Bindings' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:stages' %}"
class="pf-c-nav__link">
{% trans 'Stages' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:stage-prompts' %}"
class="pf-c-nav__link">
{% trans 'Prompts' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:stage-invitations' %}"
class="pf-c-nav__link">
{% trans 'Invitations' %}
</a>
</li>
</ul>
</section>
</li>
<li class="pf-c-nav__item pf-m-expanded">
<a href="" class="pf-c-nav__link" aria-expanded="true">{% trans 'Policies' %}
<span class="pf-c-nav__toggle">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</span>
</a>
<section class="pf-c-nav__subnav" aria-labelledby="subnav-title1">
<ul class="pf-c-nav__simple-list">
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:policies' %}"
class="pf-c-nav__link">
{% trans 'Policies' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:policies-bindings' %}"
class="pf-c-nav__link">
{% trans 'Bindings' %}
</a>
</li>
</ul>
</section>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:certificate_key_pair' %}"
class="pf-c-nav__link">
{% trans 'Certificates' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:tokens' %}"
class="pf-c-nav__link">
{% trans 'Tokens' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:users' %}"
class="pf-c-nav__link">
{% trans 'Users' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:groups' %}"
class="pf-c-nav__link">
{% trans 'Groups' %}
</a>
</li>
<li class="pf-c-nav__item">
<a href="#{% url 'passbook_admin:tasks' %}"
class="pf-c-nav__link">
{% trans 'System Tasks' %}
</a>
</li>
</ul>
</nav>
</div>
</pb-admin-sidebar> </pb-admin-sidebar>
<pb-admin-shell defaultUrl="{% url 'passbook_admin:overview' %}" role="main" class="pf-c-page__main" tabindex="-1" id="main-content"> <pb-router-outlet role="main" class="pf-c-page__main" tabindex="-1" id="main-content">
</pb-router-outlet>
<!-- <pb-admin-shell url="{% url 'passbook_admin:overview' %}" >
<div slot="body"></div> <div slot="body"></div>
</pb-admin-shell> </pb-admin-shell> -->
{% endblock %} {% endblock %}

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,16 +1,125 @@
import { customElement, html, LitElement, property } from "lit-element"; import {
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
// @ts-ignore
import PageStyle from "@patternfly/patternfly/components/Page/page.css";
// @ts-ignore
import NavStyle from "@patternfly/patternfly/components/Nav/nav.css";
// @ts-ignore
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
interface RegexAnchor { export interface SidebarItem {
anchor: HTMLAnchorElement; name: string;
match: RegExp; path?: string;
children?: SidebarItem[];
} }
export const SIDEBAR_ITEMS: SidebarItem[] = [
{
name: "Overview",
path: "overview",
},
{
name: "Applications",
path: "applications",
},
{
name: "Sources",
path: "sources",
},
{
name: "Providers",
path: "providers",
},
{
name: "Outposts",
children: [
{
name: "Outposts",
path: "outposts",
},
{
name: "Service Connections",
path: "outposts/service_connections",
},
],
},
{
name: "Property Mappings",
path: "property_mappings",
},
{
name: "Flows",
children: [
{
name: "Flows",
path: "flows",
},
{
name: "Bindings",
path: "stages/bindings",
},
{
name: "Stages",
path: "stages",
},
{
name: "Prompts",
path: "stages/prompts",
},
{
name: "Invitations",
path: "stages/invitations",
},
],
},
{
name: "Policies",
children: [
{
name: "Policies",
path: "policies",
},
{
name: "Bindings",
path: "policies/bindings",
},
],
},
{
name: "Certificates",
path: "crypto/certificates",
},
{
name: "Tokens",
path: "tokens",
},
{
name: "User",
path: "users",
},
{
name: "Groups",
path: "groups",
},
{
name: "System Tasks",
path: "tasks",
},
];
@customElement("pb-admin-sidebar") @customElement("pb-admin-sidebar")
export class AdminSideBar extends LitElement { export class AdminSideBar extends LitElement {
@property() @property()
activePath: string; activePath: string;
paths: RegexAnchor[] = []; static get styles() {
return [GlobalsStyle, PageStyle, NavStyle];
}
constructor() { constructor() {
super(); super();
@ -18,32 +127,47 @@ export class AdminSideBar extends LitElement {
window.addEventListener("hashchange", (e) => { window.addEventListener("hashchange", (e) => {
this.activePath = window.location.hash.slice(1, Infinity); this.activePath = window.location.hash.slice(1, Infinity);
}); });
this.querySelectorAll<HTMLAnchorElement>(".pf-c-nav__link").forEach( }
(a) => {
let rawValue = a.attributes.getNamedItem("pb-url-prefix") renderItem(item: SidebarItem): TemplateResult {
?.value; return html` <li
if (!rawValue) { class="pf-c-nav__item ${item.children
const parsedURL = new URL(a.href); ? "pf-m-expandable pf-m-expanded"
if (parsedURL.hash === "") { : ""}"
console.log(`Ignoring ${a}`); >
return; ${item.path
} ? html`<a
rawValue = `^${parsedURL.hash.slice(1, Infinity)}`; href="#${item.path}"
} class="pf-c-nav__link ${item.path === this.activePath
const regexp = RegExp(rawValue); ? "pf-m-current"
this.paths.push({ anchor: a, match: regexp }); : ""}"
} >
); ${item.name}
</a>`
: html`<a class="pf-c-nav__link" aria-expanded="true"
>${item.name}
<span class="pf-c-nav__toggle">
<i
class="fas fa-angle-right"
aria-hidden="true"
></i>
</span>
</a>
<section class="pf-c-nav__subnav">
<ul class="pf-c-nav__simple-list">
${item.children?.map((i) => this.renderItem(i))}
</ul>
</section>`}
</li>`;
} }
render() { render() {
this.paths.forEach((path) => { return html`<div class="pf-c-page__sidebar-body">
if (path.match.exec(this.activePath)) { <nav class="pf-c-nav" aria-label="Global">
path.anchor.classList.add("pf-m-current"); <ul class="pf-c-nav__list">
} else { ${SIDEBAR_ITEMS.map((i) => this.renderItem(i))}
path.anchor.classList.remove("pf-m-current"); </ul>
} </nav>
}); </div>`;
return html`<slot></slot>`;
} }
} }

View File

@ -5,7 +5,7 @@ export class DropdownButton extends LitElement {
constructor() { constructor() {
super(); super();
const menu = <HTMLElement>this.querySelector(".pf-c-dropdown__menu")!; const menu = <HTMLElement>this.querySelector(".pf-c-dropdown__menu")!;
this.querySelectorAll("button").forEach((btn) => { this.querySelectorAll("button.pf-c-dropdown__toggle").forEach((btn) => {
btn.addEventListener("click", (e) => { btn.addEventListener("click", (e) => {
menu.hidden = !menu.hidden; menu.hidden = !menu.hidden;
}); });

View File

@ -5,6 +5,11 @@ import ModalBoxStyle from "@patternfly/patternfly/components/ModalBox/modal-box.
import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
// @ts-ignore // @ts-ignore
import BackdropStyle from "@patternfly/patternfly/components/Backdrop/backdrop.css"; import BackdropStyle from "@patternfly/patternfly/components/Backdrop/backdrop.css";
// @ts-ignore
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
// @ts-ignore
import fa from "@patternfly/patternfly/assets/icons/fontawesome.css";
import { updateMessages } from "../elements/Messages"; import { updateMessages } from "../elements/Messages";
import { convertToSlug } from "../utils"; import { convertToSlug } from "../utils";
@ -17,7 +22,7 @@ export class ModalButton extends LitElement {
open: boolean = false; open: boolean = false;
static get styles() { static get styles() {
return [ModalBoxStyle, BullseyeStyle, BackdropStyle]; return [ModalBoxStyle, BullseyeStyle, BackdropStyle, ButtonStyle, fa];
} }
constructor() { constructor() {

View File

@ -8,6 +8,7 @@ import "./elements/FetchFillSlot";
import "./elements/Messages"; import "./elements/Messages";
import "./elements/ModalButton"; import "./elements/ModalButton";
import "./elements/Tabs"; import "./elements/Tabs";
import "./pages/AdminSiteShell"; import "./pages/SiteShell";
import "./pages/FlowShellCard"; import "./pages/FlowShellCard";
import "./pages/RouterOutlet";
import "./elements/AdminLoginsChart"; import "./elements/AdminLoginsChart";

View File

@ -0,0 +1,71 @@
import {
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
// @ts-ignore
import PF from "@patternfly/patternfly/patternfly.css";
// @ts-ignore
import PFAddons from "@patternfly/patternfly/patternfly-addons.css";
export interface Route {
url: RegExp;
element: TemplateResult;
}
export const ROUTES: Route[] = [
{
url: new RegExp("^overview$"),
element: html`<pb-site-shell url="/overview/"
><div slot="body"></div
></pb-site-shell>`,
},
// {
// url: new RegExp("^applications$"),
// element: html`<h1>test2</h1>`,
// },
];
@customElement("pb-router-outlet")
export class RouterOutlet extends LitElement {
@property()
activeRoute?: Route;
static get styles() {
return [PF, PFAddons];
}
constructor() {
super();
this.navigate();
window.addEventListener("hashchange", (e) => this.navigate());
}
navigate() {
const activeUrl = window.location.hash.slice(1, Infinity);
ROUTES.forEach((route) => {
let selectedRoute: Route | null = null;
if (route.url.exec(activeUrl)) {
selectedRoute = route;
}
if (!selectedRoute) {
console.log(
`passbook/router: route "${activeUrl}" not defined, defaulting to shell`
);
selectedRoute = {
url: RegExp(""),
element: html`<pb-site-shell url=${activeUrl}
><div slot="body"></div
></pb-site-shell>`,
};
}
this.activeRoute = selectedRoute;
});
}
render() {
return this.activeRoute?.element;
}
}

View File

@ -1,20 +1,15 @@
import { import { css, customElement, html, LitElement, property } from "lit-element";
css,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
// @ts-ignore // @ts-ignore
import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css"; import BullseyeStyle from "@patternfly/patternfly/layouts/Bullseye/bullseye.css";
// @ts-ignore // @ts-ignore
import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css"; import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css";
// @ts-ignore
import BackdropStyle from "@patternfly/patternfly/components/Backdrop/backdrop.css";
@customElement("pb-admin-shell") @customElement("pb-site-shell")
export class AdminSiteShell extends LitElement { export class SiteShell extends LitElement {
@property() @property()
set defaultUrl(value: string) { set url(value: string) {
if (window.location.hash === "" && value !== undefined) { if (window.location.hash === "" && value !== undefined) {
window.location.hash = `#${value}`; window.location.hash = `#${value}`;
} }
@ -38,6 +33,7 @@ export class AdminSiteShell extends LitElement {
z-index: 2000; z-index: 2000;
} }
`, `,
BackdropStyle,
BullseyeStyle, BullseyeStyle,
SpinnerStyle, SpinnerStyle,
]; ];
@ -101,17 +97,19 @@ export class AdminSiteShell extends LitElement {
render() { render() {
return html` ${this.loading return html` ${this.loading
? html` <div class="pf-l-bullseye"> ? html` <div class="pf-c-backdrop">
<div class="pf-l-bullseye__item"> <div class="pf-l-bullseye">
<span <div class="pf-l-bullseye__item">
class="pf-c-spinner pf-m-xl" <span
role="progressbar" class="pf-c-spinner pf-m-xl"
aria-valuetext="Loading..." 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__clipper"></span>
<span class="pf-c-spinner__tail-ball"></span> <span class="pf-c-spinner__lead-ball"></span>
</span> <span class="pf-c-spinner__tail-ball"></span>
</span>
</div>
</div> </div>
</div>` </div>`
: ""} : ""}