@homefile/components-v2 2.57.0 → 2.59.0
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/dist/assets/locales/pt/index.json +3 -1
- package/dist/components/animations/Birds.d.ts +11 -0
- package/dist/components/animations/Birds.js +196 -0
- package/dist/components/animations/CloudsAnimation.d.ts +0 -1
- package/dist/components/animations/CloudsAnimation.js +54 -19
- package/dist/components/animations/Stars.d.ts +6 -0
- package/dist/components/animations/Stars.js +88 -0
- package/dist/components/animations/index.d.ts +2 -0
- package/dist/components/animations/index.js +2 -0
- package/dist/components/animations/timeOfDayContext.d.ts +9 -0
- package/dist/components/animations/timeOfDayContext.js +9 -0
- package/dist/components/onboarding/Footer.js +6 -1
- package/package.json +1 -1
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"terms": "Criando uma conta Homefile você concorda com os nossos ",
|
|
16
16
|
"signupBt": "Criar conta",
|
|
17
17
|
"signin": "Já possui uma conta Homefile?",
|
|
18
|
-
"signinBt": "Log in"
|
|
18
|
+
"signinBt": "Log in",
|
|
19
|
+
"continueGoogle": "Continuar com o Google",
|
|
20
|
+
"or": "ou"
|
|
19
21
|
},
|
|
20
22
|
"partner": {
|
|
21
23
|
"wizardSteps": {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Birds — self-contained animated flock overlay for the onboarding sky.
|
|
3
|
+
*
|
|
4
|
+
* Renders SVG birds onto a fixed, non-interactive layer that sits over the
|
|
5
|
+
* CloudsAnimation sky (CloudsAnimation supplies the gradient, clouds and trees).
|
|
6
|
+
* Two archetypes: small fast "flappers" (short wings, quick eased beat) ride low;
|
|
7
|
+
* wide-winged "soarers" ride high and slow. Flight paths curve, wingbeats are
|
|
8
|
+
* eased (fast down / slow up with a glide hold), and flight undulates — flap to
|
|
9
|
+
* climb, glide to sink. Honors prefers-reduced-motion.
|
|
10
|
+
*/
|
|
11
|
+
export declare const Birds: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Birds — self-contained animated flock overlay for the onboarding sky.
|
|
5
|
+
*
|
|
6
|
+
* Renders SVG birds onto a fixed, non-interactive layer that sits over the
|
|
7
|
+
* CloudsAnimation sky (CloudsAnimation supplies the gradient, clouds and trees).
|
|
8
|
+
* Two archetypes: small fast "flappers" (short wings, quick eased beat) ride low;
|
|
9
|
+
* wide-winged "soarers" ride high and slow. Flight paths curve, wingbeats are
|
|
10
|
+
* eased (fast down / slow up with a glide hold), and flight undulates — flap to
|
|
11
|
+
* climb, glide to sink. Honors prefers-reduced-motion.
|
|
12
|
+
*/
|
|
13
|
+
export const Birds = () => {
|
|
14
|
+
const layerRef = useRef(null);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const layer = layerRef.current;
|
|
17
|
+
if (!layer)
|
|
18
|
+
return;
|
|
19
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
20
|
+
const rand = (a, b) => a + Math.random() * (b - a);
|
|
21
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
22
|
+
const f = (n) => +(+n).toFixed(2);
|
|
23
|
+
// Eased wing-flap splines: fast down-stroke, slow up-stroke, then a held glide.
|
|
24
|
+
const S_DOWN = '0.2 0.75 0.35 1';
|
|
25
|
+
const S_UP = '0.4 0 0.28 1';
|
|
26
|
+
const S_GIN = '0.35 0 0.6 1';
|
|
27
|
+
const S_HOLD = '0.5 0 0.5 1';
|
|
28
|
+
const S_GOUT = '0.45 0 0.5 1';
|
|
29
|
+
const buildFlap = (nBeats, glideFrac, vUp, vDown, vGlide) => {
|
|
30
|
+
const flap = 1 - glideFrac;
|
|
31
|
+
const seg = flap / (nBeats * 2);
|
|
32
|
+
const values = [vUp];
|
|
33
|
+
const times = [0];
|
|
34
|
+
const splines = [];
|
|
35
|
+
let t = 0;
|
|
36
|
+
for (let b = 0; b < nBeats; b++) {
|
|
37
|
+
t += seg;
|
|
38
|
+
values.push(vDown);
|
|
39
|
+
times.push(t);
|
|
40
|
+
splines.push(S_DOWN);
|
|
41
|
+
t += seg;
|
|
42
|
+
values.push(vUp);
|
|
43
|
+
times.push(t);
|
|
44
|
+
splines.push(S_UP);
|
|
45
|
+
}
|
|
46
|
+
const gin = flap + (1 - flap) * 0.25;
|
|
47
|
+
const hold = gin + (1 - gin) * 0.5;
|
|
48
|
+
values.push(vGlide);
|
|
49
|
+
times.push(gin);
|
|
50
|
+
splines.push(S_GIN);
|
|
51
|
+
values.push(vGlide);
|
|
52
|
+
times.push(hold);
|
|
53
|
+
splines.push(S_HOLD);
|
|
54
|
+
values.push(vUp);
|
|
55
|
+
times.push(1);
|
|
56
|
+
splines.push(S_GOUT);
|
|
57
|
+
return {
|
|
58
|
+
values: values.join('; '),
|
|
59
|
+
keyTimes: times.map(f).join('; '),
|
|
60
|
+
keySplines: splines.join('; '),
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const makeBird = (W, H) => {
|
|
64
|
+
const depth = Math.random(); // haze/opacity/blur depth (independent of size)
|
|
65
|
+
const soarer = Math.random() < 0.42;
|
|
66
|
+
const size = soarer ? rand(22, 46) : rand(9, 22);
|
|
67
|
+
const driftSpeed = (soarer ? rand(30, 52) : rand(13, 30)) + rand(-3, 3);
|
|
68
|
+
const nBeats = soarer ? (Math.random() < 0.5 ? 1 : 2) : Math.round(rand(3, 6));
|
|
69
|
+
const glideFrac = soarer ? rand(0.55, 0.8) : rand(0.04, 0.2);
|
|
70
|
+
const P = soarer ? rand(3.4, 5.4) : rand(0.5, 0.95); // flappers beat much quicker
|
|
71
|
+
// flapper bounce dampened (was 7–17) so a couple don't over-bob on small bodies
|
|
72
|
+
const boundAmp = (soarer ? rand(3, 8) : rand(6, 11)) * (0.6 + depth * 0.7);
|
|
73
|
+
const wingAmp = soarer ? rand(0.55, 0.9) : rand(0.9, 1.4);
|
|
74
|
+
const upTip = f(15 - 9 * wingAmp);
|
|
75
|
+
const upBody = f(15 + 2 * wingAmp);
|
|
76
|
+
const dnTip = f(15 + 5 * wingAmp);
|
|
77
|
+
const dnBody = f(15 - 1.5 * wingAmp);
|
|
78
|
+
const glTip = f(15 - 3 * wingAmp);
|
|
79
|
+
// small fast birds get shorter (stubbier) wingspans
|
|
80
|
+
const wx0 = soarer ? 5 : 10;
|
|
81
|
+
const wx1 = soarer ? 35 : 30;
|
|
82
|
+
const vUp = `M ${wx0},${upTip} L 20,${upBody} L ${wx1},${upTip}`;
|
|
83
|
+
const vDown = `M ${wx0},${dnTip} L 20,${dnBody} L ${wx1},${dnTip}`;
|
|
84
|
+
const vGlide = `M ${wx0},${glTip} L 20,16 L ${wx1},${glTip}`;
|
|
85
|
+
const flapAnim = buildFlap(nBeats, glideFrac, vUp, vDown, vGlide);
|
|
86
|
+
const phase = -rand(0, P);
|
|
87
|
+
const ltr = Math.random() < 0.6;
|
|
88
|
+
const x0 = ltr ? -80 : W + 80;
|
|
89
|
+
const x1 = ltr ? W + 80 : -80;
|
|
90
|
+
let pathStr;
|
|
91
|
+
if (soarer) {
|
|
92
|
+
// soarers ride thermals: glide in, circle once (a banking turn), drift out
|
|
93
|
+
const cx = rand(0.25, 0.75) * W;
|
|
94
|
+
const cy = rand(0.06, 0.3) * H; // high band
|
|
95
|
+
const r = rand(45, 95);
|
|
96
|
+
const k = 0.5523 * r; // cubic-bezier circle constant
|
|
97
|
+
const eY = clamp(cy + rand(-0.12, 0.12) * H, 0.03 * H, 0.4 * H);
|
|
98
|
+
const xY = clamp(cy + rand(-0.12, 0.12) * H, 0.03 * H, 0.4 * H);
|
|
99
|
+
const rx = cx + r; // circle entry point (right of center)
|
|
100
|
+
const loop = `C ${f(x0 + (rx - x0) * 0.5)},${f(eY)} ${f(rx)},${f(cy - r)} ${f(rx)},${f(cy)} ` + // glide in to circle
|
|
101
|
+
`C ${f(rx)},${f(cy - k)} ${f(cx + k)},${f(cy - r)} ${f(cx)},${f(cy - r)} ` + // right → top
|
|
102
|
+
`C ${f(cx - k)},${f(cy - r)} ${f(cx - r)},${f(cy - k)} ${f(cx - r)},${f(cy)} ` + // top → left
|
|
103
|
+
`C ${f(cx - r)},${f(cy + k)} ${f(cx - k)},${f(cy + r)} ${f(cx)},${f(cy + r)} ` + // left → bottom
|
|
104
|
+
`C ${f(cx + k)},${f(cy + r)} ${f(rx)},${f(cy + k)} ${f(rx)},${f(cy)} ` + // bottom → right (loop closed)
|
|
105
|
+
`C ${f(rx)},${f(cy - r)} ${f(x1 + (rx - x1) * 0.5)},${f(xY)} ${f(x1)},${f(xY)}`; // drift out
|
|
106
|
+
pathStr = `path("M ${f(x0)},${f(eY)} ${loop}")`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const y0 = rand(0.42, 0.8) * H; // flappers low
|
|
110
|
+
const y1 = clamp(y0 + rand(-0.2, 0.2) * H, 0.03 * H, 0.86 * H);
|
|
111
|
+
const cx1 = x0 + (x1 - x0) * rand(0.2, 0.42);
|
|
112
|
+
const cy1 = clamp(y0 + rand(-0.32, 0.32) * H, 0, H);
|
|
113
|
+
const cx2 = x0 + (x1 - x0) * rand(0.6, 0.82);
|
|
114
|
+
const cy2 = clamp(y1 + rand(-0.32, 0.32) * H, 0, H);
|
|
115
|
+
pathStr = `path("M ${f(x0)},${f(y0)} C ${f(cx1)},${f(cy1)} ${f(cx2)},${f(cy2)} ${f(x1)},${f(y1)}")`;
|
|
116
|
+
}
|
|
117
|
+
const bird = document.createElement('div');
|
|
118
|
+
bird.className = 'hf-bird';
|
|
119
|
+
bird.style.width = `${size}px`;
|
|
120
|
+
bird.style.height = `${f(size * 0.77)}px`;
|
|
121
|
+
bird.style.offsetPath = pathStr;
|
|
122
|
+
bird.style.animationDuration = `${driftSpeed}s`;
|
|
123
|
+
bird.style.animationDelay = `${-rand(0, driftSpeed)}s`;
|
|
124
|
+
// darker/more-present background birds: higher opacity floor + lighter haze so the far ones don't disappear
|
|
125
|
+
bird.style.opacity = String(f(0.34 + depth * 0.54));
|
|
126
|
+
bird.style.zIndex = String(Math.round(depth * 10));
|
|
127
|
+
if (depth < 0.32)
|
|
128
|
+
bird.style.filter = `blur(${((0.32 - depth) * 2).toFixed(1)}px)`;
|
|
129
|
+
const bob = document.createElement('div');
|
|
130
|
+
bob.className = 'hf-bob';
|
|
131
|
+
bob.style.setProperty('--p', `${P}s`);
|
|
132
|
+
bob.style.setProperty('--amp', `${f(boundAmp)}px`);
|
|
133
|
+
bob.style.animationDelay = `${phase}s`;
|
|
134
|
+
const svg = document.createElementNS(NS, 'svg');
|
|
135
|
+
svg.setAttribute('viewBox', '0 0 40 30');
|
|
136
|
+
svg.setAttribute('fill', 'none');
|
|
137
|
+
svg.setAttribute('stroke', '#4e2d68');
|
|
138
|
+
svg.setAttribute('stroke-width', soarer ? '2.4' : '3');
|
|
139
|
+
svg.setAttribute('stroke-linecap', 'round');
|
|
140
|
+
svg.setAttribute('stroke-linejoin', 'round');
|
|
141
|
+
const path = document.createElementNS(NS, 'path');
|
|
142
|
+
path.setAttribute('d', vUp);
|
|
143
|
+
const anim = document.createElementNS(NS, 'animate');
|
|
144
|
+
anim.setAttribute('attributeName', 'd');
|
|
145
|
+
anim.setAttribute('dur', `${P}s`);
|
|
146
|
+
anim.setAttribute('repeatCount', 'indefinite');
|
|
147
|
+
anim.setAttribute('calcMode', 'spline');
|
|
148
|
+
anim.setAttribute('values', flapAnim.values);
|
|
149
|
+
anim.setAttribute('keyTimes', flapAnim.keyTimes);
|
|
150
|
+
anim.setAttribute('keySplines', flapAnim.keySplines);
|
|
151
|
+
anim.setAttribute('begin', `${phase}s`);
|
|
152
|
+
path.appendChild(anim);
|
|
153
|
+
svg.appendChild(path);
|
|
154
|
+
bob.appendChild(svg);
|
|
155
|
+
bird.appendChild(bob);
|
|
156
|
+
return bird;
|
|
157
|
+
};
|
|
158
|
+
const makeFlock = () => {
|
|
159
|
+
layer.innerHTML = '';
|
|
160
|
+
const W = window.innerWidth;
|
|
161
|
+
const H = window.innerHeight;
|
|
162
|
+
// a few at a time, not a constant flock — sparse, so they come and go
|
|
163
|
+
const count = Math.round(clamp(W / 340, 3, 6));
|
|
164
|
+
for (let i = 0; i < count; i++)
|
|
165
|
+
layer.appendChild(makeBird(W, H));
|
|
166
|
+
};
|
|
167
|
+
makeFlock();
|
|
168
|
+
let rt;
|
|
169
|
+
const onResize = () => {
|
|
170
|
+
clearTimeout(rt);
|
|
171
|
+
rt = setTimeout(makeFlock, 300);
|
|
172
|
+
};
|
|
173
|
+
window.addEventListener('resize', onResize);
|
|
174
|
+
return () => {
|
|
175
|
+
window.removeEventListener('resize', onResize);
|
|
176
|
+
clearTimeout(rt);
|
|
177
|
+
layer.innerHTML = '';
|
|
178
|
+
};
|
|
179
|
+
}, []);
|
|
180
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: BIRDS_CSS }), _jsx("div", { className: "hf-birds", ref: layerRef, "aria-hidden": "true" })] }));
|
|
181
|
+
};
|
|
182
|
+
const BIRDS_CSS = `
|
|
183
|
+
.hf-birds{position:fixed; inset:0; z-index:1; pointer-events:none; overflow:hidden;}
|
|
184
|
+
.hf-bird{position:absolute; top:0; left:0; will-change:offset-distance; offset-rotate:0deg;
|
|
185
|
+
animation-name:hf-glide-path; animation-timing-function:linear; animation-iteration-count:infinite;}
|
|
186
|
+
.hf-bird svg{display:block; width:100%; height:100%;}
|
|
187
|
+
@keyframes hf-glide-path{ from{offset-distance:0%;} to{offset-distance:100%;} }
|
|
188
|
+
.hf-bob{width:100%; height:100%; will-change:transform; animation-name:hf-bound;
|
|
189
|
+
animation-duration:var(--p,3s); animation-iteration-count:infinite;}
|
|
190
|
+
@keyframes hf-bound{
|
|
191
|
+
0%{transform:translateY(0); animation-timing-function:cubic-bezier(.25,.7,.35,1);}
|
|
192
|
+
40%{transform:translateY(calc(-1 * var(--amp,10px))); animation-timing-function:cubic-bezier(.55,0,.75,1);}
|
|
193
|
+
100%{transform:translateY(0);}
|
|
194
|
+
}
|
|
195
|
+
@media (prefers-reduced-motion:reduce){ .hf-bird,.hf-bob{animation:none !important;} }
|
|
196
|
+
`;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
-
export declare const moveLeftToRightAirplane: import("@emotion/serialize").Keyframes;
|
|
3
2
|
export declare const moveRightToLeft: import("@emotion/serialize").Keyframes;
|
|
4
3
|
export declare const moveRightToLeftWithFade: import("@emotion/serialize").Keyframes;
|
|
5
4
|
export declare const moveRightToLeftWithFade2: import("@emotion/serialize").Keyframes;
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Cloud1, Cloud2, Cloud3 } from '../../assets/images';
|
|
4
4
|
import { useWindowDimensions } from '../../hooks';
|
|
5
5
|
import { colors } from '../../theme/colors';
|
|
6
|
-
import { getTimeOfDay,
|
|
6
|
+
import { getTimeOfDay, } from '../../utils';
|
|
7
7
|
import { Box, Image } from '@chakra-ui/react';
|
|
8
8
|
import { keyframes } from '@emotion/react';
|
|
9
9
|
import { WeatherTrees } from './WeatherTrees';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
100% {
|
|
15
|
-
transform: translateX(500%);
|
|
16
|
-
}
|
|
17
|
-
`;
|
|
10
|
+
import { Birds } from './Birds';
|
|
11
|
+
import { Stars } from './Stars';
|
|
12
|
+
import { TimeOfDayContext } from './timeOfDayContext';
|
|
18
13
|
export const moveRightToLeft = keyframes `
|
|
19
14
|
0% {
|
|
20
15
|
transform: translateX(250%);
|
|
@@ -69,22 +64,62 @@ export const moveRightToLeftWithFade3 = keyframes `
|
|
|
69
64
|
`;
|
|
70
65
|
export const CloudsAnimation = ({ children }) => {
|
|
71
66
|
const [timeOfDay, setTimeOfDay] = useState(getTimeOfDay());
|
|
72
|
-
|
|
67
|
+
// Preview toggle: flip to true to run a fast day→night loop locally. MUST be false in shipped code.
|
|
68
|
+
const DEMO = false;
|
|
69
|
+
const CYCLE = 40000; // demo loop length (ms), only used when DEMO is true
|
|
73
70
|
const gradients = {
|
|
74
71
|
day: colors.dayGradient,
|
|
75
72
|
evening: colors.eveningGradient,
|
|
76
73
|
morning: colors.morningGradient,
|
|
77
|
-
night
|
|
74
|
+
// dark night sky for the login background only; the shared nightGradient token is left as-is for other surfaces
|
|
75
|
+
night: 'linear(to-b, #080c22, #1c2347)',
|
|
78
76
|
};
|
|
77
|
+
// clouds are pushed back (faint) by day; at night they are not rendered at all (clear night sky)
|
|
78
|
+
// clouds are faint by day, and very transparent at dusk (evening sky is richer); hidden at night
|
|
79
|
+
const cloudOpacity = timeOfDay === 'night' ? 0 : timeOfDay === 'evening' ? 0.08 : 0.4;
|
|
80
|
+
const showClouds = timeOfDay !== 'night';
|
|
81
|
+
// each corner stacks the time-of-day tree variants and crossfades between them:
|
|
82
|
+
// morning = yellow, day = green, evening = pink (bespoke art); night reuses the day
|
|
83
|
+
// shape tinted dark purple so it matches day. Opacity crossfade = smooth color change.
|
|
84
|
+
const treeLayers = [
|
|
85
|
+
{ key: 'morning', season: 'morning-summer' },
|
|
86
|
+
{ key: 'day', season: 'day-summer' },
|
|
87
|
+
{ key: 'evening', season: 'evening-summer' },
|
|
88
|
+
{
|
|
89
|
+
key: 'night',
|
|
90
|
+
season: 'day-summer',
|
|
91
|
+
filter: 'brightness(0.36) saturate(1.65) hue-rotate(158deg)',
|
|
92
|
+
},
|
|
93
|
+
];
|
|
79
94
|
useEffect(() => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
if (DEMO) {
|
|
96
|
+
const order = ['night', 'morning', 'day', 'evening'];
|
|
97
|
+
let i = 0;
|
|
98
|
+
setTimeOfDay(order[0]);
|
|
99
|
+
const id = setInterval(() => {
|
|
100
|
+
i = (i + 1) % order.length;
|
|
101
|
+
setTimeOfDay(order[i]);
|
|
102
|
+
}, CYCLE / 4);
|
|
103
|
+
return () => clearInterval(id);
|
|
104
|
+
}
|
|
105
|
+
const interval = setInterval(() => setTimeOfDay(getTimeOfDay()), 60000);
|
|
83
106
|
return () => clearInterval(interval);
|
|
84
|
-
}, []);
|
|
107
|
+
}, [DEMO, CYCLE]);
|
|
85
108
|
const { windowDimensions: { width }, } = useWindowDimensions();
|
|
86
109
|
const isMobile = width < 768;
|
|
87
110
|
if (isMobile)
|
|
88
|
-
return _jsx(
|
|
89
|
-
return (
|
|
111
|
+
return (_jsx(TimeOfDayContext.Provider, { value: timeOfDay, children: children }));
|
|
112
|
+
return (_jsx(TimeOfDayContext.Provider, { value: timeOfDay, children: _jsxs(Box, { minH: "100vh", w: "full", position: "relative", overflowX: "hidden", overflowY: "scroll", children: [Object.keys(gradients).map((key) => (_jsx(Box, { position: "fixed", inset: 0, zIndex: 0, pointerEvents: "none", bgGradient: gradients[key], opacity: timeOfDay === key ? 1 : 0, transition: timeOfDay === key
|
|
113
|
+
? 'opacity 1.6s ease-in' // incoming sky fades in first
|
|
114
|
+
: 'opacity 1.6s ease-out 1.4s' // outgoing waits, then fades — no transparent (white) gap
|
|
115
|
+
}, key))), _jsx(Box, { position: "fixed", inset: 0, zIndex: 0, pointerEvents: "none", bgGradient: "linear(to-b, #060a1f 0%, #080c22 28%, rgba(8,12,34,0) 66%)", opacity: timeOfDay === 'night' ? 1 : 0, transition: "opacity 0.9s ease-in" }), children, (timeOfDay === 'night' || timeOfDay === 'evening') && (_jsx(Box, { style: {
|
|
116
|
+
opacity: timeOfDay === 'night' ? 1 : 0,
|
|
117
|
+
transition: 'opacity 2s ease-in',
|
|
118
|
+
}, children: _jsx(Stars, {}) })), _jsxs(Box, { position: "fixed", inset: 0, zIndex: "1", pointerEvents: "none", style: { opacity: cloudOpacity, transition: 'opacity 1.8s ease-in 1.2s' }, children: [_jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "80px", top: "50px", animation: `${moveRightToLeftWithFade3} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "120px", top: "80px", animation: `${moveRightToLeftWithFade3} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "120px", animation: `${moveRightToLeftWithFade3} 130s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud1, position: "fixed", w: "auto", h: "100px", top: "160px", animation: `${moveRightToLeftWithFade2} 400s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud2, position: "fixed", w: "auto", h: "112px", top: "220px", animation: `${moveRightToLeftWithFade2} 200s linear infinite`, zIndex: "1" }), _jsx(Image, { src: Cloud3, position: "fixed", w: "auto", h: "160px", top: "300px", animation: `${moveRightToLeftWithFade2} 130s linear infinite`, zIndex: "1" })] }), showClouds && _jsx(Birds, {}), treeLayers.map((t) => (_jsx(WeatherTrees, { timeOfDaySeason: t.season, filter: t.filter, position: "fixed", bottom: "0", right: "0", w: "auto", h: "100px", zIndex: "1", style: {
|
|
119
|
+
opacity: timeOfDay === t.key ? 1 : 0,
|
|
120
|
+
transition: 'opacity 1.8s ease-in-out',
|
|
121
|
+
} }, `tree-r-${t.key}`))), treeLayers.map((t) => (_jsx(WeatherTrees, { timeOfDaySeason: t.season, filter: t.filter, position: "fixed", bottom: "0", left: "2", w: "auto", transform: "rotateY(180deg)", h: "100px", zIndex: "1", style: {
|
|
122
|
+
opacity: timeOfDay === t.key ? 1 : 0,
|
|
123
|
+
transition: 'opacity 1.8s ease-in-out',
|
|
124
|
+
} }, `tree-l-${t.key}`)))] }) }));
|
|
90
125
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stars — a faint twinkling starfield for the night sky. Rendered only at night
|
|
3
|
+
* by CloudsAnimation. Sits behind the clouds/birds layer; concentrated in the
|
|
4
|
+
* upper sky. Honors prefers-reduced-motion.
|
|
5
|
+
*/
|
|
6
|
+
export declare const Stars: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Stars — a faint twinkling starfield for the night sky. Rendered only at night
|
|
5
|
+
* by CloudsAnimation. Sits behind the clouds/birds layer; concentrated in the
|
|
6
|
+
* upper sky. Honors prefers-reduced-motion.
|
|
7
|
+
*/
|
|
8
|
+
export const Stars = () => {
|
|
9
|
+
const stars = useMemo(() => {
|
|
10
|
+
const n = 80;
|
|
11
|
+
return Array.from({ length: n }, () => ({
|
|
12
|
+
left: Math.random() * 100,
|
|
13
|
+
top: Math.random() * 70, // keep to the upper sky
|
|
14
|
+
// weighted toward tiny — mostly pinpricks, a few slightly larger, none big
|
|
15
|
+
size: 0.6 + Math.pow(Math.random(), 2) * 2.1,
|
|
16
|
+
delay: -Math.random() * 5,
|
|
17
|
+
dur: 2.5 + Math.random() * 3.5,
|
|
18
|
+
base: 0.4 + Math.random() * 0.55,
|
|
19
|
+
}));
|
|
20
|
+
}, []);
|
|
21
|
+
// a couple of shooting stars that streak across on a diagonal, then fade — rare, staggered
|
|
22
|
+
const shooters = useMemo(() => Array.from({ length: 2 }, () => {
|
|
23
|
+
const dx = (Math.random() < 0.5 ? 1 : -1) * (380 + Math.random() * 320);
|
|
24
|
+
const dy = 150 + Math.random() * 190;
|
|
25
|
+
return {
|
|
26
|
+
left: 10 + Math.random() * 55,
|
|
27
|
+
top: 2 + Math.random() * 22,
|
|
28
|
+
dx,
|
|
29
|
+
dy,
|
|
30
|
+
rot: (Math.atan2(dy, dx) * 180) / Math.PI,
|
|
31
|
+
delay: -Math.random() * 25,
|
|
32
|
+
dur: 18 + Math.random() * 22,
|
|
33
|
+
};
|
|
34
|
+
}), []);
|
|
35
|
+
// a few small accent stars with a stronger, faster twinkle for a bit of sparkle
|
|
36
|
+
const twinklers = useMemo(() => Array.from({ length: 14 }, () => ({
|
|
37
|
+
left: Math.random() * 100,
|
|
38
|
+
top: Math.random() * 62,
|
|
39
|
+
size: 0.8 + Math.random() * 1.3,
|
|
40
|
+
delay: -Math.random() * 4,
|
|
41
|
+
dur: 1.4 + Math.random() * 2.2,
|
|
42
|
+
})), []);
|
|
43
|
+
return (_jsxs(_Fragment, { children: [_jsx("style", { children: STARS_CSS }), _jsxs("div", { className: "hf-stars", "aria-hidden": "true", children: [stars.map((s, i) => (_jsx("span", { className: "hf-star", style: {
|
|
44
|
+
left: `${s.left}%`,
|
|
45
|
+
top: `${s.top}%`,
|
|
46
|
+
width: `${s.size}px`,
|
|
47
|
+
height: `${s.size}px`,
|
|
48
|
+
animationDelay: `${s.delay}s`,
|
|
49
|
+
animationDuration: `${s.dur}s`,
|
|
50
|
+
'--b': s.base,
|
|
51
|
+
} }, i))), twinklers.map((s, i) => (_jsx("span", { className: "hf-star hf-twinkler", style: {
|
|
52
|
+
left: `${s.left}%`,
|
|
53
|
+
top: `${s.top}%`,
|
|
54
|
+
width: `${s.size}px`,
|
|
55
|
+
height: `${s.size}px`,
|
|
56
|
+
animationDelay: `${s.delay}s`,
|
|
57
|
+
animationDuration: `${s.dur}s`,
|
|
58
|
+
} }, `tw-${i}`))), shooters.map((s, i) => (_jsx("span", { className: "hf-shooting", style: {
|
|
59
|
+
left: `${s.left}%`,
|
|
60
|
+
top: `${s.top}%`,
|
|
61
|
+
animationDelay: `${s.delay}s`,
|
|
62
|
+
animationDuration: `${s.dur}s`,
|
|
63
|
+
'--dx': `${s.dx}px`,
|
|
64
|
+
'--dy': `${s.dy}px`,
|
|
65
|
+
'--rot': `${s.rot}deg`,
|
|
66
|
+
} }, `shoot-${i}`)))] })] }));
|
|
67
|
+
};
|
|
68
|
+
const STARS_CSS = `
|
|
69
|
+
.hf-stars{position:fixed; inset:0; z-index:0; pointer-events:none; overflow:hidden;}
|
|
70
|
+
.hf-star{position:absolute; border-radius:50%; background:#fdfdff;
|
|
71
|
+
box-shadow:0 0 4px rgba(255,255,255,.7); opacity:var(--b,.6);
|
|
72
|
+
animation-name:hf-twinkle; animation-iteration-count:infinite; animation-timing-function:ease-in-out;}
|
|
73
|
+
@keyframes hf-twinkle{ 0%,100%{opacity:calc(var(--b,.6) * .3);} 50%{opacity:var(--b,.6);} }
|
|
74
|
+
.hf-twinkler{animation-name:hf-sparkle;}
|
|
75
|
+
@keyframes hf-sparkle{ 0%,100%{opacity:.12; transform:scale(.6);} 50%{opacity:1; transform:scale(1.15);} }
|
|
76
|
+
.hf-shooting{position:absolute; width:100px; height:2px; border-radius:2px;
|
|
77
|
+
background:linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,.95));
|
|
78
|
+
box-shadow:0 0 6px 1px rgba(200,220,255,.6); opacity:0; will-change:transform,opacity;
|
|
79
|
+
transform:translate3d(0,0,0) rotate(var(--rot,25deg));
|
|
80
|
+
animation-name:hf-shoot; animation-iteration-count:infinite; animation-timing-function:linear;}
|
|
81
|
+
@keyframes hf-shoot{
|
|
82
|
+
0%{ transform:translate3d(0,0,0) rotate(var(--rot,25deg)); opacity:0; }
|
|
83
|
+
1%{ opacity:1; }
|
|
84
|
+
7%{ transform:translate3d(var(--dx,600px),var(--dy,260px),0) rotate(var(--rot,25deg)); opacity:0; }
|
|
85
|
+
100%{ transform:translate3d(var(--dx,600px),var(--dy,260px),0) rotate(var(--rot,25deg)); opacity:0; }
|
|
86
|
+
}
|
|
87
|
+
@media (prefers-reduced-motion:reduce){ .hf-star,.hf-shooting{animation:none !important;} }
|
|
88
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TimeOfDay } from '../../utils';
|
|
2
|
+
/**
|
|
3
|
+
* Shares the CloudsAnimation background's current time-of-day with anything
|
|
4
|
+
* rendered inside it (e.g. the onboarding Footer), so text/colors can adapt to
|
|
5
|
+
* the sky actually on screen rather than reading the clock independently.
|
|
6
|
+
* Consumers get `null` when rendered outside a provider and should fall back.
|
|
7
|
+
*/
|
|
8
|
+
export declare const TimeOfDayContext: import("react").Context<TimeOfDay | null>;
|
|
9
|
+
export declare const useTimeOfDay: () => TimeOfDay | null;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Shares the CloudsAnimation background's current time-of-day with anything
|
|
4
|
+
* rendered inside it (e.g. the onboarding Footer), so text/colors can adapt to
|
|
5
|
+
* the sky actually on screen rather than reading the clock independently.
|
|
6
|
+
* Consumers get `null` when rendered outside a provider and should fall back.
|
|
7
|
+
*/
|
|
8
|
+
export const TimeOfDayContext = createContext(null);
|
|
9
|
+
export const useTimeOfDay = () => useContext(TimeOfDayContext);
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { t } from 'i18next';
|
|
3
3
|
import { Flex, Text } from '@chakra-ui/react';
|
|
4
|
+
import { getTimeOfDay } from '../../utils';
|
|
5
|
+
import { useTimeOfDay } from '../animations/timeOfDayContext';
|
|
4
6
|
export const Footer = () => {
|
|
7
|
+
var _a;
|
|
5
8
|
const currentYear = new Date().getFullYear();
|
|
6
|
-
|
|
9
|
+
// follow the background's actual time-of-day (falls back to the clock when outside CloudsAnimation)
|
|
10
|
+
const isNight = ((_a = useTimeOfDay()) !== null && _a !== void 0 ? _a : getTimeOfDay()) === 'night';
|
|
11
|
+
return (_jsx(Flex, { justifyContent: "center", px: 4, my: 4, children: _jsx(Text, { variant: "label", textAlign: "center", color: isNight ? 'rgba(226,232,248,0.6)' : 'rgba(55,50,62,0.8)', children: `©${currentYear} ${t('footer.copyright')}` }) }));
|
|
7
12
|
};
|