O
OneJS•3mo ago
amorphous

Migrating from v1 -> v2

Hi there! I have a pretty big project that is in OneJS v1, and you might've seen my posts recently about various issues I've been grappling with it, as well as some perf concerns. I'd like to see if I can migrate it to v2, but that's been a pretty thorny task that has come with its own set of problems. So I'm creating this forum thread to both ask for help along the way, and also document exactly what has changed for me. Some context from me: * Even with v1, I was using a custom esbuild setup to create a minified JS file to use * I use hookstate as my main state management system. This requires a full preact-compat, so I have a custom version vendored in. So far, in attempting to work with v2, I have 1. interop between my existing C# and TS seem fine, and I appreciate the new generated typedefs 2. DOTween also seems to include fine without errors. 3. It builds Nothing shows up ingame at the moment though! There's still a whole slew of errors to work through first.
57 Replies
amorphous
amorphousOP•3mo ago
Open Questions So Far 1. OneJS's preact (or its types?) seems to be incomplete compared to what we had in v1. The most prominent is the import { Style } from "preact/jsx" definition, which was a part of Scriptlib. Is that available in v2? (Other missing definitions include Fragment and a lot of the pieces of JSXInternal. 2. Do the onejs/comps components still exist? (I can probably vendor them in if needed, but was curious) 3. Typescript complains if we reach into a C# class and use nested variables or functions; is there something I'm missing there? I generated the type defs from the ScriptEngine object. 4. I asked 5 months ago (?!) about how to handle actions, and got https://discord.com/channels/971959898655051806/971963260704260146/1231847737620697088 as an answer. Is this still the case?
Singtaa
Singtaa•3mo ago
1. The def for Fragment was rencently added. But yeah, to get full npm and esbuild workflow working, I had to manually pull in and check every single declaration for onejs-preact and had to leave out some typings that had conflicts at the time. Feel free to PR https://github.com/DragonGround/onejs-preact to get missing typings in. 2. It's been on my mind, but I haven't gotten around to these yet for V2. 3. Nested classes? Those can be tricky when translating them from C# to TS. Half of time, they'll require some manual adjustment, especially when combined with explicit interfaces. 4. Reading over your code again, I think you might have something else that's triggering the delegate bridge error. Do you still get the error if your event is Action<float> instead of Action<bool>? Because it just seem weird that Action<float> works fine and Action<bool> doesn't. The former is being used in both the Ul Meter tutorial and the fortnite UI sample. I didn't need to explicitly declare UsingAction<float> for these. oh for Style, you can use Partial<CS.OneJS.Dom.DomStyle> in most cases I will start migrating the headless comps from v1 to v2 https://github.com/Singtaa/onejs-comps Just a slider right now (needed that one for something Yesterday)
amorphous
amorphousOP•3mo ago
Is there an equivalent for onEngineReload for v2? I used that a lot to remove C# bindings in v1 and it's saying it doesn't exist now. In v1, you could use p, h1, etc. tags that don't seem to exist in v2. Those can easily be substituted with label or textElement. More importantly, I used <span> tags in v1 -- how would I go about recreating those? Are there any docs regarding how to handle Lists, Arrays, and Dictionaries in v2? In v1, I think things generally auto-converted to JS objects, and I was able to iterate through, say, arrays and dicts using for (... of ...) loops. Now Typescript is producing errors when I do that.
Singtaa
Singtaa•3mo ago
Yes, you can use onejs.subscribe (which is also in V1; I need to document this). See here for more info: https://discord.com/channels/971959898655051806/971963260704260146/1253911013128929311 technically, they also don't exist in V1, and will default to plain VisualElement. I think you are only experiencing typing issues right? You can just augment it like this:
declare global {
namespace JSX {
interface IntrinsicElements {
p: any
span: any
}
}
}
declare global {
namespace JSX {
interface IntrinsicElements {
p: any
span: any
}
}
}
amorphous
amorphousOP•3mo ago
One more question! How do we generate Unity types so that they have their fields populated? My vector3 typings look like
/** Representation of 3D vectors and points.*/
class Vector3 extends System.ValueType implements System.IEquatable$1<UnityEngine.Vector3>, System.IFormattable
{
protected [__keep_incompatibility]: never;
}
/** Representation of 3D vectors and points.*/
class Vector3 extends System.ValueType implements System.IEquatable$1<UnityEngine.Vector3>, System.IFormattable
{
protected [__keep_incompatibility]: never;
}
so doing something as simple as vector.x produces a type error
Singtaa
Singtaa•3mo ago
Singtaa
Singtaa•3mo ago
Singtaa
Singtaa•3mo ago
You need to include your types explicitly either in DTSGen if you are doing it programmatically, or the DTSGenerator component on ScriptEngine.
amorphous
amorphousOP•3mo ago
Yep, I've been trying to include my types on DTSGenerator, and can successfully do so with my assemblies. However, Vector3 is in the UnityEngine namespace, and when I try to include that, it doesn't seem to populate.
Singtaa
Singtaa•3mo ago
hmm Vector3 is in the UnityEngine.CoreModule assembly, so you need to make sure that's included. But Vector3 is already included by onejs-core, you shouldn't need to generate it again
amorphous
amorphousOP•3mo ago
I guess something is generating an empty Vector3 definition and that's overwriting VSCode's autocomplete from the one in onejs-core
Singtaa
Singtaa•3mo ago
could be. Then you try to blacklist the type, namespace, or assembly in your own generation.
amorphous
amorphousOP•3mo ago
Blacklisting Vector3 does not appear to do anything to type generation 🤔 Looks like checking 'strict namespaces and assemblies' did the trick, though
Singtaa
Singtaa•3mo ago
Cool, yeah I thin kthe former only blacklist from the starting set of types, but doesn't prevent supporting ones from being generated
amorphous
amorphousOP•3mo ago
I think I've squashed most of the issues, one more type error I'm seeing is Property 'PreventDefault' does not exist on type 'NavigationMoveEvent'. I think I saw that there was an upgrade to onejs-core to fix something about events, but this is happening with the latest npm version
Singtaa
Singtaa•3mo ago
I'm not sure if it's because it's obsolete:
[Obsolete("Use StopPropagation and/or FocusController.IgnoreEvent. Before proceeding, make sure you understand the latest changes to UIToolkit event propagation rules by visiting Unity's manual page https://docs.unity3d.com/Manual/UIE-Events-Dispatching.html")]
public void PreventDefault()
{
this.StopPropagation();
this.elementTarget?.focusController?.IgnoreEvent(this);
}
[Obsolete("Use StopPropagation and/or FocusController.IgnoreEvent. Before proceeding, make sure you understand the latest changes to UIToolkit event propagation rules by visiting Unity's manual page https://docs.unity3d.com/Manual/UIE-Events-Dispatching.html")]
public void PreventDefault()
{
this.StopPropagation();
this.elementTarget?.focusController?.IgnoreEvent(this);
}
(just seeing this now)
amorphous
amorphousOP•3mo ago
Uhhh, hm. Which version of UI Toolkit / Unity is that for? PreventDefault is definitely not obsolete in 2022.3, and doesn't look quite like what you shared there either. I'll just add it to the type definition for now.
amorphous
amorphousOP•3mo ago
ok, it now builds in esbuild, but the C# <> JS array thing might be a thornier problem. I have a C# object that I pass into my JS, and in v1 it contained an array that I do JS-array manipulations on with an external library (spread operator...) before calling C# functions on the original object. In v2, that kind of breaks apart. Is there any way to have a C# object have a member-variable array be represented as a JS array in v2, while still being able to call functions from C#? Trying to do something like
function convertArraysInCSharpObject(obj: CSObj) {
return {
lines: toJSArray(obj.lines) //where obj.lines is Array$1
AdvanceLine: obj.AdvanceLine
}
}
function convertArraysInCSharpObject(obj: CSObj) {
return {
lines: toJSArray(obj.lines) //where obj.lines is Array$1
AdvanceLine: obj.AdvanceLine
}
}
caused a Error: c# exception:Non-static method requires a target complaint when AdvanceLine is called from the wrapper object. Ok, worked around that by forking the library and creating JS arrays to use in there 😅 One more error! I'm still having Action issues, though it looks like basic actions are working fine. But I'm still seeing the issue for Error: c# exception:can not find delegate bridge for System.Action<bool, string>, so I'll first see if the answer I got 5 months ago helps with that. Hrm. that didn't seem to have changed the error message. I guess I can also work around this, I probably don't need both a bool and a string. ...or not? Doing that caused me to get Exception: <unknow>:-1: Error: c# exception:can not find delegate bridge for System.Action<bool>, Please use JsEnv.UsingAction() Or JsEnv.UsingFunc() following the FAQ. issues from somewhere else
Singtaa
Singtaa•3mo ago
you can see if adding _jsEnv.UsingAction<bool, string>() directly in ScriptEngine's Init() works. do it right after _jsEnv = new JsEnv(); line
amorphous
amorphousOP•3mo ago
Ok, I had to add all the permutations of actions I'm using but that error has now gone away. There are no more errors logging in console, but no UI is showing up.
Singtaa
Singtaa•3mo ago
So adding them with OnReload had no effect? Only worked inside the Init() method?
amorphous
amorphousOP•3mo ago
Yes, they worked in Init() OnReload didn't seem to help, unfortunately. (well, 'worked'. I'm not sure why nothing is being drawn.)
Singtaa
Singtaa•3mo ago
Okay I added a PreInit event that you can use for this. But I've no idea about your UI situation. probably need some code
amorphous
amorphousOP•3mo ago
Have you seen this warning before? hello world worked and when I live reloaded to my UI I got this:
unhandledRejection TypeError: parentDom.contains is not a function
at insert (chunk:1085:18)
at diffChildren (chunk:952:18)
at diffElementNodes (chunk:1575:9)
at diff (chunk:1451:25)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11) TypeError: parentDom.contains is not a function
at insert (chunk:1085:18)
at diffChildren (chunk:952:18)
at diffElementNodes (chunk:1575:9)
at diff (chunk:1451:25)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
System.Reflection.MethodBase:Invoke (object,object[])
Puerts.OverloadReflectionWrap:Invoke (Puerts.JSCallInfo) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Wrapper/ReflectionWrap.cs:319)
Puerts.MethodReflectionWrap:Invoke (intptr,intptr,intptr,int) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Wrapper/ReflectionWrap.cs:414)
Puerts.JsEnv:InvokeCallback (intptr,int,intptr,intptr,int) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JsEnv.cs:411)
Puerts.StaticCallbacks:JsEnvCallbackWrap (intptr,intptr,intptr,int,long) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Native/StaticCallbacks.cs:23)
Puerts.JsEnv:Tick () (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JsEnv.cs:735)
OneJS.ScriptEngine:Update () (at Assets/Vendor/OneJS/Runtime/Engine/ScriptEngine.cs:68)
unhandledRejection TypeError: parentDom.contains is not a function
at insert (chunk:1085:18)
at diffChildren (chunk:952:18)
at diffElementNodes (chunk:1575:9)
at diff (chunk:1451:25)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11) TypeError: parentDom.contains is not a function
at insert (chunk:1085:18)
at diffChildren (chunk:952:18)
at diffElementNodes (chunk:1575:9)
at diff (chunk:1451:25)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
at diffChildren (chunk:925:7)
at diff (chunk:1411:11)
System.Reflection.MethodBase:Invoke (object,object[])
Puerts.OverloadReflectionWrap:Invoke (Puerts.JSCallInfo) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Wrapper/ReflectionWrap.cs:319)
Puerts.MethodReflectionWrap:Invoke (intptr,intptr,intptr,int) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Wrapper/ReflectionWrap.cs:414)
Puerts.JsEnv:InvokeCallback (intptr,int,intptr,intptr,int) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JsEnv.cs:411)
Puerts.StaticCallbacks:JsEnvCallbackWrap (intptr,intptr,intptr,int,long) (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/Native/StaticCallbacks.cs:23)
Puerts.JsEnv:Tick () (at Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JsEnv.cs:735)
OneJS.ScriptEngine:Update () (at Assets/Vendor/OneJS/Runtime/Engine/ScriptEngine.cs:68)
Singtaa
Singtaa•3mo ago
what's the version on your onejs-preact?
amorphous
amorphousOP•3mo ago
onejs-preact is 0.3.22 onejs-core is 0.3.26
Singtaa
Singtaa•3mo ago
oh okay try updating onejs-core to 0.3.28
amorphous
amorphousOP•3mo ago
the version on the C# side was a little out of date and didn't have contains yet. I'm upgrading that now.
amorphous
amorphousOP•3mo ago
ok, stuck on another error but it is 3AM now so I think I'll pick this up later.
Exception: undefined:0: Error: c# exception:@outputs/esbuild/app.js:1254: TypeError: Cannot read properties of undefined (reading '_attached')

