Skip to content

Commit

Permalink
Streamline CSS numeric types a bit (#1493)
Browse files Browse the repository at this point in the history
* Bypass Inumeric (and friends) internal interfaces

* Improve dimension-percentage typing

* Improve Percentage typing

* Improve percentage hinting, add partial resolver

* Add changesets

* Adapt rest of the code

* Clean up

* Clean up
  • Loading branch information
Jym77 authored Oct 26, 2023
1 parent b5e99a2 commit 7fde8b4
Show file tree
Hide file tree
Showing 24 changed files with 450 additions and 411 deletions.
5 changes: 5 additions & 0 deletions .changeset/green-days-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siteimprove/alfa-css": minor
---

**Added:** `Percentage` can now be partially resolved into fixed `Percentage`.
5 changes: 5 additions & 0 deletions .changeset/sharp-apricots-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siteimprove/alfa-css": minor
---

**Added:** a `AnglePercentage.resolve()` helper is now available to handle `Percentage` shenanigans.
5 changes: 5 additions & 0 deletions .changeset/shiny-worms-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siteimprove/alfa-css": minor
---

**Added:** `Percentage` builders now accept an optional type hint indicating into what the percentage resolves.
12 changes: 6 additions & 6 deletions packages/alfa-css/src/value/color/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ export namespace Color {

export function hsl<
H extends Number.Canonical | Angle.Canonical,
A extends Number.Canonical | Percentage.Canonical
A extends Number.Canonical | Percentage.Canonical,
>(
hue: H,
saturation: Percentage,
lightness: Percentage,
alpha: A
saturation: Percentage<"percentage">,
lightness: Percentage<"percentage">,
alpha: A,
): HSL<H, A> {
return HSL.of(hue, saturation, lightness, alpha);
}
Expand All @@ -52,7 +52,7 @@ export namespace Color {

export function rgb<
C extends Number.Canonical | Percentage.Canonical,
A extends Number.Canonical | Percentage.Canonical
A extends Number.Canonical | Percentage.Canonical,
>(red: C, green: C, blue: C, alpha: A): RGB<C, A> {
return RGB.of(red, green, blue, alpha);
}
Expand All @@ -70,7 +70,7 @@ export namespace Color {
RGB.parse,
HSL.parse,
Current.parse,
System.parse
System.parse,
);

export function isTransparent(color: Color): boolean {
Expand Down
10 changes: 5 additions & 5 deletions packages/alfa-css/src/value/color/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ export namespace Format {

function toPercentage(
channel: Number.Canonical | Percentage.Canonical,
max: number
max: number,
): Percentage.Canonical {
return Percentage.of(
Real.clamp(channel.value / (Number.isNumber(channel) ? max : 1), 0, 1)
return Percentage.of<"percentage">(
Real.clamp(channel.value / (Number.isNumber(channel) ? max : 1), 0, 1),
);
}

Expand All @@ -69,12 +69,12 @@ export namespace Format {
red: Number.Canonical | Percentage.Canonical,
green: Number.Canonical | Percentage.Canonical,
blue: Number.Canonical | Percentage.Canonical,
alpha: Number.Canonical | Percentage.Canonical
alpha: Number.Canonical | Percentage.Canonical,
): [
Percentage.Canonical,
Percentage.Canonical,
Percentage.Canonical,
Percentage.Canonical
Percentage.Canonical,
] {
return [
toPercentage(red, 0xff),
Expand Down
117 changes: 61 additions & 56 deletions packages/alfa-css/src/value/color/hsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,72 +14,75 @@ const { pair, map, either, option, right, take, delimited } = Parser;

// We cannot easily use Resolvable.Resolved because Percentage may resolve to
// anything depending on the base, here we want to keep them as percentages.
type ToCanonical<T extends Angle | Number | Percentage> = T extends Angle
? Angle.Canonical
: T extends Number
? Number.Canonical
: T extends Percentage
? Percentage.Canonical
: Angle.Canonical | Number.Canonical | Percentage.Canonical;
type ToCanonical<T extends Angle | Number | Percentage<"percentage">> =
T extends Angle
? Angle.Canonical
: T extends Number
? Number.Canonical
: T extends Percentage
? Percentage.Canonical
: Angle.Canonical | Number.Canonical | Percentage.Canonical;

/**
* @public
*/
export class HSL<
H extends Number.Fixed | Angle.Fixed = Number.Fixed | Angle.Fixed,
A extends Number.Fixed | Percentage.Fixed = Number.Fixed | Percentage.Fixed
A extends Number.Fixed | Percentage.Fixed<"percentage"> =
| Number.Fixed
| Percentage.Fixed<"percentage">,
> extends Format<"hsl"> {
public static of<
H extends Number.Canonical | Angle.Canonical,
A extends Number.Canonical | Percentage.Canonical,
S extends Percentage,
L extends Percentage
S extends Percentage<"percentage">,
L extends Percentage<"percentage">,
>(hue: H, saturation: S, lightness: L, alpha: A): HSL<H, A>;

public static of<
H extends Number | Angle,
A extends Number | Percentage,
S extends Percentage,
L extends Percentage
A extends Number | Percentage<"percentage">,
S extends Percentage<"percentage">,
L extends Percentage<"percentage">,
>(
hue: H,
saturation: S,
lightness: L,
alpha: A
alpha: A,
): HSL<ToCanonical<H>, ToCanonical<A>>;

public static of<
H extends Number | Angle,
A extends Number | Percentage,
S extends Percentage,
L extends Percentage
A extends Number | Percentage<"percentage">,
S extends Percentage<"percentage">,
L extends Percentage<"percentage">,
>(
hue: H,
saturation: S,
lightness: L,
alpha: A
alpha: A,
): HSL<ToCanonical<H>, ToCanonical<A>> {
return new HSL(
hue.resolve() as ToCanonical<H>,
saturation.resolve(),
lightness.resolve(),
alpha.resolve() as ToCanonical<A>
alpha.resolve() as ToCanonical<A>,
);
}

private readonly _hue: H;
private readonly _saturation: Percentage.Fixed;
private readonly _lightness: Percentage.Fixed;
private readonly _red: Percentage.Fixed;
private readonly _green: Percentage.Fixed;
private readonly _blue: Percentage.Fixed;
private readonly _saturation: Percentage.Canonical;
private readonly _lightness: Percentage.Canonical;
private readonly _red: Percentage.Canonical;
private readonly _green: Percentage.Canonical;
private readonly _blue: Percentage.Canonical;
private readonly _alpha: A;

private constructor(
hue: H,
saturation: Percentage.Fixed,
lightness: Percentage.Fixed,
alpha: A
saturation: Percentage.Canonical,
lightness: Percentage.Canonical,
alpha: A,
) {
super("hsl");
this._hue = hue;
Expand All @@ -92,35 +95,35 @@ export class HSL<
const [red, green, blue] = hslToRgb(
Real.modulo(degrees, 360) / 60,
Real.clamp(saturation.value, 0, 1),
Real.clamp(lightness.value, 0, 1)
Real.clamp(lightness.value, 0, 1),
);

this._red = Percentage.of(red);
this._green = Percentage.of(green);
this._blue = Percentage.of(blue);
this._red = Percentage.of<"percentage">(red);
this._green = Percentage.of<"percentage">(green);
this._blue = Percentage.of<"percentage">(blue);
}

public get hue(): H {
return this._hue;
}

public get saturation(): Percentage.Fixed {
public get saturation(): Percentage.Canonical {
return this._saturation;
}

public get lightness(): Percentage.Fixed {
public get lightness(): Percentage.Canonical {
return this._lightness;
}

public get red(): Percentage.Fixed {
public get red(): Percentage.Canonical {
return this._red;
}

public get green(): Percentage.Fixed {
public get green(): Percentage.Canonical {
return this._green;
}

public get blue(): Percentage.Fixed {
public get blue(): Percentage.Canonical {
return this._blue;
}

Expand All @@ -130,7 +133,7 @@ export class HSL<

public resolve(): RGB.Canonical {
return RGB.of(
...Format.resolve(this.red, this.green, this.blue, this.alpha)
...Format.resolve(this.red, this.green, this.blue, this.alpha),
);
}

Expand Down Expand Up @@ -182,15 +185,15 @@ export namespace HSL {

export function isHSL<
H extends Number.Fixed | Angle.Fixed,
A extends Number.Fixed | Percentage.Fixed
A extends Number.Fixed | Percentage.Fixed,
>(value: unknown): value is HSL<H, A> {
return value instanceof HSL;
}

/**
* {@link https://drafts.csswg.org/css-color/#typedef-alpha-value}
*/
const parseAlpha = either(Number.parse, Percentage.parse);
const parseAlpha = either(Number.parse, Percentage.parse<"percentage">);

/**
* {@link https://drafts.csswg.org/css-color/#typedef-hue}
Expand All @@ -200,30 +203,32 @@ export namespace HSL {
const orNone = <T>(parser: CSSParser<T>) =>
either(
parser,
map(Keyword.parse("none"), () => Percentage.of(0))
map(Keyword.parse("none"), () => Percentage.of<"percentage">(0)),
);

const parseTriplet = (
separator: CSSParser<any>,
acceptNone: boolean = false
acceptNone: boolean = false,
) =>
map(
pair(
acceptNone
? either(
parseHue,
map(Keyword.parse("none"), () => Number.of(0))
map(Keyword.parse("none"), () => Number.of(0)),
)
: parseHue,
take(
right(
separator,
acceptNone ? orNone(Percentage.parse) : Percentage.parse
acceptNone
? orNone(Percentage.parse<"percentage">)
: Percentage.parse<"percentage">,
),
2
)
2,
),
),
([h, [s, l]]) => [h, s, l] as const
([h, [s, l]]) => [h, s, l] as const,
);

/**
Expand All @@ -238,27 +243,27 @@ export namespace HSL {
option(
right(
delimited(option(Token.parseWhitespace), Token.parseDelim("/")),
orNone(parseAlpha)
)
)
orNone(parseAlpha),
),
),
);

const parseLegacy = pair(
parseTriplet(delimited(option(Token.parseWhitespace), Token.parseComma)),
option(
right(
delimited(option(Token.parseWhitespace), Token.parseComma),
parseAlpha
)
)
parseAlpha,
),
),
);
/**
* {@link https://drafts.csswg.org/css-color/#funcdef-hsl}
*/
export const parse: CSSParser<HSL> = map(
Function.parse(
(fn) => fn.value === "hsl" || fn.value === "hsla",
either(parseLegacy, parseModern)
either(parseLegacy, parseModern),
),
(result) => {
const [, [[hue, saturation, lightness], alpha]] = result;
Expand All @@ -267,9 +272,9 @@ export namespace HSL {
hue,
saturation,
lightness,
alpha.getOrElse(() => Number.of(1))
alpha.getOrElse(() => Number.of(1)),
);
}
},
);
}

Expand All @@ -279,7 +284,7 @@ export namespace HSL {
function hslToRgb(
hue: number,
saturation: number,
lightness: number
lightness: number,
): [number, number, number] {
const t2 =
lightness <= 0.5
Expand Down
Loading

0 comments on commit 7fde8b4

Please sign in to comment.