aspen-decorations
Version:
Complex styling for react-aspen w/ inheritance and negations
141 lines (117 loc) • 5.41 kB
text/typescript
import { Directory, FileEntry } from 'aspen-core'
import { IDisposable, Notificar } from 'notificar'
import { DecorationEvent, TargetMatchMode } from './types'
export class Decoration {
/**
* Do not mutate directly, use `Decoration#addCSSClass()` / `Decoration#removeCSSClass` instead
*/
public readonly cssClasslist: Set<string>
private _appliedTargetsDisposables: WeakMap<FileEntry | Directory, IDisposable> = new WeakMap()
private _negatedTargetsDisposables: WeakMap<FileEntry | Directory, IDisposable> = new WeakMap()
private _appliedTargets: Map<FileEntry | Directory, TargetMatchMode> = new Map()
private _negatedTargets: Map<FileEntry | Directory, TargetMatchMode> = new Map()
private _disabled = false
private events = new Notificar<DecorationEvent>()
constructor()
constructor(...cssClasslist: string[])
constructor(...cssClasslist: string[]) {
if (Array.isArray(cssClasslist)) {
if (cssClasslist.every((classname) => typeof classname === 'string')) {
this.cssClasslist = new Set(cssClasslist)
} else {
throw new TypeError('classlist must be of type `Array<string>`')
}
} else {
this.cssClasslist = new Set()
}
}
get disabled() {
return this._disabled
}
set disabled(disabled: boolean) {
this._disabled = disabled
this.events.dispatch(disabled ? DecorationEvent.DecorationDisabled : DecorationEvent.DecorationEnabled, this)
}
get appliedTargets() {
return this._appliedTargets
}
get negatedTargets() {
return this._negatedTargets
}
public addCSSClass(classname: string): void {
if (this.cssClasslist.has(classname)) { return }
this.cssClasslist.add(classname)
this.events.dispatch(DecorationEvent.AddCSSClassname, this, classname)
}
public removeCSSClass(classname: string): void {
if (!this.cssClasslist.has(classname)) { return }
this.cssClasslist.delete(classname)
this.events.dispatch(DecorationEvent.RemoveCSSClassname, this, classname)
}
public addTarget(directory: Directory, flags: TargetMatchMode): void
public addTarget(file: FileEntry): void
public addTarget(target: FileEntry | Directory, flags: TargetMatchMode = TargetMatchMode.Self): void {
const existingFlags = this._appliedTargets.get(target)
if (existingFlags === flags) { return }
if (!(target instanceof FileEntry)) { return }
this._appliedTargets.set(target, flags)
this.events.dispatch(DecorationEvent.AddTarget, this, target, flags)
this._appliedTargetsDisposables.set(target, target.root.onOnceDisposed(target, () => this.removeTarget(target)))
}
public removeTarget(directory: Directory): void
public removeTarget(file: FileEntry): void
public removeTarget(target: FileEntry | Directory): void {
if (this._appliedTargets.delete(target)) {
const disposable = this._appliedTargetsDisposables.get(target)
if (disposable) {
disposable.dispose()
}
this.events.dispatch(DecorationEvent.RemoveTarget, this, target)
}
}
public negateTarget(directory: Directory, flags: TargetMatchMode): void
public negateTarget(file: FileEntry): void
public negateTarget(target: FileEntry | Directory, flags: TargetMatchMode = TargetMatchMode.Self): void {
const existingFlags = this._negatedTargets.get(target)
if (existingFlags === flags) { return }
if (!(target instanceof FileEntry)) { return }
this._negatedTargets.set(target, flags)
this.events.dispatch(DecorationEvent.NegateTarget, this, target, flags)
this._negatedTargetsDisposables.set(target, target.root.onOnceDisposed(target, () => this.unNegateTarget(target)))
}
public unNegateTarget(directory: Directory): void
public unNegateTarget(file: FileEntry): void
public unNegateTarget(target: FileEntry | Directory): void {
if (this._negatedTargets.delete(target)) {
const disposable = this._negatedTargetsDisposables.get(target)
if (disposable) {
disposable.dispose()
}
this.events.dispatch(DecorationEvent.UnNegateTarget, this, target)
}
}
public onDidAddTarget(callback: (decoration: Decoration, target: FileEntry | Directory, flags: TargetMatchMode) => void): IDisposable {
return this.events.add(DecorationEvent.AddTarget, callback)
}
public onDidRemoveTarget(callback: (decoration: Decoration, target: FileEntry | Directory) => void): IDisposable {
return this.events.add(DecorationEvent.RemoveTarget, callback)
}
public onDidNegateTarget(callback: (decoration: Decoration, target: FileEntry | Directory, flags: TargetMatchMode) => void): IDisposable {
return this.events.add(DecorationEvent.NegateTarget, callback)
}
public onDidUnNegateTarget(callback: (decoration: Decoration, target: FileEntry | Directory) => void): IDisposable {
return this.events.add(DecorationEvent.UnNegateTarget, callback)
}
public onDidRemoveCSSClassname(callback: (decoration: Decoration, classname: string) => void): IDisposable {
return this.events.add(DecorationEvent.RemoveCSSClassname, callback)
}
public onDidAddCSSClassname(callback: (decoration: Decoration, classname: string) => void): IDisposable {
return this.events.add(DecorationEvent.AddCSSClassname, callback)
}
public onDidEnableDecoration(callback: (decoration: Decoration) => void): IDisposable {
return this.events.add(DecorationEvent.DecorationEnabled, callback)
}
public onDidDisableDecoration(callback: (decoration: Decoration) => void): IDisposable {
return this.events.add(DecorationEvent.DecorationDisabled, callback)
}
}