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:
164
projects/ui-animations/README.md
Normal file
164
projects/ui-animations/README.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# UI Animations
|
||||
|
||||
A comprehensive CSS animation library with Angular services and directives for programmatic control.
|
||||
|
||||
## Features
|
||||
|
||||
- **Pure CSS animations** - Framework agnostic, works everywhere
|
||||
- **Angular integration** - Services and directives for programmatic control
|
||||
- **Comprehensive collection** - Entrance, exit, emphasis, and transition animations
|
||||
- **Design system integration** - Uses semantic motion tokens for consistent timing and easing
|
||||
- **Utility classes** - Animation timing, delays, and control utilities
|
||||
- **SCSS mixins** - Create custom animations easily
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install ui-animations
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Import Styles
|
||||
|
||||
Add to your global styles or component styles:
|
||||
|
||||
```scss
|
||||
// Complete animation library
|
||||
@use 'ui-animations/src/styles' as animations;
|
||||
|
||||
// Or import specific modules
|
||||
@use 'ui-animations/src/styles/animations/entrances';
|
||||
@use 'ui-animations/src/styles/animations/emphasis';
|
||||
@use 'ui-animations/src/styles/utilities/animation-utilities';
|
||||
```
|
||||
|
||||
### CSS Classes
|
||||
|
||||
Simply add animation classes to your HTML elements:
|
||||
|
||||
```html
|
||||
<!-- Entrance animations -->
|
||||
<div class="animate-fade-in">Fade in animation</div>
|
||||
<div class="animate-slide-in-up animation-delay-200">Delayed slide up</div>
|
||||
|
||||
<!-- Emphasis animations -->
|
||||
<button class="animate-bounce">Bouncing button</button>
|
||||
<div class="animate-pulse animation-infinite">Pulsing element</div>
|
||||
|
||||
<!-- Exit animations -->
|
||||
<div class="animate-fade-out">Fade out animation</div>
|
||||
```
|
||||
|
||||
### Angular Service
|
||||
|
||||
```typescript
|
||||
import { UiAnimationsService } from 'ui-animations';
|
||||
|
||||
constructor(private animationService: UiAnimationsService) {}
|
||||
|
||||
// Animate an element
|
||||
animateElement() {
|
||||
const element = document.getElementById('my-element');
|
||||
this.animationService.animateOnce(element, 'animate-bounce');
|
||||
}
|
||||
|
||||
// Animate with callback
|
||||
animateWithCallback() {
|
||||
const element = document.getElementById('my-element');
|
||||
this.animationService.animate(element, 'animate-fade-out', () => {
|
||||
console.log('Animation completed!');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Angular Directive
|
||||
|
||||
```typescript
|
||||
import { AnimateDirective } from 'ui-animations';
|
||||
|
||||
@Component({
|
||||
imports: [AnimateDirective]
|
||||
})
|
||||
```
|
||||
|
||||
```html
|
||||
<!-- Immediate animation -->
|
||||
<div uiAnimate="animate-fade-in-up">Auto animates on load</div>
|
||||
|
||||
<!-- Hover trigger -->
|
||||
<div uiAnimate="animate-bounce" [animationTrigger]="'hover'">
|
||||
Hover to animate
|
||||
</div>
|
||||
|
||||
<!-- Click trigger with single use -->
|
||||
<button uiAnimate="animate-tada" [animationTrigger]="'click'" [animationOnce]="true">
|
||||
Click me!
|
||||
</button>
|
||||
```
|
||||
|
||||
## Animation Categories
|
||||
|
||||
### Entrances
|
||||
- `animate-fade-in` - Basic fade in
|
||||
- `animate-fade-in-up/down/left/right` - Fade with direction
|
||||
- `animate-slide-in-up/down` - Slide animations
|
||||
- `animate-zoom-in` - Scale up animation
|
||||
- `animate-rotate-in` - Rotate entrance
|
||||
|
||||
### Exits
|
||||
- `animate-fade-out` - Basic fade out
|
||||
- `animate-fade-out-up/down/left/right` - Fade with direction
|
||||
- `animate-slide-out-up/down` - Slide animations
|
||||
- `animate-zoom-out` - Scale down animation
|
||||
- `animate-rotate-out` - Rotate exit
|
||||
|
||||
### Emphasis
|
||||
- `animate-bounce` - Bouncing effect
|
||||
- `animate-shake` - Horizontal shake
|
||||
- `animate-pulse` - Scaling pulse
|
||||
- `animate-wobble` - Wobble motion
|
||||
- `animate-tada` - Celebration animation
|
||||
- `animate-heartbeat` - Heart beat effect
|
||||
|
||||
### Utilities
|
||||
- `animation-delay-100/200/300/500/1000` - Animation delays
|
||||
- `animation-duration-fast/normal/slow/slower` - Duration control
|
||||
- `animation-infinite/once/twice` - Iteration control
|
||||
- `animation-paused/running` - Playback control
|
||||
|
||||
## SCSS Mixins
|
||||
|
||||
Create custom animations using provided mixins with semantic tokens:
|
||||
|
||||
```scss
|
||||
@use 'ui-animations/src/styles/mixins/animation-mixins' as mixins;
|
||||
@use 'ui-design-system/src/styles/semantic/motion' as motion;
|
||||
|
||||
.my-custom-animation {
|
||||
@include mixins.animate(myKeyframes, motion.$semantic-motion-duration-normal, motion.$semantic-motion-easing-ease-out);
|
||||
}
|
||||
|
||||
.hover-effect {
|
||||
@include mixins.hover-animation(transform, scale(1.1), motion.$semantic-motion-duration-fast);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
@include mixins.loading-animation(40px, #007bff);
|
||||
}
|
||||
```
|
||||
|
||||
## Design System Integration
|
||||
|
||||
All animations now use semantic motion tokens from the design system:
|
||||
|
||||
- **Durations**: `$semantic-motion-duration-fast`, `$semantic-motion-duration-normal`, `$semantic-motion-duration-slow`, etc.
|
||||
- **Easing**: `$semantic-motion-easing-ease-out`, `$semantic-motion-easing-spring`, `$semantic-motion-easing-bounce`, etc.
|
||||
- **Spacing**: Animation distances use semantic spacing tokens for consistency
|
||||
- **Timing**: Delays use semantic transition timing tokens
|
||||
|
||||
This ensures animations are consistent with your design system's motion principles.
|
||||
|
||||
## Demo
|
||||
|
||||
Visit the demo application to see all animations in action and experiment with different options.
|
||||
7
projects/ui-animations/ng-package.json
Normal file
7
projects/ui-animations/ng-package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||
"dest": "../../dist/ui-animations",
|
||||
"lib": {
|
||||
"entryFile": "src/public-api.ts"
|
||||
}
|
||||
}
|
||||
14
projects/ui-animations/package.json
Normal file
14
projects/ui-animations/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "ui-animations",
|
||||
"version": "0.0.1",
|
||||
"description": "CSS animation library with Angular services and directives for programmatic control",
|
||||
"keywords": ["angular", "animations", "css", "ui", "transitions"],
|
||||
"peerDependencies": {
|
||||
"@angular/common": "^19.2.0",
|
||||
"@angular/core": "^19.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"sideEffects": false
|
||||
}
|
||||
66
projects/ui-animations/src/lib/animate.directive.ts
Normal file
66
projects/ui-animations/src/lib/animate.directive.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Directive, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
|
||||
import { UiAnimationsService } from './ui-animations.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[uiAnimate]',
|
||||
standalone: true
|
||||
})
|
||||
export class AnimateDirective implements OnInit, OnDestroy {
|
||||
@Input('uiAnimate') animationClass: string = '';
|
||||
@Input() animationTrigger: 'immediate' | 'hover' | 'click' | 'manual' = 'immediate';
|
||||
@Input() animationOnce: boolean = false;
|
||||
|
||||
private hoverHandler?: () => void;
|
||||
private clickHandler?: () => void;
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef<HTMLElement>,
|
||||
private animationService: UiAnimationsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.animationTrigger === 'immediate') {
|
||||
this.animate();
|
||||
} else if (this.animationTrigger === 'hover') {
|
||||
this.setupHoverTrigger();
|
||||
} else if (this.animationTrigger === 'click') {
|
||||
this.setupClickTrigger();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.hoverHandler) {
|
||||
this.elementRef.nativeElement.removeEventListener('mouseenter', this.hoverHandler);
|
||||
}
|
||||
if (this.clickHandler) {
|
||||
this.elementRef.nativeElement.removeEventListener('click', this.clickHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private setupHoverTrigger() {
|
||||
this.hoverHandler = () => this.animate();
|
||||
this.elementRef.nativeElement.addEventListener('mouseenter', this.hoverHandler);
|
||||
}
|
||||
|
||||
private setupClickTrigger() {
|
||||
this.clickHandler = () => this.animate();
|
||||
this.elementRef.nativeElement.addEventListener('click', this.clickHandler);
|
||||
}
|
||||
|
||||
private animate() {
|
||||
if (!this.animationClass) return;
|
||||
|
||||
if (this.animationOnce) {
|
||||
this.animationService.animateOnce(this.elementRef.nativeElement, this.animationClass);
|
||||
} else {
|
||||
this.animationService.animate(this.elementRef.nativeElement, this.animationClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually trigger animation (for manual trigger mode)
|
||||
*/
|
||||
public trigger() {
|
||||
this.animate();
|
||||
}
|
||||
}
|
||||
16
projects/ui-animations/src/lib/ui-animations.service.spec.ts
Normal file
16
projects/ui-animations/src/lib/ui-animations.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UiAnimationsService } from './ui-animations.service';
|
||||
|
||||
describe('UiAnimationsService', () => {
|
||||
let service: UiAnimationsService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(UiAnimationsService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
46
projects/ui-animations/src/lib/ui-animations.service.ts
Normal file
46
projects/ui-animations/src/lib/ui-animations.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UiAnimationsService {
|
||||
|
||||
/**
|
||||
* Add animation class to element with optional completion callback
|
||||
*/
|
||||
animate(element: HTMLElement, animationClass: string, onComplete?: () => void): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
const handleAnimationEnd = () => {
|
||||
element.removeEventListener('animationend', handleAnimationEnd);
|
||||
if (onComplete) onComplete();
|
||||
resolve();
|
||||
};
|
||||
|
||||
element.addEventListener('animationend', handleAnimationEnd);
|
||||
element.classList.add(animationClass);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove animation class from element
|
||||
*/
|
||||
removeAnimation(element: HTMLElement, animationClass: string): void {
|
||||
element.classList.remove(animationClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add animation class and automatically remove it after completion
|
||||
*/
|
||||
animateOnce(element: HTMLElement, animationClass: string): Promise<void> {
|
||||
return this.animate(element, animationClass, () => {
|
||||
this.removeAnimation(element, animationClass);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if element has animation class
|
||||
*/
|
||||
hasAnimation(element: HTMLElement, animationClass: string): boolean {
|
||||
return element.classList.contains(animationClass);
|
||||
}
|
||||
}
|
||||
6
projects/ui-animations/src/public-api.ts
Normal file
6
projects/ui-animations/src/public-api.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Public API Surface of ui-animations
|
||||
*/
|
||||
|
||||
export * from './lib/ui-animations.service';
|
||||
export * from './lib/animate.directive';
|
||||
142
projects/ui-animations/src/styles/animations/_emphasis.scss
Normal file
142
projects/ui-animations/src/styles/animations/_emphasis.scss
Normal file
@@ -0,0 +1,142 @@
|
||||
// Emphasis Animations
|
||||
// Attention-grabbing animations for highlighting elements
|
||||
|
||||
// Bounce
|
||||
@keyframes bounce {
|
||||
0%, 20%, 53%, 80%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
40%, 43% {
|
||||
transform: translateY(-24px);
|
||||
}
|
||||
70% {
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
90% {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-bounce {
|
||||
animation: bounce 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Shake
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(4px); }
|
||||
}
|
||||
|
||||
.animate-shake {
|
||||
animation: shake 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Pulse
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.animate-pulse {
|
||||
animation: pulse 0.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
// Flash
|
||||
@keyframes flash {
|
||||
0%, 50%, 100% { opacity: 1; }
|
||||
25%, 75% { opacity: 0; }
|
||||
}
|
||||
|
||||
.animate-flash {
|
||||
animation: flash 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Wobble
|
||||
@keyframes wobble {
|
||||
0% { transform: translateX(0%); }
|
||||
15% { transform: translateX(-25%) rotate(-5deg); }
|
||||
30% { transform: translateX(20%) rotate(3deg); }
|
||||
45% { transform: translateX(-15%) rotate(-3deg); }
|
||||
60% { transform: translateX(10%) rotate(2deg); }
|
||||
75% { transform: translateX(-5%) rotate(-1deg); }
|
||||
100% { transform: translateX(0%); }
|
||||
}
|
||||
|
||||
.animate-wobble {
|
||||
animation: wobble 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Swing
|
||||
@keyframes swing {
|
||||
20% { transform: rotate(15deg); }
|
||||
40% { transform: rotate(-10deg); }
|
||||
60% { transform: rotate(5deg); }
|
||||
80% { transform: rotate(-5deg); }
|
||||
100% { transform: rotate(0deg); }
|
||||
}
|
||||
|
||||
.animate-swing {
|
||||
animation: swing 0.6s ease-in-out;
|
||||
transform-origin: top center;
|
||||
}
|
||||
|
||||
// Rubber Band
|
||||
@keyframes rubberBand {
|
||||
0% { transform: scaleX(1); }
|
||||
30% { transform: scaleX(1.25) scaleY(0.75); }
|
||||
40% { transform: scaleX(0.75) scaleY(1.25); }
|
||||
50% { transform: scaleX(1.15) scaleY(0.85); }
|
||||
65% { transform: scaleX(0.95) scaleY(1.05); }
|
||||
75% { transform: scaleX(1.05) scaleY(0.95); }
|
||||
100% { transform: scaleX(1) scaleY(1); }
|
||||
}
|
||||
|
||||
.animate-rubber-band {
|
||||
animation: rubberBand 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Tada
|
||||
@keyframes tada {
|
||||
0% { transform: scaleX(1) scaleY(1); }
|
||||
10%, 20% { transform: scaleX(0.9) scaleY(0.9) rotate(-3deg); }
|
||||
30%, 50%, 70%, 90% { transform: scaleX(1.1) scaleY(1.1) rotate(3deg); }
|
||||
40%, 60%, 80% { transform: scaleX(1.1) scaleY(1.1) rotate(-3deg); }
|
||||
100% { transform: scaleX(1) scaleY(1) rotate(0); }
|
||||
}
|
||||
|
||||
.animate-tada {
|
||||
animation: tada 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
// Heartbeat
|
||||
@keyframes heartbeat {
|
||||
0% { transform: scale(1); }
|
||||
14% { transform: scale(1.3); }
|
||||
28% { transform: scale(1); }
|
||||
42% { transform: scale(1.3); }
|
||||
70% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.animate-heartbeat {
|
||||
animation: heartbeat 0.6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
// Jello
|
||||
@keyframes jello {
|
||||
11.1% { transform: skewX(-12.5deg) skewY(-12.5deg); }
|
||||
22.2% { transform: skewX(6.25deg) skewY(6.25deg); }
|
||||
33.3% { transform: skewX(-3.125deg) skewY(-3.125deg); }
|
||||
44.4% { transform: skewX(1.5625deg) skewY(1.5625deg); }
|
||||
55.5% { transform: skewX(-0.78125deg) skewY(-0.78125deg); }
|
||||
66.6% { transform: skewX(0.390625deg) skewY(0.390625deg); }
|
||||
77.7% { transform: skewX(-0.1953125deg) skewY(-0.1953125deg); }
|
||||
88.8% { transform: skewX(0.09765625deg) skewY(0.09765625deg); }
|
||||
100% { transform: skewX(0) skewY(0); }
|
||||
}
|
||||
|
||||
.animate-jello {
|
||||
animation: jello 0.6s ease-in-out;
|
||||
transform-origin: center;
|
||||
}
|
||||
151
projects/ui-animations/src/styles/animations/_entrances.scss
Normal file
151
projects/ui-animations/src/styles/animations/_entrances.scss
Normal file
@@ -0,0 +1,151 @@
|
||||
// Entrance Animations
|
||||
// Elements appearing/entering the view
|
||||
|
||||
|
||||
// Fade In
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Fade In Up
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-up {
|
||||
animation: fadeInUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Fade In Down
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-down {
|
||||
animation: fadeInDown 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Fade In Left
|
||||
@keyframes fadeInLeft {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-left {
|
||||
animation: fadeInLeft 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Fade In Right
|
||||
@keyframes fadeInRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-in-right {
|
||||
animation: fadeInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Slide In Up
|
||||
@keyframes slideInUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-in-up {
|
||||
animation: slideInUp 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Slide In Down
|
||||
@keyframes slideInDown {
|
||||
from {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-in-down {
|
||||
animation: slideInDown 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Zoom In
|
||||
@keyframes zoomIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-zoom-in {
|
||||
animation: zoomIn 0.3s ease-out;
|
||||
}
|
||||
|
||||
// Scale Up
|
||||
@keyframes scaleUp {
|
||||
from {
|
||||
transform: scale(0);
|
||||
}
|
||||
to {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-scale-up {
|
||||
animation: scaleUp 0.15s ease-out;
|
||||
}
|
||||
|
||||
// Rotate In
|
||||
@keyframes rotateIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: rotate(-200deg);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-rotate-in {
|
||||
animation: rotateIn 0.3s ease-out;
|
||||
}
|
||||
151
projects/ui-animations/src/styles/animations/_exits.scss
Normal file
151
projects/ui-animations/src/styles/animations/_exits.scss
Normal file
@@ -0,0 +1,151 @@
|
||||
// Exit Animations
|
||||
// Elements disappearing/leaving the view
|
||||
|
||||
|
||||
// Fade Out
|
||||
@keyframes fadeOut {
|
||||
from { opacity: 1; }
|
||||
to { opacity: 0; }
|
||||
}
|
||||
|
||||
.animate-fade-out {
|
||||
animation: fadeOut 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Fade Out Up
|
||||
@keyframes fadeOutUp {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-24px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-out-up {
|
||||
animation: fadeOutUp 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Fade Out Down
|
||||
@keyframes fadeOutDown {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-out-down {
|
||||
animation: fadeOutDown 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Fade Out Left
|
||||
@keyframes fadeOutLeft {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(-24px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-out-left {
|
||||
animation: fadeOutLeft 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Fade Out Right
|
||||
@keyframes fadeOutRight {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateX(24px);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-fade-out-right {
|
||||
animation: fadeOutRight 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Slide Out Up
|
||||
@keyframes slideOutUp {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-out-up {
|
||||
animation: slideOutUp 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Slide Out Down
|
||||
@keyframes slideOutDown {
|
||||
from {
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-slide-out-down {
|
||||
animation: slideOutDown 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Zoom Out
|
||||
@keyframes zoomOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: scale(0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-zoom-out {
|
||||
animation: zoomOut 0.3s ease-in;
|
||||
}
|
||||
|
||||
// Scale Down
|
||||
@keyframes scaleDown {
|
||||
from {
|
||||
transform: scale(1);
|
||||
}
|
||||
to {
|
||||
transform: scale(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-scale-down {
|
||||
animation: scaleDown 0.15s ease-in;
|
||||
}
|
||||
|
||||
// Rotate Out
|
||||
@keyframes rotateOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: rotate(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: rotate(200deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-rotate-out {
|
||||
animation: rotateOut 0.3s ease-in;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Base Transitions
|
||||
// Smooth transitions for common properties
|
||||
|
||||
// Transition utilities
|
||||
.transition-all {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.transition-opacity {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.transition-transform {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.transition-colors {
|
||||
transition: color, background-color, border-color 0.3s ease;
|
||||
}
|
||||
|
||||
// Transition durations
|
||||
.transition-fast {
|
||||
transition-duration: 0.15s;
|
||||
}
|
||||
|
||||
.transition-normal {
|
||||
transition-duration: 0.3s;
|
||||
}
|
||||
|
||||
.transition-slow {
|
||||
transition-duration: 0.6s;
|
||||
}
|
||||
|
||||
// Transition timing functions
|
||||
.transition-linear {
|
||||
transition-timing-function: linear;
|
||||
}
|
||||
|
||||
.transition-ease-in {
|
||||
transition-timing-function: ease-in;
|
||||
}
|
||||
|
||||
.transition-ease-out {
|
||||
transition-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.transition-ease-in-out {
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.transition-bounce {
|
||||
transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
21
projects/ui-animations/src/styles/index.scss
Normal file
21
projects/ui-animations/src/styles/index.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
// UI Animations - Main Entry Point
|
||||
// Import all animation styles
|
||||
|
||||
// Animations
|
||||
@forward 'animations/transitions';
|
||||
@forward 'animations/entrances';
|
||||
@forward 'animations/exits';
|
||||
@forward 'animations/emphasis';
|
||||
|
||||
// Utilities
|
||||
@forward 'utilities/animation-utilities';
|
||||
|
||||
// Mixins (use @use to access mixins in your components)
|
||||
@forward 'mixins/animation-mixins';
|
||||
|
||||
// Export all animations and utilities
|
||||
@use 'animations/transitions';
|
||||
@use 'animations/entrances';
|
||||
@use 'animations/exits';
|
||||
@use 'animations/emphasis';
|
||||
@use 'utilities/animation-utilities';
|
||||
130
projects/ui-animations/src/styles/mixins/_animation-mixins.scss
Normal file
130
projects/ui-animations/src/styles/mixins/_animation-mixins.scss
Normal file
@@ -0,0 +1,130 @@
|
||||
// Animation Mixins
|
||||
// Reusable SCSS mixins for creating custom animations
|
||||
|
||||
|
||||
// Generic animation mixin
|
||||
@mixin animate(
|
||||
$name,
|
||||
$duration: 0.3s,
|
||||
$timing-function: ease-out,
|
||||
$delay: motion.$semantic-motion-transition-delay-0,
|
||||
$iteration-count: 1,
|
||||
$direction: normal,
|
||||
$fill-mode: both
|
||||
) {
|
||||
animation-name: $name;
|
||||
animation-duration: $duration;
|
||||
animation-timing-function: $timing-function;
|
||||
animation-delay: $delay;
|
||||
animation-iteration-count: $iteration-count;
|
||||
animation-direction: $direction;
|
||||
animation-fill-mode: $fill-mode;
|
||||
}
|
||||
|
||||
// Transition mixin with design system tokens
|
||||
@mixin transition(
|
||||
$property: motion.$semantic-motion-transition-property-all,
|
||||
$duration: 0.3s,
|
||||
$timing-function: ease,
|
||||
$delay: motion.$semantic-motion-transition-delay-0
|
||||
) {
|
||||
transition-property: $property;
|
||||
transition-duration: $duration;
|
||||
transition-timing-function: $timing-function;
|
||||
transition-delay: $delay;
|
||||
}
|
||||
|
||||
// Fade animation mixin
|
||||
@mixin fade-animation($direction: 'in', $distance: 24px) {
|
||||
@if $direction == 'in' {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY($distance);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
} @else {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(-#{$distance});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slide animation mixin
|
||||
@mixin slide-animation($direction: 'up', $distance: 100%) {
|
||||
@if $direction == 'up' {
|
||||
from { transform: translateY($distance); }
|
||||
to { transform: translateY(0); }
|
||||
} @else if $direction == 'down' {
|
||||
from { transform: translateY(-$distance); }
|
||||
to { transform: translateY(0); }
|
||||
} @else if $direction == 'left' {
|
||||
from { transform: translateX($distance); }
|
||||
to { transform: translateX(0); }
|
||||
} @else if $direction == 'right' {
|
||||
from { transform: translateX(-$distance); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
}
|
||||
|
||||
// Scale animation mixin
|
||||
@mixin scale-animation($from: 0, $to: 1) {
|
||||
from {
|
||||
transform: scale($from);
|
||||
}
|
||||
to {
|
||||
transform: scale($to);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate animation mixin
|
||||
@mixin rotate-animation($from: 0deg, $to: 360deg) {
|
||||
from {
|
||||
transform: rotate($from);
|
||||
}
|
||||
to {
|
||||
transform: rotate($to);
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animation mixin
|
||||
@mixin hover-animation($property: transform, $value: scale(1.05), $duration: 0.15s) {
|
||||
transition: $property $duration ease;
|
||||
|
||||
&:hover {
|
||||
#{$property}: $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading animation mixin
|
||||
@mixin loading-animation($size: 40px, $color: currentColor) {
|
||||
width: $size;
|
||||
height: $size;
|
||||
border: 3px solid rgba($color, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: $color;
|
||||
animation: spin 0.6sest ease-in-out infinite;
|
||||
}
|
||||
|
||||
// Keyframes for common animations
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
// Staggered animation mixin for lists
|
||||
@mixin staggered-animation($base-delay: motion.$semantic-motion-transition-delay-100, $animation: fadeInUp 0.3s ease-out) {
|
||||
@for $i from 1 through 10 {
|
||||
&:nth-child(#{$i}) {
|
||||
animation: $animation;
|
||||
animation-delay: #{$base-delay * $i};
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Animation Utilities
|
||||
// Helper classes for animation control
|
||||
|
||||
|
||||
// Animation delays
|
||||
.animation-delay-100 {
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
.animation-delay-200 {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.animation-delay-300 {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.animation-delay-500 {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.animation-delay-1000 {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
// Animation durations
|
||||
.animation-duration-fast {
|
||||
animation-duration: 0.15s;
|
||||
}
|
||||
|
||||
.animation-duration-normal {
|
||||
animation-duration: 0.3s;
|
||||
}
|
||||
|
||||
.animation-duration-slow {
|
||||
animation-duration: 0.6s;
|
||||
}
|
||||
|
||||
.animation-duration-slower {
|
||||
animation-duration: 1s;
|
||||
}
|
||||
|
||||
// Animation fill modes
|
||||
.animation-fill-none {
|
||||
animation-fill-mode: none;
|
||||
}
|
||||
|
||||
.animation-fill-forwards {
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
.animation-fill-backwards {
|
||||
animation-fill-mode: backwards;
|
||||
}
|
||||
|
||||
.animation-fill-both {
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
// Animation iteration counts
|
||||
.animation-infinite {
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.animation-once {
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
.animation-twice {
|
||||
animation-iteration-count: 2;
|
||||
}
|
||||
|
||||
// Animation play states
|
||||
.animation-paused {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.animation-running {
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
// Animation directions
|
||||
.animation-normal {
|
||||
animation-direction: normal;
|
||||
}
|
||||
|
||||
.animation-reverse {
|
||||
animation-direction: reverse;
|
||||
}
|
||||
|
||||
.animation-alternate {
|
||||
animation-direction: alternate;
|
||||
}
|
||||
|
||||
.animation-alternate-reverse {
|
||||
animation-direction: alternate-reverse;
|
||||
}
|
||||
|
||||
// Animation timing functions
|
||||
.animation-linear {
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.animation-ease-in {
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
|
||||
.animation-ease-out {
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
.animation-ease-in-out {
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.animation-bounce-timing {
|
||||
animation-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
}
|
||||
15
projects/ui-animations/tsconfig.lib.json
Normal file
15
projects/ui-animations/tsconfig.lib.json
Normal 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"
|
||||
]
|
||||
}
|
||||
11
projects/ui-animations/tsconfig.lib.prod.json
Normal file
11
projects/ui-animations/tsconfig.lib.prod.json
Normal 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"
|
||||
}
|
||||
}
|
||||
15
projects/ui-animations/tsconfig.spec.json
Normal file
15
projects/ui-animations/tsconfig.spec.json
Normal 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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user