Clan-app: add popover - remove clan confirm

This commit is contained in:
Johannes Kirschbauer
2024-07-25 13:10:06 +02:00
parent 7783f17425
commit 53d7c2507e
2 changed files with 196 additions and 14 deletions

View File

@@ -0,0 +1,128 @@
import { createEffect, createMemo, createSignal, onCleanup } from "solid-js";
import type {
ComputePositionConfig,
ComputePositionReturn,
ReferenceElement,
} from "@floating-ui/dom";
import { computePosition } from "@floating-ui/dom";
export interface UseFloatingOptions<
R extends ReferenceElement,
F extends HTMLElement,
> extends Partial<ComputePositionConfig> {
whileElementsMounted?: (
reference: R,
floating: F,
update: () => void
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
) => void | (() => void);
}
interface UseFloatingState extends Omit<ComputePositionReturn, "x" | "y"> {
x?: number | null;
y?: number | null;
}
export interface UseFloatingResult extends UseFloatingState {
update(): void;
}
export function useFloating<R extends ReferenceElement, F extends HTMLElement>(
reference: () => R | undefined | null,
floating: () => F | undefined | null,
options?: UseFloatingOptions<R, F>
): UseFloatingResult {
const placement = () => options?.placement ?? "bottom";
const strategy = () => options?.strategy ?? "absolute";
const [data, setData] = createSignal<UseFloatingState>({
x: null,
y: null,
placement: placement(),
strategy: strategy(),
middlewareData: {},
});
const [error, setError] = createSignal<{ value: unknown } | undefined>();
createEffect(() => {
const currentError = error();
if (currentError) {
throw currentError.value;
}
});
const version = createMemo(() => {
reference();
floating();
return {};
});
function update() {
const currentReference = reference();
const currentFloating = floating();
if (currentReference && currentFloating) {
const capturedVersion = version();
computePosition(currentReference, currentFloating, {
middleware: options?.middleware,
placement: placement(),
strategy: strategy(),
}).then(
(currentData) => {
// Check if it's still valid
if (capturedVersion === version()) {
setData(currentData);
}
},
(err) => {
setError(err);
}
);
}
}
createEffect(() => {
const currentReference = reference();
const currentFloating = floating();
options?.middleware;
placement();
strategy();
if (currentReference && currentFloating) {
if (options?.whileElementsMounted) {
const cleanup = options.whileElementsMounted(
currentReference,
currentFloating,
update
);
if (cleanup) {
onCleanup(cleanup);
}
} else {
update();
}
}
});
return {
get x() {
return data().x;
},
get y() {
return data().y;
},
get placement() {
return data().placement;
},
get strategy() {
return data().strategy;
},
get middlewareData() {
return data().middlewareData;
},
update,
};
}

View File

@@ -12,8 +12,18 @@ import {
setRoute, setRoute,
clanList, clanList,
} from "@/src/App"; } from "@/src/App";
import { For, Show } from "solid-js"; import { createEffect, createSignal, For, Show } from "solid-js";
import { createQuery } from "@tanstack/solid-query"; import { createQuery } from "@tanstack/solid-query";
import { useFloating } from "@/src/floating";
import {
arrow,
autoUpdate,
flip,
hide,
offset,
shift,
size,
} from "@floating-ui/dom";
export const registerClan = async () => { export const registerClan = async () => {
try { try {
@@ -54,6 +64,30 @@ const ClanDetails = (props: ClanDetailsProps) => {
}, },
})); }));
const [reference, setReference] = createSignal<HTMLElement>();
const [floating, setFloating] = createSignal<HTMLElement>();
const [arrowEl, setArrowEl] = createSignal<HTMLElement>();
// `position` is a reactive object.
const position = useFloating(reference, floating, {
placement: "top",
// pass options. Ensure the cleanup function is returned.
whileElementsMounted: (reference, floating, update) =>
autoUpdate(reference, floating, update, {
animationFrame: true,
}),
middleware: [
offset(5),
shift(),
flip(),
hide({
strategy: "referenceHidden",
}),
],
});
return ( return (
<div class="stat"> <div class="stat">
<div class="stat-figure text-primary"> <div class="stat-figure text-primary">
@@ -72,23 +106,43 @@ const ClanDetails = (props: ClanDetailsProps) => {
{activeURI() === clan_dir ? "active" : "select"} {activeURI() === clan_dir ? "active" : "select"}
</button> </button>
<button <button
popovertarget={`clan-delete-popover-${clan_dir}`}
popovertargetaction="toggle"
ref={setReference}
class="btn btn-ghost btn-outline join-item btn-sm" class="btn btn-ghost btn-outline join-item btn-sm"
onClick={() => {
setClanList((s) =>
s.filter((v, idx) => {
if (v == clan_dir) {
setActiveURI(
clanList()[idx - 1] || clanList()[idx + 1] || null
);
return false;
}
return true;
})
);
}}
> >
Remove Remove
</button> </button>
<div
popover="auto"
id={`clan-delete-popover-${clan_dir}`}
ref={setFloating}
style={{
position: position.strategy,
top: `${position.y ?? 0}px`,
left: `${position.x ?? 0}px`,
}}
class="bg-transparent"
>
<button
class="btn btn-warning btn-sm"
onClick={() => {
setClanList((s) =>
s.filter((v, idx) => {
if (v == clan_dir) {
setActiveURI(
clanList()[idx - 1] || clanList()[idx + 1] || null
);
return false;
}
return true;
})
);
}}
>
Remove from App
</button>
</div>
</div> </div>
</div> </div>
<div class="stat-title">Clan URI</div> <div class="stat-title">Clan URI</div>