How to fix parse error during Vue app deployment? - javascript

I try to deploy Vue app for uploading on GitHub Pages, but got such parse error:
95: </script>
^
96: <template>
97: <Navbar
error during build:
Error: Parse error #:95:10
at parse$b (file:///C:/Users/User/vContact/vContact/node_modules/vite/dist/node/chunks/dep-a713b95d.js:33668:355)
at Object.transform (file:///C:/Users/User/vContact/vContact/node_modules/vite/dist/node/chunks/dep-a713b95d.js:42856:27)
My code:
<script >
import { RouterLink, RouterView } from "vue-router";
import Navbar from "./components/Navbar.vue";
import Contacts from "./components/Contacts.vue";
import Modal from "./components/Modal.vue";
import AddButton from "./components/AddButton.vue";
export default{
components: {
Navbar,
Contacts,
Modal,
AddButton
},
data(){
return {
currentId: 1,
modalOpen: false,
contacts: [],
edit: false,
editContact: {},
search: ''
}
},
created(){
this.getContacts()
},
computed: {
// filterContacts(){
// return this.search ? this.contacts.filter(contact =>
// contact.fullName.toLowerCase().includes(this.search.toLowerCase())) :
this.contacts;
// },
filterContacts(){
return this.search ?
this.contacts.filter(contact => {
for(let key in contact){
if(String(contact[key]).toLowerCase().includes(this.search.toLowerCase())){
return contact;
}
}
})
: this.contacts;
},
// filterContactsCategory(){
// return this.search ? this.contacts.filter(contact =>
contact.category.toLowerCase().includes(this.search.toLowerCase())) : this.contacts;
// }
},
methods: {
openModal(){
this.modalOpen = true
},
closeModal(){
this.modalOpen = false
this.edit = false
},
addContact(item){
this.contacts.push(item)
this.modalOpen = false
},
deleteContact(id){
let index = this.contacts.findIndex(contact => contact.id == id)
this.contacts.splice(index, 1)
},
changeContact(id){
this.edit = this.modalOpen = true
let pickedContact = this.contacts.find(contact => contact.id == id)
this.editContact = pickedContact
},
editedContact(contactEdited){
this.contacts.forEach(contact => {
if(contact.id == contactEdited.id) {
contact.fullName = contactEdited.fullName
contact.phoneNumber = contactEdited.phoneNumber
contact.email = contactEdited.email
contact.category = contactEdited.category
}
})
},
getContacts(){
const localContacts = localStorage.contacts
if(localContacts){
this.contacts = JSON.parse(localContacts)
}
}
},
watch: {
contacts: {
handler(newContacts){
localStorage.contacts = JSON.stringify(this.contacts)
},
deep: true
}
}
}
</script>
<template>
<Navbar
#searchValue="search = $event"
/>
<Contacts
:contacts="filterContacts"
#delContact="deleteContact"
#changeContact="changeContact"
:search="search"
/>
<Modal
:edit="edit"
:editContact="editContact"
#addContact="addContact"
:currentId="currentId"
v-show="modalOpen"
#closeModal="closeModal"
#editedContact="editedContact"
/>
<AddButton
#openModal="openModal"
/>
<RouterView />
</template>

did you put
import vue from '#vitejs/plugin-vue';
in your vite.config.js file, after (of course) installing it via npm?

Expanding on Stephan Franks answer, I had the exact same error as OP, and putting this in vite.config.ts did the trick for me.
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()], // <-- This is where the magic happens
})
For more information, please have a look at the documentation for #vitejs/plugin-vue.

Related

The right way to draw a Map when data is ready

