jmp
jmp
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
I think I've figured it out... getting closer at least
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
other than this snag the tokens api has been really nice to work with!
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
@bigmistqke I'm finally getting around to using tokens! So far everything has been mostly smooth, but I'm wondering how to use higher-order components with tokenized children? I wrap all my components in some contexts to implicitly pass & generate ids I've tried something like this (as well as the commented out version)
return (
<ParentScopeIdContext.Provider value={scopeId}>
<IdContext.Provider value={id}>
{/* {untrack(() => {
const tokens = resolveTokens(ScenegraphTokenizer, () => (
// <Dynamic
// component={WrappedComponent}
// {...(props as WithBluefishProps<ComponentProps>)}
// name={id()}
// />

));

return tokens();
})} */}
<WrappedComponent
{...(props as WithBluefishProps<ComponentProps>)}
name={id()}
/>
</IdContext.Provider>
</ParentScopeIdContext.Provider>
);
return (
<ParentScopeIdContext.Provider value={scopeId}>
<IdContext.Provider value={id}>
{/* {untrack(() => {
const tokens = resolveTokens(ScenegraphTokenizer, () => (
// <Dynamic
// component={WrappedComponent}
// {...(props as WithBluefishProps<ComponentProps>)}
// name={id()}
// />

));

return tokens();
})} */}
<WrappedComponent
{...(props as WithBluefishProps<ComponentProps>)}
name={id()}
/>
</IdContext.Provider>
</ParentScopeIdContext.Provider>
);
WrappedComponent is a token. I get the "Tokens can only be rendered with resolveTokens" error. I need the context here b/c I'm inspecting the name prop before it is passed to the wrapped component.
33 replies
SSolidJS
Created by bigmistqke on 12/4/2023 in #support
setting signal inside createRenderEffect
I guess I'm not sure what dependency graph you have in mind? do you want "render" to run every time other is updated or only when other is updated by count? If the latter, maybe there's another way to express that dependency graph
5 replies
SSolidJS
Created by bigmistqke on 12/4/2023 in #support
setting signal inside createRenderEffect
I'm not sure if this would work for your use case, but if you make "render" dependent on updates to other rather than updates to count, you'll get properly alternating effects and renders: https://playground.solidjs.com/anonymous/231201e1-546a-4621-9124-b345fea0caf9
5 replies
SSolidJS
Created by jmp on 7/5/2023 in #support
Using SolidJS Playground in custom docs
Thanks! I'll try that
4 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
thanks that's super helpful! This route does seem very promising and I'll explore it once I start to hit some more roadblocks. thanks so much for clarifying
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
race condition was fake news...
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
I think I'm running into race conditions with effects now. (The bug appears when I run my code normally but when I insert a debug statement it disappears.) Is that something that the tokenizer solves?
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
That looks very cool! I'm having some trouble understanding what the expressiveness of jsx-tokenizer is. What is an example of something I can't do with effects but that I can do with jsx-tokenizer? e.g. are you trying to walk the canvas tree in the same order every frame?
33 replies
SSolidJS
Created by SilentRhetoric on 7/3/2023 in #support
Why is storageSignal possibly null?
15 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Got it, thanks I'll look into those approaches then.
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Alternatively, maybe there's a way to use createComputed for this?
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Out of curiosity, do you know what breaks when you write signals from createMemo? The docs say:
The memo function should not change other signals by calling setters (it should be "pure"). This enables Solid to optimize the execution order of memo updates according to their dependency graph, so that all memos can update at most once in response to a dependency change.
Does that mean if I write signals but can ensure the memo won't run a second time in response to those changes, then it's safe?
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Thanks for the thoughts! The SVG tiler looks cool. I do share your worries about the layout update order. My previous system prototype only supported static diagrams so there was no problem with ordering. My current approaches are either (i) have parent layouts call child layouts to enforce a layout order or (ii) ensure that layouts can happen in any order. (i) is how mobile frameworks and CSS (I think) accomplish this so whenever a parent is stale they re-run that layout and also check if the child layouts are stale. Maybe I can implement a different caching system just for the layout functions. (ii) might be possible thanks to the ownership properties on each bbox. Those are established on first render. (I haven't thought about what happens on subsequent renders when the graph might change, though.) After they're established, the layouts can run in any order it might just be faster or slower since layouts may have to run multiple times before converging. But I haven't implemented flexbox in this new system, so I'm not 100% sure if it works with the ownership model. But yeah I was hoping there would be a more Solid-native way of ensuring this. I'll keep thinking about it.
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Would be curious to hear your thoughts! I don't have the API for this nailed down very well.
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
This works alright for a lot of things, but one tricky addition is making it so that you can adapt to screen size. For example, you might want to shrink a Rect so that it fits inside the screen. To accomplish this in Jetpack Compose, they pass width and height information from the parent to the child, which is used during the child's layout. Here's a simplified example of that:
const parentLayout = (children, { maxHeight, maxWidth }) => {
const [child0, ...] = children;
child0.layout({ maxHeight: 5000, maxWidth: 3000});
...
}
const parentLayout = (children, { maxHeight, maxWidth }) => {
const [child0, ...] = children;
child0.layout({ maxHeight: 5000, maxWidth: 3000});
...
}
But notice that this changes how layouts are run. Now the parent controls when the child layout happens. This is useful in some cases like for implementing flexbox. In that case you have a flow sort of like this:
const parentLayout = (children, { maxHeight, maxWidth }) => {
for (const child of children) {
child.layout({...});
// do some stuff
}
const parentLayout = (children, { maxHeight, maxWidth }) => {
for (const child of children) {
child.layout({...});
// do some stuff
}
So parent and child layout computations are interleaved in a way that I don't think is possible using createEffect? Another route to take is to store maxHeight and maxWidth in a local context for each child. This can be useful so that you can change the structure of the component based on those constraints. E.g.
const Child = () => {
const { maxWidth, maxHeight } = useDims(); // sort of like a media query

<Switch fallback={<></>}>
<Match when={maxWidth() > 1000}>
...
</Match>
<Match when={maxWidth() > 500}>
...
</Match>
</Switch>
}
const Child = () => {
const { maxWidth, maxHeight } = useDims(); // sort of like a media query

<Switch fallback={<></>}>
<Match when={maxWidth() > 1000}>
...
</Match>
<Match when={maxWidth() > 500}>
...
</Match>
</Switch>
}
This seems pretty expressive and a lot nicer than writing layout functions directly, but I haven't figured out how the API would work if you could also write stuff like child0.layout({ maxHeight: 5000, maxWidth: 3000}). I think the problem is that within a layout function you want the child's layout computation to have settled after the child0.layout call is complete, and I'm finding it pretty hard to reason about when that happens.
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Basically I have a store
type Scenegraph = { [key: Id]: ScenegraphNode; } // this is the store type
type Id = string;
type ScenegraphNode =
| {
type: "node";
bbox: BBox;
bboxOwners: BBoxOwners;
transform: Transform;
transformOwners: TransformOwners;
children: Set<Id>;
parent: Id | null;
}
| {
type: "ref";
refId: Id;
parent: Id | null;
};
type Scenegraph = { [key: Id]: ScenegraphNode; } // this is the store type
type Id = string;
type ScenegraphNode =
| {
type: "node";
bbox: BBox;
bboxOwners: BBoxOwners;
transform: Transform;
transformOwners: TransformOwners;
children: Set<Id>;
parent: Id | null;
}
| {
type: "ref";
refId: Id;
parent: Id | null;
};
It sets up a DAG of scenegraph nodes. The bbox and transform (and ownership of the properties within those fields) is updated by setBBox. For example, I may have an AlignLeft parent that sets the left bbox property of each of its children and obtains ownership of each of its children's left bbox property. The ref nodes allow indirection so multiple nodes in the scenegraph can modify the same bbox and transforms. (This is why I can't derive bbox and transform information directly from the layout functions and just memo that.) The ownership information ensures that even when a node is written to by multiple parents, the properties are uniquely writable by a single parent so there are no edit conflicts. Suppose I have a component like this:
<AlignLeft ...>
<Rect width={50} height={50} y={0} ... />
<Rect ... />
</AlignLeft>
<AlignLeft ...>
<Rect width={50} height={50} y={0} ... />
<Rect ... />
</AlignLeft>
Then the first Rect will run its layout, setting its width, height, and y values. The AlignLeft's layout will run later, thus filling in the x value.
33 replies
SSolidJS
Created by jmp on 6/24/2023 in #support
Coordinating Signals in a Layout System
Hi Erik, thanks for the help! The scenegraph is a Solid store and updateBBox calls setScenegraph, so the layout effect has a Solid-specific side effect. But swapping the order of the jsx and effect did the trick! In terms of moving the layout functions into the scenegraph store I was mostly wondering if there was a way to keep the reactive computation in the store so it can be called by other components. Right now I'm dependent on the Solid effect queue for layout updates. That being said, all the layout scheduling problems I've run into so far can be solved by the jsx-effect swap trick. So I'll just stick with that for now. Thanks again!
33 replies