Giter Club home page Giter Club logo

vue-draggable-plus's Introduction

🤺 About Me

Vue & VueUse team member

  • 👋 Hi, I'm 远方os, an ordinary frontend programmer who enjoys exploring various exciting things, sharing knowledge, and engaging in discussions.
  • Every bug is an opportunity for you to become a better developer. Don't be afraid of making mistakes; instead, learn from them and extract valuable lessons.

🤾‍♂️ My Open Source Projects

  • vue-draggable-plus - A draggable component compatible with both Vue 2 and Vue 3. It supports features such as drag-and-drop sorting, copying, deleting, adding, moving, animations, and event handling.
  • v-scale-screen - A responsive solution for large screens compatible with Vue 2 and Vue 3. It offers customization options for scaling ratio, scaling center, scaling animations, animation duration, and animation curves.

🛠 Technology Stack

Vue.js JavaScript TypeScript Vite Node.js

☎️ Contact Me

  • Email
  • VX: yuanfang0353 (备注GitHub)

🏆 Github Status

vue-draggable-plus's People

Contributors

aaron-zon avatar alfred-skyblue avatar chouchouji avatar gsmith-daed avatar heniker avatar laurens94 avatar ogios avatar pkc918 avatar tangjian1891 avatar wjp980108 avatar xuguojing-xgj avatar zclsx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vue-draggable-plus's Issues

slow scroll speed

Thank you for the wonderful library.
I'm applying it to the website I'm currently developing.
However, if when I drag an item out of the current screen,
the scrolling is too slow. Do you know how to solve this in component usage??


Oh, I can use the scroll-speed attribute the same as sortablejs. :)

Target and Item - how to?

Hi all,
I've been looking everywhere but it seems not possible to get info regarding the item being dragged, the source and the destination at the same time.
I have a situation like this:

<script setup lang="ts">
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout.vue";
import { Head, router } from "@inertiajs/vue3";
import { ref } from "vue";
import { VueDraggable } from "vue-draggable-plus";
const props = defineProps<{
    orders: App.Models.Order[];
    statuses: App.Models.Status[];
}>();
const statusOrders = ref([] as App.Models.Order[][]);
props.statuses.forEach((status) =>
    statusOrders.value.push(
        props.orders.filter((order) => order.status_id == status.id)
    )
);

defineOptions({ layout: AuthenticatedLayout });

function handleDrag(item: App.Models.Order) {
    statusOrders.value.forEach((el, index) => {
        if (el.map((e) => e.id).indexOf(item.id) >= 0) {
            item.status_id = props.statuses[index].id;
            router.patch(`/order/${item.id}`, item as Record<string, any>);
        }
    });
}
</script>

<template>
            <div
                class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg flex"
            >
                <div v-for="(status, index) in statuses">
                    {{ status.name }}
                    <VueDraggable
                        class="flex flex-col gap-2 p-4 w-300px h-300px m-auto bg-gray-500/5 rounded overflow-auto"
                        v-model="statusOrders[index]"
                        :animation="150"
                        ghostClass="ghost"
                        group="people"
                        :key="status.id"
                        :id="'status' + index"
                        tag="div"
                    >
                        <div
                            v-for="item in statusOrders[index]"
                            :key="item.id"
                            class="cursor-move h-30 bg-gray-500/5 rounded p-3"
                            @dragend="handleDrag(item)"
                        >
                            {{ item.code }}
                        </div>
                    </VueDraggable>
                </div>
            </div>

</template>

As you can see, I need to update the order status depending on where I drag the item. If I had the target div ID, I would be able to immediately retrieve the corresponding status. However, currently, the only way is to loop through the array and check for the dragged item. Additionally, the only way I can obtain the item is by using the 'ondragend' event on the dragged item.

Do you have any suggestions? Am I doing something wrong?

Thanks

How to specify sub-draggable item

I have the following scenario:
image

Items 1 and 2 are the draggable targets, within the same group.
Item 3 is the actual child that I want to drag from target container 2 to 1.
Notice that in container 2 I add a different background as well as the name of the child, but I don't want those to be dragged. I want only item 3 to be dragged.

Today, the entire child goes dragged and dropped, like this:
image

When I finish dropping the element (mouse up), everything goes ok, because the object being transported is correct. The problem is just visual during dragging and dropping.

Is there any way to do that today?

Strange effect when dragging to an empty list

ezgif com-optimize

I noticed, when I have a list group and drag an item to an empty list, there is a fly back effect on the item dragged. This does not happen aways.

  1. When I drag the item slowly the issue does not occur.
  2. When both list have at least one item, the issue does not occur.

Any idea what I can do with it? Thanks

Vitepress dependency outdated

I noticed when loading a page like https://alfred-skyblue.github.io/vue-draggable-plus/en/ the page does not load properly, even though that is the correct url, which can you get to by switching to English using the language switcher.

I was able to resolve the issue locally by simply upgrading vitepress to 1.0.0-rc.20 and vue to 3.3.4. However, the website seems a bit broken, the language switcher at the top right no longer exists, seemingly because the vitepress i18n api changed: https://vitepress.dev/guide/i18n#internationalization. I could not fix those because vitepress was still sort of using an older version, since @ruabick/md-demo-plugins also depends on an older vitepress version: dewfall123/ruabick#36.

clone有问题

