feat(ui): add InfoTooltip and PresetSlider shared components

This commit is contained in:
oyi77
2026-05-17 05:33:27 +07:00
parent 7e9efe7cb0
commit 8669bf69ec
3 changed files with 100 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
"use client";
import { cn } from "@/shared/utils/cn";
interface InfoTooltipProps {
text: string;
className?: string;
}
export default function InfoTooltip({ text, className }: InfoTooltipProps) {
return (
<span className={cn("relative inline-flex group", className)}>
<span
className="material-symbols-outlined text-[16px] text-text-muted cursor-help"
aria-label={text}
>
info
</span>
<span
role="tooltip"
className={cn(
"absolute bottom-full left-1/2 -translate-x-1/2 mb-2",
"px-2.5 py-1.5 text-xs font-medium text-white bg-gray-900/95 rounded-md shadow-lg",
"whitespace-nowrap pointer-events-none",
"border border-white/10",
"opacity-0 group-hover:opacity-100 transition-opacity duration-150",
"z-50"
)}
>
{text}
</span>
</span>
);
}

View File

@@ -0,0 +1,64 @@
"use client";
import { cn } from "@/shared/utils/cn";
interface PresetSliderProps {
value: number;
onChange: (value: number) => void;
presets: { label: string; value: number }[];
min?: number;
max?: number;
step?: number;
className?: string;
}
export default function PresetSlider({
value,
onChange,
presets,
min = 0,
max = 100,
step = 1,
className,
}: PresetSliderProps) {
return (
<div className={cn("flex flex-col gap-3", className)}>
<div className="flex flex-wrap gap-2">
{presets.map((preset) => (
<button
key={preset.value}
type="button"
onClick={() => onChange(preset.value)}
className={cn(
"px-3 py-1.5 text-xs font-medium rounded-full transition-colors",
"border",
value === preset.value
? "bg-primary text-white border-primary"
: "bg-transparent text-text-muted border-black/10 dark:border-white/10 hover:border-primary/30 hover:text-text-main"
)}
>
{preset.label}
</button>
))}
</div>
<input
type="range"
min={min}
max={max}
step={step}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
className={cn(
"w-full h-1.5 rounded-full appearance-none cursor-pointer",
"bg-black/10 dark:bg-white/10",
"accent-primary"
)}
/>
<div className="flex justify-between text-[10px] text-text-muted">
<span>{min}</span>
<span className="font-medium text-text-main">{value}</span>
<span>{max}</span>
</div>
</div>
);
}

View File

@@ -33,6 +33,8 @@ export { default as NotificationToast } from "./NotificationToast";
export { default as FilterBar } from "./FilterBar";
export { default as ColumnToggle } from "./ColumnToggle";
export { default as DataTable } from "./DataTable";
export { default as InfoTooltip } from "./InfoTooltip";
export { default as PresetSlider } from "./PresetSlider";
// Layouts
export * from "./layouts";