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:
@@ -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`);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './solid-background.component';
|
||||
export * from './gradient-background.component';
|
||||
export * from './pattern-background.component';
|
||||
export * from './image-background.component';
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
projects/ui-backgrounds/src/lib/components/index.ts
Normal file
1
projects/ui-backgrounds/src/lib/components/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './backgrounds/index';
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
1
projects/ui-backgrounds/src/lib/directives/index.ts
Normal file
1
projects/ui-backgrounds/src/lib/directives/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './background.directive';
|
||||
191
projects/ui-backgrounds/src/lib/services/background.service.ts
Normal file
191
projects/ui-backgrounds/src/lib/services/background.service.ts
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
1
projects/ui-backgrounds/src/lib/services/index.ts
Normal file
1
projects/ui-backgrounds/src/lib/services/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './background.service';
|
||||
142
projects/ui-backgrounds/src/lib/types/background.types.ts
Normal file
142
projects/ui-backgrounds/src/lib/types/background.types.ts
Normal 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;
|
||||
}
|
||||
1
projects/ui-backgrounds/src/lib/types/index.ts
Normal file
1
projects/ui-backgrounds/src/lib/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './background.types';
|
||||
234
projects/ui-backgrounds/src/lib/utils/background-generator.ts
Normal file
234
projects/ui-backgrounds/src/lib/utils/background-generator.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
1
projects/ui-backgrounds/src/lib/utils/index.ts
Normal file
1
projects/ui-backgrounds/src/lib/utils/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './background-generator';
|
||||
Reference in New Issue
Block a user