<div class="main">
  <a-card title="可选控件" class="main-left">
    <template v-if="leftDraggableArray.length">
      <VueDraggable
        class="draggableWrap"
        v-model="leftDraggableArray"
        ghostClass="ghost"
        :group="{ name: 'people', pull: 'clone', put: false }"
        :sort="false"
        :clone="clone"
      >
        <div
          v-for="leftItem in leftDraggableArray"
          :key="leftItem.name"
          class="leftDraggableItem"
        >
          <icon-formula />
          {{ leftItem.type }}
        </div>
      </VueDraggable>
    </template>
  </a-card>

  <a-card title="任务设计器主体" class="main-center">
    <a-form class="center-form" :model="centerForm" :size="centerForm.config.size">
      <VueDraggable
        class="draggableWrap"
        v-model="centerForm.list"
        animation="150"
        group="people"
        ghostClass="centerGhost"
      >
        <template
          v-for="(centerFormItem, index) in centerForm.list"
          :key="centerFormItem.key"
        >
          <!-- 判断是否为删格布局 start-->
          <template v-if="centerFormItem.type == 'grid'">
            <a-row
              v-if="centerFormItem && centerFormItem.key"
              :gutter="
                centerFormItem.options.gutter ? centerFormItem.options.gutter : 0
              "
              :justify="centerFormItem.options.justify"
              :align="centerFormItem.options.align"
            >
              <a-col :span="8">
                <FormItem />
                gird 布局,暂时不做
              </a-col>
            </a-row>
          </template>
          <!-- 判断是否为删格布局 end-->

          <!-- 其他类型的元素,直接渲染为表单项  start-->
          <template v-else>
            <FormItem
              v-if="centerFormItem && centerFormItem.key"
              :centerFormItem="centerFormItem"
              :index="index"
              :centerForm="centerForm"
            />
            {{ centerFormItem.key }}
          </template>
          <!-- 其他类型的元素,直接渲染为表单项  end-->
        </template>
      </VueDraggable>
    </a-form>
  </a-card>

  <a-card title="属性配置" class="main-right"> 最右边 </a-card>
</div>
这个是我的代码然后clone 方法如下:

const clone = (element: any) => {
// 为拖拽到容器的元素添加唯一 key

// 给拖拽的元素添加唯一 key
element.key = new Date().getTime().toString() + "_" + Math.ceil(Math.random() * 99999);

return element; // 返回克隆的元素
};
我想每次拖动克隆给每个都加个唯一的key,
但发现一个问题,如果我多次克隆左边的同一项到右边,会导致key全部一样,然后导致绑值也是一样的了
f5f64476bc652cdce00a7d1079f6ed6

feature Request: Make compatible with vue-virtual-scroller

I'm currently trying to combine the vue-virtual-scroller with draggablePlus. Apparently the drag items can not be obtained if they are rendered within the dynamic scrollerItem.
My current setup is as follows:

<DynamicScroller page-mode class="scroller" :items="vScrollItems"
  :min-item-size="ticketHeight" key-field="docUUID"
  v-draggable="[vScrollItems, draggableDirectiveConf]">
  <template v-slot="{ item, index, active }">
    <DynamicScrollerItem :item="item" :active="active" :data-index="index" class="ticketDrag">
      <ticket-item :key="item.docUUID"
        :ticketItemId="item.docUUID" />
    </DynamicScrollerItem>
  </template>
</DynamicScroller>
const vScrollItems = [
  {
    docUUID:'77d8f970-bace-4d34-bcc0-6c932242e0ae'
  },
  {
    docUUID: '76d8f970-bace-4d34-bcc0-6c932242e0ae'
  },
  {
    docUUID: '77d8f970-bace-4d34-bcc0-9c932242e0ae'
  }
];
const ticketDrag = {
  name: 'ticketSort',
  pull: ['ticketSort'],
  put: ['ticketSort']
};
const draggableDirectiveConf = {
  animation: 150,
  target: '.ticketDrag',
  group: ticketDrag,
  handle: ".v-card-item__prepend"
}

whilst the ticket-item is a seperate component containing a vuetify card.
No Matter if I use target or el or place the class on the ticketItem or the scrollerItem, it always selects all items within the dynamicScroller for dragging, not each individually. Or am I doing something wrong here?

Regards

Weird Display issue and scrollSensitivity Now Working

I checked the API and tried To use the scrollsensitivity, but it didn't work

<VueDraggable
    v-model="formStructure[sectionIndex]"
    animation="150"
    ghostClass="ghost"
    chosenClass="ghost"
    dragClass="drag"
    handle=".handle"
    group="questions"
    @choose="sorting = true"
    @unchoose="sorting = false"
    :sort="true"
    :scrollSensitivity="75"
  >

I also tried

<VueDraggable
    ...
    scrollSensitivity="75"
  >

But neither seemed to have an effect

Another Issue I'm having is...

if I drag the last item and place it anywhere above, and then add a new item to the list, then the new item will be displayed at the second last position even though in the array, it's at the last position.

Example : http://feedme.bhaviljain.co.in, you'll need to login,
user : [email protected]
pass : Pa$$w0rd!

on the side drawer, click survey builder

How to use with props?

In my use case, my component gets the array from a parent component in a prop. Isn't this likely more common in real-world usage than having a data array? What is the best way to use <VueDraggable> with a prop? This might be a something to document or an example to show...

All the examples use v-model, but when I tried that, I get this error:

[plugin:vite:vue] v-model cannot be used on a prop, because local prop bindings are not writable.
Use a v-bind binding combined with a v-on listener that emits update:x event instead.

Which does make sense. If I try to use v-bind instead, I get:

