Puru
Puru
Explore posts from servers
SSolidJS
Created by Puru on 2/22/2025 in #support
useDraggable how to make second argument reactive
I am building a draggable library neodrag v3, writing the solidJS adapter for it. I want the usage to look like this:
useDraggable(draggableRef, [
positionPlugin({ current: position() }),
events({
onDrag({ offset }) {
console.log(1);
setPosition({ x: offset.x, y: offset.y });
},
}),
]);
useDraggable(draggableRef, [
positionPlugin({ current: position() }),
events({
onDrag({ offset }) {
console.log(1);
setPosition({ x: offset.x, y: offset.y });
},
}),
]);
I am expecting that when position() is updated, the entire second argument to useDraggable is recreated, and my logic can then call update cycle on it. But that does not happen! Here is my internal code:
import { createDraggable } from '@neodrag/core';
import { DragEventData, unstable_definePlugin, type Plugin } from '@neodrag/core/plugins';
import { createSignal, onCleanup, onMount, createEffect, untrack } from 'solid-js';
import type { Accessor, Setter } from 'solid-js';

const draggable_factory = createDraggable();

interface DragState extends DragEventData {
isDragging: boolean;
}

const default_drag_state: DragState = {
offset: { x: 0, y: 0 },
rootNode: null as unknown as HTMLElement,
currentNode: null as unknown as HTMLElement,
isDragging: false,
};

// Create the state sync plugin with the provided setter function
const state_sync = unstable_definePlugin<[Setter<DragState>]>({
// Ommitted for brevity
});

type PluginOrFactory = Plugin | (() => Plugin);

function resolve_plugin(pluginOrFactory: PluginOrFactory): Plugin {
return typeof pluginOrFactory === 'function' ? pluginOrFactory() : pluginOrFactory;
}

export function wrapper(draggableFactory: ReturnType<typeof createDraggable>) {
return (
element: Accessor<HTMLElement | SVGElement | null | undefined>,
plugins: PluginOrFactory[] = [],
) => {
const [drag_state, set_drag_state] = createSignal<DragState>(default_drag_state);
let instance: ReturnType<typeof draggableFactory.draggable> | undefined;
const state_sync_plugin = state_sync(set_drag_state);

// Initialize draggable instance
onMount(() => {
const node = element();
if (!node) return;

// Initial plugin resolution
const resolved_plugins = untrack(() => plugins.map(resolve_plugin).concat(state_sync_plugin));

instance = draggableFactory.draggable(node, resolved_plugins);

// Cleanup on unmount
onCleanup(() => {
instance?.destroy();
});
});

// Handle plugin updates
createEffect(() => {
if (!instance) return;

// Resolve all plugins, including reactive ones
const resolved_plugins = plugins.map(resolve_plugin).concat(state_sync_plugin);

instance.update(resolved_plugins);
});

return drag_state;
};
}

export const useDraggable = wrapper(draggable_factory);

// Export necessary types and utilities
export * from '@neodrag/core/plugins';
export const instances = draggable_factory.instances;
import { createDraggable } from '@neodrag/core';
import { DragEventData, unstable_definePlugin, type Plugin } from '@neodrag/core/plugins';
import { createSignal, onCleanup, onMount, createEffect, untrack } from 'solid-js';
import type { Accessor, Setter } from 'solid-js';

const draggable_factory = createDraggable();

interface DragState extends DragEventData {
isDragging: boolean;
}

const default_drag_state: DragState = {
offset: { x: 0, y: 0 },
rootNode: null as unknown as HTMLElement,
currentNode: null as unknown as HTMLElement,
isDragging: false,
};

// Create the state sync plugin with the provided setter function
const state_sync = unstable_definePlugin<[Setter<DragState>]>({
// Ommitted for brevity
});

type PluginOrFactory = Plugin | (() => Plugin);

function resolve_plugin(pluginOrFactory: PluginOrFactory): Plugin {
return typeof pluginOrFactory === 'function' ? pluginOrFactory() : pluginOrFactory;
}

export function wrapper(draggableFactory: ReturnType<typeof createDraggable>) {
return (
element: Accessor<HTMLElement | SVGElement | null | undefined>,
plugins: PluginOrFactory[] = [],
) => {
const [drag_state, set_drag_state] = createSignal<DragState>(default_drag_state);
let instance: ReturnType<typeof draggableFactory.draggable> | undefined;
const state_sync_plugin = state_sync(set_drag_state);

// Initialize draggable instance
onMount(() => {
const node = element();
if (!node) return;

// Initial plugin resolution
const resolved_plugins = untrack(() => plugins.map(resolve_plugin).concat(state_sync_plugin));

instance = draggableFactory.draggable(node, resolved_plugins);

// Cleanup on unmount
onCleanup(() => {
instance?.destroy();
});
});

// Handle plugin updates
createEffect(() => {
if (!instance) return;

// Resolve all plugins, including reactive ones
const resolved_plugins = plugins.map(resolve_plugin).concat(state_sync_plugin);

instance.update(resolved_plugins);
});

return drag_state;
};
}

export const useDraggable = wrapper(draggable_factory);

// Export necessary types and utilities
export * from '@neodrag/core/plugins';
export const instances = draggable_factory.instances;
What could be going wrong here?
5 replies
SSolidJS
Created by Puru on 12/21/2023 in #support
React.createElement equivalent?
Trying to make a little library which exports a component. The component's JSX is simply return <div ref></div>, hence i'd prefer not to use JSX itself, to simplify the build pipeline. Is it possible to do so without JSX?
46 replies