@lumx/core 3.20.1-alpha.26 → 3.20.1-alpha.28

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.
Files changed (43) hide show
  1. package/js/utils/className/getBasicClass.js +2 -3
  2. package/js/utils/className/getBasicClass.test.js +1 -2
  3. package/js/utils/className/getBasicClass.ts +2 -3
  4. package/js/utils/className/getRootClassName.js +1 -1
  5. package/js/utils/className/getRootClassName.test.js +1 -1
  6. package/js/utils/className/getRootClassName.ts +1 -2
  7. package/js/utils/className/handleBasicClasses.js +3 -4
  8. package/js/utils/className/handleBasicClasses.test.js +2 -3
  9. package/js/utils/className/handleBasicClasses.ts +3 -5
  10. package/js/utils/className/index.js +2 -3
  11. package/js/utils/collection/castArray.js +4 -0
  12. package/js/utils/collection/castArray.test.js +15 -0
  13. package/js/utils/collection/castArray.test.ts +15 -0
  14. package/js/utils/collection/castArray.ts +3 -0
  15. package/js/utils/collection/chunk.js +8 -0
  16. package/js/utils/collection/chunk.test.js +13 -0
  17. package/js/utils/collection/chunk.test.ts +15 -0
  18. package/js/utils/collection/chunk.ts +6 -0
  19. package/js/utils/collection/isEmpty.js +4 -0
  20. package/js/utils/collection/isEmpty.test.js +20 -0
  21. package/js/utils/collection/isEmpty.test.ts +20 -0
  22. package/js/utils/collection/isEmpty.ts +4 -0
  23. package/js/utils/collection/last.js +4 -0
  24. package/js/utils/collection/last.ts +2 -0
  25. package/js/utils/collection/partitionMulti.js +28 -0
  26. package/js/utils/collection/partitionMulti.test.js +26 -0
  27. package/js/utils/collection/partitionMulti.test.ts +35 -0
  28. package/js/utils/collection/partitionMulti.ts +29 -0
  29. package/js/utils/collection/pull.js +9 -0
  30. package/js/utils/collection/pull.test.js +16 -0
  31. package/js/utils/collection/pull.test.ts +17 -0
  32. package/js/utils/collection/pull.ts +7 -0
  33. package/js/utils/collection/range.js +6 -0
  34. package/js/utils/collection/range.test.js +9 -0
  35. package/js/utils/collection/range.test.ts +9 -0
  36. package/js/utils/collection/range.ts +2 -0
  37. package/js/utils/index.js +6 -8
  38. package/js/utils/index.ts +4 -6
  39. package/js/utils/string/kebabCase.js +28 -0
  40. package/js/utils/string/kebabCase.test.js +41 -0
  41. package/js/utils/string/kebabCase.test.ts +50 -0
  42. package/js/utils/string/kebabCase.ts +28 -0
  43. package/package.json +9 -4
@@ -1,5 +1,4 @@
1
- import isBoolean from 'lodash/isBoolean';
2
- import kebabCase from 'lodash/kebabCase';
1
+ import { kebabCase } from '../string/kebabCase.js';
3
2
 
