New:Socket for Asana Is Now Available.Learn more
Get Started

@neodrag/solid

Package Overview
Dependencies
Maintainers
1
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@neodrag/solid - npm Package Compare versions

Comparing version
3.0.0-next.8
to
3.0.0-next.10
+4
dist/_internal-B_qrKGUN.d.ts
//#region src/_internal.d.ts
type Ref = (node: HTMLElement) => void;
//#endregion
export { Ref as t };
import { createContext, useContext } from "solid-js";
//#region src/_room-context.ts
/**
* The room a `<RoomProvider>` supplies, or `undefined` when none is mounted (collab is opt-in, so the
* capability primitives just skip their join). This module holds only a context handle + type-only
* `Room` references — importing it never pulls the heavy `Room` orchestrator into the bundle (that
* lives behind `@neodrag/solid/collab`), so `createSortable` without collab stays lean.
*/
const RoomContext = createContext();
/**
* Raw (non-reactive) access to the ambient room — capability primitives use this to `add` their
* instance. For reactive presence UI (`peers`/`presences`) use `useRoom()` from `@neodrag/solid/collab`.
*/
function useRoomContext() {
return useContext(RoomContext);
}
/**
* Resolves the room from the `room` option or the ambient `<RoomProvider>`, and returns `join`/`leave`
* to call from the `ref` setter, co-located with the instance create + its `onCleanup`. `leave` is the
* per-target disposer (idempotent).
*/
function useRoomBinding(option_room) {
const room = option_room ?? useRoomContext();
let off = null;
const join = (instance, id) => {
off = room?.add(instance, id) ?? null;
};
const leave = () => {
off?.();
off = null;
};
return {
join,
leave
};
}
//#endregion
export { useRoomBinding as n, useRoomContext as r, RoomContext as t };
import { Accessor, JSX } from "solid-js";
import { CollabBackend, PresenceFrame, Room, RoomOptions } from "@neodrag/core/collab";
export * from "@neodrag/core/collab";
//#region src/_room-context.d.ts
/**
* Raw (non-reactive) access to the ambient room — capability primitives use this to `add` their
* instance. For reactive presence UI (`peers`/`presences`) use `useRoom()` from `@neodrag/solid/collab`.
*/
declare function useRoomContext(): Room | undefined;
//#endregion
//#region src/collab.d.ts
type RoomProviderProps = {
/** Use an existing room (you own its lifetime — the provider won't destroy it). */room?: Room; /** Create a room from this backend (destroyed when the provider unmounts). */
backend?: CollabBackend;
children?: JSX.Element;
} & RoomOptions;
/**
* Provides a room to every capability primitive rendered below it — they auto-join (and leave on
* cleanup). Pass `room` to share an existing one, or `backend` to have the provider create and own it.
*/
declare function RoomProvider(props: RoomProviderProps): JSX.Element;
/**
* Reactive view of the ambient room — `peers` (connected ids) and `presences` (their in-flight
* gestures) are accessors that update as peers join, move and leave. Use for avatars, "N editing",
* remote cursors. Pass a room or rely on an ancestor `<RoomProvider>`.
*/
declare function useRoom(room?: Room): {
room: Room;
peers: Accessor<readonly string[]>;
presences: Accessor<ReadonlyMap<string, PresenceFrame>>;
};
//#endregion
export { RoomProvider, RoomProviderProps, useRoom, useRoomContext };
import { r as useRoomContext, t as RoomContext } from "./_room-context-Cg1LI93i.js";
import { createComponent, createSignal, onCleanup } from "solid-js";
import { Room } from "@neodrag/core/collab";
export * from "@neodrag/core/collab";
//#region src/collab.ts
/**
* Provides a room to every capability primitive rendered below it — they auto-join (and leave on
* cleanup). Pass `room` to share an existing one, or `backend` to have the provider create and own it.
*/
function RoomProvider(props) {
const room = props.room ?? new Room(props.backend, {
presenceThrottleMs: props.presenceThrottleMs,
presenceTtlMs: props.presenceTtlMs,
onRemotePresence: props.onRemotePresence,
onRemoteOp: props.onRemoteOp,
mirror: props.mirror
});
if (!props.room) onCleanup(() => room.destroy());
return createComponent(RoomContext.Provider, {
value: room,
get children() {
return props.children;
}
});
}
/**
* Reactive view of the ambient room — `peers` (connected ids) and `presences` (their in-flight
* gestures) are accessors that update as peers join, move and leave. Use for avatars, "N editing",
* remote cursors. Pass a room or rely on an ancestor `<RoomProvider>`.
*/
function useRoom(room) {
const active = room ?? useRoomContext();
if (!active) throw new Error("`useRoom()` needs a room — pass one or mount a <RoomProvider>.");
const [peers, set_peers] = createSignal(active.peers);
const [presences, set_presences] = createSignal(active.presences);
onCleanup(active.subscribe(() => {
set_peers(active.peers);
set_presences(active.presences);
}));
return {
room: active,
peers,
presences
};
}
//#endregion
export { RoomProvider, useRoom, useRoomContext };
import { t as Ref } from "./_internal-B_qrKGUN.js";
import { Accessor } from "solid-js";
import { CollisionPolicy, DropAcceptCtx, DropEventData, DropOptions, DropOptions as DropOptions$1, Droppable, REMOTE_HOVER_ATTR, REMOTE_HOVER_MARKER_ATTR } from "@neodrag/core/drop";
import { Room } from "@neodrag/core/collab";
//#region src/drop.d.ts
declare function createDroppable(options?: DropOptions$1 & {
room?: Room;
}): {
ref: Ref;
isOver: Accessor<boolean>;
};
//#endregion
export { type CollisionPolicy, type DropAcceptCtx, type DropEventData, type DropOptions, Droppable, REMOTE_HOVER_ATTR, REMOTE_HOVER_MARKER_ATTR, createDroppable };
import { n as useRoomBinding } from "./_room-context-Cg1LI93i.js";
import { createEffect, createSignal, onCleanup } from "solid-js";
import { Droppable, Droppable as Droppable$1, REMOTE_HOVER_ATTR, REMOTE_HOVER_MARKER_ATTR } from "@neodrag/core/drop";
//#region src/drop.ts
function createDroppable(options = {}) {
const [isOver, set_over] = createSignal(false);
let inst = null;
const { join, leave } = useRoomBinding(options.room);
const build = () => ({
...options,
onEnter: (e) => {
set_over(true);
options.onEnter?.(e);
},
onLeave: (e) => {
set_over(false);
options.onLeave?.(e);
}
});
const ref = (node) => {
leave();
inst?.destroy();
inst = new Droppable$1(node, build());
join(inst, options.id);
onCleanup(() => {
leave();
inst?.destroy();
inst = null;
});
};
createEffect(() => {
const next = build();
inst?.update(next);
});
return {
ref,
isOver
};
}
//#endregion
export { Droppable, REMOTE_HOVER_ATTR, REMOTE_HOVER_MARKER_ATTR, createDroppable };
import { Accessor } from "solid-js";
import { PanZoomBindOptions, PanZoomBindOptions as PanZoomBindOptions$1, PanZoomOptions, PanZoomTransform, PanZoomTransform as PanZoomTransform$1 } from "@neodrag/core/panzoom";
//#region src/panzoom.d.ts
type RefSetter = (node: HTMLElement | null) => void;
/**
* Pan/zoom canvas primitive — a thin adapter over the core `PanZoom` binder (which owns the pan
* drag, wheel/pinch zoom, and the world transform). Put `viewport` on the clipping element's `ref`
* and `world` on the inner content layer's `ref`; read live `scale()` / `x()` / `y()` / `transform()`.
*/
declare function createPanZoom(options?: PanZoomBindOptions$1): {
viewport: RefSetter;
world: RefSetter;
scale: Accessor<number>;
x: Accessor<number>;
y: Accessor<number>;
transform: Accessor<PanZoomTransform$1>;
zoomBy: (factor: number, cx?: number, cy?: number) => void;
zoomTo: (scale: number, cx?: number, cy?: number) => void;
panBy: (dx: number, dy: number) => void;
setTransform: (t: Partial<PanZoomTransform$1>) => void;
reset: () => void;
};
//#endregion
export { type PanZoomBindOptions, type PanZoomOptions, type PanZoomTransform, createPanZoom };
import { createSignal, onCleanup } from "solid-js";
import { PanZoom } from "@neodrag/core/panzoom";
//#region src/panzoom.ts
/**
* Pan/zoom canvas primitive — a thin adapter over the core `PanZoom` binder (which owns the pan
* drag, wheel/pinch zoom, and the world transform). Put `viewport` on the clipping element's `ref`
* and `world` on the inner content layer's `ref`; read live `scale()` / `x()` / `y()` / `transform()`.
*/
function createPanZoom(options = {}) {
const [transform, set_transform] = createSignal({
x: options.x ?? 0,
y: options.y ?? 0,
scale: options.scale ?? 1
});
const inst = new PanZoom({
...options,
onChange: (t) => set_transform(t)
});
let v_dispose = null;
const viewport = (node) => {
v_dispose?.();
v_dispose = node ? inst.viewport(node) : null;
};
let w_dispose = null;
const world = (node) => {
w_dispose?.();
w_dispose = node ? inst.world(node) : null;
};
const zoomBy = (factor, cx, cy) => inst.zoomBy(factor, cx, cy);
const zoomTo = (scale, cx, cy) => inst.zoomTo(scale, cx, cy);
const panBy = (dx, dy) => inst.panBy(dx, dy);
const setTransform = (t) => inst.setTransform(t);
const reset = () => inst.reset();
onCleanup(() => {
v_dispose?.();
v_dispose = null;
w_dispose?.();
w_dispose = null;
});
return {
viewport,
world,
scale: () => transform().scale,
x: () => transform().x,
y: () => transform().y,
transform,
zoomBy,
zoomTo,
panBy,
setTransform,
reset
};
}
//#endregion
export { createPanZoom };
import { t as Ref } from "./_internal-B_qrKGUN.js";
import { Accessor } from "solid-js";
import { RESIZE_EDGES, RESIZE_HANDLE_ATTR, Resizable, ResizeBoundsInput, ResizeEdge, ResizeEdge as ResizeEdge$1, ResizeEventData, ResizeHandleProps, ResizeHandleProps as ResizeHandleProps$1, ResizeOptions, ResizeOptions as ResizeOptions$1, preserveUnits } from "@neodrag/core/resize";
import { Room } from "@neodrag/core/collab";
//#region src/resize.d.ts
declare function createResizable(options?: ResizeOptions$1 & {
room?: Room;
}): {
ref: Ref;
handle: (edge: ResizeEdge$1) => ResizeHandleProps$1;
isResizing: Accessor<boolean>;
size: Accessor<{
width: number;
height: number;
} | undefined>;
position: Accessor<{
x: number;
y: number;
} | undefined>;
};
//#endregion
export { RESIZE_EDGES, RESIZE_HANDLE_ATTR, Resizable, type ResizeBoundsInput, type ResizeEdge, type ResizeEventData, type ResizeHandleProps, type ResizeOptions, createResizable, preserveUnits };
import { n as useRoomBinding } from "./_room-context-Cg1LI93i.js";
import { createEffect, createSignal, onCleanup } from "solid-js";
import { RESIZE_EDGES, RESIZE_HANDLE_ATTR, RESIZE_HANDLE_ATTR as RESIZE_HANDLE_ATTR$1, Resizable, Resizable as Resizable$1, preserveUnits } from "@neodrag/core/resize";
//#region src/resize.ts
function createResizable(options = {}) {
const [isResizing, set_resizing] = createSignal(false);
const [size, set_size] = createSignal(void 0);
const [position, set_position] = createSignal(void 0);
let inst = null;
const { join, leave } = useRoomBinding(options.room);
const build = () => ({
...options,
onResizeStart: (e) => {
set_resizing(true);
set_size({
width: e.width,
height: e.height
});
set_position({
x: e.x,
y: e.y
});
options.onResizeStart?.(e);
},
onResize: (e) => {
set_size({
width: e.width,
height: e.height
});
set_position({
x: e.x,
y: e.y
});
options.onResize?.(e);
},
onResizeEnd: (e) => {
set_resizing(false);
set_size({
width: e.width,
height: e.height
});
set_position({
x: e.x,
y: e.y
});
options.onResizeEnd?.(e);
}
});
const ref = (node) => {
leave();
inst?.destroy();
inst = new Resizable$1(node, build());
join(inst, options.id);
onCleanup(() => {
leave();
inst?.destroy();
inst = null;
});
};
createEffect(() => {
inst?.update(build());
});
return {
ref,
handle: (edge) => ({ [RESIZE_HANDLE_ATTR$1]: edge }),
isResizing,
size,
position
};
}
//#endregion
export { RESIZE_EDGES, RESIZE_HANDLE_ATTR, Resizable, createResizable, preserveUnits };
import { t as Ref } from "./_internal-B_qrKGUN.js";
import { Accessor } from "solid-js";
import { ROTATE_HANDLE_ATTR, Rotatable, RotateEventData, RotateHandlePos, RotateHandlePos as RotateHandlePos$1, RotateHandleProps, RotateHandleProps as RotateHandleProps$1, RotateOptions, RotateOptions as RotateOptions$1, RotateOrigin } from "@neodrag/core/rotate";
import { Room } from "@neodrag/core/collab";
//#region src/rotate.d.ts
/**
* Rotate primitive. Put `ref` on the element and spread `{...handle('top')}` on the rotate grip.
* `angle` (degrees) + `isRotating` come back as accessors.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and rotation syncs live.
*/
declare function createRotatable(options?: RotateOptions$1 & {
room?: Room;
}): {
ref: Ref;
handle: (pos?: RotateHandlePos$1) => RotateHandleProps$1;
isRotating: Accessor<boolean>;
angle: Accessor<number>;
};
//#endregion
export { ROTATE_HANDLE_ATTR, Rotatable, type RotateEventData, type RotateHandlePos, type RotateHandleProps, type RotateOptions, type RotateOrigin, createRotatable };
import { n as useRoomBinding } from "./_room-context-Cg1LI93i.js";
import { createEffect, createSignal, onCleanup } from "solid-js";
import { ROTATE_HANDLE_ATTR, ROTATE_HANDLE_ATTR as ROTATE_HANDLE_ATTR$1, Rotatable, Rotatable as Rotatable$1 } from "@neodrag/core/rotate";
//#region src/rotate.ts
/**
* Rotate primitive. Put `ref` on the element and spread `{...handle('top')}` on the rotate grip.
* `angle` (degrees) + `isRotating` come back as accessors.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and rotation syncs live.
*/
function createRotatable(options = {}) {
const [isRotating, set_rotating] = createSignal(false);
const [angle, set_angle] = createSignal(0);
let inst = null;
const { join, leave } = useRoomBinding(options.room);
const build = () => ({
...options,
onRotateStart: (e) => {
set_rotating(true);
set_angle(e.angle);
options.onRotateStart?.(e);
},
onRotate: (e) => {
set_angle(e.angle);
options.onRotate?.(e);
},
onRotateEnd: (e) => {
set_rotating(false);
set_angle(e.angle);
options.onRotateEnd?.(e);
}
});
const ref = (node) => {
leave();
inst?.destroy();
inst = new Rotatable$1(node, build());
join(inst, options.id);
onCleanup(() => {
leave();
inst?.destroy();
inst = null;
});
};
createEffect(() => {
inst?.update(build());
});
return {
ref,
handle: (pos = "top") => ({ [ROTATE_HANDLE_ATTR$1]: pos }),
isRotating,
angle
};
}
//#endregion
export { ROTATE_HANDLE_ATTR, Rotatable, createRotatable };
import { Accessor } from "solid-js";
import { MarqueeOptions, SelectableOptions, SelectableOptions as SelectableOptions$1, rectsOverlap } from "@neodrag/core/select";
//#region src/select.d.ts
type RefSetter = (node: HTMLElement | null) => void;
/**
* Rubber-band multi-select primitive — a thin adapter over the core `Selectable` binder (the region
* is a `Draggable` running the `marqueeSelect` plugin). Put `container` on the region's `ref` and
* `item(value)` on each child's `ref`. `selected()` is the live array of selected values; selected
* items also carry a `data-neodrag-selected` attribute to style.
*/
declare function createSelect<V = string>(options?: SelectableOptions$1<V>): {
container: RefSetter;
item: (value: V) => RefSetter;
selected: Accessor<V[]>;
clear: () => void;
};
//#endregion
export { type MarqueeOptions, type SelectableOptions, createSelect, rectsOverlap };
import { createSignal, onCleanup } from "solid-js";
import { Selectable, rectsOverlap } from "@neodrag/core/select";
//#region src/select.ts
/**
* Rubber-band multi-select primitive — a thin adapter over the core `Selectable` binder (the region
* is a `Draggable` running the `marqueeSelect` plugin). Put `container` on the region's `ref` and
* `item(value)` on each child's `ref`. `selected()` is the live array of selected values; selected
* items also carry a `data-neodrag-selected` attribute to style.
*/
function createSelect(options = {}) {
const [selected, set_selected] = createSignal([]);
const item_refs = /* @__PURE__ */ new Map();
const inst = new Selectable({
...options,
onChange: (list) => set_selected(list)
});
let c_dispose = null;
const container = (node) => {
c_dispose?.();
c_dispose = node ? inst.container(node) : null;
};
const item = (value) => {
let cb = item_refs.get(value);
if (!cb) {
let off = null;
cb = (node) => {
off?.();
off = node ? inst.item(value, node) : null;
};
item_refs.set(value, cb);
}
return cb;
};
const clear = () => inst.clear();
onCleanup(() => {
c_dispose?.();
c_dispose = null;
});
return {
container,
item,
selected,
clear
};
}
//#endregion
export { createSelect, rectsOverlap };
import { t as Ref } from "./_internal-B_qrKGUN.js";
import { MoveOp, SORTABLE_KEY_ATTR, SortAxis, SortStrategy, SortableList, SortableOp, SortableOptions, SortableOptions as SortableOptions$1, SortableRow, SortableRow as SortableRow$1, TransferContainer, TransferOp } from "@neodrag/core/sortable";
import { Room } from "@neodrag/core/collab";
//#region src/sortable.d.ts
/**
* Sortable primitive. Put `ref` on the container and spread `{...row(item.id)}` on each item instead
* of hand-writing `data-neodrag-sortable-key`. Pass `items` (and any option) as a getter to keep it live.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and reorders sync live.
*/
declare function createSortable<T = unknown>(options: SortableOptions$1<T> & {
room?: Room;
}): {
ref: Ref;
row: (key: string) => SortableRow$1;
};
//#endregion
export { type MoveOp, SORTABLE_KEY_ATTR, type SortAxis, type SortStrategy, SortableList, type SortableOp, type SortableOptions, type SortableRow, type TransferContainer, type TransferOp, createSortable };
import { n as useRoomBinding } from "./_room-context-Cg1LI93i.js";
import { createEffect, onCleanup } from "solid-js";
import { SORTABLE_KEY_ATTR, SORTABLE_KEY_ATTR as SORTABLE_KEY_ATTR$1, SortableList, SortableList as SortableList$1 } from "@neodrag/core/sortable";
//#region src/sortable.ts
/**
* Sortable primitive. Put `ref` on the container and spread `{...row(item.id)}` on each item instead
* of hand-writing `data-neodrag-sortable-key`. Pass `items` (and any option) as a getter to keep it live.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and reorders sync live.
*/
function createSortable(options) {
let inst = null;
const { join, leave } = useRoomBinding(options.room);
const ref = (node) => {
leave();
inst?.destroy();
inst = new SortableList$1(node, { ...options });
join(inst, options.id);
onCleanup(() => {
leave();
inst?.destroy();
inst = null;
});
};
createEffect(() => {
const next = { ...options };
inst?.update(next);
});
return {
ref,
row: (key) => ({ [SORTABLE_KEY_ATTR$1]: key })
};
}
//#endregion
export { SORTABLE_KEY_ATTR, SortableList, createSortable };
import { Accessor } from "solid-js";
import { Room } from "@neodrag/core/collab";
import { SplitAxis, SplitPaneOptions, SplitPaneOptions as SplitPaneOptions$1 } from "@neodrag/core/splitpane";
//#region src/splitpane.d.ts
type RefSetter = (node: HTMLElement | null) => void;
/**
* Split-pane primitive — a thin adapter over the core `SplitPane` binder. Put `container` on the
* wrapper's `ref`, `pane(i)` on each pane's `ref`, and `gutter(i)` on each divider's `ref` (the
* gutter between pane `i` and `i + 1`). `sizes()` is the live array of weights. Nests: each call is
* independent. Pass a `room` to sync the layout.
*/
declare function createSplitPane(options?: SplitPaneOptions$1 & {
room?: Room;
}): {
container: RefSetter;
pane: (index: number) => RefSetter;
gutter: (index: number) => RefSetter;
sizes: Accessor<number[]>;
setSizes: (sizes: number[]) => void;
};
//#endregion
export { type SplitAxis, type SplitPaneOptions, createSplitPane };
import { createSignal, onCleanup } from "solid-js";
import { SplitPane } from "@neodrag/core/splitpane";
//#region src/splitpane.ts
/**
* Split-pane primitive — a thin adapter over the core `SplitPane` binder. Put `container` on the
* wrapper's `ref`, `pane(i)` on each pane's `ref`, and `gutter(i)` on each divider's `ref` (the
* gutter between pane `i` and `i + 1`). `sizes()` is the live array of weights. Nests: each call is
* independent. Pass a `room` to sync the layout.
*/
function createSplitPane(options = {}) {
const [sizes, set_sizes] = createSignal(options.sizes ?? []);
const pane_refs = /* @__PURE__ */ new Map();
const gutter_refs = /* @__PURE__ */ new Map();
const inst = new SplitPane({
...options,
onChange: (s) => set_sizes(s.slice())
});
let c_dispose = null;
const container = (node) => {
c_dispose?.();
c_dispose = node ? inst.container(node) : null;
};
const make = (cache, kind, index) => {
let cb = cache.get(index);
if (!cb) {
let off = null;
cb = (node) => {
off?.();
off = node ? kind === "pane" ? inst.pane(node, index) : inst.gutter(node, index) : null;
};
cache.set(index, cb);
}
return cb;
};
const pane = (index) => make(pane_refs, "pane", index);
const gutter = (index) => make(gutter_refs, "gutter", index);
const setSizes = (s) => inst.setSizes(s);
onCleanup(() => {
c_dispose?.();
c_dispose = null;
});
return {
container,
pane,
gutter,
sizes,
setSizes
};
}
//#endregion
export { createSplitPane };
import { Accessor } from "solid-js";
import { SwipeAxis, SwipeOptions, SwipeOptions as SwipeOptions$1, SwipeResult, resolveSwipe } from "@neodrag/core/swipe";
//#region src/swipe.d.ts
type RefSetter = (node: HTMLElement | null) => void;
/**
* Swipe-to-dismiss primitive — a thin adapter over the core `Swipeable` binder. Put `ref` on the
* element; read `offset()` / `isDismissed()`. Released past `threshold` the element flies out and
* fires `onDismiss`, otherwise it springs back.
*/
declare function createSwipe(options?: SwipeOptions$1): {
ref: RefSetter;
offset: Accessor<{
x: number;
y: number;
}>;
isDismissed: Accessor<boolean>;
reset: () => void;
};
//#endregion
export { type SwipeAxis, type SwipeOptions, type SwipeResult, createSwipe, resolveSwipe };
import { createSignal, onCleanup } from "solid-js";
import { Swipeable, resolveSwipe } from "@neodrag/core/swipe";
//#region src/swipe.ts
/**
* Swipe-to-dismiss primitive — a thin adapter over the core `Swipeable` binder. Put `ref` on the
* element; read `offset()` / `isDismissed()`. Released past `threshold` the element flies out and
* fires `onDismiss`, otherwise it springs back.
*/
function createSwipe(options = {}) {
let inst = null;
let dispose = null;
const [offset, set_offset] = createSignal({
x: 0,
y: 0
});
const [isDismissed, set_dismissed] = createSignal(false);
const ref = (node) => {
dispose?.();
dispose = null;
inst = null;
if (node) {
inst = new Swipeable({
...options,
onChange: (s) => {
set_offset(s.offset);
set_dismissed(s.dismissed);
}
});
dispose = inst.attach(node);
}
};
onCleanup(() => {
dispose?.();
dispose = null;
inst = null;
});
const reset = () => inst?.reset();
return {
ref,
offset,
isDismissed,
reset
};
}
//#endregion
export { createSwipe, resolveSwipe };
+25
-13

