How can I make Angular component animations follow CSS class changes, rather than static styles?

Published

I’ve created a block-cursor date/time input field that uses Angular states and animations to indicate both ongoing states (like being disabled) or animated transitional states (like a red flash when an invalid keystroke has been entered). These input fields can be seen in use here: https://skyviewcafe.com/

export const BACKGROUND_ANIMATIONS = trigger('displayState', [
  state('error',     style({ backgroundColor: getBackgroundColor(ERROR_BACKGROUND, '#F67') })),
  state('normal',    style({ backgroundColor: getBackgroundColor(NORMAL_BACKGROUND, 'white') })),
  state('confirm',   style({ backgroundColor: getBackgroundColor(CONFIRM_BACKGROUND, '#6C6') })),
  state('warning',   style({ backgroundColor: getBackgroundColor(WARNING_BACKGROUND, '#FC6') })),
  state('view-only', style({ backgroundColor: getBackgroundColor(VIEW_ONLY_BACKGROUND, 'black') })),
  state('disabled',  style({ backgroundColor: getBackgroundColor(DISABLED_BACKGROUND, '#CCC') })),
  state('dark-error',     style({ backgroundColor: getBackgroundColor(ERROR_BACKGROUND, '#C36', true) })),
  state('dark-normal',    style({ backgroundColor: getBackgroundColor(NORMAL_BACKGROUND, '#333', true) })),
  state('dark-confirm',   style({ backgroundColor: getBackgroundColor(CONFIRM_BACKGROUND, '#292', true) })),
  state('dark-warning',   style({ backgroundColor: getBackgroundColor(WARNING_BACKGROUND, '#B80', true) })),
  state('dark-view-only', style({ backgroundColor: getBackgroundColor(VIEW_ONLY_BACKGROUND, '#0A0', true) })),
  state('dark-disabled',  style({ backgroundColor: getBackgroundColor(DISABLED_BACKGROUND, '#444', true) })),
  transition('normal => error',   animate(FLASH_DURATION)),
  transition('error => normal',   animate(FLASH_DURATION)),
  transition('normal => confirm', animate(FLASH_DURATION)),
  transition('confirm => normal', animate(FLASH_DURATION)),
  transition('warning => error',  animate(FLASH_DURATION)),
  transition('error => warning',  animate(FLASH_DURATION)),
  transition('dark-normal => dark-error',   animate(FLASH_DURATION)),
  transition('dark-error => dark-normal',   animate(FLASH_DURATION)),
  transition('dark-normal => dark-confirm', animate(FLASH_DURATION)),
  transition('dark-confirm => dark-normal', animate(FLASH_DURATION)),
  transition('dark-warning => dark-error',  animate(FLASH_DURATION)),
  transition('dark-error => dark-warning',  animate(FLASH_DURATION))
]);

The above animations are defined in an abstract superclass so that concrete subclasses can use them like this:

@Component({
  selector: 'tbw-time-editor',
  animations: [BACKGROUND_ANIMATIONS],
  templateUrl: '../digit-sequence-editor/digit-sequence-editor.directive.html',
  styleUrls: ['../digit-sequence-editor/digit-sequence-editor.directive.scss', './time-editor.component.scss'],
  providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TimeEditorComponent), multi: true },
              { provide: NG_VALIDATORS, useExisting: forwardRef(() => TimeEditorComponent), multi: true }]
})
export class TimeEditorComponent extends DigitSequenceEditorDirective<number> implements OnInit {
  ...

First of all, I’d prefer to cut the number of display states in half. I consider it a hack to have to have all of those dark_-prefixed states. I’d rather have these colors automatically update, as they do for DOM elements, simply by adding the tbw-dark-mode class to the body of my HTML document.

I’m also limited to supporting only the two pre-defined modes, light and dark. Users can customize both the light mode and the dark mode, but a third mode, or generalized "skins" would be impossible.

Timing also matters. These colors are generated once during the initialization of the application in which my components are embedded, and I see no access to an Angular API that allows these state definitions to be updated later.

The function getBackgroundColor() at least allows me to check once how colors have been defined at start-up, but it’s not perfect (iOS Safari messes up and thinks everything is transparent), so I have to define fixed fallback values which can’t be updated from a style sheet.

I did discover a disgusting hack to get some dynamic updating of colors, but it’s not something I’d want to rely on! Instead of using a string for each color, I defined a dynamic color object like this:

class DynamicColor extends String {
  constructor(private colorKey: string) {}

  // ...

  toString(): string {
    return this.colorTable[colorKey];
  }
}

Even that requires some forced type coercion to string, and forced screen refreshing to make changes take effect.

Is there a better way? A way to use CSS classes instead of statically-defined styles? An API I haven’t found yet to update the animations which are installed by the @Component decorator?

Source: Angular Questions

Published
Categorized as angular, animation, css, javascript, typescript Tagged , , , ,

Answers

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Still Have Questions?


Our dedicated development team is here for you!

We can help you find answers to your question for as low as 5$.

Contact Us
faq