Skip to content

Commit 0e653b8

Browse files
fix(angular-3d): resolve lifecycle hooks and test issues
1 parent 9dc111e commit 0e653b8

50 files changed

Lines changed: 4236 additions & 1652 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# Angular GSAP directives (current implementation) + consumers
2+
3+
This doc captures the **exact GSAP/ScrollTrigger-based directives/components currently living inside `dev-brand-ui`** and every known consumer, as the source-of-truth for extracting a new standalone library: **`@hive-academy/angular-gsap`**.
4+
5+
Scope:
6+
7+
- ✅ Document existing behavior and public APIs (as implemented today).
8+
- ✅ List all components that use these directives/components.
9+
- ✅ Describe how they should move into the new `@hive-academy/angular-gsap` workspace.
10+
11+
## Why a separate `@hive-academy/angular-gsap` library
12+
13+
Right now the GSAP directives live under the Angular 3D area:
14+
15+
- `apps/dev-brand-ui/src/app/core/angular-3d/directives/*`
16+
17+
But they are **not 3D-specific**:
18+
19+
- `scrollAnimation` animates regular DOM elements.
20+
- `hijackedScroll` is a generic “scroll-jacked” content sequence.
21+
22+
Extracting them into `@hive-academy/angular-gsap` lets the 3D package (`@hive-academy/angular-3d`) stay focused on Three.js primitives while still reusing scroll UX patterns.
23+
24+
## Current GSAP directives (to migrate)
25+
26+
### 1) `ScrollAnimationDirective`
27+
28+
- File: [apps/dev-brand-ui/src/app/core/angular-3d/directives/scroll-animation.directive.ts](apps/dev-brand-ui/src/app/core/angular-3d/directives/scroll-animation.directive.ts)
29+
- Selector: `[scrollAnimation]`
30+
- What it does:
31+
- Creates a paused GSAP timeline, binds it to a `ScrollTrigger`.
32+
- Supports predefined animation types (`fadeIn`, `slideUp`, `scaleIn`, `parallax`, etc.) and fully custom `from/to`.
33+
- Cleans up on destroy and re-initializes when config changes.
34+
35+
**Primary input**
36+
37+
- `scrollConfig: ScrollAnimationConfig` (signal-based input)
38+
- Defaults: `{ animation: 'fadeIn', start: 'top 80%', duration: 1, ease: 'power2.out' }`
39+
40+
**Key config fields**
41+
42+
- ScrollTrigger: `trigger`, `start`, `end`, `scrub`, `pin`, `pinSpacing`, `markers`, `once`, `toggleActions`
43+
- Animation: `duration`, `delay`, `ease`, `stagger`, `from`, `to`
44+
- Callbacks: `onEnter`, `onLeave`, `onEnterBack`, `onLeaveBack`, `onUpdate(progress)`
45+
46+
**Important behavior notes**
47+
48+
- It currently guards against being attached to non-`HTMLElement` targets and will log a warning.
49+
50+
### 2) `HijackedScrollDirective`
51+
52+
- File: [apps/dev-brand-ui/src/app/core/angular-3d/directives/hijacked-scroll.directive.ts](apps/dev-brand-ui/src/app/core/angular-3d/directives/hijacked-scroll.directive.ts)
53+
- Selector: `[hijackedScroll]`
54+
- What it does:
55+
- Pins the container while scrolling through “steps”.
56+
- Builds a GSAP master timeline where each step fades/slides in/out.
57+
- Emits current step index and overall progress.
58+
- Supports per-step “decoration” animations via `[data-decoration-index]` + `.decoration-inner`.
59+
60+
**Inputs**
61+
62+
- `scrollHeightPerStep` (vh per step, default `100`)
63+
- `animationDuration` (seconds, default `0.3`)
64+
- `ease` (default `power2.out`)
65+
- `markers` (default `false`)
66+
- `minHeight` (default `100vh`)
67+
- `start` (default `top top`)
68+
- `end` (optional; otherwise calculated)
69+
70+
**Outputs**
71+
72+
- `currentStepChange: number`
73+
- `progressChange: number`
74+
75+
**Child dependency**
76+
77+
- Discovers its steps via `contentChildren(HijackedScrollItemDirective, { descendants: true })`.
78+
79+
### 3) `HijackedScrollItemDirective`
80+
81+
- File: [apps/dev-brand-ui/src/app/core/angular-3d/directives/hijacked-scroll-item.directive.ts](apps/dev-brand-ui/src/app/core/angular-3d/directives/hijacked-scroll-item.directive.ts)
82+
- Selector: `[hijackedScrollItem]`
83+
- What it does:
84+
- Marks an element as a step inside a hijacked scroll container.
85+
- Provides per-step config used by `HijackedScrollDirective`.
86+
87+
**Inputs**
88+
89+
- `slideDirection: 'left' | 'right' | 'up' | 'down' | 'none'` (default `none`)
90+
- `fadeIn: boolean` (default `true`)
91+
- `scale: boolean` (default `true`)
92+
- `customFrom?: Record<string, unknown>`
93+
- `customTo?: Record<string, unknown>`
94+
95+
## Components that wrap or use these directives
96+
97+
### 1) `HijackedScrollTimelineComponent` (wrapper)
98+
99+
- File: [apps/dev-brand-ui/src/app/shared/components/hijacked-scroll-timeline.component.ts](apps/dev-brand-ui/src/app/shared/components/hijacked-scroll-timeline.component.ts)
100+
- Selector: `app-hijacked-scroll-timeline`
101+
- What it does:
102+
- Thin wrapper around `HijackedScrollDirective` via `hostDirectives`.
103+
- Pass-through inputs/outputs, with `ng-content` so each feature section can own its markup.
104+
105+
### 2) `ScrollingCodeTimelineComponent` (direct user)
106+
107+
- File: [apps/dev-brand-ui/src/app/shared/components/scrolling-code-timeline.component.ts](apps/dev-brand-ui/src/app/shared/components/scrolling-code-timeline.component.ts)
108+
- What it does:
109+
- Uses `HijackedScrollDirective` + `HijackedScrollItemDirective` directly.
110+
- Implements an `@for`-driven progressive “story” layout.
111+
112+
## Consumers inventory (who uses what)
113+
114+
### Uses `ScrollAnimationDirective` (`[scrollAnimation]`)
115+
116+
Landing page sections importing/using `ScrollAnimationDirective`:
117+
118+
- [temp/capabilities-matrix-section.component.ts](temp/capabilities-matrix-section.component.ts)
119+
- [temp/problem-solution-section.component.ts](temp/problem-solution-section.component.ts)
120+
- [temp/value-propositions-section.component.ts](temp/value-propositions-section.component.ts)
121+
- [temp/workflow-examples-section.component.ts](temp/workflow-examples-section.component.ts)
122+
- [temp/neo4j-section.component.ts](temp/neo4j-section.component.ts)
123+
- [temp/langgraph-memory-section.component.ts](temp/langgraph-memory-section.component.ts)
124+
- [temp/langgraph-core-section.component.ts](temp/langgraph-core-section.component.ts)
125+
- [temp/hero-section.component.ts](temp/hero-section.component.ts)
126+
- [temp/developer-experience-section.component.ts](temp/developer-experience-section.component.ts)
127+
- [temp/cta-section.component.ts](temp/cta-section.component.ts)
128+
- [temp/chromadb-section.component.ts](temp/chromadb-section.component.ts)
129+
130+
Also present (currently commented out DOM overlay):
131+
132+
- [temp/hero-section-space.component.ts](temp/hero-section-space.component.ts)
133+
134+
### Uses `HijackedScrollTimelineComponent` (`<app-hijacked-scroll-timeline>`)
135+
136+
- [temp/langgraph-core-section.component.ts](temp/langgraph-core-section.component.ts)
137+
- [temp/langgraph-memory-section.component.ts](temp/langgraph-memory-section.component.ts)
138+
- [temp/neo4j-section.component.ts](temp/neo4j-section.component.ts)
139+
- [temp/chromadb-section.component.ts](temp/chromadb-section.component.ts)
140+
141+
### Uses `HijackedScrollDirective` / `HijackedScrollItemDirective` directly
142+
143+
- [apps/dev-brand-ui/src/app/shared/components/scrolling-code-timeline.component.ts](apps/dev-brand-ui/src/app/shared/components/scrolling-code-timeline.component.ts)
144+
145+
## Extraction guide: `@hive-academy/angular-gsap`
146+
147+
### Recommended library surface
148+
149+
For the new workspace package, treat these as the initial public API:
150+
151+
- `ScrollAnimationDirective`
152+
- `HijackedScrollDirective`
153+
- `HijackedScrollItemDirective`
154+
- `HijackedScrollTimelineComponent` (optional, but it’s already a stable convenience wrapper)
155+
- Types:
156+
- `ScrollAnimationConfig`, `AnimationType`
157+
- `HijackedScrollConfig`, `HijackedScrollItemConfig`, `SlideDirection`
158+
159+
### Suggested folder layout
160+
161+
- `libs/angular-gsap/src/lib/scroll/scroll-animation.directive.ts`
162+
- `libs/angular-gsap/src/lib/scroll/hijacked-scroll.directive.ts`
163+
- `libs/angular-gsap/src/lib/scroll/hijacked-scroll-item.directive.ts`
164+
- `libs/angular-gsap/src/lib/components/hijacked-scroll-timeline.component.ts`
165+
- `libs/angular-gsap/src/index.ts` (public exports)
166+
167+
### Dependencies and packaging
168+
169+
- `gsap` should be a dependency (or peerDependency if you want app-level control).
170+
- `@angular/*` should follow the workspace Angular version (peer deps for publishing).
171+
172+
### SSR / platform note (important)
173+
174+
Today, both directives call `gsap.registerPlugin(ScrollTrigger)` at module load time.
175+
176+
In the new library, prefer registering ScrollTrigger only in the browser (e.g., via `isPlatformBrowser`), so SSR builds don’t accidentally execute DOM-dependent code.
177+
178+
## “Related GSAP usage” to consider later
179+
180+
Not part of the directive set above, but these are additional GSAP touchpoints you may want to fold into `@hive-academy/angular-gsap` (or a separate animation lib):
181+
182+
- [apps/dev-brand-ui/src/app/core/angular-3d/services/animation.service.ts](apps/dev-brand-ui/src/app/core/angular-3d/services/animation.service.ts) (GSAP-driven service, currently also depends on `angular-three`)
183+
- [apps/dev-brand-ui/src/app/core/angular-3d/components/primitives/planet.component.ts](apps/dev-brand-ui/src/app/core/angular-3d/components/primitives/planet.component.ts) (uses GSAP tween for rotation)
184+
185+
## Quick “known-good” usage patterns (from current code)
186+
187+
### `scrollAnimation` on hero elements
188+
189+
See patterns in:
190+
191+
- [temp/developer-experience-section.component.ts](temp/developer-experience-section.component.ts)
192+
- [temp/workflow-examples-section.component.ts](temp/workflow-examples-section.component.ts)
193+
194+
### Hijacked scroll timeline with projected content
195+
196+
See:
197+
198+
- [temp/chromadb-section.component.ts](temp/chromadb-section.component.ts)
199+
- [temp/langgraph-core-section.component.ts](temp/langgraph-core-section.component.ts)

