Skip to content

Commit c91178d

Browse files
jamesnwjgerigmeyernex3
authored
[Color 4] Dart JS API implementation (#2117)
Co-authored-by: Jonny Gerig Meyer <[email protected]> Co-authored-by: Natalie Weizenbaum <[email protected]>
1 parent 65f60e6 commit c91178d

File tree

8 files changed

+584
-107
lines changed

8 files changed

+584
-107
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,32 @@
152152
* Added `InterpolationMethod` and `HueInterpolationMethod` which collectively
153153
represent the method to use to interpolate two colors.
154154

155+
### JS API
156+
157+
* Modify `SassColor` to accept a new `space` option, with support for all the
158+
new color spaces defined in Color Level 4.
159+
160+
* Add `SassColor.space` which returns a color's color space.
161+
162+
* Add `SassColor.channels` and `.channelsOrNull` which returns a list of channel
163+
values, with missing channels converted to 0 or exposed as null, respectively.
164+
165+
* Add `SassColor.isLegacy`, `.isInGamut()`, `.channel()`, `.isChannelMissing()`,
166+
`.isChannelPowerless()`, `.toSpace()`, `.toGamut()`, `.change()`, and
167+
`.interpolate()` which do the same thing as the Sass functions of the
168+
corresponding names.
169+
170+
* Deprecate `SassColor.red`, `.green`, `.blue`, `.hue`, `.saturation`,
171+
`.lightness`, `.whiteness`, and `.blackness` in favor of
172+
`SassColor.channel()`.
173+
174+
### Embedded Sass
175+
176+
* Add `Color` SassScript value, with support for all the new color spaces
177+
defined in Color Level 4.
178+
179+
* Remove `RgbColor`, `HslColor` and `HwbColor` SassScript values.
180+
155181
## 1.69.5
156182

157183
### JS API

lib/src/deprecation.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ enum Deprecation {
7676
deprecatedIn: '1.70.0',
7777
description: 'Using global Sass color functions.'),
7878

79+
color4Api('color-4-api',
80+
deprecatedIn: '1.70.0',
81+
description: 'Methods of interacting with legacy SassColors.'),
82+
7983
/// Deprecation for `@import` rules.
8084
import.future('import', description: '@import rules.'),
8185

lib/src/embedded/protofier.dart

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,12 @@ final class Protofier {
5353
..quoted = value.hasQuotes;
5454
case SassNumber():
5555
result.number = _protofyNumber(value);
56-
case SassColor(space: ColorSpace.hsl):
57-
result.hslColor = Value_HslColor()
58-
..hue = value.channel('hue') * 1.0
59-
..saturation = value.channel('saturation')
60-
..lightness = value.channel('lightness')
61-
..alpha = value.alpha * 1.0;
6256
case SassColor():
63-
result.rgbColor = Value_RgbColor()
64-
..red = value.channel('red').clamp(0, 255).round()
65-
..green = value.channel('green').clamp(0, 255).round()
66-
..blue = value.channel('blue').clamp(0, 255).round()
57+
result.color = Value_Color()
58+
..space = value.space.name
59+
..channel1 = value.channel0
60+
..channel2 = value.channel1
61+
..channel3 = value.channel2
6762
..alpha = value.alpha * 1.0;
6863
case SassArgumentList():
6964
_argumentLists.add(value);
@@ -181,17 +176,85 @@ final class Protofier {
181176
case Value_Value.number:
182177
return _deprotofyNumber(value.number);
183178

184-
case Value_Value.rgbColor:
185-
return SassColor.rgb(value.rgbColor.red, value.rgbColor.green,
186-
value.rgbColor.blue, value.rgbColor.alpha);
187-
188-
case Value_Value.hslColor:
189-
return SassColor.hsl(value.hslColor.hue, value.hslColor.saturation,
190-
value.hslColor.lightness, value.hslColor.alpha);
191-
192-
case Value_Value.hwbColor:
193-
return SassColor.hwb(value.hwbColor.hue, value.hwbColor.whiteness,
194-
value.hwbColor.blackness, value.hwbColor.alpha);
179+
case Value_Value.color:
180+
var space = ColorSpace.fromName(value.color.space);
181+
switch (space) {
182+
case ColorSpace.rgb:
183+
return SassColor.rgb(value.color.channel1, value.color.channel2,
184+
value.color.channel3, value.color.alpha);
185+
186+
case ColorSpace.hsl:
187+
return SassColor.hsl(value.color.channel1, value.color.channel2,
188+
value.color.channel3, value.color.alpha);
189+
190+
case ColorSpace.hwb:
191+
return SassColor.hwb(value.color.channel1, value.color.channel2,
192+
value.color.channel3, value.color.alpha);
193+
194+
case ColorSpace.lab:
195+
return SassColor.lab(value.color.channel1, value.color.channel2,
196+
value.color.channel3, value.color.alpha);
197+
case ColorSpace.oklab:
198+
return SassColor.oklab(value.color.channel1, value.color.channel2,
199+
value.color.channel3, value.color.alpha);
200+
201+
case ColorSpace.lch:
202+
return SassColor.lch(value.color.channel1, value.color.channel2,
203+
value.color.channel3, value.color.alpha);
204+
case ColorSpace.oklch:
205+
return SassColor.oklch(value.color.channel1, value.color.channel2,
206+
value.color.channel3, value.color.alpha);
207+
208+
case ColorSpace.srgb:
209+
return SassColor.srgb(value.color.channel1, value.color.channel2,
210+
value.color.channel3, value.color.alpha);
211+
case ColorSpace.srgbLinear:
212+
return SassColor.srgbLinear(
213+
value.color.channel1,
214+
value.color.channel2,
215+
value.color.channel3,
216+
value.color.alpha);
217+
case ColorSpace.displayP3:
218+
return SassColor.displayP3(
219+
value.color.channel1,
220+
value.color.channel2,
221+
value.color.channel3,
222+
value.color.alpha);
223+
case ColorSpace.a98Rgb:
224+
return SassColor.a98Rgb(
225+
value.color.channel1,
226+
value.color.channel2,
227+
value.color.channel3,
228+
value.color.alpha);
229+
case ColorSpace.prophotoRgb:
230+
return SassColor.prophotoRgb(
231+
value.color.channel1,
232+
value.color.channel2,
233+
value.color.channel3,
234+
value.color.alpha);
235+
case ColorSpace.rec2020:
236+
return SassColor.rec2020(
237+
value.color.channel1,
238+
value.color.channel2,
239+
value.color.channel3,
240+
value.color.alpha);
241+
242+
case ColorSpace.xyzD50:
243+
return SassColor.xyzD50(
244+
value.color.channel1,
245+
value.color.channel2,
246+
value.color.channel3,
247+
value.color.alpha);
248+
case ColorSpace.xyzD65:
249+
return SassColor.xyzD65(
250+
value.color.channel1,
251+
value.color.channel2,
252+
value.color.channel3,
253+
value.color.alpha);
254+
255+
default:
256+
throw "Unreachable";
257+
}
195258

196259
case Value_Value.argumentList:
197260
if (value.argumentList.id != 0) {
@@ -276,10 +339,8 @@ final class Protofier {
276339
throw paramsError(error.toString());
277340
}
278341

279-
if (value.whichValue() == Value_Value.rgbColor) {
280-
name = 'RgbColor.$name';
281-
} else if (value.whichValue() == Value_Value.hslColor) {
282-
name = 'HslColor.$name';
342+
if (value.whichValue() == Value_Value.color) {
343+
name = 'Color.$name';
283344
}
284345

285346
throw paramsError(

lib/src/evaluation_context.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:async';
77
import 'package:source_span/source_span.dart';
88

99
import 'deprecation.dart';
10+
import 'logger.dart';
1011

1112
/// An interface that exposes information about the current Sass evaluation.
1213
///
@@ -25,6 +26,16 @@ abstract interface class EvaluationContext {
2526
}
2627
}
2728

29+
/// The current evaluation context, or null if there isn't a Sass stylesheet
30+
/// currently being evaluated.
31+
static EvaluationContext? get _currentOrNull {
32+
if (Zone.current[#_evaluationContext] case EvaluationContext context) {
33+
return context;
34+
} else {
35+
return null;
36+
}
37+
}
38+
2839
/// Returns the span for the currently executing callable.
2940
///
3041
/// For normal exception reporting, this should be avoided in favor of
@@ -58,6 +69,16 @@ void warnForDeprecation(String message, Deprecation deprecation) {
5869
EvaluationContext.current.warn(message, deprecation);
5970
}
6071

72+
/// Prints a deprecation warning with [message] of type [deprecation],
73+
/// using stderr if there is no [EvaluationContext.current].
74+
void warnForDeprecationFromApi(String message, Deprecation deprecation) {
75+
if (EvaluationContext._currentOrNull case var context?) {
76+
context.warn(message, deprecation);
77+
} else {
78+
Logger.stderr().warnForDeprecation(deprecation, message);
79+
}
80+
}
81+
6182
/// Runs [callback] with [context] as [EvaluationContext.current].
6283
///
6384
/// This is zone-based, so if [callback] is asynchronous [warn] is set for the

lib/src/js/utils.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ bool isUndefined(Object? value) => _isUndefined.call(value) as bool;
2727

2828
final _isUndefined = JSFunction("value", "return value === undefined;");
2929

30+
/// Returns whether or not [value] is the JS `null` value.
31+
bool isNull(Object? value) => _isNull.call(value) as bool;
32+
33+
final _isNull = JSFunction("value", "return value === null;");
34+
3035
@JS("Error")
3136
external JSClass get jsErrorClass;
3237

0 commit comments

Comments
 (0)