4
3
  /**
5
4
  * Get the basic CSS class for the given type.
@@ -14,7 +13,7 @@ function getBasicClass({
14
13
  type,
15
14
  value
16
15
  }) {
17
- if (isBoolean(value)) {
16
+ if (typeof value === 'boolean') {
18
17
  if (!value) {
19
18
  // False value should not return a class.
20
19
  return '';
@@ -1,6 +1,5 @@
1
1
  import { getBasicClass } from './getBasicClass.js';
2
- import 'lodash/isBoolean';
3
- import 'lodash/kebabCase';
2
+ import '../string/kebabCase.js';
4
3
 
5
4
  describe(getBasicClass, () => {
6
5
  it('should return correct basic CSS class for different types and values', () => {
@@ -1,5 +1,4 @@
1
- import isBoolean from 'lodash/isBoolean';
2
- import kebabCase from 'lodash/kebabCase';
1
+ import { kebabCase } from '../string/kebabCase';
3
2
 
4
3
  /**
5
4
  * Get the basic CSS class for the given type.
@@ -18,7 +17,7 @@ export function getBasicClass({
18
17
  type: string;
19
18
  value: string | number | boolean | undefined;
20
19
  }): string {
21
- if (isBoolean(value)) {
20
+ if (typeof value === 'boolean') {
22
21
  if (!value) {
23
22
  // False value should not return a class.
24
23
  return '';
@@ -1,5 +1,5 @@
1
- import kebabCase from 'lodash/kebabCase';
2
1
  import { CSS_PREFIX } from '../../constants/index.js';
2
+ import { kebabCase } from '../string/kebabCase.js';
3
3
  import '../../constants/keycodes.js';
4
4
 
5
5
  // See https://regex101.com/r/YjS1uI/3
@@ -1,7 +1,7 @@
1
1
  import { getRootClassName } from './getRootClassName.js';
2
- import 'lodash/kebabCase';
3
2
  import '../../constants/index.js';
4
3
  import '../../constants/keycodes.js';
4
+ import '../string/kebabCase.js';
5
5
 
6
6
  describe(getRootClassName, () => {
7
7
  it('should transform the component name into a lumx class', () => {
@@ -1,6 +1,5 @@
1
- import kebabCase from 'lodash/kebabCase';
2
-
3
1
  import { CSS_PREFIX } from '../../constants';
2
+ import { kebabCase } from '../string/kebabCase';
4
3
 
5
4
  // See https://regex101.com/r/YjS1uI/3
6
5
  const LAST_PART_CLASSNAME = /^(.*)-(.+)$/gi;
@@ -1,8 +1,7 @@
1
1
  import classNames from 'classnames';
2
- import isBoolean from 'lodash/isBoolean';
3
- import isEmpty from 'lodash/isEmpty';
2
+ import { isEmpty } from '@lumx/core/js/utils/collection/isEmpty';
4
3
  import { getBasicClass } from './getBasicClass.js';
5
- import 'lodash/kebabCase';
4
+ import '../string/kebabCase.js';
6
5
 
7
6
  /**
8
7
  * Enhance isEmpty method to also works with numbers.
@@ -39,7 +38,7 @@ function handleBasicClasses({
39
38
  prefix,
40
39
  type: prop,
41
40
  value: props[prop]
42
- })] = isBoolean(props[prop]) ? props[prop] : !_isEmpty(props[prop]);
41
+ })] = typeof props[prop] === 'boolean' ? props[prop] : !_isEmpty(props[prop]);
43
42
  });
44
43
  }
45
44
  return classNames(prefix, otherClasses);
@@ -1,9 +1,8 @@
1
1
  import { handleBasicClasses } from './handleBasicClasses.js';
2
2
  import 'classnames';
3
- import 'lodash/isBoolean';
4
- import 'lodash/isEmpty';
3
+ import '@lumx/core/js/utils/collection/isEmpty';
5
4
  import './getBasicClass.js';
6
- import 'lodash/kebabCase';
5
+ import '../string/kebabCase.js';
7
6
 
8
7
  describe(handleBasicClasses, () => {
9
8
  it('should return correct combined CSS classes based on props', () => {
@@ -1,7 +1,6 @@
1
1
  import classNames from 'classnames';
2
2
 
3
- import isBoolean from 'lodash/isBoolean';
4
- import isEmpty from 'lodash/isEmpty';
3
+ import { isEmpty } from '@lumx/core/js/utils/collection/isEmpty';
5
4
 
6
5
  import { getBasicClass } from './getBasicClass';
7
6
 
@@ -34,9 +33,8 @@ export function handleBasicClasses({ prefix, ...props }: { prefix: string; [prop
34
33
  const otherClasses: any = {};
35
34
  if (!isEmpty(props)) {
36
35
  Object.keys(props).forEach((prop) => {
37
- otherClasses[getBasicClass({ prefix, type: prop, value: props[prop] })] = isBoolean(props[prop])
38
- ? props[prop]
39
- : !_isEmpty(props[prop]);
36
+ otherClasses[getBasicClass({ prefix, type: prop, value: props[prop] })] =
37
+ typeof props[prop] === 'boolean' ? props[prop] : !_isEmpty(props[prop]);
40
38
  });
41
39
  }
42
40
 
@@ -5,8 +5,7 @@ export { getTypographyClassName } from './getTypographyClassName.js';
5
5
  export { fontColorClass } from './fontColorClass.js';
6
6
  export { resolveColorWithVariants } from './resolveColorWithVariants.js';
7
7
  import 'classnames';
8
- import 'lodash/isBoolean';
9
- import 'lodash/isEmpty';
10
- import 'lodash/kebabCase';
8
+ import '@lumx/core/js/utils/collection/isEmpty';
9
+ import '../string/kebabCase.js';
11
10
  import '../../constants/index.js';
12
11
  import '../../constants/keycodes.js';
@@ -0,0 +1,4 @@
1
+ /** Cast a value into an array if needed */
2
+ const castArray = arrayOrElement => Array.isArray(arrayOrElement) ? arrayOrElement : [arrayOrElement];
3
+
4
+ export { castArray };
@@ -0,0 +1,15 @@
1
+ import { castArray } from '@lumx/core/js/utils/collection/castArray';
2
+
3
+ describe(castArray, () => {
4
+ it('should keep existing array', () => {
5
+ const input = [1, 2];
6
+ const output = castArray(input);
7
+ expect(output).toEqual([1, 2]);
8
+ expect(output).toBe(input);
9
+ });
10
+ it('should cast item to array', () => {
11
+ const input = 1;
12
+ const output = castArray(input);
13
+ expect(output).toEqual([1]);
14
+ });
15
+ });
@@ -0,0 +1,15 @@
1
+ import { castArray } from '@lumx/core/js/utils/collection/castArray';
2
+
3
+ describe(castArray, () => {
4
+ it('should keep existing array', () => {
5
+ const input = [1, 2];
6
+ const output = castArray(input);
7
+ expect(output).toEqual([1, 2]);
8
+ expect(output).toBe(input);
9
+ });
10
+ it('should cast item to array', () => {
11
+ const input = 1;
12
+ const output = castArray(input);
13
+ expect(output).toEqual([1]);
14
+ });
15
+ });
@@ -0,0 +1,3 @@
1
+ /** Cast a value into an array if needed */
2
+ export const castArray = <T>(arrayOrElement: T[] | T) =>
3
+ Array.isArray(arrayOrElement) ? arrayOrElement : [arrayOrElement];
@@ -0,0 +1,8 @@
1
+ /** Chunk array in slices of given size */
2
+ function chunk(input, size) {
3
+ return input.reduce((arr, item, idx) => {
4
+ return idx % size === 0 ? [...arr, [item]] : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
5
+ }, []);
6
+ }
7
+
8
+ export { chunk };
@@ -0,0 +1,13 @@
1
+ import { chunk } from '@lumx/core/js/utils/collection/chunk';
2
+
3
+ describe(chunk, () => {
4
+ it('should do nothing on empty array', () => {
5
+ expect(chunk([], 2)).toEqual([]);
6
+ });
7
+ it('should work with size larger than input array', () => {
8
+ expect(chunk([1, 2], 4)).toEqual([[1, 2]]);
9
+ });
10
+ it('should chunk array with size not perfectly dividing the array length', () => {
11
+ expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
12
+ });
13
+ });
@@ -0,0 +1,15 @@
1
+ import { chunk } from '@lumx/core/js/utils/collection/chunk';
2
+
3
+ describe(chunk, () => {
4
+ it('should do nothing on empty array', () => {
5
+ expect(chunk([], 2)).toEqual([]);
6
+ });
7
+
8
+ it('should work with size larger than input array', () => {
9
+ expect(chunk([1, 2], 4)).toEqual([[1, 2]]);
10
+ });
11
+
12
+ it('should chunk array with size not perfectly dividing the array length', () => {
13
+ expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
14
+ });
15
+ });
@@ -0,0 +1,6 @@
1
+ /** Chunk array in slices of given size */
2
+ export function chunk<T>(input: Array<T>, size: number): T[][] {
3
+ return input.reduce((arr, item, idx) => {
4
+ return idx % size === 0 ? [...arr, [item]] : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]];
5
+ }, [] as T[][]);
6
+ }
@@ -0,0 +1,4 @@
1
+ /** Check if object or array is empty (true on falsy values) */
2
+ const isEmpty = obj => !obj || Object.entries(obj).length === 0;
3
+
4
+ export { isEmpty };
@@ -0,0 +1,20 @@
1
+ import { isEmpty } from './isEmpty.js';
2
+
3
+ describe(isEmpty, () => {
4
+ it('should return true for falsy values', () => {
5
+ expect(isEmpty(undefined)).toBe(true);
6
+ expect(isEmpty(null)).toBe(true);
7
+ expect(isEmpty(0)).toBe(true);
8
+ expect(isEmpty('')).toBe(true);
9
+ });
10
+ it('should return true for empty object or array', () => {
11
+ expect(isEmpty([])).toBe(true);
12
+ expect(isEmpty({})).toBe(true);
13
+ });
14
+ it('should return false for non empty object or array', () => {
15
+ expect(isEmpty([''])).toBe(false);
16
+ expect(isEmpty({
17
+ foo: false
18
+ })).toBe(false);
19
+ });
20
+ });
@@ -0,0 +1,20 @@
1
+ import { isEmpty } from './isEmpty';
2
+
3
+ describe(isEmpty, () => {
4
+ it('should return true for falsy values', () => {
5
+ expect(isEmpty(undefined)).toBe(true);
6
+ expect(isEmpty(null)).toBe(true);
7
+ expect(isEmpty(0)).toBe(true);
8
+ expect(isEmpty('')).toBe(true);
9
+ });
10
+
11
+ it('should return true for empty object or array', () => {
12
+ expect(isEmpty([])).toBe(true);
13
+ expect(isEmpty({})).toBe(true);
14
+ });
15
+
16
+ it('should return false for non empty object or array', () => {
17
+ expect(isEmpty([''])).toBe(false);
18
+ expect(isEmpty({ foo: false })).toBe(false);
19
+ });
20
+ });
@@ -0,0 +1,4 @@
1
+ import type { Falsy } from '../../types';
2
+
3
+ /** Check if object or array is empty (true on falsy values) */
4
+ export const isEmpty = (obj: {} | Falsy) => !obj || Object.entries(obj).length === 0;
@@ -0,0 +1,4 @@
1
+ /** Get last item from array */
2
+ const last = array => array[array.length - 1];
3
+
4
+ export { last };
@@ -0,0 +1,2 @@
1
+ /** Get last item from array */
2
+ export const last = <T>(array: Array<T>): T | undefined => array[array.length - 1];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Similar to lodash `partition` function but working with multiple predicates.
3
+ *
4
+ * @example
5
+ * const isString = (s) => typeof s === 'string'
6
+ * const isNumber = (s) => typeof s === 'number'
7
+ * const [strings, numbers, others] = partitionMulti(['a', 1, 'b', false], [isString, isNumber])
8
+ * //=> [['a', 'b'], [1], [false]]
9
+ *
10
+ * @param elements array of elements
11
+ * @param predicates array of predicates to apply on elements
12
+ * @return partitioned elements by the given predicates
13
+ */
14
+ function partitionMulti(elements, predicates) {
15
+ const others = [];
16
+ const groups = predicates.map(() => []);
17
+ for (const element of elements) {
18
+ const index = predicates.findIndex(predicate => predicate(element));
19
+ if (index !== -1) {
20
+ groups[index].push(element);
21
+ } else {
22
+ others.push(element);
23
+ }
24
+ }
25
+ return [...groups, others];
26
+ }
27
+
28
+ export { partitionMulti };
@@ -0,0 +1,26 @@
1
+ import { partitionMulti } from './partitionMulti.js';
2
+
3
+ describe('partitionMulti', () => {
4
+ it('should partition with single predicate', () => {
5
+ const data = [0, 1, 2, 3, 4, 5];
6
+ const isEven = n => n % 2 === 0;
7
+ const actual = partitionMulti(data, [isEven]);
8
+ expect(actual).toEqual([[0, 2, 4], [1, 3, 5]]);
9
+ });
10
+ it('should partition on multiple predicates', () => {
11
+ const data = ['a', 1, 'b', false, true];
12
+ const isString = s => typeof s === 'string';
13
+ const isNumber = s => typeof s === 'number';
14
+ const isNull = s => s === null;
15
+ const partitions = partitionMulti(data, [isString, isNumber, isNull]);
16
+ expect(partitions).toEqual([
17
+ // strings
18
+ ['a', 'b'],
19
+ // numbers
20
+ [1],
21
+ // nulls
22
+ [],
23
+ // others
24
+ [false, true]]);
25
+ });
26
+ });
@@ -0,0 +1,35 @@
1
+ import { partitionMulti } from './partitionMulti';
2
+
3
+ describe('partitionMulti', () => {
4
+ it('should partition with single predicate', () => {
5
+ const data = [0, 1, 2, 3, 4, 5];
6
+ const isEven = (n: number): boolean => n % 2 === 0;
7
+
8
+ const actual = partitionMulti(data, [isEven]);
9
+
10
+ expect(actual).toEqual([
11
+ [0, 2, 4],
12
+ [1, 3, 5],
13
+ ]);
14
+ });
15
+
16
+ it('should partition on multiple predicates', () => {
17
+ type T = string | number | boolean;
18
+ const data: T[] = ['a', 1, 'b', false, true];
19
+ const isString = (s: T): boolean => typeof s === 'string';
20
+ const isNumber = (s: T): boolean => typeof s === 'number';
21
+ const isNull = (s: T): boolean => s === null;
22
+
23
+ const partitions = partitionMulti(data, [isString, isNumber, isNull]);
24
+ expect(partitions).toEqual([
25
+ // strings
26
+ ['a', 'b'],
27
+ // numbers
28
+ [1],
29
+ // nulls
30
+ [],
31
+ // others
32
+ [false, true],
33
+ ]);
34
+ });
35
+ });
@@ -0,0 +1,29 @@
1
+ import type { Predicate } from '@lumx/core/js/types';
2
+
3
+ /**
4
+ * Similar to lodash `partition` function but working with multiple predicates.
5
+ *
6
+ * @example
7
+ * const isString = (s) => typeof s === 'string'
8
+ * const isNumber = (s) => typeof s === 'number'
9
+ * const [strings, numbers, others] = partitionMulti(['a', 1, 'b', false], [isString, isNumber])
10
+ * //=> [['a', 'b'], [1], [false]]
11
+ *
12
+ * @param elements array of elements
13
+ * @param predicates array of predicates to apply on elements
14
+ * @return partitioned elements by the given predicates
15
+ */
16
+ export function partitionMulti<T>(elements: T[], predicates: Array<Predicate<T>>): T[][] {
17
+ const others = [] as T[];
18
+ const groups = predicates.map(() => []) as T[][];
19
+
20
+ for (const element of elements) {
21
+ const index = predicates.findIndex((predicate) => predicate(element));
22
+ if (index !== -1) {
23
+ groups[index].push(element);
24
+ } else {
25
+ others.push(element);
26
+ }
27
+ }
28
+ return [...groups, others];
29
+ }
@@ -0,0 +1,9 @@
1
+ /** Pull an element from an array (inverse of array.push) */
2
+ const pull = (array, element) => {
3
+ const index = array.indexOf(element);
4
+ if (index > -1) {
5
+ array.splice(index, 1);
6
+ }
7
+ };
8
+
9
+ export { pull };
@@ -0,0 +1,16 @@
1
+ import { pull } from '@lumx/core/js/utils/collection//pull';
2
+
3
+ describe(pull, () => {
4
+ it('should do nothing if element does not exist', () => {
5
+ const a = [1, 2];
6
+ pull(a, 0);
7
+ expect(a).toBe(a);
8
+ expect(a).toEqual([1, 2]);
9
+ });
10
+ it('should pull an element from the array', () => {
11
+ const a = [1, 2];
12
+ pull(a, 1);
13
+ expect(a).toBe(a);
14
+ expect(a).toEqual([2]);
15
+ });
16
+ });
@@ -0,0 +1,17 @@
1
+ import { pull } from '@lumx/core/js/utils/collection//pull';
2
+
3
+ describe(pull, () => {
4
+ it('should do nothing if element does not exist', () => {
5
+ const a = [1, 2];
6
+ pull(a, 0);
7
+ expect(a).toBe(a);
8
+ expect(a).toEqual([1, 2]);
9
+ });
10
+
11
+ it('should pull an element from the array', () => {
12
+ const a = [1, 2];
13
+ pull(a, 1);
14
+ expect(a).toBe(a);
15
+ expect(a).toEqual([2]);
16
+ });
17
+ });
@@ -0,0 +1,7 @@
1
+ /** Pull an element from an array (inverse of array.push) */
2
+ export const pull = <T>(array: Array<T>, element: T) => {
3
+ const index = array.indexOf(element);
4
+ if (index > -1) {
5
+ array.splice(index, 1);
6
+ }
7
+ };
@@ -0,0 +1,6 @@
1
+ /** Generate a range of number starting at 0 and ending before the given number */
2
+ const range = end => Array.from({
3
+ length: end
4
+ }, (_, i) => i);
5
+
6
+ export { range };
@@ -0,0 +1,9 @@
1
+ import { range } from './range.js';
2
+
3
+ describe(range, () => {
4
+ it('should generate a number range', () => {
5
+ expect(range(0)).toEqual([]);
6
+ expect(range(1)).toEqual([0]);
7
+ expect(range(5)).toEqual([0, 1, 2, 3, 4]);
8
+ });
9
+ });
@@ -0,0 +1,9 @@
1
+ import { range } from './range';
2
+
3
+ describe(range, () => {
4
+ it('should generate a number range', () => {
5
+ expect(range(0)).toEqual([]);
6
+ expect(range(1)).toEqual([0]);
7
+ expect(range(5)).toEqual([0, 1, 2, 3, 4]);
8
+ });
9
+ });
@@ -0,0 +1,2 @@
1
+ /** Generate a range of number starting at 0 and ending before the given number */
2
+ export const range = (end: number) => Array.from({ length: end }, (_, i) => i);
package/js/utils/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import noop from 'lodash/noop';
2
1
  export { handleBasicClasses } from './className/handleBasicClasses.js';
