Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module.exports = {
extends: [
'react-app',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
'prettier',
],
};
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.DS_Store
node_modules
.turbo
.idea/
*.log
.next
dist
Expand All @@ -17,3 +18,4 @@ storybook-static/
/packages/**/*.cjs.map
/packages/**/*.d.ts
/packages/**/*.d.cts
!/packages/**/.eslintrc.cjs
Binary file modified bun.lockb
Binary file not shown.
13 changes: 13 additions & 0 deletions config/typescript/vue.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Vue",
"extends": "./base.json",
"compilerOptions": {
"lib": ["dom", "ES2019"],
"target": "es6",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"importHelpers": true,
"sourceMap": true
}
}
5 changes: 5 additions & 0 deletions packages/react/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
extends: [
'react-app',
],
};
2 changes: 1 addition & 1 deletion packages/react/src/core/hooks/useDragDropMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {useEffect} from 'react';
import type {DragDropEvents, Data} from '@dnd-kit/abstract';
import type {Draggable, Droppable, DragDropManager} from '@dnd-kit/dom';

import {useDragDropManager} from './useDragDropManager.js';
import {useDragDropManager} from './useDragDropManager.ts';
import {CleanupFunction} from '@dnd-kit/state';

// Manual mapping of event names to handler names
Expand Down
6 changes: 6 additions & 0 deletions packages/vue/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
extends: [
'plugin:vue/recommended',
'prettier',
],
};
1 change: 1 addition & 0 deletions packages/vue/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @dnd-kit/vue
17 changes: 17 additions & 0 deletions packages/vue/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# @dnd-kit/vue

