Skip to content

Replace Angular animations with browser native animation#600

Merged
uldisrudzitis merged 2 commits intomasterfrom
fix-deprecated-animations
Oct 29, 2025
Merged

Replace Angular animations with browser native animation#600
uldisrudzitis merged 2 commits intomasterfrom
fix-deprecated-animations

Conversation

@uldisrudzitis
Copy link
Copy Markdown
Collaborator

@uldisrudzitis uldisrudzitis commented Oct 29, 2025

Summary by CodeRabbit

  • Refactor

    • Removed animated transitions from settings panels and collapsible sections throughout the editor; panels now expand/collapse instantly.
    • Simplified templates and layout for many editors and panels, consolidating settings groups and reducing nested conditional blocks.
    • Shop section navigation now derives state reactively and uses async handling for toggling sections.
  • Style

    • Updated aside/settings layout to use grid-based expansion behavior for smoother layout changes.

@uldisrudzitis uldisrudzitis self-assigned this Oct 29, 2025
@uldisrudzitis uldisrudzitis added Enhancement dependencies Pull requests that update a dependency file labels Oct 29, 2025
@uldisrudzitis uldisrudzitis changed the title Replace Angular animations with browsers native animation Replace Angular animations with browser native animation Oct 29, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

This PR removes Angular animation usage across the editor: deletes BrowserAnimationsModule and the shared animations file, strips animation bindings/imports from multiple components, restructures templates to non-animated layouts, and replaces an overflow-based expansion with a grid-based CSS transition.

Changes

Cohort / File(s) Summary
Animation infra
editor/src/app/app.module.ts, editor/src/app/shared/animations.ts
Removed BrowserAnimationsModule import/usage and deleted the shared Animations (slideToggle) definitions.
Component templates & metadata
editor/src/app/shop/shop.component.ts, editor/src/app/sites/media/entry-gallery-editor.component.ts, editor/src/app/sites/media/entry-gallery-image-editor.component.ts, editor/src/app/sites/sections/background-gallery-editor.component.ts, editor/src/app/sites/sections/section.component.ts, editor/src/app/sites/settings/site-settings.component.ts, editor/src/app/sites/template-settings/site-template-settings.component.ts
Removed Animations imports and animations: [...] metadata and stripped [@isExpanded] bindings. Templates were restructured to always-render or conditionally-render static containers instead of animated wrappers; ShopComponent now exposes currentShopSection$ and makes toggleSection async using firstValueFrom.
Styling
editor/src/styles/_aside.scss
Replaced overflow-based expansion with a grid-based layout using display: grid, grid-template-rows, and transitions on grid-template-rows to drive expansion effects.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant S as ShopComponent
    participant R as ActivatedRoute
    participant N as Router

    U->>S: click toggleSection(section)
    Note over S: async toggleSection\nawait currentShopSection$
    S->>R: read route params (currentShopSection$)
    R-->>S: returns section (via observable)
    alt same section
        S->>N: navigate to shop root (clear section)
    else different section
        S->>N: navigate to `/shop/<section>`
    end
    N-->>S: navigation result
    S->>U: UI updates via route-driven bindings
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to inspect closely:
    • shop.component.ts async toggleSection logic and observable usage (firstValueFrom and template async bindings).
    • Major template restructures in entry-gallery-editor.component.ts and entry-gallery-image-editor.component.ts for UI/UX regressions.
    • CSS grid-based expansion in editor/src/styles/_aside.scss to ensure transitions and overflow handling behave across browsers.
    • Removal of BrowserAnimationsModule in app.module.ts and potential side-effects where animation providers were relied upon.

Possibly related PRs

Poem

🐰 I hopped through code with nimble paws,

Removed the twirls and quieted the draws.
Grid rows hum where motion once played,
Simpler edges where shadows swayed.
A quiet editor, soft and bright—thank you, devs, for tidy light.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Replace Angular animations with browser native animation" directly and accurately describes the primary change across the entire changeset. The changeset removes Angular's animation system (BrowserAnimationsModule, animations.ts file, and Animations imports from multiple components) and replaces animated transitions with CSS-based browser-native alternatives (specifically using CSS Grid transitions in the _aside.scss file). The title is concise, specific, and clearly communicates the core intent without vague language, making it informative for developers scanning the PR history.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-deprecated-animations

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 167a243 and 2ca5d91.

📒 Files selected for processing (1)
  • editor/src/app/shop/shop.component.ts (5 hunks)
🔇 Additional comments (6)
editor/src/app/shop/shop.component.ts (6)

1-3: LGTM! Import changes align with reactive pattern.

The removal of OnInit and addition of firstValueFrom are consistent with the shift to an Observable-based approach for managing section state.


16-18: LGTM! Reactive class binding is implemented correctly.

The async pipe properly drives the is-expanded class based on the current route section. Angular's async pipe manages subscriptions efficiently even when used multiple times in the loop.


42-56: Verify the necessity of the nested wrapper div.

The inner <div> (line 43) wrapping the conditional components appears to serve no structural purpose. If it's required for CSS transitions (as mentioned in the PR description about grid-based transitions), please add a comment or class to clarify its purpose for future maintainability.


70-71: LGTM! Clean reactive state management.

The shift from imperative lifecycle initialization to declarative Observable-based state is a solid improvement. The Observable<string | null> type correctly reflects that the section parameter may be absent.


78-81: LGTM! Observable initialization is clean and correct.

Deriving currentShopSection$ directly from route parameters is idiomatic Angular. The async pipe in the template will handle subscription management automatically.


118-120: Good use of firstValueFrom, but consider edge case handling.

The reactive approach is correct. Note that firstValueFrom will throw if the observable completes without emitting a value. While route.paramMap should always emit at least once, you might consider wrapping this in a try-catch if there are edge cases where the route might not be initialized, though this is unlikely in practice.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
editor/src/app/sites/media/entry-gallery-image-editor.component.ts (1)

27-28: @Component.imports without standalone: true causes compilation error in Angular 20.

The imports property in @component decorator is only valid for standalone components. This component is declared in SiteMediaModule, making it non-standalone.

Required fixes:

  1. Remove imports from the decorator and set standalone: false:
 @Component({
   selector: 'berta-entry-gallery-image-editor',
+  standalone: false,
-  imports: [ImageCropperComponent, SitesSharedModule],
   template: `
  1. In site-media.module.ts, move the component from imports to declarations and import ImageCropperModule:
 @NgModule({
   imports: [
     CommonModule,
     RouterModule.forChild([]),
     NgsgModule,
     SitesSharedModule,
+    ImageCropperModule,
-    EntryGalleryImageEditorComponent,
   ],
   declarations: [
     SiteMediaComponent,
     EntryGalleryComponent,
     EntryGalleryEditorComponent,
+    EntryGalleryImageEditorComponent,
   ],
 })

Add the import at the top of site-media.module.ts:

import { ImageCropperModule } from 'ngx-image-cropper';
🧹 Nitpick comments (7)
editor/src/styles/_aside.scss (2)

175-182: Nice CSS grid expander; consider reduced‑motion.

Grid 0fr→1fr transition is clean. Add an opt‑out for users with reduced motion preferences.

   > .settings {
     display: grid;
     grid-template-rows: 0fr;
     transition: grid-template-rows 0.25s ease;
 
     > div {
       overflow: hidden;
     }
   }
+  @media (prefers-reduced-motion: reduce) {
+    > .settings {
+      transition: none;
+    }
+  }

278-279: Preserve border state during transition.

When toggling, the bottom border appears only in expanded state. If you want a consistent divider regardless of motion setting, move the border to the container and toggle color/alpha instead. Optional, stylistic.

editor/src/app/shop/shop.component.ts (1)

40-66: Add keyboard support to the section toggle.

(click) on <h3> isn’t keyboard‑accessible. Provide Enter/Space handlers and tabindex.

// In the <h3> element
<h3
  (click)="toggleSection(shopSection.urlSegment)"
  (keydown.enter)="toggleSection(shopSection.urlSegment)"
  (keydown.space)="toggleSection(shopSection.urlSegment)"
  [attr.tabindex]="0"
  role="button"
  class="hoverable"
>
editor/src/app/sites/sections/background-gallery-editor.component.ts (2)

52-67: Make the “Item settings” toggle accessible.

Add Enter/Space handlers and a tabindex on the <h3> that toggles fileSettingsIsOpen.

<h3
  (click)="fileSettingsIsOpen = !fileSettingsIsOpen"
  (keydown.enter)="fileSettingsIsOpen = !fileSettingsIsOpen"
  (keydown.space)="fileSettingsIsOpen = !fileSettingsIsOpen"
  [attr.tabindex]="0"
  role="button"
  class="hoverable"
>

92-99: Mirror the same a11y for “Gallery settings”.

Apply the same key handlers/tabindex to the gallery header.

editor/src/app/sites/media/entry-gallery-editor.component.ts (2)

54-106: Add keyboard support to open/close “Item settings”.

Mirror the a11y tweak: Enter/Space handlers and tabindex on the toggling <h3>.


133-344: Minor: prefer forEach (or for..of) over map for side effects.

files.map(…) ignores the returned array. Use forEach or for..of for clarity.

- files.map((file) => {
+ files.forEach((file) => {
    this.store
      .dispatch(/* … */)
      .pipe(take(1))
      .subscribe(
        () => {},
        (error) => { this.uploadFilesErrors.push(error.error.error); },
      );
- });
+ });

Also add the same Enter/Space handlers/tabindex on the “Gallery settings” header.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7df8507 and 167a243.

📒 Files selected for processing (10)
  • editor/src/app/app.module.ts (0 hunks)
  • editor/src/app/shared/animations.ts (0 hunks)
  • editor/src/app/shop/shop.component.ts (1 hunks)
  • editor/src/app/sites/media/entry-gallery-editor.component.ts (5 hunks)
  • editor/src/app/sites/media/entry-gallery-image-editor.component.ts (1 hunks)
  • editor/src/app/sites/sections/background-gallery-editor.component.ts (2 hunks)
  • editor/src/app/sites/sections/section.component.ts (1 hunks)
  • editor/src/app/sites/settings/site-settings.component.ts (1 hunks)
  • editor/src/app/sites/template-settings/site-template-settings.component.ts (1 hunks)
  • editor/src/styles/_aside.scss (2 hunks)
💤 Files with no reviewable changes (2)
  • editor/src/app/shared/animations.ts
  • editor/src/app/app.module.ts
🔇 Additional comments (2)
editor/src/app/sites/sections/section.component.ts (1)

78-133: LGTM – structure matches the new CSS expander semantics.

editor/src/app/sites/settings/site-settings.component.ts (1)

61-148: Fix children reuse logic in scan (null guard + correct comparison).

Two issues can throw or prevent proper referential reuse:

  • Potential NPE: prevSettingChildren.length when children is undefined.
  • Likely typo: comparing prevChild.setting === child.config instead of child.setting.

Apply:

- const prevSettingChildren = prevSettingGroup.settings[index].children;
- if (prevSettingChildren.length > 0) {
+ const prevSettingChildren = prevSettingGroup.settings[index].children;
+ if (prevSettingChildren && prevSettingChildren.length > 0) {
    setting.children = setting.children.map((row, index) => {
      const prevSettingRow = prevSettingChildren[index];
      if (prevSettingRow) {
        return row.map((child, i) => {
          const prevChild = prevSettingRow[i];
-         if (prevChild && prevChild.setting === child.config) {
+         if (prevChild && prevChild.setting === child.setting) {
            return prevChild;
          }
          return child;
        });
      }
      return row;
    });
  }

I can open a follow‑up to add unit tests for this reuse path.

Likely an incorrect or invalid review comment.

Comment on lines +88 to +106
<div class="setting">
@if (cropperIsReady) {
<button
type="button"
class="button"
[class.disabled]="!canCrop"
(click)="cropImage()"
>
Crop
</button>
}
<button
type="button"
class="button"
[class.disabled]="!canCrop"
(click)="cropImage()"
class="button inverse"
(click)="navigateBack()"
>
Crop
Close
</button>
}
<button
type="button"
class="button inverse"
(click)="navigateBack()"
>
Close
</button>
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Prevent invalid cropping actions (runtime safety + UX).

  • Button uses a CSS “disabled” class only; clicks still fire.
  • Methods assume imageCroppedEvent is set.

Add [disabled] and guards.

-<button
+<button
   type="button"
   class="button"
-  [class.disabled]="!canCrop"
+  [class.disabled]="!canCrop"
+  [disabled]="!canCrop"
   (click)="cropImage()"
>
   Crop
</button>
- cropImage() {
+ cropImage() {
+   if (!this.imageCroppedEvent) { return; }
    this.store.dispatch(
      new UpdateEntryGalleryImageCropAction(
        this.site.name,
        this.sectionName,
        this.entryId,
        this.imageOrder.toString(),
        {
          x: this.imageCroppedEvent.imagePosition.x1,
          y: this.imageCroppedEvent.imagePosition.y1,
          width: this.imageCroppedEvent.width,
          height: this.imageCroppedEvent.height,
        },
      ),
    );
  }

Optionally guard updateSize(e) similarly:

if (!this.imageCroppedEvent) { return; }
🤖 Prompt for AI Agents
In editor/src/app/sites/media/entry-gallery-image-editor.component.ts around
lines 88 to 106, the Crop button only uses a CSS class to look disabled but
still fires clicks and methods assume imageCroppedEvent exists; update the
template to add a real disabled attribute (e.g. [disabled]="!canCrop") so clicks
are prevented by the browser and then add runtime guards in the component
methods: at the start of cropImage() check if (!this.canCrop ||
!this.imageCroppedEvent) return; and similarly add if (!this.imageCroppedEvent)
return; to updateSize(e) to avoid null/undefined access. Ensure the Close button
behavior is unchanged.

Comment on lines +58 to +75
<div class="settings">
<div>
@for (setting of settingGroup.settings; track setting) {
<berta-setting
[templateSlug]="settingGroup.templateSlug"
[setting]="setting.setting"
[config]="setting.config"
[disabled]="
settingUpdate[settingGroup.slug + ':' + setting.setting.slug]
"
[error]="
settingError[settingGroup.slug + ':' + setting.setting.slug]
"
(update)="updateSetting(settingGroup.slug, $event)"
(emitAction)="emitAction($event)"
></berta-setting>
}
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Fix type mismatch and RxJS scan signature (build-time blocker).

The template uses settingGroup.templateSlug, but the declared type puts templateSlug on each item in settings[]. Mapping code also attaches templateSlug on the group, not on items. This inconsistency will fail strict template type‑checking and confuses tooling. Also, scan((prev, curr, templateSlug) => …) names the third param as if it were a slug; it’s actually the emission index.

  • Align the type so templateSlug lives on the group.
  • Simplify scan to the 2‑arg form (or rename the 3rd param to _index and remove the falsy check).

Apply the following fixes:

Type shape:

// change the group shape so templateSlug is on the group, not items
templateSettings$: Observable<Array<{
  config: SettingGroupConfigModel['_'];
  settings: Array<{
    setting: SettingModel;
    config: SettingConfigModel;
  }>;
  slug: string;
  templateSlug: string;
}>>;

scan:

// current
scan((prevSettingGroups, settingGroups, templateSlug) => {
// proposed
scan((prevSettingGroups, settingGroups) => {
  if (!prevSettingGroups || prevSettingGroups.length === 0) {
    return settingGroups;
  }
  // …rest unchanged
})
🤖 Prompt for AI Agents
In editor/src/app/sites/template-settings/site-template-settings.component.ts
around lines 58 to 75, the template expects templateSlug on the settingGroup but
the TypeScript shape places templateSlug on each setting item and the RxJS scan
callback uses a 3‑arg signature (third param misnamed templateSlug) which breaks
strict type checking and the build; update the group type so templateSlug is a
property of the settingGroup (remove it from individual setting items) and
adjust the scan operator to the 2‑argument form scan((prev, curr) => { ... })
(or rename the third param to _index and drop any falsy-checks that treated it
as a slug) so the types and template align and the RxJS callback uses the
correct parameters.

@uldisrudzitis uldisrudzitis merged commit 1f77875 into master Oct 29, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Nov 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file Enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant