Drag and drop in Tauri + Yew - rust

I am having issues with handling drag and drop. I prevent_default on my drop target for both the dragEnter and dragOver events, which I can verify are firing, but the drop event is never triggered. I have also tried setting the dataTransfer.dropEffect property, which has no effect.
use yew::prelude::*;
use web_sys::{DragEvent, console::log_1};
#[function_component(App)]
pub fn app() -> Html {
let ondragenter = Callback::from(|e: DragEvent| {
e.prevent_default();
e.data_transfer().unwrap().set_drop_effect("move"); // optional
log_1(&"enter".into());
});
let ondragover = Callback::from(|e: DragEvent| {
e.prevent_default();
log_1(&"over".into());
});
let ondrop = Callback::from(|e: DragEvent| {
log_1(&"drop".into());
});
html! {
<main style="position: absolute; top: 0; bottom: 0; left: 0; right: 0;"
{ondragenter}
{ondragover}
{ondrop}>
</main>
}
}
with web_sys's DragEvent and DataTransfer features enabled.
I am running this using Tauri and have tried with Yew v. 0.19 and 0.20.

Related

How to Inject CSS changes into my Electron js app?

I use loadURL to load the web, I want to make some changes. Is there a way to inject a css snippet once the page is rendered?
There are extensions for chrome that do what I want (like UserCSS), but I understand that they can't be used inside an Electron App.
After your page has completed loading...
Here is an example of adding CSS:
// When document has loaded, initialize
document.onreadystatechange = (event) => {
if (document.readyState == "complete") {
// Document has finished loading here
var styles = `img#license {
animation: none;
animation-iteration-count: 1;
animation-fill-mode: forwards;
position: absolute;
z-index: 3;
width: 375px;
top: 204px;
left: 112px;
visibility: hidden;
}`
var styleSheet = document.createElement("style");
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
};
I use the above to display a unlicensed image over my Electron app from the preload.js script, as it has access to the document element.
Edit the CSS to your needs.

Empty Div Preventing Interaction with amCharts5 MapChart on Vue3

I decided to dip my toes in Vue and have had an idea for a website for a while which I'd like to use amCharts5 for.
I had some issues initially as all the info I could find was related to Vue2, but I think I've somewhat wrapped my head around Vue3 and its composition API.
The MapChart is created, however there is always a div slapped on top of it which prevent any interaction. If I delete this element via DevTools, the MapChart becomes interactive.
I've tried debugging this and commenting sections of the code out, regardless this div is always created. And I simply can't figure out if it's injected by Vue or if amCharts 5 is the culprit.
The highlighted element is the one I must delete for it to become interactive.
Here's how the component is setup;
<template>
<div class="testClass" ref="chartdiv">
</div>
</template>
<script setup lang="ts">
import * as am5 from "#amcharts/amcharts5";
import * as am5map from "#amcharts/amcharts5/map";
import am5geodata_worldLow from "#amcharts/amcharts5-geodata/worldLow";
import am5themes_Animated from '#amcharts/amcharts5/themes/Animated';
import { ref, onMounted, onUnmounted } from "vue";
const chartdiv = ref<HTMLElement | null>()
var root!: am5.Root;
onMounted(() => {
if (chartdiv.value) {
// Create the Root
var root = am5.Root.new(chartdiv.value);
// Setup the MapChart
var chart = root.container.children.push(
am5map.MapChart.new(root, {
panX: "rotateX",
panY: "rotateY",
projection: am5map.geoOrthographic(),
centerMapOnZoomOut: false
})
);
// Setup Animations
root.setThemes([
am5themes_Animated.new(root)
]);
// Create MapPolygons
var polygonSeries = chart.series.push(
am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_worldLow
})
);
// Setup MapPolygon Styling
polygonSeries.mapPolygons.template.setAll({
tooltipText: "{name}",
fill: am5.color("#909090")
});
// Setup MapPolygon Hover Styling
polygonSeries.mapPolygons.template.states.create("hover", {
fill: am5.color("#FF0000"),
stroke: am5.color("#00FF00"),
strokeWidth: 2
});
polygonSeries.mapPolygons.template.events.on("click", function(event) {
//console.log("Clicked: {0}", event.target);
});
// Setup Background
var backgroundSeries = chart.series.unshift(
am5map.MapPolygonSeries.new(root, {})
);
backgroundSeries.mapPolygons.template.setAll({
fill: am5.color(0x2c84d0),
stroke: am5.color(0x2c84d0)
});
backgroundSeries.data.push({
geometry: am5map.getGeoRectangle(90, 180, -90, -180)
});
}
});
onUnmounted(() => {
if (root) {
root.dispose();
}
});
</script>
<style scoped>
.testClass {
width: 50vw;
height: 50vh;
}
</style>
When you create a Vite-powered Vue project, it automatically creates a bunch of CSS files for you. One of those is base.css.
Inside this file, you'll find these lines which causes all the headache;
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
Removing those lines will fix the issue.

