1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
import { snapToStep } from "@/utils/math";
export type ParamValue = number | string | boolean;
export type ParamValues = Record<string, ParamValue>;
export type ParamGroup = "stroke";
interface BaseParamDefinition<TKey extends string = string> {
key: TKey;
label: string;
group?: ParamGroup;
keyframable?: boolean;
dependencies?: Array<{ param: string; equals: ParamValue }>;
}
export interface NumberParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "number";
default: number;
min: number;
max?: number;
step: number;
/** When set, min/max/step are in display space. display = stored * displayMultiplier. */
displayMultiplier?: number;
/** Show as percentage of max. min/max/step/default stay in stored space. */
unit?: "percent";
/** Short label shown as the scrub handle icon in the number field (e.g. "W", "R"). */
shortLabel?: string;
}
export interface BooleanParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "boolean";
default: boolean;
}
export interface ColorParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "color";
default: string;
}
export interface SelectParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "select";
default: string;
options: Array<{ value: string; label: string }>;
}
export interface TextParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "text";
default: string;
}
export interface FontParamDefinition<TKey extends string = string>
extends BaseParamDefinition<TKey> {
type: "font";
default: string;
}
export type ParamDefinition<TKey extends string = string> =
| NumberParamDefinition<TKey>
| BooleanParamDefinition<TKey>
| ColorParamDefinition<TKey>
| SelectParamDefinition<TKey>
| TextParamDefinition<TKey>
| FontParamDefinition<TKey>;
export function getParamValueKind({
param,
}: {
param: ParamDefinition;
}): "number" | "color" | "discrete" {
if (param.type === "number") {
return "number";
}
if (param.type === "color") {
return "color";
}
return "discrete";
}
export function getParamDefaultInterpolation({
param,
}: {
param: ParamDefinition;
}): "linear" | "hold" {
return param.type === "number" || param.type === "color" ? "linear" : "hold";
}
export function getParamNumericRange({
param,
}: {
param: ParamDefinition;
}): { min?: number; max?: number; step?: number } | undefined {
if (param.type !== "number") {
return undefined;
}
return {
min: param.min,
max: param.max,
step: param.step,
};
}
export function coerceParamValue({
param,
value,
}: {
param: ParamDefinition;
value: unknown;
}): ParamValue | null {
if (param.type === "number") {
if (typeof value !== "number" || Number.isNaN(value)) {
return null;
}
const steppedValue = snapToStep({ value, step: param.step });
const maxValue = param.max ?? Number.POSITIVE_INFINITY;
return Math.min(maxValue, Math.max(param.min, steppedValue));
}
if (param.type === "boolean") {
return typeof value === "boolean" ? value : null;
}
if (param.type === "color" || param.type === "text" || param.type === "font") {
return typeof value === "string" ? value : null;
}
if (typeof value !== "string") {
return null;
}
return param.options.some((option) => option.value === value) ? value : null;
}
|