diff --git a/src/parse.js b/src/parse.js index af9cfa843..6a5cffcf8 100644 --- a/src/parse.js +++ b/src/parse.js @@ -74,7 +74,7 @@ export default function parse (str, {meta} = {}) { let alpha = 1; - if (format.lastAlpha || env.parsed.lastAlpha) { + if (format.alpha === true || env.parsed.lastAlpha) { alpha = env.parsed.args.pop(); } diff --git a/src/serialize.js b/src/serialize.js index 15cd43046..f67e38ddd 100644 --- a/src/serialize.js +++ b/src/serialize.js @@ -8,16 +8,19 @@ import clone from "./clone.js"; /** * Generic toString() method, outputs a color(spaceId ...coords) function, a functional syntax, or custom formats defined by the color space - * @param {Object} options - * @param {number} options.precision - Significant digits - * @param {boolean} options.inGamut - Adjust coordinates to fit in gamut first? [default: false] + * @param {Object} [options] + * @param {number} [options.precision] - Significant digits + * @param {boolean} [options.inGamut=false] - Adjust coordinates to fit in gamut first? + * @param {string} [options.format="default"] - Output format id, default is "default" + * @param {string} [options.coords] - Coordinate format to override the default + * @param {string | boolean | {type: string, include: boolean}} [options.alpha] - Alpha format */ export default function serialize (color, { precision = defaults.precision, format = "default", inGamut = true, - coordTypes, - alphaType, + coords: coordFormat, + alpha: alphaFormat, ...customOptions } = {}) { let ret; @@ -56,7 +59,7 @@ export default function serialize (color, { // Functional syntax let name = format.name || "color"; - let args = format.serializeCoords(coords, precision, coordTypes); + let args = format.serializeCoords(coords, precision, coordFormat); if (name === "color") { // If output is a color() function, add colorspace id as first argument @@ -65,11 +68,30 @@ export default function serialize (color, { } let alpha = color.alpha; - if (precision !== null) { - alpha = util.serializeNumber(alpha, {precision}); + + if (alphaFormat !== undefined && !(typeof alphaFormat === "object")) { + alphaFormat = typeof alphaFormat === "string" ? {type: alphaFormat} : {include: alphaFormat}; + } + + let alphaType = alphaFormat?.type ?? ""; + let serializeAlpha = alphaFormat?.include === true || format.alpha === true || (alphaFormat?.include !== false && format.alpha !== false && alpha < 1); + let strAlpha = ""; + + if (serializeAlpha) { + if (precision !== null) { + let unit; + + if (alphaType === "") { + unit = "%"; + alpha *= 100; + } + + alpha = util.serializeNumber(alpha, {precision}); + } + + strAlpha = `${format.commas ? "," : " /"} ${alpha}`; } - let strAlpha = color.alpha >= 1 || format.noAlpha ? "" : `${format.commas ? "," : " /"} ${alpha}`; ret = `${name}(${args.join(format.commas ? ", " : " ")}${strAlpha})`; } diff --git a/src/spaces/hsl.js b/src/spaces/hsl.js index 7cb5410bd..7ec82a878 100644 --- a/src/spaces/hsl.js +++ b/src/spaces/hsl.js @@ -85,7 +85,7 @@ export default new ColorSpace({ "hsla": { coords: [" | ", "", ""], commas: true, - lastAlpha: true, + alpha: true, }, }, }); diff --git a/src/spaces/srgb.js b/src/spaces/srgb.js index 692abf0b4..f47aaaae8 100644 --- a/src/spaces/srgb.js +++ b/src/spaces/srgb.js @@ -47,13 +47,13 @@ export default new RGBColorSpace({ name: "rgb", commas: true, coords: coordGrammarNumber, - noAlpha: true, + alpha: false, }, "color": { /* use defaults */ }, "rgba": { coords: coordGrammar, commas: true, - lastAlpha: true, + alpha: true, }, "rgba_number": { name: "rgba", diff --git a/test/serialize.js b/test/serialize.js index 5cdc6dc35..eb54d965e 100644 --- a/test/serialize.js +++ b/test/serialize.js @@ -33,7 +33,45 @@ const tests = { args: ["oklab", [.65, 0.2, .1]], expect: "oklab(65% 0.2 0.1)", }, - ] + ], + }, + { + name: "With alpha", + tests: [ + { + args: ["srgb", [1, 0.5, 0], .5], + expect: "rgb(100% 50% 0% / 0.5)", + }, + { + args: ["lch", [65, 20, 90], .5], + expect: "lch(65% 20 90 / 0.5)", + }, + { + args: ["lab", [65, 20, 90], .5], + expect: "lab(65% 20 90 / 0.5)", + }, + { + args: ["oklch", [.65, 0.2, 90], .5], + expect: "oklch(65% 0.2 90 / 0.5)", + }, + { + args: ["oklab", [.65, 0.2, .1], .5], + expect: "oklab(65% 0.2 0.1 / 0.5)", + }, + ], + }, + { + name: "Mandatory alpha", + tests: [ + { + args: ["srgb", [1, 0.5, 0], 1, {format: "rgba"}], + expect: "rgba(100%, 50%, 0%, 1)", + }, + { + args: ["hsl", [180, 50, 50], 1, {format: "hsla"}], + expect: "hsla(180, 50%, 50%, 1)", + }, + ], }, { name: "Alternate formats", @@ -43,8 +81,18 @@ const tests = { args: ["srgb", [1, 0.5, 0], 1, {format: "hex"}], expect: "#ff8000", }, - ] - } + ], + }, + { + name: "Custom alpha format", + tests: [ + { + name: "Force alpha", + args: ["srgb", [1, 0.5, 0], 1, {alpha: true}], + expect: "rgb(100% 50% 0% / 1)", + }, + ], + }, ], }; diff --git a/types/src/space.d.ts b/types/src/space.d.ts index 4b2e52876..9bbdcb8d0 100644 --- a/types/src/space.d.ts +++ b/types/src/space.d.ts @@ -21,10 +21,8 @@ export interface Format { toGamut?: boolean | undefined; /** Whether commas should separate arguments for a format */ commas?: boolean | undefined; - /** Whether the last coordinate is the alpha coordinate */ - lastAlpha?: boolean | undefined; - /** Whether the format has an alpha channel */ - noAlpha?: boolean | undefined; + /** Whether to always have alpha at the end (true), never (false), or auto (undefined) */ + alpha?: boolean | undefined; test?: ((str: string) => boolean) | undefined; /** Function to parse a string into a color */ parse?: ((str: string) => ColorConstructor) | undefined;