caught (in promise) DOMException: Failed to execute 'setAttribute' on 'Element': '0' is not a valid attribute name.
    at patchAttr (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:8103:10)
    at patchProp (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:8234:5)
    at mountElement (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:5786:11)
    at processElement (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:5764:7)
    at patch (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:5698:11)
    at ReactiveEffect.componentUpdateFn [as fn] (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:6128:11)
    at ReactiveEffect.run (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:405:19)
    at instance.update (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:6220:52)
    at setupRenderEffect (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:6228:5)
    at mountComponent (http://localhost:5173/node_modules/.vite/deps/chunk-3NMN3MUW.js?v=08779242:6046:5)

Which is less straight-forward to interpret.

What I've ended up doing is to have a copy of the array prop and then have watchers on both the prop and the copy to update "the other one". But it feels like a hack...

Multi Drag

Does multidrag plugin from sortablejs works with this draggable-plus?

在使用<component>标签时嵌套内容拖动后控件会直接消失, 但是数据内容是正确的

// TestView.vue
<template>
  <div class="container">
    <VueDraggable
      class="left"
      :group="{ name: 'g1', pull: 'clone', put: false }"
      :clone="clone"
      :sort="false"
      animation="150"
      v-model="data"
    >
      <p v-for="(el, index) in data" :key="el.name + index">{{ el.name }}</p>
    </VueDraggable>
    <NestedDraggable v-model="list" class="right"></NestedDraggable>
  </div>
  <button @click="console.log(list)">log</button>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { VueDraggable } from 'vue-draggable-plus';
import NestedDraggable from './NestedComponent.vue';
import { v4 as uuidv4 } from 'uuid';
import data from './data';
import type { IComponentList } from './type';

const list = ref<IComponentList[]>([]);

const clone = (element: any) => {
  console.log('clone');
  let e = {
    key: uuidv4(),
    name: element.name,
    component: element.component,
    children: element.children
  };

  if (list.value.length === 0) {
    console.log('push');
    list.value.push(e);
    return;
  }

  return e;
};
</script>

<style lang="scss">
.container {
  display: flex;
  justify-content: space-between;
  height: 80vh;

  .left {
    width: 50%;
  }

  .right {
    width: 50%;
    background: green;
  }
}
</style>
// NestedComponent.vue
<template>
  <VueDraggable class="drag-area" v-model="modelValue" group="g1">
    <div v-for="(el, index) in modelValue" :key="el.name">
      <component :is="el.component" :index="index" v-model="modelValue"></component>
    </div>
  </VueDraggable>
</template>
<script setup lang="ts">
import { VueDraggable } from 'vue-draggable-plus';
import type { IComponentList } from './type';
// import TestComponent from './TestComponent.vue';

const modelValue = defineModel<IComponentList[]>({ required: true });
</script>
<style scoped>
.drag-area {
  min-height: 50px;
  outline: 1px dashed;
}
</style>
// TestComponent.vue
<template>
  <p>{{ modelValue[index].name }}</p>
  <NestedDraggable v-model="modelValue[index].children"></NestedDraggable>
  <br />
</template>

<script setup lang="ts">
import type { IComponentList } from './type';
import NestedDraggable from './NestedComponent.vue';

defineProps<{ index: number }>();

const modelValue = defineModel<IComponentList[]>({ required: true });
</script>
// data.ts
import type { IComponentList } from './type';

import TestComponentVue from './TestComponent.vue';

const data = [
  { key: '', name: '控件1', component: TestComponentVue },
  { key: '', name: '控件2', component: TestComponentVue },
  { key: '', name: '控件3', component: TestComponentVue, children: [{ key: '', name: '控件1', component: TestComponentVue }] },
  { key: '', name: '控件4', component: TestComponentVue },
  { key: '', name: '控件5', component: TestComponentVue }
];

export default data;
// type.ts
export interface IComponentList {
  key: string;
  name: string;
  component: any;
  children: IComponentList[];
}
image 如果将`NestedComponent.vue`中的``改为``就不会出现问题,不知道是什么特性引起的

Drag-and-Drop Issue between Two List Components Using Draggable-Plus and Sortable

There are two list components, A and B. Component B is utilizing draggable-plus, and they are not part of the same group. However, there are cases where they need to process list items from each other. Items can be dragged from component A and dropped into component B, triggering the onDrop event, and it works fine. However, dragging items from component B to component A is disabled.

When replacing draggable-plus with sortable, the drag-and-drop functionality works as expected. Is this issue related to draggable-plus? If so, what is the solution to enable drag-and-drop from component B to A while using draggable-plus?

没有类型提示

vue-demi模块在devDependencies中,而生成的component.d.ts中import('vue-demi'),导致最终VueDraggable的类型是any

Breaks after modifying the list later (e.g. async fetching)

I installed the latest version from npm into my vue 3 vuetify project, tried to use the directive method just like in the docs and while it all works perfect when doing just some statically defined data like in the docs, trying to do anything a little more advanced such as fetching the list items asynchronously (or just modifying the list value) breaks the draggable element and while you can still drag, it goes back to the original form once you release the item.

Example:

const list = ref([])
setTimeout(function() {
  list.value = [
    { id: 1, title: 'Item 1'},
    { id: 2, title: 'Item 2' },
    { id: 3, title: 'Item 3'},
  ]
}, 100)
<VList
    v-draggable="[list, { }]"
  >
    <VListItem
      v-for="item in list"
      :key="item.id"
      :title="item.title"
    />
  </VList>

Drag any element and let it go, the list will still be in the same order.
One workaround is to delay rendering of the VList component using v-if="list.length" but what in case I want to update the existing list?

View not updated

When I package the latest version with "^", the data is successfully updated after dragging, but the view is not updated.

嵌套的Draggable的内部无法触发拖动

我的代码如下,外部的触发函数changeGroupDrag没问题。我拖动外部的分组卡片时是可以触发的。但是分组卡片内部也是用的VueDraggable,其触发函数changeListDrag就一直不触发。

关于自定义clone的问题

类似低代码场景,我从左侧列表拖拽组件到右侧画布中,使用自定义克隆,当添加到右侧画布时,会自动将自定义克隆中返回的对象加入到右侧画布中model-value绑定的list对象中,但是我不想这么做,因为在添加数据前,我还需要对数据做一些处理,在添加到list数据中,这个如何实现呢?

添加完成后会触发onAdd方法,其中可以拿到添加的索引,目前是可以手动根据添加的索引将元素删除,在手动处理,放在list列表中,还有其他好的方法吗?

拖动有点问题

当我使用组件时候无法拖动

 <VueDraggable v-model="productImages" animation="150" handle=".product-image-header-drag">
<ul>
 <li  v-for="(item, index) in productImages" :key="index"></li>
</ul>
 </VueDraggable>


<script lang="ts" setup>
const productImages = ref<[]>([]);
</script >

当我使用指令的时候,数据无法排序,但是可以拖动,但我table tr 是正常的

<ul v-draggable="[productImages, { animation: 300, onUpdate: onUpdate, handle: '.product-image-header-drag' }]">
 <li  v-for="(item, index) in productImages" :key="index"></li>
</ul>


<script lang="ts" setup>
const productImages = ref<[]>([]);
</script >

测试发现,它无法对 ul li 元素起作用, 是我的使用方法不对吗? 还是它不支持 ul 元素

当我改成 div 结构的时候它生效了

还有一个问题,当我的数据从另外一个数组直接赋值的时候 它也是不生效

<script lang="ts" setup>
const productImages = ref<ProductModel.Image[]>([]);
const producOptions = ref<ProductModel.Options[]>([]);
const producVariants = ref<ProductModel.Variant[]>([]);

 //从服务器请求数据
  let value = await ProductService.getAsync();

  productImages.value = value.images;  //数据无法排序
  producOptions.value = value.options; //数据无法排序
  producVariants.value = value.variants; //正常 最后一个它既然正常

//该方法数据正常排序
   value.images.forEach(item => {
            productImages.value.push(item)
      })
value.options.forEach(item => {
            producOptions.value.push(item)
        })
  value.variants.forEach(item => {
            producVariants.value.push(item)
        })


</script >

这应该是vue内置机制问题,可以优化它的问题吗?

这是类

/**
 * 产品信息
 */
export namespace ProductModel {

    /**基类 */
    export class Base {

        /**
         * 产品描述
         */
        body_html: string = "";

        /**
         * 创建日期
         */
        created_at?: string;


        /**产品id */
        id: string = "";

        /**
         * 对象图片
         */
        images: Image[] = [];

        /**
         * 自定义产品属性。例如,大小、颜色和材料。每个产品最多可以有 3 个选项
         */
        options: Options[] = [];


        /**产品的分类 */
        product_type: string = "";


        /**发布时间 */
        published_at?: string;

        /**发布到销售点渠道 */
        published_scope: string = "";

        /**产品的状态  active=活跃 archived=下架 draft=草稿 */
        status: string = "active";

        /** 筛选和搜索的逗号分隔标记字符串 一个产品最多可以有 250 个标签。每个标签最多可包含 255 个字符。 */
        tags: string = "";

        /**产品名称 */
        title: string = "";

        /**修改时间 */
        updated_at?: string;

        /**多属性 */
        variants: Variant[] = [];

        /**厂商名称 */
        vendor: string = "";
    }

    /**商品图片 */
    export class Image {
        /**
         * 商品图片的唯一数字标识符。
         */
        id: string = "0";
        /**
         * 产品图像在列表中的顺序。第一个产品图片位于位置 1,是商品的“主”图片。
         */
        position: number = 0;

        /**
         * 产品图像在列表中的顺序。第一个产品图片位于位置 1,是商品的“主”图片。
         */
        product_id: string = "0";
        /**
         * 与图像关联的变体 ID 数组
         */
        variant_ids: string[] = [];
        /**
         * 图片地址
         */
        src: string = "";
        /**
         * 上传时确定的图像的宽度尺寸
         */
        width?: number;
        /**
         * 上传时确定的图像的高度尺寸
         */
        height?: number;

        /**创建时间 */
        created_at?: string;

        /**修改时间 */
        updated_at?: string;

        /**是否选中 */
        _vIsSelected?: boolean;

        /**显示操作 */
        _vShowTools?: boolean

    }

    /**SKU 属性选项值 */
    export class Options {
        /**
         * id
         */
        id: string = "0";

        /**商品ID */
        product_id: string = "0";

        /**属性选项key */
        name: string = "";

        /**排序位置 */
        position: number = 1;

        /**属性选项值 */
        values: string[] = [];

        /**验证key */
        _validateName?: boolean;


        /**验证选项值  */
        _validateValues?: number[]
    }

    /**商品属性 */
    export class Variant {


        /**id */
        id: string = "0";

        /**
         * 商品的条形码、UPC 或 ISBN 编号
         */
        barcode?: string;

        /**创建日期 */
        created_at?: string = "";

        /**
         * 调整或销售前商品的原始价格
         */
        compare_at_price: string = "";

        /**储存产品多属性的履行服务的句柄 */
        fulfillment_service: string = "manual";


        /**产品变型的重量(以克为单位) */
        grams: number = 0;

        /**
         * 产品图片的唯一数字标识符。 图片必须与多属性关联到同一商品
         */
        image_id: string = "0";

        /**清单项的唯一标识符,在清单 API 中用于查询清单信息 */
        inventory_item_id: string = "0";

        /**跟踪产品多属性的库存物料数量的履行服务。 有效值  shopify  null */
        inventory_management?: string;


        /**是否允许客户在产品多属性缺货时下订单。有效值  deny=不允许  continue=允许 */
        inventory_policy: string = "deny";

        /**所有位置的广告资源汇总。若要调整特定位置的库存 */
        inventory_quantity?: string = "";

        /**
         * 选项值 option1  
         */
        option1?: string;

        /**
         * 选项值  option2 option3
         */
        option2?: string;

        /**
         * 选项值   option3
         */
        option3?: string;

        /**多属性的呈现价格列表 */
        presentment_prices?: any = {};

        /**产品多属性的价格 */
        price: string = "0";

        /**产品ID */
        product_id: string = "0";

        /** */
        sku: string = "";

        /**销售产品变型时是否收取税费 */
        taxable: boolean = false;

        /**
         * 产品变型的标题。该字段是和字段的串联。您只能使用这些字段间接更新。
         */
        title: string = "";

        /**
         * 单位值
         */
        weight: number = 0;

        /**
         * 单位选项值 g=克 kg=千克 oz=盎司 lb=磅
         */
        weight_unit: string = "g";

    }
}


el-upload无法使用,

如题,element plus没法使用,我用的hook方式,只能整合列表拖动,没办法拖动子项

Usage with multiple lists (group, dynamic refs)

Hello @Alfred-Skyblue,

I just tested your version of 'vue-draggable', which looks promising due to recent bugs I found with alternatives.

It works fine for a single list (with hooks fashion), as with 2 "connected" lists manually. But I can't make it work for dynamic lists (or nested v-for), eg. a first v-for for say 2 columns, the second one for the items in each column.

Using recommended methods for "dynamic refs" won't work :

https://vuejs.org/guide/essentials/template-refs.html#refs-inside-v-for
https://vuejs.org/guide/essentials/template-refs.html#function-refs

Any idea about this use case? Is that even possible with current state of library?

PS: I got these 2 errors :

[vue-draggable-plus]: Root element not found
Uncaught Sortable: `el` must be an HTMLElement, not [object Undefined]

can this be used with v-data-table to move between tables in the same group?

Hello,
im trying to move between multiple v-data-tables in the same group,
is it possible to do it with vue-draggable-plus?
is there any example code of it?
i tried to do it with Vue.Draggable but didnt work
with sortable i was able to move only the HTML part, and not the actual data.
thats why im hoping vue-draggable-plus might be the solution im searching for.
thanks.

swiper+draggable

image

disabled如何根据icons[index]遍历出来的item内的属性来判断是否可以拖拽?

Is it possible to validate whether or not you can drag?

Is there a way for me to validate in a method whether or not an element can be dragged to another parent component?
In summary my use case is:
I have 2 lists and some specific list items can be dragged to another list and some not

Example:

if (event.clone.className === "fixed" && event.to.id === "availableFields") {

would be equivalent to prop :move="function" of vuedraggable

Dynamically imported components blink when moving them between lists

Hi, thanks for working on this library! I like it a lot.

Here's one issue that I've noticed. I can't provide a reproduction right now, I'll deliver it later, for now I'm putting it here so that I won't forget.

While any component imported the stardard way behaves just fine using vue-draggable-plus:

import SomeComponent from 'SomeComponent.vue'

If the same component is imported dynamically with defineAsyncComponent, it will blink when moving from one list to another.

const SomeComponent = defineAsyncComponent(() => import(`${someComponentName}.vue`))

Every instance of such a component on a changed list will blink, no matter if Sortable's animation is used or not, and regardless of TransitionGroup.
If a normally imported component contains dynamically imported one, only the child will blink.

Such blinking doesn't happen when changing order in the same list.

class not working

When I place the tag element the tag div is placed in the HTML

image

But i need to pass the class too and I cant figure out how to do it.

I'm trying to pass the class batatas but with no effect

(Small) bugs in stackblitz.com example

Hi,

The example on the front page points to https://stackblitz.com/edit/vue-rpa7f8?file=src%2FApp.vue.

This has a couple of bugs:

  • On lines 9 and 10 there are two open <div> tags. Only one of them is closed (on line 19)
  • There is an unused src/components/Draggable.vue file with (almost?) the same contents as App.vue.
  • If one attempts to download the example and run it locally:
    • npm install shows many deprecated packages and 19 high-severity vulnerabilities (see below)
    • npm run serve fails (see below)
    • I guess this is because the Vue template for stackblitz.com is very bad/old (see newly created stackblitz#2504 bug), but it is still a problem for this example.

npm install output

$ npm install
npm WARN EBADENGINE Unsupported engine {
npm WARN EBADENGINE   package: '@achrinza/[email protected]',
npm WARN EBADENGINE   required: { node: '8 || 10 || 12 || 14 || 16 || 17' },
npm WARN EBADENGINE   current: { node: 'v18.13.0', npm: '9.2.0' }
npm WARN EBADENGINE }
npm WARN deprecated [email protected]: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
npm WARN deprecated [email protected]: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated @hapi/[email protected]: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/[email protected]: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated [email protected]: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated [email protected]: this library is no longer supported
npm WARN deprecated [email protected]: This loader has been deprecated. Please use eslint-webpack-plugin
npm WARN deprecated [email protected]: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated [email protected]: See https://github.com/lydell/source-map-resolve#deprecated
npm WARN deprecated [email protected]: Please use @jridgewell/sourcemap-codec instead
npm WARN deprecated [email protected]: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm WARN deprecated [email protected]: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
npm WARN deprecated [email protected]: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated [email protected]: 3.x is no longer supported
npm WARN deprecated [email protected]: babel-eslint is now @babel/eslint-parser. This package will no longer receive updates.
npm WARN deprecated @hapi/[email protected]: Moved to 'npm install @sideway/address'
npm WARN deprecated [email protected]: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated @hapi/[email protected]: This version has been deprecated and is no longer supported or maintained
npm WARN deprecated @hapi/[email protected]: Switch to 'npm install joi'
npm WARN deprecated [email protected]: This SVGO version is no longer supported. Upgrade to v2.x.x.

added 1389 packages, and audited 1390 packages in 46s

118 packages are looking for funding
  run `npm fund` for details

30 vulnerabilities (1 low, 5 moderate, 19 high, 5 critical)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.

npm run serve output

$ npm run serve

> [email protected] serve
> vue-cli-service serve

 INFO  Starting development server...
10% building 2/5 modules 3 active ...ef--14-0!/home/pmorch/work/ost/src/main.jsError: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:133:10)
    at module.exports (/home/pmorch/work/ost/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:417:16)
    at handleParseError (/home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:471:10)
    at /home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:503:5
    at /home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:358:12
    at /home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at iterateNormalLoaders (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:221:10)
    at /home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:236:3
    at runSyncOrAsync (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:130:11)
    at iterateNormalLoaders (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:232:2)
    at Array.<anonymous> (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:205:4)
    at Storage.finished (/home/pmorch/work/ost/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:55:16)
    at /home/pmorch/work/ost/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:91:9
node:internal/crypto/hash:71
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:133:10)
    at module.exports (/home/pmorch/work/ost/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:417:16)
    at handleParseError (/home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:471:10)
    at /home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:503:5
    at /home/pmorch/work/ost/node_modules/webpack/lib/NormalModule.js:358:12
    at /home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at Array.<anonymous> (/home/pmorch/work/ost/node_modules/loader-runner/lib/LoaderRunner.js:205:4)
    at Storage.finished (/home/pmorch/work/ost/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:55:16)
    at /home/pmorch/work/ost/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:91:9
    at /home/pmorch/work/ost/node_modules/graceful-fs/graceful-fs.js:123:16
    at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read_file_context:68:3) {
  opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
  library: 'digital envelope routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_EVP_UNSUPPORTED'
}

