"use client";
import * as React from "react";
import {
ChannelMixer,
Color,
ColorOpacity,
Curves,
FontPicker,
Gradient,
ImagePicker,
Palette,
type ChannelMixerValues,
type ColorControlInput,
type ColorControlInputPair,
type ControlChangeMeta,
type ImagePickerItem,
} from "@repo/ui";
import type { ToolcraftControlSchema } from "../../../schema/types";
import {
asColorOpacityValue,
asColorValue,
asCurveInterpolation,
asFontPickerValue,
asGradientValue,
asString,
defaultChannelMixerValues,
isRecord,
} from "../values/controls-panel-values";
export type CompoundControlCommit = (
nextValue: unknown,
meta?: ControlChangeMeta,
) => void;
export type CompoundControlCommitWithLabel = (
label: string,
) => CompoundControlCommit;
export type CompoundControlKeyframeWrap = (args: {
children: React.ReactNode;
control: ToolcraftControlSchema;
disableAction: boolean;
labelActionName?: string;
name: string;
providerKey: string;
value: unknown;
}) => React.ReactNode;
export type CompoundShouldShowColorFieldLabel = (args: {
control: ToolcraftControlSchema;
sectionHasOnlyColorFields: boolean;
}) => boolean;
export type CompoundControlRenderArgs = {
commit: CompoundControlCommit;
commitWithLabel: CompoundControlCommitWithLabel;
control: ToolcraftControlSchema;
id: string;
name: string;
sectionHasOnlyColorFields: boolean;
shouldShowColorFieldLabel: CompoundShouldShowColorFieldLabel;
usesHeaderKeyframeAction: boolean;
value: unknown;
withKeyframeLabelAction: CompoundControlKeyframeWrap;
};
export type CompoundColorGroupRenderArgs = {
entries: readonly [id: string, control: ToolcraftControlSchema][];
getControlName: (
id: string,
label: ToolcraftControlSchema["label"],
) => string;
getControlValue: (control: ToolcraftControlSchema) => unknown;
headerKeyframeTarget: string | null;
maybeUpsertControlKeyframe: (
control: ToolcraftControlSchema,
label: string,
value: unknown,
) => void;
sectionHasOnlyColorFields: boolean;
setControlValue: (
target: string,
value: unknown,
label?: string,
meta?: ControlChangeMeta,
) => void;
shouldShowColorFieldLabel: CompoundShouldShowColorFieldLabel;
withKeyframeLabelAction: CompoundControlKeyframeWrap;
};
export function renderCompoundColorGroup({
entries,
getControlName,
getControlValue,
headerKeyframeTarget,
maybeUpsertControlKeyframe,
sectionHasOnlyColorFields,
setControlValue,
shouldShowColorFieldLabel,
withKeyframeLabelAction,
}: CompoundColorGroupRenderArgs): React.JSX.Element | null {
const colorInputs = entries.map(([id, control]) => {
const name = getControlName(id, control.label);
const value = getControlValue(control);
const colorValue = asColorValue(value);
return {
hex: colorValue.hex,
name,
onValueChange: (nextValue, meta) => {
setControlValue(control.target, nextValue, name, meta);
maybeUpsertControlKeyframe(control, name, nextValue);
},
showLabel: shouldShowColorFieldLabel({
control,
sectionHasOnlyColorFields,
}),
} satisfies ColorControlInput;
});
const [firstInput, secondInput] = colorInputs;
if (!firstInput) {
return null;
}
if (!secondInput) {
const firstEntry = entries[0];
const firstControl = firstEntry?.[1];
const firstValue = firstControl ? getControlValue(firstControl) : undefined;
return firstControl ? (
withKeyframeLabelAction({
children: (
),
control: firstControl,
disableAction: firstControl.target === headerKeyframeTarget,
name: firstInput.name,
providerKey: firstInput.name,
value: firstValue,
}) as React.JSX.Element
) : (
);
}
return (
);
}
export function renderCompoundControl({
commit,
commitWithLabel,
control,
id,
name,
sectionHasOnlyColorFields,
shouldShowColorFieldLabel,
usesHeaderKeyframeAction,
value,
withKeyframeLabelAction,
}: CompoundControlRenderArgs): React.ReactNode | null {
switch (control.type) {
case "channelMixer": {
const channelMixerName = name;
return withKeyframeLabelAction({
children: (
commitWithLabel(channelMixerName)(nextValue.values)
}
values={
isRecord(value) ? (value as ChannelMixerValues) : defaultChannelMixerValues
}
/>
),
control,
disableAction: false,
labelActionName: channelMixerName,
name,
providerKey: id,
value,
});
}
case "color": {
const colorValue = asColorValue(value);
return withKeyframeLabelAction({
children: (
),
control,
disableAction: usesHeaderKeyframeAction,
name,
providerKey: id,
value,
});
}
case "colorOpacity": {
const colorOpacityValue = asColorOpacityValue(value);
return withKeyframeLabelAction({
children: (
),
control,
disableAction: usesHeaderKeyframeAction,
name,
providerKey: id,
value: colorOpacityValue,
});
}
case "curves": {
const curvesName = control.label === false ? "Curves" : name;
return withKeyframeLabelAction({
children: (
),
control,
disableAction: false,
labelActionName: curvesName,
name: curvesName,
providerKey: id,
value,
});
}
case "gradient": {
const gradientValue = asGradientValue(value);
return withKeyframeLabelAction({
children: (
),
control,
disableAction: usesHeaderKeyframeAction,
name,
providerKey: id,
value,
});
}
case "fontPicker": {
const fontPickerValue = asFontPickerValue(value);
return withKeyframeLabelAction({
children: (
),
control,
disableAction: usesHeaderKeyframeAction,
name,
providerKey: id,
value: fontPickerValue,
});
}
case "imagePicker":
return (
);
case "palette":
return withKeyframeLabelAction({
children: (
["defaultValue"])
: undefined
}
key={id}
onValueChange={commit}
value={
isRecord(value)
? (value as React.ComponentProps["value"])
: undefined
}
/>
),
control,
disableAction: usesHeaderKeyframeAction,
name,
providerKey: id,
value,
});
default:
return null;
}
}