Open Tab on Hover

I just thought I would put it out there that it would be nice if a sidebar tab, or any tab for that matter, that contains a draggable list, like open, or a drop zone, it would be nice if we were dragging a payload over it, that it could change to the current tab.

Some History

I mention it so;

  • Someone may tell me if they have done it before
  • Know some tricks to help implement it
  • Be considered if the TAB’s macro is under review to use new TiddlyWiki script
1 Like

Having mistakenly tried to drag something into the story river via the open tab only to realize that what I was looking at was the “recent” tab, I love this idea!

From a look at the documentation and a look through all CSS pseudo-classes, it doesn’t look possible in native TW, because:

  1. There is no CSS pseudo-class for being dragged over. (My thought was, if there were such a pseudo-class, then CSS trickery might’ve been sufficient to create a pop-up containing the dragged-over tab’s content. Then you could drag whatever you’re dragging there.)
  2. Not everything draggable is a draggable widget. (My thought was, you could utilize the draggable widget’s “start actions” to do something to enable the above CSS trickery but for the pseodo-class “hover”, and “end actions” to disable it again so that the popup doesn’t appear any time you hover over the tab, but only when dragging. You could override the draggable widget to add these automatically, but from my understanding, it would apply to things like external files dragged into your wiki. It might not even work for selecting and dragging text.)
  3. Google searching suggests that the way to do or show things in websites on drag-over is with JavaScript event handling. (I imagine this is how TW changes the display of droppables and drop zones when dragging things over them.)

If you only need actual draggable widgets to cause tabs to open when dragged over and a popup version is acceptable, then overriding the draggable widget might work. (Even so, the CSS trickery seems like a nightmare to me. But perhaps for someone more skilled than I, it would just be a fun exercise?)

As best as I can tell, anything more generic would require JavaScript tinkering.

Then again, if you’d like to see the popup on any hover rather than just on drag, then perhaps using CSS :hover might work.

Thanks @bluepenguindeveloper for your contribution.

Keep in mind for Tabs at least, they are all made using the tabs macros, in $:/core/macros/tabs. An in fact there is a parameter that lets us “relplace the button”, the buttonTemplate parameter.

This button is defined as follows;

\define tabs-button()
\whitespace trim
<$button
	set=<<tabsState>>
	setTo=<<currentTab>>
	default=<<__default__>>
	selectedClass="tc-tab-selected"
	tooltip={{!!tooltip}}
	role="switch"
	data-tab-title=<<currentTab>>
>
	<$tiddler tiddler=<<save-currentTiddler>>>
		<$set name="tv-wikilinks" value="no">
			<$transclude tiddler=<<__buttonTemplate__>> mode="inline">
				<$transclude tiddler=<<currentTab>> field="caption">
					<$macrocall $name="currentTab" $type="text/plain" $output="text/plain"/>
				</$transclude>
			</$transclude>
		</$set>
	</$tiddler>
	<<__actions__>>
</$button>
\end

So we can replace or edit this to on hover to;

set=<<tabsState>>
setTo=<<currentTab>>

Keeping in mind it needs to know about the tab being hovered not the payload in the drag.

I will look around because I thought I had done the simpler open on hover before (without the drag payload).

  • Even better if we could only open on drag on selected tabs, and with a payload, because it can be hard to use tabs when they are all sensitive to hover.

Are we looking for an action on hover?

@TW_Tones, I went looking for the plugin that adds a $hover widget and discovered that I’d linked you to it before—so here’s that thread, in case it resurfaces any other ideas you might be looking for. (I note that you did share a package that switches tabs on hover. :slight_smile:) Is it possible to trigger an action on mouse over or hover?

IIRC, I’ve wrapped a $hover around a normal $button in the past, so you could probably make an alternate tab button template that preserved the normal tab functionality, too.

Perhaps wrap the $hover/$button in a $list/conditional that only displays your modified tab button if <<currentTab>>'s text field contains your target widget, and falls back to the standard button otherwise?

I do like the concept, BTW, though I’m not sure I have an immediate use for dropzones in the sidebar. Do you have any specific use-cases in mind?

Yes, Specifically one could create a custom dropzone, or dragable list in a sidebar tab, The open tab already is. The if you have a title or list you want to drag and drop you do not need to open the sidebar tab first, you can just grab it and hover other the tab to first open the tab, then drop it.

  • But of course you could have a set of tabs in any tiddler, each with a draggable option. You grab something from the open tab and drop it on another tab, but since the first one is already open you need the destination tab to open on hover.

Thanks to your @etardiff previous mention of the $hover widget I went looking for it and note

Additionally one can specify the mouse event for which the actions are triggered.

  • Which in effect allows us to use any mouse event I imagin.

on One of hover, mousein, mouseout. Defaults to hover. Specifies when the actions are triggered, hover ensures a symmetric popup action.

Wow, and to think I was thinking about trying to use CSS to make popups on hover…! Thanks for this!

1 Like

