@texel/color 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texel/color",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "a minimal and modern color library",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/core.js CHANGED
@@ -64,6 +64,7 @@ export const serialize = (input, inputSpace, outputSpace = inputSpace) => {
64
64
  }
65
65
  const id = outputSpace.id;
66
66
  if (id == "srgb") {
67
+ // uses the legacy rgb() format
67
68
  const r = floatToByte(tmp3[0]);
68
69
  const g = floatToByte(tmp3[1]);
69
70
  const b = floatToByte(tmp3[2]);
@@ -72,7 +73,8 @@ export const serialize = (input, inputSpace, outputSpace = inputSpace) => {
72
73
  } else {
73
74
  const alphaSuffix = alpha === 1 ? "" : ` / ${alpha}`;
74
75
  if (id == "oklab" || id == "oklch") {
75
- return `${id}(${tmp3[0]} ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
76
+ // older versions of Safari don't support oklch with 0..1 L but do support %
77
+ return `${id}(${tmp3[0] * 100}% ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
76
78
  } else {
77
79
  return `color(${id} ${tmp3[0]} ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
78
80
  }
@@ -84,6 +86,13 @@ const stripAlpha = (coords) => {
84
86
  return coords;
85
87
  };
86
88
 
89
+ const parseFloatValue = str => parseFloat(str) || 0;
90
+
91
+ const parseColorValue = (str, is255 = false) => {
92
+ if (is255) return clamp(parseFloatValue(str) / 0xff, 0, 0xff);
93
+ else return str.includes('%') ? parseFloatValue(str) / 100 : parseFloatValue(str);
94
+ };
95
+
87
96
  export const deserialize = (input) => {
88
97
  if (typeof input !== "string") {
89
98
  throw new Error(`expected a string as input`);
@@ -105,26 +114,19 @@ export const deserialize = (input) => {
105
114
  throw new Error(`could not parse color string ${input}`);
106
115
  }
107
116
  const fn = parts[1].toLowerCase();
108
- if (/^rgba?$/i.test(fn)) {
109
- const hasAlpha = fn == "rgba";
110
- const coords = parts[2]
111
- .split(",")
112
- .map((v, i) =>
113
- i < 3 ? clamp(parseInt(v, 10) || 0, 0, 255) / 255 : parseFloat(v)
114
- );
115
- const expectedLen = hasAlpha ? 4 : 3;
116
- if (coords.length !== expectedLen) {
117
- throw new Error(
118
- `got ${fn} with incorrect number of coords, expected ${expectedLen}`
119
- );
120
- }
117
+ if (/^rgba?$/i.test(fn) && parts[2].includes(',')) {
118
+ const coords = parts[2].split(',').map((v, i) => {
119
+ return parseColorValue(v.trim(), i < 3)
120
+ });
121
121
  return {
122
122
  id: "srgb",
123
123
  coords: stripAlpha(coords),
124
124
  };
125
125
  } else {
126
126
  let id, coordsStrings;
127
- if (fn === "color") {
127
+ let div255 = false;
128
+
129
+ if (/^color$/i.test(fn)) {
128
130
  const params =
129
131
  /([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(
130
132
  parts[2]
@@ -133,8 +135,15 @@ export const deserialize = (input) => {
133
135
  throw new Error(`could not parse color() function ${input}`);
134
136
  id = params[1].toLowerCase();
135
137
  coordsStrings = params.slice(2, 6);
136
- } else if (/^(oklab|oklch)$/.test(fn)) {
137
- id = fn;
138
+ } else {
139
+ if (/^(oklab|oklch)$/i.test(fn)) {
140
+ id = fn;
141
+ } else if (/rgba?/i.test(fn)) {
142
+ id = 'srgb';
143
+ div255 = true;
144
+ } else {
145
+ throw new Error(`unknown color function ${fn}`);
146
+ }
138
147
  const params =
139
148
  /([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(parts[2]);
140
149
  if (!params)
@@ -146,7 +155,9 @@ export const deserialize = (input) => {
146
155
  coordsStrings = coordsStrings.slice(0, 3);
147
156
  }
148
157
 
149
- const coords = coordsStrings.map((f) => parseFloat(f));
158
+ const coords = coordsStrings.map((f, i) => {
159
+ return parseColorValue(f.trim(), div255 && i < 3);
160
+ });
150
161
  if (coords.length < 3 || coords.length > 4)
151
162
  throw new Error(`invalid number of coordinates`);
152
163
  return {
@@ -156,6 +167,7 @@ export const deserialize = (input) => {
156
167
  }
157
168
  }
158
169
  };
170
+
159
171
  export const parse = (input, targetSpace, out = vec3()) => {
160
172
  if (!targetSpace)
161
173
  throw new Error(`must specify a target space to parse into`);
@@ -1,3 +1,8 @@
1
+ // HSL space (hue, saturation, lightness within sRGB gamut)
2
+
3
+ // Reference:
4
+ // https://github.com/color-js/color.js/blob/cfe55d358adb6c2e23c8a897282adf42904fd32d/src/spaces/hsl.js
5
+
1
6
  import { sRGB, sRGBLinear } from "../../src/index.js";
2
7
 
3
8
  export const HSL = {
@@ -1,6 +1,8 @@
1
1
  // Lab aka CIELAB aka L*a*b* (uses a D50 WHITE_D50 point and has to be adapted)
2
2
  // refer to CSS Color Module Level 4 Spec for more details
3
- // Source code reference: Colorjs.io
3
+
4
+ // Reference:
5
+ // https://github.com/color-js/color.js/blob/cfe55d358adb6c2e23c8a897282adf42904fd32d/src/spaces/lab.js
4
6
  import { D50_to_D65_M, D65_to_D50_M } from "../../src/index.js";
5
7
 
6
8
  // K * e = 2^3 = 8
package/test/test.js CHANGED
@@ -230,61 +230,95 @@ test("should serialize", async (t) => {
230
230
  t.deepEqual(serialize([0, 0.5, 1], sRGB), "rgb(0, 128, 255)");
231
231
  t.deepEqual(serialize([0, 0.5, 1], sRGBLinear), "color(srgb-linear 0 0.5 1)");
232
232
  t.deepEqual(serialize([1, 0, 0], OKLCH, sRGB), "rgb(255, 255, 255)");
233
- t.deepEqual(serialize([1, 0, 0], OKLCH), "oklch(1 0 0)");
234
- t.deepEqual(serialize([1, 0, 0], OKLab), "oklab(1 0 0)");
233
+ t.deepEqual(serialize([1, 0, 0], OKLCH), "oklch(100% 0 0)");
234
+ t.deepEqual(serialize([1, 0, 0], OKLab), "oklab(100% 0 0)");
235
235
  t.deepEqual(
236
236
  serialize([1, 0, 0, 0.4523], OKLCH, sRGB),
237
237
  "rgba(255, 255, 255, 0.4523)"
238
238
  );
239
239
  t.deepEqual(
240
240
  serialize([1, 0, 0, 0.4523], OKLCH, OKLCH),
241
- "oklch(1 0 0 / 0.4523)"
241
+ "oklch(100% 0 0 / 0.4523)"
242
242
  );
243
- t.deepEqual(serialize([1, 0, 0, 0.4523], OKLCH), "oklch(1 0 0 / 0.4523)");
243
+ t.deepEqual(serialize([1, 0, 0, 0.4523], OKLCH), "oklch(100% 0 0 / 0.4523)");
244
244
  t.deepEqual(
245
245
  serialize([1, 0, 0, 0.4523], DisplayP3),
246
246
  "color(display-p3 1 0 0 / 0.4523)"
247
247
  );
248
248
  });
249
249
 
250
- test("should parse to a color coord", async (t) => {
251
- t.deepEqual(parse("rgb(0, 128, 255)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
252
- t.deepEqual(parse("rgba(0, 128, 255, .25)", sRGB), [
253
- 0,
254
- 128 / 0xff,
255
- 255 / 0xff,
256
- 0.25,
257
- ]);
258
- let outVec = [0, 0, 0];
259
- t.deepEqual(parse("rgba(0, 128, 255, 1)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
260
- let out;
261
- out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
262
- t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
263
- t.equal(out, outVec);
264
-
265
- // trims to 3
266
- outVec = [0, 0, 0, 0];
267
- out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
268
- t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
269
- t.equal(out, outVec);
270
-
271
- // ensures 4
272
- outVec = [0, 0, 0, 0];
273
- out = parse("rgba(0, 128, 255, 0.91)", sRGB, outVec);
274
- t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff, 0.91]);
275
- t.equal(out, outVec);
276
-
277
- t.deepEqual(
278
- serialize(parse("oklch(1 0 0)", sRGB), sRGB),
279
- "rgb(255, 255, 255)"
280
- );
281
- });
250
+ // not yet finished
251
+ // test("should parse to a color coord", async (t) => {
252
+ // t.deepEqual(parse("rgb(0, 128, 255)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
253
+ // t.deepEqual(parse("rgba(0, 128, 255, .25)", sRGB), [
254
+ // 0,
255
+ // 128 / 0xff,
256
+ // 255 / 0xff,
257
+ // 0.25,
258
+ // ]);
259
+ // let outVec = [0, 0, 0];
260
+ // t.deepEqual(parse("rgba(0, 128, 255, 1)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
261
+ // let out;
262
+ // out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
263
+ // t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
264
+ // t.equal(out, outVec);
265
+
266
+ // // trims to 3
267
+ // outVec = [0, 0, 0, 0];
268
+ // out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
269
+ // t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
270
+ // t.equal(out, outVec);
271
+
272
+ // // ensures 4
273
+ // outVec = [0, 0, 0, 0];
274
+ // out = parse("rgba(0, 128, 255, 0.91)", sRGB, outVec);
275
+ // t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff, 0.91]);
276
+ // t.equal(out, outVec);
277
+
278
+ // t.deepEqual(
279
+ // serialize(parse("oklch(1 0 0)", sRGB), sRGB),
280
+ // "rgb(255, 255, 255)"
281
+ // );
282
+ // });
282
283
 
283
284
  test("should deserialize color string information", async (t) => {
285
+
284
286
  t.deepEqual(deserialize("rgb(0, 128, 255)"), {
285
287
  coords: [0, 128 / 0xff, 255 / 0xff],
286
288
  id: "srgb",
287
289
  });
290
+ t.deepEqual(deserialize("rgba(0, 128, 255)"), {
291
+ coords: [0, 128 / 0xff, 255 / 0xff],
292
+ id: "srgb",
293
+ });
294
+ t.deepEqual(deserialize("rgba(0, 128, 255, 50%)"), {
295
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
296
+ id: "srgb",
297
+ });
298
+ t.deepEqual(deserialize("rgb(0, 128, 255, 0.5)"), {
299
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
300
+ id: "srgb",
301
+ });
302
+ t.deepEqual(deserialize("rgb(0 128 255)"), {
303
+ coords: [0, 128 / 0xff, 255 / 0xff],
304
+ id: "srgb",
305
+ });
306
+ t.deepEqual(deserialize("rgb(0 128 255 / 0.5)"), {
307
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
308
+ id: "srgb",
309
+ });
310
+ t.deepEqual(deserialize("rgb(0 128 255 / 50%)"), {
311
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
312
+ id: "srgb",
313
+ });
314
+ t.deepEqual(deserialize("rgb(0 128 255 / 0.35)"), {
315
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
316
+ id: "srgb",
317
+ });
318
+ t.deepEqual(deserialize("RGBA(0 128 255 / 0.35)"), {
319
+ coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
320
+ id: "srgb",
321
+ });
288
322
  t.deepEqual(deserialize("rgba(0, 128, 255, 0.35)"), {
289
323
  coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
290
324
  id: "srgb",
@@ -297,6 +331,14 @@ test("should deserialize color string information", async (t) => {
297
331
  id: "srgb",
298
332
  coords: [1, 0, 0.8, 0.8],
299
333
  });
334
+ t.deepEqual(deserialize("COLOR(sRGB-Linear 0 0.5 1)"), {
335
+ id: "srgb-linear",
336
+ coords: [0, 0.5, 1],
337
+ });
338
+ t.deepEqual(deserialize("COLOR(sRGB-Linear 0 50% 1)"), {
339
+ id: "srgb-linear",
340
+ coords: [0, 0.5, 1],
341
+ });
300
342
  t.deepEqual(deserialize("color(srgb-linear 0 0.5 1)"), {
301
343
  id: "srgb-linear",
302
344
  coords: [0, 0.5, 1],