Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { Maximize, Check } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { useModelStore, SCALE_PRESETS, type ScaleRatio } from '@/stores/useModelStore';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface ScaleSelectorProps {
|
||||
/** "compact" = small icon-style for XR HUD, "default" = labeled button for 2D viewer */
|
||||
variant?: 'default' | 'compact';
|
||||
}
|
||||
|
||||
export function ScaleSelector({ variant = 'default' }: ScaleSelectorProps) {
|
||||
const { scaleRatio, setScaleRatio, resetFineTuning } = useModelStore();
|
||||
|
||||
const reductions = SCALE_PRESETS.filter(p => p.factor < 1);
|
||||
const enlargements = SCALE_PRESETS.filter(p => p.factor > 1);
|
||||
const oneToOne = SCALE_PRESETS.find(p => p.label === '1:1')!;
|
||||
|
||||
const apply = (r: ScaleRatio) => {
|
||||
setScaleRatio(r);
|
||||
// Reset two-hand scale gesture so it doesn't fight the preset.
|
||||
if (r.label !== '1:1') resetFineTuning();
|
||||
toast.success(`Escala ${r.label}`, {
|
||||
description: 'Medições e ajuste fino mostrados em mm reais.',
|
||||
});
|
||||
};
|
||||
|
||||
const Trigger = variant === 'compact' ? (
|
||||
<Button variant="outline" size="sm" className="h-8 gap-1.5 text-[10px] font-mono" title="Escala do modelo">
|
||||
<Maximize className="h-3.5 w-3.5" />
|
||||
{scaleRatio.label}
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant={scaleRatio.label !== '1:1' ? 'default' : 'outline'} size="sm" className="gap-2 h-9">
|
||||
<Maximize className="h-3.5 w-3.5" />
|
||||
<span className="font-mono text-xs">Escala {scaleRatio.label}</span>
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>{Trigger}</PopoverTrigger>
|
||||
<PopoverContent className="w-72 space-y-3" side="top" align="start">
|
||||
<div>
|
||||
<p className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground mb-1">
|
||||
Escala atual
|
||||
</p>
|
||||
<p className="font-mono text-sm font-bold text-primary">{scaleRatio.label}</p>
|
||||
<p className="text-[10px] text-muted-foreground mt-1">
|
||||
Medições, ajuste fino e snap são compensados — todos os valores exibidos continuam em mm reais.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground mb-1.5">
|
||||
Real
|
||||
</p>
|
||||
<PresetButton current={scaleRatio} preset={oneToOne} onSelect={apply} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground mb-1.5">
|
||||
Reduzir (modelo menor)
|
||||
</p>
|
||||
<div className="grid grid-cols-3 gap-1.5">
|
||||
{reductions.map(p => (
|
||||
<PresetButton key={p.label} current={scaleRatio} preset={p} onSelect={apply} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-mono text-[10px] uppercase tracking-widest text-muted-foreground mb-1.5">
|
||||
Ampliar (modelo maior)
|
||||
</p>
|
||||
<div className="grid grid-cols-3 gap-1.5">
|
||||
{enlargements.map(p => (
|
||||
<PresetButton key={p.label} current={scaleRatio} preset={p} onSelect={apply} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
function PresetButton({
|
||||
preset, current, onSelect,
|
||||
}: { preset: ScaleRatio; current: ScaleRatio; onSelect: (r: ScaleRatio) => void }) {
|
||||
const active = preset.label === current.label;
|
||||
return (
|
||||
<Button
|
||||
variant={active ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="h-8 font-mono text-[11px] gap-1"
|
||||
onClick={() => onSelect(preset)}
|
||||
>
|
||||
{active && <Check className="h-3 w-3" />}
|
||||
{preset.label}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user