@gardev/components 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ng-package.json +7 -0
- package/package.json +12 -26
- package/src/lib/components/bottle/bottle.component.css +4 -0
- package/src/lib/components/bottle/bottle.component.html +1 -0
- package/src/lib/components/bottle/bottle.component.spec.ts +23 -0
- package/src/lib/components/bottle/bottle.component.ts +164 -0
- package/src/lib/components/caroussel/caroussel-menu/caroussel-menu.component.css +23 -0
- package/src/lib/components/caroussel/caroussel-menu/caroussel-menu.component.html +5 -0
- package/src/lib/components/caroussel/caroussel-menu/caroussel-menu.component.ts +63 -0
- package/src/lib/components/caroussel/caroussel-menu/caroussel-menu.spec.ts +23 -0
- package/src/lib/components/caroussel/caroussel.component.css +11 -0
- package/src/lib/components/caroussel/caroussel.component.html +8 -0
- package/src/lib/components/caroussel/caroussel.component.ts +24 -0
- package/src/lib/components/cronogram/cronogram.component.css +58 -0
- package/src/lib/components/cronogram/cronogram.component.html +47 -0
- package/src/lib/components/cronogram/cronogram.component.spec.ts +23 -0
- package/src/lib/components/cronogram/cronogram.component.ts +99 -0
- package/src/lib/components/stack-caroussel/stack-caroussel.css +25 -0
- package/src/lib/components/stack-caroussel/stack-caroussel.html +4 -0
- package/src/lib/components/stack-caroussel/stack-caroussel.spec.ts +23 -0
- package/src/lib/components/stack-caroussel/stack-caroussel.ts +50 -0
- package/src/lib/layouts/menu/menu.component.css +50 -0
- package/src/lib/layouts/menu/menu.component.html +21 -0
- package/src/lib/layouts/menu/menu.component.ts +98 -0
- package/src/lib/layouts/menu/menu.spec.ts +23 -0
- package/src/lib/layouts/navbar/navbar.component.css +34 -0
- package/src/lib/layouts/navbar/navbar.component.html +10 -0
- package/src/lib/layouts/navbar/navbar.component.spec.ts +23 -0
- package/src/lib/layouts/navbar/navbar.component.ts +46 -0
- package/src/lib/layouts/page-layout/page-layout.component.css +13 -0
- package/src/lib/layouts/page-layout/page-layout.component.html +32 -0
- package/src/lib/layouts/page-layout/page-layout.component.spec.ts +23 -0
- package/src/lib/layouts/page-layout/page-layout.component.ts +13 -0
- package/src/lib/layouts/scroll-reactive-section/scroll-reactive-section.component.css +0 -0
- package/src/lib/layouts/scroll-reactive-section/scroll-reactive-section.component.html +10 -0
- package/src/lib/layouts/scroll-reactive-section/scroll-reactive-section.component.spec.ts +23 -0
- package/src/lib/layouts/scroll-reactive-section/scroll-reactive-section.component.ts +40 -0
- package/src/lib/layouts/section/section.component.css +0 -0
- package/src/lib/layouts/section/section.component.html +10 -0
- package/src/lib/layouts/section/section.component.spec.ts +23 -0
- package/src/lib/layouts/section/section.component.ts +12 -0
- package/src/lib/theme.service.ts +22 -0
- package/src/public-api.ts +14 -0
- package/tsconfig.lib.json +17 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +15 -0
- package/fesm2022/ardev-components.mjs +0 -575
- package/fesm2022/ardev-components.mjs.map +0 -1
- package/types/ardev-components.d.ts +0 -139
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@gardev/components",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
},
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
},
|
|
14
|
-
"sideEffects": false,
|
|
15
|
-
"module": "fesm2022/ardev-components.mjs",
|
|
16
|
-
"typings": "types/ardev-components.d.ts",
|
|
17
|
-
"exports": {
|
|
18
|
-
"./package.json": {
|
|
19
|
-
"default": "./package.json"
|
|
20
|
-
},
|
|
21
|
-
".": {
|
|
22
|
-
"types": "./types/ardev-components.d.ts",
|
|
23
|
-
"default": "./fesm2022/ardev-components.mjs"
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@gardev/components",
|
|
3
|
+
"version": "0.0.4",
|
|
4
|
+
"peerDependencies": {
|
|
5
|
+
"@angular/common": "^21.1.0",
|
|
6
|
+
"@angular/core": "^21.1.0"
|
|
7
|
+
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"tslib": "^2.3.0"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div #container class="three-container"></div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { Bottle } from './bottle';
|
|
4
|
+
|
|
5
|
+
describe('Bottle', () => {
|
|
6
|
+
let component: Bottle;
|
|
7
|
+
let fixture: ComponentFixture<Bottle>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [Bottle]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(Bottle);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Component, ElementRef, AfterViewInit, ViewChild, Input } from '@angular/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { SVGLoader } from 'three/examples/jsm/Addons.js';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'app-bottle',
|
|
7
|
+
standalone: true,
|
|
8
|
+
templateUrl: './bottle.component.html',
|
|
9
|
+
styleUrls: ['./bottle.component.css'],
|
|
10
|
+
})
|
|
11
|
+
export class BottleComponent implements AfterViewInit {
|
|
12
|
+
|
|
13
|
+
@Input() color: `#${string}` = '#868132';
|
|
14
|
+
@Input() wineColor: `#${string}` = '#fff6f5';
|
|
15
|
+
@ViewChild('container', { static: true }) container!: ElementRef<HTMLDivElement>;
|
|
16
|
+
|
|
17
|
+
private scene!: THREE.Scene;
|
|
18
|
+
private camera!: THREE.PerspectiveCamera;
|
|
19
|
+
private renderer!: THREE.WebGLRenderer;
|
|
20
|
+
|
|
21
|
+
private glassMaterial!: THREE.MeshPhysicalMaterial;
|
|
22
|
+
private liquidMaterial!: THREE.MeshPhysicalMaterial;
|
|
23
|
+
private foilMaterial!: THREE.MeshPhysicalMaterial;
|
|
24
|
+
ngAfterViewInit() {
|
|
25
|
+
this.glassMaterial = new THREE.MeshPhysicalMaterial({
|
|
26
|
+
color: new THREE.Color(this.color),
|
|
27
|
+
metalness: 0,
|
|
28
|
+
roughness: 0,
|
|
29
|
+
transmission: 1,
|
|
30
|
+
thickness: 0,
|
|
31
|
+
ior: 1.5,
|
|
32
|
+
clearcoat: 1.0,
|
|
33
|
+
clearcoatRoughness: 0.05,
|
|
34
|
+
opacity: 1,
|
|
35
|
+
side: THREE.FrontSide,
|
|
36
|
+
transparent: true,
|
|
37
|
+
depthWrite: false,
|
|
38
|
+
envMapIntensity: 2.5
|
|
39
|
+
});
|
|
40
|
+
this.liquidMaterial = new THREE.MeshPhysicalMaterial({
|
|
41
|
+
color: new THREE.Color('#e2d744'),
|
|
42
|
+
metalness: 0.4,
|
|
43
|
+
// roughness: 0.25,
|
|
44
|
+
|
|
45
|
+
transmission: 0.2,
|
|
46
|
+
thickness: 0.4,
|
|
47
|
+
ior: 1.5,
|
|
48
|
+
clearcoat: 1.0,
|
|
49
|
+
clearcoatRoughness: 0.05,
|
|
50
|
+
transparent: true,
|
|
51
|
+
opacity: 0.8,
|
|
52
|
+
|
|
53
|
+
depthWrite: false, // 🔑 el líquid SÍ escriu profunditat
|
|
54
|
+
side: THREE.BackSide,
|
|
55
|
+
// envMapIntensity: 1.2,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
this.foilMaterial = new THREE.MeshPhysicalMaterial({
|
|
60
|
+
color: 0x156289,
|
|
61
|
+
metalness: 0.25,
|
|
62
|
+
roughness: 0.3,
|
|
63
|
+
side: THREE.DoubleSide
|
|
64
|
+
});
|
|
65
|
+
this.createSceneAndLighting();
|
|
66
|
+
this.loadModelFromSVGFile();
|
|
67
|
+
this.animate();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private animate = () => {
|
|
71
|
+
requestAnimationFrame(this.animate);
|
|
72
|
+
|
|
73
|
+
if (this.camera) {
|
|
74
|
+
const t = Date.now() * 0.0002;
|
|
75
|
+
this.camera.position.x = Math.sin(t) * 50;
|
|
76
|
+
this.camera.position.z = Math.cos(t) * 50;
|
|
77
|
+
this.camera.lookAt(0, 0, 0);
|
|
78
|
+
}
|
|
79
|
+
this.renderer.shadowMap.enabled = true;
|
|
80
|
+
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
81
|
+
|
|
82
|
+
this.renderer.render(this.scene, this.camera);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
private loadModelFromSVGFile() {
|
|
86
|
+
const loader = new SVGLoader();
|
|
87
|
+
|
|
88
|
+
loader.load('assets/bitmap.svg', (data) => {
|
|
89
|
+
const bottleGroup = new THREE.Group();
|
|
90
|
+
|
|
91
|
+
data.paths.forEach((path, index) => {
|
|
92
|
+
const shapes = SVGLoader.createShapes(path);
|
|
93
|
+
|
|
94
|
+
shapes.forEach((shape) => {
|
|
95
|
+
const points = shape.getPoints().map(p => new THREE.Vector2(p.x * 0.1, -p.y * 0.1));
|
|
96
|
+
|
|
97
|
+
const geometry = new THREE.LatheGeometry(points, 128); // més segments → més suau
|
|
98
|
+
geometry.computeVertexNormals(); // normals correctes
|
|
99
|
+
|
|
100
|
+
const material = (index === 0) ? this.glassMaterial :
|
|
101
|
+
(index === 1) ? this.liquidMaterial : this.foilMaterial
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
105
|
+
|
|
106
|
+
const layerGroup = new THREE.Group();
|
|
107
|
+
layerGroup.add(mesh);
|
|
108
|
+
bottleGroup.add(layerGroup);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const box = new THREE.Box3().setFromObject(bottleGroup);
|
|
113
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
114
|
+
bottleGroup.position.sub(center);
|
|
115
|
+
|
|
116
|
+
this.scene.add(bottleGroup);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private createSceneAndLighting() {
|
|
121
|
+
this.scene = new THREE.Scene();
|
|
122
|
+
|
|
123
|
+
const width = this.container.nativeElement.clientWidth;
|
|
124
|
+
const height = this.container.nativeElement.clientHeight;
|
|
125
|
+
|
|
126
|
+
this.camera = new THREE.PerspectiveCamera(35, width / height, 0.1, 5000);
|
|
127
|
+
this.camera.position.set(30, 20, 50);
|
|
128
|
+
this.camera.lookAt(0, 0, 0);
|
|
129
|
+
|
|
130
|
+
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
131
|
+
this.renderer.setSize(width, height);
|
|
132
|
+
this.renderer.toneMapping = THREE.ACESFilmicToneMapping; // millora realisme colors
|
|
133
|
+
this.container.nativeElement.appendChild(this.renderer.domElement);
|
|
134
|
+
|
|
135
|
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
|
|
136
|
+
this.scene.add(ambientLight);
|
|
137
|
+
|
|
138
|
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.0);
|
|
139
|
+
directionalLight.position.set(50, 80, 50);
|
|
140
|
+
directionalLight.castShadow = true;
|
|
141
|
+
directionalLight.shadow.mapSize.width = 2048;
|
|
142
|
+
directionalLight.shadow.mapSize.height = 2048;
|
|
143
|
+
directionalLight.shadow.radius = 4;
|
|
144
|
+
this.scene.add(directionalLight);
|
|
145
|
+
|
|
146
|
+
const backLight = new THREE.PointLight(0xffffff, 0.5);
|
|
147
|
+
backLight.position.set(-30, 40, -30);
|
|
148
|
+
this.scene.add(backLight);
|
|
149
|
+
|
|
150
|
+
const sideLight = new THREE.PointLight(0xffffff, 0.3);
|
|
151
|
+
sideLight.position.set(30, 10, -40);
|
|
152
|
+
this.scene.add(sideLight);
|
|
153
|
+
|
|
154
|
+
const textureLoader = new THREE.TextureLoader();
|
|
155
|
+
textureLoader.load('assets/environment-map.png', (texture) => {
|
|
156
|
+
texture.mapping = THREE.EquirectangularReflectionMapping;
|
|
157
|
+
this.scene.environment = texture;
|
|
158
|
+
//this.scene.background = texture;
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
button {
|
|
2
|
+
width: calc(1rem - 4px);
|
|
3
|
+
height: calc(1rem - 4px);
|
|
4
|
+
background: black;
|
|
5
|
+
border: 0;
|
|
6
|
+
border-radius: 1rem;
|
|
7
|
+
z-index: 1;
|
|
8
|
+
margin: 2px;
|
|
9
|
+
cursor: pointer
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#selectedIndex {
|
|
13
|
+
width: 1rem;
|
|
14
|
+
height: 1rem;
|
|
15
|
+
background: rgb(255, 255, 255);
|
|
16
|
+
border-radius: 1rem;
|
|
17
|
+
position: absolute;
|
|
18
|
+
z-index: 0;
|
|
19
|
+
transition:
|
|
20
|
+
transform 300ms cubic-bezier(0.35, 0, 0.25, 1),
|
|
21
|
+
width 300ms cubic-bezier(0.35, 0, 0.25, 1);
|
|
22
|
+
will-change: transform, width;
|
|
23
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Component,
|
|
3
|
+
ElementRef,
|
|
4
|
+
QueryList,
|
|
5
|
+
ViewChild,
|
|
6
|
+
ViewChildren,
|
|
7
|
+
Output,
|
|
8
|
+
EventEmitter,
|
|
9
|
+
Input,
|
|
10
|
+
AfterViewInit
|
|
11
|
+
} from '@angular/core';
|
|
12
|
+
import { NgForOf } from '@angular/common';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'ard-caroussel-menu',
|
|
16
|
+
standalone: true,
|
|
17
|
+
imports: [NgForOf],
|
|
18
|
+
templateUrl: './caroussel-menu.component.html',
|
|
19
|
+
styleUrls: ['./caroussel-menu.component.css'],
|
|
20
|
+
})
|
|
21
|
+
export class CarousselMenuComponent implements AfterViewInit {
|
|
22
|
+
@Input() length = 0;
|
|
23
|
+
|
|
24
|
+
@ViewChildren('btn', { read: ElementRef })
|
|
25
|
+
buttons!: QueryList<ElementRef<HTMLButtonElement>>;
|
|
26
|
+
|
|
27
|
+
@ViewChild('selectedIndex')
|
|
28
|
+
selectedIndex!: ElementRef<HTMLElement>;
|
|
29
|
+
|
|
30
|
+
@Output() indexChange = new EventEmitter<number>();
|
|
31
|
+
|
|
32
|
+
private positions: number[] = [];
|
|
33
|
+
private resetWidthId?: number;
|
|
34
|
+
|
|
35
|
+
ngAfterViewInit() {
|
|
36
|
+
const btns = this.buttons.toArray();
|
|
37
|
+
const firstRight = btns[0]?.nativeElement.getBoundingClientRect().right ?? 0;
|
|
38
|
+
|
|
39
|
+
this.positions = btns.map(
|
|
40
|
+
b => b.nativeElement.getBoundingClientRect().right - firstRight
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
selectIndex(i: number) {
|
|
45
|
+
this.indexChange.emit(i);
|
|
46
|
+
|
|
47
|
+
const el = this.selectedIndex.nativeElement;
|
|
48
|
+
const x = this.positions[i] ?? 0;
|
|
49
|
+
|
|
50
|
+
el.style.transform = `translateX(${x}px)`;
|
|
51
|
+
|
|
52
|
+
const baseWidth = el.getBoundingClientRect().width;
|
|
53
|
+
el.style.width = `${baseWidth * 1.5}px`;
|
|
54
|
+
|
|
55
|
+
if (this.resetWidthId) {
|
|
56
|
+
clearTimeout(this.resetWidthId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.resetWidthId = window.setTimeout(() => {
|
|
60
|
+
el.style.width = `${baseWidth}px`;
|
|
61
|
+
}, 150);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CarousselMenuComponent } from './caroussel-menu.component';
|
|
4
|
+
|
|
5
|
+
describe('CarousselMenuComponent', () => {
|
|
6
|
+
let component: CarousselMenuComponent;
|
|
7
|
+
let fixture: ComponentFixture<CarousselMenuComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [CarousselMenuComponent]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(CarousselMenuComponent);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div style="position: relative; top: 0; width: 100%; height: 100%; overflow: hidden;">
|
|
2
|
+
<img *ngFor="let img of images; let i = index" [src]="img" [style.opacity]="i === index ? 1 : 0" class="image"
|
|
3
|
+
alt="">
|
|
4
|
+
|
|
5
|
+
<div style="position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%); display: flex; gap: 8px;">
|
|
6
|
+
<ard-caroussel-menu (indexChange)="goTo($event)" [length]="images.length"></ard-caroussel-menu>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Component, Input } from '@angular/core';
|
|
2
|
+
import { NgForOf } from "@angular/common";
|
|
3
|
+
import { CarousselMenuComponent } from './caroussel-menu/caroussel-menu.component';
|
|
4
|
+
|
|
5
|
+
@Component({
|
|
6
|
+
selector: 'ard-caroussel',
|
|
7
|
+
imports: [NgForOf, CarousselMenuComponent],
|
|
8
|
+
templateUrl: './caroussel.component.html',
|
|
9
|
+
styleUrl: './caroussel.component.css',
|
|
10
|
+
})
|
|
11
|
+
export class CarousselComponent {
|
|
12
|
+
@Input() images: string[] = [];
|
|
13
|
+
|
|
14
|
+
index = 0;
|
|
15
|
+
|
|
16
|
+
next() {
|
|
17
|
+
this.index = (this.index + 1) % this.images.length;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
goTo(i: number) {
|
|
21
|
+
this.index = i;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
.left-container.odd {
|
|
2
|
+
padding: 0 1rem
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.right-container.even {
|
|
6
|
+
padding: 0 1rem;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.left-container,
|
|
10
|
+
.right-container {
|
|
11
|
+
padding: 0 1rem;
|
|
12
|
+
max-width: 35ch;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.general-container {
|
|
16
|
+
transition: opacity 0.6s;
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.line {
|
|
21
|
+
background: var(--color-text);
|
|
22
|
+
width: 100%;
|
|
23
|
+
position: absolute;
|
|
24
|
+
border-radius: 1rem 1rem 0 0;
|
|
25
|
+
animation-timing-function: ease-out;
|
|
26
|
+
width: 0.4rem
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.bullet {
|
|
30
|
+
width: 0.4rem;
|
|
31
|
+
min-height: 0.4rem;
|
|
32
|
+
background: var(--color-text);
|
|
33
|
+
outline: 3px solid var(--color-text);
|
|
34
|
+
border-radius: 2rem;
|
|
35
|
+
position: absolute;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.hidden {
|
|
39
|
+
opacity: 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.visible {
|
|
43
|
+
opacity: 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.center {
|
|
47
|
+
position: relative;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
.left-container {
|
|
53
|
+
display: flex;
|
|
54
|
+
justify-content: end;
|
|
55
|
+
text-align: end;
|
|
56
|
+
justify-self: end;
|
|
57
|
+
flex-flow: column
|
|
58
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<h4>cronogram.component.ts</h4>
|
|
2
|
+
|
|
3
|
+
<p>
|
|
4
|
+
en la vista mobile, estarà tot en un mateix costat?
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<div #wrapper>
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@for(day of items; track day.date; let i = $index;) {
|
|
12
|
+
|
|
13
|
+
<div style="display: grid; grid-template-columns: 1fr 0fr 1fr;" class="general-container" #CronogramContent>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
<div class="left-container" [ngClass]="i % 2 == 0 ? 'odd' : 'even'">
|
|
17
|
+
@if(i % 2 === 0) {
|
|
18
|
+
<h3>{{day.date}}</h3>
|
|
19
|
+
<p>lorem ipsum dolor sit amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit
|
|
20
|
+
amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit amet,</p>
|
|
21
|
+
}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
<div class="center" style="height: 100%; display: flex; width: 1rem; flex-flow: column; position: relative">
|
|
26
|
+
<div class="bullet" style="z-index: 2">
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
@if (i == 0) {
|
|
30
|
+
<div class="line" #line>
|
|
31
|
+
|
|
32
|
+
</div>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
<div class="right-container cronogram-content" [ngClass]="i % 2 == 0 ? 'odd' : 'even'">
|
|
37
|
+
@if(i % 2 !== 0) {
|
|
38
|
+
<h3>{{day.date}}</h3>
|
|
39
|
+
<p>lorem ipsum dolor sit amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit
|
|
40
|
+
amet, lorem ipsum dolor sit amet, lorem ipsum dolor sit amet,</p>
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
}
|
|
47
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { CronogramComponent } from './cronogram.component';
|
|
4
|
+
|
|
5
|
+
describe('CronogramComponent', () => {
|
|
6
|
+
let component: CronogramComponent;
|
|
7
|
+
let fixture: ComponentFixture<CronogramComponent>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [CronogramComponent]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(CronogramComponent);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
await fixture.whenStable();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Component, ElementRef, HostListener, QueryList, ViewChild, ViewChildren } from '@angular/core';
|
|
2
|
+
import { NgClass } from "@angular/common";
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'ard-cronogram',
|
|
6
|
+
imports: [NgClass],
|
|
7
|
+
templateUrl: './cronogram.component.html',
|
|
8
|
+
styleUrl: './cronogram.component.css',
|
|
9
|
+
})
|
|
10
|
+
export class CronogramComponent {
|
|
11
|
+
@ViewChild('wrapper', { read: ElementRef }) wrapper!: ElementRef<HTMLElement>;
|
|
12
|
+
@ViewChild('line', { read: ElementRef }) line!: ElementRef<HTMLElement>;
|
|
13
|
+
|
|
14
|
+
@ViewChildren('CronogramContent', { read: ElementRef })
|
|
15
|
+
children!: QueryList<ElementRef>;
|
|
16
|
+
items = [
|
|
17
|
+
{ date: 2000, content: 'Lorem ipsum dolor sit amet', image: 'lorem' },
|
|
18
|
+
{ date: 2001, content: 'Lorem ipsum dolor sit amet', image: 'lorem' },
|
|
19
|
+
{ date: 2002, content: 'Lorem ipsum dolor sit amet', image: 'lorem' }
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
positionsY: number[] = []
|
|
23
|
+
|
|
24
|
+
ngAfterViewInit() {
|
|
25
|
+
this.positionsY = this.children.map(i => i.nativeElement.getBoundingClientRect().y)
|
|
26
|
+
|
|
27
|
+
this.line.nativeElement.style.maxHeight = `${this.wrapper.nativeElement.getBoundingClientRect().height}px`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lastScrollY = window.scrollY;
|
|
31
|
+
|
|
32
|
+
@HostListener('window:scroll', [])
|
|
33
|
+
onScroll() {
|
|
34
|
+
const scrollPosition = window.scrollY;
|
|
35
|
+
|
|
36
|
+
if (scrollPosition > this.lastScrollY) {
|
|
37
|
+
this.onScrollDown(scrollPosition);
|
|
38
|
+
} else if (scrollPosition < this.lastScrollY) {
|
|
39
|
+
this.onScrollUp(scrollPosition);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.lastScrollY = scrollPosition;
|
|
43
|
+
|
|
44
|
+
this.line.nativeElement.style.height = `${this.visibleHeight(this.wrapper.nativeElement)}px`
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
onScrollDown(scrollPosition: number) {
|
|
49
|
+
let index = this.positionsY.findIndex(pos => pos > scrollPosition - 100);
|
|
50
|
+
|
|
51
|
+
if (index === -1) {
|
|
52
|
+
index = this.positionsY.length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.children.forEach((child, i) => {
|
|
56
|
+
const el = child.nativeElement as HTMLElement;
|
|
57
|
+
|
|
58
|
+
if (i < index) {
|
|
59
|
+
el.classList.remove("hidden");
|
|
60
|
+
el.classList.add("visible");
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.line.nativeElement.style.transition = 'height 1.2s';
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
onScrollUp(scrollPosition: number) {
|
|
68
|
+
let index = this.positionsY.findIndex(pos => pos > scrollPosition - 50);
|
|
69
|
+
|
|
70
|
+
if (index === -1) {
|
|
71
|
+
index = this.positionsY.length;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.children.forEach((child, i) => {
|
|
75
|
+
const el = child.nativeElement as HTMLElement;
|
|
76
|
+
|
|
77
|
+
if (i >= index) {
|
|
78
|
+
el.classList.add("hidden");
|
|
79
|
+
el.classList.remove("visible");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.line.nativeElement.style.transition = 'height 0s';
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
visibleHeight(el: HTMLElement) {
|
|
90
|
+
const rect = el.getBoundingClientRect();
|
|
91
|
+
const viewportHeight = window.innerHeight;
|
|
92
|
+
|
|
93
|
+
const topVisible = Math.max(rect.top, 0);
|
|
94
|
+
const bottomVisible = Math.min(rect.bottom, viewportHeight);
|
|
95
|
+
|
|
96
|
+
return Math.max(0, bottomVisible - topVisible);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
--anim-speed: 300ms;
|
|
3
|
+
/* VARIABLE DE VELOCITAT */
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.stack-container {
|
|
7
|
+
position: relative;
|
|
8
|
+
width: 400px;
|
|
9
|
+
height: 400px;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.stack-image {
|
|
14
|
+
position: absolute;
|
|
15
|
+
width: 200px;
|
|
16
|
+
height: 200px;
|
|
17
|
+
object-fit: cover;
|
|
18
|
+
transition:
|
|
19
|
+
transform var(--anim-speed) ease,
|
|
20
|
+
opacity var(--anim-speed) ease;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.stack-image.move-out {
|
|
24
|
+
transform: translate(200px, 0px) !important;
|
|
25
|
+
}
|