From 8f90f0d2f12d4ac4125705ac73a29e9cecb865bb Mon Sep 17 00:00:00 2001 From: thepaperpilot Date: Thu, 26 Dec 2024 16:46:02 -0600 Subject: [PATCH] Missing files --- docs/guide/advanced-concepts/mixins.md | 36 ++++++++ docs/guide/migrations/0-7.md | 120 +++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 docs/guide/advanced-concepts/mixins.md create mode 100644 docs/guide/migrations/0-7.md diff --git a/docs/guide/advanced-concepts/mixins.md b/docs/guide/advanced-concepts/mixins.md new file mode 100644 index 00000000..3c5242a7 --- /dev/null +++ b/docs/guide/advanced-concepts/mixins.md @@ -0,0 +1,36 @@ +# Mixins and Wrappers + +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, `vueFeatureMixin`. + +## Mixins + +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, `vueFeatureMixin` takes a couple parameters - a string for identifying features, an options object that can contain settings like `visibility`, `style`, `classes`, 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: + +```ts +const clickable = { + type: ClickableType, + ...(props as Omit), + ...vueFeatureMixin("clickable", options, () => ( + + )), + ... +} satisfies Clickable; +``` + +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 `satisfies` clause won't cause issues (assuming you've correctly made `Clickable` extend `VueFeature` or whatever the name of the feature and mixin are). + +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. + +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 [here](https://forums.moddingtree.com/t/using-the-bonus-mixins-on-repeatables-and-challenges/1648). + +## Wrappers + +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. + +Wrappers take a constructed feature and modify or add properties on it. For example, `addTooltip` 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. + +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 [runAfterEvaluation](../../api/modules/util/proxies#runAfterEvaluation) 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 `wrappers` array inside the callback. diff --git a/docs/guide/migrations/0-7.md b/docs/guide/migrations/0-7.md new file mode 100644 index 00000000..a1dee939 --- /dev/null +++ b/docs/guide/migrations/0-7.md @@ -0,0 +1,120 @@ +# Migrating to Profectus 0.7 + +This update involved the feature rewrite and board rewrite, the largest refactors in Profectus' history at time of release. Here's how you can handle these changes. + +## Boards + +If you have an existing board, there's no real way to migrate to the new board system. You'll likely want to either re-create it within the new system (see how to [here](../advanced-concepts/boards)) or use the legacy board implementation available here. + +## Grids + +Grids have been removed, and you'll need to either use Row and Column directly with whatever component you like, or use the legacy grid implementation available [here](https://forums.moddingtree.com/t/profectus-grid-feature/1651). + +## Generic Feature Types + +Feature types no longer take the options as a type parameter, and are therefore generic by default. Any uses of the previous types (the generic type or passing a type parameter) will need to be updated. The typing of a feature should include any custom properties you add to the options object. + +## Displays + +Displays are a bit different. There is no `jsx` function nor `JSXFunction` types anymore. Instead, you'll just use a function directly, or just a string if its purely static. Any feature that would previously except a function or ref returning an object of individual pieces to display (e.g. description, title, etc.) will now take the object directly, and allow each individual piece to be a function or ref. + +## Tooltips and Marks + +Tooltips and marks now take options funcs like other features. Additionally, they're now considered "wrappers" so the import statement will need adjusting. + +## `this` in options funcs + +As a general rule, you should not use `this` inside of the functions that return the options object for a feature. In some cases it'll still work, but often will not. The typing of the features should allow you to reference the feature by name without causing cyclical dependencies now, though. So, for example, this code is perfectly fine: + +```ts +const myMilestone = createAchievement(() => ({ + ..., + style() { + if (myMilestone.earned.value !== false) { + return { "--layer-color": "#1111DD" }; + } + return {}; + } +})); +``` + +## Decorators + +The decorators system is no longer necessary with the rewrite, so features do not support them anymore. The bonus amount/completions decorators have been converted to mixins, so you can still use them. + +## Styling + +`style` properties should now always be in the form of an object, not a string. This allows them to more easily override individual properties. Additionally, vue features are structured a little bit differently in the DOM, so you may need to update any custom CSS you wrote. + +## Custom features + +The feature rewrite, obviously, most significantly impacts any custom features you've written. The general process for updating a feature went like this: + +### Update types + +The options object should now extend `VueFeatureOptions`, and every Computable should be replaced with MaybeRefOrGetter. You'll no longer need the `visibility`, `style` and `classes` properties. If you had a `mark` property that can also be removed. If you have a custom display object, make the properties within it `MaybeGetter`, and don't wrap the object itself in anything (neither `Computable` nor `MaybeRefOrGetter`). Otherwise, you can just make it take a `MaybeGetter` directly. + +The `Base` variant of your feature is going to become the new interface for the feature itself. You'll make it extend `VueFeature` and remove the `id`, `[Component]`, and `[GatherProps]` properties. Since we're not relying on any utility functions here, you'll need to add the `MaybeRef` version of every `MaybeRefOrGetter` property in the options object, and copy over any other properties that just get passed through. You won't handle default values for the properties since this is a generic interface, but you can mark those properties as required since they'll always be present. You can then delete the previous type for the feature itself and the `Generic` variant of it. + +### Update constructor + +First, make the options func simply typed as `() => T`, and remove the return type annotation. You'll also remove the `feature` parameter from the function passed to `createLazyProxy`. Instead of creating the feature object immediately, we'll just destructure the options, and create the feature object later. For example, here's the first section of `createInfobox`'s proxy function: + +```ts +const options = optionsFunc(); +const { color, titleStyle, bodyStyle, title, display, ...props } = options; +``` + +We can then construct the feature object, passing in `type`, adding the extra props and vue feature properties, and handling processing getters and default values. Here's the end of the `createInfobox` proxy function: + +```ts +const infobox = { + type: InfoboxType, + ...(props as Omit), + ...vueFeatureMixin("infobox", options, () => ( + + )), + collapsed, + color: processGetter(color) ?? "--layer-color", + titleStyle: processGetter(titleStyle), + bodyStyle: processGetter(bodyStyle), + title, + display +} satisfies Infobox; + +return infobox; +``` + +For more complex use cases, I'd check out the other constructors of existing features. You can also ask the [discord](https://discord.gg/yJ4fjnjU54) if you're having trouble. + +If you'd added support for the previous decorators system, you'll remove all of that code from the feature as its no longer necessary with the feature rewrite. + +### Update vue component + +All the vue components in Profectus now use `script setup` and the composition API, and I recommend you use it as well. The props should just link to the individual properties in the class for maintainability (but you can'y just do `defineProps()` because of the limitations on the vue compiler). Here's an example for Infobox: + +```ts +const props = defineProps<{ + color: Infobox["color"]; + titleStyle: Infobox["titleStyle"]; + bodyStyle: Infobox["bodyStyle"]; + collapsed: Infobox["collapsed"]; + display: Infobox["display"]; + title: Infobox["title"]; +}>(); +``` + +For rendering a `Renderable`, it should just be defining a PascalCase variable that's just a function passing the renderable into `render`, like so: + +`const Title = () => render(props.title);` + +You can then just add `` in the template. + +Finally, make sure to remove the parts that are now handle by the vue feature mixin. Specifically, remove the `Node` component, the `Mark` component if present, and the handling for `visibility`, `style`, and `classes` props.