Node.js v18.13.0

Hello, I read the source code, there are one question

file:useDraggable.ts
/**

  • Changed sorting within list
  • @param {DraggableEvent} evt
    */
    function onUpdate(evt: DraggableEvent) {
    const { from, item, oldIndex, newIndex } = evt
    removeNode(item) //146
    insertNodeAt(from, item, oldIndex!) //147
    moveArrayElement(unref(list), oldIndex!, newIndex!)
    }

update function, why do I delete the node and then insert the node, and I find that these two lines of code, when executed, move the dom of sortablejs back, the actual effective code is moveArrayElement function.

So, are these two lines of code useless?

Without ID, an unusual behavior occurs when moving between lists

Without an id field in the object, transferring an entry between lists is not functional.
As a result, some entries may suddenly disappear or be deleted.

Component: https://pastebin.com/b9x1FF1Q

	<div class="row">
		<div class="col-6">
			<VueDraggable
				class="list-group"
				v-model="list1"
				animation="150"
				ghostClass="ghost"
				group="people"
			>
				<div
					v-for="item in list1"
					:key="item.no-id"
					class="list-group-item"
				>
					{{ item.name }}
				</div>
			</VueDraggable>
		</div>
		<div class="col-6">
			<VueDraggable
				class="list-group"
				v-model="list2"
				animation="150"
				group="people"
				ghostClass="ghost"
			>
				<div
					v-for="item in list2"
					:key="item.no-id"
					class="list-group-item"
				>
					{{ item.name }}
				</div>
			</VueDraggable>
		</div>
	</div>
