feat(TypographyMenu): merge SetupFontMenu and TypographyMenu into one component, add drawer logic for mobile resolution

This commit is contained in:
Ilia Mashkov
2026-02-07 19:15:04 +03:00
parent cb0e89b257
commit 4d7ae6c1c6
5 changed files with 98 additions and 65 deletions

View File

@@ -1,7 +1,4 @@
export {
SetupFontMenu,
TypographyMenu,
} from './ui';
export { TypographyMenu } from './ui';
export {
type ControlId,
@@ -20,6 +17,9 @@ export {
MIN_FONT_SIZE,
MIN_FONT_WEIGHT,
MIN_LINE_HEIGHT,
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
} from './model';
export {

View File

@@ -1,46 +0,0 @@
<!--
Component: SetupFontMenu
Contains controls for setting up font properties.
-->
<script lang="ts">
import type { ResponsiveManager } from '$shared/lib';
import { ComboControl } from '$shared/ui';
import { getContext } from 'svelte';
import { controlManager } from '../model';
const responsive = getContext<ResponsiveManager>('responsive');
$effect(() => {
if (!responsive) {
return;
}
switch (true) {
case responsive.isMobile:
controlManager.multiplier = 0.5;
break;
case responsive.isTablet:
controlManager.multiplier = 0.75;
break;
case responsive.isDesktop:
controlManager.multiplier = 1;
break;
default:
controlManager.multiplier = 1;
break;
}
});
</script>
<div class="sm:py-2 sm:px-10 flex flex-row items-center gap-2">
<div class="flex flex-row gap-3">
{#each controlManager.controls as control (control.id)}
<ComboControl
control={control.instance}
increaseLabel={control.increaseLabel}
decreaseLabel={control.decreaseLabel}
controlLabel={control.controlLabel}
reduced={responsive.isMobile}
/>
{/each}
</div>
</div>

View File

@@ -1,41 +1,121 @@
<!--
Component: TypographyMenu
Provides a menu for selecting and configuring typography settings
- On mobile the menu is displayed as a drawer
-->
<script lang="ts">
import { SetupFontMenu } from '$features/SetupFont';
import type { ResponsiveManager } from '$shared/lib';
import {
Content as ItemContent,
Root as ItemRoot,
} from '$shared/shadcn/ui/item';
import { cn } from '$shared/shadcn/utils/shadcn-utils';
import {
ComboControl,
ComboControlV2,
Drawer,
IconButton,
} from '$shared/ui';
import SlidersIcon from '@lucide/svelte/icons/sliders-vertical';
import { getContext } from 'svelte';
import { cubicOut } from 'svelte/easing';
import { crossfade } from 'svelte/transition';
import {
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
controlManager,
} from '../model';
interface Props {
class?: string;
}
const { class: className }: Props = $props();
const responsive = getContext<ResponsiveManager>('responsive');
const [send, receive] = crossfade({
duration: 400,
duration: 300,
easing: cubicOut,
fallback(node, params) {
// If it can't find a pair, it falls back to a simple fade/slide
return {
duration: 400,
duration: 300,
css: t => `opacity: ${t}; transform: translateY(${(1 - t) * 10}px);`,
};
},
});
/**
* Sets the common font size multiplier based on the current responsive state.
*/
$effect(() => {
if (!responsive) {
return;
}
switch (true) {
case responsive.isMobile:
controlManager.multiplier = MULTIPLIER_S;
break;
case responsive.isTablet:
controlManager.multiplier = MULTIPLIER_M;
break;
case responsive.isDesktop:
controlManager.multiplier = MULTIPLIER_L;
break;
default:
controlManager.multiplier = MULTIPLIER_L;
break;
}
});
</script>
<div
class="w-auto fixed bottom-4 sm:bottom-5 left-4 right-4 sm:left-1/2 sm:right-[unset] sm:-translate-x-1/2 sm:inset-x-0 max-screen z-10 flex justify-center"
class={cn('w-auto max-screen z-10 flex justify-center', className)}
in:receive={{ key: 'panel' }}
out:send={{ key: 'panel' }}
>
<ItemRoot
variant="outline"
class="w-full sm:w-auto max-w-full sm:max-w-max p-2 sm:p-2.5 rounded-xl sm:rounded-2xl backdrop-blur-lg"
>
<ItemContent class="flex flex-row justify-center items-center max-w-full sm:max-w-max">
<SetupFontMenu />
</ItemContent>
</ItemRoot>
{#if responsive.isMobile}
<Drawer>
{#snippet trigger({ onClick })}
<IconButton onclick={onClick}>
{#snippet icon({ className })}
<SlidersIcon class={className} />
{/snippet}
</IconButton>
{/snippet}
{#snippet content()}
<div class="flex flex-col gap-6 px-2 py-4">
{#each controlManager.controls as control (control.id)}
<ComboControlV2
control={control.instance}
orientation="horizontal"
/>
{/each}
</div>
{/snippet}
</Drawer>
{:else}
<ItemRoot
variant="outline"
class="w-full sm:w-auto max-w-full sm:max-w-max p-2 sm:p-2.5 rounded-xl sm:rounded-2xl backdrop-blur-lg"
>
<ItemContent class="flex flex-row justify-center items-center max-w-full sm:max-w-max">
<div class="sm:py-2 sm:px-10 flex flex-row items-center gap-2">
<div class="flex flex-row gap-3">
{#each controlManager.controls as control (control.id)}
<ComboControl
control={control.instance}
increaseLabel={control.increaseLabel}
decreaseLabel={control.decreaseLabel}
controlLabel={control.controlLabel}
reduced={responsive.isMobile}
/>
{/each}
</div>
</div>
</ItemContent>
</ItemRoot>
{/if}
</div>

View File

@@ -1,2 +1 @@
export { default as SetupFontMenu } from './SetupFontMenu.svelte';
export { default as TypographyMenu } from './TypographyMenu.svelte';

View File

@@ -102,6 +102,6 @@ function checkPosition() {
</FontVirtualList>
{#if isAboveMiddle}
<TypographyMenu />
<TypographyMenu class="fixed bottom-4 sm:bottom-5 right-4 sm:left-1/2 sm:right-[unset] sm:-translate-x-1/2" />
{/if}
</div>