@timber-js/app 0.2.0-alpha.2 → 0.2.0-alpha.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"top-loader.d.ts","sourceRoot":"","sources":["../../src/client/top-loader.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6CD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CA6F7F"}
1
+ {"version":3,"file":"top-loader.d.ts","sourceRoot":"","sources":["../../src/client/top-loader.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAmDD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,GAAG,IAAI,CA0F7F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.2",
3
+ "version": "0.2.0-alpha.3",
4
4
  "description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -60,6 +60,7 @@ const DEFAULT_Z_INDEX = 1600;
60
60
  // Unique keyframes name to avoid collisions with user styles.
61
61
  const CRAWL_KEYFRAMES = '__timber_top_loader_crawl';
62
62
  const APPEAR_KEYFRAMES = '__timber_top_loader_appear';
63
+ const FINISH_KEYFRAMES = '__timber_top_loader_finish';
63
64
 
64
65
  // Track whether the @keyframes rules have been injected into the document.
65
66
  let keyframesInjected = false;
@@ -83,6 +84,11 @@ function ensureKeyframes(): void {
83
84
  from { opacity: 0; }
84
85
  to { opacity: 1; }
85
86
  }
87
+ @keyframes ${FINISH_KEYFRAMES} {
88
+ 0% { width: 90%; opacity: 1; }
89
+ 50% { width: 100%; opacity: 1; }
90
+ 100% { width: 100%; opacity: 0; }
91
+ }
86
92
  `;
87
93
  document.head.appendChild(style);
88
94
  keyframesInjected = true;
@@ -161,14 +167,13 @@ export function TopLoader({ config }: { config?: TopLoaderConfig }): React.React
161
167
  ].join(', '),
162
168
  }
163
169
  : {
164
- // Finishing: snap to 100% width (200ms), THEN fade out (200ms).
165
- // The opacity transition is delayed so the user sees the bar
166
- // reach 100% before it disappears. Without the delay, both
167
- // transitions run simultaneously and the bar fades before the
168
- // fill animation is visible.
169
- width: '100%',
170
- opacity: 0,
171
- transition: 'width 200ms ease, opacity 200ms ease 200ms',
170
+ // Finishing: fill to 100% then fade out via a keyframe animation.
171
+ // We use a keyframe instead of a CSS transition because the
172
+ // animation-to-transition handoff is unreliable the browser
173
+ // may not capture the animated width as the transition's "from"
174
+ // value when both the animation removal and transition are
175
+ // applied in the same render frame.
176
+ animation: `${FINISH_KEYFRAMES} 400ms ease forwards`,
172
177
  }),
173
178
  ...(shadow
174
179
  ? {
@@ -177,12 +182,10 @@ export function TopLoader({ config }: { config?: TopLoaderConfig }): React.React
177
182
  : {}),
178
183
  };
179
184
 
180
- // Clean up the finishing phase when the CSS transition completes.
181
- // onTransitionEnd fires once per transitioned property — we act on
182
- // the first one (opacity) and ignore subsequent (width).
183
- const handleTransitionEnd = phase === 'finishing'
184
- ? (e: React.TransitionEvent) => {
185
- if (e.propertyName === 'opacity') {
185
+ // Clean up the finishing phase when the finish animation completes.
186
+ const handleAnimationEnd = phase === 'finishing'
187
+ ? (e: React.AnimationEvent) => {
188
+ if (e.animationName === FINISH_KEYFRAMES) {
186
189
  setPhase('hidden');
187
190
  }
188
191
  }
@@ -195,6 +198,6 @@ export function TopLoader({ config }: { config?: TopLoaderConfig }): React.React
195
198
  'aria-hidden': 'true',
196
199
  'data-timber-top-loader': '',
197
200
  },
198
- createElement('div', { style: barStyle, onTransitionEnd: handleTransitionEnd })
201
+ createElement('div', { style: barStyle, onAnimationEnd: handleAnimationEnd })
199
202
  );
200
203
  }