</template>

<script setup>
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
const list1 = ref([
	{
		"name": "test",
		"no-id": "employees.id"
	},
	{
		"name": "active",
		"no-id": "employees.active"
	},
	{
		"name": "firstname",
		"no-id": "employees.firstname"
	},
	{
		"name": "lastname",
		"no-id": "employees.lastname"
	}
])
const list2 = ref([
	{
		"name": "short",
		"no-id": "employees.short"
	},
	{
		"name": "birthday",
		"no-id": "employees.birthday"
	},
	{
		"name": "number",
		"no-id": "employees.number"
	},
	{
		"name": "fullname",
		"no-id": "employees.fullname"
	}
])
</script>

API documentation?

Hi,
I was looking at your documentation and I couldn't find a guide to the API (Props the components accepts, Emitted events)
Could you please guide me?

I want to increase the area that triggers the scroll and dont want to show the ghost ... are these things possible?

[Bug Report]双向列表拖拽+指定目标容器时(容器为组件时),最后一个元素拖拽不消失

https://element-plus.run/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuICA8ZGl2IGNsYXNzPVwiZmxleFwiIHN0eWxlPVwiZGlzcGxheTogZmxleFwiPlxuICAgIDxWdWVEcmFnZ2FibGVcbiAgICAgIGNsYXNzPVwiZmxleCBmbGV4LWNvbCBnYXAtMiBwLTQgdy0zMDBweCBoLTMwMHB4IG0tYXV0byBiZy1ncmF5LTUwMC81IHJvdW5kZWQgb3ZlcmZsb3ctYXV0b1wiXG4gICAgICB2LW1vZGVsPVwibGlzdDFcIlxuICAgICAgYW5pbWF0aW9uPVwiMTUwXCJcbiAgICAgIGdob3N0Q2xhc3M9XCJnaG9zdFwiXG4gICAgICBncm91cD1cInBlb3BsZVwiXG4gICAgICBAdXBkYXRlPVwib25VcGRhdGVcIlxuICAgICAgQGFkZD1cIm9uQWRkXCJcbiAgICAgIEByZW1vdmU9XCJyZW1vdmVcIlxuICAgICAgc3R5bGU9XCJtYXJnaW4tcmlnaHQ6IDUwcHhcIlxuICAgICAgdGFyZ2V0PVwiLndyYXBcIlxuICAgID5cbiAgICAgIDxlbC1yb3cgY2xhc3M9XCJ3cmFwXCI+XG4gICAgICAgIDxlbC1jb2wgdi1mb3I9XCJpdGVtIGluIGxpc3QxXCIgOmtleT1cIml0ZW0uaWRcIiBjbGFzcz1cImN1cnNvci1tb3ZlIGgtMzAgYmctZ3JheS01MDAvNSByb3VuZGVkIHAtM1wiPlxuICAgICAgICAgIHt7IGl0ZW0ubmFtZSB9fVxuICAgICAgICA8L2VsLWNvbD5cbiAgICAgIDwvZWwtcm93PlxuICAgIDwvVnVlRHJhZ2dhYmxlPlxuICAgIDxWdWVEcmFnZ2FibGVcbiAgICAgIGNsYXNzPVwiZmxleCBmbGV4LWNvbCBnYXAtMiBwLTQgdy0zMDBweCBoLTMwMHB4IG0tYXV0byBiZy1ncmF5LTUwMC81IHJvdW5kZWQgb3ZlcmZsb3ctYXV0b1wiXG4gICAgICB2LW1vZGVsPVwibGlzdDJcIlxuICAgICAgYW5pbWF0aW9uPVwiMTUwXCJcbiAgICAgIGdyb3VwPVwicGVvcGxlXCJcbiAgICAgIGdob3N0Q2xhc3M9XCJnaG9zdFwiXG4gICAgICBAdXBkYXRlPVwib25VcGRhdGVcIlxuICAgICAgQGFkZD1cIm9uQWRkXCJcbiAgICAgIEByZW1vdmU9XCJyZW1vdmVcIlxuICAgICAgdGFyZ2V0PVwiLnF3ZXJcIlxuICAgID5cbiAgICAgIDxkaXYgY2xhc3M9XCJxd2VyXCI+XG4gICAgICAgIDxkaXYgdi1mb3I9XCJpdGVtIGluIGxpc3QyXCIgOmtleT1cIml0ZW0uaWRcIiBjbGFzcz1cImN1cnNvci1tb3ZlIGgtMzAgYmctZ3JheS01MDAvNSByb3VuZGVkIHAtM1wiPlxuICAgICAgICAgIHt7IGl0ZW0ubmFtZSB9fVxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgIDwvVnVlRHJhZ2dhYmxlPlxuICA8L2Rpdj5cbjwvdGVtcGxhdGU+XG5cbjxzY3JpcHQgc2V0dXA+XG5pbXBvcnQgeyByZWYgfSBmcm9tIFwidnVlXCI7XG5pbXBvcnQgeyBWdWVEcmFnZ2FibGUgfSBmcm9tIFwidnVlLWRyYWdnYWJsZS1wbHVzXCI7XG5pbXBvcnQge0VsUm93LEVsQ29sfSBmcm9tIFwiZWxlbWVudC1wbHVzXCJcbmNvbnN0IGxpc3QxID0gcmVmKFtcbiAge1xuICAgIG5hbWU6IFwiSm9hb1wiLFxuICAgIGlkOiBcIjFcIixcbiAgfSxcbiAge1xuICAgIG5hbWU6IFwiSmVhblwiLFxuICAgIGlkOiBcIjJcIixcbiAgfSxcbiAge1xuICAgIG5hbWU6IFwiSm9oYW5uYVwiLFxuICAgIGlkOiBcIjNcIixcbiAgfSxcbiAge1xuICAgIG5hbWU6IFwiSnVhblwiLFxuICAgIGlkOiBcIjRcIixcbiAgfSxcbl0pO1xuY29uc3QgbGlzdDIgPSByZWYoXG4gIGxpc3QxLnZhbHVlLm1hcCgoaXRlbSkgPT4gKHtcbiAgICBuYW1lOiBgJHtpdGVtLm5hbWV9LTJgLFxuICAgIGlkOiBgJHtpdGVtLmlkfS0yYCxcbiAgfSkpXG4pO1xuZnVuY3Rpb24gb25VcGRhdGUoKSB7XG4gIGNvbnNvbGUubG9nKFwidXBkYXRlXCIpO1xufVxuZnVuY3Rpb24gb25BZGQoKSB7XG4gIGNvbnNvbGUubG9nKFwiYWRkXCIpO1xufVxuZnVuY3Rpb24gcmVtb3ZlKCkge1xuICBjb25zb2xlLmxvZyhcInJlbW92ZVwiKTtcbn1cbjwvc2NyaXB0PlxuPHN0eWxlPjwvc3R5bGU+XG4iLCJpbXBvcnRfbWFwLmpzb24iOiJ7XG4gIFwiaW1wb3J0c1wiOiB7XG4gICAgXCJ2dWUtZHJhZ2dhYmxlLXBsdXNcIjogXCJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL3Z1ZS1kcmFnZ2FibGUtcGx1c0AwLjEuNS9kaXN0L3Z1ZS1kcmFnZ2FibGUtcGx1cy5qc1wiXG4gIH1cbn0iLCJfbyI6e319

