Add feature label component
This commit is contained in:
parent
c91561d7fe
commit
392f73467b
3 changed files with 45 additions and 11 deletions
|
|
@ -10,6 +10,7 @@ import { groupFeaturesByCategory } from '../lib/features';
|
||||||
import InfoPopup from './InfoPopup';
|
import InfoPopup from './InfoPopup';
|
||||||
import { FeatureInfoPopup } from './FeatureInfoPopup';
|
import { FeatureInfoPopup } from './FeatureInfoPopup';
|
||||||
import { FeatureActions } from './FeatureIcons';
|
import { FeatureActions } from './FeatureIcons';
|
||||||
|
import { FeatureLabel } from './ui/FeatureLabel';
|
||||||
|
|
||||||
interface FiltersProps {
|
interface FiltersProps {
|
||||||
features: FeatureMeta[];
|
features: FeatureMeta[];
|
||||||
|
|
@ -91,7 +92,7 @@ function FeatureBrowser({
|
||||||
className="flex items-start justify-between px-3 py-1.5 hover:bg-teal-50 dark:hover:bg-teal-900/30 dark:text-warm-300"
|
className="flex items-start justify-between px-3 py-1.5 hover:bg-teal-50 dark:hover:bg-teal-900/30 dark:text-warm-300"
|
||||||
>
|
>
|
||||||
<div className="min-w-0 mr-2">
|
<div className="min-w-0 mr-2">
|
||||||
<span className="text-sm truncate block">{f.name}</span>
|
<FeatureLabel feature={f} onShowInfo={setInfoFeature} size="sm" />
|
||||||
{f.description && (
|
{f.description && (
|
||||||
<span className="text-xs text-warm-400 dark:text-warm-500 truncate block">
|
<span className="text-xs text-warm-400 dark:text-warm-500 truncate block">
|
||||||
{f.description}
|
{f.description}
|
||||||
|
|
@ -102,7 +103,6 @@ function FeatureBrowser({
|
||||||
feature={f}
|
feature={f}
|
||||||
isPinned={isPinned}
|
isPinned={isPinned}
|
||||||
onTogglePin={onTogglePin}
|
onTogglePin={onTogglePin}
|
||||||
onShowInfo={setInfoFeature}
|
|
||||||
onAdd={onAddFilter}
|
onAdd={onAddFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -232,12 +232,11 @@ export default memo(function Filters({
|
||||||
className={`space-y-1 p-3 rounded ${pinnedFeature === feature.name ? 'ring-2 ring-teal-400 bg-teal-50/50 dark:bg-teal-900/20' : ''}`}
|
className={`space-y-1 p-3 rounded ${pinnedFeature === feature.name ? 'ring-2 ring-teal-400 bg-teal-50/50 dark:bg-teal-900/20' : ''}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>{feature.name}</Label>
|
<FeatureLabel feature={feature} onShowInfo={setActiveInfoFeature} size="sm" />
|
||||||
<FeatureActions
|
<FeatureActions
|
||||||
feature={feature}
|
feature={feature}
|
||||||
isPinned={pinnedFeature === feature.name}
|
isPinned={pinnedFeature === feature.name}
|
||||||
onTogglePin={onTogglePin}
|
onTogglePin={onTogglePin}
|
||||||
onShowInfo={setActiveInfoFeature}
|
|
||||||
onRemove={onRemoveFilter}
|
onRemove={onRemoveFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -281,15 +280,16 @@ export default memo(function Filters({
|
||||||
className={`space-y-1 p-3 rounded ${isActive ? 'ring-2 ring-teal-400 bg-teal-50 dark:bg-teal-900/30' : isPinned ? 'ring-2 ring-teal-400 bg-teal-50/50 dark:bg-teal-900/20' : ''}`}
|
className={`space-y-1 p-3 rounded ${isActive ? 'ring-2 ring-teal-400 bg-teal-50 dark:bg-teal-900/30' : isPinned ? 'ring-2 ring-teal-400 bg-teal-50/50 dark:bg-teal-900/20' : ''}`}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<Label>
|
<div className="flex items-center gap-1 min-w-0">
|
||||||
{feature.name}: {formatFilterValue(displayValue[0])} -{' '}
|
<FeatureLabel feature={feature} onShowInfo={setActiveInfoFeature} size="sm" />
|
||||||
{formatFilterValue(displayValue[1])}
|
<span className="text-sm text-warm-500 dark:text-warm-400">
|
||||||
</Label>
|
{formatFilterValue(displayValue[0])} - {formatFilterValue(displayValue[1])}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<FeatureActions
|
<FeatureActions
|
||||||
feature={feature}
|
feature={feature}
|
||||||
isPinned={isPinned}
|
isPinned={isPinned}
|
||||||
onTogglePin={onTogglePin}
|
onTogglePin={onTogglePin}
|
||||||
onShowInfo={setActiveInfoFeature}
|
|
||||||
onRemove={onRemoveFilter}
|
onRemove={onRemoveFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
35
frontend/src/components/ui/FeatureLabel.tsx
Normal file
35
frontend/src/components/ui/FeatureLabel.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import type { FeatureMeta } from '../../types';
|
||||||
|
import { InfoIcon } from './icons';
|
||||||
|
|
||||||
|
interface FeatureLabelProps {
|
||||||
|
feature: FeatureMeta;
|
||||||
|
onShowInfo?: (feature: FeatureMeta) => void;
|
||||||
|
className?: string;
|
||||||
|
size?: 'xs' | 'sm';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeatureLabel({
|
||||||
|
feature,
|
||||||
|
onShowInfo,
|
||||||
|
className = '',
|
||||||
|
size = 'xs',
|
||||||
|
}: FeatureLabelProps) {
|
||||||
|
const textClass = size === 'sm' ? 'text-sm' : 'text-xs';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex items-center gap-1 min-w-0 ${className}`}>
|
||||||
|
<span className={`${textClass} text-warm-700 dark:text-warm-300 truncate`}>
|
||||||
|
{feature.name}
|
||||||
|
</span>
|
||||||
|
{feature.detail && onShowInfo && (
|
||||||
|
<button
|
||||||
|
onClick={() => onShowInfo(feature)}
|
||||||
|
className="p-1 -m-0.5 rounded text-warm-400 hover:text-warm-700 dark:hover:text-warm-300 hover:bg-warm-100 dark:hover:bg-warm-700 shrink-0"
|
||||||
|
title="Feature info"
|
||||||
|
>
|
||||||
|
<InfoIcon className="w-3.5 h-3.5" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ interface TabButtonProps {
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TabButton({ label, count, isActive, onClick }: TabButtonProps) {
|
export function TabButton({ label, isActive, onClick }: TabButtonProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className={`flex-1 p-3 ${
|
className={`flex-1 p-3 ${
|
||||||
|
|
@ -16,7 +16,6 @@ export function TabButton({ label, count, isActive, onClick }: TabButtonProps) {
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{count !== undefined && count > 0 && ` (${count})`}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue