@kastov/uuid-color 0.0.2 → 0.1.0

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/lib/index.d.ts CHANGED
@@ -42,7 +42,7 @@ export interface Options {
42
42
  * Attach callbacks to {@link SupportedColorNotations | supported color notations} as keys that will be called with the corresponding generated color components or code. Eliminates the need to parse the returned string if further manipulation is desired.
43
43
  *
44
44
  * @remarks
45
- * Does not affect the return value of {@link colorFromUuid | the `colorFromUuid` function}.
45
+ * Does not affect the return value of the color generation functions.
46
46
  *
47
47
  * @see {@link Receivers | the Receivers type}
48
48
  */
@@ -61,4 +61,28 @@ export interface Options {
61
61
  * @public
62
62
  */
63
63
  export declare function colorFromUuid(uuid: string, options?: Options): string;
64
+ /**
65
+ * Returns the generated color associated with the given numeric id.
66
+ *
67
+ * @remarks
68
+ * Unlike {@link colorFromUuid | colorFromUuid}, whose input bits are already uniformly
69
+ * distributed, numeric ids are typically sequential. The id is therefore run through a
70
+ * SplitMix64 bit-mixer so that consecutive ids (e.g. `1`, `2`, `3`) map to well-separated,
71
+ * visually distinct colors instead of near-identical ones. The mapping is deterministic:
72
+ * the same id always yields the same color.
73
+ *
74
+ * The id must be a non-negative integer. `number` inputs are subject to the usual
75
+ * `Number.MAX_SAFE_INTEGER` limit; pass a `bigint` for larger ids.
76
+ *
77
+ * @param id - The numeric id (a `number` or `bigint`) for which to generate a color
78
+ * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
79
+ * @returns The generated color as a CSS `<color>` notation string
80
+ *
81
+ * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
82
+ * This exception is thrown if the input id is not a non-negative integer.
83
+ *
84
+ * @public
85
+ */
86
+ export declare function colorFromId(id: number | bigint, options?: Options): string;
64
87
  export {};
88
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,KAAK,uBAAuB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAErD,KAAK,SAAS,GAAG;IACb,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtF,CAAC;AAEF,MAAM,WAAW,OAAO;IACpB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IAEd;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAEjC;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB;AA4FD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,OAAY,GAAG,MAAM,CAEzE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,GAAE,OAAY,GAAG,MAAM,CAe9E"}
package/lib/index.js CHANGED
@@ -1,57 +1,43 @@
1
- "use strict";
2
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
3
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
4
- if (ar || !(i in from)) {
5
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
6
- ar[i] = from[i];
7
- }
8
- }
9
- return to.concat(ar || Array.prototype.slice.call(from));
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.colorFromUuid = colorFromUuid;
13
- var convert = require("color-convert");
14
- var DEFAULT_COLOR_FORMAT = 'hex';
15
- var DEFAULT_IS_RAW = false;
1
+ import convert from 'color-convert';
2
+ const DEFAULT_COLOR_FORMAT = 'hex';
3
+ const DEFAULT_IS_RAW = false;
4
+ const U64_MASK = 0xffffffffffffffffn;
5
+ const MIX_C1 = 0xbf58476d1ce4e5b9n;
6
+ const MIX_C2 = 0x94d049bb133111ebn;
16
7
  function uuidToBigInt(uuid) {
17
8
  return BigInt('0x' + uuid.replace(/-/g, ''));
18
9
  }
19
10
  /**
20
- * Returns the generated color associated with the given uuid.
21
- *
22
- * @param uuid - The uuid for which to generate a color
23
- * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
24
- * @returns The generated color as a CSS `<color>` notation string
25
- *
26
- * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
27
- * This exception is thrown if the input uuid string is not a valid UUID.
28
- *
29
- * @public
11
+ * Mixes a non-negative integer into a well-distributed 64-bit value using the
12
+ * SplitMix64 finalizer, so that sequential ids (1, 2, 3, …) map to colors that
13
+ * are far apart rather than nearly identical.
30
14
  */
31
- function colorFromUuid(uuid, options) {
32
- if (options === void 0) { options = {}; }
33
- var encodedUuid = uuidToBigInt(uuid);
34
- var colorCode = Number(encodedUuid % BigInt(0x1000000));
35
- var red = colorCode >> 16;
36
- var green = (colorCode >> 8) & 0xff;
37
- var blue = colorCode & 0xff;
38
- var receivers = {};
39
- if (options.hasOwnProperty('receivers')) {
40
- ['rgb', 'hsl', 'hex'].forEach(function (format) {
41
- if (options.receivers.hasOwnProperty(format)) {
42
- receivers[format] = options.receivers[format]; // link to callbacks
43
- }
44
- });
45
- }
46
- var isRaw = DEFAULT_IS_RAW;
47
- if (options.hasOwnProperty('raw')) {
48
- isRaw = options.raw;
49
- }
50
- var alpha;
51
- if (options.hasOwnProperty('alpha')) {
52
- alpha = Math.min(Math.max(options.alpha, 0), 1); // clamp to [0; 1]
53
- }
54
- if ('rgb' in receivers) {
15
+ function mixId(id) {
16
+ let z = id & U64_MASK;
17
+ z = ((z ^ (z >> 30n)) * MIX_C1) & U64_MASK;
18
+ z = ((z ^ (z >> 27n)) * MIX_C2) & U64_MASK;
19
+ z = z ^ (z >> 31n);
20
+ return z & U64_MASK;
21
+ }
22
+ /** Converts a clamped alpha value in [0; 1] to its hexadecimal channel representation. */
23
+ function alphaToHex(alpha) {
24
+ return Math.floor(alpha * 255).toString(16);
25
+ }
26
+ /**
27
+ * Returns the generated color associated with the given encoded value by taking
28
+ * its lowest 24 bits as the RGB channels and formatting them per {@link Options}.
29
+ */
30
+ function colorFromBigInt(encoded, options) {
31
+ const colorCode = Number(encoded % 0x1000000n);
32
+ const red = colorCode >> 16;
33
+ const green = (colorCode >> 8) & 0xff;
34
+ const blue = colorCode & 0xff;
35
+ const isRaw = options.raw ?? DEFAULT_IS_RAW;
36
+ // clamp to [0; 1]
37
+ const alpha = options.alpha === undefined ? undefined : Math.min(Math.max(options.alpha, 0), 1);
38
+ const toHsl = () => isRaw ? convert.rgb.hsl.raw(red, green, blue) : convert.rgb.hsl(red, green, blue);
39
+ const receivers = options.receivers;
40
+ if (receivers?.rgb) {
55
41
  if (alpha === undefined) {
56
42
  receivers.rgb(red, green, blue);
57
43
  }
@@ -59,47 +45,89 @@ function colorFromUuid(uuid, options) {
59
45
  receivers.rgb(red, green, blue, alpha);
60
46
  }
61
47
  }
62
- if ('hsl' in receivers) {
63
- var hsl = isRaw
64
- ? convert.default.rgb.hsl.raw(red, green, blue)
65
- : convert.default.rgb.hsl(red, green, blue);
66
- if (alpha === undefined) {
67
- receivers.hsl.apply(receivers, hsl);
68
- }
69
- else {
70
- receivers.hsl.apply(receivers, __spreadArray(__spreadArray([], hsl, true), [alpha], false));
71
- }
72
- }
73
- if ('hex' in receivers) {
74
- var hexColorCode = convert.default.rgb.hex(red, green, blue).toLowerCase();
48
+ if (receivers?.hsl) {
49
+ const [hue, saturation, lightness] = toHsl();
75
50
  if (alpha === undefined) {
76
- receivers.hex(hexColorCode);
51
+ receivers.hsl(hue, saturation, lightness);
77
52
  }
78
53
  else {
79
- var hexAlphaCode = Math.floor(alpha * 255).toString(16);
80
- receivers.hex(hexColorCode + hexAlphaCode);
54
+ receivers.hsl(hue, saturation, lightness, alpha);
81
55
  }
82
56
  }
83
- var format = DEFAULT_COLOR_FORMAT;
84
- if (options.hasOwnProperty('format')) {
85
- format = options.format;
57
+ if (receivers?.hex) {
58
+ const hexColorCode = convert.rgb.hex(red, green, blue).toLowerCase();
59
+ receivers.hex(alpha === undefined ? hexColorCode : hexColorCode + alphaToHex(alpha));
86
60
  }
61
+ const format = options.format ?? DEFAULT_COLOR_FORMAT;
87
62
  switch (format) {
88
63
  case 'rgb':
89
64
  return alpha === undefined
90
- ? "rgb(".concat(red, ", ").concat(green, ", ").concat(blue, ")")
91
- : "rgb(".concat(red, ", ").concat(green, ", ").concat(blue, ", ").concat(alpha, ")");
92
- case 'hsl':
93
- var hsl = isRaw
94
- ? convert.default.rgb.hsl.raw(red, green, blue)
95
- : convert.default.rgb.hsl(red, green, blue);
65
+ ? `rgb(${red}, ${green}, ${blue})`
66
+ : `rgb(${red}, ${green}, ${blue}, ${alpha})`;
67
+ case 'hsl': {
68
+ const [hue, saturation, lightness] = toHsl();
96
69
  return alpha === undefined
97
- ? "hsl(".concat(hsl[0], ", ").concat(hsl[1], "%, ").concat(hsl[2], "%)")
98
- : "hsl(".concat(hsl[0], ", ").concat(hsl[1], "%, ").concat(hsl[2], "%, ").concat(alpha, ")");
99
- default: // don't error
70
+ ? `hsl(${hue}, ${saturation}%, ${lightness}%)`
71
+ : `hsl(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;
72
+ }
100
73
  case 'hex':
101
- var hexColorCode = convert.default.rgb.hex(red, green, blue).toLowerCase();
102
- var hexAlphaCode = Math.floor(alpha * 255).toString(16);
103
- return alpha === undefined ? "#".concat(hexColorCode) : "#".concat(hexColorCode).concat(hexAlphaCode);
74
+ default: {
75
+ const hexColorCode = convert.rgb.hex(red, green, blue).toLowerCase();
76
+ return alpha === undefined ? `#${hexColorCode}` : `#${hexColorCode}${alphaToHex(alpha)}`;
77
+ }
78
+ }
79
+ }
80
+ /**
81
+ * Returns the generated color associated with the given uuid.
82
+ *
83
+ * @param uuid - The uuid for which to generate a color
84
+ * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
85
+ * @returns The generated color as a CSS `<color>` notation string
86
+ *
87
+ * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
88
+ * This exception is thrown if the input uuid string is not a valid UUID.
89
+ *
90
+ * @public
91
+ */
92
+ export function colorFromUuid(uuid, options = {}) {
93
+ return colorFromBigInt(uuidToBigInt(uuid), options);
94
+ }
95
+ /**
96
+ * Returns the generated color associated with the given numeric id.
97
+ *
98
+ * @remarks
99
+ * Unlike {@link colorFromUuid | colorFromUuid}, whose input bits are already uniformly
100
+ * distributed, numeric ids are typically sequential. The id is therefore run through a
101
+ * SplitMix64 bit-mixer so that consecutive ids (e.g. `1`, `2`, `3`) map to well-separated,
102
+ * visually distinct colors instead of near-identical ones. The mapping is deterministic:
103
+ * the same id always yields the same color.
104
+ *
105
+ * The id must be a non-negative integer. `number` inputs are subject to the usual
106
+ * `Number.MAX_SAFE_INTEGER` limit; pass a `bigint` for larger ids.
107
+ *
108
+ * @param id - The numeric id (a `number` or `bigint`) for which to generate a color
109
+ * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
110
+ * @returns The generated color as a CSS `<color>` notation string
111
+ *
112
+ * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
113
+ * This exception is thrown if the input id is not a non-negative integer.
114
+ *
115
+ * @public
116
+ */
117
+ export function colorFromId(id, options = {}) {
118
+ let encoded;
119
+ if (typeof id === 'bigint') {
120
+ encoded = id;
121
+ }
122
+ else {
123
+ if (!Number.isInteger(id)) {
124
+ throw new Error(`Invalid id: expected an integer, received ${id}`);
125
+ }
126
+ encoded = BigInt(id);
127
+ }
128
+ if (encoded < 0n) {
129
+ throw new Error(`Invalid id: expected a non-negative integer, received ${id}`);
104
130
  }
131
+ return colorFromBigInt(mixId(encoded), options);
105
132
  }
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,24 +1,32 @@
1
1
  {
2
2
  "name": "@kastov/uuid-color",
3
- "version": "0.0.2",
4
- "description": "A lightweight package to generate unique and uniformly sampled colors from UUIDs.",
5
- "main": "lib/index.js",
3
+ "version": "0.1.0",
4
+ "description": "A lightweight package to generate unique and uniformly sampled colors from UUIDs and numeric ids.",
5
+ "type": "module",
6
+ "main": "./lib/index.js",
7
+ "types": "./lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "lib",
16
+ "src"
17
+ ],
18
+ "engines": {
19
+ "node": ">=24"
20
+ },
6
21
  "scripts": {
7
22
  "build": "tsc",
8
- "prepare": "rm -rf lib && NODE_ENV=production tsc"
23
+ "clean": "rm -rf lib",
24
+ "prepare": "npm run clean && tsc"
9
25
  },
10
26
  "repository": {
11
27
  "type": "git",
12
28
  "url": "git+https://github.com/kastov/uuid-color.git"
13
29
  },
14
- "keywords": [
15
- "uuid",
16
- "color",
17
- "colour",
18
- "unique",
19
- "hash",
20
- "uuid-v4"
21
- ],
22
30
  "author": "Louca Dufault",
23
31
  "license": "ISC",
24
32
  "bugs": {
@@ -32,4 +40,4 @@
32
40
  "color-convert": "^3.1.3"
33
41
  },
34
42
  "sideEffects": false
35
- }
43
+ }
package/src/index.ts ADDED
@@ -0,0 +1,202 @@
1
+ import convert from 'color-convert';
2
+
3
+ type SupportedColorNotations = 'hex' | 'rgb' | 'hsl';
4
+
5
+ type Receivers = {
6
+ hex?: (hexString: string) => void;
7
+ rgb?: (red: number, green: number, blue: number, alpha?: number) => void;
8
+ hsl?: (hue: number, saturation: number, lightness: number, alpha?: number) => void;
9
+ };
10
+
11
+ export interface Options {
12
+ /**
13
+ * Determines whether to skip rounding the generated color components.
14
+ *
15
+ * @remarks
16
+ * Only applies when the specified format is `"hsl"`, as this is the only output format that involves a lossy conversion (from RGB model components).
17
+ *
18
+ * @defaultValue `false`
19
+ */
20
+ raw?: boolean;
21
+
22
+ /**
23
+ * Determines the alpha of the generated color as a number between 0 and 1. Passed through to the final generated color without any involvement in the generation process.
24
+ *
25
+ * @remarks
26
+ * Specifying any value (even if it is equal to or greater than 1) will cause the function to return the generated color as a notation string including the alpha component (see the alpha variants in {@link Options.format | Options.format}), and cause the alpha to be included in the parameter(s) of the call to the supplied {@link Receivers} value(s).
27
+ * Values are not validated, but are clamped to between 0 and 1.
28
+ *
29
+ * @defaultValue `1`
30
+ */
31
+ alpha?: number;
32
+
33
+ /**
34
+ * Determines the output format of the generated color.
35
+ *
36
+ * hex: #rrggbb[aa]
37
+ * rgb: rgb[a](R, G, B[, A])
38
+ * hsl: hsl[a](H, S, L[, A])
39
+ * @see { @link https://developer.mozilla.org/en-US/docs/Web/CSS/color_value | \<color\> }
40
+ *
41
+ * @remarks
42
+ * Colors are returned as strings in the CSS <color> data type hexadecimal or comma-separated functional notation corresponding to the specified format.
43
+ *
44
+ * @defaultValue `"hex"`
45
+ */
46
+ format?: SupportedColorNotations;
47
+
48
+ /**
49
+ * Attach callbacks to {@link SupportedColorNotations | supported color notations} as keys that will be called with the corresponding generated color components or code. Eliminates the need to parse the returned string if further manipulation is desired.
50
+ *
51
+ * @remarks
52
+ * Does not affect the return value of the color generation functions.
53
+ *
54
+ * @see {@link Receivers | the Receivers type}
55
+ */
56
+ receivers?: Receivers;
57
+ }
58
+
59
+ const DEFAULT_COLOR_FORMAT: SupportedColorNotations = 'hex';
60
+ const DEFAULT_IS_RAW = false;
61
+
62
+ const U64_MASK = 0xffffffffffffffffn;
63
+ const MIX_C1 = 0xbf58476d1ce4e5b9n;
64
+ const MIX_C2 = 0x94d049bb133111ebn;
65
+
66
+ function uuidToBigInt(uuid: string): bigint {
67
+ return BigInt('0x' + uuid.replace(/-/g, ''));
68
+ }
69
+
70
+ /**
71
+ * Mixes a non-negative integer into a well-distributed 64-bit value using the
72
+ * SplitMix64 finalizer, so that sequential ids (1, 2, 3, …) map to colors that
73
+ * are far apart rather than nearly identical.
74
+ */
75
+ function mixId(id: bigint): bigint {
76
+ let z = id & U64_MASK;
77
+ z = ((z ^ (z >> 30n)) * MIX_C1) & U64_MASK;
78
+ z = ((z ^ (z >> 27n)) * MIX_C2) & U64_MASK;
79
+ z = z ^ (z >> 31n);
80
+ return z & U64_MASK;
81
+ }
82
+
83
+ /** Converts a clamped alpha value in [0; 1] to its hexadecimal channel representation. */
84
+ function alphaToHex(alpha: number): string {
85
+ return Math.floor(alpha * 255).toString(16);
86
+ }
87
+
88
+ /**
89
+ * Returns the generated color associated with the given encoded value by taking
90
+ * its lowest 24 bits as the RGB channels and formatting them per {@link Options}.
91
+ */
92
+ function colorFromBigInt(encoded: bigint, options: Options): string {
93
+ const colorCode = Number(encoded % 0x1000000n);
94
+ const red = colorCode >> 16;
95
+ const green = (colorCode >> 8) & 0xff;
96
+ const blue = colorCode & 0xff;
97
+
98
+ const isRaw = options.raw ?? DEFAULT_IS_RAW;
99
+
100
+ // clamp to [0; 1]
101
+ const alpha =
102
+ options.alpha === undefined ? undefined : Math.min(Math.max(options.alpha, 0), 1);
103
+
104
+ const toHsl = () =>
105
+ isRaw ? convert.rgb.hsl.raw(red, green, blue) : convert.rgb.hsl(red, green, blue);
106
+
107
+ const receivers = options.receivers;
108
+ if (receivers?.rgb) {
109
+ if (alpha === undefined) {
110
+ receivers.rgb(red, green, blue);
111
+ } else {
112
+ receivers.rgb(red, green, blue, alpha);
113
+ }
114
+ }
115
+ if (receivers?.hsl) {
116
+ const [hue, saturation, lightness] = toHsl();
117
+ if (alpha === undefined) {
118
+ receivers.hsl(hue, saturation, lightness);
119
+ } else {
120
+ receivers.hsl(hue, saturation, lightness, alpha);
121
+ }
122
+ }
123
+ if (receivers?.hex) {
124
+ const hexColorCode = convert.rgb.hex(red, green, blue).toLowerCase();
125
+ receivers.hex(alpha === undefined ? hexColorCode : hexColorCode + alphaToHex(alpha));
126
+ }
127
+
128
+ const format = options.format ?? DEFAULT_COLOR_FORMAT;
129
+
130
+ switch (format) {
131
+ case 'rgb':
132
+ return alpha === undefined
133
+ ? `rgb(${red}, ${green}, ${blue})`
134
+ : `rgb(${red}, ${green}, ${blue}, ${alpha})`;
135
+ case 'hsl': {
136
+ const [hue, saturation, lightness] = toHsl();
137
+ return alpha === undefined
138
+ ? `hsl(${hue}, ${saturation}%, ${lightness}%)`
139
+ : `hsl(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;
140
+ }
141
+ case 'hex':
142
+ default: {
143
+ const hexColorCode = convert.rgb.hex(red, green, blue).toLowerCase();
144
+ return alpha === undefined ? `#${hexColorCode}` : `#${hexColorCode}${alphaToHex(alpha)}`;
145
+ }
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Returns the generated color associated with the given uuid.
151
+ *
152
+ * @param uuid - The uuid for which to generate a color
153
+ * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
154
+ * @returns The generated color as a CSS `<color>` notation string
155
+ *
156
+ * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
157
+ * This exception is thrown if the input uuid string is not a valid UUID.
158
+ *
159
+ * @public
160
+ */
161
+ export function colorFromUuid(uuid: string, options: Options = {}): string {
162
+ return colorFromBigInt(uuidToBigInt(uuid), options);
163
+ }
164
+
165
+ /**
166
+ * Returns the generated color associated with the given numeric id.
167
+ *
168
+ * @remarks
169
+ * Unlike {@link colorFromUuid | colorFromUuid}, whose input bits are already uniformly
170
+ * distributed, numeric ids are typically sequential. The id is therefore run through a
171
+ * SplitMix64 bit-mixer so that consecutive ids (e.g. `1`, `2`, `3`) map to well-separated,
172
+ * visually distinct colors instead of near-identical ones. The mapping is deterministic:
173
+ * the same id always yields the same color.
174
+ *
175
+ * The id must be a non-negative integer. `number` inputs are subject to the usual
176
+ * `Number.MAX_SAFE_INTEGER` limit; pass a `bigint` for larger ids.
177
+ *
178
+ * @param id - The numeric id (a `number` or `bigint`) for which to generate a color
179
+ * @param options - An optional object to configure the color generation, and attach callbacks that directly receive the generated color code or components in various formats
180
+ * @returns The generated color as a CSS `<color>` notation string
181
+ *
182
+ * @throws {@link https://developer.mozilla.org/en-US/docs/web/javascript/reference/global_objects/error | Error}
183
+ * This exception is thrown if the input id is not a non-negative integer.
184
+ *
185
+ * @public
186
+ */
187
+ export function colorFromId(id: number | bigint, options: Options = {}): string {
188
+ let encoded: bigint;
189
+ if (typeof id === 'bigint') {
190
+ encoded = id;
191
+ } else {
192
+ if (!Number.isInteger(id)) {
193
+ throw new Error(`Invalid id: expected an integer, received ${id}`);
194
+ }
195
+ encoded = BigInt(id);
196
+ }
197
+ if (encoded < 0n) {
198
+ throw new Error(`Invalid id: expected a non-negative integer, received ${id}`);
199
+ }
200
+
201
+ return colorFromBigInt(mixId(encoded), options);
202
+ }
package/.prettierrc DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "bracketSpacing": true,
3
- "tabWidth": 4,
4
- "printWidth": 100,
5
- "singleQuote": true,
6
- "trailingComma": "all",
7
- "overrides": [
8
- {
9
- "files": ["*.js", "*.jsx", "*.ts", "*.tsx"],
10
- "options": {
11
- "parser": "typescript"
12
- }
13
- },
14
- {
15
- "files": ["*.md", "*.json", "*.yaml", "*.yml"],
16
- "options": {
17
- "tabWidth": 2
18
- }
19
- }
20
- ]
21
- }