TypeError: Cannot read properties of undefined (reading '_attached')
at _DomWrapper.<anonymous> (@outputs/esbuild/app.js:1254:41)
at _DomWrapper.focus (@outputs/esbuild/app.js:145:21)
at Object._value (@outputs/esbuild/app.js:9426:27)
at invokeEffect (@outputs/esbuild/app.js:2107:26)
at Array.forEach (<anonymous>)
at Object.flushAfterPaintEffects [as handler] (@outputs/esbuild/app.js:2069:43)
at timerUpdate (puerts\timer.mjs:110:22),stack: at Puerts.GenericDelegate.Action[T1] (T1 p1) [0x00057] in /Users/amorphous/sanctuary/scratchpad/Desert-Angels/Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JSType/GenericDelegate.cs:472
Exception: undefined:0: Error: c# exception:@outputs/esbuild/app.js:1254: TypeError: Cannot read properties of undefined (reading '_attached')

TypeError: Cannot read properties of undefined (reading '_attached')
at _DomWrapper.<anonymous> (@outputs/esbuild/app.js:1254:41)
at _DomWrapper.focus (@outputs/esbuild/app.js:145:21)
at Object._value (@outputs/esbuild/app.js:9426:27)
at invokeEffect (@outputs/esbuild/app.js:2107:26)
at Array.forEach (<anonymous>)
at Object.flushAfterPaintEffects [as handler] (@outputs/esbuild/app.js:2069:43)
at timerUpdate (puerts\timer.mjs:110:22),stack: at Puerts.GenericDelegate.Action[T1] (T1 p1) [0x00057] in /Users/amorphous/sanctuary/scratchpad/Desert-Angels/Assets/Vendor/OneJS/Puerts/Runtime/Src/Default/JSType/GenericDelegate.cs:472
It looks like eventHandler is null on this line https://github.com/Singtaa/onejs-preact/blob/dc4c534e1d9305929313141c526021abad906116/diff/props.js#L185 but I haven't dug into why that could happen
GitHub
onejs-preact/diff/props.js at dc4c534e1d9305929313141c526021abad906...
Preact for OneJS. Contribute to Singtaa/onejs-preact development by creating an account on GitHub.
Singtaa
Singtaa•3mo ago
If you install source-map-support from npm and put the following at the start of your entry file, you will have better stacktraces.
require("source-map-support").install({
retrieveSourceMap: (source: string) => {
// @ts-ignore
const fullpath = require('path').join(___workingDir, source + ".map")
if (!require('fs').existsSync(fullpath))
return null
return {
// @ts-ignore
map: require('fs').readFileSync(fullpath, 'utf8')
}
}
})
require("source-map-support").install({
retrieveSourceMap: (source: string) => {
// @ts-ignore
const fullpath = require('path').join(___workingDir, source + ".map")
if (!require('fs').existsSync(fullpath))
return null
return {
// @ts-ignore
map: require('fs').readFileSync(fullpath, 'utf8')
}
}
})
looks like a bug. Do you know which event it's processing at that time?
amorphous
amorphousOP•3mo ago
would this only work with node backend? (esbuild is complaining about platform: node, which I can easily fix)
Singtaa
Singtaa•3mo ago
oh yes, other backends will require a bit more effort. but nodejs will work out-of-the-box
amorphous
amorphousOP•3mo ago
Hmmmmm, no dice on that one. Even with node.js backend, it complains that require is not defined. It looks like we're losing the _attached variable every time I navigate to something. Also, I figured out why nothing was drawing, I think: somehow the UI lost practically all of the styles I was setting, and the root element was being set to height 0. Transitions and fades also caused errors and aren't working. (This is using the built-in style prop for the most part)
Singtaa
Singtaa•3mo ago
See if you can reproduce some of this in a minimal code sample, especially for the "navigation" you were referring to. I'm also west coast, gonna hit the sack soon as well 🙂
amorphous
amorphousOP•3mo ago
Still debugging -- a minimal repro attempt did not have the same problems, so I'm diagnosing that now. I'm thinking it's in one of the following areas, since these are not part of the current minimal: * images - I confirmed resource loading worked with fonts, but none of my images have shown up properly in the actual game * transitions - I used the Transition component extensively from the old onejs/comps, which hasn't been formally ported to v2. * C# Interop - haven't made anything that works with this yet Things that are probably not the issue * Hookstate - this worked in the minimal repro (despite being the bane of my existence in previous debugging sessions) * absolute positioning - worked in minimal repro As an aside, @Singtaa , the PreInit hook right now is not particularly helpful because there isn't an easy way to listen to that hook before Init is called. ScriptEngine OnEnable might run before another component's Awake -- I guess we can play with component initialization order to try to fix that, but for the time being I just have the Action handlers inside of Init.
Singtaa
Singtaa•3mo ago
Maybe I should rename it. It's meant for just setting up the JsEnv after its creation
amorphous
amorphousOP•3mo ago
Slowly getting stuff to show up ingame! The big 'nothing is rendering' error had the same root cause as https://discord.com/channels/971959898655051806/971963260704260146/1278838350748581955 -- nothing much I can do there except patch in styles that I was missing, because this has now been logged as an official Unity crash bug (that still repros all the way to Unity 6). However, a lot of my styles are still missing. I'll start making a running log of the ones that didn't seem to make the transition via the style prop: * backgroundColor as an rgba() string produced pure white all the time. I had to switch it to a Unity Color object. * unitySlice styles (unitySliceTop, unitySliceBottom, etc.) appear to do nothing * transition styles appear to be broken Images are confirmed to be working, but they look wonky because of the unity slice issues @Singtaa I have a minimal repro for some of the issues stated now, which is in the same repository as usual on the latest main: https://github.com/a-morphous/onejs-hookstate-sample Issues that you can see with this one: * unitySlice styles don't populate * Whenever you click or hover on the special button, the _attached error shows up. There's a note in the FadeTransition component where you can uncomment a version of the component that has no transition; doing that will fix the _attached error on hover, but it'll still exist on click. Unity slice issues can be worked around with emo. This worked:
const unitySliceClass = emo`
-unity-slice-left: 30;
-unity-slice-top: 10;
-unity-slice-right: 30;
-unity-slice-bottom: 80;
-unity-slice-scale: 0.5;
`
const unitySliceClass = emo`
-unity-slice-left: 30;
-unity-slice-top: 10;
-unity-slice-right: 30;
-unity-slice-bottom: 80;
-unity-slice-scale: 0.5;
`
The _attached issue is still an issue, and I'm sure I'll find more as I keep porting. It looks like _attached may be an issue any time there is any transition. This is not captured in the minimal repro, but that error also shows up when I play the animation to translate the game title on my title screen too.
Singtaa
Singtaa•3mo ago
1) the colors are now directly parsed with ColorUtility.TryParseHtmlString to conform with the rest of UIElements. In OneJS V1, all the styles were processed on the JS side, so there were more flexibility. In V1, all the style processors were moved to C# for performance reasons. 2) something weird is going on with the slice values. I'll need a closer look.
import { useEffect, useRef } from "preact/hooks"
import { h, render } from "preact"
import { Resources, Texture2D } from "UnityEngine"

