Skip to content

Commit

Permalink
[Color 4] Dart JS API implementation (#2117)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonny Gerig Meyer <jonny@oddbird.net>
Co-authored-by: Natalie Weizenbaum <nweiz@google.com>
  • Loading branch information
3 people authored Nov 17, 2023
1 parent 65f60e6 commit c91178d
Show file tree
Hide file tree
Showing 8 changed files with 584 additions and 107 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,32 @@
* Added `InterpolationMethod` and `HueInterpolationMethod` which collectively
represent the method to use to interpolate two colors.

### JS API

* Modify `SassColor` to accept a new `space` option, with support for all the
new color spaces defined in Color Level 4.

* Add `SassColor.space` which returns a color's color space.

* Add `SassColor.channels` and `.channelsOrNull` which returns a list of channel
values, with missing channels converted to 0 or exposed as null, respectively.

* Add `SassColor.isLegacy`, `.isInGamut()`, `.channel()`, `.isChannelMissing()`,
`.isChannelPowerless()`, `.toSpace()`, `.toGamut()`, `.change()`, and
`.interpolate()` which do the same thing as the Sass functions of the
corresponding names.

* Deprecate `SassColor.red`, `.green`, `.blue`, `.hue`, `.saturation`,
`.lightness`, `.whiteness`, and `.blackness` in favor of
`SassColor.channel()`.

### Embedded Sass

* Add `Color` SassScript value, with support for all the new color spaces
defined in Color Level 4.

* Remove `RgbColor`, `HslColor` and `HwbColor` SassScript values.

## 1.69.5

### JS API
Expand Down
4 changes: 4 additions & 0 deletions lib/src/deprecation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ enum Deprecation {
deprecatedIn: '1.70.0',
description: 'Using global Sass color functions.'),

color4Api('color-4-api',
deprecatedIn: '1.70.0',
description: 'Methods of interacting with legacy SassColors.'),

/// Deprecation for `@import` rules.
import.future('import', description: '@import rules.'),

Expand Down
111 changes: 86 additions & 25 deletions lib/src/embedded/protofier.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,12 @@ final class Protofier {
..quoted = value.hasQuotes;
case SassNumber():
result.number = _protofyNumber(value);
case SassColor(space: ColorSpace.hsl):
result.hslColor = Value_HslColor()
..hue = value.channel('hue') * 1.0
..saturation = value.channel('saturation')
..lightness = value.channel('lightness')
..alpha = value.alpha * 1.0;
case SassColor():
result.rgbColor = Value_RgbColor()
..red = value.channel('red').clamp(0, 255).round()
..green = value.channel('green').clamp(0, 255).round()
..blue = value.channel('blue').clamp(0, 255).round()
result.color = Value_Color()
..space = value.space.name
..channel1 = value.channel0
..channel2 = value.channel1
..channel3 = value.channel2
..alpha = value.alpha * 1.0;
case SassArgumentList():
_argumentLists.add(value);
Expand Down Expand Up @@ -181,17 +176,85 @@ final class Protofier {
case Value_Value.number:
return _deprotofyNumber(value.number);

case Value_Value.rgbColor:
return SassColor.rgb(value.rgbColor.red, value.rgbColor.green,
value.rgbColor.blue, value.rgbColor.alpha);

case Value_Value.hslColor:
return SassColor.hsl(value.hslColor.hue, value.hslColor.saturation,
value.hslColor.lightness, value.hslColor.alpha);

case Value_Value.hwbColor:
return SassColor.hwb(value.hwbColor.hue, value.hwbColor.whiteness,
value.hwbColor.blackness, value.hwbColor.alpha);
case Value_Value.color:
var space = ColorSpace.fromName(value.color.space);
switch (space) {
case ColorSpace.rgb:
return SassColor.rgb(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);

case ColorSpace.hsl:
return SassColor.hsl(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);

case ColorSpace.hwb:
return SassColor.hwb(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);

case ColorSpace.lab:
return SassColor.lab(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);
case ColorSpace.oklab:
return SassColor.oklab(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);

case ColorSpace.lch:
return SassColor.lch(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);
case ColorSpace.oklch:
return SassColor.oklch(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);

case ColorSpace.srgb:
return SassColor.srgb(value.color.channel1, value.color.channel2,
value.color.channel3, value.color.alpha);
case ColorSpace.srgbLinear:
return SassColor.srgbLinear(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);
case ColorSpace.displayP3:
return SassColor.displayP3(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);
case ColorSpace.a98Rgb:
return SassColor.a98Rgb(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);
case ColorSpace.prophotoRgb:
return SassColor.prophotoRgb(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);
case ColorSpace.rec2020:
return SassColor.rec2020(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);

case ColorSpace.xyzD50:
return SassColor.xyzD50(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);
case ColorSpace.xyzD65:
return SassColor.xyzD65(
value.color.channel1,
value.color.channel2,
value.color.channel3,
value.color.alpha);

default:
throw "Unreachable";
}

case Value_Value.argumentList:
if (value.argumentList.id != 0) {
Expand Down Expand Up @@ -276,10 +339,8 @@ final class Protofier {
throw paramsError(error.toString());
}

if (value.whichValue() == Value_Value.rgbColor) {
name = 'RgbColor.$name';
} else if (value.whichValue() == Value_Value.hslColor) {
name = 'HslColor.$name';
if (value.whichValue() == Value_Value.color) {
name = 'Color.$name';
}

throw paramsError(
Expand Down
21 changes: 21 additions & 0 deletions lib/src/evaluation_context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:async';
import 'package:source_span/source_span.dart';

import 'deprecation.dart';
import 'logger.dart';

/// An interface that exposes information about the current Sass evaluation.
///
Expand All @@ -25,6 +26,16 @@ abstract interface class EvaluationContext {
}
}

/// The current evaluation context, or null if there isn't a Sass stylesheet
/// currently being evaluated.
static EvaluationContext? get _currentOrNull {
if (Zone.current[#_evaluationContext] case EvaluationContext context) {
return context;
} else {
return null;
}
}

/// Returns the span for the currently executing callable.
///
/// For normal exception reporting, this should be avoided in favor of
Expand Down Expand Up @@ -58,6 +69,16 @@ void warnForDeprecation(String message, Deprecation deprecation) {
EvaluationContext.current.warn(message, deprecation);
}

/// Prints a deprecation warning with [message] of type [deprecation],
/// using stderr if there is no [EvaluationContext.current].
void warnForDeprecationFromApi(String message, Deprecation deprecation) {
if (EvaluationContext._currentOrNull case var context?) {
context.warn(message, deprecation);
} else {
Logger.stderr().warnForDeprecation(deprecation, message);
}
}

/// Runs [callback] with [context] as [EvaluationContext.current].
///
/// This is zone-based, so if [callback] is asynchronous [warn] is set for the
Expand Down
5 changes: 5 additions & 0 deletions lib/src/js/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ bool isUndefined(Object? value) => _isUndefined.call(value) as bool;

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

/// Returns whether or not [value] is the JS `null` value.
bool isNull(Object? value) => _isNull.call(value) as bool;

final _isNull = JSFunction("value", "return value === null;");

@JS("Error")
external JSClass get jsErrorClass;

Expand Down
Loading

0 comments on commit c91178d

Please sign in to comment.