Help with some JSX logic in Typescript on an Advanced Resizing Component

I am working on making a custom resizing component wrapper for my app. I have the resizing logic all working as i would like it to, however i am trying to implement edge handlers so that i can attach the resize event handlers to the edge of a child component. Here is the github gist to my resizer.tsx The following is an example usage:
import { ParentComponent, createSignal } from 'solid-js'
import { Resizer, ResizerContent } from '@components/ui/resize'

const Sidebar: ParentComponent = (props) => {
const [sidebar, setSidebar] = createSignal<HTMLDivElement | null>(null)
const [width, setWidth] = createSignal<number>(425)
let resizer!: HTMLDivElement

const changeWidth = (clientY: number) => {
if (clientY < 0) return
setWidth(clientY)
}

return (
<div class="card h-auto pb-8 min-h-0">
<Resizer
ref={resizer}
side="right"
onResize={(clientY, clientX) => {
changeWidth(clientY)
}}>
{(edgeHandler) => (
<ResizerContent edgeHandler={edgeHandler}>
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
{props.children}
</aside>
</ResizerContent>
)}
</Resizer>
</div>
)
}
import { ParentComponent, createSignal } from 'solid-js'
import { Resizer, ResizerContent } from '@components/ui/resize'

const Sidebar: ParentComponent = (props) => {
const [sidebar, setSidebar] = createSignal<HTMLDivElement | null>(null)
const [width, setWidth] = createSignal<number>(425)
let resizer!: HTMLDivElement

const changeWidth = (clientY: number) => {
if (clientY < 0) return
setWidth(clientY)
}

return (
<div class="card h-auto pb-8 min-h-0">
<Resizer
ref={resizer}
side="right"
onResize={(clientY, clientX) => {
changeWidth(clientY)
}}>
{(edgeHandler) => (
<ResizerContent edgeHandler={edgeHandler}>
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
{props.children}
</aside>
</ResizerContent>
)}
</Resizer>
</div>
)
}
However this approach is not working, i will post the error below.
17 Replies
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
error:
Type '(edgeHandler: Element) => Element' is not assignable to type '((number | boolean | Node | ArrayElement | (string & {})) & (number | boolean | Node | ArrayElement | (string & {}) | ((edgeHandler: Element) => Element))) | null | undefined'.
Type '(edgeHandler: Element) => Element' is not assignable to type 'number & ((edgeHandler: Element) => Element)'.
Type '(edgeHandler: Element) => Element' is not assignable to type 'number'.ts(2322)
Sidebar.tsx(22, 18): Did you mean to call this expression?
(parameter) edgeHandler: JSX.Element
Type '(edgeHandler: Element) => Element' is not assignable to type '((number | boolean | Node | ArrayElement | (string & {})) & (number | boolean | Node | ArrayElement | (string & {}) | ((edgeHandler: Element) => Element))) | null | undefined'.
Type '(edgeHandler: Element) => Element' is not assignable to type 'number & ((edgeHandler: Element) => Element)'.
Type '(edgeHandler: Element) => Element' is not assignable to type 'number'.ts(2322)
Sidebar.tsx(22, 18): Did you mean to call this expression?
(parameter) edgeHandler: JSX.Element
This error isn't really making sense to me, tbh. Iam passing everything as JSXElement. Is there A: a solution to the error itself or B: an alternative approach that is more idiomatic.
REEEEE
REEEEEā€¢12mo ago
I think the way to do this is to use the children helper
const ResizerChild = (props) => {
const resolvedChildren = children(() => {
const body = props.children;

if (typeof body === 'function') {
return body(props.edgeHandler);
}

return body;
});

return <>{resolvedChildren()}</>;
}
const ResizerChild = (props) => {
const resolvedChildren = children(() => {
const body = props.children;

if (typeof body === 'function') {
return body(props.edgeHandler);
}

return body;
});

return <>{resolvedChildren()}</>;
}
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
interesting, haven't seen this yet. Will look at the docs. Where does body come from?
REEEEE
REEEEEā€¢12mo ago
its just a variable
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
yeah, i see that now. Just read too fast. šŸ˜… i get:
This expression is not callable.
Type 'never' has no call signatures.ts(2349)
const body: never
This expression is not callable.
Type 'never' has no call signatures.ts(2349)
const body: never
how does body go from JSX.Element to never?
REEEEE
REEEEEā€¢12mo ago
uhh, that shouldn't happen You should define children the same as you have in Resizer props JSXElement | ((edgeHandler: JSXElement) => JSXElement)
interface ResizerChildProps{
children: JSXElement | ((edgeHandler: JSXElement) => JSXElement)
edgeHandler: JSXElement
}
const ResizerChild = (props: ResizerChildProps) => {
const resolvedChildren = children(() => {
const body = props.children;

if (typeof body === 'function') {
return body(props.edgeHandler);
}

return body;
});

return <>{resolvedChildren()}</>;
}
interface ResizerChildProps{
children: JSXElement | ((edgeHandler: JSXElement) => JSXElement)
edgeHandler: JSXElement
}
const ResizerChild = (props: ResizerChildProps) => {
const resolvedChildren = children(() => {
const body = props.children;

if (typeof body === 'function') {
return body(props.edgeHandler);
}

return body;
});

return <>{resolvedChildren()}</>;
}
then in Resizer just
<ResizerChild children={props.children} edgeHandler={edgeHandler} />
<ResizerChild children={props.children} edgeHandler={edgeHandler} />
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
Hha, yup, you're right. Interestingly enough, i am one step closer. however, it seems that i am not doing something correctly:
No description
REEEEE
REEEEEā€¢12mo ago
in ResizerContent you need to pull edgehandler prop out in the splitProps usage const [, rest] = splitProps(props, ['class', 'edgeHandler'])
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
indeed, but when i do that, my handler is not attached. I don't see it in the DOM, nor are my event handlers being added. huh ...
REEEEE
REEEEEā€¢12mo ago
hmm Could you show what you have now?
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
Yup, one minute. Ill update the gist.
import { ParentComponent, createSignal } from 'solid-js'
import { Resizer } from '@components/ui/resize'

