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) 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.
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:
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<Renderable>
, and don't wrap the object itself in anything (neither Computable
nor MaybeRefOrGetter
). Otherwise, you can just make it take a MaybeGetter<Renderable>
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:
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:
const infobox = {
type: InfoboxType,
...(props as Omit<typeof props, keyof VueFeature | keyof InfoboxOptions>),
...vueFeatureMixin("infobox", options, () => (
<Infobox
color={infobox.color}
titleStyle={infobox.titleStyle}
bodyStyle={infobox.bodyStyle}
collapsed={infobox.collapsed}
title={infobox.title}
display={infobox.display}
/>
)),
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 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<MyFeature>()
because of the limitations on the vue compiler). Here's an example for Infobox:
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 <Title />
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.