dnd-kit-svelte

repository·experimental·Indexed 43 days ago

https://github.com/hanielu/dnd-kit-svelte

A Svelte port of the dnd-kit library providing a lightweight and accessible toolkit for drag-and-drop functionality. It adapts the original React-based API for Svelte's reactivity system, featuring hooks like useDraggable, useDroppable, and useSortable, and utilizing Svelte 5 runes for reactive state management.

Tokens
7K
Snippets
35
Records
42
Agent score
56%

What's inside dnd-kit-svelte

  1. Accessing returned hook state via .current

    experimental

    In Svelte, hooks do not trigger component re-renders in the same way React hooks do. To ensure you always access the most up-to-date state, all non-function properties returned from hooks (such as isDragging, isDropping, isDragSource, and isDropTarget) must be accessed using a .current getter.

    This pattern applies to:

    • useDraggable
    • useDroppable
    • useSortable
    <script>
    	import {useSortable} from '@dnd-kit-svelte/svelte/sortable';
    
    	const {ref, isDragging} = useSortable({id});
    </script>
    
    <div {@attach ref}>
    	{isDragging.current ? 'Dragging' : 'Not dragging'}
    </div>
  2. Core Concepts of dnd-kit-svelte

    experimental

    The library maintains feature parity with the original dnd-kit. The core mental model consists of:

    • Draggable elements: Items that can be moved.
    • Droppable areas: Regions that can receive draggable items.
    • DragDropProvider: The provider component that manages the drag-and-drop context.
    • Sensors: Input handlers (e.g., mouse, touch, keyboard).
    • Modifiers: Functions to constrain drag behavior.
    • Collision detection: Algorithms to determine when a draggable overlaps a droppable.
  3. How to handle reactive inputs in dnd-kit hooks

    experimental

    Because Svelte handles reactivity differently than React, you should pass a function to any field in hooks (like useSortable, useDraggable, etc.) that needs to be reactive. This ensures the hook always accesses the latest value from Svelte's reactive scope.

    When passing data that depends on reactive state, wrap it in an arrow function.

    import {useSortable} from '@dnd-kit-svelte/svelte/sortable';
    
    useSortable({
    	// Static value
    	id: item.id,
    	// Reactive value using a function
    	data: () => item, // Access reactive state value
    });
  4. Create a new Svelte project with sv

    experimental

    Use the sv CLI to scaffold a new Svelte project. You can either initialize the project in your current directory or specify a new directory name.

    # create a new project in the current directory
    npx sv create
    
    # create a new project in my-app
    npx sv create my-app
  5. Develop and build your Svelte project

    experimental

    After creating your project and installing dependencies (npm install, pnpm install, or yarn), use the following commands to manage your development lifecycle:

    • Development: Run npm run dev to start the development server. Use npm run dev -- --open to automatically open the app in a new browser tab.
    • Production Build: Run npm run build to create a production-ready version of your application.
    • Preview Build: Run npm run preview to locally preview the production build you just created.

    Note: For deployment, you may need to install a SvelteKit adapter specific to your target environment.

    # Start development server
    npm run dev
    
    # Start development server and open browser
    npm run dev -- --open
    
    # Build for production
    npm run build
    
    # Preview production build
    npm run preview
  6. Use Getter and MaybeGetter for reactive inputs

    experimental

    To support Svelte's reactivity, many inputs in dnd-kit-svelte accept either a direct value of type T or a Getter<T> (a function that returns T). This allows you to pass reactive state via a function, ensuring the library can track changes to the value.

    • Getter<T>: A function type () => T.
    • MaybeGetter<T>: A type that accepts either T or Getter<T>. If T is already a function, it specifically expects a Getter pattern to avoid ambiguity.
    • MaybeGetterObject<T>: A utility that transforms an object type so that all its properties are MaybeGetter compatible.
    // Example of a Getter
    const myGetter = () => someReactiveState;
    
    // Example of a MaybeGetter (can be the value or the getter)
    const value: MaybeGetter<string> = "hello";
    const reactiveValue: MaybeGetter<string> = () => someReactiveState;
    
    // Example of a MaybeGetterObject
    interface Config {
      id: string;
      count: number;
    }
    
    const config: MaybeGetterObject<Config> = {
      id: "static-id",
      count: () => reactiveCount
    };
  7. Understand the Lens and WritableLens types

    experimental

    Lenses are built on top of the Box abstraction.

    • Lens<T>: A read-only reactive object that exposes a current property of type T.
    • WritableLens<T>: An extension of Lens<T> that allows the current property to be assigned a new value. It is identified by the internal WritableLensSymbol.
    export type Lens<T> = Box<T> & {
    	readonly current: T;
    };
    
    export type WritableLens<T> = Lens<T> & {
    	[WritableLensSymbol]: true;
    };
  8. Resolve a value or getter using resolve()

    experimental

    The resolve<T>(value: MaybeGetter<T>): T function handles values that might be passed as either a direct value or a getter function (a function that returns the value). If the input is a function, it is invoked to retrieve the actual value; otherwise, the value is returned as-is.

    import { resolve } from '@dnd-kit-svelte/svelte';
    
    // If value is a direct value
    const val1 = resolve(10);
    // val1 is 10
    
    // If value is a getter function
    const val2 = resolve(() => 20);
    // val2 is 20
  9. Use the box primitive for reactive containers

    experimental

    The box and isBox functions provide primitives for creating and identifying reactive containers (a Box). A Box acts as a wrapper around a value to manage its reactivity lifecycle.

    Available types:

    • Box<T>: The type representing the reactive container.
    import { box, isBox } from '@dnd-kit-svelte/svelte';
    
    // Example usage (conceptual based on exports):
    // const myBox = box(initialValue);
    // if (isBox(myBox)) { ... }