const Sidebar: ParentComponent = (props) => {
const [sidebar, setSidebar] = createSignal<HTMLDivElement | null>(null)
const [width, setWidth] = createSignal<number>(425)
let resizer!: HTMLDivElement

const changeWidth = (clientY: number) => {
if (clientY < 0) return
setWidth(clientY)
}

return (
<div class="card h-auto pb-8 min-h-0">
<Resizer
ref={resizer}
side="top"
onResize={(clientY) => {
changeWidth(clientY)
}}>
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
{props.children}
</aside>
</Resizer>
</div>
)
}
import { ParentComponent, createSignal } from 'solid-js'
import { Resizer } from '@components/ui/resize'

const Sidebar: ParentComponent = (props) => {
const [sidebar, setSidebar] = createSignal<HTMLDivElement | null>(null)
const [width, setWidth] = createSignal<number>(425)
let resizer!: HTMLDivElement

const changeWidth = (clientY: number) => {
if (clientY < 0) return
setWidth(clientY)
}

return (
<div class="card h-auto pb-8 min-h-0">
<Resizer
ref={resizer}
side="top"
onResize={(clientY) => {
changeWidth(clientY)
}}>
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
{props.children}
</aside>
</Resizer>
</div>
)
}
gist is updated, and that's the example file that i am working with to test it.
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
I was able to find the ResizeContent component in the DOM:
No description
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
though i can't see any event handlers attached to it.
REEEEE
REEEEEā€¢12mo ago
you aren't using the edgeHandler anywhere though
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
šŸ¤¦šŸ» i forgot to use it in the div okay, my event handles are attached, though it is binding to the parent, not the child. so close, thanks for the help. the entire reason i am going through this is to try and attach the resize handler top the child of the Resizer. That way the resize is triggered by clicking on the edge of the child, not the Resizer.
REEEEE
REEEEEā€¢12mo ago
You want it attached to the aside or the edgeHandler? also a possible better way to do this is provide callbacks to the children in the render function
<Resizer>
{(state) => (
<div onMouseDown={state.onResizeStart} />
)}
</Resizer>
<Resizer>
{(state) => (
<div onMouseDown={state.onResizeStart} />
)}
</Resizer>
or a context
DaOfficialWizardšŸ§™
DaOfficialWizardšŸ§™OPā€¢12mo ago
well, the aside is just an example - but yes, i want it to attach to the first child element passed to the Resizer. Essentially the Resizer is a wrapper that allows me to resize the child when i drag on it. I originally wanted to "clamp" the Resizer to always be the same size as the child that is being resized, but then i thought attaching the events handler itself to the child would be better. Providing a callback was my original plan, but i think i over-complicated it tbh. A context wouldn't be too nice, as i want this component to be generic and able to wrap any component that i decide should be resizable. Thank you very much, i solved the problem. I was over complicating it, like usual.
return (
<div class="card h-auto pb-8 min-h-0">
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
<Resizer
ref={resizer}
side="right"
onResize={(clientY) => {
changeWidth(clientY)
}}>
{props.children}
</Resizer>
</aside>
</div>
)
return (
<div class="card h-auto pb-8 min-h-0">
<aside
ref={setSidebar}
style={{
width: `${width()}px`,
}}>
<Resizer
ref={resizer}
side="right"
onResize={(clientY) => {
changeWidth(clientY)
}}>
{props.children}
</Resizer>
</aside>
</div>
)
Just put the Resizer in the div i want to resize. That solved it. Duh. Tht way i am automatically bound. Haha, better than a wrapper tbh.
Want results from more Discord servers?
Add your server