在线案例,如下图
image

Possible Bug when after draging an element next elements creates in wrong position

When I swap two items being the one that I drag the one of the bottom, after the swap is made correclty, when I create a new Item in the list, this new item appear in the wrong place (under the item thet used to be the last, see images). The list of elements is in the right order when I log it to the console.

List inital state
Initial state

After items 3-2 swapped dragging the inial item 3 to the 2 place (check coordinates for reference)
After move

New item 4 created in the wrong place
After new item

Interestingly if you swap the items the other way araund it wokrs fine.
This is my code simplified

<div>
  <VueDraggable ref="el" v-if="matrix[index]" v-model="matrix[index].elements" :animation="150" handle=".handle"
    @update="updateList(index)"
    >
    <div v-for="(item, index) in matrix[index].elements" :key="index" 
        <p>{{ index + 1 }}.</p>
        <i class="fa-solid fa-bars handle cursor-move" style="color: #df0024"></i>
        ({{item}})
    </div>
  </VueDraggable>
</div>

Note that the matrix is an Vue Ref<Element[][]> injected in the component.

v-model variable updated *after* @update event is sent

If I have:

    <VueDraggable
      v-model="list"
      @update="listUpdate"
    >

I expect to see the updated list in my listUpdate method. But list gets updated after listUpdate is called, forcing me to use a setTimeout(func, 0) or Vue.nextTick(func) hack to see the updated variable. It would be better if the new updated variable was available when @update fires.

