feat(ComboControlV2): crete ComboControlV2 - without increase/decrease buttons. Refresh styling of the original one

This commit is contained in:
Ilia Mashkov
2026-01-21 21:50:30 +03:00
parent a0f184665d
commit c6d20aae3d
3 changed files with 96 additions and 17 deletions

View File

@@ -8,9 +8,13 @@
<script lang="ts"> <script lang="ts">
import type { TypographyControl } from '$shared/lib'; import type { TypographyControl } from '$shared/lib';
import { Button } from '$shared/shadcn/ui/button'; import { Button } from '$shared/shadcn/ui/button';
import * as ButtonGroup from '$shared/shadcn/ui/button-group'; import { Root as ButtonGroupRoot } from '$shared/shadcn/ui/button-group';
import { Input } from '$shared/shadcn/ui/input'; import { Input } from '$shared/shadcn/ui/input';
import * as Popover from '$shared/shadcn/ui/popover'; import {
Content as PopoverContent,
Root as PopoverRoot,
Trigger as PopoverTrigger,
} from '$shared/shadcn/ui/popover';
import { Slider } from '$shared/shadcn/ui/slider'; import { Slider } from '$shared/shadcn/ui/slider';
import MinusIcon from '@lucide/svelte/icons/minus'; import MinusIcon from '@lucide/svelte/icons/minus';
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
@@ -67,30 +71,32 @@ const handleSliderChange = (newValue: number) => {
}; };
</script> </script>
<ButtonGroup.Root> <ButtonGroupRoot class="bg-transparent border-none shadow-none">
<Button <Button
variant="outline" variant="ghost"
class="hover:bg-white/50 bg-white/20 border-none"
size="icon" size="icon"
aria-label={decreaseLabel} aria-label={decreaseLabel}
onclick={control.decrease} onclick={control.decrease}
disabled={control.isAtMin} disabled={control.isAtMin}
> >
<MinusIcon /> <MinusIcon class="size-4" />
</Button> </Button>
<Popover.Root> <PopoverRoot>
<Popover.Trigger> <PopoverTrigger>
{#snippet child({ props })} {#snippet child({ props })}
<Button <Button
{...props} {...props}
variant="outline" variant="ghost"
class="hover:bg-white/50 bg-white/20 border-none"
size="icon" size="icon"
aria-label={controlLabel} aria-label={controlLabel}
> >
{control.value} {control.value}
</Button> </Button>
{/snippet} {/snippet}
</Popover.Trigger> </PopoverTrigger>
<Popover.Content class="w-auto p-4"> <PopoverContent class="w-auto p-4">
<div class="flex flex-col items-center gap-3"> <div class="flex flex-col items-center gap-3">
<Slider <Slider
min={control.min} min={control.min}
@@ -110,15 +116,16 @@ const handleSliderChange = (newValue: number) => {
class="w-16 text-center" class="w-16 text-center"
/> />
</div> </div>
</Popover.Content> </PopoverContent>
</Popover.Root> </PopoverRoot>
<Button <Button
variant="outline" variant="ghost"
class="hover:bg-white/50 bg-white/20 border-none"
size="icon" size="icon"
aria-label={increaseLabel} aria-label={increaseLabel}
onclick={control.increase} onclick={control.increase}
disabled={control.isAtMax} disabled={control.isAtMax}
> >
<PlusIcon /> <PlusIcon class="size-4" />
</Button> </Button>
</ButtonGroup.Root> </ButtonGroupRoot>

View File

@@ -0,0 +1,72 @@
<!--
Component: ComboControl
Provides the same functionality as the original ComboControl but lacks increase/decrease buttons.
-->
<script lang="ts">
import type { TypographyControl } from '$shared/lib';
import { Input } from '$shared/shadcn/ui/input';
import { Slider } from '$shared/shadcn/ui/slider';
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import type { Snippet } from 'svelte';
import type { ChangeEventHandler } from 'svelte/elements';
interface Props {
control: TypographyControl;
ref?: Snippet;
}
let {
control,
ref = $bindable(),
}: Props = $props();
let sliderValue = $state(Number(control.value));
$effect(() => {
sliderValue = Number(control.value);
});
const handleInputChange: ChangeEventHandler<HTMLInputElement> = event => {
const parsedValue = parseFloat(event.currentTarget.value);
if (!isNaN(parsedValue)) {
control.value = parsedValue;
}
};
const handleSliderChange = (newValue: number) => {
control.value = newValue;
};
// Shared glass button class for consistency
// const glassBtnClass = cn(
// 'border-none transition-all duration-200',
// 'bg-white/10 hover:bg-white/40 active:scale-90',
// 'text-slate-900 font-medium',
// );
// const ghostStyle = cn(
// 'flex items-center justify-center transition-all duration-300 ease-out',
// 'text-slate-900/40 hover:text-slate-950 hover:bg-white/20 active:scale-90',
// 'disabled:opacity-10 disabled:pointer-events-none',
// );
</script>
<div class="flex flex-col items-center gap-4">
<Input
value={control.value}
onchange={handleInputChange}
min={control.min}
max={control.max}
class="w-14 h-8 text-xs text-center bg-white/40 border-none rounded-lg focus-visible:ring-indigo-500/50"
/>
<Slider
min={control.min}
max={control.max}
step={control.step}
value={sliderValue}
onValueChange={handleSliderChange}
type="single"
orientation="vertical"
class="h-30"
/>
</div>

View File

@@ -6,7 +6,7 @@
import CheckboxFilter from './CheckboxFilter/CheckboxFilter.svelte'; import CheckboxFilter from './CheckboxFilter/CheckboxFilter.svelte';
import ComboControl from './ComboControl/ComboControl.svelte'; import ComboControl from './ComboControl/ComboControl.svelte';
import ComparisonSlider from './ComparisonSlider/ComparisonSlider.svelte'; import ComboControlV2 from './ComboControlV2/ComboControlV2.svelte';
import ContentEditable from './ContentEditable/ContentEditable.svelte'; import ContentEditable from './ContentEditable/ContentEditable.svelte';
import SearchBar from './SearchBar/SearchBar.svelte'; import SearchBar from './SearchBar/SearchBar.svelte';
import VirtualList from './VirtualList/VirtualList.svelte'; import VirtualList from './VirtualList/VirtualList.svelte';
@@ -14,7 +14,7 @@ import VirtualList from './VirtualList/VirtualList.svelte';
export { export {
CheckboxFilter, CheckboxFilter,
ComboControl, ComboControl,
ComparisonSlider, ComboControlV2,
ContentEditable, ContentEditable,
SearchBar, SearchBar,
VirtualList, VirtualList,