feature(ComboControl):
Some checks failed
Some checks failed
- 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:
155
src/shared/ui/ComboControl/ComboControl.svelte
Normal file
155
src/shared/ui/ComboControl/ComboControl.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user