I need to render a map using Mapbox only when data is ready.
I have the following code in my Vuex store:
/store/index.js
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
In my App.vue I fetch all APIs requests using an async created method.
App.vue:
<template>
<div id="app">
<router-view />
</div>
</template>
<script>
export default {
name: "App",
async created() {
await this.$store.dispatch("FETCH_ACTIVITY");
await this.$store.dispatch("FETCH_GEO_IPS");
},
};
</script>
In my Dashboard component I have a conditional rendering to draw the maps component only when geoIps.length > 0
Dashboard.vue:
<template>
<div v-if="geoIps.length > 0">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
geoIps() {
return this.$store.state.geoIps;
},
};
</script>
Then I load the Maps component.
<template>
<q-card class="bg-primary APP__card APP__card-highlight">
<q-card-section class="no-padding no-margin">
<div id="map"></div>
</q-card-section>
</q-card>
</template>
<script>
import "mapbox-gl/dist/mapbox-gl.css";
import mapboxgl from "mapbox-gl/dist/mapbox-gl";
export default {
name: "maps-geo-ips-card",
computed: {
geoIps() {
return this.$store.state.geoIps;
},
},
created() {
mapboxgl.accessToken = process.env.MAPBOX_API_KEY;
},
mounted() {
const mapbox = new mapboxgl.Map({
container: "map",
center: [0, 15],
zoom: 1,
});
this.geoIps.map((geoIp) =>
new mapboxgl.Marker()
.setLngLat([geoIp.longitude, geoIp.latitude])
.addTo(mapbox)
);
},
};
</script>
<style>
#map {
height: 500px;
width: 100%;
border-radius: 25px;
overflow: hidden;
}
</style>
The problem is that when the function resolves the first IP address, the map is drawn showing only one address and not all the others like this:
What is the best way to only draw the map when my FETCH_GEO_IPS function has finished?
Thanks in advance
I think the answer lies in this bit of code:
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
});
});
Your map function loops through every element of the array and commits each IP one by one. So when the first one is committed, your v-if="geoIps.length > 0" is true.
A workaround would be to set a flag only when the IPs are set.
This is a proposed solution:
import Vue from "vue";
import Vuex from "vuex";
import _ from "lodash";
import { backendCaller } from "src/core/speakers/backend";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
// Activity
activity: [],
geoIps: [],
isReady: false
},
mutations: {
// Activity
setActivity: (state, value) => {
state.activity = value;
},
setGeoIp: (state, value) => {
state.geoIps.push(value);
},
setIsReady: (state, value) => {
state.isReady = value;
}
},
actions: {
// Activity
async FETCH_ACTIVITY({ commit, state }, force = false) {
if (!state.activity.length || force) {
await backendCaller.get("activity").then((response) => {
commit("setActivity", response.data.data);
});
}
},
async FETCH_GEO_IPS({ commit, getters }) {
let tofetch = getters.activityIps.length; // get the number of fetch to do
const geoIpsPromises = getters.activityIps.map(async (activityIp) => {
return await Vue.prototype.$axios
.get(
`http://api.ipstack.com/${activityIp}?access_key=${process.env.IP_STACK_API_KEY}`
)
.then((response) => {
return response.data;
});
});
geoIpsPromises.map((geoIp) => {
return geoIp.then((result) => {
commit("setGeoIp", result);
toFetch -= 1; // decrement after each commit
if (toFetch === 0) {
commit("setIsReady", true); // all commits are done
}
});
});
},
},
getters: {
activityIps: (state) => {
return _.uniq(state.activity.map((activityRow) => activityRow.ip));
},
},
strict: process.env.DEV,
});
And in your view:
<template>
<div v-if="isReady">
<maps-geo-ips-card />
</div>
</template>
<script>
import mapsGeoIpsCard from "components/cards/mapsGeoIpsCard";
export default {
name: "dashboard",
components: {
mapsGeoIpsCard,
},
computed: {
activity() {
return this.$store.state.activity;
},
activityIps() {
return this.$store.getters.activityIps;
},
isReady() {
return this.$store.state.isReady;
},
};
</script>

Apollo Queries triggered and causing rerendering in useContext on URL Change

GIF of Rerender occuring
I'm not sure how to proceed. As you can see, the Header's state (as passed down via context) is switching from the user's data --> undefined --> same user's data. This occurs every time there's a url change, and doesn't happen when I do things that don't change the url (like opening the cart for example).
Is this expected behaviour? Is there any way I can get the query in my context to only be called when there is no user data or when the user data changes? I tried using useMemo, but to no avail.
auth.context
import React, { useState} from "react";
import {
CURRENT_USER,
GET_LOGGED_IN_CUSTOMER,
} from "graphql/query/customer.query";
import { gql, useQuery, useLazyQuery } from "#apollo/client";
import { isBrowser } from "components/helpers/isBrowser";
export const AuthContext = React.createContext({});
export const AuthProvider = ({ children }) => {
const [customer, { data, loading, error }] = useLazyQuery(
GET_LOGGED_IN_CUSTOMER,
{
ssr: true,
}
);
const { data: auth } = useQuery(CURRENT_USER, {
onCompleted: (auth) => {
console.log(auth);
customer({
variables: {
where: {
id: auth.currentUser.id,
},
},
});
},
ssr: true,
});
console.log(data);
const isValidToken = () => {
if (isBrowser && data) {
const token = localStorage.getItem("token");
if (error) {
console.log("error", error);
}
if (token && data) {
console.log("token + auth");
return true;
} else return false;
}
};
const [isAuthenticated, makeAuthenticated] = useState(isValidToken());
function authenticate() {
makeAuthenticated(isValidToken());
}
function signout() {
makeAuthenticated(false);
localStorage.removeItem("token");
}
return (
<AuthContext.Provider
value={{
isAuthenticated,
data,
authenticate,
auth,
signout,
}}
>
{children}
</AuthContext.Provider>
);
};
(In Header, userData is equal to data just passed through an intermediary component (to provide to mobile version)).
header.tsx
import React, { useContext } from "react";
import Router, { useRouter } from "next/router";
import { useApolloClient } from "#apollo/client";
import { openModal } from "#redq/reuse-modal";
import SearchBox from "components/SearchBox/SearchBox";
import { SearchContext } from "contexts/search/search.context";
import { AuthContext } from "contexts/auth/auth.context";
import LoginModal from "containers/LoginModal";
import { RightMenu } from "./Menu/RightMenu/RightMenu";
import { LeftMenu } from "./Menu/LeftMenu/LeftMenu";
import HeaderWrapper from "./Header.style";
import LogoImage from "image/hatchli-reduced-logo.svg";
import { isCategoryPage } from "../is-home-page";
type Props = {
className?: string;
token?: string;
pathname?: string;
userData?: any;
};
const Header: React.FC<Props> = ({ className, userData }) => {
const client = useApolloClient();
const { isAuthenticated, signout } = useContext<any>(AuthContext);
const { state, dispatch } = useContext(SearchContext);
console.log(isAuthenticated);
console.log(userData);
const { pathname, query } = useRouter();
const handleLogout = () => {
if (typeof window !== "undefined") {
signout();
client.resetStore();
Router.push("/medicine");
}
};
const handleJoin = () => {
openModal({
config: {
className: "login-modal",
disableDragging: true,
width: "auto",
height: "auto",
animationFrom: { transform: "translateY(100px)" },
animationTo: { transform: "translateY(0)" },
transition: {
mass: 1,
tension: 180,
friction: 26,
},
},
component: LoginModal,
componentProps: {},
closeComponent: "",
closeOnClickOutside: true,
});
};
const onSearch = (text: any) => {
dispatch({
type: "UPDATE",
payload: {
...state,
text,
},
});
};
const { text } = state;
const onClickHandler = () => {
const updatedQuery = query.category
? { text: text, category: query.category }
: { text };
Router.push({
pathname: pathname,
query: updatedQuery,
});
};
const showSearch = isCategoryPage(pathname);
return (
<HeaderWrapper className={className}>
<LeftMenu logo={LogoImage} />
{showSearch && (
<SearchBox
className="headerSearch"
handleSearch={(value: any) => onSearch(value)}
onClick={onClickHandler}
placeholder="Search anything..."
hideType={true}
minimal={true}
showSvg={true}
style={{ width: "100%" }}
value={text || ""}
/>
)}
<RightMenu
isAuth={userData}
onJoin={handleJoin}
onLogout={handleLogout}
avatar={userData && userData.user && userData.user.avatar}
/>
</HeaderWrapper>
);
};
export default Header;

gatsby build fail when passing navigate state from react component to page

I'm building a help page with Gatsby and have a search bar (Searchbar.js) where I'm trying to pass the user's input in the field (search bar is always present within the page--think like Evernote's help page) to a component that conducts the search (search.js), which then passes that output to the actual results page (SearchResults.js).
When I do gatsby develop everything works as it should, but when I do a gatsby build I get an error where it says it cant read the property "query" because its undefined (line 63 of search.js: var search = location.state.query.trim()). Why is this failing on build?
Searchbar.js
import React from 'react'
import { navigate } from 'gatsby'
import { FaSearch } from 'react-icons/fa'
import searchbarStyles from "./searchbar.module.css"
export default class Search extends React.Component {
constructor(props) {
super(props)
this.state = {
search: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleSubmit(event) {
event.preventDefault()
var query = this.state.search
navigate(
"/search/",
{
state: { query },
}
)
}
handleChange(event) {
this.setState({
search: event.target.value
})
}
render(){
return (
<div className={searchbarStyles.global_search}>
<div className={searchbarStyles.row}>
<form className={searchbarStyles.search} onSubmit={this.handleSubmit}>
<input
type='text'
id='globalSearchInput'
className=''
placeholder='Search Help & Training'
autoComplete='off'
value={this.state.search}
onChange={this.handleChange}
/>
<button
type='submit'
disabled={!this.state.search}
><FaSearch className={searchbarStyles.searchIcon}/></button>
</form>
</div>
</div>
)
}
}
search.js
import React, { useMemo } from 'react'
import Layout from '../components/Layout'
import SearchResults from '../components/SearchResults'
import { graphql } from 'gatsby'
import Fuse from 'fuse.js'
const matchThreshold = .65
//options for the fuzzy search
var fuseOptions = {
shouldSort: true,
threshold: matchThreshold,
location: 0,
distance: 99999999999,
minMatchCharLength: 1,
includeMatches: true,
includeScore: true,
keys: [
{name: "title", weight: 0.3 },
{name: "content", weight: 0.7}
]
};
function cleanString(string) {
const re = /( |<([^>]+)>)/ig
return string.replace(re,'')
}
function FuzzySearch (query, data) {
fuseOptions.minMatchCharLength = query.length
var dataPrepped = data.map(function(element) {
return {
"title": element.node.frontmatter.title,
"content": cleanString(element.node.html),
"slug": element.node.fields.slug,
}
})
var fuse = useMemo(() => new Fuse(dataPrepped, fuseOptions), [])
var results = fuse.search(query)
//customize the results to only return matches within desired threshold
return results.filter(function(match) {
if(match.score <= matchThreshold) {
return true
}
return false
}).map(function(match) {
return {
"title": match.item.title,
"slug": match.item.slug,
"matches": match.matches
}
})
}
export default ({ location, data }) => {
console.log("SERACH.JS\n")
console.log(JSON.stringify(location))
var search = location.state.query.trim()
var results = []
if(search.length) results = FuzzySearch(search, data.allMarkdownRemark.edges)
return (
<Layout>
<SearchResults FoundItems={results} SearchedTerm={search}> </SearchResults>
</Layout>
)
}
export const query = graphql `
query MyQuery {
allMarkdownRemark {
edges {
node {
fields {
slug
}
frontmatter {
title
date
doctype
}
html
}
}
}
}
`
SearchResults.js
import React from 'react'
import { Link } from 'gatsby'
import searchResultStyles from "./searchresults.module.css"
function resultsPage(resultsBlurb, results) {
return(
<div className={searchResultStyles.content}>
<h1>Search Results</h1>
<p className={searchResultStyles.resultBlurb}>{resultsBlurb}</p>
<ol>
{results.map((match) => (
<li>
<div className={searchResultStyles.resultContent}>
<Link to={match.slug} className={searchResultStyles.resultTitle}>{match.title}</Link>
{match.matches.map(function(instanceOfMatch) {
if(instanceOfMatch.key === "content") {
let startIndex = instanceOfMatch.indices[0][0]
return(
<p className={searchResultStyles.resultExcerpt}>{`...${instanceOfMatch.value.substring(startIndex, startIndex + 100)}...`}</p>
)
}
})}
</div>
</li>
))}
</ol>
</div>
)
}
class SearchResults extends React.Component {
constructor(props) {
super(props)
this.state = {
results: props.FoundItems,
term: props.SearchedTerm,
}
this.updateBlurb = this.updateBlurb.bind(this)
}
updateBlurb() {
let resultsBlurb = `Sorry--could not find any results for "${this.state.term}"`
if (this.state.results.length) {
resultsBlurb = (this.state.results.length === 1) ? `Only 1 item found for "${this.state.term}"` : `Found ${this.state.results.length} items for "${this.state.term}"`
}
return resultsBlurb
}
componentDidUpdate(prevProps) {
if(prevProps!== this.props) {
this.setState ({
results: this.props.FoundItems,
term: this.props.SearchedTerm
})
}
return(resultsPage(this.updateBlurb(), this.props.FoundItems))
}
render() {
return(resultsPage(this.updateBlurb(), this.state.results))
}
}
export default SearchResults
SOLUTION
(within search.js)
export default ({ location, data }) => {
var search = ''
var results = []
if(typeof window !== "undefiend") search = location.state.query.trim()
if(search.length) results = ...
location is short for window.location, but at build-time your code is running in Node.js which does not have a window. Instead consider testing for the existence of window (typeof window !== "undefined") before running your location.state.query.trim call, and fall back to a default value in the case that window does not exist.

Jest spyOn is not a function

I'm using Jest to test my React component but I've encountered an error I had never seen before.
here's my <Row/> component:
class Row extends React.Component {
constructor() {
super();
this.state = { userId: '', showDetails: false, showModal: false, status: '', value: '' }
this.handleStatusChange = this.handleStatusChange.bind(this)
this.handleModalCancel = this.handleModalCancel.bind(this)
this.handleModalConfirm = this.handleModalConfirm.bind(this)
this.showDetailsPanel = this.showDetailsPanel.bind(this)
}
showDetailsPanel() {
if(!this.state.showDetails) {
this.setState({
showDetails: true
});
} else {
this.setState({
showDetails: false
});
}
}
handleModalCancel() {
this.setState({
showModal: false
});
}
handleStatusChange(e) {
this.setState({
showModal: true,
value: e.target.value,
});
}
handleModalConfirm() {
this.setState({
showModal: false
});
this.props.model.status = this.state.value;
}
componentWillMount() {
this.props.model.fullName = `${this.props.model.firstName} ${this.props.model.lastName}`
}
render() {
const { model, modelProps, actions } = this.props;
return (
<div className="c-table__row">
{this.state.showModal ?
<Modal
title="Are you sure?"
copy={`Are you sure that you want to change the staus of this user to ${this.state.value}?`}
confirmText="Yes I'm sure"
cancel={this.handleModalCancel}
submit={this.handleModalConfirm}
/>
: null
}
<div className="c-table__row-wrapper">
{modelProps.map((p, i) =>
<div className={`c-table__item ${model[p] === "Active" || model[p] === "Suspended" || model[p] === "Locked" ? model[p] : ""}`} key={i}>
{model[p]}
</div>
)}
<div className="c-table__item c-table__item-sm">
<a onClick={this.showDetailsPanel} className={this.state.showDetails ? "info showing" : "info"}><Icon yicon="Expand_Cross_30_by_30" /></a>
</div>
</div>
{this.state.showDetails ?
<Details
tel={model.telephoneNumber}
dept={model.department}
role={model.role}
username={model.username}
status={model.status}
statusToggle={this.handleStatusChange}
/>
: null }
</div>
);
}
}
export default Row;
And here's my test:
import React from 'react';
import {shallow, mount} from 'enzyme';
import { shallowToJson } from 'enzyme-to-json'
import renderer from 'react-test-renderer';
import Row from '../../../src/components/tables/row.jsx';
test('clicking the info button should call showDetailsPanel', () => {
const component = mount(<Row model={{a: 1, b: 2}} modelProps={['a', 'b']}/>)
const showDetailsPanel = jest.spyOn(component.instance(), 'showDetailsPanel');
component.update();
const button = component.find('.info')
button.simulate('click')
expect(showDetailsPanel).toBeCalled();
})
So I'm simply trying to check that when the info button is clicked the showDetailsPanel is called, but it fails with TypeError: jest.spyOn is not a function.
I'm using "jest": "^18.1.0","jest-cli": "^18.1.0","jest-junit": "^1.5.1".
Any idea?
Thanks in advance
You're using Jest version 18.1.0 but jest.spyOn has been added in 19.0.0.
If you want to use it, you need to upgrade Jest.
npm install --save-dev jest#latest
Or if you're using Yarn:
yarn upgrade jest --latest

Trouble with drag-and-drop sortable list using ReactJs and react-dnd

Using ReactJs and react-dnd
I want a user to be able to sort the form fields (a.k.a. properties)
I set up the code almost identical to the source code for the Cards in the simple sort demo. There are no console warnings or errors, and I can't figure out why this won't work. I can neither drag nor drop anything.
What it looks like:
Code:
App.js
import EditForm from './Forms/EditForm.js';
var id = $('#form').data('id');
var source = `/api/forms/${id}?include=type,properties.type`;
React.render(
<EditForm source={source} />,
document.getElementById('form')
);
EditForm.js
import React from 'react/addons';
import update from 'react/lib/update';
import Property from './Property.js';
var EditForm = React.createClass({
mixins: [ React.addons.LinkedStateMixin ],
getInitialState: function() {
return {
id: null,
name: null,
slug: null,
properties: []
}
},
componentDidMount: function() {
this.getFormFromServer();
},
getFormFromServer: function () {
$.get(this.props.source, (result) => {
if (this.isMounted()) {
this.setState({
id: result.id,
name: result.name,
slug: result.slug,
properties: result.properties.data
});
}
});
},
moveProperty: function(id, afterId) {
const { properties } = this.state;
const property = properties.filter(p => p.id === id)[0];
const afterProperty = properties.filter(p => p.id === afterId)[0];
const propertyIndex = properties.indexOf(property);
const afterIndex = properties.indexOf(afterProperty);
this.setState(update(this.state, {
properties: {
$splice: [
[propertyIndex, 1],
[afterIndex, 0, property]
]
}
}));
},
render: function() {
const { properties } = this.state;
var propertiesList = properties.map((property, i) => {
return (
<Property
key={property.id}
id={property.id}
type={property.type.name}
name={property.name}
moveProperty={this.moveProperty} />
);
});
return (
<div>
<h1>Form</h1>
<form>
<div className="form-group">
<label>Name:</label>
<input type="text" name="name" valueLink={this.linkState('name')} className="form-control" />
</div>
<div className="form-group">
<label>Properties:</label>
<div className="list-group properties-list">
{propertiesList}
</div>
</div>
</form>
</div>
);
}
});
export default EditForm;
Property.js
import React, { PropTypes } from 'react/addons';
import { DragDropMixin } from 'react-dnd';
import ItemTypes from './ItemTypes';
const dragSource = {
beginDrag(component) {
return {
item: {
id: component.props.id
}
};
}
};
const dropTarget = {
over(component, item) {
component.props.moveProperty(item.id, component.props.id);
}
};
var Property = React.createClass({
mixins: [ React.addons.LinkedStateMixin, DragDropMixin ],
propTypes: {
id: PropTypes.any.isRequired,
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
moveProperty: PropTypes.func.isRequired
},
statics: {
configureDragDrop(register) {
register(ItemTypes.PROPERTY, {
dragSource,
dropTarget
});
}
},
render: function () {
const { type } = this.props;
const { name } = this.props;
const { isDragging } = this.getDragState(ItemTypes.PROPERTY);
const opacity = isDragging ? 0 : 1;
return (
<a className="list-group-item"
{...this.dragSourceFor(ItemTypes.PROPERTY)}
{...this.dropTargetFor(ItemTypes.PROPERTY)}>
{type}: {name}
</a>
);
}
});
export default Property;
ItemTypes.js
module.exports = {
PROPERTY: 'property'
};
If anybody could help I would greatly appreciate it. It's kind of sad how much time I've actually spent trying to figure this out.
Reference links:
My code on github
Demo example
Demo source on github
After spending over a day trying to get the drag and drop working I fixed it with one single line of code.
import React from 'react/addons';
How it compiled and rendered at all without that, I don't even know.

Categories