Add comprehensive library expansion with new components and demos

- Add new libraries: ui-accessibility, ui-animations, ui-backgrounds, ui-code-display, ui-data-utils, ui-font-manager, hcl-studio
- Add extensive layout components: gallery-grid, infinite-scroll-container, kanban-board, masonry, split-view, sticky-layout
- Add comprehensive demo components for all new features
- Update project configuration and dependencies
- Expand component exports and routing structure
- Add UI landing pages planning document

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
skyai_dev
2025-09-05 05:37:37 +10:00
parent 876eb301a0
commit 5346d6d0c9
5476 changed files with 350855 additions and 10 deletions

View File

@@ -0,0 +1,63 @@
# UiBackgrounds
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.0.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the library, run:
```bash
ng build ui-backgrounds
```
This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
### Publishing the Library
Once the project is built, you can publish your library by following these steps:
1. Navigate to the `dist` directory:
```bash
cd dist/ui-backgrounds
```
2. Run the `npm publish` command to publish your library to the npm registry:
```bash
npm publish
```
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

View File

@@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ui-backgrounds",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@@ -0,0 +1,12 @@
{
"name": "ui-backgrounds",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^19.2.0",
"@angular/core": "^19.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
},
"sideEffects": false
}

View File

@@ -0,0 +1,97 @@
import { Component, Input, OnInit, OnDestroy, OnChanges, SimpleChanges, ElementRef, inject, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { LinearGradientConfig, RadialGradientConfig, ConicGradientConfig, ColorStop, GradientDirection } from '../../types/background.types';
import { BackgroundService } from '../../services/background.service';
@Component({
selector: 'ui-gradient-background',
standalone: true,
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'class': 'ui-background ui-background--gradient'
}
})
export class GradientBackgroundComponent implements OnInit, OnDestroy, OnChanges {
private readonly elementRef = inject(ElementRef<HTMLElement>);
private readonly backgroundService = inject(BackgroundService);
private backgroundId?: string;
@Input() type: 'linear' | 'radial' | 'conic' = 'linear';
@Input() colors: string[] = ['#000000', '#ffffff'];
@Input() direction?: GradientDirection | string;
@Input() shape?: 'circle' | 'ellipse';
@Input() size?: 'closest-side' | 'closest-corner' | 'farthest-side' | 'farthest-corner';
@Input() position?: string;
@Input() angle?: string | number;
@Input() colorStops?: ColorStop[];
@Input() fullScreen = false;
@Input() zIndex?: number;
ngOnInit(): void {
this.applyBackground();
}
ngOnChanges(changes: SimpleChanges): void {
const relevantChanges = ['type', 'colors', 'direction', 'shape', 'size', 'position', 'angle', 'colorStops', 'fullScreen', 'zIndex'];
if (relevantChanges.some(key => changes[key])) {
this.applyBackground();
}
}
ngOnDestroy(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
}
private applyBackground(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
const colorStops = this.colorStops || this.colors.map(color => ({ color }));
let config: LinearGradientConfig | RadialGradientConfig | ConicGradientConfig;
switch (this.type) {
case 'linear':
config = {
type: 'linear-gradient',
direction: this.direction,
colors: colorStops
};
break;
case 'radial':
config = {
type: 'radial-gradient',
shape: this.shape,
size: this.size,
position: this.position,
colors: colorStops
};
break;
case 'conic':
config = {
type: 'conic-gradient',
angle: this.angle,
position: this.position,
colors: colorStops
};
break;
}
if (this.fullScreen) {
this.backgroundId = this.backgroundService.applyFullScreenBackground(config, {
zIndex: this.zIndex || -1
});
} else {
this.backgroundId = this.backgroundService.applyBackground(
this.elementRef.nativeElement,
config
);
}
// Update host class
this.elementRef.nativeElement.classList.add(`ui-background--${this.type}-gradient`);
}
}

View File

@@ -0,0 +1,76 @@
import { Component, Input, OnInit, OnDestroy, OnChanges, SimpleChanges, ElementRef, inject, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { ImageBackgroundConfig } from '../../types/background.types';
import { BackgroundService } from '../../services/background.service';
@Component({
selector: 'ui-image-background',
standalone: true,
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'class': 'ui-background ui-background--image'
}
})
export class ImageBackgroundComponent implements OnInit, OnDestroy, OnChanges {
private readonly elementRef = inject(ElementRef<HTMLElement>);
private readonly backgroundService = inject(BackgroundService);
private backgroundId?: string;
@Input({ required: true }) url!: string;
@Input() size: 'auto' | 'cover' | 'contain' | string = 'cover';
@Input() position = 'center';
@Input() repeat: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat' = 'no-repeat';
@Input() attachment?: 'scroll' | 'fixed' | 'local';
@Input() opacity = 1;
@Input() fullScreen = false;
@Input() zIndex?: number;
ngOnInit(): void {
this.applyBackground();
}
ngOnChanges(changes: SimpleChanges): void {
const relevantChanges = ['url', 'size', 'position', 'repeat', 'attachment', 'opacity', 'fullScreen', 'zIndex'];
if (relevantChanges.some(key => changes[key])) {
this.applyBackground();
}
}
ngOnDestroy(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
}
private applyBackground(): void {
if (!this.url) {
return;
}
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
const config: ImageBackgroundConfig = {
type: 'image',
url: this.url,
size: this.size,
position: this.position,
repeat: this.repeat,
attachment: this.attachment,
opacity: this.opacity
};
if (this.fullScreen) {
this.backgroundId = this.backgroundService.applyFullScreenBackground(config, {
zIndex: this.zIndex || -1
});
} else {
this.backgroundId = this.backgroundService.applyBackground(
this.elementRef.nativeElement,
config
);
}
}
}

