feature(ComboControl):
Some checks failed
Lint / Lint Code (push) Failing after 7m14s
Test / Svelte Checks (push) Failing after 7m20s
Build / build (pull_request) Failing after 7m6s
Lint / Lint Code (pull_request) Failing after 7m14s
Test / Svelte Checks (pull_request) Failing after 7m16s

- create ComboControl component for typography settings (font size, font
  weight, line height)
- integrate it to TypographyMenu and integrate it to Layout
This commit is contained in:
Ilia Mashkov
2026-01-05 09:03:31 +03:00
parent d8e5f5a0b5
commit 3d35f1901d
30 changed files with 897 additions and 4 deletions

View File

@@ -0,0 +1,155 @@
<script lang="ts">
import { Button } from '$shared/shadcn/ui/button';
import * as ButtonGroup from '$shared/shadcn/ui/button-group';
import { Input } from '$shared/shadcn/ui/input';
import * as Popover from '$shared/shadcn/ui/popover';
import { Slider } from '$shared/shadcn/ui/slider';
import MinusIcon from '@lucide/svelte/icons/minus';
import PlusIcon from '@lucide/svelte/icons/plus';
import type { ChangeEventHandler } from 'svelte/elements';
interface ComboControlProps {
/**
* Controlled value
*/
value: number;
/**
* Callback function to handle value change
*/
onChange: (value: number) => void;
/**
* Callback function to handle increase
*/
onIncrease: () => void;
/**
* Callback function to handle decrease
*/
onDecrease: () => void;
/**
* Text for increase button aria-label
*/
increaseLabel?: string;
/**
* Text for decrease button aria-label
*/
decreaseLabel?: string;
/**
* Flag for disabling increase button
*/
increaseDisabled?: boolean;
/**
* Flag for disabling decrease button
*/
decreaseDisabled?: boolean;
/**
* Text for control button aria-label
*/
controlLabel?: string;
/**
* Minimum value for the input
*/
minValue?: number;
/**
* Maximum value for the input
*/
maxValue?: number;
/**
* Step value for the slider
*/
step?: number;
}
const {
value,
onChange,
onIncrease,
onDecrease,
increaseLabel,
decreaseLabel,
increaseDisabled,
decreaseDisabled,
controlLabel,
minValue = 0,
maxValue = 100,
step = 1,
}: ComboControlProps = $props();
// Local state for the slider to prevent infinite loops
let sliderValue = $state(value);
// Sync sliderValue when external value changes
$effect(() => {
sliderValue = value;
});
const handleInputChange: ChangeEventHandler<HTMLInputElement> = event => {
const parsedValue = parseFloat(event.currentTarget.value);
if (!isNaN(parsedValue)) {
onChange(parsedValue);
}
};
/**
* Handle slider value change.
* The Slider component passes the value as a number directly.
*/
const handleSliderChange = (value: number) => {
onChange(value);
};
</script>
<ButtonGroup.Root>
<Button
variant="outline"
size="icon"
aria-label={decreaseLabel}
onclick={onDecrease}
disabled={decreaseDisabled}
>
<MinusIcon />
</Button>
<Popover.Root>
<Popover.Trigger>
{#snippet child({ props })}
<Button
{...props}
variant="outline"
size="icon"
aria-label={controlLabel}
>
{value}
</Button>
{/snippet}
</Popover.Trigger>
<Popover.Content class="w-auto p-4">
<div class="flex flex-col items-center gap-3">
<Slider
min={minValue}
max={maxValue}
step={step}
value={sliderValue}
onValueChange={handleSliderChange}
type="single"
orientation="vertical"
class="h-48"
/>
<Input
value={String(value)}
min={minValue}
max={maxValue}
onchange={handleInputChange}
class="w-16 text-center"
/>
</div>
</Popover.Content>
</Popover.Root>
<Button
variant="outline"
size="icon"
aria-label={increaseLabel}
onclick={onIncrease}
disabled={increaseDisabled}
>
<PlusIcon />
</Button>
</ButtonGroup.Root>