Related
I used the Composition API when migrating the project from vue2.x to vue3.0, but the page does not work properly, in vue3.0
The environment prompts me to have an "Unexpected mutation of "task" prop" error, I want to know how to write the correct compos API
This is Vue2.x code
<template>
<transition name="fade">
<div class="task" v-if="!task.deleted">
<input :id="id" type="checkbox" v-model="task.done" />
<label :for="id">{{ task.title }}</label>
<transition name="fade">
<span
class="task_delete"
v-show="task.done"
#click="deleteTask({ task })"
>
<i class="fa fa-trash"></i>
</span>
</transition>
</div>
</transition>
</template>
<script>
import { mapMutations } from "vuex";
let GID = 1;
export default {
props: {
task: {
type: Object,
required: true,
},
},
data() {
return {
id: `task-${GID++}`,
};
},
methods: {
...mapMutations(["deleteTask"]),
},
};
</script>
<style lang="scss">
.task {
display: flex;
padding: 12px 0;
border-bottom: 1px solid #eee;
font-size: 14px;
}
.task input {
display: none;
}
.task label {
flex: 1;
line-height: 20px;
}
.task label:before,
.task label:after {
content: "";
display: inline-block;
margin-right: 20px;
margin-top: 1px;
width: 14px;
height: 14px;
vertical-align: top;
}
.task label:before {
border: 1px solid #ccc;
border-radius: 2px;
background-color: white;
}
.task label:after {
content: "\f00c";
position: relative;
display: none;
z-index: 10;
margin-right: -16px;
width: 10px;
height: 10px;
padding: 3px;
border-radius: 2px;
font: normal normal normal 10px/1 FontAwesome;
color: white;
background-color: #ccc;
float: left;
}
.task input:checked + label:after {
display: inline-block;
}
.task_delete {
padding: 0 10px;
color: #ccc;
font-size: 16px;
}
.fade-leave-to,
.fade-enter {
opacity: 0;
}
.fade-enter-to,
.fade-leave {
opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.3s ease;
}
</style>
This is Vue3.0 code use Composition API, but not work
<template>
<transition name="fade">
<div class="task" v-if="!data.task.deleted">
<input :id="id" type="checkbox" v-model="data.task.done" />
<label :for="id">{{ data.task.title }}</label>
<transition name="fade">
<span
class="task_delete"
v-show="data.task.done"
#click="deleteTask({ task })"
>
<i class="fa fa-trash"></i>
</span>
</transition>
</div>
</transition>
</template>
<script>
import { reactive } from "vue";
import { mapMutations } from "vuex";
let GID = 1;
export default {
name: "Task",
props: {
task: {
type: Object,
required: true,
},
},
setup(props) {
const data = reactive({
task: props.task,
id: `task-${GID++}`,
});
return { data };
},
methods: {
...mapMutations(["deleteTask"]),
},
};
</script>
<style lang="scss">
.task {
display: flex;
padding: 12px 0;
border-bottom: 1px solid #eee;
font-size: 14px;
}
.task input {
display: none;
}
.task label {
flex: 1;
line-height: 20px;
}
.task label:before,
.task label:after {
content: "";
display: inline-block;
margin-right: 20px;
margin-top: 1px;
width: 14px;
height: 14px;
vertical-align: top;
}
.task label:before {
border: 1px solid #ccc;
border-radius: 2px;
background-color: white;
}
.task label:after {
content: "\f00c";
position: relative;
display: none;
z-index: 10;
margin-right: -16px;
width: 10px;
height: 10px;
padding: 3px;
border-radius: 2px;
font: normal normal normal 10px/1 FontAwesome;
color: white;
background-color: #ccc;
float: left;
}
.task input:checked + label:after {
display: inline-block;
}
.task_delete {
padding: 0 10px;
color: #ccc;
font-size: 16px;
}
.fade-leave-to,
.fade-enter {
opacity: 0;
}
.fade-enter-to,
.fade-leave {
opacity: 1;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.3s ease;
}
</style>
I think the problem is you're using v-model for props on checkbox, it will change props value directly and vue not allow it. Try update props value manual by emit event. And u dont need to use props with reactive in child component, but need to reactive that props in parent component
<input :id="id" type="checkbox" v-value="task.done" #change="updateCheckbox($event)")/>
In script:
export default {
name: "Task",
props: {
task: {
type: Object,
required: true,
},
},
emits: ['updateCheckbox'],
setup(props) {
const data = reactive({
id: `task-${GID++}`,
});
return { data };
},
methods: {
updateCheckbox(e) {
this.$emit('updateCheckbox', e.target.value)
}
},
};
I have developed a split sidebar menu. On the left there is icons which when pressed activate the menu bar on the right side for the submenu items. The submenu contains the router-link tags which I am able to tap into the active class of the selected link to highlight it. The issue is I need to apply the active class to the left icon bar as well since that is the parent menu. Once the application is loaded when you click the icons the active class will become active if the item is selected. My issue is purely during load because if someone types in the URL the custom router link the left sidebar does not have any functionality linking it to the router since the links are in the right part of the sidebar. Any methodology built into vue to handle something like this? If not is there another method I could try?
My sidebar component code is below. The code basically creates two sides, the icon side and the menu side. The icons and menu's are developed using v-for and looping through the data set which provides the links and icons to use.
<template id="side-navigation">
<div :class="theme">
<nav :class="sidebarcontainer">
<div class="sidebar-left">
<div class="arms-icon">
</div>
<div class="main-menu-items">
<ul>
<li v-for="(item,i) in MainNavLinks"
:key="i"
:class="{'active-main-menu-item': i === activeIconIndex}"
v-on:click="selectIconItem(i)">
<a>
<i :class="item.icon"></i>
</a>
</li>
</ul>
</div>
<div class="bottom-menu-items">
<ul>
<li v-for="(item,i) in FeaturesNavLinks"
:key="i"
:class="{ 'active-main-menu-item': i+MainNavLinks.length === activeIconIndex}"
v-on:click="selectIconItem(i+MainNavLinks.length)">
<a>
<i :class="item.icon"></i>
</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-right" :class="{'active-right-sidebar' : isIconActive}">
<div class="sidebar-content">
<div class="searchbarcontent">
<div class="inputWithIcon">
<input placeholder="Search" id="sub-nav-seachbar" class="searchbar" type="text">
<i class="fas fa-search" id="searchicon-btn"></i>
</div>
</div>
<div v-for="(item,i) in MainNavLinks"
:key="i"
:class="{'active-sub-menu-item' : i === activeIconIndex}"
class="right-menu-content">
<ul>
<li v-for="(SubNavLink,i) in item.SubNavLinks"
class="sub-nav-group">
<h4 class="sub-nav-header">
{{SubNavLink.SubNavHeader}}
</h4>
<ul>
<li v-for="(SubNavMenuItem,i) in SubNavLink.SubNavMenuItems"
class="sub-nav-items">
<router-link :to="SubNavMenuItem.link"><span class="sub-menu-icons"><i :class="SubNavMenuItem.icon"></i></span>{{SubNavMenuItem.title}}</router-link>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</nav>
</div>
</template>
<script>
Vue.component('side-navigation', {
template: '#side-navigation',
methods: {
selectIconItem(i) {
if (this.activeIconIndex === i) {
if (this.isIconActive === true) {
this.isIconActive = false;
} else {
this.isIconActive = true;
}
} else {
this.activeIconIndex = i;
this.isIconActive = true;
}
},
},
data() {
return {
theme: 'color',
activeIconIndex: null,
isIconActive: false,
sidebarcontainer: 'sidebar-container',
MainNavLinks: [
{
name: 'Dashboard',
link: '/',
icon: 'fa fa-th-large fa-lg',
SubNavLinks: [
{
SubNavHeader: 'Dash Category 1',
SubNavMenuItems: [{
title: 'Item 1',
link: '/',
icon: 'fas fa-list-ul'
},
{
title: 'Item 2',
link: '/item2',
icon: 'fas fa-list-ul'
}]
},
{
SubNavHeader: 'Dash Category 2',
SubNavMenuItems: [{
title: 'Item 3',
link: '/item3',
icon: 'fas fa-list-ul'
},
{
title: 'Item 4',
link: '/item4',
icon: 'fas fa-list-ul'
}]
}
]
},
{
name: 'Reviews',
link: '/Reviews',
icon: 'far fa-clipboard fa-lg',
SubNavLinks: [
{
SubNavHeader: 'Reviews Category 1',
SubNavMenuItems: [{
title: 'Item 1',
link: '/item1',
icon: 'fas fa-list-ul'
},
{
title: 'Item 2',
link: '/item2',
icon: 'fas fa-list-ul'
}]
},
{
SubNavHeader: 'Reviews Category 2',
SubNavMenuItems: [{
title: 'Item 3',
link: '/item3',
icon: 'fas fa-list-ul'
},
{
title: 'Item 4',
link: '/item4',
icon: 'fas fa-list-ul'
}]
}
]
},
{
name: 'Upload',
link: '/Upload',
icon: 'fa fa-upload fa-lg',
SubNavLinks: [
]
},
{
name: 'Analytics',
link: '/Analytics',
icon: 'fas fa-chart-line fa-lg',
SubNavLinks: [
]
},
{
name: 'Files',
link: '/Files',
icon: 'far fa-folder-open fa-lg',
SubNavLinks: [
]
}
],
FeaturesNavLinks: [
{
name: 'Notifications',
link: '/',
icon: 'fas fa-bell fa-lg'
},
{
name: 'Chat',
link: '/',
icon: 'fas fa-comment-alt fa-lg'
},
{
name: 'Email',
link: '/',
icon: 'fas fa-envelope fa-lg'
},
{
name: 'Profile',
link: '/',
icon: 'fas fa-user fa-lg'
}
]
}
}
})
</script>
<style scoped>
.sidebar-container {
display: flex;
flex-direction: row;
box-shadow: 2px 0px 4px -1px rgb(0 0 0 / 15%);
position: sticky;
}
/*left icon section of sidebar*/
.sidebar-left {
display: flex;
flex-direction: column;
align-items: center;
width: 100px;
position: relative;
height: 100vh;
overflow-y: auto;
overflow-x: hidden;
}
.color .sidebar-left {
background-color: #ff7100;
}
.light .sidebar-left {
border-right: 1px solid #ebedf3;
}
.main-menu-items {
padding: 20px 20px;
flex-grow: 1;
}
.sidebar-left ul {
padding: 0px;
}
.sidebar-left li {
list-style-type: none;
text-align: center;
margin-bottom: 10px;
}
.sidebar-left a {
height: 50px;
width: 50px;
display: flex;
justify-content: center;
align-items: center;
text-decoration: none;
border-radius: .42rem;
}
.color .sidebar-left a:hover > *, .color .sidebar-left a:hover {
background-color: #da6000f7;
color: #fff !important;
}
.light .sidebar-left a:hover > *, .light .sidebar-left a:hover {
background-color: #f3f6f9;
color: #ff7d44 !important;
}
.color .active-main-menu-item a > *, .color .active-main-menu-item a {
background-color: #da6000f7;
color: #fff !important;
}
.light .active-main-menu-item a > *, .light .active-main-menu-item a {
background-color: #f3f6f9;
color: #ff7d44 !important;
}
.color .sidebar-left i {
color: #fff;
}
.light .sidebar-left i {
color: #a5a5a5;
}
/*right sidebar styling*/
.sidebar-right {
width: 0px;
position: relative;
transition: all 1s;
overflow: hidden;
height: 100vh;
overflow-y: auto;
}
.active-right-sidebar {
width: 325px;
transition: all 1s;
}
.sidebar-content{
padding:20px;
}
.right-menu-content {
display: none;
overflow: hidden;
white-space: nowrap;
padding: 0px 20px;
}
.active-sub-menu-item {
display: block;
}
/*sidebar searchbar*/
.searchbar {
width: 100%;
height: 40px;
border: 0px;
border-radius: 40px;
outline: none;
padding: 8px;
box-sizing: border-box;
transition: 0.3s;
letter-spacing: 2px;
background-color: #e6e6e6;
}
.inputWithIcon input[type="text"], .inputWithIcon input[type="password"] {
padding-left: 35px;
}
.inputWithIcon {
position: relative;
height: 40px;
overflow: hidden;
width: 100%;
}
.searchbarcontent {
padding: 0px 20px;
display: flex;
height: 50px;
width: 100%;
justify-content: center;
align-items: center;
margin-bottom: 10px;
}
#searchicon-btn {
padding: 9px 25px 9px 5px;
top: 4px;
color: #aaa;
position: absolute;
right: 0px;
cursor: pointer;
}
.sidebar-right ul {
padding: 0px;
}
.sidebar-right li {
list-style-type: none;
}
.sidebar-right a {
text-decoration: none;
}
.sub-nav-header {
display: flex;
align-items: center;
height: 50px;
font-size: 1rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: .3px;
color: #7e8299;
}
.sub-nav-items {
display: flex;
align-items: center;
height: 45px;
font-size: .9rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: .3px;
}
.sub-nav-items a {
color: #7f818d;
}
.sub-nav-group{
margin-bottom: 15px;
}
.sub-menu-icons {
width: 30px;
padding-right: 20px;
}
.router-link-exact-active {
color: #ff7100 !important;
}
/*scrollbar*/
/* custom scrollbar */
::-webkit-scrollbar {
width: 20px;
}
::-webkit-scrollbar-track {
background-color: transparent;
}
::-webkit-scrollbar-thumb {
background-color: #00000014;
border-radius: 20px;
border: 6px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background-color: #0000001f;
}
</style>
Use nested routes.
Parent <router-link>'s should have the class .router-link-active.
The exact <router-link> should have .router-link-exact-active.
Check out this code for an interactive example.
I've been doing some vuejs for almost a year now and I never really got animations and transitions to work, it seems very unclear to me how all this is supposed to work and today I decided to try and finally understand them but once again I am stuck, the transition simply won't work and I don't understand what I'm doing wrong.
Here is my code:
The HTML
<div id="app">
<div class="row">
<div class="container-fluid">
<transition-group name="flip-list" tag="div" class="row horizontal-scroll">
<div class="col-xs-12 col-md-2 col-lg-2 col-card"
v-for="(someCard, index) in someCards" v-bind:key="index">
<div class="some-card" #click="changeOrder(someCard)">
{{ someCard.someTitle }}
</div>
</div>
</transition-group>
</div>
</div>
</div>
The JS
new Vue({
el: "#app",
data: {
someCards: [
{
order: 2,
someTitle: 'Title 1'
},
{
order: 2,
someTitle: 'Title 2'
},
{
order: 2,
someTitle: 'Title 3'
},
{
order: 3,
someTitle: 'Title 4'
},
{
order: 3,
someTitle: 'Title 5'
},
{
order: 1,
someTitle: 'Title 6'
},
{
order: 1,
someTitle: 'Title 7'
},
{
order: 1,
someTitle: 'Title 8'
},
{
order: 3,
someTitle: 'Title 1'
}
]
},
methods: {
changeOrder(someCardData) {
let newRandom = Math.floor(Math.random() * 3) + 1;
while (newRandom === someCardData.order) {
newRandom = Math.floor(Math.random() * 3) + 1;
}
someCardData.order = newRandom;
this.reorderList();
},
reorderList() {
this.someCards.sort(function(objA, objB) {
return objA.order - objB.order;
})
},
}
})
The CSS
/* default */
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
/* bootstrap */
.row {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col, .col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm, .col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md, .col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg, .col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl, .col-xl-auto {
position: relative;
width: 100%;
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
}
/* my css */
.horizontal-scroll {
overflow-x: auto;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
}
.col-card {
max-width: 14%;
}
.some-card {
height: 220px;
font-size: 16px;
text-align: center;
padding-right: 5px;
margin-top: 5px;
-webkit-box-shadow: 1px 1px 5px rgba(0,0,0,.1);
box-shadow: 1px 1px 5px rgba(0,0,0,.1);
border-radius: 2px;
background: #fff;
margin-bottom: 20px;
position: relative;
border-top-style: solid;
}
.flip-list-move {
transition: transform 1s;
}
https://jsfiddle.net/eywraw8t/459088/
I am trying to have an animation triggered when I reorder a card in a list of cards aligned in a row. To change the position of a card, simply click on it. The reorder works but no transition is done.
I used this documentation
https://v2.vuejs.org/v2/guide/transitions.html#List-Move-Transitions
and have seen a couple of fairly easy tutorials but I must be missing something. Can someone please check the code out and tell me what it is ?
Thanks
It's because you're using the index as a key. The index will always be the same for the same position even if the card changes. I'd give each card a unique id and then use that as a key instead ..
new Vue({
el: "#app",
data: {
someCards: [{
id: 0,
order: 2,
someTitle: 'Title 1'
},
{
id: 1,
order: 2,
someTitle: 'Title 2'
},
{
id: 2,
order: 2,
someTitle: 'Title 3'
},
{
id: 3,
order: 3,
someTitle: 'Title 4'
},
{
id: 4,
order: 3,
someTitle: 'Title 5'
},
{
id: 5,
order: 1,
someTitle: 'Title 6'
},
{
id: 6,
order: 1,
someTitle: 'Title 7'
},
{
id: 7,
order: 1,
someTitle: 'Title 8'
},
{
id: 8,
order: 3,
someTitle: 'Title 1'
}
]
},
methods: {
changeOrder(someCardData) {
let newRandom = Math.floor(Math.random() * 3) + 1;
while (newRandom === someCardData.order) {
newRandom = Math.floor(Math.random() * 3) + 1;
}
someCardData.order = newRandom;
this.reorderList();
},
reorderList() {
this.someCards.sort(function(objA, objB) {
return objA.order - objB.order;
})
},
}
})
/* default */
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
/* bootstrap */
.row {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.col-1,
.col-2,
.col-3,
.col-4,
.col-5,
.col-6,
.col-7,
.col-8,
.col-9,
.col-10,
.col-11,
.col-12,
.col,
.col-auto,
.col-sm-1,
.col-sm-2,
.col-sm-3,
.col-sm-4,
.col-sm-5,
.col-sm-6,
.col-sm-7,
.col-sm-8,
.col-sm-9,
.col-sm-10,
.col-sm-11,
.col-sm-12,
.col-sm,
.col-sm-auto,
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11,
.col-md-12,
.col-md,
.col-md-auto,
.col-lg-1,
.col-lg-2,
.col-lg-3,
.col-lg-4,
.col-lg-5,
.col-lg-6,
.col-lg-7,
.col-lg-8,
.col-lg-9,
.col-lg-10,
.col-lg-11,
.col-lg-12,
.col-lg,
.col-lg-auto,
.col-xl-1,
.col-xl-2,
.col-xl-3,
.col-xl-4,
.col-xl-5,
.col-xl-6,
.col-xl-7,
.col-xl-8,
.col-xl-9,
.col-xl-10,
.col-xl-11,
.col-xl-12,
.col-xl,
.col-xl-auto {
position: relative;
width: 100%;
min-height: 1px;
padding-right: 15px;
padding-left: 15px;
}
/* my css */
.horizontal-scroll {
overflow-x: auto;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
}
.col-card {
max-width: 14%;
}
.some-card {
height: 220px;
font-size: 16px;
text-align: center;
padding-right: 5px;
margin-top: 5px;
-webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1);
box-shadow: 1px 1px 5px rgba(0, 0, 0, .1);
border-radius: 2px;
background: #fff;
margin-bottom: 20px;
position: relative;
border-top-style: solid;
}
.flip-list-move {
transition: transform 1s;
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<div class="row">
<div class="container-fluid">
<transition-group name="flip-list" tag="div" class="row horizontal-scroll">
<div class="col-xs-12 col-md-2 col-lg-2 col-card" v-for="(someCard, index) in someCards" v-bind:key="someCard.id">
<div class="some-card" #click="changeOrder(someCard)">
{{ someCard.someTitle }}
</div>
</div>
</transition-group>
</div>
</div>
</div>
JSFiddle
I am creating a full screen navigation
This navigation is opening on a button click. The problem is that the liand close button are not accessible. I am not able to click on them.
Html
<div id="myNav" class="overlay">
<v-btn class="white--text closebtn" icon v-on:click.prevent="CloseDialog">
<v-icon>cancel</v-icon>
</v-btn>
<div class="overlay-content">
About
Services
Clients
Contact
</div>
</div>
Css
.overlay {
height: 100%;
width: 0;
position: fixed;
z-index: 4;
top: 0;
left: 0;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0, 0.9);
overflow-x: hidden;
transition: 0.5s;
}
.overlay-content {
z-index :99;
position: relative;
top: 25%;
width: 100%;
text-align: center;
margin-top: 30px;
}
.overlay a {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #818181;
display: block;
transition: 0.3s;
}
.overlay a:hover, .overlay a:focus {
color: #f1f1f1;
}
.overlay .closebtn {
position: absolute;
top: 40px;
right: 55px;
font-size: 80px;
cursor :pointer
}
#media screen and (max-height: 450px) {
.overlay a {font-size: 20px}
.overlay .closebtn {
font-size: 40px;
top: 15px;
right: 35px;
}
}
Javscript
<script>
import { mapGetters } from "vuex"
export default {
computed: mapGetters({ isLoggedIn: 'CheckAuth', items: 'GetItems' }),
data() {
return {
clipped: true,
drawer: true,
fixed: false,
miniVariant: true,
right: true,
rightDrawer: false,
title: 'Vuetify.js'
}
},
methods: {
Login() {
this.$store.dispatch('ChangeAuth');
},
OpenDialog() {
document.getElementById("myNav").style.width = "100%";
},
CloseDialog() {
document.getElementById("myNav").style.width = "0%";
}
}
}
</script>
This is a pure CSS issue. You can either add :after pseudo element and create the background with it. Or you can use pointer-events: none;
CSS property on the overlay element.
I have tried jsplumb script in .html file under the script tag. This is working fine.
<body>
<div id="q-app"></div>
<!-- built files will be auto injected -->
<script>
jsPlumb.ready(function() {
jsPlumb.connect({
source:"item_left",
target:"item_right",
endpoint:"Rectangle"
});
jsPlumb.draggable("item_left");
jsPlumb.draggable("item_right");
});
</script>
</body>
But now i want to integrate this jsplumb code/script in .vue file.I tried to put this script in .vue file under script tag, but i did not get any output except an blank page with zero errors. How can i proceed further?.Guide me with some simple example.
You can try this:
mydraggableview.vue
<template>
<div id="diagramContainer">
<div id="item_left" class="item"></div>
<div id="item_right" class="item" style="margin-left:50px;"></div>
</div>
</template>
<script>
import jsplumb from 'jsplumb'
export default {
props: ['yourProps'],
data() {
return {
your: data
}
},
mounted(){
jsPlumb.ready(function() {
jsPlumb.connect({
source:"item_left",
target:"item_right",
endpoint:"Rectangle"
})
})
}
}
</script>
Then you can import where you are going to use it.
otherfile.js
<script>
import myDraggableComponent from './path/to/component/mydraggableview'
</script>
and use it as directive or inside your component.
I managed to put together working code based on jsplumb demo community example -
<template>
<div id="canvas" class="jtk-demo-canvas canvas-wide flowchart-demo jtk-surface jtk-surface-nopan">
<div id="flowchartWindow1" class="window jtk-node">1</div>
<div id="flowchartWindow2" class="window jtk-node">2</div>
<div id="flowchartWindow3" class="window jtk-node">3</div>
<div id="flowchartWindow4" class="window jtk-node">4</div>
</div>
</template>
<script>
import { jsPlumb as JSPlumb } from 'jsplumb'
export default {
name: 'JsPlumb',
data () {
return {
}
},
mounted () {
JSPlumb.ready(function() {
var instance = window.jsp = JSPlumb.getInstance({
// default drag options
DragOptions: { cursor: 'pointer', zIndex: 2000 },
// the overlays to decorate each connection with. note that the label overlay uses a function to generate the label text; in this
// case it returns the 'labelText' member that we set on each connection in the 'init' method below.
ConnectionOverlays: [
[ "Arrow", {
location: 1,
visible:true,
width:11,
length:11,
id:"ARROW",
events:{
click:function() { alert("you clicked on the arrow overlay")}
}
} ],
[ "Label", {
location: 0.1,
id: "label",
cssClass: "aLabel",
events:{
tap:function() { alert("hey"); }
}
}]
],
Container: "canvas"
});
var basicType = {
connector: "StateMachine",
paintStyle: { stroke: "red", strokeWidth: 4 },
hoverPaintStyle: { stroke: "blue" },
overlays: [
"Arrow"
]
};
instance.registerConnectionType("basic", basicType);
// this is the paint style for the connecting lines..
var connectorPaintStyle = {
strokeWidth: 2,
stroke: "#61B7CF",
joinstyle: "round",
outlineStroke: "white",
outlineWidth: 2
},
// .. and this is the hover style.
connectorHoverStyle = {
strokeWidth: 3,
stroke: "#216477",
outlineWidth: 5,
outlineStroke: "white"
},
endpointHoverStyle = {
fill: "#216477",
stroke: "#216477"
},
// the definition of source endpoints (the small blue ones)
sourceEndpoint = {
endpoint: "Dot",
paintStyle: {
stroke: "#7AB02C",
fill: "transparent",
radius: 7,
strokeWidth: 1
},
isSource: true,
connector: [ "Flowchart", { stub: [40, 60], gap: 10, cornerRadius: 5, alwaysRespectStubs: true } ],
connectorStyle: connectorPaintStyle,
hoverPaintStyle: endpointHoverStyle,
connectorHoverStyle: connectorHoverStyle,
dragOptions: {},
overlays: [
[ "Label", {
location: [0.5, 1.5],
label: "Drag",
cssClass: "endpointSourceLabel",
visible:false
} ]
]
},
// the definition of target endpoints (will appear when the user drags a connection)
targetEndpoint = {
endpoint: "Dot",
paintStyle: { fill: "#7AB02C", radius: 7 },
hoverPaintStyle: endpointHoverStyle,
maxConnections: -1,
dropOptions: { hoverClass: "hover", activeClass: "active" },
isTarget: true,
overlays: [
[ "Label", { location: [0.5, -0.5], label: "Drop", cssClass: "endpointTargetLabel", visible:false } ]
]
},
init = function (connection) {
connection.getOverlay("label").setLabel(connection.sourceId.substring(15) + "-" + connection.targetId.substring(15));
};
var _addEndpoints = function (toId, sourceAnchors, targetAnchors) {
for (var i = 0; i < sourceAnchors.length; i++) {
var sourceUUID = toId + sourceAnchors[i];
instance.addEndpoint("flowchart" + toId, sourceEndpoint, {
anchor: sourceAnchors[i], uuid: sourceUUID
});
}
for (var j = 0; j < targetAnchors.length; j++) {
var targetUUID = toId + targetAnchors[j];
instance.addEndpoint("flowchart" + toId, targetEndpoint, { anchor: targetAnchors[j], uuid: targetUUID });
}
};
// suspend drawing and initialise.
instance.batch(function () {
_addEndpoints("Window4", ["TopCenter", "BottomCenter"], ["LeftMiddle", "RightMiddle"]);
_addEndpoints("Window2", ["LeftMiddle", "BottomCenter"], ["TopCenter", "RightMiddle"]);
_addEndpoints("Window3", ["RightMiddle", "BottomCenter"], ["LeftMiddle", "TopCenter"]);
_addEndpoints("Window1", ["LeftMiddle", "RightMiddle"], ["TopCenter", "BottomCenter"]);
// listen for new connections; initialise them the same way we initialise the connections at startup.
instance.bind("connection", function (connInfo, originalEvent) {
init(connInfo.connection);
});
// make all the window divs draggable
instance.draggable(JSPlumb.getSelector(".flowchart-demo .window"), { grid: [20, 20] });
// THIS DEMO ONLY USES getSelector FOR CONVENIENCE. Use your library's appropriate selector
// method, or document.querySelectorAll:
//JSPlumb.draggable(document.querySelectorAll(".window"), { grid: [20, 20] });
// connect a few up
instance.connect({uuids: ["Window2BottomCenter", "Window3TopCenter"]});
instance.connect({uuids: ["Window2LeftMiddle", "Window4LeftMiddle"]});
instance.connect({uuids: ["Window4TopCenter", "Window4RightMiddle"]});
instance.connect({uuids: ["Window3RightMiddle", "Window2RightMiddle"]});
instance.connect({uuids: ["Window4BottomCenter", "Window1TopCenter"]});
instance.connect({uuids: ["Window3BottomCenter", "Window1BottomCenter"] });
//
//
// listen for clicks on connections, and offer to delete connections on click.
//
instance.bind("click", function (conn, originalEvent) {
// if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
// instance.detach(conn);
conn.toggleType("basic");
});
instance.bind("connectionDrag", function (connection) {
console.log("connection " + connection.id + " is being dragged. suspendedElement is ", connection.suspendedElement, " of type ", connection.suspendedElementType);
});
instance.bind("connectionDragStop", function (connection) {
console.log("connection " + connection.id + " was dragged");
});
instance.bind("connectionMoved", function (params) {
console.log("connection " + params.connection.id + " was moved");
});
});
JSPlumb.fire("jsPlumbDemoLoaded", instance);
})
}
}
</script>
<style>
.item{
height:50px;
width:50px;
background-color: red;
display: inline-block;
}
.demo {
/* for IE10+ touch devices */
touch-action:none;
}
.flowchart-demo .window {
border: 1px solid #346789;
box-shadow: 2px 2px 19px #aaa;
-o-box-shadow: 2px 2px 19px #aaa;
-webkit-box-shadow: 2px 2px 19px #aaa;
-moz-box-shadow: 2px 2px 19px #aaa;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
opacity: 0.8;
width: 80px;
height: 80px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
text-align: center;
z-index: 20;
position: absolute;
background-color: #eeeeef;
color: black;
font-family: helvetica, sans-serif;
padding: 0.5em;
font-size: 0.9em;
-webkit-transition: -webkit-box-shadow 0.15s ease-in;
-moz-transition: -moz-box-shadow 0.15s ease-in;
-o-transition: -o-box-shadow 0.15s ease-in;
transition: box-shadow 0.15s ease-in;
}
.flowchart-demo .window:hover {
box-shadow: 2px 2px 19px #444;
-o-box-shadow: 2px 2px 19px #444;
-webkit-box-shadow: 2px 2px 19px #444;
-moz-box-shadow: 2px 2px 19px #444;
opacity: 0.6;
}
.flowchart-demo .active {
border: 1px dotted green;
}
.flowchart-demo .hover {
border: 1px dotted red;
}
#flowchartWindow1 {
top: 34em;
left: 5em;
}
#flowchartWindow2 {
top: 7em;
left: 36em;
}
#flowchartWindow3 {
top: 27em;
left: 48em;
}
#flowchartWindow4 {
top: 23em;
left: 22em;
}
.flowchart-demo .jtk-connector {
z-index: 4;
}
.flowchart-demo .jtk-endpoint, .endpointTargetLabel, .endpointSourceLabel {
z-index: 21;
cursor: pointer;
}
.flowchart-demo .aLabel {
background-color: white;
padding: 0.4em;
font: 12px sans-serif;
color: #444;
z-index: 21;
border: 1px dotted gray;
opacity: 0.8;
cursor: pointer;
}
.flowchart-demo .aLabel.jtk-hover {
background-color: #5C96BC;
color: white;
border: 1px solid white;
}
.window.jtk-connected {
border: 1px solid green;
}
.jtk-drag {
outline: 4px solid pink !important;
}
path, .jtk-endpoint {
cursor: pointer;
}
.jtk-overlay {
background-color:transparent;
}
/* ---------------------------------------------------------------------------------------------------- */
/* --- page structure --------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------- */
body {
background-color: #FFF;
color: #434343;
font-family: "Lato", sans-serif;
font-size: 14px;
font-weight: 400;
height: 100%;
padding: 0;
}
.jtk-bootstrap {
min-height:100vh;
display:flex;
flex-direction: column;
}
.jtk-bootstrap .jtk-page-container {
display:flex;
width:100vw;
justify-content: center;
flex:1;
}
.jtk-bootstrap .jtk-container {
width: 60%;
max-width:800px;
}
.jtk-bootstrap-wide .jtk-container {
width: 80%;
max-width:1187px;
}
.jtk-demo-main {
position: relative;
margin-top:98px;
display:flex;
flex-direction:column;
}
.jtk-demo-main .description {
font-size: 13px;
margin-top: 25px;
padding: 13px;
margin-bottom: 22px;
background-color: #f4f5ef;
}
.jtk-demo-main .description li {
list-style-type: disc !important;
}
.jtk-demo-canvas {
height:750px;
max-height:700px;
border:1px solid #CCC;
background-color:white;
display: flex;
flex-grow:1;
}
.canvas-wide {
margin-left:0;
}
.miniview {
position: absolute;
top: 25px;
right: 25px;
z-index: 100;
}
.jtk-demo-dataset {
text-align: left;
max-height: 600px;
overflow: auto;
}
.demo-title {
float:left;
font-size:18px;
}
.controls {
top: 25px;
color: #FFF;
margin-right: 10px;
position: absolute;
left: 25px;
z-index: 1;
display:flex;
}
.controls i {
background-color: #5184a0;
border-radius: 4px;
cursor: pointer;
margin-right: 4px;
padding: 4px;
}
li {
list-style-type: none;
}
/* ------------------------ node palette -------------------- */
.sidebar {
margin:0;
padding:10px 0;
background-color: white;
display:flex;
flex-direction:column;
border: 1px solid #CCC;
align-items: center;
}
.sidebar-item {
background-color: #CCC;
border-radius: 11px;
color: #585858;
cursor: move;
padding: 8px;
width: 128px;
text-align: center;
margin: 10px;
outline:none;
}
button.sidebar-item {
cursor:pointer;
width:150px;
}
.sidebar select {
height:35px;
width:150px;
outline:none;
}
.sidebar-item.katavorio-clone-drag {
margin:0;
border:1px solid white;
}
.sidebar-item:hover, .sidebar-item.katavorio-clone-drag {
background-color: #5184a0;
color:white;
}
/*
.sidebar button {
background-color: #30686d;
outline: none;
border: none;
margin-left: 25px;
padding: 7px;
color: white;
cursor:pointer;
}*/
.sidebar i {
float:left;
}
#media (max-width: 600px) {
.sidebar {
float:none;
height: 55px;
width: 100%;
padding-top:0;
}
.sidebar ul li {
display:inline-block;
margin-top: 7px;
width:67px;
}
.jtk-demo-canvas {
margin-left: 0;
margin-top:10px;
height:364px;
}
}
/* ---------------------------------------------------------------------------------------------------- */
/* --- jsPlumb setup ---------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------- */
.jtk-surface-pan {
display:none;
}
.jtk-connector {
z-index:9;
}
.jtk-connector:hover, .jtk-connector.jtk-hover {
z-index:10;
}
.jtk-endpoint {
z-index:12;
opacity:0.8;
cursor:pointer;
}
.jtk-overlay {
background-color: white;
color: #434343;
font-weight: 400;
padding: 4px;
z-index:10;
}
.jtk-overlay.jtk-hover {
color: #434343;
}
path {
cursor:pointer;
}
.delete {
padding: 2px;
cursor: pointer;
float: left;
font-size: 10px;
line-height: 20px;
}
.add, .edit {
cursor: pointer;
float:right;
font-size: 10px;
line-height: 20px;
margin-right:2px;
padding: 2px;
}
.edit:hover {
color: #ff8000;
}
.selected-mode {
color:#E4F013;
}
.connect {
width:10px;
height:10px;
background-color:#f76258;
position:absolute;
bottom: 13px;
right: 5px;
}
/* header styles */
.demo-links {
position: fixed;
right: 0;
top: 57px;
font-size: 11px;
background-color: white;
opacity: 0.8;
padding-right: 10px;
padding-left: 5px;
text-transform: uppercase;
z-index:100001;
}
.demo-links div {
display:inline;
margin-right:7px;
margin-left:7px;
}
.demo-links i {
padding:4px;
}
.jtk-node {
background-color: #5184a0;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
position: absolute;
z-index: 11;
overflow: hidden;
min-width:80px;
min-height:30px;
width: auto;
}
.jtk-node .name {
color: white;
cursor: move;
font-size: 13px;
line-height: 24px;
padding: 6px;
text-align: center;
}
.jtk-node .name span {
cursor:pointer;
}
[undo], [redo] { background-color:darkgray !important; }
[can-undo='true'] [undo], [can-redo='true'] [redo] { background-color: #3E7E9C !important; }
</style>
See the official article to integrate with VueJs:
https://docs.jsplumbtoolkit.com/toolkit/current/articles/demo-vue2.html