Skip to content

Instantly share code, notes, and snippets.

@Eunomiac
Last active November 16, 2022 13:23
Show Gist options
  • Save Eunomiac/481615cd7500470997711ffa8d23eb27 to your computer and use it in GitHub Desktop.
Save Eunomiac/481615cd7500470997711ffa8d23eb27 to your computer and use it in GitHub Desktop.
A fully modular method of applying seamless Mixins to Javascript classes, intended for Foundry VTT development.
/* MIXINS GUIDE
A "mixin" is a block of code that can be injected into any class, without regard to the standard chain of inheritance.
This allows code to be written once, as a mixin, and then applied to any number of classes regardless of whether they
inherit from each other.
In the context of Foundry, this is especially helpful for code that is shared between the Actor and Item classes (or
subclasses thereof), which would normally have to be duplicated within each.
It also provides a new means of sharing code, since integrating a mixin into an existing class is seamless, requiring
no modification of the inheritance structure, and affecting no code other than that explicitly contained within the
mixin itself.
Mixins can do everything a standard subclass can do: override methods, introduce new methods (including new static methods),
and tap into the receiving class' properties and methods. The context remains that of the receiving class: 'this' and 'super'
written in a mixin will refer to the instance and superclass, respectively, of that class.
USAGE: DEFINING MIXINS
======================
Define a mixin by wrapping the following code around the methods, properties, etc. you want to inject. Your code should
be written as if the mixin were a subclass of any classes you'll be applying it to.
const MyCloseButtonMixin = (superclass) => class extends superclass {
// @override
activateListeners(html) {
super.activateListeners(html);
// Add close button functionality to id'd elements
html.find("#closeButton").on({click: () => this.close()});
}
}
USAGE: SUBCLASSING MIXINS
=========================
Mixins can subclass from other mixins just as with a normal class, though again with a special wrapper:
export const MySoftCloseSubMixin = (superclass) => class extends MyCloseButtonMixin(superclass) {
// @override
activateListeners(html) {
super.activateListeners(html);
// Replace that close button with a minimize button.
html.find("#closeButton")
.off("click")
.on({click: () => this.minimize()});
}
};
USAGE: APPLYING MIXINS TO CLASSES
=================================
The "MIX" class factory, exported from this module, makes applying multiple mixins to a receiving class
syntactically simple:
import MIX, {MySoftCloseSubMixin} from "mixins.js";
class MyActorSheet extends MIX(ActorSheet).with(MySoftCloseSubMixin) { ... }
class MyItemSheet extends MIX(ItemSheet).with(MySoftCloseSubMixin) { ... }
NOTES
==================
When writing mixins that will be applied to Item and Actor subclasses, use "this.doc" instead of "this.actor" or "this.item"
to retrieve the corresponding objects.
*/
// ===== "MIX" CLASS FACTORY =====
class MixinBuilder {
constructor(superclass) { this.superclass = superclass }
with(...mixins) { return mixins.reduce((cls, mixin = (x) => x) => mixin(cls), this.superclass) }
}
const MIX = (superclass) => new MixinBuilder(superclass);
export default MIX;
// ===== MIXINS =====
export const CloseButton = (superclass) => class extends superclass {
activateListeners(html) {
super.activateListeners(html);
html.find("#closeButton").click(() => this.close());
}
};
// (Note you can define mixins anywhere: No imports needed to define a mixin.)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment