Added DropDown and Updated Config with Theme and Color Switcher

This commit is contained in:
2025-08-17 11:31:18 +05:30
parent 94729c83d6
commit b2500e3060
7 changed files with 623 additions and 16 deletions
+27
View File
@@ -9,6 +9,8 @@
"version": "0.0.2-canary.21",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/dom": "^1.7.3",
"@tailwindcss/cli": "^4.1.12",
"@tailwindcss/vite": "^4.1.12",
"astro-icon": "^1.1.5",
@@ -657,6 +659,31 @@
"node": ">=18"
}
},
"node_modules/@floating-ui/core": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz",
"integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT"
},
"node_modules/@iconify-json/lets-icons": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@iconify-json/lets-icons/-/lets-icons-1.2.1.tgz",
+2
View File
@@ -67,6 +67,8 @@
"release": "npm publish --access public"
},
"dependencies": {
"@floating-ui/core": "^1.7.3",
"@floating-ui/dom": "^1.7.3",
"@tailwindcss/cli": "^4.1.12",
"@tailwindcss/vite": "^4.1.12",
"astro-icon": "^1.1.5",
+3 -8
View File
@@ -37,13 +37,9 @@ const {
const dot = compact ? "wr:h-3.5 wr:w-3.5" : "wr:h-4 wr:w-4";
const btn =
"wr:inline-flex wr:items-center wr:gap-2 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
"wr:text-foreground wr:h-10 wr:px-3 wr:text-sm wr:shadow-sm wr:transition wr:duration-200 wr:ease-out " +
"wr:hover:bg-muted/70 wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
"wr:py-3 wr:px-4 wr:inline-flex wr:items-center wr:gap-x-2 wr:text-sm wr:font-medium wr:rounded-lg wr:border wr:border-gray-200 wr:bg-white wr:text-gray-800 wr:shadow-2xs wr:hover:bg-gray-50 wr:focus:outline-hidden wr:focus:bg-gray-50 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:text-white wr:dark:hover:bg-neutral-700 wr:dark:focus:bg-neutral-700";
const iconBtn =
"wr:inline-flex wr:items-center wr:justify-center wr:h-9 wr:w-9 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
"wr:text-foreground wr:shadow-sm wr:transition wr:duration-200 wr:ease-out wr:hover:bg-muted/70 " +
"wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
"wr:py-3 wr:px-4 wr:inline-flex wr:items-center wr:gap-x-2 wr:text-sm wr:font-medium wr:rounded-lg wr:border wr:border-gray-200 wr:bg-white wr:text-gray-800 wr:shadow-2xs wr:hover:bg-gray-50 wr:focus:outline-hidden wr:focus:bg-gray-50 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:text-white wr:dark:hover:bg-neutral-700 wr:dark:focus:bg-neutral-700";
const item =
"wr:flex wr:items-center wr:gap-3 wr:w-full wr:px-3 wr:py-2.5 wr:rounded-lg wr:text-sm wr:hover:bg-muted/70 wr:transition wr:duration-150";
---
@@ -108,8 +104,7 @@ const item =
)}
<div
class="wr:absolute wr:right-0 wr:z-50 wr:mt-2 wr:w-56 wr:rounded-2xl wr:border wr:border-border wr:bg-popover/90 wr:backdrop-blur-md wr:text-popover-foreground wr:shadow-lg wr:p-1.5 wr:hidden
wr:origin-top-right wr:scale-95 wr:opacity-0 wr:transition wr:duration-150 wr:ease-out"
class="wr:absolute wr:right-0 wr:z-50 wr:w-56 wr:border-border wr:bg-popover/90 wr:backdrop-blur-md wr:text-popover-foreground wr:p-1.5 wr:origin-top-right wr:scale-95 wr:duration-150 wr:ease-out wr:transition-[opacity,margin] wr:border wr:border-gray-100 wr:duration wr:hs-dropdown-open:opacity-100 wr:opacity-0 wr:hidden wr:min-w-60 wr:bg-white wr:shadow-md wr:rounded-lg wr:mt-2 wr:dark:bg-neutral-800 wr:dark:border wr:dark:border-neutral-700 wr:dark:divide-neutral-700 wr:after:h-4 wr:after:absolute wr:after:-bottom-4 wr:after:start-0 wr:after:w-full wr:before:h-4 wr:before:absolute wr:before:-top-4 wr:before:start-0 wr:before:w-full"
role="menu"
aria-label="Brand color"
data-menu-panel
+518
View File
@@ -0,0 +1,518 @@
---
import type { HTMLAttributes } from "astro/types";
interface Props extends HTMLAttributes<"div"> {
openOn?: "click" | "hover" | "contextmenu";
placement?:
| "bottom-start"
| "bottom"
| "bottom-end"
| "top-start"
| "top"
| "top-end"
| "right-start"
| "right"
| "right-end"
| "left-start"
| "left"
| "left-end";
offset?: number;
shiftPadding?: number;
showArrow?: boolean;
closeOnSelect?: boolean;
disabled?: boolean;
matchTriggerWidth?: boolean;
initialOpen?: boolean;
}
const {
openOn = "click",
placement = "bottom-start",
offset = 8,
shiftPadding = 8,
showArrow = true,
closeOnSelect = true,
disabled = false,
matchTriggerWidth = false,
initialOpen = false,
class: className,
...rest
}: Props = Astro.props;
const id = `dd-${Math.random().toString(36).slice(2)}`;
---
<div
id={id}
class={["wr:relative wr:inline-flex", className].join(" ")}
{...rest}
data-dd-root
data-open-on={openOn}
data-placement={placement}
data-offset={offset}
data-shift-padding={shiftPadding}
data-show-arrow={showArrow ? "1" : ""}
data-close-on-select={closeOnSelect ? "1" : ""}
data-match-trigger-width={matchTriggerWidth ? "1" : ""}
data-initial-open={initialOpen ? "1" : ""}
>
<button
type="button"
class="wr:py-3 wr:px-4 wr:inline-flex wr:items-center wr:gap-x-2 wr:text-sm wr:font-medium wr:rounded-lg wr:border wr:border-gray-200 wr:bg-white wr:text-gray-800 wr:shadow-2xs wr:hover:bg-gray-50 wr:focus:outline-hidden wr:focus:bg-gray-50 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:text-white wr:dark:hover:bg-neutral-700 wr:dark:focus:bg-neutral-700"
data-dd-trigger
aria-haspopup="menu"
aria-expanded="false"
aria-controls={`${id}-menu`}
disabled={disabled}
>
<slot name="trigger">
<span>Menu</span>
</slot>
</button>
<div
id={`${id}-menu`}
class="wr:hidden wr:z-50 wr:min-w-60 wr:rounded-lg wr:bg-white wr:shadow-md wr:mt-2
wr:ring-1 wr:ring-border/70 wr:dark:bg-neutral-800 wr:dark:ring-neutral-700
wr:will-change-[transform,opacity] wr:transform-gpu
wr:transition-[transform,opacity] wr:duration-200 wr:ease-[cubic-bezier(0.16,1,0.3,1)]
wr:origin-[var(--dd-origin,top_left)] wr:translate-y-1 wr:scale-95 wr:opacity-0"
role="menu"
tabindex="-1"
data-dd-panel
aria-labelledby={id}
>
{
showArrow && (
<div data-dd-arrow class="wr:absolute wr:size-2 wr:rotate-45" />
)
}
<div class="wr:flex wr:flex-col wr:py-0.5" data-dd-list>
<slot>
<button
class="wr:text-left wr:px-3 wr:py-2 wr:rounded-xl wr:text-sm wr:hover:bg-muted/70 wr:transition-colors"
role="menuitem"
data-dd-item
>
Item One
</button>
<button
class="wr:text-left wr:px-3 wr:py-2 wr:rounded-xl wr:text-sm wr:hover:bg-muted/70 wr:transition-colors"
role="menuitem"
data-dd-item
>
Item Two
</button>
<div class="wr:h-px wr:bg-border/60 wr:my-1" aria-hidden="true">
</div>
<button
class="wr:text-left wr:px-3 wr:py-2 wr:rounded-xl wr:text-sm wr:text-danger wr:hover:bg-danger/10 wr:transition-colors"
role="menuitem"
data-dd-item
>
Delete
</button>
</slot>
</div>
</div>
</div>
<style>
/* Single outline (no double rings) handled via ring utilities above */
/* Arrow: no border — soft shadow only, so it blends with the panel ring */
[data-dd-arrow] {
background: hsl(var(--popover));
filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.18));
}
/* Rotate any user-provided chevron (add data-dd-chev on your icon) */
[data-dd-root][data-open="1"] [data-dd-chev] {
transform: rotate(180deg);
}
/* Open state animation */
[data-dd-panel].is-open {
transform: translateY(0) scale(1);
opacity: 1;
}
</style>
<script type="module" is:inline define:vars={{ id }}>
(() => {
const root = document.getElementById(id);
if (!root) return;
const trigger = root.querySelector("[data-dd-trigger]");
const panel = root.querySelector("[data-dd-panel]");
const arrowEl = root.querySelector("[data-dd-arrow]");
const openOn = root.dataset.openOn || "click";
const placement = root.dataset.placement || "bottom-start";
const offset = Number(root.dataset.offset || 8);
const shiftPadding = Number(root.dataset.shiftPadding || 8);
const showArrow = !!root.dataset.showArrow;
const closeOnSelect = !!root.dataset.closeOnSelect;
const matchTriggerWidth = !!root.dataset.matchTriggerWidth;
const initialOpen = !!root.dataset.initialOpen;
// Portal for fixed positioning
const portal = document.createElement("div");
portal.style.position = "fixed";
document.body.appendChild(portal);
panel.style.position = "fixed";
portal.appendChild(panel);
let open = false,
hoverTimer = 0;
const sideOf = (p) => p.split("-")[0];
const alignOf = (p) => p.split("-")[1] || "center";
function setOriginFor(side, align) {
const mapSide =
{ top: "bottom", bottom: "top", left: "right", right: "left" }[
side
] || "top";
const mapAlign = (() => {
if (side === "top" || side === "bottom") {
if (align === "start") return "left";
if (align === "end") return "right";
return "center";
} else {
if (align === "start") return "top";
if (align === "end") return "bottom";
return "center";
}
})();
panel.style.setProperty("--dd-origin", `${mapSide} ${mapAlign}`);
}
function placeOnce(pref) {
const t = trigger.getBoundingClientRect();
const p = panel.getBoundingClientRect();
const vw = document.documentElement.clientWidth;
const vh = document.documentElement.clientHeight;
const side = sideOf(pref);
const align = alignOf(pref);
let x = 0,
y = 0;
if (side === "bottom") y = t.bottom + offset;
else if (side === "top") y = t.top - p.height - offset;
else if (side === "right") x = t.right + offset;
else if (side === "left") x = t.left - p.width - offset;
if (side === "top" || side === "bottom") {
if (align === "start") x = t.left;
else if (align === "end") x = t.right - p.width;
else x = t.left + (t.width - p.width) / 2;
} else {
if (align === "start") y = t.top;
else if (align === "end") y = t.bottom - p.height;
else y = t.top + (t.height - p.height) / 2;
}
const pad = shiftPadding;
x = Math.max(pad, Math.min(x, vw - p.width - pad));
y = Math.max(pad, Math.min(y, vh - p.height - pad));
return { x, y }; // no Math.round
}
function positionPanel() {
if (!open) return;
if (matchTriggerWidth) {
const w = Math.max(192, trigger.getBoundingClientRect().width);
panel.style.minWidth = `${w}px`;
}
const best = smartPosition();
panel.style.left = `${best.x}px`;
panel.style.top = `${best.y}px`;
const side = sideOf(best.cand),
align = alignOf(best.cand);
setOriginFor(side, align);
panel.dataset.side = side;
if (showArrow && arrowEl) {
Object.assign(arrowEl.style, {
left: "",
top: "",
right: "",
bottom: "",
});
const t = trigger.getBoundingClientRect();
const p = panel.getBoundingClientRect();
const aw = arrowEl.getBoundingClientRect().width;
const ah = arrowEl.getBoundingClientRect().height;
const AR = 7;
if (side === "bottom") {
arrowEl.style.top = `${-AR}px`;
const cx = t.left + t.width / 2;
arrowEl.style.left = `${cx - p.left - aw / 2}px`;
} else if (side === "top") {
arrowEl.style.bottom = `${-AR}px`;
const cx = t.left + t.width / 2;
arrowEl.style.left = `${cx - p.left - aw / 2}px`;
} else if (side === "right") {
arrowEl.style.left = `${-AR}px`;
const cy = t.top + t.height / 2;
arrowEl.style.top = `${cy - p.top - ah / 2}px`;
} else if (side === "left") {
arrowEl.style.right = `${-AR}px`;
const cy = t.top + t.height / 2;
arrowEl.style.top = `${cy - p.top - ah / 2}px`;
}
}
}
function smartPosition() {
const s = sideOf(placement),
a = alignOf(placement);
const flips = {
top: "bottom",
bottom: "top",
left: "right",
right: "left",
};
const candidates = [placement, `${flips[s]}${a ? `-${a}` : ""}`];
if (s === "top" || s === "bottom")
candidates.push(
`left-${a || "start"}`,
`right-${a || "start"}`,
);
else
candidates.push(
`top-${a || "start"}`,
`bottom-${a || "start"}`,
);
const vw = document.documentElement.clientWidth;
const vh = document.documentElement.clientHeight;
let best = null;
for (const cand of candidates) {
const pos = placeOnce(cand);
const p = panel.getBoundingClientRect();
const w = p.width,
h = p.height;
const over =
Math.max(0, -pos.x) +
Math.max(0, -pos.y) +
Math.max(0, pos.x + w - vw) +
Math.max(0, pos.y + h - vh);
if (!best || over < best.score)
best = { cand, ...pos, score: over };
}
return best;
}
function positionPanel() {
if (!open) return;
if (matchTriggerWidth) {
const w = Math.max(192, trigger.getBoundingClientRect().width);
panel.style.minWidth = `${Math.round(w)}px`;
}
const best = smartPosition();
panel.style.left = best.x + "px";
panel.style.top = best.y + "px";
const side = sideOf(best.cand),
align = alignOf(best.cand);
setOriginFor(side, align);
panel.dataset.side = side;
if (showArrow && arrowEl) {
Object.assign(arrowEl.style, {
left: "",
top: "",
right: "",
bottom: "",
});
const t = trigger.getBoundingClientRect();
const p = panel.getBoundingClientRect();
const aw = arrowEl.getBoundingClientRect().width;
const ah = arrowEl.getBoundingClientRect().height;
const AR = 7;
if (side === "bottom") {
arrowEl.style.top = -AR + "px";
const cx = t.left + t.width / 2;
arrowEl.style.left =
Math.round(cx - p.left - aw / 2) + "px";
} else if (side === "top") {
arrowEl.style.bottom = -AR + "px";
const cx = t.left + t.width / 2;
arrowEl.style.left =
Math.round(cx - p.left - aw / 2) + "px";
} else if (side === "right") {
arrowEl.style.left = -AR + "px";
const cy = t.top + t.height / 2;
arrowEl.style.top = Math.round(cy - p.top - ah / 2) + "px";
} else if (side === "left") {
arrowEl.style.right = -AR + "px";
const cy = t.top + t.height / 2;
arrowEl.style.top = Math.round(cy - p.top - ah / 2) + "px";
}
}
}
function setOpen(v, { focusFirst = false } = {}) {
if (open === v) return;
open = v;
root.dataset.open = open ? "1" : "";
trigger.setAttribute("aria-expanded", String(open));
if (open) {
// 1) Make present but invisible, then position to final spot
panel.style.visibility = "hidden";
panel.classList.remove("wr:hidden");
// enforce start state (closed pose)
panel.classList.remove("is-open");
panel.classList.add(
"wr:translate-y-1",
"wr:scale-95",
"wr:opacity-0",
);
positionPanel(); // measure & place before user sees it
// 2) Reveal and animate to open pose on next frame
requestAnimationFrame(() => {
panel.style.visibility = "";
panel.classList.add("is-open");
panel.classList.remove(
"wr:translate-y-1",
"wr:scale-95",
"wr:opacity-0",
);
});
// listeners while open
window.addEventListener("resize", positionPanel, {
passive: true,
});
window.addEventListener("scroll", positionPanel, {
passive: true,
});
if (focusFirst) {
panel
.querySelector(
"[data-dd-item], [role='menuitem'], a[href], button:not([disabled])",
)
?.focus?.();
}
} else {
// animate to closed pose, then hide on transition end
const onDone = (ev) => {
if (ev.target !== panel) return; // ignore children transitions
panel.removeEventListener("transitionend", onDone);
panel.classList.add("wr:hidden");
};
panel.addEventListener("transitionend", onDone);
panel.classList.remove("is-open");
panel.classList.add(
"wr:translate-y-1",
"wr:scale-95",
"wr:opacity-0",
);
window.removeEventListener("resize", positionPanel);
window.removeEventListener("scroll", positionPanel);
}
}
const openDelayed = () => {
clearTimeout(hoverTimer);
hoverTimer = window.setTimeout(() => setOpen(true), 60);
};
const closeDelayed = () => {
clearTimeout(hoverTimer);
hoverTimer = window.setTimeout(() => setOpen(false), 120);
};
if (openOn === "click") {
trigger.addEventListener("click", () => setOpen(!open));
} else if (openOn === "hover") {
trigger.addEventListener("pointerenter", openDelayed);
trigger.addEventListener("pointerleave", closeDelayed);
panel.addEventListener("pointerenter", () =>
clearTimeout(hoverTimer),
);
panel.addEventListener("pointerleave", closeDelayed);
trigger.addEventListener("click", (e) => e.preventDefault());
} else if (openOn === "contextmenu") {
trigger.addEventListener("contextmenu", (e) => {
e.preventDefault();
setOpen(true, { focusFirst: true });
positionPanel();
});
}
trigger.addEventListener("keydown", (e) => {
if (e.key === "ArrowDown") {
e.preventDefault();
setOpen(true, { focusFirst: true });
}
if (e.key === "ArrowUp") {
e.preventDefault();
setOpen(true);
}
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setOpen(!open, { focusFirst: true });
}
if (e.key === "Escape") setOpen(false);
});
panel.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
e.preventDefault();
setOpen(false);
trigger.focus();
}
if (e.key === "Tab") setOpen(false);
});
document.addEventListener("pointerdown", (e) => {
if (!open) return;
if (root.contains(e.target) || panel.contains(e.target)) return;
setOpen(false);
});
panel.addEventListener("click", (e) => {
const item = e.target.closest(
"[data-dd-item], [role='menuitem'], a[href], button",
);
if (item && closeOnSelect) setOpen(false);
});
if (initialOpen) setOpen(true);
document.addEventListener("astro:before-swap", () => {
try {
panel.remove();
} catch {}
try {
portal.remove();
} catch {}
});
})();
</script>
<style>
[data-dd-arrow] {
background: hsl(var(--popover));
filter: drop-shadow(0 8px 14px rgba(0, 0, 0, 0.18));
}
@media (prefers-reduced-motion: reduce) {
[data-dd-panel] {
transition: none !important;
}
}
</style>
+3 -8
View File
@@ -25,14 +25,10 @@ const {
}: Props = Astro.props;
const iconBtn =
"wr:inline-flex wr:items-center wr:justify-center wr:h-9 wr:w-9 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
"wr:text-foreground wr:shadow-sm wr:transition wr:duration-200 wr:ease-out wr:hover:bg-muted/70 " +
"wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2 wr:ring-offset-background";
"wr:py-3 wr:px-4 wr:inline-flex wr:items-center wr:gap-x-2 wr:text-sm wr:font-medium wr:rounded-lg wr:border wr:border-gray-200 wr:bg-white wr:text-gray-800 wr:shadow-2xs wr:hover:bg-gray-50 wr:focus:outline-hidden wr:focus:bg-gray-50 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:text-white wr:dark:hover:bg-neutral-700 wr:dark:focus:bg-neutral-700";
const menuBtn =
"wr:inline-flex wr:items-center wr:gap-2 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
"wr:text-foreground wr:h-10 wr:px-3 wr:text-sm wr:shadow-sm wr:transition wr:duration-200 wr:ease-out " +
"wr:hover:bg-muted/70 wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2 wr:ring-offset-background";
"wr:py-3 wr:px-4 wr:inline-flex wr:items-center wr:gap-x-2 wr:text-sm wr:font-medium wr:rounded-lg wr:border wr:border-gray-200 wr:bg-white wr:text-gray-800 wr:shadow-2xs wr:hover:bg-gray-50 wr:focus:outline-hidden wr:focus:bg-gray-50 wr:disabled:opacity-50 wr:disabled:pointer-events-none wr:dark:bg-neutral-800 wr:dark:border-neutral-700 wr:dark:text-white wr:dark:hover:bg-neutral-700 wr:dark:focus:bg-neutral-700";
const item =
"wr:flex wr:items-center wr:gap-3 wr:w-full wr:px-3 wr:py-2.5 wr:rounded-lg wr:text-sm wr:hover:bg-muted/70 wr:transition wr:duration-150";
@@ -63,8 +59,7 @@ const containerId = `ts-${Math.random().toString(36).slice(2)}`;
<!-- Dropdown -->
<div
class="wr:absolute wr:right-0 wr:z-50 wr:mt-2 wr:w-56 wr:rounded-2xl wr:border wr:border-border wr:bg-popover/90 wr:backdrop-blur-md wr:text-popover-foreground wr:shadow-lg wr:p-1.5 wr:hidden
wr:origin-top-right wr:scale-95 wr:opacity-0 wr:transition wr:duration-150 wr:ease-out"
class="wr:absolute wr:right-0 wr:z-50 wr:w-56 wr:border-border wr:bg-popover/90 wr:backdrop-blur-md wr:text-popover-foreground wr:p-1.5 wr:origin-top-right wr:scale-95 wr:duration-150 wr:ease-out wr:transition-[opacity,margin] wr:border wr:border-gray-100 wr:duration wr:hs-dropdown-open:opacity-100 wr:opacity-0 wr:hidden wr:min-w-60 wr:bg-white wr:shadow-md wr:rounded-lg wr:mt-2 wr:dark:bg-neutral-800 wr:dark:border wr:dark:border-neutral-700 wr:dark:divide-neutral-700 wr:after:h-4 wr:after:absolute wr:after:-bottom-4 wr:after:start-0 wr:after:w-full wr:before:h-4 wr:before:absolute wr:before:-top-4 wr:before:start-0 wr:before:w-full"
role="menu"
aria-label="Theme"
data-menu-panel
+67
View File
@@ -0,0 +1,67 @@
---
import Base from "../layouts/Base.astro";
import Dropdown from "../components/Dropdown.astro";
import ThemeSwitcher from "../components/ThemeSwitcher.astro";
import ColorSwitcher from "../components/ColorSwitcher.astro";
---
<Base>
<div
class="wrnx wr:flex wr:items-center wr:flex-col wr:gap-4 wr:w-full wr:h-screen"
>
<ThemeSwitcher
darkBgClass={["wr:dark:bg-neutral-500", "wr:text-white"]}
lightBgClass={["wr:bg-white", "wr:text-black"]}
/>
<ColorSwitcher ui="menu" />
<Dropdown placement="bottom">
<div slot="trigger" class="wr:inline-flex wr:items-center wr:gap-2">
Actions
<svg
data-dd-chev
class="wr:size-4 wr:opacity-70 wr:transition-transform wr:duration-150"
viewBox="0 0 24 24"
fill="none"
>
<path
d="M7 10l5 5 5-5"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"></path>
</svg>
</div>
<div class="wr:flex wr:p-2 wr:flex-col">
<a
href="/theme"
data-dd-item
class="wr:w-full wr:flex wr:items-center wr:gap-x-3.5 wr:py-2 wr:px-3 wr:rounded-lg wr:text-sm wr:text-gray-800 wr:hover:bg-gray-100 wr:focus:outline-hidden wr:focus:bg-gray-100 wr:dark:text-neutral-400 wr:dark:hover:bg-neutral-700 wr:dark:hover:text-neutral-300 wr:dark:focus:bg-neutral-700"
>Theme</a
>
<button
data-dd-item
class="wr:w-full wr:flex wr:items-center wr:gap-x-3.5 wr:py-2 wr:px-3 wr:rounded-lg wr:text-sm wr:text-gray-800 wr:hover:bg-gray-100 wr:focus:outline-hidden wr:focus:bg-gray-100 wr:dark:text-neutral-400 wr:dark:hover:bg-neutral-700 wr:dark:hover:text-neutral-300 wr:dark:focus:bg-neutral-700"
>Edit</button
>
<hr class="wr:my-2 wr:border-gray-300" aria-hidden="true" />
<button
data-dd-item
id="alertHere"
class="wr:w-full wr:flex wr:items-center wr:gap-x-3.5 wr:py-2 wr:px-3 wr:rounded-lg wr:text-sm wr:text-gray-800 wr:hover:bg-gray-100 wr:focus:outline-hidden wr:focus:bg-gray-100 wr:dark:text-neutral-400 wr:dark:hover:bg-neutral-700 wr:dark:hover:text-neutral-300 wr:dark:focus:bg-neutral-700"
>Delete</button
>
</div>
</Dropdown>
</div>
</Base>
<script>
const alerthere = document.getElementById("alertHere");
if (alerthere)
alerthere.addEventListener("click", () => {
alert("ALERT MSG");
});
</script>
+3
View File
@@ -17,5 +17,8 @@ import Base from "../layouts/Base.astro";
<li class="list-item">
<a href="/theme">Theme</a>
</li>
<li class="list-item">
<a href="/dropdown">Dropdown</a>
</li>
</ul>
</Base>