@flanksource/clicky-ui
Version:
Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.
154 lines (153 loc) • 4.98 kB
JavaScript
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
import { cn } from "../lib/utils.js";
import { Icon } from "./Icon.js";
function bodyClasses(mode) {
switch (mode) {
case "active":
return "bg-primary/10 border-primary/40 text-primary font-medium";
case "include":
return "bg-green-500/10 border-green-500/50 text-green-700 dark:text-green-400 font-medium";
case "exclude":
return "bg-red-500/10 border-red-500/50 text-red-700 dark:text-red-400 font-medium";
case "neutral":
default:
return "border-border text-muted-foreground hover:bg-accent";
}
}
const SLOT_TRANSLATE = {
exclude: "translate-x-0",
neutral: "translate-x-4",
include: "translate-x-8"
};
function TristateSwitch({
mode,
onChange,
ariaLabel
}) {
const slot = mode === "exclude" ? "exclude" : mode === "include" ? "include" : "neutral";
const bg = slot === "exclude" ? "bg-red-500/80" : slot === "include" ? "bg-green-500/80" : "bg-muted";
return /* @__PURE__ */ jsx(
"button",
{
type: "button",
"aria-label": ariaLabel,
title: ariaLabel,
onClick: (event) => {
event.preventDefault();
event.stopPropagation();
onChange(nextTriStateMode(mode));
},
className: cn(
"relative inline-flex h-5 w-[52px] shrink-0 overflow-hidden rounded-full transition-colors duration-200",
bg
),
children: /* @__PURE__ */ jsx(
"span",
{
"aria-hidden": true,
className: cn(
"pointer-events-none absolute left-0.5 top-0.5 z-20 flex size-4 items-center justify-center rounded-full bg-white shadow-sm transition-transform duration-150",
SLOT_TRANSLATE[slot]
),
children: slot !== "neutral" && /* @__PURE__ */ jsx(
Icon,
{
name: slot === "exclude" ? "codicon:close" : "codicon:check",
className: cn(
"text-[10px]",
slot === "exclude" && "text-red-600",
slot === "include" && "text-green-600"
)
}
)
}
)
}
);
}
function FilterPill({
mode = "neutral",
label,
count,
icon,
badge,
onModeChange,
onClick,
title,
togglePosition = "left",
className
}) {
const triState = !!onModeChange;
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
count !== void 0 && /* @__PURE__ */ jsx(
"span",
{
className: cn(
"inline-flex items-center justify-center min-w-[16px] h-[16px] px-1 rounded-full text-[10px] font-bold text-white",
badge ?? "bg-muted-foreground"
),
children: count
}
),
icon && /* @__PURE__ */ jsx(Icon, { name: icon, className: "text-sm" }),
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
] });
if (triState) {
const control = /* @__PURE__ */ jsx(
TristateSwitch,
{
mode,
onChange: onModeChange,
...title ? { ariaLabel: title } : {}
}
);
const labelContent = /* @__PURE__ */ jsx("span", { className: "inline-flex min-w-0 items-center gap-1.5 text-xs text-foreground", children: content });
return /* @__PURE__ */ jsx("span", { className: cn("inline-flex items-center gap-1.5 select-none", className), title, children: togglePosition === "right" ? /* @__PURE__ */ jsxs(Fragment, { children: [
labelContent,
control
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
control,
labelContent
] }) });
}
return /* @__PURE__ */ jsxs(
"button",
{
type: "button",
onClick,
title,
className: cn(
"inline-flex items-center gap-1.5 text-xs px-2 py-0.5 rounded-full border transition-colors",
bodyClasses(mode),
className
),
children: [
/* @__PURE__ */ jsx(LegacyMarker, { mode }),
content
]
}
);
}
function LegacyMarker({ mode }) {
if (mode === "include") return /* @__PURE__ */ jsx(Icon, { name: "codicon:add", className: "text-xs" });
if (mode === "exclude") return /* @__PURE__ */ jsx(Icon, { name: "codicon:remove", className: "text-xs" });
if (mode === "active") return /* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-current" });
return /* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-current opacity-30" });
}
function nextTriStateMode(mode) {
if (mode === "include") return "exclude";
if (mode === "exclude") return "neutral";
return "include";
}
function FilterPillGroup({ children, className }) {
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-1.5 flex-wrap", className), children });
}
function FilterSeparator() {
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-border mx-0.5", children: "|" });
}
export {
FilterPill,
FilterPillGroup,
FilterSeparator
};
//# sourceMappingURL=FilterPill.js.map