[![Stable release](https://img.shields.io/npm/v/@dnd-kit/vue.svg)](https://npm.im/@dnd-kit/vue)

The Vue layer for @dnd-kit, built on top of @dnd-kit/dom.

## Installation

To get started, install the `@dnd-kit/vue` package via npm or yarn:

```
npm install @dnd-kit/vue
```

## Usage

Visit [docs.dndkit.com](https://docs.dndkit.com) to learn how to get started with @dnd-kit.
78 changes: 78 additions & 0 deletions packages/vue/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"name": "@dnd-kit/vue",
"version": "0.1.0",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving a note here for us to make sure we sync this with whatever the latest versions are of other packages before merging

"main": "./index.cjs",
"module": "./index.js",
"type": "module",
"types": "./index.d.ts",
"sideEffects": false,
"license": "MIT",
"files": [
"LICENSE",
"README.md",
"index.js",
"index.d.ts",
"index.cjs",
"composables.js",
"composables.d.ts",
"composables.cjs",
"sortable.js",
"sortable.d.ts",
"sortable.cjs",
"utilities.js",
"utilities.d.ts",
"utilities.cjs"
],
"exports": {
".": {
"types": "./index.d.ts",
"import": "./index.js",
"require": "./index.cjs"
},
"./composables": {
"types": "./composables.d.ts",
"import": "./composables.js",
"require": "./composables.cjs"
},
"./sortable": {
"types": "./sortable.d.ts",
"import": "./sortable.js",
"require": "./sortable.cjs"
},
"./utilities": {
"types": "./utilities.d.ts",
"import": "./utilities.js",
"require": "./utilities.cjs"
}
},
"scripts": {
"build": "bun build:utilities && bun build:composables && bun build:core && bun build:sortable",
"build:core": "tsup src/core/index.ts",
"build:composables": "tsup --entry.composables src/composables/index.ts",
"build:sortable": "tsup --entry.sortable src/sortable/index.ts",
"build:utilities": "tsup --entry.utilities src/utilities/index.ts",
"dev": "bun build:utilities --watch & bun build:composables --watch & bun build:core --watch & bun build:sortable --watch",
"lint": "TIMING=1 eslint src/**/*.ts* --fix",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
},
"dependencies": {
"@dnd-kit/abstract": "^0.1.0",
"@dnd-kit/dom": "^0.1.0",
"@dnd-kit/state": "^0.1.0",
"tslib": "^2.6.2"
},
"peerDependencies": {
"vue": "^3.5.0"
},
"devDependencies": {
"@dnd-kit/eslint-config": "*",
"eslint": "^8.38.0",
"eslint-plugin-vue": "^10.0.0",
"tsup": "~8.2.0",
"typescript": "^5.5.2",
"vue": "^3.5.0"
},
"publishConfig": {
"access": "public"
}
}
1 change: 1 addition & 0 deletions packages/vue/src/composables/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useDeepSignal.ts';
58 changes: 58 additions & 0 deletions packages/vue/src/composables/useDeepSignal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {effect, untracked} from '@dnd-kit/state';
import type {ComputedRef, MaybeRefOrGetter} from 'vue';
import {computed, onWatcherCleanup, ref, toValue, watchEffect} from 'vue';

/** Trigger a recompute when reading signal properties of an object. */
export function useDeepSignal<T extends object | null | undefined>(
target: MaybeRefOrGetter<T>
): ComputedRef<T> {
const tracked = ref(new Map<string | symbol, any>());
const dirty = ref(0);

watchEffect(() => {
const _target = toValue(target);
if (!_target) {
tracked.value.clear();
return;
}

onWatcherCleanup(
effect(() => {
let stale = false;

for (const entry of tracked.value) {
const [key] = entry;
const value = untracked(() => entry[1]);
const latestValue = (_target as any)[key];

if (value !== latestValue) {
stale = true;
tracked.value.set(key, latestValue);
}
}

if (stale) {
dirty.value++;
}
})
);
});

return computed(() => {
const _target = toValue(target);

void dirty.value;

return _target
? new Proxy(_target, {
get(target, key) {
const value = (target as any)[key];

tracked.value.set(key, value);

return value;
},
})
: _target;
});
}
5 changes: 5 additions & 0 deletions packages/vue/src/core/composables/useDragDropManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {injectDragDropContext} from '../context/context.ts';

export function useDragDropManager() {
return injectDragDropContext();
}
76 changes: 76 additions & 0 deletions packages/vue/src/core/composables/useDragDropMonitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type {DragDropEvents, Data} from '@dnd-kit/abstract';
import type {Draggable, Droppable, DragDropManager} from '@dnd-kit/dom';
import {onWatcherCleanup, watchEffect} from 'vue';

import {useDragDropManager} from './useDragDropManager.ts';
import {CleanupFunction} from '@dnd-kit/state';

// Manual mapping of event names to handler names
type DragDropEventMap = {
beforedragstart: 'onBeforeDragStart';
};

// Automatically generate the event handler name from the event name
type EventHandlerName<T extends string> = T extends keyof DragDropEventMap
? DragDropEventMap[T]
: T extends `drag${infer Second}${infer Rest}`
? `onDrag${Uppercase<Second>}${Rest}`
: `on${Capitalize<T>}`;

/**
* Type for all possible event handlers
*/
type Events<T extends Data> = DragDropEvents<
Draggable<T>,
Droppable<T>,
DragDropManager<Draggable<T>, Droppable<T>>
>;

export type EventHandlers<T extends Data = Data> = {
[K in keyof Events<T> as EventHandlerName<K>]?: Events<T>[K];
};

/**
* Composable to monitor drag and drop events anywhere within a DragDropProvider
* @param handlers Object containing event handlers for drag and drop events
*/
export function useDragDropMonitor<T extends Data = Data>(
handlers: EventHandlers<T>
): void {
const manager = useDragDropManager();

watchEffect(() => {
if (!manager) {
if (process.env.NODE_ENV !== 'production') {
console.warn(
'useDragDropMonitor composable was called outside of a DragDropProvider. ' +
'Make sure your app is wrapped in a DragDropProvider component.'
);
}
return;
}

const cleanupFns = Object.entries(handlers).reduce<CleanupFunction[]>(
(acc, [handlerName, handler]) => {
if (handler) {
// Convert handler name (e.g. 'onDragStart') to event name (e.g. 'dragstart')
const eventName = handlerName
.replace(/^on/, '')
.toLowerCase() as keyof Events<T>;

const unsubscribe = manager.value.monitor.addEventListener(
eventName,
handler
);

acc.push(unsubscribe);
}

return acc;
},
[]
);

onWatcherCleanup(() => cleanupFns.forEach((cleanup) => cleanup()));
});
}
14 changes: 14 additions & 0 deletions packages/vue/src/core/composables/useDragOperation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {computed} from 'vue';

import {useDragDropManager} from './useDragDropManager.ts';

export function useDragOperation() {
const manager = useDragDropManager();
const source = computed(() => manager.value.dragOperation.source);
const target = computed(() => manager.value.dragOperation.target);

return {
source,
target,
};
}
23 changes: 23 additions & 0 deletions packages/vue/src/core/composables/useInstance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type {DragDropManager} from '@dnd-kit/dom';
import type {CleanupFunction} from '@dnd-kit/state';
import {computed, onWatcherCleanup, shallowRef, watchEffect} from 'vue';

import {useDragDropManager} from './useDragDropManager.ts';

export interface Instance<T extends DragDropManager = DragDropManager> {
manager: T | undefined;
register(): CleanupFunction | void;
}

export function useInstance<T extends Instance>(
initializer: (manager: DragDropManager | undefined) => T
) {
const manager = useDragDropManager() ?? undefined;
const instance = shallowRef(initializer(manager.value));

watchEffect(() => {
onWatcherCleanup(instance.value.register(manager.value));
});

return computed(() => instance.value);
}
Loading
Loading