Skip to content

Instantly share code, notes, and snippets.

@ZanzyTHEbar
Last active April 28, 2024 19:17
Show Gist options
  • Save ZanzyTHEbar/8f15aebf3accccc9e3301e69ead075b2 to your computer and use it in GitHub Desktop.
Save ZanzyTHEbar/8f15aebf3accccc9e3301e69ead075b2 to your computer and use it in GitHub Desktop.
Resizing Component in SolidJS
import { throttle } from '@solid-primitives/scheduled'
import {
splitProps,
createSignal,
createEffect,
onCleanup,
JSXElement,
type Component,
type ComponentProps,
children,
} from 'solid-js'
import { cn } from '@src/lib/utils'
type SolidRef = (el: HTMLDivElement) => void
type ResizeProps = ComponentProps<'div'> & {
ref: HTMLDivElement | SolidRef
isHorizontal?: boolean
side: 'left' | 'right' | 'top' | 'bottom'
onResize: (clientX: number, clientY: number) => void
children: JSXElement | ((edgeHandler: JSXElement) => JSXElement)
}
type ResizerContentProps = ComponentProps<'div'> & {
edgeHandler: JSXElement
children: JSXElement | ((edgeHandler: JSXElement) => JSXElement)
}
const Resizer: Component<ResizeProps> = (props) => {
const [isDragging, setIsDragging] = createSignal(false)
const [throttledOnPointerMove, setThrottledOnPointerMove] =
createSignal<(e: PointerEvent) => void>()
const onResizeStart = (e: MouseEvent) => {
// Prevents the event from bubbling up to the parent element
e.stopPropagation()
setIsDragging(true)
}
const onResizeEnd = () => setIsDragging(false)
const setRef = (el: HTMLDivElement) => {
if (!el) return
;(props.ref as SolidRef)(el)
onCleanup(() => {
el.removeEventListener('pointerdown', onResizeStart)
})
}
createEffect(() => {
const onPointerMove = (e: PointerEvent) => {
if (e.buttons !== 1)
window.removeEventListener('pointermove', throttledOnPointerMove()!)
props.onResize(e.clientX, e.clientY)
}
setThrottledOnPointerMove(() => throttle(onPointerMove, 10))
})
createEffect(() => {
if (isDragging()) {
window.addEventListener('pointermove', throttledOnPointerMove()!, { passive: true })
window.addEventListener('pointerup', onResizeEnd, { passive: true })
} else {
window.removeEventListener('pointermove', throttledOnPointerMove()!)
window.removeEventListener('pointerup', onResizeEnd)
}
})
// Edge handler styles based on the side prop
const edgeHandlerStyles = {
left: 'absolute top-0 bottom-0 left-0 w-2 cursor-col-resize',
right: 'absolute top-0 bottom-0 right-0 w-2 cursor-col-resize',
top: 'absolute top-0 left-0 right-0 h-2 cursor-row-resize',
bottom: 'absolute bottom-0 left-0 right-0 h-2 cursor-row-resize',
}
// Handler elements
const edgeHandler = <div class={edgeHandlerStyles[props.side]} onMouseDown={onResizeStart} />
return (
<div
ref={(el) => setRef(el)}
class={cn('relative transition-all duration-300 ease-in-out delay-300', props.class)}>
<ResizerContent children={props.children} edgeHandler={edgeHandler} />
</div>
)
}
const ResizerContent: Component<ResizerContentProps> = (props) => {
const [, rest] = splitProps(props, ['class', 'edgeHandler'])
const resolvedChildren = children(() => {
const body = props.children
if (typeof body === 'function') {
return body(props.edgeHandler)
}
return body
})
return (
<div class={cn('relative', props.class)} {...rest}>
{props.edgeHandler}
{resolvedChildren()}
</div>
)
}
export default Resizer
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)
const [height, setHeight] = createSignal<number>(225)
let resizer!: HTMLDivElement
const changeWidth = (clientY: number, clientX: number) => {
if (clientY < 0 || clientX < 0) return
setWidth(clientY)
}
return (
<div class="card h-auto pb-8 min-h-0">
<aside
ref={setSidebar}
class="bg-base-200 card m-2 block min-h-0 min-w-0 p-0 overflow-hidden bg-base-100/90 border-base-100 text-primary h-full"
style={{
height: `${height()}px`,
width: `${width()}px`,
}}>
<Resizer
ref={resizer}
side="right"
onResize={changeWidth}>
{props.children}
</Resizer>
</aside>
</div>
)
}
export default Sidebar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment