Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- _[BREAKING]_ Replace `getRootClassName` utility function with type `LumxClassName`
- _[BREAKING]_ Removed `CSS_PREFIX` constant
- _[BREAKING]_ Removed deprecated `@lumx/core/js/custom-colors` utils
- _[BREAKING]_ Removed `@lumx/core/js/constants/design-tokens` (internal use only) and `@lumx/core/js/utils/className` (soon to be rewritten)
- _[BREAKING]_ Removed `@lumx/core/js/constants/design-tokens` (internal use only) and `@lumx/core/js/utils/classNames` (soon to be rewritten)
- `@lumx/icons`
- _[BREAKING]_ Migrate to ESM module format
- `@lumx/angularjs`: removed from the project. Future updates to `@lumx/core` will continue to avoid breaking change to the deprecated angularjs directives but without any guarantee.

### Added

- `@lumx/core`:
- Added `spacing`, `margin`, `padding`, `typography`, `color`, `font`, `background` and `visuallyHidden` utility
functions to generate lumx class names (exported in `classNames` namespace in `@lumx/core/js/utils` or as individual
exports in `@lumx/core/js/utils/classNames`)

## [3.21.1][] - 2025-12-16

### Fixed
Expand Down
15 changes: 12 additions & 3 deletions packages/lumx-core/rollup.config.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import path from 'path';
import sass from 'sass';
import fs from 'fs/promises';
import glob from 'glob';
import postcss from 'postcss';

import typescript from '@rollup/plugin-typescript';
import cleaner from 'rollup-plugin-cleaner';
import copy from 'rollup-plugin-copy';
import glob from 'glob';
import postcss from 'postcss';

import pkg from './package.json' with { type: 'json' };
import CONFIGS from '../../configs/index.js';
Expand All @@ -17,6 +18,12 @@ const ROOT_PATH = path.resolve(__dirname, '..', '..');
const DIST_PATH = path.resolve(__dirname, pkg.publishConfig.directory);
const SRC_PATH = path.resolve(__dirname, 'src');

// Move internal modules to hash named files
function formatPath(entry) {
if (entry.name.includes('_internal')) return '_internal/[hash].js';
return '[name].js';
}