Cytoscape Cola Layout: How to restart the layout without any change of positions?

I'm trying to use the Cytoscape cola layout to render a graph that should apply a force directed layout while using it (so when dragging nodes around, they should act as if there is some gravity involved).
Relevant libraries:
https://github.com/cytoscape/cytoscape.js
https://github.com/tgdwyer/WebCola
https://github.com/cytoscape/cytoscape.js-cola
My first problem is that adding nodes to the graph via add(node) doesn't include them in the cola layout algorithm. The only way I found around that is to destroy the layout, re-initialize it and start it again. But this causes the nodes to jump in some cases.
I assumed that this was due to the fact that I completely destroyed the old layout but when setting up a minimal example, I realized that even just calling layout.stop() and layout.run() leads to nodes being repositioned.
In the following example, there is only one node. Moving the node via drag and drop, then pressing the "stop" button and then the "start" button causes the node to jump back to its initial position:
document.addEventListener('DOMContentLoaded', function(){
// Register cola layout
cytoscapeCola(cytoscape);
var nodes = [{ data: { id: 1, name: 1 } }]
var edges = [];
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'node[name]',
style: {
'content': 'data(name)'
}
},
{
selector: 'edge',
style: {
'curve-style': 'bezier',
'target-arrow-shape': 'triangle'
}
},
],
elements: {
nodes: nodes,
edges: edges
}
});
var layout = cy.layout({
name: 'cola',
infinite: true,
fit: false,
});
layout.run();
document.querySelector('#start').addEventListener('click', function() {
layout.run();
});
document.querySelector('#stop').addEventListener('click', function() {
layout.stop();
});
document.querySelector('#add-node').addEventListener('click', function() {
var id = Math.random();
cy.add({ group: 'nodes', data: { id: id, name: id } });
cy.add({ group: 'edges', data: { source: id, target: _.head(nodes).data.id } });
layout.stop();
layout.destroy();
layout = cy.layout({
name: 'cola',
infinite: true,
fit: false,
});
layout.run();
});
});
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px;
}
#cy {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
#buttons {
position: absolute;
right: 0;
bottom: 0;
z-index: 99999;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-edgehandles.js demo for infinite layout</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/webcola/WebCola/cola.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cola#2.4.0/cytoscape-cola.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
<script src="cytoscape-edgehandles.js"></script>
</head>
<body>
<h1>cytoscape-edgehandles demo with an infinite layout</h1>
<div id="cy"></div>
<div id="buttons">
<button id="start">Start</button>
<button id="stop">Stop</button>
<button id="add-node">Add Node</button>
</div>
</body>
</html>
Is this a bug or am I doing something wrong? Does anyone know how to stop and restart the layout without the nodes changing their position?
Thanks a lot,
Jesse
Okay actually you were very close #Stephan.
The problem was that WebCola centers the nodes when calling start by default:
https://github.com/tgdwyer/WebCola/blob/78a24fc0dbf0b4eb4a12386db9c09b087633267d/src/layout.ts#L504
The cytoscape wrapper for WebCola does not currently support this option, so I forked it and added the option myself:
https://github.com/deje1011/cytoscape.js-cola/commit/f357b97aba900327e12f97b1530c4df624ff9d61
I'll open a pull request at some point.
Now you can smoothly restart the layout like this:
layout.stop();
layout.destroy(); // cleanup event listeners
layout = graph.layout({ name: 'cola', infinite: true, fit: false, centerGraph: false });
layout.run()
This way, the nodes keep their position 🎉

