Help with Dojo Drag & Drop - javascript

Hi I am trying to implement some drag and drop functionality into a project but am having a bit of difficulty with a few aspects. One of the problems I am having is with the creation of a custom avatar, I am able to achieve a custom avatar but have to cheat slightly and hide some elements with CSS.
Below is a snippet of my code not the entire code, I have required all necessary pacakges etc.
dojo.query(".canDrag").forEach(function(node, index, nodelist){
var createSource = new dojo.dnd.Source(
node, {copyOnly: true, creator: createAvatar}
);
function createAvatar(item, hint) {
var node = dojo.doc.createElement("span");
dojo.addClass(node, "avatarStyle");
if ( hint == "avatar" ) {
var dHtml = item;
console.log("trying " + dHtml);
node.innerHTML = item;
}
else {
console.log("if this show remove redudant call");
}
return {node: node, data: item, type: "product", copyOnly: true};
};
Ok so as you can see I create my DnD source then give it a custom creator where I attempt to build my own custom Avatar. The actyual draggable markup is below:
<div class="canDrag panelLeft">
<div class="dojoDndItem" dndType="product" title="digitalCamera" id="12345">
<h3 class="productTitle">Samsung ES71</h3>
<p class="productType">Compact Digital Camera</p>
<img src="img/small_Cam.png" class="imgLMargin" alt="Samsung ES71"/>
<div class="dragHandle">
</div>
</div>
</div>
Rather than append the entire div from canDrag and down I would like to grab different elements such as the image and .product title and just display those. If any one has any ideas I thank you in advance also if my question has not been clear enough I can try to rephrase.
Cheers

CSS should be fine. Otherwise, you can either use the dndData attribute for your items, or add the items manually to your DnD source object.
When using dojoDndItem class, Dojo expects the visualization of the avatar to be already resolved in the markup itself. That's why it passes the inner HTML as the item data. This is for the most simple and common case, when you would not use a custom creator. I think using CSS to customize the avatar is fine. Even if you don't use a custom creator to set the avatarStyle class, you can take advantage of the fact that Dojo puts the avatar inside its own container marked with the class dojoDndAvatar (or dojoDndAvatarItem). Take a look at the source at dojo/dnd/Avatar.js.
If you still want to use a custom creator, you have a couple of options:
Add a dndData attribute to your items. In that case, that's what gets passed to the creator function as the item parameter. It can be anything, and you can use that to further customize the avatar. Eg. you could serialize a JSON object and dynamically create the avatar from that object, or you could set it to a unique id and then use dojo.query() to access the nodes below it.
Remove the items entirely add them programmatically with the insertNodes() method. In that case, your creator function must implement both the case for the avatar and the case for the actual item.
It doesn't address your question in particular, but here's an excellent Dojo DnD tutorial.
The Dojo reference guide is also helpful, once you understand what's happening. And of course, use the source Luke!

Related

Is it possible to mix two GraphQL queries into one query?

I am using Gatsby as a frontend to a WordPress backend. I have successfully fetched the menu items but I now need to link them to the pages I have created. I have added the Menu Links in my Gatsbyconfig.js but I have no idea on how I can go about mapping it to the menu items at the same time as the menu itself. It ends up contradicting each other. Is this possible to do? I am quite new at graphQL. Never touched it till this project. Below is the GraphQl Query
{
allWpMenuItem(filter: {menu: {node: {name: {eq: "Navigation Menu"}}}}) {
edges {
node {
label
}
}
}
site {
siteMetadata {
title
menuLinks {
name
link
}
}
}
}
The label holds the name for each menu and the link holds the link to the pages I have created. I am trying to fix this into this bit of code
<div>
{props.allWpMenuItem.edges.map(edge =>(
<a href="#" key={edge.node.label}>
{edge.node.label}
</a>
))}
</div>
I am trying to query the menu links change the anchor tag to the link item and then point it to the menu link.
The short answer is that you can't, natively each query is a separate entity. However, you can combine queries using createResolvers, for further details check: https://github.com/gatsbyjs/gatsby/discussions/28275 (thanks LekoArts for the feedback).
However, you have a few approaches to bypass this limitation. Given that each query is transformed into a standalone JavaScript object, you can always manipulate them using native JavaScript filtering or doing whatever you need. The idea is to create an empty object and populate it with links and labels, in a single loop or using two different, then, in your component, iterate through this object instead of through the props like you are doing now.
I can't create a minimal example without knowing the internal structure or without knowing what's inside menuLinks.name.
By the way, if you are using internal navigation I would hardly suggest using <Link> component, among other things, using the #reach/router (extended from React's router) will only render the necessary parts of each component, creating smoother navigation than using native anchor tag, what will refresh all the page.

Custom properties and select/drop down menus

I'm customising mxGraph and am looking into custom properties. I'm using the mxDraw template. Creating custom properties for a shape is easy enough such as the below (modifying diagrameditor.xml):
<add as="rectangle">
<Rect label="Rectangle" href="" new_property="hi there">
<mxCell vertex="1">
<mxGeometry as="geometry" width="80" height="40"/>
</mxCell>
</Rect>
</add>
When using mxDraw these properties are immediately visible and all is well, except I want to be able to choose a value for this property using a drop-down menu.
I'm having trouble finding an example of this elsewhere on the internet, and I'm not really sure what my options are here, or where to start.
In my mind there are a few options, but am hoping I can get some guidance on where to even start here...
Disregard custom properties like the above and instead use a separate DIV, an event listener for selected shape and use HTML forms to capture properties/values
Modify diagrameditor.xml like the above code snippet and somehow modify existing javascript library
Some sort of hybrid of the above 2?
Eventually the resulting graph XML/data will be POSTed to the web server for storage, which can then merge/correlate data accordingly.
I know SO has some experienced mxGraph programmers so hoping to get some thoughts from those more experienced than I, so I can go and research. I can't seem to find what I'm looking for so far (in terms of guidance, examples or others attempting a similar thing).
Any help appreciated, thanks.
I kept researching and found a solution that suits me.
Update diagrameditor.xml and create custom data fields. Example shown in my original post
Create a new DIV on the page
Setup a listener for cell selection, add some HTML forms then use mxGraph calls to update the property values
To be done: disable the right-click menu so it cannot be updated manually using the text field, to ensure inputs are valid
Below is the starting code for the HTML page.
<div id="customdata" style="position:relative;padding-left:100px;padding-top:10px;">
<script>
var selectedCell;
mxGraph.prototype.addListener(mxEvent.CLICK, function(sender, event){
selectedCell = event.getProperty('cell');
//update DIV content example. Show cell ID and a drop-down to update a custom data field called 'new_property' (as created in diagrameditor.xml)
var newhtml = "Cell ID is: ";
newhtml += selectedCell.getId();
newhtml += `<select><option id="selectmenu" value="one">one</option><option value="two">two</option></select>`
newhtml += `<button onclick="updateCustomData();">save</button>`
document.getElementById("customdata").innerHTML = newhtml;
//Can also iterate through a multiple-selection and do more things, if need be
/*
for (var i = 0; i < sender.getSelectionCount(); i++) {
//example, alert showing the label of the cell at this array index i
alert(sender.getSelectionCells()[i].getAttribute('label', ''));
}
*/
});
function updateCustomData() {
selectedCell.setAttribute("new_property", document.getElementById("selectmenu").value);
}
</script>
div content
</div>
I hope this question can be left here to serve as potential help for others. I'm still a new SO user, great community. Hope this can be my little contribution.

Prototype not defined when accessing children on creation of custom-tag

__What I am trying todo____
Right now I am working with custom HTML5 tags. I am trying to create a tab-control element that is easy to set up. Todo this I create a parent element called 'tab-set' which works much like the 'ul' tag.
To insert tabs, you simply insert 'tab-element' tags (like 'li' tags). The tags can implement own behavior through custom prototypes which extend standardized element-prototypes such as the basic HTMLElement and are then registered with 'document.registerElement()'. At that point there are also opportunities to set callbacks that let you know whenever your element has been created or attached to something, which is what I use to do the necessary calculations on the placement of the individual tabs on the tab-control.
Let me say up-front that I've had trouble with this at first, then got it working, but upon rewriting the whole thing had troubles again for who knows why.
So, in the creation routine of the tab-set I iterate through all the child-tab-elements, calling their custom function 'setDimension', or atleast I am trying to. For some reason Chrome won't initialize the tab-element prototype (setDimension etc) before it has called both 'createdCallback' and 'attachedCallback' on my tab-set. This means that I can't call the child elements custom functions to set it's placement on creation of the tab-set.
Here you have some code samples of what I just described.
simple.html
...
<tab-set>
<tab-element>
<img>guybrush</img>
</tab-element>
<tab-element>
<img>le chuck</img>
</tab-element>
</tab-set>
...
tabs.js
...
tabSet = Object.create(HTMLDivElement.prototype);
tabSet.attachedCallback = function(){
for(/** calculations here **/)
listOfChildren[index].setDimensions(/** placement info **/);
//
// Chrome console: 'setDimensions' is not a function!
//
}
tabElement = Object.create(HTMLDivElement.prototype);
tabElement.setDimensions = function(/** placement info **/){
$(this).css(...);
}
document.registerElement('tab-set',tabSet);
document.registerElement('tab-element',tabElement);
...
The weird thing is that I have a working version of this, and yes, I have tried to emulate it's particular conditions such as for example loading the html-portion through jquery's .load() routine. But no matter what I do, I can not get this to work in my current script. What knowledge am I missing?
Thanks in advance for any help.
__ Solved __
All I had todo was add a link-tag inside the tab-set and have the tab-elements load it's containing style-class. I guess making the tab-elements have a css-class is somehow provoking Chrome to load their prototypes 'prematurely'.

Need help with adding Classes to HTML Editor Elements

I'm using a WYSIWYG Editor called CKEditor and its really awesome. Inside the editor, whenever I add a new Heading/Text/DIV/Image/ anything else for that matter, I want it to stay attached with a class:
Like
<h2 class="blah">Sample Text</h2>
<img src="/abc.png" class="blah1" />
Here's a reference link:
http://docs.cksource.com/CKEditor_3.x/Developers_Guide
I'm not good with Javascript, if anyone can help me out, I would be really Grateful!
Thank you.
For example, the following code will ensure that elements will have their "alt" attribute filled:
editor.dataProcessor.htmlFilter.addRules(
{
elements :
{
img : function( element )
{
if ( !element.attributes.alt )
element.attributes.alt = 'An image';
}
}
});
Read the Overview (http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Data_Processor) this example was taken from there.
You can do the same for "class". Take a look at the existing output, then either add "class" if it's missing or replace them if that's your intent.
take a look at answers for this question.
customize the dialogs during the define, add a "class" field and then set and get the contents in the setup and commit functions.
look at ckeditor/_samples/api_dialog.html for background on modifying dialogs.
for the Headings you'd have to look at modifying the commands. Those don't have dialogs.
There's probably a way to always apply the same class based on the particular tag in the "data processor". Do you want to always set the same class everytime or allow the user to choose the class, that's important because it changes your options quite a bit.

html & javascript: How to store data referring to html elements

I'm working on a web application that uses ajax to communicate to the server.
My specific situation is the following:
I have a list of users lined out in the html page. On each of these users i can do the following: change their 'status' or 'remove' them from the account.
What's a good practice for storing information in the page about the following:
the user id
the current status of the user
P.S.: I'm using jQuery.
There is jQuery's data function
$('li').data('userid',uid); // sets the value of userid
uid = $('li').data('userid'); // retrieves the value of userid
official documentation: http://docs.jquery.com/Data
There is a lot of debate as to what is best to use. You can store the data a lot of ways, and they all make someone happy - custom attributes will of course not validate if you use XHTML, and using classes to store one or two bits of data is clumsy at best and only gets worse with the amount of things you want to know. Personally, not only am I not a big fan of XHTML, I am also not much of a validation nazi, so I recommend going with the custom attributes.
There is, however, an option that reconciles custom attributes with standards: data- attributes. As John Resig (the author of jQuery) writes about in his blog, this is a new attribute being introduced in HTML5 that allows you to specify custom data attributes with the data- prefix. So a perfectly valid element might look like this:
<ul>
<li data-userid='5' data-status='active'>Paolo Bergantino</li>
</ul>
This has the upside that while you are still using custom attributes which may be bad if you are using XHTML, your code is going to age very well as this is the way that we will be storing data related to a particular item in the future.
Some further reading is Attributes > Classes: Custom DOM Attributes for Fun and Profit.
Perhaps using custom attributes, so for a user's list element, add attributes for the variables:
<li uid="theuserid" ustatus="thestatus"></li>
The values can then be got with the attr function:
$("li").attr("uid")
And
$("li").attr("ustatus")
Note: The selectors will need to be changed, obviously
Please note that there are differing opinions on the use of custom attributes - however, this should be fine for every major browser. It is also the least complex solution I can think of. It doesn't require jumping to sibling elements to get data, or finding elements nearby, which can all add a small amount of overhead to processing - I try to minimise the amount of extra bloat I add to the DOM when doing such things.
jQuery Data
If you want to store custom data against a jQuery object, use the data function.
$('#myField').data('name', 'Jack');
var name = $('#myField').data('name');
HTML5 data-* Attributes
You can also use the HTML5 data-* attributes, though the APIs for accessing these are only partially supported by the different browsers. Here's some more information about that.
<div data-userid="123" class="user-row">
programmatically:
$('#myElement').attr('data-fruit', 'apple');
// or
document.getElementById('myElement').dataset.fruit = 'apple';
Hidden Fields
If you want to do things the old browser-compatible way and stuff the metadata directly into your html, you can use hidden fields. It's a bit cruder but easy enough to do.
<input type="hidden" name="UserID" value="[userid]" />
You can easily use jQuery selectors to query your list and find html blocks that contain user items that have the relevant hidden fields that match the metadata you are querying for.
In this case, I think custom attributes might be overkill. You can store the user-id in the id-attribute, since there will only be one instance of the user in the list, right? Also, the status of the user could be stored in the class-attribute. In this way, each user could be given different styling in CSS, such as green for active, yellow for a non-activated account, and red for a suspended account.
The code for fetching the status will, however, be a little more complex compared to using a custom attribute (But only if you also want to have multiple classes). On a more positive note, the HTML will validate with this approach whereas it would not with custom attributes.
<ul id="userList">
<li id="uid123" class="active">UserName X</li>
<li id="uid456" class="suspended">Mr. Troll</li>
</ul>
/**
* Simple function for searching (strict) for a value in an array
* #param array arr The array to look in
* #param mixed val The value to look for in arr. Note that the value is looked for using strict comparison
* #return boolean true if val is found in arr, else false
*/
function searchArray(arr, val) {
for(var i = 0, len = arr.length; i < len; i++) {
if(arr[i] === val) {
return true;
}
}
return false;
}
/**
* Gets a known status from a string of class names. Each class name should be separated
* by a space.
* #param string classNames The string to check for a known status
* #return string|false The status if found in classNames, else false
*/
function getStatus(classNames) {
// The different statuses a user can have. Change this into your own!
var statuses = ['active', 'suspended', 'inactive'],
nameArr = classNames.split(" ");
for(var i = 0, nameLen = nameArr.length; i < nameLen; i++) {
// If we find a valid status among the class names, return it
if(searchArray(statuses, nameArr[i])) {
return nameArr[i];
}
}
return false; // We didn't find any known status in classNames
}
var id = $("li").attr("id"); // Fetches the id for the first user
var status = getStatus($("li").attr("class")); // Fetches the status of the first user
The metadata plugin to jquery is your answer.
<html >
<head>
<script src="/js/jquery-1.3.2.js"></script>
<script src="/js/jquery.metadata.js"></script>
</head>
<body>
<ul>
<li type="text" class="{UID:'1',status:'alive'}">Adam</li>
<li type="text" class="{UID:'2',status:'alive'}">Bob</li>
<li type="text" class="{UID:'3',status:'alive'}">Carol</li>
</ul>
<script>
$('li').each(function(){window.console.log($(this).metadata().UID)});
</script>
</body>
</html>
There are various ways of doing it depending on the kind of data you are storing and how much information you are storing in the page in general. It's best to devise a consistent scheme so you can write a simple library call to do the work. For example,
You can store data in the class of a particular element. This may require additional wrapper elements in order to be able to provide an additional class to drive your CSS. This also restricts the storable content format. User ID may well fit into a class attribute.
You can store data in an unused href of a Javascript activated link. This has the additional feature of showing the data in the status bar as part of the URL on rollover. For instance you can store '#userid' or even just 'userid' in the href.
You can store data in additional elements. For instance you can have a nested div with a class that indicates storage which also triggers CSS to take the element out of the display. This is the most general and extensive support you can probably arrange in page.
Rather than a nested div you could also use nested input tags with type="hidden". This is kind of more expected / traditional and doesn't require CSS to take them out of the display. You can use the id attribute to identify these inputs, or you can use the location on the page. For instance, put them inside the link that the user clicks and then just search inside the current link in the onclick handler.
To answer the question of "how to get it into the document in the first place", I suggest a layout similar to this:
<ul id="users">
<li id="someUserId" class="someStatus">Some Username</li>
<li id="someOtherUserId" class="someOtherStatus">Some Username</li>
</ul>
This allows you to easily select a lot of info about your users:
$('#users > li') // all user elements
$('.someStatus') // all users of a particular status
Then in your event handlers it's also easy to get the current status:
$(this).attr('class') //get current status once you have a user element selected.
Another alternative is to dump javascript to the page and simply have it use the jquery data functionality to store the data as soon as the page loads. You'd still need an id on the element in order to find the right one though.

Categories

Resources