123 lines
3.8 KiB
Svelte
123 lines
3.8 KiB
Svelte
<!--
|
|
Component: TypographyMenu
|
|
Provides a menu for selecting and configuring typography settings
|
|
- On mobile the menu is displayed as a drawer
|
|
-->
|
|
<script lang="ts">
|
|
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 {
|
|
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;
|
|
hidden?: boolean;
|
|
}
|
|
|
|
const { class: className, hidden = false }: Props = $props();
|
|
const responsive = getContext<ResponsiveManager>('responsive');
|
|
|
|
const [send, receive] = crossfade({
|
|
duration: 300,
|
|
easing: cubicOut,
|
|
fallback(node, params) {
|
|
// If it can't find a pair, it falls back to a simple fade/slide
|
|
return {
|
|
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={cn('w-auto max-screen z-10 flex justify-center', hidden && 'hidden', className)}
|
|
in:receive={{ key: 'panel' }}
|
|
out:send={{ key: 'panel' }}
|
|
>
|
|
{#if responsive.isMobile}
|
|
<Drawer>
|
|
{#snippet trigger({ onClick })}
|
|
<IconButton onclick={onClick}>
|
|
{#snippet icon({ className })}
|
|
<SlidersIcon class={className} />
|
|
{/snippet}
|
|
</IconButton>
|
|
{/snippet}
|
|
{#snippet content({ className })}
|
|
<div class={cn(className, 'flex flex-col gap-6')}>
|
|
{#each controlManager.controls as control (control.id)}
|
|
<ComboControlV2
|
|
control={control.instance}
|
|
orientation="horizontal"
|
|
reduced
|
|
/>
|
|
{/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)}
|
|
<ComboControlV2
|
|
control={control.instance}
|
|
increaseLabel={control.increaseLabel}
|
|
decreaseLabel={control.decreaseLabel}
|
|
controlLabel={control.controlLabel}
|
|
orientation="vertical"
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
</ItemContent>
|
|
</ItemRoot>
|
|
{/if}
|
|
</div>
|