const book = Resources.Load("book", puer.$typeof(Texture2D)) as Texture2D

function App() {
const ref = useRef<Element>()

useEffect(() => {
const style = ref.current.style
style.unitySliceTop = 10 // <-- These work
style.unitySliceBottom = 8
style.unitySliceLeft = 15
style.unitySliceRight = 20
}, [])

return <div class="w-full h-full flex-row justify-center items-center">
<div ref={ref} class="w-[256px] h-[256px]" style={{
backgroundImage: book,
unitySliceBottom: 8, // <-- These don't work (somehow)
unitySliceLeft: 15,
unitySliceRight: 20,
unitySliceTop: 10
}}></div>
</div>
}

render(<App />, document.body)
import { useEffect, useRef } from "preact/hooks"
import { h, render } from "preact"
import { Resources, Texture2D } from "UnityEngine"

const book = Resources.Load("book", puer.$typeof(Texture2D)) as Texture2D

function App() {
const ref = useRef<Element>()

useEffect(() => {
const style = ref.current.style
style.unitySliceTop = 10 // <-- These work
style.unitySliceBottom = 8
style.unitySliceLeft = 15
style.unitySliceRight = 20
}, [])

return <div class="w-full h-full flex-row justify-center items-center">
<div ref={ref} class="w-[256px] h-[256px]" style={{
backgroundImage: book,
unitySliceBottom: 8, // <-- These don't work (somehow)
unitySliceLeft: 15,
unitySliceRight: 20,
unitySliceTop: 10
}}></div>
</div>
}

