static: rewrite custom components to typescript
This commit is contained in:
parent
9c3bc4eb38
commit
1e1a002ab2
|
@ -64,9 +64,9 @@
|
|||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
<button is="action-button" class="pf-c-button pf-m-primary" url="{% url 'passbook_api:admin_system_tasks-retry' pk=task.task_name %}">
|
||||
<pb-action-button url="{% url 'passbook_api:admin_system_tasks-retry' pk=task.task_name %}">
|
||||
{% trans 'Retry Task' %}
|
||||
</button>
|
||||
</pb-action-button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@patternfly/patternfly/patternfly-addons.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'node_modules/@fortawesome/fontawesome-free/css/fontawesome.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'passbook/passbook.css' %}">
|
||||
<script src="{% static 'passbook/main.js' %}" defer></script>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
|
@ -37,6 +38,5 @@
|
|||
{% endblock %}
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
<script src="{% static 'passbook/passbook.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -38,6 +38,16 @@
|
|||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-4.59.1.tgz",
|
||||
"integrity": "sha512-zk3aqg62JXMTzzJMJsyVgt5fXlcxUUkRKkaxUv/hwpjhGiyLexZ1l3Gupb9ziYl74p38KzbbfcfdnlFCwJZfgg=="
|
||||
},
|
||||
"@rollup/plugin-typescript": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-6.1.0.tgz",
|
||||
"integrity": "sha512-hJxaiE6WyNOsK+fZpbFh9CUijZYqPQuAOWO5khaGTUkM8DYNNyA2TDlgamecE+qLOG1G1+CwbWMAx3rbqpp6xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
"resolve": "^1.17.0"
|
||||
}
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||
|
@ -591,6 +601,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.7",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
"codemirror": "^5.58.2",
|
||||
"lit-element": "^2.4.0",
|
||||
"lit-html": "^1.3.0",
|
||||
"rollup": "^2.33.2"
|
||||
"rollup": "^2.33.2",
|
||||
"tslib": "^2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^6.1.0",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-minify-html-literals": "^1.2.5",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -3,26 +3,24 @@ import commonjs from 'rollup-plugin-commonjs';
|
|||
import minifyHTML from 'rollup-plugin-minify-html-literals';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
|
||||
export default [{
|
||||
input: './src/passbook.js',
|
||||
input: './src/main.ts',
|
||||
output: [
|
||||
{
|
||||
format: 'iife',
|
||||
dir: 'passbook',
|
||||
sourcemap: true,
|
||||
sourcemap: true
|
||||
}
|
||||
],
|
||||
|
||||
plugins: [
|
||||
typescript(),
|
||||
resolve({browser: true}),
|
||||
commonjs(),
|
||||
sourcemaps(),
|
||||
minifyHTML(),
|
||||
terser(),
|
||||
],
|
||||
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
}];
|
||||
|
|
|
@ -1,37 +1,33 @@
|
|||
import { getCookie } from "./utils.js";
|
||||
import { updateMessages } from "./Messages.js";
|
||||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
|
||||
const PRIMARY_CLASS = "pf-m-primary";
|
||||
const SUCCESS_CLASS = "pf-m-success";
|
||||
const ERROR_CLASS = "pf-m-danger";
|
||||
const PROGRESS_CLASSES = ["pf-m-progress", "pf-m-in-progress"];
|
||||
|
||||
class ActionButton extends HTMLButtonElement {
|
||||
@customElement("pb-action-button")
|
||||
export class ActionButton extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener('click', e => this.callAction());
|
||||
this.querySelector("button")?.addEventListener('click', e => this.callAction());
|
||||
}
|
||||
|
||||
@property()
|
||||
url: string = "";
|
||||
|
||||
isRunning = false;
|
||||
oldBody = "";
|
||||
|
||||
setLoading() {
|
||||
this.isRunning = true;
|
||||
this.classList.add(...PROGRESS_CLASSES);
|
||||
this.oldBody = this.innerText;
|
||||
this.innerHTML = `<span class="pf-c-button__progress">
|
||||
<span class="pf-c-spinner pf-m-md" 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>
|
||||
</span>${this.oldBody}`;
|
||||
}
|
||||
|
||||
setDone(statusClass) {
|
||||
setDone(statusClass: string) {
|
||||
this.isRunning = false;
|
||||
this.classList.remove(...PROGRESS_CLASSES);
|
||||
this.innerText = this.oldBody;
|
||||
this.classList.replace(PRIMARY_CLASS, statusClass);
|
||||
// Trigger messages to update
|
||||
updateMessages();
|
||||
|
@ -47,8 +43,8 @@ class ActionButton extends HTMLButtonElement {
|
|||
this.setLoading();
|
||||
const csrftoken = getCookie('passbook_csrf');
|
||||
const request = new Request(
|
||||
this.attributes["url"].value,
|
||||
{ headers: { 'X-CSRFToken': csrftoken } }
|
||||
this.url,
|
||||
{ headers: { 'X-CSRFToken': csrftoken! } }
|
||||
);
|
||||
fetch(request, {
|
||||
method: "POST",
|
||||
|
@ -60,6 +56,18 @@ class ActionButton extends HTMLButtonElement {
|
|||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<button class="pf-c-button pf-m-primary">
|
||||
${this.isRunning ? html`
|
||||
<span class="pf-c-button__progress">
|
||||
<span class="pf-c-spinner pf-m-md" 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>
|
||||
</span>` : ""}
|
||||
<slot></slot>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
customElements.define('action-button', ActionButton, { extends: 'button' });
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { customElement, html, LitElement } from "lit-element";
|
||||
|
||||
@customElement("pb-dropdown")
|
||||
export class DropdownButton extends LitElement {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
const menu = <HTMLElement>this.querySelector('.pf-c-dropdown__menu')!;
|
||||
this.querySelectorAll("button").forEach(btn => {
|
||||
btn.addEventListener("click", e => {
|
||||
menu.hidden = !menu.hidden;
|
||||
});
|
||||
btn.addEventListener("blur", e => {
|
||||
menu.hidden = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +1,43 @@
|
|||
import { LitElement, html } from 'lit-element';
|
||||
import { LitElement, html, customElement, property } from 'lit-element';
|
||||
|
||||
class FetchFillSlot extends LitElement {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
url: { type: String },
|
||||
key: { type: String },
|
||||
value: { type: Number },
|
||||
};
|
||||
interface ComparisonHash {
|
||||
[key: string]: (a: any, b: any) => boolean
|
||||
}
|
||||
|
||||
comparison(slotName) {
|
||||
var comparisonOperatorsHash = {
|
||||
'<': function (a, b) { return a < b; },
|
||||
'>': function (a, b) { return a > b; },
|
||||
'>=': function (a, b) { return a >= b; },
|
||||
'<=': function (a, b) { return a <= b; },
|
||||
'==': function (a, b) { return a == b; },
|
||||
'!=': function (a, b) { return a != b; },
|
||||
'===': function (a, b) { return a === b; },
|
||||
'!==': function (a, b) { return a !== b; },
|
||||
@customElement("fetch-fill-slot")
|
||||
export class FetchFillSlot extends LitElement {
|
||||
|
||||
@property()
|
||||
url: string = "";
|
||||
|
||||
@property()
|
||||
key: string = "";
|
||||
|
||||
@property()
|
||||
value: string = "";
|
||||
|
||||
comparison(slotName: string) {
|
||||
let comparisonOperatorsHash = <ComparisonHash>{
|
||||
'<': function (a: any, b: any) { return a < b; },
|
||||
'>': function (a: any, b: any) { return a > b; },
|
||||
'>=': function (a: any, b: any) { return a >= b; },
|
||||
'<=': function (a: any, b: any) { return a <= b; },
|
||||
'==': function (a: any, b: any) { return a == b; },
|
||||
'!=': function (a: any, b: any) { return a != b; },
|
||||
'===': function (a: any, b: any) { return a === b; },
|
||||
'!==': function (a: any, b: any) { return a !== b; },
|
||||
};
|
||||
const tokens = slotName.split(" ");
|
||||
if (tokens.length < 3) {
|
||||
throw new Error("nah");
|
||||
}
|
||||
let a = tokens[0];
|
||||
let a: any = tokens[0];
|
||||
if (a === "value") {
|
||||
a = this.value;
|
||||
} else {
|
||||
a = parseInt(a, 10);
|
||||
}
|
||||
let b = tokens[2];
|
||||
let b: any = tokens[2];
|
||||
if (b === "value") {
|
||||
b = this.value;
|
||||
} else {
|
||||
|
@ -54,7 +60,7 @@ class FetchFillSlot extends LitElement {
|
|||
}
|
||||
let selectedSlot = "";
|
||||
this.querySelectorAll("[slot]").forEach(slot => {
|
||||
const comp = slot.getAttribute("slot");
|
||||
const comp = slot.getAttribute("slot")!;
|
||||
if (this.comparison(comp)) {
|
||||
selectedSlot = comp;
|
||||
}
|
||||
|
@ -65,5 +71,3 @@ class FetchFillSlot extends LitElement {
|
|||
return html`<slot name=${selectedSlot}></slot>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('fetch-fill-slot', FetchFillSlot);
|
|
@ -1,15 +1,26 @@
|
|||
import { LitElement, html } from 'lit-element';
|
||||
import { updateMessages } from "./Messages.js";
|
||||
import { LitElement, html, customElement, property } from 'lit-element';
|
||||
import { updateMessages } from "./Messages";
|
||||
|
||||
class FlowShellCard extends LitElement {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
flowBodyUrl: { type: String },
|
||||
flowBody: { type: String },
|
||||
};
|
||||
enum ResponseType {
|
||||
redirect,
|
||||
template
|
||||
}
|
||||
|
||||
interface Response {
|
||||
type: ResponseType;
|
||||
to?: string;
|
||||
body?: string;
|
||||
}
|
||||
|
||||
@customElement("flow-shell-card")
|
||||
export class FlowShellCard extends LitElement {
|
||||
|
||||
@property()
|
||||
flowBodyUrl: string = "";
|
||||
|
||||
@property()
|
||||
flowBody?: string = undefined;
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
@ -30,12 +41,12 @@ class FlowShellCard extends LitElement {
|
|||
});
|
||||
}
|
||||
|
||||
async updateCard(data) {
|
||||
async updateCard(data: Response) {
|
||||
switch (data.type) {
|
||||
case "redirect":
|
||||
window.location = data.to
|
||||
case ResponseType.redirect:
|
||||
window.location.assign(data.to!);
|
||||
break;
|
||||
case "template":
|
||||
case ResponseType.template:
|
||||
this.flowBody = data.body;
|
||||
await this.requestUpdate();
|
||||
this.checkAutofocus();
|
||||
|
@ -56,26 +67,26 @@ class FlowShellCard extends LitElement {
|
|||
}
|
||||
|
||||
checkAutofocus() {
|
||||
const autofocusElement = this.querySelector("[autofocus]");
|
||||
const autofocusElement = <HTMLElement>this.querySelector("[autofocus]");
|
||||
if (autofocusElement !== null) {
|
||||
autofocusElement.focus();
|
||||
}
|
||||
}
|
||||
|
||||
updateFormAction(form) {
|
||||
updateFormAction(form: HTMLFormElement) {
|
||||
for (let index = 0; index < form.elements.length; index++) {
|
||||
const element = form.elements[index];
|
||||
const element = <HTMLInputElement>form.elements[index];
|
||||
if (element.value === form.action) {
|
||||
console.log("pb-flow: Found Form action URL in form elements, not changing form action.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
form.action = this.flowBodyUrl;
|
||||
console.log(`pb-flow: updated form.action ${this.flowBodyUrl}`);
|
||||
console.log(`passbook/flows: updated form.action ${this.flowBodyUrl}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
checkAutosubmit(form) {
|
||||
checkAutosubmit(form: HTMLFormElement) {
|
||||
if ("autosubmit" in form.attributes) {
|
||||
return form.submit();
|
||||
}
|
||||
|
@ -83,11 +94,11 @@ class FlowShellCard extends LitElement {
|
|||
|
||||
setFormSubmitHandlers() {
|
||||
this.querySelectorAll("form").forEach(form => {
|
||||
console.log(`pb-flow: Checking for autosubmit attribute ${form}`);
|
||||
console.log(`passbook/flows: Checking for autosubmit attribute ${form}`);
|
||||
this.checkAutosubmit(form);
|
||||
console.log(`pb-flow: Setting action for form ${form}`);
|
||||
console.log(`passbook/flows: Setting action for form ${form}`);
|
||||
this.updateFormAction(form);
|
||||
console.log(`pb-flow: Adding handler for form ${form}`);
|
||||
console.log(`passbook/flows: Adding handler for form ${form}`);
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
let formData = new FormData(form);
|
||||
|
@ -107,7 +118,7 @@ class FlowShellCard extends LitElement {
|
|||
});
|
||||
}
|
||||
|
||||
errorMessage(error) {
|
||||
errorMessage(error: string) {
|
||||
this.flowBody = `
|
||||
<style>
|
||||
.pb-exception {
|
||||
|
@ -141,10 +152,9 @@ class FlowShellCard extends LitElement {
|
|||
|
||||
render() {
|
||||
if (this.flowBody !== undefined) {
|
||||
return html([this.flowBody]);
|
||||
// return html(<TemplateStringsArray>[this.flowBody]);
|
||||
return html`${this.flowBody}`;
|
||||
}
|
||||
return this.loading();
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('flow-shell-card', FlowShellCard);
|
|
@ -1,29 +1,35 @@
|
|||
import { LitElement, html } from 'lit-element';
|
||||
import { LitElement, html, customElement, property } from 'lit-element';
|
||||
|
||||
const LEVEL_ICON_MAP = {
|
||||
const LEVEL_ICON_MAP: { [key: string]: string } = {
|
||||
"error": "fas fa-exclamation-circle",
|
||||
"warning": "fas fa-exclamation-triangle",
|
||||
"success": "fas fa-check-circle",
|
||||
"info": "fas fa-info",
|
||||
};
|
||||
|
||||
let ID = function (prefix) {
|
||||
let ID = function (prefix: string) {
|
||||
return prefix + Math.random().toString(36).substr(2, 9);
|
||||
};
|
||||
|
||||
export function updateMessages() {
|
||||
document.querySelector("pb-messages").fetchMessages();
|
||||
const messageElement = <Messages>document?.querySelector("pb-messages");
|
||||
messageElement.fetchMessages();
|
||||
}
|
||||
|
||||
class Messages extends LitElement {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
url: { type: String },
|
||||
messages: { type: Array },
|
||||
};
|
||||
interface Message {
|
||||
level_tag: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@customElement("pb-messages")
|
||||
export class Messages extends LitElement {
|
||||
|
||||
@property()
|
||||
url: string = "";
|
||||
|
||||
@property()
|
||||
messages: string[] = [];
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
@ -34,15 +40,15 @@ class Messages extends LitElement {
|
|||
|
||||
fetchMessages() {
|
||||
return fetch(this.url).then(r => r.json()).then(r => this.messages = r).then((r) => {
|
||||
const container = this.querySelector(".pf-c-alert-group");
|
||||
r.forEach(message => {
|
||||
const container = <HTMLElement>this.querySelector(".pf-c-alert-group")!;
|
||||
r.forEach((message: Message) => {
|
||||
const messageElement = this.renderMessage(message);
|
||||
container.appendChild(messageElement);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
renderMessage(message) {
|
||||
renderMessage(message: Message): ChildNode {
|
||||
const id = ID("pb-message");
|
||||
const el = document.createElement("template");
|
||||
el.innerHTML = `<li id=${id} class="pf-c-alert-group__item">
|
||||
|
@ -56,14 +62,12 @@ class Messages extends LitElement {
|
|||
</div>
|
||||
</li>`;
|
||||
setTimeout(() => {
|
||||
this.querySelector(`#${id}`).remove();
|
||||
this.querySelector(`#${id}`)?.remove();
|
||||
}, 1500);
|
||||
return el.content.firstChild;
|
||||
return el.content.firstChild!;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`<ul class="pf-c-alert-group pf-m-toast"></ul>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('pb-messages', Messages);
|
|
@ -0,0 +1,58 @@
|
|||
import { customElement, html, LitElement, property } from "lit-element";
|
||||
|
||||
const PRIMARY_CLASS = "pf-m-primary";
|
||||
const SUCCESS_CLASS = "pf-m-success";
|
||||
const ERROR_CLASS = "pf-m-danger";
|
||||
const PROGRESS_CLASSES = ["pf-m-progress", "pf-m-in-progress"];
|
||||
|
||||
@customElement("pb-modal-button")
|
||||
export class ModalButton extends LitElement {
|
||||
|
||||
@property()
|
||||
href: string = "";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener('click', e => this.callAction(e));
|
||||
}
|
||||
|
||||
getModal() {
|
||||
return html`<div class="pf-c-backdrop">
|
||||
<div class="pf-l-bullseye">
|
||||
<div class="pf-c-modal-box pf-m-md" role="dialog" aria-modal="true" aria-labelledby="modal-md-title" aria-describedby="modal-md-description">
|
||||
<button class="pf-c-button pf-m-plain" type="button" aria-label="Close dialog">
|
||||
<i class="fas fa-times" aria-hidden="true"></i>
|
||||
</button>
|
||||
|
||||
<header class="pf-c-modal-box__header">
|
||||
<h1 class="pf-c-modal-box__title" id="modal-md-title">This is a long header title that will truncate because modal titles should be very short. Use the modal body to provide more info.</h1>
|
||||
</header>
|
||||
<div class="pf-c-modal-box__body">
|
||||
<p id="modal-md-description">The "aria-describedby" attribute can be applied to any text that adequately describes the modal's purpose. It does not have to be assigned to ".pf-c-modal-box__body"</p>
|
||||
<p>Form here</p>
|
||||
</div>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<button class="pf-c-button pf-m-primary" type="button">Save</button>
|
||||
<button class="pf-c-button pf-m-link" type="button">Cancel</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
callAction(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
const request = new Request(
|
||||
this.href,
|
||||
);
|
||||
fetch(request, {
|
||||
method: "POST",
|
||||
mode: 'same-origin',
|
||||
}).then(r => {
|
||||
// this.
|
||||
}).catch(() => {
|
||||
// this.setDone(ERROR_CLASS);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import { LitElement, html } from 'lit-element';
|
||||
import { LitElement, html, customElement } from 'lit-element';
|
||||
|
||||
class Tabs extends LitElement {
|
||||
@customElement("pb-tabs")
|
||||
export class Tabs extends LitElement {
|
||||
|
||||
_currentPage = "";
|
||||
_firstPage = "";
|
||||
_currentPage? = "";
|
||||
_firstPage? = "";
|
||||
|
||||
get currentPage() {
|
||||
return this._currentPage
|
||||
|
@ -12,9 +13,9 @@ class Tabs extends LitElement {
|
|||
set currentPage(value) {
|
||||
try {
|
||||
// Show active tab page
|
||||
this.querySelector(`.pf-c-tab-content[tab-name='${value}']`).removeAttribute("hidden");
|
||||
this.querySelector(`.pf-c-tab-content[tab-name='${value}']`)?.removeAttribute("hidden");
|
||||
// Update active status on buttons
|
||||
this.querySelector(`.pf-c-tabs__item[tab-name='${value}']`).classList.add("pf-m-current");
|
||||
this.querySelector(`.pf-c-tabs__item[tab-name='${value}']`)?.classList.add("pf-m-current");
|
||||
// Hide other tab pages
|
||||
this.querySelectorAll(`.pf-c-tab-content:not([tab-name='${value}'])`).forEach((el) => {
|
||||
el.setAttribute("hidden", "");
|
||||
|
@ -24,7 +25,7 @@ class Tabs extends LitElement {
|
|||
el.classList.remove("pf-m-current");
|
||||
});
|
||||
// Update window hash
|
||||
window.location.hash = value;
|
||||
window.location.hash = `#${value}`;
|
||||
this._currentPage = value;
|
||||
} catch (e) {
|
||||
this.currentPage = this._firstPage;
|
||||
|
@ -36,7 +37,7 @@ class Tabs extends LitElement {
|
|||
}
|
||||
|
||||
firstUpdated() {
|
||||
this._firstPage = this.querySelector(".pf-c-tab-content").getAttribute("tab-name");
|
||||
this._firstPage = this.querySelector(".pf-c-tab-content")?.getAttribute("tab-name")!;
|
||||
if (window.location.hash) {
|
||||
this.currentPage = window.location.hash;
|
||||
} else {
|
||||
|
@ -44,12 +45,10 @@ class Tabs extends LitElement {
|
|||
}
|
||||
this.querySelectorAll(".pf-c-tabs__item > button").forEach((button) => {
|
||||
button.addEventListener("click", (e) => {
|
||||
let tabPage = button.parentElement.getAttribute("tab-name");
|
||||
let tabPage = button.parentElement?.getAttribute("tab-name")!;
|
||||
this.currentPage = tabPage;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
customElements.define('pb-tabs', Tabs);
|
|
@ -1,18 +1,3 @@
|
|||
import './FetchFillSlot.js';
|
||||
import './ActionButton.js';
|
||||
import './Messages.js';
|
||||
import './FlowShellCard.js';
|
||||
import './Tabs.js';
|
||||
|
||||
// Button Dropdowns
|
||||
document.querySelectorAll("button.pf-c-dropdown__toggle").forEach((b) => {
|
||||
b.addEventListener('click', (e) => {
|
||||
const parent = e.target.closest('.pf-c-dropdown');
|
||||
const menu = parent.querySelector('.pf-c-dropdown__menu');
|
||||
menu.hidden = !menu.hidden;
|
||||
});
|
||||
});
|
||||
|
||||
// Search clearing
|
||||
document.querySelectorAll("input[type=search]").forEach((si) => {
|
||||
si.addEventListener("search", (e) => {
|
|
@ -0,0 +1,9 @@
|
|||
import "./legacy.js";
|
||||
|
||||
import './ActionButton';
|
||||
import './Dropdown';
|
||||
import './FetchFillSlot';
|
||||
import './FlowShellCard';
|
||||
import './Messages';
|
||||
import './Tabs';
|
||||
import './ModalButton';
|
|
@ -1,4 +1,4 @@
|
|||
export function getCookie(name) {
|
||||
export function getCookie(name: string) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"target": "es2017",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"lib": [
|
||||
"es2017",
|
||||
"dom",
|
||||
"dom.iterable"
|
||||
],
|
||||
}
|
||||
}
|
Reference in New Issue