I'm using DOJO's ContentPane module. I have a div element in one of the panes and I need to give it a certain height - 100 pixels less than the height of the ContentPane so that the div changes its height dynamically when you change the ContentPane size by dragging the splitters. I'm new to Dojo and would be happy if somebody could help me with this.
Thanks.
I think the best solution is via nested BorderContainers with properly set splitters, because that way dijit/layout will take care of resizing and you won't need to write any JavaScript code and your layout will be based solely on CSS.
It's kinda cumbersome to explain, so I created a working example for you at jsFiddle: http://jsfiddle.net/phusick/Ayg8F/ + a diagram:
NB: Do not forget to set height: 100% for html, body and the top BorderContainer.
The drawback of this solution is you will have to replace plain divs with ContentPanes. If you do not want to or can't you can use dojo/aspect and connect to BorderContainer or ContentPane resize method and resize your divs manually whenever the size changes:
require([
"dojo/ready",
"dojo/aspect",
"dijit/registry",
"dijit/layout/ContentPane",
"dijit/layout/BorderContainer"
], function(
ready,
aspect,
registry
) {
ready(function() {
var bc = registry.byId("borderContainer1");
aspect.after(bc, "resize", function() {
// calculate and set <div> size here
console.log("resize divs");
});
});
});
Related
Does anyone know of a masonry layout which works with Vue 3 and Server Side rendering?
My requirements is that I can not specify the columns up front, I want the masonry layout to work that out.
In my Vue 2 application I am using "vue-masonry". I had to also use "vue-client-only" as my application as my application is a server rendered application.
<!-- Only rendered during client side rendering, vue-masonry is not support in SSR -->
<client-only>
<div
class="grid"
v-masonry="containerId"
transition-duration="0.3s"
item-selector=".grid-item">
<div
v-masonry-tile class="grid-item"
v-for="(item, i) in items"
v-bind:key="i">
<img
:src="getItemImage(item)"
:data-key=i
alt="Small preview">
</div>
</div>
</client-only>
When I have this in my Vue 3 project I get the error
slot is not a function
I tried to perhaps use "vue-masonry-css" but that fails with
Uncaught TypeError: Cannot read property 'use' of undefined
For the following code
import Vue from 'vue';
import VueMasonry from 'vue-masonry-css';
Vue.use(VueMasonry);
I was also looking for a masonry layout with SSR and Vue 3 support.
Since I couldn't find one for my use case I created https://github.com/DerYeger/vue-masonry-wall.
Check out the demo at https://vue-masonry-wall.yeger.eu/ to see if it fits your requirements.
I have been looking for an answer myself to implement dynamic Masonry layout and nothing worked for me properly so I had to develop my own algorithm for my blog page that support even IE11 and Edge browsers.
1. The grid layout should have the following structure:
<div class="grid-container">
<div class="grid-item">
<div class="grid-item-content"></div>
</div>
</div>
2. style the grid container:
.grid-container {
min-width: 70%;
max-width: 100%;
display: grid;
column-gap: 1rem;
grid-template-columns: repeat( auto-fit, minmax(22em , 1fr));
grid-auto-rows: 300px;
}
.grid-item {
height: fit-content;
/*add the rest of your desired styling properties*/
}
You can change the width of the container and assign it whatever value you want.
The other two important properties from the css snippet above are:
grid-template-columns to generate responsive grid items with minimum width of 22em and max width that equals the width of the grid container 1fr.
grid-auto-rows property that, as the name suggests, gives rows height implicitly. for our masonry layout algorithm to work, I gave the minimum height value that a grid item in my case can have.
3. the following js algorithm will adjust the grid items after they have been loaded to achieve a masonry layout: ##
resizeAllGridItems() {
//calculate the grid container with
let gridWidth = document.getElementsByClassName("grid-container")[0].offsetWidth;
/*calculate the grid item width (the width should be the same for all grid items
because we used `repeat( auto-fit, minmax(22em , 1fr))` to generate our responsive
columns)*/
let gridItemWidth = document.getElementsByClassName("grid-item")[0].offsetWidth;
/*devide the with of the grid container by the with of the grid item item to get
the number of generated columns*/
let columnsNumber = ~~(gridWidth / gridItemWidth);
/*the second part of the algorithm with loop through all the generated grid items.
Starting with the second row, the grid item in that row will be given a `margin
-top` value that equals the height of the grid item situated right above it, minus
the value of `grid-auto-rows`. This way whenever there's an extra space, the grid
item below will have it's `margin-top` value adjusted to take the extra space.*/
let x = columnsNumber;
let colIdx = 0;
let columnsHeights = [0, 0, 0];
let tempColumnsHeights = [];
let allItems = document.getElementsByClassName("grid-item");
for(x; x<allItems.length; x++) {
let topItemHeight = columnsHeights[colIdx] + allItems[x - columnsNumber].offsetHeight;
allItems[x].style.marginTop = (topItemHeight - 300) + 'px';
tempColumnsHeights.push(topItemHeight - 300);
colIdx++;
/*move to the next row of grid items to adjust them if all the items of the
previous row are adjusted*/
if (colIdx === columnsNumber) {
colIdx = 0;
columnsHeights = tempColumnsHeights;
tempColumnsHeights = [];
}
}
}
That's it. Now you have a masonry layout that is made by adjusting the margin top of grid items programatically taking into consideration several variables like the value of auto rows, the height of grid items, their width and the width of the grid container.
I came across several articles that also explain other approaches and algorithms to implement masonry layout but they didn't work for me. This article from css-Trick explains a lot of methods to implement a masonry layout. However I already tried these two other methods but didn't work for me:
the first adjusts the value of grid-row-end according the height of the grid item and it's content to span the grid item by one or more rows.
the second approach is using a third party library like Masonry.
Note if you're using grid items with image elements: I found this article that I tried that uses the imagesLoaded.js library. Using this library will make it possible to execute the algorithm after all the images are loaded. In my case I gave the images container a fixed height that way my articles' cards heights will be independent from the images it contains.
IE11 and Edge support
Use autoprefixer npm package which is a PostCSS plugin to parse CSS and add vendor prefixes to CSS rules using values from Can I Use. It is recommended by Google and used in Twitter and Alibaba. It add all necessary prefixes and parses your grid display properties for IE11 and edge. You can refer to this answer on how to enable grid support with autoprefixer since it's disabled by default: https://stackoverflow.com/a/61144097/8453311
In Vue 3, there's no export global Vue instance like in Vue 2.
As I checked the vue-masonry source code, they using Vue global instance, Vue 2 directive API (breaking change in Vue 3).
So I think have to read and port that library to Vue 3 to make it work.
I run into the same situation and still looking for a library that supports Vue 3.
If I couldn't find any maybe I will port that my self but in the next 2-3 weeks.
Did you try adding Vue Mansory in main.js
like:
import VueMasonry from 'vue-masonry-css'
createApp(App).use(VueMasonry).mount('#app')
According to the react-virtualized docs, "The AutoSizer component decorates a React element and automatically manages width and height properties so that decorated element fills the available space".
The suggestion is usually to add height: 100%; or flex: 1; to all parent elements in the DOM to render the table's full height.
What if one of those elements, e.g. an absolutely positioned full page overlay container, has height: 100vh; overflow: scroll; ?
In this case, the Table's parent height is 100vh, but allows overflow if the children have height greater than 100vh.
Say our table has many rows of varying height and exceeds 100vh when rendered. Autosizer will return a height in pixels that equals 100vh, as a maximum, meaning the last rows in our table will be cutoff as AutoSizer will not stretch its parents height to render all rows.
My current workaround is to use <CellMeasurer /> and CellMeasurerCache() to manually determine table height from this.cache; // (component instance of CellMeasurerCache) using private properties, for example in my table component:
componentDidUpdate = () => {
const { tableHeight } = this.state;
const tableRowHeights = Object.values(this.cache._rowHeightCache);
const newRowsHeight = tableRowHeights.reduce(
(height, nextRowHeight) => height + nextRowHeight,
0
);
if (tableHeight !== newRowsHeight) {
this.setState({ tableHeight: newRowsHeight });
}
}
Is there no way to accomplish this with react-virtualized components and APIs,without accessing private properties from the CellMeasurerCache() instance?
What if one of those elements, e.g. an absolutely positioned full page overlay container, has height: 100vh; overflow: scroll; ?
In this case, the Table's parent height is 100vh, but allows overflow if the children have height greater than 100vh.
I don't think this (overflow behavior) make sense in the case of react-virtualized. In most cases- unless you're using WindowScroller for a Facebook/Twitter like layout- react-virtualized components should manage their own scrolling.
So in that case, if 100vh height is available, you would want RV to fill exactly that amount and- if there's more content than will fit into that area- (which is likely, if you're using RV in the first place)- it will setup the scrolling styles within itself.
On the other hand, if you tell a react-virtualized component that its height is numRows * rowHeight then it's going to render everything, and completely defeat the purpose of windowing. :)
I've 3 (loader, locker and debug view) hidden views (touchEnabled and visible set to false, and zIndex to 1) above the main view (zIndex = 2).
Each 'over' view has this method:
$.debugView.show = function() {
$.debugView.touchEnabled = $.debugView.visible = true;
$.debugView.zIndex = 3;
};
$.debugView.hide = function() {
$.debugView.touchEnabled = $.debugView.visible = false;
$.debugView.zIndex = 1;
};
This screen has the 3 'over' views hidden:
Now, I'm opening the 'debug view', but, SOMETIMES it seems like it changes the positions (as if the center it's on the top left corner instead of the center of the device).
Instead of the required result:
If I use the opacity instead of the visible property, it works properly.
This might be an SDK bug right?
<Alloy>
<Window>
<View id="content"/>
<View id="locker"/>
<View id="loader"/>
<View id="debugView"/>
</Window>
</Alloy>
All of these 4 views don't have width or height (so it uses the Ti.UI.FILL as default)
I have noticed this too with a completely different implementation. I had just one view that I included in a window.
Apparently the left and top calculations were not done properly if the elements is hidden.
What I did to solve the issue is to hardcode the left/top position by calculating the left position using this:
$.content.left = (Ti.Platform.displayCaps.platformWidth - 75) / 2;
Where in my case 75 is the width the element has, so that'll be bigger in your case. You can do the same for height.
Now, this is an iOS only solution. On Android you will need to take DPI into consideration calculating it.
I do think it is a bug, though this solution works perfectly for me. I recommend looking at JIRA and see if it is a known issue, and if not, raise it with a very specific explanation of the problem, preferably with a reproducible case delivered as an app. Classic would help most. And if it is not reproducible in classic it might be an alloy issue.
I have a webpage with an SVG. On some of its shapes I need to display a tooltip. However, I can't get the tooltip to appear where it should, just some pixels away from the shape itself.
It appears way on the right hand side of the screen, maybe some 300px away.
The code I am using to get the coordinates is as follows:
d3.select("body")
.select("svg")
.select("g")
.selectAll("circle")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){
var svgPos = $('svg').offset(),
/*** Tooltip ***/
//This should be the correct one, but is displaying at all working at all.
/*x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;*/
//This displays a tool tip but way much to the left of the screen.
x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;
Tooltip
window.alert("svgPos: "+svgPos+" top: "+y+"px left: "+x+"px "+d3.event.target.cx.animVal.value);
return tooltip.style("top", x+"px").style("left",y+"px");
})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
I got to this code following this SO post.
I have changed $(ev.target).attr(cx) as it is not returning a value on my machine; d3.event.target.cx is, even though it seems it is not affecting the end result anyway.
What am I doing wrong? Could somebody help me please? Thank you very much in advance for your time.
If your tooltip is an HTML element, then you want to position it relative to the page as a whole, not the internal SVG coordinates, so accessing the cx/cy value is just complicating things. I can't say for sure without looking at your code, but if you have any transforms on your <svg> or <g> elements, then that could be what's throwing you off.
However, there is a much easier solution. Just access the mouse event's default .pageX and .pageY properties, which give the position of the mouse relative to the HTML body, and use these coordinates to position your tooltip div.
Example here: http://fiddle.jshell.net/tPv46/1/
Key code:
.on("mousemove", function () {
//console.log(d3.event);
return tooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Even with rotational transforms on the SVG circles, the mouse knows where it is on the page and the tooltip is positioned accordingly.
There are other ways to do this, including getting a tooltip to show up in a fixed location relative to the circle instead of following the mouse around, but I just checked the examples I was working on and realized they aren't cross-browser compatible, so I'll have to standardize them and get back to you. In the meantime, I hope this gets you back on track with your project.
Edit 1
For comparison, here is the same example implemented with both an HTML tooltip (a <div> element) and an SVG tooltip (a <g> element).
http://fiddle.jshell.net/tPv46/4/
The default mouse event coordinates may be great for positioning HTML elements that are direct children of <body>, but they are less useful for positioning SVG elements. The d3.mouse() function calculates the mouse coordinates of the current event relative to a specified SVG element's coordinate system, after all transformations have been applied. It can therefore be used to get the mouse coordinates in the form we need to position an SVG tooltip.
Key code:
.on("mousemove", function () {
var mouseCoords = d3.mouse(
SVGtooltip[0][0].parentNode);
//the d3.mouse() function calculates the mouse
//position relative to an SVG Element, in that
//element's coordinate system
//(after transform or viewBox attributes).
//Because we're using the coordinates to position
//the SVG tooltip, we want the coordinates to be
//with respect to that element's parent.
//SVGtooltip[0][0] accesses the (first and only)
//selected element from the saved d3 selection object.
SVGtooltip
.attr("transform", "translate(" + (mouseCoords[0]-30)
+ "," + (mouseCoords[1]-30) + ")");
HTMLtooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Note that it works even though I've scaled the SVG with a viewBox attribute and put the tooltip inside a <g> with a transform attribute.
Tested and works in Chrome, Firefox, and Opera (reasonably recent versions) -- although the text in the SVG tooltip might extend past its rectangle depending on your font settings. One reason to use an HTML tooltip! Another reason is that it doesn't get cut off by the edge of the SVG.
Leave a comment if you have any bugs in Safari or IE9/10/11. (IE8 and under are out of luck, since they don't do SVG).
Edit 2
So what about your original idea, to position the tooltip on the circle itself? There are definite benefits to being able to position the tip exactly: better layout control, and the text doesn't wiggle around with the mouse. And most importantly, you can just position it once, on the mouseover event, instead of reacting to every mousemove event.
But to do this, you can no longer just use the mouse position to figure out where to put the tooltip -- you need to figure out the position of the element, which means you have to deal with transformations. The SVG spec introduces a set of interfaces for locating SVG elements relative to other parts of the DOM.
For converting between two SVG transformation systems you use SVGElement.getTransformToElement(SVGElement); for converting between an SVG coordinate system and the screen, you use SVGElement.getScreenCTM(). The result are transformation matrices from which you can
extract the net horizontal and vertical translation.
The key code for the SVG tooltip is
var tooltipParent = SVGtooltip[0][0].parentNode;
var matrix =
this.getTransformToElement(tooltipParent)
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
SVGtooltip
.attr("transform", "translate(" + (matrix.e)
+ "," + (matrix.f - 30) + ")");
The key code for the HTML tooltip is
var matrix = this.getScreenCTM()
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
absoluteHTMLtooltip
.style("left",
(window.pageXOffset + matrix.e) + "px")
.style("top",
(window.pageYOffset + matrix.f + 30) + "px");
Live example: http://fiddle.jshell.net/tPv46/89/
Again, I'd appreciate a confirmation comment from anyone who can test this in Safari or IE -- or any mobile browser. I'm pretty sure I've used standard API for everything, but just because the API is standard doesn't mean it's universally implemented!
Please note: This question relates to the Appcelerator Titanium platform, not the stock iOS SDK.
I'm making a tag cloud with a layout: horizontal view. I'm most of the way there, but I can't get the final Titanium.UI.Label on a line to wrap if it doesn't fit. Instead, it gets ellipsized (in a useless manner).
Is there a way I can prevent this on iOS? Seems to work fine on Android.
If you try to set the label width to auto, Titanium will calculate the label width in runtime.
It make sense to get a ellipsized label in horizontal view.
You may need to determine the dynamic label width in your tag cloud case. But just leave it to titanium, you just need to change the dynamic width to static width with this tricky code.
for (var i = 0; i < data.length; i++) {
var label = Ti.UI.createLabel({ text: data[i], width: 'auto', height: 20,left: 5, top: 5});
label.width = label.width + 5; //this determine width in runtime assign back to static width
view.add(label);
}
The iPhone's answer to this is minimumFontSize but that makes no sense in a tag cloud... Have you considered adding this to a horizontal scrollview and setting the contentWidth to auto?
Also does each of your label have it's width set to 'auto'? I imagine setting that would cause the word to overflow the layout and be pushed down to the next line.