render(<App />, document.body)
3) There were some transition bugs (only affected the style prop. Tailwind and uss worked fine). Fixed in latest commits. Thx for testing all these btw. much appreciated! let me know if you can get the _attached issue reliably reproduced. It does sound like a bug
amorphous
amorphousOP•3mo ago
The _attached issue is reliably reproduced on the example I sent, if you run it and click the button it reproduces. I updated that repository to use v2 and show this issue. I've been able to reproduce it on most transition-y things, but I haven't updated to the latest commits.
amorphous
amorphousOP•3mo ago
There's a perhaps-more-intuitive stack trace of the _attached issue when you comment out the FadeTransition and click on the button:
amorphous
amorphousOP•3mo ago
I also don't think the system in v2 right now likes setTimeout Even more minimal example; this one can be copied and pasted to a fresh project I think
import { useEffect, useRef, useState } from 'onejs-preact/hooks';
import { h } from 'preact';

export const App = () => {
const [y, setY] = useState(0)
useEffect(() => {
setTimeout(() => {
setY(60)
}, 5000)
}, [])
const onPress = useRef(() => {
setY((value) => {
if (value > 0) {
return 0
}
return 60
})
})
const buttonState = {
focus: () => {},
blur: () => {},
hover: () => {},
leave: () => {}
}
return <div style={{
position: "Absolute",
top: 50,
bottom: 50,
left: 50,
right: 50,
}}>
<div style={{
position: "Relative",
transitionProperty: ["opacity", "translate"],
transitionDuration: [0.2, 0.3],
translate: [0, y],
opacity: 1
}}>
<label style={{
fontSize: 32
}} text="Hello World"></label>
</div>
<button
onClick={onPress.current}
onNavigationSubmit={onPress.current}
onFocusIn={buttonState.focus}
onFocusOut={buttonState.blur}
onBlur={buttonState.blur}
onPointerEnter={buttonState.hover}
onPointerLeave={buttonState.leave}
>
<label text="change"></label>
</button>
</div>
}
import { useEffect, useRef, useState } from 'onejs-preact/hooks';
import { h } from 'preact';