3
2
  export { getBasicClass } from './className/getBasicClass.js';
4
3
  export { getRootClassName } from './className/getRootClassName.js';
@@ -6,9 +5,8 @@ export { getTypographyClassName } from './className/getTypographyClassName.js';
6
5
  export { fontColorClass } from './className/fontColorClass.js';
7
6
  export { resolveColorWithVariants } from './className/resolveColorWithVariants.js';
8
7
  import 'classnames';
9
- import 'lodash/isBoolean';
10
- import 'lodash/isEmpty';
11
- import 'lodash/kebabCase';
8
+ import '@lumx/core/js/utils/collection/isEmpty';
9
+ import './string/kebabCase.js';
12
10
  import '../constants/index.js';
13
11
  import '../constants/keycodes.js';
14
12
 
@@ -65,7 +63,7 @@ function onButtonPressed(handler) {
65
63
  * @param handleSwipe Callback function.
66
64
  * @return Function to remove listeners.
67
65
  */
68
- function detectSwipe(touchSurface, handleSwipe = noop) {
66
+ function detectSwipe(touchSurface, handleSwipe) {
69
67
  let distX;
70
68
  let distY;
71
69
  let startX;
@@ -113,7 +111,7 @@ function detectSwipe(touchSurface, handleSwipe = noop) {
113
111
  direction = distY < 0 ? 'up' : 'down';
114
112
  }
115
113
  }
116
- handleSwipe(direction);
114
+ handleSwipe?.(direction);
117
115
  evt.preventDefault();
118
116
  };
119
117
  touchSurface.addEventListener('touchstart', onTouchStart, false);
@@ -138,8 +136,8 @@ function isPassiveEventAvailable() {
138
136
  supportsPassiveOption = true;
139
137
  }
140
138
  });