View File

@@ -0,0 +1,4 @@
export * from './solid-background.component';
export * from './gradient-background.component';
export * from './pattern-background.component';
export * from './image-background.component';

View File

@@ -0,0 +1,73 @@
import { Component, Input, OnInit, OnDestroy, OnChanges, SimpleChanges, ElementRef, inject, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { PatternConfig, PatternType } from '../../types/background.types';
import { BackgroundService } from '../../services/background.service';
@Component({
selector: 'ui-pattern-background',
standalone: true,
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'class': 'ui-background ui-background--pattern'
}
})
export class PatternBackgroundComponent implements OnInit, OnDestroy, OnChanges {
private readonly elementRef = inject(ElementRef<HTMLElement>);
private readonly backgroundService = inject(BackgroundService);
private backgroundId?: string;
@Input() pattern: PatternType = 'dots';
@Input() primaryColor = '#000000';
@Input() secondaryColor = '#ffffff';
@Input() size: string | number = 20;
@Input() opacity = 1;
@Input() fullScreen = false;
@Input() zIndex?: number;
ngOnInit(): void {
this.applyBackground();
}
ngOnChanges(changes: SimpleChanges): void {
const relevantChanges = ['pattern', 'primaryColor', 'secondaryColor', 'size', 'opacity', 'fullScreen', 'zIndex'];
if (relevantChanges.some(key => changes[key])) {
this.applyBackground();
}
}
ngOnDestroy(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
}
private applyBackground(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
const config: PatternConfig = {
type: 'pattern',
pattern: this.pattern,
primaryColor: this.primaryColor,
secondaryColor: this.secondaryColor,
size: this.size,
opacity: this.opacity
};
if (this.fullScreen) {
this.backgroundId = this.backgroundService.applyFullScreenBackground(config, {
zIndex: this.zIndex || -1
});
} else {
this.backgroundId = this.backgroundService.applyBackground(
this.elementRef.nativeElement,
config
);
}
// Update host class
this.elementRef.nativeElement.classList.add(`ui-background--pattern-${this.pattern}`);
}
}

View File

@@ -0,0 +1,61 @@
import { Component, Input, OnInit, OnDestroy, OnChanges, SimpleChanges, ElementRef, inject, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
import { SolidBackgroundConfig } from '../../types/background.types';
import { BackgroundService } from '../../services/background.service';
@Component({
selector: 'ui-solid-background',
standalone: true,
template: '<ng-content></ng-content>',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'class': 'ui-background ui-background--solid'
}
})
export class SolidBackgroundComponent implements OnInit, OnDestroy, OnChanges {
private readonly elementRef = inject(ElementRef<HTMLElement>);
private readonly backgroundService = inject(BackgroundService);
private backgroundId?: string;
@Input() color = '#000000';
@Input() fullScreen = false;
@Input() zIndex?: number;
ngOnInit(): void {
this.applyBackground();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['color'] || changes['fullScreen'] || changes['zIndex']) {
this.applyBackground();
}
}
ngOnDestroy(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
}
private applyBackground(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
}
const config: SolidBackgroundConfig = {
type: 'solid',
color: this.color
};
if (this.fullScreen) {
this.backgroundId = this.backgroundService.applyFullScreenBackground(config, {
zIndex: this.zIndex || -1
});
} else {
this.backgroundId = this.backgroundService.applyBackground(
this.elementRef.nativeElement,
config
);
}
}
}

View File

@@ -0,0 +1 @@
export * from './backgrounds/index';

View File

