12 lines
8.7 KiB
JavaScript
12 lines
8.7 KiB
JavaScript
import{_ as s,c as e,a0 as a,o as t}from"./chunks/framework.P9qPzDnn.js";const D=JSON.parse('{"title":"Mixins and Wrappers","description":"","frontmatter":{},"headers":[],"relativePath":"guide/advanced-concepts/mixins.md","filePath":"guide/advanced-concepts/mixins.md","lastUpdated":1737059031000}'),n={name:"guide/advanced-concepts/mixins.md"};function l(r,i,h,p,k,o){return t(),e("div",null,i[0]||(i[0]=[a(`<h1 id="mixins-and-wrappers" tabindex="-1">Mixins and Wrappers <a class="header-anchor" href="#mixins-and-wrappers" aria-label="Permalink to "Mixins and Wrappers""></a></h1><p>Mixins and wrappers are ways of adding functionality to your features in a modular way, allowing them to be shared with the community or reused between projects with ease. There's already a couple built into the engine, including one that every renderable feature uses, <code>vueFeatureMixin</code>.</p><h2 id="mixins" tabindex="-1">Mixins <a class="header-anchor" href="#mixins" aria-label="Permalink to "Mixins""></a></h2><p>Mixins are for adding additional properties to the feature, and are used by the one writing the feature itself (rather than just instantiating it). For example, <code>vueFeatureMixin</code> takes a couple parameters - a string for identifying features, an options object that can contain settings like <code>visibility</code>, <code>style</code>, <code>classes</code>, etc. - and a render function - and adds various properties required for rendering the feature. The mixin gets implemented by having the feature's options object extending the mixin's options, the feature interface extending the mixin itself, and destructuring the mixin's return object in the constructor itself:</p><div class="language-ts vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ts</span><pre class="shiki shiki-themes material-theme-palenight material-theme-palenight vp-code" tabindex="0"><code><span class="line"><span style="--shiki-light:#C792EA;--shiki-dark:#C792EA;">const</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> clickable </span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">=</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> {</span></span>
|
||
<span class="line"><span style="--shiki-light:#F07178;--shiki-dark:#F07178;"> type</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">:</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> ClickableType</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> ...</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">(props </span><span style="--shiki-light:#89DDFF;--shiki-light-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic;">as</span><span style="--shiki-light:#FFCB6B;--shiki-dark:#FFCB6B;"> Omit</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"><typeof</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> props</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> keyof</span><span style="--shiki-light:#FFCB6B;--shiki-dark:#FFCB6B;"> VueFeature</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> |</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> keyof</span><span style="--shiki-light:#FFCB6B;--shiki-dark:#FFCB6B;"> ClickableOptions</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">></span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">)</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> ...</span><span style="--shiki-light:#82AAFF;--shiki-dark:#82AAFF;">vueFeatureMixin</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">(</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">"</span><span style="--shiki-light:#C3E88D;--shiki-dark:#C3E88D;">clickable</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">"</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> options</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> ()</span><span style="--shiki-light:#C792EA;--shiki-dark:#C792EA;"> =></span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> (</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> <</span><span style="--shiki-light:#BABED8;--shiki-light-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic;">Clickable</span></span>
|
||
<span class="line"><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> canClick</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">={</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">clickable.canClick</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">}</span></span>
|
||
<span class="line"><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> onClick</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">={</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">clickable.onClick</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">}</span></span>
|
||
<span class="line"><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> display</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">={</span><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;">clickable.display</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">}</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> /></span></span>
|
||
<span class="line"><span style="--shiki-light:#BABED8;--shiki-dark:#BABED8;"> ))</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">,</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;"> ...</span></span>
|
||
<span class="line"><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">}</span><span style="--shiki-light:#89DDFF;--shiki-light-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic;"> satisfies</span><span style="--shiki-light:#FFCB6B;--shiki-dark:#FFCB6B;"> Clickable</span><span style="--shiki-light:#89DDFF;--shiki-dark:#89DDFF;">;</span></span></code></pre></div><p>You'll note the properties included by the mixin also get omitted from the props object, since they'll be overwritten. In the end, you'll know its setup correctly because the <code>satisfies</code> clause won't cause issues (assuming you've correctly made <code>Clickable</code> extend <code>VueFeature</code> or whatever the name of the feature and mixin are).</p><p>Custom mixins should work similarly to the existing mixins and support everything mentioned above. The bonus amounts/completions mixins are good examples of what a simpler mixin would look like.</p><p>If there's a feature you'd like to write that could work as either a mixin or wrapper, prefer mixins due to their better typing. If the person using the mixin or wrapper wasn't creating a new feature, they can easily extend the feature to use the mixin like I demonstrate <a href="https://forums.moddingtree.com/t/using-the-bonus-mixins-on-repeatables-and-challenges/1648" target="_blank" rel="noreferrer">here</a>.</p><h2 id="wrappers" tabindex="-1">Wrappers <a class="header-anchor" href="#wrappers" aria-label="Permalink to "Wrappers""></a></h2><p>Wrappers have the advantage of not requiring extending the feature and being able to access pre-defined properties on the feature, but make types a bit more tricky to work with.</p><p>Wrappers take a constructed feature and modify or add properties on it. For example, <code>addTooltip</code> will take a vue feature and make it have a tooltip on hover. It and all wrappers should take the feature as the first param and the options func as the second.</p><p>Similar to a feature, the wrapper should make a lazy proxy with all its properties. But, you can't add this lazy object to the feature directly, because that would force its evaluation. Instead, you should use <a href="/api/util/proxies/functions/runAfterEvaluation">runAfterEvaluation</a> and inside its callback add the wrapper object to the feature and make sure the wrapper object gets evaluated by referencing a property within it. If it is a wrapper around a vue component, you can also add the wrapper element to the feature's <code>wrappers</code> array inside the callback.</p>`,12)]))}const F=s(n,[["render",l]]);export{D as __pageData,F as default};
|