docs/angular-3d-library/DOCUMENT-INDEX.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
6. **[05-angular-3d-package-requirements.md](./05-angular-3d-package-requirements.md)** - Minimal replacement checklist (explicitly includes copying angular-3d + scene-graphs into a new workspace)
1313
7. **[06-new-workspace-blueprint.md](./06-new-workspace-blueprint.md)** - Template migration vs ngt-tag compatibility + how to reuse threejs-vanilla docs
1414
8. **[07-threejs-vanilla-to-angular-mapping.md](./07-threejs-vanilla-to-angular-mapping.md)** - Chapter-by-chapter mapping into package folders
15-
9. **[08-git-hooks-linting-standards.md](./08-git-hooks-linting-standards.md)** - Husky + lint-staged + commitlint + ESLint standards to mirror this repo
15+
9. **[09-angular-gsap-directives-and-consumers.md](./09-angular-gsap-directives-and-consumers.md)** - GSAP/ScrollTrigger directives + all current consumers (for new `@hive-academy/angular-gsap`)
16+
10. **[08-git-hooks-linting-standards.md](./08-git-hooks-linting-standards.md)** - Husky + lint-staged + commitlint + ESLint standards to mirror this repo
1617

1718
### 🚧 Planned (Component Patterns)
1819

19-
10. **06-primitive-components.md** - Wrapping Three.js meshes (Planet, Box, Sphere)
20-
11. **07-material-management.md** - Reactive material updates with signals
21-
12. **08-geometry-handling.md** - BufferGeometry, instancing patterns
22-
13. **09-loader-components.md** - GLTF, SVG, texture loaders
20+
11. **06-primitive-components.md** - Wrapping Three.js meshes (Planet, Box, Sphere)
21+
12. **07-material-management.md** - Reactive material updates with signals
22+
13. **08-geometry-handling.md** - BufferGeometry, instancing patterns
23+
14. **09-loader-components.md** - GLTF, SVG, texture loaders
2324

2425
### 🚧 Planned (Directive Patterns)
2526

libs/angular-3d/src/lib/primitives/box.component.ts

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
Component,
33
ChangeDetectionStrategy,
4-
OnInit,
54
OnDestroy,
65
inject,
76
input,
@@ -16,7 +15,7 @@ import { NG_3D_PARENT } from '../types/tokens';
1615
changeDetection: ChangeDetectionStrategy.OnPush,
1716
template: '',
1817
})
19-
export class BoxComponent implements OnInit, OnDestroy {
18+
export class BoxComponent implements OnDestroy {
2019
// Transformation inputs
2120
public readonly position = input<[number, number, number]>([0, 0, 0]);
2221
public readonly rotation = input<[number, number, number]>([0, 0, 0]);
@@ -61,45 +60,63 @@ export class BoxComponent implements OnInit, OnDestroy {
6160
}
6261
});
6362
effect(() => {
64-
// Re-create geometry if args change (expensive but necessary for geometry params)
63+
// Re-create geometry and material if args or material props change
64+
// Note: Typically geometry and material are separate concerns.
65+
// But for primitives, it's often cleaner to rebuild everything or use distinct effects.
66+
// Optimally:
67+
// 1. Geometry effect (depends on args)
68+
// 2. Material effect (depends on color, wireframe)
69+
// 3. Mesh effect (depends on geometry, material)
70+
// Let's do that for clarity and performance.
71+
6572
const [width, height, depth] = this.args();
66-
if (this.geometry) {
67-
this.geometry.dispose();
68-
}
69-
this.geometry = new THREE.BoxGeometry(width, height, depth);
73+
const newGeometry = new THREE.BoxGeometry(width, height, depth);
74+
75+
// Dispose old
76+
if (this.geometry) this.geometry.dispose();
77+
this.geometry = newGeometry;
78+
79+
// Update mesh
7080
if (this.mesh) {
7181
this.mesh.geometry = this.geometry;
82+
} else {
83+
// First run? We need to ensure mesh exists.
84+
// But constructor runs before ngOnInit used to.
85+
// We can create the mesh container in constructor?
86+
// Or create it here?
87+
// If we split effects, we need a common coordinate.
88+
// Pattern: Create mesh structure in a main effect logic?
89+
this.mesh = new THREE.Mesh(this.geometry, this.material);
90+
this.mesh.position.set(...this.position());
91+
this.mesh.rotation.set(...this.rotation());
92+
this.mesh.scale.set(...this.scale());
93+
this.addToParent();
7294
}
7395
});
74-
}
7596