@@ -0,0 +1,122 @@
import {
Directive,
Input,
OnInit,
OnDestroy,
OnChanges,
SimpleChanges,
ElementRef,
inject
} from '@angular/core';
import { BackgroundConfig, BackgroundOptions } from '../types/background.types';
import { BackgroundService } from '../services/background.service';
@Directive({
selector: '[uiBackground]',
standalone: true
})
export class BackgroundDirective implements OnInit, OnDestroy, OnChanges {
private readonly elementRef = inject(ElementRef<HTMLElement>);
private readonly backgroundService = inject(BackgroundService);
private backgroundId?: string;
@Input('uiBackground') config?: BackgroundConfig;
@Input() backgroundOptions?: Partial<BackgroundOptions>;
@Input() backgroundDisabled = false;
ngOnInit(): void {
this.applyBackground();
}
ngOnChanges(changes: SimpleChanges): void {
if (changes['config'] || changes['backgroundOptions'] || changes['backgroundDisabled']) {
if (this.backgroundDisabled) {
this.removeBackground();
} else {
this.applyBackground();
}
}
}
ngOnDestroy(): void {
this.removeBackground();
}
private applyBackground(): void {
if (!this.config || this.backgroundDisabled) {
return;
}
// Remove existing background if present
this.removeBackground();
// Apply new background
this.backgroundId = this.backgroundService.applyBackground(
this.elementRef.nativeElement,
this.config
);
// Add base CSS class for styling
this.elementRef.nativeElement.classList.add('ui-background');
// Add specific CSS classes based on configuration
this.addTypeClasses();
}
private removeBackground(): void {
if (this.backgroundId) {
this.backgroundService.removeBackground(this.backgroundId);
this.backgroundId = undefined;
}
// Remove CSS classes
this.removeTypeClasses();
this.elementRef.nativeElement.classList.remove('ui-background');
}
private addTypeClasses(): void {
if (!this.config) return;
const element = this.elementRef.nativeElement;
element.classList.add(`ui-background--${this.config.type}`);
// Add additional classes based on type
switch (this.config.type) {
case 'linear-gradient':
case 'radial-gradient':
case 'conic-gradient':
element.classList.add('ui-background--gradient');
break;
case 'pattern':
element.classList.add('ui-background--pattern');
if (this.config.pattern) {
element.classList.add(`ui-background--pattern-${this.config.pattern}`);
}
break;
case 'image':
element.classList.add('ui-background--image');
break;
case 'animated':
element.classList.add('ui-background--animated');
break;
case 'glass':
element.classList.add('ui-background--glass');
break;
}
}
private removeTypeClasses(): void {
if (!this.config) return;
const element = this.elementRef.nativeElement;
const classesToRemove: string[] = [];
element.classList.forEach((cls: string) => {
if (cls.startsWith('ui-background--')) {
classesToRemove.push(cls);
}
});
classesToRemove.forEach(cls => element.classList.remove(cls));
}
}

View File

@@ -0,0 +1 @@
export * from './background.directive';

View File

