Turns out that was one layer too many; the topmost component was fine for
maintaining the context.
This commit is contained in:
parent
7fae17dac3
commit
c0294191ad
|
@ -1,85 +0,0 @@
|
||||||
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { provide } from "@lit-labs/context";
|
|
||||||
import { customElement, property, state } from "@lit/reactive-element/decorators.js";
|
|
||||||
import { LitElement, html } from "lit";
|
|
||||||
|
|
||||||
import { akWizardCurrentStepContextName } from "./akWizardCurrentStepContextName";
|
|
||||||
import { akWizardStepsContextName } from "./akWizardStepsContextName";
|
|
||||||
import type { WizardStep, WizardStepId } from "./types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AkWizardContext
|
|
||||||
*
|
|
||||||
* @element ak-wizard-context
|
|
||||||
*
|
|
||||||
* The WizardContext controls the navigation for the wizard. It listens for navigation events from
|
|
||||||
* the wizard frame and responds with changes to the view, including handling the close button.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
@customElement("ak-wizard-context")
|
|
||||||
export class AkWizardContext extends CustomListenerElement(LitElement) {
|
|
||||||
@property()
|
|
||||||
eventName: string = "ak-wizard-nav";
|
|
||||||
|
|
||||||
@provide({ context: akWizardStepsContextName })
|
|
||||||
@property({ attribute: false })
|
|
||||||
steps: WizardStep[] = [];
|
|
||||||
|
|
||||||
@provide({ context: akWizardCurrentStepContextName })
|
|
||||||
@state()
|
|
||||||
currentStep!: WizardStep;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.handleNavigation = this.handleNavigation.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the only case where currentStep could be anything other than a valid entry. Unless,
|
|
||||||
// of course, a step itself is so badly messed up it can't point to a real object.
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
willUpdate(_changedProperties: Map<string, any>) {
|
|
||||||
if (this.currentStep === undefined) {
|
|
||||||
this.currentStep = this.steps[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that we always scan for the valid next step and throw an error if we can't find it.
|
|
||||||
// There should never be a question that the currentStep is a *valid* step.
|
|
||||||
//
|
|
||||||
// TODO: Put a phase in there so that the current step can validate the contents asynchronously
|
|
||||||
// before setting the currentStep. Especially since setting the currentStep triggers a second
|
|
||||||
// asynchronous event-- scheduling a re-render of everything interested in the currentStep
|
|
||||||
// object.
|
|
||||||
handleNavigation(event: CustomEvent<{ step: WizardStepId }>) {
|
|
||||||
const requestedStep = event.detail.step;
|
|
||||||
if (!requestedStep) {
|
|
||||||
throw new Error("Request for next step when no next step is available");
|
|
||||||
}
|
|
||||||
const step = this.steps.find(({ id }) => id === requestedStep);
|
|
||||||
if (!step) {
|
|
||||||
throw new Error("Request for next step when no next step is available.");
|
|
||||||
}
|
|
||||||
if (step.disabled) {
|
|
||||||
throw new Error("Request for next step when the next step is disabled.");
|
|
||||||
}
|
|
||||||
this.currentStep = step;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
this.addCustomListener(this.eventName, this.handleNavigation);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnectedCallback() {
|
|
||||||
this.removeCustomListener(this.eventName, this.handleNavigation);
|
|
||||||
super.disconnectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`<slot></slot>`;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -123,6 +123,8 @@ export class AkWizardFrame extends CustomEmitterElement(ModalButton) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is where the panel is shown. We expect the panel to get its information from an
|
||||||
|
// independent context.
|
||||||
renderMainSection() {
|
renderMainSection() {
|
||||||
return html`<main class="pf-c-wizard__main">
|
return html`<main class="pf-c-wizard__main">
|
||||||
<div class="pf-c-wizard__main-body">${this.currentStep.renderer()}</div>
|
<div class="pf-c-wizard__main-body">${this.currentStep.renderer()}</div>
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
|
|
||||||
import { customElement } from "@lit/reactive-element/decorators/custom-element.js";
|
import { provide } from "@lit-labs/context";
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { property } from "lit/decorators.js";
|
import { customElement, property, state } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
|
|
||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
|
import PFRadio from "@patternfly/patternfly/components/Radio/radio.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import "./ak-wizard-context";
|
|
||||||
import "./ak-wizard-frame";
|
import "./ak-wizard-frame";
|
||||||
|
import { akWizardCurrentStepContextName } from "./akWizardCurrentStepContextName";
|
||||||
|
import { akWizardStepsContextName } from "./akWizardStepsContextName";
|
||||||
import type { WizardStep } from "./types";
|
import type { WizardStep } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,21 +25,39 @@ import type { WizardStep } from "./types";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@customElement("ak-wizard-main")
|
@customElement("ak-wizard-main")
|
||||||
export class AkWizardMain extends AKElement {
|
export class AkWizardMain extends CustomListenerElement(AKElement) {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [PFBase, PFButton, PFRadio];
|
return [PFBase, PFButton, PFRadio];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property()
|
||||||
|
eventName: string = "ak-wizard-nav";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The steps of the Wizard.
|
* The steps of the Wizard.
|
||||||
*
|
*
|
||||||
* @attribute
|
* @attribute
|
||||||
*/
|
*/
|
||||||
|
@provide({ context: akWizardStepsContextName })
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
steps: WizardStep[] = [];
|
steps: WizardStep[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The text of the button
|
* The current step of the wizard.
|
||||||
|
*
|
||||||
|
* @attribute
|
||||||
|
*/
|
||||||
|
@provide({ context: akWizardCurrentStepContextName })
|
||||||
|
@state()
|
||||||
|
currentStep!: WizardStep;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.handleNavigation = this.handleNavigation.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The text of the modal button
|
||||||
*
|
*
|
||||||
* @attribute
|
* @attribute
|
||||||
*/
|
*/
|
||||||
|
@ -68,17 +88,58 @@ export class AkWizardMain extends AKElement {
|
||||||
@property()
|
@property()
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
|
// Guarantee that if the current step was not passed in by the client, that we know
|
||||||
|
// and set to the first step.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
willUpdate(_changedProperties: Map<string, any>) {
|
||||||
|
if (this.currentStep === undefined) {
|
||||||
|
this.currentStep = this.steps[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.addCustomListener(this.eventName, this.handleNavigation);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.removeCustomListener(this.eventName, this.handleNavigation);
|
||||||
|
super.disconnectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we always scan for the valid next step and throw an error if we can't find it.
|
||||||
|
// There should never be a question that the currentStep is a *valid* step.
|
||||||
|
//
|
||||||
|
// TODO: Put a phase in there so that the current step can validate the contents asynchronously
|
||||||
|
// before setting the currentStep. Especially since setting the currentStep triggers a second
|
||||||
|
// asynchronous event-- scheduling a re-render of everything interested in the currentStep
|
||||||
|
// object.
|
||||||
|
handleNavigation(event: CustomEvent<{ step: string }>) {
|
||||||
|
const requestedStep = event.detail.step;
|
||||||
|
if (!requestedStep) {
|
||||||
|
throw new Error("Request for next step when no next step is available");
|
||||||
|
}
|
||||||
|
const step = this.steps.find(({ id }) => id === requestedStep);
|
||||||
|
if (!step) {
|
||||||
|
throw new Error("Request for next step when no next step is available.");
|
||||||
|
}
|
||||||
|
if (step.disabled) {
|
||||||
|
throw new Error("Request for next step when the next step is disabled.");
|
||||||
|
}
|
||||||
|
this.currentStep = step;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<ak-wizard-context .steps=${this.steps}>
|
|
||||||
<ak-wizard-frame
|
<ak-wizard-frame
|
||||||
?open=${this.open}
|
?open=${this.open}
|
||||||
header=${this.header}
|
header=${this.header}
|
||||||
description=${ifDefined(this.description)}
|
description=${ifDefined(this.description)}
|
||||||
|
eventName=${this.eventName}
|
||||||
>
|
>
|
||||||
<button slot="trigger" class="pf-c-button pf-m-primary">${this.prompt}</button>
|
<button slot="trigger" class="pf-c-button pf-m-primary">${this.prompt}</button>
|
||||||
</ak-wizard-frame>
|
</ak-wizard-frame>
|
||||||
</ak-wizard-context>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue