Compare commits

...

10 Commits

Author SHA1 Message Date
Ilia Mashkov 42bcc915c7 chore(lint): enable import/no-cycle
Guards against circular-dependency regressions in barrels. Requires the
import plugin, which is not enabled by default.
2026-05-31 19:16:44 +03:00
Ilia Mashkov c72b51b1c7 refactor(shared): keep BaseQueryStore out of the lib barrels
BaseQueryStore pulls @tanstack/query-core. Re-exporting it through the
broad $shared/lib and $shared/lib/helpers barrels made every consumer of
those barrels eager-load TanStack at module-eval time (no tree-shaking in
vitest/vite-node), which is what surfaced the queryClient mock init-order
failure. Its single consumer now imports it by path.
2026-05-31 19:16:44 +03:00
Ilia Mashkov 6888f67f14 test(Font): make queryClient mock factory self-contained
The vi.mock factory referenced the top-level QueryClient import. Once
BaseQueryStore landed in the $shared/lib barrel, importing that barrel
for createFilter eagerly pulled @tanstack/query-core into the init
cycle, so the hoisted factory hit the import before initialization
(Cannot access '__vi_import_2__' before initialization). Import
QueryClient inside the factory to keep it independent of module order.
2026-05-31 17:51:34 +03:00
Ilia Mashkov a651d3d16f refactor(font): absorb FetchFontsByIds store into Font entity
FetchFontsByIds was data-access for the Font aggregate wearing a feature
costume: a single FontsByIdsStore (BaseQueryStore over Font's proxy API),
no UI, consumed only by comparisonStore. Move it beside fontCatalogStore
in the Font entity, collapse its deep $entities/Font imports to relative
paths, expose via the Font public API, and delete the feature slice.
2026-05-31 17:49:59 +03:00
Ilia Mashkov 4d8dcf52e0 refactor: move BaseQueryStore into separate directory, adjust exports/imports 2026-05-31 17:33:06 +03:00
Ilia Mashkov 907145c655 refactor(shared): drop createTypographyControl from shared/lib 2026-05-31 17:08:55 +03:00
Ilia Mashkov e49148008b refactor(Font): remove typography-control config, keep render defaults 2026-05-31 17:08:48 +03:00
Ilia Mashkov c613d4cf88 refactor(AdjustTypography): consume typography-control module from its new home 2026-05-31 17:04:51 +03:00
Ilia Mashkov 7834c7cbf2 refactor(AdjustTypography): add typography-control module (factory, types, constants) 2026-05-31 16:52:37 +03:00
Ilia Mashkov 4640d6e521 test(shared): test ComboControl against NumericControl mock, not the factory 2026-05-31 16:50:55 +03:00
29 changed files with 286 additions and 332 deletions
+3 -1
View File
@@ -1,4 +1,5 @@
{
"plugins": ["import"],
"categories": {
"correctness": "error",
"suspicious": "warn",
@@ -22,6 +23,7 @@
"rules": {
"no-console": "off",
"no-debugger": "error",
"no-alert": "warn"
"no-alert": "warn",
"import/no-cycle": "error"
}
}
-57
View File
@@ -1,6 +1,3 @@
import type { ControlModel } from '$shared/lib';
import type { ControlId } from '../types/typography';
/**
* Font size constants
*/
@@ -33,60 +30,6 @@ export const MIN_LETTER_SPACING = -0.1;
export const MAX_LETTER_SPACING = 0.5;
export const LETTER_SPACING_STEP = 0.01;
export const DEFAULT_TYPOGRAPHY_CONTROLS_DATA: ControlModel<ControlId>[] = [
{
id: 'font_size',
value: DEFAULT_FONT_SIZE,
max: MAX_FONT_SIZE,
min: MIN_FONT_SIZE,
step: FONT_SIZE_STEP,
increaseLabel: 'Increase Font Size',
decreaseLabel: 'Decrease Font Size',
controlLabel: 'Size',
},
{
id: 'font_weight',
value: DEFAULT_FONT_WEIGHT,
max: MAX_FONT_WEIGHT,
min: MIN_FONT_WEIGHT,
step: FONT_WEIGHT_STEP,
increaseLabel: 'Increase Font Weight',
decreaseLabel: 'Decrease Font Weight',
controlLabel: 'Weight',
},
{
id: 'line_height',
value: DEFAULT_LINE_HEIGHT,
max: MAX_LINE_HEIGHT,
min: MIN_LINE_HEIGHT,
step: LINE_HEIGHT_STEP,
increaseLabel: 'Increase Line Height',
decreaseLabel: 'Decrease Line Height',
controlLabel: 'Leading',
},
{
id: 'letter_spacing',
value: DEFAULT_LETTER_SPACING,
max: MAX_LETTER_SPACING,
min: MIN_LETTER_SPACING,
step: LETTER_SPACING_STEP,
increaseLabel: 'Increase Letter Spacing',
decreaseLabel: 'Decrease Letter Spacing',
controlLabel: 'Tracking',
},
];
/**
* Font size multipliers
*/
export const MULTIPLIER_S = 0.5;
export const MULTIPLIER_M = 0.75;
export const MULTIPLIER_L = 1;
/**
* Index value for items not yet loaded in a virtualized list.
* Treated as being at the very bottom of the infinite scroll.
@@ -2,7 +2,6 @@ import {
generateMixedCategoryFonts,
generateMockFonts,
} from '$entities/Font/testing';
import { QueryClient } from '@tanstack/query-core';
import { flushSync } from 'svelte';
import {
afterEach,
@@ -20,6 +19,13 @@ import type { UnifiedFont } from '../../types';
import { FontCatalogStore } from './fontCatalogStore.svelte';
vi.mock('$shared/api/queryClient', async importOriginal => {
/**
* Import QueryClient inside the factory rather than referencing the top-level binding.
* A hoisted vi.mock factory that touches a module-level import can hit that import
* before it is initialized (ReferenceError) when the import sits in a circular/eager
* barrel chain — which it now does via $shared/lib → BaseQueryStore → query-core.
*/
const { QueryClient } = await import('@tanstack/query-core');
const actual = await importOriginal<typeof import('$shared/api/queryClient')>();
return {
...actual,
@@ -1,14 +1,14 @@
import { fontKeys } from '$shared/api/queryKeys';
import { BaseQueryStore } from '$shared/lib/helpers/BaseQueryStore/BaseQueryStore.svelte';
import {
fetchFontsByIds,
seedFontCache,
} from '$entities/Font/api/proxy/proxyFonts';
} from '../../../api/proxy/proxyFonts';
import {
FontNetworkError,
FontResponseError,
} from '$entities/Font/lib/errors/errors';
import type { UnifiedFont } from '$entities/Font/model/types';
import { fontKeys } from '$shared/api/queryKeys';
import { BaseQueryStore } from '$shared/lib/helpers/BaseQueryStore.svelte';
} from '../../../lib/errors/errors';
import type { UnifiedFont } from '../../types';
/**
* Internal fetcher that seeds the cache and handles error wrapping.
@@ -1,8 +1,3 @@
import * as api from '$entities/Font/api/proxy/proxyFonts';
import {
FontNetworkError,
FontResponseError,
} from '$entities/Font/lib/errors/errors';
import { queryClient } from '$shared/api/queryClient';
import { fontKeys } from '$shared/api/queryKeys';
import {
@@ -12,6 +7,11 @@ import {
it,
vi,
} from 'vitest';
import * as api from '../../../api/proxy/proxyFonts';
import {
FontNetworkError,
FontResponseError,
} from '../../../lib/errors/errors';
import { FontsByIdsStore } from './fontsByIdsStore.svelte';
describe('FontsByIdsStore', () => {
+3
View File
@@ -7,3 +7,6 @@ export {
FontCatalogStore,
fontCatalogStore,
} from './fontCatalogStore/fontCatalogStore.svelte';
// Batch fetch by IDs (detail-cache seeding)
export { FontsByIdsStore } from './fontsByIdsStore/fontsByIdsStore.svelte';
-1
View File
@@ -24,4 +24,3 @@ export type {
} from './store';
export * from './store/fontLifecycle';
export * from './typography';
@@ -1 +0,0 @@
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
+3
View File
@@ -1,5 +1,8 @@
export {
createTypographySettingsStore,
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
type TypographySettingsStore,
typographySettingsStore,
} from './model';
@@ -0,0 +1,76 @@
import {
DEFAULT_FONT_SIZE,
DEFAULT_FONT_WEIGHT,
DEFAULT_LETTER_SPACING,
DEFAULT_LINE_HEIGHT,
FONT_SIZE_STEP,
FONT_WEIGHT_STEP,
LETTER_SPACING_STEP,
LINE_HEIGHT_STEP,
MAX_FONT_SIZE,
MAX_FONT_WEIGHT,
MAX_LETTER_SPACING,
MAX_LINE_HEIGHT,
MIN_FONT_SIZE,
MIN_FONT_WEIGHT,
MIN_LETTER_SPACING,
MIN_LINE_HEIGHT,
} from '$entities/Font';
import type {
ControlId,
ControlModel,
} from '../types/typography';
/**
* Responsive font-size scaling factors applied by typographySettingsStore.
*/
export const MULTIPLIER_S = 0.5;
export const MULTIPLIER_M = 0.75;
export const MULTIPLIER_L = 1;
/**
* Default control definitions seeding the typography settings store.
* Composed from the font-render ranges/defaults owned by the Font entity.
*/
export const DEFAULT_TYPOGRAPHY_CONTROLS_DATA: ControlModel<ControlId>[] = [
{
id: 'font_size',
value: DEFAULT_FONT_SIZE,
max: MAX_FONT_SIZE,
min: MIN_FONT_SIZE,
step: FONT_SIZE_STEP,
increaseLabel: 'Increase Font Size',
decreaseLabel: 'Decrease Font Size',
controlLabel: 'Size',
},
{
id: 'font_weight',
value: DEFAULT_FONT_WEIGHT,
max: MAX_FONT_WEIGHT,
min: MIN_FONT_WEIGHT,
step: FONT_WEIGHT_STEP,
increaseLabel: 'Increase Font Weight',
decreaseLabel: 'Decrease Font Weight',
controlLabel: 'Weight',
},
{
id: 'line_height',
value: DEFAULT_LINE_HEIGHT,
max: MAX_LINE_HEIGHT,
min: MIN_LINE_HEIGHT,
step: LINE_HEIGHT_STEP,
increaseLabel: 'Increase Line Height',
decreaseLabel: 'Decrease Line Height',
controlLabel: 'Leading',
},
{
id: 'letter_spacing',
value: DEFAULT_LETTER_SPACING,
max: MAX_LETTER_SPACING,
min: MIN_LETTER_SPACING,
step: LETTER_SPACING_STEP,
increaseLabel: 'Increase Letter Spacing',
decreaseLabel: 'Decrease Letter Spacing',
controlLabel: 'Tracking',
},
];
@@ -1,3 +1,8 @@
export {
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
} from './const/const';
export {
createTypographySettingsStore,
type TypographySettingsStore,
@@ -11,22 +11,23 @@
*/
import {
type ControlId,
DEFAULT_FONT_SIZE,
DEFAULT_FONT_WEIGHT,
DEFAULT_LETTER_SPACING,
DEFAULT_LINE_HEIGHT,
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
} from '$entities/Font';
import {
type ControlDataModel,
type ControlModel,
type PersistentStore,
type TypographyControl,
createPersistentStore,
createTypographyControl,
} from '$shared/lib';
import type { NumericControl } from '$shared/ui';
import { SvelteMap } from 'svelte/reactivity';
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '../../const/const';
import type {
ControlId,
ControlModel,
} from '../../types/typography';
import { createTypographyControl } from '../../typographyControl/createTypographyControl.svelte';
/**
* Epsilon for detecting "significant" base-size changes when reconciling
@@ -36,7 +37,7 @@ import { SvelteMap } from 'svelte/reactivity';
*/
const BASE_SIZE_EPSILON = 0.01;
type ControlOnlyFields<T extends string = string> = Omit<ControlModel<T>, keyof ControlDataModel>;
type ControlOnlyFields<T extends string = string> = Omit<ControlModel<T>, 'value' | 'min' | 'max' | 'step'>;
/**
* A control with its associated instance
@@ -45,7 +46,7 @@ export interface Control extends ControlOnlyFields<ControlId> {
/**
* The reactive typography control instance
*/
instance: TypographyControl;
instance: NumericControl;
}
/**
@@ -6,7 +6,6 @@ import {
DEFAULT_FONT_WEIGHT,
DEFAULT_LETTER_SPACING,
DEFAULT_LINE_HEIGHT,
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
} from '$entities/Font';
import {
beforeEach,
@@ -15,6 +14,7 @@ import {
it,
vi,
} from 'vitest';
import { DEFAULT_TYPOGRAPHY_CONTROLS_DATA } from '../../const/const';
import {
type TypographySettings,
TypographySettingsStore,
@@ -0,0 +1,27 @@
import type {
ControlLabels,
NumericControl,
} from '$shared/ui';
/**
* Identifiers for the adjustable typography axes
*/
export type ControlId = 'font_size' | 'font_weight' | 'line_height' | 'letter_spacing';
/**
* Static configuration for one typography control.
*
* Derived from the SSOT contract types — declares no fields of its own beyond
* the domain `id`. Bounds come from NumericControl, labels from ControlLabels.
*
* @template T - Control identifier type
*/
export type ControlModel<T extends string = string> =
& Pick<NumericControl, 'value' | 'min' | 'max' | 'step'>
& ControlLabels
& {
/**
* Unique identifier for the control
*/
id: T;
};
@@ -0,0 +1,67 @@
/**
* Bounded numeric control for typography settings.
*
* Produces a reactive control that clamps to [min, max] and rounds to step.
* Implements the NumericControl contract that ComboControl renders.
*/
import {
clampNumber,
roundToStepPrecision,
} from '$shared/lib/utils';
import type { NumericControl } from '$shared/ui';
/**
* Bounds + initial value seed for a control
*/
type ControlSeed = Pick<NumericControl, 'value' | 'min' | 'max' | 'step'>;
/**
* Create a reactive bounded numeric control.
*
* @param initialState - Initial value and bounds
* @returns A NumericControl whose value is always clamped and step-rounded
*/
export function createTypographyControl(initialState: ControlSeed): NumericControl {
let value = $state(initialState.value);
let max = $state(initialState.max);
let min = $state(initialState.min);
let step = $state(initialState.step);
const { isAtMax, isAtMin } = $derived({
isAtMax: value >= max,
isAtMin: value <= min,
});
return {
get value() {
return value;
},
set value(newValue) {
const rounded = roundToStepPrecision(clampNumber(newValue, min, max), step);
if (value !== rounded) {
value = rounded;
}
},
get max() {
return max;
},
get min() {
return min;
},
get step() {
return step;
},
get isAtMax() {
return isAtMax;
},
get isAtMin() {
return isAtMin;
},
increase() {
value = roundToStepPrecision(clampNumber(value + step, min, max), step);
},
decrease() {
value = roundToStepPrecision(clampNumber(value - step, min, max), step);
},
};
}
@@ -1,12 +1,10 @@
import {
type TypographyControl,
createTypographyControl,
} from '$shared/lib';
import type { NumericControl } from '$shared/ui';
import {
describe,
expect,
it,
} from 'vitest';
import { createTypographyControl } from './createTypographyControl.svelte';
/**
* Test Strategy for createTypographyControl Helper
@@ -34,7 +32,7 @@ describe('createTypographyControl - Unit Tests', () => {
min?: number;
max?: number;
step?: number;
}): TypographyControl {
}): NumericControl {
return createTypographyControl({
value: initialValue,
min: options?.min ?? 0,
@@ -5,11 +5,6 @@
Desktop: inline bar with combo controls.
-->
<script lang="ts">
import {
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
} from '$entities/Font';
import type { ResponsiveManager } from '$shared/lib';
import { cn } from '$shared/lib';
import {
@@ -24,7 +19,12 @@ import { Popover } from 'bits-ui';
import { getContext } from 'svelte';
import { cubicOut } from 'svelte/easing';
import { fly } from 'svelte/transition';
import { typographySettingsStore } from '../../model';
import {
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
typographySettingsStore,
} from '../../model';
interface Props {
/**
-1
View File
@@ -1 +0,0 @@
export { FontsByIdsStore } from './model';
@@ -1 +0,0 @@
export { FontsByIdsStore } from './store/fontsByIdsStore/fontsByIdsStore.svelte';
@@ -1,200 +0,0 @@
/**
* Numeric control with bounded values and step precision
*
* Creates a reactive control for numeric values that enforces min/max bounds
* and rounds to a specific step increment. Commonly used for typography controls
* like font size, line height, and letter spacing.
*
* @example
* ```ts
* const fontSize = createTypographyControl({
* value: 16,
* min: 12,
* max: 72,
* step: 1
* });
*
* // Access current value
* fontSize.value; // 16
* fontSize.isAtMin; // false
*
* // Modify value (automatically clamped and rounded)
* fontSize.increase();
* fontSize.value = 100; // Will be clamped to max (72)
* ```
*/
import {
clampNumber,
roundToStepPrecision,
} from '$shared/lib/utils';
/**
* Core numeric control configuration
* Defines the bounds and stepping behavior for a control
*/
export interface ControlDataModel {
/**
* Initial or current numeric value
*/
value: number;
/**
* Lower inclusive bound
*/
min: number;
/**
* Upper inclusive bound
*/
max: number;
/**
* Precision for increment/decrement operations
*/
step: number;
}
/**
* Full control model including accessibility labels
*
* @template T - Type for the control identifier
*/
export interface ControlModel<T extends string = string> extends ControlDataModel {
/**
* Unique string identifier for the control
*/
id: T;
/**
* Label used by screen readers for the increase button
*/
increaseLabel?: string;
/**
* Label used by screen readers for the decrease button
*/
decreaseLabel?: string;
/**
* Overall label describing the control's purpose
*/
controlLabel?: string;
}
/**
* Creates a reactive numeric control with bounds and stepping
*
* The control automatically:
* - Clamps values to the min/max range
* - Rounds values to the step precision
* - Tracks whether at min/max bounds
*
* @param initialState - Initial value, bounds, and step configuration
* @returns Typography control instance with reactive state and methods
*
* @example
* ```ts
* // Font size control: 12-72px in 1px increments
* const fontSize = createTypographyControl({
* value: 16,
* min: 12,
* max: 72,
* step: 1
* });
*
* // Line height control: 1.0-2.0 in 0.1 increments
* const lineHeight = createTypographyControl({
* value: 1.5,
* min: 1.0,
* max: 2.0,
* step: 0.1
* });
*
* // Direct assignment (auto-clamped)
* fontSize.value = 100; // Becomes 72 (max)
* ```
*/
export function createTypographyControl<T extends ControlDataModel>(
initialState: T,
) {
let value = $state(initialState.value);
let max = $state(initialState.max);
let min = $state(initialState.min);
let step = $state(initialState.step);
// Derived state for boundary detection
const { isAtMax, isAtMin } = $derived({
isAtMax: value >= max,
isAtMin: value <= min,
});
return {
/**
* Clamped and rounded control value (reactive)
*/
get value() {
return value;
},
set value(newValue) {
const rounded = roundToStepPrecision(clampNumber(newValue, min, max), step);
if (value !== rounded) {
value = rounded;
}
},
/**
* Upper limit for the control value
*/
get max() {
return max;
},
/**
* Lower limit for the control value
*/
get min() {
return min;
},
/**
* Configured step increment
*/
get step() {
return step;
},
/**
* True if current value is equal to or greater than max
*/
get isAtMax() {
return isAtMax;
},
/**
* True if current value is equal to or less than min
*/
get isAtMin() {
return isAtMin;
},
/**
* Increase value by one step (clamped to max)
*/
increase() {
value = roundToStepPrecision(
clampNumber(value + step, min, max),
step,
);
},
/**
* Decrease value by one step (clamped to min)
*/
decrease() {
value = roundToStepPrecision(
clampNumber(value - step, min, max),
step,
);
},
};
}
/**
* Type representing a typography control instance
*/
export type TypographyControl = ReturnType<typeof createTypographyControl>;
+7 -25
View File
@@ -3,7 +3,6 @@
*
* Provides composable state management patterns for common UI needs:
* - Filter management with multi-selection
* - Typography controls with bounds and stepping
* - Virtual scrolling for large lists
* - Debounced state for search inputs
* - Entity stores with O(1) lookups
@@ -13,11 +12,10 @@
*
* @example
* ```ts
* import { createFilter, createVirtualizer, createTypographyControl } from '$shared/lib/helpers';
* import { createFilter, createVirtualizer } from '$shared/lib/helpers';
*
* const filter = createFilter({ properties: [...] });
* const virtualizer = createVirtualizer(() => ({ count: 1000, estimateSize: () => 50 }));
* const control = createTypographyControl({ value: 16, min: 12, max: 72, step: 1 });
* ```
*/
@@ -43,28 +41,6 @@ export {
type Property,
} from './createFilter/createFilter.svelte';
/**
* Bounded numeric controls
*/
export {
/**
* Base numeric configuration
*/
type ControlDataModel,
/**
* Extended model with labels
*/
type ControlModel,
/**
* Reactive control factory
*/
createTypographyControl,
/**
* Control instance type
*/
type TypographyControl,
} from './createTypographyControl/createTypographyControl.svelte';
/**
* List virtualization
*/
@@ -160,3 +136,9 @@ export {
*/
type PerspectiveManager,
} from './createPerspectiveManager/createPerspectiveManager.svelte';
/*
* BaseQueryStore is intentionally NOT re-exported here.
* It pulls @tanstack/query-core, so routing it through this leaf barrel would
* make every consumer of the barrel eager-load TanStack. Import it by path.
*/
-4
View File
@@ -5,15 +5,12 @@
*/
export {
type ControlDataModel,
type ControlModel,
createDebouncedState,
createEntityStore,
createFilter,
createPersistentStore,
createPerspectiveManager,
createResponsiveManager,
createTypographyControl,
createVirtualizer,
type Entity,
type EntityStore,
@@ -24,7 +21,6 @@ export {
type Property,
type ResponsiveManager,
responsiveManager,
type TypographyControl,
type VirtualItem,
type Virtualizer,
type VirtualizerOptions,
@@ -1,7 +1,7 @@
<script module>
import { createTypographyControl } from '$shared/lib';
import { defineMeta } from '@storybook/addon-svelte-csf';
import ComboControl from './ComboControl.svelte';
import { createNumericControlMock } from './testing/createNumericControlMock.svelte';
const { Story } = defineMeta({
title: 'Shared/ComboControl',
@@ -23,7 +23,7 @@ const { Story } = defineMeta({
},
control: {
control: 'object',
description: 'TypographyControl instance managing the value and bounds',
description: 'NumericControl instance managing the value and bounds',
},
},
});
@@ -31,7 +31,7 @@ const { Story } = defineMeta({
<script lang="ts">
import type { ComponentProps } from 'svelte';
const horizontalControl = createTypographyControl({ min: 0, max: 100, step: 1, value: 50 });
const horizontalControl = createNumericControlMock({ min: 0, max: 100, step: 1, value: 50 });
</script>
<Story
@@ -1,4 +1,3 @@
import { createTypographyControl } from '$shared/lib';
import {
fireEvent,
render,
@@ -6,9 +5,10 @@ import {
waitFor,
} from '@testing-library/svelte';
import ComboControl from './ComboControl.svelte';
import { createNumericControlMock } from './testing/createNumericControlMock.svelte';
function makeControl(value: number, opts: { min?: number; max?: number; step?: number } = {}) {
return createTypographyControl({
return createNumericControlMock({
value,
min: opts.min ?? 0,
max: opts.max ?? 100,
@@ -0,0 +1,47 @@
/**
* Test/story mock implementing the NumericControl contract.
*
* Lives in shared/ui because ComboControl must be testable without importing
* the real typography factory (which sits in a feature, above shared).
*/
import type { NumericControl } from '../types';
/**
* Build a reactive NumericControl mock with clamping and stepping.
*/
export function createNumericControlMock(
init: Pick<NumericControl, 'value' | 'min' | 'max' | 'step'>,
): NumericControl {
let value = $state(init.value);
const clamp = (v: number) => Math.min(Math.max(v, init.min), init.max);
return {
get value() {
return value;
},
set value(v) {
value = clamp(v);
},
get min() {
return init.min;
},
get max() {
return init.max;
},
get step() {
return init.step;
},
get isAtMin() {
return value <= init.min;
},
get isAtMax() {
return value >= init.max;
},
increase() {
value = clamp(value + init.step);
},
decrease() {
value = clamp(value - init.step);
},
};
}
@@ -15,13 +15,13 @@
import {
type FontLoadRequestConfig,
FontsByIdsStore,
type UnifiedFont,
fontCatalogStore,
fontLifecycleManager,
getFontUrl,
} from '$entities/Font';
import { typographySettingsStore } from '$features/AdjustTypography/model';
import { FontsByIdsStore } from '$features/FetchFontsByIds';
import { createPersistentStore } from '$shared/lib';
import { untrack } from 'svelte';
import { getPretextFontString } from '../../lib';
@@ -11,13 +11,15 @@
import {
type ComparisonResult,
DualFontLayout,
findSplitIndex,
} from '$entities/Font';
import {
MULTIPLIER_L,
MULTIPLIER_M,
MULTIPLIER_S,
findSplitIndex,
} from '$entities/Font';
import { TypographyMenu } from '$features/AdjustTypography';
import { typographySettingsStore } from '$features/AdjustTypography/model';
TypographyMenu,
typographySettingsStore,
} from '$features/AdjustTypography';
import {
type ResponsiveManager,
debounce,