export const App = () => {
const [y, setY] = useState(0)
useEffect(() => {
setTimeout(() => {
setY(60)
}, 5000)
}, [])
const onPress = useRef(() => {
setY((value) => {
if (value > 0) {
return 0
}
return 60
})
})
const buttonState = {
focus: () => {},
blur: () => {},
hover: () => {},
leave: () => {}
}
return <div style={{
position: "Absolute",
top: 50,
bottom: 50,
left: 50,
right: 50,
}}>
<div style={{
position: "Relative",
transitionProperty: ["opacity", "translate"],
transitionDuration: [0.2, 0.3],
translate: [0, y],
opacity: 1
}}>
<label style={{
fontSize: 32
}} text="Hello World"></label>
</div>
<button
onClick={onPress.current}
onNavigationSubmit={onPress.current}
onFocusIn={buttonState.focus}
onFocusOut={buttonState.blur}
onBlur={buttonState.blur}
onPointerEnter={buttonState.hover}
onPointerLeave={buttonState.leave}
>
<label text="change"></label>
</button>
</div>
}
Issues this has: * _attached error when interacting with the button (I think it's the event handlers) * transition doesn't work (this is true even when I upgraded OneJS)
Singtaa
Singtaa•3mo ago
Okay I think I found the culprit. There's this code in original preact code:
if (
name.toLowerCase() in dom ||
name === 'onFocusOut' ||
name === 'onFocusIn'
)
name = name.toLowerCase().slice(2);
else name = name.slice(2);
if (
name.toLowerCase() in dom ||
name === 'onFocusOut' ||
name === 'onFocusIn'
)
name = name.toLowerCase().slice(2);
else name = name.slice(2);
I'll just remove this block then. Should be updated in latest onejs-preact
amorphous
amorphousOP•3mo ago
Yep, that fixes it for buttons. There's apparently multiple _attached errors, and I've seen more of them on other components. I'll track down a repro for those. (I still don't see any transitions, do you have any working examples with them?)
Singtaa
Singtaa•3mo ago
sure, here was my test code:
import { emo } from "onejs"
import { Color } from "UnityEngine"
import { Align } from "UnityEngine/UIElements"

