web: provide a "select / select all" tool for the dual list multiselect
**This commit** Provides one of several of the sub-controls needed to make the multi-list multi-select thing work. This is the simplest control, and I decided to go with it first because it's all presentation; all it does is show the buttons and send events from those buttons. A Storybook component is provided to show how well it works.
This commit is contained in:
parent
6fb543b94f
commit
1cba9e88cb
|
@ -0,0 +1,101 @@
|
||||||
|
import "@goauthentik/elements/messages/MessageContainer";
|
||||||
|
import { Meta, StoryObj } from "@storybook/web-components";
|
||||||
|
|
||||||
|
import { TemplateResult, html } from "lit";
|
||||||
|
|
||||||
|
import "./ak-dual-select-controls";
|
||||||
|
import { AkDualSelectControls } from "./ak-dual-select-controls";
|
||||||
|
|
||||||
|
const metadata: Meta<AkDualSelectControls> = {
|
||||||
|
title: "Elements / Dual Select / Control Panel",
|
||||||
|
component: "ak-dual-select-controls",
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component: "The vertical panel separating two dual-select elements.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
addActive: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Highlighted if the sample panel has something to move to the result panel.",
|
||||||
|
},
|
||||||
|
removeActive: {
|
||||||
|
type: "boolean",
|
||||||
|
description:
|
||||||
|
"Highlighted if the result panel has something to move to the sample panel.",
|
||||||
|
},
|
||||||
|
selectAll: {
|
||||||
|
type: "boolean",
|
||||||
|
description: "Enable if you want both the 'move all visible' buttons.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default metadata;
|
||||||
|
|
||||||
|
const container = (testItem: TemplateResult) =>
|
||||||
|
html` <div style="background: #fff; padding: 2em">
|
||||||
|
<style>
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<ak-message-container></ak-message-container>
|
||||||
|
${testItem}
|
||||||
|
<p>Messages received from the button:</p>
|
||||||
|
<ul id="action-button-message-pad" style="margin-top: 1em"></ul>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const displayMessage = (result: any) => {
|
||||||
|
const doc = new DOMParser().parseFromString(`<li><i>Event</i>: ${result}</li>`, "text/xml");
|
||||||
|
const target = document.querySelector("#action-button-message-pad");
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
target!.appendChild(doc.firstChild!);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("ak-dual-select-add", () => displayMessage("add"));
|
||||||
|
window.addEventListener("ak-dual-select-remove", () => displayMessage("remove"));
|
||||||
|
window.addEventListener("ak-dual-select-add-all", () => displayMessage("add all"));
|
||||||
|
window.addEventListener("ak-dual-select-remove-all", () => displayMessage("remove all"));
|
||||||
|
|
||||||
|
type Story = StoryObj;
|
||||||
|
|
||||||
|
export const Default: Story = {
|
||||||
|
render: () => container(html` <ak-dual-select-controls></ak-dual-select-controls>`),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddActive: Story = {
|
||||||
|
render: () => container(html` <ak-dual-select-controls add-active></ak-dual-select-controls>`),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RemoveActive: Story = {
|
||||||
|
render: () =>
|
||||||
|
container(html` <ak-dual-select-controls remove-active></ak-dual-select-controls>`),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddAllActive: Story = {
|
||||||
|
render: () =>
|
||||||
|
container(
|
||||||
|
html` <ak-dual-select-controls
|
||||||
|
enable-select-all
|
||||||
|
add-all-active
|
||||||
|
></ak-dual-select-controls>`,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RemoveAllActive: Story = {
|
||||||
|
render: () =>
|
||||||
|
container(
|
||||||
|
html` <ak-dual-select-controls
|
||||||
|
enable-select-all
|
||||||
|
remove-all-active
|
||||||
|
></ak-dual-select-controls>`,
|
||||||
|
),
|
||||||
|
};
|
84
web/src/elements/ak-dual-select/ak-dual-select-controls.ts
Normal file
84
web/src/elements/ak-dual-select/ak-dual-select-controls.ts
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
||||||
|
|
||||||
|
import { msg } from "@lit/localize";
|
||||||
|
import { html, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
|
import PFDualListSelector from "@patternfly/patternfly/components/DualListSelector/dual-list-selector.css";
|
||||||
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
const styles = [PFBase, PFButton, PFDualListSelector];
|
||||||
|
|
||||||
|
@customElement("ak-dual-select-controls")
|
||||||
|
export class AkDualSelectControls extends CustomEmitterElement(AKElement) {
|
||||||
|
static get styles() {
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ attribute: "add-active", type: Boolean })
|
||||||
|
addActive = false;
|
||||||
|
|
||||||
|
@property({ attribute: "remove-active", type: Boolean })
|
||||||
|
removeActive = false;
|
||||||
|
|
||||||
|
@property({ attribute: "add-all-active", type: Boolean })
|
||||||
|
addAllActive = false;
|
||||||
|
|
||||||
|
@property({ attribute: "remove-all-active", type: Boolean })
|
||||||
|
removeAllActive = false;
|
||||||
|
|
||||||
|
@property({ attribute: "disabled", type: Boolean })
|
||||||
|
disabled = false;
|
||||||
|
|
||||||
|
@property({ attribute: "enable-select-all", type: Boolean })
|
||||||
|
selectAll = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick(eventName: string) {
|
||||||
|
this.dispatchCustomEvent(eventName);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton(label: string, event: string, active: boolean, direction: string) {
|
||||||
|
return html`
|
||||||
|
<div class="pf-c-dual-list-selector__controls-item">
|
||||||
|
<button
|
||||||
|
?aria-disabled=${this.disabled || !active}
|
||||||
|
?disabled=${this.disabled || !active}
|
||||||
|
aria-label=${label}
|
||||||
|
class="pf-c-button pf-m-plain"
|
||||||
|
type="button"
|
||||||
|
@click=${() => this.onClick(event)}
|
||||||
|
data-ouia-component-type="AK/Button"
|
||||||
|
>
|
||||||
|
<i class="fa ${direction}"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// prettier-ignore
|
||||||
|
return html`
|
||||||
|
<div class="pf-c-dual-list-selector">
|
||||||
|
<div class="pf-c-dual-list-selector__controls">
|
||||||
|
${this.renderButton(msg("Add"), "ak-dual-select-add", this.addActive, "fa-angle-right")}
|
||||||
|
${this.selectAll
|
||||||
|
? html`
|
||||||
|
${this.renderButton(msg("Add All"), "ak-dual-select-add-all", this.addAllActive, "fa-angle-double-right")}
|
||||||
|
${this.renderButton(msg("Remove All"), "ak-dual-select-remove-all", this.removeAllActive, "fa-angle-double-left")}
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this.renderButton(msg("Remove"), "ak-dual-select-remove", this.removeActive, "fa-angle-left")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AkDualSelectControls;
|
Reference in a new issue