So I expect this to show what is now the first element, but in fact it will show the first element before i sorted them:

  methods: {
    listUpdate() {
      this.firstElementInUpdate = this.list[0]
    }
  },

And this will show the new first element as I expect:

  methods: {
    listUpdate() {
      nextTick(() => {
        this.firstElementInUpdate = this.list[0]
      })
    }
  },

See https://vue-pys8h3.stackblitz.io for a full reproduction.

Some other way to get the new value of list from listUpdate would also be good. The sortable doc says to access it as evt.to, but then I get the updated DOM node, where I want the Vue model variable.

This reproduces in 0.1.3 and 0.1.5. 0.1.4 didn't work.

onMove生命周期中,return false无法取消拖拽

如下代码,可以直接运行,在onMove事件中,返回false,无法阻止(取消)拖拽,望修复,感谢!

<template>
    <div class="flex">
        <VueDraggable ref="el" v-model="list" animation="150" ghostClass="ghost"
            class="flex flex-col gap-2 p-4 w-300px h-300px m-auto bg-gray-500/5 rounded" :onMove="onMove">
            <div v-for="item in list" :key="item.id" class="cursor-move h-30 bg-gray-500/5 rounded p-3 cursor-move">
                {{ item.name }}
            </div>
        </VueDraggable>
    </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { type UseDraggableReturn, VueDraggable } from 'vue-draggable-plus'