76-
public ngOnInit(): void {
77-
// Initialize geometry
78-
const [width, height, depth] = this.args();
79-
this.geometry = new THREE.BoxGeometry(width, height, depth);
97+
effect(() => {
98+
// Material updates
99+
const color = this.color();
100+
const wireframe = this.wireframe();
80101

81-
// Initialize material
82-
this.material = new THREE.MeshStandardMaterial({
83-
color: this.color(),
84-
wireframe: this.wireframe(),
102+
if (this.material) {
103+
this.material.color.set(color);
104+
this.material.wireframe = wireframe;
105+
this.material.needsUpdate = true;
106+
} else {
107+
this.material = new THREE.MeshStandardMaterial({ color, wireframe });
108+
if (this.mesh) this.mesh.material = this.material;
109+
}
85110
});
111+
}
86112

87-
// Initialize mesh
88-
this.mesh = new THREE.Mesh(this.geometry, this.material);
89-
this.mesh.position.set(...this.position());
90-
this.mesh.rotation.set(...this.rotation());
91-
this.mesh.scale.set(...this.scale());
92-
93-
// Add to parent
94-
if (this.parentFn) {
113+
// Helper to add to parent safely
114+
private addToParent(): void {
115+
if (this.parentFn && this.mesh) {
95116
const parent = this.parentFn();
96117
if (parent) {
97118
parent.add(this.mesh);
98-
} else {
99-
console.warn('BoxComponent: Parent not ready');
100119
}
101-
} else {
102-
console.warn('BoxComponent: No parent found');
103120
}
104121
}
105122

0 commit comments

Comments
 (0)