Mijn aanvragen: list enter/leave animations + 204 null-body fix
- animate.enter/leave on aanvraag cards and upload rows (native Angular, no @angular/animations) - reduced-motion: skip animation → instant removal - api-client: null-body statuses (204/205/304) must pass null to Response() Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -39,7 +39,7 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
|
|||||||
<app-heading [level]="2" i18n="@@dashboard.mijnAanvragen">Mijn aanvragen</app-heading>
|
<app-heading [level]="2" i18n="@@dashboard.mijnAanvragen">Mijn aanvragen</app-heading>
|
||||||
<div class="app-stack app-section">
|
<div class="app-stack app-section">
|
||||||
@for (a of aanvragen(); track a.id) {
|
@for (a of aanvragen(); track a.id) {
|
||||||
<app-aanvraag-block [aanvraag]="a" (resume)="resume(a)" (cancel)="cancelAanvraag(a)" />
|
<app-aanvraag-block animate.enter="app-item-enter" animate.leave="app-item-leave" [aanvraag]="a" (resume)="resume(a)" (cancel)="cancelAanvraag(a)" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ function httpClientFetch(http: HttpClient) {
|
|||||||
})
|
})
|
||||||
.pipe(timeout(REQUEST_TIMEOUT_MS)),
|
.pipe(timeout(REQUEST_TIMEOUT_MS)),
|
||||||
);
|
);
|
||||||
return new Response(res.body ?? '', { status: res.status || 200 });
|
// 204/205/304 are null-body statuses — new Response(body, …) throws for any non-null body.
|
||||||
|
const nullBody = res.status === 204 || res.status === 205 || res.status === 304;
|
||||||
|
return new Response(nullBody ? null : (res.body ?? ''), { status: res.status || 200 });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof TimeoutError) return new Response('', { status: 504 });
|
if (e instanceof TimeoutError) return new Response('', { status: 504 });
|
||||||
const err = e as HttpErrorResponse;
|
const err = e as HttpErrorResponse;
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ import { SingleUploadComponent } from '../single-upload/single-upload.component'
|
|||||||
<div class="uploads">
|
<div class="uploads">
|
||||||
@for (u of uploads(); track u.localId) {
|
@for (u of uploads(); track u.localId) {
|
||||||
<app-single-upload
|
<app-single-upload
|
||||||
|
animate.enter="app-item-enter"
|
||||||
|
animate.leave="app-item-leave"
|
||||||
[upload]="u"
|
[upload]="u"
|
||||||
[previewUrlFor]="previewUrlFor()"
|
[previewUrlFor]="previewUrlFor()"
|
||||||
(remove)="onRemove(u)"
|
(remove)="onRemove(u)"
|
||||||
|
|||||||
@@ -86,8 +86,26 @@ app-site-footer { view-transition-name: site-footer; }
|
|||||||
animation-timing-function: ease;
|
animation-timing-function: ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* List-item enter/leave for lists that add/remove at runtime (aanvraag cards, upload
|
||||||
|
rows). Used via Angular's native `animate.enter`/`animate.leave` — no @angular/animations. */
|
||||||
|
@keyframes app-item-enter { from { opacity: 0; transform: translateY(-0.5rem); } to { opacity: 1; transform: none; } }
|
||||||
|
@keyframes app-item-leave {
|
||||||
|
from { opacity: 1; max-block-size: 50rem; }
|
||||||
|
to { opacity: 0; transform: translateY(-0.25rem); max-block-size: 0; margin-block: 0; padding-block: 0; }
|
||||||
|
}
|
||||||
|
.app-item-enter { animation: app-item-enter 220ms ease both; }
|
||||||
|
.app-item-leave {
|
||||||
|
display: block; /* collapse needs block formatting (host is inline/flex by default) */
|
||||||
|
overflow: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
/* ponytail: 50rem max-height hack; if an item is taller the collapse eases slightly late — measure height in JS then. */
|
||||||
|
animation: app-item-leave 260ms ease both;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
::view-transition-group(*),
|
::view-transition-group(*),
|
||||||
::view-transition-old(*),
|
::view-transition-old(*),
|
||||||
::view-transition-new(*) { animation: none !important; }
|
::view-transition-new(*) { animation: none !important; }
|
||||||
|
/* No animation → Angular removes the leaving node immediately (instant, no motion). */
|
||||||
|
.app-item-enter, .app-item-leave { animation: none; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user