const { $ref, $unref, $generic, $promise, $typeof } = puer;

var divs: Element[] = []
var container = document.createElement("div")
container.classname = emo`
width:100%;height:100%;padding:10px;
flex-direction:row; flex-start:center;
justify-content:center;flex-wrap:wrap;
`
container.style.alignItems = Align.Center

for (var i = 0; i < 4; i++) {
var div = document.createElement("div")
div.classname = emo`width:100px;height:100px;margin:10px;justify-content:center;align-items:center;`
div.appendChild(document.createTextNode(i.toString()))
container.appendChild(div)
divs.push(div)
}
document.body.appendChild(container)

divs[0].style.setProperty("backgroundColor", "yellow")
divs[0].style.setProperty("borderRadius", 10)
divs[0].style.setProperty("borderWidth", 2)
divs[0].style.setProperty("borderColor", Color.red)

divs[1].style.setProperty("backgroundColor", Color.cyan)
divs[1].style.setProperty("borderRadius", [10, 20, 30, 40])
divs[1].style.setProperty("borderWidth", 5)
divs[1].style.setProperty("borderColor", "red green")

divs[2].style.setProperty("backgroundColor", "#aabbcc")
divs[2].style.setProperty("borderRadius", "50% 20px")
divs[2].style.setProperty("borderWidth", "5px 10 15 20")
divs[2].style.setProperty("borderColor", [Color.red, Color.green, Color.blue, Color.yellow])
divs[2].style.scale = [0.8, 1.2]

divs[3].style.setProperty("backgroundColor", "white")
divs[3].style.setProperty("borderBottomLeftRadius", 20)
divs[3].style.setProperty("borderBottomWidth", 5)
divs[3].style.setProperty("borderBottomColor", "cyan")
// divs[3].style.rotate = 30
divs[3].style.scale = 0.6

divs[3].classname += " rotate-[0] hover:rotate-[45deg]"
divs[3].style.transitionProperty = "rotate"
divs[3].style.transitionDuration = "2s"
divs[3].style.transitionTimingFunction = "ease-in-out"
import { emo } from "onejs"
import { Color } from "UnityEngine"
import { Align } from "UnityEngine/UIElements"

const { $ref, $unref, $generic, $promise, $typeof } = puer;

var divs: Element[] = []
var container = document.createElement("div")
container.classname = emo`
width:100%;height:100%;padding:10px;
flex-direction:row; flex-start:center;
justify-content:center;flex-wrap:wrap;
`
container.style.alignItems = Align.Center

for (var i = 0; i < 4; i++) {
var div = document.createElement("div")
div.classname = emo`width:100px;height:100px;margin:10px;justify-content:center;align-items:center;`
div.appendChild(document.createTextNode(i.toString()))
container.appendChild(div)
divs.push(div)
}
document.body.appendChild(container)

divs[0].style.setProperty("backgroundColor", "yellow")
divs[0].style.setProperty("borderRadius", 10)
divs[0].style.setProperty("borderWidth", 2)
divs[0].style.setProperty("borderColor", Color.red)

divs[1].style.setProperty("backgroundColor", Color.cyan)
divs[1].style.setProperty("borderRadius", [10, 20, 30, 40])
divs[1].style.setProperty("borderWidth", 5)
divs[1].style.setProperty("borderColor", "red green")

divs[2].style.setProperty("backgroundColor", "#aabbcc")
divs[2].style.setProperty("borderRadius", "50% 20px")
divs[2].style.setProperty("borderWidth", "5px 10 15 20")
divs[2].style.setProperty("borderColor", [Color.red, Color.green, Color.blue, Color.yellow])
divs[2].style.scale = [0.8, 1.2]

divs[3].style.setProperty("backgroundColor", "white")
divs[3].style.setProperty("borderBottomLeftRadius", 20)
divs[3].style.setProperty("borderBottomWidth", 5)
divs[3].style.setProperty("borderBottomColor", "cyan")
// divs[3].style.rotate = 30
divs[3].style.scale = 0.6

divs[3].classname += " rotate-[0] hover:rotate-[45deg]"
divs[3].style.transitionProperty = "rotate"
divs[3].style.transitionDuration = "2s"
divs[3].style.transitionTimingFunction = "ease-in-out"
You can hover over the last element to see the transition
amorphous
amorphousOP•3mo ago
Ok, it seems like another case where the style prop isn't working, but it does work on emo. the minimal example from before does not transition properly, but this does
import { useEffect, useRef, useState } from 'onejs-preact/hooks';
import { h } from 'preact'
import { emo } from 'onejs-core';

