I have a kendo UI treewiew, which represents some directory structure and context menu:
#(Html.Kendo().ContextMenu()
.Name("menu")
.Target("#treeview")
.Orientation(ContextMenuOrientation.Horizontal)
.Animation(animation => animation.Open(open =>
{
open.Fade(FadeDirection.In);
open.Duration(500);
}))
.Items(items =>
{
items.Add()
.Text("Test");
})
//.Events(e=>e.Open("contextOpen"))
)
<div class="treeview-back">
#(Html.Kendo().TreeView()
.Name("treeview")
.TemplateId("treeview-template")
.Checkboxes(checkboxes => checkboxes
.Name("checkedFiles")
.CheckChildren(true)
)
.Events(events => events
.Check("onCheck")
)
.DataTextField("Name")
.DataSource(dataSource => dataSource
.Model(m => m.Id("Path").HasChildren("HasChildren"))
.Read("ReadDirectory", "Download"))
)
I need to show context menu only for elements that represent folders. But I can't find the way to show kendo context menu by condition. I added right click capturing method, which looks like this:
$("#treeview").on('mousedown', '.k-item', function (event) {
var treeView = $('#treeview').data('kendoTreeView');
var dataSource = treeView.dataSource;
var itemUId = $(this).attr("data-uid");
var node = dataSource.getByUid(itemUId);
if (event.which === 3 && node.hasChildren) {
event.stopPropagation(); // to avoid propagation of this event to the root of the treeview
$('#treeview').data('kendoTreeView').select(this);
...//here I should prevent context menu from showing somehow
}
});
but I can't find the way to cancel context menu showing. I also tried to add open event to context menu, but I can't get selected item there.
Thanks in advance.
To show menu due to the condition use filter property of ContextMenu object:
#(Html.Kendo().ContextMenu()
.Name("menu")
.Filter(".menu-on")
)
It only shows menu for items that has class menu-on.
Secondly you have to add this class to all your folders. You should find by javascript all <span class="k-in"> elements in your tree and add menu-on class to all elements associated with folders.
Related
This is something quite simple but somehow resulted in a crazy rabbit hole.
This link shows what I want:
https://www.w3schools.com/howto/howto_js_active_element.asp
Nothing special, now the thing becomes hairy for me when the elements in the navbar are rendered from an array of objects (from the specs). The approach I am following is basically rendering a list of buttons, this list of buttons is the state, since supposedly when you update a state it triggers a re-render, then when a button is clicked it "sets" the active class to false on the entire array-state then activates it only for the clicked one. So far it works.
The problem is that the active class is rendered two steps behind. One for the moment when the class in the array-state's elements are set to false, the other when the clicked element gets updated.
As far as I understand useState and setState are queues, hence those are applied asynchronously on each render, in order to avoid that and get the renders to show the current state, useEffect is utilized.
Now the thing is that I am not sure how to apply useEffect in order to achieve the immediate render of the "active" class.
This is the code I have:
import { options } from 'somewhere...'
export default function SideMenu(props){
let auxArr = []
let targetName
const [stateOptions, setStateOptions] = useState([...options])
const [currentOption, SetCurrentOption] = useState({})
function activeOption(e){
// this helps with event bubbling
if (e.target.tagName == "P" || e.target.tagName == "SPAN"){
targetName = e.target.parentElement.id
} else if (e.target.tagName == "IMG"){
targetName = e.target.parentElement.parentElement.id
} else {
targetName = e.target.id
}
// since the main state is an array of objects I am updating it
// in three steps, first the current object is "activated"
// then the main array-state gets "inactivated" to erase all
// the previous "active" classes, finally the activated object
// replaces the corresponding inactive object in the main state.
let targetElement = stateOptions.filter(e => e.id==targetName)[0]
SetCurrentOption({
id: targetElement.id,
activity:true,
img: targetElement.img,
name: targetElement.name
})
// first the "classes" are set to false, then the
// "activated" object replaces the corresponding one
// in the main object, from here comes the two
// steps delay.
auxArr = [...stateOptions]
auxArr.forEach(e => e.activity=false)
setStateOptions(auxArr)
const newOptions = stateOptions.map(e =>
e.id==currentOption.id ? currentOption : e
)
setStateOptions(newOptions)
}
return(
<aside className={styles.sideDiv}>
<nav>
{stateOptions.map(({id, img, name, activity, link}) => {
return(
<button key={id} id={id} onClick={activeOption} className={activity?styles.active:""}>
<Image src={img}/>
<p className={timeColor.theme}> {name} </p>
</button>
)
})}
</nav>
</aside>
)
}
Thanks in advance for any help you can provide.
Greetings
I have built a search and every time user types word it renders new checkboxes but new checkboxes don't work like they used to be none of the event listeners work on new checkboxes, when I'm clicking on checkboxes they just don't react, but in old ones, until search will render this they are working normally
//search in checkbox data
const checkOptions = (container, value, containerId) => {
for (let i = 0; i < props.unique[containerId].length; i++) {
let item = props.unique[containerId][i];
if (
props.unique[containerId][i] !== null &&
props.unique[containerId][i].includes(value)
) {
element = (
<label
onClick={(e) => {e.stopPropagation(); ifAnyChecked(e);}} key={i}>
<input onClick={(e) => {tableSearch(e);}} type="checkbox" value={item ? item : "empty"}/>
{item && item.length > 28 ? (
handleCheckbox(item)
) : (
<p>{item}</p>
)}
</label>
);
tempData += ReactDOMServer.renderToString(element);
}
}
container.innerHTML = tempData;
};
any idea what's happening?
Have you tried to use onChange event instead of onClick? As far as I know, input type checkbox doesn't have such an event like onClick.
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox
I used to get this problem when I was working with Vanilla JS whenever i render a new element then that element was not triggering my events. That was because they were generated on runtime so the event wasn't bound to that element. Now I think that thing is happening here as well. So I changed your code and put it inside a state now it is working. I hope I helped. Do let me know if this is not the solution that you were looking for but it solves your problem though
I put the html inside a state array then i mapped it out inside the newCheckBox div. I changed the input to controlled input with fieldValue state. Lastly i changed the new checkbox alert from onClick={alert("doesn't goes in")} to onClick={() => alert("I think its working now right?")}
Here is the complete code sandbox
https://codesandbox.io/s/polished-sea-vedvh?file=/src/App.js
This question already has answers here:
Getting the ID of the element that fired an event
(24 answers)
Closed 10 months ago.
<tooltip message="Click" content="preview"></tooltip>
<tooltip message="Tooltip 1" class="repeat-tooltip" content="Click tooltip 1 preview"></tooltip>
<tooltip trigger="hover" class="repeat-tooltip" message="Hover Tooltip" content="Hover tooltip preview"></tooltip>
New and trying to create a custom tooltip tag and only one tooltip will be active at a time.
<tooltip>
<p class="tooltip-content" control="tooltip">{ message } ref="target"</p>
<div class="tooltip-wrapper" show={show_message} ref="content">
//inner html
</div>
</tooltip>
Trying to use show toggling show_message value to display and hide the tooltips. But show_message is within the context of that particular elements click event. Onclick of a particular tooltip, how can I access other custom tag's context to hide the value of that particular element if that tooltip already open?
this.root.addEventListener('click', (e) => that.toggle_message(e));
this.toggle_message = function(e) {
//here close all other tooltips before opening this one. How can I access the refs of all the open tooltip?
this.show_message = !this.show_message;
this.update();
};
From the specs of Riot.js I could not find anything that will allow you to keep track of all tags of same type so solution that I can think of for this particular scenario is to store collection of tooltips under windows and show/hide them on click of each individual tooltip.
As I do not have all component that you posted, I created bare minimum working example over here.
My demo component look like:
<tooltip>
<p>{ content }</p>
<span riot-style="display: { show_message ? 'inline-block' : 'none' }; background: black; color: white; padding:3px;"> { message } </span>
const self = this;
this.content = opts.content || '';
this.message = opts.message || '';
this.root.addEventListener('click', (e) => self.showTooltip(e));
this.toggle_message = function(show) {
self.show_message = show;
self.update();
};
this.showTooltip = function(e){
const bShow = !self.show_message;
for(var i=0; i<window.tooltips.length; i++){
window.tooltips[i].toggle_message(false);
}
self.toggle_message(bShow);
};
<script>
this.on('mount', function(){
if(!window.tooltips)
window.tooltips = [];
window.tooltips.push(this);
});
</script>
</tooltip>
On mount event it adds it's self to window.tooltips array collection and later when one of the components is clicked, event handler iterates through all registered components and hides them before showing current component.
Update - I found a better solution here using riot events. Add the events to windows and listen to that event on document body click and other trigger elements click, so that you will get the context of all tooltips and close it.
I'm using InboxSDK to add sidebar to thread view on GMail.
I do it like this:
InboxSDK.load(2, 'MY_KEY').then(sdk => {
sdk.Conversations.registerThreadViewHandler(threadView => {
const el = document.createElement("div");
el.id = 'mySidebar';
ReactDOM.render(<App />, el);
threadView.addSidebarContentPanel({
title: 'Sidebar Example',
iconUrl:chrome.extension.getURL('icon.png'),
hideTitleBar:true,
el:el
});
});
});
But I couldn't find any setting in InboxSDK docs that would enable me to show this panel as collapsed after it's created. Is it possible, or should I do it the dirty way, by adding/removing classes from elements manually?
How about trigger a click event on the button programmatically if the sidebar is opened?
if ($('.companion_app_sidebar_wrapper_visible').length > 0) {
$('.sidebar_thread_iconArea button[data-tooltip="Sidebar Example"]').trigger('click');
}
So basically what I am doing is iterating through an array of data and making some kind of list. What I want to achieve here is on clicking on a particular list item a css class should get attached.
Iteration to make a list
var sports = allSports.sportList.map((sport) => {
return (
<SportItem icon= {sport.colorIcon} text = {sport.name} onClick={this.handleClick()} key= {sport.id}/>
)
})
A single list item
<div className="display-type icon-pad ">
<div className="icons link">
<img className="sport-icon" src={icon}/>
</div>
<p className="text-center">{text}</p>
</div>
I am not able to figure out what to do with handleClick so that If I click on a particular list it gets highlighted.
If you want to highlight the particular list item it's way better to call the handleClick function on the list item itself, and you can add CSS classes more accurately with this approach,
here is my sample code to implement the single list component
var SingleListItem = React.createClass({
getInitialState: function() {
return {
isClicked: false
};
},
handleClick: function() {
this.setState({
isClicked: true
})
},
render: function() {
var isClicked = this.state.isClicked;
var style = {
'background-color': ''
};
if (isClicked) {
style = {
'background-color': '#D3D3D3'
};
}
return (
<li onClick={this.handleClick} style={style}>{this.props.text}</li>
);
}
});
Keep a separate state variable for every item that can be selected and use classnames library to conditionally manipulate classes as facebook recommends.
Edit: ok, you've mentioned that only 1 element can be selected at a time,it means that we only need to store which one of them was selected (I'm going to use the selected item's id). And also I've noticed a typo in your code, you need to link the function when you declare a component, not call it
<SportItem onClick={this.handleClick} ...
(notice how handleClick no longer contains ()).
And now we're going to pass the element's id along with the event to the handleClick handler using partial application - bind method:
<SportItem onClick={this.handleClick.bind(this,sport.id} ...
And as I said we want to store the selected item's id in the state, so the handleClick could look like:
handleClick(id,event){
this.setState({selectedItemId: id})
...
}
Now we need to pass the selectedItemId to SportItem instances so they're aware of the current selection: <SportItem selectedItemId={selectedItemId} ....Also, don't forget to attach the onClick={this.handleClick} callback to where it needs to be, invoking which is going to trigger the change of the state in the parent:
<div onClick={this.props.onClick} className={classNames('foo', { myClass: this.props.selectedItemId == this.props.key}); // => the div will always have 'foo' class but 'myClass' will be added only if this is the element that's currently selected}>
</div>