141
- window.addEventListener('testPassiveEventSupport', noop, opts);
142
- window.removeEventListener('testPassiveEventSupport', noop, opts);
139
+ window.addEventListener('testPassiveEventSupport', () => {}, opts);
140
+ window.removeEventListener('testPassiveEventSupport', () => {}, opts);
143
141
  } catch (e) {
144
142
  // ignored
145
143
  }
package/js/utils/index.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
2
2
 
3
- import noop from 'lodash/noop';
4
-
5
3
  export * from './className';
6
4
 
7
5
  type KeyboardEventHandler<E extends KeyboardEvent | ReactKeyboardEvent> = (event: E) => void;
@@ -68,7 +66,7 @@ declare type SwipeDirection = 'none' | 'up' | 'down' | 'left' | 'right';
68
66
  * @param handleSwipe Callback function.
69
67
  * @return Function to remove listeners.
70
68
  */
71
- export function detectSwipe(touchSurface: Element, handleSwipe: (direction: SwipeDirection) => void = noop) {
69
+ export function detectSwipe(touchSurface: Element, handleSwipe?: (direction: SwipeDirection) => void) {
72
70
  let distX: number;
73
71
  let distY: number;
74
72
  let startX: number;
@@ -119,7 +117,7 @@ export function detectSwipe(touchSurface: Element, handleSwipe: (direction: Swip
119
117
  direction = distY < 0 ? 'up' : 'down';
120
118
  }
121
119
  }
122
- handleSwipe(direction);
120
+ handleSwipe?.(direction);
123
121
  evt.preventDefault();
124
122
  };
125
123
 
@@ -146,8 +144,8 @@ function isPassiveEventAvailable() {
146
144
  supportsPassiveOption = true;
147
145
  },
148
146
  });
