Component library to drag and drop items across lists in Vue3. Try out here:
Comparable to, and inspired by, sortablejs, but unrelated.
Features:
- Rearranging a component list. Dragging/dropping items from one list to another.
- Fully customizable dragging element.
- Supports using a handle.
- Drop zones, e.g. for trash bin.
- Utility-class friendly
- Completely built in Typescript. Supports generic items. (Javascript users can live happily omitting all the type references below (the things between <>), but it is still useful to know what is being returned by events etc.)
npm add vue-arrange
- clone the package
npm i; npm run dev
See ArrangeableProps below
#before
: template to display something before the list of items.- Props:
arrangedItems
: the list of items as it is rendered now.
- Props:
#default
: template for the listed items;- Props:
item
: the list itemarrangedItems
: the list of items as it is rendered now.
- Props:
#after
: template to display something after the list of items.- Props:
arrangedItems
: the list of items as it is rendered now.
- Props:
liftItem
: fired when an item is being picked up out of this list. Payload:MovingItem
.dropItem
: fired when an item from this is dropped somewhere. Payload:MovingItem
.
- The consumer needs to take care that the list prop gets stored/updated upon
dropItem
event, otherwise it the list will reset to its original position on the next re-render phase. This is can be done using the dropItem event, which carries a MovingItem argument with all the required information. - Can be customized using options (see below).
- class options can take any list of (utility) classes like they would be normally passed to a component. Tailwind works perfectly for example. They are empty by default.
- There is no default transition for the list, this needs to be provided by the consumer (if desired) with the listTransition option.
- by default, items can only be moved within the same list.
- It can be used both by touch devices, mouse and any other type of pointer, as it uses PointerEvents. To be able to do this, it puts
touch-action: 'none'
css class on the entire document.
Simple example without typescript.
<script setup>
import { ref } from "vue";
import { ArrangeableList } from "vue-arrange";
const twColors = ref([
{ name: "red", color: "#fecaca" },
{ name: "orange", color: "#fed7aa" },
{ name: "amber", color: "#fde68a" },
{ name: "yellow", color: "#fef08a" },
{ name: "lime", color: "#d9f99d" },
{ name: "green", color: "#bbf7d0" },
{ name: "emerald", color: "#a7f3d0" },
{ name: "teal", color: "#99f6e4" },
{ name: "cyan", color: "#a5f3fc" },
{ name: "sky", color: "#bae6fd" },
{ name: "blue", color: "#bfdbfe" },
{ name: "indigo", color: "#c7d2fe" },
{ name: "violet", color: "#ddd6fe" },
{ name: "purple", color: "#e9d5ff" },
{ name: "fuchsia", color: "#f5d0fe" },
{ name: "pink", color: "#fbcfe8" },
{ name: "rose", color: "#fecdd3" },
]);
const dropItem = ({ destinationList }) => {
twColors.value = destinationList;
};
</script>
<template>
<ArrangeableList
:list="twColors"
:options="{
pickedItemClass: 'invisible',
hoverClass: 'hover',
listTransition: { name: 'list-transition' },
}"
@drop-item="dropItem"
v-slot="{ item }"
>
<div
style="margin: 8px;padding: 8px;height: 40px;width: 160px;border-radius: 8px;"
:style="item.color"
>
{{ item.name }}
</div>
</ArrangeableList>
</template>
<style>
.invisible {
visibility: hidden;
}
.hover {
opacity: 0.65;
}
.list-transition-move,
.list-transition-enter-active,
.list-transition-leave-active {
transition: all 0.2s ease;
}
.list-transition-leave-active {
position: absolute;
}
</style>
See example folder for a complete example using typescript and tailwindcss.
Exposes:
movingItem
, of typeMovingItem<PayloadType>
; the item currently being dragged around by the user from any list at all. If using typescript, it should use the samePayloadType
as the list items of theArrangeableList
.isMoving(item: PayloadType) => boolean
; test whetheritem
is currently being dragged.
const { movingItem, isMoving } = useMovingItem<MyObjectType>();
Information about what is/was being dragged/dropped.
type MovingItem<PayloadType extends object> = {
// PayloadType is the generic type used by ArrangeableList.
payload: PayloadType; // The item object being moved.
hoverElement?: Ref<HTMLElement>; // the element ref of the hovering item.
fromIndex: number; // the 0-based list-index where it was picked up.
origin: string | symbol; // the name of the list from which it was picked up.
originList: PayloadType[]; // the arranged items-list where the item came from
toIndex?: number; // the 0-based list-index where the item is hovering or dropped.
destination?: string | symbol; // the name of the list where it is hovering or dropped.
destinationList?: PayloadType[]; // the arranged items-list where the item is hovering over or being dropped
originItemBoundingBox?: DOMRect; // the bounding box of the original location of the picked item.
targets: Array<string | symbol>; // the names/groups of the lists where this item can be dropped.
};
options to pass to the arrangeable list.
type ArrangeableOptions = {
hoverClass?: string; // class or classes to place on the element that is being dragged by the user
pickedItemClass?: string; // class or classes to place on the original of the element in the list that is being picked up. Typically: 'invisible' or something like this.
unpickedItemClass?: string; // class or classes to place on the items that are _not_ being picked up
listTransition?: TransitionGroupProps; // Transition props for the list, dictating how moving, removing and adding items to the list looks. See: Vue TransitionGroup API documentation: https://vuejs.org/api/built-in-components.html#transitiongroup
handle?: boolean | string; // indicate if the elements should be only dragged using a handle. If so, any descendant elements with attribute: 'name="handle"' are used as a handle. If a string is given, the name should be set to that string.
};
props object for arrangeable list
type ArrangeableProps<PayloadType extends object> = {
// PayloadType is the generic type used by ArrangeableList.
options?: ArrangeableOptions; // See above
list: PayloadType[]; // list of items passed to the ArrangeableList.
listKey?: string; // in template: 'list-key'; key of PayloadType, needs to be unique within list. (Only necessary if list items are reassigned in place.)
name?: symbol | string; // unique name of the list. Defaults to an unnamed symbol which gets exposed.
group?: string | symbol; // group of the list. Items can be arbitrarily moved across member lists of this group.
targets?: string | symbol | Array<string | symbol>; // By supplying one or more targets, items from this list can only be moved to other groups/lists named in 'targets' (using the name/group).
};