• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

graphty-org / graphty-monorepo / 20912147990

12 Jan 2026 07:48AM UTC coverage: 82.139% (+0.4%) from 81.707%
20912147990

push

github

apowers313
ci: fix flaky socket

13680 of 18076 branches covered (75.68%)

Branch coverage included in aggregate %.

42217 of 49976 relevant lines covered (84.47%)

152436.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.51
/graphty/src/components/sidebar/node-controls/NodeColorControl.tsx
1
import { Box, SegmentedControl, Stack, Text } from "@mantine/core";
1✔
2
import React from "react";
3

4
import type { ColorConfig, ColorMode, ColorStop } from "../../../types/style-layer";
5
import { createColorStop } from "../../../utils/color-stops";
1✔
6
import { CompactColorInput } from "../controls/CompactColorInput";
1✔
7
import { GradientEditor } from "../controls/GradientEditor";
1✔
8

9

10
interface NodeColorControlProps {
11
    value: ColorConfig;
12
    onChange: (value: ColorConfig) => void;
13
}
14

15
/**
16
 * Control for node color with solid, gradient, and radial modes.
17
 * Figma-style layout with color swatch + hex + opacity on same line.
18
 * @param root0 - Component props
19
 * @param root0.value - The current color configuration
20
 * @param root0.onChange - Called when the color configuration changes
21
 * @returns The node color control component
22
 */
23
export function NodeColorControl({ value, onChange }: NodeColorControlProps): React.JSX.Element {
1✔
24
    const handleModeChange = (newMode: string): void => {
56✔
25
        const mode = newMode as ColorMode;
5✔
26

27
        if (mode === value.mode) {
5!
28
            return;
×
29
        }
×
30

31
        if (mode === "solid") {
5✔
32
            const color = value.mode === "solid" ? value.color : (value.stops[0]?.color ?? "#5b8ff9");
1!
33
            onChange({
1✔
34
                mode: "solid",
1✔
35
                color,
1✔
36
                opacity: value.opacity,
1✔
37
            });
1✔
38
        } else if (mode === "gradient") {
5✔
39
            const stops =
2✔
40
                value.mode === "solid" ? [createColorStop(0, value.color), createColorStop(1, "#ffffff")] : value.stops;
2✔
41
            onChange({
2✔
42
                mode: "gradient",
2✔
43
                stops,
2✔
44
                direction: 0,
2✔
45
                opacity: value.opacity,
2✔
46
            });
2✔
47
        } else {
2✔
48
            const stops =
2✔
49
                value.mode === "solid" ? [createColorStop(0, value.color), createColorStop(1, "#ffffff")] : value.stops;
2✔
50
            onChange({
2✔
51
                mode: "radial",
2✔
52
                stops,
2✔
53
                opacity: value.opacity,
2✔
54
            });
2✔
55
        }
2✔
56
    };
5✔
57

58
    const handleColorChange = (color: string): void => {
56✔
59
        if (value.mode === "solid") {
2✔
60
            onChange({ ...value, color });
2✔
61
        }
2✔
62
    };
2✔
63

64
    const handleOpacityChange = (opacityPercent: number): void => {
56✔
65
        onChange({ ...value, opacity: opacityPercent / 100 });
1✔
66
    };
1✔
67

68
    // Combined handler for color picker to update both color and opacity atomically
69
    const handleColorAndOpacityChange = (color: string, opacityPercent: number): void => {
56✔
70
        if (value.mode === "solid") {
×
71
            onChange({
×
72
                ...value,
×
73
                color,
×
74
                opacity: opacityPercent / 100,
×
75
            });
×
76
        }
×
77
    };
×
78

79
    const handleGradientChange = (stops: ColorStop[], direction?: number): void => {
56✔
80
        if (value.mode === "gradient") {
×
81
            onChange({
×
82
                ...value,
×
83
                stops,
×
84
                direction: direction ?? value.direction,
×
85
            });
×
86
        } else if (value.mode === "radial") {
×
87
            onChange({
×
88
                ...value,
×
89
                stops,
×
90
            });
×
91
        }
×
92
    };
×
93

94
    return (
56✔
95
        <Stack gap={4}>
56✔
96
            {/* Color Mode selector */}
97
            <Box>
56✔
98
                <Text size="xs" c="dimmed" mb={1} lh={1.2}>
56✔
99
                    Color Mode
100
                </Text>
56✔
101
                <SegmentedControl
56✔
102
                    value={value.mode}
56✔
103
                    onChange={handleModeChange}
56✔
104
                    data={[
56✔
105
                        { value: "solid", label: "Solid" },
56✔
106
                        { value: "gradient", label: "Gradient" },
56✔
107
                        { value: "radial", label: "Radial" },
56✔
108
                    ]}
56✔
109
                    size="compact"
56✔
110
                    fullWidth
56✔
111
                />
56✔
112
            </Box>
56✔
113

114
            {/* Solid color: Figma-style color input */}
115
            {value.mode === "solid" && (
56✔
116
                <CompactColorInput
47✔
117
                    label="Color"
47✔
118
                    color={value.color}
47✔
119
                    opacity={Math.round(value.opacity * 100)}
47✔
120
                    onColorChange={handleColorChange}
47✔
121
                    onOpacityChange={handleOpacityChange}
47✔
122
                    onColorAndOpacityChange={handleColorAndOpacityChange}
47✔
123
                />
47✔
124
            )}
125

126
            {/* Gradient/Radial editor */}
127
            {(value.mode === "gradient" || value.mode === "radial") && (
56✔
128
                <GradientEditor
9✔
129
                    stops={value.stops}
9✔
130
                    direction={value.mode === "gradient" ? value.direction : undefined}
9✔
131
                    showDirection={value.mode === "gradient"}
9✔
132
                    onChange={handleGradientChange}
9✔
133
                />
9✔
134
            )}
135
        </Stack>
56✔
136
    );
137
}
56✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc