Tabulator layout:"fitColumns" causes vibrating DIV - flexbox

I have a tabulator 4.9 contained within one element of a flexbox. The flexbox element has flex-grow>0.
Some of my columns are hidden during the initial draw. The select element above the table hides columns and shows the selected column, and redraws the table.
When using layout:"fitColumns", the table has two issues when first rendered-
The table is wider than it's container, by roughly the width of the scrollbar. Changing the selectbox above the table causes the table to redraw and fixes this issue.
The table vibrates erratically. Both the container DIV and the table are shifting back and forth by 1 or 2 pixels. They appear to be stuck in a feedback loop.
I am using Chrome 87.0.4280.141 (Official Build) (64-bit)
Example here:
https://beta.tspcenter.com/tabulator.php
Tabulator code:
table = new Tabulator("#leaderboardTablesContainer", {
height: "311px",
layout:"fitColumns",
index:"Username",
data: results,
columns:headers,
initialSort:[{column:firstVisibleColumn, dir:"desc"}]
});
Select element code:
select.addEventListener('change',function(e) {
table.clearFilter();
table.hideColumn(table.currentColumnVisible);
table.showColumn(e.target.value);
table.setSort(e.target.value,"desc");
table.currentColumnVisible = e.target.value;
table.setFilter(e.target.value, "!=", "");
table.redraw();
});

I'm not sure why this works. But specifying a width on the flex-element fixed the problem.
flex: 2000 1 450px; width: 5px;
It seems to go against my understanding of flexbox. And indeed, the flexbox grows well past the 5px anyway. It seems kind of hacky but it stops the feedback loop problem and the table displays correctly.

Related

Vue 3 Masonry Layout

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')

How to make label text in jointjs elements not selectable?

i'm trying to find a solution for the following. When I drag a link between elements the label text inside the elements get selected for some reason.
Lets say I have an element A with the property A.attr("body/magnet", "active"); set and A.attr("label/text", "some text"); When I create a link from that element by clicking and dragging the label text gets selected on elements the link goes through.
This seems a little bit random though as sometimes all the labels in the graph gets selected when dragging the link.
Is there a way to make the label text not to be selectable?
We solved it by adding the following label style to the shapes.
let element = new joint.shapes.standard.Rectangle();
element.attr(
"label/style",
"-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;"
);
From the answer above the links will still be selected, so you can put css on you #paper is or canvas like so
#paper {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

React Virtualized - Render Table with full height to show all rows

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. :)

QML Row vs. RowLayout

I'm trying to write a topbar for my application that should contain mainly the app logo (a small image) and the app title (just text). Moreover, I'd like this topbar to automatically resize according to the window's height.
I'm new to QML, but I suppose that I should wrap these components inside a Row or a RowLayout component. This is my sample code:
import QtQuick 2.0
import QtQuick.Layouts 1.0
Rectangle
{
id: mainwindow
width: 1024
height: 600
Row
{
id: rowlayout
height: logoimage.height
spacing: 5
property int count: 3
anchors
{
left: parent.left
right: parent.right
top: parent.top
}
Image
{
id: logoimage
source: "qrc:/images/resources/images/icon.png"
height: mainwindow.height / 20
anchors.top: parent.top
anchors.left: parent.left
}
Text
{
id: logotext
text: qsTr("This is my logo text")
font.pixelSize: parent.height
font.family: "Sans Serif"
height: parent.height
verticalAlignment: Text.AlignVCenter
anchors.top: parent.top
anchors.left: logoimage.right
}
/*
Rectangle
{
id: otherrect
height: parent.height
color: "lightgreen"
anchors.top: parent.top
anchors.left: logotext.right
anchors.right: parent.right
}
*/
}
}
I tell to the Row component that its height should follow the logo's height, and to the Image (logo) component that its height should be 1/20th of the Rectangle (mainwindow) component.
Using a Row container, the code behaves as expected but I get an annoying warning (QML Row: Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row. Row will not function.) and I have to do a lot of anchoring. Conversely, if I use a RowLayout container, I can remove most of the anchors but the Image completely ignores its height attribute (but the text still resizes correctly). So the questions are:
is this a bug of the RowLayout component? I'm using Qt-5.1.0-Beta with Android support, so this could be an explanation
how can I use a Row component without using anchors in its children and thus avoid the warning?
I'm missing something important or I'm almost on the right track but I have to bear with this beta of Qt until a stable version is released?
You said that you get the expected behavior with Row, so you should probably use it. The warning that Row is giving is asking you to remove the vertical anchors (top and bottom) from its child elements.
The Row element provides horizontal (left and right) anchor-like behavior for its child elements, but it doesn't mind if you use top and bottom anchors (notice that top and bottom were not in the warning).
In other words remove "anchors.left" and/or "anchors.right" lines from "logoimage", "logotext", and "otherrect" (if you plan on uncommenting it at some point), but not the "anchors.top" lines, and that should stop the warning and keep the correct behavior.
An alternative is to just remove the Row element and use Item or FocusScope (if you plan on having input elements in your "top bar" area), which will not try to take over anchoring operations, and that may be a better fit for you if you really like anchors.
You need to give a width to your layout if you want it to strecht its children, either with width: parent.width, or better, with anchors { left: parent.left; right: parent.right } and no anchors on vertical lines inside childrens of the layout.
1) NO, it is no a bug of RowLayout
2) Consider that RowLayout is preferred to Row because is most expressive for components placing. The Row component is better that Rowlayout only for graphics or animation apps
3) The stable version is now available, but your errors are not bugs ;)

DOJO ContentPane inner DIV height changing on ContentPane resize

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");
});
});
});​

Resources