export const App = () => {
const [y, setY] = useState(0)
const onPress = useRef(() => {
setY((value) => {
if (value > 0) {
return 0
}
return 60
})
})
const buttonState = useRef({
focus: () => {},
blur: () => {},
hover: () => {},
leave: () => {}
}).current
return <div style={{
position: "Absolute",
top: 50,
bottom: 50,
left: 50,
right: 50,
}}>
<div class={emo`
transition-property: translate;
transition-duration: 1s;
`} style={{
position: "Relative",
translate: [0, y],
opacity: 1
}}>
<label style={{
fontSize: 32
}} text="Hello World"></label>
</div>
<button
onClick={onPress.current}
onNavigationSubmit={onPress.current}
onFocusIn={buttonState.focus}
onFocusOut={buttonState.blur}
onBlur={buttonState.blur}
onPointerEnter={buttonState.hover}
onPointerLeave={buttonState.leave}
>
<label text="change"></label>
</button>
</div>
}
import { useEffect, useRef, useState } from 'onejs-preact/hooks';
import { h } from 'preact'
import { emo } from 'onejs-core';

export const App = () => {
const [y, setY] = useState(0)
const onPress = useRef(() => {
setY((value) => {
if (value > 0) {
return 0
}
return 60
})
})
const buttonState = useRef({
focus: () => {},
blur: () => {},
hover: () => {},
leave: () => {}
}).current
return <div style={{
position: "Absolute",
top: 50,
bottom: 50,
left: 50,
right: 50,
}}>
<div class={emo`
transition-property: translate;
transition-duration: 1s;
`} style={{
position: "Relative",
translate: [0, y],
opacity: 1
}}>
<label style={{
fontSize: 32
}} text="Hello World"></label>
</div>
<button
onClick={onPress.current}
onNavigationSubmit={onPress.current}
onFocusIn={buttonState.focus}
onFocusOut={buttonState.blur}
onBlur={buttonState.blur}
onPointerEnter={buttonState.hover}
onPointerLeave={buttonState.leave}
>
<label text="change"></label>
</button>
</div>
}
This doesn't appear to be consistent. It works in some components to move them to emo, and not others. Emo seems to be consistently working as long as you never try to set transition properties through the style prop. I'm assuming that style is just broken for this. Tailwind also seems to work.
amorphous
amorphousOP•3mo ago
Also, a somewehat silly but also pretty serious error: items in a list do not always appear to be drawn in the order they are meant to be in the list
<TitleButton
text="Begin"
></TitleButton>
<TitleButton
text="Settings"
></TitleButton>
<TitleButton
text="Credits"
></TitleButton>
<TitleButton
text="Quit to Desktop"
></TitleButton>
<TitleButton
text="Begin"
></TitleButton>
<TitleButton
text="Settings"
></TitleButton>
<TitleButton
text="Credits"
></TitleButton>
<TitleButton
text="Quit to Desktop"
></TitleButton>
No description
amorphous
amorphousOP•3mo ago
this does not appear to be entirely deterministic, nor are there any errors that show up in console. Sometimes things are just drawn out of order. This seems to happen the most often when any kind of redrawing happens; the entire list rendering for the first time, fading objects in and out in the list, etc. it seems to always be the second object in the list being moved to the very end
Singtaa
Singtaa•3mo ago
Okay I solved the unitySlice issue. Preact is appending raw numbers with "px" suffix for styling values. I just made the fix in latest commit (C#) are you still having issue with transition not working with the style prop?
amorphous
amorphousOP•3mo ago
I never got transition working with the style prop. Instead I manually converted them all to emo classes. I have resolved (tentatively) the remaining _attach issues. They were firing on transitionrun and transitionend events when using the Transition object from onejs/comps v1; I think setting style on ref.current and having transition event listeners at the same time was probably related? I ended up rewriting the Transition effect to not use refs at all, and that fixed it. As of now besides some graphical glitches, the only blocking issue is the list ordering error.
Singtaa
Singtaa•3mo ago
For transitions with style prop, I have it working like this. I think if you update onejs-preact you may also fix some prior issues (with transitions)
import { useState } from "preact/hooks"
import { h, render } from "preact"
import { Resources, Texture2D } from "UnityEngine"

const book = Resources.Load("book", puer.$typeof(Texture2D)) as Texture2D

function App() {
const [rotate, setRotate] = useState(0)

function onClick() {
console.log(rotate)
setRotate(rotate + 45)
}

return <div class="w-full h-full flex-row justify-center items-center">
<div class="w-[256px] h-[256px]" onClick={onClick} style={{
backgroundImage: book,
unitySliceBottom: 8,
unitySliceLeft: 15,
unitySliceRight: 20,
unitySliceTop: 10,
rotate: rotate,
transitionProperty: "rotate",
transitionDuration: "2s",
transitionTimingFunction: "ease-in-out",
}}></div>
</div>
}

render(<App />, document.body)
import { useState } from "preact/hooks"
import { h, render } from "preact"
import { Resources, Texture2D } from "UnityEngine"

const book = Resources.Load("book", puer.$typeof(Texture2D)) as Texture2D

function App() {
const [rotate, setRotate] = useState(0)

function onClick() {
console.log(rotate)
setRotate(rotate + 45)
}

return <div class="w-full h-full flex-row justify-center items-center">
<div class="w-[256px] h-[256px]" onClick={onClick} style={{
backgroundImage: book,
unitySliceBottom: 8,
unitySliceLeft: 15,
unitySliceRight: 20,
unitySliceTop: 10,
rotate: rotate,
transitionProperty: "rotate",
transitionDuration: "2s",
transitionTimingFunction: "ease-in-out",
}}></div>
</div>
}

render(<App />, document.body)
for ordering issue, can you reliably repro? does using key={} prop help in any way?
amorphous
amorphousOP•3mo ago
It's reliably reproing ingame with the title screen. Key does not help. I'm trying to create a minimal repro, but it's so far not reproing in the minimal. I'll keep investigating. I have a minimal repro
import { h } from "preact"
import { useEffect, useState } from "preact/hooks"

const WrapDiv = (props: any) => {
const [reallyOn, setReallyOn] = useState(false)
useEffect(() => {
setTimeout(() => setReallyOn(true), 10)
}, [])

const style: any = {}
if (!reallyOn) {
style.translate = "25% 0%"
} else {
style.translate = "0% 0%"
}
return <div style={style}>{props.children}</div>
}

export const App = () => {
return (
<WrapDiv>
<button text="Button 1"></button>
<button text="Button 2"></button>
<button text="Button 3"></button>
<button text="Button 4"></button>
<button text="Button 5"></button>
<button text="Button 6"></button>
</WrapDiv>
)
}
import { h } from "preact"
import { useEffect, useState } from "preact/hooks"

const WrapDiv = (props: any) => {
const [reallyOn, setReallyOn] = useState(false)
useEffect(() => {
setTimeout(() => setReallyOn(true), 10)
}, [])

const style: any = {}
if (!reallyOn) {
style.translate = "25% 0%"
} else {
style.translate = "0% 0%"
}
return <div style={style}>{props.children}</div>
}

export const App = () => {
return (
<WrapDiv>
<button text="Button 1"></button>
<button text="Button 2"></button>
<button text="Button 3"></button>
<button text="Button 4"></button>
<button text="Button 5"></button>
<button text="Button 6"></button>
</WrapDiv>
)
}
It has to do with changing a style with a timeout. if you render(<App />, document.body) Button 2 will end up all the way at the end of the list
Singtaa
Singtaa•3mo ago
Okay I think I fixed it! Dom.insertBefore did not properly take care of the case when both a and b are the same element. Phew, that's tough one to crack
amorphous
amorphousOP•3mo ago
Ok, the UI works in the editor now! ...but it breaks in build. Probably because DOTween isn't linking up correctly in Puerts? I have DOTween set as an Assembly in the ScriptEngine; is there more I need to do here?
unhandledRejection TypeError: labelRef.current.ve.DOText is not a function
unhandledRejection TypeError: labelRef.current.ve.DOText is not a function
I fixed this by adding these lines to index.tsx:
puer.$extension(CS.UnityEngine.UIElements.VisualElement, CS.VisualElementDOTweenExtensions);
puer.$extension(CS.DG.Tweening.Sequence, CS.VisualElementDOTweenExtensions);
puer.$extension(CS.DG.Tweening.Sequence, CS.DG.Tweening.TweenSettingsExtensions);
puer.$extension(CS.UnityEngine.UIElements.VisualElement, CS.VisualElementDOTweenExtensions);
puer.$extension(CS.DG.Tweening.Sequence, CS.VisualElementDOTweenExtensions);
puer.$extension(CS.DG.Tweening.Sequence, CS.DG.Tweening.TweenSettingsExtensions);
(The visualelementDOTweenExtensions are custom code from me)
Singtaa
Singtaa•3mo ago
Great! Hopefully things will be much snappier from now on. To me, even though puerts is a bit more rigid than Jint, it just feels good knowing you are getting the theoretically best performance in terms of interop.
amorphous
amorphousOP•2mo ago
Ok, not blocking but one more issue: there are certain transitions between components that cause random textelement items to be inserted into the DOM - is there any code in OneJS that could account for that? I don't use textelement elements at all in my code. They're a nuisance, in that they add extra height and padding to the UI unnecessarily, but I guess they're not really a problem (since I don't use them I can hide all of them)? More a curiosity.
Singtaa
Singtaa•2mo ago
So in regard to the TextNode, OneJS only provide this one function Document.createTextNode():
public Dom createTextNode(string text) {
var tn = new TextElement();
tn.text = text;
return new Dom(tn, this);
}
public Dom createTextNode(string text) {
var tn = new TextElement();
tn.text = text;
return new Dom(tn, this);
}
Which gets called by Preact in diffElementNodes():
if (dom == null) {
if (nodeType === null) {
return document.createTextNode(newProps);
}
...
if (dom == null) {
if (nodeType === null) {
return document.createTextNode(newProps);
}
...
Other than that, I've got no ideas. Will need to see some of your code to go on
Want results from more Discord servers?
Add your server