Skip to content

Commit

Permalink
fix(color-picker): attempt to fix nondeterministic color-picker bug l…
Browse files Browse the repository at this point in the history
…ogic rewriting
  • Loading branch information
Raiper34 committed Jan 18, 2025
1 parent 42c882a commit 14ca5c3
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<button class="st-button st-toolbar-item" [title]="button?.title || ''" (stAction)="colorPicker.showPicker()">
{{rgbStringToHex(state)}}
<input #colorPicker class="st-color-picker" type="color" [ngModel]="rgbStringToHex(state)" tabindex="-1"
(change)="onCommand(button?.command, $event.target.value)" onmousedown='return false;' onselectstart='return false;'>
(stNgModelChangeDebounced)="onCommand(button?.command, $event)">
<i [class]="button?.icon" [style.color]="state"></i>
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NgModelChangeDebouncedDirective } from './ng-model-change-debounced.directive';
import {from, of} from "rxjs";
import {NgModel} from "@angular/forms";
import {fakeAsync, flush} from "@angular/core/testing";

describe('NgModelChangeDebouncedDirective', () => {

it('should create an instance', () => {
const ngModel = {control: {valueChanges: of()}} as NgModel;

const directive = new NgModelChangeDebouncedDirective(ngModel);

expect(directive).toBeTruthy();
});

it('should emit debounced ngChange event', fakeAsync(() => {
const ngModel = {control: {valueChanges: from([null, '1', '2'])}} as NgModel;
const directive = new NgModelChangeDebouncedDirective(ngModel);
directive.stNgModelChangeDebounced.emit = jasmine.createSpy();

directive.ngOnInit();

expect(directive.stNgModelChangeDebounced.emit).toHaveBeenCalledWith('2');
flush();
}));

it('should unsubscribe from ngModel', () => {
const unsubscribe = jasmine.createSpy();
const observable = {subscribe: () => ({unsubscribe})};
const ngModel = {control: {valueChanges: {pipe: () => observable}}} as unknown as NgModel;
const directive = new NgModelChangeDebouncedDirective(ngModel);
directive.ngOnInit();

directive.ngOnDestroy();

expect(unsubscribe).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {Directive, EventEmitter, OnDestroy, OnInit, Output} from '@angular/core';
import {NgModel} from "@angular/forms";
import {Subscription} from "rxjs";
import {debounceTime, distinctUntilChanged, skip} from "rxjs/operators";

const DEBOUNCE_TIME_MS = 100;

@Directive({
selector: '[stNgModelChangeDebounced]',
})
export class NgModelChangeDebouncedDirective implements OnInit, OnDestroy {

// eslint-disable-next-line @typescript-eslint/no-explicit-any
@Output() stNgModelChangeDebounced = new EventEmitter<any>();
private subscription: Subscription;

constructor(private readonly ngModel: NgModel) {}

ngOnInit(): void {
this.subscription = this.ngModel.control.valueChanges.pipe(
skip(1),
distinctUntilChanged(),
debounceTime(DEBOUNCE_TIME_MS)
).subscribe((value) => this.stNgModelChangeDebounced.emit(value));
}

ngOnDestroy(): void {
this.subscription.unsubscribe();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { DomModifyDirective } from './directives/dom-modify.directive';
import { SelectionChangeDirective } from './directives/selection-change.directive';
import { EditorDropdownComponent } from './components/editor-dropdown/editor-dropdown.component';
import {ActionDirective} from "./directives/action.directive";
import {NgModelChangeDebouncedDirective} from "./directives/ng-model-change-debounced.directive";



Expand All @@ -26,6 +27,7 @@ import {ActionDirective} from "./directives/action.directive";
SelectionChangeDirective,
EditorDropdownComponent,
ActionDirective,
NgModelChangeDebouncedDirective,
],
imports: [
CommonModule,
Expand Down

0 comments on commit 14ca5c3

Please # to comment.