149
- window.addEventListener('testPassiveEventSupport', noop, opts);
150
- window.removeEventListener('testPassiveEventSupport', noop, opts);
147
+ window.addEventListener('testPassiveEventSupport', () => {}, opts);
148
+ window.removeEventListener('testPassiveEventSupport', () => {}, opts);
151
149
  } catch (e) {
152
150
  // ignored
153
151
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Converts a string to kebab-case.
3
+ *
4
+ * @param str The string to convert
5
+ * @returns The kebab-cased string
6
+ *
7
+ * @example
8
+ * kebabCase('fooBar') // 'foo-bar'
9
+ * kebabCase('FooBar') // 'foo-bar'
10
+ * kebabCase('foo_bar') // 'foo-bar'
11
+ * kebabCase('foo bar') // 'foo-bar'
12
+ * kebabCase('FOO_BAR') // 'foo-bar'
13
+ */
14
+ function kebabCase(str) {
15
+ return str
16
+ // Insert a hyphen before any uppercase letter that follows a lowercase letter or number
17
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
18
+ // Insert a hyphen before any uppercase letter that is followed by a lowercase letter and preceded by an uppercase letter
19
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
20
+ // Replace spaces and underscores with hyphens
21
+ .replace(/[\s_]+/g, '-')
22
+ // Convert to lowercase
23
+ .toLowerCase()
24
+ // Remove leading/trailing hyphens
25
+ .replace(/^-+|-+$/g, '');
26
+ }
27
+
28
+ export { kebabCase };
@@ -0,0 +1,41 @@
1
+ import { kebabCase } from '@lumx/core/js/utils/string/kebabCase';
2
+
3
+ describe(kebabCase, () => {
4
+ it('should convert camelCase to kebab-case', () => {
5
+ expect(kebabCase('fooBar')).toBe('foo-bar');
6
+ expect(kebabCase('fooBarBaz')).toBe('foo-bar-baz');
7
+ });
8
+ it('should convert PascalCase to kebab-case', () => {
9
+ expect(kebabCase('FooBar')).toBe('foo-bar');
10
+ expect(kebabCase('FooBarBaz')).toBe('foo-bar-baz');
11
+ });
12
+ it('should convert snake_case to kebab-case', () => {
13
+ expect(kebabCase('foo_bar')).toBe('foo-bar');
14
+ expect(kebabCase('foo_bar_baz')).toBe('foo-bar-baz');
15
+ });
16
+ it('should convert spaces to hyphens', () => {
17
+ expect(kebabCase('foo bar')).toBe('foo-bar');
18
+ expect(kebabCase('foo bar baz')).toBe('foo-bar-baz');
19
+ });
20
+ it('should handle UPPERCASE strings', () => {
21
+ expect(kebabCase('FOO_BAR')).toBe('foo-bar');
22
+ expect(kebabCase('FOOBAR')).toBe('foobar');
23
+ });
24
+ it('should handle mixed formats', () => {
25
+ expect(kebabCase('fooBar_baz qux')).toBe('foo-bar-baz-qux');
26
+ });
27
+ it('should handle strings that are already kebab-case', () => {
28
+ expect(kebabCase('foo-bar')).toBe('foo-bar');
29
+ });
30
+ it('should handle empty strings', () => {
31
+ expect(kebabCase('')).toBe('');
32
+ });
33
+ it('should handle single words', () => {
34
+ expect(kebabCase('foo')).toBe('foo');
35
+ expect(kebabCase('Foo')).toBe('foo');
36
+ });
37
+ it('should remove leading and trailing hyphens', () => {
38
+ expect(kebabCase('_foo_bar_')).toBe('foo-bar');
39
+ expect(kebabCase(' foo bar ')).toBe('foo-bar');
40
+ });
41
+ });
@@ -0,0 +1,50 @@
1
+ import { kebabCase } from '@lumx/core/js/utils/string/kebabCase';
2
+
3
+ describe(kebabCase, () => {
4
+ it('should convert camelCase to kebab-case', () => {
5
+ expect(kebabCase('fooBar')).toBe('foo-bar');
6
+ expect(kebabCase('fooBarBaz')).toBe('foo-bar-baz');
7
+ });
8
+
9
+ it('should convert PascalCase to kebab-case', () => {
10
+ expect(kebabCase('FooBar')).toBe('foo-bar');
11
+ expect(kebabCase('FooBarBaz')).toBe('foo-bar-baz');
12
+ });
13
+
14
+ it('should convert snake_case to kebab-case', () => {
15
+ expect(kebabCase('foo_bar')).toBe('foo-bar');
16
+ expect(kebabCase('foo_bar_baz')).toBe('foo-bar-baz');
17
+ });
18
+
19
+ it('should convert spaces to hyphens', () => {
20
+ expect(kebabCase('foo bar')).toBe('foo-bar');
21
+ expect(kebabCase('foo bar baz')).toBe('foo-bar-baz');
22
+ });
23
+
24
+ it('should handle UPPERCASE strings', () => {
25
+ expect(kebabCase('FOO_BAR')).toBe('foo-bar');
26
+ expect(kebabCase('FOOBAR')).toBe('foobar');
27
+ });
28
+
29
+ it('should handle mixed formats', () => {
30
+ expect(kebabCase('fooBar_baz qux')).toBe('foo-bar-baz-qux');
31
+ });
32
+
33
+ it('should handle strings that are already kebab-case', () => {
34
+ expect(kebabCase('foo-bar')).toBe('foo-bar');
35
+ });
36
+
37
+ it('should handle empty strings', () => {
38
+ expect(kebabCase('')).toBe('');
39
+ });
40
+
41
+ it('should handle single words', () => {
42
+ expect(kebabCase('foo')).toBe('foo');
43
+ expect(kebabCase('Foo')).toBe('foo');
44
+ });
45
+
46
+ it('should remove leading and trailing hyphens', () => {
47
+ expect(kebabCase('_foo_bar_')).toBe('foo-bar');
48
+ expect(kebabCase(' foo bar ')).toBe('foo-bar');
49
+ });
50
+ });
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Converts a string to kebab-case.
3
+ *
4
+ * @param str The string to convert
5
+ * @returns The kebab-cased string
6
+ *
7
+ * @example
8
+ * kebabCase('fooBar') // 'foo-bar'
9
+ * kebabCase('FooBar') // 'foo-bar'
10
+ * kebabCase('foo_bar') // 'foo-bar'
11
+ * kebabCase('foo bar') // 'foo-bar'
12
+ * kebabCase('FOO_BAR') // 'foo-bar'
13
+ */
14
+ export function kebabCase(str: string): string {
15
+ return (
16
+ str
17
+ // Insert a hyphen before any uppercase letter that follows a lowercase letter or number
18
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
19
+ // Insert a hyphen before any uppercase letter that is followed by a lowercase letter and preceded by an uppercase letter
20
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
21
+ // Replace spaces and underscores with hyphens
22
+ .replace(/[\s_]+/g, '-')
23
+ // Convert to lowercase
24
+ .toLowerCase()
25
+ // Remove leading/trailing hyphens
26
+ .replace(/^-+|-+$/g, '')
27
+ );
28
+ }
package/package.json CHANGED
@@ -31,25 +31,30 @@
31
31
  "scripts": {
32
32
  "generate:design-tokens": "yarn node style-dictionary",
33
33
  "build": "rollup -c",
34
+ "test": "vitest run",
34
35
  "update-version-changelog": "yarn version-changelog ../../CHANGELOG.md"
35
36
  },
36
37
  "sideEffects": false,
37
- "version": "3.20.1-alpha.26",
38
+ "version": "3.20.1-alpha.28",
38
39
  "devDependencies": {
39
40
  "@babel/preset-typescript": "^7.26.0",
40
41
  "@rollup/plugin-babel": "^6.0.4",
41
- "@rollup/plugin-commonjs": "^19.0.2",
42
+ "@rollup/plugin-commonjs": "^29.0.0",
42
43
  "@rollup/plugin-node-resolve": "16.0.0",
43
44
  "@types/react": "^17.0.2",
44
45
  "autoprefixer": "^9.7.4",
45
46
  "glob": "^7.1.6",
46
47
  "postcss": "^8.5.6",
47
- "rollup": "^2.79.1",
48
+ "rollup": "3.29.5",
48
49
  "rollup-plugin-cleaner": "^1.0.0",
49
50
  "rollup-plugin-copy": "^3.5.0",
50
51
  "sass": "^1.54.0",
51
52
  "style-dictionary": "^3.9.0",
52
53
  "tinycolor2": "^1.4.1",
53
- "version-changelog": "^3.1.1"
54
+ "typescript": "^5.4.3",
55
+ "version-changelog": "^3.1.1",
56
+ "vite": "^6.3.5",
57
+ "vite-tsconfig-paths": "^5.1.4",
58
+ "vitest": "^3.0.0"
54
59
  }
55
60
  }