@@ -1,14 +0,26 @@

import * as _neodrag_core from '@neodrag/core';
import { Plugin, PluginResolver, DragEventData, Compartment } from '@neodrag/core/plugins';
export * from '@neodrag/core/plugins';
import { Accessor } from 'solid-js';
import { AriaDragAnnounce, AriaDragOptions, AutoScrollOptions, Axis, BoundsInput, DragEventData, DragOptions, DragOptions as DragOptions$1, DragPlugin, GhostOptions, MagneticOptions, MagneticSpring, MarqueeOptions, ScrollLockOptions, SnapGuidesOptions, ariaDrag, autoScroll, ghost, haptics, magnetic, marqueeSelect, onMove, scrollLock, snapGuides } from "@neodrag/core";
import { Accessor } from "solid-js";
import { Room } from "@neodrag/core/collab";
interface DragState extends DragEventData {
isDragging: boolean;
}
declare const useDraggable: (element: Accessor<HTMLElement | SVGElement | null | undefined>, plugins?: Accessor<Plugin[]> | ReturnType<PluginResolver>) => Accessor<DragState>;
declare function createCompartment(reactive: ConstructorParameters<typeof Compartment>[0]): Compartment;
declare const instances: Map<HTMLElement | SVGElement, _neodrag_core.DraggableInstance>;
export { createCompartment, instances, useDraggable };
//#region src/index.d.ts
type Ref = (node: HTMLElement) => void;
/**
* Solid v3 — `create*` primitives returning a `ref` setter + reactive state accessors.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and the draggable auto-joins
* that room on mount and leaves on unmount — its committed position syncs as a `drag` op.
*/
declare function createDraggable(options?: DragOptions$1 & {
room?: Room;
}): {
ref: Ref;
isDragging: Accessor<boolean>;
handle: (opts?: {
priority?: number;
}) => Ref;
cancel: (opts?: {
priority?: number;
}) => Ref;
};
//#endregion
export { type AriaDragAnnounce, type AriaDragOptions, type AutoScrollOptions, type Axis, type BoundsInput, type DragEventData, type DragOptions, type DragPlugin, type GhostOptions, type MagneticOptions, type MagneticSpring, type MarqueeOptions, type ScrollLockOptions, type SnapGuidesOptions, ariaDrag, autoScroll, createDraggable, ghost, haptics, magnetic, marqueeSelect, onMove, scrollLock, snapGuides };

