Skip to content

Commit 0484d9c

Browse files
experimental: add gradient controller to update color stops using ui (#4159)
## Description The `GradientControl` helps in displaying the colour stops that are available in the linear-gradient that users added. There are two knows issues at the moment. - The only limitation is. We can't swap the colours when a colour stop crosses an existing colour stop. In some tools, the colours are swapped resulting in the swiping of the gradient too. This is because of a limitation/bug on the radix side, they pre-sort the slider stops. So, we can never know which crosses an existing stop and changed it's value. radix-ui/primitives#2247 ## Steps for reproduction - Passing a linear-gradient should show all the `color-stops` in the gradient. - The `color-hints` are displayed at the bottom of the gradient bar. These can't be moved along, more info about it is in the code component. Because, this basically changes the whole gradient just by moving. - The `color-hints` are showed at the bottom of the gradient. Just to notify users about the existence of the hints. - Users should be able to move the color-stops along the slider track. - The color-stops can be moved using the arrow keys from the keyboard. <img width="299" alt="Screenshot 2024-09-25 at 10 32 47 AM" src="https://github.com/user-attachments/assets/d65e6af5-bba9-490d-96ff-0e58b8fb93bc"> <img width="289" alt="Screenshot 2024-09-25 at 10 32 43 AM" src="https://github.com/user-attachments/assets/a3ac83e1-d379-49dc-a5d8-42936960f761"> <img width="560" alt="Screenshot 2024-09-25 at 10 32 38 AM" src="https://github.com/user-attachments/assets/fef11d9e-2d0c-4d71-8587-227f22551edb"> ![color-change-gradients](https://github.com/user-attachments/assets/44f71bc5-1c52-4bf9-8458-c12bd355759f) ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 5de6) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file --------- Co-authored-by: Oleg Isonen <oleg008@gmail.com>
1 parent 85fd2e7 commit 0484d9c

File tree

12 files changed

+623
-13
lines changed

12 files changed

+623
-13
lines changed

packages/css-data/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export {
88
} from "./__generated__/property-value-descriptions";
99
export * from "./__generated__/animatable-properties";
1010
export * from "./__generated__/pseudo-elements";
11+
export * from "./property-parsers";
1112

1213
// shorthand property parsers
1314
export * from "./parse-css-value";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./linear-gradient";

packages/css-data/src/property-parsers/linear-gradient.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,17 @@ import namesPlugin from "colord/plugins/names";
1212

1313
extend([namesPlugin]);
1414

15-
interface GradientStop {
15+
export type GradientStop = {
1616
color?: RgbValue;
1717
position?: UnitValue;
1818
hint?: UnitValue;
19-
}
19+
};
2020

21-
interface ParsedGradient {
21+
export type ParsedGradient = {
2222
angle?: UnitValue;
2323
sideOrCorner?: KeywordValue;
2424
stops: GradientStop[];
25-
}
25+
};
2626

2727
const sideOrCorderIdentifiers = ["to", "top", "bottom", "left", "right"];
2828

@@ -182,7 +182,7 @@ const getColor = (
182182
};
183183

184184
export const reconstructLinearGradient = (parsed: ParsedGradient): string => {
185-
const direction = parsed.angle || parsed.sideOrCorner;
185+
const direction = parsed?.angle || parsed?.sideOrCorner;
186186
const stops = parsed.stops
187187
.map((stop: GradientStop) => {
188188
let result = toValue(stop.color);

packages/design-system/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@
5858
"@react-aria/interactions": "^3.23.0",
5959
"@react-aria/utils": "^3.27.0",
6060
"@stitches/react": "1.3.1-1",
61+
"@webstudio-is/css-data": "workspace:*",
62+
"@webstudio-is/css-engine": "workspace:*",
6163
"@webstudio-is/icons": "workspace:*",
64+
"colord": "^2.9.3",
6265
"change-case": "^5.4.4",
6366
"cmdk": "^1.1.1",
6467
"downshift": "^6.1.7",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
parseLinearGradient,
3+
reconstructLinearGradient,
4+
type ParsedGradient,
5+
} from "@webstudio-is/css-data";
6+
import { useState } from "react";
7+
import { GradientPicker } from "./gradient-picker";
8+
import { Flex } from "./flex";
9+
import { Text } from "./text";
10+
11+
export default {
12+
title: "Library/GradientPicker",
13+
};
14+
15+
export const GradientWithoutAngle = () => {
16+
const gradientString = "linear-gradient(black 0%, white 100%)";
17+
const [gradient, setGradient] = useState<string>(gradientString);
18+
19+
return (
20+
<Flex direction="column" gap="4">
21+
<GradientPicker
22+
gradient={parseLinearGradient(gradientString) as ParsedGradient}
23+
onChange={(value) => {
24+
setGradient(reconstructLinearGradient(value));
25+
}}
26+
onThumbSelected={() => {}}
27+
/>
28+
<Text>{gradient}</Text>
29+
</Flex>
30+
);
31+
};
32+
33+
export const GradientWithAngleAndHints = () => {
34+
const gradientString =
35+
"linear-gradient(145deg, #ff00fa 0%, #00f497 34% 34%, #ffa800 56% 56%, #00eaff 100%)";
36+
const [gradient, setGradient] = useState<string>(gradientString);
37+
38+
return (
39+
<Flex direction="column" gap="4">
40+
<GradientPicker
41+
gradient={parseLinearGradient(gradientString) as ParsedGradient}
42+
onChange={(value) => {
43+
setGradient(reconstructLinearGradient(value));
44+
}}
45+
onThumbSelected={() => {}}
46+
/>
47+
<Text>{gradient}</Text>
48+
</Flex>
49+
);
50+
};
51+
52+
export const GradientWithSideOrCorner = () => {
53+
const gradientString = "linear-gradient(to left top, blue 0%, red 100%)";
54+
const [gradient, setGradient] = useState<string>(gradientString);
55+
56+
return (
57+
<Flex direction="column" gap="4">
58+
<GradientPicker
59+
gradient={parseLinearGradient(gradientString) as ParsedGradient}
60+
onChange={(value) => {
61+
setGradient(reconstructLinearGradient(value));
62+
}}
63+
onThumbSelected={() => {}}
64+
/>
65+
<Text>{gradient}</Text>
66+
</Flex>
67+
);
68+
};

0 commit comments

Comments
 (0)