export default {
// Bundle all TS files
input: glob.sync('src/js/**/index.ts', {
Expand All @@ -27,6 +34,8 @@ export default {
dir: DIST_PATH,
preserveModules: true,
preserveModulesRoot: 'src',
entryFileNames: formatPath,
chunkFileNames: formatPath,
},
// Externalize all dependencies
external: [
Expand All @@ -43,7 +52,7 @@ export default {
target: 'ESNext',
module: 'ESNext',
},
exclude: ['**/*.test.*', '**/_internal/**'],
exclude: ['**/*.test.*'],
}),
copy({
targets: [
Expand Down
4 changes: 4 additions & 0 deletions packages/lumx-core/src/js/constants/className/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Visually hidden a11y utility class name
*/
export const VISUALLY_HIDDEN = 'visually-hidden';
1 change: 1 addition & 0 deletions packages/lumx-core/src/js/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './keycodes';
export * from './components';
export * from './enums';
export * from './className';
1 change: 1 addition & 0 deletions packages/lumx-core/src/js/types/Direction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Direction = 'top' | 'right' | 'bottom' | 'left' | 'vertical' | 'horizontal' | 'all';
1 change: 1 addition & 0 deletions packages/lumx-core/src/js/types/Spacing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Spacing = 'margin' | 'padding';
2 changes: 2 additions & 0 deletions packages/lumx-core/src/js/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ export type { RectSize } from './RectSize';
export type { TextElement } from './TextElement';
export type { ValueOf } from './ValueOf';
export type { LumxClassName } from './LumxClassName';
export type { Direction } from './Direction';
export type { Spacing } from './Spacing';

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
export { handleBasicClasses } from './handleBasicClasses';
export { getBasicClass } from './getBasicClass';
export { getTypographyClassName } from './getTypographyClassName';
export { fontColorClass } from './fontColorClass';
export { resolveColorWithVariants } from './resolveColorWithVariants';
1 change: 1 addition & 0 deletions packages/lumx-core/src/js/utils/_internal/color/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { resolveColorWithVariants } from './resolveColorWithVariants';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ColorPalette, ColorVariant, ColorWithVariants } from '../../../constants';
import type { ColorPalette, ColorVariant, ColorWithVariants } from '@lumx/core/js/constants';

/** Resolve color & color variant from a `ColorWithVariants` and optionally a `ColorVariant`. */
export function resolveColorWithVariants(
Expand Down
50 changes: 50 additions & 0 deletions packages/lumx-core/src/js/utils/classNames/color/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ColorVariant } from '@lumx/core/js/constants';
import { color } from '.';

describe(color, () => {
describe('font color', () => {
it('should generate font color class with color and variant', () => {
expect(color('font', 'dark', ColorVariant.L2)).toBe('lumx-color-font-dark-L2');
});

it('should generate font color class with default variant N when only color is provided', () => {
expect(color('font', 'primary')).toBe('lumx-color-font-primary-N');
});

it('should handle color with variant in one string', () => {
expect(color('font', 'primary-L2')).toBe('lumx-color-font-primary-L2');
});

it('should override variant when both are provided', () => {
expect(color('font', 'primary-L2', ColorVariant.D1)).toBe('lumx-color-font-primary-D1');
});
});

describe('background color', () => {
it('should generate background color class with color and variant', () => {
expect(color('background', 'dark', ColorVariant.L2)).toBe('lumx-color-background-dark-L2');
});

it('should generate background color class with default variant N when only color is provided', () => {
expect(color('background', 'light')).toBe('lumx-color-background-light-N');
});

it('should handle color with variant in one string', () => {
expect(color('background', 'primary-D2')).toBe('lumx-color-background-primary-D2');
});
});

describe('color variants', () => {
it('should handle all color variants', () => {
expect(color('font', 'primary', ColorVariant.L1)).toBe('lumx-color-font-primary-L1');
expect(color('font', 'primary', ColorVariant.L2)).toBe('lumx-color-font-primary-L2');
expect(color('font', 'primary', ColorVariant.L3)).toBe('lumx-color-font-primary-L3');
expect(color('font', 'primary', ColorVariant.L4)).toBe('lumx-color-font-primary-L4');
expect(color('font', 'primary', ColorVariant.L5)).toBe('lumx-color-font-primary-L5');
expect(color('font', 'primary', ColorVariant.L6)).toBe('lumx-color-font-primary-L6');
expect(color('font', 'primary', ColorVariant.D1)).toBe('lumx-color-font-primary-D1');
expect(color('font', 'primary', ColorVariant.D2)).toBe('lumx-color-font-primary-D2');
expect(color('font', 'primary', ColorVariant.N)).toBe('lumx-color-font-primary-N');
});
});
});
56 changes: 56 additions & 0 deletions packages/lumx-core/src/js/utils/classNames/color/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ColorVariant, ColorWithVariants } from '@lumx/core/js/constants';
import { resolveColorWithVariants } from '@lumx/core/js/utils/_internal/color';

/**
* Generates a Lumx color class name for the given type, color and variant.
* This is the base function used by font() and background() utilities.
*
* @param type - The color class type ('font' or 'background')
* @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
* @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
* @returns The Lumx color class name or undefined if no color is provided
*
* @example
* color('font', 'dark', 'L2'); // 'lumx-color-font-dark-L2'
* color('background', 'primary'); // 'lumx-color-background-primary-N'
* color('font', 'primary-L2'); // 'lumx-color-font-primary-L2'
* color('font', undefined); // undefined
*/
export function color(
type: 'font' | 'background',
propColor: ColorWithVariants,
propColorVariant?: ColorVariant,
): string {
const [cColor, cColorVariant = ColorVariant.N] = resolveColorWithVariants(propColor, propColorVariant);
return `lumx-color-${type}-${cColor}-${cColorVariant}`;
}

/**
* Generates a Lumx background color class name for the given color and variant.
*
* @param propColor - The color palette name (e.g., 'primary', 'dark', 'light')
* @param propColorVariant - The color variant (e.g., 'L1', 'L2', 'D1', 'N')
* @returns The Lumx background color class name
*
* @example
* background('dark', 'L2'); // 'lumx-color-background-dark-L2'
* background('primary', 'N'); // 'lumx-color-background-primary-N'
*/
export const background = (propColor: ColorWithVariants, propColorVariant?: ColorVariant) =>
color('background', propColor, propColorVariant);

/**
* Generates a Lumx font color class name for the given color and variant.
*
* @param propColor - The color palette name (e.g., 'primary', 'dark') with optional variant suffix (e.g., 'primary-L2')
* @param propColorVariant - Optional color variant override (e.g., 'L1', 'L2', 'D1', 'N')
* @returns The Lumx font color class name or undefined if no color is provided
*
* @example
* font('dark', 'L2'); // 'lumx-color-font-dark-L2'
* font('primary-L2'); // 'lumx-color-font-primary-L2'
* font('primary'); // 'lumx-color-font-primary-N'
* font(undefined); // undefined
*/
export const font = (propColor: ColorWithVariants, propColorVariant?: ColorVariant) =>
color('font', propColor, propColorVariant);
5 changes: 5 additions & 0 deletions packages/lumx-core/src/js/utils/classNames/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as join } from 'classnames';
export * from './color';
export * from './typography';
export * from './spacing';
export * from './visually-hidden';
106 changes: 106 additions & 0 deletions packages/lumx-core/src/js/utils/classNames/spacing/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Size } from '@lumx/core/js/constants';
import { spacing, spacings } from '.';

describe(spacing, () => {
describe('with direction and size', () => {
it('should generate spacing class with type, direction and size', () => {
expect(spacing('padding', 'right', Size.regular)).toBe('lumx-spacing-padding-right-regular');
});

it('should generate spacing class for margin', () => {
expect(spacing('margin', 'left', Size.big)).toBe('lumx-spacing-margin-left-big');
});

it('should handle all directions', () => {
expect(spacing('padding', 'top', Size.regular)).toBe('lumx-spacing-padding-top-regular');
expect(spacing('padding', 'right', Size.regular)).toBe('lumx-spacing-padding-right-regular');
expect(spacing('padding', 'bottom', Size.regular)).toBe('lumx-spacing-padding-bottom-regular');
expect(spacing('padding', 'left', Size.regular)).toBe('lumx-spacing-padding-left-regular');
expect(spacing('padding', 'vertical', Size.regular)).toBe('lumx-spacing-padding-vertical-regular');
expect(spacing('padding', 'horizontal', Size.regular)).toBe('lumx-spacing-padding-horizontal-regular');
});
});

describe('with direction "all"', () => {
it('should omit direction for "all"', () => {
expect(spacing('padding', 'all', Size.regular)).toBe('lumx-spacing-padding-regular');
});
});

describe('without direction', () => {
it('should generate spacing class without direction', () => {
expect(spacing('padding', undefined, Size.regular)).toBe('lumx-spacing-padding-regular');
});
});

describe('without size', () => {
it('should generate spacing class without size', () => {
expect(spacing('padding', 'right')).toBe('lumx-spacing-padding-right');
});

it('should generate spacing class without direction and size', () => {
expect(spacing('padding')).toBe('lumx-spacing-padding');
});
});

describe('with size null', () => {
it('should append "-none" when size is null', () => {
expect(spacing('padding', 'right', null)).toBe('lumx-spacing-padding-right-none');
});

it('should append "-none" without direction when size is null', () => {
expect(spacing('padding', undefined, null)).toBe('lumx-spacing-padding-none');
});
});

describe('with different sizes', () => {
it('should handle all size values', () => {
expect(spacing('padding', 'right', Size.tiny)).toBe('lumx-spacing-padding-right-tiny');
expect(spacing('padding', 'right', Size.regular)).toBe('lumx-spacing-padding-right-regular');
expect(spacing('padding', 'right', Size.big)).toBe('lumx-spacing-padding-right-big');
expect(spacing('padding', 'right', Size.huge)).toBe('lumx-spacing-padding-right-huge');
});
});
});

describe(spacings, () => {
it('should generate multiple spacing classes', () => {
const result = spacings([
{ type: 'padding', direction: 'right', size: Size.regular },
{ type: 'margin', direction: 'left', size: Size.big },
]);
expect(result).toContain('lumx-spacing-padding-right-regular');
expect(result).toContain('lumx-spacing-margin-left-big');
});

it('should handle single spacing configuration', () => {
const result = spacings([{ type: 'padding', direction: 'top', size: Size.tiny }]);
expect(result).toBe('lumx-spacing-padding-top-tiny');
});

it('should handle spacing without direction', () => {
const result = spacings([{ type: 'padding', size: Size.regular }]);
expect(result).toBe('lumx-spacing-padding-regular');
});

it('should handle spacing with null size', () => {
const result = spacings([{ type: 'padding', direction: 'top', size: null }]);
expect(result).toBe('lumx-spacing-padding-top-none');
});

it('should handle empty array', () => {
const result = spacings([]);
expect(result).toBe('');
});

it('should handle multiple spacings with various configurations', () => {
const result = spacings([
{ type: 'padding', direction: 'top', size: Size.regular },
{ type: 'margin', direction: 'bottom', size: Size.big },
{ type: 'padding', size: Size.tiny },
]);
expect(result).toContain('lumx-spacing-padding-top-regular');
expect(result).toContain('lumx-spacing-margin-bottom-big');
expect(result).toContain('lumx-spacing-padding-tiny');
});
});
Loading
Loading