Display a normal chart or D3Js in Node-red dashboard (over template ?)

Has somebody already achieved to display a chart, not based on timeseries (like the chart from the dashboard), but on data passed to it?
I wonder if it is, perhaps, possible to use a template to do that? But I've no clue how to do it. If someone has a small example, it would be great.
I've found a solution, using some code sample from the library...
The link between msg.payload and data is still missing, but not far away
<style>
#chart svg {
width: 800px;
height: 600px;
}
</style>
<h3>test</h3>
<div id="chart">
<svg></svg>
</div>
<script src="https://raw.githubusercontent.com/novus/nvd3/master/build/nv.d3.js"></script>
<script>
(function(scope) {
console.log('Position 1');
console.dir(scope);
console.log(scope.msg);
scope.$watch('msg.payload', function(data) {
console.log('Position 2');
console.dir(data);
});
})(scope);
function data() {
var sin = [],
cos = [];
for (var i = 0; i < 100; i++) {
sin.push({x: i, y: Math.sin(i/10)});
cos.push({x: i, y: .5 * Math.cos(i/10)});
}
return [
{
values: sin,
key: 'normal',
color: '#ff7f0e'
},
{
values: cos,
key: 'defect',
color: '#2ca02c'
}
];
}
nv.addGraph(function() {
var chart = nv.models.lineChart()
.useInteractiveGuideline(true)
;
chart.xAxis
.axisLabel('frequence')
.tickFormat(d3.format(',r'))
;
chart.yAxis
.axisLabel('amplitude')
.tickFormat(d3.format('.02f'))
;
d3.select('#chart svg')
.datum(data())
.transition().duration(500)
.call(chart)
;
nv.utils.windowResize(chart.update);
return chart;
});
</script>

How to add a listener to the x in the search field of Sencha?

I have a searchfield:
this.searchField = new Ext.form.Search({
placeHolder: 'Search by lastname',
});
which works fine but I want to add a listener to the x in the box so that it not only clears what is written on the searchfield but also clears the filter I'm doing on the stores.
I'm not sure what the name of it is so I don't know how to query it to add the listener.
I'm using Sencha Touch 1.1
Sencha Touch 2.x
this.searchField.on('clearicontap', function() {
alert('cleared');
}, this);
Sencha Touch 1.x
this.searchField = new Ext.form.Search({
placeHolder: 'Search by lastname',
onClearIconTap: function() {
if (!this.disabled) {
this.setValue('');
alert('cleared');
}
}
});
Or we can rewrite it to be more like 2.x:
Somewhere in initialization of app we override Ext.form.Text component and add 'clearicontap' event:
Ext.override('Ext.form.Text', {
initComponent: function() {
this.callOverridden(arguments);
this.addEvents('clearicontap');
},
onClearIconTap: function() {
if (!this.disabled) {
this.setValue('');
this.fireEvent('clearicontap');
}
}
});
Now we can use it as in 2.x:
this.searchField.on('clearicontap', function() {
alert('cleared');
}, this);
All things above are not tested, sorry for errors if they exist.
NOTE: If anyone tries to add this inside a toolbar, there is a bug in Sencha 1.1 that will cause it to render incorrectly and the clearIcon appears outside of the textfield. In order to fix this simply addi the following code to the application.css file
.x-toolbar .x-field-clear-container {
min-height: 0;
position: absolute;
top: 2px;
bottom: 1px;
right: 5px;
font-size: 0.8em;
border-radius:20px;
}
.x-toolbar .x-input-search, .x-field-select, .x-field-clearable {
padding-right:1em;
}
You have to code like this
this.searchField = new Ext.form.Search({
placeHolder: 'Search by lastname',
listeners:{
onClearIconTap: function() {
if (!this.disabled) {
this.setValue('');
alert('cleared');
}
}
}
});
I hope this will work for you...
thanks
naresh

Resources