Compare commits
29 Commits
safari-fol
...
web/legibi
Author | SHA1 | Date | |
---|---|---|---|
1bc8fa0a9d | |||
3ec0d30965 | |||
50d2f69332 | |||
7d972ec711 | |||
854427e463 | |||
be349e2e14 | |||
bd0e81b8ad | |||
f6afb59515 | |||
dddde09be5 | |||
6d7fc94698 | |||
1dcf9108ad | |||
7bb6a3dfe6 | |||
9cc440eee1 | |||
fe9e4526ac | |||
20b66f850c | |||
67b327414b | |||
5b8d86b5a9 | |||
67aed3e318 | |||
9809b94030 | |||
e7527c551b | |||
36b10b434a | |||
831797b871 | |||
5cc2c0f45f | |||
32442766f4 | |||
75790909a8 | |||
e0d5df89ca | |||
f25a9c624e | |||
914993a788 | |||
89dad07a66 |
@ -1,6 +1,5 @@
|
|||||||
import { AkControlElement } from "@goauthentik/elements/AkControlElement.js";
|
import { AkControlElement } from "@goauthentik/elements/AkControlElement.js";
|
||||||
import { debounce } from "@goauthentik/elements/utils/debounce";
|
import { debounce } from "@goauthentik/elements/utils/debounce";
|
||||||
import { CustomListenerElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { PropertyValues, html } from "lit";
|
import { PropertyValues, html } from "lit";
|
||||||
@ -12,6 +11,11 @@ import type { Pagination } from "@goauthentik/api";
|
|||||||
|
|
||||||
import "./ak-dual-select";
|
import "./ak-dual-select";
|
||||||
import { AkDualSelect } from "./ak-dual-select";
|
import { AkDualSelect } from "./ak-dual-select";
|
||||||
|
import {
|
||||||
|
DualSelectChangeEvent,
|
||||||
|
DualSelectPaginatorNavEvent,
|
||||||
|
DualSelectSearchEvent,
|
||||||
|
} from "./events";
|
||||||
import type { DataProvider, DualSelectPair } from "./types";
|
import type { DataProvider, DualSelectPair } from "./types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +30,7 @@ import type { DataProvider, DualSelectPair } from "./types";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@customElement("ak-dual-select-provider")
|
@customElement("ak-dual-select-provider")
|
||||||
export class AkDualSelectProvider extends CustomListenerElement(AkControlElement) {
|
export class AkDualSelectProvider extends AkControlElement {
|
||||||
/** A function that takes a page and returns the DualSelectPair[] collection with which to update
|
/** A function that takes a page and returns the DualSelectPair[] collection with which to update
|
||||||
* the "Available" pane.
|
* the "Available" pane.
|
||||||
*
|
*
|
||||||
@ -86,9 +90,9 @@ export class AkDualSelectProvider extends CustomListenerElement(AkControlElement
|
|||||||
this.onNav = this.onNav.bind(this);
|
this.onNav = this.onNav.bind(this);
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
this.onSearch = this.onSearch.bind(this);
|
this.onSearch = this.onSearch.bind(this);
|
||||||
this.addCustomListener("ak-pagination-nav-to", this.onNav);
|
this.addEventListener(DualSelectPaginatorNavEvent.eventName, this.onNav);
|
||||||
this.addCustomListener("ak-dual-select-change", this.onChange);
|
this.addEventListener(DualSelectSearchEvent.eventName, this.onSearch);
|
||||||
this.addCustomListener("ak-dual-select-search", this.onSearch);
|
this.addEventListener(DualSelectChangeEvent.eventName, this.onChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
willUpdate(changedProperties: PropertyValues<this>) {
|
willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
@ -122,26 +126,16 @@ export class AkDualSelectProvider extends CustomListenerElement(AkControlElement
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onNav(event: Event) {
|
onNav(event: DualSelectPaginatorNavEvent) {
|
||||||
if (!(event instanceof CustomEvent)) {
|
this.fetch(event.page);
|
||||||
throw new Error(`Expecting a CustomEvent for navigation, received ${event} instead`);
|
|
||||||
}
|
|
||||||
this.fetch(event.detail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(event: Event) {
|
onChange(event: DualSelectChangeEvent) {
|
||||||
if (!(event instanceof CustomEvent)) {
|
this.selected = this.internalSelected = event.selected;
|
||||||
throw new Error(`Expecting a CustomEvent for change, received ${event} instead`);
|
|
||||||
}
|
|
||||||
this.internalSelected = event.detail.value;
|
|
||||||
this.selected = this.internalSelected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch(event: Event) {
|
onSearch(event: DualSelectSearchEvent) {
|
||||||
if (!(event instanceof CustomEvent)) {
|
this.doSearch(event.search);
|
||||||
throw new Error(`Expecting a CustomEvent for change, received ${event} instead`);
|
|
||||||
}
|
|
||||||
this.doSearch(event.detail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doSearch(search: string) {
|
doSearch(search: string) {
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import {
|
import { match } from "ts-pattern";
|
||||||
CustomEmitterElement,
|
|
||||||
CustomListenerElement,
|
|
||||||
} from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { PropertyValues, html, nothing } from "lit";
|
import { PropertyValues, html, nothing } from "lit";
|
||||||
@ -23,15 +20,13 @@ import { AkDualSelectSelectedPane } from "./components/ak-dual-select-selected-p
|
|||||||
import "./components/ak-pagination";
|
import "./components/ak-pagination";
|
||||||
import "./components/ak-search-bar";
|
import "./components/ak-search-bar";
|
||||||
import {
|
import {
|
||||||
EVENT_ADD_ALL,
|
DualSelectChangeEvent,
|
||||||
EVENT_ADD_ONE,
|
DualSelectMoveRequestEvent,
|
||||||
EVENT_ADD_SELECTED,
|
DualSelectPanelSearchEvent,
|
||||||
EVENT_DELETE_ALL,
|
DualSelectSearchEvent,
|
||||||
EVENT_REMOVE_ALL,
|
DualSelectUpdateEvent,
|
||||||
EVENT_REMOVE_ONE,
|
} from "./events";
|
||||||
EVENT_REMOVE_SELECTED,
|
import type { BasePagination, DualSelectPair } from "./types";
|
||||||
} from "./constants";
|
|
||||||
import type { BasePagination, DualSelectPair, SearchbarEvent } from "./types";
|
|
||||||
|
|
||||||
function alphaSort([_k1, v1, s1]: DualSelectPair, [_k2, v2, s2]: DualSelectPair) {
|
function alphaSort([_k1, v1, s1]: DualSelectPair, [_k2, v2, s2]: DualSelectPair) {
|
||||||
const [l, r] = [s1 !== undefined ? s1 : v1, s2 !== undefined ? s2 : v2];
|
const [l, r] = [s1 !== undefined ? s1 : v1, s2 !== undefined ? s2 : v2];
|
||||||
@ -60,7 +55,7 @@ const keyfinder =
|
|||||||
k === key;
|
k === key;
|
||||||
|
|
||||||
@customElement("ak-dual-select")
|
@customElement("ak-dual-select")
|
||||||
export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKElement)) {
|
export class AkDualSelect extends AKElement {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
@ -96,21 +91,9 @@ export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKE
|
|||||||
super();
|
super();
|
||||||
this.handleMove = this.handleMove.bind(this);
|
this.handleMove = this.handleMove.bind(this);
|
||||||
this.handleSearch = this.handleSearch.bind(this);
|
this.handleSearch = this.handleSearch.bind(this);
|
||||||
[
|
this.addEventListener(DualSelectMoveRequestEvent.eventName, this.handleMove);
|
||||||
EVENT_ADD_ALL,
|
this.addEventListener(DualSelectUpdateEvent.eventName, () => this.requestUpdate());
|
||||||
EVENT_ADD_SELECTED,
|
this.addEventListener(DualSelectPanelSearchEvent.eventName, this.handleSearch);
|
||||||
EVENT_DELETE_ALL,
|
|
||||||
EVENT_REMOVE_ALL,
|
|
||||||
EVENT_REMOVE_SELECTED,
|
|
||||||
EVENT_ADD_ONE,
|
|
||||||
EVENT_REMOVE_ONE,
|
|
||||||
].forEach((eventName: string) => {
|
|
||||||
this.addCustomListener(eventName, (event: Event) => this.handleMove(eventName, event));
|
|
||||||
});
|
|
||||||
this.addCustomListener("ak-dual-select-move", () => {
|
|
||||||
this.requestUpdate();
|
|
||||||
});
|
|
||||||
this.addCustomListener("ak-search", this.handleSearch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
willUpdate(changedProperties: PropertyValues<this>) {
|
willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
@ -123,47 +106,17 @@ export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMove(eventName: string, event: Event) {
|
handleMove(event: DualSelectMoveRequestEvent) {
|
||||||
if (!(event instanceof CustomEvent)) {
|
match(event.move)
|
||||||
throw new Error(`Expected move event here, got ${eventName}`);
|
.with("add-all", () => this.addAllVisible())
|
||||||
}
|
.with("add-one", () => this.addOne(event.key))
|
||||||
|
.with("add-selected", () => this.addSelected())
|
||||||
switch (eventName) {
|
.with("delete-all", () => this.removeAll())
|
||||||
case EVENT_ADD_SELECTED: {
|
.with("remove-all", () => this.removeAllVisible())
|
||||||
this.addSelected();
|
.with("remove-one", () => this.removeOne(event.key))
|
||||||
break;
|
.with("remove-selected", () => this.removeSelected())
|
||||||
}
|
.exhaustive();
|
||||||
case EVENT_REMOVE_SELECTED: {
|
this.dispatchEvent(new DualSelectChangeEvent(this.value));
|
||||||
this.removeSelected();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_ADD_ALL: {
|
|
||||||
this.addAllVisible();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_REMOVE_ALL: {
|
|
||||||
this.removeAllVisible();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_DELETE_ALL: {
|
|
||||||
this.removeAll();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_ADD_ONE: {
|
|
||||||
this.addOne(event.detail);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EVENT_REMOVE_ONE: {
|
|
||||||
this.removeOne(event.detail);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`AkDualSelect.handleMove received unknown event type: ${eventName}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.dispatchCustomEvent("ak-dual-select-change", { value: this.value });
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +135,10 @@ export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKE
|
|||||||
this.availablePane.value!.clearMove();
|
this.availablePane.value!.clearMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
addOne(key: string) {
|
addOne(key?: string) {
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const requested = this.options.find(keyfinder(key));
|
const requested = this.options.find(keyfinder(key));
|
||||||
if (requested && !this.selected.find(keyfinder(requested[0]))) {
|
if (requested && !this.selected.find(keyfinder(requested[0]))) {
|
||||||
this.selected = [...this.selected, requested];
|
this.selected = [...this.selected, requested];
|
||||||
@ -207,7 +163,10 @@ export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKE
|
|||||||
this.selectedPane.value!.clearMove();
|
this.selectedPane.value!.clearMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeOne(key: string) {
|
removeOne(key?: string) {
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.selected = this.selected.filter(([k]) => k !== key);
|
this.selected = this.selected.filter(([k]) => k !== key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,18 +182,18 @@ export class AkDualSelect extends CustomEmitterElement(CustomListenerElement(AKE
|
|||||||
this.selectedPane.value!.clearMove();
|
this.selectedPane.value!.clearMove();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearch(event: SearchbarEvent) {
|
handleSearch(event: DualSelectPanelSearchEvent) {
|
||||||
switch (event.detail.source) {
|
switch (event.source) {
|
||||||
case "ak-dual-list-available-search":
|
case "ak-dual-list-available-search":
|
||||||
return this.handleAvailableSearch(event.detail.value);
|
return this.handleAvailableSearch(event.filterOn);
|
||||||
case "ak-dual-list-selected-search":
|
case "ak-dual-list-selected-search":
|
||||||
return this.handleSelectedSearch(event.detail.value);
|
return this.handleSelectedSearch(event.filterOn);
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvailableSearch(value: string) {
|
handleAvailableSearch(value: string) {
|
||||||
this.dispatchCustomEvent("ak-dual-select-search", value);
|
this.dispatchEvent(new DualSelectSearchEvent(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectedSearch(value: string) {
|
handleSelectedSearch(value: string) {
|
||||||
|
@ -1,26 +1,19 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { bound } from "@goauthentik/elements/decorators/bound";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { html, nothing } from "lit";
|
import { html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { classMap } from "lit/directives/class-map.js";
|
import { classMap } from "lit/directives/class-map.js";
|
||||||
import { map } from "lit/directives/map.js";
|
import { map } from "lit/directives/map.js";
|
||||||
|
|
||||||
import { availablePaneStyles, listStyles } from "./styles.css";
|
import { availablePaneStyles } from "./styles.css";
|
||||||
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";
|
|
||||||
|
|
||||||
import { EVENT_ADD_ONE } from "../constants";
|
import {
|
||||||
|
DualSelectMoveAvailableEvent,
|
||||||
|
DualSelectMoveRequestEvent,
|
||||||
|
DualSelectUpdateEvent,
|
||||||
|
} from "../events";
|
||||||
import type { DualSelectPair } from "../types";
|
import type { DualSelectPair } from "../types";
|
||||||
|
import { AkDualSelectAbstractPane } from "./ak-dual-select-pane";
|
||||||
const styles = [PFBase, PFButton, PFDualListSelector, listStyles, availablePaneStyles];
|
|
||||||
|
|
||||||
const hostAttributes = [
|
|
||||||
["aria-labelledby", "dual-list-selector-available-pane-status"],
|
|
||||||
["aria-multiselectable", "true"],
|
|
||||||
["role", "listbox"],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @element ak-dual-select-available-panel
|
* @element ak-dual-select-available-panel
|
||||||
@ -40,9 +33,9 @@ const hostAttributes = [
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@customElement("ak-dual-select-available-pane")
|
@customElement("ak-dual-select-available-pane")
|
||||||
export class AkDualSelectAvailablePane extends CustomEmitterElement(AKElement) {
|
export class AkDualSelectAvailablePane extends AkDualSelectAbstractPane {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return [...AkDualSelectAbstractPane.styles, availablePaneStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The array of key/value pairs this pane is currently showing */
|
/* The array of key/value pairs this pane is currently showing */
|
||||||
@ -56,68 +49,31 @@ export class AkDualSelectAvailablePane extends CustomEmitterElement(AKElement) {
|
|||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
readonly selected: Set<string> = new Set();
|
readonly selected: Set<string> = new Set();
|
||||||
|
|
||||||
/* This is the only mutator for this object. It collects the list of objects the user has
|
@bound
|
||||||
* clicked on *in this pane*. It is explicitly marked as "public" to emphasize that the parent
|
|
||||||
* orchestrator for the dual-select widget can and will access it to get the list of keys to be
|
|
||||||
* moved (removed) if the user so requests.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@state()
|
|
||||||
public toMove: Set<string> = new Set();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.onClick = this.onClick.bind(this);
|
|
||||||
this.onMove = this.onMove.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
hostAttributes.forEach(([attr, value]) => {
|
|
||||||
if (!this.hasAttribute(attr)) {
|
|
||||||
this.setAttribute(attr, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clearMove() {
|
|
||||||
this.toMove = new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(key: string) {
|
onClick(key: string) {
|
||||||
if (this.selected.has(key)) {
|
if (this.selected.has(key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.toMove.has(key)) {
|
this.move(key);
|
||||||
this.toMove.delete(key);
|
this.dispatchEvent(new DualSelectMoveAvailableEvent(this.moveable.sort()));
|
||||||
} else {
|
this.dispatchEvent(new DualSelectUpdateEvent());
|
||||||
this.toMove.add(key);
|
|
||||||
}
|
|
||||||
this.dispatchCustomEvent(
|
|
||||||
"ak-dual-select-available-move-changed",
|
|
||||||
Array.from(this.toMove.values()).sort(),
|
|
||||||
);
|
|
||||||
this.dispatchCustomEvent("ak-dual-select-move");
|
|
||||||
// Necessary because updating a map won't trigger a state change
|
// Necessary because updating a map won't trigger a state change
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bound
|
||||||
onMove(key: string) {
|
onMove(key: string) {
|
||||||
this.toMove.delete(key);
|
this.toMove.delete(key);
|
||||||
this.dispatchCustomEvent(EVENT_ADD_ONE, key);
|
this.dispatchEvent(new DualSelectMoveRequestEvent("add-one", key));
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
get moveable() {
|
|
||||||
return Array.from(this.toMove.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
// DO NOT use `Array.map()` instead of Lit's `map()` function. Lit's `map()` is object-aware and
|
// DO NOT use `Array.map()` instead of Lit's `map()` function. Lit's `map()` is object-aware and
|
||||||
// will not re-arrange or reconstruct the list automatically if the actual sources do not
|
// will not re-arrange or reconstruct the list automatically if the actual sources do not
|
||||||
// change; this allows the available pane to illustrate selected items with the checkmark
|
// change; this allows the available pane to illustrate selected items with the checkmark
|
||||||
// without causing the list to scroll back up to the top.
|
// without causing the list to scroll back up to the top.
|
||||||
|
|
||||||
render() {
|
override render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="pf-c-dual-list-selector__menu">
|
<div class="pf-c-dual-list-selector__menu">
|
||||||
<ul class="pf-c-dual-list-selector__list">
|
<ul class="pf-c-dual-list-selector__list">
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { msg } from "@lit/localize";
|
import { msg } from "@lit/localize";
|
||||||
import { css, html, nothing } from "lit";
|
import { css, html, nothing } from "lit";
|
||||||
@ -8,13 +7,7 @@ import { customElement, property } from "lit/decorators.js";
|
|||||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import {
|
import { DualSelectMoveRequestEvent, type MoveEventType } from "../events";
|
||||||
EVENT_ADD_ALL,
|
|
||||||
EVENT_ADD_SELECTED,
|
|
||||||
EVENT_DELETE_ALL,
|
|
||||||
EVENT_REMOVE_ALL,
|
|
||||||
EVENT_REMOVE_SELECTED,
|
|
||||||
} from "../constants";
|
|
||||||
|
|
||||||
const styles = [
|
const styles = [
|
||||||
PFBase,
|
PFBase,
|
||||||
@ -47,7 +40,7 @@ const styles = [
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@customElement("ak-dual-select-controls")
|
@customElement("ak-dual-select-controls")
|
||||||
export class AkDualSelectControls extends CustomEmitterElement(AKElement) {
|
export class AkDualSelectControls extends AKElement {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
@ -96,11 +89,11 @@ export class AkDualSelectControls extends CustomEmitterElement(AKElement) {
|
|||||||
this.onClick = this.onClick.bind(this);
|
this.onClick = this.onClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(eventName: string) {
|
onClick(eventName: MoveEventType) {
|
||||||
this.dispatchCustomEvent(eventName);
|
this.dispatchEvent(new DualSelectMoveRequestEvent(eventName));
|
||||||
}
|
}
|
||||||
|
|
||||||
renderButton(label: string, event: string, active: boolean, direction: string) {
|
renderButton(label: string, event: MoveEventType, active: boolean, direction: string) {
|
||||||
return html`
|
return html`
|
||||||
<div class="pf-c-dual-list-selector__controls-item">
|
<div class="pf-c-dual-list-selector__controls-item">
|
||||||
<button
|
<button
|
||||||
@ -121,23 +114,18 @@ export class AkDualSelectControls extends CustomEmitterElement(AKElement) {
|
|||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="ak-dual-list-selector__controls">
|
<div class="ak-dual-list-selector__controls">
|
||||||
${this.renderButton(
|
${this.renderButton(msg("Add"), "add-selected", this.addActive, "fa-angle-right")}
|
||||||
msg("Add"),
|
|
||||||
EVENT_ADD_SELECTED,
|
|
||||||
this.addActive,
|
|
||||||
"fa-angle-right",
|
|
||||||
)}
|
|
||||||
${this.selectAll
|
${this.selectAll
|
||||||
? html`
|
? html`
|
||||||
${this.renderButton(
|
${this.renderButton(
|
||||||
msg("Add All Available"),
|
msg("Add All Available"),
|
||||||
EVENT_ADD_ALL,
|
"add-all",
|
||||||
this.addAllActive,
|
this.addAllActive,
|
||||||
"fa-angle-double-right",
|
"fa-angle-double-right",
|
||||||
)}
|
)}
|
||||||
${this.renderButton(
|
${this.renderButton(
|
||||||
msg("Remove All Available"),
|
msg("Remove All Available"),
|
||||||
EVENT_REMOVE_ALL,
|
"remove-all",
|
||||||
this.removeAllActive,
|
this.removeAllActive,
|
||||||
"fa-angle-double-left",
|
"fa-angle-double-left",
|
||||||
)}
|
)}
|
||||||
@ -145,14 +133,14 @@ export class AkDualSelectControls extends CustomEmitterElement(AKElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${this.renderButton(
|
${this.renderButton(
|
||||||
msg("Remove"),
|
msg("Remove"),
|
||||||
EVENT_REMOVE_SELECTED,
|
"remove-selected",
|
||||||
this.removeActive,
|
this.removeActive,
|
||||||
"fa-angle-left",
|
"fa-angle-left",
|
||||||
)}
|
)}
|
||||||
${this.deleteAll
|
${this.deleteAll
|
||||||
? html`${this.renderButton(
|
? html`${this.renderButton(
|
||||||
msg("Remove All"),
|
msg("Remove All"),
|
||||||
EVENT_DELETE_ALL,
|
"delete-all",
|
||||||
this.enableDeleteAll,
|
this.enableDeleteAll,
|
||||||
"fa-times",
|
"fa-times",
|
||||||
)}`
|
)}`
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
|
|
||||||
|
import { TemplateResult } from "lit";
|
||||||
|
import { state } from "lit/decorators.js";
|
||||||
|
|
||||||
|
import { listStyles } from "./styles.css";
|
||||||
|
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, listStyles];
|
||||||
|
|
||||||
|
const hostAttributes = [
|
||||||
|
["aria-labelledby", "dual-list-selector-selected-pane-status"],
|
||||||
|
["aria-multiselectable", "true"],
|
||||||
|
["role", "listbox"],
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @element ak-dual-select-panel
|
||||||
|
*
|
||||||
|
* The "selected options" or "right" pane in a dual-list multi-select. It receives from its parent
|
||||||
|
* a list of the selected options, and maintains an internal list of objects selected to move.
|
||||||
|
*
|
||||||
|
* @fires ak-dual-select-selected-move-changed - When the list of "to move" entries changed.
|
||||||
|
* Includes the current `toMove` content.
|
||||||
|
*
|
||||||
|
* @fires ak-dual-select-remove-one - Double-click with the element clicked on.
|
||||||
|
*
|
||||||
|
* It is not expected that the `ak-dual-select-selected-move-changed` will be used; instead, the
|
||||||
|
* attribute will be read by the parent when a control is clicked.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export abstract class AkDualSelectAbstractPane extends AKElement {
|
||||||
|
static get styles() {
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the only mutator for this object. It collects the list of objects the user has
|
||||||
|
* clicked on *in this pane*. It is explicitly marked as "public" to emphasize that the parent
|
||||||
|
* orchestrator for the dual-select widget can and will access it to get the list of keys to be
|
||||||
|
* moved (removed) if the user so requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@state()
|
||||||
|
public toMove: Set<string> = new Set();
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
hostAttributes.forEach(([attr, value]) => {
|
||||||
|
if (!this.hasAttribute(attr)) {
|
||||||
|
this.setAttribute(attr, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearMove() {
|
||||||
|
this.toMove = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
move(key: string) {
|
||||||
|
if (this.toMove.has(key)) {
|
||||||
|
this.toMove.delete(key);
|
||||||
|
} else {
|
||||||
|
this.toMove.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get moveable() {
|
||||||
|
return Array.from(this.toMove.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract render(): TemplateResult;
|
||||||
|
}
|
@ -1,26 +1,19 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { bound } from "@goauthentik/elements/decorators/bound";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { classMap } from "lit/directives/class-map.js";
|
import { classMap } from "lit/directives/class-map.js";
|
||||||
import { map } from "lit/directives/map.js";
|
import { map } from "lit/directives/map.js";
|
||||||
|
|
||||||
import { listStyles, selectedPaneStyles } from "./styles.css";
|
import { selectedPaneStyles } from "./styles.css";
|
||||||
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";
|
|
||||||
|
|
||||||
import { EVENT_REMOVE_ONE } from "../constants";
|
import {
|
||||||
|
DualSelectMoveRequestEvent,
|
||||||
|
DualSelectMoveSelectedEvent,
|
||||||
|
DualSelectUpdateEvent,
|
||||||
|
} from "../events";
|
||||||
import type { DualSelectPair } from "../types";
|
import type { DualSelectPair } from "../types";
|
||||||
|
import { AkDualSelectAbstractPane } from "./ak-dual-select-pane";
|
||||||
const styles = [PFBase, PFButton, PFDualListSelector, listStyles, selectedPaneStyles];
|
|
||||||
|
|
||||||
const hostAttributes = [
|
|
||||||
["aria-labelledby", "dual-list-selector-selected-pane-status"],
|
|
||||||
["aria-multiselectable", "true"],
|
|
||||||
["role", "listbox"],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @element ak-dual-select-available-panel
|
* @element ak-dual-select-available-panel
|
||||||
@ -38,70 +31,32 @@ const hostAttributes = [
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@customElement("ak-dual-select-selected-pane")
|
@customElement("ak-dual-select-selected-pane")
|
||||||
export class AkDualSelectSelectedPane extends CustomEmitterElement(AKElement) {
|
export class AkDualSelectSelectedPane extends AkDualSelectAbstractPane {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return [...AkDualSelectAbstractPane.styles, selectedPaneStyles];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The array of key/value pairs that are in the selected list. ALL of them. */
|
/* The array of key/value pairs that are in the selected list. ALL of them. */
|
||||||
@property({ type: Array })
|
@property({ type: Array })
|
||||||
readonly selected: DualSelectPair[] = [];
|
readonly selected: DualSelectPair[] = [];
|
||||||
|
|
||||||
/*
|
@bound
|
||||||
* This is the only mutator for this object. It collects the list of objects the user has
|
|
||||||
* clicked on *in this pane*. It is explicitly marked as "public" to emphasize that the parent
|
|
||||||
* orchestrator for the dual-select widget can and will access it to get the list of keys to be
|
|
||||||
* moved (removed) if the user so requests.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@state()
|
|
||||||
public toMove: Set<string> = new Set();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.onClick = this.onClick.bind(this);
|
|
||||||
this.onMove = this.onMove.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
hostAttributes.forEach(([attr, value]) => {
|
|
||||||
if (!this.hasAttribute(attr)) {
|
|
||||||
this.setAttribute(attr, value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
clearMove() {
|
|
||||||
this.toMove = new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(key: string) {
|
onClick(key: string) {
|
||||||
if (this.toMove.has(key)) {
|
this.move(key);
|
||||||
this.toMove.delete(key);
|
this.dispatchEvent(new DualSelectMoveSelectedEvent(this.moveable.sort()));
|
||||||
} else {
|
this.dispatchEvent(new DualSelectUpdateEvent());
|
||||||
this.toMove.add(key);
|
|
||||||
}
|
|
||||||
this.dispatchCustomEvent(
|
|
||||||
"ak-dual-select-selected-move-changed",
|
|
||||||
Array.from(this.toMove.values()).sort(),
|
|
||||||
);
|
|
||||||
this.dispatchCustomEvent("ak-dual-select-move");
|
|
||||||
// Necessary because updating a map won't trigger a state change
|
// Necessary because updating a map won't trigger a state change
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bound
|
||||||
onMove(key: string) {
|
onMove(key: string) {
|
||||||
this.toMove.delete(key);
|
this.toMove.delete(key);
|
||||||
this.dispatchCustomEvent(EVENT_REMOVE_ONE, key);
|
this.dispatchEvent(new DualSelectMoveRequestEvent("remove-one", key));
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
get moveable() {
|
override render() {
|
||||||
return Array.from(this.toMove.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return html`
|
return html`
|
||||||
<div class="pf-c-dual-list-selector__menu">
|
<div class="pf-c-dual-list-selector__menu">
|
||||||
<ul class="pf-c-dual-list-selector__list">
|
<ul class="pf-c-dual-list-selector__list">
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { msg, str } from "@lit/localize";
|
import { msg, str } from "@lit/localize";
|
||||||
import { css, html, nothing } from "lit";
|
import { css, html, nothing } from "lit";
|
||||||
@ -9,6 +8,7 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
|||||||
import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";
|
import PFPagination from "@patternfly/patternfly/components/Pagination/pagination.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
|
import { DualSelectPaginatorNavEvent } from "../events";
|
||||||
import type { BasePagination } from "../types";
|
import type { BasePagination } from "../types";
|
||||||
|
|
||||||
const styles = [
|
const styles = [
|
||||||
@ -27,7 +27,7 @@ const styles = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
@customElement("ak-pagination")
|
@customElement("ak-pagination")
|
||||||
export class AkPagination extends CustomEmitterElement(AKElement) {
|
export class AkPagination extends AKElement {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
@ -41,7 +41,7 @@ export class AkPagination extends CustomEmitterElement(AKElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClick(nav: number | undefined) {
|
onClick(nav: number | undefined) {
|
||||||
this.dispatchCustomEvent("ak-pagination-nav-to", nav ?? 0);
|
this.dispatchEvent(new DualSelectPaginatorNavEvent(nav ?? 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { AKElement } from "@goauthentik/elements/Base";
|
import { AKElement } from "@goauthentik/elements/Base";
|
||||||
import { CustomEmitterElement } from "@goauthentik/elements/utils/eventEmitter";
|
|
||||||
|
|
||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
@ -9,12 +8,12 @@ import type { Ref } from "lit/directives/ref.js";
|
|||||||
import { globalVariables, searchStyles } from "./search.css";
|
import { globalVariables, searchStyles } from "./search.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import type { SearchbarEvent } from "../types";
|
import { DualSelectPanelSearchEvent } from "../events";
|
||||||
|
|
||||||
const styles = [PFBase, globalVariables, searchStyles];
|
const styles = [PFBase, globalVariables, searchStyles];
|
||||||
|
|
||||||
@customElement("ak-search-bar")
|
@customElement("ak-search-bar")
|
||||||
export class AkSearchbar extends CustomEmitterElement(AKElement) {
|
export class AkSearchbar extends AKElement {
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return styles;
|
return styles;
|
||||||
}
|
}
|
||||||
@ -40,10 +39,7 @@ export class AkSearchbar extends CustomEmitterElement(AKElement) {
|
|||||||
if (this.input.value) {
|
if (this.input.value) {
|
||||||
this.value = this.input.value.value;
|
this.value = this.input.value.value;
|
||||||
}
|
}
|
||||||
this.dispatchCustomEvent<SearchbarEvent>("ak-search", {
|
this.dispatchEvent(new DualSelectPanelSearchEvent(this.name, this.value));
|
||||||
source: this.name,
|
|
||||||
value: this.value,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
112
web/src/elements/ak-dual-select/events.ts
Normal file
112
web/src/elements/ak-dual-select/events.ts
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { DualSelectPair } from "./types";
|
||||||
|
|
||||||
|
// Handled by the Server layer provider
|
||||||
|
|
||||||
|
// Request to provide a different page of the paginated results in the "available" panel.
|
||||||
|
export class DualSelectPaginatorNavEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-paginator-nav";
|
||||||
|
constructor(public page: number = 0) {
|
||||||
|
super(DualSelectPaginatorNavEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request to provide a filtered collection for the "available" panel via a search string
|
||||||
|
export class DualSelectSearchEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-search";
|
||||||
|
constructor(public search: string) {
|
||||||
|
super(DualSelectSearchEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request to update the "selected" list in the provider
|
||||||
|
export class DualSelectChangeEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-change";
|
||||||
|
constructor(public selected: DualSelectPair[]) {
|
||||||
|
super(DualSelectChangeEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paginator and specific item events
|
||||||
|
|
||||||
|
export const moveEvents = [
|
||||||
|
"add-all",
|
||||||
|
"add-one",
|
||||||
|
"add-selected",
|
||||||
|
"delete-all",
|
||||||
|
"remove-all",
|
||||||
|
"remove-one",
|
||||||
|
"remove-selected",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type MoveEventType = (typeof moveEvents)[number];
|
||||||
|
|
||||||
|
// Request to add or remove all, some, or just one item from the "selected" panel
|
||||||
|
export class DualSelectMoveRequestEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-request-move";
|
||||||
|
constructor(
|
||||||
|
public move: MoveEventType,
|
||||||
|
public key?: string,
|
||||||
|
) {
|
||||||
|
super(DualSelectMoveRequestEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update events
|
||||||
|
|
||||||
|
// Request to update the viewset
|
||||||
|
export class DualSelectUpdateEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-update";
|
||||||
|
constructor() {
|
||||||
|
super(DualSelectUpdateEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DualSelectMoveChangedEvent {
|
||||||
|
keys: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request to update the list of "marked for move" items in the "available" panel
|
||||||
|
export class DualSelectMoveAvailableEvent extends Event implements DualSelectMoveChangedEvent {
|
||||||
|
static readonly eventName = "ak-dual-select-move-available";
|
||||||
|
constructor(public keys: string[]) {
|
||||||
|
super(DualSelectMoveAvailableEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request to update the list of "marked for move" items in the "selected" panel
|
||||||
|
export class DualSelectMoveSelectedEvent extends Event implements DualSelectMoveChangedEvent {
|
||||||
|
static readonly eventName = "ak-dual-select-move-selected";
|
||||||
|
constructor(public keys: string[]) {
|
||||||
|
super(DualSelectMoveSelectedEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request to update either panel with a Filter
|
||||||
|
export class DualSelectPanelSearchEvent extends Event {
|
||||||
|
static readonly eventName = "ak-dual-select-panel-search";
|
||||||
|
constructor(
|
||||||
|
public source: string,
|
||||||
|
public filterOn: string,
|
||||||
|
) {
|
||||||
|
super(DualSelectPanelSearchEvent.eventName, { bubbles: true, composed: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementEventMap {
|
||||||
|
[DualSelectUpdateEvent.eventName]: DualSelectUpdateEvent;
|
||||||
|
[DualSelectMoveAvailableEvent.eventName]: DualSelectMoveAvailableEvent;
|
||||||
|
[DualSelectMoveSelectedEvent.eventName]: DualSelectMoveSelectedEvent;
|
||||||
|
[DualSelectMoveRequestEvent.eventName]: DualSelectMoveRequestEvent;
|
||||||
|
[DualSelectPaginatorNavEvent.eventName]: DualSelectPaginatorNavEvent;
|
||||||
|
[DualSelectSearchEvent.eventName]: DualSelectSearchEvent;
|
||||||
|
[DualSelectChangeEvent.eventName]: DualSelectChangeEvent;
|
||||||
|
[DualSelectPanelSearchEvent.eventName]: DualSelectPanelSearchEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WindowEventMap {
|
||||||
|
[DualSelectMoveRequestEvent.eventName]: DualSelectMoveRequestEvent;
|
||||||
|
[DualSelectPaginatorNavEvent.eventName]: DualSelectPaginatorNavEvent;
|
||||||
|
[DualSelectMoveSelectedEvent.eventName]: DualSelectMoveSelectedEvent;
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import { TemplateResult, html } from "lit";
|
|||||||
|
|
||||||
import "../components/ak-dual-select-available-pane";
|
import "../components/ak-dual-select-available-pane";
|
||||||
import { AkDualSelectAvailablePane } from "../components/ak-dual-select-available-pane";
|
import { AkDualSelectAvailablePane } from "../components/ak-dual-select-available-pane";
|
||||||
|
import { DualSelectMoveSelectedEvent } from "../events";
|
||||||
import "./sb-host-provider";
|
import "./sb-host-provider";
|
||||||
|
|
||||||
const metadata: Meta<AkDualSelectAvailablePane> = {
|
const metadata: Meta<AkDualSelectAvailablePane> = {
|
||||||
@ -53,15 +54,15 @@ const container = (testItem: TemplateResult) =>
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const handleMoveChanged = (result: any) => {
|
const handleMoveChanged = (result: DualSelectMoveSelectedEvent) => {
|
||||||
const target = document.querySelector("#action-button-message-pad");
|
const target = document.querySelector("#action-button-message-pad");
|
||||||
target!.innerHTML = "";
|
target!.innerHTML = "";
|
||||||
result.detail.forEach((key: string) => {
|
result.keys.forEach((key: string) => {
|
||||||
target!.append(new DOMParser().parseFromString(`<li>${key}</li>`, "text/xml").firstChild!);
|
target!.append(new DOMParser().parseFromString(`<li>${key}</li>`, "text/xml").firstChild!);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("ak-dual-select-available-move-changed", handleMoveChanged);
|
window.addEventListener(DualSelectMoveSelectedEvent.eventName, handleMoveChanged);
|
||||||
|
|
||||||
type Story = StoryObj;
|
type Story = StoryObj;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { TemplateResult, html } from "lit";
|
|||||||
|
|
||||||
import "../components/ak-dual-select-controls";
|
import "../components/ak-dual-select-controls";
|
||||||
import { AkDualSelectControls } from "../components/ak-dual-select-controls";
|
import { AkDualSelectControls } from "../components/ak-dual-select-controls";
|
||||||
|
import { DualSelectMoveRequestEvent } from "../events";
|
||||||
|
|
||||||
const metadata: Meta<AkDualSelectControls> = {
|
const metadata: Meta<AkDualSelectControls> = {
|
||||||
title: "Elements / Dual Select / Control Panel",
|
title: "Elements / Dual Select / Control Panel",
|
||||||
@ -59,10 +60,9 @@ const displayMessage = (result: any) => {
|
|||||||
target!.appendChild(doc.firstChild!);
|
target!.appendChild(doc.firstChild!);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("ak-dual-select-add", () => displayMessage("add"));
|
window.addEventListener(DualSelectMoveRequestEvent.eventName, (ev: DualSelectMoveRequestEvent) =>
|
||||||
window.addEventListener("ak-dual-select-remove", () => displayMessage("remove"));
|
displayMessage(ev.move.toString()),
|
||||||
window.addEventListener("ak-dual-select-add-all", () => displayMessage("add all"));
|
);
|
||||||
window.addEventListener("ak-dual-select-remove-all", () => displayMessage("remove all"));
|
|
||||||
|
|
||||||
type Story = StoryObj;
|
type Story = StoryObj;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { Pagination } from "@goauthentik/api";
|
|||||||
|
|
||||||
import "../ak-dual-select";
|
import "../ak-dual-select";
|
||||||
import { AkDualSelect } from "../ak-dual-select";
|
import { AkDualSelect } from "../ak-dual-select";
|
||||||
|
import { DualSelectPaginatorNavEvent } from "../events";
|
||||||
import type { DualSelectPair } from "../types";
|
import type { DualSelectPair } from "../types";
|
||||||
|
|
||||||
const goodForYouRaw = `
|
const goodForYouRaw = `
|
||||||
@ -83,11 +84,11 @@ export class AkSbFruity extends LitElement {
|
|||||||
totalPages: Math.ceil(this.options.length / this.pageLength),
|
totalPages: Math.ceil(this.options.length / this.pageLength),
|
||||||
};
|
};
|
||||||
this.onNavigation = this.onNavigation.bind(this);
|
this.onNavigation = this.onNavigation.bind(this);
|
||||||
this.addEventListener("ak-pagination-nav-to", this.onNavigation);
|
this.addEventListener(DualSelectPaginatorNavEvent.eventName, this.onNavigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNavigation(evt: Event) {
|
onNavigation(evt: DualSelectPaginatorNavEvent) {
|
||||||
const current: number = (evt as CustomEvent).detail;
|
const current = evt.page;
|
||||||
const index = current - 1;
|
const index = current - 1;
|
||||||
if (index * this.pageLength > this.options.length) {
|
if (index * this.pageLength > this.options.length) {
|
||||||
console.warn(
|
console.warn(
|
||||||
|
@ -6,6 +6,7 @@ import { TemplateResult, html } from "lit";
|
|||||||
|
|
||||||
import "../components/ak-dual-select-selected-pane";
|
import "../components/ak-dual-select-selected-pane";
|
||||||
import { AkDualSelectSelectedPane } from "../components/ak-dual-select-selected-pane";
|
import { AkDualSelectSelectedPane } from "../components/ak-dual-select-selected-pane";
|
||||||
|
import { DualSelectMoveSelectedEvent } from "../events";
|
||||||
import "./sb-host-provider";
|
import "./sb-host-provider";
|
||||||
|
|
||||||
const metadata: Meta<AkDualSelectSelectedPane> = {
|
const metadata: Meta<AkDualSelectSelectedPane> = {
|
||||||
@ -50,15 +51,15 @@ const container = (testItem: TemplateResult) =>
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const handleMoveChanged = (result: any) => {
|
const handleMoveChanged = (result: DualSelectMoveSelectedEvent) => {
|
||||||
const target = document.querySelector("#action-button-message-pad");
|
const target = document.querySelector("#action-button-message-pad");
|
||||||
target!.innerHTML = "";
|
target!.innerHTML = "";
|
||||||
result.detail.forEach((key: string) => {
|
result.keys.forEach((key: string) => {
|
||||||
target!.append(new DOMParser().parseFromString(`<li>${key}</li>`, "text/xml").firstChild!);
|
target!.append(new DOMParser().parseFromString(`<li>${key}</li>`, "text/xml").firstChild!);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("ak-dual-select-selected-move-changed", handleMoveChanged);
|
window.addEventListener(DualSelectMoveSelectedEvent.eventName, handleMoveChanged);
|
||||||
|
|
||||||
type Story = StoryObj;
|
type Story = StoryObj;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import { TemplateResult, html } from "lit";
|
|||||||
|
|
||||||
import "../components/ak-pagination";
|
import "../components/ak-pagination";
|
||||||
import { AkPagination } from "../components/ak-pagination";
|
import { AkPagination } from "../components/ak-pagination";
|
||||||
|
import { DualSelectPaginatorNavEvent } from "../events";
|
||||||
|
|
||||||
const metadata: Meta<AkPagination> = {
|
const metadata: Meta<AkPagination> = {
|
||||||
title: "Elements / Dual Select / Pagination Control",
|
title: "Elements / Dual Select / Pagination Control",
|
||||||
@ -43,18 +44,18 @@ const container = (testItem: TemplateResult) =>
|
|||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const handleMoveChanged = (result: any) => {
|
const handleMoveChanged = (result: DualSelectPaginatorNavEvent) => {
|
||||||
console.debug(result);
|
console.debug(result);
|
||||||
const target = document.querySelector("#action-button-message-pad");
|
const target = document.querySelector("#action-button-message-pad");
|
||||||
target!.append(
|
target!.append(
|
||||||
new DOMParser().parseFromString(
|
new DOMParser().parseFromString(
|
||||||
`<li>Request to move to page ${result.detail}</li>`,
|
`<li>Request to move to page ${result.page}</li>`,
|
||||||
"text/xml",
|
"text/xml",
|
||||||
).firstChild!,
|
).firstChild!,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("ak-pagination-nav-to", handleMoveChanged);
|
window.addEventListener(DualSelectPaginatorNavEvent.eventName, handleMoveChanged);
|
||||||
|
|
||||||
type Story = StoryObj;
|
type Story = StoryObj;
|
||||||
|
|
||||||
|
@ -29,10 +29,3 @@ export type DataProvision = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DataProvider = (page: number, search?: string) => Promise<DataProvision>;
|
export type DataProvider = (page: number, search?: string) => Promise<DataProvision>;
|
||||||
|
|
||||||
export interface SearchbarEvent extends CustomEvent {
|
|
||||||
detail: {
|
|
||||||
source: string;
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user