@semantic-ui/utils
Advanced tools
Comparing version 0.0.25 to 0.0.26
@@ -11,3 +11,3 @@ { | ||
}, | ||
"version": "0.0.25" | ||
"version": "0.0.26" | ||
} |
148
src/utils.js
@@ -44,3 +44,3 @@ /* | ||
/*------------------- | ||
DOM | ||
Browser | ||
--------------------*/ | ||
@@ -52,2 +52,31 @@ | ||
export const getKeyFromEvent = (event) => { | ||
let pressedKey = event?.key; | ||
if(!pressedKey) { | ||
return ''; | ||
} | ||
let key = ''; | ||
if(event.ctrlKey && pressedKey !== 'Control') { | ||
key += 'ctrl+'; | ||
} | ||
if(event.altKey && pressedKey !== 'Alt') { | ||
key += 'alt+'; | ||
} | ||
if(event.shiftKey && pressedKey !== 'Shift') { | ||
key += 'shift+'; | ||
} | ||
if(event.metaKey && pressedKey !== 'Meta') { | ||
key += 'meta+'; | ||
} | ||
// standardize key names | ||
const specialKeys = { | ||
Control: 'ctrl', | ||
Escape: 'esc', | ||
' ': 'space', | ||
}; | ||
pressedKey = pressedKey.replace('Arrow', ''); // ArrowUp -> up | ||
key += specialKeys[pressedKey] || pressedKey.toLowerCase(); | ||
return key; | ||
} | ||
/*------------------- | ||
@@ -373,2 +402,15 @@ XHR | ||
export const getArticle = (word, settings = {}) => { | ||
const vowels = ['a', 'e', 'i', 'o', 'u']; | ||
const firstLetter = word.toLowerCase()[0]; | ||
const article = vowels.includes(firstLetter) | ||
? 'an' | ||
: 'a' | ||
; | ||
return (settings.capitalize) | ||
? capitalize(article) | ||
: article | ||
; | ||
}; | ||
/*------------------- | ||
@@ -893,2 +935,5 @@ Arrays | ||
export const roundNumber = (number, digits = 5) => { | ||
if(number == 0) { | ||
return 0; | ||
} | ||
if(!isNumber(number) || !Number.isFinite(number) || digits <= 0) { | ||
@@ -953,16 +998,20 @@ return number; | ||
/* | ||
Create a uniqueID from a string | ||
*/ | ||
export function hashCode(input) { | ||
let str; | ||
* Create a uniqueID from a string using an adapted UMASH algorithm | ||
https://github.com/backtrace-labs/umash | ||
*/ | ||
export function hashCode(input, { prettify = false, seed = 0x12345678 } = {}) { | ||
const prime1 = 0x9e3779b1; | ||
const prime2 = 0x85ebca77; | ||
const prime3 = 0xc2b2ae3d; | ||
// Convert input to a string | ||
if ( | ||
input && | ||
input.toString === Object.prototype.toString && | ||
typeof input === 'object' | ||
) { | ||
let inputData; | ||
if (input === null || input === undefined) { | ||
inputData = new TextEncoder().encode(''); | ||
} | ||
else if (input && input.toString === Object.prototype.toString && typeof input === 'object') { | ||
try { | ||
str = JSON.stringify(input); | ||
} catch (error) { | ||
inputData = new TextEncoder().encode(JSON.stringify(input)); | ||
} | ||
catch (error) { | ||
console.error('Error serializing input', error); | ||
@@ -973,56 +1022,39 @@ return 0; | ||
else { | ||
str = input.toString(); | ||
inputData = new TextEncoder().encode(input.toString()); | ||
} | ||
const seed = 0x12345678; | ||
let hash; | ||
const murmurhash = (key, seed) => { | ||
let h1 = seed; | ||
const c1 = 0xcc9e2d51; | ||
const c2 = 0x1b873593; | ||
const round = (k) => { | ||
k = (k * c1) & 0xffffffff; | ||
k = (k << 15) | (k >>> 17); | ||
k = (k * c2) & 0xffffffff; | ||
h1 ^= k; | ||
h1 = (h1 << 13) | (h1 >>> 19); | ||
h1 = (h1 * 5 + 0xe6546b64) & 0xffffffff; | ||
}; | ||
for (let i = 0; i < key.length; i += 4) { | ||
let k = | ||
(key.charCodeAt(i) & 0xff) | | ||
((key.charCodeAt(i + 1) & 0xff) << 8) | | ||
((key.charCodeAt(i + 2) & 0xff) << 16) | | ||
((key.charCodeAt(i + 3) & 0xff) << 24); | ||
round(k); | ||
if (inputData.length <= 8) { | ||
// optimize performance for short inputs | ||
hash = seed; | ||
for (let i = 0; i < inputData.length; i++) { | ||
hash ^= inputData[i]; | ||
hash = Math.imul(hash, prime1); | ||
hash ^= hash >>> 13; | ||
} | ||
let k1 = 0; | ||
switch (key.length & 3) { | ||
case 3: | ||
k1 ^= (key.charCodeAt(key.length - 1) & 0xff) << 16; | ||
case 2: | ||
k1 ^= (key.charCodeAt(key.length - 2) & 0xff) << 8; | ||
case 1: | ||
k1 ^= key.charCodeAt(key.length - 3) & 0xff; | ||
round(k1); | ||
} | ||
else { | ||
// compress input blocks while maintaining good mixing properties | ||
hash = seed; | ||
for (let i = 0; i < inputData.length; i++) { | ||
hash = Math.imul(hash ^ inputData[i], prime1); | ||
hash = (hash << 13) | (hash >>> 19); | ||
hash = Math.imul(hash, prime2); | ||
} | ||
h1 ^= key.length; | ||
h1 ^= h1 >>> 16; | ||
h1 = (h1 * 0x85ebca6b) & 0xffffffff; | ||
h1 ^= h1 >>> 13; | ||
h1 = (h1 * 0xc2b2ae35) & 0xffffffff; | ||
h1 ^= h1 >>> 16; | ||
// protect against length extension attacks | ||
hash ^= inputData.length; | ||
} | ||
return h1 >>> 0; | ||
}; | ||
// improve the distribution and avalanche properties of the hash | ||
hash ^= hash >>> 16; | ||
hash = Math.imul(hash, prime3); | ||
hash ^= hash >>> 13; | ||
let hash; | ||
hash = murmurhash(str, seed); // fast and pretty good collisions | ||
hash = prettifyID(hash); // pretty and easier to recognize | ||
if (prettify) { | ||
return prettifyID(hash >>> 0); | ||
} | ||
return hash; | ||
return hash >>> 0; | ||
} | ||
@@ -1029,0 +1061,0 @@ |
@@ -6,2 +6,3 @@ import { describe, expect, it, vi } from 'vitest'; | ||
asyncMap, | ||
any, | ||
camelToKebab, | ||
@@ -22,2 +23,3 @@ capitalizeWords, | ||
get, | ||
getKeyFromEvent, | ||
groupBy, | ||
@@ -29,2 +31,3 @@ hashCode, | ||
isArray, | ||
isEmpty, | ||
isBinary, | ||
@@ -44,2 +47,3 @@ isEqual, | ||
memoize, | ||
onlyKeys, | ||
noop, | ||
@@ -54,3 +58,6 @@ pick, | ||
some, | ||
isServer, | ||
isClient, | ||
sortBy, | ||
sum, | ||
toTitleCase, | ||
@@ -166,2 +173,28 @@ tokenize, | ||
describe('isEmpty', () => { | ||
it('should return true for null or undefined', () => { | ||
expect(isEmpty(null)).toBe(true); | ||
expect(isEmpty(undefined)).toBe(true); | ||
}); | ||
it('should return true for empty arrays', () => { | ||
expect(isEmpty([])).toBe(true); | ||
}); | ||
it('should return true for empty objects', () => { | ||
expect(isEmpty({})).toBe(true); | ||
}); | ||
it('should return false for non-empty arrays', () => { | ||
expect(isEmpty([1, 2, 3])).toBe(false); | ||
}); | ||
it('should return true if all keys are undefined', () => { | ||
expect(isEmpty({ a: undefined })).toBe(true); | ||
}); | ||
it('should return false for non-empty objects', () => { | ||
expect(isEmpty({ a: 1, b: 2 })).toBe(false); | ||
}); | ||
}); | ||
}); | ||
@@ -202,5 +235,83 @@ | ||
// Add more tests here for other number utilities if needed | ||
describe('sum', () => { | ||
it('should return the sum of an array of numbers', () => { | ||
expect(sum([1, 2, 3, 4, 5])).toBe(15); | ||
}); | ||
it('should return 0 for an empty array', () => { | ||
expect(sum([])).toBe(0); | ||
}); | ||
}); | ||
}); | ||
describe('Browser Utilities', () => { | ||
describe('getKeyFromEvent', () => { | ||
it('should return an empty string if the event.key is not defined', () => { | ||
const event = { ctrlKey: true }; | ||
expect(getKeyFromEvent(event)).toBe(''); | ||
}); | ||
it('should return an empty string if the event has no key property', () => { | ||
const event = {}; | ||
expect(getKeyFromEvent(event)).toBe(''); | ||
}); | ||
it('should return the lowercase key for a simple key press', () => { | ||
const event = { key: 'A' }; | ||
expect(getKeyFromEvent(event)).toBe('a'); | ||
}); | ||
it('should return the correct key for a special key press', () => { | ||
const event = { key: 'ArrowUp' }; | ||
expect(getKeyFromEvent(event)).toBe('up'); | ||
}); | ||
it('should include the "ctrl" modifier when the ctrlKey is pressed', () => { | ||
const event = { key: 'a', ctrlKey: true }; | ||
expect(getKeyFromEvent(event)).toBe('ctrl+a'); | ||
}); | ||
it('should include the "alt" modifier when the altKey is pressed', () => { | ||
const event = { key: 'b', altKey: true }; | ||
expect(getKeyFromEvent(event)).toBe('alt+b'); | ||
}); | ||
it('should include the "shift" modifier when the shiftKey is pressed', () => { | ||
const event = { key: 'c', shiftKey: true }; | ||
expect(getKeyFromEvent(event)).toBe('shift+c'); | ||
}); | ||
it('should include the "meta" modifier when the metaKey is pressed', () => { | ||
const event = { key: 'd', metaKey: true }; | ||
expect(getKeyFromEvent(event)).toBe('meta+d'); | ||
}); | ||
it('should include multiple modifiers when multiple modifier keys are pressed', () => { | ||
const event = { key: 'e', ctrlKey: true, altKey: true, shiftKey: true, metaKey: true }; | ||
expect(getKeyFromEvent(event)).toBe('ctrl+alt+shift+meta+e'); | ||
}); | ||
it('should return the correct key for the space key', () => { | ||
const event = { key: ' ' }; | ||
expect(getKeyFromEvent(event)).toBe('space'); | ||
}); | ||
}); | ||
describe('isServer', () => { | ||
it('should return true if window is undefined', () => { | ||
expect(isServer).toBe(true); | ||
}); | ||
}); | ||
describe('isClient', () => { | ||
it('should return false if window is undefined', () => { | ||
expect(isClient).toBe(false); | ||
}); | ||
}); | ||
}); | ||
describe('Type Checking Utilities', () => { | ||
@@ -612,6 +723,6 @@ | ||
}); | ||
/* | ||
it('should format date with local timezone', () => { | ||
expect(formatDate(date, 'YYYY-MM-DD HH:mm:ss', { timezone: 'local' })).toBe('2023-05-18 ' + date.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })); | ||
}); | ||
});*/ | ||
@@ -713,2 +824,4 @@ it('should format date with predefined format (LT)', () => { | ||
describe('pick', () => { | ||
@@ -900,3 +1013,22 @@ it('pick should create an object composed of the picked properties', () => { | ||
describe('onlyKeys', () => { | ||
it('should return an object with only the specified keys', () => { | ||
const obj = { a: 1, b: 2, c: 3 }; | ||
const result = onlyKeys(obj, ['a', 'c']); | ||
expect(result).toEqual({ a: 1, c: 3 }); | ||
}); | ||
it('should return an empty object if no keys are specified', () => { | ||
const obj = { a: 1, b: 2, c: 3 }; | ||
const result = onlyKeys(obj, []); | ||
expect(result).toEqual({}); | ||
}); | ||
}); | ||
describe('any', () => { | ||
it('should be an alias for some', () => { | ||
expect(any).toBe(some); | ||
}); | ||
}); | ||
}); | ||
@@ -1226,2 +1358,93 @@ | ||
}); | ||
it('should produce different hash codes for similar inputs', () => { | ||
const input1 = 'Test String'; | ||
const input2 = 'test string'; | ||
expect(hashCode(input1)).not.toBe(hashCode(input2)); | ||
}); | ||
it('should handle extremely long inputs', () => { | ||
const longInput = 'a'.repeat(100000); | ||
expect(() => hashCode(longInput)).not.toThrow(); | ||
}); | ||
it('should handle inputs with special characters', () => { | ||
const specialInput = '!@#$%^&*()_+{}[]|\\:;"<>,.?/~`'; | ||
expect(() => hashCode(specialInput)).not.toThrow(); | ||
}); | ||
it('should handle inputs with Unicode characters', () => { | ||
const unicodeInput = '你好世界 こんにちは世界 안녕하세요 세계'; | ||
expect(() => hashCode(unicodeInput)).not.toThrow(); | ||
}); | ||
it('should handle inputs with emojis', () => { | ||
const emojiInput = '😀😃😄😁😆😅😂🤣'; | ||
expect(() => hashCode(emojiInput)).not.toThrow(); | ||
}); | ||
it('should handle inputs with control characters', () => { | ||
const controlInput = '\n\r\t\b\f'; | ||
expect(() => hashCode(controlInput)).not.toThrow(); | ||
}); | ||
it('should handle empty input', () => { | ||
expect(hashCode('')).toBeDefined(); | ||
}); | ||
it('should handle null input', () => { | ||
expect(hashCode(null)).toBeDefined(); | ||
}); | ||
it('should handle undefined input', () => { | ||
expect(hashCode(undefined)).toBeDefined(); | ||
}); | ||
it('should handle input with leading/trailing whitespace', () => { | ||
const input1 = 'Test String'; | ||
const input2 = ' Test String '; | ||
expect(hashCode(input1)).not.toBe(hashCode(input2)); | ||
}); | ||
it('should handle input with large integers', () => { | ||
const input = { value: Number.MAX_SAFE_INTEGER }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
it('should handle input with small integers', () => { | ||
const input = { value: Number.MIN_SAFE_INTEGER }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
it('should handle input with floating-point numbers', () => { | ||
const input = { value: 3.14159265359 }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
it('should handle input with Infinity', () => { | ||
const input = { value: Infinity }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
it('should handle input with -Infinity', () => { | ||
const input = { value: -Infinity }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
it('should handle input with NaN', () => { | ||
const input = { value: NaN }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
/* Annoying logs | ||
it('should handle circular references in objects', () => { | ||
const input = { a: 1 }; | ||
input.self = input; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
});*/ | ||
it('should handle deeply nested objects', () => { | ||
const input = { a: { b: { c: { d: { e: 'nested' } } } } }; | ||
expect(() => hashCode(input)).not.toThrow(); | ||
}); | ||
}); | ||
@@ -1252,2 +1475,8 @@ | ||
it('should return the input value if it is not an object or a function', () => { | ||
expect(clone(123)).toBe(123); | ||
expect(clone('hello')).toBe('hello'); | ||
expect(clone(true)).toBe(true); | ||
}); | ||
it('should deeply clone an array', () => { | ||
@@ -1254,0 +1483,0 @@ const originalArray = [{ a: 1 }, { b: 2 }]; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
89008
2506