So, the Hover widget is working, but currently not on dragover. ChatGPT says it needs to be javascript, but this could be misleading.

It does suggest defining event listeners dragover and dragleave, Can we use the event catcher for this?

In the next reply I will Share ChatGPT’s answer someone may be able to use, maybe me with time.

However there would be value in just allowing drop on to the button itself, but we would need to handle this separately rather than simply use the drop list inside the tab.

In CSS, there isn’t a specific “drag over” event, as CSS is primarily a styling language and doesn’t handle user interactions or events directly. However, in web development, you can handle drag-and-drop interactions using JavaScript, which provides several events to manage these interactions, including dragover.

Chat GPT response

Here’s a brief overview of how you might use the dragover event in JavaScript:

JavaScript Drag and Drop API

The Drag and Drop API in JavaScript provides a series of events to handle various steps of the drag-and-drop process:

  • dragstart: Triggered when the user starts dragging an item.
  • drag: Occurs as the item is being dragged over a valid drop target.
  • dragenter: Fires when a dragged item enters a valid drop target.
  • dragover: Triggered as the draggable item is being dragged over a drop target. This event is crucial for preventing the default handling of the element to allow for a drop.
  • dragleave: Fires when a dragged item leaves a drop target.
  • drop: Occurs when the draggable item is dropped on a valid drop target.
  • dragend: Triggered when the drag action ends (either by dropping the item or by cancelling).

Using dragover in JavaScript

To use the dragover event, you typically set up event listeners on the elements that you want to be involved in the drag-and-drop process. Here’s a simple example:

const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', function(event) {
    // Prevent default behavior (which prevents the drop)
    event.preventDefault();

    // Optional: add some visual feedback
    dropZone.style.backgroundColor = 'lightblue';
});

dropZone.addEventListener('dragleave', function(event) {
    // Reset background color when the dragged item leaves the area
    dropZone.style.backgroundColor = '';
});

dropZone.addEventListener('drop', function(event) {
    // Prevent default action (open as link for some elements)
    event.preventDefault();

    // Handle the dropped data
    dropZone.style.backgroundColor = '';
    console.log('Item dropped');
});

In this example, the dragover event is crucial for preparing the dropZone to accept a drop by preventing the default behavior, which is necessary because many browsers will not allow a drop if the default handling is not explicitly prevented.

Styling Drag-and-Drop with CSS

While CSS itself doesn’t handle events, you can change the appearance of elements during drag-and-drop actions using the :hover pseudo-class or by dynamically adding classes with JavaScript during drag events. This allows you to provide visual feedback, such as highlighting potential drop targets when they are being dragged over.

#drop-zone {
    width: 200px;
    height: 200px;
    border: 2px dashed #ccc;
}

#drop-zone.dragover {
    background-color: lightblue;
}

You can add the class dynamically with JavaScript:

dropZone.addEventListener('dragover', function(event) {
    event.preventDefault();
    dropZone.classList.add('dragover');
});

dropZone.addEventListener('dragleave', function(event) {
    dropZone.classList.remove('dragover');
});

dropZone.addEventListener('drop', function(event) {
    event.preventDefault();
    dropZone.classList.remove('dragover');
    // further handling
});

This approach gives you the flexibility to use CSS for styling while relying on JavaScript to handle the logic of dragging and dropping.

Whoa… I missed the event catcher widget before. I had heard of it, but I didn’t realize it was for JavaScript events! In which case, catching the drag over and drag out events should work.

The “appearance” can include whether something appears at all. Thus, you can use CSS to show or hide the popup:

.popup-source .popup-content {
   visibility: hidden;
  /* additional stuff to position and size the popup */
}
.popup-source:hover .popup-content {
   visibility: visible;
}

(CSS Tooltip for more info)

I don’t think this works in TiddlyWiki. I can’t seem to quickly find the explanation in the documentation, but I believe it’s because DOM objects can be refreshed at any time, removing whatever changes you made in JS.

But, back to the event catcher widget… I think you could do it… with a button template tiddler that looks something like this:

<$eventcatcher 
	selector=".dragover-tab"
	$dragover="<$action-setfield $tiddler=<<tabsState>> $value=<<currentTab>>/>"
>
<span class="dragover-tab">
	<!-- the original backup for the button template -->
	<$transclude tiddler=<<currentTab>> field="caption">
		<$macrocall $name="currentTab" $type="text/plain" $output="text/plain"/>
	</$transclude>
</span>
</$eventCatcher>

The above, as a button template, should, on drag over, perform the same action that the button in the original macro performs, setting the <<tabsState>> tiddler to <<currentTab>>, but that’s assuming <<tabsState>> is just be a tiddler title. (If it is a full text reference, then more complicated logic will be needed to get the tiddler title and field or index out.) Since the button template is transcluded as a child of the button itself, all those variables should be available in that context.

I’m curious to know if this will work, but I think it should.

Ha! I think I can see your future :slight_smile: