Skip to content

Commit

Permalink
refactor(tooltip): migrate to signals spartan-ng#430
Browse files Browse the repository at this point in the history
  • Loading branch information
MerlinMoos committed Dec 2, 2024
1 parent 961af42 commit fb09a2b
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 181 deletions.
1 change: 1 addition & 0 deletions libs/ui/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './lib/brain/computed-previous';
export * from './lib/brain/custom-element-class-settable';
export * from './lib/brain/dev-mode';
export * from './lib/brain/exposes-side';
Expand Down
44 changes: 44 additions & 0 deletions libs/ui/core/src/lib/brain/computed-previous.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Component, signal } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { computedPrevious } from './computed-previous';

describe(computedPrevious.name, () => {
@Component({ standalone: true, template: '{{previous()}}' })
class TestComponent {
public readonly value = signal(0);
public readonly previous = computedPrevious(this.value);
}

function setup() {
const fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
return fixture.componentInstance;
}

it('should work properly', () => {
const cmp = setup();

expect(cmp.value()).toEqual(0);
expect(cmp.previous()).toEqual(0);

cmp.value.set(1);

expect(cmp.value()).toEqual(1);
expect(cmp.previous()).toEqual(0);

cmp.value.set(2);

expect(cmp.value()).toEqual(2);
expect(cmp.previous()).toEqual(1);

cmp.value.set(2);

expect(cmp.value()).toEqual(2);
expect(cmp.previous()).toEqual(1);

cmp.value.set(3);

expect(cmp.value()).toEqual(3);
expect(cmp.previous()).toEqual(2);
});
});
47 changes: 47 additions & 0 deletions libs/ui/core/src/lib/brain/computed-previous.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { computed, type Signal, untracked } from '@angular/core';

/**
* Returns a signal that emits the previous value of the given signal.
* The first time the signal is emitted, the previous value will be the same as the current value.
*
* @example
* ```ts
* const value = signal(0);
* const previous = computedPrevious(value);
*
* effect(() => {
* console.log('Current value:', value());
* console.log('Previous value:', previous());
* });
*
* Logs:
* // Current value: 0
* // Previous value: 0
*
* value.set(1);
*
* Logs:
* // Current value: 1
* // Previous value: 0
*
* value.set(2);
*
* Logs:
* // Current value: 2
* // Previous value: 1
*```
*
* @param s Signal to compute previous value for
* @returns Signal that emits previous value of `s`
*/
export function computedPrevious<T>(s: Signal<T>): Signal<T> {
let current = null as T;
let previous = untracked(() => s()); // initial value is the current value

return computed(() => {
current = s();
const result = previous;
previous = current;
return result;
});
}
21 changes: 8 additions & 13 deletions libs/ui/tooltip/brain/src/lib/brn-tooltip-content.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
* Check them out! Give them a try! Leave a star! Their work is incredible!
*/

import { NgTemplateOutlet, isPlatformBrowser } from '@angular/common';
import { isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
type ElementRef,
ElementRef,
inject,
type OnDestroy,
PLATFORM_ID,
Renderer2,
signal,
type TemplateRef,
ViewChild,
viewChild,
ViewEncapsulation,
inject,
signal,
} from '@angular/core';
import { Subject } from 'rxjs';

Expand Down Expand Up @@ -82,12 +82,7 @@ export class BrnTooltipContentComponent implements OnDestroy {
public _exitAnimationDuration = 0;

/** Reference to the internal tooltip element. */
@ViewChild('tooltip', {
// Use a static query here since we interact directly with
// the DOM which can happen before `ngAfterViewInit`.
static: true,
})
public _tooltip?: ElementRef<HTMLElement>;
public _tooltip = viewChild('tooltip', { read: ElementRef<HTMLElement> });

/** Whether interactions on the page should close the tooltip */
private _closeOnInteraction = false;
Expand Down Expand Up @@ -216,7 +211,7 @@ export class BrnTooltipContentComponent implements OnDestroy {
// We set the classes directly here ourselves so that toggling the tooltip state
// isn't bound by change detection. This allows us to hide it even if the
// view ref has been detached from the CD tree.
const tooltip = this._tooltip?.nativeElement;
const tooltip = this._tooltip()?.nativeElement;
if (!tooltip || !this._isBrowser) return;
this._renderer2.setStyle(tooltip, 'visibility', isVisible ? 'visible' : 'hidden');
if (isVisible) {
Expand All @@ -231,7 +226,7 @@ export class BrnTooltipContentComponent implements OnDestroy {
// We set the classes directly here ourselves so that toggling the tooltip state
// isn't bound by change detection. This allows us to hide it even if the
// view ref has been detached from the CD tree.
const tooltip = this._tooltip?.nativeElement;
const tooltip = this._tooltip()?.nativeElement;
if (!tooltip || !this._isBrowser) return;
this._renderer2.setAttribute(tooltip, 'data-side', side);
this._renderer2.setAttribute(tooltip, 'data-state', isVisible ? 'open' : 'closed');
Expand Down
Loading

0 comments on commit fb09a2b

Please sign in to comment.