UNPKG

aspen-decorations

Version:

Complex styling for react-aspen w/ inheritance and negations

181 lines 7.87 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const aspen_core_1 = require("aspen-core"); const notificar_1 = require("notificar"); const DecorationComposite_1 = require("./DecorationComposite"); /** * NOTES FOR CONTRIBUTORS: * - A Target is a directory or a file entry * - A Target is a DirectTarget when it is explicitly listed in a Decoration's application or negation list * - As opposed to DirectTarget, general targets are implicit targets when they simply inherit from their parent's inhertiable DecorationComposite * - All general targets "point to" their first parent's `inheritable` composite see docs for `IDecorationMeta.inheritable` */ class DecorationsManager { constructor(root) { this.decorations = new Map(); this.decorationsMeta = new WeakMap(); this.disposables = new notificar_1.DisposablesComposite(); this.disposed = false; this.targetDecoration = (decoration, target) => { const { applicable, inheritable } = this.getDecorationData(target); applicable.add(decoration); if (inheritable) { inheritable.add(decoration); } }; this.unTargetDecoration = (decoration, target) => { const { applicable, inheritable } = this.getDecorationData(target); applicable.remove(decoration); if (inheritable) { inheritable.remove(decoration); } }; this.negateDecoration = (decoration, target) => { const { applicable, inheritable } = this.getDecorationData(target); applicable.negate(decoration); if (inheritable) { inheritable.negate(decoration); } }; this.unNegateDecoration = (decoration, target) => { const { applicable, inheritable } = this.getDecorationData(target); applicable.unNegate(decoration); if (inheritable) { inheritable.unNegate(decoration); } }; this.switchParent = (target, prevParent, newParent) => { const ownMeta = this.decorationsMeta.get(target); if (!ownMeta) { return; } const newParentMeta = this.getDecorationData(newParent); ownMeta.applicable.changeParent(newParentMeta.inheritable); if (ownMeta.inheritable) { ownMeta.inheritable.changeParent(newParentMeta.inheritable); } }; // if the object is actually Root, but not of same version this condition will likely be true if (!(root instanceof aspen_core_1.Root)) { throw new TypeError('Unexpected object type. Expected `Root`. Make sure you are using the latest version of `aspen-decorations` to avoid conflicts'); } // this will act as "seed" (base) for rest of the decorations to come this.decorationsMeta.set(root, { applicable: new DecorationComposite_1.DecorationComposite(root, DecorationComposite_1.DecorationCompositeType.Applicable, null), inheritable: new DecorationComposite_1.DecorationComposite(root, DecorationComposite_1.DecorationCompositeType.Inheritable, null), }); this.disposables.add(root.onDidChangeParent(this.switchParent)); this.disposables.add(root.onDidDispose(this.decorationsMeta.delete.bind(this.decorationsMeta))); } /** * Permanently disengages the decoration system from the tree */ dispose() { for (const [decoration] of this.decorations) { this.removeDecoration(decoration); } this.disposables.dispose(); this.disposed = true; } /** * Adds the given `Decoration` to the tree * * `Decoration`s have no effect unless they are targetted at item(s). Use `Decoration#addTarget` to have them render in the filetree */ addDecoration(decoration) { if (this.disposed) { throw new Error(`DecorationManager disposed`); } if (this.decorations.has(decoration)) { return; } const disposable = new notificar_1.DisposablesComposite(); disposable.add(decoration.onDidAddTarget(this.targetDecoration)); disposable.add(decoration.onDidRemoveTarget(this.unTargetDecoration)); disposable.add(decoration.onDidNegateTarget(this.negateDecoration)); disposable.add(decoration.onDidUnNegateTarget(this.unNegateDecoration)); this.decorations.set(decoration, disposable); for (const [target] of decoration.appliedTargets) { this.targetDecoration(decoration, target); } for (const [target] of decoration.negatedTargets) { this.negateDecoration(decoration, target); } } /** * Removes a `Decoration` from the tree * * Note that this "removes" entire `Decoration` from tree but the said `Decoration`'s targets are still left intact. * * Calling `DecorationManager#addDecoration` with the same `Decoration` will undo the removal if the targets are left unchanged. */ removeDecoration(decoration) { const decorationSubscriptions = this.decorations.get(decoration); if (!decorationSubscriptions) { return; } for (const [target] of decoration.appliedTargets) { const meta = this.decorationsMeta.get(target); if (meta) { meta.applicable.remove(decoration); if (meta.inheritable) { meta.inheritable.remove(decoration); } } } for (const [target] of decoration.negatedTargets) { const meta = this.decorationsMeta.get(target); if (meta) { meta.applicable.unNegate(decoration); if (meta.inheritable) { meta.inheritable.unNegate(decoration); } } } decorationSubscriptions.dispose(); this.decorations.delete(decoration); } /** * Returns resolved decorations for given item * * Resolution includes taking inheritances into consideration, along with any negations that may void some or all of inheritances */ getDecorations(item) { if (!item || (item.type !== aspen_core_1.FileType.File && item.type !== aspen_core_1.FileType.Directory)) { return null; } const decMeta = this.getDecorationData(item); if (decMeta) { return decMeta.applicable.compositeCssClasslist; } return null; } /** * @internal */ getDecorationData(item) { if (this.disposed) { return null; } const meta = this.decorationsMeta.get(item); if (meta) { return meta; } // if we make it here that means the item was not a DirectTarget and will simply point to parent's `inheritable` composite (unless is negated) if (!item || !item.parent) { return null; } const parentMeta = this.getDecorationData(item.parent); if (parentMeta) { const ownMeta = { applicable: new DecorationComposite_1.DecorationComposite(item, DecorationComposite_1.DecorationCompositeType.Applicable, parentMeta.inheritable), inheritable: item.type === aspen_core_1.FileType.Directory ? new DecorationComposite_1.DecorationComposite(item, DecorationComposite_1.DecorationCompositeType.Inheritable, parentMeta.inheritable) : null, }; this.decorationsMeta.set(item, ownMeta); return ownMeta; } return null; } } exports.DecorationsManager = DecorationsManager; //# sourceMappingURL=DecorationManager.js.map