@@ -1,69 +0,75 @@

import { DraggableFactory, DEFAULTS } from '@neodrag/core';
import { unstable_definePlugin, Compartment } from '@neodrag/core/plugins';
export * from '@neodrag/core/plugins';
import { createSignal, createEffect, untrack, createRenderEffect } from 'solid-js';
// src/index.ts
var draggable_factory = new DraggableFactory(DEFAULTS);
var default_drag_state = {
offset: { x: 0, y: 0 },
rootNode: null,
currentNode: null,
isDragging: false,
event: null
};
var state_sync = unstable_definePlugin((set_state) => {
const update_state = (ctx, event, overrides = {}) => ctx.effect.immediate(
() => set_state((prev) => ({
...prev,
offset: { ...ctx.offset },
rootNode: ctx.rootNode,
currentNode: ctx.currentlyDraggedNode,
event,
...overrides
}))
);
return {
name: "sss",
// solid-state-sync
priority: -1e3,
cancelable: false,
start: (ctx, _state, event) => update_state(ctx, event, { isDragging: true }),
drag: (ctx, _state, event) => update_state(ctx, event),
end: (ctx, _state, event) => update_state(ctx, event, { isDragging: false })
};
});
function resolve_plugins(plugins, state_sync_plugin) {
const p = typeof plugins === "function" ? plugins() : () => plugins;
if (typeof p === "function") {
return () => p().concat(state_sync_plugin);
} else {
return p.concat(state_sync_plugin);
}
import { n as useRoomBinding } from "./_room-context-Cg1LI93i.js";
import { Draggable, ariaDrag, autoScroll, ghost, haptics, magnetic, marqueeSelect, onMove, scrollLock, snapGuides } from "@neodrag/core";
import { createEffect, createSignal, onCleanup } from "solid-js";
//#region src/index.ts
/**
* Solid v3 — `create*` primitives returning a `ref` setter + reactive state accessors.
*
* **Collab:** pass an `id` (and a `room`, or mount a `<RoomProvider>`) and the draggable auto-joins
* that room on mount and leaves on unmount — its committed position syncs as a `drag` op.
*/
function createDraggable(options = {}) {
const [isDragging, set_dragging] = createSignal(false);
let inst = null;
const { join, leave } = useRoomBinding(options.room);
const handles = /* @__PURE__ */ new Map();
const cancels = /* @__PURE__ */ new Map();
const position_two_way = Boolean(Object.getOwnPropertyDescriptor(options, "position")?.set);
const build = () => ({
...options,
onDragStart: (e) => {
set_dragging(true);
options.onDragStart?.(e);
},
onDrag: (e) => {
if (position_two_way) options.position = e.offset;
options.onDrag?.(e);
},
onDragEnd: (e) => {
set_dragging(false);
options.onDragEnd?.(e);
}
});
const ref = (node) => {
leave();
inst?.destroy();
inst = new Draggable(node, build());
join(inst, options.id);
for (const [n, e] of handles) e.off = inst.registerHandle(n, { priority: e.priority });
for (const [n, e] of cancels) e.off = inst.registerCancel(n, { priority: e.priority });
onCleanup(() => {
leave();
for (const e of handles.values()) e.off = null;
for (const e of cancels.values()) e.off = null;
inst?.destroy();
inst = null;
});
};
createEffect(() => {
const next = build();
inst?.update(next);
});
const marker = (map, is_handle, priority) => {
return (node) => {
const entry = {
priority,
off: inst ? is_handle ? inst.registerHandle(node, { priority }) : inst.registerCancel(node, { priority }) : null
};
map.set(node, entry);
onCleanup(() => {
entry.off?.();
map.delete(node);
});
};
};
const handle = (o) => marker(handles, true, o?.priority ?? 0);
const cancel = (o) => marker(cancels, false, o?.priority ?? 0);
return {
ref,
isDragging,
handle,
cancel
};
}
function wrapper(draggableFactory) {
return (element, plugins = () => []) => {
const [drag_state, set_drag_state] = createSignal(default_drag_state);
const state_sync_plugin = state_sync(set_drag_state);
createEffect(() => {
const node = element();
if (!node) return;
return draggableFactory.draggable(
node,
untrack(() => resolve_plugins(plugins, state_sync_plugin))
);
});
return drag_state;
};
}
var useDraggable = wrapper(draggable_factory);
function createCompartment(reactive) {
const compartment = new Compartment(reactive);
createRenderEffect(() => {
compartment.current = reactive?.();
});
return compartment;
}
var instances = draggable_factory.instances;
export { createCompartment, instances, useDraggable };
//#endregion
export { ariaDrag, autoScroll, createDraggable, ghost, haptics, magnetic, marqueeSelect, onMove, scrollLock, snapGuides };
{
"name": "@neodrag/solid",
"version": "3.0.0-next.8",
"version": "3.0.0-next.10",
"description": "SolidJS library to add dragging to your apps 😉",
"type": "module",
"keywords": [
"drag",
"draggable",
"neodrag",
"performant",
"react-draggable",
"small",
"solid",
"tiny"
],
"homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/solid#readme",
"bugs": {
"url": "https://github.com/PuruVJ/neodrag/issues"
},
"license": "MIT",
"author": "Puru Vijay",
"repository": {
"type": "git",
"url": "git+https://github.com/PuruVJ/neodrag.git"
},
"files": [
"dist/*"
],
"type": "module",
"sideEffects": false,

@@ -16,32 +36,65 @@ "exports": {

},
"./collab": {
"types": "./dist/collab.d.ts",
"import": "./dist/collab.js",
"default": "./dist/collab.js"
},
"./sortable": {
"types": "./dist/sortable.d.ts",
"import": "./dist/sortable.js",
"default": "./dist/sortable.js"
},
"./resize": {
"types": "./dist/resize.d.ts",
"import": "./dist/resize.js",
"default": "./dist/resize.js"
},
"./rotate": {
"types": "./dist/rotate.d.ts",
"import": "./dist/rotate.js",
"default": "./dist/rotate.js"
},
"./drop": {
"types": "./dist/drop.d.ts",
"import": "./dist/drop.js",
"default": "./dist/drop.js"
},
"./splitpane": {
"types": "./dist/splitpane.d.ts",
"import": "./dist/splitpane.js",
"default": "./dist/splitpane.js"
},
"./panzoom": {
"types": "./dist/panzoom.d.ts",
"import": "./dist/panzoom.js",
"default": "./dist/panzoom.js"
},
"./swipe": {
"types": "./dist/swipe.d.ts",
"import": "./dist/swipe.js",
"default": "./dist/swipe.js"
},
"./select": {
"types": "./dist/select.d.ts",
"import": "./dist/select.js",
"default": "./dist/select.js"
},
"./package.json": "./package.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/PuruVJ/neodrag.git"
"devDependencies": {
"@vitest/browser": "^3.2.4",
"playwright": "^1.53.1",
"solid-js": "^1.9.7",
"vite-plugin-solid": "^2.11.6",
"vitest": "^3.2.4",
"@neodrag/core": "3.0.0-next.10"
},
"keywords": [
"draggable",
"solid",
"react-draggable",
"drag",
"neodrag",
"small",
"tiny",
"performant",
"neodrag"
],
"author": "Puru Vijay",
"license": "MIT",
"bugs": {
"url": "https://github.com/PuruVJ/neodrag/issues"
},
"homepage": "https://github.com/PuruVJ/neodrag/tree/main/packages/solid#readme",
"peerDependencies": {
"solid-js": "^1.0.0",
"@neodrag/core": "3.0.0-next.8"
"@neodrag/core": "3.0.0-next.10"
},
"scripts": {
"compile": "tsup"
"compile": "vp pack",
"test": "vitest run"
}
}
+52
-27

@@ -26,4 +26,4 @@ <p align="center">

- ⚡ **Performance** - Event delegation, pointer capture, optimized for modern browsers
- 🎯 **SolidJS Native** - Built for SolidJS with `useDraggable` hook
- 🔄 **Reactive** - `createCompartment` for reactive plugin updates
- 🎯 **SolidJS Native** - `createDraggable`, `createDroppable`, `createSortable` follow Solid naming (`create*` / `with*`)
- 🔄 **Reactive** - pass `() => plugins`; the wrapper reconciles automatically

@@ -41,10 +41,11 @@ # Installing

```tsx
import { useDraggable } from '@neodrag/solid';
import { createDraggable } from '@neodrag/solid';
export const App: Component = () => {
const [draggableRef, setDraggableRef] = createSignal<HTMLElement | null>(null);
const [draggableRef, setDraggableRef] =
createSignal<HTMLElement | null>(null);
useDraggable(draggableRef);
createDraggable(draggableRef);
return <div ref={setDraggableRef}>You can drag me</div>;
return <div ref={setDraggableRef}>You can drag me</div>;
};

@@ -56,10 +57,11 @@ ```

```tsx
import { useDraggable, axis, grid } from '@neodrag/solid';
import { createDraggable, axis, grid } from '@neodrag/solid';
export const App: Component = () => {
const [draggableRef, setDraggableRef] = createSignal<HTMLElement | null>(null);
const [draggableRef, setDraggableRef] =
createSignal<HTMLElement | null>(null);
useDraggable(draggableRef, [axis('x'), grid([10, 10])]);
createDraggable(draggableRef, [axis('x'), grid([10, 10])]);
return <div ref={setDraggableRef}>Horizontal grid snapping</div>;
return <div ref={setDraggableRef}>Horizontal grid snapping</div>;
};

@@ -71,11 +73,18 @@ ```

```tsx
import { useDraggable, axis, bounds, BoundsFrom, type Plugin } from '@neodrag/solid';
import {
createDraggable,
axis,
bounds,
BoundsFrom,
type Plugin,
} from '@neodrag/solid';
export const App: Component = () => {
const [draggableRef, setDraggableRef] = createSignal<HTMLElement | null>(null);
const [draggableRef, setDraggableRef] =
createSignal<HTMLElement | null>(null);
const plugins: Plugin[] = [axis('y'), bounds(BoundsFrom.parent())];
useDraggable(draggableRef, plugins);
const plugins: Plugin[] = [axis('y'), bounds(BoundsFrom.parent())];
createDraggable(draggableRef, plugins);
return <div ref={setDraggableRef}>Type-safe dragging</div>;
return <div ref={setDraggableRef}>Type-safe dragging</div>;
};

@@ -87,22 +96,25 @@ ```

```tsx
import { useDraggable } from '@neodrag/solid';
import { createDraggable } from '@neodrag/solid';
export const App: Component = () => {
const [draggableRef, setDraggableRef] = createSignal<HTMLElement | null>(null);
const dragState = useDraggable(draggableRef);
const [draggableRef, setDraggableRef] =
createSignal<HTMLElement | null>(null);
const dragState = createDraggable(draggableRef);
createEffect(() => {
console.log('Position:', dragState().offset);
console.log('Is dragging:', dragState().isDragging);
});
createEffect(() => {
console.log('Position:', dragState().offset);
console.log('Is dragging:', dragState().isDragging);
});
return <div ref={setDraggableRef}>Check console while dragging</div>;
return (
<div ref={setDraggableRef}>Check console while dragging</div>
);
};
```
Reactive plugins with createCompartment
Reactive plugins with reactive plugin factories
```tsx
import { createSignal } from 'solid-js';
import { useDraggable, axis, createCompartment } from '@neodrag/solid';
import { createDraggable, axis } from '@neodrag/solid';

@@ -113,5 +125,4 @@ export const App: Component = () => {

const axisCompartment = createCompartment(() => axis(currentAxis()));
useDraggable(draggableRef, [axisCompartment]);
createDraggable(draggableRef, [axisreactive plugin factories]);

@@ -127,2 +138,16 @@ return (

## Sortable lists
```tsx
import { createSortable, createSortableItem } from '@neodrag/solid/sortable';
const { list, dropRef } = createSortable({
items,
keyBy: (i) => i.id,
onReorder: setItems,
});
// <ul ref={dropRef}> … createSortableItem(list, item.id) per row
```
<a href="https://next.neodrag.dev/docs/solid" style="font-size: 2rem">Read the docs</a>

@@ -129,0 +154,0 @@