const list = ref([
    {
        name: 'Joao',
        id: 1
    },
    {
        name: 'Jean',
        id: 2
    },
    {
        name: 'Johanna',
        id: 3
    },
    {
        name: 'Juan',
        id: 4
    }
])

const onMove = () => {
    return false
}
</script>

<style scoped>
.ghost {
    opacity: 0.5;
    background: #c8ebfb;
}
</style>

`change` event triggers different from `docs`

docs for change event says Triggered when an item is dragged and changes position.
However, change event triggers when dragging without change actual position like move event.
As far as I remember correctly, change event triggers when an item is dropped to different position.

<template>
  <draggable
    v-model="list"
    :tag="tag"
    :group="group"
    :move="move"
    :fallback-tolerance="3"
    :force-fallback="true"
    :fallback-on-body="true"
    :disabled="disabled"
    @start="onStart"
    @end="onEnd"
    @change="onChange"
    @update="onUpdated"
    @move="onMove"
    @dragover.prevent
  >
    <slot></slot>
  </draggable>
</template>

<script setup lang="ts">
const props = withDefaults(
  defineProps<{
    group: string;
    list?: Array<any>;
    tag?: string;
    option?: any;
    move?: (event: any, originEvent: any) => void;
    disabled?: boolean;
  }>(),
  {
    list: () => [],
    tag: "div",
    option: undefined,
    move: undefined,
    disabled: false,
  }
);
const { list } = toRefs(props);
const emits = defineEmits<{
  (e: "start", value: any): void;
  (e: "end", value: any): void;
  (e: "change", value: any): void;
}>();

const onStart = (event: any) => {
  emits("start", event);
  console.log("start : ", event);
};

const onEnd = (event: any) => {
  emits("end", event);
  console.log("end : ", event);
};

const onChange = (event: any) => {
  console.log("onChange event : ", event);
  emits("change", event);
};
const onUpdated = (event: any) => {
  console.log("onUpdated event : ", event);
  // emits("change", event);
};
const onMove = (event: any, b: any, c: any) => {
  console.log("onMove event : ", event, b, c);
  // emits("change", event);
};
</script>

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.