"use client"; import { HsvaColor, hsvaToHsvaString, hsvaToRgba, rgbaToHsva, } from "@noya-app/noya-color"; import { Sketch } from "@noya-app/noya-file-format"; import React, { memo, useMemo } from "react"; import { ColorModel } from "../types"; import { equalColorObjects } from "../utils/compare"; import { getGradientBackground } from "../utils/getGradientBackground"; import { interpolateRgba } from "../utils/interpolateRgba"; import ColorPicker from "./ColorPicker"; import { Interaction, Interactive } from "./Interactive"; import Pointer from "./Pointer"; interface ContainerProps { background: string; children: React.ReactNode; } const Container: React.FC = ({ background, children }) => (
{children}
); export type GradientStop = { color: HsvaColor; position: number; // percentage between 0 and 1 }; const hsvaColorModel: ColorModel = { defaultColor: { h: 0, s: 0, v: 0, a: 1 }, equal: equalColorObjects, toHsva: (color) => color, fromHsva: (color) => color, }; export default memo(function Gradient({ stops, selectedStopIndex: selectedStopIndexProp, onSelectStop, onChange, children, style, className, }: { stops: GradientStop[]; selectedStopIndex: number; onSelectStop: (index: number) => void; onChange: (stops: GradientStop[]) => void; children: React.ReactNode; style?: React.CSSProperties; className?: string; }) { const selectedStopIndex = useMemo(() => { return Math.max(0, Math.min(selectedStopIndexProp, stops.length - 1)); }, [selectedStopIndexProp, stops]); const handleMove = (interaction: Interaction) => { onChange( stops.map((stop, index) => ({ ...stop, position: index === selectedStopIndex ? interaction.left : stop.position, })) ); }; const handleKey = (offset: Interaction) => { if (!offset) { onChange(stops.filter((_, index) => index !== selectedStopIndex)); } // Hue measured in degrees of the color circle ranging from 0 to 360 //console.log('press'); }; const handleClick = (interaction: Interaction) => { const color = interpolateRgba( stops.map((stop) => ({ color: hsvaToRgba(stop.color), position: stop.position, })), interaction.left ); const newStop: GradientStop = { color: rgbaToHsva(color), position: interaction.left, }; onChange([...stops, newStop].sort((a, b) => a.position - b.position)); }; const handleDelete = () => { const nextStops = stops.filter((_, index) => index !== selectedStopIndex); if (nextStops.length < 2) return; onChange(nextStops); }; const background = useMemo( () => getGradientBackground(stops, Sketch.GradientType.Linear, 90), [stops] ); const handleClickPointer = (index: number) => { onSelectStop(index); }; return (
{stops.map((stop, index) => ( ))} { onChange( stops.map((stop, index) => ({ ...stop, color: index === selectedStopIndex ? hsvaColor : stop.color, })) ); }} > {children}
); });