@ui0/util 0.0.0-stage → 0.0.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/dist/index.d.ts +771 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2164 -0
- package/dist/index.js.map +1 -0
- package/package.json +33 -4
- package/README.md +0 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,2164 @@
|
|
|
1
|
+
// src/array/array.ts
|
|
2
|
+
/**
|
|
3
|
+
* Omit values from arr based on values list and return new array with this values
|
|
4
|
+
* @param {T[]} arr
|
|
5
|
+
* @param {T[]} omitted
|
|
6
|
+
* @return {T[]}
|
|
7
|
+
*/
|
|
8
|
+
function arrayOmit(arr, omitted) {
|
|
9
|
+
const _omitted = omitted || [];
|
|
10
|
+
const copy = arr ? [...arr] : [];
|
|
11
|
+
const out = copy || [];
|
|
12
|
+
_omitted.forEach((o) => {
|
|
13
|
+
const pos = copy.indexOf(o);
|
|
14
|
+
if (pos > -1) copy.splice(pos, 1);
|
|
15
|
+
});
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Omit values from arr based on values list
|
|
20
|
+
* Mutable version of arrayOmit
|
|
21
|
+
* @param {T[]} arr
|
|
22
|
+
* @param {T[]} omitted
|
|
23
|
+
|
|
24
|
+
*/
|
|
25
|
+
function arrayOmitArray(arr = [], omitted) {
|
|
26
|
+
const _omitted = omitted || [];
|
|
27
|
+
const copy = [...arr];
|
|
28
|
+
_omitted.forEach((o) => {
|
|
29
|
+
const pos = copy.indexOf(o);
|
|
30
|
+
if (pos > -1) copy.splice(pos, 1);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Make this array items unique
|
|
35
|
+
* [1,2,3,3,2,5] => [1,2,3,5]
|
|
36
|
+
* @param {T[]} arr
|
|
37
|
+
* @return {T[]}
|
|
38
|
+
*/
|
|
39
|
+
function toUnique(arr) {
|
|
40
|
+
return [...new Set(arr)];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Return the last element of an array.
|
|
44
|
+
*
|
|
45
|
+
* @param arr - the array to read
|
|
46
|
+
* @returns the last item, or `undefined` for an empty array
|
|
47
|
+
*/
|
|
48
|
+
function getLastItemFromArray(arr) {
|
|
49
|
+
return arr[arr.length - 1];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Pick element with offset from item's position in arr
|
|
53
|
+
* Next item - +1
|
|
54
|
+
* Prev item - -1
|
|
55
|
+
* returns same item if not found
|
|
56
|
+
* @param {T[]} arr
|
|
57
|
+
* @param {T} item
|
|
58
|
+
* @param {number} offset
|
|
59
|
+
* @return {T}
|
|
60
|
+
*/
|
|
61
|
+
function arrayPickFromItemOffset(arr, item, offset) {
|
|
62
|
+
if (arr.length < 2) return item;
|
|
63
|
+
if (offset < 0 && arr[0] === item) return item;
|
|
64
|
+
if (offset > 0 && arr[arr.length - offset] === item) return item;
|
|
65
|
+
for (let i = 1; i < arr.length; i++) if (arr[i] === item) return arr[i + offset];
|
|
66
|
+
return item;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Pick the element `offset` positions after `item`.
|
|
70
|
+
*
|
|
71
|
+
* Thin wrapper over {@link arrayPickFromItemOffset} with a positive offset;
|
|
72
|
+
* out-of-range offsets and unknown items return `item` itself.
|
|
73
|
+
*
|
|
74
|
+
* @param arr - the array to walk
|
|
75
|
+
* @param item - the anchor element
|
|
76
|
+
* @param offset - how many positions to move forward, defaults to `1`
|
|
77
|
+
* @returns the neighbor element, or `item`
|
|
78
|
+
*/
|
|
79
|
+
function arrayPickNext(arr, item, offset = 1) {
|
|
80
|
+
return arrayPickFromItemOffset(arr, item, offset);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Pick the element `offset` positions before `item`.
|
|
84
|
+
*
|
|
85
|
+
* Thin wrapper over {@link arrayPickFromItemOffset} with a negative offset;
|
|
86
|
+
* out-of-range offsets and unknown items return `item` itself.
|
|
87
|
+
*
|
|
88
|
+
* @param arr - the array to walk
|
|
89
|
+
* @param item - the anchor element
|
|
90
|
+
* @param offset - how many positions to move back, defaults to `1`
|
|
91
|
+
* @returns the neighbor element, or `item`
|
|
92
|
+
*/
|
|
93
|
+
function arrayPickPrev(arr, item, offset = 1) {
|
|
94
|
+
return arrayPickFromItemOffset(arr, item, -offset);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* get item from array when iterating over length
|
|
98
|
+
* getItemByOverflowIndex([0,1,2,3], 8) // => 1 ([0,1,2,3], 6) => 3
|
|
99
|
+
*
|
|
100
|
+
* @param {T[]} arr
|
|
101
|
+
* @param {number} index
|
|
102
|
+
* @param offset takes inc/dec by offset value,
|
|
103
|
+
* @default 1
|
|
104
|
+
* @return {T}
|
|
105
|
+
*/
|
|
106
|
+
function getItemByOverflowIndex(arr, index, offset = 1) {
|
|
107
|
+
if (index > arr.length - offset) return arr[index % arr.length];
|
|
108
|
+
return arr[Math.abs(index)];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Coerce every element of an array to a number.
|
|
112
|
+
*
|
|
113
|
+
* Nullish entries become `0`; non-numeric strings coerce to `NaN`.
|
|
114
|
+
*
|
|
115
|
+
* @param arr - the source array
|
|
116
|
+
* @returns a parallel array of numbers
|
|
117
|
+
*/
|
|
118
|
+
function arrayToNumberArray(arr) {
|
|
119
|
+
return arr.reduce((prev, next) => [...prev, +(next ?? 0)], []);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Compare two arrays item by item, recursing into nested arrays.
|
|
123
|
+
*
|
|
124
|
+
* @param a - the first array
|
|
125
|
+
* @param b - the second array
|
|
126
|
+
* @returns `true` when the lengths match and every pair is `===` (nested
|
|
127
|
+
* arrays compared recursively)
|
|
128
|
+
*/
|
|
129
|
+
function arrayIsEqual(a, b) {
|
|
130
|
+
if (a === b) return true;
|
|
131
|
+
if (a.length !== b.length) return false;
|
|
132
|
+
return a.every((item, index) => {
|
|
133
|
+
const other = b[index];
|
|
134
|
+
return Array.isArray(item) && Array.isArray(other) ? arrayIsEqual(item, other) : item === other;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* insert `items` to array at specific `index`
|
|
139
|
+
* delete `deleteCount` items from `index`
|
|
140
|
+
* @param arr
|
|
141
|
+
* @param index
|
|
142
|
+
* @param items
|
|
143
|
+
* @param deleteCount
|
|
144
|
+
*/
|
|
145
|
+
function arrayInsertAtIndex(arr, index, items, deleteCount = 0) {
|
|
146
|
+
const safeIndex = Math.max(0, Math.min(index, arr.length));
|
|
147
|
+
arr.splice(safeIndex, deleteCount, ...items);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Type guard: value is an array.
|
|
151
|
+
*
|
|
152
|
+
* @param value - the value to check
|
|
153
|
+
* @returns `true` when `value` is an array
|
|
154
|
+
*/
|
|
155
|
+
function isArray(value) {
|
|
156
|
+
return Array.isArray(value);
|
|
157
|
+
}
|
|
158
|
+
// src/object/objectPickKeys.ts
|
|
159
|
+
/**
|
|
160
|
+
* Pick keys from object to new object with key-value
|
|
161
|
+
*
|
|
162
|
+
* @param {T} obj
|
|
163
|
+
* @param keys
|
|
164
|
+
* @example objectPickKeys({key1: true, key2: 0, key3: 'str'}, 'key2', 'key3') // {key2: 0, key3: 'str'}
|
|
165
|
+
* @return {{[P in keyof T]?: any}}
|
|
166
|
+
*/
|
|
167
|
+
function objectPickKeys(obj, ...keys) {
|
|
168
|
+
return keys.reduce((acc, key) => {
|
|
169
|
+
if (obj.hasOwnProperty(key)) acc[key] = obj[key];
|
|
170
|
+
return acc;
|
|
171
|
+
}, {});
|
|
172
|
+
}
|
|
173
|
+
// src/object/objectOmitKeys.ts
|
|
174
|
+
/**
|
|
175
|
+
* Opposite to objectPickKeys
|
|
176
|
+
* Return new object without selected keys
|
|
177
|
+
* @param {T} obj
|
|
178
|
+
* @param {P} args
|
|
179
|
+
* @example objectOmitKeys({key1: true, key2: 0, key3: 'str'}, 'key2', 'key3') // {key1: true}
|
|
180
|
+
* @return {{[P in keyof T]?: any}}
|
|
181
|
+
*/
|
|
182
|
+
function objectOmitKeys(obj, ...args) {
|
|
183
|
+
const out = Object.assign({}, obj);
|
|
184
|
+
args.forEach((arg) => {
|
|
185
|
+
delete out[arg];
|
|
186
|
+
});
|
|
187
|
+
return out;
|
|
188
|
+
}
|
|
189
|
+
// src/primitives.ts
|
|
190
|
+
/**
|
|
191
|
+
* Replace `undefined` with `false`, keeping any other value as-is.
|
|
192
|
+
*
|
|
193
|
+
* @param x - the value to guard
|
|
194
|
+
* @returns `x`, or `false` when `x` is `undefined`
|
|
195
|
+
*/
|
|
196
|
+
function notUndefined(x) {
|
|
197
|
+
return x !== void 0 ? x : false;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Type guard: value is a function (anything callable).
|
|
201
|
+
*
|
|
202
|
+
* @param fn - the value to check
|
|
203
|
+
* @returns `true` when `fn` is callable
|
|
204
|
+
*/
|
|
205
|
+
const isFunction = (fn) => typeof fn === "function";
|
|
206
|
+
/**
|
|
207
|
+
* Type guard: value is a symbol.
|
|
208
|
+
*
|
|
209
|
+
* @param s - the value to check
|
|
210
|
+
* @returns `true` when `s` is a symbol
|
|
211
|
+
*/
|
|
212
|
+
const isSymbol = (s) => typeof s === "symbol";
|
|
213
|
+
// src/object/isObject.ts
|
|
214
|
+
/**
|
|
215
|
+
* Type guard: value is an object (not `null`, not an array).
|
|
216
|
+
*
|
|
217
|
+
* @param v - the value to check
|
|
218
|
+
* @returns `true` for non-null, non-array objects
|
|
219
|
+
*/
|
|
220
|
+
function isObject(v) {
|
|
221
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
222
|
+
}
|
|
223
|
+
// src/object/objectMerge.ts
|
|
224
|
+
/**
|
|
225
|
+
* Objects customizable deep merging
|
|
226
|
+
* @param {Record<string, any>} to
|
|
227
|
+
* @param {Record<string, any>} from
|
|
228
|
+
* @param {{replaceNonObjectIfObject: boolean}} options
|
|
229
|
+
* @return {Record<string, any>}
|
|
230
|
+
*/
|
|
231
|
+
function objectMerge(to, from, options = {}) {
|
|
232
|
+
const result = { ...to };
|
|
233
|
+
const { overwrite = true, replaceNonObjectIfObject = false, mergeFunctions = false } = options;
|
|
234
|
+
if (!isObject(to) || !isObject(from)) return overwrite ? {
|
|
235
|
+
...result,
|
|
236
|
+
...from
|
|
237
|
+
} : result;
|
|
238
|
+
for (const key in from) if (key in to) {
|
|
239
|
+
if (mergeFunctions && isFunction(from[key])) result[key] = isFunction(to[key]) ? (...arg) => {
|
|
240
|
+
to[key](...arg);
|
|
241
|
+
return from[key](...arg);
|
|
242
|
+
} : overwrite ? from[key] : to[key];
|
|
243
|
+
else if (isObject(from[key]) && isObject(to[key])) result[key] = objectMerge(to[key], from[key], options);
|
|
244
|
+
else if (replaceNonObjectIfObject && isObject(from[key])) result[key] = from[key];
|
|
245
|
+
else result[key] = overwrite ? from[key] : to[key];
|
|
246
|
+
} else result[key] = from[key];
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
// src/string/string.ts
|
|
250
|
+
const hyphenateExpr = /\B([A-Z])/g;
|
|
251
|
+
/**
|
|
252
|
+
* Convert CaseWordAAA to 'case-word-a-a-a'
|
|
253
|
+
* @param {string} str
|
|
254
|
+
* @return {string}
|
|
255
|
+
*/
|
|
256
|
+
function hyphenate(str) {
|
|
257
|
+
return str.replace(hyphenateExpr, "-$1").toLowerCase();
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Converts `is-name` to 'isName'
|
|
261
|
+
* @param s
|
|
262
|
+
*/
|
|
263
|
+
function toCamelCase(s = "") {
|
|
264
|
+
return s.replace(/-./g, (x) => x[1].toUpperCase());
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Converts string to kebab-case (including spaces).
|
|
268
|
+
* @example
|
|
269
|
+
* toKebabCase('Hello World') => 'hello-world'
|
|
270
|
+
* toKebabCase('camelCase') => 'camel-case'
|
|
271
|
+
*/
|
|
272
|
+
function toKebabCase(str) {
|
|
273
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase().replace(/^-+|-+$/g, "");
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Convert a kebab-case string to PascalCase (`'is-name'` → `'IsName'`).
|
|
277
|
+
*
|
|
278
|
+
* @param s - the kebab-case input
|
|
279
|
+
* @returns the PascalCase string
|
|
280
|
+
*/
|
|
281
|
+
function toPascalCase(s = "") {
|
|
282
|
+
const r = s.replace(/-./g, (x) => x[1].toUpperCase());
|
|
283
|
+
return r ? r[0].toUpperCase() + r.substring(1) : r;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Kebab-case a string, collapsing uppercase runs as whole words.
|
|
287
|
+
*
|
|
288
|
+
* Unlike {@link toKebabCase} (which only splits on lower→upper edges),
|
|
289
|
+
* consecutive capitals collapse together: `'FOOBar'` → `'foo-bar'`,
|
|
290
|
+
* `'XMLHttpRequest'` → `'xml-http-request'`.
|
|
291
|
+
*
|
|
292
|
+
* @param str - the string to kebab-case
|
|
293
|
+
* @returns the kebab-cased string
|
|
294
|
+
*/
|
|
295
|
+
function kebabize(str) {
|
|
296
|
+
return str.replace(/[A-Z]+(?![a-z])|[A-Z]/g, (s, ofs) => (ofs ? "-" : "") + s.toLowerCase());
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Remove vowels form word
|
|
300
|
+
* @param {string} str
|
|
301
|
+
* @example removeVowels('hello world') // hll wrld
|
|
302
|
+
* @return {string}
|
|
303
|
+
*/
|
|
304
|
+
function removeVowels(str) {
|
|
305
|
+
return str.replace(/[aoeyui]/g, "");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* returns filename from a path
|
|
309
|
+
* @param {string} name
|
|
310
|
+
* @example extractFileName('/some/path/to/file.txt') // file.txt
|
|
311
|
+
* @return {string | undefined}
|
|
312
|
+
*/
|
|
313
|
+
function extractFileName(name) {
|
|
314
|
+
return name.split(/(\\|\/)/g).pop();
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Return the directory part of a path — everything before the file name.
|
|
318
|
+
*
|
|
319
|
+
* @param file - the path to split
|
|
320
|
+
* @returns the trailing-slash directory, or `''` when the path has none
|
|
321
|
+
*/
|
|
322
|
+
function extractFileDir(file) {
|
|
323
|
+
return file.replace(extractFileName(file) || "", "");
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* process pack/icon?value=123 to {params: {value: 123}, res: ['pack', 'icon']}
|
|
327
|
+
* @param query
|
|
328
|
+
*/
|
|
329
|
+
function parseQueryString(query) {
|
|
330
|
+
if (!query) return {
|
|
331
|
+
params: {},
|
|
332
|
+
res: [""]
|
|
333
|
+
};
|
|
334
|
+
const paramsOut = {};
|
|
335
|
+
const [res, params] = query.split("?");
|
|
336
|
+
const resources = res.split("/").toReversed();
|
|
337
|
+
if (params) params.split("&").forEach((item) => {
|
|
338
|
+
const [param, value] = item.split("=");
|
|
339
|
+
paramsOut[param] = value;
|
|
340
|
+
if (value === void 0) paramsOut[param] = true;
|
|
341
|
+
else if (value.indexOf(",") > 1) paramsOut[param] = value.split(",");
|
|
342
|
+
if (value === "false") paramsOut[param] = false;
|
|
343
|
+
else if (!isNaN(+value)) paramsOut[param] = +value;
|
|
344
|
+
});
|
|
345
|
+
return {
|
|
346
|
+
params: paramsOut,
|
|
347
|
+
res: resources
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* convert 'some content here' to 'sch'
|
|
352
|
+
* @param {string} str
|
|
353
|
+
* @return {string}
|
|
354
|
+
*/
|
|
355
|
+
function stringToFirstLetters(str) {
|
|
356
|
+
return str.split(" ").reduce((x, i) => x ? x + (i.length && i[0] || "") : i[0], "");
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Generate random string (a-z 0-9) with specific len
|
|
360
|
+
*
|
|
361
|
+
* @param {number} len 6 by default
|
|
362
|
+
* @default 6
|
|
363
|
+
* @return {string}
|
|
364
|
+
*/
|
|
365
|
+
function genRandomString(len = 6) {
|
|
366
|
+
let out = "";
|
|
367
|
+
while (out.length < len) out += Math.random().toString(36).replace("0.", "");
|
|
368
|
+
return out.substring(0, len);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Typed String.split() wrapper
|
|
372
|
+
* @param input
|
|
373
|
+
* @param separator
|
|
374
|
+
*/
|
|
375
|
+
function splitString(input, separator) {
|
|
376
|
+
if (Array.isArray(input)) return input;
|
|
377
|
+
return input.split(separator).map((item) => item.trim());
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* typeof guard
|
|
381
|
+
*/
|
|
382
|
+
function isString(v) {
|
|
383
|
+
return typeof v === "string";
|
|
384
|
+
}
|
|
385
|
+
// src/vue/findFrameworkInstance.ts
|
|
386
|
+
/**
|
|
387
|
+
* retrieve instance of a any framework plugin from vue instance if exists
|
|
388
|
+
* @param app
|
|
389
|
+
* @param name
|
|
390
|
+
*/
|
|
391
|
+
function findFrameworkInstance(app, name) {
|
|
392
|
+
if (name in app.config.globalProperties) {
|
|
393
|
+
const instance = app.config.globalProperties[name];
|
|
394
|
+
if (instance) return instance;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
// src/vue/mergeCssModulesStyles.ts
|
|
398
|
+
/**
|
|
399
|
+
* Merge two CSS-module class maps, the second winning on key conflicts.
|
|
400
|
+
*
|
|
401
|
+
* Mirrors how a component's own local `$style` overrides the class map
|
|
402
|
+
* shipped by a contract.
|
|
403
|
+
*
|
|
404
|
+
* @param $from - the base map (e.g. the contract's shipped classes)
|
|
405
|
+
* @param $to - the overriding map (e.g. the component's local `$style`)
|
|
406
|
+
* @returns the merged class map
|
|
407
|
+
*/
|
|
408
|
+
function mergeCssModulesStyles($from = {}, $to = {}) {
|
|
409
|
+
return {
|
|
410
|
+
...$from,
|
|
411
|
+
...$to
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
// src/number/number.ts
|
|
415
|
+
/**
|
|
416
|
+
* Floor the mantissa of a number's scientific notation after shifting its
|
|
417
|
+
* exponent by `-exp`, then shift the exponent back.
|
|
418
|
+
*
|
|
419
|
+
* Operates on the printed `toString()` digits rather than on decimal
|
|
420
|
+
* places: `decimalAdjust(0, 2.7)` → `2`, `decimalAdjust(2, 123.456)` →
|
|
421
|
+
* `100`. A non-integer `exp` or a non-numeric `value` yields `NaN`.
|
|
422
|
+
*
|
|
423
|
+
* @param exp - the decimal exponent shift (integer)
|
|
424
|
+
* @param value - the number to adjust
|
|
425
|
+
* @returns the adjusted number
|
|
426
|
+
*/
|
|
427
|
+
function decimalAdjust(exp, value) {
|
|
428
|
+
value = Number(value);
|
|
429
|
+
if (exp % 1 !== 0 || Number.isNaN(value)) return NaN;
|
|
430
|
+
else if (exp === 0) return Math.floor(value);
|
|
431
|
+
const [magnitude, exponent = 0] = value.toString().split("e");
|
|
432
|
+
const [newMagnitude, newExponent = 0] = Math.floor(+`${magnitude}e${+exponent - exp}`).toString().split("e");
|
|
433
|
+
return Number(`${newMagnitude}e${+newExponent + exp}`);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Extract the exponent of a number's scientific-notation form.
|
|
437
|
+
*
|
|
438
|
+
* `123.456` → `2` (`1.23456e+2`), `0.00123` → `-3` (`1.23e-3`).
|
|
439
|
+
*
|
|
440
|
+
* @param num - the number to inspect
|
|
441
|
+
* @returns the decimal exponent as a signed number
|
|
442
|
+
*/
|
|
443
|
+
function exponentialToNumber(num) {
|
|
444
|
+
const x = (+num).toExponential();
|
|
445
|
+
const minus = x.split("e-");
|
|
446
|
+
const plus = x.split("e+");
|
|
447
|
+
if (minus[1] !== void 0) return -minus[1];
|
|
448
|
+
if (plus[1] !== void 0) return +plus[1];
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Tell floats from integers.
|
|
453
|
+
*
|
|
454
|
+
* @param n - the number to check
|
|
455
|
+
* @returns `true` when `n` has a non-zero fractional part
|
|
456
|
+
*/
|
|
457
|
+
function isFloat(n) {
|
|
458
|
+
return n % 1 !== 0;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Count the digits after the decimal point.
|
|
462
|
+
*
|
|
463
|
+
* @param int - the (float) number to inspect
|
|
464
|
+
* @returns the number of fractional digits; `0` for integers
|
|
465
|
+
*/
|
|
466
|
+
function getFloatDigitsCount(int) {
|
|
467
|
+
if (int % 1 !== 0) return int.toString().split(".")[1].length;
|
|
468
|
+
return 0;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Type guard: value is a number or a string coercible to one.
|
|
472
|
+
*
|
|
473
|
+
* Coercion follows `+v` — numeric strings pass, non-numeric ones don't.
|
|
474
|
+
*
|
|
475
|
+
* @param v - the value to check
|
|
476
|
+
* @returns `true` when `v` is a string/number and `+v` is not `NaN`
|
|
477
|
+
*/
|
|
478
|
+
function isNumberish(v) {
|
|
479
|
+
return (typeof v === "string" || typeof v === "number") && !Number.isNaN(+v);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Strict numeric-validity check: real, finite, non-`NaN` numbers — and
|
|
483
|
+
* numeric strings that aren't blank.
|
|
484
|
+
*
|
|
485
|
+
* @param val - the value to check
|
|
486
|
+
* @returns `true` only for genuine numeric values
|
|
487
|
+
*/
|
|
488
|
+
function isValidNumber(val) {
|
|
489
|
+
return typeof val === "number" && val - val === 0 || typeof val === "string" && Number.isFinite(+val) && val.trim() !== "";
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Coerce a number-ish value with `+val`.
|
|
493
|
+
*
|
|
494
|
+
* @param val - the value to coerce; `undefined` falls back to `def`
|
|
495
|
+
* @param def - the default for `undefined`, defaults to `0`
|
|
496
|
+
* @returns the coerced number, `def` for `undefined`, or `NaN` when `val`
|
|
497
|
+
* coerces to `NaN` (no validation is applied)
|
|
498
|
+
*/
|
|
499
|
+
function numberCast(val, def = 0) {
|
|
500
|
+
return +(val ?? def);
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Clamp a number into the inclusive `[min, max]` range.
|
|
504
|
+
*
|
|
505
|
+
* @param value - the number to clamp
|
|
506
|
+
* @param min - the lower bound
|
|
507
|
+
* @param max - the upper bound
|
|
508
|
+
* @returns the bounded value
|
|
509
|
+
*/
|
|
510
|
+
function clamp(value, min, max) {
|
|
511
|
+
return Math.min(Math.max(value, min), max);
|
|
512
|
+
}
|
|
513
|
+
// src/css/parseCssTime.ts
|
|
514
|
+
/**
|
|
515
|
+
* Parse a CSS time literal into milliseconds.
|
|
516
|
+
*
|
|
517
|
+
* Accepts the two CSS time units: `<number>s` and `<number>ms`. Anything
|
|
518
|
+
* that doesn't match the `value + unit` shape — unitless numbers, other
|
|
519
|
+
* units, arbitrary strings — returns `0`, mirroring an unset duration.
|
|
520
|
+
*
|
|
521
|
+
* @param cssTime - a CSS time literal, e.g. `'300ms'`, `'2.5s'`
|
|
522
|
+
* @returns the time in milliseconds (`'2.5s'` → `2500`), or `0` when invalid
|
|
523
|
+
*/
|
|
524
|
+
function parseCssTime(cssTime) {
|
|
525
|
+
const match = cssTime.match(/^([\d.]+)(s|ms)$/);
|
|
526
|
+
if (!match) return 0;
|
|
527
|
+
const value = parseFloat(match[1]);
|
|
528
|
+
return match[2] === "s" ? value * 1e3 : value;
|
|
529
|
+
}
|
|
530
|
+
// src/css/toUnits.ts
|
|
531
|
+
/**
|
|
532
|
+
* Append a CSS unit to a unitless value.
|
|
533
|
+
*
|
|
534
|
+
* Numbers and numeric strings get `units` appended (`20` → `'20px'`,
|
|
535
|
+
* `'2.5'` → `'2.5px'`). Anything else — strings that already carry a unit
|
|
536
|
+
* or a keyword (`'20em'`, `'auto'`), `undefined` — returns an empty string;
|
|
537
|
+
* use {@link tryToUnits} when the original value should survive.
|
|
538
|
+
*
|
|
539
|
+
* @param val - a number or a numeric string
|
|
540
|
+
* @param units - the unit to append, defaults to `'px'`
|
|
541
|
+
* @returns the value with the unit attached, or `''` when `val` is not
|
|
542
|
+
* unitless-numeric
|
|
543
|
+
*/
|
|
544
|
+
function toUnits(val, units = "px") {
|
|
545
|
+
if (typeof val === "number") return val + units;
|
|
546
|
+
else if (typeof val === "string") {
|
|
547
|
+
if (val === +val + "") return val + units;
|
|
548
|
+
}
|
|
549
|
+
return "";
|
|
550
|
+
}
|
|
551
|
+
// src/css/tryToUnits.ts
|
|
552
|
+
/**
|
|
553
|
+
* Like {@link toUnits}, but passes non-numeric values through untouched.
|
|
554
|
+
*
|
|
555
|
+
* Numberish input gets the unit appended (`20` → `'20px'`); strings that
|
|
556
|
+
* already read as CSS (`'20em'`, `'auto'`) are returned as-is; `undefined`
|
|
557
|
+
* collapses to `'0'` so an omitted value never leaks into a style
|
|
558
|
+
* declaration.
|
|
559
|
+
*
|
|
560
|
+
* @param val - a number, a numeric string, or any other CSS value
|
|
561
|
+
* @param units - the unit for numberish input, defaults to `'px'`
|
|
562
|
+
* @returns the unit-bearing value, the original string, or `'0'`
|
|
563
|
+
*/
|
|
564
|
+
function tryToUnits(val, units = "px") {
|
|
565
|
+
return isNumberish(val) ? toUnits(val, units) : val ?? "0";
|
|
566
|
+
}
|
|
567
|
+
// src/css/objectToCss.ts
|
|
568
|
+
/**
|
|
569
|
+
* Serialize a style object into an inline CSS declaration string.
|
|
570
|
+
*
|
|
571
|
+
* camelCase keys are kebab-cased (`zIndex` → `z-index`, an initial capital
|
|
572
|
+
* becomes a vendor prefix: `WebkitBackdropFilter` → `-webkit-backdrop-filter`),
|
|
573
|
+
* each pair becomes `key:value;` and pairs are concatenated without
|
|
574
|
+
* separators — the result targets a single `style` attribute or a css-in-js
|
|
575
|
+
* template, not pretty printing.
|
|
576
|
+
*
|
|
577
|
+
* @param obj - the style object (`StyleList` / any plain record)
|
|
578
|
+
* @returns the declaration string, e.g. `'width:20px;z-index:2;'`
|
|
579
|
+
*/
|
|
580
|
+
function objectToCss(obj) {
|
|
581
|
+
return Object.entries(obj).map(([k, v]) => `${k.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase())}:${v};`).join("");
|
|
582
|
+
}
|
|
583
|
+
// src/css/parseUnits.ts
|
|
584
|
+
/**
|
|
585
|
+
* convert 123px to {value: 123, units: 'px'}
|
|
586
|
+
* @param {string} str
|
|
587
|
+
* @return {{units: string, value: number}}
|
|
588
|
+
*/
|
|
589
|
+
function parseUnits(str = "") {
|
|
590
|
+
const result = /-?\d*\.?\d+/.exec(str);
|
|
591
|
+
if (result && result[0]) {
|
|
592
|
+
const value = +result[0];
|
|
593
|
+
const units = str.replace(result[0], "");
|
|
594
|
+
return {
|
|
595
|
+
value,
|
|
596
|
+
units,
|
|
597
|
+
calc: value + units
|
|
598
|
+
};
|
|
599
|
+
} else return {
|
|
600
|
+
value: 0,
|
|
601
|
+
units: "px",
|
|
602
|
+
calc: ""
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
// src/console.ts
|
|
606
|
+
/**
|
|
607
|
+
* Build the error a composable throws when it is called outside of a
|
|
608
|
+
* component's setup or lifecycle hooks.
|
|
609
|
+
*
|
|
610
|
+
* @param funcName - the composable's name for the message
|
|
611
|
+
* @returns the ready `Error` instance
|
|
612
|
+
*/
|
|
613
|
+
function newErrorForSetupCall(funcName) {
|
|
614
|
+
return /* @__PURE__ */ new Error(`${funcName} must be called from the setup or lifecycle hook methods.`);
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Build a core-prefixed framework error (`[core]: <msg>`).
|
|
618
|
+
*
|
|
619
|
+
* @param msg - the error detail
|
|
620
|
+
* @returns the ready `Error` instance
|
|
621
|
+
*/
|
|
622
|
+
function newFrameworkError(msg) {
|
|
623
|
+
return /* @__PURE__ */ new Error(`[core]: ${msg}`);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Build the error thrown when an app is requested by a name nothing
|
|
627
|
+
* registered under.
|
|
628
|
+
*
|
|
629
|
+
* @param data - the missing app's name
|
|
630
|
+
* @returns the ready `Error` instance
|
|
631
|
+
*/
|
|
632
|
+
function newErrorAppNotFound(data) {
|
|
633
|
+
return /* @__PURE__ */ new Error(`App ${data} not found.`);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Build the error thrown when an app name is registered twice.
|
|
637
|
+
*
|
|
638
|
+
* @param data - the duplicated app's name
|
|
639
|
+
* @returns the ready `Error` instance
|
|
640
|
+
*/
|
|
641
|
+
function newErrorAppExists(data) {
|
|
642
|
+
return /* @__PURE__ */ new Error(`App ${data} already exists. Please, provide unique name for each app.`);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Build the error `useApp` throws when no app context is active.
|
|
646
|
+
*
|
|
647
|
+
* @returns the ready `Error` instance
|
|
648
|
+
*/
|
|
649
|
+
function newErrorUseAppCall() {
|
|
650
|
+
return /* @__PURE__ */ new Error(`useApp must be called inside any app.
|
|
651
|
+
use data in <app data></app>`);
|
|
652
|
+
}
|
|
653
|
+
// src/hash.ts
|
|
654
|
+
const K = [
|
|
655
|
+
1116352408,
|
|
656
|
+
1899447441,
|
|
657
|
+
3049323471,
|
|
658
|
+
3921009573,
|
|
659
|
+
961987163,
|
|
660
|
+
1508970993,
|
|
661
|
+
2453635748,
|
|
662
|
+
2870763221,
|
|
663
|
+
3624381080,
|
|
664
|
+
310598401,
|
|
665
|
+
607225278,
|
|
666
|
+
1426881987,
|
|
667
|
+
1925078388,
|
|
668
|
+
2162078206,
|
|
669
|
+
2614888103,
|
|
670
|
+
3248222580,
|
|
671
|
+
3835390401,
|
|
672
|
+
4022224774,
|
|
673
|
+
264347078,
|
|
674
|
+
604807628,
|
|
675
|
+
770255983,
|
|
676
|
+
1249150122,
|
|
677
|
+
1555081692,
|
|
678
|
+
1996064986,
|
|
679
|
+
2554220882,
|
|
680
|
+
2821834349,
|
|
681
|
+
2952996808,
|
|
682
|
+
3210313671,
|
|
683
|
+
3336571891,
|
|
684
|
+
3584528711,
|
|
685
|
+
113926993,
|
|
686
|
+
338241895,
|
|
687
|
+
666307205,
|
|
688
|
+
773529912,
|
|
689
|
+
1294757372,
|
|
690
|
+
1396182291,
|
|
691
|
+
1695183700,
|
|
692
|
+
1986661051,
|
|
693
|
+
2177026350,
|
|
694
|
+
2456956037,
|
|
695
|
+
2730485921,
|
|
696
|
+
2820302411,
|
|
697
|
+
3259730800,
|
|
698
|
+
3345764771,
|
|
699
|
+
3516065817,
|
|
700
|
+
3600352804,
|
|
701
|
+
4094571909,
|
|
702
|
+
275423344,
|
|
703
|
+
430227734,
|
|
704
|
+
506948616,
|
|
705
|
+
659060556,
|
|
706
|
+
883997877,
|
|
707
|
+
958139571,
|
|
708
|
+
1322822218,
|
|
709
|
+
1537002063,
|
|
710
|
+
1747873779,
|
|
711
|
+
1955562222,
|
|
712
|
+
2024104815,
|
|
713
|
+
2227730452,
|
|
714
|
+
2361852424,
|
|
715
|
+
2428436474,
|
|
716
|
+
2756734187,
|
|
717
|
+
3204031479,
|
|
718
|
+
3329325298
|
|
719
|
+
];
|
|
720
|
+
function rotr(x, n) {
|
|
721
|
+
return x >>> n | x << 32 - n;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* SHA-256 of a string as a lowercase hex digest.
|
|
725
|
+
*
|
|
726
|
+
* Pure JS implementation (FIPS 180-4) — runs identically in the browser
|
|
727
|
+
* and in Node, so generated class-name hashes can be computed at runtime
|
|
728
|
+
* on either side without `node:crypto`.
|
|
729
|
+
*
|
|
730
|
+
* @param message - the string to hash (UTF-8 encoded)
|
|
731
|
+
* @returns the 64-character hex digest
|
|
732
|
+
*/
|
|
733
|
+
function sha256hex(message) {
|
|
734
|
+
const bytes = new TextEncoder().encode(message);
|
|
735
|
+
const bitLen = bytes.length * 8;
|
|
736
|
+
const padded = new Uint8Array((bytes.length + 8 >> 6) + 1 << 6);
|
|
737
|
+
padded.set(bytes);
|
|
738
|
+
padded[bytes.length] = 128;
|
|
739
|
+
const view = new DataView(padded.buffer);
|
|
740
|
+
view.setUint32(padded.length - 8, Math.floor(bitLen / 4294967296));
|
|
741
|
+
view.setUint32(padded.length - 4, bitLen >>> 0);
|
|
742
|
+
let h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762;
|
|
743
|
+
let h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225;
|
|
744
|
+
const w = /* @__PURE__ */ new Uint32Array(64);
|
|
745
|
+
for (let block = 0; block < padded.length; block += 64) {
|
|
746
|
+
for (let t = 0; t < 16; t++) w[t] = view.getUint32(block + t * 4);
|
|
747
|
+
for (let t = 16; t < 64; t++) {
|
|
748
|
+
const s0 = rotr(w[t - 15], 7) ^ rotr(w[t - 15], 18) ^ w[t - 15] >>> 3;
|
|
749
|
+
const s1 = rotr(w[t - 2], 17) ^ rotr(w[t - 2], 19) ^ w[t - 2] >>> 10;
|
|
750
|
+
w[t] = w[t - 16] + s0 + w[t - 7] + s1 | 0;
|
|
751
|
+
}
|
|
752
|
+
let a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7;
|
|
753
|
+
for (let t = 0; t < 64; t++) {
|
|
754
|
+
const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
755
|
+
const ch = e & f ^ ~e & g;
|
|
756
|
+
const t1 = h + S1 + ch + K[t] + w[t] | 0;
|
|
757
|
+
const t2 = (rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + (a & b ^ a & c ^ b & c) | 0;
|
|
758
|
+
h = g;
|
|
759
|
+
g = f;
|
|
760
|
+
f = e;
|
|
761
|
+
e = d + t1 | 0;
|
|
762
|
+
d = c;
|
|
763
|
+
c = b;
|
|
764
|
+
b = a;
|
|
765
|
+
a = t1 + t2 | 0;
|
|
766
|
+
}
|
|
767
|
+
h0 = h0 + a | 0;
|
|
768
|
+
h1 = h1 + b | 0;
|
|
769
|
+
h2 = h2 + c | 0;
|
|
770
|
+
h3 = h3 + d | 0;
|
|
771
|
+
h4 = h4 + e | 0;
|
|
772
|
+
h5 = h5 + f | 0;
|
|
773
|
+
h6 = h6 + g | 0;
|
|
774
|
+
h7 = h7 + h | 0;
|
|
775
|
+
}
|
|
776
|
+
return [
|
|
777
|
+
h0,
|
|
778
|
+
h1,
|
|
779
|
+
h2,
|
|
780
|
+
h3,
|
|
781
|
+
h4,
|
|
782
|
+
h5,
|
|
783
|
+
h6,
|
|
784
|
+
h7
|
|
785
|
+
].map((x) => (x >>> 0).toString(16).padStart(8, "0")).join("");
|
|
786
|
+
}
|
|
787
|
+
// src/color/hexToRgb.ts
|
|
788
|
+
/**
|
|
789
|
+
* Parse a CSS hex color into an sRGB triple.
|
|
790
|
+
*
|
|
791
|
+
* Accepts the long (`#rrggbb`) and short (`#rgb`) forms, with or without
|
|
792
|
+
* the leading `#`.
|
|
793
|
+
*
|
|
794
|
+
* @param hex - the hex literal, e.g. `'#16e3c5'`, `'f00'`
|
|
795
|
+
* @returns `[r, g, b]` with integer channels 0-255
|
|
796
|
+
*/
|
|
797
|
+
function hexToRgb(hex) {
|
|
798
|
+
hex = hex.replace("#", "");
|
|
799
|
+
if (hex.length === 3) hex = hex.split("").map((c) => c + c).join("");
|
|
800
|
+
const num = parseInt(hex, 16);
|
|
801
|
+
return [
|
|
802
|
+
num >> 16 & 255,
|
|
803
|
+
num >> 8 & 255,
|
|
804
|
+
num & 255
|
|
805
|
+
];
|
|
806
|
+
}
|
|
807
|
+
// src/color/parseRgbString.ts
|
|
808
|
+
/**
|
|
809
|
+
* Extract the RGB channels from an `rgb()` / `rgba()` string.
|
|
810
|
+
*
|
|
811
|
+
* Handles the legacy comma syntax (`rgb(1, 2, 3)`) and the modern space
|
|
812
|
+
* syntax with alpha (`rgb(1 2 3 / .5)`); the alpha component is dropped.
|
|
813
|
+
*
|
|
814
|
+
* @param s - the CSS color function string
|
|
815
|
+
* @returns `[r, g, b]` as parsed (fractional for decimals/percentages)
|
|
816
|
+
*/
|
|
817
|
+
function parseRgbString(s) {
|
|
818
|
+
const parts = s.match(/rgba?\(([^)]+)\)/)[1].replace(/\//g, " ").split(/[\s,]+/).filter(Boolean).map((x) => parseFloat(x));
|
|
819
|
+
return [
|
|
820
|
+
parts[0],
|
|
821
|
+
parts[1],
|
|
822
|
+
parts[2]
|
|
823
|
+
];
|
|
824
|
+
}
|
|
825
|
+
// src/color/namedColors.ts
|
|
826
|
+
/**
|
|
827
|
+
* The CSS named colors (CSS Color Module Level 4) as sRGB triples,
|
|
828
|
+
* keyed by lowercase name.
|
|
829
|
+
*/
|
|
830
|
+
const NAMED_COLORS = {
|
|
831
|
+
aliceblue: [
|
|
832
|
+
240,
|
|
833
|
+
248,
|
|
834
|
+
255
|
|
835
|
+
],
|
|
836
|
+
antiquewhite: [
|
|
837
|
+
250,
|
|
838
|
+
235,
|
|
839
|
+
215
|
|
840
|
+
],
|
|
841
|
+
aqua: [
|
|
842
|
+
0,
|
|
843
|
+
255,
|
|
844
|
+
255
|
|
845
|
+
],
|
|
846
|
+
aquamarine: [
|
|
847
|
+
127,
|
|
848
|
+
255,
|
|
849
|
+
212
|
|
850
|
+
],
|
|
851
|
+
azure: [
|
|
852
|
+
240,
|
|
853
|
+
255,
|
|
854
|
+
255
|
|
855
|
+
],
|
|
856
|
+
beige: [
|
|
857
|
+
245,
|
|
858
|
+
245,
|
|
859
|
+
220
|
|
860
|
+
],
|
|
861
|
+
bisque: [
|
|
862
|
+
255,
|
|
863
|
+
228,
|
|
864
|
+
196
|
|
865
|
+
],
|
|
866
|
+
black: [
|
|
867
|
+
0,
|
|
868
|
+
0,
|
|
869
|
+
0
|
|
870
|
+
],
|
|
871
|
+
blanchedalmond: [
|
|
872
|
+
255,
|
|
873
|
+
235,
|
|
874
|
+
205
|
|
875
|
+
],
|
|
876
|
+
blue: [
|
|
877
|
+
0,
|
|
878
|
+
0,
|
|
879
|
+
255
|
|
880
|
+
],
|
|
881
|
+
blueviolet: [
|
|
882
|
+
138,
|
|
883
|
+
43,
|
|
884
|
+
226
|
|
885
|
+
],
|
|
886
|
+
brown: [
|
|
887
|
+
165,
|
|
888
|
+
42,
|
|
889
|
+
42
|
|
890
|
+
],
|
|
891
|
+
burlywood: [
|
|
892
|
+
222,
|
|
893
|
+
184,
|
|
894
|
+
135
|
|
895
|
+
],
|
|
896
|
+
cadetblue: [
|
|
897
|
+
95,
|
|
898
|
+
158,
|
|
899
|
+
160
|
|
900
|
+
],
|
|
901
|
+
chartreuse: [
|
|
902
|
+
127,
|
|
903
|
+
255,
|
|
904
|
+
0
|
|
905
|
+
],
|
|
906
|
+
chocolate: [
|
|
907
|
+
210,
|
|
908
|
+
105,
|
|
909
|
+
30
|
|
910
|
+
],
|
|
911
|
+
coral: [
|
|
912
|
+
255,
|
|
913
|
+
127,
|
|
914
|
+
80
|
|
915
|
+
],
|
|
916
|
+
cornflowerblue: [
|
|
917
|
+
100,
|
|
918
|
+
149,
|
|
919
|
+
237
|
|
920
|
+
],
|
|
921
|
+
cornsilk: [
|
|
922
|
+
255,
|
|
923
|
+
248,
|
|
924
|
+
220
|
|
925
|
+
],
|
|
926
|
+
crimson: [
|
|
927
|
+
220,
|
|
928
|
+
20,
|
|
929
|
+
60
|
|
930
|
+
],
|
|
931
|
+
cyan: [
|
|
932
|
+
0,
|
|
933
|
+
255,
|
|
934
|
+
255
|
|
935
|
+
],
|
|
936
|
+
darkblue: [
|
|
937
|
+
0,
|
|
938
|
+
0,
|
|
939
|
+
139
|
|
940
|
+
],
|
|
941
|
+
darkcyan: [
|
|
942
|
+
0,
|
|
943
|
+
139,
|
|
944
|
+
139
|
|
945
|
+
],
|
|
946
|
+
darkgoldenrod: [
|
|
947
|
+
184,
|
|
948
|
+
134,
|
|
949
|
+
11
|
|
950
|
+
],
|
|
951
|
+
darkgray: [
|
|
952
|
+
169,
|
|
953
|
+
169,
|
|
954
|
+
169
|
|
955
|
+
],
|
|
956
|
+
darkgreen: [
|
|
957
|
+
0,
|
|
958
|
+
100,
|
|
959
|
+
0
|
|
960
|
+
],
|
|
961
|
+
darkgrey: [
|
|
962
|
+
169,
|
|
963
|
+
169,
|
|
964
|
+
169
|
|
965
|
+
],
|
|
966
|
+
darkkhaki: [
|
|
967
|
+
189,
|
|
968
|
+
183,
|
|
969
|
+
107
|
|
970
|
+
],
|
|
971
|
+
darkmagenta: [
|
|
972
|
+
139,
|
|
973
|
+
0,
|
|
974
|
+
139
|
|
975
|
+
],
|
|
976
|
+
darkolivegreen: [
|
|
977
|
+
85,
|
|
978
|
+
107,
|
|
979
|
+
47
|
|
980
|
+
],
|
|
981
|
+
darkorange: [
|
|
982
|
+
255,
|
|
983
|
+
140,
|
|
984
|
+
0
|
|
985
|
+
],
|
|
986
|
+
darkorchid: [
|
|
987
|
+
153,
|
|
988
|
+
50,
|
|
989
|
+
204
|
|
990
|
+
],
|
|
991
|
+
darkred: [
|
|
992
|
+
139,
|
|
993
|
+
0,
|
|
994
|
+
0
|
|
995
|
+
],
|
|
996
|
+
darksalmon: [
|
|
997
|
+
233,
|
|
998
|
+
150,
|
|
999
|
+
122
|
|
1000
|
+
],
|
|
1001
|
+
darkseagreen: [
|
|
1002
|
+
143,
|
|
1003
|
+
188,
|
|
1004
|
+
143
|
|
1005
|
+
],
|
|
1006
|
+
darkslateblue: [
|
|
1007
|
+
72,
|
|
1008
|
+
61,
|
|
1009
|
+
139
|
|
1010
|
+
],
|
|
1011
|
+
darkslategray: [
|
|
1012
|
+
47,
|
|
1013
|
+
79,
|
|
1014
|
+
79
|
|
1015
|
+
],
|
|
1016
|
+
darkslategrey: [
|
|
1017
|
+
47,
|
|
1018
|
+
79,
|
|
1019
|
+
79
|
|
1020
|
+
],
|
|
1021
|
+
darkturquoise: [
|
|
1022
|
+
0,
|
|
1023
|
+
206,
|
|
1024
|
+
209
|
|
1025
|
+
],
|
|
1026
|
+
darkviolet: [
|
|
1027
|
+
148,
|
|
1028
|
+
0,
|
|
1029
|
+
211
|
|
1030
|
+
],
|
|
1031
|
+
deeppink: [
|
|
1032
|
+
255,
|
|
1033
|
+
20,
|
|
1034
|
+
147
|
|
1035
|
+
],
|
|
1036
|
+
deepskyblue: [
|
|
1037
|
+
0,
|
|
1038
|
+
191,
|
|
1039
|
+
255
|
|
1040
|
+
],
|
|
1041
|
+
dimgray: [
|
|
1042
|
+
105,
|
|
1043
|
+
105,
|
|
1044
|
+
105
|
|
1045
|
+
],
|
|
1046
|
+
dimgrey: [
|
|
1047
|
+
105,
|
|
1048
|
+
105,
|
|
1049
|
+
105
|
|
1050
|
+
],
|
|
1051
|
+
dodgerblue: [
|
|
1052
|
+
30,
|
|
1053
|
+
144,
|
|
1054
|
+
255
|
|
1055
|
+
],
|
|
1056
|
+
firebrick: [
|
|
1057
|
+
178,
|
|
1058
|
+
34,
|
|
1059
|
+
34
|
|
1060
|
+
],
|
|
1061
|
+
floralwhite: [
|
|
1062
|
+
255,
|
|
1063
|
+
250,
|
|
1064
|
+
240
|
|
1065
|
+
],
|
|
1066
|
+
forestgreen: [
|
|
1067
|
+
34,
|
|
1068
|
+
139,
|
|
1069
|
+
34
|
|
1070
|
+
],
|
|
1071
|
+
fuchsia: [
|
|
1072
|
+
255,
|
|
1073
|
+
0,
|
|
1074
|
+
255
|
|
1075
|
+
],
|
|
1076
|
+
gainsboro: [
|
|
1077
|
+
220,
|
|
1078
|
+
220,
|
|
1079
|
+
220
|
|
1080
|
+
],
|
|
1081
|
+
ghostwhite: [
|
|
1082
|
+
248,
|
|
1083
|
+
248,
|
|
1084
|
+
255
|
|
1085
|
+
],
|
|
1086
|
+
gold: [
|
|
1087
|
+
255,
|
|
1088
|
+
215,
|
|
1089
|
+
0
|
|
1090
|
+
],
|
|
1091
|
+
goldenrod: [
|
|
1092
|
+
218,
|
|
1093
|
+
165,
|
|
1094
|
+
32
|
|
1095
|
+
],
|
|
1096
|
+
gray: [
|
|
1097
|
+
128,
|
|
1098
|
+
128,
|
|
1099
|
+
128
|
|
1100
|
+
],
|
|
1101
|
+
green: [
|
|
1102
|
+
0,
|
|
1103
|
+
128,
|
|
1104
|
+
0
|
|
1105
|
+
],
|
|
1106
|
+
greenyellow: [
|
|
1107
|
+
173,
|
|
1108
|
+
255,
|
|
1109
|
+
47
|
|
1110
|
+
],
|
|
1111
|
+
grey: [
|
|
1112
|
+
128,
|
|
1113
|
+
128,
|
|
1114
|
+
128
|
|
1115
|
+
],
|
|
1116
|
+
honeydew: [
|
|
1117
|
+
240,
|
|
1118
|
+
255,
|
|
1119
|
+
240
|
|
1120
|
+
],
|
|
1121
|
+
hotpink: [
|
|
1122
|
+
255,
|
|
1123
|
+
105,
|
|
1124
|
+
180
|
|
1125
|
+
],
|
|
1126
|
+
indianred: [
|
|
1127
|
+
205,
|
|
1128
|
+
92,
|
|
1129
|
+
92
|
|
1130
|
+
],
|
|
1131
|
+
indigo: [
|
|
1132
|
+
75,
|
|
1133
|
+
0,
|
|
1134
|
+
130
|
|
1135
|
+
],
|
|
1136
|
+
ivory: [
|
|
1137
|
+
255,
|
|
1138
|
+
255,
|
|
1139
|
+
240
|
|
1140
|
+
],
|
|
1141
|
+
khaki: [
|
|
1142
|
+
240,
|
|
1143
|
+
230,
|
|
1144
|
+
140
|
|
1145
|
+
],
|
|
1146
|
+
lavender: [
|
|
1147
|
+
230,
|
|
1148
|
+
230,
|
|
1149
|
+
250
|
|
1150
|
+
],
|
|
1151
|
+
lavenderblush: [
|
|
1152
|
+
255,
|
|
1153
|
+
240,
|
|
1154
|
+
245
|
|
1155
|
+
],
|
|
1156
|
+
lawngreen: [
|
|
1157
|
+
124,
|
|
1158
|
+
252,
|
|
1159
|
+
0
|
|
1160
|
+
],
|
|
1161
|
+
lemonchiffon: [
|
|
1162
|
+
255,
|
|
1163
|
+
250,
|
|
1164
|
+
205
|
|
1165
|
+
],
|
|
1166
|
+
lightblue: [
|
|
1167
|
+
173,
|
|
1168
|
+
216,
|
|
1169
|
+
230
|
|
1170
|
+
],
|
|
1171
|
+
lightcoral: [
|
|
1172
|
+
240,
|
|
1173
|
+
128,
|
|
1174
|
+
128
|
|
1175
|
+
],
|
|
1176
|
+
lightcyan: [
|
|
1177
|
+
224,
|
|
1178
|
+
255,
|
|
1179
|
+
255
|
|
1180
|
+
],
|
|
1181
|
+
lightgoldenrodyellow: [
|
|
1182
|
+
250,
|
|
1183
|
+
250,
|
|
1184
|
+
210
|
|
1185
|
+
],
|
|
1186
|
+
lightgray: [
|
|
1187
|
+
211,
|
|
1188
|
+
211,
|
|
1189
|
+
211
|
|
1190
|
+
],
|
|
1191
|
+
lightgreen: [
|
|
1192
|
+
144,
|
|
1193
|
+
238,
|
|
1194
|
+
144
|
|
1195
|
+
],
|
|
1196
|
+
lightgrey: [
|
|
1197
|
+
211,
|
|
1198
|
+
211,
|
|
1199
|
+
211
|
|
1200
|
+
],
|
|
1201
|
+
lightpink: [
|
|
1202
|
+
255,
|
|
1203
|
+
182,
|
|
1204
|
+
193
|
|
1205
|
+
],
|
|
1206
|
+
lightsalmon: [
|
|
1207
|
+
255,
|
|
1208
|
+
160,
|
|
1209
|
+
122
|
|
1210
|
+
],
|
|
1211
|
+
lightseagreen: [
|
|
1212
|
+
32,
|
|
1213
|
+
178,
|
|
1214
|
+
170
|
|
1215
|
+
],
|
|
1216
|
+
lightskyblue: [
|
|
1217
|
+
135,
|
|
1218
|
+
206,
|
|
1219
|
+
250
|
|
1220
|
+
],
|
|
1221
|
+
lightslategray: [
|
|
1222
|
+
119,
|
|
1223
|
+
136,
|
|
1224
|
+
153
|
|
1225
|
+
],
|
|
1226
|
+
lightslategrey: [
|
|
1227
|
+
119,
|
|
1228
|
+
136,
|
|
1229
|
+
153
|
|
1230
|
+
],
|
|
1231
|
+
lightsteelblue: [
|
|
1232
|
+
176,
|
|
1233
|
+
196,
|
|
1234
|
+
222
|
|
1235
|
+
],
|
|
1236
|
+
lightyellow: [
|
|
1237
|
+
255,
|
|
1238
|
+
255,
|
|
1239
|
+
224
|
|
1240
|
+
],
|
|
1241
|
+
lime: [
|
|
1242
|
+
0,
|
|
1243
|
+
255,
|
|
1244
|
+
0
|
|
1245
|
+
],
|
|
1246
|
+
limegreen: [
|
|
1247
|
+
50,
|
|
1248
|
+
205,
|
|
1249
|
+
50
|
|
1250
|
+
],
|
|
1251
|
+
linen: [
|
|
1252
|
+
250,
|
|
1253
|
+
240,
|
|
1254
|
+
230
|
|
1255
|
+
],
|
|
1256
|
+
magenta: [
|
|
1257
|
+
255,
|
|
1258
|
+
0,
|
|
1259
|
+
255
|
|
1260
|
+
],
|
|
1261
|
+
maroon: [
|
|
1262
|
+
128,
|
|
1263
|
+
0,
|
|
1264
|
+
0
|
|
1265
|
+
],
|
|
1266
|
+
mediumaquamarine: [
|
|
1267
|
+
102,
|
|
1268
|
+
205,
|
|
1269
|
+
170
|
|
1270
|
+
],
|
|
1271
|
+
mediumblue: [
|
|
1272
|
+
0,
|
|
1273
|
+
0,
|
|
1274
|
+
205
|
|
1275
|
+
],
|
|
1276
|
+
mediumorchid: [
|
|
1277
|
+
186,
|
|
1278
|
+
85,
|
|
1279
|
+
211
|
|
1280
|
+
],
|
|
1281
|
+
mediumpurple: [
|
|
1282
|
+
147,
|
|
1283
|
+
112,
|
|
1284
|
+
219
|
|
1285
|
+
],
|
|
1286
|
+
mediumseagreen: [
|
|
1287
|
+
60,
|
|
1288
|
+
179,
|
|
1289
|
+
113
|
|
1290
|
+
],
|
|
1291
|
+
mediumslateblue: [
|
|
1292
|
+
123,
|
|
1293
|
+
104,
|
|
1294
|
+
238
|
|
1295
|
+
],
|
|
1296
|
+
mediumspringgreen: [
|
|
1297
|
+
0,
|
|
1298
|
+
250,
|
|
1299
|
+
154
|
|
1300
|
+
],
|
|
1301
|
+
mediumturquoise: [
|
|
1302
|
+
72,
|
|
1303
|
+
209,
|
|
1304
|
+
204
|
|
1305
|
+
],
|
|
1306
|
+
mediumvioletred: [
|
|
1307
|
+
199,
|
|
1308
|
+
21,
|
|
1309
|
+
133
|
|
1310
|
+
],
|
|
1311
|
+
midnightblue: [
|
|
1312
|
+
25,
|
|
1313
|
+
25,
|
|
1314
|
+
112
|
|
1315
|
+
],
|
|
1316
|
+
mintcream: [
|
|
1317
|
+
245,
|
|
1318
|
+
255,
|
|
1319
|
+
250
|
|
1320
|
+
],
|
|
1321
|
+
mistyrose: [
|
|
1322
|
+
255,
|
|
1323
|
+
228,
|
|
1324
|
+
225
|
|
1325
|
+
],
|
|
1326
|
+
moccasin: [
|
|
1327
|
+
255,
|
|
1328
|
+
228,
|
|
1329
|
+
181
|
|
1330
|
+
],
|
|
1331
|
+
navajowhite: [
|
|
1332
|
+
255,
|
|
1333
|
+
222,
|
|
1334
|
+
173
|
|
1335
|
+
],
|
|
1336
|
+
navy: [
|
|
1337
|
+
0,
|
|
1338
|
+
0,
|
|
1339
|
+
128
|
|
1340
|
+
],
|
|
1341
|
+
oldlace: [
|
|
1342
|
+
253,
|
|
1343
|
+
245,
|
|
1344
|
+
230
|
|
1345
|
+
],
|
|
1346
|
+
olive: [
|
|
1347
|
+
128,
|
|
1348
|
+
128,
|
|
1349
|
+
0
|
|
1350
|
+
],
|
|
1351
|
+
olivedrab: [
|
|
1352
|
+
107,
|
|
1353
|
+
142,
|
|
1354
|
+
35
|
|
1355
|
+
],
|
|
1356
|
+
orange: [
|
|
1357
|
+
255,
|
|
1358
|
+
165,
|
|
1359
|
+
0
|
|
1360
|
+
],
|
|
1361
|
+
orangered: [
|
|
1362
|
+
255,
|
|
1363
|
+
69,
|
|
1364
|
+
0
|
|
1365
|
+
],
|
|
1366
|
+
orchid: [
|
|
1367
|
+
218,
|
|
1368
|
+
112,
|
|
1369
|
+
214
|
|
1370
|
+
],
|
|
1371
|
+
palegoldenrod: [
|
|
1372
|
+
238,
|
|
1373
|
+
232,
|
|
1374
|
+
170
|
|
1375
|
+
],
|
|
1376
|
+
palegreen: [
|
|
1377
|
+
152,
|
|
1378
|
+
251,
|
|
1379
|
+
152
|
|
1380
|
+
],
|
|
1381
|
+
paleturquoise: [
|
|
1382
|
+
175,
|
|
1383
|
+
238,
|
|
1384
|
+
238
|
|
1385
|
+
],
|
|
1386
|
+
palevioletred: [
|
|
1387
|
+
219,
|
|
1388
|
+
112,
|
|
1389
|
+
147
|
|
1390
|
+
],
|
|
1391
|
+
papayawhip: [
|
|
1392
|
+
255,
|
|
1393
|
+
239,
|
|
1394
|
+
213
|
|
1395
|
+
],
|
|
1396
|
+
peachpuff: [
|
|
1397
|
+
255,
|
|
1398
|
+
218,
|
|
1399
|
+
185
|
|
1400
|
+
],
|
|
1401
|
+
peru: [
|
|
1402
|
+
205,
|
|
1403
|
+
133,
|
|
1404
|
+
63
|
|
1405
|
+
],
|
|
1406
|
+
pink: [
|
|
1407
|
+
255,
|
|
1408
|
+
192,
|
|
1409
|
+
203
|
|
1410
|
+
],
|
|
1411
|
+
plum: [
|
|
1412
|
+
221,
|
|
1413
|
+
160,
|
|
1414
|
+
221
|
|
1415
|
+
],
|
|
1416
|
+
powderblue: [
|
|
1417
|
+
176,
|
|
1418
|
+
224,
|
|
1419
|
+
230
|
|
1420
|
+
],
|
|
1421
|
+
purple: [
|
|
1422
|
+
128,
|
|
1423
|
+
0,
|
|
1424
|
+
128
|
|
1425
|
+
],
|
|
1426
|
+
rebeccapurple: [
|
|
1427
|
+
102,
|
|
1428
|
+
51,
|
|
1429
|
+
153
|
|
1430
|
+
],
|
|
1431
|
+
red: [
|
|
1432
|
+
255,
|
|
1433
|
+
0,
|
|
1434
|
+
0
|
|
1435
|
+
],
|
|
1436
|
+
rosybrown: [
|
|
1437
|
+
188,
|
|
1438
|
+
143,
|
|
1439
|
+
143
|
|
1440
|
+
],
|
|
1441
|
+
royalblue: [
|
|
1442
|
+
65,
|
|
1443
|
+
105,
|
|
1444
|
+
225
|
|
1445
|
+
],
|
|
1446
|
+
saddlebrown: [
|
|
1447
|
+
139,
|
|
1448
|
+
69,
|
|
1449
|
+
19
|
|
1450
|
+
],
|
|
1451
|
+
salmon: [
|
|
1452
|
+
250,
|
|
1453
|
+
128,
|
|
1454
|
+
114
|
|
1455
|
+
],
|
|
1456
|
+
sandybrown: [
|
|
1457
|
+
244,
|
|
1458
|
+
164,
|
|
1459
|
+
96
|
|
1460
|
+
],
|
|
1461
|
+
seagreen: [
|
|
1462
|
+
46,
|
|
1463
|
+
139,
|
|
1464
|
+
87
|
|
1465
|
+
],
|
|
1466
|
+
seashell: [
|
|
1467
|
+
255,
|
|
1468
|
+
245,
|
|
1469
|
+
238
|
|
1470
|
+
],
|
|
1471
|
+
sienna: [
|
|
1472
|
+
160,
|
|
1473
|
+
82,
|
|
1474
|
+
45
|
|
1475
|
+
],
|
|
1476
|
+
silver: [
|
|
1477
|
+
192,
|
|
1478
|
+
192,
|
|
1479
|
+
192
|
|
1480
|
+
],
|
|
1481
|
+
skyblue: [
|
|
1482
|
+
135,
|
|
1483
|
+
206,
|
|
1484
|
+
235
|
|
1485
|
+
],
|
|
1486
|
+
slateblue: [
|
|
1487
|
+
106,
|
|
1488
|
+
90,
|
|
1489
|
+
205
|
|
1490
|
+
],
|
|
1491
|
+
slategray: [
|
|
1492
|
+
112,
|
|
1493
|
+
128,
|
|
1494
|
+
144
|
|
1495
|
+
],
|
|
1496
|
+
slategrey: [
|
|
1497
|
+
112,
|
|
1498
|
+
128,
|
|
1499
|
+
144
|
|
1500
|
+
],
|
|
1501
|
+
snow: [
|
|
1502
|
+
255,
|
|
1503
|
+
250,
|
|
1504
|
+
250
|
|
1505
|
+
],
|
|
1506
|
+
springgreen: [
|
|
1507
|
+
0,
|
|
1508
|
+
255,
|
|
1509
|
+
127
|
|
1510
|
+
],
|
|
1511
|
+
steelblue: [
|
|
1512
|
+
70,
|
|
1513
|
+
130,
|
|
1514
|
+
180
|
|
1515
|
+
],
|
|
1516
|
+
tan: [
|
|
1517
|
+
210,
|
|
1518
|
+
180,
|
|
1519
|
+
140
|
|
1520
|
+
],
|
|
1521
|
+
teal: [
|
|
1522
|
+
0,
|
|
1523
|
+
128,
|
|
1524
|
+
128
|
|
1525
|
+
],
|
|
1526
|
+
thistle: [
|
|
1527
|
+
216,
|
|
1528
|
+
191,
|
|
1529
|
+
216
|
|
1530
|
+
],
|
|
1531
|
+
tomato: [
|
|
1532
|
+
255,
|
|
1533
|
+
99,
|
|
1534
|
+
71
|
|
1535
|
+
],
|
|
1536
|
+
turquoise: [
|
|
1537
|
+
64,
|
|
1538
|
+
224,
|
|
1539
|
+
208
|
|
1540
|
+
],
|
|
1541
|
+
violet: [
|
|
1542
|
+
238,
|
|
1543
|
+
130,
|
|
1544
|
+
238
|
|
1545
|
+
],
|
|
1546
|
+
wheat: [
|
|
1547
|
+
245,
|
|
1548
|
+
222,
|
|
1549
|
+
179
|
|
1550
|
+
],
|
|
1551
|
+
white: [
|
|
1552
|
+
255,
|
|
1553
|
+
255,
|
|
1554
|
+
255
|
|
1555
|
+
],
|
|
1556
|
+
whitesmoke: [
|
|
1557
|
+
245,
|
|
1558
|
+
245,
|
|
1559
|
+
245
|
|
1560
|
+
],
|
|
1561
|
+
yellow: [
|
|
1562
|
+
255,
|
|
1563
|
+
255,
|
|
1564
|
+
0
|
|
1565
|
+
],
|
|
1566
|
+
yellowgreen: [
|
|
1567
|
+
154,
|
|
1568
|
+
205,
|
|
1569
|
+
50
|
|
1570
|
+
]
|
|
1571
|
+
};
|
|
1572
|
+
// src/color/isLiteralColor.ts
|
|
1573
|
+
/**
|
|
1574
|
+
* Tell literal CSS colors from palette references.
|
|
1575
|
+
*
|
|
1576
|
+
* Literal means the string is a color by itself: `#hex`, `rgb(...)` /
|
|
1577
|
+
* `rgba(...)`, or a CSS named color (case-insensitive) — as opposed to
|
|
1578
|
+
* theme references like `'brand-l2'`.
|
|
1579
|
+
*
|
|
1580
|
+
* @param s - the color string to classify
|
|
1581
|
+
* @returns `true` when `s` is directly usable as a CSS color value
|
|
1582
|
+
*/
|
|
1583
|
+
function isLiteralColor(s) {
|
|
1584
|
+
return s.startsWith("#") || s.startsWith("rgb") || s.toLowerCase() in NAMED_COLORS;
|
|
1585
|
+
}
|
|
1586
|
+
// src/color/toRgb.ts
|
|
1587
|
+
/**
|
|
1588
|
+
* Resolve any literal CSS color string to an sRGB triple.
|
|
1589
|
+
*
|
|
1590
|
+
* Dispatches on the format: `#hex` → {@link hexToRgb}, `rgb(...)` →
|
|
1591
|
+
* {@link parseRgbString}, anything else is looked up among the CSS named
|
|
1592
|
+
* colors (case-insensitive). Theme references are rejected.
|
|
1593
|
+
*
|
|
1594
|
+
* @param color - a literal color string
|
|
1595
|
+
* @returns the sRGB triple
|
|
1596
|
+
* @throws when `color` is not a literal color (e.g. a palette reference)
|
|
1597
|
+
*/
|
|
1598
|
+
function toRgb(color) {
|
|
1599
|
+
if (!isLiteralColor(color)) throw new Error("unknown literal color format: " + color);
|
|
1600
|
+
if (color.startsWith("#")) return hexToRgb(color);
|
|
1601
|
+
if (color.startsWith("rgb")) return parseRgbString(color);
|
|
1602
|
+
return NAMED_COLORS[color.toLowerCase()];
|
|
1603
|
+
}
|
|
1604
|
+
// src/color/rgbToHsl.ts
|
|
1605
|
+
/**
|
|
1606
|
+
* Convert an sRGB triple to HSL.
|
|
1607
|
+
*
|
|
1608
|
+
* @param rgb - the color (channels 0-255)
|
|
1609
|
+
* @returns `[hue 0-360, saturation 0-100, lightness 0-100]` (fractional)
|
|
1610
|
+
*/
|
|
1611
|
+
function rgbToHsl([r, g, b]) {
|
|
1612
|
+
r /= 255;
|
|
1613
|
+
g /= 255;
|
|
1614
|
+
b /= 255;
|
|
1615
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
1616
|
+
let h = 0, s;
|
|
1617
|
+
const l = (max + min) / 2;
|
|
1618
|
+
if (max === min) h = s = 0;
|
|
1619
|
+
else {
|
|
1620
|
+
const d = max - min;
|
|
1621
|
+
s = l > .5 ? d / (2 - max - min) : d / (max + min);
|
|
1622
|
+
switch (max) {
|
|
1623
|
+
case r:
|
|
1624
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
1625
|
+
break;
|
|
1626
|
+
case g:
|
|
1627
|
+
h = (b - r) / d + 2;
|
|
1628
|
+
break;
|
|
1629
|
+
case b: h = (r - g) / d + 4;
|
|
1630
|
+
}
|
|
1631
|
+
h /= 6;
|
|
1632
|
+
}
|
|
1633
|
+
return [
|
|
1634
|
+
h * 360,
|
|
1635
|
+
s * 100,
|
|
1636
|
+
l * 100
|
|
1637
|
+
];
|
|
1638
|
+
}
|
|
1639
|
+
// src/color/hslToRgb.ts
|
|
1640
|
+
/**
|
|
1641
|
+
* Convert an HSL triple to a fractional sRGB triple.
|
|
1642
|
+
*
|
|
1643
|
+
* @param hsl - `[hue 0-360, saturation 0-100, lightness 0-100]`
|
|
1644
|
+
* @returns fractional sRGB channels (0-255, not rounded)
|
|
1645
|
+
*/
|
|
1646
|
+
function hslToRgb([h, s, l]) {
|
|
1647
|
+
h /= 360;
|
|
1648
|
+
s /= 100;
|
|
1649
|
+
l /= 100;
|
|
1650
|
+
let r, g, b;
|
|
1651
|
+
if (s === 0) r = g = b = l;
|
|
1652
|
+
else {
|
|
1653
|
+
const hue2rgb = (p, q, t) => {
|
|
1654
|
+
if (t < 0) t += 1;
|
|
1655
|
+
if (t > 1) t -= 1;
|
|
1656
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
1657
|
+
if (t < 1 / 2) return q;
|
|
1658
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
1659
|
+
return p;
|
|
1660
|
+
};
|
|
1661
|
+
const q = l < .5 ? l * (1 + s) : l + s - l * s;
|
|
1662
|
+
const p = 2 * l - q;
|
|
1663
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
1664
|
+
g = hue2rgb(p, q, h);
|
|
1665
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
1666
|
+
}
|
|
1667
|
+
return [
|
|
1668
|
+
r * 255,
|
|
1669
|
+
g * 255,
|
|
1670
|
+
b * 255
|
|
1671
|
+
];
|
|
1672
|
+
}
|
|
1673
|
+
// src/color/withLightnessHsl.ts
|
|
1674
|
+
/**
|
|
1675
|
+
* Rebuild a color with a new HSL lightness, keeping hue and saturation.
|
|
1676
|
+
*
|
|
1677
|
+
* @param rgb - the source color (sRGB triple)
|
|
1678
|
+
* @param l - the target lightness, percent 0-100
|
|
1679
|
+
* @returns the recolored sRGB triple (fractional channels)
|
|
1680
|
+
*/
|
|
1681
|
+
function withLightnessHsl(rgb, l) {
|
|
1682
|
+
const [h, s] = rgbToHsl(rgb);
|
|
1683
|
+
return hslToRgb([
|
|
1684
|
+
h,
|
|
1685
|
+
s,
|
|
1686
|
+
l
|
|
1687
|
+
]);
|
|
1688
|
+
}
|
|
1689
|
+
// src/color/lightnessOfHsl.ts
|
|
1690
|
+
/**
|
|
1691
|
+
* Read the HSL lightness (percent, 0-100) of an sRGB color.
|
|
1692
|
+
*
|
|
1693
|
+
* @param rgb - the color to measure (sRGB triple)
|
|
1694
|
+
* @returns the lightness percentage (fractional)
|
|
1695
|
+
*/
|
|
1696
|
+
function lightnessOfHsl(rgb) {
|
|
1697
|
+
return rgbToHsl(rgb)[2];
|
|
1698
|
+
}
|
|
1699
|
+
// src/color/srgbToLinear.ts
|
|
1700
|
+
/**
|
|
1701
|
+
* Decode one gamma-encoded sRGB channel to linear light.
|
|
1702
|
+
*
|
|
1703
|
+
* @param v - the sRGB channel, 0-255
|
|
1704
|
+
* @returns the linear-light value (0-1)
|
|
1705
|
+
*/
|
|
1706
|
+
function srgbToLinear(v) {
|
|
1707
|
+
v /= 255;
|
|
1708
|
+
return v <= .04045 ? v / 12.92 : ((v + .055) / 1.055) ** 2.4;
|
|
1709
|
+
}
|
|
1710
|
+
// src/color/linearToSrgb.ts
|
|
1711
|
+
/**
|
|
1712
|
+
* Gamma-encode one linear-light channel to sRGB.
|
|
1713
|
+
*
|
|
1714
|
+
* @param v - the linear channel, nominally 0-1
|
|
1715
|
+
* @returns the sRGB channel, 0-255, clamped to the gamut
|
|
1716
|
+
*/
|
|
1717
|
+
function linearToSrgb(v) {
|
|
1718
|
+
const s = v <= .0031308 ? v * 12.92 : 1.055 * v ** (1 / 2.4) - .055;
|
|
1719
|
+
return Math.min(255, Math.max(0, s * 255));
|
|
1720
|
+
}
|
|
1721
|
+
// src/color/linearRgbToOklab.ts
|
|
1722
|
+
/**
|
|
1723
|
+
* Convert a linear-light sRGB triple to OKLab.
|
|
1724
|
+
*
|
|
1725
|
+
* Björn Ottosson's LMS-based transform; the input must be linear (run
|
|
1726
|
+
* {@link srgbToLinear} over gamma-encoded channels first).
|
|
1727
|
+
*
|
|
1728
|
+
* @param rgb - linear sRGB channels (0-1)
|
|
1729
|
+
* @returns `[L, a, b]` OKLab coordinates
|
|
1730
|
+
*/
|
|
1731
|
+
function linearRgbToOklab([r, g, b]) {
|
|
1732
|
+
const l = .4122214708 * r + .5363325363 * g + .0514459929 * b;
|
|
1733
|
+
const m = .2119034982 * r + .6806995451 * g + .1073969566 * b;
|
|
1734
|
+
const s = .0883024619 * r + .2817188376 * g + .6299787005 * b;
|
|
1735
|
+
const l_ = Math.cbrt(l), m_ = Math.cbrt(m), s_ = Math.cbrt(s);
|
|
1736
|
+
return [
|
|
1737
|
+
.2104542553 * l_ + .793617785 * m_ - .0040720468 * s_,
|
|
1738
|
+
1.9779984951 * l_ - 2.428592205 * m_ + .4505937099 * s_,
|
|
1739
|
+
.0259040371 * l_ + .7827717662 * m_ - .808675766 * s_
|
|
1740
|
+
];
|
|
1741
|
+
}
|
|
1742
|
+
// src/color/oklabToLinearRgb.ts
|
|
1743
|
+
/**
|
|
1744
|
+
* Convert OKLab coordinates back to a linear-light sRGB triple.
|
|
1745
|
+
*
|
|
1746
|
+
* Inverse of {@link linearRgbToOklab}; run {@link linearToSrgb} over the
|
|
1747
|
+
* result to get gamma-encoded channels.
|
|
1748
|
+
*
|
|
1749
|
+
* @param L - OKLab lightness
|
|
1750
|
+
* @param a - green-red opponent channel
|
|
1751
|
+
* @param b - blue-yellow opponent channel
|
|
1752
|
+
* @returns linear sRGB channels (0-1)
|
|
1753
|
+
*/
|
|
1754
|
+
function oklabToLinearRgb([L, a, b]) {
|
|
1755
|
+
const l_ = L + .3963377774 * a + .2158037573 * b;
|
|
1756
|
+
const m_ = L - .1055613458 * a - .0638541728 * b;
|
|
1757
|
+
const s_ = L - .0894841775 * a - 1.291485548 * b;
|
|
1758
|
+
const l = l_ ** 3, m = m_ ** 3, s = s_ ** 3;
|
|
1759
|
+
return [
|
|
1760
|
+
4.0767416621 * l - 3.3077115913 * m + .2309699292 * s,
|
|
1761
|
+
-1.2684380046 * l + 2.6097574011 * m - .3413193965 * s,
|
|
1762
|
+
-.0041960863 * l - .7034186147 * m + 1.707614701 * s
|
|
1763
|
+
];
|
|
1764
|
+
}
|
|
1765
|
+
// src/color/rgbToOklch.ts
|
|
1766
|
+
/**
|
|
1767
|
+
* Convert an sRGB triple to OKLCH (via linear sRGB + OKLab).
|
|
1768
|
+
*
|
|
1769
|
+
* @param rgb - the color (channels 0-255)
|
|
1770
|
+
* @returns `[L 0-100, C, H degrees]` (fractional)
|
|
1771
|
+
*/
|
|
1772
|
+
function rgbToOklch(rgb) {
|
|
1773
|
+
const [L, a, b] = linearRgbToOklab(rgb.map(srgbToLinear));
|
|
1774
|
+
return [
|
|
1775
|
+
L * 100,
|
|
1776
|
+
Math.sqrt(a * a + b * b),
|
|
1777
|
+
Math.atan2(b, a) * (180 / Math.PI)
|
|
1778
|
+
];
|
|
1779
|
+
}
|
|
1780
|
+
// src/color/oklchToRgb.ts
|
|
1781
|
+
/**
|
|
1782
|
+
* Convert OKLCH coordinates to a gamma-encoded sRGB triple.
|
|
1783
|
+
*
|
|
1784
|
+
* @param L100 - lightness in percent (0-100)
|
|
1785
|
+
* @param C - chroma
|
|
1786
|
+
* @param H - hue in degrees
|
|
1787
|
+
* @returns the sRGB triple (channels 0-255, fractional)
|
|
1788
|
+
*/
|
|
1789
|
+
function oklchToRgb([L100, C, H]) {
|
|
1790
|
+
const hRad = H * (Math.PI / 180);
|
|
1791
|
+
return oklabToLinearRgb([
|
|
1792
|
+
L100 / 100,
|
|
1793
|
+
C * Math.cos(hRad),
|
|
1794
|
+
C * Math.sin(hRad)
|
|
1795
|
+
]).map(linearToSrgb);
|
|
1796
|
+
}
|
|
1797
|
+
// src/color/withLightnessOklch.ts
|
|
1798
|
+
/**
|
|
1799
|
+
* Rebuild a color with a new OKLCH lightness, keeping chroma and hue.
|
|
1800
|
+
*
|
|
1801
|
+
* @param rgb - the source color (sRGB triple)
|
|
1802
|
+
* @param l - the target lightness, percent 0-100
|
|
1803
|
+
* @returns the recolored sRGB triple (fractional channels)
|
|
1804
|
+
*/
|
|
1805
|
+
function withLightnessOklch(rgb, l) {
|
|
1806
|
+
const [, C, H] = rgbToOklch(rgb);
|
|
1807
|
+
return oklchToRgb([
|
|
1808
|
+
l,
|
|
1809
|
+
C,
|
|
1810
|
+
H
|
|
1811
|
+
]);
|
|
1812
|
+
}
|
|
1813
|
+
// src/color/lightnessOfOklch.ts
|
|
1814
|
+
/**
|
|
1815
|
+
* Read the OKLCH lightness (percent, 0-100) of an sRGB color.
|
|
1816
|
+
*
|
|
1817
|
+
* @param rgb - the color to measure (sRGB triple)
|
|
1818
|
+
* @returns the OKLCH L dimension (fractional)
|
|
1819
|
+
*/
|
|
1820
|
+
function lightnessOfOklch(rgb) {
|
|
1821
|
+
return rgbToOklch(rgb)[0];
|
|
1822
|
+
}
|
|
1823
|
+
// src/color/spaces.ts
|
|
1824
|
+
/**
|
|
1825
|
+
* The color spaces the ramp math (`lighten` / `darken`) can run in.
|
|
1826
|
+
*
|
|
1827
|
+
* `oklch` is perceptually uniform — a ramp looks evenly spaced regardless
|
|
1828
|
+
* of hue; `hsl` is the legacy (cheaper) path.
|
|
1829
|
+
*/
|
|
1830
|
+
const SPACES = {
|
|
1831
|
+
hsl: {
|
|
1832
|
+
lightnessOf: lightnessOfHsl,
|
|
1833
|
+
withLightness: withLightnessHsl
|
|
1834
|
+
},
|
|
1835
|
+
oklch: {
|
|
1836
|
+
lightnessOf: lightnessOfOklch,
|
|
1837
|
+
withLightness: withLightnessOklch
|
|
1838
|
+
}
|
|
1839
|
+
};
|
|
1840
|
+
// src/color/lighten.ts
|
|
1841
|
+
/**
|
|
1842
|
+
* Lighten a color by `step` steps of a `steps`-step ramp.
|
|
1843
|
+
*
|
|
1844
|
+
* Scales the color's lightness up toward 100 percent proportionally
|
|
1845
|
+
* (`l -> l + (100 - l) * (step / steps)`) in the given color space;
|
|
1846
|
+
* `oklch` keeps ramps perceptually even, `hsl` is the legacy path.
|
|
1847
|
+
*
|
|
1848
|
+
* @param rgb - the color to lighten (sRGB triple)
|
|
1849
|
+
* @param step - how many steps up the ramp (1-based)
|
|
1850
|
+
* @param steps - the total number of ramp steps
|
|
1851
|
+
* @param space - the color space the lightness math runs in
|
|
1852
|
+
* @returns the lightened color (fractional channels)
|
|
1853
|
+
*/
|
|
1854
|
+
function lighten(rgb, step, steps, space = "hsl") {
|
|
1855
|
+
const { lightnessOf, withLightness } = SPACES[space];
|
|
1856
|
+
const l0 = lightnessOf(rgb);
|
|
1857
|
+
return withLightness(rgb, l0 + (100 - l0) * (step / steps));
|
|
1858
|
+
}
|
|
1859
|
+
// src/color/darken.ts
|
|
1860
|
+
/**
|
|
1861
|
+
* Darken a color by `step` steps of a `steps`-step ramp.
|
|
1862
|
+
*
|
|
1863
|
+
* Scales the color's lightness down proportionally
|
|
1864
|
+
* (`l -> l * (1 - step / steps)`) in the given color space; `oklch` keeps
|
|
1865
|
+
* ramps perceptually even, `hsl` is the legacy path.
|
|
1866
|
+
*
|
|
1867
|
+
* @param rgb - the color to darken (sRGB triple)
|
|
1868
|
+
* @param step - how many steps down the ramp (1-based)
|
|
1869
|
+
* @param steps - the total number of ramp steps
|
|
1870
|
+
* @param space - the color space the lightness math runs in
|
|
1871
|
+
* @returns the darkened color (fractional channels)
|
|
1872
|
+
*/
|
|
1873
|
+
function darken(rgb, step, steps, space = "hsl") {
|
|
1874
|
+
const { lightnessOf, withLightness } = SPACES[space];
|
|
1875
|
+
return withLightness(rgb, lightnessOf(rgb) * (1 - step / steps));
|
|
1876
|
+
}
|
|
1877
|
+
// src/color/blend.ts
|
|
1878
|
+
/**
|
|
1879
|
+
* Mix two sRGB colors by a depth percentage.
|
|
1880
|
+
*
|
|
1881
|
+
* Linear interpolation per channel: `depthPercent` of 0 returns `rgb1`
|
|
1882
|
+
* untouched, 100 returns `rgb2`; the channels stay fractional (not
|
|
1883
|
+
* rounded).
|
|
1884
|
+
*
|
|
1885
|
+
* @param rgb1 - the base color (sRGB triple, channels 0-255)
|
|
1886
|
+
* @param rgb2 - the color blended in
|
|
1887
|
+
* @param depthPercent - blend weight of `rgb2`, 0-100
|
|
1888
|
+
* @returns the mixed color as a fractional sRGB triple
|
|
1889
|
+
*/
|
|
1890
|
+
function blend(rgb1, rgb2, depthPercent) {
|
|
1891
|
+
const t = depthPercent / 100;
|
|
1892
|
+
return [
|
|
1893
|
+
0,
|
|
1894
|
+
1,
|
|
1895
|
+
2
|
|
1896
|
+
].map((i) => rgb1[i] * (1 - t) + rgb2[i] * t);
|
|
1897
|
+
}
|
|
1898
|
+
// src/props/castAnyPropToBoolean.ts
|
|
1899
|
+
/**
|
|
1900
|
+
* Coerces an arbitrary prop value to a boolean the way presence-classes
|
|
1901
|
+
* expect it: an empty string means "present" (`true`), an empty array means
|
|
1902
|
+
* "absent" (`false`), anything else falls through to the default truthiness
|
|
1903
|
+
* rules.
|
|
1904
|
+
* @param value - the prop value
|
|
1905
|
+
* @returns the boolean presence of the value
|
|
1906
|
+
*/
|
|
1907
|
+
function castAnyPropToBoolean(value) {
|
|
1908
|
+
if (typeof value === "string" && value === "") return true;
|
|
1909
|
+
if (typeof value === "object" && value instanceof Array && !value.length) return false;
|
|
1910
|
+
return !!value;
|
|
1911
|
+
}
|
|
1912
|
+
// src/props/namesToClassNames.ts
|
|
1913
|
+
/**
|
|
1914
|
+
* Builds a `{ [className]: true }` record from a list of state names,
|
|
1915
|
+
* keeping only the names whose prop value is "on" (defined, not `false`
|
|
1916
|
+
* and not a function).
|
|
1917
|
+
* @param names - space-separated string, list, or map of state names
|
|
1918
|
+
* @param props - the props to test each name against
|
|
1919
|
+
* @param className - prefix joined with `_` to each name; empty = bare name
|
|
1920
|
+
* @param debug - log the inputs before computing
|
|
1921
|
+
* @returns record of `className_name -> true` for the active names
|
|
1922
|
+
*/
|
|
1923
|
+
function namesToClassNames(names, props = {}, className = "state", debug = false) {
|
|
1924
|
+
debug && console.log("names to classname", names, props, className);
|
|
1925
|
+
const out = {};
|
|
1926
|
+
let list = [];
|
|
1927
|
+
if (typeof names === "object") list = names instanceof Array ? names : Object.keys(names || []);
|
|
1928
|
+
else list = names.split(" ");
|
|
1929
|
+
list.forEach((item) => {
|
|
1930
|
+
const cn = className ? `${className}_${item}` : item;
|
|
1931
|
+
if (props[item] !== void 0 && props[item] !== false && typeof props[item] !== "function") out[cn] = true;
|
|
1932
|
+
});
|
|
1933
|
+
return out;
|
|
1934
|
+
}
|
|
1935
|
+
// src/props/propValueToClass.ts
|
|
1936
|
+
/**
|
|
1937
|
+
* Converts a prop value to a class via a value -> class alias record: the
|
|
1938
|
+
* alias may be a single class name or a list of class names.
|
|
1939
|
+
* @param propValue - the prop's value token
|
|
1940
|
+
* @param values - record of value token -> class alias (string or string[])
|
|
1941
|
+
* @returns `{ className: true }` for the aliased class(es), `{}` when the
|
|
1942
|
+
* value has no alias or the alias is neither a string nor a list
|
|
1943
|
+
*/
|
|
1944
|
+
function propValueToClass(propValue, values) {
|
|
1945
|
+
if (!values[propValue]) return {};
|
|
1946
|
+
if (typeof values[propValue] === "string") return { [values[propValue]]: true };
|
|
1947
|
+
if (typeof values[propValue] === "object" && values[propValue] instanceof Array) return values[propValue].reduce((acc, cur) => ({
|
|
1948
|
+
...acc,
|
|
1949
|
+
[cur]: true
|
|
1950
|
+
}), {});
|
|
1951
|
+
return {};
|
|
1952
|
+
}
|
|
1953
|
+
// src/props/propValueToClasses.ts
|
|
1954
|
+
/**
|
|
1955
|
+
* Converts a prop value to class names via a value -> class alias record:
|
|
1956
|
+
* the alias may be a single class name or a list of class names.
|
|
1957
|
+
* @param propValue - the prop's value token
|
|
1958
|
+
* @param values - record of value token -> class alias (string or string[])
|
|
1959
|
+
* @returns the aliased class name list, `[]` when the value has no alias or
|
|
1960
|
+
* the alias is neither a string nor a list
|
|
1961
|
+
*/
|
|
1962
|
+
function propValueToClasses(propValue, values) {
|
|
1963
|
+
if (!values[propValue]) return [];
|
|
1964
|
+
if (typeof values[propValue] === "string") return [values[propValue]];
|
|
1965
|
+
if (typeof values[propValue] === "object" && values[propValue] instanceof Array) return values[propValue];
|
|
1966
|
+
return [];
|
|
1967
|
+
}
|
|
1968
|
+
// src/props/propsToCssModuleClass.ts
|
|
1969
|
+
/**
|
|
1970
|
+
* Converts prop names and values to css-module classes, returned as a
|
|
1971
|
+
* `{ [className]: boolean }` record (a `false` entry means the prop is off
|
|
1972
|
+
* but the class slot is still reported).
|
|
1973
|
+
*
|
|
1974
|
+
* A picked entry is either a bare prop name - the prop's own class is keyed
|
|
1975
|
+
* by the css module and the value's tokens add their own module classes - or
|
|
1976
|
+
* a `[propName, modifiers]` tuple that maps the value's tokens onto modifier
|
|
1977
|
+
* classes via {@link propValueToClass}.
|
|
1978
|
+
* @param props - the instance props
|
|
1979
|
+
* @param picked - the props to map (bare names or `[name, modifiers]` tuples)
|
|
1980
|
+
* @param cssModule - the css-module class map
|
|
1981
|
+
* @returns record of class name -> on/off flag
|
|
1982
|
+
*/
|
|
1983
|
+
function propsToCssModuleClass(props, picked, cssModule) {
|
|
1984
|
+
let out = {};
|
|
1985
|
+
function getRootClassBoolean(field) {
|
|
1986
|
+
let out = void 0;
|
|
1987
|
+
if (props[field] !== void 0) out = castAnyPropToBoolean(props[field]);
|
|
1988
|
+
return out;
|
|
1989
|
+
}
|
|
1990
|
+
picked.forEach((pickedProp) => {
|
|
1991
|
+
if (typeof pickedProp === "string") {
|
|
1992
|
+
const cn = cssModule[pickedProp];
|
|
1993
|
+
const value = props[pickedProp];
|
|
1994
|
+
if (value !== void 0 && cn) {
|
|
1995
|
+
if (typeof value === "boolean") out[cn] = value;
|
|
1996
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "object" && value instanceof Array) {
|
|
1997
|
+
out[cn] = true;
|
|
1998
|
+
let arr = [];
|
|
1999
|
+
if (typeof value === "string" || typeof value === "number") arr = value.toString().split(" ");
|
|
2000
|
+
if (value instanceof Array) arr = value;
|
|
2001
|
+
arr.forEach((val) => {
|
|
2002
|
+
const modifierName = cssModule[val];
|
|
2003
|
+
if (modifierName) out[modifierName] = true;
|
|
2004
|
+
});
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
if (typeof pickedProp === "object" && pickedProp instanceof Array && pickedProp.length === 2) {
|
|
2009
|
+
const _pickedProp = pickedProp[0];
|
|
2010
|
+
const _pickedPropValue = pickedProp[1];
|
|
2011
|
+
const _pickedPropClassName = cssModule[_pickedProp];
|
|
2012
|
+
const value = getRootClassBoolean(_pickedProp);
|
|
2013
|
+
if (typeof props[_pickedProp] === "string" || typeof props[_pickedProp] === "number" || typeof props[_pickedProp] === "boolean" || typeof props[_pickedProp] === "object" && props[_pickedProp] instanceof Array) {
|
|
2014
|
+
let classValues = [];
|
|
2015
|
+
if (props[_pickedProp] instanceof Array && props[_pickedProp].length) classValues = props[_pickedProp];
|
|
2016
|
+
if (typeof props[_pickedProp] === "string" || typeof props[_pickedProp] === "number") classValues = String(props[_pickedProp]).split(" ");
|
|
2017
|
+
if (typeof props[_pickedProp] === "boolean" && props[_pickedProp] || props[_pickedProp] === "") classValues = ["true"];
|
|
2018
|
+
let outClassValues = {};
|
|
2019
|
+
classValues.forEach((val) => {
|
|
2020
|
+
if (val !== void 0 && val.trim()) outClassValues = {
|
|
2021
|
+
...outClassValues,
|
|
2022
|
+
...propValueToClass(val, _pickedPropValue)
|
|
2023
|
+
};
|
|
2024
|
+
});
|
|
2025
|
+
out = {
|
|
2026
|
+
...out,
|
|
2027
|
+
...outClassValues
|
|
2028
|
+
};
|
|
2029
|
+
}
|
|
2030
|
+
if (_pickedPropClassName) out = {
|
|
2031
|
+
...out,
|
|
2032
|
+
[_pickedPropClassName]: value || false
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
2035
|
+
});
|
|
2036
|
+
return out;
|
|
2037
|
+
}
|
|
2038
|
+
// src/props/propsToCssModuleClassNext.ts
|
|
2039
|
+
/**
|
|
2040
|
+
* Converts prop names and values to a flat list of css-module classes -
|
|
2041
|
+
* the array form of {@link propsToCssModuleClass}: same mapping rules, but
|
|
2042
|
+
* only the "on" classes are collected (no `false` entries) and an extra
|
|
2043
|
+
* class can be prepended via `options.extraClass`.
|
|
2044
|
+
* @param props - the instance props
|
|
2045
|
+
* @param picked - the props to map (bare names or `[name, modifiers]` tuples)
|
|
2046
|
+
* @param cssModule - the css-module class map
|
|
2047
|
+
* @param options - `extraClass` is prepended to the result when given
|
|
2048
|
+
* @returns the resolved class name list
|
|
2049
|
+
*/
|
|
2050
|
+
function propsToCssModuleClassNext(props, picked, cssModule, options) {
|
|
2051
|
+
let out = [];
|
|
2052
|
+
const { extraClass = void 0 } = options || {};
|
|
2053
|
+
if (extraClass) out.push(extraClass);
|
|
2054
|
+
function getRootClassBoolean(field) {
|
|
2055
|
+
let out = void 0;
|
|
2056
|
+
if (props[field] !== void 0) out = castAnyPropToBoolean(props[field]);
|
|
2057
|
+
return out;
|
|
2058
|
+
}
|
|
2059
|
+
picked.forEach((pickedProp) => {
|
|
2060
|
+
if (typeof pickedProp === "string") {
|
|
2061
|
+
const cn = cssModule[pickedProp];
|
|
2062
|
+
const value = props[pickedProp];
|
|
2063
|
+
if (value !== void 0 && cn) {
|
|
2064
|
+
if (typeof value === "boolean" && value) out.push(cn);
|
|
2065
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "object" && value instanceof Array) {
|
|
2066
|
+
out.push(cn);
|
|
2067
|
+
let arr = [];
|
|
2068
|
+
if (typeof value === "string" || typeof value === "number") arr = value.toString().split(" ");
|
|
2069
|
+
if (value instanceof Array) arr = value;
|
|
2070
|
+
arr.forEach((val) => {
|
|
2071
|
+
const modifierName = cssModule[val];
|
|
2072
|
+
if (modifierName) out.push(modifierName);
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
if (typeof pickedProp === "object" && pickedProp instanceof Array && pickedProp.length === 2) {
|
|
2078
|
+
const _pickedProp = pickedProp[0];
|
|
2079
|
+
const _pickedPropValue = pickedProp[1];
|
|
2080
|
+
const _pickedPropClassName = cssModule[_pickedProp];
|
|
2081
|
+
const value = getRootClassBoolean(_pickedProp);
|
|
2082
|
+
if (typeof props[_pickedProp] === "string" || typeof props[_pickedProp] === "number" || typeof props[_pickedProp] === "boolean" || typeof props[_pickedProp] === "object" && props[_pickedProp] instanceof Array) {
|
|
2083
|
+
let classValues = [];
|
|
2084
|
+
if (props[_pickedProp] instanceof Array && props[_pickedProp].length) classValues = props[_pickedProp];
|
|
2085
|
+
if (typeof props[_pickedProp] === "string" || typeof props[_pickedProp] === "number") classValues = String(props[_pickedProp]).split(" ");
|
|
2086
|
+
if (typeof props[_pickedProp] === "boolean" && props[_pickedProp] || props[_pickedProp] === "") classValues = ["true"];
|
|
2087
|
+
let outClassValues = [];
|
|
2088
|
+
classValues.forEach((val) => {
|
|
2089
|
+
if (val !== void 0 && val.trim()) outClassValues = [...outClassValues, ...propValueToClasses(val, _pickedPropValue)];
|
|
2090
|
+
});
|
|
2091
|
+
out = [...out, ...outClassValues];
|
|
2092
|
+
}
|
|
2093
|
+
if (_pickedPropClassName && !!value) out.push(_pickedPropClassName);
|
|
2094
|
+
}
|
|
2095
|
+
});
|
|
2096
|
+
return out;
|
|
2097
|
+
}
|
|
2098
|
+
// src/browser-check.ts
|
|
2099
|
+
const isBrowser = typeof window !== "undefined";
|
|
2100
|
+
function safetyWindow() {
|
|
2101
|
+
return window && typeof window !== "undefined" ? window : {};
|
|
2102
|
+
}
|
|
2103
|
+
function safetyDocument() {
|
|
2104
|
+
return typeof document !== "undefined" ? document : {};
|
|
2105
|
+
}
|
|
2106
|
+
// src/container/record-container.ts
|
|
2107
|
+
var RecordContainer = class {
|
|
2108
|
+
records;
|
|
2109
|
+
counter = 0;
|
|
2110
|
+
options;
|
|
2111
|
+
constructor(options = {}) {
|
|
2112
|
+
this.records = {};
|
|
2113
|
+
this.options = options;
|
|
2114
|
+
}
|
|
2115
|
+
/** Returns the item with the highest remaining ID, or undefined when empty. */
|
|
2116
|
+
get last() {
|
|
2117
|
+
let lastId = -1;
|
|
2118
|
+
for (const key in this.records) if (Object.hasOwn(this.records, key)) lastId = Math.max(lastId, Number(key));
|
|
2119
|
+
if (lastId === -1) return void 0;
|
|
2120
|
+
return this.records[lastId];
|
|
2121
|
+
}
|
|
2122
|
+
/** Returns a shallow snapshot; item objects retain their original references. */
|
|
2123
|
+
get raw() {
|
|
2124
|
+
return { ...this.records };
|
|
2125
|
+
}
|
|
2126
|
+
get items() {
|
|
2127
|
+
return Object.values(this.records);
|
|
2128
|
+
}
|
|
2129
|
+
get length() {
|
|
2130
|
+
return Object.keys(this.records).length;
|
|
2131
|
+
}
|
|
2132
|
+
clear() {
|
|
2133
|
+
for (const i in this.records) delete this.records[i];
|
|
2134
|
+
}
|
|
2135
|
+
/** Validates and transforms an item before allocating its ID; returns -1 on rejection. */
|
|
2136
|
+
add(item) {
|
|
2137
|
+
if (this.options.onBeforeChange) {
|
|
2138
|
+
if (!this.options.onBeforeChange(item)) return -1;
|
|
2139
|
+
}
|
|
2140
|
+
const value = this.options.onChange ? this.options.onChange(item) : item;
|
|
2141
|
+
const id = this.counter++;
|
|
2142
|
+
this.records[id] = value;
|
|
2143
|
+
return id;
|
|
2144
|
+
}
|
|
2145
|
+
remove(id) {
|
|
2146
|
+
delete this.records[id];
|
|
2147
|
+
}
|
|
2148
|
+
getItemById(id) {
|
|
2149
|
+
return this.records[id];
|
|
2150
|
+
}
|
|
2151
|
+
};
|
|
2152
|
+
// src/dom/getClientHeight.ts
|
|
2153
|
+
function getClientHeight() {
|
|
2154
|
+
if (isBrowser) return window.innerHeight;
|
|
2155
|
+
else return 0;
|
|
2156
|
+
}
|
|
2157
|
+
// src/dom/getClientWidth.ts
|
|
2158
|
+
function getClientWidth() {
|
|
2159
|
+
if (isBrowser) return window.innerWidth;
|
|
2160
|
+
else return 0;
|
|
2161
|
+
}
|
|
2162
|
+
export { NAMED_COLORS, RecordContainer, SPACES, arrayInsertAtIndex, arrayIsEqual, arrayOmit, arrayOmitArray, arrayPickFromItemOffset, arrayPickNext, arrayPickPrev, arrayToNumberArray, blend, castAnyPropToBoolean, clamp, darken, decimalAdjust, exponentialToNumber, extractFileDir, extractFileName, findFrameworkInstance, genRandomString, getClientHeight, getClientWidth, getFloatDigitsCount, getItemByOverflowIndex, getLastItemFromArray, hexToRgb, hslToRgb, hyphenate, isArray, isBrowser, isFloat, isFunction, isLiteralColor, isNumberish, isObject, isString, isSymbol, isValidNumber, kebabize, lighten, lightnessOfHsl, lightnessOfOklch, linearRgbToOklab, linearToSrgb, mergeCssModulesStyles, namesToClassNames, newErrorAppExists, newErrorAppNotFound, newErrorForSetupCall, newErrorUseAppCall, newFrameworkError, notUndefined, numberCast, objectMerge, objectOmitKeys, objectPickKeys, objectToCss, oklabToLinearRgb, oklchToRgb, parseCssTime, parseQueryString, parseRgbString, parseUnits, propValueToClass, propValueToClasses, propsToCssModuleClass, propsToCssModuleClassNext, removeVowels, rgbToHsl, rgbToOklch, safetyDocument, safetyWindow, sha256hex, splitString, srgbToLinear, stringToFirstLetters, toCamelCase, toKebabCase, toPascalCase, toRgb, toUnique, toUnits, tryToUnits, withLightnessHsl, withLightnessOklch };
|
|
2163
|
+
|
|
2164
|
+
//# sourceMappingURL=index.js.map
|