feat(TypographyMenu): add bindable "open" prop to close popover from outside

This commit is contained in:
Ilia Mashkov
2026-04-17 16:30:41 +03:00
parent bb5c3667b4
commit 5eb9584797
2 changed files with 13 additions and 7 deletions

View File

@@ -1,7 +1,6 @@
<!--
Component: TypographyMenu
Floating controls bar for typography settings.
Warm surface, sharp corners, Settings icon header, dividers between units.
Mobile: popover with slider controls anchored to settings button.
Desktop: inline bar with combo controls.
-->
@@ -37,14 +36,17 @@ interface Props {
* @default false
*/
hidden?: boolean;
/**
* Bindable popover open state
* @default false
*/
open?: boolean;
}
const { class: className, hidden = false }: Props = $props();
let { class: className, hidden = false, open = $bindable(false) }: Props = $props();
const responsive = getContext<ResponsiveManager>('responsive');
let isOpen = $state(false);
/**
* Sets the common font size multiplier based on the current responsive state.
*/
@@ -70,7 +72,7 @@ $effect(() => {
{#if !hidden}
{#if responsive.isMobileOrTablet}
<Popover.Root bind:open={isOpen}>
<Popover.Root bind:open>
<Popover.Trigger>
{#snippet child({ props })}
<Button class={className} variant="primary" {...props}>
@@ -84,7 +86,7 @@ $effect(() => {
<Popover.Portal>
<Popover.Content
side="top"
align="start"
align="end"
sideOffset={8}
class={clsx(
'z-50 w-72',

View File

@@ -52,6 +52,7 @@ const responsive = getContext<ResponsiveManager>('responsive');
const isMobile = $derived(responsive?.isMobile ?? false);
let isDragging = $state(false);
let isTypographyMenuOpen = $state(false);
// New high-performance layout engine
const comparisonEngine = new CharacterComparisonEngine();
@@ -76,6 +77,8 @@ function handleMove(e: PointerEvent) {
function startDragging(e: PointerEvent) {
e.preventDefault();
// Close typography menu popover
isTypographyMenuOpen = false;
isDragging = true;
handleMove(e);
}
@@ -222,7 +225,7 @@ const scaleClass = $derived(
class="
relative w-full max-w-6xl h-full
flex flex-col justify-center
select-none touch-none cursor-ew-resize
select-none touch-none outline-none cursor-ew-resize
py-8 px-4 sm:py-12 sm:px-8 md:py-16 md:px-12 lg:py-20 lg:px-24
"
in:fade={{ duration: 300, delay: 300 }}
@@ -253,6 +256,7 @@ const scaleClass = $derived(
</div>
<TypographyMenu
bind:open={isTypographyMenuOpen}
class={clsx(
'absolute z-50',
responsive.isMobileOrTablet