mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-19 19:16:29 +08:00
feat(editor): replace slot with rxjs subject (#10768)
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { Slot } from '../slot/slot.js';
|
||||
|
||||
describe('slot', () => {
|
||||
test('init', () => {
|
||||
const slot = new Slot();
|
||||
expect(slot).is.toBeDefined();
|
||||
});
|
||||
|
||||
test('emit', () => {
|
||||
const slot = new Slot<void>();
|
||||
const callback = vi.fn();
|
||||
slot.on(callback);
|
||||
slot.emit();
|
||||
expect(callback).toBeCalled();
|
||||
});
|
||||
|
||||
test('emit with value', () => {
|
||||
const slot = new Slot<number>();
|
||||
const callback = vi.fn(v => expect(v).toBe(5));
|
||||
slot.on(callback);
|
||||
slot.emit(5);
|
||||
expect(callback).toBeCalled();
|
||||
});
|
||||
|
||||
test('listen once', () => {
|
||||
const slot = new Slot<number>();
|
||||
const callback = vi.fn(v => expect(v).toBe(5));
|
||||
slot.once(callback);
|
||||
slot.emit(5);
|
||||
slot.emit(6);
|
||||
expect(callback).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test('listen once with dispose', () => {
|
||||
const slot = new Slot<void>();
|
||||
const callback = vi.fn(() => {
|
||||
throw new Error('');
|
||||
});
|
||||
const disposable = slot.once(callback);
|
||||
disposable.dispose();
|
||||
slot.emit();
|
||||
expect(callback).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('cycle emit', () => {
|
||||
const slot = new Slot<number>();
|
||||
const callback = vi.fn(v => slot.emit(v + 1));
|
||||
slot.on(callback);
|
||||
slot.emit(0);
|
||||
expect(callback).toBeCalledTimes(1);
|
||||
expect(callback).toBeCalledWith(0);
|
||||
});
|
||||
});
|
||||
+29
-23
@@ -1,15 +1,19 @@
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
|
||||
type DisposeCallback = () => void;
|
||||
|
||||
export interface Disposable {
|
||||
dispose: DisposeCallback;
|
||||
}
|
||||
|
||||
export interface DisposableManager extends Disposable {
|
||||
add(d: Disposable | DisposeCallback): void;
|
||||
}
|
||||
export type DisposableMember =
|
||||
| Disposable
|
||||
| Subscription
|
||||
| Subject<any>
|
||||
| DisposeCallback;
|
||||
|
||||
export class DisposableGroup implements DisposableManager {
|
||||
private _disposables: Disposable[] = [];
|
||||
export class DisposableGroup {
|
||||
private _disposables: DisposableMember[] = [];
|
||||
|
||||
private _disposed = false;
|
||||
|
||||
@@ -21,14 +25,12 @@ export class DisposableGroup implements DisposableManager {
|
||||
* Add to group to be disposed with others.
|
||||
* This will be immediately disposed if this group has already been disposed.
|
||||
*/
|
||||
add(d: Disposable | DisposeCallback) {
|
||||
if (typeof d === 'function') {
|
||||
if (this._disposed) d();
|
||||
else this._disposables.push({ dispose: d });
|
||||
} else {
|
||||
if (this._disposed) d.dispose();
|
||||
else this._disposables.push(d);
|
||||
add(d: DisposableMember) {
|
||||
if (this._disposed) {
|
||||
disposeMember(d);
|
||||
return;
|
||||
}
|
||||
this._disposables.push(d);
|
||||
}
|
||||
|
||||
addFromEvent<N extends keyof WindowEventMap>(
|
||||
@@ -83,18 +85,22 @@ export class DisposableGroup implements DisposableManager {
|
||||
}
|
||||
}
|
||||
|
||||
export function flattenDisposables(disposables: Disposable[]): Disposable {
|
||||
return {
|
||||
dispose: () => disposeAll(disposables),
|
||||
};
|
||||
}
|
||||
|
||||
function disposeAll(disposables: Disposable[]) {
|
||||
for (const disposable of disposables) {
|
||||
try {
|
||||
export function disposeMember(disposable: DisposableMember) {
|
||||
try {
|
||||
if (disposable instanceof Subscription) {
|
||||
disposable.unsubscribe();
|
||||
} else if (disposable instanceof Subject) {
|
||||
disposable.complete();
|
||||
} else if (typeof disposable === 'function') {
|
||||
disposable();
|
||||
} else {
|
||||
disposable.dispose();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function disposeAll(disposables: DisposableMember[]) {
|
||||
disposables.forEach(disposeMember);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { LitElement } from 'lit';
|
||||
|
||||
import { DisposableGroup } from '../slot/disposable.js';
|
||||
import { DisposableGroup } from '../disposable/index.js';
|
||||
import type { Constructor } from '../utils/types.js';
|
||||
|
||||
// See https://lit.dev/docs/composition/mixins/#mixins-in-typescript
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './disposable.js';
|
||||
export * from './slot.js';
|
||||
@@ -1,86 +0,0 @@
|
||||
import { type Disposable, flattenDisposables } from './disposable.js';
|
||||
|
||||
export class Slot<T = void> implements Disposable {
|
||||
private _callbacks: ((v: T) => unknown)[] = [];
|
||||
|
||||
private _disposables: Disposable[] = [];
|
||||
|
||||
private _emitting = false;
|
||||
|
||||
dispose() {
|
||||
flattenDisposables(this._disposables).dispose();
|
||||
this._callbacks = [];
|
||||
this._disposables = [];
|
||||
}
|
||||
|
||||
emit(v: T) {
|
||||
// Prevent recursive emit calls
|
||||
if (this._emitting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prevEmitting = this._emitting;
|
||||
this._emitting = true;
|
||||
try {
|
||||
this._callbacks.forEach(f => {
|
||||
try {
|
||||
f(v);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
this._emitting = prevEmitting;
|
||||
}
|
||||
}
|
||||
|
||||
on(callback: (v: T) => unknown): Disposable {
|
||||
if (this._emitting) {
|
||||
const newCallback = [...this._callbacks, callback];
|
||||
this._callbacks = newCallback;
|
||||
} else {
|
||||
this._callbacks.push(callback);
|
||||
}
|
||||
return {
|
||||
dispose: () => {
|
||||
if (this._emitting) {
|
||||
this._callbacks = this._callbacks.filter(v => v !== callback);
|
||||
} else {
|
||||
const index = this._callbacks.indexOf(callback);
|
||||
if (index > -1) {
|
||||
this._callbacks.splice(index, 1); // remove one item only
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
once(callback: (v: T) => unknown): Disposable {
|
||||
let dispose: Disposable['dispose'] | undefined = undefined;
|
||||
const handler = (v: T) => {
|
||||
callback(v);
|
||||
if (dispose) {
|
||||
dispose();
|
||||
}
|
||||
};
|
||||
const disposable = this.on(handler);
|
||||
dispose = disposable.dispose;
|
||||
return disposable;
|
||||
}
|
||||
|
||||
filter(testFun: (v: T) => boolean): Slot<T> {
|
||||
const result = new Slot<T>();
|
||||
// if the original slot is disposed, dispose the filtered one
|
||||
this._disposables.push({
|
||||
dispose: () => result.dispose(),
|
||||
});
|
||||
|
||||
this.on((v: T) => {
|
||||
if (testFun(v)) {
|
||||
result.emit(v);
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user