@@ -0,0 +1,191 @@
import { Injectable, signal, computed } from '@angular/core';
import { BackgroundConfig, BackgroundOptions, BackgroundState } from '../types/background.types';
import { BackgroundGenerator } from '../utils/background-generator';
@Injectable({
providedIn: 'root'
})
export class BackgroundService {
private _activeBackgrounds = signal<Map<string, BackgroundState>>(new Map());
// Public computed signals
readonly activeBackgrounds = computed(() => Array.from(this._activeBackgrounds().values()));
readonly hasActiveBackgrounds = computed(() => this._activeBackgrounds().size > 0);
/**
* Apply a background to an element
*/
applyBackground(element: HTMLElement, config: BackgroundConfig, id?: string): string {
const backgroundId = id || this.generateId();
const styles = BackgroundGenerator.generateCss(config);
// Apply styles to element
Object.entries(styles).forEach(([property, value]) => {
element.style.setProperty(property, value);
});
// Add to active backgrounds
this._activeBackgrounds.update(backgrounds => {
const newMap = new Map(backgrounds);
newMap.set(backgroundId, {
active: true,
config,
element
});
return newMap;
});
return backgroundId;
}
/**
* Apply a full-screen background
*/
applyFullScreenBackground(config: BackgroundConfig, options?: Partial<BackgroundOptions>): string {
const backgroundId = this.generateId();
const element = this.createFullScreenElement(options);
const styles = BackgroundGenerator.generateCss(config);
Object.entries(styles).forEach(([property, value]) => {
element.style.setProperty(property, value);
});
document.body.appendChild(element);
this._activeBackgrounds.update(backgrounds => {
const newMap = new Map(backgrounds);
newMap.set(backgroundId, {
active: true,
config,
element
});
return newMap;
});
return backgroundId;
}
/**
* Update an existing background
*/
updateBackground(id: string, config: BackgroundConfig): boolean {
const backgroundState = this._activeBackgrounds().get(id);
if (!backgroundState || !backgroundState.element) {
return false;
}
const styles = BackgroundGenerator.generateCss(config);
Object.entries(styles).forEach(([property, value]) => {
backgroundState.element!.style.setProperty(property, value);
});
this._activeBackgrounds.update(backgrounds => {
const newMap = new Map(backgrounds);
const existing = newMap.get(id);
if (existing) {
existing.config = config;
newMap.set(id, existing);
}
return newMap;
});
return true;
}
/**
* Remove a background
*/
removeBackground(id: string): boolean {
const backgroundState = this._activeBackgrounds().get(id);
if (!backgroundState) {
return false;
}
// Remove full-screen element if it was created by this service
if (backgroundState.element?.classList.contains('ui-background-fullscreen')) {
backgroundState.element.remove();
} else if (backgroundState.element) {
// Clear inline styles for regular elements
this.clearElementStyles(backgroundState.element);
}
this._activeBackgrounds.update(backgrounds => {
const newMap = new Map(backgrounds);
newMap.delete(id);
return newMap;
});
return true;
}
/**
* Remove all backgrounds
*/
removeAllBackgrounds(): void {
this._activeBackgrounds().forEach((state, id) => {
this.removeBackground(id);
});
}
/**
* Get background state
*/
getBackground(id: string): BackgroundState | undefined {
return this._activeBackgrounds().get(id);
}
/**
* Generate CSS for a configuration without applying it
*/
generateCss(config: BackgroundConfig): { [key: string]: string } {
return BackgroundGenerator.generateCss(config);
}
/**
* Generate a background ID
*/
private generateId(): string {
return `bg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Create a full-screen background element
*/
private createFullScreenElement(options?: Partial<BackgroundOptions>): HTMLElement {
const element = document.createElement('div');
element.className = `ui-background-fullscreen ${options?.className || ''}`.trim();
// Base full-screen styles
element.style.position = 'fixed';
element.style.top = '0';
element.style.left = '0';
element.style.width = '100%';
element.style.height = '100%';
element.style.pointerEvents = 'none';
element.style.zIndex = (options?.zIndex || -1).toString();
return element;
}
/**
* Clear background-related styles from an element
*/
private clearElementStyles(element: HTMLElement): void {
const propertiesToClear = [
'background',
'background-image',
'background-color',
'background-size',
'background-position',
'background-repeat',
'background-attachment',
'backdrop-filter',
'opacity',
'animation'
];
propertiesToClear.forEach(property => {
element.style.removeProperty(property);
});
}
}

View File

@@ -0,0 +1 @@
export * from './background.service';

View File

@@ -0,0 +1,142 @@
export type BackgroundType =
| 'solid'
| 'linear-gradient'
| 'radial-gradient'
| 'conic-gradient'
| 'pattern'
| 'image'
| 'animated'
| 'mesh'
| 'noise'
| 'glass';
export type PatternType =
| 'dots'
| 'grid'
| 'stripes'
| 'diagonal-stripes'
| 'chevron'
| 'waves'
| 'hexagon'
| 'triangles'
| 'checkerboard'
| 'circles'
| 'crosshatch'
| 'polka-dots';
export type GradientDirection =
| 'to-top'
| 'to-top-right'
| 'to-right'
| 'to-bottom-right'
| 'to-bottom'
| 'to-bottom-left'
| 'to-left'
| 'to-top-left';
export interface ColorStop {
color: string;
position?: string | number;
}
export interface SolidBackgroundConfig {
type: 'solid';
color: string;
}
export interface LinearGradientConfig {
type: 'linear-gradient';
direction?: GradientDirection | string;
colors: ColorStop[];
}
export interface RadialGradientConfig {
type: 'radial-gradient';
shape?: 'circle' | 'ellipse';
size?: 'closest-side' | 'closest-corner' | 'farthest-side' | 'farthest-corner';
position?: string;
colors: ColorStop[];
}
export interface ConicGradientConfig {
type: 'conic-gradient';
angle?: string | number;
position?: string;
colors: ColorStop[];
}
export interface PatternConfig {
type: 'pattern';
pattern: PatternType;
primaryColor?: string;
secondaryColor?: string;
size?: string | number;
opacity?: number;
}
export interface ImageBackgroundConfig {
type: 'image';
url: string;
size?: 'auto' | 'cover' | 'contain' | string;
position?: string;
repeat?: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat';
attachment?: 'scroll' | 'fixed' | 'local';
opacity?: number;
}
export interface AnimatedBackgroundConfig {
type: 'animated';
baseConfig: LinearGradientConfig | RadialGradientConfig | ConicGradientConfig | PatternConfig;
duration?: string;
timing?: string;
iteration?: 'infinite' | number;
}
export interface MeshGradientConfig {
type: 'mesh';
colors: string[];
complexity?: 'low' | 'medium' | 'high';
seed?: number;
}
export interface NoiseConfig {
type: 'noise';
baseColor?: string;
intensity?: number;
scale?: number;
opacity?: number;
}
export interface GlassConfig {
type: 'glass';
backdrop?: 'blur' | 'saturate' | 'both';
intensity?: number;
tint?: string;
opacity?: number;
}
export type BackgroundConfig =
| SolidBackgroundConfig
| LinearGradientConfig
| RadialGradientConfig
| ConicGradientConfig
| PatternConfig
| ImageBackgroundConfig
| AnimatedBackgroundConfig
| MeshGradientConfig
| NoiseConfig
| GlassConfig;
export interface BackgroundOptions {
config: BackgroundConfig;
zIndex?: number;
fullScreen?: boolean;
responsive?: boolean;
className?: string;
}
export interface BackgroundState {
active: boolean;
config?: BackgroundConfig;
element?: HTMLElement;
}

View File

@@ -0,0 +1 @@
export * from './background.types';

View File

@@ -0,0 +1,234 @@
import {
BackgroundConfig,
ColorStop,
LinearGradientConfig,
RadialGradientConfig,
ConicGradientConfig,
PatternConfig,
ImageBackgroundConfig,
SolidBackgroundConfig,
AnimatedBackgroundConfig,
MeshGradientConfig,
NoiseConfig,
GlassConfig
} from '../types/background.types';
export class BackgroundGenerator {
static generateCss(config: BackgroundConfig): { [key: string]: string } {
switch (config.type) {
case 'solid':
return this.generateSolid(config);
case 'linear-gradient':
return this.generateLinearGradient(config);
case 'radial-gradient':
return this.generateRadialGradient(config);
case 'conic-gradient':
return this.generateConicGradient(config);
case 'pattern':
return this.generatePattern(config);
case 'image':
return this.generateImage(config);
case 'animated':
return this.generateAnimated(config);
case 'mesh':
return this.generateMesh(config);
case 'noise':
return this.generateNoise(config);
case 'glass':
return this.generateGlass(config);
default:
return { background: 'transparent' };
}
}
private static generateSolid(config: SolidBackgroundConfig): { [key: string]: string } {
return {
background: config.color,
'background-size': 'cover',
'background-position': 'center',
'background-repeat': 'no-repeat'
};
}
private static generateLinearGradient(config: LinearGradientConfig): { [key: string]: string } {
const direction = config.direction || 'to bottom';
const colorStops = this.formatColorStops(config.colors);
return {
background: `linear-gradient(${direction}, ${colorStops})`,
'background-size': 'cover',
'background-position': 'center',
'background-repeat': 'no-repeat'
};
}
private static generateRadialGradient(config: RadialGradientConfig): { [key: string]: string } {
const shape = config.shape || 'ellipse';
const size = config.size || 'farthest-corner';
const position = config.position || 'center';
const colorStops = this.formatColorStops(config.colors);
return {
background: `radial-gradient(${shape} ${size} at ${position}, ${colorStops})`,
'background-size': 'cover',
'background-position': 'center',
'background-repeat': 'no-repeat'
};
}
private static generateConicGradient(config: ConicGradientConfig): { [key: string]: string } {
const angle = config.angle ? `from ${config.angle}deg` : '';
const position = config.position ? `at ${config.position}` : '';
const colorStops = this.formatColorStops(config.colors);
return {
background: `conic-gradient(${angle} ${position}, ${colorStops})`,
'background-size': 'cover',
'background-position': 'center',
'background-repeat': 'no-repeat'
};
}
private static generatePattern(config: PatternConfig): { [key: string]: string } {
const primaryColor = config.primaryColor || '#000000';
const secondaryColor = config.secondaryColor || '#ffffff';
const size = typeof config.size === 'number' ? `${config.size}px` : (config.size || '20px');
const opacity = config.opacity || 1;
const patternCss = this.getPatternCss(config.pattern, primaryColor, secondaryColor, size);
return {
background: patternCss,
opacity: opacity.toString(),
'background-size': size,
'background-repeat': 'repeat'
};
}
private static generateImage(config: ImageBackgroundConfig): { [key: string]: string } {
const styles: { [key: string]: string } = {
'background-image': `url(${config.url})`,
'background-size': config.size || 'cover',
'background-position': config.position || 'center',
'background-repeat': config.repeat || 'no-repeat'
};
if (config.attachment) {
styles['background-attachment'] = config.attachment;
}
if (config.opacity !== undefined) {
styles['opacity'] = config.opacity.toString();
}
return styles;
}
private static generateAnimated(config: AnimatedBackgroundConfig): { [key: string]: string } {
const baseStyles = this.generateCss(config.baseConfig);
const duration = config.duration || '5s';
const timing = config.timing || 'ease-in-out';
const iteration = config.iteration === 'infinite' ? 'infinite' : (config.iteration || 1).toString();
return {
...baseStyles,
animation: `background-animation ${duration} ${timing} ${iteration}`,
};
}
private static generateMesh(config: MeshGradientConfig): { [key: string]: string } {
// Generate mesh gradient using multiple radial gradients
const colors = config.colors;
const complexity = config.complexity || 'medium';
// Create multiple overlapping radial gradients
const gradients = this.generateMeshGradients(colors, complexity);
return {
background: gradients.join(', '),
'background-size': '400% 400%',
animation: 'mesh-animation 15s ease infinite'
};
}
private static generateNoise(config: NoiseConfig): { [key: string]: string } {
const baseColor = config.baseColor || '#000000';
const intensity = config.intensity || 0.5;
const scale = config.scale || 100;
const opacity = config.opacity || 1;
// Create noise effect using CSS filters and patterns
return {
background: baseColor,
'background-image': `url("data:image/svg+xml,%3Csvg viewBox='0 0 ${scale} ${scale}' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)' opacity='${intensity}'/%3E%3C/svg%3E")`,
opacity: opacity.toString()
};
}
private static generateGlass(config: GlassConfig): { [key: string]: string } {
const intensity = config.intensity || 10;
const tint = config.tint || 'rgba(255, 255, 255, 0.1)';
const opacity = config.opacity || 0.8;
const styles: { [key: string]: string } = {
background: tint,
opacity: opacity.toString(),
'backdrop-filter': `blur(${intensity}px)`
};
if (config.backdrop === 'saturate' || config.backdrop === 'both') {
styles['backdrop-filter'] += ` saturate(150%)`;
}
return styles;
}
private static formatColorStops(colors: ColorStop[]): string {
return colors.map(stop => {
if (stop.position !== undefined) {
const position = typeof stop.position === 'number' ? `${stop.position}%` : stop.position;
return `${stop.color} ${position}`;
}
return stop.color;
}).join(', ');
}
private static getPatternCss(pattern: string, primary: string, secondary: string, size: string): string {
const patterns: { [key: string]: string } = {
'dots': `radial-gradient(circle, ${primary} 20%, transparent 20%)`,
'grid': `linear-gradient(${primary} 1px, transparent 1px), linear-gradient(90deg, ${primary} 1px, transparent 1px)`,
'stripes': `linear-gradient(45deg, ${primary} 25%, transparent 25%, transparent 50%, ${primary} 50%, ${primary} 75%, transparent 75%)`,
'diagonal-stripes': `linear-gradient(45deg, ${primary} 25%, transparent 25%, transparent 50%, ${primary} 50%, ${primary} 75%, transparent 75%)`,
'chevron': `linear-gradient(45deg, ${primary} 25%, transparent 25%), linear-gradient(-45deg, ${primary} 25%, transparent 25%)`,
'waves': `radial-gradient(circle at 50% 100%, transparent 40%, ${primary} 40%, ${primary} 60%, transparent 60%)`,
'hexagon': `radial-gradient(circle at 50% 0%, transparent 40%, ${primary} 40%, ${primary} 60%, transparent 60%)`,
'triangles': `linear-gradient(60deg, ${primary} 25%, transparent 25%), linear-gradient(120deg, ${primary} 25%, transparent 25%)`,
'checkerboard': `linear-gradient(45deg, ${primary} 25%, transparent 25%, transparent 75%, ${primary} 75%), linear-gradient(45deg, ${primary} 25%, transparent 25%, transparent 75%, ${primary} 75%)`,
'circles': `radial-gradient(circle, ${primary} 40%, transparent 40%)`,
'crosshatch': `linear-gradient(0deg, ${primary} 1px, transparent 1px), linear-gradient(90deg, ${primary} 1px, transparent 1px), linear-gradient(45deg, ${primary} 1px, transparent 1px), linear-gradient(-45deg, ${primary} 1px, transparent 1px)`,
'polka-dots': `radial-gradient(circle, ${primary} 25%, transparent 25%)`
};
return patterns[pattern] || patterns['dots'];
}
private static generateMeshGradients(colors: string[], complexity: string): string[] {
const gradientCount = complexity === 'high' ? 6 : complexity === 'medium' ? 4 : 2;
const gradients: string[] = [];
for (let i = 0; i < gradientCount; i++) {
const color1 = colors[i % colors.length];
const color2 = colors[(i + 1) % colors.length];
const x = Math.random() * 100;
const y = Math.random() * 100;
const size = 50 + Math.random() * 50;
gradients.push(
`radial-gradient(${size}% ${size}% at ${x}% ${y}%, ${color1}, transparent)`
);
}
return gradients;
}
}

View File

@@ -0,0 +1 @@
export * from './background-generator';

View File

@@ -0,0 +1,18 @@
/*
* Public API Surface of ui-backgrounds
*/
// Types
export * from './lib/types/index';
// Services
export * from './lib/services/index';
// Directives
export * from './lib/directives/index';
// Components
export * from './lib/components/index';
// Utils
export * from './lib/utils/index';

View File

@@ -0,0 +1,113 @@
// Animation keyframes for ui-backgrounds library
// Basic gradient animation
@keyframes ui-background-animation {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
// Mesh gradient animation
@keyframes ui-mesh-animation {
0%, 100% {
background-position: 0% 0%;
}
25% {
background-position: 100% 0%;
}
50% {
background-position: 100% 100%;
}
75% {
background-position: 0% 100%;
}
}
// Floating animation for patterns
@keyframes ui-pattern-float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-20px);
}
}
// Rotation animation
@keyframes ui-background-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
// Scale animation
@keyframes ui-background-scale {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
// Pulse animation
@keyframes ui-background-pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
// Color shifting animation
@keyframes ui-background-hue-shift {
0% {
filter: hue-rotate(0deg);
}
100% {
filter: hue-rotate(360deg);
}
}
// Blur animation for glass effects
@keyframes ui-background-blur-pulse {
0%, 100% {
backdrop-filter: blur(10px);
}
50% {
backdrop-filter: blur(20px);
}
}
// Slide animation
@keyframes ui-background-slide {
0% {
background-position: -100% 0;
}
100% {
background-position: 100% 0;
}
}
// Wave animation
@keyframes ui-background-wave {
0%, 100% {
background-position: 0% 50%;
}
25% {
background-position: 50% 0%;
}
75% {
background-position: 50% 100%;
}
}

View File

@@ -0,0 +1,180 @@
// Base styles for ui-backgrounds library
// Import animations
@import 'animations';
// Base background component styles
.ui-background {
position: relative;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
// Ensure proper layering
z-index: 0;
&--fullscreen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: -1;
}
}
// Type-specific styles
.ui-background--solid {
background-image: none;
}
.ui-background--gradient {
// Base gradient styles
&.ui-background--linear-gradient {
background-size: 200% 200%;
}
&.ui-background--radial-gradient {
background-size: 200% 200%;
}
&.ui-background--conic-gradient {
background-size: 200% 200%;
}
}
.ui-background--pattern {
background-repeat: repeat;
&.ui-background--pattern-dots {
// Dots pattern specific styles
}
&.ui-background--pattern-grid {
// Grid pattern specific styles
}
&.ui-background--pattern-stripes {
// Stripes pattern specific styles
}
&.ui-background--pattern-diagonal-stripes {
// Diagonal stripes pattern specific styles
}
&.ui-background--pattern-chevron {
// Chevron pattern specific styles
}
&.ui-background--pattern-waves {
background-repeat: repeat-x;
}
&.ui-background--pattern-hexagon {
// Hexagon pattern specific styles
}
&.ui-background--pattern-triangles {
// Triangles pattern specific styles
}
&.ui-background--pattern-checkerboard {
// Checkerboard pattern specific styles
}
&.ui-background--pattern-circles {
// Circles pattern specific styles
}
&.ui-background--pattern-crosshatch {
// Crosshatch pattern specific styles
}
&.ui-background--pattern-polka-dots {
// Polka dots pattern specific styles
}
}
.ui-background--image {
// Image background specific styles
}
.ui-background--animated {
// Base animation styles
background-size: 200% 200%;
// Apply default animation
animation: ui-background-animation 5s ease-in-out infinite;
// Respect reduced motion preferences
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
.ui-background--mesh {
background-size: 400% 400%;
animation: ui-mesh-animation 15s ease infinite;
@media (prefers-reduced-motion: reduce) {
animation: none;
}
}
.ui-background--noise {
// Noise background specific styles
}
.ui-background--glass {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); // Safari support
// Fallback for browsers that don't support backdrop-filter
@supports not (backdrop-filter: blur()) {
background-color: rgba(255, 255, 255, 0.8);
}
}
// Responsive behavior
@media (max-width: 768px) {
.ui-background--animated,
.ui-background--mesh {
// Disable complex animations on mobile for performance
@media (prefers-reduced-motion: no-preference) {
animation-duration: 10s; // Slower animations
}
}
}
// High contrast mode support
@media (prefers-contrast: high) {
.ui-background {
filter: contrast(1.2);
}
.ui-background--glass {
backdrop-filter: none;
background-color: rgba(255, 255, 255, 0.95);
}
}
// Print styles
@media print {
.ui-background {
background: none !important;
backdrop-filter: none !important;
animation: none !important;
}
.ui-background--fullscreen {
display: none !important;
}
}
// Dark mode considerations
@media (prefers-color-scheme: dark) {
.ui-background--glass {
background-color: rgba(0, 0, 0, 0.3);
}
}

View File

@@ -0,0 +1,140 @@
// Background Mixins for ui-backgrounds library
// Base background mixin
@mixin ui-background-base() {
position: relative;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
// Full screen background mixin
@mixin ui-background-fullscreen($z-index: -1) {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: $z-index;
}
// Solid color background
@mixin ui-background-solid($color) {
background-color: $color;
background-image: none;
}
// Linear gradient background
@mixin ui-background-linear-gradient($direction: 'to bottom', $colors...) {
background: linear-gradient(#{$direction}, $colors);
}
// Radial gradient background
@mixin ui-background-radial-gradient($shape: 'ellipse', $size: 'farthest-corner', $position: 'center', $colors...) {
background: radial-gradient(#{$shape} #{$size} at #{$position}, $colors);
}
// Conic gradient background
@mixin ui-background-conic-gradient($angle: 0deg, $position: 'center', $colors...) {
background: conic-gradient(from #{$angle} at #{$position}, $colors);
}
// Pattern backgrounds
@mixin ui-pattern-dots($primary: #000, $secondary: transparent, $size: 20px) {
background-image: radial-gradient(circle, #{$primary} 20%, #{$secondary} 20%);
background-size: $size $size;
background-repeat: repeat;
}
@mixin ui-pattern-grid($color: #000, $size: 20px, $line-width: 1px) {
background-image:
linear-gradient(#{$color} #{$line-width}, transparent #{$line-width}),
linear-gradient(90deg, #{$color} #{$line-width}, transparent #{$line-width});
background-size: $size $size;
background-repeat: repeat;
}
@mixin ui-pattern-stripes($primary: #000, $secondary: transparent, $size: 20px) {
background-image: linear-gradient(45deg, #{$primary} 25%, #{$secondary} 25%, #{$secondary} 50%, #{$primary} 50%, #{$primary} 75%, #{$secondary} 75%);
background-size: $size $size;
background-repeat: repeat;
}
@mixin ui-pattern-diagonal-stripes($primary: #000, $secondary: transparent, $size: 20px) {
background-image: linear-gradient(-45deg, #{$primary} 25%, #{$secondary} 25%, #{$secondary} 50%, #{$primary} 50%, #{$primary} 75%, #{$secondary} 75%);
background-size: $size $size;
background-repeat: repeat;
}
@mixin ui-pattern-chevron($primary: #000, $secondary: transparent, $size: 20px) {
background-image:
linear-gradient(45deg, #{$primary} 25%, #{$secondary} 25%),
linear-gradient(-45deg, #{$primary} 25%, #{$secondary} 25%);
background-size: $size $size;
background-repeat: repeat;
background-position: 0 0, 0 ($size / 2);
}
@mixin ui-pattern-waves($primary: #000, $secondary: transparent, $size: 40px) {
background-image: radial-gradient(circle at 50% 100%, #{$secondary} 40%, #{$primary} 40%, #{$primary} 60%, #{$secondary} 60%);
background-size: $size ($size / 2);
background-repeat: repeat-x;
}
@mixin ui-pattern-checkerboard($primary: #000, $secondary: #fff, $size: 20px) {
background-image:
linear-gradient(45deg, #{$primary} 25%, transparent 25%, transparent 75%, #{$primary} 75%),
linear-gradient(45deg, #{$primary} 25%, transparent 25%, transparent 75%, #{$primary} 75%);
background-color: $secondary;
background-size: $size $size;
background-position: 0 0, ($size / 2) ($size / 2);
background-repeat: repeat;
}
// Animated backgrounds
@mixin ui-background-animated($duration: 5s, $timing: ease-in-out, $iteration: infinite) {
animation: ui-background-animation $duration $timing $iteration;
}
@mixin ui-background-mesh-animated($duration: 15s) {
background-size: 400% 400%;
animation: ui-mesh-animation $duration ease infinite;
}
// Glass/blur backgrounds
@mixin ui-background-glass($blur: 10px, $tint: rgba(255, 255, 255, 0.1), $opacity: 0.8) {
background: $tint;
backdrop-filter: blur($blur);
opacity: $opacity;
}
// Responsive background utilities
@mixin ui-background-responsive($mobile-bg, $tablet-bg: null, $desktop-bg: null) {
background: $mobile-bg;
@if $tablet-bg {
@media (min-width: 768px) {
background: $tablet-bg;
}
}
@if $desktop-bg {
@media (min-width: 1024px) {
background: $desktop-bg;
}
}
}
// Accessibility utilities
@mixin ui-background-high-contrast() {
@media (prefers-contrast: high) {
filter: contrast(1.5);
}
}
@mixin ui-background-reduced-motion() {
@media (prefers-reduced-motion: reduce) {
animation: none !important;
}
}

View File

@@ -0,0 +1,9 @@
// Main entry point for ui-backgrounds styles
// Import base components
@import 'mixins';
@import 'animations';
@import 'base';
// Export mixins for external use
// Users can import this file to get access to all background mixins and styles

View File

@@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"exclude": [
"**/*.spec.ts"
]
}

View File

@@ -0,0 +1,11 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.lib.json",
"compilerOptions": {
"declarationMap": false
},
"angularCompilerOptions": {
"compilationMode": "partial"
}
}

View File

@@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}