201 lines
4.6 KiB
TypeScript
201 lines
4.6 KiB
TypeScript
/**
|
|
* 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>;
|